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
+
+