diff --git a/.env b/.env index f23afe8b..73b7b350 100644 --- a/.env +++ b/.env @@ -50,8 +50,6 @@ EMAIL_SENDER_EMAIL=noreply@partdb.changeme EMAIL_SENDER_NAME="Part-DB Mailer" # Set this to 1 to allow reset of a password per email ALLOW_EMAIL_PW_RESET=0 -# Set this to 0 to allow to enter already available IPN. In this case a unique increment is appended to the user input. -ENFORCE_UNIQUE_IPN=1 ################################################################################### # Error pages settings @@ -62,17 +60,6 @@ ERROR_PAGE_ADMIN_EMAIL='' # If this is set to true, solutions to common problems are shown on error pages. Disable this, if you do not want your users to see them... ERROR_PAGE_SHOW_HELP=1 -################################################################################## -# Part table settings -################################################################################## - -# Configure which columns will be visible by default in the specific table (and in which order). -# This is a comma separated list of column names. See documentation for available values. -TABLE_PARTS_DEFAULT_COLUMNS=name,description,category,footprint,manufacturer,storage_location,amount -TABLE_ASSEMBLIES_DEFAULT_COLUMNS=id,ipn,name,description,referencedAssemblies,edit -TABLE_ASSEMBLIES_BOM_DEFAULT_COLUMNS=quantity,id,ipn,name,description - - ################################################################################### # SAML Single sign on-settings ################################################################################### @@ -125,22 +112,6 @@ NO_URL_REWRITE_AVAILABLE=0 # Set to 1, if Part-DB should redirect all HTTP requests to HTTPS. You dont need to configure this, if your webserver already does this. REDIRECT_TO_HTTPS=0 -# If you want to use fixer.io for currency conversion, you have to set this to your API key -FIXER_API_KEY=CHANGEME - -# Override value if you want to show to show a given text on homepage. -# When this is empty the content of config/banner.md is used as banner -BANNER="" - -# Enable the part image overlay which shows name and filename of the picture -SHOW_PART_IMAGE_OVERLAY=1 - -# Define the number of digits used for the incremental numbering of parts in the IPN (Internal Part Number) autocomplete system. -AUTOCOMPLETE_PART_DIGITS=4 - -APP_ENV=prod -APP_SECRET=a03498528f5a5fc089273ec9ae5b2849 - # Set this to zero, if you want to disable the year 2038 bug check on 32-bit systems (it will cause errors with current 32-bit PHP versions) DISABLE_YEAR2038_BUG_CHECK=0 diff --git a/config/parameters.yaml b/config/parameters.yaml index d225a52e..3931729d 100644 --- a/config/parameters.yaml +++ b/config/parameters.yaml @@ -9,8 +9,6 @@ parameters: # This is used as workaround for places where we can not access the settings directly (like the 2FA application names) partdb.title: '%env(string:settings:customization:instanceName)%' # The title shown inside of Part-DB (e.g. in the navbar and on homepage) partdb.locale_menu: ['en', 'de', 'it', 'fr', 'ru', 'ja', 'cs', 'da', 'zh', 'pl'] # The languages that are shown in user drop down menu - partdb.enforce_change_comments_for: '%env(csv:ENFORCE_CHANGE_COMMENTS_FOR)%' # The actions for which a change comment is required (e.g. "part_edit", "part_create", etc.). If this is empty, change comments are not required at all. - partdb.autocomplete_part_digits: '%env(trim:string:AUTOCOMPLETE_PART_DIGITS)%' # The number of digits used for the incremental numbering of parts in the IPN (Internal Part Number) autocomplete system. partdb.default_uri: '%env(string:DEFAULT_URI)%' # The default URI to use for the Part-DB instance (e.g. https://part-db.example.com/). This is used for generating links in emails @@ -18,26 +16,11 @@ parameters: partdb.create_assembly_use_ipn_placeholder_in_name: '%env(bool:CREATE_ASSEMBLY_USE_IPN_PLACEHOLDER_IN_NAME)%' # Use an %%ipn%% placeholder in the name of an assembly. Placeholder is replaced with the ipn input while saving. - partdb.data_sources.synonyms: # Define your own synonyms for the given data sources - # Possible datasources: category, storagelocation, footprint, manufacturer, supplier, project, assembly - # Possible locales like the ones in 'partdb.locale_menu': en, de, it, fr, ru, ja, cs, da, zh, pl - #category: - #de: 'Bauteil Kategorien' - #en: 'Part categories' - #project: - #de: 'Geräte' - #en: 'Devices' - #assembly: - #de: 'Zusammengestellte Baugruppe' - #en: 'Combined assembly' - - ###################################################################################################################### # Users and Privacy ###################################################################################################################### partdb.gdpr_compliance: true # If this option is activated, IP addresses are anonymized to be GDPR compliant partdb.users.email_pw_reset: '%env(bool:ALLOW_EMAIL_PW_RESET)%' # Config if users are able, to reset their password by email. By default this enabled, when a mail server is configured. - partdb.users.enforce_unique_ipn: '%env(bool:ENFORCE_UNIQUE_IPN)%' # Config if users are able, to enter an already available IPN. In this case a unique increment is appended to the user input. ###################################################################################################################### # Mail settings @@ -62,12 +45,6 @@ parameters: ###################################################################################################################### partdb.saml.enabled: '%env(bool:SAML_ENABLED)%' # If this is set to true, SAML authentication is enabled - ###################################################################################################################### - # Table settings - ###################################################################################################################### - partdb.table.assemblies.default_columns: '%env(trim:string:TABLE_ASSEMBLIES_DEFAULT_COLUMNS)%' # The default columns in assembly tables and their order - partdb.table.assemblies_bom.default_columns: '%env(trim:string:TABLE_ASSEMBLIES_BOM_DEFAULT_COLUMNS)%' # The default columns in assembly bom tables and their order - ###################################################################################################################### # Miscellaneous ###################################################################################################################### diff --git a/docs/configuration.md b/docs/configuration.md index ea02b227..d04f1a3a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -148,7 +148,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/Version20250304081039.php b/migrations/Version20250304081039.php index 2c04dfb0..0c64e250 100644 --- a/migrations/Version20250304081039.php +++ b/migrations/Version20250304081039.php @@ -145,7 +145,6 @@ final class Version20250304081039 extends AbstractMultiPlatformMigration id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_assembly INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, - id_project INTEGER DEFAULT NULL, id_referenced_assembly INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, @@ -157,7 +156,6 @@ final class Version20250304081039 extends AbstractMultiPlatformMigration datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_8C74887E4AD2039E FOREIGN KEY (id_assembly) REFERENCES assemblies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_8C74887EC22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, - CONSTRAINT FK_8C74887EF12E799E FOREIGN KEY (id_project) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_8C74887E22522999 FOREIGN KEY (id_referenced_assembly) REFERENCES assemblies (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_8C74887E3FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE ) @@ -168,9 +166,6 @@ final class Version20250304081039 extends AbstractMultiPlatformMigration $this->addSql(<<<'SQL' CREATE INDEX IDX_8C74887EC22F6CC4 ON assembly_bom_entries (id_part) SQL); - $this->addSql(<<<'SQL' - CREATE INDEX IDX_8C74887EF12E799E ON assembly_bom_entries (id_project) - SQL); $this->addSql(<<<'SQL' CREATE INDEX IDX_8C74887E22522999 ON assembly_bom_entries (id_referenced_assembly) SQL); @@ -220,7 +215,6 @@ final class Version20250304081039 extends AbstractMultiPlatformMigration id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, id_assembly INT DEFAULT NULL, id_part INT DEFAULT NULL, - id_project INT DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames TEXT NOT NULL, name VARCHAR(255) DEFAULT NULL, @@ -238,9 +232,6 @@ final class Version20250304081039 extends AbstractMultiPlatformMigration $this->addSql(<<<'SQL' CREATE INDEX IDX_8C74887EC22F6CC4 ON assembly_bom_entries (id_part) SQL); - $this->addSql(<<<'SQL' - CREATE INDEX IDX_8C74887EF12E799E ON assembly_bom_entries (id_project) - SQL); $this->addSql(<<<'SQL' CREATE INDEX IDX_8C74887E3FFDCD60 ON assembly_bom_entries (price_currency_id) SQL); @@ -256,9 +247,6 @@ final class Version20250304081039 extends AbstractMultiPlatformMigration $this->addSql(<<<'SQL' ALTER TABLE assembly_bom_entries ADD CONSTRAINT FK_8C74887EC22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) NOT DEFERRABLE INITIALLY IMMEDIATE SQL); - $this->addSql(<<<'SQL' - ALTER TABLE assembly_bom_entries ADD CONSTRAINT FK_8C74887EF12E799E FOREIGN KEY (id_project) REFERENCES "projects" (id) NOT DEFERRABLE INITIALLY IMMEDIATE - SQL); $this->addSql(<<<'SQL' ALTER TABLE assembly_bom_entries ADD CONSTRAINT FK_8C74887E3FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE SQL); 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/Controller/PartController.php b/src/Controller/PartController.php index 29abfad8..69e9557e 100644 --- a/src/Controller/PartController.php +++ b/src/Controller/PartController.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace App\Controller; use App\DataTables\LogDataTable; +use App\Entity\AssemblySystem\Assembly; use App\Entity\Attachments\AttachmentUpload; use App\Entity\Parts\Category; use App\Entity\Parts\Footprint; diff --git a/src/Controller/ProjectController.php b/src/Controller/ProjectController.php index 151bf18f..e510506f 100644 --- a/src/Controller/ProjectController.php +++ b/src/Controller/ProjectController.php @@ -194,14 +194,14 @@ class ProjectController extends AbstractController // For PCB imports, proceed directly $importerResult = $BOMImporter->importFileIntoProject($form->get('file')->getData(), $project, [ - 'type' => $form->get('type')->getData(), + 'type' => $import_type, ]); // Validate the project entries $errors = $validator->validateProperty($project, 'bom_entries'); - //If no validation errors occured, save the changes and redirect to edit page - if (count ($errors) === 0 && $importerResult->getViolations()->count() === 0) { + //If no validation errors occurred, save the changes and redirect to edit page + if (count($errors) === 0 && $importerResult->getViolations()->count() === 0) { $entries = $importerResult->getBomEntries(); $this->addFlash('success', t('project.bom_import.flash.success', ['%count%' => count($entries)])); diff --git a/src/DataTables/AssemblyBomEntriesDataTable.php b/src/DataTables/AssemblyBomEntriesDataTable.php index 93e1566d..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, @@ -195,7 +201,7 @@ class AssemblyBomEntriesDataTable implements DataTableTypeInterface $this->csh->applyVisibilityAndConfigureColumns($dataTable, $this->tableSettings->assembliesBomDefaultColumns, "TABLE_ASSEMBLIES_BOM_DEFAULT_COLUMNS"); - $dataTable->addOrderBy('name', DataTable::SORT_ASCENDING); + $dataTable->addOrderBy('name'); $dataTable->createAdapter(ORMAdapter::class, [ 'entity' => Attachment::class, diff --git a/src/DataTables/PartsDataTable.php b/src/DataTables/PartsDataTable.php index 716a892d..fd798620 100644 --- a/src/DataTables/PartsDataTable.php +++ b/src/DataTables/PartsDataTable.php @@ -282,34 +282,6 @@ final class PartsDataTable implements DataTableTypeInterface ]); } - //Add a assembly column to list where the part is used, when the user has the permission to see the assemblies - if ($this->security->isGranted('read', Assembly::class)) { - $this->csh->add('assemblies', TextColumn::class, [ - 'label' => $this->translator->trans('assembly.labelp'), - 'render' => function ($value, Part $context): string { - //Only show the first 5 assembly names - $assemblies = $context->getAssemblies(); - $tmp = ""; - - $max = 5; - - for ($i = 0; $i < min($max, count($assemblies)); $i++) { - $url = $this->urlGenerator->infoURL($assemblies[$i]); - $tmp .= sprintf('%s', $url, htmlspecialchars($assemblies[$i]->getName())); - if ($i < count($assemblies) - 1) { - $tmp .= ", "; - } - } - - if (count($assemblies) > $max) { - $tmp .= ", + ".(count($assemblies) - $max); - } - - return $tmp; - } - ]); - } - $this->csh ->add('edit', IconLinkColumn::class, [ 'label' => $this->translator->trans('part.table.edit'), diff --git a/src/Entity/AssemblySystem/AssemblyBOMEntry.php b/src/Entity/AssemblySystem/AssemblyBOMEntry.php index 80b7d6bb..e4337446 100644 --- a/src/Entity/AssemblySystem/AssemblyBOMEntry.php +++ b/src/Entity/AssemblySystem/AssemblyBOMEntry.php @@ -36,8 +36,6 @@ use ApiPlatform\OpenApi\Model\Operation; use ApiPlatform\Serializer\Filter\PropertyFilter; use App\ApiPlatform\Filter\LikeFilter; use App\Entity\Contracts\TimeStampableInterface; -use App\Entity\ProjectSystem\Project; -use App\Entity\AssemblySystem\Assembly; use App\Repository\DBElementRepository; use App\Validator\Constraints\AssemblySystem\AssemblyCycle; use App\Validator\Constraints\AssemblySystem\AssemblyInvalidBomEntry; @@ -86,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 @@ -105,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) */ @@ -192,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/Entity/LogSystem/CollectionElementDeleted.php b/src/Entity/LogSystem/CollectionElementDeleted.php index 15e0001e..20ed580b 100644 --- a/src/Entity/LogSystem/CollectionElementDeleted.php +++ b/src/Entity/LogSystem/CollectionElementDeleted.php @@ -61,9 +61,9 @@ use App\Entity\Attachments\UserAttachment; use App\Entity\Base\AbstractDBElement; use App\Entity\Contracts\LogWithEventUndoInterface; use App\Entity\Contracts\NamedElementInterface; +use App\Entity\Parameters\AssemblyParameter; use App\Entity\Parameters\PartCustomStateParameter; use App\Entity\Parts\PartCustomState; -use App\Entity\Parameters\AssemblyParameter; use App\Entity\ProjectSystem\Project; use App\Entity\Parameters\AbstractParameter; use App\Entity\Parameters\AttachmentTypeParameter; diff --git a/src/Entity/LogSystem/LogTargetType.php b/src/Entity/LogSystem/LogTargetType.php index 72efe6c7..29e65c3d 100644 --- a/src/Entity/LogSystem/LogTargetType.php +++ b/src/Entity/LogSystem/LogTargetType.php @@ -22,6 +22,8 @@ declare(strict_types=1); */ namespace App\Entity\LogSystem; +use App\Entity\AssemblySystem\Assembly; +use App\Entity\AssemblySystem\AssemblyBOMEntry; use App\Entity\Attachments\Attachment; use App\Entity\Attachments\AttachmentType; use App\Entity\InfoProviderSystem\BulkInfoProviderImportJob; @@ -78,6 +80,9 @@ enum LogTargetType: int case ASSEMBLY_BOM_ENTRY = 24; case PART_CUSTOM_STATE = 25; + case ASSEMBLY = 23; + case ASSEMBLY_BOM_ENTRY = 24; + /** * Returns the class name of the target type or null if the target type is NONE. * @return string|null diff --git a/src/Entity/Parts/Part.php b/src/Entity/Parts/Part.php index b0824fc8..e1836f9f 100644 --- a/src/Entity/Parts/Part.php +++ b/src/Entity/Parts/Part.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace App\Entity\Parts; +use App\Entity\Parts\PartTraits\AssemblyTrait; use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface; use ApiPlatform\Doctrine\Orm\Filter\BooleanFilter; use ApiPlatform\Doctrine\Orm\Filter\DateFilter; 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/AssemblySystem/AssemblyBuildHelper.php b/src/Services/AssemblySystem/AssemblyBuildHelper.php index ce52ef0a..9180f3e8 100644 --- a/src/Services/AssemblySystem/AssemblyBuildHelper.php +++ b/src/Services/AssemblySystem/AssemblyBuildHelper.php @@ -103,7 +103,6 @@ class AssemblyBuildHelper } /** - * Returns the assembly BOM entries for which parts are missing in the stock for the given number of builds * Returns the referenced assembly BOM entries for which parts are missing in the stock for the given number of builds * @param Assembly $assembly The assembly for which the BOM entries should be checked * @param int $number_of_builds How often should the assembly be build? diff --git a/src/Services/ElementTypeNameGenerator.php b/src/Services/ElementTypeNameGenerator.php index 9ef2a063..a378f5f0 100644 --- a/src/Services/ElementTypeNameGenerator.php +++ b/src/Services/ElementTypeNameGenerator.php @@ -22,6 +22,8 @@ declare(strict_types=1); namespace App\Services; +use App\Entity\AssemblySystem\Assembly; +use App\Entity\AssemblySystem\AssemblyBOMEntry; use App\Entity\Attachments\Attachment; use App\Entity\Attachments\AttachmentContainingDBElement; use App\Entity\Attachments\AttachmentType; diff --git a/src/Services/ImportExportSystem/BOMImporter.php b/src/Services/ImportExportSystem/BOMImporter.php index 729f10c4..5ed8c388 100644 --- a/src/Services/ImportExportSystem/BOMImporter.php +++ b/src/Services/ImportExportSystem/BOMImporter.php @@ -49,7 +49,6 @@ use Symfony\Component\Validator\ConstraintViolation; */ class BOMImporter { - private const IMPORT_TYPE_JSON = 'json'; private const IMPORT_TYPE_CSV = 'csv'; private const IMPORT_TYPE_KICAD_PCB = 'kicad_pcbnew'; @@ -64,29 +63,29 @@ class BOMImporter 5 => 'Supplier and ref', ]; + private readonly PartRepository $partRepository; + + private readonly ManufacturerRepository $manufacturerRepository; + + private readonly CategoryRepository $categoryRepository; + + private readonly DBElementRepository $projectBomEntryRepository; + + private readonly DBElementRepository $assemblyBomEntryRepository; + private string $jsonRoot = ''; - private PartRepository $partRepository; - - private ManufacturerRepository $manufacturerRepository; - - private CategoryRepository $categoryRepository; - - private DBElementRepository $projectBomEntryRepository; - - private DBElementRepository $assemblyBomEntryRepository; - public function __construct( private readonly EntityManagerInterface $entityManager, - private readonly TranslatorInterface $translator, private readonly LoggerInterface $logger, - private readonly BOMValidationService $validationService + private readonly BOMValidationService $validationService, + private readonly TranslatorInterface $translator ) { - $this->partRepository = $entityManager->getRepository(Part::class); - $this->manufacturerRepository = $entityManager->getRepository(Manufacturer::class); - $this->categoryRepository = $entityManager->getRepository(Category::class); - $this->projectBomEntryRepository = $entityManager->getRepository(ProjectBOMEntry::class); - $this->assemblyBomEntryRepository = $entityManager->getRepository(AssemblyBOMEntry::class); + $this->partRepository = $this->entityManager->getRepository(Part::class); + $this->manufacturerRepository = $this->entityManager->getRepository(Manufacturer::class); + $this->categoryRepository = $this->entityManager->getRepository(Category::class); + $this->projectBomEntryRepository = $this->entityManager->getRepository(ProjectBOMEntry::class); + $this->assemblyBomEntryRepository = $this->entityManager->getRepository(AssemblyBOMEntry::class); } protected function configureOptions(OptionsResolver $resolver): OptionsResolver @@ -167,22 +166,6 @@ class BOMImporter return $this->stringToBOMEntries($importObject, $file->getContent(), $options); } - /** - * Validate BOM data before importing - * @return array Validation result with errors, warnings, and info - */ - public function validateBOMData(string $data, array $options): array - { - $resolver = new OptionsResolver(); - $resolver = $this->configureOptions($resolver); - $options = $resolver->resolve($options); - - return match ($options['type']) { - 'kicad_pcbnew' => $this->validateKiCADPCB($data), - 'kicad_schematic' => $this->validateKiCADSchematicData($data, $options), - default => throw new InvalidArgumentException('Invalid import type!'), - }; - } /** * Handles the conversion of an uploaded file into an ImporterResult for a given project or assembly. @@ -238,6 +221,23 @@ class BOMImporter return $this->stringToImporterResult($importObject, $file->getContent(), $options); } + /** + * Validate BOM data before importing + * @return array Validation result with errors, warnings, and info + */ + public function validateBOMData(string $data, array $options): array + { + $resolver = new OptionsResolver(); + $resolver = $this->configureOptions($resolver); + $options = $resolver->resolve($options); + + return match ($options['type']) { + 'kicad_pcbnew' => $this->validateKiCADPCB($data), + 'kicad_schematic' => $this->validateKiCADSchematicData($data, $options), + default => throw new InvalidArgumentException('Invalid import type!'), + }; + } + /** * Import string data into an array of BOM entries, which are not yet assigned to a project. * @@ -255,7 +255,7 @@ class BOMImporter return match ($options['type']) { self::IMPORT_TYPE_KICAD_PCB => $this->parseKiCADPCB($data, $importObject)->getBomEntries(), - self::IMPORT_TYPE_KICAD_SCHEMATIC => $this->parseKiCADPCB($data, $importObject)->getBomEntries(), + self::IMPORT_TYPE_KICAD_SCHEMATIC => $this->parseKiCADSchematic($data, $options), default => throw new InvalidArgumentException($this->translator->trans('validator.bom_importer.invalid_import_type', [], 'validators')), }; } @@ -296,9 +296,8 @@ class BOMImporter * validates the required fields, and creates BOM entries for each record in the data. * The BOM entries are added to the provided Project or Assembly, depending on the context. * + * @param string $data The semicolon- or comma-delimited CSV data to be parsed. * @param Project|Assembly $importObject The object determining the context of the BOM entry (either a Project or Assembly). - * @param string $data The semicolon- or comma-delimited CSV data to be parsed - * * @return ImporterResult The result of the import process, containing the created BOM entries. * * @throws UnexpectedValueException If required fields are missing in the provided data. @@ -858,7 +857,7 @@ class BOMImporter if (isset($entry['name'])) { $givenName = trim($entry['name']) === '' ? null : trim ($entry['name']); - if ($givenName !== null && $part !== null && $part->getName() !== $givenName) { + if ($givenName !== null && $bomEntry->getPart() !== null && $bomEntry->getPart()->getName() !== $givenName) { //Apply different names for parts list entry $bomEntry->setName(trim($entry['name']) === '' ? null : trim ($entry['name'])); } @@ -867,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); @@ -957,6 +960,7 @@ class BOMImporter return $out; } + /** * Builds a JSON-based constraint violation. * @@ -964,25 +968,13 @@ class BOMImporter * The violation includes a message, property path, invalid value, and other contextual information. * Translations for the violation message can be applied through the translator service. * - * @param string $message The translation key for the validation message. - * @param string $propertyPath The property path where the violation occurred. - * @param mixed|null $invalidValue The value that caused the violation (optional). - * @param array $parameters Additional parameters for message placeholders (default is an empty array). + * @param string $message The translation key for the validation message. + * @param string $propertyPath The property path where the violation occurred. + * @param mixed|null $invalidValue The value that caused the violation (optional). + * @param array $parameters Additional parameters for message placeholders (default is an empty array). * * @return ConstraintViolation The created constraint violation object. */ - private function buildJsonViolation(string $message, string $propertyPath, mixed $invalidValue = null, array $parameters = []): ConstraintViolation - { - return new ConstraintViolation( - message: $this->translator->trans($message, $parameters, 'validators'), - messageTemplate: $message, - parameters: $parameters, - root: $this->jsonRoot, - propertyPath: $propertyPath, - invalidValue: $invalidValue - ); - } - /** * Parse KiCad schematic BOM with flexible field mapping */ @@ -1460,4 +1452,30 @@ class BOMImporter return array_values($headers); } + + /** + * Builds a JSON-based constraint violation. + * + * This method creates a `ConstraintViolation` object that represents a validation error. + * The violation includes a message, property path, invalid value, and other contextual information. + * Translations for the violation message can be applied through the translator service. + * + * @param string $message The translation key for the validation message. + * @param string $propertyPath The property path where the violation occurred. + * @param mixed|null $invalidValue The value that caused the violation (optional). + * @param array $parameters Additional parameters for message placeholders (default is an empty array). + * + * @return ConstraintViolation The created constraint violation object. + */ + private function buildJsonViolation(string $message, string $propertyPath, mixed $invalidValue = null, array $parameters = []): ConstraintViolation + { + return new ConstraintViolation( + message: $this->translator->trans($message, $parameters, 'validators'), + messageTemplate: $message, + parameters: $parameters, + root: $this->jsonRoot, + propertyPath: $propertyPath, + invalidValue: $invalidValue + ); + } } diff --git a/src/Services/ImportExportSystem/EntityExporter.php b/src/Services/ImportExportSystem/EntityExporter.php index 1dbdaaf4..7b48623e 100644 --- a/src/Services/ImportExportSystem/EntityExporter.php +++ b/src/Services/ImportExportSystem/EntityExporter.php @@ -36,6 +36,7 @@ use App\Entity\Parts\StorageLocation; use App\Entity\Parts\Supplier; use App\Entity\PriceInformations\Currency; use App\Entity\ProjectSystem\Project; +use App\Entity\ProjectSystem\ProjectBOMEntry; use App\Helpers\Assemblies\AssemblyPartAggregator; use App\Helpers\FilenameSanatizer; use App\Serializer\APIPlatform\SkippableItemNormalizer; @@ -379,49 +380,62 @@ 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(AssemblyBOMEntry $bomEntry) => [ + 'processBomEntries' => fn($entity, $depth) => array_map(fn(ProjectBOMEntry $bomEntry) => [ 'Id' => $entity->getId(), 'ParentId' => '', 'Type' => 'project_bom_entry', '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(), @@ -431,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), ], @@ -555,6 +572,7 @@ class EntityExporter { $rows = []; + /** @var AssemblyBOMEntry $bomEntry */ foreach ($assembly->getBomEntries() as $bomEntry) { // Add the BOM entry itself $rows[] = [ @@ -565,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 @@ -597,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'], + 'Description' => $partData['part']?->getDescription(), + 'MountNames' => '-', + 'ReferencedAssemblyId' => '-', + 'ReferencedAssemblyIpn' => '-', + 'ReferencedAssemblyFullName' => '-', ]; } } diff --git a/src/Services/Trees/ToolsTreeBuilder.php b/src/Services/Trees/ToolsTreeBuilder.php index e37a6e85..f454804f 100644 --- a/src/Services/Trees/ToolsTreeBuilder.php +++ b/src/Services/Trees/ToolsTreeBuilder.php @@ -51,15 +51,8 @@ use Symfony\Contracts\Translation\TranslatorInterface; */ class ToolsTreeBuilder { - public function __construct( - protected TranslatorInterface $translator, - protected UrlGeneratorInterface $urlGenerator, - protected TagAwareCacheInterface $cache, - protected UserCacheKeyGenerator $keyGenerator, - protected Security $security, - protected ?array $dataSourceSynonyms = [], - ) { - $this->dataSourceSynonyms = $dataSourceSynonyms ?? []; + public function __construct(protected TranslatorInterface $translator, protected UrlGeneratorInterface $urlGenerator, protected TagAwareCacheInterface $cache, protected UserCacheKeyGenerator $keyGenerator, protected Security $security) + { } /** @@ -174,43 +167,43 @@ class ToolsTreeBuilder } if ($this->security->isGranted('read', new Category())) { $nodes[] = (new TreeViewNode( - $this->getTranslatedDataSourceOrSynonym('category', 'tree.tools.edit.categories', $this->translator->getLocale()), + $this->translator->trans('tree.tools.edit.categories'), $this->urlGenerator->generate('category_new') ))->setIcon('fa-fw fa-treeview fa-solid fa-tags'); } if ($this->security->isGranted('read', new Project())) { $nodes[] = (new TreeViewNode( - $this->getTranslatedDataSourceOrSynonym('project', 'tree.tools.edit.projects', $this->translator->getLocale()), + $this->translator->trans('tree.tools.edit.projects'), $this->urlGenerator->generate('project_new') ))->setIcon('fa-fw fa-treeview fa-solid fa-archive'); } if ($this->security->isGranted('read', new Assembly())) { $nodes[] = (new TreeViewNode( - $this->getTranslatedDataSourceOrSynonym('assembly', 'tree.tools.edit.assemblies', $this->translator->getLocale()), + $this->translator->trans('tree.tools.edit.assemblies'), $this->urlGenerator->generate('assembly_new') ))->setIcon('fa-fw fa-treeview fa-solid fa-list'); } if ($this->security->isGranted('read', new Supplier())) { $nodes[] = (new TreeViewNode( - $this->getTranslatedDataSourceOrSynonym('supplier', 'tree.tools.edit.suppliers', $this->translator->getLocale()), + $this->translator->trans('tree.tools.edit.suppliers'), $this->urlGenerator->generate('supplier_new') ))->setIcon('fa-fw fa-treeview fa-solid fa-truck'); } if ($this->security->isGranted('read', new Manufacturer())) { $nodes[] = (new TreeViewNode( - $this->getTranslatedDataSourceOrSynonym('manufacturer', 'tree.tools.edit.manufacturer', $this->translator->getLocale()), + $this->translator->trans('tree.tools.edit.manufacturer'), $this->urlGenerator->generate('manufacturer_new') ))->setIcon('fa-fw fa-treeview fa-solid fa-industry'); } if ($this->security->isGranted('read', new StorageLocation())) { $nodes[] = (new TreeViewNode( - $this->getTranslatedDataSourceOrSynonym('storagelocation', 'tree.tools.edit.storelocation', $this->translator->getLocale()), + $this->translator->trans('tree.tools.edit.storelocation'), $this->urlGenerator->generate('store_location_new') ))->setIcon('fa-fw fa-treeview fa-solid fa-cube'); } if ($this->security->isGranted('read', new Footprint())) { $nodes[] = (new TreeViewNode( - $this->getTranslatedDataSourceOrSynonym('footprint', 'tree.tools.edit.footprint', $this->translator->getLocale()), + $this->translator->trans('tree.tools.edit.footprint'), $this->urlGenerator->generate('footprint_new') ))->setIcon('fa-fw fa-treeview fa-solid fa-microchip'); } @@ -324,22 +317,4 @@ class ToolsTreeBuilder return $nodes; } - - protected function getTranslatedDataSourceOrSynonym(string $dataSource, string $translationKey, string $locale): string - { - $currentTranslation = $this->translator->trans($translationKey); - - // Call alternatives from DataSourcesynonyms (if available) - if (!empty($this->dataSourceSynonyms[$dataSource][$locale])) { - $alternativeTranslation = $this->dataSourceSynonyms[$dataSource][$locale]; - - // Use alternative translation when it deviates from the standard translation - if ($alternativeTranslation !== $currentTranslation) { - return $alternativeTranslation; - } - } - - // Otherwise return the standard translation - return $currentTranslation; - } } diff --git a/src/Services/Trees/TreeViewGenerator.php b/src/Services/Trees/TreeViewGenerator.php index d5358bfa..3a097902 100644 --- a/src/Services/Trees/TreeViewGenerator.php +++ b/src/Services/Trees/TreeViewGenerator.php @@ -68,11 +68,9 @@ class TreeViewGenerator protected TranslatorInterface $translator, private readonly UrlGeneratorInterface $router, private readonly SidebarSettings $sidebarSettings, - protected ?array $dataSourceSynonyms = [], ) { $this->rootNodeEnabled = $this->sidebarSettings->rootNodeEnabled; $this->rootNodeExpandedByDefault = $this->sidebarSettings->rootNodeExpanded; - $this->dataSourceSynonyms = $dataSourceSynonyms ?? []; } /** @@ -228,16 +226,14 @@ class TreeViewGenerator protected function entityClassToRootNodeString(string $class): string { - $locale = $this->translator->getLocale(); - return match ($class) { - Category::class => $this->getTranslatedOrSynonym('category', $locale), - StorageLocation::class => $this->getTranslatedOrSynonym('storelocation', $locale), - Footprint::class => $this->getTranslatedOrSynonym('footprint', $locale), - Manufacturer::class => $this->getTranslatedOrSynonym('manufacturer', $locale), - Supplier::class => $this->getTranslatedOrSynonym('supplier', $locale), - Project::class => $this->getTranslatedOrSynonym('project', $locale), - Assembly::class => $this->getTranslatedOrSynonym('assembly', $locale), + Category::class => $this->translator->trans('category.labelp'), + StorageLocation::class => $this->translator->trans('storelocation.labelp'), + Footprint::class => $this->translator->trans('footprint.labelp'), + Manufacturer::class => $this->translator->trans('manufacturer.labelp'), + Supplier::class => $this->translator->trans('supplier.labelp'), + Project::class => $this->translator->trans('project.labelp'), + Assembly::class => $this->translator->trans('assembly.labelp'), default => $this->translator->trans('tree.root_node.text'), }; } @@ -294,22 +290,4 @@ class TreeViewGenerator return $repo->getGenericNodeTree($parent); //@phpstan-ignore-line }); } - - protected function getTranslatedOrSynonym(string $key, string $locale): string - { - $currentTranslation = $this->translator->trans($key . '.labelp'); - - // Call alternatives from DataSourcesynonyms (if available) - if (!empty($this->dataSourceSynonyms[$key][$locale])) { - $alternativeTranslation = $this->dataSourceSynonyms[$key][$locale]; - - // Use alternative translation when it deviates from the standard translation - if ($alternativeTranslation !== $currentTranslation) { - return $alternativeTranslation; - } - } - - // Otherwise return the standard translation - return $currentTranslation; - } } 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/admin/assembly_admin.html.twig b/templates/admin/assembly_admin.html.twig index 2e68a3da..b6bf6d09 100644 --- a/templates/admin/assembly_admin.html.twig +++ b/templates/admin/assembly_admin.html.twig @@ -3,9 +3,7 @@ {# @var entity App\Entity\AssemblySystem\Assembly #} {% block card_title %} - {% set dataSourceName = get_data_source_name('assembly', 'assembly.caption') %} - {% set translatedSource = 'assembly.caption'|trans %} - {% if dataSourceName != translatedSource %}{{ 'datasource.synonym'|trans({'%name%': translatedSource, '%synonym%': dataSourceName}) }}{% else %}{{ translatedSource }}{% endif %} + {% trans %}assembly.caption{% endtrans %} {% endblock %} {% block edit_title %} diff --git a/templates/assemblies/info/info.html.twig b/templates/assemblies/info/info.html.twig index 6b6a4c89..aeb039af 100644 --- a/templates/assemblies/info/info.html.twig +++ b/templates/assemblies/info/info.html.twig @@ -71,7 +71,7 @@ + >{{ source[2] | trans }} {% endif %} {% endfor %} {% endmacro %} diff --git a/templates/form/collection_types_layout_assembly.html.twig b/templates/form/collection_types_layout_assembly.html.twig index 98cb73b6..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) }}