From df2ce45e4c108bec2bba599ebea2f5aa6eda308b Mon Sep 17 00:00:00 2001 From: Marcel Diegelmann Date: Tue, 17 Jun 2025 11:28:42 +0200 Subject: [PATCH] Projekt BOM-Konfiguration um Assemblies bereinigen. Assembly BOM-Konfiguration um Projektauswahl erweitern (APS-3, APS-4) --- .../elements/project_select_controller.js | 70 +++++++ migrations/Version20250304081039.php | 109 +++++++++- migrations/Version20250304154507.php | 20 +- migrations/Version20250310160354.php | 188 ------------------ src/Controller/TypeaheadController.php | 36 ++-- .../AssemblyBomEntriesDataTable.php | 36 +++- ...eHelper.php => ProjectDataTableHelper.php} | 6 +- src/DataTables/ProjectBomEntriesDataTable.php | 25 +-- src/Entity/AssemblySystem/Assembly.php | 4 +- .../AssemblySystem/AssemblyBOMEntry.php | 26 ++- src/Entity/ProjectSystem/Project.php | 1 - src/Entity/ProjectSystem/ProjectBOMEntry.php | 38 +--- .../AssemblySystem/AssemblyBOMEntryType.php | 13 +- src/Form/AssemblySystem/AssemblyBuildType.php | 19 +- .../ProjectSystem/ProjectAddPartsType.php | 1 - .../ProjectSystem/ProjectBOMEntryType.php | 5 - src/Form/ProjectSystem/ProjectBuildType.php | 23 +-- ...lySelectType.php => ProjectSelectType.php} | 26 +-- src/Helpers/Projects/ProjectBuildRequest.php | 98 +-------- src/Repository/AssemblyRepository.php | 69 ------- src/Repository/Parts/DeviceRepository.php | 18 ++ .../AssemblySystem/AssemblyBuildHelper.php | 36 ++-- ...erator.php => ProjectPreviewGenerator.php} | 26 +-- .../ProjectSystem/ProjectBuildHelper.php | 71 ++----- src/Twig/AssemblyTwigExtension.php | 6 +- .../form/collection_types_layout.html.twig | 18 +- ...collection_types_layout_assembly.html.twig | 18 +- templates/projects/build/_form.html.twig | 57 +----- translations/messages.cs.xlf | 96 ++------- translations/messages.da.xlf | 94 ++------- translations/messages.de.xlf | 84 ++------ translations/messages.el.xlf | 82 ++------ translations/messages.en.xlf | 82 ++------ translations/messages.es.xlf | 82 ++------ translations/messages.fr.xlf | 86 ++------ translations/messages.it.xlf | 86 ++------ translations/messages.ja.xlf | 106 ++++------ translations/messages.nl.xlf | 82 ++------ translations/messages.pl.xlf | 86 ++------ translations/messages.ru.xlf | 86 ++------ translations/messages.zh.xlf | 86 ++------ translations/validators.cs.xlf | 6 + translations/validators.da.xlf | 6 + translations/validators.de.xlf | 6 + translations/validators.el.xlf | 6 + translations/validators.en.xlf | 12 +- translations/validators.fr.xlf | 6 + translations/validators.hr.xlf | 6 + translations/validators.it.xlf | 6 + translations/validators.ja.xlf | 6 + translations/validators.pl.xlf | 6 + translations/validators.ru.xlf | 6 + translations/validators.zh.xlf | 6 + 53 files changed, 738 insertions(+), 1541 deletions(-) create mode 100644 assets/controllers/elements/project_select_controller.js delete mode 100644 migrations/Version20250310160354.php rename src/DataTables/Helpers/{AssemblyDataTableHelper.php => ProjectDataTableHelper.php} (91%) rename src/Form/Type/{AssemblySelectType.php => ProjectSelectType.php} (82%) delete mode 100644 src/Repository/AssemblyRepository.php rename src/Services/Attachments/{AssemblyPreviewGenerator.php => ProjectPreviewGenerator.php} (75%) diff --git a/assets/controllers/elements/project_select_controller.js b/assets/controllers/elements/project_select_controller.js new file mode 100644 index 00000000..98702d41 --- /dev/null +++ b/assets/controllers/elements/project_select_controller.js @@ -0,0 +1,70 @@ +import {Controller} from "@hotwired/stimulus"; + +import "tom-select/dist/css/tom-select.bootstrap5.css"; +import '../../css/components/tom-select_extensions.css'; +import TomSelect from "tom-select"; +import {marked} from "marked"; + +export default class extends Controller { + _tomSelect; + + connect() { + + let settings = { + allowEmptyOption: true, + plugins: ['dropdown_input', 'clear_button'], + searchField: ["name", "description", "category", "footprint"], + valueField: "id", + labelField: "name", + preload: "focus", + render: { + item: (data, escape) => { + return '' + (data.image ? "" : "") + escape(data.name) + ''; + }, + option: (data, escape) => { + if(data.text) { + return '' + escape(data.text) + ''; + } + + let tmp = '
' + + "
" + + (data.image ? "" : "") + + "
" + + "
" + + '
' + escape(data.name) + '
' + + (data.description ? '

' + marked.parseInline(data.description) + '

' : "") + + (data.category ? '

' + escape(data.category) : ""); + + return tmp + '

' + + '
'; + } + } + }; + + + if (this.element.dataset.autocomplete) { + const base_url = this.element.dataset.autocomplete; + settings.valueField = "id"; + settings.load = (query, callback) => { + const url = base_url.replace('__QUERY__', encodeURIComponent(query)); + + fetch(url) + .then(response => response.json()) + .then(json => {callback(json);}) + .catch(() => { + callback() + }); + }; + + + this._tomSelect = new TomSelect(this.element, settings); + //this._tomSelect.clearOptions(); + } + } + + disconnect() { + super.disconnect(); + //Destroy the TomSelect instance + this._tomSelect.destroy(); + } +} \ No newline at end of file diff --git a/migrations/Version20250304081039.php b/migrations/Version20250304081039.php index 789cf328..755ae236 100644 --- a/migrations/Version20250304081039.php +++ b/migrations/Version20250304081039.php @@ -6,7 +6,6 @@ namespace DoctrineMigrations; use App\Migration\AbstractMultiPlatformMigration; use Doctrine\DBAL\Schema\Schema; -use Doctrine\Migrations\AbstractMigration; final class Version20250304081039 extends AbstractMultiPlatformMigration { @@ -17,12 +16,13 @@ final class Version20250304081039 extends AbstractMultiPlatformMigration public function mySQLUp(Schema $schema): void { - $this->addSql('CREATE TABLE assemblies (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, comment LONGTEXT NOT NULL, not_selectable TINYINT(1) NOT NULL, alternative_names LONGTEXT DEFAULT NULL, order_quantity INT NOT NULL, status VARCHAR(64) DEFAULT NULL, order_only_missing_parts TINYINT(1) NOT NULL, description LONGTEXT NOT NULL, parent_id INT DEFAULT NULL, id_preview_attachment INT DEFAULT NULL, INDEX IDX_5F3832C0727ACA70 (parent_id), INDEX IDX_5F3832C0EA7100A1 (id_preview_attachment), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci`'); - $this->addSql('CREATE TABLE assembly_bom_entries (id INT AUTO_INCREMENT NOT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames LONGTEXT NOT NULL, name VARCHAR(255) DEFAULT NULL, comment LONGTEXT NOT NULL, price NUMERIC(11, 5) DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, id_assembly INT DEFAULT NULL, id_part INT DEFAULT NULL, price_currency_id INT DEFAULT NULL, INDEX IDX_8C74887E2F180363 (id_assembly), INDEX IDX_8C74887EC22F6CC4 (id_part), INDEX IDX_8C74887E3FFDCD60 (price_currency_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci`'); + $this->addSql('CREATE TABLE assemblies (id INT AUTO_INCREMENT NOT NULL, parent_id INT DEFAULT NULL, id_preview_attachment INT DEFAULT NULL, name VARCHAR(255) NOT NULL, comment LONGTEXT NOT NULL, not_selectable TINYINT(1) NOT NULL, alternative_names LONGTEXT DEFAULT NULL, order_quantity INT NOT NULL, status VARCHAR(64) DEFAULT NULL, order_only_missing_parts TINYINT(1) NOT NULL, description LONGTEXT NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, INDEX IDX_5F3832C0727ACA70 (parent_id), INDEX IDX_5F3832C0EA7100A1 (id_preview_attachment), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci`'); + $this->addSql('CREATE TABLE assembly_bom_entries (id INT AUTO_INCREMENT NOT NULL, id_assembly INT DEFAULT NULL, id_part INT DEFAULT NULL, id_project INT DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames LONGTEXT NOT NULL, name VARCHAR(255) DEFAULT NULL, comment LONGTEXT NOT NULL, price NUMERIC(11, 5) DEFAULT NULL, price_currency_id INT DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, INDEX IDX_8C74887E2F180363 (id_assembly), INDEX IDX_8C74887EC22F6CC4 (id_part), INDEX IDX_8C74887EF12E799E (id_project), INDEX IDX_8C74887E3FFDCD60 (price_currency_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci`'); $this->addSql('ALTER TABLE assemblies ADD CONSTRAINT FK_5F3832C0727ACA70 FOREIGN KEY (parent_id) REFERENCES assemblies (id)'); $this->addSql('ALTER TABLE assemblies ADD CONSTRAINT FK_5F3832C0EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES `attachments` (id) ON DELETE SET NULL'); $this->addSql('ALTER TABLE assembly_bom_entries ADD CONSTRAINT FK_8C74887E2F180363 FOREIGN KEY (id_assembly) REFERENCES assemblies (id)'); $this->addSql('ALTER TABLE assembly_bom_entries ADD CONSTRAINT FK_8C74887EC22F6CC4 FOREIGN KEY (id_part) REFERENCES `parts` (id)'); + $this->addSql('ALTER TABLE assembly_bom_entries ADD CONSTRAINT FK_8C74887EF12E799E FOREIGN KEY (id_project) REFERENCES `projects` (id)'); $this->addSql('ALTER TABLE assembly_bom_entries ADD CONSTRAINT FK_8C74887E3FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id)'); } @@ -32,6 +32,7 @@ final class Version20250304081039 extends AbstractMultiPlatformMigration $this->addSql('ALTER TABLE assemblies DROP FOREIGN KEY FK_5F3832C0EA7100A1'); $this->addSql('ALTER TABLE assembly_bom_entries DROP FOREIGN KEY FK_8C74887E2F180363'); $this->addSql('ALTER TABLE assembly_bom_entries DROP FOREIGN KEY FK_8C74887EC22F6CC4'); + $this->addSql('ALTER TABLE assembly_bom_entries DROP FOREIGN KEY FK_8C74887EF12E799E'); $this->addSql('ALTER TABLE assembly_bom_entries DROP FOREIGN KEY FK_8C74887E3FFDCD60'); $this->addSql('DROP TABLE assemblies'); $this->addSql('DROP TABLE assembly_bom_entries'); @@ -70,6 +71,7 @@ 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, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, @@ -80,6 +82,7 @@ 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 ) SQL); @@ -89,6 +92,9 @@ 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); @@ -106,11 +112,104 @@ final class Version20250304081039 extends AbstractMultiPlatformMigration public function postgreSQLUp(Schema $schema): void { - //Not needed + $this->addSql(<<<'SQL' + CREATE TABLE assemblies ( + id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, + name VARCHAR(255) NOT NULL, + last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, + datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, + comment TEXT NOT NULL, + not_selectable BOOLEAN NOT NULL, + alternative_names TEXT DEFAULT NULL, + order_quantity INT NOT NULL, + status VARCHAR(64) DEFAULT NULL, + order_only_missing_parts BOOLEAN NOT NULL, + description TEXT NOT NULL, + parent_id INT DEFAULT NULL, + id_preview_attachment INT DEFAULT NULL, + PRIMARY KEY(id) + ) + SQL); + $this->addSql(<<<'SQL' + CREATE INDEX IDX_5F3832C0727ACA70 ON assemblies (parent_id) + SQL); + $this->addSql(<<<'SQL' + CREATE INDEX IDX_5F3832C0EA7100A1 ON assemblies (id_preview_attachment) + SQL); + $this->addSql(<<<'SQL' + CREATE TABLE assembly_bom_entries ( + 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, + comment TEXT NOT NULL, + price NUMERIC(11, 5) DEFAULT NULL, + price_currency_id INT DEFAULT NULL, + last_modified TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, + datetime_added TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL, + PRIMARY KEY(id) + ) + SQL); + $this->addSql(<<<'SQL' + CREATE INDEX IDX_8C74887E4AD2039E ON assembly_bom_entries (id_assembly) + SQL); + $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); + $this->addSql(<<<'SQL' + ALTER TABLE assemblies ADD CONSTRAINT FK_5F3832C0727ACA70 FOREIGN KEY (parent_id) REFERENCES assemblies (id) NOT DEFERRABLE INITIALLY IMMEDIATE + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE assemblies ADD CONSTRAINT FK_5F3832C0EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE assembly_bom_entries ADD CONSTRAINT FK_8C74887E4AD2039E FOREIGN KEY (id_assembly) REFERENCES assemblies (id) NOT DEFERRABLE INITIALLY IMMEDIATE + SQL); + $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); } public function postgreSQLDown(Schema $schema): void { - //Not needed + $this->addSql(<<<'SQL' + ALTER TABLE assemblies DROP CONSTRAINT FK_5F3832C0727ACA70 + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE assemblies DROP CONSTRAINT FK_5F3832C0EA7100A1 + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE assembly_bom_entries DROP CONSTRAINT FK_8C74887E4AD2039E + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE assembly_bom_entries DROP CONSTRAINT FK_8C74887EC22F6CC4 + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE assembly_bom_entries DROP CONSTRAINT FK_8C74887EF12E799E + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE assembly_bom_entries DROP CONSTRAINT FK_8C74887E3FFDCD60 + SQL); + $this->addSql(<<<'SQL' + DROP TABLE assemblies + SQL); + $this->addSql(<<<'SQL' + DROP TABLE assembly_bom_entries + SQL); } } diff --git a/migrations/Version20250304154507.php b/migrations/Version20250304154507.php index 37cf2fec..904a3b65 100644 --- a/migrations/Version20250304154507.php +++ b/migrations/Version20250304154507.php @@ -386,11 +386,27 @@ final class Version20250304154507 extends AbstractMultiPlatformMigration public function postgreSQLUp(Schema $schema): void { - //Not needed + $this->addSql(<<<'SQL' + ALTER TABLE parts ADD built_assembly_id INT DEFAULT NULL + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE parts ADD CONSTRAINT FK_6940A7FECC660B3C FOREIGN KEY (built_assembly_id) REFERENCES assemblies (id) NOT DEFERRABLE INITIALLY IMMEDIATE + SQL); + $this->addSql(<<<'SQL' + CREATE UNIQUE INDEX UNIQ_6940A7FECC660B3C ON parts (built_assembly_id) + SQL); } public function postgreSQLDown(Schema $schema): void { - //Not needed + $this->addSql(<<<'SQL' + ALTER TABLE "parts" DROP CONSTRAINT FK_6940A7FECC660B3C + SQL); + $this->addSql(<<<'SQL' + DROP INDEX UNIQ_6940A7FECC660B3C + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE "parts" DROP built_assembly_id + SQL); } } diff --git a/migrations/Version20250310160354.php b/migrations/Version20250310160354.php deleted file mode 100644 index 201e7e85..00000000 --- a/migrations/Version20250310160354.php +++ /dev/null @@ -1,188 +0,0 @@ -addSql('ALTER TABLE assembly_bom_entries RENAME INDEX idx_8c74887e2f180363 TO IDX_8C74887E4AD2039E'); - $this->addSql('ALTER TABLE project_bom_entries ADD id_assembly INT DEFAULT NULL AFTER id_part'); - $this->addSql('ALTER TABLE project_bom_entries ADD CONSTRAINT FK_1AA2DD314AD2039E FOREIGN KEY (id_assembly) REFERENCES assemblies (id)'); - $this->addSql('CREATE INDEX IDX_1AA2DD314AD2039E ON project_bom_entries (id_assembly)'); - } - - public function mySQLDown(Schema $schema): void - { - $this->addSql('ALTER TABLE assembly_bom_entries RENAME INDEX idx_8c74887e4ad2039e TO IDX_8C74887E2F180363'); - $this->addSql('ALTER TABLE project_bom_entries DROP FOREIGN KEY FK_1AA2DD314AD2039E'); - $this->addSql('DROP INDEX IDX_1AA2DD314AD2039E ON project_bom_entries'); - $this->addSql('ALTER TABLE project_bom_entries DROP id_assembly'); - } - - public function sqLiteUp(Schema $schema): void - { - $this->addSql(<<<'SQL' - CREATE TEMPORARY TABLE __temp__project_bom_entries AS - SELECT - id, - id_device, - id_part, - price_currency_id, - quantity, - mountnames, - name, - comment, - price, - last_modified, - datetime_added - FROM project_bom_entries - SQL); - - $this->addSql('DROP TABLE project_bom_entries'); - - $this->addSql(<<<'SQL' - CREATE TABLE project_bom_entries - ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - id_device INTEGER DEFAULT NULL, - id_assembly INTEGER DEFAULT NULL, - id_part INTEGER DEFAULT NULL, - price_currency_id INTEGER DEFAULT NULL, - quantity DOUBLE PRECISION NOT NULL, - mountnames CLOB NOT NULL, - name VARCHAR(255) DEFAULT NULL, - comment CLOB NOT NULL, - price NUMERIC(11, 5) DEFAULT NULL, - last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, - datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, - CONSTRAINT FK_1AA2DD312F180363 FOREIGN KEY (id_device) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, - CONSTRAINT FK_1AA2DD314AD2039E FOREIGN KEY (id_assembly) REFERENCES assemblies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, - CONSTRAINT FK_1AA2DD31C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, - CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE - ) - SQL); - - - $this->addSql(<<<'SQL' - INSERT INTO project_bom_entries ( - id, - id_device, - id_part, - price_currency_id, - quantity, - mountnames, - name, - comment, - price, - last_modified, - datetime_added - ) SELECT * FROM __temp__project_bom_entries - SQL); - $this->addSql('DROP TABLE __temp__project_bom_entries'); - - $this->addSql(<<<'SQL' - CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device) - SQL); - $this->addSql(<<<'SQL' - CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part) - SQL); - $this->addSql(<<<'SQL' - CREATE INDEX IDX_1AA2DD314AD2039E ON project_bom_entries (id_assembly) - SQL); - $this->addSql(<<<'SQL' - CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id) - SQL); - } - - public function sqLiteDown(Schema $schema): void - { - $this->addSql(<<<'SQL' - CREATE TEMPORARY TABLE __temp__project_bom_entries AS - SELECT - id, - id_device, - id_part, - price_currency_id, - quantity, - mountnames, - name, - comment, - price, - last_modified, - datetime_added - FROM project_bom_entries - SQL); - - $this->addSql('DROP TABLE project_bom_entries'); - - $this->addSql(<<<'SQL' - CREATE TABLE project_bom_entries - ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - id_device INTEGER DEFAULT NULL, - id_part INTEGER DEFAULT NULL, - price_currency_id INTEGER DEFAULT NULL, - quantity DOUBLE PRECISION NOT NULL, - mountnames CLOB NOT NULL, - name VARCHAR(255) DEFAULT NULL, - comment CLOB NOT NULL, - price NUMERIC(11, 5) DEFAULT NULL, - last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, - datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, - CONSTRAINT FK_1AA2DD312F180363 FOREIGN KEY (id_device) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, - CONSTRAINT FK_1AA2DD31C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, - CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE - ) - SQL); - - $this->addSql(<<<'SQL' - INSERT INTO project_bom_entries ( - id, - id_device, - id_part, - price_currency_id, - quantity, - mountnames, - name, - comment, - price, - last_modified, - datetime_added - ) SELECT * FROM __temp__project_bom_entries - SQL); - - $this->addSql('DROP TABLE __temp__project_bom_entries'); - - $this->addSql(<<<'SQL' - CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device) - SQL); - $this->addSql(<<<'SQL' - CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part) - SQL); - $this->addSql(<<<'SQL' - CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id) - SQL); - } - - public function postgreSQLUp(Schema $schema): void - { - //Not needed - } - - public function postgreSQLDown(Schema $schema): void - { - //Not needed - } -} diff --git a/src/Controller/TypeaheadController.php b/src/Controller/TypeaheadController.php index 09792951..4335492e 100644 --- a/src/Controller/TypeaheadController.php +++ b/src/Controller/TypeaheadController.php @@ -22,9 +22,9 @@ declare(strict_types=1); namespace App\Controller; -use App\Entity\AssemblySystem\Assembly; use App\Entity\Parameters\AbstractParameter; -use App\Services\Attachments\AssemblyPreviewGenerator; +use App\Entity\ProjectSystem\Project; +use App\Services\Attachments\ProjectPreviewGenerator; use Symfony\Component\HttpFoundation\Response; use App\Entity\Attachments\Attachment; use App\Entity\Parts\Category; @@ -151,29 +151,29 @@ class TypeaheadController extends AbstractController 'footprint' => $part->getFootprint() instanceof Footprint ? $part->getFootprint()->getName() : '', 'description' => mb_strimwidth($part->getDescription(), 0, 127, '...'), 'image' => $preview_url, - ]; + ]; } return new JsonResponse($data); } - #[Route(path: '/assemblies/search/{query}', name: 'typeahead_assemblies')] - public function assemblies( - EntityManagerInterface $entityManager, - AssemblyPreviewGenerator $assemblyPreviewGenerator, - AttachmentURLGenerator $attachmentURLGenerator, - string $query = "" + #[Route(path: '/projects/search/{query}', name: 'typeahead_projects')] + public function projects( + EntityManagerInterface $entityManager, + ProjectPreviewGenerator $projectPreviewGenerator, + AttachmentURLGenerator $attachmentURLGenerator, + string $query = "" ): JsonResponse { - $this->denyAccessUnlessGranted('@assemblies.read'); + $this->denyAccessUnlessGranted('@projects.read'); $result = []; - $assemblyRepository = $entityManager->getRepository(Assembly::class); + $projectRepository = $entityManager->getRepository(Project::class); - $assemblies = $assemblyRepository->autocompleteSearch($query, 100); + $projects = $projectRepository->autocompleteSearch($query, 100); - foreach ($assemblies as $assembly) { - $preview_attachment = $assemblyPreviewGenerator->getTablePreviewAttachment($assembly); + foreach ($projects as $project) { + $preview_attachment = $projectPreviewGenerator->getTablePreviewAttachment($project); if($preview_attachment instanceof Attachment) { $preview_url = $attachmentURLGenerator->getThumbnailURL($preview_attachment, 'thumbnail_sm'); @@ -181,13 +181,13 @@ class TypeaheadController extends AbstractController $preview_url = ''; } - /** @var Assembly $assembly */ + /** @var Project $project */ $result[] = [ - 'id' => $assembly->getID(), - 'name' => $this->translator->trans('typeahead.parts.assembly.name', ['%name%' => $assembly->getName()]), + 'id' => $project->getID(), + 'name' => $project->getName(), 'category' => '', 'footprint' => '', - 'description' => mb_strimwidth($assembly->getDescription(), 0, 127, '...'), + 'description' => mb_strimwidth($project->getDescription(), 0, 127, '...'), 'image' => $preview_url, ]; } diff --git a/src/DataTables/AssemblyBomEntriesDataTable.php b/src/DataTables/AssemblyBomEntriesDataTable.php index a953179a..b31c72bc 100644 --- a/src/DataTables/AssemblyBomEntriesDataTable.php +++ b/src/DataTables/AssemblyBomEntriesDataTable.php @@ -25,11 +25,13 @@ namespace App\DataTables; use App\DataTables\Column\EntityColumn; use App\DataTables\Column\LocaleDateTimeColumn; use App\DataTables\Column\MarkdownColumn; +use App\DataTables\Helpers\ProjectDataTableHelper; use App\DataTables\Helpers\ColumnSortHelper; use App\DataTables\Helpers\PartDataTableHelper; use App\Entity\Attachments\Attachment; use App\Entity\Parts\Part; use App\Entity\AssemblySystem\AssemblyBOMEntry; +use App\Entity\ProjectSystem\Project; use App\Services\EntityURLGenerator; use App\Services\Formatters\AmountFormatter; use Doctrine\ORM\QueryBuilder; @@ -43,12 +45,13 @@ use Symfony\Contracts\Translation\TranslatorInterface; class AssemblyBomEntriesDataTable implements DataTableTypeInterface { public function __construct( - protected TranslatorInterface $translator, - protected PartDataTableHelper $partDataTableHelper, - protected EntityURLGenerator $entityURLGenerator, - protected AmountFormatter $amountFormatter, - private string $visible_columns, - private ColumnSortHelper $csh + protected TranslatorInterface $translator, + protected PartDataTableHelper $partDataTableHelper, + protected ProjectDataTableHelper $projectDataTableHelper, + protected EntityURLGenerator $entityURLGenerator, + protected AmountFormatter $amountFormatter, + private string $visible_columns, + private ColumnSortHelper $csh ){ } @@ -86,18 +89,29 @@ class AssemblyBomEntriesDataTable implements DataTableTypeInterface 'label' => $this->translator->trans('part.table.name'), 'orderField' => 'NATSORT(part.name)', 'render' => function ($value, AssemblyBOMEntry $context) { - if(!$context->getPart() instanceof Part) { + if(!$context->getPart() instanceof Part && !$context->getProject() instanceof Project) { return htmlspecialchars((string) $context->getName()); } - //Part exists if we reach this point + if ($context->getPart() !== null) { + $tmp = $this->partDataTableHelper->renderName($context->getPart()); + $tmp = $this->translator->trans('part.table.name.value.for_part', ['%value%' => $tmp]); - $tmp = $this->partDataTableHelper->renderName($context->getPart()); - if($context->getName() !== null && $context->getName() !== '') { - $tmp .= '
'.htmlspecialchars($context->getName()).''; + if($context->getName() !== null && $context->getName() !== '') { + $tmp .= '
'.htmlspecialchars($context->getName()).''; + } + } elseif ($context->getProject() !== null) { + $tmp = $this->projectDataTableHelper->renderName($context->getProject()); + $tmp = $this->translator->trans('part.table.name.value.for_project', ['%value%' => $tmp]); + + if($context->getName() !== null && $context->getName() !== '') { + $tmp .= '
'.htmlspecialchars($context->getName()).''; + } } + return $tmp; }, + ]) ->add('ipn', TextColumn::class, [ 'label' => $this->translator->trans('part.table.ipn'), diff --git a/src/DataTables/Helpers/AssemblyDataTableHelper.php b/src/DataTables/Helpers/ProjectDataTableHelper.php similarity index 91% rename from src/DataTables/Helpers/AssemblyDataTableHelper.php rename to src/DataTables/Helpers/ProjectDataTableHelper.php index 36f7836b..0118d5d5 100644 --- a/src/DataTables/Helpers/AssemblyDataTableHelper.php +++ b/src/DataTables/Helpers/ProjectDataTableHelper.php @@ -23,18 +23,18 @@ declare(strict_types=1); namespace App\DataTables\Helpers; -use App\Entity\AssemblySystem\Assembly; +use App\Entity\ProjectSystem\Project; use App\Services\EntityURLGenerator; /** * A helper service which contains common code to render columns for assembly related tables */ -class AssemblyDataTableHelper +class ProjectDataTableHelper { public function __construct(private readonly EntityURLGenerator $entityURLGenerator) { } - public function renderName(Assembly $context): string + public function renderName(Project $context): string { $icon = ''; diff --git a/src/DataTables/ProjectBomEntriesDataTable.php b/src/DataTables/ProjectBomEntriesDataTable.php index 1c7a09e4..89572a8a 100644 --- a/src/DataTables/ProjectBomEntriesDataTable.php +++ b/src/DataTables/ProjectBomEntriesDataTable.php @@ -25,9 +25,7 @@ namespace App\DataTables; use App\DataTables\Column\EntityColumn; use App\DataTables\Column\LocaleDateTimeColumn; use App\DataTables\Column\MarkdownColumn; -use App\DataTables\Helpers\AssemblyDataTableHelper; use App\DataTables\Helpers\PartDataTableHelper; -use App\Entity\AssemblySystem\Assembly; use App\Entity\Attachments\Attachment; use App\Entity\Parts\Part; use App\Entity\ProjectSystem\ProjectBOMEntry; @@ -46,7 +44,6 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface public function __construct( protected TranslatorInterface $translator, protected PartDataTableHelper $partDataTableHelper, - protected AssemblyDataTableHelper $assemblyDataTableHelper, protected EntityURLGenerator $entityURLGenerator, protected AmountFormatter $amountFormatter ) { @@ -90,26 +87,16 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface 'label' => $this->translator->trans('part.table.name'), 'orderField' => 'NATSORT(part.name)', 'render' => function ($value, ProjectBOMEntry $context) { - if(!$context->getPart() instanceof Part && !$context->getAssembly() instanceof Assembly) { + if(!$context->getPart() instanceof Part) { return htmlspecialchars((string) $context->getName()); } - if ($context->getPart() !== null) { - $tmp = $this->partDataTableHelper->renderName($context->getPart()); - $tmp = $this->translator->trans('part.table.name.value.for_part', ['%value%' => $tmp]); + //Part exists if we reach this point - if($context->getName() !== null && $context->getName() !== '') { - $tmp .= '
'.htmlspecialchars($context->getName()).''; - } - } elseif ($context->getAssembly() !== null) { - $tmp = $this->assemblyDataTableHelper->renderName($context->getAssembly()); - $tmp = $this->translator->trans('part.table.name.value.for_assembly', ['%value%' => $tmp]); - - if($context->getName() !== null && $context->getName() !== '') { - $tmp .= '
'.htmlspecialchars($context->getName()).''; - } + $tmp = $this->partDataTableHelper->renderName($context->getPart()); + if($context->getName() !== null && $context->getName() !== '') { + $tmp .= '
'.htmlspecialchars($context->getName()).''; } - return $tmp; }, ]) @@ -121,6 +108,8 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface if($context->getPart() instanceof Part) { return $context->getPart()->getIpn(); } + + return ''; } ]) ->add('description', MarkdownColumn::class, [ diff --git a/src/Entity/AssemblySystem/Assembly.php b/src/Entity/AssemblySystem/Assembly.php index 17a6868f..54305a6f 100644 --- a/src/Entity/AssemblySystem/Assembly.php +++ b/src/Entity/AssemblySystem/Assembly.php @@ -22,7 +22,6 @@ 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; @@ -57,7 +56,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface; * * @extends AbstractStructuralDBElement */ -#[ORM\Entity(repositoryClass: AssemblyRepository::class)] +#[ORM\Entity] #[ORM\Table(name: 'assemblies')] #[ApiResource( operations: [ @@ -108,6 +107,7 @@ class Assembly extends AbstractStructuralDBElement #[Groups(['extended', 'full', 'import'])] #[ORM\OneToMany(mappedBy: 'assembly', targetEntity: AssemblyBOMEntry::class, cascade: ['persist', 'remove'], orphanRemoval: true)] #[UniqueObjectCollection(message: 'assembly.bom_entry.part_already_in_bom', fields: ['part'])] + #[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; diff --git a/src/Entity/AssemblySystem/AssemblyBOMEntry.php b/src/Entity/AssemblySystem/AssemblyBOMEntry.php index 375fef04..f6ede219 100644 --- a/src/Entity/AssemblySystem/AssemblyBOMEntry.php +++ b/src/Entity/AssemblySystem/AssemblyBOMEntry.php @@ -36,7 +36,7 @@ use ApiPlatform\OpenApi\Model\Operation; use ApiPlatform\Serializer\Filter\PropertyFilter; use App\ApiPlatform\Filter\LikeFilter; use App\Entity\Contracts\TimeStampableInterface; -use App\Entity\AssemblySystem\Assembly; +use App\Entity\ProjectSystem\Project; use App\Repository\DBElementRepository; use App\Validator\UniqueValidatableInterface; use Doctrine\DBAL\Types\Types; @@ -133,6 +133,18 @@ class AssemblyBOMEntry extends AbstractDBElement implements UniqueValidatableInt #[Groups(['bom_entry:read', 'bom_entry:write', 'full'])] protected ?Part $part = null; + /** + * @var Project|null The associated project + */ + #[Assert\Expression( + '(this.getPart() === null or this.getProject() === null) and (this.getName() === null or (this.getName() != null and this.getName() != ""))', + message: 'validator.project.bom_entry.only_part_or_assembly_allowed' + )] + #[ORM\ManyToOne(targetEntity: Project::class)] + #[ORM\JoinColumn(name: 'id_project', nullable: true)] + #[Groups(['bom_entry:read', 'bom_entry:write', ])] + protected ?Project $project = null; + /** * @var BigDecimal|null The price of this non-part BOM entry */ @@ -225,6 +237,17 @@ class AssemblyBOMEntry extends AbstractDBElement implements UniqueValidatableInt return $this; } + public function getProject(): ?Project + { + return $this->project; + } + + public function setProject(?Project $project): AssemblyBOMEntry + { + $this->project = $project; + return $this; + } + /** * Returns the price of this BOM entry, if existing. * Prices are only valid on non-Part BOM entries. @@ -297,6 +320,7 @@ class AssemblyBOMEntry extends AbstractDBElement implements UniqueValidatableInt return [ 'name' => $this->getName(), 'part' => $this->getPart()?->getID(), + 'project' => $this->getProject()?->getID(), ]; } } diff --git a/src/Entity/ProjectSystem/Project.php b/src/Entity/ProjectSystem/Project.php index 36a96377..a103d694 100644 --- a/src/Entity/ProjectSystem/Project.php +++ b/src/Entity/ProjectSystem/Project.php @@ -108,7 +108,6 @@ class Project extends AbstractStructuralDBElement #[Groups(['extended', 'full', 'import'])] #[ORM\OneToMany(mappedBy: 'project', targetEntity: ProjectBOMEntry::class, cascade: ['persist', 'remove'], orphanRemoval: true)] #[UniqueObjectCollection(message: 'project.bom_entry.part_already_in_bom', fields: ['part'])] - #[UniqueObjectCollection(message: 'project.bom_entry.assembly_already_in_bom', fields: ['assembly'])] #[UniqueObjectCollection(message: 'project.bom_entry.name_already_in_bom', fields: ['name'])] protected Collection $bom_entries; diff --git a/src/Entity/ProjectSystem/ProjectBOMEntry.php b/src/Entity/ProjectSystem/ProjectBOMEntry.php index 69240773..f58e4d5e 100644 --- a/src/Entity/ProjectSystem/ProjectBOMEntry.php +++ b/src/Entity/ProjectSystem/ProjectBOMEntry.php @@ -35,7 +35,6 @@ use ApiPlatform\Metadata\Post; use ApiPlatform\OpenApi\Model\Operation; use ApiPlatform\Serializer\Filter\PropertyFilter; use App\ApiPlatform\Filter\LikeFilter; -use App\Entity\AssemblySystem\Assembly; use App\Entity\Contracts\TimeStampableInterface; use App\Validator\UniqueValidatableInterface; use Doctrine\DBAL\Types\Types; @@ -104,10 +103,7 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte /** * @var string|null An optional name describing this BOM entry (useful for non-part entries) */ - #[Assert\Expression( - 'this.getPart() !== null or this.getAssembly() !== null or (this.getName() !== null and this.getName() != "")', - message: 'validator.project.bom_entry.part_or_assembly_needed' - )] + #[Assert\Expression('this.getPart() !== null or this.getName() !== null', message: 'validator.project.bom_entry.name_or_part_needed')] #[ORM\Column(type: Types::STRING, nullable: true)] #[Groups(['bom_entry:read', 'bom_entry:write', 'import', 'simple', 'extended', 'full'])] protected ?string $name = null; @@ -135,18 +131,6 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte #[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.getAssembly() === null) and (this.getName() === null or (this.getName() != null and this.getName() != ""))', - message: 'validator.project.bom_entry.only_part_or_assembly_allowed' - )] - #[ORM\ManyToOne(targetEntity: Assembly::class, inversedBy: 'assembly_bom_entries')] - #[ORM\JoinColumn(name: 'id_assembly')] - #[Groups(['bom_entry:read', 'bom_entry:write', ])] - protected ?Assembly $assembly = null; - /** * @var BigDecimal|null The price of this non-part BOM entry */ @@ -239,16 +223,6 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte return $this; } - public function getAssembly(): ?Assembly - { - return $this->assembly; - } - - public function setAssembly(?Assembly $assembly): void - { - $this->assembly = $assembly; - } - /** * Returns the price of this BOM entry, if existing. * Prices are only valid on non-Part BOM entries. @@ -286,15 +260,6 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte return $this->part instanceof Part; } - /** - * Checks whether this BOM entry is a assembly associated BOM entry or not. - * @return bool True if this BOM entry is a assembly associated BOM entry, false otherwise. - */ - public function isAssemblyBomEntry(): bool - { - return $this->assembly instanceof Assembly; - } - #[Assert\Callback] public function validate(ExecutionContextInterface $context, $payload): void { @@ -356,7 +321,6 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte return [ 'name' => $this->getName(), 'part' => $this->getPart()?->getID(), - 'assembly' => $this->getAssembly()?->getID(), ]; } } diff --git a/src/Form/AssemblySystem/AssemblyBOMEntryType.php b/src/Form/AssemblySystem/AssemblyBOMEntryType.php index 9addccb3..42d463bd 100644 --- a/src/Form/AssemblySystem/AssemblyBOMEntryType.php +++ b/src/Form/AssemblySystem/AssemblyBOMEntryType.php @@ -8,6 +8,7 @@ use App\Entity\AssemblySystem\AssemblyBOMEntry; use App\Form\Type\BigDecimalNumberType; use App\Form\Type\CurrencyEntityType; use App\Form\Type\PartSelectType; +use App\Form\Type\ProjectSelectType; use App\Form\Type\RichTextEditorType; use App\Form\Type\SIUnitType; use Symfony\Component\Form\AbstractType; @@ -34,11 +35,13 @@ class AssemblyBOMEntryType extends AbstractType }); $builder - ->add('part', PartSelectType::class, [ 'required' => false, ]) - + ->add('project', ProjectSelectType::class, [ + 'label' => 'assembly.bom.project', + 'required' => false, + ]) ->add('name', TextType::class, [ 'label' => 'assembly.bom.name', 'required' => false, @@ -75,10 +78,8 @@ class AssemblyBOMEntryType extends AbstractType 'required' => false, 'label' => false, 'short' => true, - ]) - - ; - + ] + ); } public function configureOptions(OptionsResolver $resolver): void diff --git a/src/Form/AssemblySystem/AssemblyBuildType.php b/src/Form/AssemblySystem/AssemblyBuildType.php index 8838706d..e87acb86 100644 --- a/src/Form/AssemblySystem/AssemblyBuildType.php +++ b/src/Form/AssemblySystem/AssemblyBuildType.php @@ -78,35 +78,34 @@ class AssemblyBuildType extends AbstractType implements DataMapperInterface 'required' => false, ]); - - //The form is initially empty, we have to define the fields after we know the data + //The form is initially empty, define the fields after we know the data $builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) { $form = $event->getForm(); - /** @var AssemblyBuildRequest $build_request */ - $build_request = $event->getData(); + /** @var AssemblyBuildRequest $assemblyBuildRequest */ + $assemblyBuildRequest = $event->getData(); $form->add('addBuildsToBuildsPart', CheckboxType::class, [ 'label' => 'assembly.build.add_builds_to_builds_part', 'required' => false, - 'disabled' => !$build_request->getAssembly()->getBuildPart() instanceof Part, + 'disabled' => !$assemblyBuildRequest->getAssembly()->getBuildPart() instanceof Part, ]); - if ($build_request->getAssembly()->getBuildPart() instanceof Part) { + if ($assemblyBuildRequest->getAssembly()->getBuildPart() instanceof Part) { $form->add('buildsPartLot', PartLotSelectType::class, [ 'label' => 'assembly.build.builds_part_lot', 'required' => false, - 'part' => $build_request->getAssembly()->getBuildPart(), + 'part' => $assemblyBuildRequest->getAssembly()->getBuildPart(), 'placeholder' => 'assembly.build.buildsPartLot.new_lot' ]); } - foreach ($build_request->getPartBomEntries() as $bomEntry) { + foreach ($assemblyBuildRequest->getPartBomEntries() as $bomEntry) { //Every part lot has a field to specify the number of parts to take from this lot - foreach ($build_request->getPartLotsForBOMEntry($bomEntry) as $lot) { + foreach ($assemblyBuildRequest->getPartLotsForBOMEntry($bomEntry) as $lot) { $form->add('lot_' . $lot->getID(), SIUnitType::class, [ 'label' => false, 'measurement_unit' => $bomEntry->getPart()->getPartUnit(), - 'max' => min($build_request->getNeededAmountForBOMEntry($bomEntry), $lot->getAmount()), + 'max' => min($assemblyBuildRequest->getNeededAmountForBOMEntry($bomEntry), $lot->getAmount()), 'disabled' => !$this->security->isGranted('withdraw', $lot), ]); } diff --git a/src/Form/ProjectSystem/ProjectAddPartsType.php b/src/Form/ProjectSystem/ProjectAddPartsType.php index c5dbe99f..61f72c41 100644 --- a/src/Form/ProjectSystem/ProjectAddPartsType.php +++ b/src/Form/ProjectSystem/ProjectAddPartsType.php @@ -59,7 +59,6 @@ class ProjectAddPartsType extends AbstractType ], 'constraints' => [ new UniqueObjectCollection(message: 'project.bom_entry.part_already_in_bom', fields: ['part']), - new UniqueObjectCollection(message: 'project.bom_entry.assembly_already_in_bom', fields: ['assembly']), new UniqueObjectCollection(message: 'project.bom_entry.name_already_in_bom', fields: ['name']), ] ]); diff --git a/src/Form/ProjectSystem/ProjectBOMEntryType.php b/src/Form/ProjectSystem/ProjectBOMEntryType.php index de8eb789..44850c30 100644 --- a/src/Form/ProjectSystem/ProjectBOMEntryType.php +++ b/src/Form/ProjectSystem/ProjectBOMEntryType.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace App\Form\ProjectSystem; use App\Entity\ProjectSystem\ProjectBOMEntry; -use App\Form\Type\AssemblySelectType; use App\Form\Type\BigDecimalNumberType; use App\Form\Type\CurrencyEntityType; use App\Form\Type\PartSelectType; @@ -39,10 +38,6 @@ class ProjectBOMEntryType extends AbstractType 'label' => 'project.bom.part', 'required' => false, ]) - ->add('assembly', AssemblySelectType::class, [ - 'label' => 'project.bom.assembly', - 'required' => false, - ]) ->add('name', TextType::class, [ 'label' => 'project.bom.name', 'required' => false, diff --git a/src/Form/ProjectSystem/ProjectBuildType.php b/src/Form/ProjectSystem/ProjectBuildType.php index d0d4e343..b13dd12f 100644 --- a/src/Form/ProjectSystem/ProjectBuildType.php +++ b/src/Form/ProjectSystem/ProjectBuildType.php @@ -22,7 +22,6 @@ declare(strict_types=1); */ namespace App\Form\ProjectSystem; -use App\Helpers\Assemblies\AssemblyBuildRequest; use Symfony\Bundle\SecurityBundle\Security; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; @@ -39,11 +38,10 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Contracts\Translation\TranslatorInterface; class ProjectBuildType extends AbstractType implements DataMapperInterface { - public function __construct(private readonly Security $security, private readonly TranslatorInterface $translator) + public function __construct(private readonly Security $security) { } @@ -113,25 +111,6 @@ class ProjectBuildType extends AbstractType implements DataMapperInterface ]); } } - - foreach ($projectBuildRequest->getAssemblyBomEntries() as $bomEntry) { - $assemblyBuildRequest = new AssemblyBuildRequest($bomEntry->getAssembly(), $projectBuildRequest->getNumberOfBuilds()); - - //Add fields for assembly bom entries - foreach ($assemblyBuildRequest->getPartBomEntries() as $partBomEntry) { - foreach ($assemblyBuildRequest->getPartLotsForBOMEntry($partBomEntry) as $lot) { - $form->add('lot_' . $lot->getID(), SIUnitType::class, [ - 'label' => $this->translator->trans('project.build.builds_part_lot_label', [ - '%name%' => $partBomEntry->getPart()->getName(), - '%quantity%' => $partBomEntry->getQuantity() * $projectBuildRequest->getNumberOfBuilds() - ]), - 'measurement_unit' => $partBomEntry->getPart()->getPartUnit(), - 'max' => min($assemblyBuildRequest->getNeededAmountForBOMEntry($partBomEntry), $lot->getAmount()), - 'disabled' => !$this->security->isGranted('withdraw', $lot), - ]); - } - } - } }); } diff --git a/src/Form/Type/AssemblySelectType.php b/src/Form/Type/ProjectSelectType.php similarity index 82% rename from src/Form/Type/AssemblySelectType.php rename to src/Form/Type/ProjectSelectType.php index 10e858f2..18a10c08 100644 --- a/src/Form/Type/AssemblySelectType.php +++ b/src/Form/Type/ProjectSelectType.php @@ -4,9 +4,9 @@ declare(strict_types=1); namespace App\Form\Type; -use App\Entity\AssemblySystem\Assembly; use App\Entity\Attachments\Attachment; -use App\Services\Attachments\AssemblyPreviewGenerator; +use App\Entity\ProjectSystem\Project; +use App\Services\Attachments\ProjectPreviewGenerator; use App\Services\Attachments\AttachmentURLGenerator; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bridge\Doctrine\Form\Type\EntityType; @@ -20,9 +20,9 @@ use Symfony\Component\Form\FormEvents; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -class AssemblySelectType extends AbstractType implements DataMapperInterface +class ProjectSelectType extends AbstractType implements DataMapperInterface { - public function __construct(private readonly UrlGeneratorInterface $urlGenerator, private readonly EntityManagerInterface $em, private readonly AssemblyPreviewGenerator $previewGenerator, private readonly AttachmentURLGenerator $attachmentURLGenerator) + public function __construct(private readonly UrlGeneratorInterface $urlGenerator, private readonly EntityManagerInterface $em, private readonly ProjectPreviewGenerator $previewGenerator, private readonly AttachmentURLGenerator $attachmentURLGenerator) { } @@ -69,28 +69,28 @@ class AssemblySelectType extends AbstractType implements DataMapperInterface public function configureOptions(OptionsResolver $resolver): void { $resolver->setDefaults([ - 'class' => Assembly::class, + 'class' => Project::class, 'choice_label' => 'name', 'compound' => true, 'error_bubbling' => false, ]); - error_log($this->urlGenerator->generate('typeahead_assemblies', ['query' => '__QUERY__'])); + error_log($this->urlGenerator->generate('typeahead_projects', ['query' => '__QUERY__'])); $resolver->setDefaults([ 'attr' => [ - 'data-controller' => 'elements--assembly-select', - 'data-autocomplete' => $this->urlGenerator->generate('typeahead_assemblies', ['query' => '__QUERY__']), + 'data-controller' => 'elements--project-select', + 'data-autocomplete' => $this->urlGenerator->generate('typeahead_projects', ['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) { + 'choice_attr' => ChoiceList::attr($this, function (?Project $project) { + if($project instanceof Project) { //Determine the picture to show: - $preview_attachment = $this->previewGenerator->getTablePreviewAttachment($assembly); + $preview_attachment = $this->previewGenerator->getTablePreviewAttachment($project); if ($preview_attachment instanceof Attachment) { $preview_url = $this->attachmentURLGenerator->getThumbnailURL($preview_attachment, 'thumbnail_sm'); @@ -99,8 +99,8 @@ class AssemblySelectType extends AbstractType implements DataMapperInterface } } - return $assembly instanceof Assembly ? [ - 'data-description' => $assembly->getDescription() ? mb_strimwidth($assembly->getDescription(), 0, 127, '...') : '', + return $project instanceof Project ? [ + 'data-description' => $project->getDescription() ? mb_strimwidth($project->getDescription(), 0, 127, '...') : '', 'data-category' => '', 'data-footprint' => '', 'data-image' => $preview_url, diff --git a/src/Helpers/Projects/ProjectBuildRequest.php b/src/Helpers/Projects/ProjectBuildRequest.php index 3254565a..24bb5eb7 100644 --- a/src/Helpers/Projects/ProjectBuildRequest.php +++ b/src/Helpers/Projects/ProjectBuildRequest.php @@ -22,13 +22,10 @@ declare(strict_types=1); */ namespace App\Helpers\Projects; -use App\Entity\AssemblySystem\Assembly; -use App\Entity\AssemblySystem\AssemblyBOMEntry; use App\Entity\Parts\Part; use App\Entity\Parts\PartLot; use App\Entity\ProjectSystem\Project; use App\Entity\ProjectSystem\ProjectBOMEntry; -use App\Helpers\Assemblies\AssemblyBuildRequest; use App\Validator\Constraints\ProjectSystem\ValidProjectBuildRequest; /** @@ -82,7 +79,7 @@ final class ProjectBuildRequest //Completely reset the array $this->withdraw_amounts = []; - //Now create an array for each part BOM entry + //Now create an array for each BOM entry foreach ($this->getPartBomEntries() as $bom_entry) { $remaining_amount = $this->getNeededAmountForBOMEntry($bom_entry); foreach($this->getPartLotsForBOMEntry($bom_entry) as $lot) { @@ -91,21 +88,6 @@ final class ProjectBuildRequest $remaining_amount -= max(0, $this->withdraw_amounts[$lot->getID()]); } } - - //Now create an array for each assembly BOM entry - foreach ($this->getAssemblyBomEntries() as $assemblyBomEntry) { - $assemblyBuildRequest = new AssemblyBuildRequest($assemblyBomEntry->getAssembly(), $this->number_of_builds); - - //Add fields for assembly bom entries - foreach ($assemblyBuildRequest->getPartBomEntries() as $partBomEntry) { - $remaining_amount = $assemblyBuildRequest->getNeededAmountForBOMEntry($partBomEntry) * $assemblyBomEntry->getQuantity(); - - foreach ($assemblyBuildRequest->getPartLotsForBOMEntry($partBomEntry) as $lot) { - $this->withdraw_amounts[$lot->getID()] = min($remaining_amount, $lot->getAmount()); - $remaining_amount -= max(0, $this->withdraw_amounts[$lot->getID()]); - } - } - } } /** @@ -248,77 +230,12 @@ final class ProjectBuildRequest { $this->ensureBOMEntryValid($projectBOMEntry); - if (!$projectBOMEntry->getPart() instanceof Part && !$projectBOMEntry->getAssembly() instanceof Assembly) { + if (!$projectBOMEntry->getPart() instanceof Part) { return null; } //Filter out all lots which have unknown instock - if ($projectBOMEntry->getPart() instanceof Part) { - return $projectBOMEntry->getPart()->getPartLots()->filter(fn (PartLot $lot) => !$lot->isInstockUnknown())->toArray(); - } elseif ($projectBOMEntry->getAssembly() instanceof Assembly) { - $assemblyBuildRequest = new AssemblyBuildRequest($projectBOMEntry->getAssembly(), $this->number_of_builds); - - //Add fields for assembly bom entries - $result = []; - foreach ($assemblyBuildRequest->getPartBomEntries() as $assemblyBOMEntry) { - $tmp = $assemblyBOMEntry->getPart()->getPartLots()->filter(fn (PartLot $lot) => !$lot->isInstockUnknown())->toArray(); - $result = array_merge($result, $tmp); - } - - return $result; - } - - return null; - } - - /** - * Returns all available assembly BOM-entries with no part assigned. - * @return AssemblyBOMEntry[]|null Returns null if no entries found - */ - public function getAssemblyBomEntriesWithoutPart(ProjectBOMEntry $projectBOMEntry): ?array - { - $this->ensureBOMEntryValid($projectBOMEntry); - - if (!$projectBOMEntry->getAssembly() instanceof Assembly) { - return null; - } - - $assemblyBuildRequest = new AssemblyBuildRequest($projectBOMEntry->getAssembly(), $this->number_of_builds); - - $result = []; - - foreach ($assemblyBuildRequest->getBomEntries() as $assemblyBOMEntry) { - if ($assemblyBOMEntry->getPart() === null) { - $result[] = $assemblyBOMEntry; - } - } - - return count($result) > 0 ? $result : null; - } - - /** - * Returns all available assembly BOM-entries with no part assigned. - * @return AssemblyBOMEntry[]|null Returns null if no entries found - */ - public function getAssemblyBomEntriesWithPartNoStock(ProjectBOMEntry $projectBOMEntry): ?array - { - $this->ensureBOMEntryValid($projectBOMEntry); - - if (!$projectBOMEntry->getAssembly() instanceof Assembly) { - return null; - } - - $assemblyBuildRequest = new AssemblyBuildRequest($projectBOMEntry->getAssembly(), $this->number_of_builds); - - $result = []; - - foreach ($assemblyBuildRequest->getBomEntries() as $assemblyBOMEntry) { - if ($assemblyBOMEntry->getPart() instanceof Part && $assemblyBOMEntry->getPart()->getPartLots()->filter(fn (PartLot $lot) => !$lot->isInstockUnknown())->count() === 0) { - $result[] = $assemblyBOMEntry; - } - } - - return count($result) > 0 ? $result : null; + return $projectBOMEntry->getPart()->getPartLots()->filter(fn (PartLot $lot) => !$lot->isInstockUnknown())->toArray(); } /** @@ -349,15 +266,6 @@ final class ProjectBuildRequest return $this->project->getBomEntries()->filter(fn(ProjectBOMEntry $entry) => $entry->isPartBomEntry())->toArray(); } - /** - * Returns the all assembly bom entries that have to be built. - * @return ProjectBOMEntry[] - */ - public function getAssemblyBomEntries(): array - { - return $this->project->getBomEntries()->filter(fn(ProjectBOMEntry $entry) => $entry->isAssemblyBomEntry())->toArray(); - } - /** * Returns which project should be build */ diff --git a/src/Repository/AssemblyRepository.php b/src/Repository/AssemblyRepository.php deleted file mode 100644 index 031e6e82..00000000 --- a/src/Repository/AssemblyRepository.php +++ /dev/null @@ -1,69 +0,0 @@ -. - */ - -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/Repository/Parts/DeviceRepository.php b/src/Repository/Parts/DeviceRepository.php index 442c91e5..c714523a 100644 --- a/src/Repository/Parts/DeviceRepository.php +++ b/src/Repository/Parts/DeviceRepository.php @@ -51,4 +51,22 @@ class DeviceRepository extends StructuralDBElementRepository //Prevent user from deleting devices, to not accidentally remove filled devices from old versions return 1; } + + /** + * @return Project[] + */ + public function autocompleteSearch(string $query, int $max_limits = 50): array + { + $qb = $this->createQueryBuilder('project'); + $qb->select('project') + ->where('ILIKE(project.name, :query) = TRUE') + ->orWhere('ILIKE(project.description, :query) = TRUE'); + + $qb->setParameter('query', '%'.$query.'%'); + + $qb->setMaxResults($max_limits); + $qb->orderBy('NATSORT(project.name)', 'ASC'); + + return $qb->getQuery()->getResult(); + } } diff --git a/src/Services/AssemblySystem/AssemblyBuildHelper.php b/src/Services/AssemblySystem/AssemblyBuildHelper.php index 8c95a4b6..3fb3221a 100644 --- a/src/Services/AssemblySystem/AssemblyBuildHelper.php +++ b/src/Services/AssemblySystem/AssemblyBuildHelper.php @@ -27,14 +27,17 @@ use App\Entity\AssemblySystem\AssemblyBOMEntry; use App\Entity\Parts\Part; use App\Helpers\Assemblies\AssemblyBuildRequest; use App\Services\Parts\PartLotWithdrawAddHelper; +use App\Services\ProjectSystem\ProjectBuildHelper; /** * @see \App\Tests\Services\AssemblySystem\AssemblyBuildHelperTest */ class AssemblyBuildHelper { - public function __construct(private readonly PartLotWithdrawAddHelper $withdraw_add_helper) - { + public function __construct( + private readonly PartLotWithdrawAddHelper $withdraw_add_helper, + private readonly ProjectBuildHelper $projectBuildHelper + ) { } /** @@ -66,12 +69,16 @@ class AssemblyBuildHelper $maximum_buildable_count = PHP_INT_MAX; foreach ($assembly->getBomEntries() as $bom_entry) { //Skip BOM entries without a part (as we can not determine that) - if (!$bom_entry->isPartBomEntry()) { + if (!$bom_entry->isPartBomEntry() && $bom_entry->getProject() === null) { continue; } - //The maximum buildable count for the whole assembly is the minimum of all BOM entries - $maximum_buildable_count = min($maximum_buildable_count, $this->getMaximumBuildableCountForBOMEntry($bom_entry)); + //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())); + } } return $maximum_buildable_count; @@ -97,7 +104,7 @@ class AssemblyBuildHelper } /** - * Returns the assembly BOM entries for which parts are missing in the stock for the given number of builds + * Returns the project 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? * @return AssemblyBOMEntry[] @@ -108,24 +115,29 @@ class AssemblyBuildHelper throw new \InvalidArgumentException('The number of builds must be greater than 0!'); } - $non_buildable_entries = []; + $nonBuildableEntries = []; foreach ($assembly->getBomEntries() as $bomEntry) { $part = $bomEntry->getPart(); //Skip BOM entries without a part (as we can not determine that) - if (!$part instanceof Part) { + if (!$part instanceof Part && $bomEntry->getAssembly() === null) { continue; } - $amount_sum = $part->getAmountSum(); + if ($bomEntry->getPart() !== null) { + $amount_sum = $part->getAmountSum(); - if ($amount_sum < $bomEntry->getQuantity() * $number_of_builds) { - $non_buildable_entries[] = $bomEntry; + if ($amount_sum < $bomEntry->getQuantity() * $number_of_builds) { + $nonBuildableEntries[] = $bomEntry; + } + } elseif ($bomEntry->getAssembly() !== null) { + $nonBuildableAssemblyEntries = $this->projectBuildHelper->getNonBuildableProjectBomEntries($bomEntry->getProject(), $number_of_builds); + $nonBuildableEntries = array_merge($nonBuildableEntries, $nonBuildableAssemblyEntries); } } - return $non_buildable_entries; + return $nonBuildableEntries; } /** diff --git a/src/Services/Attachments/AssemblyPreviewGenerator.php b/src/Services/Attachments/ProjectPreviewGenerator.php similarity index 75% rename from src/Services/Attachments/AssemblyPreviewGenerator.php rename to src/Services/Attachments/ProjectPreviewGenerator.php index 9ecbbd07..9929dbd3 100644 --- a/src/Services/Attachments/AssemblyPreviewGenerator.php +++ b/src/Services/Attachments/ProjectPreviewGenerator.php @@ -22,38 +22,38 @@ declare(strict_types=1); namespace App\Services\Attachments; -use App\Entity\AssemblySystem\Assembly; use App\Entity\Attachments\Attachment; +use App\Entity\ProjectSystem\Project; -class AssemblyPreviewGenerator +class ProjectPreviewGenerator { public function __construct(protected AttachmentManager $attachmentHelper) { } /** - * Returns a list of attachments that can be used for previewing the assembly ordered by priority. + * Returns a list of attachments that can be used for previewing the project ordered by priority. * - * @param Assembly $assembly the assembly for which the attachments should be determined + * @param Project $project the project for which the attachments should be determined * * @return (Attachment|null)[] * * @psalm-return list */ - public function getPreviewAttachments(Assembly $assembly): array + public function getPreviewAttachments(Project $project): array { $list = []; //Master attachment has top priority - $attachment = $assembly->getMasterPictureAttachment(); + $attachment = $project->getMasterPictureAttachment(); if ($this->isAttachmentValidPicture($attachment)) { $list[] = $attachment; } - //Then comes the other images of the assembly - foreach ($assembly->getAttachments() as $attachment) { + //Then comes the other images of the project + foreach ($project->getAttachments() as $attachment) { //Dont show the master attachment twice - if ($this->isAttachmentValidPicture($attachment) && $attachment !== $assembly->getMasterPictureAttachment()) { + if ($this->isAttachmentValidPicture($attachment) && $attachment !== $project->getMasterPictureAttachment()) { $list[] = $attachment; } } @@ -62,14 +62,14 @@ class AssemblyPreviewGenerator } /** - * Determines what attachment should be used for previewing a assembly (especially in assembly table). + * Determines what attachment should be used for previewing a project (especially in project 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 + * @param Project $project The project for which the attachment should be determined */ - public function getTablePreviewAttachment(Assembly $assembly): ?Attachment + public function getTablePreviewAttachment(Project $project): ?Attachment { - $attachment = $assembly->getMasterPictureAttachment(); + $attachment = $project->getMasterPictureAttachment(); if ($this->isAttachmentValidPicture($attachment)) { return $attachment; } diff --git a/src/Services/ProjectSystem/ProjectBuildHelper.php b/src/Services/ProjectSystem/ProjectBuildHelper.php index d7ba9e6c..269c7e4c 100644 --- a/src/Services/ProjectSystem/ProjectBuildHelper.php +++ b/src/Services/ProjectSystem/ProjectBuildHelper.php @@ -22,13 +22,10 @@ declare(strict_types=1); */ namespace App\Services\ProjectSystem; -use App\Entity\AssemblySystem\AssemblyBOMEntry; use App\Entity\Parts\Part; use App\Entity\ProjectSystem\Project; use App\Entity\ProjectSystem\ProjectBOMEntry; -use App\Helpers\Assemblies\AssemblyBuildRequest; use App\Helpers\Projects\ProjectBuildRequest; -use App\Services\AssemblySystem\AssemblyBuildHelper; use App\Services\Parts\PartLotWithdrawAddHelper; /** @@ -36,10 +33,8 @@ use App\Services\Parts\PartLotWithdrawAddHelper; */ class ProjectBuildHelper { - public function __construct( - private readonly PartLotWithdrawAddHelper $withdrawAddHelper, - private readonly AssemblyBuildHelper $assemblyBuildHelper - ) { + public function __construct(private readonly PartLotWithdrawAddHelper $withdraw_add_helper) + { } /** @@ -71,16 +66,12 @@ class ProjectBuildHelper $maximum_buildable_count = PHP_INT_MAX; foreach ($project->getBomEntries() as $bom_entry) { //Skip BOM entries without a part (as we can not determine that) - if (!$bom_entry->isPartBomEntry() && $bom_entry->getAssembly() === null) { + if (!$bom_entry->isPartBomEntry()) { continue; } //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->getAssembly() !== null) { - $maximum_buildable_count = min($maximum_buildable_count, $this->assemblyBuildHelper->getMaximumBuildableCount($bom_entry->getAssembly())); - } + $maximum_buildable_count = min($maximum_buildable_count, $this->getMaximumBuildableCountForBOMEntry($bom_entry)); } return $maximum_buildable_count; @@ -106,10 +97,10 @@ class ProjectBuildHelper } /** - * Returns the project or assembly BOM entries for which parts are missing in the stock for the given number of builds + * Returns the project BOM entries for which parts are missing in the stock for the given number of builds * @param Project $project The project for which the BOM entries should be checked * @param int $number_of_builds How often should the project be build? - * @return ProjectBOMEntry[]|AssemblyBOMEntry[] + * @return ProjectBOMEntry[] */ public function getNonBuildableProjectBomEntries(Project $project, int $number_of_builds = 1): array { @@ -117,29 +108,24 @@ class ProjectBuildHelper throw new \InvalidArgumentException('The number of builds must be greater than 0!'); } - $nonBuildableEntries = []; + $non_buildable_entries = []; foreach ($project->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) { continue; } - if ($bomEntry->getPart() !== null) { - $amount_sum = $part->getAmountSum(); + $amount_sum = $part->getAmountSum(); - if ($amount_sum < $bomEntry->getQuantity() * $number_of_builds) { - $nonBuildableEntries[] = $bomEntry; - } - } elseif ($bomEntry->getAssembly() !== null) { - $nonBuildableAssemblyEntries = $this->assemblyBuildHelper->getNonBuildableAssemblyBomEntries($bomEntry->getAssembly(), $number_of_builds); - $nonBuildableEntries = array_merge($nonBuildableEntries, $nonBuildableAssemblyEntries); + if ($amount_sum < $bomEntry->getQuantity() * $number_of_builds) { + $non_buildable_entries[] = $bomEntry; } } - return $nonBuildableEntries; + return $non_buildable_entries; } /** @@ -147,37 +133,22 @@ class ProjectBuildHelper * The ProjectBuildRequest has to be validated before!! * You have to flush changes to DB afterward */ - public function doBuild(ProjectBuildRequest $projectBuildRequest): void + public function doBuild(ProjectBuildRequest $buildRequest): void { - $message = $projectBuildRequest->getComment(); - $message .= ' (Project build: '.$projectBuildRequest->getProject()->getName().')'; + $message = $buildRequest->getComment(); + $message .= ' (Project build: '.$buildRequest->getProject()->getName().')'; - foreach ($projectBuildRequest->getPartBomEntries() as $bomEntry) { - foreach ($projectBuildRequest->getPartLotsForBOMEntry($bomEntry) as $partLot) { - $amount = $projectBuildRequest->getLotWithdrawAmount($partLot); + foreach ($buildRequest->getPartBomEntries() as $bom_entry) { + foreach ($buildRequest->getPartLotsForBOMEntry($bom_entry) as $part_lot) { + $amount = $buildRequest->getLotWithdrawAmount($part_lot); if ($amount > 0) { - $this->withdrawAddHelper->withdraw($partLot, $amount, $message); + $this->withdraw_add_helper->withdraw($part_lot, $amount, $message); } } } - foreach ($projectBuildRequest->getAssemblyBomEntries() as $bomEntry) { - $assemblyBuildRequest = new AssemblyBuildRequest($bomEntry->getAssembly(), $projectBuildRequest->getNumberOfBuilds()); - - //Add fields for assembly bom entries - foreach ($assemblyBuildRequest->getPartBomEntries() as $partBomEntry) { - foreach ($assemblyBuildRequest->getPartLotsForBOMEntry($partBomEntry) as $partLot) { - //Read amount from build configuration of the projectBuildRequest - $amount = $projectBuildRequest->getLotWithdrawAmount($partLot); - if ($amount > 0) { - $this->withdrawAddHelper->withdraw($partLot, $amount, $message); - } - } - } - } - - if ($projectBuildRequest->getAddBuildsToBuildsPart()) { - $this->withdrawAddHelper->add($projectBuildRequest->getBuildsPartLot(), $projectBuildRequest->getNumberOfBuilds(), $message); + if ($buildRequest->getAddBuildsToBuildsPart()) { + $this->withdraw_add_helper->add($buildRequest->getBuildsPartLot(), $buildRequest->getNumberOfBuilds(), $message); } } } diff --git a/src/Twig/AssemblyTwigExtension.php b/src/Twig/AssemblyTwigExtension.php index 3430f7d1..d43c201e 100644 --- a/src/Twig/AssemblyTwigExtension.php +++ b/src/Twig/AssemblyTwigExtension.php @@ -10,14 +10,14 @@ class AssemblyTwigExtension extends AbstractExtension public function getFunctions(): array { return [ - new TwigFunction('has_assembly', [$this, 'hasAssembly']), + new TwigFunction('has_project', [$this, 'hasProject']), ]; } - public function hasAssembly(array $bomEntries): bool + public function hasProject(array $bomEntries): bool { foreach ($bomEntries as $entry) { - if ($entry->getAssembly() !== null) { + if ($entry->getProject() !== null) { return true; } } diff --git a/templates/form/collection_types_layout.html.twig b/templates/form/collection_types_layout.html.twig index 552fd542..96b71bf0 100644 --- a/templates/form/collection_types_layout.html.twig +++ b/templates/form/collection_types_layout.html.twig @@ -6,7 +6,7 @@ {# expand button #} {% trans %}project.bom.quantity{% endtrans %} - {% trans %}project.bom.partOrAssembly{% endtrans %} + {% trans %}project.bom.part{% endtrans %} {% trans %}project.bom.name{% endtrans %} {# Remove button #} @@ -41,21 +41,9 @@ {{ form_widget(form.quantity) }} {{ form_errors(form.quantity) }} - - {{ form_row(form.part) }} + + {{ form_widget(form.part) }} {{ form_errors(form.part) }} - - {% if form.vars.value is not null and form.vars.value.project is not null %} - {% set hasAssembly = false %} - {% if is_granted("@assemblies.read") or has_assembly(form.vars.value.project.bomEntries.toArray) %} -
- {{ form_widget(form.assembly) }} - {{ form_errors(form.assembly) }} - {% endif %} - {% elseif is_granted("@assemblies.read") %} - {{ form_widget(form.assembly) }} - {{ form_errors(form.assembly) }} - {% endif %} {{ form_widget(form.name) }} diff --git a/templates/form/collection_types_layout_assembly.html.twig b/templates/form/collection_types_layout_assembly.html.twig index c5acebda..24964801 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.part{% endtrans %} + {% trans %}assembly.bom.partOrProject{% endtrans %} {% trans %}assembly.bom.name{% endtrans %} {# Remove button #} @@ -41,9 +41,21 @@ {{ form_widget(form.quantity) }} {{ form_errors(form.quantity) }} - - {{ form_widget(form.part) }} + + {{ 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.name) }} diff --git a/templates/projects/build/_form.html.twig b/templates/projects/build/_form.html.twig index 340b8670..b25ca81e 100644 --- a/templates/projects/build/_form.html.twig +++ b/templates/projects/build/_form.html.twig @@ -27,9 +27,7 @@ {% if bom_entry.part %} - {{ 'projects.build.form.part'|trans({'%name%': bom_entry.part.name}) }} {% if bom_entry.name %}({{ bom_entry.name }}){% endif %} - {% elseif bom_entry.assembly %} - {{ 'projects.build.form.assembly'|trans({'%name%': bom_entry.assembly.name}) }} {% if bom_entry.name %}({{ bom_entry.name }}){% endif %} + {{ bom_entry.part.name }} {% if bom_entry.name %}({{ bom_entry.name }}){% endif %} {% else %} {{ bom_entry.name }} {% endif %} @@ -47,29 +45,9 @@ {% set lots = build_request.partLotsForBOMEntry(bom_entry) %} - {% set assemblyBomEntriesWithoutPart = build_request.assemblyBomEntriesWithoutPart(bom_entry) %} - {% set assemblyBomEntriesWithPartNoStock = build_request.assemblyBomEntriesWithPartNoStock(bom_entry) %} {% if lots is not null %} - {% set previousLabel = null %} - {% for lot in lots %} {# @var lot \App\Entity\Parts\PartLot #} - - {% set label = '' %} - {% if form["lot_"~lot.id].vars.label is defined and form["lot_"~lot.id].vars.label is not empty %} - {% set label = form["lot_"~lot.id].vars.label %} - {% endif %} - - {% if label != '' and (previousLabel is null or label != previousLabel) %} -
- -
- {% endif %} - - {% set previousLabel = label %} -
-
+
/ {{ lot.amount | format_amount(lot.part.partUnit) }} {% trans %}project.builds.stocked{% endtrans %}
{% endfor %} {% endif %} - {% if assemblyBomEntriesWithoutPart is not null %} - {% for bomEntryWithoutPart in assemblyBomEntriesWithoutPart %} -
- -
-
- / {% trans %}project.builds.no_stock{% endtrans %} -
-
- {% endfor %} - {% endif %} - {% if assemblyBomEntriesWithPartNoStock is not null %} - {% for bomEntryWithPartNoStock in assemblyBomEntriesWithPartNoStock %} -
-
- -
-
- / {% trans %}project.builds.no_stock{% endtrans %} -
-
-
- {% endfor %} - {% endif %} {% endfor %} @@ -126,7 +75,7 @@ {{ form_row(form.comment) }}
-{{ form_row(form.dontCheckQuantity) }} + {{ form_row(form.dontCheckQuantity) }}
{{ form_row(form.addBuildsToBuildsPart) }} diff --git a/translations/messages.cs.xlf b/translations/messages.cs.xlf index c1d1b517..c4021c2c 100644 --- a/translations/messages.cs.xlf +++ b/translations/messages.cs.xlf @@ -4741,19 +4741,7 @@ Pokud jste to provedli nesprávně nebo pokud počítač již není důvěryhodn Název - - - project.bom.assembly - Sestava - - - - - project.bom.partOrAssembly - Výběr - - - + Part-DB1\src\DataTables\PartsDataTable.php:178 Part-DB1\src\DataTables\PartsDataTable.php:126 @@ -9798,18 +9786,6 @@ Element 3 Díl - - - project.bom.assembly - Baugruppe - - - - - project.bom.partOrAssembly - Auswahl - - project.bom.add_entry @@ -10200,12 +10176,6 @@ Element 3 k dispozici - - - project.builds.no_stock - není uveden žádný sklad - - project.builds.needed @@ -10278,12 +10248,6 @@ Element 3 Cílový inventář - - - project.build.builds_part_lot_label - %name% (%quantity% požadováno) - - project.builds.number_of_builds @@ -13557,10 +13521,10 @@ 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 + %value% (Projekt) @@ -13791,12 +13755,24 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz potřebné + + + assembly.bom.delete.confirm + Opravdu chcete tuto položku smazat? + + assembly.add_parts_to_assembly Přidat součásti do sestavy + + + assembly.bom.project + Projekt + + assembly.bom.name @@ -13833,9 +13809,9 @@ 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.part + assembly.bom.partOrProject Součást @@ -14282,41 +14258,5 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz - - - typeahead.parts.part.name - %name% (součást) - - - - - typeahead.parts.assembly.name - %name% (sestava) - - - - - projects.build.form.part - Součást "%name%" - - - - - projects.build.form.assembly - Sestava "%name%" - - - - - projects.build.form.assembly.bom.entry - %name% (potřebné množství: %quantity%) - - - - - projects.build.form.assembly.bom.entry.no.stock - není skladem - - diff --git a/translations/messages.da.xlf b/translations/messages.da.xlf index 4322fd9f..24d42ac6 100644 --- a/translations/messages.da.xlf +++ b/translations/messages.da.xlf @@ -4748,18 +4748,6 @@ Bemærk også, at uden to-faktor-godkendelse er din konto ikke længere så godt Navn - - - project.bom.assembly - Montering - - - - - project.bom.partOrAssembly - Valg - - Part-DB1\src\DataTables\PartsDataTable.php:178 @@ -9824,18 +9812,6 @@ Element 3 Komponent - - - project.bom.assembly - Baugruppe - - - - - project.bom.partOrAssembly - Auswahl - - project.bom.add_entry @@ -10226,12 +10202,6 @@ Element 3 På lager - - - project.builds.no_stock - intet lager angivet - - project.builds.needed @@ -10304,12 +10274,6 @@ Element 3 Mål mængde - - - project.build.builds_part_lot_label - %name% (%quantity% påkrævet) - - project.builds.number_of_builds @@ -12274,10 +12238,10 @@ Bemærk venligst, at du ikke kan kopiere fra deaktiveret bruger. Hvis du prøver %value% (Del) - + - part.table.name.value.for_assembly - %value% (Samlingsenhed) + part.table.name.value.for_project + %value% (Projekt) @@ -12508,12 +12472,24 @@ Bemærk venligst, at du ikke kan kopiere fra deaktiveret bruger. Hvis du prøver nødvendig + + + assembly.bom.delete.confirm + Vil du virkelig slette denne post? + + assembly.add_parts_to_assembly Tilføj dele til samlingen + + + assembly.bom.project + Projekt + + assembly.bom.name @@ -12550,9 +12526,9 @@ Bemærk venligst, at du ikke kan kopiere fra deaktiveret bruger. Hvis du prøver Importer dele til samling - + - assembly.bom.part + assembly.bom.partOrProject Del @@ -12999,41 +12975,5 @@ Bemærk venligst, at du ikke kan kopiere fra deaktiveret bruger. Hvis du prøver - - - typeahead.parts.part.name - %name% (del) - - - - - typeahead.parts.assembly.name - %name% (samling) - - - - - projects.build.form.part - Del "%name%" - - - - - projects.build.form.assembly - Samling "%name%" - - - - - projects.build.form.assembly.bom.entry - %name% (%quantity% nødvendig) - - - - - projects.build.form.assembly.bom.entry.no.stock - ikke på lager - - diff --git a/translations/messages.de.xlf b/translations/messages.de.xlf index 870446fe..34879612 100644 --- a/translations/messages.de.xlf +++ b/translations/messages.de.xlf @@ -4746,10 +4746,10 @@ 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 + %value% (Projekt) @@ -9872,18 +9872,6 @@ Element 1 -> Element 1.2 Bauteil - - - project.bom.assembly - Baugruppe - - - - - project.bom.partOrAssembly - Auswahl - - project.bom.add_entry @@ -10274,12 +10262,6 @@ Element 1 -> Element 1.2 vorhanden - - - project.builds.no_stock - kein Lager angegeben - - project.builds.needed @@ -10352,12 +10334,6 @@ Element 1 -> Element 1.2 Ziel-Bestand - - - project.build.builds_part_lot_label - %name% (%quantity% benötigt) - - project.builds.number_of_builds @@ -13229,12 +13205,24 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön benötigt + + + assembly.bom.delete.confirm + Wollen sie diesen Eintrag wirklich löschen? + + assembly.add_parts_to_assembly Bauteile zur Baugruppe hinzufügen + + + assembly.bom.project + Projekt + + assembly.bom.name @@ -13271,10 +13259,10 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön Importiere Parts für Baugruppe - + - assembly.bom.part - Bauteil + assembly.bom.partOrProject + Bauteil oder Projekt @@ -13720,42 +13708,6 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön - - - typeahead.parts.part.name - %name% (Bauteil) - - - - - typeahead.parts.assembly.name - %name% (Baugruppe) - - - - - projects.build.form.part - Bauteil "%name%" - - - - - projects.build.form.assembly - Baugruppe "%name%" - - - - - projects.build.form.assembly.bom.entry - %name% (%quantity% benötigt) - - - - - projects.build.form.assembly.bom.entry.no.stock - nicht auf Lager - - part.table.actions.error diff --git a/translations/messages.el.xlf b/translations/messages.el.xlf index c97f0630..a6dda3d4 100644 --- a/translations/messages.el.xlf +++ b/translations/messages.el.xlf @@ -1541,22 +1541,10 @@ %value% (Μέρος) - + - part.table.name.value.for_assembly - %value% (Συναρμολόγηση) - - - - - project.bom.assembly - Συναρμολόγηση - - - - - project.bom.partOrAssembly - Επιλογή + part.table.name.value.for_project + %value% (Έργο) @@ -1595,18 +1583,6 @@ Αρχειοθετήθηκε - - - project.builds.no_stock - δεν έχει καθοριστεί απόθεμα - - - - - project.build.builds_part_lot_label - %name% (%quantity% απαιτείται) - - assembly.label @@ -1835,12 +1811,24 @@ απαιτούμενο + + + assembly.bom.delete.confirm + Θέλετε πραγματικά να διαγράψετε αυτήν την εγγραφή; + + assembly.add_parts_to_assembly Προσθήκη εξαρτημάτων στη συναρμολόγηση + + + assembly.bom.project + έργο + + assembly.bom.name @@ -1877,9 +1865,9 @@ Εισαγωγή εξαρτημάτων συναρμολόγησης - + - assembly.bom.part + assembly.bom.partOrProject Εξάρτημα @@ -2326,41 +2314,5 @@ - - - typeahead.parts.part.name - %name% (Εξάρτημα) - - - - - typeahead.parts.assembly.name - %name% (Συναρμολόγηση) - - - - - projects.build.form.part - Εξάρτημα "%name%" - - - - - projects.build.form.assembly - Συναρμολόγηση "%name%" - - - - - projects.build.form.assembly.bom.entry - %name% (%quantity% απαιτείται) - - - - - projects.build.form.assembly.bom.entry.no.stock - δεν υπάρχει στο απόθεμα - - diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index e0484384..080ef02e 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -4747,10 +4747,10 @@ 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 + %value% (Project) @@ -9873,18 +9873,6 @@ Element 1 -> Element 1.2 Part - - - project.bom.assembly - Assembly - - - - - project.bom.partOrAssembly - Selection - - project.bom.add_entry @@ -10275,12 +10263,6 @@ Element 1 -> Element 1.2 stocked - - - project.builds.no_stock - no stock specified - - project.builds.needed @@ -10353,12 +10335,6 @@ Element 1 -> Element 1.2 Target lot - - - project.build.builds_part_lot_label - %name% (%quantity% needed) - - project.builds.number_of_builds @@ -13230,12 +13206,24 @@ Please note, that you can not impersonate a disabled user. If you try you will g needed + + + assembly.bom.delete.confirm + Do you really want to delete this entry? + + assembly.add_parts_to_assembly Add parts to assembly + + + assembly.bom.project + Project + + assembly.bom.name @@ -13272,9 +13260,9 @@ Please note, that you can not impersonate a disabled user. If you try you will g Import part list for assembly - + - assembly.bom.part + assembly.bom.partOrProject Part @@ -13721,42 +13709,6 @@ Please note, that you can not impersonate a disabled user. If you try you will g - - - typeahead.parts.part.name - %name% (Part) - - - - - typeahead.parts.assembly.name - %name% (Assembly) - - - - - projects.build.form.part - Part "%name%" - - - - - projects.build.form.assembly - Assembly "%name%" - - - - - projects.build.form.assembly.bom.entry - %name% (%quantity% needed) - - - - - projects.build.form.assembly.bom.entry.no.stock - not in stock - - part.table.actions.error diff --git a/translations/messages.es.xlf b/translations/messages.es.xlf index 1ee1188e..0ff8df3f 100644 --- a/translations/messages.es.xlf +++ b/translations/messages.es.xlf @@ -4746,10 +4746,10 @@ Subelementos serán desplazados hacia arriba. %value% (Componente) - + - part.table.name.value.for_assembly - %value% (Ensamblaje) + part.table.name.value.for_project + %value% (Proyecto) @@ -9816,18 +9816,6 @@ Elemento 3 Componente - - - project.bom.assembly - Baugruppe - - - - - project.bom.partOrAssembly - Auswahl - - project.bom.add_entry @@ -10218,12 +10206,6 @@ Elemento 3 Almacenado - - - project.builds.no_stock - no se ha especificado stock - - project.builds.needed @@ -10296,12 +10278,6 @@ Elemento 3 Lote objetivo - - - project.build.builds_part_lot_label - %name% (se requiere %quantity%) - - project.builds.number_of_builds @@ -12668,12 +12644,24 @@ Por favor ten en cuenta que no puedes personificar a un usuario deshabilitado. S necesario + + + assembly.bom.delete.confirm + ¿Realmente desea eliminar esta entrada? + + assembly.add_parts_to_assembly Añadir piezas al ensamblaje + + + assembly.bom.project + Proyecto + + assembly.bom.name @@ -12710,9 +12698,9 @@ Por favor ten en cuenta que no puedes personificar a un usuario deshabilitado. S Importar piezas para ensamblaje - + - assembly.bom.part + assembly.bom.partOrProject Pieza @@ -13159,41 +13147,5 @@ Por favor ten en cuenta que no puedes personificar a un usuario deshabilitado. S - - - typeahead.parts.part.name - %name% (Componente) - - - - - typeahead.parts.assembly.name - %name% (Ensamblaje) - - - - - projects.build.form.part - Componente "%name%" - - - - - projects.build.form.assembly - Ensamblaje "%name%" - - - - - projects.build.form.assembly.bom.entry - %name% (%quantity% necesario) - - - - - projects.build.form.assembly.bom.entry.no.stock - sin stock - - diff --git a/translations/messages.fr.xlf b/translations/messages.fr.xlf index 9cd29321..234dee6e 100644 --- a/translations/messages.fr.xlf +++ b/translations/messages.fr.xlf @@ -4709,11 +4709,11 @@ 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% (Assemblaggio) - + + + part.table.name.value.for_project + %value% (Projet) + @@ -9109,18 +9109,6 @@ exemple de ville Si vous avez des questions à propos de Part-DB , rendez vous sur <a href="%href%" class="link-external" target="_blank">Github</a> - - - project.bom.assembly - Assemblage - - - - - project.bom.partOrAssembly - Sélection - - assembly.edit.status @@ -9157,18 +9145,6 @@ exemple de ville Archivé - - - project.builds.no_stock - aucun stock indiqué - - - - - project.build.builds_part_lot_label - %name% (%quantity% requis) - - assembly.label @@ -9397,12 +9373,24 @@ exemple de ville nécessaire + + + assembly.bom.delete.confirm + Voulez-vous vraiment supprimer cette entrée ? + + assembly.add_parts_to_assembly Ajouter des pièces à l'assemblage + + + assembly.bom.project + Projet + + assembly.bom.name @@ -9439,9 +9427,9 @@ exemple de ville Importer des pièces pour l'assemblage - + - assembly.bom.part + assembly.bom.partOrProject Pièce @@ -9888,41 +9876,5 @@ exemple de ville - - - typeahead.parts.part.name - %name% (pièce) - - - - - typeahead.parts.assembly.name - %name% (assemblage) - - - - - projects.build.form.part - Pièce "%name%" - - - - - projects.build.form.assembly - Assemblage "%name%" - - - - - projects.build.form.assembly.bom.entry - %name% (%quantity% nécessaires) - - - - - projects.build.form.assembly.bom.entry.no.stock - Non disponible en stock - - diff --git a/translations/messages.it.xlf b/translations/messages.it.xlf index 8e39c031..57654f44 100644 --- a/translations/messages.it.xlf +++ b/translations/messages.it.xlf @@ -4748,11 +4748,11 @@ 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 + %value% (Progetto) + @@ -9818,18 +9818,6 @@ Element 3 Componente - - - project.bom.assembly - Assemblaggio - - - - - project.bom.partOrAssembly - Selezione - - project.bom.add_entry @@ -10220,12 +10208,6 @@ Element 3 a magazzino - - - project.builds.no_stock - nessuna scorta specificata - - project.builds.needed @@ -10298,12 +10280,6 @@ Element 3 Lotto target - - - project.build.builds_part_lot_label - %name% (%quantity% richiesti) - - project.builds.number_of_builds @@ -12646,12 +12622,24 @@ Notare che non è possibile impersonare un utente disattivato. Quando si prova a necessari + + + assembly.bom.delete.confirm + Vuoi davvero eliminare questa voce? + + assembly.add_parts_to_assembly Aggiungi componenti al gruppo + + + assembly.bom.project + Progetto + + assembly.bom.name @@ -12688,9 +12676,9 @@ Notare che non è possibile impersonare un utente disattivato. Quando si prova a Importa componenti per il gruppo - + - assembly.bom.part + assembly.bom.partOrProject Componente @@ -13137,42 +13125,6 @@ Notare che non è possibile impersonare un utente disattivato. Quando si prova a - - - typeahead.parts.part.name - %name% (componente) - - - - - typeahead.parts.assembly.name - %name% (gruppo) - - - - - projects.build.form.part - Componente "%name%" - - - - - projects.build.form.assembly - Gruppo "%name%" - - - - - projects.build.form.assembly.bom.entry - %name% (%quantity% necessari) - - - - - projects.build.form.assembly.bom.entry.no.stock - Non disponibile in magazzino - - part.table.actions.error diff --git a/translations/messages.ja.xlf b/translations/messages.ja.xlf index fb64a0f0..2ff7a744 100644 --- a/translations/messages.ja.xlf +++ b/translations/messages.ja.xlf @@ -4709,11 +4709,11 @@ %value%(部品) - - - part.table.name.value.for_assembly - %value%(アセンブリ) - + + + part.table.name.value.for_project + %value%(プロジェクト) + @@ -8846,18 +8846,6 @@ Exampletown Part-DBについての質問は、<a href="%href%" class="link-external" target="_blank">GitHub</a> にスレッドがあります。 - - - project.bom.assembly - アセンブリ - - - - - project.bom.partOrAssembly - 選択 - - assembly.edit.status @@ -8894,18 +8882,6 @@ Exampletown アーカイブ済み - - - project.builds.no_stock - nessuna scorta specificata - - - - - project.build.builds_part_lot_label - %name% (必要数: %quantity%) - - assembly.label @@ -9134,12 +9110,48 @@ Exampletown 必要数量 + + + assembly.bom.delete.confirm + 本当にこのエントリを削除しますか? + + assembly.add_parts_to_assembly アセンブリに部品を追加 + + + assembly.bom.project + プロジェクト + + + + + assembly.bom.name + 名前 + + + + + assembly.bom.comment + コメント + + + + + assembly.builds.following_bom_entries_miss_instock_n + このアセンブリを%number_of_builds%回作成するための部品が十分に在庫にありません。以下の部品が不足しています: + + + + + assembly.build.help + どの在庫から必要な部品を取り出すか(およびその数量)を選択してください。部品を取り出した場合は、各項目のチェックをオンにするか、最上部のチェックボックスを使って一括でオンにすることができます。 + + assembly.build.required_qty @@ -9589,41 +9601,5 @@ Exampletown - - - typeahead.parts.part.name - %name%(部品) - - - - - typeahead.parts.assembly.name - %name%(アセンブリ) - - - - - projects.build.form.part - 部品「%name%」 - - - - - projects.build.form.assembly - アセンブリ「%name%」 - - - - - projects.build.form.assembly.bom.entry - %name% (必要数量: %quantity%) - - - - - projects.build.form.assembly.bom.entry.no.stock - 在庫なし - - diff --git a/translations/messages.nl.xlf b/translations/messages.nl.xlf index 7cca5975..be8728ce 100644 --- a/translations/messages.nl.xlf +++ b/translations/messages.nl.xlf @@ -730,22 +730,10 @@ %value% (Onderdeel) - + - part.table.name.value.for_assembly - %value% (Samenstelling) - - - - - project.bom.assembly - Assemblage - - - - - project.bom.partOrAssembly - Selectie + part.table.name.value.for_project + %value% (Project) @@ -784,18 +772,6 @@ Αρχειοθετήθηκε - - - project.builds.no_stock - geen voorraad opgegeven - - - - - project.build.builds_part_lot_label - %name% (%quantity% vereist) - - assembly.label @@ -1024,12 +1000,24 @@ Nodig + + + assembly.bom.delete.confirm + Weet u zeker dat u dit item wilt verwijderen? + + assembly.add_parts_to_assembly Onderdelen toevoegen aan assemblage + + + assembly.bom.project + Project + + assembly.bom.name @@ -1066,9 +1054,9 @@ Importeer onderdelen voor assemblage - + - assembly.bom.part + assembly.bom.partOrProject Onderdeel @@ -1551,41 +1539,5 @@ - - - typeahead.parts.part.name - %name% (Onderdeel) - - - - - typeahead.parts.assembly.name - %name% (Assemblage) - - - - - projects.build.form.part - Onderdelen "%name%" - - - - - projects.build.form.assembly - Assemblage "%name%" - - - - - projects.build.form.assembly.bom.entry - %name% (%quantity% benodigd) - - - - - projects.build.form.assembly.bom.entry.no.stock - niet op voorraad - - diff --git a/translations/messages.pl.xlf b/translations/messages.pl.xlf index 86894d99..d48e0d93 100644 --- a/translations/messages.pl.xlf +++ b/translations/messages.pl.xlf @@ -4751,11 +4751,11 @@ Jeśli zrobiłeś to niepoprawnie lub komputer nie jest już godny zaufania, mo %value%(部品) - - - part.table.name.value.for_assembly - %value%(アセンブリ) - + + + part.table.name.value.for_project + %value% (Projekt) + @@ -9821,18 +9821,6 @@ Element 3 Komponent - - - project.bom.assembly - Zespół - - - - - project.bom.partOrAssembly - Wybór - - project.bom.add_entry @@ -10223,12 +10211,6 @@ Element 3 dostępny - - - project.builds.no_stock - brak podanego stanu magazynowego - - project.builds.needed @@ -10301,12 +10283,6 @@ Element 3 Partia docelowa - - - project.build.builds_part_lot_label - %name% (%quantity% wymagane) - - project.builds.number_of_builds @@ -12523,12 +12499,24 @@ Należy pamiętać, że nie możesz udawać nieaktywnych użytkowników. Jeśli potrzebne + + + assembly.bom.delete.confirm + Czy na pewno chcesz usunąć ten element? + + assembly.add_parts_to_assembly Dodaj części do zespołu + + + assembly.bom.project + Projekt + + assembly.bom.name @@ -12565,9 +12553,9 @@ Należy pamiętać, że nie możesz udawać nieaktywnych użytkowników. Jeśli Importuj części dla zespołu - + - assembly.bom.part + assembly.bom.partOrProject Część @@ -13014,41 +13002,5 @@ Należy pamiętać, że nie możesz udawać nieaktywnych użytkowników. Jeśli - - - typeahead.parts.part.name - %name% (część) - - - - - typeahead.parts.assembly.name - %name% (zespół) - - - - - projects.build.form.part - Część "%name%" - - - - - projects.build.form.assembly - Zespół "%name%" - - - - - projects.build.form.assembly.bom.entry - %name% (wymagana ilość: %quantity%) - - - - - projects.build.form.assembly.bom.entry.no.stock - brak na magazynie - - diff --git a/translations/messages.ru.xlf b/translations/messages.ru.xlf index ac04ed29..f27cd858 100644 --- a/translations/messages.ru.xlf +++ b/translations/messages.ru.xlf @@ -4757,11 +4757,11 @@ %value% (Часть) - - - part.table.name.value.for_assembly - %value% (Сборка) - + + + part.table.name.value.for_project + %value% (Проект) + @@ -9825,18 +9825,6 @@ Компонент - - - project.bom.assembly - Сборка - - - - - project.bom.partOrAssembly - Выбор - - project.bom.add_entry @@ -10227,12 +10215,6 @@ запасено - - - project.builds.no_stock - склад не указан - - project.builds.needed @@ -10305,12 +10287,6 @@ Целевой лот - - - project.build.builds_part_lot_label - %name% (требуется: %quantity%) - - project.builds.number_of_builds @@ -12623,12 +12599,24 @@ Необходимо + + + assembly.bom.delete.confirm + Вы действительно хотите удалить этот элемент? + + assembly.add_parts_to_assembly Добавить детали в сборку + + + assembly.bom.project + Проект + + assembly.bom.name @@ -12665,9 +12653,9 @@ Импортировать детали для сборки - + - assembly.bom.part + assembly.bom.partOrProject Компонент @@ -13114,41 +13102,5 @@ - - - typeahead.parts.part.name - %name% (Деталь) - - - - - typeahead.parts.assembly.name - %name% (Сборка) - - - - - projects.build.form.part - Компонент "%name%" - - - - - projects.build.form.assembly - Сборка "%name%" - - - - - projects.build.form.assembly.bom.entry - %name% (необходимо: %quantity%) - - - - - projects.build.form.assembly.bom.entry.no.stock - Нет на складе - - diff --git a/translations/messages.zh.xlf b/translations/messages.zh.xlf index 42656622..9fd6855a 100644 --- a/translations/messages.zh.xlf +++ b/translations/messages.zh.xlf @@ -4755,11 +4755,11 @@ %value%(部件) - - - part.table.name.value.for_assembly - %value%(组件) - + + + part.table.name.value.for_project + %value%(项目) + @@ -9824,18 +9824,6 @@ Element 3 部件 - - - project.bom.assembly - 装配 - - - - - project.bom.partOrAssembly - 选择 - - project.bom.add_entry @@ -10226,12 +10214,6 @@ Element 3 在库 - - - project.builds.no_stock - 未指定库存 - - project.builds.needed @@ -10304,12 +10286,6 @@ Element 3 目标批次 - - - project.build.builds_part_lot_label - %name% (需求数量: %quantity%) - - project.builds.number_of_builds @@ -12508,12 +12484,24 @@ Element 3 需要 + + + assembly.bom.delete.confirm + 您确定要删除此项目吗? + + assembly.add_parts_to_assembly 添加零件到组件 + + + assembly.bom.project + 项目 + + assembly.bom.name @@ -12550,9 +12538,9 @@ Element 3 导入组件的零件 - + - assembly.bom.part + assembly.bom.partOrProject 零件 @@ -12999,41 +12987,5 @@ Element 3 - - - typeahead.parts.part.name - %name%(零件) - - - - - typeahead.parts.assembly.name - %name%(组件) - - - - - projects.build.form.part - 零件“%name%” - - - - - projects.build.form.assembly - 组件“%name%” - - - - - projects.build.form.assembly.bom.entry - %name%(需数量:%quantity%) - - - - - projects.build.form.assembly.bom.entry.no.stock - 库存不足 - - diff --git a/translations/validators.cs.xlf b/translations/validators.cs.xlf index 699b5d2f..ee69c98c 100644 --- a/translations/validators.cs.xlf +++ b/translations/validators.cs.xlf @@ -389,6 +389,12 @@ Tato součást již existuje ve skupině! + + + assembly.bom_entry.project_already_in_bom + Tento projekt již v této skupině existuje! + + assembly.bom_entry.name_already_in_bom diff --git a/translations/validators.da.xlf b/translations/validators.da.xlf index 056871bb..8494e436 100644 --- a/translations/validators.da.xlf +++ b/translations/validators.da.xlf @@ -365,6 +365,12 @@ Denne del eksisterer allerede i gruppen! + + + assembly.bom_entry.project_already_in_bom + Dette projekt eksisterer allerede i gruppen! + + assembly.bom_entry.name_already_in_bom diff --git a/translations/validators.de.xlf b/translations/validators.de.xlf index 822ddaae..5ebeac6b 100644 --- a/translations/validators.de.xlf +++ b/translations/validators.de.xlf @@ -389,6 +389,12 @@ Dieses Bauteil existiert bereits in der Gruppe! + + + assembly.bom_entry.project_already_in_bom + Dieses Projekt existiert bereits in der Gruppe! + + assembly.bom_entry.name_already_in_bom diff --git a/translations/validators.el.xlf b/translations/validators.el.xlf index bb78c799..e04b9dae 100644 --- a/translations/validators.el.xlf +++ b/translations/validators.el.xlf @@ -31,6 +31,12 @@ Αυτό το εξάρτημα υπάρχει ήδη στην ομάδα! + + + assembly.bom_entry.project_already_in_bom + Αυτό το έργο υπάρχει ήδη στην ομάδα! + + assembly.bom_entry.name_already_in_bom diff --git a/translations/validators.en.xlf b/translations/validators.en.xlf index 4c53ed18..ef4606ba 100644 --- a/translations/validators.en.xlf +++ b/translations/validators.en.xlf @@ -386,19 +386,25 @@ assembly.bom_entry.part_already_in_bom - __assembly.bom_entry.part_already_in_bom + This part already exists in the list! + + + + + assembly.bom_entry.project_already_in_bom + This project already exists in the list! assembly.bom_entry.name_already_in_bom - __assembly.bom_entry.name_already_in_bom + There is already a part with this name! validator.assembly.bom_entry.name_or_part_needed - __validator.assembly.bom_entry.name_or_part_needed + You must select a part or set a name for the entry! diff --git a/translations/validators.fr.xlf b/translations/validators.fr.xlf index 957a4791..e603bdaf 100644 --- a/translations/validators.fr.xlf +++ b/translations/validators.fr.xlf @@ -227,6 +227,12 @@ Cette pièce existe déjà dans le groupe! + + + assembly.bom_entry.project_already_in_bom + Ce projet existe déjà dans le groupe! + + assembly.bom_entry.name_already_in_bom diff --git a/translations/validators.hr.xlf b/translations/validators.hr.xlf index 639dff8b..4df9c735 100644 --- a/translations/validators.hr.xlf +++ b/translations/validators.hr.xlf @@ -383,6 +383,12 @@ Ovaj dio već postoji u grupi! + + + assembly.bom_entry.project_already_in_bom + Ovaj projekt već postoji u grupi! + + assembly.bom_entry.name_already_in_bom diff --git a/translations/validators.it.xlf b/translations/validators.it.xlf index a1b9b2f0..cbc331d5 100644 --- a/translations/validators.it.xlf +++ b/translations/validators.it.xlf @@ -383,6 +383,12 @@ Questa parte è già presente nel gruppo! + + + assembly.bom_entry.project_already_in_bom + Questo progetto esiste già nel gruppo! + + assembly.bom_entry.name_already_in_bom diff --git a/translations/validators.ja.xlf b/translations/validators.ja.xlf index 4a36a79a..070281cc 100644 --- a/translations/validators.ja.xlf +++ b/translations/validators.ja.xlf @@ -227,6 +227,12 @@ この部品はすでにグループに存在します! + + + assembly.bom_entry.project_already_in_bom + このプロジェクトは既にグループに存在しています! + + assembly.bom_entry.name_already_in_bom diff --git a/translations/validators.pl.xlf b/translations/validators.pl.xlf index e80dd23b..1ef74c8e 100644 --- a/translations/validators.pl.xlf +++ b/translations/validators.pl.xlf @@ -383,6 +383,12 @@ Ten element już istnieje w grupie! + + + assembly.bom_entry.project_already_in_bom + Ten projekt już znajduje się w grupie! + + assembly.bom_entry.name_already_in_bom diff --git a/translations/validators.ru.xlf b/translations/validators.ru.xlf index 48f0737e..a878cc93 100644 --- a/translations/validators.ru.xlf +++ b/translations/validators.ru.xlf @@ -383,6 +383,12 @@ Эта деталь уже существует в группе! + + + assembly.bom_entry.project_already_in_bom + Этот проект уже находится в группе! + + assembly.bom_entry.name_already_in_bom diff --git a/translations/validators.zh.xlf b/translations/validators.zh.xlf index dea45ccc..3ac139f1 100644 --- a/translations/validators.zh.xlf +++ b/translations/validators.zh.xlf @@ -371,6 +371,12 @@ 此零件已存在于组中! + + + assembly.bom_entry.project_already_in_bom + 该项目已在组中! + + assembly.bom_entry.name_already_in_bom