diff --git a/config/parameters.yaml b/config/parameters.yaml index b0ad2175..345b8feb 100644 --- a/config/parameters.yaml +++ b/config/parameters.yaml @@ -48,7 +48,14 @@ parameters: ###################################################################################################################### # Table settings ###################################################################################################################### +<<<<<<< HEAD partdb.table.assemblies.default_columns: '%env(trim:string:TABLE_ASSEMBLIES_DEFAULT_COLUMNS)%' # The default columns in assembly tables and their order +======= + partdb.table.default_page_size: '%env(int:TABLE_DEFAULT_PAGE_SIZE)%' # The default number of entries shown per page in tables + partdb.table.parts.default_columns: '%env(trim:string:TABLE_PARTS_DEFAULT_COLUMNS)%' # The default columns in part tables and their order + 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 +>>>>>>> 2779c55a (Baugruppen Stückliste um referenzierte Baugruppe erweitern) ###################################################################################################################### # Miscellaneous diff --git a/config/services.yaml b/config/services.yaml index 4fdf18ad..ff8ce2ec 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -171,9 +171,12 @@ services: #################################################################################################################### # Table settings #################################################################################################################### - App\DataTables\AssemblyBomEntriesDataTable: + App\DataTables\AssemblyDataTable: arguments: $visible_columns: '%partdb.table.assemblies.default_columns%' + App\DataTables\AssemblyBomEntriesDataTable: + arguments: + $visible_columns: '%partdb.table.assemblies_bom.default_columns%' App\DataTables\Helpers\ColumnSortHelper: shared: false # Service has a state so not share it between different tables diff --git a/docs/configuration.md b/docs/configuration.md index 19899c06..498308b0 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -140,7 +140,10 @@ bundled with Part-DB. Set `DATABASE_MYSQL_SSL_VERIFY_CERT` if you want to accept * `TABLE_ASSEMBLIES_DEFAULT_COLUMNS`: The columns in assemblies 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: `name`, `id`, `quantity`, `ipn`, `description`, `category`, `footprint`, `manufacturer`, `mountnames`, `instockAmount`, `storageLocations`, `addedDate`, `lastModified`. + 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`. * `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 ae2d6261..ccdb24ac 100644 --- a/migrations/Version20250304081039.php +++ b/migrations/Version20250304081039.php @@ -146,6 +146,7 @@ final class Version20250304081039 extends AbstractMultiPlatformMigration 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, mountnames CLOB NOT NULL, @@ -156,8 +157,9 @@ 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_8C74887E3FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (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 ) SQL); $this->addSql(<<<'SQL' @@ -169,6 +171,9 @@ final class Version20250304081039 extends AbstractMultiPlatformMigration $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); $this->addSql(<<<'SQL' CREATE INDEX IDX_8C74887E3FFDCD60 ON assembly_bom_entries (price_currency_id) SQL); diff --git a/migrations/Version20250627130848.php b/migrations/Version20250627130848.php new file mode 100644 index 00000000..16a6f318 --- /dev/null +++ b/migrations/Version20250627130848.php @@ -0,0 +1,78 @@ +addSql(<<<'SQL' + ALTER TABLE assembly_bom_entries ADD id_referenced_assembly INT DEFAULT NULL AFTER id_project + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE assembly_bom_entries ADD CONSTRAINT FK_8C74887E22522999 FOREIGN KEY (id_referenced_assembly) REFERENCES assemblies (id) ON DELETE SET NULL + SQL); + $this->addSql(<<<'SQL' + CREATE INDEX IDX_8C74887E22522999 ON assembly_bom_entries (id_referenced_assembly) + SQL); + } + + public function mySQLDown(Schema $schema): void + { + $this->addSql(<<<'SQL' + ALTER TABLE assembly_bom_entries DROP FOREIGN KEY FK_8C74887E22522999 + SQL); + $this->addSql(<<<'SQL' + DROP INDEX IDX_8C74887E22522999 ON assembly_bom_entries + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE assembly_bom_entries DROP id_referenced_assembly + SQL); + } + + public function sqLiteUp(Schema $schema): void + { + //nothing to do. Done via Version20250304081039 + } + + public function sqLiteDown(Schema $schema): void + { + //nothing to do. Done via Version20250304081039 + } + + public function postgreSQLUp(Schema $schema): void + { + $this->addSql(<<<'SQL' + ALTER TABLE assembly_bom_entries ADD id_referenced_assembly INT DEFAULT NULL + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE assembly_bom_entries ADD CONSTRAINT FK_8C74887E22522999 FOREIGN KEY (id_referenced_assembly) REFERENCES assemblies (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE + SQL); + $this->addSql(<<<'SQL' + CREATE INDEX IDX_8C74887E22522999 ON assembly_bom_entries (id_referenced_assembly) + SQL); + } + + public function postgreSQLDown(Schema $schema): void + { + $this->addSql(<<<'SQL' + ALTER TABLE assembly_bom_entries DROP CONSTRAINT FK_8C74887E22522999 + SQL); + $this->addSql(<<<'SQL' + DROP INDEX IDX_8C74887E22522999 + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE assembly_bom_entries DROP id_referenced_assembly + SQL); + } +} diff --git a/src/Controller/AdminPages/BaseAdminController.php b/src/Controller/AdminPages/BaseAdminController.php index c6d9cf20..039751ea 100644 --- a/src/Controller/AdminPages/BaseAdminController.php +++ b/src/Controller/AdminPages/BaseAdminController.php @@ -200,7 +200,7 @@ abstract class BaseAdminController extends AbstractController * depending on CREATE_ASSEMBLY_USE_IPN_PLACEHOLDER_IN_NAME, when creating a new one, * to avoid having to insert it manually */ - $entity->setName(str_ireplace('%%ipn%%', $entity->getIpn(), $entity->getName())); + $entity->setName(str_ireplace('%%ipn%%', $entity->getIpn() ?? '', $entity->getName())); } $this->commentHelper->setMessage($form['log_comment']->getData()); @@ -233,6 +233,13 @@ abstract class BaseAdminController extends AbstractController $repo = $this->entityManager->getRepository($this->entity_class); + $showParameters = true; + if ($this instanceof AssemblyAdminController) { + //currently not needed for assemblies + + $showParameters = false; + } + return $this->render($this->twig_template, [ 'entity' => $entity, 'form' => $form, @@ -242,7 +249,7 @@ abstract class BaseAdminController extends AbstractController 'timeTravel' => $timeTravel_timestamp, 'repo' => $repo, 'partsContainingElement' => $repo instanceof PartsContainingRepositoryInterface, - 'showParameters' => !($this instanceof AssemblyAdminController), + 'showParameters' => $showParameters, ]); } @@ -303,7 +310,7 @@ abstract class BaseAdminController extends AbstractController * depending on CREATE_ASSEMBLY_USE_IPN_PLACEHOLDER_IN_NAME, when creating a new one, * to avoid having to insert it manually */ - $new_entity->setName(str_ireplace('%%ipn%%', $new_entity->getIpn(), $new_entity->getName())); + $new_entity->setName(str_ireplace('%%ipn%%', $new_entity->getIpn() ?? '', $new_entity->getName())); } $this->commentHelper->setMessage($form['log_comment']->getData()); @@ -396,13 +403,20 @@ abstract class BaseAdminController extends AbstractController } } + $showParameters = true; + if ($this instanceof AssemblyAdminController) { + //currently not needed for assemblies + + $showParameters = false; + } + return $this->render($this->twig_template, [ 'entity' => $new_entity, 'form' => $form, 'import_form' => $import_form, 'mass_creation_form' => $mass_creation_form, 'route_base' => $this->route_base, - 'showParameters' => !($this instanceof AssemblyAdminController), + 'showParameters' => $showParameters, ]); } diff --git a/src/Controller/TypeaheadController.php b/src/Controller/TypeaheadController.php index 4335492e..ca6ed863 100644 --- a/src/Controller/TypeaheadController.php +++ b/src/Controller/TypeaheadController.php @@ -22,8 +22,10 @@ declare(strict_types=1); namespace App\Controller; +use App\Entity\AssemblySystem\Assembly; use App\Entity\Parameters\AbstractParameter; use App\Entity\ProjectSystem\Project; +use App\Services\Attachments\AssemblyPreviewGenerator; use App\Services\Attachments\ProjectPreviewGenerator; use Symfony\Component\HttpFoundation\Response; use App\Entity\Attachments\Attachment; @@ -195,6 +197,44 @@ class TypeaheadController extends AbstractController return new JsonResponse($result); } + #[Route(path: '/assemblies/search/{query}', name: 'typeahead_assemblies')] + public function assemblies( + EntityManagerInterface $entityManager, + AssemblyPreviewGenerator $assemblyPreviewGenerator, + AttachmentURLGenerator $attachmentURLGenerator, + string $query = "" + ): JsonResponse { + $this->denyAccessUnlessGranted('@assemblies.read'); + + $result = []; + + $assemblyRepository = $entityManager->getRepository(Assembly::class); + + $assemblies = $assemblyRepository->autocompleteSearch($query, 100); + + foreach ($assemblies as $assembly) { + $preview_attachment = $assemblyPreviewGenerator->getTablePreviewAttachment($assembly); + + if($preview_attachment instanceof Attachment) { + $preview_url = $attachmentURLGenerator->getThumbnailURL($preview_attachment, 'thumbnail_sm'); + } else { + $preview_url = ''; + } + + /** @var Assembly $assembly */ + $result[] = [ + 'id' => $assembly->getID(), + 'name' => $assembly->getName(), + 'category' => '', + 'footprint' => '', + 'description' => mb_strimwidth($assembly->getDescription(), 0, 127, '...'), + 'image' => $preview_url, + ]; + } + + return new JsonResponse($result); + } + #[Route(path: '/parameters/{type}/search/{query}', name: 'typeahead_parameters', requirements: ['type' => '.+'])] public function parameters(string $type, EntityManagerInterface $entityManager, string $query = ""): JsonResponse { diff --git a/src/Entity/AssemblySystem/Assembly.php b/src/Entity/AssemblySystem/Assembly.php index 7897ea36..5991b9e1 100644 --- a/src/Entity/AssemblySystem/Assembly.php +++ b/src/Entity/AssemblySystem/Assembly.php @@ -22,6 +22,7 @@ declare(strict_types=1); namespace App\Entity\AssemblySystem; +use App\Repository\AssemblyRepository; use Doctrine\Common\Collections\Criteria; use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; use ApiPlatform\Metadata\ApiFilter; @@ -38,6 +39,7 @@ use ApiPlatform\Serializer\Filter\PropertyFilter; use App\ApiPlatform\Filter\LikeFilter; use App\Entity\Attachments\Attachment; use App\Validator\Constraints\UniqueObjectCollection; +use App\Validator\Constraints\AssemblySystem\UniqueReferencedAssembly; use Doctrine\DBAL\Types\Types; use App\Entity\Attachments\AssemblyAttachment; use App\Entity\Base\AbstractStructuralDBElement; @@ -58,7 +60,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface; * * @extends AbstractStructuralDBElement */ -#[ORM\Entity] +#[ORM\Entity(repositoryClass: AssemblyRepository::class)] #[ORM\Table(name: 'assemblies')] #[UniqueEntity(fields: ['ipn'], message: 'assembly.ipn.must_be_unique')] #[ORM\Index(columns: ['ipn'], name: 'assembly_idx_ipn')] @@ -109,8 +111,9 @@ class Assembly extends AbstractStructuralDBElement */ #[Assert\Valid] #[Groups(['extended', 'full', 'import'])] - #[ORM\OneToMany(mappedBy: 'assembly', targetEntity: AssemblyBOMEntry::class, cascade: ['persist', 'remove'], orphanRemoval: true)] + #[ORM\OneToMany(targetEntity: AssemblyBOMEntry::class, mappedBy: 'assembly', cascade: ['persist', 'remove'], orphanRemoval: true)] #[UniqueObjectCollection(message: 'assembly.bom_entry.part_already_in_bom', fields: ['part'])] + #[UniqueReferencedAssembly] #[UniqueObjectCollection(message: 'assembly.bom_entry.project_already_in_bom', fields: ['project'])] #[UniqueObjectCollection(message: 'assembly.bom_entry.name_already_in_bom', fields: ['name'])] protected Collection $bom_entries; @@ -386,4 +389,22 @@ class Assembly extends AbstractStructuralDBElement } } } + + /** + * Get all referenced assemblies which uses this assembly. + * + * @return Assembly[] all referenced assemblies which uses this assembly as a one-dimensional array of assembly objects + */ + public function getReferencedAssemblies(): array + { + $assemblies = []; + + foreach($this->bom_entries as $entry) { + if ($entry->getAssembly() !== null) { + $assemblies[] = $entry->getReferencedAssembly(); + } + } + + return $assemblies; + } } diff --git a/src/Entity/AssemblySystem/AssemblyBOMEntry.php b/src/Entity/AssemblySystem/AssemblyBOMEntry.php index f6ede219..820fc2f5 100644 --- a/src/Entity/AssemblySystem/AssemblyBOMEntry.php +++ b/src/Entity/AssemblySystem/AssemblyBOMEntry.php @@ -133,6 +133,18 @@ class AssemblyBOMEntry extends AbstractDBElement implements UniqueValidatableInt #[Groups(['bom_entry:read', 'bom_entry:write', 'full'])] protected ?Part $part = null; + /** + * @var Assembly|null The associated assembly + */ + #[Assert\Expression( + '(this.getPart() === null or this.getReferencedAssembly() === null) and (this.getName() === null or (this.getName() != null and this.getName() != ""))', + message: 'validator.assembly.bom_entry.only_part_or_assembly_allowed' + )] + #[ORM\ManyToOne(targetEntity: Assembly::class)] + #[ORM\JoinColumn(name: 'id_referenced_assembly', nullable: true, onDelete: 'SET NULL')] + #[Groups(['bom_entry:read', 'bom_entry:write', ])] + protected ?Assembly $referencedAssembly = null; + /** * @var Project|null The associated project */ @@ -237,6 +249,17 @@ class AssemblyBOMEntry extends AbstractDBElement implements UniqueValidatableInt return $this; } + public function getReferencedAssembly(): ?Assembly + { + return $this->referencedAssembly; + } + + public function setReferencedAssembly(?Assembly $referencedAssembly): AssemblyBOMEntry + { + $this->referencedAssembly = $referencedAssembly; + return $this; + } + public function getProject(): ?Project { return $this->project; diff --git a/src/Form/AssemblySystem/AssemblyAddPartsType.php b/src/Form/AssemblySystem/AssemblyAddPartsType.php index 4d84881f..1fa67126 100644 --- a/src/Form/AssemblySystem/AssemblyAddPartsType.php +++ b/src/Form/AssemblySystem/AssemblyAddPartsType.php @@ -51,14 +51,17 @@ class AssemblyAddPartsType extends AbstractType $builder->add('bom_entries', AssemblyBOMEntryCollectionType::class, [ 'entry_options' => [ 'constraints' => [ - new UniqueEntity(fields: ['part', 'assembly'], message: 'assembly.bom_entry.part_already_in_bom', + new UniqueEntity(fields: ['part'], message: 'assembly.bom_entry.part_already_in_bom', entityClass: AssemblyBOMEntry::class), - new UniqueEntity(fields: ['name', 'assembly'], message: 'assembly.bom_entry.name_already_in_bom', + new UniqueEntity(fields: ['referencedAssembly'], message: 'assembly.bom_entry.assembly_already_in_bom', + entityClass: AssemblyBOMEntry::class), + new UniqueEntity(fields: ['name'], message: 'assembly.bom_entry.name_already_in_bom', entityClass: AssemblyBOMEntry::class, ignoreNull: true), ] ], 'constraints' => [ new UniqueObjectCollection(message: 'assembly.bom_entry.part_already_in_bom', fields: ['part']), + new UniqueObjectCollection(message: 'assembly.bom_entry.assembly_already_in_bom', fields: ['referencedAssembly']), new UniqueObjectCollection(message: 'assembly.bom_entry.name_already_in_bom', fields: ['name']), ] ]); diff --git a/src/Form/AssemblySystem/AssemblyBOMEntryType.php b/src/Form/AssemblySystem/AssemblyBOMEntryType.php index 42d463bd..b86b2fd1 100644 --- a/src/Form/AssemblySystem/AssemblyBOMEntryType.php +++ b/src/Form/AssemblySystem/AssemblyBOMEntryType.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace App\Form\AssemblySystem; use App\Entity\AssemblySystem\AssemblyBOMEntry; +use App\Form\Type\AssemblySelectType; use App\Form\Type\BigDecimalNumberType; use App\Form\Type\CurrencyEntityType; use App\Form\Type\PartSelectType; @@ -42,6 +43,10 @@ class AssemblyBOMEntryType extends AbstractType 'label' => 'assembly.bom.project', 'required' => false, ]) + ->add('referencedAssembly', AssemblySelectType::class, [ + 'label' => 'assembly.bom.referencedAssembly', + 'required' => false, + ]) ->add('name', TextType::class, [ 'label' => 'assembly.bom.name', 'required' => false, diff --git a/src/Form/Type/AssemblySelectType.php b/src/Form/Type/AssemblySelectType.php new file mode 100644 index 00000000..10e858f2 --- /dev/null +++ b/src/Form/Type/AssemblySelectType.php @@ -0,0 +1,124 @@ +addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) { + $form = $event->getForm(); + $config = $form->getConfig()->getOptions(); + $data = $event->getData() ?? []; + + $config['compound'] = false; + $config['choices'] = is_iterable($data) ? $data : [$data]; + $config['error_bubbling'] = true; + + $form->add('autocomplete', EntityType::class, $config); + }); + + //After form submit, we have to add the selected element as choice, otherwise the form will not accept this element + $builder->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) { + $data = $event->getData(); + $form = $event->getForm(); + $options = $form->get('autocomplete')->getConfig()->getOptions(); + + + if (!isset($data['autocomplete']) || '' === $data['autocomplete'] || empty($data['autocomplete'])) { + $options['choices'] = []; + } else { + //Extract the ID from the submitted data + $id = $data['autocomplete']; + //Find the element in the database + $element = $this->em->find($options['class'], $id); + + //Add the element as choice + $options['choices'] = [$element]; + $options['error_bubbling'] = true; + $form->add('autocomplete', EntityType::class, $options); + } + }); + + $builder->setDataMapper($this); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'class' => Assembly::class, + 'choice_label' => 'name', + 'compound' => true, + 'error_bubbling' => false, + ]); + + error_log($this->urlGenerator->generate('typeahead_assemblies', ['query' => '__QUERY__'])); + + $resolver->setDefaults([ + 'attr' => [ + 'data-controller' => 'elements--assembly-select', + 'data-autocomplete' => $this->urlGenerator->generate('typeahead_assemblies', ['query' => '__QUERY__']), + 'autocomplete' => 'off', + ], + ]); + + $resolver->setDefaults([ + //Prefill the selected choice with the needed data, so the user can see it without an additional Ajax request + 'choice_attr' => ChoiceList::attr($this, function (?Assembly $assembly) { + if($assembly instanceof Assembly) { + //Determine the picture to show: + $preview_attachment = $this->previewGenerator->getTablePreviewAttachment($assembly); + if ($preview_attachment instanceof Attachment) { + $preview_url = $this->attachmentURLGenerator->getThumbnailURL($preview_attachment, + 'thumbnail_sm'); + } else { + $preview_url = ''; + } + } + + return $assembly instanceof Assembly ? [ + 'data-description' => $assembly->getDescription() ? mb_strimwidth($assembly->getDescription(), 0, 127, '...') : '', + 'data-category' => '', + 'data-footprint' => '', + 'data-image' => $preview_url, + ] : []; + }) + ]); + } + + public function mapDataToForms($data, \Traversable $forms): void + { + $form = current(iterator_to_array($forms, false)); + $form->setData($data); + } + + public function mapFormsToData(\Traversable $forms, &$data): void + { + $form = current(iterator_to_array($forms, false)); + $data = $form->getData(); + } + +} diff --git a/src/Repository/AssemblyRepository.php b/src/Repository/AssemblyRepository.php new file mode 100644 index 00000000..031e6e82 --- /dev/null +++ b/src/Repository/AssemblyRepository.php @@ -0,0 +1,69 @@ +. + */ + +declare(strict_types=1); + +/** + * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony). + * + * Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace App\Repository; + +use App\Entity\AssemblySystem\Assembly; + +/** + * @template TEntityClass of Assembly + * @extends DBElementRepository + */ +class AssemblyRepository extends StructuralDBElementRepository +{ + /** + * @return Assembly[] + */ + public function autocompleteSearch(string $query, int $max_limits = 50): array + { + $qb = $this->createQueryBuilder('assembly'); + $qb->select('assembly') + ->where('ILIKE(assembly.name, :query) = TRUE') + ->orWhere('ILIKE(assembly.description, :query) = TRUE'); + + $qb->setParameter('query', '%'.$query.'%'); + + $qb->setMaxResults($max_limits); + $qb->orderBy('NATSORT(assembly.name)', 'ASC'); + + return $qb->getQuery()->getResult(); + } +} \ No newline at end of file diff --git a/src/Services/AssemblySystem/AssemblyBuildHelper.php b/src/Services/AssemblySystem/AssemblyBuildHelper.php index 3fb3221a..b7f2df3c 100644 --- a/src/Services/AssemblySystem/AssemblyBuildHelper.php +++ b/src/Services/AssemblySystem/AssemblyBuildHelper.php @@ -67,6 +67,7 @@ class AssemblyBuildHelper public function getMaximumBuildableCount(Assembly $assembly): int { $maximum_buildable_count = PHP_INT_MAX; + /** @var AssemblyBOMEntry $bom_entry */ foreach ($assembly->getBomEntries() as $bom_entry) { //Skip BOM entries without a part (as we can not determine that) if (!$bom_entry->isPartBomEntry() && $bom_entry->getProject() === null) { @@ -76,8 +77,8 @@ class AssemblyBuildHelper //The maximum buildable count for the whole project is the minimum of all BOM entries if ($bom_entry->getPart() !== null) { $maximum_buildable_count = min($maximum_buildable_count, $this->getMaximumBuildableCountForBOMEntry($bom_entry)); - } elseif ($bom_entry->getProject() !== null) { - $maximum_buildable_count = min($maximum_buildable_count, $this->projectBuildHelper->getMaximumBuildableCount($bom_entry->getProject())); + } elseif ($bom_entry->getReferencedAssembly() !== null) { + $maximum_buildable_count = min($maximum_buildable_count, $this->projectBuildHelper->getMaximumBuildableCount($bom_entry->getReferencedAssembly())); } } @@ -117,11 +118,12 @@ class AssemblyBuildHelper $nonBuildableEntries = []; + /** @var AssemblyBOMEntry $bomEntry */ foreach ($assembly->getBomEntries() as $bomEntry) { $part = $bomEntry->getPart(); //Skip BOM entries without a part (as we can not determine that) - if (!$part instanceof Part && $bomEntry->getAssembly() === null) { + if (!$part instanceof Part && $bomEntry->getReferencedAssembly() === null) { continue; } @@ -131,8 +133,8 @@ class AssemblyBuildHelper if ($amount_sum < $bomEntry->getQuantity() * $number_of_builds) { $nonBuildableEntries[] = $bomEntry; } - } elseif ($bomEntry->getAssembly() !== null) { - $nonBuildableAssemblyEntries = $this->projectBuildHelper->getNonBuildableProjectBomEntries($bomEntry->getProject(), $number_of_builds); + } elseif ($bomEntry->getReferencedAssembly() !== null) { + $nonBuildableAssemblyEntries = $this->getNonBuildableAssemblyBomEntries($bomEntry->getReferencedAssembly(), $number_of_builds); $nonBuildableEntries = array_merge($nonBuildableEntries, $nonBuildableAssemblyEntries); } } diff --git a/src/Services/Attachments/AssemblyPreviewGenerator.php b/src/Services/Attachments/AssemblyPreviewGenerator.php new file mode 100644 index 00000000..9ecbbd07 --- /dev/null +++ b/src/Services/Attachments/AssemblyPreviewGenerator.php @@ -0,0 +1,93 @@ +. + */ + +declare(strict_types=1); + +namespace App\Services\Attachments; + +use App\Entity\AssemblySystem\Assembly; +use App\Entity\Attachments\Attachment; + +class AssemblyPreviewGenerator +{ + public function __construct(protected AttachmentManager $attachmentHelper) + { + } + + /** + * Returns a list of attachments that can be used for previewing the assembly ordered by priority. + * + * @param Assembly $assembly the assembly for which the attachments should be determined + * + * @return (Attachment|null)[] + * + * @psalm-return list + */ + public function getPreviewAttachments(Assembly $assembly): array + { + $list = []; + + //Master attachment has top priority + $attachment = $assembly->getMasterPictureAttachment(); + if ($this->isAttachmentValidPicture($attachment)) { + $list[] = $attachment; + } + + //Then comes the other images of the assembly + foreach ($assembly->getAttachments() as $attachment) { + //Dont show the master attachment twice + if ($this->isAttachmentValidPicture($attachment) && $attachment !== $assembly->getMasterPictureAttachment()) { + $list[] = $attachment; + } + } + + return $list; + } + + /** + * Determines what attachment should be used for previewing a assembly (especially in assembly table). + * The returned attachment is guaranteed to be existing and be a picture. + * + * @param Assembly $assembly The assembly for which the attachment should be determined + */ + public function getTablePreviewAttachment(Assembly $assembly): ?Attachment + { + $attachment = $assembly->getMasterPictureAttachment(); + if ($this->isAttachmentValidPicture($attachment)) { + return $attachment; + } + + return null; + } + + /** + * Checks if a attachment is exising and a valid picture. + * + * @param Attachment|null $attachment the attachment that should be checked + * + * @return bool true if the attachment is valid + */ + protected function isAttachmentValidPicture(?Attachment $attachment): bool + { + return $attachment instanceof Attachment + && $attachment->isPicture() + && $this->attachmentHelper->isFileExisting($attachment); + } +} diff --git a/src/Services/Trees/TreeViewGenerator.php b/src/Services/Trees/TreeViewGenerator.php index fa9935c8..3a097902 100644 --- a/src/Services/Trees/TreeViewGenerator.php +++ b/src/Services/Trees/TreeViewGenerator.php @@ -188,6 +188,15 @@ class TreeViewGenerator $root_node->setExpanded($this->rootNodeExpandedByDefault); $root_node->setIcon($this->entityClassToRootNodeIcon($class)); + $generic = [$root_node]; + } elseif ($mode === 'assemblies' && $this->rootNodeEnabled) { + //We show the root node as a link to the list of all assemblies + $show_all_parts_url = $this->router->generate('assemblies_list'); + + $root_node = new TreeViewNode($this->entityClassToRootNodeString($class), $show_all_parts_url, $generic); + $root_node->setExpanded($this->rootNodeExpandedByDefault); + $root_node->setIcon($this->entityClassToRootNodeIcon($class)); + $generic = [$root_node]; } diff --git a/src/Twig/AssemblyTwigExtension.php b/src/Twig/AssemblyTwigExtension.php index d43c201e..a8ca7719 100644 --- a/src/Twig/AssemblyTwigExtension.php +++ b/src/Twig/AssemblyTwigExtension.php @@ -2,6 +2,7 @@ namespace App\Twig; +use App\Entity\AssemblySystem\AssemblyBOMEntry; use Twig\Extension\AbstractExtension; use Twig\TwigFunction; @@ -14,6 +15,9 @@ class AssemblyTwigExtension extends AbstractExtension ]; } + /** + * @param AssemblyBOMEntry[] $bomEntries + */ public function hasProject(array $bomEntries): bool { foreach ($bomEntries as $entry) { diff --git a/src/Validator/Constraints/AssemblySystem/UniqueReferencedAssembly.php b/src/Validator/Constraints/AssemblySystem/UniqueReferencedAssembly.php new file mode 100644 index 00000000..55a31440 --- /dev/null +++ b/src/Validator/Constraints/AssemblySystem/UniqueReferencedAssembly.php @@ -0,0 +1,34 @@ +. + */ +namespace App\Validator\Constraints\AssemblySystem; + +use Symfony\Component\Validator\Constraint; + +/** + * This constraint checks that the given UniqueReferencedAssembly is valid. + */ +#[\Attribute(\Attribute::TARGET_PROPERTY)] +class UniqueReferencedAssembly extends Constraint +{ + public string $message = 'assembly.bom_entry.assembly_already_in_bom'; +} \ No newline at end of file diff --git a/src/Validator/Constraints/AssemblySystem/UniqueReferencedAssemblyValidator.php b/src/Validator/Constraints/AssemblySystem/UniqueReferencedAssemblyValidator.php new file mode 100644 index 00000000..0e58c066 --- /dev/null +++ b/src/Validator/Constraints/AssemblySystem/UniqueReferencedAssemblyValidator.php @@ -0,0 +1,48 @@ +. + */ +namespace App\Validator\Constraints\AssemblySystem; + +use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\ConstraintValidator; + +class UniqueReferencedAssemblyValidator extends ConstraintValidator +{ + public function validate($value, Constraint $constraint) + { + $assemblies = []; + foreach ($value as $entry) { + $referencedAssemblyId = $entry->getReferencedAssembly()?->getId(); + if ($referencedAssemblyId === null) { + continue; + } + + if (isset($assemblies[$referencedAssemblyId])) { + $this->context->buildViolation($constraint->message) + ->atPath('referencedAssembly') + ->addViolation(); + return; + } + $assemblies[$referencedAssemblyId] = true; + } + } +} \ No newline at end of file diff --git a/templates/assemblies/build/_form.html.twig b/templates/assemblies/build/_form.html.twig index 0123ab01..97cace56 100644 --- a/templates/assemblies/build/_form.html.twig +++ b/templates/assemblies/build/_form.html.twig @@ -28,6 +28,8 @@ {% if bom_entry.part %} {{ bom_entry.part.name }} {% if bom_entry.name %}({{ bom_entry.name }}){% endif %} + {% elseif bom_entry.referencedAssembly %} + {{ 'assembly.build.form.referencedAssembly'|trans({'%name%': bom_entry.referencedAssembly.name}) }} {% if bom_entry.name %}({{ bom_entry.name }}){% endif %} {% else %} {{ bom_entry.name }} {% endif %} diff --git a/templates/assemblies/info/_attachments_info.html.twig b/templates/assemblies/info/_attachments_info.html.twig new file mode 100644 index 00000000..747426c3 --- /dev/null +++ b/templates/assemblies/info/_attachments_info.html.twig @@ -0,0 +1,91 @@ +{% import "helper.twig" as helper %} + + + + + + + + + + + + + + + + + {% for attachment in assembly.attachments %} + + + + + + + + + + {% endfor %} + + + +
{% trans %}attachment.name{% endtrans %}{% trans %}attachment.attachment_type{% endtrans %}{% trans %}attachment.file_name{% endtrans %}{% trans %}attachment.file_size{% endtrans %}
+ {% import "components/attachments.macro.html.twig" as attachments %} + {{ attachments.attachment_icon(attachment, attachment_manager) }} + {{ attachment.name }}{{ attachment.attachmentType.fullPath }} + {% if attachment.hasInternal() %} + {{ attachment.filename }} + {% endif %} + + {% if not attachment.hasInternal() %} + + {% trans %}attachment.external_only{% endtrans %} + + {% elseif attachment_manager.internalFileExisting(attachment) %} + + {{ attachment_manager.humanFileSize(attachment) }} + + {% else %} + + {% trans %}attachment.file_not_found{% endtrans %} + + {% endif %} + {% if attachment.secure %} +
+ {% trans %}attachment.secure{% endtrans %} + + {% endif %} + {% if attachment == assembly.masterPictureAttachment %} +
+ + {% trans %}attachment.preview{% endtrans %} + + {% endif %} +
+ + + + + + + + + + +
+ + +
+
\ No newline at end of file diff --git a/templates/assemblies/info/info.html.twig b/templates/assemblies/info/info.html.twig index 166535a6..d787ea08 100644 --- a/templates/assemblies/info/info.html.twig +++ b/templates/assemblies/info/info.html.twig @@ -108,7 +108,7 @@ {% include "assemblies/info/_builds.html.twig" %}
- {% include "parts/info/_attachments_info.html.twig" with {"part": assembly} %} + {% include "assemblies/info/_attachments_info.html.twig" with {"assembly": assembly} %}
diff --git a/templates/form/collection_types_layout_assembly.html.twig b/templates/form/collection_types_layout_assembly.html.twig index 24964801..6dc6d49a 100644 --- a/templates/form/collection_types_layout_assembly.html.twig +++ b/templates/form/collection_types_layout_assembly.html.twig @@ -6,7 +6,7 @@ {# expand button #} {% trans %}assembly.bom.quantity{% endtrans %} - {% trans %}assembly.bom.partOrProject{% endtrans %} + {% trans %}assembly.bom.partOrAssembly{% endtrans %} {% trans %}assembly.bom.name{% endtrans %} {# Remove button #} @@ -44,18 +44,9 @@ {{ form_row(form.part) }} {{ form_errors(form.part) }} - - {% if form.vars.value is not null and form.vars.value.assembly is not null %} - {% if is_granted("@projects.read") or has_project(form.vars.value.assembly.bomEntries.toArray) %} -
- {{ form_widget(form.project) }} - {{ form_errors(form.project) }} - {% endif %} - {% elseif is_granted("@projects.read") %} -
- {{ form_widget(form.project) }} - {{ form_errors(form.project) }} - {% endif %} +
+ {{ form_widget(form.referencedAssembly) }} + {{ form_errors(form.referencedAssembly) }} {{ form_widget(form.name) }} diff --git a/translations/messages.cs.xlf b/translations/messages.cs.xlf index b8c4f603..4191cf30 100644 --- a/translations/messages.cs.xlf +++ b/translations/messages.cs.xlf @@ -9867,7 +9867,7 @@ Element 3 assembly.edit.status - Stav + Stav sestavy @@ -13852,6 +13852,12 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz %value% (Součást) + + + part.table.name.value.for_assembly + %value% (Sestava) + + part.table.name.value.for_project @@ -13888,6 +13894,12 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz Sestavy + + + assembly.referencedAssembly.labelp + Odkazované sestavy + + assembly.edit @@ -14056,6 +14068,12 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz Sestavit + + + assembly.build.form.referencedAssembly + Sestava "%name%" + + assembly.builds.no_stocked_builds @@ -14110,6 +14128,12 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz Projekt + + + assembly.bom.referencedAssembly + Sestava + + assembly.bom.name @@ -14146,10 +14170,10 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz Importovat součásti do sestavy - + - assembly.bom.partOrProject - Součást + assembly.bom.partOrAssembly + Součást nebo sestava diff --git a/translations/messages.da.xlf b/translations/messages.da.xlf index 4cec9405..c68acdd4 100644 --- a/translations/messages.da.xlf +++ b/translations/messages.da.xlf @@ -9893,7 +9893,7 @@ Element 3 assembly.edit.status - Status + Samlingens status @@ -12569,6 +12569,12 @@ Bemærk venligst, at du ikke kan kopiere fra deaktiveret bruger. Hvis du prøver %value% (Del) + + + part.table.name.value.for_assembly + %value% (Samling) + + part.table.name.value.for_project @@ -12605,6 +12611,12 @@ Bemærk venligst, at du ikke kan kopiere fra deaktiveret bruger. Hvis du prøver Samlinger + + + assembly.referencedAssembly.labelp + Refererede samlinger + + assembly.edit @@ -12773,6 +12785,12 @@ Bemærk venligst, at du ikke kan kopiere fra deaktiveret bruger. Hvis du prøver Byg + + + assembly.build.form.referencedAssembly + Samling "%name%" + + assembly.builds.no_stocked_builds @@ -12827,6 +12845,12 @@ Bemærk venligst, at du ikke kan kopiere fra deaktiveret bruger. Hvis du prøver Projekt + + + assembly.bom.referencedAssembly + Sammenstilling + + assembly.bom.name @@ -12863,10 +12887,10 @@ Bemærk venligst, at du ikke kan kopiere fra deaktiveret bruger. Hvis du prøver Importer dele til samling - + - assembly.bom.partOrProject - Del + assembly.bom.partOrAssembly + Del eller samling diff --git a/translations/messages.de.xlf b/translations/messages.de.xlf index 62e55839..ff775b49 100644 --- a/translations/messages.de.xlf +++ b/translations/messages.de.xlf @@ -4746,6 +4746,12 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr %value% (Bauteil) + + + part.table.name.value.for_assembly + %value% (Baugruppe) + + part.table.name.value.for_project @@ -9953,7 +9959,7 @@ Element 1 -> Element 1.2 assembly.edit.status - Status + Status Baugruppe @@ -13338,6 +13344,12 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön Baugruppen + + + assembly.referencedAssembly.labelp + Referenzierte Baugruppen + + assembly.edit @@ -13506,6 +13518,12 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön Bauen + + + asssembly.build.form.referencedAssembly + Baugruppe "%name%" + + assembly.builds.no_stocked_builds @@ -13560,6 +13578,12 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön Projekt + + + assembly.bom.referencedAssembly + Baugruppe + + assembly.bom.name @@ -13596,10 +13620,10 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön Importiere Parts für Baugruppe - + - assembly.bom.partOrProject - Bauteil oder Projekt + assembly.bom.partOrAssembly + Bauteil oder Baugruppe diff --git a/translations/messages.el.xlf b/translations/messages.el.xlf index fc03f08b..97b27f53 100644 --- a/translations/messages.el.xlf +++ b/translations/messages.el.xlf @@ -1541,6 +1541,12 @@ %value% (Μέρος) + + + part.table.name.value.for_assembly + %value% (Συναρμολόγηση) + + part.table.name.value.for_project @@ -1550,7 +1556,7 @@ assembly.edit.status - Κατάσταση + Κατάσταση συναρμολόγησης @@ -1613,6 +1619,12 @@ Συναρμολογήσεις + + + assembly.referencedAssembly.labelp + Αναφερόμενες συναρμολογήσεις + + assembly.edit @@ -1781,6 +1793,12 @@ Κατασκευή + + + assembly.build.form.referencedAssembly + Συναρμολόγηση "%name%" + + assembly.builds.no_stocked_builds @@ -1835,6 +1853,12 @@ έργο + + + assembly.bom.referencedAssembly + Συναρμολόγηση + + assembly.bom.name @@ -1871,10 +1895,10 @@ Εισαγωγή εξαρτημάτων συναρμολόγησης - + - assembly.bom.partOrProject - Εξάρτημα + assembly.bom.partOrAssembly + Μέρος ή συναρμολόγηση diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index ca0020fe..0138aabb 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -4747,6 +4747,12 @@ If you have done this incorrectly or if a computer is no longer trusted, you can %value% (Part) + + + part.table.name.value.for_assembly + %value% (Assembly) + + part.table.name.value.for_project @@ -9954,7 +9960,7 @@ Element 1 -> Element 1.2 assembly.edit.status - Project status + Assembly status @@ -13339,6 +13345,12 @@ Please note, that you can not impersonate a disabled user. If you try you will g Assemblies + + + assembly.referencedAssembly.labelp + Referenced assemblies + + assembly.edit @@ -13507,6 +13519,12 @@ Please note, that you can not impersonate a disabled user. If you try you will g Build + + + assembly.build.form.referencedAssembly + Assembly "%name%" + + assembly.builds.no_stocked_builds @@ -13561,6 +13579,12 @@ Please note, that you can not impersonate a disabled user. If you try you will g Project + + + assembly.bom.referencedAssembly + Assembly + + assembly.bom.name @@ -13597,10 +13621,10 @@ Please note, that you can not impersonate a disabled user. If you try you will g Import part list for assembly - + - assembly.bom.partOrProject - Part + assembly.bom.partOrAssembly + Part or assembly diff --git a/translations/messages.es.xlf b/translations/messages.es.xlf index edef6d96..d4ece66e 100644 --- a/translations/messages.es.xlf +++ b/translations/messages.es.xlf @@ -4746,6 +4746,12 @@ Subelementos serán desplazados hacia arriba. %value% (Componente) + + + part.table.name.value.for_assembly + %value% (Ensamblaje) + + part.table.name.value.for_project @@ -9897,7 +9903,7 @@ Elemento 3 assembly.edit.status - Estatus + Estado del ensamblaje @@ -12777,6 +12783,12 @@ Por favor ten en cuenta que no puedes personificar a un usuario deshabilitado. S Ensamblajes + + + assembly.referencedAssembly.labelp + Conjuntos referenciados + + assembly.edit @@ -12945,6 +12957,12 @@ Por favor ten en cuenta que no puedes personificar a un usuario deshabilitado. S Construir + + + assembly.build.form.referencedAssembly + Ensamblaje "%name%" + + assembly.builds.no_stocked_builds @@ -12999,6 +13017,12 @@ Por favor ten en cuenta que no puedes personificar a un usuario deshabilitado. S Proyecto + + + assembly.bom.referencedAssembly + Ensamblaje + + assembly.bom.name @@ -13035,10 +13059,10 @@ Por favor ten en cuenta que no puedes personificar a un usuario deshabilitado. S Importar piezas para ensamblaje - + - assembly.bom.partOrProject - Pieza + assembly.bom.partOrAssembly + Parte o conjunto diff --git a/translations/messages.fr.xlf b/translations/messages.fr.xlf index f5c7dc97..a75db3a3 100644 --- a/translations/messages.fr.xlf +++ b/translations/messages.fr.xlf @@ -4709,6 +4709,12 @@ Si vous avez fait cela de manière incorrecte ou si un ordinateur n'est plus fia %value% (Componente) + + + part.table.name.value.for_assembly + %value% (Assemblage) + + part.table.name.value.for_project @@ -9112,7 +9118,7 @@ exemple de ville assembly.edit.status - Statut + Statut de l'assemblage @@ -9175,6 +9181,12 @@ exemple de ville Assemblages + + + assembly.referencedAssembly.labelp + Assemblages référencés + + assembly.edit @@ -9343,6 +9355,12 @@ exemple de ville Construire + + + assembly.build.form.referencedAssembly + Assemblage "%name%" + + assembly.builds.no_stocked_builds @@ -9397,6 +9415,12 @@ exemple de ville Projet + + + assembly.bom.referencedAssembly + Assemblage + + assembly.bom.name @@ -9433,10 +9457,10 @@ exemple de ville Importer des pièces pour l'assemblage - + - assembly.bom.partOrProject - Pièce + assembly.bom.partOrAssembly + Pièce ou assemblage diff --git a/translations/messages.it.xlf b/translations/messages.it.xlf index e7628984..12e20ca2 100644 --- a/translations/messages.it.xlf +++ b/translations/messages.it.xlf @@ -4748,6 +4748,12 @@ Se è stato fatto in modo errato o se un computer non è più attendibile, puoi %value% (Componente) + + + part.table.name.value.for_assembly + %value% (Assemblaggio) + + part.table.name.value.for_project @@ -9899,7 +9905,7 @@ Element 3 assembly.edit.status - Stato + Stato dell'assemblaggio @@ -12755,6 +12761,12 @@ Notare che non è possibile impersonare un utente disattivato. Quando si prova a Assemblaggi + + + assembly.referencedAssembly.labelp + Assembly referenziati + + assembly.edit @@ -12923,6 +12935,12 @@ Notare che non è possibile impersonare un utente disattivato. Quando si prova a Costruire + + + assembly.build.form.referencedAssembly + Gruppo "%name%" + + assembly.builds.no_stocked_builds @@ -12977,6 +12995,12 @@ Notare che non è possibile impersonare un utente disattivato. Quando si prova a Progetto + + + assembly.bom.referencedAssembly + Assemblaggio + + assembly.bom.name @@ -13013,10 +13037,10 @@ Notare che non è possibile impersonare un utente disattivato. Quando si prova a Importa componenti per il gruppo - + - assembly.bom.partOrProject - Componente + assembly.bom.partOrAssembly + Parte o assieme diff --git a/translations/messages.ja.xlf b/translations/messages.ja.xlf index b61b585f..b7be7490 100644 --- a/translations/messages.ja.xlf +++ b/translations/messages.ja.xlf @@ -4709,6 +4709,12 @@ %value%(部品) + + + part.table.name.value.for_assembly + %value%(アセンブリ) + + part.table.name.value.for_project @@ -8849,7 +8855,7 @@ Exampletown assembly.edit.status - ステータス + アセンブリのステータス @@ -8912,6 +8918,12 @@ Exampletown アセンブリ一覧 + + + assembly.referencedAssembly.labelp + 参照されたアセンブリ + + assembly.edit @@ -9080,6 +9092,12 @@ Exampletown ビルド + + + assembly.build.form.referencedAssembly + アセンブリ「%name%」 + + assembly.builds.no_stocked_builds @@ -9134,6 +9152,12 @@ Exampletown プロジェクト + + + assembly.bom.referencedAssembly + アセンブリ + + assembly.bom.name diff --git a/translations/messages.nl.xlf b/translations/messages.nl.xlf index c52a4733..fe17c3ac 100644 --- a/translations/messages.nl.xlf +++ b/translations/messages.nl.xlf @@ -730,6 +730,12 @@ %value% (Onderdeel) + + + part.table.name.value.for_assembly + %value% (Assemblage) + + part.table.name.value.for_project @@ -739,7 +745,7 @@ assembly.edit.status - Κατάσταση + Montagestatus @@ -802,6 +808,12 @@ Assemblages + + + assembly.referencedAssembly.labelp + Gerefereerde assemblages + + assembly.edit @@ -970,6 +982,12 @@ Bouwen + + + assembly.build.form.referencedAssembly + Assemblage "%name%" + + assembly.builds.no_stocked_builds @@ -1024,6 +1042,12 @@ Project + + + assembly.bom.referencedAssembly + Assemblage + + assembly.bom.name @@ -1060,10 +1084,10 @@ Importeer onderdelen voor assemblage - + - assembly.bom.partOrProject - Onderdeel + assembly.bom.partOrAssembly + Onderdeel of samenstelling diff --git a/translations/messages.pl.xlf b/translations/messages.pl.xlf index fc6eba18..73b6e4fc 100644 --- a/translations/messages.pl.xlf +++ b/translations/messages.pl.xlf @@ -4751,6 +4751,12 @@ Jeśli zrobiłeś to niepoprawnie lub komputer nie jest już godny zaufania, mo %value%(部品) + + + part.table.name.value.for_assembly + %value% (Złożenie) + + part.table.name.value.for_project @@ -9902,7 +9908,7 @@ Element 3 assembly.edit.status - Status + Status montażu @@ -12632,6 +12638,12 @@ Należy pamiętać, że nie możesz udawać nieaktywnych użytkowników. Jeśli Zespoły + + + assembly.referencedAssembly.labelp + Odwołane zestawy + + assembly.edit @@ -12800,6 +12812,12 @@ Należy pamiętać, że nie możesz udawać nieaktywnych użytkowników. Jeśli Zbuduj + + + assembly.build.form.referencedAssembly + Zespół "%name%" + + assembly.builds.no_stocked_builds @@ -12854,6 +12872,12 @@ Należy pamiętać, że nie możesz udawać nieaktywnych użytkowników. Jeśli Projekt + + + assembly.bom.referencedAssembly + Złożenie + + assembly.bom.name @@ -12890,10 +12914,10 @@ Należy pamiętać, że nie możesz udawać nieaktywnych użytkowników. Jeśli Importuj części dla zespołu - + - assembly.bom.partOrProject - Część + assembly.bom.partOrAssembly + Część lub zespół diff --git a/translations/messages.ru.xlf b/translations/messages.ru.xlf index 252b857b..751ff35f 100644 --- a/translations/messages.ru.xlf +++ b/translations/messages.ru.xlf @@ -4757,6 +4757,12 @@ %value% (Часть) + + + part.table.name.value.for_assembly + %value% (Сборка) + + part.table.name.value.for_project @@ -9906,7 +9912,7 @@ assembly.edit.status - Статус + Статус сборки @@ -12732,6 +12738,12 @@ Сборки + + + assembly.referencedAssembly.labelp + Ссылочные сборки + + assembly.edit @@ -12900,6 +12912,12 @@ Собрать + + + assembly.build.form.referencedAssembly + Сборка "%name%" + + assembly.builds.no_stocked_builds @@ -12954,6 +12972,12 @@ Проект + + + assembly.bom.referencedAssembly + Сборка + + assembly.bom.name @@ -12990,10 +13014,10 @@ Импортировать детали для сборки - + - assembly.bom.partOrProject - Компонент + assembly.bom.partOrAssembly + Часть или сборка diff --git a/translations/messages.zh.xlf b/translations/messages.zh.xlf index 9c0b5a76..396feae3 100644 --- a/translations/messages.zh.xlf +++ b/translations/messages.zh.xlf @@ -4755,6 +4755,12 @@ %value%(部件) + + + part.table.name.value.for_assembly + %value%(装配) + + part.table.name.value.for_project @@ -9905,7 +9911,7 @@ Element 3 assembly.edit.status - 状态 + 装配状态 @@ -12617,6 +12623,12 @@ Element 3 装配列表 + + + assembly.referencedAssembly.labelp + 引用的程序集 + + assembly.edit @@ -12785,6 +12797,12 @@ Element 3 构建 + + + assembly.build.form.referencedAssembly + 组件“%name%” + + assembly.builds.no_stocked_builds @@ -12839,6 +12857,12 @@ Element 3 项目 + + + assembly.bom.referencedAssembly + 组件 + + assembly.bom.name @@ -12875,10 +12899,10 @@ Element 3 导入组件的零件 - + - assembly.bom.partOrProject - 零件 + assembly.bom.partOrAssembly + 部件或组件 diff --git a/translations/validators.cs.xlf b/translations/validators.cs.xlf index 607b8a3b..1731c90c 100644 --- a/translations/validators.cs.xlf +++ b/translations/validators.cs.xlf @@ -251,12 +251,6 @@ Musíte vybrat díl pro položku BOM dílu nebo nastavit název pro položku BOM bez dílu. - - - validator.project.bom_entry.only_part_or_assembly_allowed - Je povoleno vybrat pouze jednu součástku nebo sestavu. Upravit prosím svůj výběr! - - project.bom_entry.name_already_in_bom @@ -395,6 +389,12 @@ Tato součást již existuje ve skupině! + + + assembly.bom_entry.assembly_already_in_bom + Tato sestava již existuje jako položka v seznamu materiálů! + + assembly.bom_entry.project_already_in_bom @@ -413,6 +413,12 @@ Musíte vybrat součást nebo nastavit název pro nesoučást! + + + validator.assembly.bom_entry.only_part_or_assembly_allowed + Je povoleno vybrat pouze jednu součástku nebo sestavu. Upravit prosím svůj výběr! + + validator.bom_importer.json_csv.quantity.required diff --git a/translations/validators.da.xlf b/translations/validators.da.xlf index f2571271..239e3572 100644 --- a/translations/validators.da.xlf +++ b/translations/validators.da.xlf @@ -251,12 +251,6 @@ Du skal vælge en komponent eller angive et navn til en ikke-komponent styklistepost! - - - validator.project.bom_entry.only_part_or_assembly_allowed - Det er kun tilladt at vælge én del eller en samling. Venligst tilpas dit valg! - - project.bom_entry.name_already_in_bom @@ -371,6 +365,12 @@ Denne del eksisterer allerede i gruppen! + + + assembly.bom_entry.assembly_already_in_bom + Denne samling findes allerede som en post! + + assembly.bom_entry.project_already_in_bom @@ -389,6 +389,12 @@ Du skal vælge en del eller sætte et navn for en ikke-del! + + + validator.assembly.bom_entry.only_part_or_assembly_allowed + Det er kun tilladt at vælge én del eller en samling. Venligst tilpas dit valg! + + validator.bom_importer.json_csv.quantity.required diff --git a/translations/validators.de.xlf b/translations/validators.de.xlf index ee91e337..3c3abb2b 100644 --- a/translations/validators.de.xlf +++ b/translations/validators.de.xlf @@ -251,12 +251,6 @@ Sie müssen ein Bauteil bzw. eine Baugruppe auswählen, oder einen Namen für ein nicht-Bauteil BOM-Eintrag setzen! - - - validator.project.bom_entry.only_part_or_assembly_allowed - Es darf nur ein Bauteil oder eine Baugruppe ausgewählt werden. Bitte passen Sie Ihre Auswahl an! - - project.bom_entry.name_already_in_bom @@ -395,6 +389,12 @@ Dieses Bauteil existiert bereits in der Gruppe! + + + assembly.bom_entry.assembly_already_in_bom + Diese Baugruppe existiert bereits als Eintrag! + + assembly.bom_entry.project_already_in_bom @@ -413,6 +413,12 @@ Sie müssen ein Bauteil auswählen, oder einen Namen für den Eintrag setzen! + + + validator.assembly.bom_entry.only_part_or_assembly_allowed + Es darf nur ein Bauteil oder eine Baugruppe ausgewählt werden. Bitte passen Sie Ihre Auswahl an! + + validator.bom_importer.json_csv.quantity.required diff --git a/translations/validators.el.xlf b/translations/validators.el.xlf index 580c9127..4e4278da 100644 --- a/translations/validators.el.xlf +++ b/translations/validators.el.xlf @@ -13,12 +13,6 @@ Ο εσωτερικός αριθμός εξαρτήματος πρέπει να είναι μοναδικός. {{ value }} χρησιμοποιείται ήδη! - - - validator.project.bom_entry.only_part_or_assembly_allowed - Det er kun tilladt at vælge én del eller en samling. Venligst tilpas dit valg! - - validator.bom_importer.invalid_import_type @@ -37,6 +31,12 @@ Αυτό το εξάρτημα υπάρχει ήδη στην ομάδα! + + + assembly.bom_entry.assembly_already_in_bom + Αυτή η συναρμολόγηση υπάρχει ήδη ως εγγραφή! + + assembly.bom_entry.project_already_in_bom @@ -55,6 +55,12 @@ Πρέπει να επιλέξετε ένα εξάρτημα ή να βάλετε ένα όνομα για ένα μη εξάρτημα! + + + validator.assembly.bom_entry.only_part_or_assembly_allowed + Det er kun tilladt at vælge én del eller en samling. Venligst tilpas dit valg! + + validator.bom_importer.json_csv.quantity.required diff --git a/translations/validators.en.xlf b/translations/validators.en.xlf index e20e7ff8..59cabf55 100644 --- a/translations/validators.en.xlf +++ b/translations/validators.en.xlf @@ -251,12 +251,6 @@ You have to select a part or assembly, or set a name for a non-component Bom entry! - - - validator.project.bom_entry.only_part_or_assembly_allowed - Only one part or assembly may be selected. Please modify your selection! - - project.bom_entry.name_already_in_bom @@ -395,6 +389,12 @@ This part already exists in the list! + + + assembly.bom_entry.assembly_already_in_bom + This assembly already exists as an entry! + + assembly.bom_entry.project_already_in_bom @@ -413,6 +413,12 @@ You must select a part or set a name for the entry! + + + validator.assembly.bom_entry.only_part_or_assembly_allowed + Only one part or assembly may be selected. Please modify your selection! + + validator.bom_importer.json_csv.quantity.required diff --git a/translations/validators.fr.xlf b/translations/validators.fr.xlf index e603bdaf..aff68a18 100644 --- a/translations/validators.fr.xlf +++ b/translations/validators.fr.xlf @@ -203,12 +203,6 @@ L'emplacement de stockage a été marqué comme "Composant seul", par conséquent aucun nouveau composant ne peut être ajouté. - - - validator.project.bom_entry.only_part_or_assembly_allowed - Seule une pièce ou un assemblage peut être sélectionné. Veuillez ajuster votre sélection! - - validator.bom_importer.invalid_import_type @@ -227,6 +221,12 @@ Cette pièce existe déjà dans le groupe! + + + assembly.bom_entry.assembly_already_in_bom + Cet assemblage existe déjà en tant qu'entrée ! + + assembly.bom_entry.project_already_in_bom @@ -245,6 +245,12 @@ Vous devez sélectionner une pièce ou attribuer un nom pour un non-élément! + + + validator.assembly.bom_entry.only_part_or_assembly_allowed + Seule une pièce ou un assemblage peut être sélectionné. Veuillez ajuster votre sélection! + + validator.bom_importer.json_csv.quantity.required diff --git a/translations/validators.hr.xlf b/translations/validators.hr.xlf index c0af0335..1ee5c06f 100644 --- a/translations/validators.hr.xlf +++ b/translations/validators.hr.xlf @@ -251,12 +251,6 @@ Morate odabrati dio za unos u BOM ili postaviti naziv za unos koji nije dio. - - - validator.project.bom_entry.only_part_or_assembly_allowed - Dozvoljeno je odabrati samo jednu komponentu ili sklop. Molimo prilagodite svoj odabir! - - project.bom_entry.name_already_in_bom @@ -389,6 +383,12 @@ Ovaj dio već postoji u grupi! + + + assembly.bom_entry.assembly_already_in_bom + Ova se montaža već nalazi kao zapis! + + assembly.bom_entry.project_already_in_bom @@ -407,6 +407,12 @@ Morate odabrati dio ili unijeti naziv za nedio! + + + validator.assembly.bom_entry.only_part_or_assembly_allowed + Dozvoljeno je odabrati samo jednu komponentu ili sklop. Molimo prilagodite svoj odabir! + + validator.bom_importer.json_csv.quantity.required diff --git a/translations/validators.it.xlf b/translations/validators.it.xlf index 88369640..ac57a2cc 100644 --- a/translations/validators.it.xlf +++ b/translations/validators.it.xlf @@ -251,12 +251,6 @@ È necessario selezionare un componente o assegnare un nome ad una voce BOM che non indica un componente! - - - validator.project.bom_entry.only_part_or_assembly_allowed - È consentito selezionare solo una parte o un assieme. Si prega di modificare la selezione! - - project.bom_entry.name_already_in_bom @@ -389,6 +383,12 @@ Questa parte è già presente nel gruppo! + + + assembly.bom_entry.assembly_already_in_bom + Questo assemblaggio è già presente come voce! + + assembly.bom_entry.project_already_in_bom @@ -407,6 +407,12 @@ È necessario selezionare una parte o inserire un nome per un non-parte! + + + validator.assembly.bom_entry.only_part_or_assembly_allowed + È consentito selezionare solo una parte o un assieme. Si prega di modificare la selezione! + + validator.bom_importer.json_csv.quantity.required diff --git a/translations/validators.ja.xlf b/translations/validators.ja.xlf index 070281cc..a316707a 100644 --- a/translations/validators.ja.xlf +++ b/translations/validators.ja.xlf @@ -203,12 +203,6 @@ 新しい部品を追加できません。保管場所は「1つの部品のみ」とマークされています。 - - - validator.project.bom_entry.only_part_or_assembly_allowed - 部品またはアセンブリのみ選択可能です。選択内容を調整してください! - - validator.bom_importer.invalid_import_type @@ -227,6 +221,12 @@ この部品はすでにグループに存在します! + + + assembly.bom_entry.assembly_already_in_bom + このアセンブリはすでにエントリとして存在します! + + assembly.bom_entry.project_already_in_bom @@ -245,6 +245,12 @@ 部品を選択するか、非部品の名前を入力する必要があります! + + + validator.assembly.bom_entry.only_part_or_assembly_allowed + 部品またはアセンブリのみ選択可能です。選択内容を調整してください! + + validator.bom_importer.json_csv.quantity.required diff --git a/translations/validators.pl.xlf b/translations/validators.pl.xlf index 60713fa0..95c44ab4 100644 --- a/translations/validators.pl.xlf +++ b/translations/validators.pl.xlf @@ -251,12 +251,6 @@ Należy wybrać część dla wpisu BOM części lub ustawić nazwę dla wpisu BOM niebędącego częścią. - - - validator.project.bom_entry.only_part_or_assembly_allowed - Można wybrać tylko jedną część lub zespół. Proszę dostosować swój wybór! - - project.bom_entry.name_already_in_bom @@ -389,6 +383,12 @@ Ten element już istnieje w grupie! + + + assembly.bom_entry.assembly_already_in_bom + To zestawienie jest już dodane jako wpis! + + assembly.bom_entry.project_already_in_bom @@ -407,6 +407,12 @@ Musisz wybrać element lub przypisać nazwę dla elementu niestandardowego! + + + validator.assembly.bom_entry.only_part_or_assembly_allowed + Można wybrać tylko jedną część lub zespół. Proszę dostosować swój wybór! + + validator.bom_importer.json_csv.quantity.required diff --git a/translations/validators.ru.xlf b/translations/validators.ru.xlf index 32540f8c..425ede5f 100644 --- a/translations/validators.ru.xlf +++ b/translations/validators.ru.xlf @@ -251,12 +251,6 @@ Вам необходимо выбрать компонент или задать имя для BOM, не относящейся к компоненту! - - - validator.project.bom_entry.only_part_or_assembly_allowed - Можно выбрать только деталь или сборку. Пожалуйста, измените ваш выбор! - - project.bom_entry.name_already_in_bom @@ -389,6 +383,12 @@ Эта деталь уже существует в группе! + + + assembly.bom_entry.assembly_already_in_bom + Этот сборочный узел уже добавлен как запись! + + assembly.bom_entry.project_already_in_bom @@ -407,6 +407,12 @@ Необходимо выбрать деталь или ввести название для недетали! + + + validator.assembly.bom_entry.only_part_or_assembly_allowed + Можно выбрать только деталь или сборку. Пожалуйста, измените ваш выбор! + + validator.bom_importer.json_csv.quantity.required diff --git a/translations/validators.zh.xlf b/translations/validators.zh.xlf index 4a0ec79e..4a02523b 100644 --- a/translations/validators.zh.xlf +++ b/translations/validators.zh.xlf @@ -251,12 +251,6 @@ 您必须为 BOM 条目选择部件,或为非部件 BOM 条目设置名称。 - - - validator.project.bom_entry.only_part_or_assembly_allowed - 只能选择一个零件或组件。请修改您的选择! - - project.bom_entry.name_already_in_bom @@ -377,6 +371,12 @@ 此零件已存在于组中! + + + assembly.bom_entry.assembly_already_in_bom + 此装配已经作为条目存在! + + assembly.bom_entry.project_already_in_bom @@ -395,6 +395,12 @@ 必须选择零件或为非零件指定名称! + + + validator.assembly.bom_entry.only_part_or_assembly_allowed + 只能选择一个零件或组件。请修改您的选择! + + validator.bom_importer.json_csv.quantity.required