mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-02-28 20:39:35 +00:00
Assemblies einführen
This commit is contained in:
parent
e1418dfdc1
commit
f0748a2123
107 changed files with 14096 additions and 98 deletions
88
src/Form/AssemblySystem/AssemblyAddPartsType.php
Normal file
88
src/Form/AssemblySystem/AssemblyAddPartsType.php
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
namespace App\Form\AssemblySystem;
|
||||
|
||||
use App\Entity\AssemblySystem\Assembly;
|
||||
use App\Entity\AssemblySystem\AssemblyBOMEntry;
|
||||
use App\Form\Type\StructuralEntityType;
|
||||
use App\Validator\Constraints\UniqueObjectCollection;
|
||||
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\NotNull;
|
||||
|
||||
class AssemblyAddPartsType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add('assembly', StructuralEntityType::class, [
|
||||
'class' => Assembly::class,
|
||||
'required' => true,
|
||||
'disabled' => $options['assembly'] instanceof Assembly, //If a assembly is given, disable the field
|
||||
'data' => $options['assembly'],
|
||||
'constraints' => [
|
||||
new NotNull()
|
||||
]
|
||||
]);
|
||||
$builder->add('bom_entries', AssemblyBOMEntryCollectionType::class, [
|
||||
'entry_options' => [
|
||||
'constraints' => [
|
||||
new UniqueEntity(fields: ['part', 'assembly'], message: 'assembly.bom_entry.part_already_in_bom',
|
||||
entityClass: AssemblyBOMEntry::class),
|
||||
new UniqueEntity(fields: ['name', 'assembly'], message: 'assembly.bom_entry.name_already_in_bom',
|
||||
entityClass: AssemblyBOMEntry::class, ignoreNull: true),
|
||||
]
|
||||
],
|
||||
'constraints' => [
|
||||
new UniqueObjectCollection(message: 'assembly.bom_entry.part_already_in_bom', fields: ['part']),
|
||||
new UniqueObjectCollection(message: 'assembly.bom_entry.name_already_in_bom', fields: ['name']),
|
||||
]
|
||||
]);
|
||||
$builder->add('submit', SubmitType::class, ['label' => 'save']);
|
||||
|
||||
//After submit set the assembly for all bom entries, so that it can be validated properly
|
||||
$builder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) {
|
||||
$form = $event->getForm();
|
||||
/** @var Assembly $assembly */
|
||||
$assembly = $form->get('assembly')->getData();
|
||||
$bom_entries = $form->get('bom_entries')->getData();
|
||||
|
||||
foreach ($bom_entries as $bom_entry) {
|
||||
$bom_entry->setAssembly($assembly);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'assembly' => null,
|
||||
]);
|
||||
|
||||
$resolver->setAllowedTypes('assembly', ['null', Assembly::class]);
|
||||
}
|
||||
}
|
||||
32
src/Form/AssemblySystem/AssemblyBOMEntryCollectionType.php
Normal file
32
src/Form/AssemblySystem/AssemblyBOMEntryCollectionType.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form\AssemblySystem;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class AssemblyBOMEntryCollectionType extends AbstractType
|
||||
{
|
||||
public function getParent(): string
|
||||
{
|
||||
return CollectionType::class;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'entry_type' => AssemblyBOMEntryType::class,
|
||||
'entry_options' => [
|
||||
'label' => false,
|
||||
],
|
||||
'allow_add' => true,
|
||||
'allow_delete' => true,
|
||||
'by_reference' => false,
|
||||
'reindex_enable' => true,
|
||||
'label' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
90
src/Form/AssemblySystem/AssemblyBOMEntryType.php
Normal file
90
src/Form/AssemblySystem/AssemblyBOMEntryType.php
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form\AssemblySystem;
|
||||
|
||||
use App\Entity\AssemblySystem\AssemblyBOMEntry;
|
||||
use App\Form\Type\BigDecimalNumberType;
|
||||
use App\Form\Type\CurrencyEntityType;
|
||||
use App\Form\Type\PartSelectType;
|
||||
use App\Form\Type\RichTextEditorType;
|
||||
use App\Form\Type\SIUnitType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Event\PreSetDataEvent;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class AssemblyBOMEntryType extends AbstractType
|
||||
{
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) {
|
||||
$form = $event->getForm();
|
||||
/** @var AssemblyBOMEntry $data */
|
||||
$data = $event->getData();
|
||||
|
||||
$form->add('quantity', SIUnitType::class, [
|
||||
'label' => 'assembly.bom.quantity',
|
||||
'measurement_unit' => $data && $data->getPart() ? $data->getPart()->getPartUnit() : null,
|
||||
]);
|
||||
});
|
||||
|
||||
$builder
|
||||
|
||||
->add('part', PartSelectType::class, [
|
||||
'required' => false,
|
||||
])
|
||||
|
||||
->add('name', TextType::class, [
|
||||
'label' => 'assembly.bom.name',
|
||||
'required' => false,
|
||||
])
|
||||
->add('mountnames', TextType::class, [
|
||||
'required' => false,
|
||||
'label' => 'assembly.bom.mountnames',
|
||||
'empty_data' => '',
|
||||
'attr' => [
|
||||
'class' => 'tagsinput',
|
||||
'data-controller' => 'elements--tagsinput',
|
||||
]
|
||||
])
|
||||
->add('comment', RichTextEditorType::class, [
|
||||
'required' => false,
|
||||
'label' => 'assembly.bom.comment',
|
||||
'empty_data' => '',
|
||||
'mode' => 'markdown-single_line',
|
||||
'attr' => [
|
||||
'rows' => 2,
|
||||
],
|
||||
])
|
||||
->add('price', BigDecimalNumberType::class, [
|
||||
'label' => false,
|
||||
'required' => false,
|
||||
'scale' => 5,
|
||||
'html5' => true,
|
||||
'attr' => [
|
||||
'min' => 0,
|
||||
'step' => 'any',
|
||||
],
|
||||
])
|
||||
->add('priceCurrency', CurrencyEntityType::class, [
|
||||
'required' => false,
|
||||
'label' => false,
|
||||
'short' => true,
|
||||
])
|
||||
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => AssemblyBOMEntry::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
183
src/Form/AssemblySystem/AssemblyBuildType.php
Normal file
183
src/Form/AssemblySystem/AssemblyBuildType.php
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
namespace App\Form\AssemblySystem;
|
||||
|
||||
use App\Helpers\Assemblies\AssemblyBuildRequest;
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use App\Entity\Parts\Part;
|
||||
use App\Entity\Parts\PartLot;
|
||||
use App\Form\Type\PartLotSelectType;
|
||||
use App\Form\Type\SIUnitType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\DataMapperInterface;
|
||||
use Symfony\Component\Form\Event\PreSetDataEvent;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class AssemblyBuildType extends AbstractType implements DataMapperInterface
|
||||
{
|
||||
public function __construct(private readonly Security $security)
|
||||
{
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'compound' => true,
|
||||
'data_class' => AssemblyBuildRequest::class
|
||||
]);
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->setDataMapper($this);
|
||||
|
||||
$builder->add('submit', SubmitType::class, [
|
||||
'label' => 'assembly.build.btn_build',
|
||||
'disabled' => !$this->security->isGranted('@parts_stock.withdraw'),
|
||||
]);
|
||||
|
||||
$builder->add('dontCheckQuantity', CheckboxType::class, [
|
||||
'label' => 'assembly.build.dont_check_quantity',
|
||||
'help' => 'assembly.build.dont_check_quantity.help',
|
||||
'required' => false,
|
||||
'attr' => [
|
||||
'data-controller' => 'pages--dont-check-quantity-checkbox'
|
||||
]
|
||||
]);
|
||||
|
||||
$builder->add('comment', TextType::class, [
|
||||
'label' => 'part.info.withdraw_modal.comment',
|
||||
'help' => 'part.info.withdraw_modal.comment.hint',
|
||||
'empty_data' => '',
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
|
||||
//The form is initially empty, we have to 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();
|
||||
|
||||
$form->add('addBuildsToBuildsPart', CheckboxType::class, [
|
||||
'label' => 'assembly.build.add_builds_to_builds_part',
|
||||
'required' => false,
|
||||
'disabled' => !$build_request->getAssembly()->getBuildPart() instanceof Part,
|
||||
]);
|
||||
|
||||
if ($build_request->getAssembly()->getBuildPart() instanceof Part) {
|
||||
$form->add('buildsPartLot', PartLotSelectType::class, [
|
||||
'label' => 'assembly.build.builds_part_lot',
|
||||
'required' => false,
|
||||
'part' => $build_request->getAssembly()->getBuildPart(),
|
||||
'placeholder' => 'assembly.build.buildsPartLot.new_lot'
|
||||
]);
|
||||
}
|
||||
|
||||
foreach ($build_request->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) {
|
||||
$form->add('lot_' . $lot->getID(), SIUnitType::class, [
|
||||
'label' => false,
|
||||
'measurement_unit' => $bomEntry->getPart()->getPartUnit(),
|
||||
'max' => min($build_request->getNeededAmountForBOMEntry($bomEntry), $lot->getAmount()),
|
||||
'disabled' => !$this->security->isGranted('withdraw', $lot),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public function mapDataToForms($data, \Traversable $forms): void
|
||||
{
|
||||
if (!$data instanceof AssemblyBuildRequest) {
|
||||
throw new \RuntimeException('Data must be an instance of ' . AssemblyBuildRequest::class);
|
||||
}
|
||||
|
||||
/** @var FormInterface[] $forms */
|
||||
$forms = iterator_to_array($forms);
|
||||
foreach ($forms as $key => $form) {
|
||||
//Extract the lot id from the form name
|
||||
$matches = [];
|
||||
if (preg_match('/^lot_(\d+)$/', $key, $matches)) {
|
||||
$lot_id = (int) $matches[1];
|
||||
$form->setData($data->getLotWithdrawAmount($lot_id));
|
||||
}
|
||||
}
|
||||
|
||||
$forms['comment']->setData($data->getComment());
|
||||
$forms['dontCheckQuantity']->setData($data->isDontCheckQuantity());
|
||||
$forms['addBuildsToBuildsPart']->setData($data->getAddBuildsToBuildsPart());
|
||||
if (isset($forms['buildsPartLot'])) {
|
||||
$forms['buildsPartLot']->setData($data->getBuildsPartLot());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function mapFormsToData(\Traversable $forms, &$data): void
|
||||
{
|
||||
if (!$data instanceof AssemblyBuildRequest) {
|
||||
throw new \RuntimeException('Data must be an instance of ' . AssemblyBuildRequest::class);
|
||||
}
|
||||
|
||||
/** @var FormInterface[] $forms */
|
||||
$forms = iterator_to_array($forms);
|
||||
|
||||
foreach ($forms as $key => $form) {
|
||||
//Extract the lot id from the form name
|
||||
$matches = [];
|
||||
if (preg_match('/^lot_(\d+)$/', $key, $matches)) {
|
||||
$lot_id = (int) $matches[1];
|
||||
$data->setLotWithdrawAmount($lot_id, (float) $form->getData());
|
||||
}
|
||||
}
|
||||
|
||||
$data->setComment($forms['comment']->getData());
|
||||
$data->setDontCheckQuantity($forms['dontCheckQuantity']->getData());
|
||||
|
||||
if (isset($forms['buildsPartLot'])) {
|
||||
$lot = $forms['buildsPartLot']->getData();
|
||||
if (!$lot) { //When the user selected "Create new lot", create a new lot
|
||||
$lot = new PartLot();
|
||||
$description = 'Build ' . date('Y-m-d H:i:s');
|
||||
if ($data->getComment() !== '') {
|
||||
$description .= ' (' . $data->getComment() . ')';
|
||||
}
|
||||
$lot->setDescription($description);
|
||||
|
||||
$data->getAssembly()->getBuildPart()->addPartLot($lot);
|
||||
}
|
||||
|
||||
$data->setBuildsPartLot($lot);
|
||||
}
|
||||
//This has to be set after the builds part lot, so that it can disable the option
|
||||
$data->setAddBuildsToBuildsPart($forms['addBuildsToBuildsPart']->getData());
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue