diff --git a/src/Controller/SelectAPIController.php b/src/Controller/SelectAPIController.php index 04b4fadd..f6fb8e13 100644 --- a/src/Controller/SelectAPIController.php +++ b/src/Controller/SelectAPIController.php @@ -21,12 +21,15 @@ namespace App\Controller; use App\Entity\Base\AbstractStructuralDBElement; +use App\Entity\Contracts\NamedElementInterface; +use App\Entity\LabelSystem\LabelProfile; 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 Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Annotation\Route; @@ -88,6 +91,48 @@ class SelectAPIController extends AbstractController return $this->getResponseForClass(Project::class, false); } + /** + * @Route("/label_profiles", name="select_label_profiles") + * @return Response + */ + public function labelProfiles(EntityManagerInterface $entityManager): Response + { + $this->denyAccessUnlessGranted('@labels.create_labels'); + + if ($this->isGranted('@labels.read_profiles')) { + $profiles = $entityManager->getRepository(LabelProfile::class)->getPartLabelProfiles(); + $nodes = $this->buildJSONStructure($profiles); + } else { + $nodes = []; + } + + //Add the empty option + $this->addEmptyNode($nodes, 'part_list.action.generate_label.empty'); + + return $this->json($nodes); + } + + /** + * @Route("/label_profiles_lot", name="select_label_profiles_lot") + * @return Response + */ + public function labelProfilesLot(EntityManagerInterface $entityManager): Response + { + $this->denyAccessUnlessGranted('@labels.create_labels'); + + if ($this->isGranted('@labels.read_profiles')) { + $profiles = $entityManager->getRepository(LabelProfile::class)->getPartLotsLabelProfiles(); + $nodes = $this->buildJSONStructure($profiles); + } else { + $nodes = []; + } + + //Add the empty option + $this->addEmptyNode($nodes, 'part_list.action.generate_label.empty'); + + return $this->json($nodes); + } + protected function getResponseForClass(string $class, bool $include_empty = false): Response { $test_obj = new $class(); @@ -98,27 +143,41 @@ class SelectAPIController extends AbstractController $json = $this->buildJSONStructure($nodes); if ($include_empty) { - array_unshift($json, [ - 'text' => '', - 'value' => null, - 'data-subtext' => $this->translator->trans('part_list.action.select_null'), - ]); + $this->addEmptyNode($json); } return $this->json($json); } + protected function addEmptyNode(array &$arr, string $text = 'part_list.action.select_null'): array + { + array_unshift($arr, [ + 'text' => $this->translator->trans($text), + 'value' => null, + ]); + + return $arr; + } + protected function buildJSONStructure(array $nodes_list): array { $entries = []; foreach ($nodes_list as $node) { - /** @var AbstractStructuralDBElement $node */ - $entry = [ - 'text' => str_repeat('   ', $node->getLevel()).htmlspecialchars($node->getName()), - 'value' => $node->getID(), - 'data-subtext' => $node->getParent() ? $node->getParent()->getFullPath() : null, - ]; + if ($node instanceof AbstractStructuralDBElement) { + $entry = [ + 'text' => str_repeat('   ', $node->getLevel()).htmlspecialchars($node->getName()), + 'value' => $node->getID(), + 'data-subtext' => $node->getParent() ? $node->getParent()->getFullPath() : null, + ]; + } elseif ($node instanceof NamedElementInterface) { + $entry = [ + 'text' => htmlspecialchars($node->getName()), + 'value' => $node->getID(), + ]; + } else { + throw new \InvalidArgumentException('Invalid node type!'); + } $entries[] = $entry; } diff --git a/src/Repository/LabelProfileRepository.php b/src/Repository/LabelProfileRepository.php index e12f6b60..76372f34 100644 --- a/src/Repository/LabelProfileRepository.php +++ b/src/Repository/LabelProfileRepository.php @@ -109,4 +109,31 @@ class LabelProfileRepository extends NamedDBElementRepository return $this->findBy(['options.supported_element' => $type], $order_by); } + + /** + * Returns all LabelProfiles that can be used for parts + * @return array + */ + public function getPartLabelProfiles(): array + { + return $this->getDropdownProfiles('part'); + } + + /** + * Returns all LabelProfiles that can be used for part lots + * @return array + */ + public function getPartLotsLabelProfiles(): array + { + return $this->getDropdownProfiles('part_lot'); + } + + /** + * Returns all LabelProfiles that can be used for storelocations + * @return array + */ + public function getStorelocationsLabelProfiles(): array + { + return $this->getDropdownProfiles('storelocation'); + } } diff --git a/src/Services/Parts/PartsTableActionHandler.php b/src/Services/Parts/PartsTableActionHandler.php index 2fde40b2..8b695141 100644 --- a/src/Services/Parts/PartsTableActionHandler.php +++ b/src/Services/Parts/PartsTableActionHandler.php @@ -25,6 +25,7 @@ use App\Entity\Parts\Footprint; use App\Entity\Parts\Manufacturer; use App\Entity\Parts\MeasurementUnit; use App\Entity\Parts\Part; +use App\Entity\Parts\PartLot; use App\Repository\DBElementRepository; use App\Repository\PartRepository; use Doctrine\ORM\EntityManagerInterface; @@ -80,6 +81,27 @@ final class PartsTableActionHandler ); } + if ($action === 'generate_label' || $action === 'generate_label_lot') { + //For parts we can just use the comma separated part IDs + if ($action === 'generate_label') { + $targets = implode(',', array_map(static fn (Part $part) => $part->getID(), $selected_parts)); + } else { //For lots we have to extract the part lots + $targets = implode(',', array_map(static function (Part $part) { + //We concat the lot IDs of every part with a comma (which are later concated with a comma too per part) + return implode(',', array_map(static fn (PartLot $lot) => $lot->getID(), $part->getPartLots()->toArray())); + }, $selected_parts)); + } + + return new RedirectResponse( + $this->urlGenerator->generate($target_id !== 0 && $target_id !== null ? 'label_dialog_profile' : 'label_dialog', [ + 'profile' => $target_id, + 'target_id' => $targets, + 'generate' => '1', + 'target_type' => $action === 'generate_label_lot' ? 'part_lot' : 'part', + ]) + ); + } + //Iterate over the parts and apply the action to it: foreach ($selected_parts as $part) { diff --git a/templates/components/datatables.macro.html.twig b/templates/components/datatables.macro.html.twig index 0c5c170c..c699838a 100644 --- a/templates/components/datatables.macro.html.twig +++ b/templates/components/datatables.macro.html.twig @@ -48,6 +48,10 @@ + + + + diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index 3ff3157f..64cf145c 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -9316,7 +9316,7 @@ Element 3 part_list.action.select_null - No elements existing! + Empty element @@ -10267,5 +10267,29 @@ Element 3 IC logos + + + part_list.action.group.labels + Labels + + + + + part_list.action.projects.generate_label + Generate labels (for parts) + + + + + part_list.action.projects.generate_label_lot + Generate labels (for part lots) + + + + + part_list.action.generate_label.empty + Empty label + +