Projekt BOM-Konfiguration um Assemblies bereinigen.

Assembly BOM-Konfiguration um Projektauswahl erweitern (APS-3, APS-4)
This commit is contained in:
Marcel Diegelmann 2025-06-17 11:28:42 +02:00
parent df85017efb
commit adbe310d7f
53 changed files with 739 additions and 1506 deletions

View file

@ -8,6 +8,7 @@ use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Form\Type\BigDecimalNumberType;
use App\Form\Type\CurrencyEntityType;
use App\Form\Type\PartSelectType;
use App\Form\Type\ProjectSelectType;
use App\Form\Type\RichTextEditorType;
use App\Form\Type\SIUnitType;
use Symfony\Component\Form\AbstractType;
@ -34,11 +35,13 @@ class AssemblyBOMEntryType extends AbstractType
});
$builder
->add('part', PartSelectType::class, [
'required' => false,
])
->add('project', ProjectSelectType::class, [
'label' => 'assembly.bom.project',
'required' => false,
])
->add('name', TextType::class, [
'label' => 'assembly.bom.name',
'required' => false,
@ -75,10 +78,8 @@ class AssemblyBOMEntryType extends AbstractType
'required' => false,
'label' => false,
'short' => true,
])
;
]
);
}
public function configureOptions(OptionsResolver $resolver): void

View file

@ -78,35 +78,34 @@ class AssemblyBuildType extends AbstractType implements DataMapperInterface
'required' => false,
]);
//The form is initially empty, we have to define the fields after we know the data
//The form is initially empty, define the fields after we know the data
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) {
$form = $event->getForm();
/** @var AssemblyBuildRequest $build_request */
$build_request = $event->getData();
/** @var AssemblyBuildRequest $assemblyBuildRequest */
$assemblyBuildRequest = $event->getData();
$form->add('addBuildsToBuildsPart', CheckboxType::class, [
'label' => 'assembly.build.add_builds_to_builds_part',
'required' => false,
'disabled' => !$build_request->getAssembly()->getBuildPart() instanceof Part,
'disabled' => !$assemblyBuildRequest->getAssembly()->getBuildPart() instanceof Part,
]);
if ($build_request->getAssembly()->getBuildPart() instanceof Part) {
if ($assemblyBuildRequest->getAssembly()->getBuildPart() instanceof Part) {
$form->add('buildsPartLot', PartLotSelectType::class, [
'label' => 'assembly.build.builds_part_lot',
'required' => false,
'part' => $build_request->getAssembly()->getBuildPart(),
'part' => $assemblyBuildRequest->getAssembly()->getBuildPart(),
'placeholder' => 'assembly.build.buildsPartLot.new_lot'
]);
}
foreach ($build_request->getPartBomEntries() as $bomEntry) {
foreach ($assemblyBuildRequest->getPartBomEntries() as $bomEntry) {
//Every part lot has a field to specify the number of parts to take from this lot
foreach ($build_request->getPartLotsForBOMEntry($bomEntry) as $lot) {
foreach ($assemblyBuildRequest->getPartLotsForBOMEntry($bomEntry) as $lot) {
$form->add('lot_' . $lot->getID(), SIUnitType::class, [
'label' => false,
'measurement_unit' => $bomEntry->getPart()->getPartUnit(),
'max' => min($build_request->getNeededAmountForBOMEntry($bomEntry), $lot->getAmount()),
'max' => min($assemblyBuildRequest->getNeededAmountForBOMEntry($bomEntry), $lot->getAmount()),
'disabled' => !$this->security->isGranted('withdraw', $lot),
]);
}

View file

@ -59,7 +59,6 @@ class ProjectAddPartsType extends AbstractType
],
'constraints' => [
new UniqueObjectCollection(message: 'project.bom_entry.part_already_in_bom', fields: ['part']),
new UniqueObjectCollection(message: 'project.bom_entry.assembly_already_in_bom', fields: ['assembly']),
new UniqueObjectCollection(message: 'project.bom_entry.name_already_in_bom', fields: ['name']),
]
]);

View file

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace App\Form\ProjectSystem;
use App\Entity\ProjectSystem\ProjectBOMEntry;
use App\Form\Type\AssemblySelectType;
use App\Form\Type\BigDecimalNumberType;
use App\Form\Type\CurrencyEntityType;
use App\Form\Type\PartSelectType;
@ -39,10 +38,6 @@ class ProjectBOMEntryType extends AbstractType
'label' => 'project.bom.part',
'required' => false,
])
->add('assembly', AssemblySelectType::class, [
'label' => 'project.bom.assembly',
'required' => false,
])
->add('name', TextType::class, [
'label' => 'project.bom.name',
'required' => false,

View file

@ -22,7 +22,6 @@ declare(strict_types=1);
*/
namespace App\Form\ProjectSystem;
use App\Helpers\Assemblies\AssemblyBuildRequest;
use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\Parts\Part;
use App\Entity\Parts\PartLot;
@ -39,11 +38,10 @@ use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Translation\TranslatorInterface;
class ProjectBuildType extends AbstractType implements DataMapperInterface
{
public function __construct(private readonly Security $security, private readonly TranslatorInterface $translator)
public function __construct(private readonly Security $security)
{
}
@ -113,25 +111,6 @@ class ProjectBuildType extends AbstractType implements DataMapperInterface
]);
}
}
foreach ($projectBuildRequest->getAssemblyBomEntries() as $bomEntry) {
$assemblyBuildRequest = new AssemblyBuildRequest($bomEntry->getAssembly(), $projectBuildRequest->getNumberOfBuilds());
//Add fields for assembly bom entries
foreach ($assemblyBuildRequest->getPartBomEntries() as $partBomEntry) {
foreach ($assemblyBuildRequest->getPartLotsForBOMEntry($partBomEntry) as $lot) {
$form->add('lot_' . $lot->getID(), SIUnitType::class, [
'label' => $this->translator->trans('project.build.builds_part_lot_label', [
'%name%' => $partBomEntry->getPart()->getName(),
'%quantity%' => $partBomEntry->getQuantity() * $projectBuildRequest->getNumberOfBuilds()
]),
'measurement_unit' => $partBomEntry->getPart()->getPartUnit(),
'max' => min($assemblyBuildRequest->getNeededAmountForBOMEntry($partBomEntry), $lot->getAmount()),
'disabled' => !$this->security->isGranted('withdraw', $lot),
]);
}
}
}
});
}

View file

@ -4,9 +4,9 @@ declare(strict_types=1);
namespace App\Form\Type;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Attachments\Attachment;
use App\Services\Attachments\AssemblyPreviewGenerator;
use App\Entity\ProjectSystem\Project;
use App\Services\Attachments\ProjectPreviewGenerator;
use App\Services\Attachments\AttachmentURLGenerator;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
@ -20,9 +20,9 @@ use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class AssemblySelectType extends AbstractType implements DataMapperInterface
class ProjectSelectType extends AbstractType implements DataMapperInterface
{
public function __construct(private readonly UrlGeneratorInterface $urlGenerator, private readonly EntityManagerInterface $em, private readonly AssemblyPreviewGenerator $previewGenerator, private readonly AttachmentURLGenerator $attachmentURLGenerator)
public function __construct(private readonly UrlGeneratorInterface $urlGenerator, private readonly EntityManagerInterface $em, private readonly ProjectPreviewGenerator $previewGenerator, private readonly AttachmentURLGenerator $attachmentURLGenerator)
{
}
@ -69,28 +69,28 @@ class AssemblySelectType extends AbstractType implements DataMapperInterface
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'class' => Assembly::class,
'class' => Project::class,
'choice_label' => 'name',
'compound' => true,
'error_bubbling' => false,
]);
error_log($this->urlGenerator->generate('typeahead_assemblies', ['query' => '__QUERY__']));
error_log($this->urlGenerator->generate('typeahead_projects', ['query' => '__QUERY__']));
$resolver->setDefaults([
'attr' => [
'data-controller' => 'elements--assembly-select',
'data-autocomplete' => $this->urlGenerator->generate('typeahead_assemblies', ['query' => '__QUERY__']),
'data-controller' => 'elements--project-select',
'data-autocomplete' => $this->urlGenerator->generate('typeahead_projects', ['query' => '__QUERY__']),
'autocomplete' => 'off',
],
]);
$resolver->setDefaults([
//Prefill the selected choice with the needed data, so the user can see it without an additional Ajax request
'choice_attr' => ChoiceList::attr($this, function (?Assembly $assembly) {
if($assembly instanceof Assembly) {
'choice_attr' => ChoiceList::attr($this, function (?Project $project) {
if($project instanceof Project) {
//Determine the picture to show:
$preview_attachment = $this->previewGenerator->getTablePreviewAttachment($assembly);
$preview_attachment = $this->previewGenerator->getTablePreviewAttachment($project);
if ($preview_attachment instanceof Attachment) {
$preview_url = $this->attachmentURLGenerator->getThumbnailURL($preview_attachment,
'thumbnail_sm');
@ -99,8 +99,8 @@ class AssemblySelectType extends AbstractType implements DataMapperInterface
}
}
return $assembly instanceof Assembly ? [
'data-description' => $assembly->getDescription() ? mb_strimwidth($assembly->getDescription(), 0, 127, '...') : '',
return $project instanceof Project ? [
'data-description' => $project->getDescription() ? mb_strimwidth($project->getDescription(), 0, 127, '...') : '',
'data-category' => '',
'data-footprint' => '',
'data-image' => $preview_url,