diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php index 3fac4ad9..d8d1fd19 100644 --- a/src/Controller/ProjectController.php +++ b/src/Controller/ProjectController.php @@ -25,7 +25,9 @@ use App\Entity\Parts\Part; use App\Entity\ProjectSystem\Project; use App\Entity\ProjectSystem\ProjectBOMEntry; use App\Form\ProjectSystem\ProjectBOMEntryCollectionType; +use App\Form\ProjectSystem\ProjectBuildType; use App\Form\Type\StructuralEntityType; +use App\Helpers\Projects\ProjectBuildRequest; use App\Services\ProjectSystem\ProjectBuildHelper; use Doctrine\Common\Collections\ArrayCollection; use Doctrine\ORM\EntityManagerInterface; @@ -84,11 +86,20 @@ class ProjectController extends AbstractController $number_of_builds = 1; } + $projectBuildRequest = new ProjectBuildRequest($project, $number_of_builds); + $form = $this->createForm(ProjectBuildType::class, $projectBuildRequest); - return $this->render('Projects/build/build.html.twig', [ + $form->handleRequest($request); + if ($form->isSubmitted() && $form->isValid()) { + //TODO + } + + return $this->renderForm('Projects/build/build.html.twig', [ 'buildHelper' => $buildHelper, 'project' => $project, + 'build_request' => $projectBuildRequest, 'number_of_builds' => $number_of_builds, + 'form' => $form, ]); } diff --git a/src/Form/ProjectSystem/ProjectBuildType.php b/src/Form/ProjectSystem/ProjectBuildType.php new file mode 100644 index 00000000..2e4ae602 --- /dev/null +++ b/src/Form/ProjectSystem/ProjectBuildType.php @@ -0,0 +1,80 @@ +. + */ + +namespace App\Form\ProjectSystem; + +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\SubmitType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormEvents; +use Symfony\Component\OptionsResolver\OptionsResolver; + +class ProjectBuildType extends AbstractType implements DataMapperInterface +{ + public function configureOptions(OptionsResolver $resolver) + { + $resolver->setDefaults([ + 'compound' => true, + 'data_class' => ProjectBuildRequest::class + ]); + } + + public function buildForm(FormBuilderInterface $builder, array $options) + { + $builder->setDataMapper($this); + + $builder->add('submit', SubmitType::class, []); + + //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(); + + 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(), + ]); + } + } + + }); + } + + public function mapDataToForms($viewData, \Traversable $forms) + { + $forms = iterator_to_array($forms); + + dump($viewData); + dump ($forms); + } + + public function mapFormsToData(\Traversable $forms, &$viewData) + { + // TODO: Implement mapFormsToData() method. + } +} \ No newline at end of file diff --git a/src/Helpers/Projects/ProjectBuildRequest.php b/src/Helpers/Projects/ProjectBuildRequest.php new file mode 100644 index 00000000..f04ed0da --- /dev/null +++ b/src/Helpers/Projects/ProjectBuildRequest.php @@ -0,0 +1,120 @@ +. + */ + +namespace App\Helpers\Projects; + +use App\Entity\Parts\PartLot; +use App\Entity\ProjectSystem\Project; +use App\Entity\ProjectSystem\ProjectBOMEntry; +use Doctrine\Common\Collections\Collection; + +final class ProjectBuildRequest +{ + private Project $project; + private int $number_of_builds; + + /** + * @param Project $project The project that should be build + * @param int $number_of_builds The number of builds that should be created + */ + public function __construct(Project $project, int $number_of_builds) + { + $this->project = $project; + $this->number_of_builds = $number_of_builds; + } + + /** + * Ensure that the projectBOMEntry belongs to the project, otherwise throw an exception. + * @param ProjectBOMEntry $entry + * @return void + */ + private function ensureBOMEntryValid(ProjectBOMEntry $entry): void + { + if ($entry->getProject() !== $this->project) { + throw new \InvalidArgumentException('The given BOM entry does not belong to the project!'); + } + } + + /** + * Returns the number of available lots to take stock from for the given BOM entry. + * @parm ProjectBOMEntry $entry + * @return PartLot[]|null Returns null if the entry is a non-part BOM entry + */ + public function getPartLotsForBOMEntry(ProjectBOMEntry $projectBOMEntry): ?array + { + $this->ensureBOMEntryValid($projectBOMEntry); + + if ($projectBOMEntry->getPart() === null) { + return null; + } + + return $projectBOMEntry->getPart()->getPartLots()->toArray(); + } + + /** + * Returns the needed amount of parts for the given BOM entry. + * @param ProjectBOMEntry $entry + * @return float + */ + public function getNeededAmountForBOMEntry(ProjectBOMEntry $entry): float + { + $this->ensureBOMEntryValid($entry); + + return $entry->getQuantity() * $this->number_of_builds; + } + + /** + * Returns the list of all bom entries that have to be build. + * @return ProjectBOMEntry[] + */ + public function getBomEntries(): array + { + return $this->project->getBomEntries()->toArray(); + } + + /** + * Returns the all part bom entries that have to be build. + * @return ProjectBOMEntry[] + */ + public function getPartBomEntries(): array + { + return $this->project->getBomEntries()->filter(function (ProjectBOMEntry $entry) { + return $entry->isPartBomEntry(); + })->toArray(); + } + + /** + * Returns which project should be build + * @return Project + */ + public function getProject(): Project + { + return $this->project; + } + + /** + * Returns the number of builds that should be created. + * @return int + */ + public function getNumberOfBuilds(): int + { + return $this->number_of_builds; + } +} \ No newline at end of file diff --git a/src/Services/ProjectSystem/ProjectBuildHelper.php b/src/Services/ProjectSystem/ProjectBuildHelper.php index 983313a5..33a8bc4d 100644 --- a/src/Services/ProjectSystem/ProjectBuildHelper.php +++ b/src/Services/ProjectSystem/ProjectBuildHelper.php @@ -74,12 +74,24 @@ class ProjectBuildHelper * Checks if the given project can be build with the current stock. * This means that the maximum buildable count is greater or equal than the requested $number_of_projects * @param Project $project - * @parm int $number_of_projects + * @parm int $number_of_builds * @return bool */ - public function isProjectBuildable(Project $project, int $number_of_projects = 1): bool + public function isProjectBuildable(Project $project, int $number_of_builds = 1): bool { - return $this->getMaximumBuildableCount($project) >= $number_of_projects; + return $this->getMaximumBuildableCount($project) >= $number_of_builds; + } + + /** + * Check if the given BOM entry can be build with the current stock. + * This means that the maximum buildable count is greater or equal than the requested $number_of_projects + * @param ProjectBOMEntry $bom_entry + * @param int $number_of_builds + * @return bool + */ + public function isBOMEntryBuildable(ProjectBOMEntry $bom_entry, int $number_of_builds = 1): bool + { + return $this->getMaximumBuildableCountForBOMEntry($bom_entry) >= $number_of_builds; } /** diff --git a/templates/Projects/build/_form.html.twig b/templates/Projects/build/_form.html.twig new file mode 100644 index 00000000..28843e93 --- /dev/null +++ b/templates/Projects/build/_form.html.twig @@ -0,0 +1,62 @@ +{% import "helper.twig" as helper %} + +{{ form_start(form) }} + +
+
+
+ {#
+
+ |
+ + {% if bom_entry.part %} + {{ bom_entry.part.name }} {% if bom_entry.name %}({{ bom_entry.name }}){% endif %} + {% endif %} + | ++ {{ bom_entry.mountnames }} + | ++ {{ build_request.neededAmountForBOMEntry(bom_entry) | format_amount(bom_entry.part.partUnit ?? null) }} {% trans %}project.builds.needed{% endtrans %} + (= {{ number_of_builds }} x {{ bom_entry.quantity | format_amount(bom_entry.part.partUnit ?? null) }}) + | +
+ {% set lots = build_request.partLotsForBOMEntry(bom_entry) %}
+ {% if lots is not null %}
+ {% for lot in lots %}
+ {# @var lot \App\Entity\Parts\PartLot #}
+
+
+
+ {% endfor %}
+ {% endif %}
+
+ {{ form_widget(form["lot_"~lot.id]) }}
+
+
+ / {{ lot.amount | format_amount(lot.part.partUnit) }} {% trans %}project.builds.stocked{% endtrans %}
+
+ |
+