Allow to directly add build as stock to the associated builds part.

This commit is contained in:
Jan Böhmer 2023-01-22 23:27:45 +01:00
parent 015c71cbd2
commit 2f42eb7cff
7 changed files with 207 additions and 3 deletions

View file

@ -92,7 +92,9 @@ class ProjectController extends AbstractController
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$buildHelper->doWithdrawForProjectBuildRequest($projectBuildRequest);
//We have to do a flush already here, so that the newly created partLot gets an ID and can be logged to DB later.
$entityManager->flush();
$buildHelper->doBuild($projectBuildRequest);
$entityManager->flush();
$this->addFlash('success', 'project.build.flash.success');

View file

@ -20,11 +20,14 @@
namespace App\Form\ProjectSystem;
use App\Entity\Parts\PartLot;
use App\Form\Type\PartLotSelectType;
use App\Form\Type\SIUnitType;
use App\Helpers\Projects\ProjectBuildRequest;
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;
@ -53,15 +56,32 @@ class ProjectBuildType extends AbstractType implements DataMapperInterface
$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 ProjectBuildRequest $build_request */
$build_request = $event->getData();
$form->add('addBuildsToBuildsPart', CheckboxType::class, [
'label' => 'project.build.add_builds_to_builds_part',
'required' => false,
'disabled' => $build_request->getProject()->getBuildPart() === null,
]);
if ($build_request->getProject()->getBuildPart()) {
$form->add('buildsPartLot', PartLotSelectType::class, [
'label' => 'project.build.builds_part_lot',
'required' => false,
'part' => $build_request->getProject()->getBuildPart(),
'placeholder' => 'project.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) {
@ -94,6 +114,10 @@ class ProjectBuildType extends AbstractType implements DataMapperInterface
}
$forms['comment']->setData($data->getComment());
$forms['addBuildsToBuildsPart']->setData($data->getAddBuildsToBuildsPart());
if (isset($forms['buildsPartLot'])) {
$forms['buildsPartLot']->setData($data->getBuildsPartLot());
}
}
@ -116,5 +140,22 @@ class ProjectBuildType extends AbstractType implements DataMapperInterface
}
$data->setComment($forms['comment']->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 (!empty($data->getComment())) {
$description .= ' (' . $data->getComment() . ')';
}
$lot->setDescription($description);
$data->getProject()->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());
}
}

View file

@ -0,0 +1,63 @@
<?php
/*
* 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\Type;
use App\Entity\Parts\Part;
use App\Entity\Parts\PartLot;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\ChoiceList\ChoiceList;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PartLotSelectType extends AbstractType
{
public function getParent()
{
return EntityType::class;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setRequired('part');
$resolver->setAllowedTypes('part', Part::class);
$resolver->setDefaults([
'class' => PartLot::class,
'choice_label' => ChoiceList::label($this, function (PartLot $part_lot) {
return ($part_lot->getStorageLocation() ? $part_lot->getStorageLocation()->getFullPath() : '')
. ' (' . $part_lot->getName() . '): ' . $part_lot->getAmount();
}),
'attr' => [
'data-controller' => 'elements--selectpicker',
'data-live-search' => true,
],
'query_builder' => function (Options $options) {
return function (EntityRepository $er) use ($options) {
return $er->createQueryBuilder('l')
->where('l.part = :part')
->setParameter('part', $options['part']);
};
}
]);
}
}

View file

@ -40,6 +40,10 @@ final class ProjectBuildRequest
private string $comment = '';
private ?PartLot $builds_lot = null;
private bool $add_build_to_builds_part = false;
/**
* @param Project $project The project that should be build
* @param int $number_of_builds The number of builds that should be created
@ -50,6 +54,17 @@ final class ProjectBuildRequest
$this->number_of_builds = $number_of_builds;
$this->initializeArray();
//By default, use the first available lot of builds part if there is one.
if($project->getBuildPart() !== null) {
$this->add_build_to_builds_part = true;
foreach( $project->getBuildPart()->getPartLots() as $lot) {
if (!$lot->isInstockUnknown()) {
$this->builds_lot = $lot;
break;
}
}
}
}
private function initializeArray(): void
@ -80,6 +95,62 @@ final class ProjectBuildRequest
}
}
/**
* Returns the partlot where the builds should be added to, or null if it should not be added to any lot.
* @return PartLot|null
*/
public function getBuildsPartLot(): ?PartLot
{
return $this->builds_lot;
}
/**
* Return if the builds should be added to the builds part of this project as new stock
* @return bool
*/
public function getAddBuildsToBuildsPart(): bool
{
return $this->add_build_to_builds_part;
}
/**
* Set if the builds should be added to the builds part of this project as new stock
* @param bool $new_value
* @return $this
*/
public function setAddBuildsToBuildsPart(bool $new_value): self
{
$this->add_build_to_builds_part = $new_value;
if ($new_value === false) {
$this->builds_lot = null;
}
return $this;
}
/**
* Set the partlot where the builds should be added to, or null if it should not be added to any lot.
* The part lot must belong to the project build part, or an exception is thrown!
* @param PartLot|null $new_part_lot
* @return $this
*/
public function setBuildsPartLot(?PartLot $new_part_lot): self
{
//Ensure that this new_part_lot belongs to the project
if (($new_part_lot !== null && $new_part_lot->getPart() !== $this->project->getBuildPart()) || $this->project->getBuildPart() === null) {
throw new \InvalidArgumentException('The given part lot does not belong to the projects build part!');
}
if ($new_part_lot !== null) {
$this->setAddBuildsToBuildsPart(true);
}
$this->builds_lot = $new_part_lot;
return $this;
}
/**
* Returns the comment where the user can write additional information about the build.
* @return string

View file

@ -135,13 +135,13 @@ class ProjectBuildHelper
}
/**
* Withdraw the parts from the stock using the given ProjectBuildRequest.
* Withdraw the parts from the stock using the given ProjectBuildRequest and create the build parts entries, if needed.
* The ProjectBuildRequest has to be validated before!!
* You have to flush changes to DB afterwards
* @param ProjectBuildRequest $buildRequest
* @return void
*/
public function doWithdrawForProjectBuildRequest(ProjectBuildRequest $buildRequest): void
public function doBuild(ProjectBuildRequest $buildRequest): void
{
$message = $buildRequest->getComment();
$message .= ' (Project build: '.$buildRequest->getProject()->getName().')';
@ -154,5 +154,9 @@ class ProjectBuildHelper
}
}
}
if ($buildRequest->getAddBuildsToBuildsPart()) {
$this->withdraw_add_helper->add($buildRequest->getBuildsPartLot(), $buildRequest->getNumberOfBuilds(), $message);
}
}
}

View file

@ -75,6 +75,11 @@
{{ form_row(form.comment) }}
{{ form_row(form.addBuildsToBuildsPart) }}
{% if form.buildsPartLot is defined %}
{{ form_row(form.buildsPartLot) }}
{% endif %}
{{ form_row(form.submit) }}
{{ form_end(form) }}

View file

@ -10369,5 +10369,23 @@ Element 3</target>
<target>Choose from which part lots the stock to build this project should be taken (and in which amount). Check the checkbox for each BOM Entry, when you are finished withdrawing the parts, or use the top checkbox to check all boxes at once.</target>
</segment>
</unit>
<unit id="rvdUHEn" name="project.build.buildsPartLot.new_lot">
<segment>
<source>project.build.buildsPartLot.new_lot</source>
<target>Create new lot</target>
</segment>
</unit>
<unit id="Kn_jkyo" name="project.build.add_builds_to_builds_part">
<segment>
<source>project.build.add_builds_to_builds_part</source>
<target>Add builds to project builds part</target>
</segment>
</unit>
<unit id="92b1_QD" name="project.build.builds_part_lot">
<segment>
<source>project.build.builds_part_lot</source>
<target>Target lot</target>
</segment>
</unit>
</file>
</xliff>