Added custom part status (#1053)
Some checks failed
Build assets artifact / Build assets artifact (push) Has been cancelled
Docker Image Build / docker (push) Has been cancelled
Docker Image Build (FrankenPHP) / docker (push) Has been cancelled
Static analysis / Static analysis (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, sqlite) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, sqlite) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, sqlite) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, sqlite) (push) Has been cancelled

* Benutzerdefinierten Bauteilstatus einführen

* PartCustomStateController hinzufügen

* Umstellung Migrationen bzgl. Multi-Plattform-Support.
Zunächst MySQL, SQLite Statements integrieren.

* Postgre Statements integrieren

* Semikolon in Migration entfernen

* Migration für PartCustomState aktualisieren

* Benutzerdefinierten Bauteilstatus in TableSettings aufnehmen

* PartCustomStateControllerTest: Attribute für PHPUnit-Gruppen umgestellt

* PartCustomState: Mapping für Parameter korrigieren

* PartCustomState: Darstellung und Zuordnung von Anhängen ergänzt

Die Sidebar wurde um die Anzeige des benutzerdefinierten Bauteilstatus erweitert, inklusive Vorschaubild, sofern vorhanden.

* Migrationen zusammenführen

* PartCustomState: Anpassungen bzgl. Tests

* PartCustomStateEndpoint hinzufügen

* Made custom part states plural for consistency with other entity captions

* Fixed phpunit error

* Fixed phpstan issues

---------

Co-authored-by: Marcel Diegelmann <marcel.diegelmann@gmail.com>
Co-authored-by: Jan Böhmer <mail@jan-boehmer.de>
This commit is contained in:
web-devinition.de 2025-10-27 21:58:16 +01:00 committed by GitHub
parent 600686c32b
commit 14a4f1f437
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
65 changed files with 2044 additions and 18 deletions

View file

@ -24,7 +24,7 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
label: "perm.read"
# If a part can be read by a user, he can also see all the datastructures (except devices)
alsoSet: ['storelocations.read', 'footprints.read', 'categories.read', 'suppliers.read', 'manufacturers.read',
'currencies.read', 'attachment_types.read', 'measurement_units.read']
'currencies.read', 'attachment_types.read', 'measurement_units.read', 'part_custom_states.read']
apiTokenRole: ROLE_API_READ_ONLY
edit:
label: "perm.edit"
@ -133,6 +133,10 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
<<: *PART_CONTAINING
label: "perm.measurement_units"
part_custom_states:
<<: *PART_CONTAINING
label: "perm.part_custom_states"
tools:
label: "perm.part.tools"
operations:

View file

@ -136,7 +136,7 @@ bundled with Part-DB. Set `DATABASE_MYSQL_SSL_VERIFY_CERT` if you want to accept
* `TABLE_PARTS_DEFAULT_COLUMNS`: The columns in parts tables, which are visible by default (when loading table for first
time).
Also specify the default order of the columns. This is a comma separated list of column names. Available columns
are: `name`, `id`, `ipn`, `description`, `category`, `footprint`, `manufacturer`, `storage_location`, `amount`, `minamount`, `partUnit`, `addedDate`, `lastModified`, `needs_review`, `favorite`, `manufacturing_status`, `manufacturer_product_number`, `mass`, `tags`, `attachments`, `edit`.
are: `name`, `id`, `ipn`, `description`, `category`, `footprint`, `manufacturer`, `storage_location`, `amount`, `minamount`, `partUnit`, `partCustomState`, `addedDate`, `lastModified`, `needs_review`, `favorite`, `manufacturing_status`, `manufacturer_product_number`, `mass`, `tags`, `attachments`, `edit`.
### History/Eventlog-related settings

View file

@ -0,0 +1,605 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use App\Migration\AbstractMultiPlatformMigration;
use Doctrine\DBAL\Schema\Schema;
final class Version20250321075747 extends AbstractMultiPlatformMigration
{
public function getDescription(): string
{
return 'Create entity table for custom part states and add custom state to parts';
}
public function mySQLUp(Schema $schema): void
{
$this->addSql(<<<'SQL'
CREATE TABLE part_custom_states (
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,
last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
INDEX IDX_F552745D727ACA70 (parent_id),
INDEX IDX_F552745DEA7100A1 (id_preview_attachment),
INDEX part_custom_state_name (name),
PRIMARY KEY(id)
)
DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci`
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE part_custom_states ADD CONSTRAINT FK_F552745D727ACA70 FOREIGN KEY (parent_id) REFERENCES part_custom_states (id)
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE part_custom_states ADD CONSTRAINT FK_F552745DEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON DELETE SET NULL
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE parts ADD id_part_custom_state INT DEFAULT NULL AFTER id_part_unit
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE parts ADD CONSTRAINT FK_6940A7FEA3ED1215 FOREIGN KEY (id_part_custom_state) REFERENCES part_custom_states (id)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_6940A7FEA3ED1215 ON parts (id_part_custom_state)
SQL);
}
public function mySQLDown(Schema $schema): void
{
$this->addSql(<<<'SQL'
ALTER TABLE parts DROP FOREIGN KEY FK_6940A7FEA3ED1215
SQL);
$this->addSql(<<<'SQL'
DROP INDEX IDX_6940A7FEA3ED1215 ON parts
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE parts DROP id_part_custom_state
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE part_custom_states DROP FOREIGN KEY FK_F552745D727ACA70
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE part_custom_states DROP FOREIGN KEY FK_F552745DEA7100A1
SQL);
$this->addSql(<<<'SQL'
DROP TABLE part_custom_states
SQL);
}
public function sqLiteUp(Schema $schema): void
{
$this->addSql(<<<'SQL'
CREATE TABLE "part_custom_states" (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
parent_id INTEGER DEFAULT NULL,
id_preview_attachment INTEGER DEFAULT NULL,
name VARCHAR(255) NOT NULL,
comment CLOB NOT NULL,
not_selectable BOOLEAN NOT NULL,
alternative_names CLOB DEFAULT NULL,
last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
CONSTRAINT FK_F552745D727ACA70 FOREIGN KEY (parent_id) REFERENCES "part_custom_states" (id) NOT DEFERRABLE INITIALLY IMMEDIATE,
CONSTRAINT FK_F5AF83CFEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE
)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_F552745D727ACA70 ON "part_custom_states" (parent_id)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX part_custom_state_name ON "part_custom_states" (name)
SQL);
$this->addSql(<<<'SQL'
CREATE TEMPORARY TABLE __temp__parts AS
SELECT
id,
id_preview_attachment,
id_category,
id_footprint,
id_part_unit,
id_manufacturer,
order_orderdetails_id,
built_project_id,
datetime_added,
name,
last_modified,
needs_review,
tags,
mass,
description,
comment,
visible,
favorite,
minamount,
manufacturer_product_url,
manufacturer_product_number,
manufacturing_status,
order_quantity,
manual_order,
ipn,
provider_reference_provider_key,
provider_reference_provider_id,
provider_reference_provider_url,
provider_reference_last_updated,
eda_info_reference_prefix,
eda_info_value,
eda_info_invisible,
eda_info_exclude_from_bom,
eda_info_exclude_from_board,
eda_info_exclude_from_sim,
eda_info_kicad_symbol,
eda_info_kicad_footprint
FROM parts
SQL);
$this->addSql(<<<'SQL'
DROP TABLE parts
SQL);
$this->addSql(<<<'SQL'
CREATE TABLE parts (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
id_preview_attachment INTEGER DEFAULT NULL,
id_category INTEGER NOT NULL,
id_footprint INTEGER DEFAULT NULL,
id_part_unit INTEGER DEFAULT NULL,
id_manufacturer INTEGER DEFAULT NULL,
id_part_custom_state INTEGER DEFAULT NULL,
order_orderdetails_id INTEGER DEFAULT NULL,
built_project_id INTEGER DEFAULT NULL,
datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
name VARCHAR(255) NOT NULL,
last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
needs_review BOOLEAN NOT NULL,
tags CLOB NOT NULL,
mass DOUBLE PRECISION DEFAULT NULL,
description CLOB NOT NULL,
comment CLOB NOT NULL,
visible BOOLEAN NOT NULL,
favorite BOOLEAN NOT NULL,
minamount DOUBLE PRECISION NOT NULL,
manufacturer_product_url CLOB NOT NULL,
manufacturer_product_number VARCHAR(255) NOT NULL,
manufacturing_status VARCHAR(255) DEFAULT NULL,
order_quantity INTEGER NOT NULL,
manual_order BOOLEAN NOT NULL,
ipn VARCHAR(100) DEFAULT NULL,
provider_reference_provider_key VARCHAR(255) DEFAULT NULL,
provider_reference_provider_id VARCHAR(255) DEFAULT NULL,
provider_reference_provider_url VARCHAR(255) DEFAULT NULL,
provider_reference_last_updated DATETIME DEFAULT NULL,
eda_info_reference_prefix VARCHAR(255) DEFAULT NULL,
eda_info_value VARCHAR(255) DEFAULT NULL,
eda_info_invisible BOOLEAN DEFAULT NULL,
eda_info_exclude_from_bom BOOLEAN DEFAULT NULL,
eda_info_exclude_from_board BOOLEAN DEFAULT NULL,
eda_info_exclude_from_sim BOOLEAN DEFAULT NULL,
eda_info_kicad_symbol VARCHAR(255) DEFAULT NULL,
eda_info_kicad_footprint VARCHAR(255) DEFAULT NULL,
CONSTRAINT FK_6940A7FEEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE,
CONSTRAINT FK_6940A7FE5697F554 FOREIGN KEY (id_category) REFERENCES categories (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE,
CONSTRAINT FK_6940A7FE7E371A10 FOREIGN KEY (id_footprint) REFERENCES footprints (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE,
CONSTRAINT FK_6940A7FE2626CEF9 FOREIGN KEY (id_part_unit) REFERENCES measurement_units (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE,
CONSTRAINT FK_6940A7FE1ECB93AE FOREIGN KEY (id_manufacturer) REFERENCES manufacturers (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE,
CONSTRAINT FK_6940A7FEA3ED1215 FOREIGN KEY (id_part_custom_state) REFERENCES "part_custom_states" (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE,
CONSTRAINT FK_6940A7FE81081E9B FOREIGN KEY (order_orderdetails_id) REFERENCES orderdetails (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE,
CONSTRAINT FK_6940A7FEE8AE70D9 FOREIGN KEY (built_project_id) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE
)
SQL);
$this->addSql(<<<'SQL'
INSERT INTO parts (
id,
id_preview_attachment,
id_category,
id_footprint,
id_part_unit,
id_manufacturer,
order_orderdetails_id,
built_project_id,
datetime_added,
name,
last_modified,
needs_review,
tags,
mass,
description,
comment,
visible,
favorite,
minamount,
manufacturer_product_url,
manufacturer_product_number,
manufacturing_status,
order_quantity,
manual_order,
ipn,
provider_reference_provider_key,
provider_reference_provider_id,
provider_reference_provider_url,
provider_reference_last_updated,
eda_info_reference_prefix,
eda_info_value,
eda_info_invisible,
eda_info_exclude_from_bom,
eda_info_exclude_from_board,
eda_info_exclude_from_sim,
eda_info_kicad_symbol,
eda_info_kicad_footprint)
SELECT
id,
id_preview_attachment,
id_category,
id_footprint,
id_part_unit,
id_manufacturer,
order_orderdetails_id,
built_project_id,
datetime_added,
name,
last_modified,
needs_review,
tags,
mass,
description,
comment,
visible,
favorite,
minamount,
manufacturer_product_url,
manufacturer_product_number,
manufacturing_status,
order_quantity,
manual_order,
ipn,
provider_reference_provider_key,
provider_reference_provider_id,
provider_reference_provider_url,
provider_reference_last_updated,
eda_info_reference_prefix,
eda_info_value,
eda_info_invisible,
eda_info_exclude_from_bom,
eda_info_exclude_from_board,
eda_info_exclude_from_sim,
eda_info_kicad_symbol,
eda_info_kicad_footprint
FROM __temp__parts
SQL);
$this->addSql(<<<'SQL'
DROP TABLE __temp__parts
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX parts_idx_name ON parts (name)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX parts_idx_ipn ON parts (ipn)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX parts_idx_datet_name_last_id_needs ON parts (datetime_added, name, last_modified, id, needs_review)
SQL);
$this->addSql(<<<'SQL'
CREATE UNIQUE INDEX UNIQ_6940A7FEE8AE70D9 ON parts (built_project_id)
SQL);
$this->addSql(<<<'SQL'
CREATE UNIQUE INDEX UNIQ_6940A7FE81081E9B ON parts (order_orderdetails_id)
SQL);
$this->addSql(<<<'SQL'
CREATE UNIQUE INDEX UNIQ_6940A7FE3D721C14 ON parts (ipn)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_6940A7FEEA7100A1 ON parts (id_preview_attachment)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_6940A7FE7E371A10 ON parts (id_footprint)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_6940A7FE5697F554 ON parts (id_category)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_6940A7FE2626CEF9 ON parts (id_part_unit)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_6940A7FE1ECB93AE ON parts (id_manufacturer)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_6940A7FEA3ED1215 ON parts (id_part_custom_state)
SQL);
}
public function sqLiteDown(Schema $schema): void
{
$this->addSql(<<<'SQL'
CREATE TEMPORARY TABLE __temp__parts AS
SELECT
id,
id_preview_attachment,
id_category,
id_footprint,
id_part_unit,
id_manufacturer,
order_orderdetails_id,
built_project_id,
datetime_added,
name,
last_modified,
needs_review,
tags,
mass,
description,
comment,
visible,
favorite,
minamount,
manufacturer_product_url,
manufacturer_product_number,
manufacturing_status,
order_quantity,
manual_order,
ipn,
provider_reference_provider_key,
provider_reference_provider_id,
provider_reference_provider_url,
provider_reference_last_updated,
eda_info_reference_prefix,
eda_info_value,
eda_info_invisible,
eda_info_exclude_from_bom,
eda_info_exclude_from_board,
eda_info_exclude_from_sim,
eda_info_kicad_symbol,
eda_info_kicad_footprint
FROM "parts"
SQL);
$this->addSql(<<<'SQL'
DROP TABLE "parts"
SQL);
$this->addSql(<<<'SQL'
CREATE TABLE "parts" (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
id_preview_attachment INTEGER DEFAULT NULL,
id_category INTEGER NOT NULL,
id_footprint INTEGER DEFAULT NULL,
id_part_unit INTEGER DEFAULT NULL,
id_manufacturer INTEGER DEFAULT NULL,
order_orderdetails_id INTEGER DEFAULT NULL,
built_project_id INTEGER DEFAULT NULL,
datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
name VARCHAR(255) NOT NULL,
last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
needs_review BOOLEAN NOT NULL,
tags CLOB NOT NULL,
mass DOUBLE PRECISION DEFAULT NULL,
description CLOB NOT NULL,
comment CLOB NOT NULL,
visible BOOLEAN NOT NULL,
favorite BOOLEAN NOT NULL,
minamount DOUBLE PRECISION NOT NULL,
manufacturer_product_url CLOB NOT NULL,
manufacturer_product_number VARCHAR(255) NOT NULL,
manufacturing_status VARCHAR(255) DEFAULT NULL,
order_quantity INTEGER NOT NULL,
manual_order BOOLEAN NOT NULL,
ipn VARCHAR(100) DEFAULT NULL,
provider_reference_provider_key VARCHAR(255) DEFAULT NULL,
provider_reference_provider_id VARCHAR(255) DEFAULT NULL,
provider_reference_provider_url VARCHAR(255) DEFAULT NULL,
provider_reference_last_updated DATETIME DEFAULT NULL,
eda_info_reference_prefix VARCHAR(255) DEFAULT NULL,
eda_info_value VARCHAR(255) DEFAULT NULL,
eda_info_invisible BOOLEAN DEFAULT NULL,
eda_info_exclude_from_bom BOOLEAN DEFAULT NULL,
eda_info_exclude_from_board BOOLEAN DEFAULT NULL,
eda_info_exclude_from_sim BOOLEAN DEFAULT NULL,
eda_info_kicad_symbol VARCHAR(255) DEFAULT NULL,
eda_info_kicad_footprint VARCHAR(255) DEFAULT NULL,
CONSTRAINT FK_6940A7FEEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE,
CONSTRAINT FK_6940A7FE5697F554 FOREIGN KEY (id_category) REFERENCES "categories" (id) NOT DEFERRABLE INITIALLY IMMEDIATE,
CONSTRAINT FK_6940A7FE7E371A10 FOREIGN KEY (id_footprint) REFERENCES "footprints" (id) NOT DEFERRABLE INITIALLY IMMEDIATE,
CONSTRAINT FK_6940A7FE2626CEF9 FOREIGN KEY (id_part_unit) REFERENCES "measurement_units" (id) NOT DEFERRABLE INITIALLY IMMEDIATE,
CONSTRAINT FK_6940A7FE1ECB93AE FOREIGN KEY (id_manufacturer) REFERENCES "manufacturers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE,
CONSTRAINT FK_6940A7FE81081E9B FOREIGN KEY (order_orderdetails_id) REFERENCES "orderdetails" (id) NOT DEFERRABLE INITIALLY IMMEDIATE,
CONSTRAINT FK_6940A7FEE8AE70D9 FOREIGN KEY (built_project_id) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE
)
SQL);
$this->addSql(<<<'SQL'
INSERT INTO "parts" (
id,
id_preview_attachment,
id_category,
id_footprint,
id_part_unit,
id_manufacturer,
order_orderdetails_id,
built_project_id,
datetime_added,
name,
last_modified,
needs_review,
tags,
mass,
description,
comment,
visible,
favorite,
minamount,
manufacturer_product_url,
manufacturer_product_number,
manufacturing_status,
order_quantity,
manual_order,
ipn,
provider_reference_provider_key,
provider_reference_provider_id,
provider_reference_provider_url,
provider_reference_last_updated,
eda_info_reference_prefix,
eda_info_value,
eda_info_invisible,
eda_info_exclude_from_bom,
eda_info_exclude_from_board,
eda_info_exclude_from_sim,
eda_info_kicad_symbol,
eda_info_kicad_footprint
) SELECT
id,
id_preview_attachment,
id_category,
id_footprint,
id_part_unit,
id_manufacturer,
order_orderdetails_id,
built_project_id,
datetime_added,
name,
last_modified,
needs_review,
tags,
mass,
description,
comment,
visible,
favorite,
minamount,
manufacturer_product_url,
manufacturer_product_number,
manufacturing_status,
order_quantity,
manual_order,
ipn,
provider_reference_provider_key,
provider_reference_provider_id,
provider_reference_provider_url,
provider_reference_last_updated,
eda_info_reference_prefix,
eda_info_value,
eda_info_invisible,
eda_info_exclude_from_bom,
eda_info_exclude_from_board,
eda_info_exclude_from_sim,
eda_info_kicad_symbol,
eda_info_kicad_footprint
FROM __temp__parts
SQL);
$this->addSql(<<<'SQL'
DROP TABLE __temp__parts
SQL);
$this->addSql(<<<'SQL'
CREATE UNIQUE INDEX UNIQ_6940A7FE3D721C14 ON "parts" (ipn)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_6940A7FEEA7100A1 ON "parts" (id_preview_attachment)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_6940A7FE5697F554 ON "parts" (id_category)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_6940A7FE7E371A10 ON "parts" (id_footprint)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_6940A7FE2626CEF9 ON "parts" (id_part_unit)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_6940A7FE1ECB93AE ON "parts" (id_manufacturer)
SQL);
$this->addSql(<<<'SQL'
CREATE UNIQUE INDEX UNIQ_6940A7FE81081E9B ON "parts" (order_orderdetails_id)
SQL);
$this->addSql(<<<'SQL'
CREATE UNIQUE INDEX UNIQ_6940A7FEE8AE70D9 ON "parts" (built_project_id)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX parts_idx_datet_name_last_id_needs ON "parts" (datetime_added, name, last_modified, id, needs_review)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX parts_idx_name ON "parts" (name)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX parts_idx_ipn ON "parts" (ipn)
SQL);
$this->addSql(<<<'SQL'
DROP TABLE "part_custom_states"
SQL);
}
public function postgreSQLUp(Schema $schema): void
{
$this->addSql(<<<'SQL'
CREATE TABLE "part_custom_states" (
id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
parent_id INT DEFAULT NULL,
id_preview_attachment INT DEFAULT NULL, PRIMARY KEY(id),
name VARCHAR(255) NOT NULL,
comment TEXT NOT NULL,
not_selectable BOOLEAN NOT NULL,
alternative_names TEXT 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
)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_F552745D727ACA70 ON "part_custom_states" (parent_id)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_F552745DEA7100A1 ON "part_custom_states" (id_preview_attachment)
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE "part_custom_states"
ADD CONSTRAINT FK_F552745D727ACA70
FOREIGN KEY (parent_id) REFERENCES "part_custom_states" (id) NOT DEFERRABLE INITIALLY IMMEDIATE
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE "part_custom_states"
ADD CONSTRAINT FK_F552745DEA7100A1
FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE parts ADD id_part_custom_state INT DEFAULT NULL
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE parts ADD CONSTRAINT FK_6940A7FEA3ED1215 FOREIGN KEY (id_part_custom_state) REFERENCES "part_custom_states" (id) NOT DEFERRABLE INITIALLY IMMEDIATE
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_6940A7FEA3ED1215 ON parts (id_part_custom_state)
SQL);
}
public function postgreSQLDown(Schema $schema): void
{
$this->addSql(<<<'SQL'
ALTER TABLE "parts" DROP CONSTRAINT FK_6940A7FEA3ED1215
SQL);
$this->addSql(<<<'SQL'
DROP INDEX IDX_6940A7FEA3ED1215
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE "parts" DROP id_part_custom_state
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE "part_custom_states" DROP CONSTRAINT FK_F552745D727ACA70
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE "part_custom_states" DROP CONSTRAINT FK_F552745DEA7100A1
SQL);
$this->addSql(<<<'SQL'
DROP TABLE "part_custom_states"
SQL);
}
}

View file

@ -121,6 +121,11 @@ class ImportPartKeeprCommand extends Command
$count = $this->datastructureImporter->importPartUnits($data);
$io->success('Imported '.$count.' measurement units.');
//Import the custom states
$io->info('Importing custom states...');
$count = $this->datastructureImporter->importPartCustomStates($data);
$io->success('Imported '.$count.' custom states.');
//Import manufacturers
$io->info('Importing manufacturers...');
$count = $this->datastructureImporter->importManufacturers($data);

View file

@ -232,6 +232,7 @@ abstract class BaseAdminController extends AbstractController
'timeTravel' => $timeTravel_timestamp,
'repo' => $repo,
'partsContainingElement' => $repo instanceof PartsContainingRepositoryInterface,
'showParameters' => !($this instanceof PartCustomStateController),
]);
}
@ -382,6 +383,7 @@ abstract class BaseAdminController extends AbstractController
'import_form' => $import_form,
'mass_creation_form' => $mass_creation_form,
'route_base' => $this->route_base,
'showParameters' => !($this instanceof PartCustomStateController),
]);
}

View file

@ -0,0 +1,83 @@
<?php
/**
* 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 <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Controller\AdminPages;
use App\Entity\Attachments\PartCustomStateAttachment;
use App\Entity\Parameters\PartCustomStateParameter;
use App\Entity\Parts\PartCustomState;
use App\Form\AdminPages\PartCustomStateAdminForm;
use App\Services\ImportExportSystem\EntityExporter;
use App\Services\ImportExportSystem\EntityImporter;
use App\Services\Trees\StructuralElementRecursionHelper;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
/**
* @see \App\Tests\Controller\AdminPages\PartCustomStateControllerTest
*/
#[Route(path: '/part_custom_state')]
class PartCustomStateController extends BaseAdminController
{
protected string $entity_class = PartCustomState::class;
protected string $twig_template = 'admin/part_custom_state_admin.html.twig';
protected string $form_class = PartCustomStateAdminForm::class;
protected string $route_base = 'part_custom_state';
protected string $attachment_class = PartCustomStateAttachment::class;
protected ?string $parameter_class = PartCustomStateParameter::class;
#[Route(path: '/{id}', name: 'part_custom_state_delete', methods: ['DELETE'])]
public function delete(Request $request, PartCustomState $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse
{
return $this->_delete($request, $entity, $recursionHelper);
}
#[Route(path: '/{id}/edit/{timestamp}', name: 'part_custom_state_edit', requirements: ['id' => '\d+'])]
#[Route(path: '/{id}', requirements: ['id' => '\d+'])]
public function edit(PartCustomState $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
{
return $this->_edit($entity, $request, $em, $timestamp);
}
#[Route(path: '/new', name: 'part_custom_state_new')]
#[Route(path: '/{id}/clone', name: 'part_custom_state_clone')]
#[Route(path: '/')]
public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?PartCustomState $entity = null): Response
{
return $this->_new($request, $em, $importer, $entity);
}
#[Route(path: '/export', name: 'part_custom_state_export_all')]
public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response
{
return $this->_exportAll($em, $exporter, $request);
}
#[Route(path: '/{id}/export', name: 'part_custom_state_export')]
public function exportEntity(PartCustomState $entity, EntityExporter $exporter, Request $request): Response
{
return $this->_exportEntity($entity, $exporter, $request);
}
}

View file

@ -24,6 +24,7 @@ namespace App\DataFixtures;
use App\Entity\Attachments\AttachmentType;
use App\Entity\Base\AbstractStructuralDBElement;
use App\Entity\Parts\PartCustomState;
use App\Entity\ProjectSystem\Project;
use App\Entity\Parts\Category;
use App\Entity\Parts\Footprint;
@ -50,7 +51,7 @@ class DataStructureFixtures extends Fixture implements DependentFixtureInterface
{
//Reset autoincrement
$types = [AttachmentType::class, Project::class, Category::class, Footprint::class, Manufacturer::class,
MeasurementUnit::class, StorageLocation::class, Supplier::class,];
MeasurementUnit::class, StorageLocation::class, Supplier::class, PartCustomState::class];
foreach ($types as $type) {
$this->createNodesForClass($type, $manager);

View file

@ -41,6 +41,7 @@ use App\Entity\Parts\Category;
use App\Entity\Parts\Footprint;
use App\Entity\Parts\Manufacturer;
use App\Entity\Parts\MeasurementUnit;
use App\Entity\Parts\PartCustomState;
use App\Entity\Parts\PartLot;
use App\Entity\Parts\StorageLocation;
use App\Entity\Parts\Supplier;
@ -86,6 +87,7 @@ class PartFilter implements FilterInterface
public readonly EntityConstraint $lotOwner;
public readonly EntityConstraint $measurementUnit;
public readonly EntityConstraint $partCustomState;
public readonly TextConstraint $manufacturer_product_url;
public readonly TextConstraint $manufacturer_product_number;
public readonly IntConstraint $attachmentsCount;
@ -128,6 +130,7 @@ class PartFilter implements FilterInterface
$this->favorite = new BooleanConstraint('part.favorite');
$this->needsReview = new BooleanConstraint('part.needs_review');
$this->measurementUnit = new EntityConstraint($nodesListBuilder, MeasurementUnit::class, 'part.partUnit');
$this->partCustomState = new EntityConstraint($nodesListBuilder, PartCustomState::class, 'part.partCustomState');
$this->mass = new NumberConstraint('part.mass');
$this->dbId = new IntConstraint('part.id');
$this->ipn = new TextConstraint('part.ipn');

View file

@ -174,6 +174,19 @@ final class PartsDataTable implements DataTableTypeInterface
return $tmp;
}
])
->add('partCustomState', TextColumn::class, [
'label' => $this->translator->trans('part.table.partCustomState'),
'orderField' => 'NATSORT(_partCustomState.name)',
'render' => function($value, Part $context): string {
$partCustomState = $context->getPartCustomState();
if ($partCustomState === null) {
return '';
}
return htmlspecialchars($partCustomState->getName());
}
])
->add('addedDate', LocaleDateTimeColumn::class, [
'label' => $this->translator->trans('part.table.addedDate'),
])
@ -309,6 +322,7 @@ final class PartsDataTable implements DataTableTypeInterface
->addSelect('footprint')
->addSelect('manufacturer')
->addSelect('partUnit')
->addSelect('partCustomState')
->addSelect('master_picture_attachment')
->addSelect('footprint_attachment')
->addSelect('partLots')
@ -327,6 +341,7 @@ final class PartsDataTable implements DataTableTypeInterface
->leftJoin('orderdetails.supplier', 'suppliers')
->leftJoin('part.attachments', 'attachments')
->leftJoin('part.partUnit', 'partUnit')
->leftJoin('part.partCustomState', 'partCustomState')
->leftJoin('part.parameters', 'parameters')
->where('part.id IN (:ids)')
->setParameter('ids', $ids)
@ -344,6 +359,7 @@ final class PartsDataTable implements DataTableTypeInterface
->addGroupBy('suppliers')
->addGroupBy('attachments')
->addGroupBy('partUnit')
->addGroupBy('partCustomState')
->addGroupBy('parameters');
//Get the results in the same order as the IDs were passed
@ -415,6 +431,10 @@ final class PartsDataTable implements DataTableTypeInterface
$builder->leftJoin('part.partUnit', '_partUnit');
$builder->addGroupBy('_partUnit');
}
if (str_contains($dql, '_partCustomState')) {
$builder->leftJoin('part.partCustomState', '_partCustomState');
$builder->addGroupBy('_partCustomState');
}
if (str_contains($dql, '_parameters')) {
$builder->leftJoin('part.parameters', '_parameters');
//Do not group by many-to-* relations, as it would restrict the COUNT having clauses to be maximum 1

View file

@ -97,7 +97,7 @@ use function in_array;
#[DiscriminatorMap(typeProperty: '_type', mapping: self::API_DISCRIMINATOR_MAP)]
abstract class Attachment extends AbstractNamedDBElement
{
private const ORM_DISCRIMINATOR_MAP = ['Part' => PartAttachment::class, 'Device' => ProjectAttachment::class,
private const ORM_DISCRIMINATOR_MAP = ['Part' => PartAttachment::class, 'PartCustomState' => PartCustomStateAttachment::class, 'Device' => ProjectAttachment::class,
'AttachmentType' => AttachmentTypeAttachment::class,
'Category' => CategoryAttachment::class, 'Footprint' => FootprintAttachment::class, 'Manufacturer' => ManufacturerAttachment::class,
'Currency' => CurrencyAttachment::class, 'Group' => GroupAttachment::class, 'MeasurementUnit' => MeasurementUnitAttachment::class,
@ -107,7 +107,8 @@ abstract class Attachment extends AbstractNamedDBElement
/*
* The discriminator map used for API platform. The key should be the same as the api platform short type (the @type JSONLD field).
*/
private const API_DISCRIMINATOR_MAP = ["Part" => PartAttachment::class, "Project" => ProjectAttachment::class, "AttachmentType" => AttachmentTypeAttachment::class,
private const API_DISCRIMINATOR_MAP = ["Part" => PartAttachment::class, "PartCustomState" => PartCustomStateAttachment::class, "Project" => ProjectAttachment::class,
"AttachmentType" => AttachmentTypeAttachment::class,
"Category" => CategoryAttachment::class, "Footprint" => FootprintAttachment::class, "Manufacturer" => ManufacturerAttachment::class,
"Currency" => CurrencyAttachment::class, "Group" => GroupAttachment::class, "MeasurementUnit" => MeasurementUnitAttachment::class,
"StorageLocation" => StorageLocationAttachment::class, "Supplier" => SupplierAttachment::class, "User" => UserAttachment::class, "LabelProfile" => LabelAttachment::class];

View file

@ -0,0 +1,45 @@
<?php
/**
* 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 <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Entity\Attachments;
use App\Entity\Parts\PartCustomState;
use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context;
/**
* An attachment attached to a part custom state element.
* @extends Attachment<PartCustomState>
*/
#[UniqueEntity(['name', 'attachment_type', 'element'])]
#[ORM\Entity]
class PartCustomStateAttachment extends Attachment
{
final public const ALLOWED_ELEMENT_CLASS = PartCustomState::class;
#[ORM\ManyToOne(targetEntity: PartCustomState::class, inversedBy: 'attachments')]
#[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')]
#[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])]
protected ?AttachmentContainingDBElement $element = null;
}

View file

@ -33,6 +33,7 @@ use App\Entity\Attachments\LabelAttachment;
use App\Entity\Attachments\ManufacturerAttachment;
use App\Entity\Attachments\MeasurementUnitAttachment;
use App\Entity\Attachments\PartAttachment;
use App\Entity\Attachments\PartCustomStateAttachment;
use App\Entity\Attachments\ProjectAttachment;
use App\Entity\Attachments\StorageLocationAttachment;
use App\Entity\Attachments\SupplierAttachment;
@ -40,6 +41,7 @@ use App\Entity\Attachments\UserAttachment;
use App\Entity\Parameters\AbstractParameter;
use App\Entity\Parts\Category;
use App\Entity\PriceInformations\Pricedetail;
use App\Entity\Parts\PartCustomState;
use App\Entity\ProjectSystem\Project;
use App\Entity\ProjectSystem\ProjectBOMEntry;
use App\Entity\Parts\Footprint;
@ -68,7 +70,41 @@ use Symfony\Component\Serializer\Annotation\Groups;
* Every database table which are managed with this class (or a subclass of it)
* must have the table row "id"!! The ID is the unique key to identify the elements.
*/
#[DiscriminatorMap(typeProperty: 'type', mapping: ['attachment_type' => AttachmentType::class, 'attachment' => Attachment::class, 'attachment_type_attachment' => AttachmentTypeAttachment::class, 'category_attachment' => CategoryAttachment::class, 'currency_attachment' => CurrencyAttachment::class, 'footprint_attachment' => FootprintAttachment::class, 'group_attachment' => GroupAttachment::class, 'label_attachment' => LabelAttachment::class, 'manufacturer_attachment' => ManufacturerAttachment::class, 'measurement_unit_attachment' => MeasurementUnitAttachment::class, 'part_attachment' => PartAttachment::class, 'project_attachment' => ProjectAttachment::class, 'storelocation_attachment' => StorageLocationAttachment::class, 'supplier_attachment' => SupplierAttachment::class, 'user_attachment' => UserAttachment::class, 'category' => Category::class, 'project' => Project::class, 'project_bom_entry' => ProjectBOMEntry::class, 'footprint' => Footprint::class, 'group' => Group::class, 'manufacturer' => Manufacturer::class, 'orderdetail' => Orderdetail::class, 'part' => Part::class, 'pricedetail' => Pricedetail::class, 'storelocation' => StorageLocation::class, 'part_lot' => PartLot::class, 'currency' => Currency::class, 'measurement_unit' => MeasurementUnit::class, 'parameter' => AbstractParameter::class, 'supplier' => Supplier::class, 'user' => User::class])]
#[DiscriminatorMap(typeProperty: 'type', mapping: [
'attachment_type' => AttachmentType::class,
'attachment' => Attachment::class,
'attachment_type_attachment' => AttachmentTypeAttachment::class,
'category_attachment' => CategoryAttachment::class,
'currency_attachment' => CurrencyAttachment::class,
'footprint_attachment' => FootprintAttachment::class,
'group_attachment' => GroupAttachment::class,
'label_attachment' => LabelAttachment::class,
'manufacturer_attachment' => ManufacturerAttachment::class,
'measurement_unit_attachment' => MeasurementUnitAttachment::class,
'part_attachment' => PartAttachment::class,
'part_custom_state_attachment' => PartCustomStateAttachment::class,
'project_attachment' => ProjectAttachment::class,
'storelocation_attachment' => StorageLocationAttachment::class,
'supplier_attachment' => SupplierAttachment::class,
'user_attachment' => UserAttachment::class,
'category' => Category::class,
'project' => Project::class,
'project_bom_entry' => ProjectBOMEntry::class,
'footprint' => Footprint::class,
'group' => Group::class,
'manufacturer' => Manufacturer::class,
'orderdetail' => Orderdetail::class,
'part' => Part::class,
'part_custom_state' => PartCustomState::class,
'pricedetail' => Pricedetail::class,
'storelocation' => StorageLocation::class,
'part_lot' => PartLot::class,
'currency' => Currency::class,
'measurement_unit' => MeasurementUnit::class,
'parameter' => AbstractParameter::class,
'supplier' => Supplier::class,
'user' => User::class]
)]
#[ORM\MappedSuperclass(repositoryClass: DBElementRepository::class)]
abstract class AbstractDBElement implements JsonSerializable
{

View file

@ -46,6 +46,7 @@ use App\Entity\Attachments\AttachmentType;
use App\Entity\Attachments\AttachmentTypeAttachment;
use App\Entity\Attachments\CategoryAttachment;
use App\Entity\Attachments\CurrencyAttachment;
use App\Entity\Attachments\PartCustomStateAttachment;
use App\Entity\Attachments\ProjectAttachment;
use App\Entity\Attachments\FootprintAttachment;
use App\Entity\Attachments\GroupAttachment;
@ -58,6 +59,8 @@ use App\Entity\Attachments\UserAttachment;
use App\Entity\Base\AbstractDBElement;
use App\Entity\Contracts\LogWithEventUndoInterface;
use App\Entity\Contracts\NamedElementInterface;
use App\Entity\Parameters\PartCustomStateParameter;
use App\Entity\Parts\PartCustomState;
use App\Entity\ProjectSystem\Project;
use App\Entity\Parameters\AbstractParameter;
use App\Entity\Parameters\AttachmentTypeParameter;
@ -158,6 +161,7 @@ class CollectionElementDeleted extends AbstractLogEntry implements LogWithEventU
Part::class => PartParameter::class,
StorageLocation::class => StorageLocationParameter::class,
Supplier::class => SupplierParameter::class,
PartCustomState::class => PartCustomStateParameter::class,
default => throw new \RuntimeException('Unknown target class for parameter: '.$this->getTargetClass()),
};
}
@ -173,6 +177,7 @@ class CollectionElementDeleted extends AbstractLogEntry implements LogWithEventU
Manufacturer::class => ManufacturerAttachment::class,
MeasurementUnit::class => MeasurementUnitAttachment::class,
Part::class => PartAttachment::class,
PartCustomState::class => PartCustomStateAttachment::class,
StorageLocation::class => StorageLocationAttachment::class,
Supplier::class => SupplierAttachment::class,
User::class => UserAttachment::class,

View file

@ -34,6 +34,7 @@ use App\Entity\Parts\Manufacturer;
use App\Entity\Parts\MeasurementUnit;
use App\Entity\Parts\Part;
use App\Entity\Parts\PartAssociation;
use App\Entity\Parts\PartCustomState;
use App\Entity\Parts\PartLot;
use App\Entity\Parts\StorageLocation;
use App\Entity\Parts\Supplier;
@ -71,6 +72,7 @@ enum LogTargetType: int
case PART_ASSOCIATION = 20;
case BULK_INFO_PROVIDER_IMPORT_JOB = 21;
case BULK_INFO_PROVIDER_IMPORT_JOB_PART = 22;
case PART_CUSTOM_STATE = 23;
/**
* Returns the class name of the target type or null if the target type is NONE.
@ -102,6 +104,7 @@ enum LogTargetType: int
self::PART_ASSOCIATION => PartAssociation::class,
self::BULK_INFO_PROVIDER_IMPORT_JOB => BulkInfoProviderImportJob::class,
self::BULK_INFO_PROVIDER_IMPORT_JOB_PART => BulkInfoProviderImportJobPart::class,
self::PART_CUSTOM_STATE => PartCustomState::class
};
}

View file

@ -73,7 +73,8 @@ use function sprintf;
#[ORM\DiscriminatorMap([0 => CategoryParameter::class, 1 => CurrencyParameter::class, 2 => ProjectParameter::class,
3 => FootprintParameter::class, 4 => GroupParameter::class, 5 => ManufacturerParameter::class,
6 => MeasurementUnitParameter::class, 7 => PartParameter::class, 8 => StorageLocationParameter::class,
9 => SupplierParameter::class, 10 => AttachmentTypeParameter::class])]
9 => SupplierParameter::class, 10 => AttachmentTypeParameter::class,
12 => PartCustomStateParameter::class])]
#[ORM\Table('parameters')]
#[ORM\Index(columns: ['name'], name: 'parameter_name_idx')]
#[ORM\Index(columns: ['param_group'], name: 'parameter_group_idx')]
@ -105,7 +106,7 @@ abstract class AbstractParameter extends AbstractNamedDBElement implements Uniqu
"AttachmentType" => AttachmentTypeParameter::class, "Category" => CategoryParameter::class, "Currency" => CurrencyParameter::class,
"Project" => ProjectParameter::class, "Footprint" => FootprintParameter::class, "Group" => GroupParameter::class,
"Manufacturer" => ManufacturerParameter::class, "MeasurementUnit" => MeasurementUnitParameter::class,
"StorageLocation" => StorageLocationParameter::class, "Supplier" => SupplierParameter::class];
"StorageLocation" => StorageLocationParameter::class, "Supplier" => SupplierParameter::class, "PartCustomState" => PartCustomStateParameter::class];
/**
* @var string The class of the element that can be passed to this attachment. Must be overridden in subclasses.

View file

@ -0,0 +1,65 @@
<?php
/*
* 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 <https://www.gnu.org/licenses/>.
*/
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 <https://www.gnu.org/licenses/>.
*/
namespace App\Entity\Parameters;
use App\Entity\Base\AbstractDBElement;
use App\Entity\Parts\PartCustomState;
use App\Repository\ParameterRepository;
use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context;
#[UniqueEntity(fields: ['name', 'group', 'element'])]
#[ORM\Entity(repositoryClass: ParameterRepository::class)]
class PartCustomStateParameter extends AbstractParameter
{
final public const ALLOWED_ELEMENT_CLASS = PartCustomState::class;
/**
* @var PartCustomState the element this para is associated with
*/
#[ORM\ManyToOne(targetEntity: PartCustomState::class, inversedBy: 'parameters')]
#[ORM\JoinColumn(name: 'element_id', nullable: false, onDelete: 'CASCADE')]
#[Context(denormalizationContext: [OverrideClassDenormalizer::CONTEXT_KEY => self::ALLOWED_ELEMENT_CLASS])]
protected ?AbstractDBElement $element = null;
}

View file

@ -107,7 +107,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
denormalizationContext: ['groups' => ['part:write', 'api:basic:write', 'eda_info:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'],
)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(EntityFilter::class, properties: ["category", "footprint", "manufacturer", "partUnit"])]
#[ApiFilter(EntityFilter::class, properties: ["category", "footprint", "manufacturer", "partUnit", "partCustomState"])]
#[ApiFilter(PartStoragelocationFilter::class, properties: ["storage_location"])]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment", "description", "ipn", "manufacturer_product_number"])]
#[ApiFilter(TagFilter::class, properties: ["tags"])]

View file

@ -0,0 +1,127 @@
<?php
/**
* 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 <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Entity\Parts;
use ApiPlatform\Metadata\ApiProperty;
use App\Entity\Attachments\Attachment;
use App\Entity\Attachments\PartCustomStateAttachment;
use ApiPlatform\Doctrine\Common\Filter\DateFilterInterface;
use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\Serializer\Filter\PropertyFilter;
use App\ApiPlatform\Filter\LikeFilter;
use App\Entity\Base\AbstractPartsContainingDBElement;
use App\Entity\Base\AbstractStructuralDBElement;
use App\Entity\Parameters\PartCustomStateParameter;
use App\Repository\Parts\PartCustomStateRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
/**
* This entity represents a custom part state.
* If an organisation uses Part-DB and has its custom part states, this is useful.
*
* @extends AbstractPartsContainingDBElement<PartCustomStateAttachment,PartCustomStateParameter>
*/
#[ORM\Entity(repositoryClass: PartCustomStateRepository::class)]
#[ORM\Table('`part_custom_states`')]
#[ORM\Index(columns: ['name'], name: 'part_custom_state_name')]
#[ApiResource(
operations: [
new Get(security: 'is_granted("read", object)'),
new GetCollection(security: 'is_granted("@part_custom_states.read")'),
new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'),
],
normalizationContext: ['groups' => ['part_custom_state:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['part_custom_state:write', 'api:basic:write'], 'openapi_definition_name' => 'Write'],
)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name"])]
#[ApiFilter(DateFilter::class, strategy: DateFilterInterface::EXCLUDE_NULL)]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
class PartCustomState extends AbstractPartsContainingDBElement
{
/**
* @var string The comment info for this element as markdown
*/
#[Groups(['part_custom_state:read', 'part_custom_state:write', 'full', 'import'])]
protected string $comment = '';
#[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class, cascade: ['persist'])]
#[ORM\OrderBy(['name' => Criteria::ASC])]
protected Collection $children;
#[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')]
#[ORM\JoinColumn(name: 'parent_id')]
#[Groups(['part_custom_state:read', 'part_custom_state:write'])]
#[ApiProperty(readableLink: false, writableLink: false)]
protected ?AbstractStructuralDBElement $parent = null;
/**
* @var Collection<int, PartCustomStateAttachment>
*/
#[Assert\Valid]
#[ORM\OneToMany(targetEntity: PartCustomStateAttachment::class, mappedBy: 'element', cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => Criteria::ASC])]
#[Groups(['part_custom_state:read', 'part_custom_state:write'])]
protected Collection $attachments;
#[ORM\ManyToOne(targetEntity: PartCustomStateAttachment::class)]
#[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')]
#[Groups(['part_custom_state:read', 'part_custom_state:write'])]
protected ?Attachment $master_picture_attachment = null;
/** @var Collection<int, PartCustomStateParameter>
*/
#[Assert\Valid]
#[ORM\OneToMany(mappedBy: 'element', targetEntity: PartCustomStateParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => 'ASC'])]
#[Groups(['part_custom_state:read', 'part_custom_state:write'])]
protected Collection $parameters;
#[Groups(['part_custom_state:read'])]
protected ?\DateTimeImmutable $addedDate = null;
#[Groups(['part_custom_state:read'])]
protected ?\DateTimeImmutable $lastModified = null;
public function __construct()
{
parent::__construct();
$this->children = new ArrayCollection();
$this->attachments = new ArrayCollection();
$this->parameters = new ArrayCollection();
}
}

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace App\Entity\Parts\PartTraits;
use App\Entity\Parts\InfoProviderReference;
use App\Entity\Parts\PartCustomState;
use Doctrine\DBAL\Types\Types;
use App\Entity\Parts\Part;
use Doctrine\ORM\Mapping as ORM;
@ -73,6 +74,14 @@ trait AdvancedPropertyTrait
#[Groups(['full', 'part:read'])]
protected InfoProviderReference $providerReference;
/**
* @var ?PartCustomState the custom state for the part
*/
#[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\ManyToOne(targetEntity: PartCustomState::class)]
#[ORM\JoinColumn(name: 'id_part_custom_state')]
protected ?PartCustomState $partCustomState = null;
/**
* Checks if this part is marked, for that it needs further review.
*/
@ -180,7 +189,24 @@ trait AdvancedPropertyTrait
return $this;
}
/**
* Gets the custom part state for the part
* Returns null if no specific part state is set.
*/
public function getPartCustomState(): ?PartCustomState
{
return $this->partCustomState;
}
/**
* Sets the custom part state.
*
* @return $this
*/
public function setPartCustomState(?PartCustomState $partCustomState): self
{
$this->partCustomState = $partCustomState;
return $this;
}
}

View file

@ -0,0 +1,27 @@
<?php
/**
* 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 <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Form\AdminPages;
class PartCustomStateAdminForm extends BaseEntityAdminForm
{
}

View file

@ -130,6 +130,7 @@ class LogFilterType extends AbstractType
LogTargetType::PART_ASSOCIATION => 'part_association.label',
LogTargetType::BULK_INFO_PROVIDER_IMPORT_JOB => 'bulk_info_provider_import_job.label',
LogTargetType::BULK_INFO_PROVIDER_IMPORT_JOB_PART => 'bulk_info_provider_import_job_part.label',
LogTargetType::PART_CUSTOM_STATE => 'part_custom_state.label',
},
]);

View file

@ -32,6 +32,7 @@ use App\Entity\Parts\Category;
use App\Entity\Parts\Footprint;
use App\Entity\Parts\Manufacturer;
use App\Entity\Parts\MeasurementUnit;
use App\Entity\Parts\PartCustomState;
use App\Entity\Parts\StorageLocation;
use App\Entity\Parts\Supplier;
use App\Entity\ProjectSystem\Project;
@ -139,6 +140,11 @@ class PartFilterType extends AbstractType
'entity_class' => MeasurementUnit::class
]);
$builder->add('partCustomState', StructuralEntityConstraintType::class, [
'label' => 'part.edit.partCustomState',
'entity_class' => PartCustomState::class
]);
$builder->add('lastModified', DateTimeConstraintType::class, [
'label' => 'lastModified'
]);

View file

@ -30,6 +30,7 @@ use App\Entity\Parts\Manufacturer;
use App\Entity\Parts\ManufacturingStatus;
use App\Entity\Parts\MeasurementUnit;
use App\Entity\Parts\Part;
use App\Entity\Parts\PartCustomState;
use App\Entity\PriceInformations\Orderdetail;
use App\Form\AttachmentFormType;
use App\Form\ParameterType;
@ -171,6 +172,12 @@ class PartBaseType extends AbstractType
'disable_not_selectable' => true,
'label' => 'part.edit.partUnit',
])
->add('partCustomState', StructuralEntityType::class, [
'class' => PartCustomState::class,
'required' => false,
'disable_not_selectable' => true,
'label' => 'part.edit.partCustomState',
])
->add('ipn', TextType::class, [
'required' => false,
'empty_data' => null,

View file

@ -0,0 +1,48 @@
<?php
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 <https://www.gnu.org/licenses/>.
*/
namespace App\Repository\Parts;
use App\Entity\Parts\PartCustomState;
use App\Repository\AbstractPartsContainingRepository;
use InvalidArgumentException;
class PartCustomStateRepository extends AbstractPartsContainingRepository
{
public function getParts(object $element, string $nameOrderDirection = "ASC"): array
{
if (!$element instanceof PartCustomState) {
throw new InvalidArgumentException('$element must be an PartCustomState!');
}
return $this->getPartsByField($element, $nameOrderDirection, 'partUnit');
}
public function getPartsCount(object $element): int
{
if (!$element instanceof PartCustomState) {
throw new InvalidArgumentException('$element must be an PartCustomState!');
}
return $this->getPartsCountByField($element, 'partUnit');
}
}

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Security\Voter;
use App\Entity\Attachments\PartCustomStateAttachment;
use App\Services\UserSystem\VoterHelper;
use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\Attachments\AttachmentContainingDBElement;
@ -99,6 +100,8 @@ final class AttachmentVoter extends Voter
$param = 'measurement_units';
} elseif (is_a($subject, PartAttachment::class, true)) {
$param = 'parts';
} elseif (is_a($subject, PartCustomStateAttachment::class, true)) {
$param = 'part_custom_states';
} elseif (is_a($subject, StorageLocationAttachment::class, true)) {
$param = 'storelocations';
} elseif (is_a($subject, SupplierAttachment::class, true)) {

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
*/
namespace App\Security\Voter;
use App\Entity\Parameters\PartCustomStateParameter;
use App\Services\UserSystem\VoterHelper;
use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\Base\AbstractDBElement;
@ -97,6 +98,8 @@ final class ParameterVoter extends Voter
$param = 'measurement_units';
} elseif (is_a($subject, PartParameter::class, true)) {
$param = 'parts';
} elseif (is_a($subject, PartCustomStateParameter::class, true)) {
$param = 'part_custom_states';
} elseif (is_a($subject, StorageLocationParameter::class, true)) {
$param = 'storelocations';
} elseif (is_a($subject, SupplierParameter::class, true)) {

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace App\Security\Voter;
use App\Entity\Attachments\AttachmentType;
use App\Entity\Parts\PartCustomState;
use App\Entity\ProjectSystem\Project;
use App\Entity\Parts\Category;
use App\Entity\Parts\Footprint;
@ -53,6 +54,7 @@ final class StructureVoter extends Voter
Supplier::class => 'suppliers',
Currency::class => 'currencies',
MeasurementUnit::class => 'measurement_units',
PartCustomState::class => 'part_custom_states',
];
public function __construct(private readonly VoterHelper $helper)

View file

@ -30,6 +30,7 @@ use App\Entity\Attachments\AttachmentUpload;
use App\Entity\Attachments\CategoryAttachment;
use App\Entity\Attachments\CurrencyAttachment;
use App\Entity\Attachments\LabelAttachment;
use App\Entity\Attachments\PartCustomStateAttachment;
use App\Entity\Attachments\ProjectAttachment;
use App\Entity\Attachments\FootprintAttachment;
use App\Entity\Attachments\GroupAttachment;
@ -80,6 +81,7 @@ class AttachmentSubmitHandler
//The mapping used to determine which folder will be used for an attachment type
$this->folder_mapping = [
PartAttachment::class => 'part',
PartCustomStateAttachment::class => 'part_custom_state',
AttachmentTypeAttachment::class => 'attachment_type',
CategoryAttachment::class => 'category',
CurrencyAttachment::class => 'currency',

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace App\Services\Attachments;
use App\Entity\Parts\Footprint;
use App\Entity\Parts\PartCustomState;
use App\Entity\ProjectSystem\Project;
use App\Entity\Parts\Category;
use App\Entity\Parts\StorageLocation;

View file

@ -233,6 +233,10 @@ class KiCadHelper
}
$result["fields"]["Part-DB Unit"] = $this->createField($unit);
}
if ($part->getPartCustomState() !== null) {
$customState = $part->getPartCustomState()->getName();
$result["fields"]["Part-DB Custom state"] = $this->createField($customState);
}
if ($part->getMass()) {
$result["fields"]["Mass"] = $this->createField($part->getMass() . ' g');
}

View file

@ -37,6 +37,7 @@ use App\Entity\Parts\Manufacturer;
use App\Entity\Parts\MeasurementUnit;
use App\Entity\Parts\Part;
use App\Entity\Parts\PartAssociation;
use App\Entity\Parts\PartCustomState;
use App\Entity\Parts\PartLot;
use App\Entity\Parts\StorageLocation;
use App\Entity\Parts\Supplier;
@ -83,6 +84,7 @@ class ElementTypeNameGenerator
PartAssociation::class => $this->translator->trans('part_association.label'),
BulkInfoProviderImportJob::class => $this->translator->trans('bulk_info_provider_import_job.label'),
BulkInfoProviderImportJobPart::class => $this->translator->trans('bulk_info_provider_import_job_part.label'),
PartCustomState::class => $this->translator->trans('part_custom_state.label'),
];
}

View file

@ -65,6 +65,7 @@ class PartMerger implements EntityMergerInterface
$this->useOtherValueIfNotNull($target, $other, 'footprint');
$this->useOtherValueIfNotNull($target, $other, 'category');
$this->useOtherValueIfNotNull($target, $other, 'partUnit');
$this->useOtherValueIfNotNull($target, $other, 'partCustomState');
//We assume that the higher value is the correct one for minimum instock
$this->useLargerValue($target, $other, 'minamount');

View file

@ -27,6 +27,7 @@ use App\Entity\Attachments\AttachmentType;
use App\Entity\Attachments\PartAttachment;
use App\Entity\Base\AbstractDBElement;
use App\Entity\Parameters\PartParameter;
use App\Entity\Parts\PartCustomState;
use App\Entity\ProjectSystem\Project;
use App\Entity\LabelSystem\LabelProfile;
use App\Entity\Parts\Category;
@ -107,6 +108,7 @@ class EntityURLGenerator
MeasurementUnit::class => 'measurement_unit_edit',
Group::class => 'group_edit',
LabelProfile::class => 'label_profile_edit',
PartCustomState::class => 'part_custom_state_edit',
];
try {
@ -213,6 +215,7 @@ class EntityURLGenerator
MeasurementUnit::class => 'measurement_unit_edit',
Group::class => 'group_edit',
LabelProfile::class => 'label_profile_edit',
PartCustomState::class => 'part_custom_state_edit',
];
return $this->urlGenerator->generate($this->mapToController($map, $entity), ['id' => $entity->getID()]);
@ -243,6 +246,7 @@ class EntityURLGenerator
MeasurementUnit::class => 'measurement_unit_edit',
Group::class => 'group_edit',
LabelProfile::class => 'label_profile_edit',
PartCustomState::class => 'part_custom_state_edit',
];
return $this->urlGenerator->generate($this->mapToController($map, $entity), ['id' => $entity->getID()]);
@ -274,6 +278,7 @@ class EntityURLGenerator
MeasurementUnit::class => 'measurement_unit_new',
Group::class => 'group_new',
LabelProfile::class => 'label_profile_new',
PartCustomState::class => 'part_custom_state_new',
];
return $this->urlGenerator->generate($this->mapToController($map, $entity));
@ -305,6 +310,7 @@ class EntityURLGenerator
MeasurementUnit::class => 'measurement_unit_clone',
Group::class => 'group_clone',
LabelProfile::class => 'label_profile_clone',
PartCustomState::class => 'part_custom_state_clone',
];
return $this->urlGenerator->generate($this->mapToController($map, $entity), ['id' => $entity->getID()]);
@ -350,6 +356,7 @@ class EntityURLGenerator
MeasurementUnit::class => 'measurement_unit_delete',
Group::class => 'group_delete',
LabelProfile::class => 'label_profile_delete',
PartCustomState::class => 'part_custom_state_delete',
];
return $this->urlGenerator->generate($this->mapToController($map, $entity), ['id' => $entity->getID()]);

View file

@ -29,6 +29,7 @@ use App\Entity\Parts\Category;
use App\Entity\Parts\Footprint;
use App\Entity\Parts\Manufacturer;
use App\Entity\Parts\MeasurementUnit;
use App\Entity\Parts\PartCustomState;
use App\Entity\Parts\StorageLocation;
use App\Entity\Parts\Supplier;
use Doctrine\ORM\EntityManagerInterface;
@ -148,6 +149,26 @@ class PKDatastructureImporter
return is_countable($partunit_data) ? count($partunit_data) : 0;
}
public function importPartCustomStates(array $data): int
{
if (!isset($data['partcustomstate'])) {
throw new \RuntimeException('$data must contain a "partcustomstate" key!');
}
$partCustomStateData = $data['partcustomstate'];
foreach ($partCustomStateData as $partCustomState) {
$customState = new PartCustomState();
$customState->setName($partCustomState['name']);
$this->setIDOfEntity($customState, $partCustomState['id']);
$this->em->persist($customState);
}
$this->em->flush();
return is_countable($partCustomStateData) ? count($partCustomStateData) : 0;
}
public function importCategories(array $data): int
{
if (!isset($data['partcategory'])) {

View file

@ -91,6 +91,8 @@ class PKPartImporter
$this->setAssociationField($entity, 'partUnit', MeasurementUnit::class, $part['partUnit_id']);
}
$this->setAssociationField($entity, 'partCustomState', MeasurementUnit::class, $part['partCustomState_id']);
//Create a part lot to store the stock level and location
$lot = new PartLot();
$lot->setAmount((float) ($part['stockLevel'] ?? 0));

View file

@ -133,7 +133,7 @@ final class SandboxedTwigFactory
Supplier::class => ['getShippingCosts', 'getDefaultCurrency'],
Part::class => ['isNeedsReview', 'getTags', 'getMass', 'getIpn', 'getProviderReference',
'getDescription', 'getComment', 'isFavorite', 'getCategory', 'getFootprint',
'getPartLots', 'getPartUnit', 'useFloatAmount', 'getMinAmount', 'getAmountSum', 'isNotEnoughInstock', 'isAmountUnknown', 'getExpiredAmountSum',
'getPartLots', 'getPartUnit', 'getPartCustomState', 'useFloatAmount', 'getMinAmount', 'getAmountSum', 'isNotEnoughInstock', 'isAmountUnknown', 'getExpiredAmountSum',
'getManufacturerProductUrl', 'getCustomProductURL', 'getManufacturingStatus', 'getManufacturer',
'getManufacturerProductNumber', 'getOrderdetails', 'isObsolete',
'getParameters', 'getGroupedParameters',

View file

@ -29,6 +29,7 @@ use App\Entity\Parts\Footprint;
use App\Entity\Parts\Manufacturer;
use App\Entity\Parts\MeasurementUnit;
use App\Entity\Parts\Part;
use App\Entity\Parts\PartCustomState;
use App\Entity\Parts\StorageLocation;
use App\Entity\Parts\Supplier;
use App\Entity\PriceInformations\Currency;
@ -217,6 +218,12 @@ class ToolsTreeBuilder
$this->urlGenerator->generate('label_profile_new')
))->setIcon('fa-fw fa-treeview fa-solid fa-qrcode');
}
if ($this->security->isGranted('read', new PartCustomState())) {
$nodes[] = (new TreeViewNode(
$this->translator->trans('tree.tools.edit.part_custom_state'),
$this->urlGenerator->generate('part_custom_state_new')
))->setIcon('fa-fw fa-treeview fa-solid fa-tools');
}
if ($this->security->isGranted('create', new Part())) {
$nodes[] = (new TreeViewNode(
$this->translator->trans('tree.tools.edit.part'),

View file

@ -102,6 +102,7 @@ class PermissionPresetsHelper
$this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'attachment_types', PermissionData::ALLOW);
$this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'currencies', PermissionData::ALLOW);
$this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'measurement_units', PermissionData::ALLOW);
$this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'part_custom_states', PermissionData::ALLOW);
$this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'suppliers', PermissionData::ALLOW);
$this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'projects', PermissionData::ALLOW);
@ -131,6 +132,7 @@ class PermissionPresetsHelper
$this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'attachment_types', PermissionData::ALLOW, ['import']);
$this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'currencies', PermissionData::ALLOW, ['import']);
$this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'measurement_units', PermissionData::ALLOW, ['import']);
$this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'part_custom_states', PermissionData::ALLOW, ['import']);
$this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'suppliers', PermissionData::ALLOW, ['import']);
$this->permissionResolver->setAllOperationsOfPermissionExcept($permHolder, 'projects', PermissionData::ALLOW, ['import']);

View file

@ -46,6 +46,7 @@ enum PartTableColumns : string implements TranslatableInterface
case FAVORITE = "favorite";
case MANUFACTURING_STATUS = "manufacturing_status";
case MPN = "manufacturer_product_number";
case CUSTOM_PART_STATE = 'partCustomState';
case MASS = "mass";
case TAGS = "tags";
case ATTACHMENTS = "attachments";

View file

@ -68,7 +68,7 @@ class TableSettings
#[Assert\All([new Assert\Type(PartTableColumns::class)])]
public array $partsDefaultColumns = [PartTableColumns::NAME, PartTableColumns::DESCRIPTION,
PartTableColumns::CATEGORY, PartTableColumns::FOOTPRINT, PartTableColumns::MANUFACTURER,
PartTableColumns::LOCATION, PartTableColumns::AMOUNT];
PartTableColumns::LOCATION, PartTableColumns::AMOUNT, PartTableColumns::CUSTOM_PART_STATE];
#[SettingsParameter(label: new TM("settings.behavior.table.preview_image_min_width"),
formOptions: ['attr' => ['min' => 1, 'max' => 100]],

View file

@ -24,6 +24,7 @@ namespace App\Twig;
use App\Entity\Attachments\Attachment;
use App\Entity\Base\AbstractDBElement;
use App\Entity\Parts\PartCustomState;
use App\Entity\ProjectSystem\Project;
use App\Entity\LabelSystem\LabelProfile;
use App\Entity\Parts\Category;
@ -115,6 +116,7 @@ final class EntityExtension extends AbstractExtension
Currency::class => 'currency',
MeasurementUnit::class => 'measurement_unit',
LabelProfile::class => 'label_profile',
PartCustomState::class => 'part_custom_state',
];
foreach ($map as $class => $type) {

View file

@ -86,7 +86,7 @@
<li class="nav-item">
<a data-bs-toggle="tab" class="nav-link link-anchor" href="#attachments">{% trans %}admin.attachments{% endtrans %}</a>
</li>
{% if entity.parameters is defined %}
{% if entity.parameters is defined and showParameters == true %}
<li class="nav-item">
<a data-bs-toggle="tab" class="nav-link link-anchor" href="#parameters">{% trans %}admin.parameters{% endtrans %}</a>
</li>

View file

@ -0,0 +1,14 @@
{% extends "admin/base_admin.html.twig" %}
{% block card_title %}
<i class="fas fa-balance-scale fa-tools"></i> {% trans %}part_custom_state.caption{% endtrans %}
{% endblock %}
{% block edit_title %}
{% trans %}part_custom_state.edit{% endtrans %}: {{ entity.name }}
{% endblock %}
{% block new_title %}
{% trans %}part_custom_state.new{% endtrans %}
{% endblock %}

View file

@ -3,3 +3,4 @@
{{ form_row(form.mass) }}
{{ form_row(form.ipn) }}
{{ form_row(form.partUnit) }}
{{ form_row(form.partCustomState) }}

View file

@ -36,6 +36,19 @@
</div>
{% endif %}
{% if part.partCustomState is not null %}
<div class="mt-1">
<h6>
<span class="badge bg-primary" title="{% trans %}part_custom_state.caption{% endtrans %}"><i class="fas fa-tools fa-fw"></i> {{ part.partCustomState.name }}</span>
{% if part.partCustomState is not null and part.partCustomState.masterPictureAttachment and attachment_manager.fileExisting(part.partCustomState.masterPictureAttachment) %}
<br/>
<img class="img-fluid img-thumbnail thumbnail-sm" src="{{ attachment_thumbnail(part.partCustomState.masterPictureAttachment, 'thumbnail_md') }}" alt="{% trans %}attachment.preview.alt{% endtrans %}" />
{% endif %}
</h6>
</div>
{% endif %}
{# Favorite Status tag #}
{% if part.favorite %}
<div class="mt-1">

View file

@ -61,6 +61,7 @@
{{ form_row(filterForm.favorite) }}
{{ form_row(filterForm.needsReview) }}
{{ form_row(filterForm.measurementUnit) }}
{{ form_row(filterForm.partCustomState) }}
{{ form_row(filterForm.mass) }}
{{ form_row(filterForm.dbId) }}
{{ form_row(filterForm.ipn) }}

View file

@ -0,0 +1,69 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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 <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Tests\API\Endpoints;
class PartCustomStateEndpointTest extends CrudEndpointTestCase
{
protected function getBasePath(): string
{
return '/api/part_custom_states';
}
public function testGetCollection(): void
{
$this->_testGetCollection();
self::assertJsonContains([
'hydra:totalItems' => 7,
]);
}
public function testGetItem(): void
{
$this->_testGetItem(1);
$this->_testGetItem(2);
$this->_testGetItem(3);
}
public function testCreateItem(): void
{
$this->_testPostItem([
'name' => 'Test API',
'parent' => '/api/part_custom_states/1',
]);
}
public function testUpdateItem(): void
{
$this->_testPatchItem(5, [
'name' => 'Updated',
'parent' => '/api/part_custom_states/2',
]);
}
public function testDeleteItem(): void
{
$this->_testDeleteItem(4);
}
}

View file

@ -0,0 +1,34 @@
<?php
/**
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2020 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 <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Tests\Controller\AdminPages;
use App\Entity\Parts\PartCustomState;
use PHPUnit\Framework\Attributes\Group;
#[Group('slow')]
#[Group('DB')]
class PartCustomStateControllerTest extends AbstractAdminController
{
protected static string $base_path = '/en/part_custom_state';
protected static string $entity_class = PartCustomState::class;
}

View file

@ -29,6 +29,7 @@ use App\Entity\Attachments\AttachmentType;
use App\Entity\Attachments\AttachmentTypeAttachment;
use App\Entity\Attachments\CategoryAttachment;
use App\Entity\Attachments\CurrencyAttachment;
use App\Entity\Attachments\PartCustomStateAttachment;
use App\Entity\Attachments\ProjectAttachment;
use App\Entity\Attachments\FootprintAttachment;
use App\Entity\Attachments\GroupAttachment;
@ -38,6 +39,7 @@ use App\Entity\Attachments\PartAttachment;
use App\Entity\Attachments\StorageLocationAttachment;
use App\Entity\Attachments\SupplierAttachment;
use App\Entity\Attachments\UserAttachment;
use App\Entity\Parts\PartCustomState;
use App\Entity\ProjectSystem\Project;
use App\Entity\Parts\Category;
use App\Entity\Parts\Footprint;
@ -86,6 +88,7 @@ class AttachmentTest extends TestCase
yield [ManufacturerAttachment::class, Manufacturer::class];
yield [MeasurementUnitAttachment::class, MeasurementUnit::class];
yield [PartAttachment::class, Part::class];
yield [PartCustomStateAttachment::class, PartCustomState::class];
yield [StorageLocationAttachment::class, StorageLocation::class];
yield [SupplierAttachment::class, Supplier::class];
yield [UserAttachment::class, User::class];

View file

@ -29,6 +29,7 @@ use App\Entity\Parts\Manufacturer;
use App\Entity\Parts\MeasurementUnit;
use App\Entity\Parts\Part;
use App\Entity\Parts\PartAssociation;
use App\Entity\Parts\PartCustomState;
use App\Entity\Parts\PartLot;
use App\Entity\PriceInformations\Orderdetail;
use App\Services\EntityMergers\Mergers\PartMerger;
@ -54,6 +55,7 @@ class PartMergerTest extends KernelTestCase
$manufacturer1 = new Manufacturer();
$manufacturer2 = new Manufacturer();
$unit = new MeasurementUnit();
$customState = new PartCustomState();
$part1 = (new Part())
->setCategory($category)
@ -62,7 +64,8 @@ class PartMergerTest extends KernelTestCase
$part2 = (new Part())
->setFootprint($footprint)
->setManufacturer($manufacturer2)
->setPartUnit($unit);
->setPartUnit($unit)
->setPartCustomState($customState);
$merged = $this->merger->merge($part1, $part2);
$this->assertSame($merged, $part1);
@ -70,6 +73,7 @@ class PartMergerTest extends KernelTestCase
$this->assertSame($footprint, $merged->getFootprint());
$this->assertSame($manufacturer1, $merged->getManufacturer());
$this->assertSame($unit, $merged->getPartUnit());
$this->assertSame($customState, $merged->getPartCustomState());
}
public function testMergeOfTags(): void

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace App\Tests\Twig;
use App\Entity\Attachments\PartAttachment;
use App\Entity\Parts\PartCustomState;
use App\Entity\ProjectSystem\Project;
use App\Entity\LabelSystem\LabelProfile;
use App\Entity\Parts\Category;
@ -67,6 +68,7 @@ class EntityExtensionTest extends WebTestCase
$this->assertSame('currency', $this->service->getEntityType(new Currency()));
$this->assertSame('measurement_unit', $this->service->getEntityType(new MeasurementUnit()));
$this->assertSame('label_profile', $this->service->getEntityType(new LabelProfile()));
$this->assertSame('part_custom_state', $this->service->getEntityType(new PartCustomState()));
}
}

View file

@ -7437,11 +7437,13 @@
<field Field="lowStock" Type="tinyint(1)" Null="NO" Key="" Extra="" Comment="" />
<field Field="metaPart" Type="tinyint(1)" Null="NO" Key="" Default="0" Extra="" Comment="" />
<field Field="partUnit_id" Type="int(11)" Null="YES" Key="MUL" Default="NULL" Extra="" Comment="" />
<field Field="partCustomState_id" Type="int(11)" Null="YES" Key="MUL" Default="NULL" Extra="" Comment="" />
<field Field="storageLocation_id" Type="int(11)" Null="YES" Key="MUL" Default="NULL" Extra="" Comment="" />
<key Table="part" Non_unique="0" Key_name="PRIMARY" Seq_in_index="1" Column_name="id" Collation="A" Cardinality="3" Null="" Index_type="BTREE" Comment="" Index_comment="" Ignored="NO" />
<key Table="part" Non_unique="1" Key_name="IDX_E93DDFF812469DE2" Seq_in_index="1" Column_name="category_id" Collation="A" Cardinality="3" Null="YES" Index_type="BTREE" Comment="" Index_comment="" Ignored="NO" />
<key Table="part" Non_unique="1" Key_name="IDX_E93DDFF851364C98" Seq_in_index="1" Column_name="footprint_id" Collation="A" Cardinality="3" Null="YES" Index_type="BTREE" Comment="" Index_comment="" Ignored="NO" />
<key Table="part" Non_unique="1" Key_name="IDX_E93DDFF8F7A36E87" Seq_in_index="1" Column_name="partUnit_id" Collation="A" Cardinality="3" Null="YES" Index_type="BTREE" Comment="" Index_comment="" Ignored="NO" />
<key Table="part" Non_unique="1" Key_name="IDX_E93DDFF8F7A36E88" Seq_in_index="1" Column_name="partCustomState_id" Collation="A" Cardinality="3" Null="YES" Index_type="BTREE" Comment="" Index_comment="" Ignored="NO" />
<key Table="part" Non_unique="1" Key_name="IDX_E93DDFF873CD58AF" Seq_in_index="1" Column_name="storageLocation_id" Collation="A" Cardinality="3" Null="YES" Index_type="BTREE" Comment="" Index_comment="" Ignored="NO" />
<options Name="part" Engine="InnoDB" Version="10" Row_format="Dynamic" Rows="3" Avg_row_length="5461" Data_length="16384" Max_data_length="0" Index_length="65536" Data_free="0" Auto_increment="4" Create_time="2023-03-22 22:28:36" Update_time="2023-03-22 22:40:40" Collation="utf8mb3_unicode_ci" Create_options="" Comment="" Max_index_length="0" Temporary="N" />
</table_structure>

View file

@ -548,6 +548,12 @@
<target>Měrné jednotky</target>
</segment>
</unit>
<unit id="3bcKBzY" name="part_custom_state.caption">
<segment state="translated">
<source>part_custom_state.caption</source>
<target>Vlastní stav komponenty</target>
</segment>
</unit>
<unit id="crdkzlg" name="storelocation.labelp">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5</note>
@ -4831,6 +4837,12 @@ Pokud jste to provedli nesprávně nebo pokud počítač již není důvěryhodn
<target>Měrné jednotky</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.table.partCustomState">
<segment state="translated">
<source>part.table.partCustomState</source>
<target>Vlastní stav součásti</target>
</segment>
</unit>
<unit id="R5pc2FR" name="part.table.addedDate">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:236</note>
@ -5695,6 +5707,12 @@ Pokud jste to provedli nesprávně nebo pokud počítač již není důvěryhodn
<target>Měrná jednotka</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.edit.partCustomState">
<segment state="translated">
<source>part.edit.partCustomState</source>
<target>Vlastní stav součásti</target>
</segment>
</unit>
<unit id="oY_9HE9" name="part.edit.comment">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Form\Part\PartBaseType.php:212</note>
@ -5982,6 +6000,12 @@ Pokud jste to provedli nesprávně nebo pokud počítač již není důvěryhodn
<target>Měrná jednotka</target>
</segment>
</unit>
<unit id="a1mPcMw" name="part_custom_state.label">
<segment state="translated">
<source>part_custom_state.label</source>
<target>Vlastní stav součásti</target>
</segment>
</unit>
<unit id="__SP1Zy" name="currency.label">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\ElementTypeNameGenerator.php:90</note>
@ -6225,6 +6249,12 @@ Pokud jste to provedli nesprávně nebo pokud počítač již není důvěryhodn
<target>Měrné jednotky</target>
</segment>
</unit>
<unit id="5adacKb" name="tree.tools.edit.part_custom_state">
<segment state="translated">
<source>tree.tools.edit.part_custom_state</source>
<target>Vlastní stav součásti</target>
</segment>
</unit>
<unit id=".Ux4R3T" name="tree.tools.edit.label_profile">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203</note>
@ -8495,6 +8525,12 @@ Element 3</target>
<target>Měrná jednotka</target>
</segment>
</unit>
<unit id="1b5ja1c" name="perm.part_custom_states">
<segment state="translated">
<source>perm.part_custom_states</source>
<target>Vlastní stav součásti</target>
</segment>
</unit>
<unit id="coNue69" name="user.settings.pw_old.label">
<notes>
<note priority="1">obsolete</note>
@ -10806,6 +10842,12 @@ Element 3</target>
<target>Měrná jednotka</target>
</segment>
</unit>
<unit id="2COnw1k" name="log.element_edited.changed_fields.partCustomState">
<segment state="translated">
<source>log.element_edited.changed_fields.partCustomState</source>
<target>Vlastní stav součásti</target>
</segment>
</unit>
<unit id="c9XMmAp" name="log.element_edited.changed_fields.expiration_date">
<segment state="translated">
<source>log.element_edited.changed_fields.expiration_date</source>
@ -11064,6 +11106,18 @@ Element 3</target>
<target>Upravit měrnou jednotku</target>
</segment>
</unit>
<unit id="ba52d.g" name="part_custom_state.new">
<segment state="translated">
<source>part_custom_state.new</source>
<target>Nový vlastní stav komponenty</target>
</segment>
</unit>
<unit id="c1.gb2d" name="part_custom_state.edit">
<segment state="translated">
<source>part_custom_state.edit</source>
<target>Upravit vlastní stav komponenty</target>
</segment>
</unit>
<unit id="uW2WHHC" name="user.aboutMe.label">
<segment state="translated">
<source>user.aboutMe.label</source>

View file

@ -548,6 +548,12 @@
<target>Måleenhed</target>
</segment>
</unit>
<unit id="3bcKBzY" name="part_custom_state.caption">
<segment state="translated">
<source>part_custom_state.caption</source>
<target>Brugerdefineret komponentstatus</target>
</segment>
</unit>
<unit id="vZGwiMS" name="storelocation.labelp">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5</note>
@ -4838,6 +4844,12 @@ Bemærk også, at uden to-faktor-godkendelse er din konto ikke længere så godt
<target>Måleenhed</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.table.partCustomState">
<segment state="translated">
<source>part.table.partCustomState</source>
<target>Brugerdefineret komponentstatus</target>
</segment>
</unit>
<unit id="pw75u4x" name="part.table.addedDate">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:236</note>
@ -5702,6 +5714,12 @@ Bemærk også, at uden to-faktor-godkendelse er din konto ikke længere så godt
<target>Måleenhed</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.edit.partCustomState">
<segment state="translated">
<source>part.edit.partCustomState</source>
<target>Brugerdefineret deltilstand</target>
</segment>
</unit>
<unit id="LTZRVlq" name="part.edit.comment">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Form\Part\PartBaseType.php:212</note>
@ -5989,6 +6007,12 @@ Bemærk også, at uden to-faktor-godkendelse er din konto ikke længere så godt
<target>Måleenhed</target>
</segment>
</unit>
<unit id="a1mPcMw" name="part_custom_state.label">
<segment state="translated">
<source>part_custom_state.label</source>
<target>Brugerdefineret deltilstand</target>
</segment>
</unit>
<unit id="5lJftbn" name="currency.label">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\ElementTypeNameGenerator.php:90</note>
@ -6232,6 +6256,12 @@ Bemærk også, at uden to-faktor-godkendelse er din konto ikke længere så godt
<target>Måleenhed</target>
</segment>
</unit>
<unit id="5adacKb" name="tree.tools.edit.part_custom_state">
<segment state="translated">
<source>tree.tools.edit.part_custom_state</source>
<target>Brugerdefineret komponenttilstand</target>
</segment>
</unit>
<unit id="YAalchf" name="tree.tools.edit.label_profile">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203</note>
@ -8502,6 +8532,12 @@ Element 3</target>
<target>Måleenhed</target>
</segment>
</unit>
<unit id="1b5ja1c" name="perm.part_custom_states">
<segment state="translated">
<source>perm.part_custom_states</source>
<target>Brugerdefineret komponentstatus</target>
</segment>
</unit>
<unit id="ZrVNh2o" name="user.settings.pw_old.label">
<notes>
<note priority="1">obsolete</note>
@ -10832,6 +10868,12 @@ Element 3</target>
<target>Måleenhed</target>
</segment>
</unit>
<unit id="2COnw1k" name="log.element_edited.changed_fields.partCustomState">
<segment state="translated">
<source>log.element_edited.changed_fields.partCustomState</source>
<target>Brugerdefineret komponentstatus</target>
</segment>
</unit>
<unit id="nwxlI_C" name="log.element_edited.changed_fields.expiration_date">
<segment state="translated">
<source>log.element_edited.changed_fields.expiration_date</source>
@ -11096,6 +11138,18 @@ Oversættelsen
<target>Ret måleenhed</target>
</segment>
</unit>
<unit id="ba52d.g" name="part_custom_state.new">
<segment state="translated">
<source>part_custom_state.new</source>
<target>Ny brugerdefineret komponentstatus</target>
</segment>
</unit>
<unit id="c1.gb2d" name="part_custom_state.edit">
<segment state="translated">
<source>part_custom_state.edit</source>
<target>Rediger brugerdefineret komponentstatus</target>
</segment>
</unit>
<unit id="gRatnCn" name="user.aboutMe.label">
<segment state="translated">
<source>user.aboutMe.label</source>

View file

@ -548,6 +548,12 @@
<target>Maßeinheit</target>
</segment>
</unit>
<unit id="3bcKBzY" name="part_custom_state.caption">
<segment state="translated">
<source>part_custom_state.caption</source>
<target>Benutzerdefinierter Bauteilstatus</target>
</segment>
</unit>
<unit id="crdkzlg" name="storelocation.labelp">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5</note>
@ -4830,6 +4836,12 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr
<target>Maßeinheit</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.table.partCustomState">
<segment state="translated">
<source>part.table.partCustomState</source>
<target>Benutzerdefinierter Bauteilstatus</target>
</segment>
</unit>
<unit id="R5pc2FR" name="part.table.addedDate">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:236</note>
@ -5694,6 +5706,12 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr
<target>Maßeinheit</target>
</segment>
</unit>
<unit id="kE1wJ1a" name="part.edit.partCustomState">
<segment state="translated">
<source>part.edit.partCustomState</source>
<target>Benutzerdefinierter Bauteilstatus</target>
</segment>
</unit>
<unit id="oY_9HE9" name="part.edit.comment">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Form\Part\PartBaseType.php:212</note>
@ -5981,6 +5999,12 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr
<target>Maßeinheit</target>
</segment>
</unit>
<unit id="a1mPcMw" name="part_custom_state.label">
<segment state="translated">
<source>part_custom_state.label</source>
<target>Benutzerdefinierter Bauteilstatus</target>
</segment>
</unit>
<unit id="__SP1Zy" name="currency.label">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\ElementTypeNameGenerator.php:90</note>
@ -6224,6 +6248,12 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr
<target>Maßeinheiten</target>
</segment>
</unit>
<unit id="5adacKb" name="tree.tools.edit.part_custom_state">
<segment state="translated">
<source>tree.tools.edit.part_custom_state</source>
<target>Benutzerdefinierter Bauteilstatus</target>
</segment>
</unit>
<unit id=".Ux4R3T" name="tree.tools.edit.label_profile">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203</note>
@ -8497,6 +8527,12 @@ Element 1 -&gt; Element 1.2</target>
<target>Maßeinheiten</target>
</segment>
</unit>
<unit id="1b5ja1c" name="perm.part_custom_states">
<segment state="translated">
<source>perm.part_custom_states</source>
<target>Benutzerdefinierter Bauteilstatus</target>
</segment>
</unit>
<unit id="coNue69" name="user.settings.pw_old.label">
<notes>
<note priority="1">obsolete</note>
@ -10880,6 +10916,12 @@ Element 1 -&gt; Element 1.2</target>
<target>Maßeinheit</target>
</segment>
</unit>
<unit id="2COnw1k" name="log.element_edited.changed_fields.partCustomState">
<segment state="translated">
<source>log.element_edited.changed_fields.partCustomState</source>
<target>Benutzerdefinierter Bauteilstatus</target>
</segment>
</unit>
<unit id="c9XMmAp" name="log.element_edited.changed_fields.expiration_date">
<segment state="translated">
<source>log.element_edited.changed_fields.expiration_date</source>
@ -11144,6 +11186,18 @@ Element 1 -&gt; Element 1.2</target>
<target>Bearbeite Maßeinheit</target>
</segment>
</unit>
<unit id="ba52d.g" name="part_custom_state.new">
<segment state="translated">
<source>part_custom_state.new</source>
<target>Neuer benutzerdefinierter Bauteilstatus</target>
</segment>
</unit>
<unit id="c1.gb2d" name="part_custom_state.edit">
<segment state="translated">
<source>part_custom_state.edit</source>
<target>Bearbeite benutzerdefinierten Bauteilstatus</target>
</segment>
</unit>
<unit id="uW2WHHC" name="user.aboutMe.label">
<segment state="translated">
<source>user.aboutMe.label</source>

View file

@ -318,6 +318,12 @@
<target>Μονάδα μέτρησης</target>
</segment>
</unit>
<unit id="3bcKBzY" name="part_custom_state.caption">
<segment state="translated">
<source>part_custom_state.caption</source>
<target>Προσαρμοσμένη κατάσταση εξαρτήματος</target>
</segment>
</unit>
<unit id="vZGwiMS" name="storelocation.labelp">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5</note>
@ -1535,5 +1541,53 @@
<target>Επεξεργασία</target>
</segment>
</unit>
<unit id="1b5ja1c" name="perm.part_custom_states">
<segment state="translated">
<source>perm.part_custom_states</source>
<target>Προσαρμοσμένη κατάσταση εξαρτήματος</target>
</segment>
</unit>
<unit id="5adacKb" name="tree.tools.edit.part_custom_state">
<segment state="translated">
<source>tree.tools.edit.part_custom_state</source>
<target>Προσαρμοσμένη κατάσταση εξαρτήματος</target>
</segment>
</unit>
<unit id="ba52d.g" name="part_custom_state.new">
<segment state="translated">
<source>part_custom_state.new</source>
<target>Νέα προσαρμοσμένη κατάσταση εξαρτήματος</target>
</segment>
</unit>
<unit id="c1.gb2d" name="part_custom_state.edit">
<segment state="translated">
<source>part_custom_state.edit</source>
<target>Επεξεργασία προσαρμοσμένης κατάστασης εξαρτήματος</target>
</segment>
</unit>
<unit id="a1mPcMw" name="part_custom_state.label">
<segment state="translated">
<source>part_custom_state.label</source>
<target>Προσαρμοσμένη κατάσταση μέρους</target>
</segment>
</unit>
<unit id="2COnw1k" name="log.element_edited.changed_fields.partCustomState">
<segment state="translated">
<source>log.element_edited.changed_fields.partCustomState</source>
<target>Προσαρμοσμένη κατάσταση εξαρτήματος</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.edit.partCustomState">
<segment state="translated">
<source>part.edit.partCustomState</source>
<target>Προσαρμοσμένη κατάσταση εξαρτήματος</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.table.partCustomState">
<segment state="translated">
<source>part.table.partCustomState</source>
<target>Προσαρμοσμένη κατάσταση μέρους</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -548,6 +548,12 @@
<target>Measurement Unit</target>
</segment>
</unit>
<unit id="3bcKBzY" name="part_custom_state.caption">
<segment state="translated">
<source>part_custom_state.caption</source>
<target>Custom part states</target>
</segment>
</unit>
<unit id="crdkzlg" name="storelocation.labelp">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5</note>
@ -4831,6 +4837,12 @@ If you have done this incorrectly or if a computer is no longer trusted, you can
<target>Measurement Unit</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.table.partCustomState">
<segment state="translated">
<source>part.table.partCustomState</source>
<target>Custom part state</target>
</segment>
</unit>
<unit id="R5pc2FR" name="part.table.addedDate">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:236</note>
@ -5695,6 +5707,12 @@ If you have done this incorrectly or if a computer is no longer trusted, you can
<target>Measuring unit</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.edit.partCustomState">
<segment state="translated">
<source>part.edit.partCustomState</source>
<target>Custom part state</target>
</segment>
</unit>
<unit id="oY_9HE9" name="part.edit.comment">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Form\Part\PartBaseType.php:212</note>
@ -5982,6 +6000,12 @@ If you have done this incorrectly or if a computer is no longer trusted, you can
<target>Measurement unit</target>
</segment>
</unit>
<unit id="a1mPcMw" name="part_custom_state.label">
<segment state="translated">
<source>part_custom_state.label</source>
<target>Custom part state</target>
</segment>
</unit>
<unit id="__SP1Zy" name="currency.label">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\ElementTypeNameGenerator.php:90</note>
@ -6225,6 +6249,12 @@ If you have done this incorrectly or if a computer is no longer trusted, you can
<target>Measurement Unit</target>
</segment>
</unit>
<unit id="5adacKb" name="tree.tools.edit.part_custom_state">
<segment state="translated">
<source>tree.tools.edit.part_custom_state</source>
<target>Custom part states</target>
</segment>
</unit>
<unit id=".Ux4R3T" name="tree.tools.edit.label_profile">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203</note>
@ -8498,6 +8528,12 @@ Element 1 -&gt; Element 1.2</target>
<target>Measurement unit</target>
</segment>
</unit>
<unit id="1b5ja1c" name="perm.part_custom_states">
<segment state="translated">
<source>perm.part_custom_states</source>
<target>Custom part state</target>
</segment>
</unit>
<unit id="coNue69" name="user.settings.pw_old.label">
<notes>
<note priority="1">obsolete</note>
@ -10881,6 +10917,12 @@ Element 1 -&gt; Element 1.2</target>
<target>Measuring Unit</target>
</segment>
</unit>
<unit id="2COnw1k" name="log.element_edited.changed_fields.partCustomState">
<segment state="translated">
<source>log.element_edited.changed_fields.partCustomState</source>
<target>Custom part state</target>
</segment>
</unit>
<unit id="c9XMmAp" name="log.element_edited.changed_fields.expiration_date">
<segment state="translated">
<source>log.element_edited.changed_fields.expiration_date</source>
@ -11145,6 +11187,18 @@ Element 1 -&gt; Element 1.2</target>
<target>Edit Measurement Unit</target>
</segment>
</unit>
<unit id="ba52d.g" name="part_custom_state.new">
<segment state="translated">
<source>part_custom_state.new</source>
<target>New custom part state</target>
</segment>
</unit>
<unit id="c1.gb2d" name="part_custom_state.edit">
<segment state="translated">
<source>part_custom_state.edit</source>
<target>Edit custom part state</target>
</segment>
</unit>
<unit id="uW2WHHC" name="user.aboutMe.label">
<segment state="translated">
<source>user.aboutMe.label</source>

View file

@ -548,6 +548,12 @@
<target>Unidad de medida</target>
</segment>
</unit>
<unit id="3bcKBzY" name="part_custom_state.caption">
<segment state="translated">
<source>part_custom_state.caption</source>
<target>Estado personalizado del componente</target>
</segment>
</unit>
<unit id="crdkzlg" name="storelocation.labelp">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5</note>
@ -4830,6 +4836,12 @@ Subelementos serán desplazados hacia arriba.</target>
<target>Unidad de Medida</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.table.partCustomState">
<segment state="translated">
<source>part.table.partCustomState</source>
<target>Estado personalizado del componente</target>
</segment>
</unit>
<unit id="R5pc2FR" name="part.table.addedDate">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:236</note>
@ -5694,6 +5706,12 @@ Subelementos serán desplazados hacia arriba.</target>
<target>Unidad de medida</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.edit.partCustomState">
<segment state="translated">
<source>part.edit.partCustomState</source>
<target>Estado personalizado de la pieza</target>
</segment>
</unit>
<unit id="oY_9HE9" name="part.edit.comment">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Form\Part\PartBaseType.php:212</note>
@ -5981,6 +5999,12 @@ Subelementos serán desplazados hacia arriba.</target>
<target>Unidad de medida</target>
</segment>
</unit>
<unit id="a1mPcMw" name="part_custom_state.label">
<segment state="translated">
<source>part_custom_state.label</source>
<target>Estado personalizado de la pieza</target>
</segment>
</unit>
<unit id="__SP1Zy" name="currency.label">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\ElementTypeNameGenerator.php:90</note>
@ -6224,6 +6248,12 @@ Subelementos serán desplazados hacia arriba.</target>
<target>Unidad de medida</target>
</segment>
</unit>
<unit id="5adacKb" name="tree.tools.edit.part_custom_state">
<segment state="translated">
<source>tree.tools.edit.part_custom_state</source>
<target>Estado personalizado del componente</target>
</segment>
</unit>
<unit id=".Ux4R3T" name="tree.tools.edit.label_profile">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203</note>
@ -8494,6 +8524,12 @@ Elemento 3</target>
<target>Unidad de medida</target>
</segment>
</unit>
<unit id="1b5ja1c" name="perm.part_custom_states">
<segment state="translated">
<source>perm.part_custom_states</source>
<target>Estado personalizado del componente</target>
</segment>
</unit>
<unit id="coNue69" name="user.settings.pw_old.label">
<notes>
<note priority="1">obsolete</note>
@ -10824,6 +10860,12 @@ Elemento 3</target>
<target>Unidad de medida</target>
</segment>
</unit>
<unit id="2COnw1k" name="log.element_edited.changed_fields.partCustomState">
<segment state="translated">
<source>log.element_edited.changed_fields.partCustomState</source>
<target>Estado personalizado del componente</target>
</segment>
</unit>
<unit id="c9XMmAp" name="log.element_edited.changed_fields.expiration_date">
<segment state="translated">
<source>log.element_edited.changed_fields.expiration_date</source>
@ -11082,6 +11124,18 @@ Elemento 3</target>
<target>Editar Unidad de Medida</target>
</segment>
</unit>
<unit id="ba52d.g" name="part_custom_state.new">
<segment state="translated">
<source>part_custom_state.new</source>
<target>Nuevo estado personalizado del componente</target>
</segment>
</unit>
<unit id="c1.gb2d" name="part_custom_state.edit">
<segment state="translated">
<source>part_custom_state.edit</source>
<target>Editar estado personalizado del componente</target>
</segment>
</unit>
<unit id="uW2WHHC" name="user.aboutMe.label">
<segment state="translated">
<source>user.aboutMe.label</source>

View file

@ -517,6 +517,12 @@
<target>Unité de mesure</target>
</segment>
</unit>
<unit id="3bcKBzY" name="part_custom_state.caption">
<segment state="translated">
<source>part_custom_state.caption</source>
<target>État personnalisé du composant</target>
</segment>
</unit>
<unit id="vZGwiMS" name="storelocation.labelp">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5</note>
@ -4793,6 +4799,12 @@ Si vous avez fait cela de manière incorrecte ou si un ordinateur n'est plus fia
<target>Unité de mesure</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.table.partCustomState">
<segment state="translated">
<source>part.table.partCustomState</source>
<target>État personnalisé de la pièce</target>
</segment>
</unit>
<unit id="pw75u4x" name="part.table.addedDate">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:236</note>
@ -5657,6 +5669,12 @@ Si vous avez fait cela de manière incorrecte ou si un ordinateur n'est plus fia
<target>Unité de mesure</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.edit.partCustomState">
<segment state="translated">
<source>part.edit.partCustomState</source>
<target>État personnalisé de la pièce</target>
</segment>
</unit>
<unit id="LTZRVlq" name="part.edit.comment">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Form\Part\PartBaseType.php:212</note>
@ -5934,6 +5952,12 @@ Si vous avez fait cela de manière incorrecte ou si un ordinateur n'est plus fia
<target>Unité de mesure</target>
</segment>
</unit>
<unit id="a1mPcMw" name="part_custom_state.label">
<segment state="translated">
<source>part_custom_state.label</source>
<target>État personnalisé de la pièce</target>
</segment>
</unit>
<unit id="5lJftbn" name="currency.label">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\ElementTypeNameGenerator.php:90</note>
@ -6166,6 +6190,12 @@ Si vous avez fait cela de manière incorrecte ou si un ordinateur n'est plus fia
<target>Unité de mesure</target>
</segment>
</unit>
<unit id="5adacKb" name="tree.tools.edit.part_custom_state">
<segment state="translated">
<source>tree.tools.edit.part_custom_state</source>
<target>État personnalisé de la pièce</target>
</segment>
</unit>
<unit id="YAalchf" name="tree.tools.edit.label_profile">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203</note>
@ -8423,6 +8453,12 @@ exemple de ville</target>
<target>Unités de mesure</target>
</segment>
</unit>
<unit id="1b5ja1c" name="perm.part_custom_states">
<segment state="translated">
<source>perm.part_custom_states</source>
<target>État personnalisé du composant</target>
</segment>
</unit>
<unit id="ZrVNh2o" name="user.settings.pw_old.label">
<notes>
<note priority="1">obsolete</note>
@ -9097,5 +9133,23 @@ exemple de ville</target>
<target>Si vous avez des questions à propos de Part-DB , rendez vous sur &lt;a href="%href%" class="link-external" target="_blank"&gt;Github&lt;/a&gt;</target>
</segment>
</unit>
<unit id="ba52d.g" name="part_custom_state.new">
<segment state="translated">
<source>part_custom_state.new</source>
<target>Nouveau statut personnalisé du composant</target>
</segment>
</unit>
<unit id="c1.gb2d" name="part_custom_state.edit">
<segment state="translated">
<source>part_custom_state.edit</source>
<target>Modifier le statut personnalisé du composant</target>
</segment>
</unit>
<unit id="2COnw1k" name="log.element_edited.changed_fields.partCustomState">
<segment state="translated">
<source>log.element_edited.changed_fields.partCustomState</source>
<target>État personnalisé de la pièce</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -548,6 +548,12 @@
<target>Unità di misura</target>
</segment>
</unit>
<unit id="3bcKBzY" name="part_custom_state.caption">
<segment state="translated">
<source>part_custom_state.caption</source>
<target>Stato personalizzato del componente</target>
</segment>
</unit>
<unit id="crdkzlg" name="storelocation.labelp">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5</note>
@ -4832,6 +4838,12 @@ Se è stato fatto in modo errato o se un computer non è più attendibile, puoi
<target>Unità di misura</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.table.partCustomState">
<segment state="translated">
<source>part.table.partCustomState</source>
<target>Stato personalizzato del componente</target>
</segment>
</unit>
<unit id="R5pc2FR" name="part.table.addedDate">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:236</note>
@ -5696,6 +5708,12 @@ Se è stato fatto in modo errato o se un computer non è più attendibile, puoi
<target>Unità di misura</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.edit.partCustomState">
<segment state="translated">
<source>part.edit.partCustomState</source>
<target>Stato personalizzato della parte</target>
</segment>
</unit>
<unit id="oY_9HE9" name="part.edit.comment">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Form\Part\PartBaseType.php:212</note>
@ -5983,6 +6001,12 @@ Se è stato fatto in modo errato o se un computer non è più attendibile, puoi
<target>Unità di misura</target>
</segment>
</unit>
<unit id="a1mPcMw" name="part_custom_state.label">
<segment state="translated">
<source>part_custom_state.label</source>
<target>Stato personalizzato della parte</target>
</segment>
</unit>
<unit id="__SP1Zy" name="currency.label">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\ElementTypeNameGenerator.php:90</note>
@ -6226,6 +6250,12 @@ Se è stato fatto in modo errato o se un computer non è più attendibile, puoi
<target>Unità di misura</target>
</segment>
</unit>
<unit id="5adacKb" name="tree.tools.edit.part_custom_state">
<segment state="translated">
<source>tree.tools.edit.part_custom_state</source>
<target>Stato personalizzato del componente</target>
</segment>
</unit>
<unit id=".Ux4R3T" name="tree.tools.edit.label_profile">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203</note>
@ -8496,6 +8526,12 @@ Element 3</target>
<target>Unità di misura</target>
</segment>
</unit>
<unit id="1b5ja1c" name="perm.part_custom_states">
<segment state="translated">
<source>perm.part_custom_states</source>
<target>Stato personalizzato del componente</target>
</segment>
</unit>
<unit id="coNue69" name="user.settings.pw_old.label">
<notes>
<note priority="1">obsolete</note>
@ -10826,6 +10862,12 @@ Element 3</target>
<target>Unità di misura</target>
</segment>
</unit>
<unit id="2COnw1k" name="log.element_edited.changed_fields.partCustomState">
<segment state="translated">
<source>log.element_edited.changed_fields.partCustomState</source>
<target>Stato personalizzato della parte</target>
</segment>
</unit>
<unit id="c9XMmAp" name="log.element_edited.changed_fields.expiration_date">
<segment state="translated">
<source>log.element_edited.changed_fields.expiration_date</source>
@ -11084,6 +11126,18 @@ Element 3</target>
<target>Modificare l'unità di misura</target>
</segment>
</unit>
<unit id="ba52d.g" name="part_custom_state.new">
<segment state="translated">
<source>part_custom_state.new</source>
<target>Nuovo stato personalizzato del componente</target>
</segment>
</unit>
<unit id="c1.gb2d" name="part_custom_state.edit">
<segment state="translated">
<source>part_custom_state.edit</source>
<target>Modifica stato personalizzato del componente</target>
</segment>
</unit>
<unit id="uW2WHHC" name="user.aboutMe.label">
<segment state="translated">
<source>user.aboutMe.label</source>

View file

@ -517,6 +517,12 @@
<target>単位</target>
</segment>
</unit>
<unit id="3bcKBzY" name="part_custom_state.caption">
<segment state="translated">
<source>part_custom_state.caption</source>
<target>部品のカスタム状態</target>
</segment>
</unit>
<unit id="vZGwiMS" name="storelocation.labelp">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5</note>
@ -4793,6 +4799,12 @@
<target>単位</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.table.partCustomState">
<segment state="translated">
<source>part.table.partCustomState</source>
<target>部品のカスタム状態</target>
</segment>
</unit>
<unit id="pw75u4x" name="part.table.addedDate">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:236</note>
@ -5657,6 +5669,12 @@
<target>単位</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.edit.partCustomState">
<segment state="translated">
<source>part.edit.partCustomState</source>
<target>部品のカスタム状態</target>
</segment>
</unit>
<unit id="LTZRVlq" name="part.edit.comment">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Form\Part\PartBaseType.php:212</note>
@ -5934,6 +5952,12 @@
<target>単位</target>
</segment>
</unit>
<unit id="a1mPcMw" name="part_custom_state.label">
<segment state="translated">
<source>part_custom_state.label</source>
<target>部品のカスタム状態</target>
</segment>
</unit>
<unit id="5lJftbn" name="currency.label">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\ElementTypeNameGenerator.php:90</note>
@ -6166,6 +6190,12 @@
<target>単位</target>
</segment>
</unit>
<unit id="5adacKb" name="tree.tools.edit.part_custom_state">
<segment state="translated">
<source>tree.tools.edit.part_custom_state</source>
<target>部品のカスタム状態</target>
</segment>
</unit>
<unit id="YAalchf" name="tree.tools.edit.label_profile">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203</note>
@ -8424,6 +8454,12 @@ Exampletown</target>
<target>単位</target>
</segment>
</unit>
<unit id="1b5ja1c" name="perm.part_custom_states">
<segment state="translated">
<source>perm.part_custom_states</source>
<target>部品のカスタム状態</target>
</segment>
</unit>
<unit id="ZrVNh2o" name="user.settings.pw_old.label">
<notes>
<note priority="1">obsolete</note>
@ -8834,5 +8870,23 @@ Exampletown</target>
<target>Part-DBについての質問は、&lt;a href="%href%" class="link-external" target="_blank"&gt;GitHub&lt;/a&gt; にスレッドがあります。</target>
</segment>
</unit>
<unit id="ba52d.g" name="part_custom_state.new">
<segment state="translated">
<source>part_custom_state.new</source>
<target>部品の新しいカスタム状態</target>
</segment>
</unit>
<unit id="c1.gb2d" name="part_custom_state.edit">
<segment state="translated">
<source>part_custom_state.edit</source>
<target>部品のカスタム状態を編集</target>
</segment>
</unit>
<unit id="2COnw1k" name="log.element_edited.changed_fields.partCustomState">
<segment state="translated">
<source>log.element_edited.changed_fields.partCustomState</source>
<target>部品のカスタム状態</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -548,6 +548,12 @@
<target>Meeteenheden</target>
</segment>
</unit>
<unit id="3bcKBzY" name="part_custom_state.caption">
<segment state="translated">
<source>part_custom_state.caption</source>
<target>Aangepaste status van het onderdeel</target>
</segment>
</unit>
<unit id="vZGwiMS" name="storelocation.labelp">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5</note>
@ -724,5 +730,53 @@
<target>Weet u zeker dat u wilt doorgaan?</target>
</segment>
</unit>
<unit id="1b5ja1c" name="perm.part_custom_states">
<segment state="translated">
<source>perm.part_custom_states</source>
<target>Aangepaste status van onderdeel</target>
</segment>
</unit>
<unit id="5adacKb" name="tree.tools.edit.part_custom_state">
<segment state="translated">
<source>tree.tools.edit.part_custom_state</source>
<target>Aangepaste status van het onderdeel</target>
</segment>
</unit>
<unit id="ba52d.g" name="part_custom_state.new">
<segment state="translated">
<source>part_custom_state.new</source>
<target>Nieuwe aangepaste status van het onderdeel</target>
</segment>
</unit>
<unit id="c1.gb2d" name="part_custom_state.edit">
<segment state="translated">
<source>part_custom_state.edit</source>
<target>Aangepaste status van het onderdeel bewerken</target>
</segment>
</unit>
<unit id="a1mPcMw" name="part_custom_state.label">
<segment state="translated">
<source>part_custom_state.label</source>
<target>Aangepaste staat van onderdeel</target>
</segment>
</unit>
<unit id="2COnw1k" name="log.element_edited.changed_fields.partCustomState">
<segment state="translated">
<source>log.element_edited.changed_fields.partCustomState</source>
<target>Aangepaste staat van het onderdeel</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.edit.partCustomState">
<segment state="translated">
<source>part.edit.partCustomState</source>
<target>Aangepaste staat van het onderdeel</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.table.partCustomState">
<segment state="translated">
<source>part.table.partCustomState</source>
<target>Aangepaste status van onderdeel</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -548,6 +548,12 @@
<target>Jednostka miary</target>
</segment>
</unit>
<unit id="3bcKBzY" name="part_custom_state.caption">
<segment state="translated">
<source>part_custom_state.caption</source>
<target>Stan niestandardowy komponentu</target>
</segment>
</unit>
<unit id="crdkzlg" name="storelocation.labelp">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5</note>
@ -4835,6 +4841,12 @@ Jeśli zrobiłeś to niepoprawnie lub komputer nie jest już godny zaufania, mo
<target>Jednostka pomiarowa</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.table.partCustomState">
<segment state="translated">
<source>part.table.partCustomState</source>
<target>Niestandardowy stan części</target>
</segment>
</unit>
<unit id="R5pc2FR" name="part.table.addedDate">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:236</note>
@ -5699,6 +5711,12 @@ Jeśli zrobiłeś to niepoprawnie lub komputer nie jest już godny zaufania, mo
<target>Jednostka pomiarowa</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.edit.partCustomState">
<segment state="translated">
<source>part.edit.partCustomState</source>
<target>Własny stan części</target>
</segment>
</unit>
<unit id="oY_9HE9" name="part.edit.comment">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Form\Part\PartBaseType.php:212</note>
@ -5986,6 +6004,12 @@ Jeśli zrobiłeś to niepoprawnie lub komputer nie jest już godny zaufania, mo
<target>Jednostka pomiarowa</target>
</segment>
</unit>
<unit id="a1mPcMw" name="part_custom_state.label">
<segment state="translated">
<source>part_custom_state.label</source>
<target>Własny stan części</target>
</segment>
</unit>
<unit id="__SP1Zy" name="currency.label">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\ElementTypeNameGenerator.php:90</note>
@ -6229,6 +6253,12 @@ Jeśli zrobiłeś to niepoprawnie lub komputer nie jest już godny zaufania, mo
<target>Jednostka miary</target>
</segment>
</unit>
<unit id="5adacKb" name="tree.tools.edit.part_custom_state">
<segment state="translated">
<source>tree.tools.edit.part_custom_state</source>
<target>Niestandardowy stan części</target>
</segment>
</unit>
<unit id=".Ux4R3T" name="tree.tools.edit.label_profile">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203</note>
@ -8499,6 +8529,12 @@ Element 3</target>
<target>Jednostka miary</target>
</segment>
</unit>
<unit id="1b5ja1c" name="perm.part_custom_states">
<segment state="translated">
<source>perm.part_custom_states</source>
<target>Stan niestandardowy komponentu</target>
</segment>
</unit>
<unit id="coNue69" name="user.settings.pw_old.label">
<notes>
<note priority="1">obsolete</note>
@ -10829,6 +10865,12 @@ Element 3</target>
<target>Jednostka pomiarowa</target>
</segment>
</unit>
<unit id="2COnw1k" name="log.element_edited.changed_fields.partCustomState">
<segment state="translated">
<source>log.element_edited.changed_fields.partCustomState</source>
<target>Niestandardowy stan części</target>
</segment>
</unit>
<unit id="c9XMmAp" name="log.element_edited.changed_fields.expiration_date">
<segment state="translated">
<source>log.element_edited.changed_fields.expiration_date</source>
@ -11087,6 +11129,18 @@ Element 3</target>
<target>Edytuj jednostkę miary</target>
</segment>
</unit>
<unit id="ba52d.g" name="part_custom_state.new">
<segment state="translated">
<source>part_custom_state.new</source>
<target>Nowy niestandardowy stan części</target>
</segment>
</unit>
<unit id="c1.gb2d" name="part_custom_state.edit">
<segment state="translated">
<source>part_custom_state.edit</source>
<target>Edytuj niestandardowy stan części</target>
</segment>
</unit>
<unit id="uW2WHHC" name="user.aboutMe.label">
<segment state="translated">
<source>user.aboutMe.label</source>

View file

@ -548,6 +548,12 @@
<target>Единица измерения</target>
</segment>
</unit>
<unit id="3bcKBzY" name="part_custom_state.caption">
<segment state="translated">
<source>part_custom_state.caption</source>
<target>Пользовательское состояние компонента</target>
</segment>
</unit>
<unit id="crdkzlg" name="storelocation.labelp">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5</note>
@ -4841,6 +4847,12 @@
<target>Единица измерения</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.table.partCustomState">
<segment state="translated">
<source>part.table.partCustomState</source>
<target>Пользовательское состояние детали</target>
</segment>
</unit>
<unit id="R5pc2FR" name="part.table.addedDate">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:236</note>
@ -5705,6 +5717,12 @@
<target>Единица измерения</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.edit.partCustomState">
<segment state="translated">
<source>part.edit.partCustomState</source>
<target>Пользовательское состояние части</target>
</segment>
</unit>
<unit id="oY_9HE9" name="part.edit.comment">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Form\Part\PartBaseType.php:212</note>
@ -5992,6 +6010,12 @@
<target>Единица измерения</target>
</segment>
</unit>
<unit id="a1mPcMw" name="part_custom_state.label">
<segment state="translated">
<source>part_custom_state.label</source>
<target>Пользовательское состояние детали</target>
</segment>
</unit>
<unit id="__SP1Zy" name="currency.label">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\ElementTypeNameGenerator.php:90</note>
@ -6235,6 +6259,12 @@
<target>Единица измерения</target>
</segment>
</unit>
<unit id="5adacKb" name="tree.tools.edit.part_custom_state">
<segment state="translated">
<source>tree.tools.edit.part_custom_state</source>
<target>Пользовательское состояние компонента</target>
</segment>
</unit>
<unit id=".Ux4R3T" name="tree.tools.edit.label_profile">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203</note>
@ -8503,6 +8533,12 @@
<target>Единица измерения</target>
</segment>
</unit>
<unit id="1b5ja1c" name="perm.part_custom_states">
<segment state="translated">
<source>perm.part_custom_states</source>
<target>Пользовательское состояние компонента</target>
</segment>
</unit>
<unit id="coNue69" name="user.settings.pw_old.label">
<notes>
<note priority="1">obsolete</note>
@ -10833,6 +10869,12 @@
<target>Единица измерения</target>
</segment>
</unit>
<unit id="2COnw1k" name="log.element_edited.changed_fields.partCustomState">
<segment state="translated">
<source>log.element_edited.changed_fields.partCustomState</source>
<target>Пользовательское состояние детали</target>
</segment>
</unit>
<unit id="c9XMmAp" name="log.element_edited.changed_fields.expiration_date">
<segment state="translated">
<source>log.element_edited.changed_fields.expiration_date</source>
@ -11091,6 +11133,18 @@
<target>Изменить единицу измерения</target>
</segment>
</unit>
<unit id="ba52d.g" name="part_custom_state.new">
<segment state="translated">
<source>part_custom_state.new</source>
<target>Новое пользовательское состояние компонента</target>
</segment>
</unit>
<unit id="c1.gb2d" name="part_custom_state.edit">
<segment state="translated">
<source>part_custom_state.edit</source>
<target>Редактировать пользовательское состояние компонента</target>
</segment>
</unit>
<unit id="uW2WHHC" name="user.aboutMe.label">
<segment state="translated">
<source>user.aboutMe.label</source>

View file

@ -548,6 +548,12 @@
<target>计量单位</target>
</segment>
</unit>
<unit id="3bcKBzY" name="part_custom_state.caption">
<segment state="translated">
<source>part_custom_state.caption</source>
<target>部件的自定义状态</target>
</segment>
</unit>
<unit id="vZGwiMS" name="storelocation.labelp">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\AdminPages\StorelocationAdmin.html.twig:5</note>
@ -4839,6 +4845,12 @@
<target>计量单位</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.table.partCustomState">
<segment state="translated">
<source>part.table.partCustomState</source>
<target>部件的自定义状态</target>
</segment>
</unit>
<unit id="pw75u4x" name="part.table.addedDate">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:236</note>
@ -5703,6 +5715,12 @@
<target>计量单位</target>
</segment>
</unit>
<unit id="G1hmQdb" name="part.edit.partCustomState">
<segment state="translated">
<source>part.edit.partCustomState</source>
<target>部件的自定义状态</target>
</segment>
</unit>
<unit id="LTZRVlq" name="part.edit.comment">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Form\Part\PartBaseType.php:212</note>
@ -5990,6 +6008,12 @@
<target>计量单位</target>
</segment>
</unit>
<unit id="a1mPcMw" name="part_custom_state.label">
<segment state="translated">
<source>part_custom_state.label</source>
<target>部件自定义状态</target>
</segment>
</unit>
<unit id="5lJftbn" name="currency.label">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\ElementTypeNameGenerator.php:90</note>
@ -6233,6 +6257,12 @@
<target>计量单位</target>
</segment>
</unit>
<unit id="5adacKb" name="tree.tools.edit.part_custom_state">
<segment state="translated">
<source>tree.tools.edit.part_custom_state</source>
<target>部件自定义状态</target>
</segment>
</unit>
<unit id="YAalchf" name="tree.tools.edit.label_profile">
<notes>
<note category="file-source" priority="1">Part-DB1\src\Services\Trees\ToolsTreeBuilder.php:203</note>
@ -8502,6 +8532,12 @@ Element 3</target>
<target>计量单位</target>
</segment>
</unit>
<unit id="1b5ja1c" name="perm.part_custom_states">
<segment state="translated">
<source>perm.part_custom_states</source>
<target>部件的自定义状态</target>
</segment>
</unit>
<unit id="ZrVNh2o" name="user.settings.pw_old.label">
<notes>
<note priority="1">obsolete</note>
@ -10832,6 +10868,12 @@ Element 3</target>
<target>计量单位</target>
</segment>
</unit>
<unit id="2COnw1k" name="log.element_edited.changed_fields.partCustomState">
<segment state="translated">
<source>log.element_edited.changed_fields.partCustomState</source>
<target>部件的自定义状态</target>
</segment>
</unit>
<unit id="nwxlI_C" name="log.element_edited.changed_fields.expiration_date">
<segment state="translated">
<source>log.element_edited.changed_fields.expiration_date</source>
@ -11090,6 +11132,18 @@ Element 3</target>
<target>编辑度量单位</target>
</segment>
</unit>
<unit id="ba52d.g" name="part_custom_state.new">
<segment state="translated">
<source>part_custom_state.new</source>
<target>部件的新自定义状态</target>
</segment>
</unit>
<unit id="c1.gb2d" name="part_custom_state.edit">
<segment state="translated">
<source>part_custom_state.edit</source>
<target>编辑部件的自定义状态</target>
</segment>
</unit>
<unit id="gRatnCn" name="user.aboutMe.label">
<segment state="translated">
<source>user.aboutMe.label</source>