From a1390b36a8e4f4b09fb8cbe7575b589c016b34e6 Mon Sep 17 00:00:00 2001 From: Marcel Diegelmann Date: Tue, 30 Sep 2025 15:30:12 +0200 Subject: [PATCH] =?UTF-8?q?F=C3=BCge=20Designator=20zu=20St=C3=BCcklisten-?= =?UTF-8?q?Eintr=C3=A4gen=20f=C3=BCr=20Freitext-Angabe=20hinzu.=20EntityEx?= =?UTF-8?q?porter:=20Spaltennamen=20in=20lesbaren=20Export=20anpassen.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neue Unterstützung für Designator-Feld in AssemblyBomEntries eingeführt, einschließlich Updates für Migrationen, Übersetzungen und Frontend-Layout. Dies ermöglicht die Verwaltung freier Bezeichnungskennungen in der Stückliste. --- docs/configuration.md | 2 +- migrations/Version20250929140755.php | 26 ++++ .../AssemblyBomEntriesDataTable.php | 6 + .../AssemblySystem/AssemblyBOMEntry.php | 19 ++- .../AssemblySystem/AssemblyBOMEntryType.php | 7 +- .../Assemblies/AssemblyPartAggregator.php | 6 + .../ImportExportSystem/BOMImporter.php | 6 +- .../ImportExportSystem/EntityExporter.php | 143 ++++++++++-------- .../AssemblyBomTableColumns.php | 10 +- .../BehaviorSettings/TableSettings.php | 4 +- ...collection_types_layout_assembly.html.twig | 13 +- translations/messages.cs.xlf | 78 ++++++++++ translations/messages.da.xlf | 78 ++++++++++ translations/messages.de.xlf | 78 ++++++++++ translations/messages.el.xlf | 78 ++++++++++ translations/messages.en.xlf | 78 ++++++++++ translations/messages.es.xlf | 78 ++++++++++ translations/messages.fr.xlf | 78 ++++++++++ translations/messages.it.xlf | 78 ++++++++++ translations/messages.ja.xlf | 90 +++++++++++ translations/messages.nl.xlf | 78 ++++++++++ translations/messages.pl.xlf | 78 ++++++++++ translations/messages.ru.xlf | 78 ++++++++++ translations/messages.zh.xlf | 78 ++++++++++ 24 files changed, 1196 insertions(+), 72 deletions(-) create mode 100644 migrations/Version20250929140755.php diff --git a/docs/configuration.md b/docs/configuration.md index 242164bf..d2f1310a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -142,7 +142,7 @@ bundled with Part-DB. Set `DATABASE_MYSQL_SSL_VERIFY_CERT` if you want to accept are: `name`, `id`, `ipn`, `description`, `referencedAssemblies`, `edit`, `addedDate`, `lastModified`. * `TABLE_ASSEMBLIES_BOM_DEFAULT_COLUMNS`: The columns in assemblies bom tables, which are visible by default (when loading table for first time). Also specify the default order of the columns. This is a comma separated list of column names. Available columns - are: `quantity`, `name`, `id`, `ipn`, `description`, `addedDate`, `lastModified`. + are: `quantity`, `name`, `id`, `ipn`, `description`, `category`, `footprint`, `manufacturer`, `designator`, `mountnames`, `storage_location`, `amount`, `addedDate`, `lastModified`. * `CREATE_ASSEMBLY_USE_IPN_PLACEHOLDER_IN_NAME`: Use an %%ipn%% placeholder in the name of a assembly. Placeholder is replaced with the ipn input while saving. ### History/Eventlog-related settings diff --git a/migrations/Version20250929140755.php b/migrations/Version20250929140755.php new file mode 100644 index 00000000..a45dd8fb --- /dev/null +++ b/migrations/Version20250929140755.php @@ -0,0 +1,26 @@ +addSql('ALTER TABLE assembly_bom_entries ADD designator LONGTEXT NOT NULL AFTER mountnames'); + } + + public function down(Schema $schema): void + { + $this->addSql('ALTER TABLE assembly_bom_entries DROP designator'); + } +} diff --git a/src/DataTables/AssemblyBomEntriesDataTable.php b/src/DataTables/AssemblyBomEntriesDataTable.php index ddeedba2..55a47584 100644 --- a/src/DataTables/AssemblyBomEntriesDataTable.php +++ b/src/DataTables/AssemblyBomEntriesDataTable.php @@ -162,6 +162,12 @@ class AssemblyBomEntriesDataTable implements DataTableTypeInterface return $html; }, ]) + ->add('designator', TextColumn::class, [ + 'label' => 'assembly.bom.designator', + 'render' => function ($value, AssemblyBOMEntry $context) { + return htmlspecialchars($context->getDesignator()); + }, + ]) ->add('instockAmount', TextColumn::class, [ 'label' => 'assembly.bom.instockAmount', 'visible' => false, diff --git a/src/Entity/AssemblySystem/AssemblyBOMEntry.php b/src/Entity/AssemblySystem/AssemblyBOMEntry.php index 9620e489..e4337446 100644 --- a/src/Entity/AssemblySystem/AssemblyBOMEntry.php +++ b/src/Entity/AssemblySystem/AssemblyBOMEntry.php @@ -84,7 +84,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface; normalizationContext: ['groups' => ['bom_entry:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'] )] #[ApiFilter(PropertyFilter::class)] -#[ApiFilter(LikeFilter::class, properties: ["name", "comment", 'mountnames'])] +#[ApiFilter(LikeFilter::class, properties: ["name", 'mountnames', 'designator', "comment"])] #[ApiFilter(RangeFilter::class, properties: ['quantity'])] #[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified', 'quantity'])] class AssemblyBOMEntry extends AbstractDBElement implements UniqueValidatableInterface, TimeStampableInterface @@ -103,6 +103,13 @@ class AssemblyBOMEntry extends AbstractDBElement implements UniqueValidatableInt #[Groups(['bom_entry:read', 'bom_entry:write', 'import', 'simple', 'extended', 'full'])] protected string $mountnames = ''; + /** + * @var string Reference mark on the circuit diagram/PCB + */ + #[ORM\Column(name: 'designator', type: Types::TEXT)] + #[Groups(['bom_entry:read', 'bom_entry:write', 'import', 'simple', 'extended', 'full'])] + protected string $designator = ''; + /** * @var string|null An optional name describing this BOM entry (useful for non-part entries) */ @@ -190,6 +197,16 @@ class AssemblyBOMEntry extends AbstractDBElement implements UniqueValidatableInt return $this; } + public function getDesignator(): string + { + return $this->designator; + } + + public function setDesignator(string $designator): void + { + $this->designator = $designator; + } + /** * @return string */ diff --git a/src/Form/AssemblySystem/AssemblyBOMEntryType.php b/src/Form/AssemblySystem/AssemblyBOMEntryType.php index 851ab815..f8a7a86d 100644 --- a/src/Form/AssemblySystem/AssemblyBOMEntryType.php +++ b/src/Form/AssemblySystem/AssemblyBOMEntryType.php @@ -45,12 +45,17 @@ class AssemblyBOMEntryType extends AbstractType ]) ->add('name', TextType::class, [ 'label' => 'assembly.bom.name', + 'help' => 'assembly.bom.name.help', 'required' => false, ]) + ->add('designator', TextType::class, [ + 'label' => 'assembly.bom.designator', + 'help' => 'assembly.bom.designator.help', + 'required' => false + ]) ->add('mountnames', TextType::class, [ 'required' => false, 'label' => 'assembly.bom.mountnames', - 'empty_data' => '', 'attr' => [ 'class' => 'tagsinput', 'data-controller' => 'elements--tagsinput', diff --git a/src/Helpers/Assemblies/AssemblyPartAggregator.php b/src/Helpers/Assemblies/AssemblyPartAggregator.php index 2346075a..46495935 100644 --- a/src/Helpers/Assemblies/AssemblyPartAggregator.php +++ b/src/Helpers/Assemblies/AssemblyPartAggregator.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace App\Helpers\Assemblies; use App\Entity\AssemblySystem\Assembly; +use App\Entity\AssemblySystem\AssemblyBOMEntry; use App\Entity\Parts\Part; use Dompdf\Dompdf; use Dompdf\Options; @@ -61,6 +62,7 @@ class AssemblyPartAggregator */ private function processAssembly(Assembly $assembly, float $multiplier, array &$aggregatedParts): void { + /** @var AssemblyBOMEntry $bomEntry */ foreach ($assembly->getBomEntries() as $bomEntry) { // If the BOM entry refers to a part, add its quantity if ($bomEntry->getPart() instanceof Part) { @@ -70,6 +72,8 @@ class AssemblyPartAggregator $aggregatedParts[$part->getId()] = [ 'part' => $part, 'assembly' => $assembly, + 'name' => $bomEntry->getName(), + 'designator' => $bomEntry->getDesignator(), 'quantity' => $bomEntry->getQuantity(), 'multiplier' => $multiplier, ]; @@ -81,6 +85,8 @@ class AssemblyPartAggregator $aggregatedParts[] = [ 'part' => null, 'assembly' => $assembly, + 'name' => $bomEntry->getName(), + 'designator' => $bomEntry->getDesignator(), 'quantity' => $bomEntry->getQuantity(), 'multiplier' => $multiplier, ]; diff --git a/src/Services/ImportExportSystem/BOMImporter.php b/src/Services/ImportExportSystem/BOMImporter.php index 59e3d187..5ed8c388 100644 --- a/src/Services/ImportExportSystem/BOMImporter.php +++ b/src/Services/ImportExportSystem/BOMImporter.php @@ -866,7 +866,11 @@ class BOMImporter } if (isset($entry['designator'])) { - $bomEntry->setMountnames(trim($entry['designator']) === '' ? '' : trim($entry['designator'])); + if ($bomEntry instanceof ProjectBOMEntry) { + $bomEntry->setMountnames(trim($entry['designator']) === '' ? '' : trim($entry['designator'])); + } elseif ($bomEntry instanceof AssemblyBOMEntry) { + $bomEntry->setDesignator(trim($entry['designator']) === '' ? '' : trim($entry['designator'])); + } } $bomEntry->setPart($part); diff --git a/src/Services/ImportExportSystem/EntityExporter.php b/src/Services/ImportExportSystem/EntityExporter.php index 3766343c..1bdb0d8a 100644 --- a/src/Services/ImportExportSystem/EntityExporter.php +++ b/src/Services/ImportExportSystem/EntityExporter.php @@ -380,25 +380,31 @@ class EntityExporter ], Project::class => [ 'header' => [ - 'Id', 'ParentId', 'Type', 'ProjectNameHierarchical', 'ProjectName', 'ProjectFullName', 'BomQuantity', - 'BomPartId', 'BomPartIpn', 'BomPartMpnr', 'BomPartName', 'BomDesignator', 'BomPartDescription', - 'BomMountNames' + 'Id', 'ParentId', 'Type', 'ProjectNameHierarchical', 'ProjectName', 'ProjectFullName', + + //BOM relevant attributes + 'Quantity', 'PartId', 'PartName', 'Ipn', 'Manufacturer', 'Mpn', 'Name', 'Designator', + 'Description', 'MountNames' ], 'processEntity' => fn($entity, $depth) => [ - 'ProjectId' => $entity->getId(), - 'ParentProjectId' => $entity->getParent()?->getId() ?? '', + 'Id' => $entity->getId(), + 'ParentId' => $entity->getParent()?->getId() ?? '', 'Type' => 'project', 'ProjectNameHierarchical' => str_repeat('--', $depth) . ' ' . $entity->getName(), 'ProjectName' => $entity->getName(), 'ProjectFullName' => $this->getFullName($entity), - 'BomQuantity' => '-', - 'BomPartId' => '-', - 'BomPartIpn' => '-', - 'BomPartMpnr' => '-', - 'BomPartName' => '-', - 'BomDesignator' => '-', - 'BomPartDescription' => '-', - 'BomMountNames' => '-', + + //BOM relevant attributes + 'Quantity' => '-', + 'PartId' => '-', + 'PartName' => '-', + 'Ipn' => '-', + 'Manufacturer' => '-', + 'Mpn' => '-', + 'Name' => '-', + 'Designator' => '-', + 'Description' => '-', + 'MountNames' => '-', ], 'processBomEntries' => fn($entity, $depth) => array_map(fn(ProjectBOMEntry $bomEntry) => [ 'Id' => $entity->getId(), @@ -407,22 +413,29 @@ class EntityExporter 'ProjectNameHierarchical' => str_repeat('--', $depth) . '> ' . $entity->getName(), 'ProjectName' => $entity->getName(), 'ProjectFullName' => $this->getFullName($entity), - 'BomQuantity' => $bomEntry->getQuantity() ?? '', - 'BomPartId' => $bomEntry->getPart()?->getId() ?? '', - 'BomPartIpn' => $bomEntry->getPart()?->getIpn() ?? '', - 'BomPartMpnr' => $bomEntry->getPart()?->getManufacturerProductNumber() ?? '', - 'BomPartName' => $bomEntry->getPart()?->getName() ?? '', - 'BomDesignator' => $bomEntry->getName() ?? '', - 'BomPartDescription' => $bomEntry->getPart()?->getDescription() ?? '', - 'BomMountNames' => $bomEntry->getMountNames(), + + //BOM relevant attributes + 'Quantity' => $bomEntry->getQuantity() ?? '', + 'PartId' => $bomEntry->getPart()?->getId() ?? '', + 'PartName' => $bomEntry->getPart()?->getName() ?? '', + 'Ipn' => $bomEntry->getPart()?->getIpn() ?? '', + 'Manufacturer' => $bomEntry->getPart()?->getManufacturer()?->getName() ?? '', + 'Mpn' => $bomEntry->getPart()?->getManufacturerProductNumber() ?? '', + 'Name' => $bomEntry->getPart()?->getName() ?? '', + 'Designator' => $bomEntry->getMountnames() ?? '', + 'Description' => $bomEntry->getPart()?->getDescription() ?? '', + 'MountNames' => $bomEntry->getMountNames(), ], $entity->getBomEntries()->toArray()), ], Assembly::class => [ 'header' => [ 'Id', 'ParentId', 'Type', 'AssemblyIpn', 'AssemblyNameHierarchical', 'AssemblyName', - 'AssemblyFullName', 'BomQuantity', 'BomMultiplier', 'BomPartId', 'BomPartIpn', 'BomPartMpnr', - 'BomPartName', 'BomDesignator', 'BomPartDescription', 'BomMountNames', 'BomReferencedAssemblyId', - 'BomReferencedAssemblyIpn', 'BomReferencedAssemblyFullName' + 'AssemblyFullName', + + //BOM relevant attributes + 'Quantity', 'PartId', 'PartName', 'Ipn', 'Manufacturer', 'Mpn', 'Name', 'Designator', + 'Description', 'MountNames', 'ReferencedAssemblyId', 'ReferencedAssemblyIpn', + 'ReferencedAssemblyFullName' ], 'processEntity' => fn($entity, $depth) => [ 'Id' => $entity->getId(), @@ -432,18 +445,21 @@ class EntityExporter 'AssemblyNameHierarchical' => str_repeat('--', $depth) . ' ' . $entity->getName(), 'AssemblyName' => $entity->getName(), 'AssemblyFullName' => $this->getFullName($entity), - 'BomQuantity' => '-', - 'BomMultiplier' => '-', - 'BomPartId' => '-', - 'BomPartIpn' => '-', - 'BomPartMpnr' => '-', - 'BomPartName' => '-', - 'BomDesignator' => '-', - 'BomPartDescription' => '-', - 'BomMountNames' => '-', - 'BomReferencedAssemblyId' => '-', - 'BomReferencedAssemblyIpn' => '-', - 'BomReferencedAssemblyFullName' => '-', + + //BOM relevant attributes + 'Quantity' => '-', + 'PartId' => '-', + 'PartName' => '-', + 'Ipn' => '-', + 'Manufacturer' => '-', + 'Mpn' => '-', + 'Name' => '-', + 'Designator' => '-', + 'Description' => '-', + 'MountNames' => '-', + 'ReferencedAssemblyId' => '-', + 'ReferencedAssemblyIpn' => '-', + 'ReferencedAssemblyFullName' => '-', ], 'processBomEntries' => fn($entity, $depth) => $this->processBomEntriesWithAggregatedParts($entity, $depth), ], @@ -556,6 +572,7 @@ class EntityExporter { $rows = []; + /** @var AssemblyBOMEntry $bomEntry */ foreach ($assembly->getBomEntries() as $bomEntry) { // Add the BOM entry itself $rows[] = [ @@ -566,18 +583,21 @@ class EntityExporter 'AssemblyNameHierarchical' => str_repeat('--', $depth) . '> ' . $assembly->getName(), 'AssemblyName' => $assembly->getName(), 'AssemblyFullName' => $this->getFullName($assembly), - 'BomQuantity' => $bomEntry->getQuantity() ?? '', - 'BomMultiplier' => '', - 'BomPartId' => $bomEntry->getPart()?->getId() ?? '-', - 'BomPartIpn' => $bomEntry->getPart()?->getIpn() ?? '-', - 'BomPartMpnr' => $bomEntry->getPart()?->getManufacturerProductNumber() ?? '-', - 'BomPartName' => $bomEntry->getPart()?->getName() ?? '-', - 'BomDesignator' => $bomEntry->getName() ?? '-', - 'BomPartDescription' => $bomEntry->getPart()?->getDescription() ?? '-', - 'BomMountNames' => $bomEntry->getMountNames(), - 'BomReferencedAssemblyId' => $bomEntry->getReferencedAssembly()?->getId() ?? '-', - 'BomReferencedAssemblyIpn' => $bomEntry->getReferencedAssembly()?->getIpn() ?? '-', - 'BomReferencedAssemblyFullName' => $this->getFullName($bomEntry->getReferencedAssembly() ?? null), + + //BOM relevant attributes + 'Quantity' => $bomEntry->getQuantity() ?? '', + 'PartId' => $bomEntry->getPart()?->getId() ?? '-', + 'PartName' => $bomEntry->getPart()?->getName() ?? '-', + 'Ipn' => $bomEntry->getPart()?->getIpn() ?? '-', + 'Manufacturer' => $bomEntry->getPart()?->getManufacturer()?->getName() ?? '-', + 'Mpn' => $bomEntry->getPart()?->getManufacturerProductNumber() ?? '-', + 'Name' => $bomEntry->getName() ?? '-', + 'Designator' => $bomEntry->getDesignator(), + 'MountNames' => $bomEntry->getMountNames(), + 'Description' => $bomEntry->getPart()?->getDescription() ?? '-', + 'ReferencedAssemblyId' => $bomEntry->getReferencedAssembly()?->getId() ?? '-', + 'ReferencedAssemblyIpn' => $bomEntry->getReferencedAssembly()?->getIpn() ?? '-', + 'ReferencedAssemblyFullName' => $this->getFullName($bomEntry->getReferencedAssembly() ?? null), ]; // If a referenced assembly exists, add aggregated parts @@ -598,18 +618,21 @@ class EntityExporter 'AssemblyNameHierarchical' => '', 'AssemblyName' => $partAssembly ? $partAssembly->getName() : '', 'AssemblyFullName' => $this->getFullName($partAssembly), - 'BomQuantity' => $partData['quantity'], - 'BomMultiplier' => $partData['multiplier'], - 'BomPartId' => $partData['part']?->getId(), - 'BomPartIpn' => $partData['part']?->getIpn(), - 'BomPartMpnr' => $partData['part']?->getManufacturerProductNumber(), - 'BomPartName' => $partData['part']?->getName(), - 'BomDesignator' => $partData['part']?->getName(), - 'BomPartDescription' => $partData['part']?->getDescription(), - 'BomMountNames' => '-', - 'BomReferencedAssemblyId' => '-', - 'BomReferencedAssemblyIpn' => '-', - 'BomReferencedAssemblyFullName' => '-', + + //BOM relevant attributes + 'Quantity' => $partData['quantity'], + 'PartId' => $partData['part']?->getId(), + 'PartName' => $partData['part']?->getName(), + 'Ipn' => $partData['part']?->getIpn(), + 'Manufacturer' => $partData['part']?->getManufacturer()?->getName(), + 'Mpn' => $partData['part']?->getManufacturerProductNumber(), + 'Name' => $partData['name'] ?? '', + 'Designator' => $partData['designator']?->getName(), + 'Description' => $partData['part']?->getDescription(), + 'MountNames' => '-', + 'ReferencedAssemblyId' => '-', + 'ReferencedAssemblyIpn' => '-', + 'ReferencedAssemblyFullName' => '-', ]; } } diff --git a/src/Settings/BehaviorSettings/AssemblyBomTableColumns.php b/src/Settings/BehaviorSettings/AssemblyBomTableColumns.php index da8557c2..2833a3df 100644 --- a/src/Settings/BehaviorSettings/AssemblyBomTableColumns.php +++ b/src/Settings/BehaviorSettings/AssemblyBomTableColumns.php @@ -28,12 +28,20 @@ use Symfony\Contracts\Translation\TranslatorInterface; enum AssemblyBomTableColumns : string implements TranslatableInterface { - case NAME = "name"; case ID = "id"; case QUANTITY = "quantity"; case IPN = "ipn"; case DESCRIPTION = "description"; + case CATEGORY = "category"; + case MANUFACTURER = "manufacturer"; + case DESIGNATOR = "designator"; + case MOUNTNAMES = "mountnames"; + case STORAGE_LOCATION = "storage_location"; + case AMOUNT = "amount"; + case ADDED_DATE = "addedDate"; + case LAST_MODIFIED = "lastModified"; + case EDIT = "edit"; public function trans(TranslatorInterface $translator, ?string $locale = null): string { diff --git a/src/Settings/BehaviorSettings/TableSettings.php b/src/Settings/BehaviorSettings/TableSettings.php index 5c7455e2..cfe7e71b 100644 --- a/src/Settings/BehaviorSettings/TableSettings.php +++ b/src/Settings/BehaviorSettings/TableSettings.php @@ -97,8 +97,8 @@ class TableSettings #[Assert\Unique()] #[Assert\All([new Assert\Type(AssemblyBomTableColumns::class)])] - public array $assembliesBomDefaultColumns = [AssemblyBomTableColumns::QUANTITY, AssemblyTableColumns::ID, AssemblyTableColumns::IPN, - AssemblyTableColumns::NAME, AssemblyTableColumns::DESCRIPTION]; + public array $assembliesBomDefaultColumns = [AssemblyBomTableColumns::QUANTITY, AssemblyBomTableColumns::ID, + AssemblyBomTableColumns::IPN, AssemblyBomTableColumns::NAME, AssemblyBomTableColumns::DESCRIPTION]; #[SettingsParameter(label: new TM("settings.behavior.table.preview_image_min_width"), formOptions: ['attr' => ['min' => 1, 'max' => 100]], diff --git a/templates/form/collection_types_layout_assembly.html.twig b/templates/form/collection_types_layout_assembly.html.twig index 6dc6d49a..681b33b8 100644 --- a/templates/form/collection_types_layout_assembly.html.twig +++ b/templates/form/collection_types_layout_assembly.html.twig @@ -7,7 +7,7 @@ {# expand button #} {% trans %}assembly.bom.quantity{% endtrans %} {% trans %}assembly.bom.partOrAssembly{% endtrans %} - {% trans %}assembly.bom.name{% endtrans %} + {% trans %}assembly.bom.identifiers{% endtrans %} {# Remove button #} @@ -41,16 +41,19 @@ {{ form_widget(form.quantity) }} {{ form_errors(form.quantity) }} - + {{ form_row(form.part) }} {{ form_errors(form.part) }}
{{ form_widget(form.referencedAssembly) }} {{ form_errors(form.referencedAssembly) }} - - {{ form_widget(form.name) }} + + {{ form_row(form.name) }} {{ form_errors(form.name) }} +
+ {{ form_row(form.designator) }} + {{ form_errors(form.designator) }}