diff --git a/src/Controller/PartListsController.php b/src/Controller/PartListsController.php index 5d10da0b..6a59f817 100644 --- a/src/Controller/PartListsController.php +++ b/src/Controller/PartListsController.php @@ -77,7 +77,7 @@ class PartListsController extends AbstractController $this->addFlash('error', 'part.table.actions.no_params_given'); } else { $parts = $actionHandler->idStringToArray($ids); - $actionHandler->handleAction($action, $parts, $target ? (int) $target : null); + $redirectResponse = $actionHandler->handleAction($action, $parts, $target ? (int) $target : null, $redirect); //Save changes $this->entityManager->flush(); @@ -85,6 +85,11 @@ class PartListsController extends AbstractController $this->addFlash('success', 'part.table.actions.success'); } + //If the action handler returned a response, we use it, otherwise we redirect back to the previous page. + if ($redirectResponse){ + return $redirectResponse; + } + return $this->redirect($redirect); } diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php index ede384e0..ea740166 100644 --- a/src/Controller/ProjectController.php +++ b/src/Controller/ProjectController.php @@ -21,10 +21,18 @@ namespace App\Controller; use App\DataTables\ProjectBomEntriesDataTable; +use App\Entity\Parts\Part; use App\Entity\ProjectSystem\Project; +use App\Entity\ProjectSystem\ProjectBOMEntry; +use App\Form\ProjectSystem\ProjectBOMEntryCollectionType; +use App\Form\Type\StructuralEntityType; +use Doctrine\Common\Collections\ArrayCollection; +use Doctrine\ORM\EntityManagerInterface; use Omines\DataTablesBundle\DataTableFactory; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; /** @@ -58,4 +66,69 @@ class ProjectController extends AbstractController 'project' => $project, ]); } + + /** + * @Route("/{id}/add_parts", name="project_add_parts") + * @param Request $request + * @param Project|null $project + */ + public function addPart(Request $request, Project $project, EntityManagerInterface $entityManager): Response + { + $this->denyAccessUnlessGranted('edit', $project); + + $builder = $this->createFormBuilder(); + $builder->add('project', StructuralEntityType::class, [ + 'class' => Project::class, + 'required' => true, + 'disabled' => true, + 'data' => $project, + ]); + $builder->add('bom_entries', ProjectBOMEntryCollectionType::class); + $builder->add('submit', SubmitType::class, ['label' => 'save']); + $form = $builder->getForm(); + + //Preset the BOM entries with the selected parts, when the form was not submitted yet + $preset_data = new ArrayCollection(); + foreach (explode(',', $request->get('parts', '')) as $part_id) { + $part = $entityManager->getRepository(Part::class)->find($part_id); + if (null !== $part) { + //If there is already a BOM entry for this part, we use this one (we edit it then) + $bom_entry = $entityManager->getRepository(ProjectBOMEntry::class)->findOneBy([ + 'project' => $project, + 'part' => $part + ]); + if ($bom_entry) { + $preset_data->add($bom_entry); + } else { //Otherwise create an empty one + $entry = new ProjectBOMEntry(); + $entry->setProject($project); + $entry->setPart($part); + $preset_data->add($entry); + } + } + } + $form['bom_entries']->setData($preset_data); + + $form->handleRequest($request); + if ($form->isSubmitted() && $form->isValid()) { + $data = $form->getData(); + $bom_entries = $data['bom_entries']; + foreach ($bom_entries as $bom_entry){ + $project->addBOMEntry($bom_entry); + } + $entityManager->flush(); + + //If a redirect query parameter is set, redirect to this page + if ($request->query->get('_redirect')) { + return $this->redirect($request->query->get('_redirect')); + } + //Otherwise just show the project info page + return $this->redirectToRoute('project_info', ['id' => $project->getID()]); + } + + return $this->renderForm('Projects/add_parts.html.twig', [ + 'project' => $project, + 'form' => $form, + ]); + } } \ No newline at end of file diff --git a/src/Controller/SelectAPIController.php b/src/Controller/SelectAPIController.php index f26c11f3..04b4fadd 100644 --- a/src/Controller/SelectAPIController.php +++ b/src/Controller/SelectAPIController.php @@ -25,6 +25,7 @@ use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; +use App\Entity\ProjectSystem\Project; use App\Services\Trees\NodesListBuilder; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; @@ -79,6 +80,14 @@ class SelectAPIController extends AbstractController return $this->getResponseForClass(MeasurementUnit::class, true); } + /** + * @Route("/project", name="select_project") + */ + public function projects(): Response + { + return $this->getResponseForClass(Project::class, false); + } + protected function getResponseForClass(string $class, bool $include_empty = false): Response { $test_obj = new $class(); diff --git a/src/Services/Parts/PartsTableActionHandler.php b/src/Services/Parts/PartsTableActionHandler.php index 2022c496..bd57282f 100644 --- a/src/Services/Parts/PartsTableActionHandler.php +++ b/src/Services/Parts/PartsTableActionHandler.php @@ -29,6 +29,8 @@ use App\Repository\DBElementRepository; use App\Repository\PartRepository; use Doctrine\ORM\EntityManagerInterface; use InvalidArgumentException; +use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; use Symfony\Component\Security\Core\Security; @@ -36,11 +38,13 @@ final class PartsTableActionHandler { private EntityManagerInterface $entityManager; private Security $security; + private UrlGeneratorInterface $urlGenerator; - public function __construct(EntityManagerInterface $entityManager, Security $security) + public function __construct(EntityManagerInterface $entityManager, Security $security, UrlGeneratorInterface $urlGenerator) { $this->entityManager = $entityManager; $this->security = $security; + $this->urlGenerator = $urlGenerator; } /** @@ -62,9 +66,21 @@ final class PartsTableActionHandler /** * @param Part[] $selected_parts + * @return RedirectResponse|null Returns a redirect response if the user should be redirected to another page, otherwise null */ - public function handleAction(string $action, array $selected_parts, ?int $target_id): void + public function handleAction(string $action, array $selected_parts, ?int $target_id, ?string $redirect_url = null): ?RedirectResponse { + if ($action === 'add_to_project') { + return new RedirectResponse( + $this->urlGenerator->generate('project_add_parts', [ + 'id' => $target_id, + 'parts' => implode(',', array_map(static fn (Part $part) => $part->getID(), $selected_parts)), + '_redirect' => $redirect_url + ]) + ); + } + + //Iterate over the parts and apply the action to it: foreach ($selected_parts as $part) { if (!$part instanceof Part) { @@ -115,6 +131,8 @@ final class PartsTableActionHandler default: throw new InvalidArgumentException('The given action is unknown! ('.$action.')'); } + + return null; } } diff --git a/templates/Projects/add_parts.html.twig b/templates/Projects/add_parts.html.twig new file mode 100644 index 00000000..7a7ae61f --- /dev/null +++ b/templates/Projects/add_parts.html.twig @@ -0,0 +1,22 @@ +{% extends "main_card.html.twig" %} + +{% block title %}{% trans %}project.add_parts_to_project{% endtrans %}{% endblock %} + +{% block card_title %} + + {% trans %}project.add_parts_to_project{% endtrans %}{% if project %}: {{ project.name }}{% endif %} +{% endblock %} + +{% block card_content %} + + {{ form_start(form) }} + + {{ form_row(form.project) }} + {% form_theme form.bom_entries with ['Form/collection_types_layout.html.twig'] %} + {{ form_widget(form.bom_entries) }} + + {{ form_row(form.submit) }} + + {{ form_end(form) }} + +{% endblock %} \ No newline at end of file diff --git a/templates/components/datatables.macro.html.twig b/templates/components/datatables.macro.html.twig index ccf3ba95..c65600e7 100644 --- a/templates/components/datatables.macro.html.twig +++ b/templates/components/datatables.macro.html.twig @@ -48,6 +48,9 @@ + + + diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index 391c3803..507ca9da 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -9983,5 +9983,29 @@ Element 3 Add entry + + + part_list.action.group.projects + Projects + + + + + part_list.action.projects.add_to_project + Add parts to project + + + + + project.bom.delete.confirm + Do you really want to delete this BOM entry? + + + + + project.add_parts_to_project + Add parts to project BOM + +