Assemblies einführen

This commit is contained in:
Marcel Diegelmann 2025-03-19 08:13:45 +01:00
parent e1418dfdc1
commit 6fa960df42
107 changed files with 14101 additions and 96 deletions

View file

@ -0,0 +1,70 @@
import {Controller} from "@hotwired/stimulus";
import "tom-select/dist/css/tom-select.bootstrap5.css";
import '../../css/components/tom-select_extensions.css';
import TomSelect from "tom-select";
import {marked} from "marked";
export default class extends Controller {
_tomSelect;
connect() {
let settings = {
allowEmptyOption: true,
plugins: ['dropdown_input', 'clear_button'],
searchField: ["name", "description", "category", "footprint"],
valueField: "id",
labelField: "name",
preload: "focus",
render: {
item: (data, escape) => {
return '<span>' + (data.image ? "<img style='height: 1.5rem; margin-right: 5px;' ' src='" + data.image + "'/>" : "") + escape(data.name) + '</span>';
},
option: (data, escape) => {
if(data.text) {
return '<span>' + escape(data.text) + '</span>';
}
let tmp = '<div class="row m-0">' +
"<div class='col-2 p-0 d-flex align-items-center' style='max-width: 80px;'>" +
(data.image ? "<img class='typeahead-image' src='" + data.image + "'/>" : "") +
"</div>" +
"<div class='col-10'>" +
'<h6 class="m-0">' + escape(data.name) + '</h6>' +
(data.description ? '<p class="m-0">' + marked.parseInline(data.description) + '</p>' : "") +
(data.category ? '<p class="m-0"><span class="fa-solid fa-tags fa-fw"></span> ' + escape(data.category) : "");
return tmp + '</p>' +
'</div></div>';
}
}
};
if (this.element.dataset.autocomplete) {
const base_url = this.element.dataset.autocomplete;
settings.valueField = "id";
settings.load = (query, callback) => {
const url = base_url.replace('__QUERY__', encodeURIComponent(query));
fetch(url)
.then(response => response.json())
.then(json => {callback(json);})
.catch(() => {
callback()
});
};
this._tomSelect = new TomSelect(this.element, settings);
//this._tomSelect.clearOptions();
}
}
disconnect() {
super.disconnect();
//Destroy the TomSelect instance
this._tomSelect.destroy();
}
}

View file

@ -12,7 +12,7 @@ export default class extends Controller {
let settings = {
allowEmptyOption: true,
plugins: ['dropdown_input'],
plugins: ['dropdown_input', 'clear_button'],
searchField: ["name", "description", "category", "footprint"],
valueField: "id",
labelField: "name",

View file

@ -38,7 +38,7 @@ export default class extends Controller {
connect() {
//Add event listener to the checkbox
this.getCheckbox().addEventListener('change', this.toggleInputLimits.bind(this));
this.getCheckbox()?.addEventListener('change', this.toggleInputLimits.bind(this));
}
toggleInputLimits() {

View file

@ -121,6 +121,10 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
<<: *PART_CONTAINING
label: "perm.projects"
assemblies:
<<: *PART_CONTAINING
label: "perm.assemblies"
attachment_types:
<<: *PART_CONTAINING
label: "perm.part.attachment_types"

View file

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20250304081039 extends AbstractMigration
{
public function up(Schema $schema): void
{
$this->addSql('CREATE TABLE assemblies (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, comment LONGTEXT NOT NULL, not_selectable TINYINT(1) NOT NULL, alternative_names LONGTEXT DEFAULT NULL, order_quantity INT NOT NULL, status VARCHAR(64) DEFAULT NULL, order_only_missing_parts TINYINT(1) NOT NULL, description LONGTEXT NOT NULL, parent_id INT DEFAULT NULL, id_preview_attachment INT DEFAULT NULL, INDEX IDX_5F3832C0727ACA70 (parent_id), INDEX IDX_5F3832C0EA7100A1 (id_preview_attachment), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci`');
$this->addSql('CREATE TABLE assembly_bom_entries (id INT AUTO_INCREMENT NOT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames LONGTEXT NOT NULL, name VARCHAR(255) DEFAULT NULL, comment LONGTEXT NOT NULL, price NUMERIC(11, 5) DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, id_assembly INT DEFAULT NULL, id_part INT DEFAULT NULL, price_currency_id INT DEFAULT NULL, INDEX IDX_8C74887E2F180363 (id_assembly), INDEX IDX_8C74887EC22F6CC4 (id_part), INDEX IDX_8C74887E3FFDCD60 (price_currency_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci`');
$this->addSql('ALTER TABLE assemblies ADD CONSTRAINT FK_5F3832C0727ACA70 FOREIGN KEY (parent_id) REFERENCES assemblies (id)');
$this->addSql('ALTER TABLE assemblies ADD CONSTRAINT FK_5F3832C0EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES `attachments` (id) ON DELETE SET NULL');
$this->addSql('ALTER TABLE assembly_bom_entries ADD CONSTRAINT FK_8C74887E2F180363 FOREIGN KEY (id_assembly) REFERENCES assemblies (id)');
$this->addSql('ALTER TABLE assembly_bom_entries ADD CONSTRAINT FK_8C74887EC22F6CC4 FOREIGN KEY (id_part) REFERENCES `parts` (id)');
$this->addSql('ALTER TABLE assembly_bom_entries ADD CONSTRAINT FK_8C74887E3FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id)');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE assemblies DROP FOREIGN KEY FK_5F3832C0727ACA70');
$this->addSql('ALTER TABLE assemblies DROP FOREIGN KEY FK_5F3832C0EA7100A1');
$this->addSql('ALTER TABLE assembly_bom_entries DROP FOREIGN KEY FK_8C74887E2F180363');
$this->addSql('ALTER TABLE assembly_bom_entries DROP FOREIGN KEY FK_8C74887EC22F6CC4');
$this->addSql('ALTER TABLE assembly_bom_entries DROP FOREIGN KEY FK_8C74887E3FFDCD60');
$this->addSql('DROP TABLE assemblies');
$this->addSql('DROP TABLE assembly_bom_entries');
}
}

View file

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20250304154507 extends AbstractMigration
{
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE parts ADD built_assembly_id INT DEFAULT NULL');
$this->addSql('ALTER TABLE parts ADD CONSTRAINT FK_6940A7FECC660B3C FOREIGN KEY (built_assembly_id) REFERENCES assemblies (id)');
$this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FECC660B3C ON parts (built_assembly_id)');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE `parts` DROP FOREIGN KEY FK_6940A7FECC660B3C');
$this->addSql('DROP INDEX UNIQ_6940A7FECC660B3C ON `parts`');
$this->addSql('ALTER TABLE `parts` DROP built_assembly_id');
}
}

View file

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20250310160354 extends AbstractMigration
{
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE assembly_bom_entries RENAME INDEX idx_8c74887e2f180363 TO IDX_8C74887E4AD2039E');
$this->addSql('ALTER TABLE project_bom_entries ADD id_assembly INT DEFAULT NULL AFTER id_part');
$this->addSql('ALTER TABLE project_bom_entries ADD CONSTRAINT FK_1AA2DD314AD2039E FOREIGN KEY (id_assembly) REFERENCES assemblies (id)');
$this->addSql('CREATE INDEX IDX_1AA2DD314AD2039E ON project_bom_entries (id_assembly)');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE assembly_bom_entries RENAME INDEX idx_8c74887e4ad2039e TO IDX_8C74887E2F180363');
$this->addSql('ALTER TABLE project_bom_entries DROP FOREIGN KEY FK_1AA2DD314AD2039E');
$this->addSql('DROP INDEX IDX_1AA2DD314AD2039E ON project_bom_entries');
$this->addSql('ALTER TABLE project_bom_entries DROP id_assembly');
}
}

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Command\Migrations;
use App\Entity\AssemblySystem\Assembly;
use Symfony\Component\Console\Attribute\AsCommand;
use App\Entity\Attachments\AttachmentType;
use App\Entity\Base\AbstractNamedDBElement;
@ -88,6 +89,7 @@ class ConvertBBCodeCommand extends Command
AttachmentType::class => ['comment'],
StorageLocation::class => ['comment'],
Project::class => ['comment'],
Assembly::class => ['comment'],
Category::class => ['comment'],
Manufacturer::class => ['comment'],
MeasurementUnit::class => ['comment'],

View file

@ -0,0 +1,80 @@
<?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\AssemblySystem\Assembly;
use App\Entity\Attachments\AssemblyAttachment;
use App\Entity\Parameters\AssemblyParameter;
use App\Form\AdminPages\AssemblyAdminForm;
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;
#[Route(path: '/assembly')]
class AssemblyAdminController extends BaseAdminController
{
protected string $entity_class = Assembly::class;
protected string $twig_template = 'admin/assembly_admin.html.twig';
protected string $form_class = AssemblyAdminForm::class;
protected string $route_base = 'assembly';
protected string $attachment_class = AssemblyAttachment::class;
protected ?string $parameter_class = AssemblyParameter::class;
#[Route(path: '/{id}', name: 'assembly_delete', methods: ['DELETE'])]
public function delete(Request $request, Assembly $entity, StructuralElementRecursionHelper $recursionHelper): RedirectResponse
{
return $this->_delete($request, $entity, $recursionHelper);
}
#[Route(path: '/{id}/edit/{timestamp}', name: 'assembly_edit', requirements: ['id' => '\d+'])]
#[Route(path: '/{id}/edit', requirements: ['id' => '\d+'])]
public function edit(Assembly $entity, Request $request, EntityManagerInterface $em, ?string $timestamp = null): Response
{
return $this->_edit($entity, $request, $em, $timestamp);
}
#[Route(path: '/new', name: 'assembly_new')]
#[Route(path: '/{id}/clone', name: 'assembly_clone')]
#[Route(path: '/')]
public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer, ?Assembly $entity = null): Response
{
return $this->_new($request, $em, $importer, $entity);
}
#[Route(path: '/export', name: 'assembly_export_all')]
public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request): Response
{
return $this->_exportAll($em, $exporter, $request);
}
#[Route(path: '/{id}/export', name: 'assembly_export')]
public function exportEntity(Assembly $entity, EntityExporter $exporter, Request $request): Response
{
return $this->_exportEntity($entity, $exporter, $request);
}
}

View file

@ -0,0 +1,302 @@
<?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\Controller;
use App\DataTables\AssemblyBomEntriesDataTable;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Entity\Parts\Part;
use App\Form\AssemblySystem\AssemblyAddPartsType;
use App\Form\AssemblySystem\AssemblyBuildType;
use App\Helpers\Assemblies\AssemblyBuildRequest;
use App\Repository\PartRepository;
use App\Services\ImportExportSystem\BOMImporter;
use App\Services\AssemblySystem\AssemblyBuildHelper;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use League\Csv\SyntaxError;
use Omines\DataTablesBundle\DataTableFactory;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use function Symfony\Component\Translation\t;
#[Route(path: '/assembly')]
class AssemblyController extends AbstractController
{
private PartRepository $partRepository;
public function __construct(
private readonly DataTableFactory $dataTableFactory,
private readonly EntityManagerInterface $entityManager,
private readonly TranslatorInterface $translator,
) {
$this->partRepository = $this->entityManager->getRepository(Part::class);
}
#[Route(path: '/{id}/info', name: 'assembly_info', requirements: ['id' => '\d+'])]
public function info(Assembly $assembly, Request $request, AssemblyBuildHelper $buildHelper): Response
{
$this->denyAccessUnlessGranted('read', $assembly);
$table = $this->dataTableFactory->createFromType(AssemblyBomEntriesDataTable::class, ['assembly' => $assembly])
->handleRequest($request);
if ($table->isCallback()) {
return $table->getResponse();
}
return $this->render('assemblies/info/info.html.twig', [
'buildHelper' => $buildHelper,
'datatable' => $table,
'assembly' => $assembly,
]);
}
#[Route(path: '/{id}/build', name: 'assembly_build', requirements: ['id' => '\d+'])]
public function build(Assembly $assembly, Request $request, AssemblyBuildHelper $buildHelper, EntityManagerInterface $entityManager): Response
{
$this->denyAccessUnlessGranted('read', $assembly);
//If no number of builds is given (or it is invalid), just assume 1
$number_of_builds = $request->query->getInt('n', 1);
if ($number_of_builds < 1) {
$number_of_builds = 1;
}
$assemblyBuildRequest = new AssemblyBuildRequest($assembly, $number_of_builds);
$form = $this->createForm(AssemblyBuildType::class, $assemblyBuildRequest);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
//Ensure that the user can withdraw stock from all parts
$this->denyAccessUnlessGranted('@parts_stock.withdraw');
//We have to do a flush already here, so that the newly created partLot gets an ID and can be logged to DB later.
$entityManager->flush();
$buildHelper->doBuild($assemblyBuildRequest);
$entityManager->flush();
$this->addFlash('success', 'assembly.build.flash.success');
return $this->redirect(
$request->get('_redirect',
$this->generateUrl('assembly_info', ['id' => $assembly->getID()]
)));
}
$this->addFlash('error', 'assembly.build.flash.invalid_input');
}
return $this->render('assemblies/build/build.html.twig', [
'buildHelper' => $buildHelper,
'assembly' => $assembly,
'build_request' => $assemblyBuildRequest,
'number_of_builds' => $number_of_builds,
'form' => $form,
]);
}
#[Route(path: '/{id}/import_bom', name: 'assembly_import_bom', requirements: ['id' => '\d+'])]
public function importBOM(Request $request, EntityManagerInterface $entityManager, Assembly $assembly,
BOMImporter $BOMImporter, ValidatorInterface $validator): Response
{
$this->denyAccessUnlessGranted('edit', $assembly);
$builder = $this->createFormBuilder();
$builder->add('file', FileType::class, [
'label' => 'import.file',
'required' => true,
'attr' => [
'accept' => '.csv, .json'
]
]);
$builder->add('type', ChoiceType::class, [
'label' => 'assembly.bom_import.type',
'required' => true,
'choices' => [
'assembly.bom_import.type.json' => 'json',
'assembly.bom_import.type.kicad_pcbnew' => 'kicad_pcbnew',
]
]);
$builder->add('clear_existing_bom', CheckboxType::class, [
'label' => 'assembly.bom_import.clear_existing_bom',
'required' => false,
'data' => false,
'help' => 'assembly.bom_import.clear_existing_bom.help',
]);
$builder->add('submit', SubmitType::class, [
'label' => 'import.btn',
]);
$form = $builder->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
//Clear existing BOM entries if requested
if ($form->get('clear_existing_bom')->getData()) {
$assembly->getBomEntries()->clear();
$entityManager->flush();
}
try {
$entries = $BOMImporter->importFileIntoAssembly($form->get('file')->getData(), $assembly, [
'type' => $form->get('type')->getData(),
]);
//Validate the assembly entries
$errors = $validator->validateProperty($assembly, 'bom_entries');
//If no validation errors occured, save the changes and redirect to edit page
if (count ($errors) === 0) {
foreach ($entries as $entry) {
if ($entry instanceof AssemblyBOMEntry && $entry->getPart() !== null) {
$part = $entry->getPart();
if ($part->getID() === null) {
$this->partRepository->save($part);
}
}
}
$this->addFlash('success', t('assembly.bom_import.flash.success', ['%count%' => count($entries)]));
$entityManager->flush();
return $this->redirectToRoute('assembly_edit', ['id' => $assembly->getID()]);
}
//When we get here, there were validation errors
$this->addFlash('error', t('assembly.bom_import.flash.invalid_entries'));
} catch (\UnexpectedValueException|\RuntimeException|SyntaxError $e) {
$this->addFlash('error', t('assembly.bom_import.flash.invalid_file', ['%message%' => $e->getMessage()]));
}
}
$jsonTemplate = [
[
"quantity" => 1.0,
"name" => $this->translator->trans('assembly.bom_import.template.entry.name'),
"part" => [
"id" => null,
"ipn" => $this->translator->trans('assembly.bom_import.template.entry.part.ipn'),
"mpnr" => $this->translator->trans('assembly.bom_import.template.entry.part.mpnr'),
"name" => $this->translator->trans('assembly.bom_import.template.entry.part.name'),
"description" => null,
"manufacturer" => [
"id" => null,
"name" => $this->translator->trans('assembly.bom_import.template.entry.part.manufacturer.name')
],
"category" => [
"id" => null,
"name" => $this->translator->trans('assembly.bom_import.template.entry.part.category.name')
]
]
]
];
return $this->render('assemblies/import_bom.html.twig', [
'assembly' => $assembly,
'jsonTemplate' => $jsonTemplate,
'form' => $form,
'errors' => $errors ?? null,
]);
}
#[Route(path: '/add_parts', name: 'assembly_add_parts_no_id')]
#[Route(path: '/{id}/add_parts', name: 'assembly_add_parts', requirements: ['id' => '\d+'])]
public function addPart(Request $request, EntityManagerInterface $entityManager, ?Assembly $assembly): Response
{
if($assembly instanceof Assembly) {
$this->denyAccessUnlessGranted('edit', $assembly);
} else {
$this->denyAccessUnlessGranted('@assemblies.edit');
}
$form = $this->createForm(AssemblyAddPartsType::class, null, [
'assembly' => $assembly,
]);
//Preset the BOM entries with the selected parts, when the form was not submitted yet
$preset_data = new ArrayCollection();
foreach (explode(',', (string) $request->get('parts', '')) as $part_id) {
//Skip empty part IDs. Postgres seems to be especially sensitive to empty strings, as it does not allow them in integer columns
if ($part_id === '') {
continue;
}
$part = $entityManager->getRepository(Part::class)->find($part_id);
if (null !== $part) {
//If there is already a BOM entry for this part, we use this one (we edit it then)
$bom_entry = $entityManager->getRepository(AssemblyBOMEntry::class)->findOneBy([
'assembly' => $assembly,
'part' => $part
]);
if ($bom_entry !== null) {
$preset_data->add($bom_entry);
} else { //Otherwise create an empty one
$entry = new AssemblyBOMEntry();
$entry->setAssembly($assembly);
$entry->setPart($part);
$preset_data->add($entry);
}
}
}
$form['bom_entries']->setData($preset_data);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$target_assembly = $assembly ?? $form->get('assembly')->getData();
//Ensure that we really have acces to the selected assembly
$this->denyAccessUnlessGranted('edit', $target_assembly);
$data = $form->getData();
$bom_entries = $data['bom_entries'];
foreach ($bom_entries as $bom_entry){
$target_assembly->addBOMEntry($bom_entry);
}
$entityManager->flush();
//If a redirect query parameter is set, redirect to this page
if ($request->query->get('_redirect')) {
return $this->redirect($request->query->get('_redirect'));
}
//Otherwise just show the assembly info page
return $this->redirectToRoute('assembly_info', ['id' => $target_assembly->getID()]);
}
return $this->render('assemblies/add_parts.html.twig', [
'assembly' => $assembly,
'form' => $form,
]);
}
}

View file

@ -33,6 +33,7 @@ use App\Entity\Parts\StorageLocation;
use App\Entity\Parts\Supplier;
use App\Entity\PriceInformations\Orderdetail;
use App\Entity\ProjectSystem\Project;
use App\Entity\AssemblySystem\Assembly;
use App\Exceptions\AttachmentDownloadException;
use App\Form\Part\PartBaseType;
use App\Services\Attachments\AttachmentSubmitHandler;
@ -45,6 +46,7 @@ use App\Services\LogSystem\TimeTravel;
use App\Services\Parameters\ParameterExtractor;
use App\Services\Parts\PartLotWithdrawAddHelper;
use App\Services\Parts\PricedetailHelper;
use App\Services\AssemblySystem\AssemblyBuildPartHelper;
use App\Services\ProjectSystem\ProjectBuildPartHelper;
use App\Settings\BehaviorSettings\PartInfoSettings;
use DateTime;
@ -205,14 +207,17 @@ final class PartController extends AbstractController
#[Route(path: '/new', name: 'part_new')]
#[Route(path: '/{id}/clone', name: 'part_clone')]
#[Route(path: '/new_build_part/{project_id}', name: 'part_new_build_part')]
#[Route(path: '/new_build_part_assembly/{assembly_id}', name: 'part_new_build_part_assembly')]
public function new(
Request $request,
EntityManagerInterface $em,
TranslatorInterface $translator,
AttachmentSubmitHandler $attachmentSubmitHandler,
ProjectBuildPartHelper $projectBuildPartHelper,
AssemblyBuildPartHelper $assemblyBuildPartHelper,
#[MapEntity(mapping: ['id' => 'id'])] ?Part $part = null,
#[MapEntity(mapping: ['project_id' => 'id'])] ?Project $project = null
#[MapEntity(mapping: ['project_id' => 'id'])] ?Project $project = null,
#[MapEntity(mapping: ['assembly_id' => 'id'])] ?Assembly $assembly = null
): Response {
if ($part instanceof Part) {
@ -226,6 +231,14 @@ final class PartController extends AbstractController
return $this->redirectToRoute('part_edit', ['id' => $project->getBuildPart()->getID()]);
}
$new_part = $projectBuildPartHelper->getPartInitialization($project);
} elseif ($assembly instanceof Assembly) {
//Initialize a new part for a build part from the given assembly
//Ensure that the assembly has not already a build part
if ($project->getBuildPart() instanceof Part) {
$this->addFlash('error', 'part.new_build_part.error.build_part_already_exists');
return $this->redirectToRoute('part_edit', ['id' => $project->getBuildPart()->getID()]);
}
$new_part = $assemblyBuildPartHelper->getPartInitialization($assembly);
} else { //Create an empty part from scratch
$new_part = new Part();
}

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Controller;
use App\Entity\AssemblySystem\Assembly;
use Symfony\Component\HttpFoundation\Response;
use App\Entity\ProjectSystem\Project;
use App\Entity\Parts\Category;
@ -129,4 +130,17 @@ class TreeController extends AbstractController
return new JsonResponse($tree);
}
#[Route(path: '/assembly/{id}', name: 'tree_assembly')]
#[Route(path: '/assemblies', name: 'tree_assembly_root')]
public function assemblyTree(?Assembly $assembly = null): JsonResponse
{
if ($this->isGranted('@assemblies.read')) {
$tree = $this->treeGenerator->getTreeView(Assembly::class, $assembly, 'assemblies');
} else {
return new JsonResponse("Access denied", Response::HTTP_FORBIDDEN);
}
return new JsonResponse($tree);
}
}

View file

@ -22,7 +22,9 @@ declare(strict_types=1);
namespace App\Controller;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Parameters\AbstractParameter;
use App\Services\Attachments\AssemblyPreviewGenerator;
use Symfony\Component\HttpFoundation\Response;
use App\Entity\Attachments\Attachment;
use App\Entity\Parts\Category;
@ -53,6 +55,8 @@ use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Contracts\Translation\TranslatorInterface;
use InvalidArgumentException;
/**
* In this controller the endpoints for the typeaheads are collected.
@ -60,8 +64,11 @@ use Symfony\Component\Serializer\Serializer;
#[Route(path: '/typeahead')]
class TypeaheadController extends AbstractController
{
public function __construct(protected AttachmentURLGenerator $urlGenerator, protected Packages $assets)
{
public function __construct(
protected AttachmentURLGenerator $urlGenerator,
protected Packages $assets,
protected TranslatorInterface $translator
) {
}
#[Route(path: '/builtInResources/search', name: 'typeahead_builtInRessources')]
@ -109,19 +116,22 @@ class TypeaheadController extends AbstractController
'group' => GroupParameter::class,
'measurement_unit' => MeasurementUnitParameter::class,
'currency' => Currency::class,
default => throw new \InvalidArgumentException('Invalid parameter type: '.$type),
default => throw new InvalidArgumentException('Invalid parameter type: '.$type),
};
}
#[Route(path: '/parts/search/{query}', name: 'typeahead_parts')]
public function parts(EntityManagerInterface $entityManager, PartPreviewGenerator $previewGenerator,
AttachmentURLGenerator $attachmentURLGenerator, string $query = ""): JsonResponse
{
public function parts(
EntityManagerInterface $entityManager,
PartPreviewGenerator $previewGenerator,
AttachmentURLGenerator $attachmentURLGenerator,
string $query = ""
): JsonResponse {
$this->denyAccessUnlessGranted('@parts.read');
$repo = $entityManager->getRepository(Part::class);
$partRepository = $entityManager->getRepository(Part::class);
$parts = $repo->autocompleteSearch($query, 100);
$parts = $partRepository->autocompleteSearch($query, 100);
$data = [];
foreach ($parts as $part) {
@ -147,6 +157,44 @@ class TypeaheadController extends AbstractController
return new JsonResponse($data);
}
#[Route(path: '/assemblies/search/{query}', name: 'typeahead_assemblies')]
public function assemblies(
EntityManagerInterface $entityManager,
AssemblyPreviewGenerator $assemblyPreviewGenerator,
AttachmentURLGenerator $attachmentURLGenerator,
string $query = ""
): JsonResponse {
$this->denyAccessUnlessGranted('@assemblies.read');
$result = [];
$assemblyRepository = $entityManager->getRepository(Assembly::class);
$assemblies = $assemblyRepository->autocompleteSearch($query, 100);
foreach ($assemblies as $assembly) {
$preview_attachment = $assemblyPreviewGenerator->getTablePreviewAttachment($assembly);
if($preview_attachment instanceof Attachment) {
$preview_url = $attachmentURLGenerator->getThumbnailURL($preview_attachment, 'thumbnail_sm');
} else {
$preview_url = '';
}
/** @var Assembly $assembly */
$result[] = [
'id' => $assembly->getID(),
'name' => $this->translator->trans('typeahead.parts.assembly.name', ['%name%' => $assembly->getName()]),
'category' => '',
'footprint' => '',
'description' => mb_strimwidth($assembly->getDescription(), 0, 127, '...'),
'image' => $preview_url,
];
}
return new JsonResponse($result);
}
#[Route(path: '/parameters/{type}/search/{query}', name: 'typeahead_parameters', requirements: ['type' => '.+'])]
public function parameters(string $type, EntityManagerInterface $entityManager, string $query = ""): JsonResponse
{

View file

@ -0,0 +1,209 @@
<?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\DataTables;
use App\DataTables\Column\EntityColumn;
use App\DataTables\Column\LocaleDateTimeColumn;
use App\DataTables\Column\MarkdownColumn;
use App\DataTables\Helpers\PartDataTableHelper;
use App\Entity\Attachments\Attachment;
use App\Entity\Parts\Part;
use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Services\EntityURLGenerator;
use App\Services\Formatters\AmountFormatter;
use Doctrine\ORM\QueryBuilder;
use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchCriteriaProvider;
use Omines\DataTablesBundle\Adapter\Doctrine\ORMAdapter;
use Omines\DataTablesBundle\Column\TextColumn;
use Omines\DataTablesBundle\DataTable;
use Omines\DataTablesBundle\DataTableTypeInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class AssemblyBomEntriesDataTable implements DataTableTypeInterface
{
public function __construct(protected TranslatorInterface $translator, protected PartDataTableHelper $partDataTableHelper, protected EntityURLGenerator $entityURLGenerator, protected AmountFormatter $amountFormatter)
{
}
public function configure(DataTable $dataTable, array $options): void
{
$dataTable
//->add('select', SelectColumn::class)
->add('picture', TextColumn::class, [
'label' => '',
'className' => 'no-colvis',
'render' => function ($value, AssemblyBOMEntry $context) {
if(!$context->getPart() instanceof Part) {
return '';
}
return $this->partDataTableHelper->renderPicture($context->getPart());
},
])
->add('id', TextColumn::class, [
'label' => $this->translator->trans('part.table.id'),
'visible' => false,
])
->add('quantity', TextColumn::class, [
'label' => $this->translator->trans('assembly.bom.quantity'),
'className' => 'text-center',
'orderField' => 'bom_entry.quantity',
'render' => function ($value, AssemblyBOMEntry $context): float|string {
//If we have a non-part entry, only show the rounded quantity
if (!$context->getPart() instanceof Part) {
return round($context->getQuantity());
}
//Otherwise use the unit of the part to format the quantity
return htmlspecialchars($this->amountFormatter->format($context->getQuantity(), $context->getPart()->getPartUnit()));
},
])
->add('name', TextColumn::class, [
'label' => $this->translator->trans('part.table.name'),
'orderField' => 'NATSORT(part.name)',
'render' => function ($value, AssemblyBOMEntry $context) {
if(!$context->getPart() instanceof Part) {
return htmlspecialchars((string) $context->getName());
}
//Part exists if we reach this point
$tmp = $this->partDataTableHelper->renderName($context->getPart());
if($context->getName() !== null && $context->getName() !== '') {
$tmp .= '<br><b>'.htmlspecialchars($context->getName()).'</b>';
}
return $tmp;
},
])
->add('ipn', TextColumn::class, [
'label' => $this->translator->trans('part.table.ipn'),
'orderField' => 'NATSORT(part.ipn)',
'visible' => false,
'render' => function ($value, AssemblyBOMEntry $context) {
if($context->getPart() instanceof Part) {
return $context->getPart()->getIpn();
}
}
])
->add('description', MarkdownColumn::class, [
'label' => $this->translator->trans('part.table.description'),
'data' => function (AssemblyBOMEntry $context) {
if($context->getPart() instanceof Part) {
return $context->getPart()->getDescription();
}
//For non-part BOM entries show the comment field
return $context->getComment();
},
])
->add('category', EntityColumn::class, [
'label' => $this->translator->trans('part.table.category'),
'property' => 'part.category',
'orderField' => 'NATSORT(category.name)',
])
->add('footprint', EntityColumn::class, [
'property' => 'part.footprint',
'label' => $this->translator->trans('part.table.footprint'),
'orderField' => 'NATSORT(footprint.name)',
])
->add('manufacturer', EntityColumn::class, [
'property' => 'part.manufacturer',
'label' => $this->translator->trans('part.table.manufacturer'),
'orderField' => 'NATSORT(manufacturer.name)',
])
->add('mountnames', TextColumn::class, [
'label' => 'assembly.bom.mountnames',
'render' => function ($value, AssemblyBOMEntry $context) {
$html = '';
foreach (explode(',', $context->getMountnames()) as $mountname) {
$html .= sprintf('<span class="badge badge-secondary bg-secondary">%s</span> ', htmlspecialchars($mountname));
}
return $html;
},
])
->add('instockAmount', TextColumn::class, [
'label' => 'assembly.bom.instockAmount',
'visible' => false,
'render' => function ($value, AssemblyBOMEntry $context) {
if ($context->getPart() !== null) {
return $this->partDataTableHelper->renderAmount($context->getPart());
}
return '';
}
])
->add('storageLocations', TextColumn::class, [
'label' => 'part.table.storeLocations',
'visible' => false,
'render' => function ($value, AssemblyBOMEntry $context) {
if ($context->getPart() !== null) {
return $this->partDataTableHelper->renderStorageLocations($context->getPart());
}
return '';
}
])
->add('addedDate', LocaleDateTimeColumn::class, [
'label' => $this->translator->trans('part.table.addedDate'),
'visible' => false,
])
->add('lastModified', LocaleDateTimeColumn::class, [
'label' => $this->translator->trans('part.table.lastModified'),
'visible' => false,
])
;
$dataTable->addOrderBy('name', DataTable::SORT_ASCENDING);
$dataTable->createAdapter(ORMAdapter::class, [
'entity' => Attachment::class,
'query' => function (QueryBuilder $builder) use ($options): void {
$this->getQuery($builder, $options);
},
'criteria' => [
function (QueryBuilder $builder) use ($options): void {
$this->buildCriteria($builder, $options);
},
new SearchCriteriaProvider(),
],
]);
}
private function getQuery(QueryBuilder $builder, array $options): void
{
$builder->select('bom_entry')
->addSelect('part')
->from(AssemblyBOMEntry::class, 'bom_entry')
->leftJoin('bom_entry.part', 'part')
->leftJoin('part.category', 'category')
->leftJoin('part.footprint', 'footprint')
->leftJoin('part.manufacturer', 'manufacturer')
->where('bom_entry.assembly = :assembly')
->setParameter('assembly', $options['assembly'])
;
}
private function buildCriteria(QueryBuilder $builder, array $options): void
{
}
}

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\DataTables\Helpers;
use App\Entity\AssemblySystem\Assembly;
use App\Services\EntityURLGenerator;
/**
* A helper service which contains common code to render columns for assembly related tables
*/
class AssemblyDataTableHelper
{
public function __construct(private readonly EntityURLGenerator $entityURLGenerator) {
}
public function renderName(Assembly $context): string
{
$icon = '';
return sprintf(
'<a href="%s">%s%s</a>',
$this->entityURLGenerator->infoURL($context),
$icon,
htmlspecialchars($context->getName())
);
}
}

View file

@ -25,7 +25,9 @@ namespace App\DataTables;
use App\DataTables\Column\EntityColumn;
use App\DataTables\Column\LocaleDateTimeColumn;
use App\DataTables\Column\MarkdownColumn;
use App\DataTables\Helpers\AssemblyDataTableHelper;
use App\DataTables\Helpers\PartDataTableHelper;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Attachments\Attachment;
use App\Entity\Parts\Part;
use App\Entity\ProjectSystem\ProjectBOMEntry;
@ -41,11 +43,15 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class ProjectBomEntriesDataTable implements DataTableTypeInterface
{
public function __construct(protected TranslatorInterface $translator, protected PartDataTableHelper $partDataTableHelper, protected EntityURLGenerator $entityURLGenerator, protected AmountFormatter $amountFormatter)
{
public function __construct(
protected TranslatorInterface $translator,
protected PartDataTableHelper $partDataTableHelper,
protected AssemblyDataTableHelper $assemblyDataTableHelper,
protected EntityURLGenerator $entityURLGenerator,
protected AmountFormatter $amountFormatter
) {
}
public function configure(DataTable $dataTable, array $options): void
{
$dataTable
@ -84,16 +90,26 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
'label' => $this->translator->trans('part.table.name'),
'orderField' => 'NATSORT(part.name)',
'render' => function ($value, ProjectBOMEntry $context) {
if(!$context->getPart() instanceof Part) {
if(!$context->getPart() instanceof Part && !$context->getAssembly() instanceof Assembly) {
return htmlspecialchars((string) $context->getName());
}
//Part exists if we reach this point
if ($context->getPart() !== null) {
$tmp = $this->partDataTableHelper->renderName($context->getPart());
$tmp = $this->translator->trans('part.table.name.value.for_part', ['%value%' => $tmp]);
$tmp = $this->partDataTableHelper->renderName($context->getPart());
if($context->getName() !== null && $context->getName() !== '') {
$tmp .= '<br><b>'.htmlspecialchars($context->getName()).'</b>';
if($context->getName() !== null && $context->getName() !== '') {
$tmp .= '<br><b>'.htmlspecialchars($context->getName()).'</b>';
}
} elseif ($context->getAssembly() !== null) {
$tmp = $this->assemblyDataTableHelper->renderName($context->getAssembly());
$tmp = $this->translator->trans('part.table.name.value.for_assembly', ['%value%' => $tmp]);
if($context->getName() !== null && $context->getName() !== '') {
$tmp .= '<br><b>'.htmlspecialchars($context->getName()).'</b>';
}
}
return $tmp;
},
])

View file

@ -0,0 +1,358 @@
<?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\AssemblySystem;
use App\Repository\AssemblyRepository;
use Doctrine\Common\Collections\Criteria;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\OpenApi\Model\Operation;
use ApiPlatform\Serializer\Filter\PropertyFilter;
use App\ApiPlatform\Filter\LikeFilter;
use App\Entity\Attachments\Attachment;
use App\Validator\Constraints\UniqueObjectCollection;
use Doctrine\DBAL\Types\Types;
use App\Entity\Attachments\AssemblyAttachment;
use App\Entity\Base\AbstractStructuralDBElement;
use App\Entity\Parameters\AssemblyParameter;
use App\Entity\Parts\Part;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use InvalidArgumentException;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* This class represents a assembly in the database.
*
* @extends AbstractStructuralDBElement<AssemblyAttachment, AssemblyParameter>
*/
#[ORM\Entity(repositoryClass: AssemblyRepository::class)]
#[ORM\Table(name: 'assemblies')]
#[ApiResource(
operations: [
new Get(security: 'is_granted("read", object)'),
new GetCollection(security: 'is_granted("@assemblies.read")'),
new Post(securityPostDenormalize: 'is_granted("create", object)'),
new Patch(security: 'is_granted("edit", object)'),
new Delete(security: 'is_granted("delete", object)'),
],
normalizationContext: ['groups' => ['assembly:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['assembly:write', 'api:basic:write', 'attachment:write', 'parameter:write'], 'openapi_definition_name' => 'Write'],
)]
#[ApiResource(
uriTemplate: '/assemblies/{id}/children.{_format}',
operations: [
new GetCollection(
openapi: new Operation(summary: 'Retrieves the children elements of a assembly.'),
security: 'is_granted("@assemblies.read")'
)
],
uriVariables: [
'id' => new Link(fromProperty: 'children', fromClass: Assembly::class)
],
normalizationContext: ['groups' => ['assembly:read', 'api:basic:read'], 'openapi_definition_name' => 'Read']
)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment"])]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified'])]
class Assembly extends AbstractStructuralDBElement
{
#[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)]
#[ORM\OrderBy(['name' => Criteria::ASC])]
protected Collection $children;
#[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')]
#[ORM\JoinColumn(name: 'parent_id')]
#[Groups(['assembly:read', 'assembly:write'])]
#[ApiProperty(readableLink: false, writableLink: false)]
protected ?AbstractStructuralDBElement $parent = null;
#[Groups(['assembly:read', 'assembly:write'])]
protected string $comment = '';
/**
* @var Collection<int, AssemblyBOMEntry>
*/
#[Assert\Valid]
#[Groups(['extended', 'full', 'import'])]
#[ORM\OneToMany(mappedBy: 'assembly', targetEntity: AssemblyBOMEntry::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[UniqueObjectCollection(message: 'assembly.bom_entry.part_already_in_bom', fields: ['part'])]
#[UniqueObjectCollection(message: 'assembly.bom_entry.name_already_in_bom', fields: ['name'])]
protected Collection $bom_entries;
#[ORM\Column(type: Types::INTEGER)]
protected int $order_quantity = 0;
/**
* @var string|null The current status of the assembly
*/
#[Assert\Choice(['draft', 'planning', 'in_production', 'finished', 'archived'])]
#[Groups(['extended', 'full', 'assembly:read', 'assembly:write', 'import'])]
#[ORM\Column(type: Types::STRING, length: 64, nullable: true)]
protected ?string $status = null;
/**
* @var Part|null The (optional) part that represents the builds of this assembly in the stock
*/
#[ORM\OneToOne(mappedBy: 'built_assembly', targetEntity: Part::class, cascade: ['persist'], orphanRemoval: true)]
#[Groups(['assembly:read', 'assembly:write'])]
protected ?Part $build_part = null;
#[ORM\Column(type: Types::BOOLEAN)]
protected bool $order_only_missing_parts = false;
#[Groups(['simple', 'extended', 'full', 'assembly:read', 'assembly:write'])]
#[ORM\Column(type: Types::TEXT)]
protected string $description = '';
/**
* @var Collection<int, AssemblyAttachment>
*/
#[ORM\OneToMany(mappedBy: 'element', targetEntity: AssemblyAttachment::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['name' => Criteria::ASC])]
#[Groups(['assembly:read', 'assembly:write'])]
protected Collection $attachments;
#[ORM\ManyToOne(targetEntity: AssemblyAttachment::class)]
#[ORM\JoinColumn(name: 'id_preview_attachment', onDelete: 'SET NULL')]
#[Groups(['assembly:read', 'assembly:write'])]
protected ?Attachment $master_picture_attachment = null;
/** @var Collection<int, AssemblyParameter>
*/
#[ORM\OneToMany(mappedBy: 'element', targetEntity: AssemblyParameter::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[ORM\OrderBy(['group' => Criteria::ASC, 'name' => 'ASC'])]
#[Groups(['assembly:read', 'assembly:write'])]
protected Collection $parameters;
#[Groups(['assembly:read'])]
protected ?\DateTimeImmutable $addedDate = null;
#[Groups(['assembly:read'])]
protected ?\DateTimeImmutable $lastModified = null;
/********************************************************************************
*
* Getters
*
*********************************************************************************/
public function __construct()
{
$this->attachments = new ArrayCollection();
$this->parameters = new ArrayCollection();
parent::__construct();
$this->bom_entries = new ArrayCollection();
$this->children = new ArrayCollection();
}
public function __clone()
{
//When cloning this assembly, we have to clone each bom entry too.
if ($this->id) {
$bom_entries = $this->bom_entries;
$this->bom_entries = new ArrayCollection();
//Set master attachment is needed
foreach ($bom_entries as $bom_entry) {
$clone = clone $bom_entry;
$this->addBomEntry($clone);
}
}
//Parent has to be last call, as it resets the ID
parent::__clone();
}
/**
* Get the order quantity of this assembly.
*
* @return int the order quantity
*/
public function getOrderQuantity(): int
{
return $this->order_quantity;
}
/**
* Get the "order_only_missing_parts" attribute.
*
* @return bool the "order_only_missing_parts" attribute
*/
public function getOrderOnlyMissingParts(): bool
{
return $this->order_only_missing_parts;
}
/********************************************************************************
*
* Setters
*
*********************************************************************************/
/**
* Set the order quantity.
*
* @param int $new_order_quantity the new order quantity
*
* @return $this
*/
public function setOrderQuantity(int $new_order_quantity): self
{
if ($new_order_quantity < 0) {
throw new InvalidArgumentException('The new order quantity must not be negative!');
}
$this->order_quantity = $new_order_quantity;
return $this;
}
/**
* Set the "order_only_missing_parts" attribute.
*
* @param bool $new_order_only_missing_parts the new "order_only_missing_parts" attribute
*/
public function setOrderOnlyMissingParts(bool $new_order_only_missing_parts): self
{
$this->order_only_missing_parts = $new_order_only_missing_parts;
return $this;
}
public function getBomEntries(): Collection
{
return $this->bom_entries;
}
/**
* @return $this
*/
public function addBomEntry(AssemblyBOMEntry $entry): self
{
$entry->setAssembly($this);
$this->bom_entries->add($entry);
return $this;
}
/**
* @return $this
*/
public function removeBomEntry(AssemblyBOMEntry $entry): self
{
$this->bom_entries->removeElement($entry);
return $this;
}
public function getDescription(): string
{
return $this->description;
}
public function setDescription(string $description): Assembly
{
$this->description = $description;
return $this;
}
/**
* @return string
*/
public function getStatus(): ?string
{
return $this->status;
}
/**
* @param string $status
*/
public function setStatus(?string $status): void
{
$this->status = $status;
}
/**
* Checks if this assembly has an associated part representing the builds of this assembly in the stock.
*/
public function hasBuildPart(): bool
{
return $this->build_part instanceof Part;
}
/**
* Gets the part representing the builds of this assembly in the stock, if it is existing
*/
public function getBuildPart(): ?Part
{
return $this->build_part;
}
/**
* Sets the part representing the builds of this assembly in the stock.
*/
public function setBuildPart(?Part $build_part): void
{
$this->build_part = $build_part;
if ($build_part instanceof Part) {
$build_part->setBuiltAssembly($this);
}
}
#[Assert\Callback]
public function validate(ExecutionContextInterface $context, $payload): void
{
//If this assembly has subassemblies, and these have builds part, they must be included in the BOM
foreach ($this->getChildren() as $child) {
if (!$child->getBuildPart() instanceof Part) {
continue;
}
//We have to search all bom entries for the build part
$found = false;
foreach ($this->getBomEntries() as $bom_entry) {
if ($bom_entry->getPart() === $child->getBuildPart()) {
$found = true;
break;
}
}
//When the build part is not found, we have to add an error
if (!$found) {
$context->buildViolation('assembly.bom_has_to_include_all_subelement_parts')
->atPath('bom_entries')
->setParameter('%assembly_name%', $child->getName())
->setParameter('%part_name%', $child->getBuildPart()->getName())
->addViolation();
}
}
}
}

View file

@ -0,0 +1,302 @@
<?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\AssemblySystem;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Doctrine\Orm\Filter\RangeFilter;
use ApiPlatform\Metadata\ApiFilter;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
use ApiPlatform\Metadata\Get;
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\Link;
use ApiPlatform\Metadata\Patch;
use ApiPlatform\Metadata\Post;
use ApiPlatform\OpenApi\Model\Operation;
use ApiPlatform\Serializer\Filter\PropertyFilter;
use App\ApiPlatform\Filter\LikeFilter;
use App\Entity\Contracts\TimeStampableInterface;
use App\Entity\AssemblySystem\Assembly;
use App\Repository\DBElementRepository;
use App\Validator\UniqueValidatableInterface;
use Doctrine\DBAL\Types\Types;
use App\Entity\Base\AbstractDBElement;
use App\Entity\Base\TimestampTrait;
use App\Entity\Parts\Part;
use App\Entity\PriceInformations\Currency;
use App\Validator\Constraints\BigDecimal\BigDecimalPositive;
use App\Validator\Constraints\Selectable;
use Brick\Math\BigDecimal;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
/**
* The AssemblyBOMEntry class represents an entry in a assembly's BOM.
*/
#[ORM\HasLifecycleCallbacks]
#[ORM\Entity(repositoryClass: DBElementRepository::class)]
#[ORM\Table('assembly_bom_entries')]
#[ApiResource(
operations: [
new Get(uriTemplate: '/assembly_bom_entries/{id}.{_format}', security: 'is_granted("read", object)',),
new GetCollection(uriTemplate: '/assembly_bom_entries.{_format}', security: 'is_granted("@assemblies.read")',),
new Post(uriTemplate: '/assembly_bom_entries.{_format}', securityPostDenormalize: 'is_granted("create", object)',),
new Patch(uriTemplate: '/assembly_bom_entries/{id}.{_format}', security: 'is_granted("edit", object)',),
new Delete(uriTemplate: '/assembly_bom_entries/{id}.{_format}', security: 'is_granted("delete", object)',),
],
normalizationContext: ['groups' => ['bom_entry:read', 'api:basic:read'], 'openapi_definition_name' => 'Read'],
denormalizationContext: ['groups' => ['bom_entry:write', 'api:basic:write'], 'openapi_definition_name' => 'Write'],
)]
#[ApiResource(
uriTemplate: '/assemblies/{id}/bom.{_format}',
operations: [
new GetCollection(
openapi: new Operation(summary: 'Retrieves the BOM entries of the given assembly.'),
security: 'is_granted("@assemblies.read")'
)
],
uriVariables: [
'id' => new Link(fromProperty: 'bom_entries', fromClass: Assembly::class)
],
normalizationContext: ['groups' => ['bom_entry:read', 'api:basic:read'], 'openapi_definition_name' => 'Read']
)]
#[ApiFilter(PropertyFilter::class)]
#[ApiFilter(LikeFilter::class, properties: ["name", "comment", 'mountnames'])]
#[ApiFilter(RangeFilter::class, properties: ['quantity'])]
#[ApiFilter(OrderFilter::class, properties: ['name', 'id', 'addedDate', 'lastModified', 'quantity'])]
class AssemblyBOMEntry extends AbstractDBElement implements UniqueValidatableInterface, TimeStampableInterface
{
use TimestampTrait;
#[Assert\Positive]
#[ORM\Column(name: 'quantity', type: Types::FLOAT)]
#[Groups(['bom_entry:read', 'bom_entry:write', 'import', 'simple', 'extended', 'full'])]
protected float $quantity = 1.0;
/**
* @var string A comma separated list of the names, where this parts should be placed
*/
#[ORM\Column(name: 'mountnames', type: Types::TEXT)]
#[Groups(['bom_entry:read', 'bom_entry:write', 'import', 'simple', 'extended', 'full'])]
protected string $mountnames = '';
/**
* @var string|null An optional name describing this BOM entry (useful for non-part entries)
*/
#[Assert\Expression('this.getPart() !== null or this.getName() !== null', message: 'validator.assembly.bom_entry.name_or_part_needed')]
#[ORM\Column(type: Types::STRING, nullable: true)]
#[Groups(['bom_entry:read', 'bom_entry:write', 'import', 'simple', 'extended', 'full'])]
protected ?string $name = null;
/**
* @var string An optional comment for this BOM entry
*/
#[ORM\Column(type: Types::TEXT)]
#[Groups(['bom_entry:read', 'bom_entry:write', 'import', 'extended', 'full'])]
protected string $comment = '';
/**
* @var Assembly|null
*/
#[ORM\ManyToOne(targetEntity: Assembly::class, inversedBy: 'bom_entries')]
#[ORM\JoinColumn(name: 'id_assembly', nullable: true)]
#[Groups(['bom_entry:read', 'bom_entry:write', ])]
protected ?Assembly $assembly = null;
/**
* @var Part|null The part associated with this
*/
#[ORM\ManyToOne(targetEntity: Part::class, inversedBy: 'assembly_bom_entries')]
#[ORM\JoinColumn(name: 'id_part')]
#[Groups(['bom_entry:read', 'bom_entry:write', 'full'])]
protected ?Part $part = null;
/**
* @var BigDecimal|null The price of this non-part BOM entry
*/
#[Assert\AtLeastOneOf([new BigDecimalPositive(), new Assert\IsNull()])]
#[ORM\Column(type: 'big_decimal', precision: 11, scale: 5, nullable: true)]
#[Groups(['bom_entry:read', 'bom_entry:write', 'import', 'extended', 'full'])]
protected ?BigDecimal $price = null;
/**
* @var ?Currency The currency for the price of this non-part BOM entry
*/
#[ORM\ManyToOne(targetEntity: Currency::class)]
#[ORM\JoinColumn]
#[Selectable]
protected ?Currency $price_currency = null;
public function __construct()
{
}
public function getQuantity(): float
{
return $this->quantity;
}
public function setQuantity(float $quantity): AssemblyBOMEntry
{
$this->quantity = $quantity;
return $this;
}
public function getMountnames(): string
{
return $this->mountnames;
}
public function setMountnames(string $mountnames): AssemblyBOMEntry
{
$this->mountnames = $mountnames;
return $this;
}
/**
* @return string
*/
public function getName(): ?string
{
return $this->name;
}
/**
* @param string $name
*/
public function setName(?string $name): AssemblyBOMEntry
{
$this->name = $name;
return $this;
}
public function getComment(): string
{
return $this->comment;
}
public function setComment(string $comment): AssemblyBOMEntry
{
$this->comment = $comment;
return $this;
}
public function getAssembly(): ?Assembly
{
return $this->assembly;
}
public function setAssembly(?Assembly $assembly): AssemblyBOMEntry
{
$this->assembly = $assembly;
return $this;
}
public function getPart(): ?Part
{
return $this->part;
}
public function setPart(?Part $part): AssemblyBOMEntry
{
$this->part = $part;
return $this;
}
/**
* Returns the price of this BOM entry, if existing.
* Prices are only valid on non-Part BOM entries.
*/
public function getPrice(): ?BigDecimal
{
return $this->price;
}
/**
* Sets the price of this BOM entry.
* Prices are only valid on non-Part BOM entries.
*/
public function setPrice(?BigDecimal $price): void
{
$this->price = $price;
}
public function getPriceCurrency(): ?Currency
{
return $this->price_currency;
}
public function setPriceCurrency(?Currency $price_currency): void
{
$this->price_currency = $price_currency;
}
/**
* Checks whether this BOM entry is a part associated BOM entry or not.
* @return bool True if this BOM entry is a part associated BOM entry, false otherwise.
*/
public function isPartBomEntry(): bool
{
return $this->part instanceof Part;
}
#[Assert\Callback]
public function validate(ExecutionContextInterface $context, $payload): void
{
//Round quantity to whole numbers, if the part is not a decimal part
if ($this->part instanceof Part && (!$this->part->getPartUnit() || $this->part->getPartUnit()->isInteger())) {
$this->quantity = round($this->quantity);
}
//Non-Part BOM entries are rounded
if (!$this->part instanceof Part) {
$this->quantity = round($this->quantity);
}
//Check that the part is not the build representation part of this assembly or one of its parents
if ($this->part && $this->part->getBuiltAssembly() instanceof Assembly) {
//Get the associated assembly
$associated_assembly = $this->part->getBuiltAssembly();
//Check that it is not the same as the current assembly neither one of its parents
$current_assembly = $this->assembly;
while ($current_assembly) {
if ($associated_assembly === $current_assembly) {
$context->buildViolation('assembly.bom_entry.can_not_add_own_builds_part')
->atPath('part')
->addViolation();
}
$current_assembly = $current_assembly->getParent();
}
}
}
public function getComparableFields(): array
{
return [
'name' => $this->getName(),
'part' => $this->getPart()?->getID(),
];
}
}

View file

@ -0,0 +1,48 @@
<?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\AssemblySystem\Assembly;
use App\Serializer\APIPlatform\OverrideClassDenormalizer;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Attribute\Context;
/**
* A attachment attached to a device element.
* @extends Attachment<Assembly>
*/
#[UniqueEntity(['name', 'attachment_type', 'element'])]
#[UniqueEntity(['name', 'attachment_type', 'element'])]
#[ORM\Entity]
class AssemblyAttachment extends Attachment
{
final public const ALLOWED_ELEMENT_CLASS = Assembly::class;
/**
* @var Assembly|null the element this attachment is associated with
*/
#[ORM\ManyToOne(targetEntity: Assembly::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

@ -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, 'Device' => ProjectAttachment::class, 'Assembly' => AssemblyAttachment::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, "Project" => ProjectAttachment::class, "Assembly" => AssemblyAttachment::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

@ -22,6 +22,9 @@ declare(strict_types=1);
namespace App\Entity\Base;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Entity\Attachments\AssemblyAttachment;
use App\Entity\Attachments\AttachmentType;
use App\Entity\Attachments\Attachment;
use App\Entity\Attachments\AttachmentTypeAttachment;
@ -68,7 +71,7 @@ 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, 'project_attachment' => ProjectAttachment::class, 'assembly_attachment' => AssemblyAttachment::class, 'storelocation_attachment' => StorageLocationAttachment::class, 'supplier_attachment' => SupplierAttachment::class, 'user_attachment' => UserAttachment::class, 'category' => Category::class, 'project' => Project::class, 'project_bom_entry' => ProjectBOMEntry::class, 'assembly' => Assembly::class, 'assembly_bom_entry' => AssemblyBOMEntry::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])]
#[ORM\MappedSuperclass(repositoryClass: DBElementRepository::class)]
abstract class AbstractDBElement implements JsonSerializable
{

View file

@ -41,6 +41,8 @@ declare(strict_types=1);
namespace App\Entity\LogSystem;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Attachments\AssemblyAttachment;
use App\Entity\Attachments\Attachment;
use App\Entity\Attachments\AttachmentType;
use App\Entity\Attachments\AttachmentTypeAttachment;
@ -58,6 +60,7 @@ use App\Entity\Attachments\UserAttachment;
use App\Entity\Base\AbstractDBElement;
use App\Entity\Contracts\LogWithEventUndoInterface;
use App\Entity\Contracts\NamedElementInterface;
use App\Entity\Parameters\AssemblyParameter;
use App\Entity\ProjectSystem\Project;
use App\Entity\Parameters\AbstractParameter;
use App\Entity\Parameters\AttachmentTypeParameter;
@ -147,6 +150,7 @@ class CollectionElementDeleted extends AbstractLogEntry implements LogWithEventU
{
if (is_a($abstract_class, AbstractParameter::class, true)) {
return match ($this->getTargetClass()) {
Assembly::class => AssemblyParameter::class,
AttachmentType::class => AttachmentTypeParameter::class,
Category::class => CategoryParameter::class,
Currency::class => CurrencyParameter::class,
@ -168,6 +172,7 @@ class CollectionElementDeleted extends AbstractLogEntry implements LogWithEventU
Category::class => CategoryAttachment::class,
Currency::class => CurrencyAttachment::class,
Project::class => ProjectAttachment::class,
Assembly::class => AssemblyAttachment::class,
Footprint::class => FootprintAttachment::class,
Group::class => GroupAttachment::class,
Manufacturer::class => ManufacturerAttachment::class,

View file

@ -42,6 +42,8 @@ use App\Entity\PriceInformations\Orderdetail;
use App\Entity\PriceInformations\Pricedetail;
use App\Entity\ProjectSystem\Project;
use App\Entity\ProjectSystem\ProjectBOMEntry;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Entity\UserSystem\Group;
use App\Entity\UserSystem\User;
@ -71,6 +73,8 @@ enum LogTargetType: int
case PART_ASSOCIATION = 20;
case BULK_INFO_PROVIDER_IMPORT_JOB = 21;
case BULK_INFO_PROVIDER_IMPORT_JOB_PART = 22;
case ASSEMBLY = 23;
case ASSEMBLY_BOM_ENTRY = 24;
/**
* Returns the class name of the target type or null if the target type is NONE.
@ -86,6 +90,8 @@ enum LogTargetType: int
self::CATEGORY => Category::class,
self::PROJECT => Project::class,
self::BOM_ENTRY => ProjectBOMEntry::class,
self::ASSEMBLY => Assembly::class,
self::ASSEMBLY_BOM_ENTRY => AssemblyBOMEntry::class,
self::FOOTPRINT => Footprint::class,
self::GROUP => Group::class,
self::MANUFACTURER => Manufacturer::class,

View file

@ -73,7 +73,7 @@ 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, 11 => AssemblyParameter::class])]
#[ORM\Table('parameters')]
#[ORM\Index(columns: ['name'], name: 'parameter_name_idx')]
#[ORM\Index(columns: ['param_group'], name: 'parameter_group_idx')]
@ -103,7 +103,7 @@ abstract class AbstractParameter extends AbstractNamedDBElement implements Uniqu
*/
private const API_DISCRIMINATOR_MAP = ["Part" => PartParameter::class,
"AttachmentType" => AttachmentTypeParameter::class, "Category" => CategoryParameter::class, "Currency" => CurrencyParameter::class,
"Project" => ProjectParameter::class, "Footprint" => FootprintParameter::class, "Group" => GroupParameter::class,
"Project" => ProjectParameter::class, "Assembly" => AssemblyParameter::class, "Footprint" => FootprintParameter::class, "Group" => GroupParameter::class,
"Manufacturer" => ManufacturerParameter::class, "MeasurementUnit" => MeasurementUnitParameter::class,
"StorageLocation" => StorageLocationParameter::class, "Supplier" => SupplierParameter::class];

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\AssemblySystem\Assembly;
use App\Entity\Base\AbstractDBElement;
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 AssemblyParameter extends AbstractParameter
{
final public const ALLOWED_ELEMENT_CLASS = Assembly::class;
/**
* @var Assembly the element this para is associated with
*/
#[ORM\ManyToOne(targetEntity: Assembly::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

@ -54,6 +54,7 @@ use App\Entity\Parts\PartTraits\InstockTrait;
use App\Entity\Parts\PartTraits\ManufacturerTrait;
use App\Entity\Parts\PartTraits\OrderTrait;
use App\Entity\Parts\PartTraits\ProjectTrait;
use App\Entity\Parts\PartTraits\AssemblyTrait;
use App\EntityListeners\TreeCacheInvalidationListener;
use App\Repository\PartRepository;
use App\Validator\Constraints\UniqueObjectCollection;
@ -125,6 +126,7 @@ class Part extends AttachmentContainingDBElement
use OrderTrait;
use ParametersTrait;
use ProjectTrait;
use AssemblyTrait;
use AssociationTrait;
use EDATrait;
@ -186,6 +188,7 @@ class Part extends AttachmentContainingDBElement
$this->orderdetails = new ArrayCollection();
$this->parameters = new ArrayCollection();
$this->project_bom_entries = new ArrayCollection();
$this->assembly_bom_entries = new ArrayCollection();
$this->associated_parts_as_owner = new ArrayCollection();
$this->associated_parts_as_other = new ArrayCollection();

View file

@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
namespace App\Entity\Parts\PartTraits;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\AssemblySystem\AssemblyBOMEntry;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
trait AssemblyTrait
{
/**
* @var Collection<AssemblyBOMEntry> $assembly_bom_entries
*/
#[ORM\OneToMany(mappedBy: 'part', targetEntity: AssemblyBOMEntry::class, cascade: ['remove'], orphanRemoval: true)]
protected Collection $assembly_bom_entries;
/**
* @var Assembly|null If a assembly is set here, then this part is special and represents the builds of an assembly.
*/
#[ORM\OneToOne(inversedBy: 'build_part', targetEntity: Assembly::class)]
#[ORM\JoinColumn]
protected ?Assembly $built_assembly = null;
/**
* Returns all AssemblyBOMEntry that use this part.
*
* @phpstan-return Collection<int, AssemblyBOMEntry>
*/
public function getAssemblyBomEntries(): Collection
{
return $this->assembly_bom_entries;
}
/**
* Checks whether this part represents the builds of a assembly
* @return bool True if it represents the builds, false if not
*/
#[Groups(['part:read'])]
public function isAssemblyBuildPart(): bool
{
return $this->built_assembly !== null;
}
/**
* Returns the assembly that this part represents the builds of, or null if it doesn't
*/
public function getBuiltAssembly(): ?Assembly
{
return $this->built_assembly;
}
/**
* Sets the assembly that this part represents the builds of
* @param Assembly|null $built_assembly The assembly that this part represents the builds of, or null if it is not a build part
*/
public function setBuiltAssembly(?Assembly $built_assembly): self
{
$this->built_assembly = $built_assembly;
return $this;
}
/**
* Get all assemblies which uses this part.
*
* @return Assembly[] all assemblies which uses this part as a one-dimensional array of Assembly objects
*/
public function getAssemblies(): array
{
$assemblies = [];
foreach($this->assembly_bom_entries as $entry) {
$assemblies[] = $entry->getAssembly();
}
return $assemblies;
}
}

View file

@ -108,6 +108,7 @@ class Project extends AbstractStructuralDBElement
#[Groups(['extended', 'full', 'import'])]
#[ORM\OneToMany(mappedBy: 'project', targetEntity: ProjectBOMEntry::class, cascade: ['persist', 'remove'], orphanRemoval: true)]
#[UniqueObjectCollection(message: 'project.bom_entry.part_already_in_bom', fields: ['part'])]
#[UniqueObjectCollection(message: 'project.bom_entry.assembly_already_in_bom', fields: ['assembly'])]
#[UniqueObjectCollection(message: 'project.bom_entry.name_already_in_bom', fields: ['name'])]
protected Collection $bom_entries;

View file

@ -35,6 +35,7 @@ use ApiPlatform\Metadata\Post;
use ApiPlatform\OpenApi\Model\Operation;
use ApiPlatform\Serializer\Filter\PropertyFilter;
use App\ApiPlatform\Filter\LikeFilter;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Contracts\TimeStampableInterface;
use App\Validator\UniqueValidatableInterface;
use Doctrine\DBAL\Types\Types;
@ -103,7 +104,10 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte
/**
* @var string|null An optional name describing this BOM entry (useful for non-part entries)
*/
#[Assert\Expression('this.getPart() !== null or this.getName() !== null', message: 'validator.project.bom_entry.name_or_part_needed')]
#[Assert\Expression(
'this.getPart() !== null or this.getAssembly() !== null or (this.getName() !== null and this.getName() != "")',
message: 'validator.project.bom_entry.part_or_assembly_needed'
)]
#[ORM\Column(type: Types::STRING, nullable: true)]
#[Groups(['bom_entry:read', 'bom_entry:write', 'import', 'simple', 'extended', 'full'])]
protected ?string $name = null;
@ -131,6 +135,18 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte
#[Groups(['bom_entry:read', 'bom_entry:write', 'full'])]
protected ?Part $part = null;
/**
* @var Assembly|null The associated assembly
*/
#[Assert\Expression(
'(this.getPart() === null or this.getAssembly() === null) and (this.getName() === null or (this.getName() != null and this.getName() != ""))',
message: 'validator.project.bom_entry.only_part_or_assembly_allowed'
)]
#[ORM\ManyToOne(targetEntity: Assembly::class, inversedBy: 'assembly_bom_entries')]
#[ORM\JoinColumn(name: 'id_assembly')]
#[Groups(['bom_entry:read', 'bom_entry:write', ])]
protected ?Assembly $assembly = null;
/**
* @var BigDecimal|null The price of this non-part BOM entry
*/
@ -212,8 +228,6 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte
return $this;
}
public function getPart(): ?Part
{
return $this->part;
@ -225,6 +239,16 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte
return $this;
}
public function getAssembly(): ?Assembly
{
return $this->assembly;
}
public function setAssembly(?Assembly $assembly): void
{
$this->assembly = $assembly;
}
/**
* Returns the price of this BOM entry, if existing.
* Prices are only valid on non-Part BOM entries.
@ -262,6 +286,15 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte
return $this->part instanceof Part;
}
/**
* Checks whether this BOM entry is a assembly associated BOM entry or not.
* @return bool True if this BOM entry is a assembly associated BOM entry, false otherwise.
*/
public function isAssemblyBomEntry(): bool
{
return $this->assembly instanceof Assembly;
}
#[Assert\Callback]
public function validate(ExecutionContextInterface $context, $payload): void
{
@ -323,6 +356,7 @@ class ProjectBOMEntry extends AbstractDBElement implements UniqueValidatableInte
return [
'name' => $this->getName(),
'part' => $this->getPart()?->getID(),
'assembly' => $this->getAssembly()?->getID(),
];
}
}

View file

@ -0,0 +1,64 @@
<?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\Form\AdminPages;
use App\Entity\Base\AbstractNamedDBElement;
use App\Form\AssemblySystem\AssemblyBOMEntryCollectionType;
use App\Form\Type\RichTextEditorType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
class AssemblyAdminForm extends BaseEntityAdminForm
{
protected function additionalFormElements(FormBuilderInterface $builder, array $options, AbstractNamedDBElement $entity): void
{
$builder->add('description', RichTextEditorType::class, [
'required' => false,
'label' => 'part.edit.description',
'mode' => 'markdown-single_line',
'empty_data' => '',
'attr' => [
'placeholder' => 'part.edit.description.placeholder',
'rows' => 2,
],
]);
$builder->add('bom_entries', AssemblyBOMEntryCollectionType::class);
$builder->add('status', ChoiceType::class, [
'attr' => [
'class' => 'form-select',
],
'label' => 'assembly.edit.status',
'required' => false,
'empty_data' => '',
'choices' => [
'assembly.status.draft' => 'draft',
'assembly.status.planning' => 'planning',
'assembly.status.in_production' => 'in_production',
'assembly.status.finished' => 'finished',
'assembly.status.archived' => 'archived',
],
]);
}
}

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Form\AdminPages;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\PriceInformations\Currency;
use App\Entity\ProjectSystem\Project;
use App\Entity\UserSystem\Group;
@ -114,7 +115,7 @@ class BaseEntityAdminForm extends AbstractType
);
}
if ($entity instanceof AbstractStructuralDBElement && !($entity instanceof Group || $entity instanceof Project || $entity instanceof Currency)) {
if ($entity instanceof AbstractStructuralDBElement && !($entity instanceof Group || $entity instanceof Project || $entity instanceof Assembly || $entity instanceof Currency)) {
$builder->add('alternative_names', TextType::class, [
'required' => false,
'label' => 'entity.edit.alternative_names.label',

View file

@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2023 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\Form\AssemblySystem;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Form\Type\StructuralEntityType;
use App\Validator\Constraints\UniqueObjectCollection;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\NotNull;
class AssemblyAddPartsType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('assembly', StructuralEntityType::class, [
'class' => Assembly::class,
'required' => true,
'disabled' => $options['assembly'] instanceof Assembly, //If a assembly is given, disable the field
'data' => $options['assembly'],
'constraints' => [
new NotNull()
]
]);
$builder->add('bom_entries', AssemblyBOMEntryCollectionType::class, [
'entry_options' => [
'constraints' => [
new UniqueEntity(fields: ['part', 'assembly'], message: 'assembly.bom_entry.part_already_in_bom',
entityClass: AssemblyBOMEntry::class),
new UniqueEntity(fields: ['name', 'assembly'], message: 'assembly.bom_entry.name_already_in_bom',
entityClass: AssemblyBOMEntry::class, ignoreNull: true),
]
],
'constraints' => [
new UniqueObjectCollection(message: 'assembly.bom_entry.part_already_in_bom', fields: ['part']),
new UniqueObjectCollection(message: 'assembly.bom_entry.name_already_in_bom', fields: ['name']),
]
]);
$builder->add('submit', SubmitType::class, ['label' => 'save']);
//After submit set the assembly for all bom entries, so that it can be validated properly
$builder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) {
$form = $event->getForm();
/** @var Assembly $assembly */
$assembly = $form->get('assembly')->getData();
$bom_entries = $form->get('bom_entries')->getData();
foreach ($bom_entries as $bom_entry) {
$bom_entry->setAssembly($assembly);
}
});
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'assembly' => null,
]);
$resolver->setAllowedTypes('assembly', ['null', Assembly::class]);
}
}

View file

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace App\Form\AssemblySystem;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class AssemblyBOMEntryCollectionType extends AbstractType
{
public function getParent(): string
{
return CollectionType::class;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'entry_type' => AssemblyBOMEntryType::class,
'entry_options' => [
'label' => false,
],
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'reindex_enable' => true,
'label' => false,
]);
}
}

View file

@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace App\Form\AssemblySystem;
use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Form\Type\BigDecimalNumberType;
use App\Form\Type\CurrencyEntityType;
use App\Form\Type\PartSelectType;
use App\Form\Type\RichTextEditorType;
use App\Form\Type\SIUnitType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Event\PreSetDataEvent;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
class AssemblyBOMEntryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) {
$form = $event->getForm();
/** @var AssemblyBOMEntry $data */
$data = $event->getData();
$form->add('quantity', SIUnitType::class, [
'label' => 'assembly.bom.quantity',
'measurement_unit' => $data && $data->getPart() ? $data->getPart()->getPartUnit() : null,
]);
});
$builder
->add('part', PartSelectType::class, [
'required' => false,
])
->add('name', TextType::class, [
'label' => 'assembly.bom.name',
'required' => false,
])
->add('mountnames', TextType::class, [
'required' => false,
'label' => 'assembly.bom.mountnames',
'empty_data' => '',
'attr' => [
'class' => 'tagsinput',
'data-controller' => 'elements--tagsinput',
]
])
->add('comment', RichTextEditorType::class, [
'required' => false,
'label' => 'assembly.bom.comment',
'empty_data' => '',
'mode' => 'markdown-single_line',
'attr' => [
'rows' => 2,
],
])
->add('price', BigDecimalNumberType::class, [
'label' => false,
'required' => false,
'scale' => 5,
'html5' => true,
'attr' => [
'min' => 0,
'step' => 'any',
],
])
->add('priceCurrency', CurrencyEntityType::class, [
'required' => false,
'label' => false,
'short' => true,
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => AssemblyBOMEntry::class,
]);
}
}

View file

@ -0,0 +1,183 @@
<?php
declare(strict_types=1);
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2023 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\Form\AssemblySystem;
use App\Helpers\Assemblies\AssemblyBuildRequest;
use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\Parts\Part;
use App\Entity\Parts\PartLot;
use App\Form\Type\PartLotSelectType;
use App\Form\Type\SIUnitType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Event\PreSetDataEvent;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class AssemblyBuildType extends AbstractType implements DataMapperInterface
{
public function __construct(private readonly Security $security)
{
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'compound' => true,
'data_class' => AssemblyBuildRequest::class
]);
}
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->setDataMapper($this);
$builder->add('submit', SubmitType::class, [
'label' => 'assembly.build.btn_build',
'disabled' => !$this->security->isGranted('@parts_stock.withdraw'),
]);
$builder->add('dontCheckQuantity', CheckboxType::class, [
'label' => 'assembly.build.dont_check_quantity',
'help' => 'assembly.build.dont_check_quantity.help',
'required' => false,
'attr' => [
'data-controller' => 'pages--dont-check-quantity-checkbox'
]
]);
$builder->add('comment', TextType::class, [
'label' => 'part.info.withdraw_modal.comment',
'help' => 'part.info.withdraw_modal.comment.hint',
'empty_data' => '',
'required' => false,
]);
//The form is initially empty, we have to define the fields after we know the data
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) {
$form = $event->getForm();
/** @var AssemblyBuildRequest $build_request */
$build_request = $event->getData();
$form->add('addBuildsToBuildsPart', CheckboxType::class, [
'label' => 'assembly.build.add_builds_to_builds_part',
'required' => false,
'disabled' => !$build_request->getAssembly()->getBuildPart() instanceof Part,
]);
if ($build_request->getAssembly()->getBuildPart() instanceof Part) {
$form->add('buildsPartLot', PartLotSelectType::class, [
'label' => 'assembly.build.builds_part_lot',
'required' => false,
'part' => $build_request->getAssembly()->getBuildPart(),
'placeholder' => 'assembly.build.buildsPartLot.new_lot'
]);
}
foreach ($build_request->getPartBomEntries() as $bomEntry) {
//Every part lot has a field to specify the number of parts to take from this lot
foreach ($build_request->getPartLotsForBOMEntry($bomEntry) as $lot) {
$form->add('lot_' . $lot->getID(), SIUnitType::class, [
'label' => false,
'measurement_unit' => $bomEntry->getPart()->getPartUnit(),
'max' => min($build_request->getNeededAmountForBOMEntry($bomEntry), $lot->getAmount()),
'disabled' => !$this->security->isGranted('withdraw', $lot),
]);
}
}
});
}
public function mapDataToForms($data, \Traversable $forms): void
{
if (!$data instanceof AssemblyBuildRequest) {
throw new \RuntimeException('Data must be an instance of ' . AssemblyBuildRequest::class);
}
/** @var FormInterface[] $forms */
$forms = iterator_to_array($forms);
foreach ($forms as $key => $form) {
//Extract the lot id from the form name
$matches = [];
if (preg_match('/^lot_(\d+)$/', $key, $matches)) {
$lot_id = (int) $matches[1];
$form->setData($data->getLotWithdrawAmount($lot_id));
}
}
$forms['comment']->setData($data->getComment());
$forms['dontCheckQuantity']->setData($data->isDontCheckQuantity());
$forms['addBuildsToBuildsPart']->setData($data->getAddBuildsToBuildsPart());
if (isset($forms['buildsPartLot'])) {
$forms['buildsPartLot']->setData($data->getBuildsPartLot());
}
}
public function mapFormsToData(\Traversable $forms, &$data): void
{
if (!$data instanceof AssemblyBuildRequest) {
throw new \RuntimeException('Data must be an instance of ' . AssemblyBuildRequest::class);
}
/** @var FormInterface[] $forms */
$forms = iterator_to_array($forms);
foreach ($forms as $key => $form) {
//Extract the lot id from the form name
$matches = [];
if (preg_match('/^lot_(\d+)$/', $key, $matches)) {
$lot_id = (int) $matches[1];
$data->setLotWithdrawAmount($lot_id, (float) $form->getData());
}
}
$data->setComment($forms['comment']->getData());
$data->setDontCheckQuantity($forms['dontCheckQuantity']->getData());
if (isset($forms['buildsPartLot'])) {
$lot = $forms['buildsPartLot']->getData();
if (!$lot) { //When the user selected "Create new lot", create a new lot
$lot = new PartLot();
$description = 'Build ' . date('Y-m-d H:i:s');
if ($data->getComment() !== '') {
$description .= ' (' . $data->getComment() . ')';
}
$lot->setDescription($description);
$data->getAssembly()->getBuildPart()->addPartLot($lot);
}
$data->setBuildsPartLot($lot);
}
//This has to be set after the builds part lot, so that it can disable the option
$data->setAddBuildsToBuildsPart($forms['addBuildsToBuildsPart']->getData());
}
}

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace App\Form\Filters;
use App\DataTables\Filters\AttachmentFilter;
use App\Entity\Attachments\AssemblyAttachment;
use App\Entity\Attachments\AttachmentType;
use App\Entity\Attachments\AttachmentTypeAttachment;
use App\Entity\Attachments\CategoryAttachment;
@ -80,6 +81,7 @@ class AttachmentFilterType extends AbstractType
'category.label' => CategoryAttachment::class,
'currency.label' => CurrencyAttachment::class,
'project.label' => ProjectAttachment::class,
'assembly.label' => AssemblyAttachment::class,
'footprint.label' => FootprintAttachment::class,
'group.label' => GroupAttachment::class,
'label_profile.label' => LabelAttachment::class,

View file

@ -59,6 +59,7 @@ class ProjectAddPartsType extends AbstractType
],
'constraints' => [
new UniqueObjectCollection(message: 'project.bom_entry.part_already_in_bom', fields: ['part']),
new UniqueObjectCollection(message: 'project.bom_entry.assembly_already_in_bom', fields: ['assembly']),
new UniqueObjectCollection(message: 'project.bom_entry.name_already_in_bom', fields: ['name']),
]
]);

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\Form\ProjectSystem;
use App\Entity\ProjectSystem\ProjectBOMEntry;
use App\Form\Type\AssemblySelectType;
use App\Form\Type\BigDecimalNumberType;
use App\Form\Type\CurrencyEntityType;
use App\Form\Type\PartSelectType;
@ -22,8 +23,6 @@ class ProjectBOMEntryType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) {
$form = $event->getForm();
/** @var ProjectBOMEntry $data */
@ -36,11 +35,14 @@ class ProjectBOMEntryType extends AbstractType
});
$builder
->add('part', PartSelectType::class, [
'label' => 'project.bom.part',
'required' => false,
])
->add('assembly', AssemblySelectType::class, [
'label' => 'project.bom.assembly',
'required' => false,
])
->add('name', TextType::class, [
'label' => 'project.bom.name',
'required' => false,
@ -77,10 +79,7 @@ class ProjectBOMEntryType extends AbstractType
'required' => false,
'label' => false,
'short' => true,
])
;
]);
}
public function configureOptions(OptionsResolver $resolver): void

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
*/
namespace App\Form\ProjectSystem;
use App\Helpers\Assemblies\AssemblyBuildRequest;
use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\Parts\Part;
use App\Entity\Parts\PartLot;
@ -38,10 +39,11 @@ use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Contracts\Translation\TranslatorInterface;
class ProjectBuildType extends AbstractType implements DataMapperInterface
{
public function __construct(private readonly Security $security)
public function __construct(private readonly Security $security, private readonly TranslatorInterface $translator)
{
}
@ -82,36 +84,54 @@ class ProjectBuildType extends AbstractType implements DataMapperInterface
//The form is initially empty, we have to define the fields after we know the data
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) {
$form = $event->getForm();
/** @var ProjectBuildRequest $build_request */
$build_request = $event->getData();
/** @var ProjectBuildRequest $projectBuildRequest */
$projectBuildRequest = $event->getData();
$form->add('addBuildsToBuildsPart', CheckboxType::class, [
'label' => 'project.build.add_builds_to_builds_part',
'required' => false,
'disabled' => !$build_request->getProject()->getBuildPart() instanceof Part,
'disabled' => !$projectBuildRequest->getProject()->getBuildPart() instanceof Part,
]);
if ($build_request->getProject()->getBuildPart() instanceof Part) {
if ($projectBuildRequest->getProject()->getBuildPart() instanceof Part) {
$form->add('buildsPartLot', PartLotSelectType::class, [
'label' => 'project.build.builds_part_lot',
'required' => false,
'part' => $build_request->getProject()->getBuildPart(),
'part' => $projectBuildRequest->getProject()->getBuildPart(),
'placeholder' => 'project.build.buildsPartLot.new_lot'
]);
}
foreach ($build_request->getPartBomEntries() as $bomEntry) {
foreach ($projectBuildRequest->getPartBomEntries() as $bomEntry) {
//Every part lot has a field to specify the number of parts to take from this lot
foreach ($build_request->getPartLotsForBOMEntry($bomEntry) as $lot) {
foreach ($projectBuildRequest->getPartLotsForBOMEntry($bomEntry) as $lot) {
$form->add('lot_' . $lot->getID(), SIUnitType::class, [
'label' => false,
'measurement_unit' => $bomEntry->getPart()->getPartUnit(),
'max' => min($build_request->getNeededAmountForBOMEntry($bomEntry), $lot->getAmount()),
'max' => min($projectBuildRequest->getNeededAmountForBOMEntry($bomEntry), $lot->getAmount()),
'disabled' => !$this->security->isGranted('withdraw', $lot),
]);
}
}
foreach ($projectBuildRequest->getAssemblyBomEntries() as $bomEntry) {
$assemblyBuildRequest = new AssemblyBuildRequest($bomEntry->getAssembly(), $projectBuildRequest->getNumberOfBuilds());
//Add fields for assembly bom entries
foreach ($assemblyBuildRequest->getPartBomEntries() as $partBomEntry) {
foreach ($assemblyBuildRequest->getPartLotsForBOMEntry($partBomEntry) as $lot) {
$form->add('lot_' . $lot->getID(), SIUnitType::class, [
'label' => $this->translator->trans('project.build.builds_part_lot_label', [
'%name%' => $partBomEntry->getPart()->getName(),
'%quantity%' => $partBomEntry->getQuantity() * $projectBuildRequest->getNumberOfBuilds()
]),
'measurement_unit' => $partBomEntry->getPart()->getPartUnit(),
'max' => min($assemblyBuildRequest->getNeededAmountForBOMEntry($partBomEntry), $lot->getAmount()),
'disabled' => !$this->security->isGranted('withdraw', $lot),
]);
}
}
}
});
}

View file

@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
namespace App\Form\Type;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Attachments\Attachment;
use App\Services\Attachments\AssemblyPreviewGenerator;
use App\Services\Attachments\AttachmentURLGenerator;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\ChoiceList\ChoiceList;
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Event\PreSetDataEvent;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
class AssemblySelectType extends AbstractType implements DataMapperInterface
{
public function __construct(private readonly UrlGeneratorInterface $urlGenerator, private readonly EntityManagerInterface $em, private readonly AssemblyPreviewGenerator $previewGenerator, private readonly AttachmentURLGenerator $attachmentURLGenerator)
{
}
public function buildForm(FormBuilderInterface $builder, array $options): void
{
//At initialization, we have to fill the form element with our selected data, so the user can see it
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) {
$form = $event->getForm();
$config = $form->getConfig()->getOptions();
$data = $event->getData() ?? [];
$config['compound'] = false;
$config['choices'] = is_iterable($data) ? $data : [$data];
$config['error_bubbling'] = true;
$form->add('autocomplete', EntityType::class, $config);
});
//After form submit, we have to add the selected element as choice, otherwise the form will not accept this element
$builder->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) {
$data = $event->getData();
$form = $event->getForm();
$options = $form->get('autocomplete')->getConfig()->getOptions();
if (!isset($data['autocomplete']) || '' === $data['autocomplete'] || empty($data['autocomplete'])) {
$options['choices'] = [];
} else {
//Extract the ID from the submitted data
$id = $data['autocomplete'];
//Find the element in the database
$element = $this->em->find($options['class'], $id);
//Add the element as choice
$options['choices'] = [$element];
$options['error_bubbling'] = true;
$form->add('autocomplete', EntityType::class, $options);
}
});
$builder->setDataMapper($this);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'class' => Assembly::class,
'choice_label' => 'name',
'placeholder' => 'None',
'compound' => true,
'error_bubbling' => false,
]);
error_log($this->urlGenerator->generate('typeahead_assemblies', ['query' => '__QUERY__']));
$resolver->setDefaults([
'attr' => [
'data-controller' => 'elements--assembly-select',
'data-autocomplete' => $this->urlGenerator->generate('typeahead_assemblies', ['query' => '__QUERY__']),
'autocomplete' => 'off',
],
]);
$resolver->setDefaults([
//Prefill the selected choice with the needed data, so the user can see it without an additional Ajax request
'choice_attr' => ChoiceList::attr($this, function (?Assembly $assembly) {
if($assembly instanceof Assembly) {
//Determine the picture to show:
$preview_attachment = $this->previewGenerator->getTablePreviewAttachment($assembly);
if ($preview_attachment instanceof Attachment) {
$preview_url = $this->attachmentURLGenerator->getThumbnailURL($preview_attachment,
'thumbnail_sm');
} else {
$preview_url = '';
}
}
return $assembly instanceof Assembly ? [
'data-description' => $assembly->getDescription() ? mb_strimwidth($assembly->getDescription(), 0, 127, '...') : '',
'data-category' => '',
'data-footprint' => '',
'data-image' => $preview_url,
] : [];
})
]);
}
public function mapDataToForms($data, \Traversable $forms): void
{
$form = current(iterator_to_array($forms, false));
$form->setData($data);
}
public function mapFormsToData(\Traversable $forms, &$data): void
{
$form = current(iterator_to_array($forms, false));
$data = $form->getData();
}
}

View file

@ -50,7 +50,7 @@ class PartSelectType extends AbstractType implements DataMapperInterface
$options = $form->get('autocomplete')->getConfig()->getOptions();
if (!isset($data['autocomplete']) || '' === $data['autocomplete']) {
if (!isset($data['autocomplete']) || '' === $data['autocomplete'] || empty($data['autocomplete'])) {
$options['choices'] = [];
} else {
//Extract the ID from the submitted data
@ -84,7 +84,6 @@ class PartSelectType extends AbstractType implements DataMapperInterface
'data-autocomplete' => $this->urlGenerator->generate('typeahead_parts', ['query' => '__QUERY__']),
//Disable browser autocomplete
'autocomplete' => 'off',
],
]);
@ -103,7 +102,7 @@ class PartSelectType extends AbstractType implements DataMapperInterface
}
return $part instanceof Part ? [
'data-description' => mb_strimwidth($part->getDescription(), 0, 127, '...'),
'data-description' => $part->getDescription() ? mb_strimwidth($part->getDescription(), 0, 127, '...') : '',
'data-category' => $part->getCategory() instanceof Category ? $part->getCategory()->getName() : '',
'data-footprint' => $part->getFootprint() instanceof Footprint ? $part->getFootprint()->getName() : '',
'data-image' => $preview_url,

View file

@ -0,0 +1,306 @@
<?php
declare(strict_types=1);
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2023 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\Helpers\Assemblies;
use App\Entity\Parts\Part;
use App\Entity\Parts\PartLot;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Validator\Constraints\AssemblySystem\ValidAssemblyBuildRequest;
/**
* @see \App\Tests\Helpers\Assemblies\AssemblyBuildRequestTest
*/
#[ValidAssemblyBuildRequest]
final class AssemblyBuildRequest
{
private readonly int $number_of_builds;
/**
* @var array<int, float>
*/
private array $withdraw_amounts = [];
private string $comment = '';
private ?PartLot $builds_lot = null;
private bool $add_build_to_builds_part = false;
private bool $dont_check_quantity = false;
/**
* @param Assembly $assembly The assembly that should be build
* @param int $number_of_builds The number of builds that should be created
*/
public function __construct(private readonly Assembly $assembly, int $number_of_builds)
{
if ($number_of_builds < 1) {
throw new \InvalidArgumentException('Number of builds must be at least 1!');
}
$this->number_of_builds = $number_of_builds;
$this->initializeArray();
//By default, use the first available lot of builds part if there is one.
if($assembly->getBuildPart() instanceof Part) {
$this->add_build_to_builds_part = true;
foreach( $assembly->getBuildPart()->getPartLots() as $lot) {
if (!$lot->isInstockUnknown()) {
$this->builds_lot = $lot;
break;
}
}
}
}
private function initializeArray(): void
{
//Completely reset the array
$this->withdraw_amounts = [];
//Now create an array for each BOM entry
foreach ($this->getPartBomEntries() as $bom_entry) {
$remaining_amount = $this->getNeededAmountForBOMEntry($bom_entry);
foreach($this->getPartLotsForBOMEntry($bom_entry) as $lot) {
//If the lot has instock use it for the build
$this->withdraw_amounts[$lot->getID()] = min($remaining_amount, $lot->getAmount());
$remaining_amount -= max(0, $this->withdraw_amounts[$lot->getID()]);
}
}
}
/**
* Ensure that the assemblyBOMEntry belongs to the assembly, otherwise throw an exception.
*/
private function ensureBOMEntryValid(AssemblyBOMEntry $entry): void
{
if ($entry->getAssembly() !== $this->assembly) {
throw new \InvalidArgumentException('The given BOM entry does not belong to the assembly!');
}
}
/**
* Returns the partlot where the builds should be added to, or null if it should not be added to any lot.
*/
public function getBuildsPartLot(): ?PartLot
{
return $this->builds_lot;
}
/**
* Return if the builds should be added to the builds part of this assembly as new stock
*/
public function getAddBuildsToBuildsPart(): bool
{
return $this->add_build_to_builds_part;
}
/**
* Set if the builds should be added to the builds part of this assembly as new stock
* @return $this
*/
public function setAddBuildsToBuildsPart(bool $new_value): self
{
$this->add_build_to_builds_part = $new_value;
if ($new_value === false) {
$this->builds_lot = null;
}
return $this;
}
/**
* Set the partlot where the builds should be added to, or null if it should not be added to any lot.
* The part lot must belong to the assembly build part, or an exception is thrown!
* @return $this
*/
public function setBuildsPartLot(?PartLot $new_part_lot): self
{
//Ensure that this new_part_lot belongs to the assembly
if (($new_part_lot instanceof PartLot && $new_part_lot->getPart() !== $this->assembly->getBuildPart()) || !$this->assembly->getBuildPart() instanceof Part) {
throw new \InvalidArgumentException('The given part lot does not belong to the assemblies build part!');
}
if ($new_part_lot instanceof PartLot) {
$this->setAddBuildsToBuildsPart(true);
}
$this->builds_lot = $new_part_lot;
return $this;
}
/**
* Returns the comment where the user can write additional information about the build.
*/
public function getComment(): string
{
return $this->comment;
}
/**
* Sets the comment where the user can write additional information about the build.
*/
public function setComment(string $comment): void
{
$this->comment = $comment;
}
/**
* Returns the amount of parts that should be withdrawn from the given lot for the corresponding BOM entry.
* @param PartLot|int $lot The part lot (or the ID of the part lot) for which the withdrawal amount should be got
*/
public function getLotWithdrawAmount(PartLot|int $lot): float
{
$lot_id = $lot instanceof PartLot ? $lot->getID() : $lot;
if (! array_key_exists($lot_id, $this->withdraw_amounts)) {
throw new \InvalidArgumentException('The given lot is not in the withdraw amounts array!');
}
return $this->withdraw_amounts[$lot_id];
}
/**
* Sets the amount of parts that should be withdrawn from the given lot for the corresponding BOM entry.
* @param PartLot|int $lot The part lot (or the ID of the part lot) for which the withdrawal amount should be got
* @return $this
*/
public function setLotWithdrawAmount(PartLot|int $lot, float $amount): self
{
if ($lot instanceof PartLot) {
$lot_id = $lot->getID();
} elseif (is_int($lot)) {
$lot_id = $lot;
} else {
throw new \InvalidArgumentException('The given lot must be an instance of PartLot or an ID of a PartLot!');
}
$this->withdraw_amounts[$lot_id] = $amount;
return $this;
}
/**
* Returns the sum of all withdraw amounts for the given BOM entry.
*/
public function getWithdrawAmountSum(AssemblyBOMEntry $entry): float
{
$this->ensureBOMEntryValid($entry);
$sum = 0;
foreach ($this->getPartLotsForBOMEntry($entry) as $lot) {
$sum += $this->getLotWithdrawAmount($lot);
}
if ($entry->getPart() && !$entry->getPart()->useFloatAmount()) {
$sum = round($sum);
}
return $sum;
}
/**
* Returns the number of available lots to take stock from for the given BOM entry.
* @return PartLot[]|null Returns null if the entry is a non-part BOM entry
*/
public function getPartLotsForBOMEntry(AssemblyBOMEntry $assemblyBOMEntry): ?array
{
$this->ensureBOMEntryValid($assemblyBOMEntry);
if (!$assemblyBOMEntry->getPart() instanceof Part) {
return null;
}
//Filter out all lots which have unknown instock
return $assemblyBOMEntry->getPart()->getPartLots()->filter(fn (PartLot $lot) => !$lot->isInstockUnknown())->toArray();
}
/**
* Returns the needed amount of parts for the given BOM entry.
*/
public function getNeededAmountForBOMEntry(AssemblyBOMEntry $entry): float
{
$this->ensureBOMEntryValid($entry);
return $entry->getQuantity() * $this->number_of_builds;
}
/**
* Returns the list of all bom entries.
* @return AssemblyBOMEntry[]
*/
public function getBomEntries(): array
{
return $this->assembly->getBomEntries()->toArray();
}
/**
* Returns all part bom entries.
* @return AssemblyBOMEntry[]
*/
public function getPartBomEntries(): array
{
return $this->assembly->getBomEntries()->filter(fn(AssemblyBOMEntry $entry) => $entry->isPartBomEntry())->toArray();
}
/**
* Returns which assembly should be build
*/
public function getAssembly(): Assembly
{
return $this->assembly;
}
/**
* Returns the number of builds that should be created.
*/
public function getNumberOfBuilds(): int
{
return $this->number_of_builds;
}
/**
* If Set to true, the given withdraw amounts are used without any checks for requirements.
* @return bool
*/
public function isDontCheckQuantity(): bool
{
return $this->dont_check_quantity;
}
/**
* Set to true, the given withdraw amounts are used without any checks for requirements.
* @param bool $dont_check_quantity
* @return $this
*/
public function setDontCheckQuantity(bool $dont_check_quantity): AssemblyBuildRequest
{
$this->dont_check_quantity = $dont_check_quantity;
return $this;
}
}

View file

@ -22,10 +22,13 @@ declare(strict_types=1);
*/
namespace App\Helpers\Projects;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Entity\Parts\Part;
use App\Entity\Parts\PartLot;
use App\Entity\ProjectSystem\Project;
use App\Entity\ProjectSystem\ProjectBOMEntry;
use App\Helpers\Assemblies\AssemblyBuildRequest;
use App\Validator\Constraints\ProjectSystem\ValidProjectBuildRequest;
/**
@ -79,7 +82,7 @@ final class ProjectBuildRequest
//Completely reset the array
$this->withdraw_amounts = [];
//Now create an array for each BOM entry
//Now create an array for each part BOM entry
foreach ($this->getPartBomEntries() as $bom_entry) {
$remaining_amount = $this->getNeededAmountForBOMEntry($bom_entry);
foreach($this->getPartLotsForBOMEntry($bom_entry) as $lot) {
@ -88,6 +91,21 @@ final class ProjectBuildRequest
$remaining_amount -= max(0, $this->withdraw_amounts[$lot->getID()]);
}
}
//Now create an array for each assembly BOM entry
foreach ($this->getAssemblyBomEntries() as $assemblyBomEntry) {
$assemblyBuildRequest = new AssemblyBuildRequest($assemblyBomEntry->getAssembly(), $this->number_of_builds);
//Add fields for assembly bom entries
foreach ($assemblyBuildRequest->getPartBomEntries() as $partBomEntry) {
$remaining_amount = $assemblyBuildRequest->getNeededAmountForBOMEntry($partBomEntry) * $assemblyBomEntry->getQuantity();
foreach ($assemblyBuildRequest->getPartLotsForBOMEntry($partBomEntry) as $lot) {
$this->withdraw_amounts[$lot->getID()] = min($remaining_amount, $lot->getAmount());
$remaining_amount -= max(0, $this->withdraw_amounts[$lot->getID()]);
}
}
}
}
/**
@ -230,12 +248,77 @@ final class ProjectBuildRequest
{
$this->ensureBOMEntryValid($projectBOMEntry);
if (!$projectBOMEntry->getPart() instanceof Part) {
if (!$projectBOMEntry->getPart() instanceof Part && !$projectBOMEntry->getAssembly() instanceof Assembly) {
return null;
}
//Filter out all lots which have unknown instock
return $projectBOMEntry->getPart()->getPartLots()->filter(fn (PartLot $lot) => !$lot->isInstockUnknown())->toArray();
if ($projectBOMEntry->getPart() instanceof Part) {
return $projectBOMEntry->getPart()->getPartLots()->filter(fn (PartLot $lot) => !$lot->isInstockUnknown())->toArray();
} elseif ($projectBOMEntry->getAssembly() instanceof Assembly) {
$assemblyBuildRequest = new AssemblyBuildRequest($projectBOMEntry->getAssembly(), $this->number_of_builds);
//Add fields for assembly bom entries
$result = [];
foreach ($assemblyBuildRequest->getPartBomEntries() as $assemblyBOMEntry) {
$tmp = $assemblyBOMEntry->getPart()->getPartLots()->filter(fn (PartLot $lot) => !$lot->isInstockUnknown())->toArray();
$result = array_merge($result, $tmp);
}
return $result;
}
return null;
}
/**
* Returns all available assembly BOM-entries with no part assigned.
* @return AssemblyBOMEntry[]|null Returns null if no entries found
*/
public function getAssemblyBomEntriesWithoutPart(ProjectBOMEntry $projectBOMEntry): ?array
{
$this->ensureBOMEntryValid($projectBOMEntry);
if (!$projectBOMEntry->getAssembly() instanceof Assembly) {
return null;
}
$assemblyBuildRequest = new AssemblyBuildRequest($projectBOMEntry->getAssembly(), $this->number_of_builds);
$result = [];
foreach ($assemblyBuildRequest->getBomEntries() as $assemblyBOMEntry) {
if ($assemblyBOMEntry->getPart() === null) {
$result[] = $assemblyBOMEntry;
}
}
return count($result) > 0 ? $result : null;
}
/**
* Returns all available assembly BOM-entries with no part assigned.
* @return AssemblyBOMEntry[]|null Returns null if no entries found
*/
public function getAssemblyBomEntriesWithPartNoStock(ProjectBOMEntry $projectBOMEntry): ?array
{
$this->ensureBOMEntryValid($projectBOMEntry);
if (!$projectBOMEntry->getAssembly() instanceof Assembly) {
return null;
}
$assemblyBuildRequest = new AssemblyBuildRequest($projectBOMEntry->getAssembly(), $this->number_of_builds);
$result = [];
foreach ($assemblyBuildRequest->getBomEntries() as $assemblyBOMEntry) {
if ($assemblyBOMEntry->getPart() instanceof Part && $assemblyBOMEntry->getPart()->getPartLots()->filter(fn (PartLot $lot) => !$lot->isInstockUnknown())->count() === 0) {
$result[] = $assemblyBOMEntry;
}
}
return count($result) > 0 ? $result : null;
}
/**
@ -266,6 +349,15 @@ final class ProjectBuildRequest
return $this->project->getBomEntries()->filter(fn(ProjectBOMEntry $entry) => $entry->isPartBomEntry())->toArray();
}
/**
* Returns the all assembly bom entries that have to be built.
* @return ProjectBOMEntry[]
*/
public function getAssemblyBomEntries(): array
{
return $this->project->getBomEntries()->filter(fn(ProjectBOMEntry $entry) => $entry->isAssemblyBomEntry())->toArray();
}
/**
* Returns which project should be build
*/
@ -301,6 +393,4 @@ final class ProjectBuildRequest
$this->dont_check_quantity = $dont_check_quantity;
return $this;
}
}
}

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 - 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\Repository;
use App\Entity\AssemblySystem\Assembly;
/**
* @template TEntityClass of Assembly
* @extends DBElementRepository<TEntityClass>
*/
class AssemblyRepository extends StructuralDBElementRepository
{
/**
* @return Assembly[]
*/
public function autocompleteSearch(string $query, int $max_limits = 50): array
{
$qb = $this->createQueryBuilder('assembly');
$qb->select('assembly')
->where('ILIKE(assembly.name, :query) = TRUE')
->orWhere('ILIKE(assembly.description, :query) = TRUE');
$qb->setParameter('query', '%'.$query.'%');
$qb->setMaxResults($max_limits);
$qb->orderBy('NATSORT(assembly.name)', 'ASC');
return $qb->getQuery()->getResult();
}
}

View file

@ -154,4 +154,14 @@ class DBElementRepository extends EntityRepository
$property->setAccessible(true);
$property->setValue($element, $new_value);
}
protected function save(AbstractDBElement $entity, bool $flush = true): void
{
$manager = $this->getEntityManager();
$manager->persist($entity);
if ($flush) {
$manager->flush();
}
}
}

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Security\Voter;
use App\Entity\Attachments\AssemblyAttachment;
use App\Services\UserSystem\VoterHelper;
use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\Attachments\AttachmentContainingDBElement;
@ -89,6 +90,8 @@ final class AttachmentVoter extends Voter
$param = 'currencies';
} elseif (is_a($subject, ProjectAttachment::class, true)) {
$param = 'projects';
} elseif (is_a($subject, AssemblyAttachment::class, true)) {
$param = 'assemblies';
} elseif (is_a($subject, FootprintAttachment::class, true)) {
$param = 'footprints';
} elseif (is_a($subject, GroupAttachment::class, true)) {

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Security\Voter;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Attachments\AttachmentType;
use App\Entity\ProjectSystem\Project;
use App\Entity\Parts\Category;
@ -47,6 +48,7 @@ final class StructureVoter extends Voter
AttachmentType::class => 'attachment_types',
Category::class => 'categories',
Project::class => 'projects',
Assembly::class => 'assemblies',
Footprint::class => 'footprints',
Manufacturer::class => 'manufacturers',
StorageLocation::class => 'storelocations',

View file

@ -0,0 +1,154 @@
<?php
declare(strict_types=1);
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2023 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\Services\AssemblySystem;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Entity\Parts\Part;
use App\Helpers\Assemblies\AssemblyBuildRequest;
use App\Services\Parts\PartLotWithdrawAddHelper;
/**
* @see \App\Tests\Services\AssemblySystem\AssemblyBuildHelperTest
*/
class AssemblyBuildHelper
{
public function __construct(private readonly PartLotWithdrawAddHelper $withdraw_add_helper)
{
}
/**
* Returns the maximum buildable amount of the given BOM entry based on the stock of the used parts.
* This function only works for BOM entries that are associated with a part.
*/
public function getMaximumBuildableCountForBOMEntry(AssemblyBOMEntry $assemblyBOMEntry): int
{
$part = $assemblyBOMEntry->getPart();
if (!$part instanceof Part) {
throw new \InvalidArgumentException('This function cannot determine the maximum buildable count for a BOM entry without a part!');
}
if ($assemblyBOMEntry->getQuantity() <= 0) {
throw new \RuntimeException('The quantity of the BOM entry must be greater than 0!');
}
$amount_sum = $part->getAmountSum();
return (int) floor($amount_sum / $assemblyBOMEntry->getQuantity());
}
/**
* Returns the maximum buildable amount of the given assembly, based on the stock of the used parts in the BOM.
*/
public function getMaximumBuildableCount(Assembly $assembly): int
{
$maximum_buildable_count = PHP_INT_MAX;
foreach ($assembly->getBomEntries() as $bom_entry) {
//Skip BOM entries without a part (as we can not determine that)
if (!$bom_entry->isPartBomEntry()) {
continue;
}
//The maximum buildable count for the whole assembly is the minimum of all BOM entries
$maximum_buildable_count = min($maximum_buildable_count, $this->getMaximumBuildableCountForBOMEntry($bom_entry));
}
return $maximum_buildable_count;
}
/**
* Checks if the given assembly can be built with the current stock.
* This means that the maximum buildable count is greater or equal than the requested $number_of_assemblies
* @param int $number_of_builds
*/
public function isAssemblyBuildable(Assembly $assembly, int $number_of_builds = 1): bool
{
return $this->getMaximumBuildableCount($assembly) >= $number_of_builds;
}
/**
* Check if the given BOM entry can be built with the current stock.
* This means that the maximum buildable count is greater or equal than the requested $number_of_assemblies
*/
public function isBOMEntryBuildable(AssemblyBOMEntry $bom_entry, int $number_of_builds = 1): bool
{
return $this->getMaximumBuildableCountForBOMEntry($bom_entry) >= $number_of_builds;
}
/**
* Returns the assembly BOM entries for which parts are missing in the stock for the given number of builds
* @param Assembly $assembly The assembly for which the BOM entries should be checked
* @param int $number_of_builds How often should the assembly be build?
* @return AssemblyBOMEntry[]
*/
public function getNonBuildableAssemblyBomEntries(Assembly $assembly, int $number_of_builds = 1): array
{
if ($number_of_builds < 1) {
throw new \InvalidArgumentException('The number of builds must be greater than 0!');
}
$non_buildable_entries = [];
foreach ($assembly->getBomEntries() as $bomEntry) {
$part = $bomEntry->getPart();
//Skip BOM entries without a part (as we can not determine that)
if (!$part instanceof Part) {
continue;
}
$amount_sum = $part->getAmountSum();
if ($amount_sum < $bomEntry->getQuantity() * $number_of_builds) {
$non_buildable_entries[] = $bomEntry;
}
}
return $non_buildable_entries;
}
/**
* Withdraw the parts from the stock using the given AssemblyBuildRequest and create the build parts entries, if needed.
* The AssemblyBuildRequest has to be validated before!!
* You have to flush changes to DB afterward
*/
public function doBuild(AssemblyBuildRequest $buildRequest): void
{
$message = $buildRequest->getComment();
$message .= ' (Assembly build: '.$buildRequest->getAssembly()->getName().')';
foreach ($buildRequest->getPartBomEntries() as $bom_entry) {
foreach ($buildRequest->getPartLotsForBOMEntry($bom_entry) as $part_lot) {
$amount = $buildRequest->getLotWithdrawAmount($part_lot);
if ($amount > 0) {
$this->withdraw_add_helper->withdraw($part_lot, $amount, $message);
}
}
}
if ($buildRequest->getAddBuildsToBuildsPart()) {
$this->withdraw_add_helper->add($buildRequest->getBuildsPartLot(), $buildRequest->getNumberOfBuilds(), $message);
}
}
}

View file

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace App\Services\AssemblySystem;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Parts\Part;
/**
* @see \App\Tests\Services\AssemblySystem\AssemblyBuildPartHelperTest
*/
class AssemblyBuildPartHelper
{
/**
* Returns a part that represents the builds of a assembly. This part is not saved to the database, and can be used
* as initial data for the new part form.
*/
public function getPartInitialization(Assembly $assembly): Part
{
$part = new Part();
//Associate the part with the assembly
$part->setBuiltAssembly($assembly);
//Set the name of the part to the name of the assembly
$part->setName($assembly->getName());
//Set the description of the part to the description of the assembly
$part->setDescription($assembly->getDescription());
//Add a tag to the part that indicates that it is a build part
$part->setTags('assembly-build');
//Associate the part with the assembly
$assembly->setBuildPart($part);
return $part;
}
}

View file

@ -0,0 +1,93 @@
<?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\Services\Attachments;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Attachments\Attachment;
class AssemblyPreviewGenerator
{
public function __construct(protected AttachmentManager $attachmentHelper)
{
}
/**
* Returns a list of attachments that can be used for previewing the assembly ordered by priority.
*
* @param Assembly $assembly the assembly for which the attachments should be determined
*
* @return (Attachment|null)[]
*
* @psalm-return list<Attachment|null>
*/
public function getPreviewAttachments(Assembly $assembly): array
{
$list = [];
//Master attachment has top priority
$attachment = $assembly->getMasterPictureAttachment();
if ($this->isAttachmentValidPicture($attachment)) {
$list[] = $attachment;
}
//Then comes the other images of the assembly
foreach ($assembly->getAttachments() as $attachment) {
//Dont show the master attachment twice
if ($this->isAttachmentValidPicture($attachment) && $attachment !== $assembly->getMasterPictureAttachment()) {
$list[] = $attachment;
}
}
return $list;
}
/**
* Determines what attachment should be used for previewing a assembly (especially in assembly table).
* The returned attachment is guaranteed to be existing and be a picture.
*
* @param Assembly $assembly The assembly for which the attachment should be determined
*/
public function getTablePreviewAttachment(Assembly $assembly): ?Attachment
{
$attachment = $assembly->getMasterPictureAttachment();
if ($this->isAttachmentValidPicture($attachment)) {
return $attachment;
}
return null;
}
/**
* Checks if a attachment is exising and a valid picture.
*
* @param Attachment|null $attachment the attachment that should be checked
*
* @return bool true if the attachment is valid
*/
protected function isAttachmentValidPicture(?Attachment $attachment): bool
{
return $attachment instanceof Attachment
&& $attachment->isPicture()
&& $this->attachmentHelper->isFileExisting($attachment);
}
}

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Services\Attachments;
use App\Entity\Attachments\AssemblyAttachment;
use App\Entity\Attachments\Attachment;
use App\Entity\Attachments\AttachmentContainingDBElement;
use App\Entity\Attachments\AttachmentType;
@ -84,6 +85,7 @@ class AttachmentSubmitHandler
CategoryAttachment::class => 'category',
CurrencyAttachment::class => 'currency',
ProjectAttachment::class => 'project',
AssemblyAttachment::class => 'assembly',
FootprintAttachment::class => 'footprint',
GroupAttachment::class => 'group',
ManufacturerAttachment::class => 'manufacturer',

View file

@ -45,6 +45,8 @@ use App\Entity\PriceInformations\Orderdetail;
use App\Entity\PriceInformations\Pricedetail;
use App\Entity\ProjectSystem\Project;
use App\Entity\ProjectSystem\ProjectBOMEntry;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Entity\UserSystem\Group;
use App\Entity\UserSystem\User;
use App\Exceptions\EntityNotSupportedException;
@ -66,6 +68,8 @@ class ElementTypeNameGenerator
AttachmentType::class => $this->translator->trans('attachment_type.label'),
Project::class => $this->translator->trans('project.label'),
ProjectBOMEntry::class => $this->translator->trans('project_bom_entry.label'),
Assembly::class => $this->translator->trans('assembly.label'),
AssemblyBOMEntry::class => $this->translator->trans('assembly_bom_entry.label'),
Footprint::class => $this->translator->trans('footprint.label'),
Manufacturer::class => $this->translator->trans('manufacturer.label'),
MeasurementUnit::class => $this->translator->trans('measurement_unit.label'),
@ -182,6 +186,8 @@ class ElementTypeNameGenerator
$on = $entity->getOrderdetail()->getPart();
} elseif ($entity instanceof ProjectBOMEntry && $entity->getProject() instanceof Project) {
$on = $entity->getProject();
} elseif ($entity instanceof AssemblyBOMEntry && $entity->getAssembly() instanceof Assembly) {
$on = $entity->getAssembly();
}
if (isset($on) && $on instanceof NamedElementInterface) {

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Services;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Attachments\Attachment;
use App\Entity\Attachments\AttachmentType;
use App\Entity\Attachments\PartAttachment;
@ -98,6 +99,7 @@ class EntityURLGenerator
AttachmentType::class => 'attachment_type_edit',
Category::class => 'category_edit',
Project::class => 'project_edit',
Assembly::class => 'assembly_edit',
Supplier::class => 'supplier_edit',
Manufacturer::class => 'manufacturer_edit',
StorageLocation::class => 'store_location_edit',
@ -204,6 +206,7 @@ class EntityURLGenerator
AttachmentType::class => 'attachment_type_edit',
Category::class => 'category_edit',
Project::class => 'project_info',
Assembly::class => 'assembly_info',
Supplier::class => 'supplier_edit',
Manufacturer::class => 'manufacturer_edit',
StorageLocation::class => 'store_location_edit',
@ -234,6 +237,7 @@ class EntityURLGenerator
AttachmentType::class => 'attachment_type_edit',
Category::class => 'category_edit',
Project::class => 'project_edit',
Assembly::class => 'assembly_edit',
Supplier::class => 'supplier_edit',
Manufacturer::class => 'manufacturer_edit',
StorageLocation::class => 'store_location_edit',
@ -265,6 +269,7 @@ class EntityURLGenerator
AttachmentType::class => 'attachment_type_new',
Category::class => 'category_new',
Project::class => 'project_new',
Assembly::class => 'assembly_new',
Supplier::class => 'supplier_new',
Manufacturer::class => 'manufacturer_new',
StorageLocation::class => 'store_location_new',
@ -296,6 +301,7 @@ class EntityURLGenerator
AttachmentType::class => 'attachment_type_clone',
Category::class => 'category_clone',
Project::class => 'device_clone',
Assembly::class => 'assembly_clone',
Supplier::class => 'supplier_clone',
Manufacturer::class => 'manufacturer_clone',
StorageLocation::class => 'store_location_clone',
@ -323,6 +329,7 @@ class EntityURLGenerator
{
$map = [
Project::class => 'project_info',
Assembly::class => 'assembly_info',
Category::class => 'part_list_category',
Footprint::class => 'part_list_footprint',
@ -341,6 +348,7 @@ class EntityURLGenerator
AttachmentType::class => 'attachment_type_delete',
Category::class => 'category_delete',
Project::class => 'project_delete',
Assembly::class => 'assembly_delete',
Supplier::class => 'supplier_delete',
Manufacturer::class => 'manufacturer_delete',
StorageLocation::class => 'store_location_delete',

View file

@ -22,15 +22,25 @@ declare(strict_types=1);
*/
namespace App\Services\ImportExportSystem;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Entity\Parts\Category;
use App\Entity\Parts\Manufacturer;
use App\Entity\Parts\Part;
use App\Entity\ProjectSystem\Project;
use App\Entity\ProjectSystem\ProjectBOMEntry;
use App\Repository\DBElementRepository;
use App\Repository\PartRepository;
use App\Repository\Parts\CategoryRepository;
use App\Repository\Parts\ManufacturerRepository;
use Doctrine\ORM\EntityManagerInterface;
use InvalidArgumentException;
use League\Csv\Reader;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\OptionsResolver\OptionsResolver;
use RuntimeException;
use UnexpectedValueException;
/**
* @see \App\Tests\Services\ImportExportSystem\BOMImporterTest
@ -47,17 +57,29 @@ class BOMImporter
5 => 'Supplier and ref',
];
private readonly PartRepository $partRepository;
private readonly ManufacturerRepository $manufacturerRepository;
private readonly CategoryRepository $categoryRepository;
private readonly DBElementRepository $assemblyBOMEntryRepository;
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
private readonly BOMValidationService $validationService
) {
$this->partRepository = $entityManager->getRepository(Part::class);
$this->manufacturerRepository = $entityManager->getRepository(Manufacturer::class);
$this->categoryRepository = $entityManager->getRepository(Category::class);
$this->assemblyBOMEntryRepository = $entityManager->getRepository(AssemblyBOMEntry::class);
}
protected function configureOptions(OptionsResolver $resolver): OptionsResolver
{
$resolver->setRequired('type');
$resolver->setAllowedValues('type', ['kicad_pcbnew', 'kicad_schematic']);
$resolver->setAllowedValues('type', ['kicad_pcbnew', 'kicad_schematic', 'json']);
// For flexible schematic import with field mapping
$resolver->setDefined(['field_mapping', 'field_priorities', 'delimiter']);
@ -88,12 +110,29 @@ class BOMImporter
}
/**
* Converts the given file into an array of BOM entries using the given options.
* @return ProjectBOMEntry[]
* Converts the given file into an array of BOM entries using the given options and save them into the given assembly.
* The changes are not saved into the database yet.
* @return AssemblyBOMEntry[]
*/
public function fileToBOMEntries(File $file, array $options): array
public function importFileIntoAssembly(File $file, Assembly $assembly, array $options): array
{
return $this->stringToBOMEntries($file->getContent(), $options);
$bomEntries = $this->fileToBOMEntries($file, $options, AssemblyBOMEntry::class);
//Assign the bom_entries to the assembly
foreach ($bomEntries as $bom_entry) {
$assembly->addBomEntry($bom_entry);
}
return $bomEntries;
}
/**
* Converts the given file into an array of BOM entries using the given options.
* @return ProjectBOMEntry[]|AssemblyBOMEntry[]
*/
public function fileToBOMEntries(File $file, array $options, string $objectType = ProjectBOMEntry::class): array
{
return $this->stringToBOMEntries($file->getContent(), $options, $objectType);
}
/**
@ -117,22 +156,22 @@ class BOMImporter
* Import string data into an array of BOM entries, which are not yet assigned to a project.
* @param string $data The data to import
* @param array $options An array of options
* @return ProjectBOMEntry[] An array of imported entries
* @return ProjectBOMEntry[]|AssemblyBOMEntry[] An array of imported entries
*/
public function stringToBOMEntries(string $data, array $options): array
public function stringToBOMEntries(string $data, array $options, string $objectType = ProjectBOMEntry::class): array
{
$resolver = new OptionsResolver();
$resolver = $this->configureOptions($resolver);
$options = $resolver->resolve($options);
return match ($options['type']) {
'kicad_pcbnew' => $this->parseKiCADPCB($data),
'kicad_schematic' => $this->parseKiCADSchematic($data, $options),
'kicad_pcbnew' => $this->parseKiCADPCB($data, $objectType),
'json' => $this->parseJson($data, $options, $objectType),
default => throw new InvalidArgumentException('Invalid import type!'),
};
}
private function parseKiCADPCB(string $data): array
private function parseKiCADPCB(string $data, string $objectType = ProjectBOMEntry::class): array
{
$csv = Reader::createFromString($data);
$csv->setDelimiter(';');
@ -158,8 +197,13 @@ class BOMImporter
throw new \UnexpectedValueException('Quantity missing at line ' . ($offset + 1) . '!');
}
$bom_entry = new ProjectBOMEntry();
$bom_entry->setName($entry['Designation'] . ' (' . $entry['Package'] . ')');
$bom_entry = $objectType === ProjectBOMEntry::class ? new ProjectBOMEntry() : new AssemblyBOMEntry();
if ($objectType === ProjectBOMEntry::class) {
$bom_entry->setName($entry['Designation'] . ' (' . $entry['Package'] . ')');
} else {
$bom_entry->setName($entry['Designation']);
}
$bom_entry->setMountnames($entry['Designator'] ?? '');
$bom_entry->setComment($entry['Supplier and ref'] ?? '');
$bom_entry->setQuantity((float) ($entry['Quantity'] ?? 1));
@ -227,6 +271,174 @@ class BOMImporter
return $this->validationService->validateBOMEntries($mapped_entries, $options);
}
private function parseJson(string $data, array $options = [], string $objectType = ProjectBOMEntry::class): array
{
$result = [];
$data = json_decode($data, true);
foreach ($data as $entry) {
// Check quantity
if (!isset($entry['quantity'])) {
throw new UnexpectedValueException('quantity missing');
}
if (!is_float($entry['quantity']) || $entry['quantity'] <= 0) {
throw new UnexpectedValueException('quantity expected as float greater than 0.0');
}
// Check name
if (isset($entry['name']) && !is_string($entry['name'])) {
throw new UnexpectedValueException('name of part list entry expected as string');
}
// Check if part is assigned with relevant information
if (isset($entry['part'])) {
if (!is_array($entry['part'])) {
throw new UnexpectedValueException('The property "part" should be an array');
}
$partIdValid = isset($entry['part']['id']) && is_int($entry['part']['id']) && $entry['part']['id'] > 0;
$partNameValid = isset($entry['part']['name']) && is_string($entry['part']['name']) && trim($entry['part']['name']) !== '';
$partMpnrValid = isset($entry['part']['mpnr']) && is_string($entry['part']['mpnr']) && trim($entry['part']['mpnr']) !== '';
$partIpnValid = isset($entry['part']['ipn']) && is_string($entry['part']['ipn']) && trim($entry['part']['ipn']) !== '';
if (!$partIdValid && !$partNameValid && !$partMpnrValid && !$partIpnValid) {
throw new UnexpectedValueException(
'The property "part" must have either assigned: "id" as integer greater than 0, "name", "mpnr", or "ipn" as non-empty string'
);
}
$part = $partIdValid ? $this->partRepository->findOneBy(['id' => $entry['part']['id']]) : null;
$part = $part ?? ($partMpnrValid ? $this->partRepository->findOneBy(['manufacturer_product_number' => trim($entry['part']['mpnr'])]) : null);
$part = $part ?? ($partIpnValid ? $this->partRepository->findOneBy(['ipn' => trim($entry['part']['ipn'])]) : null);
$part = $part ?? ($partNameValid ? $this->partRepository->findOneBy(['name' => trim($entry['part']['name'])]) : null);
if ($part === null) {
$part = new Part();
$part->setName($entry['part']['name']);
}
if ($partNameValid && $part->getName() !== trim($entry['part']['name'])) {
throw new RuntimeException(sprintf('Part name does not match exact the given name. Given for import: %s, found part: %s', $entry['part']['name'], $part->getName()));
}
if ($partIpnValid && $part->getManufacturerProductNumber() !== trim($entry['part']['mpnr'])) {
throw new RuntimeException(sprintf('Part mpnr does not match exact the given mpnr. Given for import: %s, found part: %s', $entry['part']['mpnr'], $part->getManufacturerProductNumber()));
}
if ($partIpnValid && $part->getIpn() !== trim($entry['part']['ipn'])) {
throw new RuntimeException(sprintf('Part ipn does not match exact the given ipn. Given for import: %s, found part: %s', $entry['part']['ipn'], $part->getIpn()));
}
// Part: Description check
if (isset($entry['part']['description']) && !is_null($entry['part']['description'])) {
if (!is_string($entry['part']['description']) || trim($entry['part']['description']) === '') {
throw new UnexpectedValueException('The property path "part.description" must be a non-empty string if not null');
}
}
$partDescription = $entry['part']['description'] ?? '';
// Part: Manufacturer check
$manufacturerIdValid = false;
$manufacturerNameValid = false;
if (array_key_exists('manufacturer', $entry['part'])) {
if (!is_array($entry['part']['manufacturer'])) {
throw new UnexpectedValueException('The property path "part.manufacturer" must be an array');
}
$manufacturerIdValid = isset($entry['part']['manufacturer']['id']) && is_int($entry['part']['manufacturer']['id']) && $entry['part']['manufacturer']['id'] > 0;
$manufacturerNameValid = isset($entry['part']['manufacturer']['name']) && is_string($entry['part']['manufacturer']['name']) && trim($entry['part']['manufacturer']['name']) !== '';
// Stellen sicher, dass mindestens eine Bedingung für manufacturer erfüllt sein muss
if (!$manufacturerIdValid && !$manufacturerNameValid) {
throw new UnexpectedValueException(
'The property "manufacturer" must have either assigned: "id" as integer greater than 0, or "name" as non-empty string'
);
}
}
$manufacturer = $manufacturerIdValid ? $this->manufacturerRepository->findOneBy(['id' => $entry['part']['manufacturer']['id']]) : null;
$manufacturer = $manufacturer ?? ($manufacturerNameValid ? $this->manufacturerRepository->findOneBy(['name' => trim($entry['part']['manufacturer']['name'])]) : null);
if ($manufacturer === null) {
throw new RuntimeException(
'Manufacturer not found'
);
}
if ($manufacturerNameValid && $manufacturer->getName() !== trim($entry['part']['manufacturer']['name'])) {
throw new RuntimeException(sprintf('Manufacturer name does not match exact the given name. Given for import: %s, found manufacturer: %s', $entry['manufacturer']['name'], $manufacturer->getName()));
}
// Part: Category check
$categoryIdValid = false;
$categoryNameValid = false;
if (array_key_exists('category', $entry['part'])) {
if (!is_array($entry['part']['category'])) {
throw new UnexpectedValueException('part.category must be an array');
}
$categoryIdValid = isset($entry['part']['category']['id']) && is_int($entry['part']['category']['id']) && $entry['part']['category']['id'] > 0;
$categoryNameValid = isset($entry['part']['category']['name']) && is_string($entry['part']['category']['name']) && trim($entry['part']['category']['name']) !== '';
if (!$categoryIdValid && !$categoryNameValid) {
throw new UnexpectedValueException(
'The property "category" must have either assigned: "id" as integer greater than 0, or "name" as non-empty string'
);
}
}
$category = $categoryIdValid ? $this->categoryRepository->findOneBy(['id' => $entry['part']['category']['id']]) : null;
$category = $category ?? ($categoryNameValid ? $this->categoryRepository->findOneBy(['name' => trim($entry['part']['category']['name'])]) : null);
if ($category === null) {
throw new RuntimeException(
'Category not found'
);
}
if ($categoryNameValid && $category->getName() !== trim($entry['part']['category']['name'])) {
throw new RuntimeException(sprintf('Category name does not match exact the given name. Given for import: %s, found category: %s', $entry['category']['name'], $category->getName()));
}
$part->setDescription($partDescription);
$part->setManufacturer($manufacturer);
$part->setCategory($category);
if ($partMpnrValid) {
$part->setManufacturerProductNumber($entry['part']['mpnr'] ?? '');
}
if ($partIpnValid) {
$part->setIpn($entry['part']['ipn'] ?? '');
}
if ($objectType === AssemblyBOMEntry::class) {
$bomEntry = $this->assemblyBOMEntryRepository->findOneBy(['part' => $part]);
if ($bomEntry === null) {
$name = isset($entry['name']) && $entry['name'] !== null ? trim($entry['name']) : '';
$bomEntry = $this->assemblyBOMEntryRepository->findOneBy(['name' => $name]);
if ($bomEntry === null) {
$bomEntry = new AssemblyBOMEntry();
}
}
} else {
$bomEntry = new ProjectBOMEntry();
}
$bomEntry->setQuantity($entry['quantity']);
$bomEntry->setName($entry['name'] ?? '');
$bomEntry->setPart($part);
}
$result[] = $bomEntry;
}
return $result;
}
/**
* This function uses the order of the fields in the CSV files to make them locale independent.
* @param array $entry
@ -243,7 +455,7 @@ class BOMImporter
}
//@phpstan-ignore-next-line We want to keep this check just to be safe when something changes
$new_index = self::MAP_KICAD_PCB_FIELDS[$index] ?? throw new \UnexpectedValueException('Invalid field index!');
$new_index = self::MAP_KICAD_PCB_FIELDS[$index] ?? throw new UnexpectedValueException('Invalid field index!');
$out[$new_index] = $field;
}

View file

@ -22,10 +22,13 @@ declare(strict_types=1);
*/
namespace App\Services\ProjectSystem;
use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Entity\Parts\Part;
use App\Entity\ProjectSystem\Project;
use App\Entity\ProjectSystem\ProjectBOMEntry;
use App\Helpers\Assemblies\AssemblyBuildRequest;
use App\Helpers\Projects\ProjectBuildRequest;
use App\Services\AssemblySystem\AssemblyBuildHelper;
use App\Services\Parts\PartLotWithdrawAddHelper;
/**
@ -33,8 +36,10 @@ use App\Services\Parts\PartLotWithdrawAddHelper;
*/
class ProjectBuildHelper
{
public function __construct(private readonly PartLotWithdrawAddHelper $withdraw_add_helper)
{
public function __construct(
private readonly PartLotWithdrawAddHelper $withdrawAddHelper,
private readonly AssemblyBuildHelper $assemblyBuildHelper
) {
}
/**
@ -66,12 +71,16 @@ class ProjectBuildHelper
$maximum_buildable_count = PHP_INT_MAX;
foreach ($project->getBomEntries() as $bom_entry) {
//Skip BOM entries without a part (as we can not determine that)
if (!$bom_entry->isPartBomEntry()) {
if (!$bom_entry->isPartBomEntry() && $bom_entry->getAssembly() === null) {
continue;
}
//The maximum buildable count for the whole project is the minimum of all BOM entries
$maximum_buildable_count = min($maximum_buildable_count, $this->getMaximumBuildableCountForBOMEntry($bom_entry));
if ($bom_entry->getPart() !== null) {
$maximum_buildable_count = min($maximum_buildable_count, $this->getMaximumBuildableCountForBOMEntry($bom_entry));
} elseif ($bom_entry->getAssembly() !== null) {
$maximum_buildable_count = min($maximum_buildable_count, $this->assemblyBuildHelper->getMaximumBuildableCount($bom_entry->getAssembly()));
}
}
return $maximum_buildable_count;
@ -97,10 +106,10 @@ class ProjectBuildHelper
}
/**
* Returns the project BOM entries for which parts are missing in the stock for the given number of builds
* Returns the project or assembly BOM entries for which parts are missing in the stock for the given number of builds
* @param Project $project The project for which the BOM entries should be checked
* @param int $number_of_builds How often should the project be build?
* @return ProjectBOMEntry[]
* @return ProjectBOMEntry[]|AssemblyBOMEntry[]
*/
public function getNonBuildableProjectBomEntries(Project $project, int $number_of_builds = 1): array
{
@ -108,24 +117,29 @@ class ProjectBuildHelper
throw new \InvalidArgumentException('The number of builds must be greater than 0!');
}
$non_buildable_entries = [];
$nonBuildableEntries = [];
foreach ($project->getBomEntries() as $bomEntry) {
$part = $bomEntry->getPart();
//Skip BOM entries without a part (as we can not determine that)
if (!$part instanceof Part) {
if (!$part instanceof Part && $bomEntry->getAssembly() === null) {
continue;
}
$amount_sum = $part->getAmountSum();
if ($bomEntry->getPart() !== null) {
$amount_sum = $part->getAmountSum();
if ($amount_sum < $bomEntry->getQuantity() * $number_of_builds) {
$non_buildable_entries[] = $bomEntry;
if ($amount_sum < $bomEntry->getQuantity() * $number_of_builds) {
$nonBuildableEntries[] = $bomEntry;
}
} elseif ($bomEntry->getAssembly() !== null) {
$nonBuildableAssemblyEntries = $this->assemblyBuildHelper->getNonBuildableAssemblyBomEntries($bomEntry->getAssembly(), $number_of_builds);
$nonBuildableEntries = array_merge($nonBuildableEntries, $nonBuildableAssemblyEntries);
}
}
return $non_buildable_entries;
return $nonBuildableEntries;
}
/**
@ -133,22 +147,37 @@ class ProjectBuildHelper
* The ProjectBuildRequest has to be validated before!!
* You have to flush changes to DB afterward
*/
public function doBuild(ProjectBuildRequest $buildRequest): void
public function doBuild(ProjectBuildRequest $projectBuildRequest): void
{
$message = $buildRequest->getComment();
$message .= ' (Project build: '.$buildRequest->getProject()->getName().')';
$message = $projectBuildRequest->getComment();
$message .= ' (Project build: '.$projectBuildRequest->getProject()->getName().')';
foreach ($buildRequest->getPartBomEntries() as $bom_entry) {
foreach ($buildRequest->getPartLotsForBOMEntry($bom_entry) as $part_lot) {
$amount = $buildRequest->getLotWithdrawAmount($part_lot);
foreach ($projectBuildRequest->getPartBomEntries() as $bomEntry) {
foreach ($projectBuildRequest->getPartLotsForBOMEntry($bomEntry) as $partLot) {
$amount = $projectBuildRequest->getLotWithdrawAmount($partLot);
if ($amount > 0) {
$this->withdraw_add_helper->withdraw($part_lot, $amount, $message);
$this->withdrawAddHelper->withdraw($partLot, $amount, $message);
}
}
}
if ($buildRequest->getAddBuildsToBuildsPart()) {
$this->withdraw_add_helper->add($buildRequest->getBuildsPartLot(), $buildRequest->getNumberOfBuilds(), $message);
foreach ($projectBuildRequest->getAssemblyBomEntries() as $bomEntry) {
$assemblyBuildRequest = new AssemblyBuildRequest($bomEntry->getAssembly(), $projectBuildRequest->getNumberOfBuilds());
//Add fields for assembly bom entries
foreach ($assemblyBuildRequest->getPartBomEntries() as $partBomEntry) {
foreach ($assemblyBuildRequest->getPartLotsForBOMEntry($partBomEntry) as $partLot) {
//Read amount from build configuration of the projectBuildRequest
$amount = $projectBuildRequest->getLotWithdrawAmount($partLot);
if ($amount > 0) {
$this->withdrawAddHelper->withdraw($partLot, $amount, $message);
}
}
}
}
if ($projectBuildRequest->getAddBuildsToBuildsPart()) {
$this->withdrawAddHelper->add($projectBuildRequest->getBuildsPartLot(), $projectBuildRequest->getNumberOfBuilds(), $message);
}
}
}

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Services\Trees;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Attachments\AttachmentType;
use App\Entity\LabelSystem\LabelProfile;
use App\Entity\Parts\Category;
@ -175,6 +176,12 @@ class ToolsTreeBuilder
$this->urlGenerator->generate('project_new')
))->setIcon('fa-fw fa-treeview fa-solid fa-archive');
}
if ($this->security->isGranted('read', new Assembly())) {
$nodes[] = (new TreeViewNode(
$this->translator->trans('tree.tools.edit.assemblies'),
$this->urlGenerator->generate('assembly_new')
))->setIcon('fa-fw fa-treeview fa-solid fa-list');
}
if ($this->security->isGranted('read', new Supplier())) {
$nodes[] = (new TreeViewNode(
$this->translator->trans('tree.tools.edit.suppliers'),

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
namespace App\Services\Trees;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Base\AbstractDBElement;
use App\Entity\Base\AbstractNamedDBElement;
use App\Entity\Base\AbstractStructuralDBElement;
@ -154,6 +155,10 @@ class TreeViewGenerator
$href_type = 'list_parts';
}
if ($mode === 'assemblies') {
$href_type = 'list_parts';
}
$generic = $this->getGenericTree($class, $parent);
$treeIterator = new TreeViewNodeIterator($generic);
$recursiveIterator = new RecursiveIteratorIterator($treeIterator, RecursiveIteratorIterator::SELF_FIRST);
@ -219,6 +224,7 @@ class TreeViewGenerator
Manufacturer::class => $this->translator->trans('manufacturer.labelp'),
Supplier::class => $this->translator->trans('supplier.labelp'),
Project::class => $this->translator->trans('project.labelp'),
Assembly::class => $this->translator->trans('assembly.labelp'),
default => $this->translator->trans('tree.root_node.text'),
};
}
@ -233,6 +239,7 @@ class TreeViewGenerator
Manufacturer::class => $icon.'fa-industry',
Supplier::class => $icon.'fa-truck',
Project::class => $icon.'fa-archive',
Assembly::class => $icon.'fa-list',
default => null,
};
}

View file

@ -22,6 +22,7 @@ declare(strict_types=1);
*/
namespace App\Twig;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Attachments\Attachment;
use App\Entity\Base\AbstractDBElement;
use App\Entity\ProjectSystem\Project;
@ -108,6 +109,7 @@ final class EntityExtension extends AbstractExtension
Manufacturer::class => 'manufacturer',
Category::class => 'category',
Project::class => 'device',
Assembly::class => 'assembly',
Attachment::class => 'attachment',
Supplier::class => 'supplier',
User::class => 'user',

View file

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2023 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\Validator\Constraints\AssemblySystem;
use Symfony\Component\Validator\Constraint;
/**
* This constraint checks that the given ValidAssemblyBuildRequest is valid.
*/
#[\Attribute(\Attribute::TARGET_CLASS)]
class ValidAssemblyBuildRequest extends Constraint
{
public function getTargets(): string
{
return self::CLASS_CONSTRAINT;
}
}

View file

@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2023 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\Validator\Constraints\AssemblySystem;
use App\Entity\Parts\PartLot;
use App\Helpers\Assemblies\AssemblyBuildRequest;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface;
class ValidAssemblyBuildRequestValidator extends ConstraintValidator
{
private function buildViolationForLot(PartLot $partLot, string $message): ConstraintViolationBuilderInterface
{
return $this->context->buildViolation($message)
->atPath('lot_' . $partLot->getID())
->setParameter('{{ lot }}', $partLot->getName());
}
public function validate($value, Constraint $constraint): void
{
if (!$constraint instanceof ValidAssemblyBuildRequest) {
throw new UnexpectedTypeException($constraint, ValidAssemblyBuildRequest::class);
}
if (null === $value || '' === $value) {
return;
}
if (!$value instanceof AssemblyBuildRequest) {
throw new UnexpectedTypeException($value, AssemblyBuildRequest::class);
}
foreach ($value->getPartBomEntries() as $bom_entry) {
$withdraw_sum = $value->getWithdrawAmountSum($bom_entry);
$needed_amount = $value->getNeededAmountForBOMEntry($bom_entry);
foreach ($value->getPartLotsForBOMEntry($bom_entry) as $lot) {
$withdraw_amount = $value->getLotWithdrawAmount($lot);
if ($withdraw_amount < 0) {
$this->buildViolationForLot($lot, 'validator.assembly_build.lot_must_not_smaller_0')
->addViolation();
}
if ($withdraw_amount > $lot->getAmount()) {
$this->buildViolationForLot($lot, 'validator.assembly_build.lot_must_not_bigger_than_stock')
->addViolation();
}
if ($withdraw_sum > $needed_amount && $value->isDontCheckQuantity() === false) {
$this->buildViolationForLot($lot, 'validator.assembly_build.lot_bigger_than_needed')
->addViolation();
}
if ($withdraw_sum < $needed_amount && $value->isDontCheckQuantity() === false) {
$this->buildViolationForLot($lot, 'validator.assembly_build.lot_smaller_than_needed')
->addViolation();
}
}
}
}
}

View file

@ -0,0 +1,62 @@
{% extends "admin/base_admin.html.twig" %}
{# @var entity App\Entity\AssemblySystem\Assembly #}
{% block card_title %}
<i class="fas fa-archive fa-fw"></i> {% trans %}assembly.caption{% endtrans %}
{% endblock %}
{% block edit_title %}
{% trans %}assembly.edit{% endtrans %}: {{ entity.name }}
{% endblock %}
{% block new_title %}
{% trans %}assembly.new{% endtrans %}
{% endblock %}
{% block additional_pills %}
<li class="nav-item"><a data-bs-toggle="tab" class="nav-link link-anchor" href="#bom">BOM</a></li>
{% endblock %}
{% block quick_links %}
<div class="btn-toolbar" style="display: inline-block;">
<div class="btn-group">
<a class="btn btn-outline-secondary" href="{{ entity_url(entity) }}"><i class="fas fa-eye fa-fw"></i></a>
</div>
</div>
{% endblock %}
{% block additional_controls %}
{{ form_row(form.description) }}
{{ form_row(form.status) }}
{% if entity.id %}
<div class="mb-2 row">
<label class="col-form-label col-sm-3">{% trans %}assembly.edit.associated_build_part{% endtrans %}</label>
<div class="col-sm-9">
{% if entity.buildPart %}
<span class="form-control-static"><a href="{{ entity_url(entity.buildPart) }}">{{ entity.buildPart.name }}</a></span>
{% else %}
<a href="{{ path('part_new_build_part_assembly', {"assembly_id": entity.id , "_redirect": uri_without_host(app.request)}) }}"
class="btn btn-outline-success">{% trans %}assembly.edit.associated_build_part.add{% endtrans %}</a>
{% endif %}
<p class="text-muted">{% trans %}assembly.edit.associated_build.hint{% endtrans %}</p>
</div>
</div>
{% endif %}
{% endblock %}
{% block additional_panes %}
<div class="tab-pane" id="bom">
{% form_theme form.bom_entries with ['form/collection_types_layout_assembly.html.twig'] %}
{{ form_errors(form.bom_entries) }}
{{ form_widget(form.bom_entries) }}
{% if entity.id %}
<a href="{{ path('assembly_import_bom', {'id': entity.id}) }}" class="btn btn-secondary mb-2"
{% if not is_granted('edit', entity) %}disabled="disabled"{% endif %}>
<i class="fa-solid fa-file-import fa-fw"></i>
{% trans %}assembly.edit.bom.import_bom{% endtrans %}
</a>
{% endif %}
</div>
{% endblock %}

View file

@ -36,7 +36,7 @@
{% if entity.buildPart %}
<span class="form-control-static"><a href="{{ entity_url(entity.buildPart) }}">{{ entity.buildPart.name }}</a></span>
{% else %}
<a href="{{ path('part_new_build_part', {"project_id": entity.id , "_redirect": uri_without_host(app.request)}) }}"
<a href="{{ path('part_new_build_part_project', {"project_id": entity.id , "_redirect": uri_without_host(app.request)}) }}"
class="btn btn-outline-success">{% trans %}project.edit.associated_build_part.add{% endtrans %}</a>
{% endif %}
<p class="text-muted">{% trans %}project.edit.associated_build.hint{% endtrans %}</p>

View file

@ -0,0 +1,22 @@
{% extends "main_card.html.twig" %}
{% block title %}{% trans %}assembly.add_parts_to_assembly{% endtrans %}{% endblock %}
{% block card_title %}
<i class="fa-solid fa-magnifying-glass-plus fa-fw"></i>
{% trans %}assembly.add_parts_to_assembly{% endtrans %}{% if assembly %}: <i>{{ assembly.name }}</i>{% endif %}
{% endblock %}
{% block card_content %}
{{ form_start(form) }}
{{ form_row(form.assembly) }}
{% form_theme form.bom_entries with ['form/collection_types_layout_assembly.html.twig'] %}
{{ form_widget(form.bom_entries) }}
{{ form_row(form.submit) }}
{{ form_end(form) }}
{% endblock %}

View file

@ -0,0 +1,88 @@
{% import "helper.twig" as helper %}
{{ form_start(form) }}
<table class="table table-sm table-responsive table-hover">
<thead>
<tr>
<th>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" data-multicheck-name="lots_check" {{ stimulus_controller('pages/checkbox_multicheck') }}>
</div>
</th>
<th>{% trans %}part.table.name{% endtrans %}</th>
<th>{% trans %}assembly.bom.mountnames{% endtrans %}</th>
<th class="text-end">{% trans %}assembly.build.required_qty{% endtrans %}</th>
</tr>
</thead>
<tbody>
{% for bom_entry in build_request.bomEntries %}
{# 1st row basic infos about the BOM entry #}
<tr class="{% if bom_entry.part is null or buildHelper.bOMEntryBuildable(bom_entry, number_of_builds) %}table-primary{% else %}table-danger{% endif %}">
<td style="width: 20px;">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" data-multicheck-name="lots_check" required>
{# <label class="form-check-label" for="checkbox_{{ loop.index }}"> #}
</div>
</td>
<td >
{% if bom_entry.part %}
<b><a target="_blank" href="{{ entity_url(bom_entry.part) }}">{{ bom_entry.part.name }}</a></b> {% if bom_entry.name %}({{ bom_entry.name }}){% endif %}
{% else %}
<b>{{ bom_entry.name }}</b>
{% endif %}
</td>
<td>
{% for tag in bom_entry.mountnames|split(',') %}
<span class="badge bg-secondary badge-secondary" >{{ tag | trim }}</span>
{% endfor %}
</td>
<td class="text-end">
<b>{{ build_request.neededAmountForBOMEntry(bom_entry) | format_amount(bom_entry.part.partUnit ?? null) }}</b> {% trans %}assembly.builds.needed{% endtrans %}
(= {{ number_of_builds }} x {{ bom_entry.quantity | format_amount(bom_entry.part.partUnit ?? null) }})
</td>
</tr>
<tr>
<td colspan="4">
{% set lots = build_request.partLotsForBOMEntry(bom_entry) %}
{% if lots is not null %}
{% for lot in lots %}
{# @var lot \App\Entity\Parts\PartLot #}
<div class="mb-2 row">
<label class="col-form-label col-sm-4" for="category_admin_form_parent">
{% if lot.storageLocation %}
<small>{{ helper.structural_entity_link(lot.storageLocation) }}</small>
{% endif %}
{% if lot.name is not empty %}
(<small>{{ lot.name }}</small>)
{% endif %}
</label>
<div class="col-sm-6">
{{ form_errors(form["lot_"~lot.id]) }}
{{ form_widget(form["lot_"~lot.id]) }}
</div>
<div class="col-sm-2 mt-1 text-end">
/ <b>{{ lot.amount | format_amount(lot.part.partUnit) }}</b> {% trans %}assembly.builds.stocked{% endtrans %}
</div>
</div>
{% endfor %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
{{ form_row(form.comment) }}
<div {{ stimulus_controller('pages/dont_check_quantity_checkbox') }}>
{{ form_row(form.dontCheckQuantity) }}
</div>
{{ form_row(form.addBuildsToBuildsPart) }}
{% if form.buildsPartLot is defined %}
{{ form_row(form.buildsPartLot) }}
{% endif %}
{{ form_row(form.submit) }}
{{ form_end(form) }}

View file

@ -0,0 +1,40 @@
{% extends "main_card.html.twig" %}
{% block title %}{% trans %}assembly.info.builds.label{% endtrans %}: {{ number_of_builds }}x {{ assembly.name }}{% endblock %}
{% block card_title %}
<i class="fa-solid fa-bolt fa-fw"></i>
{% trans %}assembly.info.builds.label{% endtrans %}: <b>{{ number_of_builds }}x</b> <i>{{ assembly.name }}</i>
{% endblock %}
{% block card_content %}
{% set can_build = buildHelper.assemblyBuildable(assembly, number_of_builds) %}
{% import "components/assemblies.macro.html.twig" as assembly_macros %}
{% if assembly.status is not empty and assembly.status != "in_production" %}
<div class="alert alert-warning" role="alert">
<i class="fa-solid fa-triangle-exclamation fa-fw"></i> {% trans with {"%assembly_status%": ('assembly.status.'~assembly.status)|trans } %}assembly.builds.check_assembly_status{% endtrans %}
</div>
{% endif %}
<div class="alert {% if can_build %}alert-success{% else %}alert-danger{% endif %}" role="alert">
{% if not can_build %}
<h5><i class="fa-solid fa-circle-exclamation fa-fw"></i> {% trans %}assembly.builds.build_not_possible{% endtrans %}</h5>
<b>{% trans with {"%number_of_builds%": number_of_builds} %}assembly.builds.following_bom_entries_miss_instock_n{% endtrans %}</b>
<ul>
{% for bom_entry in buildHelper.nonBuildableAssemblyBomEntries(assembly, number_of_builds) %}
<li>{{ assembly_macros.assembly_bom_entry_with_missing_instock(bom_entry, number_of_builds) }}</li>
{% endfor %}
</ul>
{% else %}
<h5><i class="fa-solid fa-circle-check fa-fw"></i> {% trans %}assembly.builds.build_possible{% endtrans %}</h5>
<span>{% trans with {"%max_builds%": number_of_builds} %}assembly.builds.number_of_builds_possible{% endtrans %}</span>
{% endif %}
</div>
<p class="text-muted">{% trans %}assembly.build.help{% endtrans %}</p>
{% include 'assemblies/build/_form.html.twig' %}
{% endblock %}

View file

@ -0,0 +1,60 @@
{% extends "main_card.html.twig" %}
{% block title %}{% trans %}assembly.import_bom{% endtrans %}{% endblock %}
{% block before_card %}
{% if errors %}
<div class="alert alert-danger">
<h4><i class="fa-solid fa-exclamation-triangle fa-fw"></i> {% trans %}parts.import.errors.title{% endtrans %}</h4>
<ul>
{% for violation in errors %}
<li>
<b>{{ violation.propertyPath }}: </b>
{{ violation.message|trans(violation.parameters, 'validators') }}
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endblock %}
{% block card_title %}
<i class="fa-solid fa-file-import fa-fw"></i>
{% trans %}assembly.import_bom{% endtrans %}{% if assembly %}: <i>{{ assembly.name }}</i>{% endif %}
{% endblock %}
{% block card_content %}
{{ form(form) }}
{% endblock %}
{% block additional_content %}
<div class="container-fluid row d-flex align-items-stretch pe-0 me-0">
<div class="col-md-12 col-lg-6 ps-0 mt-3 d-flex">
<div class="card border-secondary flex-grow-1 overflow-auto">
<div class="card-header bg-secondary text-white">
{% trans %}assembly.import_bom.template.header.json{% endtrans %}
</div>
<div class="card-body">
<pre>{{ jsonTemplate|json_encode(constant('JSON_PRETTY_PRINT') b-or constant('JSON_UNESCAPED_UNICODE')) }}</pre>
{{ 'assembly.bom_import.template.json.table'|trans|raw }}
</div>
</div>
</div>
<div class="col-md-12 col-lg-6 ps-0 mt-3 d-flex overflow-auto">
<div class="card border-secondary flex-grow-1 overflow-auto">
<div class="card-header bg-secondary text-white">
{% trans %}assembly.import_bom.template.header.kicad_pcbnew{% endtrans %}
</div>
<div class="card-body">
{{ 'assembly.bom_import.template.kicad_pcbnew.exptected_columns'|trans }}
<pre>Id;Designator;Package;Quantity;Designation;Supplier and ref</pre>
{{ 'assembly.bom_import.template.kicad_pcbnew.exptected_columns.note'|trans|raw }}
{{ 'assembly.bom_import.template.json.table'|trans|raw }}
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,22 @@
{% import "components/datatables.macro.html.twig" as datatables %}
<div class="btn-group mb-2 mt-2">
<a class="btn btn-success" {% if not is_granted('@assemblies.edit') %}disabled{% endif %}
href="{{ path('assembly_add_parts', {"id": assembly.id, "_redirect": uri_without_host(app.request)}) }}">
<i class="fa-solid fa-square-plus fa-fw"></i>
{% trans %}assembly.info.bom_add_parts{% endtrans %}
</a>
<button type="button" class="btn btn-success dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item" href="{{ path('assembly_import_bom', {'id': assembly.id}) }}" {% if not is_granted('edit', assembly) %}disabled="disabled"{% endif %}>
<i class="fa-solid fa-file-import fa-fw"></i>
{% trans %}assembly.edit.bom.import_bom{% endtrans %}
</a>
</li>
</ul>
</div>
{{ datatables.datatable(datatable, 'elements/datatables/datatables', 'assemblies') }}

View file

@ -0,0 +1,40 @@
{% set can_build = buildHelper.assemblyBuildable(assembly) %}
{% import "components/assemblies.macro.html.twig" as assembly_macros %}
{% if assembly.status is not empty and assembly.status != "in_production" %}
<div class="alert mt-2 alert-warning" role="alert">
<i class="fa-solid fa-triangle-exclamation fa-fw"></i> {% trans with {"%assembly_status%": ('assembly.status.'~assembly.status)|trans } %}assembly.builds.check_assembly_status{% endtrans %}
</div>
{% endif %}
<div class="alert mt-2 {% if can_build %}alert-success{% else %}alert-danger{% endif %}" role="alert">
{% if not can_build %}
<h5><i class="fa-solid fa-circle-exclamation fa-fw"></i> {% trans %}assembly.builds.build_not_possible{% endtrans %}</h5>
<b>{% trans %}assembly.builds.following_bom_entries_miss_instock{% endtrans %}</b>
<ul>
{% for bom_entry in buildHelper.nonBuildableAssemblyBomEntries(assembly) %}
<li>{{ assembly_macros.assembly_bom_entry_with_missing_instock(bom_entry) }}</li>
{% endfor %}
</ul>
{% else %}
<h5><i class="fa-solid fa-circle-check fa-fw"></i> {% trans %}assembly.builds.build_possible{% endtrans %}</h5>
<span>{% trans with {"%max_builds%": buildHelper.maximumBuildableCount(assembly)} %}assembly.builds.number_of_builds_possible{% endtrans %}</span>
{% endif %}
</div>
<form method="get" action="{{ path('assembly_build', {"id": assembly.iD }) }}">
<div class="row mt-2">
<div class="col-4">
<div class="input-group mb-3">
<input type="number" min="1" class="form-control" placeholder="{% trans %}assembly.builds.number_of_builds{% endtrans %}" name="n" required>
<input type="hidden" name="_redirect" value="{{ uri_without_host(app.request) }}">
<button class="btn btn-outline-secondary" type="submit" id="button-addon2">{% trans %}assembly.build.btn_build{% endtrans %}</button>
</div>
</div>
</div>
</form>
{% if assembly.buildPart %}
<p><b>{% trans %}assembly.builds.no_stocked_builds{% endtrans %}:</b> <a href="{{ entity_url(assembly.buildPart) }}">{{ assembly.buildPart.amountSum }}</a></p>
{% endif %}

View file

@ -0,0 +1,77 @@
{% import "helper.twig" as helper %}
<div class="row mt-2">
<div class="col-md-8">
<div class="row">
<div class="col-md-3 col-lg-4 col-4 mt-auto mb-auto">
{% if assembly.masterPictureAttachment %}
<a href="{{ entity_url(assembly.masterPictureAttachment, 'file_view') }}" data-turbo="false" target="_blank" rel="noopener">
<img class="d-block w-100 img-fluid img-thumbnail bg-body-tertiary part-info-image" src="{{ entity_url(assembly.masterPictureAttachment, 'file_view') }}" alt="">
</a>
{% else %}
<img src="{{ asset('img/part_placeholder.svg') }}" class="img-fluid img-thumbnail bg-body-tertiary mb-2 " alt="Part main image" height="300" width="300">
{% endif %}
</div>
<div class="col-md-9 col-lg-8 col-7">
<h3 class="w-fit" title="{% trans %}name.label{% endtrans %}">{{ assembly.name }}
{# You need edit permission to use the edit button #}
{% if is_granted('edit', assembly) %}
<a href="{{ entity_url(assembly, 'edit') }}"><i class="fas fa-fw fa-sm fa-edit"></i></a>
{% endif %}
</h3>
<h6 class="text-muted w-fit" title="{% trans %}description.label{% endtrans %}"><span>{{ assembly.description|format_markdown(true) }}</span></h6>
{% if assembly.buildPart %}
<h6>{% trans %}assembly.edit.associated_build_part{% endtrans %}:</h6>
<a href="{{ entity_url(assembly.buildPart) }}">{{ assembly.buildPart.name }}</a>
{% endif %}
</div>
</div>
</div>
<div class="col-md-4"> {# Sidebar panel with infos about last creation date, etc. #}
<div class="mb-3">
<span class="text-muted" title="{% trans %}lastModified{% endtrans %}">
<i class="fas fa-history fa-fw"></i> {{ helper.date_user_combination(assembly, true) }}
</span>
<br>
<span class="text-muted mt-1" title="{% trans %}createdAt{% endtrans %}">
<i class="fas fa-calendar-plus fa-fw"></i> {{ helper.date_user_combination(assembly, false) }}
</span>
</div>
<div class="mt-1">
<h6>
{{ helper.assemblies_status_to_badge(assembly.status) }}
</h6>
</div>
<div class="mt-1">
<h6>
<span class="badge badge-primary bg-primary">
<i class="fa-solid fa-list-check fa-fw"></i>
{{ assembly.bomEntries | length }}
{% trans %}assembly.info.bom_entries_count{% endtrans %}
</span>
</h6>
</div>
{% if assembly.children is not empty %}
<div class="mt-1">
<h6>
<span class="badge badge-primary bg-secondary">
<i class="fa-solid fa-folder-tree fa-fw"></i>
{{ assembly.children | length }}
{% trans %}assembly.info.sub_assemblies_count{% endtrans %}
</span>
</h6>
</div>
{% endif %}
</div>
{% if assembly.comment is not empty %}
<p>
<h5>{% trans %}comment.label{% endtrans %}:</h5>
{{ assembly.comment|format_markdown }}
</p>
{% endif %}
</div>

View file

@ -0,0 +1,133 @@
{% import "helper.twig" as helper %}
{% import "label_system/dropdown_macro.html.twig" as dropdown %}
{{ helper.breadcrumb_entity_link(assembly) }}
<div class="accordion mb-4" id="listAccordion">
<div class="accordion-item">
<div class="accordion-header">
<button class="accordion-button collapsed py-2" data-bs-toggle="collapse" data-bs-target="#entityInfo" aria-expanded="true">
{% if assembly.masterPictureAttachment is not null and attachment_manager.isFileExisting(assembly.masterPictureAttachment) %}
<img class="hoverpic ms-0 me-1 d-inline" {{ stimulus_controller('elements/hoverpic') }} data-thumbnail="{{ entity_url(assembly.masterPictureAttachment, 'file_view') }}" src="{{ attachment_thumbnail(assembly.masterPictureAttachment, 'thumbnail_sm') }}">
{% else %}
{{ helper.entity_icon(assembly, "me-1") }}
{% endif %}
{% trans %}assembly.label{% endtrans %}:&nbsp;<b>{{ assembly.name }}</b>
</button>
</div>
<div id="entityInfo" class="accordion-collapse collapse show" data-bs-parent="#listAccordion">
<div class="accordion-body">
{% if assembly.description is not empty %}
{{ assembly.description|format_markdown }}
{% endif %}
<div class="row">
<div class="col-sm-2">
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">
<a class="nav-link active" id="v-pills-home-tab" data-bs-toggle="pill" href="#v-pills-home" role="tab" aria-controls="v-pills-home" aria-selected="true">
<i class="fas fa-info-circle fa-fw"></i>
{% trans %}entity.info.common.tab{% endtrans %}
</a>
<a class="nav-link" id="v-pills-statistics-tab" data-bs-toggle="pill" href="#v-pills-statistics" role="tab" aria-controls="v-pills-profile" aria-selected="false">
<i class="fas fa-chart-pie fa-fw"></i>
{% trans %}entity.info.statistics.tab{% endtrans %}
</a>
{% if assembly.attachments is not empty %}
<a class="nav-link" id="v-pills-attachments-tab" data-bs-toggle="pill" href="#v-pills-attachments" role="tab" aria-controls="v-pills-attachments" aria-selected="false">
<i class="fas fa-paperclip fa-fw"></i>
{% trans %}entity.info.attachments.tab{% endtrans %}
</a>
{% endif %}
{% if assembly.parameters is not empty %}
<a class="nav-link" id="v-pills-parameters-tab" data-bs-toggle="pill" href="#v-pills-parameters" role="tab" aria-controls="v-pills-parameters" aria-selected="false">
<i class="fas fa-atlas fa-fw"></i>
{% trans %}entity.info.parameters.tab{% endtrans %}
</a>
{% endif %}
{% if assembly.comment is not empty %}
<a class="nav-link" id="v-pills-comment-tab" data-bs-toggle="pill" href="#v-pills-comment" role="tab">
<i class="fas fa-comment-alt fa-fw"></i>
{% trans %}comment.label{% endtrans %}
</a>
{% endif %}
</div>
</div>
<div class="col-sm-10">
<div class="tab-content" id="v-pills-tabContent">
<div class="tab-pane fade show active" id="v-pills-home" role="tabpanel" aria-labelledby="v-pills-home-tab">
<div class="row">
<div class="col-sm-9 form-horizontal">
<div class="form-group">
<label class="col-sm-4">{% trans %}entity.info.name{% endtrans %}:</label>
<span class="col-sm form-control-static">{{ assembly.name }}</span>
</div>
<div class="form-group">
<label class="col-sm-4">{% trans %}entity.info.parent{% endtrans %}:</label>
<span class="col-sm form-control-static">
{% if assembly.parent %}
{{ assembly.parent.fullPath }}
{% else %}
-
{% endif %}
</span>
</div>
</div>
<div class="col-sm-3">
{% block quick_links %}{% endblock %}
<a class="btn btn-secondary w-100 mb-2" href="{{ entity_url(assembly, 'edit') }}">
<i class="fas fa-edit"></i> {% trans %}entity.edit.btn{% endtrans %}
</a>
<div class="">
<span class="text-muted" title="{% trans %}lastModified{% endtrans %}">
<i class="fas fa-history fa-fw"></i> {{ assembly.lastModified | format_datetime("short") }}
</span>
<br>
<span class="text-muted mt-1" title="{% trans %}createdAt{% endtrans %}">
<i class="fas fa-calendar-plus fa-fw"></i> {{ assembly.addedDate | format_datetime("short") }}
</span>
</div>
</div>
</div>
</div>
<div class="tab-pane fade" id="v-pills-statistics" role="tabpanel" aria-labelledby="v-pills-statistics-tab">
<div class="form-horizontal">
<div class="form-group">
<label class="col-sm-4">{% trans %}entity.info.children_count{% endtrans %}:</label>
<span class="col-sm form-control-static">{{ assembly.children | length }}</span>
</div>
<div class="form-group">
<label class="col-sm-4">{% trans %}entity.info.parts_count{% endtrans %}:</label>
<span class="col-sm form-control-static">{{ assembly.bomEntries | length }}</span>
</div>
</div>
</div>
{% if assembly.attachments is not empty %}
<div class="tab-pane fade" id="v-pills-attachments" role="tabpanel" aria-labelledby="v-pills-attachments-tab">
{% include "parts/info/_attachments_info.html.twig" with {"part": assembly} %}
</div>
{% endif %}
{% if assembly.parameters is not empty %}
<div class="tab-pane fade" id="v-pills-parameters" role="tabpanel" aria-labelledby="v-pills-parameters-tab">
{% for name, parameters in assembly.groupedParameters %}
{% if name is not empty %}<h5 class="mt-1">{{ name }}</h5>{% endif %}
{{ helper.parameters_table(assembly) }}
{% endfor %}
</div>
{% endif %}
{% if assembly.comment is not empty %}
<div class="tab-pane fade" id="v-pills-comment" role="tabpanel" aria-labelledby="home-tab">
<div class="container-fluid mt-2 latex" data-controller="common--latex">
{{ assembly.comment|format_markdown }}
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,28 @@
<table class="table table-striped table-hover">
<thead>
<tr>
<th>{% trans %}name.label{% endtrans %}</th>
<th>{% trans %}description.label{% endtrans %}</th>
<th># {% trans %}assembly.info.bom_entries_count{% endtrans %}</th>
<th># {% trans %}assembly.info.sub_assemblies_count{% endtrans %}</th>
</tr>
</thead>
<tbody>
{% for subassembly in assembly.children %}
<tr>
<td> {# Name #}
<a href="{{ entity_url(subassembly, 'info') }}">{{ subassembly.name }}</a>
</td>
<td> {# Description #}
{{ subassembly.description | format_markdown }}
</td>
<td>
{{ subassembly.bomEntries | length }}
</td>
<td>
{{ subassembly.children | length }}
</td>
</tr>
{% endfor %}
</tbody>
</table>

View file

@ -0,0 +1,105 @@
{% extends "main_card.html.twig" %}
{% import "helper.twig" as helper %}
{% block title %}
{% trans %}assembly.info.title{% endtrans %}: {{ assembly.name }}
{% endblock %}
{% block content %}
{{ helper.breadcrumb_entity_link(assembly) }}
{{ parent() }}
{% endblock %}
{% block card_title %}
{% if assembly.masterPictureAttachment is not null and attachment_manager.isFileExisting(assembly.masterPictureAttachment) %}
<img class="hoverpic ms-0 me-1 d-inline" {{ stimulus_controller('elements/hoverpic') }} data-thumbnail="{{ entity_url(assembly.masterPictureAttachment, 'file_view') }}" src="{{ attachment_thumbnail(assembly.masterPictureAttachment, 'thumbnail_sm') }}">
{% else %}
{{ helper.entity_icon(assembly, "me-1") }}
{% endif %}
{% trans %}assembly.info.title{% endtrans %}:&nbsp;<b>{{ assembly.name }}</b>
{% endblock %}
{% block card_content %}
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="info-tab" data-bs-toggle="tab" data-bs-target="#info-tab-pane"
type="button" role="tab" aria-controls="info-tab-pane" aria-selected="true">
<i class="fa-solid fa-circle-info fa-fw"></i>
{% trans %}assembly.info.info.label{% endtrans %}
</button>
</li>
{% if assembly.children is not empty %}
<li class="nav-item" role="presentation">
<button class="nav-link" id="subassemblies-tab" data-bs-toggle="tab" data-bs-target="#subassemblies-tab-pane"
type="button" role="tab" aria-controls="subassemblies-tab-pane" aria-selected="false">
<i class="fa-solid fa-folder-tree fa-fw"></i>
{% trans %}assembly.info.sub_assemblies.label{% endtrans %}
<span class="badge bg-secondary">{{ assembly.children | length }}</span>
</button>
</li>
{% endif %}
<li class="nav-item" role="presentation">
<button class="nav-link" id="bom-tab" data-bs-toggle="tab" data-bs-target="#bom-tab-pane"
type="button" role="tab" aria-controls="bom-tab-pane" aria-selected="false">
<i class="fa-solid fa-list-check fa-fw"></i>
{% trans %}assembly_bom_entry.label{% endtrans %}
<span class="badge bg-secondary">{{ assembly.bomEntries | length }}</span>
</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="builds-tab" data-bs-toggle="tab" data-bs-target="#builds-tab-pane"
type="button" role="tab" aria-controls="builds-tab-pane" aria-selected="false">
<i class="fa-solid fa-bolt fa-fw"></i>
{% trans %}assembly.info.builds.label{% endtrans %}
</button>
</li>
{% if assembly.attachments is not empty %}
<li class="nav-item">
<a class="nav-link" id="attachments-tab" data-bs-toggle="tab"
data-bs-target="#attachments-tab-pane" role="tab">
<i class="fas fa-paperclip fa-fw"></i>
{% trans %}attachment.labelp{% endtrans %}
<span class="badge bg-secondary">{{ assembly.attachments | length }}</span>
</a>
</li>
{% endif %}
{% if assembly.parameters is not empty %}
<li class="nav-item">
<a class="nav-link" id="parameters-tab" data-bs-toggle="tab"
data-bs-target="#parameters-tab-pane" role="tab">
<i class="fas fa-atlas fa-fw"></i>
{% trans %}entity.info.parameters.tab{% endtrans %}
<span class="badge bg-secondary">{{ assembly.parameters | length }}</span>
</a>
</li>
{% endif %}
</ul>
<div class="tab-content">
<div class="tab-pane fade show active" id="info-tab-pane" role="tabpanel" aria-labelledby="info-tab" tabindex="0">
{% include "assemblies/info/_info.html.twig" %}
</div>
{% if assembly.children is not empty %}
<div class="tab-pane fade" id="subassemblies-tab-pane" role="tabpanel" aria-labelledby="bom-tab" tabindex="0">
{% include "assemblies/info/_subassemblies.html.twig" %}
</div>
{% endif %}
<div class="tab-pane fade" id="bom-tab-pane" role="tabpanel" aria-labelledby="bom-tab" tabindex="0">
{% include "assemblies/info/_bom.html.twig" %}
</div>
<div class="tab-pane fade" id="builds-tab-pane" role="tabpanel" aria-labelledby="builds-tab" tabindex="0">
{% include "assemblies/info/_builds.html.twig" %}
</div>
<div class="tab-pane fade" id="attachments-tab-pane" role="tabpanel" aria-labelledby="attachments-tab" tabindex="0">
{% include "parts/info/_attachments_info.html.twig" with {"part": assembly} %}
</div>
<div class="tab-pane fade" id="parameters-tab-pane" role="tabpanel" aria-labelledby="parameters-tab">
{% for name, parameters in assembly.groupedParameters %}
{% if name is not empty %}<h5 class="mt-1">{{ name }}</h5>{% endif %}
{{ helper.parameters_table(assembly.parameters) }}
{% endfor %}
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,8 @@
{% macro assembly_bom_entry_with_missing_instock(assembly_bom_entry, number_of_builds = 1) %}
{# @var \App\Entity\AssemblySystem\AssemblyBOMEntry assembly_bom_entry #}
<b><a href="{{ entity_url(assembly_bom_entry.part) }}">{{ assembly_bom_entry.part.name }}</a></b>
{% if assembly_bom_entry.name %}&nbsp;({{ assembly_bom_entry.name }}){% endif %}:
<b>{{ assembly_bom_entry.part.amountSum | format_amount(assembly_bom_entry.part.partUnit) }}</b> {% trans %}assembly.builds.stocked{% endtrans %}
/
<b>{{ (assembly_bom_entry.quantity * number_of_builds) | format_amount(assembly_bom_entry.part.partUnit) }}</b> {% trans %}assembly.builds.needed{% endtrans %}
{% endmacro %}

View file

@ -7,6 +7,7 @@
['manufacturers', path('tree_manufacturer_root'), 'manufacturer.labelp', is_granted('@manufacturers.read') and is_granted('@parts.read')],
['suppliers', path('tree_supplier_root'), 'supplier.labelp', is_granted('@suppliers.read') and is_granted('@parts.read')],
['projects', path('tree_device_root'), 'project.labelp', is_granted('@projects.read')],
['assembly', path('tree_assembly_root'), 'assembly.labelp', is_granted('@assemblies.read')],
['tools', path('tree_tools'), 'tools.label', true],
] %}

View file

@ -6,7 +6,7 @@
<tr>
<th></th> {# expand button #}
<th>{% trans %}project.bom.quantity{% endtrans %}</th>
<th>{% trans %}project.bom.part{% endtrans %}</th>
<th>{% trans %}project.bom.partOrAssembly{% endtrans %}</th>
<th>{% trans %}project.bom.name{% endtrans %}</th>
<th></th> {# Remove button #}
</tr>
@ -41,9 +41,11 @@
{{ form_widget(form.quantity) }}
{{ form_errors(form.quantity) }}
</td>
<td style="min-width: 250px;">
{{ form_widget(form.part) }}
<td style="min-width: 300px;">
{{ form_row(form.part) }}
{{ form_errors(form.part) }}
{{ form_widget(form.assembly) }}
{{ form_errors(form.assembly) }}
</td>
<td>
{{ form_widget(form.name) }}

View file

@ -0,0 +1,80 @@
{% block assembly_bom_entry_collection_widget %}
{% import 'components/collection_type.macro.html.twig' as collection %}
<div {{ collection.controller(form, 'assembly.bom.delete.confirm', 3) }}>
<table class="table table-striped table-bordered table-sm" {{ collection.target() }}>
<thead>
<tr>
<th></th> {# expand button #}
<th>{% trans %}assembly.bom.quantity{% endtrans %}</th>
<th>{% trans %}assembly.bom.part{% endtrans %}</th>
<th>{% trans %}assembly.bom.name{% endtrans %}</th>
<th></th> {# Remove button #}
</tr>
</thead>
<tbody>
{% for entry in form %}
{{ form_widget(entry) }}
{% endfor %}
</tbody>
</table>
<button type="button" class="btn btn-success mb-2" {{ collection.create_btn() }}>
<i class="fas fa-plus-square fa-fw"></i>
{% trans %}assembly.bom.add_entry{% endtrans %}
</button>
</div>
{% endblock %}
{% block assembly_bom_entry_widget %}
{% set target_id = 'expand_row-' ~ form.vars.name %}
{% import 'components/collection_type.macro.html.twig' as collection %}
<tr>
<td>
<button class="btn btn-sm btn-outline-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#{{ target_id }}">
<i class="fa-solid fa-eye"></i>
</button>
</td>
<td>
{{ form_widget(form.quantity) }}
{{ form_errors(form.quantity) }}
</td>
<td style="min-width: 250px;">
{{ form_widget(form.part) }}
{{ form_errors(form.part) }}
</td>
<td>
{{ form_widget(form.name) }}
{{ form_errors(form.name) }}
</td>
<td>
<button type="button" class="btn btn-danger lot_btn_delete position-relative" {{ collection.delete_btn() }}>
<i class="fas fa-trash-alt fa-fw"></i>
{{ collection.new_element_indicator(value) }}
</button>
{{ form_errors(form) }}
</td>
</tr>
<tr class="p-0 d-none"></tr>
<tr class="p-0">
<td colspan="5" class="accordion-body collapse" id="{{ target_id }}">
<div class="">
{{ form_row(form.mountnames) }}
<div class="row mb-2">
<label class="col-form-label col-sm-3">{% trans %}assembly.bom.price{% endtrans %}</label>
<div class="col-sm-9">
<div class="input-group">
{{ form_widget(form.price) }}
{{ form_widget(form.priceCurrency) }}
</div>
{{ form_errors(form.price) }}
{{ form_errors(form.priceCurrency) }}
</div>
</div>
{{ form_row(form.comment) }}
</div>
</td>
</tr>
{% endblock %}

View file

@ -76,6 +76,21 @@
{% endif %}
{% endmacro %}
{% macro assemblies_status_to_badge(status, class="badge") %}
{% if status is not empty %}
{% set color = " bg-secondary" %}
{% if status == "in_production" %}
{% set color = " bg-success" %}
{% endif %}
<span class="{{ class ~ color}}">
<i class="fa-fw fas fa-info-circle"></i>
{{ ("assembly.status." ~ status) | trans }}
</span>
{% endif %}
{% endmacro %}
{% macro structural_entity_link(entity, link_type = "list_parts") %}
{# @var entity \App\Entity\Base\StructuralDBElement #}
{% if entity %}
@ -101,6 +116,7 @@
"category": ["fa-solid fa-tags", "category.label"],
"currency": ["fa-solid fa-coins", "currency.label"],
"device": ["fa-solid fa-archive", "project.label"],
"assembly": ["fa-solid fa-list", "assembly.label"],
"footprint": ["fa-solid fa-microchip", "footprint.label"],
"group": ["fa-solid fa-users", "group.label"],
"label_profile": ["fa-solid fa-qrcode", "label_profile.label"],

View file

@ -27,7 +27,9 @@
</td>
<td >
{% if bom_entry.part %}
<b><a target="_blank" href="{{ entity_url(bom_entry.part) }}">{{ bom_entry.part.name }}</a></b> {% if bom_entry.name %}({{ bom_entry.name }}){% endif %}
<b><a target="_blank" href="{{ entity_url(bom_entry.part) }}">{{ 'projects.build.form.part'|trans({'%name%': bom_entry.part.name}) }}</a></b> {% if bom_entry.name %}({{ bom_entry.name }}){% endif %}
{% elseif bom_entry.assembly %}
<b><a target="_blank" href="{{ entity_url(bom_entry.assembly) }}">{{ 'projects.build.form.assembly'|trans({'%name%': bom_entry.assembly.name}) }}</a></b> {% if bom_entry.name %}({{ bom_entry.name }}){% endif %}
{% else %}
<b>{{ bom_entry.name }}</b>
{% endif %}
@ -45,9 +47,29 @@
<tr>
<td colspan="4">
{% set lots = build_request.partLotsForBOMEntry(bom_entry) %}
{% set assemblyBomEntriesWithoutPart = build_request.assemblyBomEntriesWithoutPart(bom_entry) %}
{% set assemblyBomEntriesWithPartNoStock = build_request.assemblyBomEntriesWithPartNoStock(bom_entry) %}
{% if lots is not null %}
{% set previousLabel = null %}
{% for lot in lots %}
{# @var lot \App\Entity\Parts\PartLot #}
{% set label = '' %}
{% if form["lot_"~lot.id].vars.label is defined and form["lot_"~lot.id].vars.label is not empty %}
{% set label = form["lot_"~lot.id].vars.label %}
{% endif %}
{% if label != '' and (previousLabel is null or label != previousLabel) %}
<div class="mb-2 row">
<label class="col-form-label col-lg-4">
<small>{{ label|raw }}</small>
</label>
</div>
{% endif %}
{% set previousLabel = label %}
<div class="mb-2 row">
<label class="col-form-label col-sm-4" for="category_admin_form_parent">
{% if lot.storageLocation %}
@ -61,12 +83,41 @@
{{ form_errors(form["lot_"~lot.id]) }}
{{ form_widget(form["lot_"~lot.id]) }}
</div>
<div class="col-sm-2 mt-1 text-end">
<div class="col-sm-2 mt-1 text-end">
/ <b>{{ lot.amount | format_amount(lot.part.partUnit) }}</b> {% trans %}project.builds.stocked{% endtrans %}
</div>
</div>
{% endfor %}
{% endif %}
{% if assemblyBomEntriesWithoutPart is not null %}
{% for bomEntryWithoutPart in assemblyBomEntriesWithoutPart %}
<div class="mb-2 row">
<label class="col-form-label col-sm-4" for="category_admin_form_parent">
<small>{{ 'projects.build.form.assembly.bom.entry'|trans({'%name%': bomEntryWithoutPart.name, '%quantity%': bomEntryWithoutPart.quantity * number_of_builds}) }}</small>
</label>
<div class="col-sm-6"></div>
<div class="col-sm-2 mt-1 text-end">
/ {% trans %}project.builds.no_stock{% endtrans %}
</div>
</div>
{% endfor %}
{% endif %}
{% if assemblyBomEntriesWithPartNoStock is not null %}
{% for bomEntryWithPartNoStock in assemblyBomEntriesWithPartNoStock %}
<div class="alert alert-danger">
<div class="mb-2 row">
<label class="col-form-label col-sm-4" for="category_admin_form_parent">
{% trans %}projects.build.form.assembly.bom.entry.no.stock{% endtrans %}<br/>
<small>{{ 'projects.build.form.assembly.bom.entry'|trans({'%name%': bomEntryWithPartNoStock.name ? bomEntryWithPartNoStock.name : bomEntryWithPartNoStock.part.name, '%quantity%': bomEntryWithPartNoStock.quantity * number_of_builds}) }}</small>
</label>
<div class="col-sm-6"></div>
<div class="col-sm-2 mt-1 text-end">
/ {% trans %}project.builds.no_stock{% endtrans %}
</div>
</div>
</div>
{% endfor %}
{% endif %}
</td>
</tr>
{% endfor %}

View file

@ -24,6 +24,8 @@ namespace App\Tests\Entity\Attachments;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Depends;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\Attachments\AssemblyAttachment;
use App\Entity\Attachments\Attachment;
use App\Entity\Attachments\AttachmentType;
use App\Entity\Attachments\AttachmentTypeAttachment;
@ -81,6 +83,7 @@ class AttachmentTest extends TestCase
yield [CategoryAttachment::class, Category::class];
yield [CurrencyAttachment::class, Currency::class];
yield [ProjectAttachment::class, Project::class];
yield [AssemblyAttachment::class, Assembly::class];
yield [FootprintAttachment::class, Footprint::class];
yield [GroupAttachment::class, Group::class];
yield [ManufacturerAttachment::class, Manufacturer::class];

View file

@ -0,0 +1,177 @@
<?php
declare(strict_types=1);
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2023 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\Tests\Helpers\Assemblies;
use App\Entity\Parts\MeasurementUnit;
use App\Entity\Parts\Part;
use App\Entity\Parts\PartLot;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Helpers\Assemblies\AssemblyBuildRequest;
use PHPUnit\Framework\TestCase;
class AssemblyBuildRequestTest extends TestCase
{
/** @var MeasurementUnit $float_unit */
private MeasurementUnit $float_unit;
/** @var Assembly */
private Assembly $assembly1;
/** @var AssemblyBOMEntry */
private AssemblyBOMEntry $bom_entry1a;
/** @var AssemblyBOMEntry */
private AssemblyBOMEntry $bom_entry1b;
/** @var AssemblyBOMEntry */
private AssemblyBOMEntry $bom_entry1c;
private PartLot $lot1a;
private PartLot $lot1b;
private PartLot $lot2;
/** @var Part */
private Part $part1;
/** @var Part */
private Part $part2;
public function setUp(): void
{
$this->float_unit = new MeasurementUnit();
$this->float_unit->setName('float');
$this->float_unit->setUnit('f');
$this->float_unit->setIsInteger(false);
$this->float_unit->setUseSIPrefix(true);
//Setup some example parts and part lots
$this->part1 = new Part();
$this->part1->setName('Part 1');
$this->lot1a = new class extends PartLot {
public function getID(): ?int
{
return 1;
}
};
$this->part1->addPartLot($this->lot1a);
$this->lot1a->setAmount(10);
$this->lot1a->setDescription('Lot 1a');
$this->lot1b = new class extends PartLot {
public function getID(): ?int
{
return 2;
}
};
$this->part1->addPartLot($this->lot1b);
$this->lot1b->setAmount(20);
$this->lot1b->setDescription('Lot 1b');
$this->part2 = new Part();
$this->part2->setName('Part 2');
$this->part2->setPartUnit($this->float_unit);
$this->lot2 = new PartLot();
$this->part2->addPartLot($this->lot2);
$this->lot2->setAmount(2.5);
$this->lot2->setDescription('Lot 2');
$this->bom_entry1a = new AssemblyBOMEntry();
$this->bom_entry1a->setPart($this->part1);
$this->bom_entry1a->setQuantity(2);
$this->bom_entry1b = new AssemblyBOMEntry();
$this->bom_entry1b->setPart($this->part2);
$this->bom_entry1b->setQuantity(1.5);
$this->bom_entry1c = new AssemblyBOMEntry();
$this->bom_entry1c->setName('Non-part BOM entry');
$this->bom_entry1c->setQuantity(4);
$this->assembly1 = new Assembly();
$this->assembly1->setName('Assembly 1');
$this->assembly1->addBomEntry($this->bom_entry1a);
$this->assembly1->addBomEntry($this->bom_entry1b);
$this->assembly1->addBomEntry($this->bom_entry1c);
}
public function testInitialization(): void
{
//The values should be already prefilled correctly
$request = new AssemblyBuildRequest($this->assembly1, 10);
//We need totally 20: Take 10 from the first (maximum 10) and 10 from the second (maximum 20)
$this->assertEqualsWithDelta(10.0, $request->getLotWithdrawAmount($this->lot1a), PHP_FLOAT_EPSILON);
$this->assertEqualsWithDelta(10.0, $request->getLotWithdrawAmount($this->lot1b), PHP_FLOAT_EPSILON);
//If the needed amount is higher than the maximum, we should get the maximum
$this->assertEqualsWithDelta(2.5, $request->getLotWithdrawAmount($this->lot2), PHP_FLOAT_EPSILON);
}
public function testGetNumberOfBuilds(): void
{
$build_request = new AssemblyBuildRequest($this->assembly1, 5);
$this->assertSame(5, $build_request->getNumberOfBuilds());
}
public function testGetAssembly(): void
{
$build_request = new AssemblyBuildRequest($this->assembly1, 5);
$this->assertEquals($this->assembly1, $build_request->getAssembly());
}
public function testGetNeededAmountForBOMEntry(): void
{
$build_request = new AssemblyBuildRequest($this->assembly1, 5);
$this->assertEqualsWithDelta(10.0, $build_request->getNeededAmountForBOMEntry($this->bom_entry1a), PHP_FLOAT_EPSILON);
$this->assertEqualsWithDelta(7.5, $build_request->getNeededAmountForBOMEntry($this->bom_entry1b), PHP_FLOAT_EPSILON);
$this->assertEqualsWithDelta(20.0, $build_request->getNeededAmountForBOMEntry($this->bom_entry1c), PHP_FLOAT_EPSILON);
}
public function testGetSetLotWithdrawAmount(): void
{
$build_request = new AssemblyBuildRequest($this->assembly1, 5);
//We can set the amount for a lot either via the lot object or via the ID
$build_request->setLotWithdrawAmount($this->lot1a, 2);
$build_request->setLotWithdrawAmount($this->lot1b->getID(), 3);
//And it should be possible to get the amount via the lot object or via the ID
$this->assertEqualsWithDelta(2.0, $build_request->getLotWithdrawAmount($this->lot1a->getID()), PHP_FLOAT_EPSILON);
$this->assertEqualsWithDelta(3.0, $build_request->getLotWithdrawAmount($this->lot1b), PHP_FLOAT_EPSILON);
}
public function testGetWithdrawAmountSum(): void
{
//The sum of all withdraw amounts for an BOM entry (over all lots of the associated part) should be correct
$build_request = new AssemblyBuildRequest($this->assembly1, 5);
$build_request->setLotWithdrawAmount($this->lot1a, 2);
$build_request->setLotWithdrawAmount($this->lot1b, 3);
$this->assertEqualsWithDelta(5.0, $build_request->getWithdrawAmountSum($this->bom_entry1a), PHP_FLOAT_EPSILON);
$build_request->setLotWithdrawAmount($this->lot2, 1.5);
$this->assertEqualsWithDelta(1.5, $build_request->getWithdrawAmountSum($this->bom_entry1b), PHP_FLOAT_EPSILON);
}
}

View file

@ -0,0 +1,117 @@
<?php
declare(strict_types=1);
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2023 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\Tests\Services\AssemblySystem;
use App\Entity\AssemblySystem\Assembly;
use App\Entity\AssemblySystem\AssemblyBOMEntry;
use App\Entity\Parts\Part;
use App\Entity\Parts\PartLot;
use App\Services\AssemblySystem\AssemblyBuildHelper;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class AssemblyBuildHelperTest extends WebTestCase
{
/** @var AssemblyBuildHelper */
protected $service;
protected function setUp(): void
{
self::bootKernel();
$this->service = self::getContainer()->get(AssemblyBuildHelper::class);
}
public function testGetMaximumBuildableCountForBOMEntryNonPartBomEntry(): void
{
$bom_entry = new AssemblyBOMEntry();
$bom_entry->setPart(null);
$bom_entry->setQuantity(10);
$bom_entry->setName('Test');
$this->expectException(\InvalidArgumentException::class);
$this->service->getMaximumBuildableCountForBOMEntry($bom_entry);
}
public function testGetMaximumBuildableCountForBOMEntry(): void
{
$assembly_bom_entry = new AssemblyBOMEntry();
$assembly_bom_entry->setQuantity(10);
$part = new Part();
$lot1 = new PartLot();
$lot1->setAmount(120);
$lot2 = new PartLot();
$lot2->setAmount(5);
$part->addPartLot($lot1);
$part->addPartLot($lot2);
$assembly_bom_entry->setPart($part);
//We have 125 parts in stock, so we can build 12 times the assembly (125 / 10 = 12.5)
$this->assertSame(12, $this->service->getMaximumBuildableCountForBOMEntry($assembly_bom_entry));
$lot1->setAmount(0);
//We have 5 parts in stock, so we can build 0 times the assembly (5 / 10 = 0.5)
$this->assertSame(0, $this->service->getMaximumBuildableCountForBOMEntry($assembly_bom_entry));
}
public function testGetMaximumBuildableCount(): void
{
$assembly = new Assembly();
$assembly_bom_entry1 = new AssemblyBOMEntry();
$assembly_bom_entry1->setQuantity(10);
$part = new Part();
$lot1 = new PartLot();
$lot1->setAmount(120);
$lot2 = new PartLot();
$lot2->setAmount(5);
$part->addPartLot($lot1);
$part->addPartLot($lot2);
$assembly_bom_entry1->setPart($part);
$assembly->addBomEntry($assembly_bom_entry1);
$assembly_bom_entry2 = new AssemblyBOMEntry();
$assembly_bom_entry2->setQuantity(5);
$part2 = new Part();
$lot3 = new PartLot();
$lot3->setAmount(10);
$part2->addPartLot($lot3);
$assembly_bom_entry2->setPart($part2);
$assembly->addBomEntry($assembly_bom_entry2);
$assembly->addBomEntry((new AssemblyBOMEntry())->setName('Non part entry')->setQuantity(1));
//Restricted by the few parts in stock of part2
$this->assertSame(2, $this->service->getMaximumBuildableCount($assembly));
$lot3->setAmount(1000);
//Now the build count is restricted by the few parts in stock of part1
$this->assertSame(12, $this->service->getMaximumBuildableCount($assembly));
$lot3->setAmount(0);
//Now the build count must be 0, as we have no parts in stock
$this->assertSame(0, $this->service->getMaximumBuildableCount($assembly));
}
}

View file

@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2023 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\Tests\Services\AssemblySystem;
use App\Entity\AssemblySystem\Assembly;
use App\Services\AssemblySystem\AssemblyBuildPartHelper;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class AssemblyBuildPartHelperTest extends WebTestCase
{
/** @var AssemblyBuildPartHelper */
protected $service;
protected function setUp(): void
{
self::bootKernel();
$this->service = self::getContainer()->get(AssemblyBuildPartHelper::class);
}
public function testGetPartInitialization(): void
{
$assembly = new Assembly();
$assembly->setName('Assembly 1');
$assembly->setDescription('Description 1');
$part = $this->service->getPartInitialization($assembly);
$this->assertSame('Assembly 1', $part->getName());
$this->assertSame('Description 1', $part->getDescription());
$this->assertSame($assembly, $part->getBuiltAssembly());
$this->assertSame($part, $assembly->getBuildPart());
}
}

View file

@ -4741,6 +4741,18 @@ Pokud jste to provedli nesprávně nebo pokud počítač již není důvěryhodn
<target>Název</target>
</segment>
</unit>
<unit id="KJuXVR5" name="project.bom.assembly">
<segment state="translated">
<source>project.bom.assembly</source>
<target>Sestava</target>
</segment>
</unit>
<unit id="LKvYWS6" name="project.bom.partOrAssembly">
<segment state="translated">
<source>project.bom.partOrAssembly</source>
<target>Výběr</target>
</segment>
</unit>
<unit id="rW_SFJE" name="part.table.id">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:178</note>
@ -9786,6 +9798,18 @@ Element 3</target>
<target>Díl</target>
</segment>
</unit>
<unit id="KJuXVR5" name="project.bom.assembly">
<segment state="translated">
<source>project.bom.assembly</source>
<target>Baugruppe</target>
</segment>
</unit>
<unit id="LKvYWS6" name="project.bom.partOrAssembly">
<segment state="translated">
<source>project.bom.partOrAssembly</source>
<target>Auswahl</target>
</segment>
</unit>
<unit id="kGqIirz" name="project.bom.add_entry">
<segment state="translated">
<source>project.bom.add_entry</source>
@ -9864,6 +9888,42 @@ Element 3</target>
<target>Archivováno</target>
</segment>
</unit>
<unit id="hIIFtI1" name="assembly.edit.status">
<segment state="translated">
<source>assembly.edit.status</source>
<target>Stav</target>
</segment>
</unit>
<unit id="zRd.psv" name="assembly.status.draft">
<segment state="translated">
<source>assembly.status.draft</source>
<target>Návrh</target>
</segment>
</unit>
<unit id="_U8bb1t" name="assembly.status.planning">
<segment state="translated">
<source>assembly.status.planning</source>
<target>Plánování</target>
</segment>
</unit>
<unit id="GgUh7RT" name="assembly.status.in_production">
<segment state="translated">
<source>assembly.status.in_production</source>
<target>Ve výrobě</target>
</segment>
</unit>
<unit id="IluD8iU" name="assembly.status.finished">
<segment state="translated">
<source>assembly.status.finished</source>
<target>Dokončeno</target>
</segment>
</unit>
<unit id="Mybkd1s" name="assembly.status.archived">
<segment state="translated">
<source>assembly.status.archived</source>
<target>Archivováno</target>
</segment>
</unit>
<unit id="9GtmqC1" name="part.new_build_part.error.build_part_already_exists">
<segment state="translated">
<source>part.new_build_part.error.build_part_already_exists</source>
@ -10140,6 +10200,12 @@ Element 3</target>
<target>k dispozici</target>
</segment>
</unit>
<unit id="gHU1vgc" name="project.builds.no_stock">
<segment state="translated">
<source>project.builds.no_stock</source>
<target>není uveden žádný sklad</target>
</segment>
</unit>
<unit id="s5DQlqF" name="project.builds.needed">
<segment state="translated">
<source>project.builds.needed</source>
@ -10212,6 +10278,12 @@ Element 3</target>
<target>Cílový inventář</target>
</segment>
</unit>
<unit id="OsmK1Iv" name="project.build.builds_part_lot_label">
<segment state="translated">
<source>project.build.builds_part_lot_label</source>
<target>%name% (%quantity% požadováno)</target>
</segment>
</unit>
<unit id="5DTAvWG" name="project.builds.number_of_builds">
<segment state="translated">
<source>project.builds.number_of_builds</source>
@ -13479,5 +13551,633 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz
<target>Minimální šířka náhledu (px)</target>
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Součást)</target>
</segment>
</unit>
<unit id="GW8ZOX7" name="part.table.name.value.for_assembly">
<segment state="translated">
<source>part.table.name.value.for_assembly</source>
<target>%value% (Sestava)</target>
</segment>
</unit>
<unit id="G2sXzh7" name="assembly.label">
<segment>
<source>assembly.label</source>
<target>Sestava</target>
</segment>
</unit>
<unit id="dL51AVa" name="assembly.caption">
<segment>
<source>assembly.caption</source>
<target>Sestava</target>
</segment>
</unit>
<unit id="G_rlE3w" name="perm.assemblies">
<segment>
<source>perm.assemblies</source>
<target>Sestavy</target>
</segment>
</unit>
<unit id="dipIGt4" name="assembly_bom_entry.label">
<segment>
<source>assembly_bom_entry.label</source>
<target>Součásti</target>
</segment>
</unit>
<unit id="TvHlYfl" name="assembly.labelp">
<segment>
<source>assembly.labelp</source>
<target>Sestavy</target>
</segment>
</unit>
<unit id="gyRGdfv" name="assembly.edit">
<segment>
<source>assembly.edit</source>
<target>Upravit sestavu</target>
</segment>
</unit>
<unit id="4Tdtoj_" name="assembly.new">
<segment>
<source>assembly.new</source>
<target>Nová sestava</target>
</segment>
</unit>
<unit id="M51YUE." name="assembly.edit.associated_build_part">
<segment>
<source>assembly.edit.associated_build_part</source>
<target>Přidružená součást</target>
</segment>
</unit>
<unit id="nH9R9f." name="assembly.edit.associated_build_part.add">
<segment>
<source>assembly.edit.associated_build_part.add</source>
<target>Přidat součást</target>
</segment>
</unit>
<unit id="oVfOk.i" name="assembly.edit.associated_build.hint">
<segment>
<source>assembly.edit.associated_build.hint</source>
<target>Tato součást představuje vyrobené instance sestavy. Zadejte, pokud jsou vyrobené instance potřeba. Pokud ne, počet součástí bude použit až při sestavení daného projektu.</target>
</segment>
</unit>
<unit id="_wZ_JZY" name="assembly.edit.bom.import_bom">
<segment>
<source>assembly.edit.bom.import_bom</source>
<target>Importovat součásti</target>
</segment>
</unit>
<unit id="vsmgKMw" name="log.database_updated.failed">
<segment>
<source>log.database_updated.failed</source>
<target>__log.database_updated.failed</target>
</segment>
</unit>
<unit id="GcQrTTE" name="log.database_updated.old_version">
<segment>
<source>log.database_updated.old_version</source>
<target>__log.database_updated.old_version</target>
</segment>
</unit>
<unit id="JJF47vK" name="log.database_updated.new_version">
<segment>
<source>log.database_updated.new_version</source>
<target>__log.database_updated.new_version</target>
</segment>
</unit>
<unit id="iVHS_sh" name="tree.tools.edit.assemblies">
<segment>
<source>tree.tools.edit.assemblies</source>
<target>Sestavy</target>
</segment>
</unit>
<unit id="naAMjcH" name="assembly.bom_import.flash.success">
<segment>
<source>assembly.bom_import.flash.success</source>
<target>%count% součástí úspěšně importováno do sestavy.</target>
</segment>
</unit>
<unit id="ScQhV.o" name="assembly.bom_import.flash.invalid_entries">
<segment>
<source>assembly.bom_import.flash.invalid_entries</source>
<target>Chyba ověření! Zkontrolujte svůj importovaný soubor!</target>
</segment>
</unit>
<unit id="luj_uCZ" name="assembly.bom_import.flash.invalid_file">
<segment>
<source>assembly.bom_import.flash.invalid_file</source>
<target>Soubor nelze importovat. Zkontrolujte, zda jste vybrali správný typ souboru. Chybová zpráva: %message%</target>
</segment>
</unit>
<unit id="u7SYWcB" name="assembly.bom.quantity">
<segment>
<source>assembly.bom.quantity</source>
<target>Množství</target>
</segment>
</unit>
<unit id="D7dPvPL" name="assembly.bom.mountnames">
<segment>
<source>assembly.bom.mountnames</source>
<target>Názvy osazení</target>
</segment>
</unit>
<unit id="rswC4eS" name="assembly.bom.instockAmount">
<segment state="translated">
<source>assembly.bom.instockAmount</source>
<target>Stav na skladě</target>
</segment>
</unit>
<unit id="oNutri3" name="assembly.info.title">
<segment>
<source>assembly.info.title</source>
<target>Info o sestavě</target>
</segment>
</unit>
<unit id="aO1rzVQ" name="assembly.info.info.label">
<segment>
<source>assembly.info.info.label</source>
<target>Informace</target>
</segment>
</unit>
<unit id="9dOByT_" name="assembly.info.sub_assemblies.label">
<segment>
<source>assembly.info.sub_assemblies.label</source>
<target>Podskupina</target>
</segment>
</unit>
<unit id="Hmf0EwN" name="assembly.info.builds.label">
<segment>
<source>assembly.info.builds.label</source>
<target>Sestavení</target>
</segment>
</unit>
<unit id="z3F4Rcu" name="assembly.info.bom_add_parts">
<segment>
<source>assembly.info.bom_add_parts</source>
<target>Přidat součásti</target>
</segment>
</unit>
<unit id="ZbDTUTS" name="assembly.builds.check_assembly_status">
<segment>
<source>assembly.builds.check_assembly_status</source>
<target><![CDATA[Aktuální stav sestavy je <b>"%assembly_status%"</b>. Měli byste zkontrolovat, zda opravdu chcete sestavu postavit s tímto stavem!]]></target>
</segment>
</unit>
<unit id="xq1Soad" name="assembly.builds.build_not_possible">
<segment>
<source>assembly.builds.build_not_possible</source>
<target>Sestavení není možné: Nedostatek součástí</target>
</segment>
</unit>
<unit id="njRyDHQ" name="assembly.builds.following_bom_entries_miss_instock">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock</source>
<target>Není dostatek součástí na skladě pro postavení tohoto projektu %number_of_builds% krát. Následující součásti nejsou skladem v dostatečném množství.</target>
</segment>
</unit>
<unit id="sp7Z0MS" name="assembly.builds.build_possible">
<segment>
<source>assembly.builds.build_possible</source>
<target>Sestavení je možné</target>
</segment>
</unit>
<unit id="1uZzdEl" name="assembly.builds.number_of_builds_possible">
<segment>
<source>assembly.builds.number_of_builds_possible</source>
<target><![CDATA[Máte dostatek součástí na skladě k vytvoření <b>%max_builds%</b> kusů této sestavy.]]></target>
</segment>
</unit>
<unit id="pW6QLXf" name="assembly.builds.number_of_builds">
<segment>
<source>assembly.builds.number_of_builds</source>
<target>Počet sestavení</target>
</segment>
</unit>
<unit id="mtJqT_a" name="assembly.build.btn_build">
<segment>
<source>assembly.build.btn_build</source>
<target>Sestavit</target>
</segment>
</unit>
<unit id="LFSVVcP" name="assembly.builds.no_stocked_builds">
<segment>
<source>assembly.builds.no_stocked_builds</source>
<target>Počet skladovaných vyrobených instancí</target>
</segment>
</unit>
<unit id="dGFHutJ" name="assembly.info.bom_entries_count">
<segment>
<source>assembly.info.bom_entries_count</source>
<target>Součásti</target>
</segment>
</unit>
<unit id="xJ7oBM4" name="assembly.info.sub_assemblies_count">
<segment>
<source>assembly.info.sub_assemblies_count</source>
<target>Podskupiny</target>
</segment>
</unit>
<unit id="HZYhTlb" name="assembly.builds.stocked">
<segment>
<source>assembly.builds.stocked</source>
<target>skladem</target>
</segment>
</unit>
<unit id="9EG0PLW" name="assembly.builds.needed">
<segment>
<source>assembly.builds.needed</source>
<target>potřebné</target>
</segment>
</unit>
<unit id="tgs_7u9" name="assembly.add_parts_to_assembly">
<segment>
<source>assembly.add_parts_to_assembly</source>
<target>Přidat součásti do sestavy</target>
</segment>
</unit>
<unit id="PPsM0Dg" name="assembly.bom.name">
<segment>
<source>assembly.bom.name</source>
<target>Název</target>
</segment>
</unit>
<unit id="nUEs.ld" name="assembly.bom.comment">
<segment>
<source>assembly.bom.comment</source>
<target>Poznámky</target>
</segment>
</unit>
<unit id="87YpQ_u" name="assembly.builds.following_bom_entries_miss_instock_n">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock_n</source>
<target>Není dostatek součástí na skladě pro sestavení této sestavy %number_of_builds% krát. Následující součásti nejsou skladem:</target>
</segment>
</unit>
<unit id="JlQhDdS" name="assembly.build.help">
<segment>
<source>assembly.build.help</source>
<target>Vyberte, ze kterých zásob se mají brát potřebné součásti pro sestavení (a v jakém množství). Zaškrtněte políčko u každého dílu, pokud jste jej odebrali, nebo použijte horní políčko k výběru všech naráz.</target>
</segment>
</unit>
<unit id="iP5_QVj" name="assembly.build.required_qty">
<segment>
<source>assembly.build.required_qty</source>
<target>Požadované množství</target>
</segment>
</unit>
<unit id="UJpD7n6" name="assembly.import_bom">
<segment>
<source>assembly.import_bom</source>
<target>Importovat součásti do sestavy</target>
</segment>
</unit>
<unit id="WTasGao" name="assembly.bom.part">
<segment>
<source>assembly.bom.part</source>
<target>Součást</target>
</segment>
</unit>
<unit id="jHKh8Zp" name="assembly.bom.add_entry">
<segment>
<source>assembly.bom.add_entry</source>
<target>Přidat položku</target>
</segment>
</unit>
<unit id="RsZ77df" name="assembly.bom.price">
<segment>
<source>assembly.bom.price</source>
<target>Cena</target>
</segment>
</unit>
<unit id="63adIrC" name="assembly.build.dont_check_quantity">
<segment state="translated">
<source>assembly.build.dont_check_quantity</source>
<target>Neověřovat množství</target>
</segment>
</unit>
<unit id="O0DP6tK" name="assembly.build.dont_check_quantity.help">
<segment state="translated">
<source>assembly.build.dont_check_quantity.help</source>
<target>Pokud je tato volba vybrána, budou vybraná množství odstraněna ze skladu bez ohledu na to, zda je méně nebo více součástí, než je skutečně potřeba pro sestavení sestavy.</target>
</segment>
</unit>
<unit id="9bc0nzK" name="assembly.build.add_builds_to_builds_part">
<segment state="translated">
<source>assembly.build.add_builds_to_builds_part</source>
<target>Přidat vyrobené instance do součásti sestavy</target>
</segment>
</unit>
<unit id="nl.jtSx" name="assembly.bom_import.type">
<segment state="translated">
<source>assembly.bom_import.type</source>
<target>Typ</target>
</segment>
</unit>
<unit id="LtbSLHx" name="assembly.bom_import.type.json">
<segment state="translated">
<source>assembly.bom_import.type.json</source>
<target>JSON pro sestavu</target>
</segment>
</unit>
<unit id="clXFAdN" name="assembly.bom_import.type.kicad_pcbnew">
<segment state="translated">
<source>assembly.bom_import.type.kicad_pcbnew</source>
<target>CSV (KiCAD Pcbnew BOM)</target>
</segment>
</unit>
<unit id="0IekETE" name="assembly.bom_import.clear_existing_bom">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom</source>
<target>Smazat existující položky před importem</target>
</segment>
</unit>
<unit id="S4QY6pA" name="assembly.bom_import.clear_existing_bom.help">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom.help</source>
<target>Pokud je tato možnost vybrána, budou všechny již existující součásti sestavy smazány a nahrazeny importovanými daty součástí.</target>
</segment>
</unit>
<unit id="RVNWPsh" name="assembly.import_bom.template.header.json">
<segment>
<source>assembly.import_bom.template.header.json</source>
<target>Šablona importu JSON pro sestavu</target>
</segment>
</unit>
<unit id="eU4FfCr" name="assembly.import_bom.template.header.kicad_pcbnew">
<segment>
<source>assembly.import_bom.template.header.kicad_pcbnew</source>
<target>Šablona importu CSV (KiCAD Pcbnew BOM) pro sestavu</target>
</segment>
</unit>
<unit id="aLomVVS" name="assembly.bom_import.template.entry.name">
<segment>
<source>assembly.bom_import.template.entry.name</source>
<target>Název součásti v sestavě</target>
</segment>
</unit>
<unit id="o4ZcLfV" name="assembly.bom_import.template.entry.part.mpnr">
<segment>
<source>assembly.bom_import.template.entry.part.mpnr</source>
<target>Unikátní číslo produktu u výrobce</target>
</segment>
</unit>
<unit id="n3YbKeU" name="assembly.bom_import.template.entry.part.ipn">
<segment>
<source>assembly.bom_import.template.entry.part.ipn</source>
<target>Unikátní IPN součásti</target>
</segment>
</unit>
<unit id="l2SYJP5" name="assembly.bom_import.template.entry.part.name">
<segment>
<source>assembly.bom_import.template.entry.part.name</source>
<target>Unikátní název součásti</target>
</segment>
</unit>
<unit id="VgXQ1xW" name="assembly.bom_import.template.entry.part.manufacturer.name">
<segment>
<source>assembly.bom_import.template.entry.part.manufacturer.name</source>
<target>Unikátní jméno výrobce</target>
</segment>
</unit>
<unit id="MpB.o_L" name="assembly.bom_import.template.entry.part.category.name">
<segment>
<source>assembly.bom_import.template.entry.part.category.name</source>
<target>Unikátní název kategorie</target>
</segment>
</unit>
<unit id="NIcfgj84" name="assembly.bom_import.template.json.table">
<segment>
<source>assembly.bom_import.template.json.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Pole</th>
<th>Podmínka</th>
<th>Datový typ</th>
<th>Popis</th>
</tr>
</thead>
<tbody>
<tr>
<td>quantity</td>
<td>Povinné</td>
<td>Číslo s plovoucí desetinnou čárkou (Float)</td>
<td>Musí být uvedeno a obsahovat hodnotu s plovoucí desetinnou čárkou (Float) větší než 0,0.</td>
</tr>
<tr>
<td>name</td>
<td>Volitelné</td>
<td>Řetězec (String)</td>
<td>Pokud je přítomen, musí být neprázdný řetězec.</td>
</tr>
<tr>
<td>part</td>
<td>Volitelné</td>
<td>Objekt/Array</td>
<td>
Pokud je uvedeno, musí to být objekt/array a minimálně jedno pole musí být vyplněno:
<ul>
<li>part.id</li>
<li>part.name</li>
</ul>
</td>
</tr>
<tr>
<td>part.id</td>
<td>Volitelné</td>
<td>Celé číslo (Integer)</td>
<td>Celé číslo (Integer) &gt; 0. Odpovídá internímu číselnému ID součástky v Part-DB.</td>
</tr>
<tr>
<td>part.name</td>
<td>Volitelné</td>
<td>Řetězec (String)</td>
<td>Neprázdný řetězec, pokud není zadáno part.mpnr nebo part.ipn.</td>
</tr>
<tr>
<td>part.mpnr</td>
<td>Volitelné</td>
<td>Řetězec (String)</td>
<td>Neprázdný řetězec, pokud není zadáno part.name nebo part.ipn.</td>
</tr>
<tr>
<td>part.ipn</td>
<td>Volitelné</td>
<td>Řetězec (String)</td>
<td>Neprázdný řetězec, pokud není zadáno part.name nebo part.mpnr.</td>
</tr>
<tr>
<td>part.description</td>
<td>Volitelné</td>
<td>Řetězec nebo null</td>
<td>Pokud je přítomen, musí být neprázdný řetězec nebo null.</td>
</tr>
<tr>
<td>part.manufacturer</td>
<td>Volitelné</td>
<td>Objekt/Array</td>
<td>
Pokud je přítomen, musí to být objekt/array a minimálně jedno pole musí být vyplněno:
<ul>
<li>manufacturer.id</li>
<li>manufacturer.name</li>
</ul>
</td>
</tr>
<tr>
<td>manufacturer.id</td>
<td>Volitelné</td>
<td>Celé číslo (Integer)</td>
<td>Celé číslo (Integer) &gt; 0. Odpovídá internímu číselnému ID výrobce.</td>
</tr>
<tr>
<td>manufacturer.name</td>
<td>Volitelné</td>
<td>Řetězec (String)</td>
<td>Neprázdný řetězec, pokud není uvedeno manufacturer.id.</td>
</tr>
<tr>
<td>part.category</td>
<td>Volitelné</td>
<td>Objekt/Array</td>
<td>
Pokud je přítomen, musí to být objekt/array a minimálně jedno pole musí být vyplněno:
<ul>
<li>category.id</li>
<li>category.name</li>
</ul>
</td>
</tr>
<tr>
<td>category.id</td>
<td>Volitelné</td>
<td>Celé číslo (Integer)</td>
<td>Celé číslo (Integer) &gt; 0. Odpovídá internímu číselnému ID kategorie součástky.</td>
</tr>
<tr>
<td>category.name</td>
<td>Volitelné</td>
<td>Řetězec (String)</td>
<td>Neprázdný řetězec, pokud není uvedeno category.id.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="cU1bfDa" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns</source>
<target>Očekávané sloupce:</target>
</segment>
</unit>
<unit id="gvaB1sb" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns.note">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns.note</source>
<target>
<![CDATA[
<p><strong>Poznámka:</strong> Neprobíhá přiřazení ke konkrétním součástem ze správy kategorií.</p>
]]>
</target>
</segment>
</unit>
<unit id="translationUnit1" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Pole</th>
<th>Podmínka</th>
<th>Datový typ</th>
<th>Popis</th>
</tr>
</thead>
<tbody>
<tr>
<td>Id</td>
<td>Volitelný</td>
<td>Celé číslo (Integer)</td>
<td>Volný údaj. Jedinečné identifikační číslo pro každou součástku.</td>
</tr>
<tr>
<td>Designator</td>
<td>Volitelný</td>
<td>Řetězec (String)</td>
<td>Volný údaj. Jedinečný referenční označovač součástky na desce plošných spojů, např. „R1“ pro rezistor 1. Používá se pro název osazení součástky v rámci skupiny součástek.</td>
</tr>
<tr>
<td>Package</td>
<td>Volitelný</td>
<td>Řetězec (String)</td>
<td>Volný údaj. Pouzdro nebo tvar součástky, např. „0805“ pro SMD rezistory.</td>
</tr>
<tr>
<td>Množství</td>
<td>Povinný</td>
<td>Celé číslo (Integer)</td>
<td>Počet identických součástek, které jsou potřeba k vytvoření jedné instance sestavy.</td>
</tr>
<tr>
<td>Určení</td>
<td>Povinný</td>
<td>Řetězec (String)</td>
<td>Popis nebo funkce součástky, např. hodnota rezistoru „10kΩ“ nebo hodnota kondenzátoru „100nF“. Používá se pro název položky v BOM.</td>
</tr>
<tr>
<td>Dodavatel a ref</td>
<td>Volitelný</td>
<td>Řetězec (String)</td>
<td>Volný údaj. Může obsahovat např. specifické údaje distributora.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="QiZM3zY" name="typeahead.parts.part.name">
<segment>
<source>typeahead.parts.part.name</source>
<target>%name% (součást)</target>
</segment>
</unit>
<unit id="WhYL2yX" name="typeahead.parts.assembly.name">
<segment>
<source>typeahead.parts.assembly.name</source>
<target>%name% (sestava)</target>
</segment>
</unit>
<unit id="4cgba2c" name="projects.build.form.part">
<segment>
<source>projects.build.form.part</source>
<target>Součást "%name%"</target>
</segment>
</unit>
<unit id="1bCA1zb" name="projects.build.form.assembly">
<segment>
<source>projects.build.form.assembly</source>
<target>Sestava "%name%"</target>
</segment>
</unit>
<unit id="2cDB2ac" name="projects.build.form.assembly.bom.entry">
<segment>
<source>projects.build.form.assembly.bom.entry</source>
<target>%name% (potřebné množství: %quantity%)</target>
</segment>
</unit>
<unit id="3dEC3bd" name="projects.build.form.assembly.bom.entry.no.stock">
<segment>
<source>projects.build.form.assembly.bom.entry.no.stock</source>
<target>není skladem</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -4748,6 +4748,18 @@ Bemærk også, at uden to-faktor-godkendelse er din konto ikke længere så godt
<target>Navn</target>
</segment>
</unit>
<unit id="KJuXVR5" name="project.bom.assembly">
<segment state="translated">
<source>project.bom.assembly</source>
<target>Montering</target>
</segment>
</unit>
<unit id="LKvYWS6" name="project.bom.partOrAssembly">
<segment state="translated">
<source>project.bom.partOrAssembly</source>
<target>Valg</target>
</segment>
</unit>
<unit id="eshqdG." name="part.table.id">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:178</note>
@ -9812,6 +9824,18 @@ Element 3</target>
<target>Komponent</target>
</segment>
</unit>
<unit id="KJuXVR5" name="project.bom.assembly">
<segment state="translated">
<source>project.bom.assembly</source>
<target>Baugruppe</target>
</segment>
</unit>
<unit id="LKvYWS6" name="project.bom.partOrAssembly">
<segment state="translated">
<source>project.bom.partOrAssembly</source>
<target>Auswahl</target>
</segment>
</unit>
<unit id="apnWXEq" name="project.bom.add_entry">
<segment state="translated">
<source>project.bom.add_entry</source>
@ -9890,6 +9914,42 @@ Element 3</target>
<target>Arkiveret</target>
</segment>
</unit>
<unit id="hIIFtI1" name="assembly.edit.status">
<segment state="translated">
<source>assembly.edit.status</source>
<target>Status</target>
</segment>
</unit>
<unit id="zRd.psv" name="assembly.status.draft">
<segment state="translated">
<source>assembly.status.draft</source>
<target>Kladde</target>
</segment>
</unit>
<unit id="_U8bb1t" name="assembly.status.planning">
<segment state="translated">
<source>assembly.status.planning</source>
<target>Under planlægning</target>
</segment>
</unit>
<unit id="GgUh7RT" name="assembly.status.in_production">
<segment state="translated">
<source>assembly.status.in_production</source>
<target>I produktion</target>
</segment>
</unit>
<unit id="IluD8iU" name="assembly.status.finished">
<segment state="translated">
<source>assembly.status.finished</source>
<target>Ophørt</target>
</segment>
</unit>
<unit id="Mybkd1s" name="assembly.status.archived">
<segment state="translated">
<source>assembly.status.archived</source>
<target>Arkiveret</target>
</segment>
</unit>
<unit id="jcf.5wX" name="part.new_build_part.error.build_part_already_exists">
<segment state="translated">
<source>part.new_build_part.error.build_part_already_exists</source>
@ -10166,6 +10226,12 @@ Element 3</target>
<target>På lager</target>
</segment>
</unit>
<unit id="gHU1vgc" name="project.builds.no_stock">
<segment state="translated">
<source>project.builds.no_stock</source>
<target>intet lager angivet</target>
</segment>
</unit>
<unit id="mwL3d70" name="project.builds.needed">
<segment state="translated">
<source>project.builds.needed</source>
@ -10238,6 +10304,12 @@ Element 3</target>
<target>Mål mængde</target>
</segment>
</unit>
<unit id="OsmK1Iv" name="project.build.builds_part_lot_label">
<segment state="translated">
<source>project.build.builds_part_lot_label</source>
<target>%name% (%quantity% påkrævet)</target>
</segment>
</unit>
<unit id="pyv0k6b" name="project.builds.number_of_builds">
<segment state="translated">
<source>project.builds.number_of_builds</source>
@ -12196,5 +12268,633 @@ Bemærk venligst, at du ikke kan kopiere fra deaktiveret bruger. Hvis du prøver
<target>Du forsøgte at fjerne/tilføje en mængde sat til nul! Der blev ikke foretaget nogen handling.</target>
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Del)</target>
</segment>
</unit>
<unit id="GW8ZOX7" name="part.table.name.value.for_assembly">
<segment state="translated">
<source>part.table.name.value.for_assembly</source>
<target>%value% (Samlingsenhed)</target>
</segment>
</unit>
<unit id="G2sXzh7" name="assembly.label">
<segment>
<source>assembly.label</source>
<target>Samling</target>
</segment>
</unit>
<unit id="dL51AVa" name="assembly.caption">
<segment>
<source>assembly.caption</source>
<target>Samling</target>
</segment>
</unit>
<unit id="G_rlE3w" name="perm.assemblies">
<segment>
<source>perm.assemblies</source>
<target>Samlinger</target>
</segment>
</unit>
<unit id="dipIGt4" name="assembly_bom_entry.label">
<segment>
<source>assembly_bom_entry.label</source>
<target>Komponenter</target>
</segment>
</unit>
<unit id="TvHlYfl" name="assembly.labelp">
<segment>
<source>assembly.labelp</source>
<target>Samlinger</target>
</segment>
</unit>
<unit id="gyRGdfv" name="assembly.edit">
<segment>
<source>assembly.edit</source>
<target>Rediger samling</target>
</segment>
</unit>
<unit id="4Tdtoj_" name="assembly.new">
<segment>
<source>assembly.new</source>
<target>Ny samling</target>
</segment>
</unit>
<unit id="M51YUE." name="assembly.edit.associated_build_part">
<segment>
<source>assembly.edit.associated_build_part</source>
<target>Tilknyttet komponent</target>
</segment>
</unit>
<unit id="nH9R9f." name="assembly.edit.associated_build_part.add">
<segment>
<source>assembly.edit.associated_build_part.add</source>
<target>Tilføj komponent</target>
</segment>
</unit>
<unit id="oVfOk.i" name="assembly.edit.associated_build.hint">
<segment>
<source>assembly.edit.associated_build.hint</source>
<target>Denne komponent repræsenterer de fremstillede instanser af samlingen. Angiv, hvis fremstillede instanser er påkrævet. Hvis ikke, vil antallet af komponenter først blive anvendt ved opbygning af det pågældende projekt.</target>
</segment>
</unit>
<unit id="_wZ_JZY" name="assembly.edit.bom.import_bom">
<segment>
<source>assembly.edit.bom.import_bom</source>
<target>Importér komponenter</target>
</segment>
</unit>
<unit id="vsmgKMw" name="log.database_updated.failed">
<segment>
<source>log.database_updated.failed</source>
<target>__log.database_updated.failed</target>
</segment>
</unit>
<unit id="GcQrTTE" name="log.database_updated.old_version">
<segment>
<source>log.database_updated.old_version</source>
<target>__log.database_updated.old_version</target>
</segment>
</unit>
<unit id="JJF47vK" name="log.database_updated.new_version">
<segment>
<source>log.database_updated.new_version</source>
<target>__log.database_updated.new_version</target>
</segment>
</unit>
<unit id="iVHS_sh" name="tree.tools.edit.assemblies">
<segment>
<source>tree.tools.edit.assemblies</source>
<target>Samlinger</target>
</segment>
</unit>
<unit id="naAMjcH" name="assembly.bom_import.flash.success">
<segment>
<source>assembly.bom_import.flash.success</source>
<target>%count% komponent(er) blev importeret til samlingen med succes.</target>
</segment>
</unit>
<unit id="ScQhV.o" name="assembly.bom_import.flash.invalid_entries">
<segment>
<source>assembly.bom_import.flash.invalid_entries</source>
<target>Valideringsfejl! Kontroller venligst den importerede fil!</target>
</segment>
</unit>
<unit id="luj_uCZ" name="assembly.bom_import.flash.invalid_file">
<segment>
<source>assembly.bom_import.flash.invalid_file</source>
<target>Filen kunne ikke importeres. Kontrollér, at du har valgt den korrekte filtype. Fejlmeddelelse: %message%</target>
</segment>
</unit>
<unit id="u7SYWcB" name="assembly.bom.quantity">
<segment>
<source>assembly.bom.quantity</source>
<target>Mængde</target>
</segment>
</unit>
<unit id="D7dPvPL" name="assembly.bom.mountnames">
<segment>
<source>assembly.bom.mountnames</source>
<target>Monteringsnavne</target>
</segment>
</unit>
<unit id="rswC4eS" name="assembly.bom.instockAmount">
<segment state="translated">
<source>assembly.bom.instockAmount</source>
<target>Antal på lager</target>
</segment>
</unit>
<unit id="oNutri3" name="assembly.info.title">
<segment>
<source>assembly.info.title</source>
<target>Samleinfo</target>
</segment>
</unit>
<unit id="aO1rzVQ" name="assembly.info.info.label">
<segment>
<source>assembly.info.info.label</source>
<target>Info</target>
</segment>
</unit>
<unit id="9dOByT_" name="assembly.info.sub_assemblies.label">
<segment>
<source>assembly.info.sub_assemblies.label</source>
<target>Undergruppe</target>
</segment>
</unit>
<unit id="Hmf0EwN" name="assembly.info.builds.label">
<segment>
<source>assembly.info.builds.label</source>
<target>Byggeri</target>
</segment>
</unit>
<unit id="z3F4Rcu" name="assembly.info.bom_add_parts">
<segment>
<source>assembly.info.bom_add_parts</source>
<target>Tilføj dele</target>
</segment>
</unit>
<unit id="ZbDTUTS" name="assembly.builds.check_assembly_status">
<segment>
<source>assembly.builds.check_assembly_status</source>
<target><![CDATA[Den aktuelle samlingsstatus er <b>"%assembly_status%"</b>. Du bør kontrollere, om du virkelig ønsker at bygge samlingen med denne status!]]></target>
</segment>
</unit>
<unit id="xq1Soad" name="assembly.builds.build_not_possible">
<segment>
<source>assembly.builds.build_not_possible</source>
<target>Opbygning ikke mulig: Ikke nok komponenter til rådighed</target>
</segment>
</unit>
<unit id="njRyDHQ" name="assembly.builds.following_bom_entries_miss_instock">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock</source>
<target>Der er ikke nok dele på lager til at bygge dette projekt %number_of_builds% gange. Følgende dele mangler på lager:</target>
</segment>
</unit>
<unit id="sp7Z0MS" name="assembly.builds.build_possible">
<segment>
<source>assembly.builds.build_possible</source>
<target>Byggeri muligt</target>
</segment>
</unit>
<unit id="1uZzdEl" name="assembly.builds.number_of_builds_possible">
<segment>
<source>assembly.builds.number_of_builds_possible</source>
<target><![CDATA[Du har nok dele på lager til at bygge <b>%max_builds%</b> eksemplarer af denne samling.]]></target>
</segment>
</unit>
<unit id="pW6QLXf" name="assembly.builds.number_of_builds">
<segment>
<source>assembly.builds.number_of_builds</source>
<target>Antal opbygninger</target>
</segment>
</unit>
<unit id="mtJqT_a" name="assembly.build.btn_build">
<segment>
<source>assembly.build.btn_build</source>
<target>Byg</target>
</segment>
</unit>
<unit id="LFSVVcP" name="assembly.builds.no_stocked_builds">
<segment>
<source>assembly.builds.no_stocked_builds</source>
<target>Antal lagrede byggede enheder</target>
</segment>
</unit>
<unit id="dGFHutJ" name="assembly.info.bom_entries_count">
<segment>
<source>assembly.info.bom_entries_count</source>
<target>Komponenter</target>
</segment>
</unit>
<unit id="xJ7oBM4" name="assembly.info.sub_assemblies_count">
<segment>
<source>assembly.info.sub_assemblies_count</source>
<target>Undergrupper</target>
</segment>
</unit>
<unit id="HZYhTlb" name="assembly.builds.stocked">
<segment>
<source>assembly.builds.stocked</source>
<target>på lager</target>
</segment>
</unit>
<unit id="9EG0PLW" name="assembly.builds.needed">
<segment>
<source>assembly.builds.needed</source>
<target>nødvendig</target>
</segment>
</unit>
<unit id="tgs_7u9" name="assembly.add_parts_to_assembly">
<segment>
<source>assembly.add_parts_to_assembly</source>
<target>Tilføj dele til samlingen</target>
</segment>
</unit>
<unit id="PPsM0Dg" name="assembly.bom.name">
<segment>
<source>assembly.bom.name</source>
<target>Navn</target>
</segment>
</unit>
<unit id="nUEs.ld" name="assembly.bom.comment">
<segment>
<source>assembly.bom.comment</source>
<target>Notater</target>
</segment>
</unit>
<unit id="87YpQ_u" name="assembly.builds.following_bom_entries_miss_instock_n">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock_n</source>
<target>Der er ikke nok dele på lager til at bygge denne samling %number_of_builds% gange. Følgende dele mangler på lager:</target>
</segment>
</unit>
<unit id="JlQhDdS" name="assembly.build.help">
<segment>
<source>assembly.build.help</source>
<target>Vælg, hvilke lagre de nødvendige dele til bygningen skal tages fra (og i hvilken mængde). Marker afkrydsningsfeltet for hver delpost, når du har fjernet delene, eller brug det øverste afkrydsningsfelt for at markere alle på én gang.</target>
</segment>
</unit>
<unit id="iP5_QVj" name="assembly.build.required_qty">
<segment>
<source>assembly.build.required_qty</source>
<target>Krævet antal</target>
</segment>
</unit>
<unit id="UJpD7n6" name="assembly.import_bom">
<segment>
<source>assembly.import_bom</source>
<target>Importer dele til samling</target>
</segment>
</unit>
<unit id="WTasGao" name="assembly.bom.part">
<segment>
<source>assembly.bom.part</source>
<target>Del</target>
</segment>
</unit>
<unit id="jHKh8Zp" name="assembly.bom.add_entry">
<segment>
<source>assembly.bom.add_entry</source>
<target>Tilføj post</target>
</segment>
</unit>
<unit id="RsZ77df" name="assembly.bom.price">
<segment>
<source>assembly.bom.price</source>
<target>Pris</target>
</segment>
</unit>
<unit id="63adIrC" name="assembly.build.dont_check_quantity">
<segment state="translated">
<source>assembly.build.dont_check_quantity</source>
<target>Tjek ikke mængder</target>
</segment>
</unit>
<unit id="O0DP6tK" name="assembly.build.dont_check_quantity.help">
<segment state="translated">
<source>assembly.build.dont_check_quantity.help</source>
<target>Hvis denne mulighed vælges, fjernes de valgte mængder fra lageret, uanset om der er mere eller mindre end nødvendigt for at bygge samlingen.</target>
</segment>
</unit>
<unit id="9bc0nzK" name="assembly.build.add_builds_to_builds_part">
<segment state="translated">
<source>assembly.build.add_builds_to_builds_part</source>
<target>Tilføj byggede enheder til assemblies del</target>
</segment>
</unit>
<unit id="nl.jtSx" name="assembly.bom_import.type">
<segment state="translated">
<source>assembly.bom_import.type</source>
<target>Type</target>
</segment>
</unit>
<unit id="LtbSLHx" name="assembly.bom_import.type.json">
<segment state="translated">
<source>assembly.bom_import.type.json</source>
<target>JSON for en samling</target>
</segment>
</unit>
<unit id="clXFAdN" name="assembly.bom_import.type.kicad_pcbnew">
<segment state="translated">
<source>assembly.bom_import.type.kicad_pcbnew</source>
<target>CSV (KiCAD Pcbnew BOM)</target>
</segment>
</unit>
<unit id="0IekETE" name="assembly.bom_import.clear_existing_bom">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom</source>
<target>Slet eksisterende poster før import</target>
</segment>
</unit>
<unit id="S4QY6pA" name="assembly.bom_import.clear_existing_bom.help">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom.help</source>
<target>Hvis dette valg er markeret, slettes alle eksisterende komponentposter i samlingen og erstattes med de importerede.</target>
</segment>
</unit>
<unit id="RVNWPsh" name="assembly.import_bom.template.header.json">
<segment>
<source>assembly.import_bom.template.header.json</source>
<target>JSON-importskabelon til en samling</target>
</segment>
</unit>
<unit id="eU4FfCr" name="assembly.import_bom.template.header.kicad_pcbnew">
<segment>
<source>assembly.import_bom.template.header.kicad_pcbnew</source>
<target>Importskabelon CSV (KiCAD Pcbnew BOM) til en samling</target>
</segment>
</unit>
<unit id="aLomVVS" name="assembly.bom_import.template.entry.name">
<segment>
<source>assembly.bom_import.template.entry.name</source>
<target>Delens navn i samlingen</target>
</segment>
</unit>
<unit id="o4ZcLfV" name="assembly.bom_import.template.entry.part.mpnr">
<segment>
<source>assembly.bom_import.template.entry.part.mpnr</source>
<target>Unik produktnummer hos producenten</target>
</segment>
</unit>
<unit id="n3YbKeU" name="assembly.bom_import.template.entry.part.ipn">
<segment>
<source>assembly.bom_import.template.entry.part.ipn</source>
<target>Unik IPN for delen</target>
</segment>
</unit>
<unit id="l2SYJP5" name="assembly.bom_import.template.entry.part.name">
<segment>
<source>assembly.bom_import.template.entry.part.name</source>
<target>Unikt komponentnavn</target>
</segment>
</unit>
<unit id="VgXQ1xW" name="assembly.bom_import.template.entry.part.manufacturer.name">
<segment>
<source>assembly.bom_import.template.entry.part.manufacturer.name</source>
<target>Unikt producenter navn</target>
</segment>
</unit>
<unit id="MpB.o_L" name="assembly.bom_import.template.entry.part.category.name">
<segment>
<source>assembly.bom_import.template.entry.part.category.name</source>
<target>Kategoriens unikke navn</target>
</segment>
</unit>
<unit id="NIcfgj84" name="assembly.bom_import.template.json.table">
<segment>
<source>assembly.bom_import.template.json.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Felt</th>
<th>Betingelse</th>
<th>Datatype</th>
<th>Beskrivelse</th>
</tr>
</thead>
<tbody>
<tr>
<td>quantity</td>
<td>Påkrævet</td>
<td>Flydende tal (Float)</td>
<td>Skal være til stede og indeholde en flydende værdi (Float), der er større end 0,0.</td>
</tr>
<tr>
<td>name</td>
<td>Valgfri</td>
<td>String</td>
<td>Hvis til stede, skal det være en ikke-tom streng.</td>
</tr>
<tr>
<td>part</td>
<td>Valgfri</td>
<td>Objekt/Array</td>
<td>
Hvis angivet, skal det være et objekt/array, og mindst ét af felterne skal udfyldes:
<ul>
<li>part.id</li>
<li>part.name</li>
</ul>
</td>
</tr>
<tr>
<td>part.id</td>
<td>Valgfri</td>
<td>Heltal (Integer)</td>
<td>Heltal (Integer) &gt; 0. Matcher Part-DB's interne numeriske ID for komponenten.</td>
</tr>
<tr>
<td>part.name</td>
<td>Valgfri</td>
<td>String</td>
<td>Ikke-tom streng, hvis part.mpnr eller part.ipn ikke er givet.</td>
</tr>
<tr>
<td>part.mpnr</td>
<td>Valgfri</td>
<td>String</td>
<td>Ikke-tom streng, hvis part.name eller part.ipn ikke er givet.</td>
</tr>
<tr>
<td>part.ipn</td>
<td>Valgfri</td>
<td>String</td>
<td>Ikke-tom streng, hvis part.name eller part.mpnr ikke er givet.</td>
</tr>
<tr>
<td>part.description</td>
<td>Valgfri</td>
<td>String eller null</td>
<td>Hvis til stede, skal det være en ikke-tom streng eller null.</td>
</tr>
<tr>
<td>part.manufacturer</td>
<td>Valgfri</td>
<td>Objekt/Array</td>
<td>
Hvis til stede, skal det være et objekt/array, og mindst ét af felterne skal udfyldes:
<ul>
<li>manufacturer.id</li>
<li>manufacturer.name</li>
</ul>
</td>
</tr>
<tr>
<td>manufacturer.id</td>
<td>Valgfri</td>
<td>Heltal (Integer)</td>
<td>Heltal (Integer) &gt; 0. Matcher producentens interne numeriske ID.</td>
</tr>
<tr>
<td>manufacturer.name</td>
<td>Valgfri</td>
<td>String</td>
<td>Ikke-tom streng, hvis manufacturer.id ikke er givet.</td>
</tr>
<tr>
<td>part.category</td>
<td>Valgfri</td>
<td>Objekt/Array</td>
<td>
Hvis til stede, skal det være et objekt/array, og mindst ét af felterne skal udfyldes:
<ul>
<li>category.id</li>
<li>category.name</li>
</ul>
</td>
</tr>
<tr>
<td>category.id</td>
<td>Valgfri</td>
<td>Heltal (Integer)</td>
<td>Heltal (Integer) &gt; 0. Matcher komponentkategoriens interne numeriske ID.</td>
</tr>
<tr>
<td>category.name</td>
<td>Valgfri</td>
<td>String</td>
<td>Ikke-tom streng, hvis category.id ikke er givet.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="cU1bfDa" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns</source>
<target>Forventede kolonner:</target>
</segment>
</unit>
<unit id="gvaB1sb" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns.note">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns.note</source>
<target>
<![CDATA[
<p><strong>Bemærk:</strong> Der foretages ingen tildelinger til konkrete komponenter fra kategoristyringen.</p>
]]>
</target>
</segment>
</unit>
<unit id="translationUnit2" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Felt</th>
<th>Betingelse</th>
<th>Datatype</th>
<th>Beskrivelse</th>
</tr>
</thead>
<tbody>
<tr>
<td>Id</td>
<td>Valgfri</td>
<td>Heltal (Integer)</td>
<td>Fri oplysning. Et unikt identifikationsnummer for hver komponent.</td>
</tr>
<tr>
<td>Designator</td>
<td>Valgfri</td>
<td>Streng</td>
<td>Fri oplysning. En unik referencebetegnelse for komponenten på printkortet, f.eks. "R1" for modstand 1. Bruges til navngivning af monteringssæt i komponentgruppen.</td>
</tr>
<tr>
<td>Package</td>
<td>Valgfri</td>
<td>Streng</td>
<td>Fri oplysning. Komponentenheden eller -formatet, f.eks. "0805" for SMD-modstande.</td>
</tr>
<tr>
<td>Antal</td>
<td>Påkrævet</td>
<td>Heltal (Integer)</td>
<td>Antallet af identiske komponenter, der kræves for at oprette en enkelt instans af samling.</td>
</tr>
<tr>
<td>Betegnelse</td>
<td>Påkrævet</td>
<td>Streng</td>
<td>Beskrivelse eller funktion for komponenten, f.eks. modstandsværdi "10kΩ" eller kondensatorværdi "100nF". Bruges til navnet i BOM-posten.</td>
</tr>
<tr>
<td>Leverandør og ref</td>
<td>Valgfri</td>
<td>Streng</td>
<td>Fri oplysning. Kan indeholde f.eks. distributørspecifik værdi.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="QiZM3zY" name="typeahead.parts.part.name">
<segment>
<source>typeahead.parts.part.name</source>
<target>%name% (del)</target>
</segment>
</unit>
<unit id="WhYL2yX" name="typeahead.parts.assembly.name">
<segment>
<source>typeahead.parts.assembly.name</source>
<target>%name% (samling)</target>
</segment>
</unit>
<unit id="4cgba2c" name="projects.build.form.part">
<segment>
<source>projects.build.form.part</source>
<target>Del "%name%"</target>
</segment>
</unit>
<unit id="1bCA1zb" name="projects.build.form.assembly">
<segment>
<source>projects.build.form.assembly</source>
<target>Samling "%name%"</target>
</segment>
</unit>
<unit id="2cDB2ac" name="projects.build.form.assembly.bom.entry">
<segment>
<source>projects.build.form.assembly.bom.entry</source>
<target>%name% (%quantity% nødvendig)</target>
</segment>
</unit>
<unit id="3dEC3bd" name="projects.build.form.assembly.bom.entry.no.stock">
<segment>
<source>projects.build.form.assembly.bom.entry.no.stock</source>
<target>ikke på lager</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -4740,6 +4740,18 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr
<target>Name</target>
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Bauteil)</target>
</segment>
</unit>
<unit id="GW8ZOX7" name="part.table.name.value.for_assembly">
<segment state="translated">
<source>part.table.name.value.for_assembly</source>
<target>%value% (Baugruppe)</target>
</segment>
</unit>
<unit id="rW_SFJE" name="part.table.id">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:178</note>
@ -9860,6 +9872,18 @@ Element 1 -&gt; Element 1.2</target>
<target>Bauteil</target>
</segment>
</unit>
<unit id="KJuXVR5" name="project.bom.assembly">
<segment state="translated">
<source>project.bom.assembly</source>
<target>Baugruppe</target>
</segment>
</unit>
<unit id="LKvYWS6" name="project.bom.partOrAssembly">
<segment state="translated">
<source>project.bom.partOrAssembly</source>
<target>Auswahl</target>
</segment>
</unit>
<unit id="kGqIirz" name="project.bom.add_entry">
<segment state="translated">
<source>project.bom.add_entry</source>
@ -9938,6 +9962,42 @@ Element 1 -&gt; Element 1.2</target>
<target>Archiviert</target>
</segment>
</unit>
<unit id="hIIFtI1" name="assembly.edit.status">
<segment state="translated">
<source>assembly.edit.status</source>
<target>Status</target>
</segment>
</unit>
<unit id="zRd.psv" name="assembly.status.draft">
<segment state="translated">
<source>assembly.status.draft</source>
<target>Entwurf</target>
</segment>
</unit>
<unit id="_U8bb1t" name="assembly.status.planning">
<segment state="translated">
<source>assembly.status.planning</source>
<target>In Planung</target>
</segment>
</unit>
<unit id="GgUh7RT" name="assembly.status.in_production">
<segment state="translated">
<source>assembly.status.in_production</source>
<target>In Produktion</target>
</segment>
</unit>
<unit id="IluD8iU" name="assembly.status.finished">
<segment state="translated">
<source>assembly.status.finished</source>
<target>Abgeschlossen</target>
</segment>
</unit>
<unit id="Mybkd1s" name="assembly.status.archived">
<segment state="translated">
<source>assembly.status.archived</source>
<target>Archiviert</target>
</segment>
</unit>
<unit id="9GtmqC1" name="part.new_build_part.error.build_part_already_exists">
<segment state="translated">
<source>part.new_build_part.error.build_part_already_exists</source>
@ -10214,6 +10274,12 @@ Element 1 -&gt; Element 1.2</target>
<target>vorhanden</target>
</segment>
</unit>
<unit id="gHU1vgc" name="project.builds.no_stock">
<segment state="translated">
<source>project.builds.no_stock</source>
<target>kein Lager angegeben</target>
</segment>
</unit>
<unit id="s5DQlqF" name="project.builds.needed">
<segment state="translated">
<source>project.builds.needed</source>
@ -10286,6 +10352,12 @@ Element 1 -&gt; Element 1.2</target>
<target>Ziel-Bestand</target>
</segment>
</unit>
<unit id="OsmK1Iv" name="project.build.builds_part_lot_label">
<segment state="translated">
<source>project.build.builds_part_lot_label</source>
<target>%name% (%quantity% benötigt)</target>
</segment>
</unit>
<unit id="5DTAvWG" name="project.builds.number_of_builds">
<segment state="translated">
<source>project.builds.number_of_builds</source>
@ -12929,6 +13001,622 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön
<target>Externe Version anzeigen</target>
</segment>
</unit>
<unit id="G2sXzh7" name="assembly.label">
<segment>
<source>assembly.label</source>
<target>Baugruppe</target>
</segment>
</unit>
<unit id="dL51AVa" name="assembly.caption">
<segment>
<source>assembly.caption</source>
<target>Baugruppe</target>
</segment>
</unit>
<unit id="G_rlE3w" name="perm.assemblies">
<segment>
<source>perm.assemblies</source>
<target>Baugruppen</target>
</segment>
</unit>
<unit id="dipIGt4" name="assembly_bom_entry.label">
<segment>
<source>assembly_bom_entry.label</source>
<target>Bauteile</target>
</segment>
</unit>
<unit id="TvHlYfl" name="assembly.labelp">
<segment>
<source>assembly.labelp</source>
<target>Baugruppen</target>
</segment>
</unit>
<unit id="gyRGdfv" name="assembly.edit">
<segment>
<source>assembly.edit</source>
<target>Bearbeite Baugruppe</target>
</segment>
</unit>
<unit id="4Tdtoj_" name="assembly.new">
<segment>
<source>assembly.new</source>
<target>Neue Baugruppe</target>
</segment>
</unit>
<unit id="M51YUE." name="assembly.edit.associated_build_part">
<segment>
<source>assembly.edit.associated_build_part</source>
<target>Verknüpftes Bauteil</target>
</segment>
</unit>
<unit id="nH9R9f." name="assembly.edit.associated_build_part.add">
<segment>
<source>assembly.edit.associated_build_part.add</source>
<target>Bauteil hinzufügen</target>
</segment>
</unit>
<unit id="oVfOk.i" name="assembly.edit.associated_build.hint">
<segment>
<source>assembly.edit.associated_build.hint</source>
<target>Dieses Bauteil repräsentiert die gebauten Instanzen der Baugruppe. Anzugeben, sofern gebaute Instanzen benötigt werden. Wenn nein, werden die Stückzahlen bzgl. der Baugruppe erst beim Build des jeweiligen Projektes herangezogen.</target>
</segment>
</unit>
<unit id="_wZ_JZY" name="assembly.edit.bom.import_bom">
<segment>
<source>assembly.edit.bom.import_bom</source>
<target>Bauteile importieren</target>
</segment>
</unit>
<unit id="vsmgKMw" name="log.database_updated.failed">
<segment>
<source>log.database_updated.failed</source>
<target>__log.database_updated.failed</target>
</segment>
</unit>
<unit id="GcQrTTE" name="log.database_updated.old_version">
<segment>
<source>log.database_updated.old_version</source>
<target>__log.database_updated.old_version</target>
</segment>
</unit>
<unit id="JJF47vK" name="log.database_updated.new_version">
<segment>
<source>log.database_updated.new_version</source>
<target>__log.database_updated.new_version</target>
</segment>
</unit>
<unit id="iVHS_sh" name="tree.tools.edit.assemblies">
<segment>
<source>tree.tools.edit.assemblies</source>
<target>Baugruppen</target>
</segment>
</unit>
<unit id="naAMjcH" name="assembly.bom_import.flash.success">
<segment>
<source>assembly.bom_import.flash.success</source>
<target>%count% Part Einträge erfolgreich in Baugruppe importiert.</target>
</segment>
</unit>
<unit id="ScQhV.o" name="assembly.bom_import.flash.invalid_entries">
<segment>
<source>assembly.bom_import.flash.invalid_entries</source>
<target>Validierungsfehler! Bitte überprüfen Sie die importierte Datei!</target>
</segment>
</unit>
<unit id="luj_uCZ" name="assembly.bom_import.flash.invalid_file">
<segment>
<source>assembly.bom_import.flash.invalid_file</source>
<target>Datei konnte nicht importiert werden. Überprüfen Sie, dass Sie den richtigen Dateityp gewählt haben. Fehlermeldung: %message%</target>
</segment>
</unit>
<unit id="u7SYWcB" name="assembly.bom.quantity">
<segment>
<source>assembly.bom.quantity</source>
<target>Menge</target>
</segment>
</unit>
<unit id="D7dPvPL" name="assembly.bom.mountnames">
<segment>
<source>assembly.bom.mountnames</source>
<target>Bestückungsnamen</target>
</segment>
</unit>
<unit id="rswC4eS" name="assembly.bom.instockAmount">
<segment state="translated">
<source>assembly.bom.instockAmount</source>
<target>Bestand im Lager</target>
</segment>
</unit>
<unit id="oNutri3" name="assembly.info.title">
<segment>
<source>assembly.info.title</source>
<target>Baugruppen-Info</target>
</segment>
</unit>
<unit id="aO1rzVQ" name="assembly.info.info.label">
<segment>
<source>assembly.info.info.label</source>
<target>Info</target>
</segment>
</unit>
<unit id="9dOByT_" name="assembly.info.sub_assemblies.label">
<segment>
<source>assembly.info.sub_assemblies.label</source>
<target>Untergruppe</target>
</segment>
</unit>
<unit id="Hmf0EwN" name="assembly.info.builds.label">
<segment>
<source>assembly.info.builds.label</source>
<target>Bau</target>
</segment>
</unit>
<unit id="z3F4Rcu" name="assembly.info.bom_add_parts">
<segment>
<source>assembly.info.bom_add_parts</source>
<target>Bauteile hinzufügen</target>
</segment>
</unit>
<unit id="ZbDTUTS" name="assembly.builds.check_assembly_status">
<segment>
<source>assembly.builds.check_assembly_status</source>
<target><![CDATA[Der aktuelle Baugruppen-Status ist <b>"%assembly_status%"</b>. Sie sollten überprüfen, ob sie die Baugruppe mit diesem Status wirklich bauen wollen!]]></target>
</segment>
</unit>
<unit id="xq1Soad" name="assembly.builds.build_not_possible">
<segment>
<source>assembly.builds.build_not_possible</source>
<target>Bau nicht möglich: Nicht genügend Bauteile vorhanden</target>
</segment>
</unit>
<unit id="njRyDHQ" name="assembly.builds.following_bom_entries_miss_instock">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock</source>
<target>Es sind nicht genügend Bauteile auf Lager, um dieses Projekt %number_of_builds% mal zu bauen. Von folgenden Bauteilen ist nicht genügend auf Lager.</target>
</segment>
</unit>
<unit id="sp7Z0MS" name="assembly.builds.build_possible">
<segment>
<source>assembly.builds.build_possible</source>
<target>Bau möglich</target>
</segment>
</unit>
<unit id="1uZzdEl" name="assembly.builds.number_of_builds_possible">
<segment>
<source>assembly.builds.number_of_builds_possible</source>
<target><![CDATA[Sie haben genug Bauteile auf Lager, um <b>%max_builds%</b> Exemplare dieser Baugruppe zu bauen.]]></target>
</segment>
</unit>
<unit id="pW6QLXf" name="assembly.builds.number_of_builds">
<segment>
<source>assembly.builds.number_of_builds</source>
<target>Zu bauende Anzahl</target>
</segment>
</unit>
<unit id="mtJqT_a" name="assembly.build.btn_build">
<segment>
<source>assembly.build.btn_build</source>
<target>Bauen</target>
</segment>
</unit>
<unit id="LFSVVcP" name="assembly.builds.no_stocked_builds">
<segment>
<source>assembly.builds.no_stocked_builds</source>
<target>Anzahl gelagerter gebauter Instanzen</target>
</segment>
</unit>
<unit id="dGFHutJ" name="assembly.info.bom_entries_count">
<segment>
<source>assembly.info.bom_entries_count</source>
<target>Bauteile</target>
</segment>
</unit>
<unit id="xJ7oBM4" name="assembly.info.sub_assemblies_count">
<segment>
<source>assembly.info.sub_assemblies_count</source>
<target>Untergruppen</target>
</segment>
</unit>
<unit id="HZYhTlb" name="assembly.builds.stocked">
<segment>
<source>assembly.builds.stocked</source>
<target>vorhanden</target>
</segment>
</unit>
<unit id="9EG0PLW" name="assembly.builds.needed">
<segment>
<source>assembly.builds.needed</source>
<target>benötigt</target>
</segment>
</unit>
<unit id="tgs_7u9" name="assembly.add_parts_to_assembly">
<segment>
<source>assembly.add_parts_to_assembly</source>
<target>Bauteile zur Baugruppe hinzufügen</target>
</segment>
</unit>
<unit id="PPsM0Dg" name="assembly.bom.name">
<segment>
<source>assembly.bom.name</source>
<target>Name</target>
</segment>
</unit>
<unit id="nUEs.ld" name="assembly.bom.comment">
<segment>
<source>assembly.bom.comment</source>
<target>Notizen</target>
</segment>
</unit>
<unit id="87YpQ_u" name="assembly.builds.following_bom_entries_miss_instock_n">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock_n</source>
<target>Es sind nicht genügend Bauteile auf Lager, um diese Baugruppe %number_of_builds% mal zu bauen. Von folgenden Bauteilen ist nicht genügend auf Lager:</target>
</segment>
</unit>
<unit id="JlQhDdS" name="assembly.build.help">
<segment>
<source>assembly.build.help</source>
<target>Wählen Sie aus, aus welchen Beständen die zum Bau notwendigen Bauteile genommen werden sollen (und in welcher Anzahl). Setzen Sie den Haken für jeden Part Eintrag, wenn sie die Bauteile entnommen haben, oder nutzen Sie die oberste Checkbox, um alle Haken auf einmal zu setzen.</target>
</segment>
</unit>
<unit id="iP5_QVj" name="assembly.build.required_qty">
<segment>
<source>assembly.build.required_qty</source>
<target>Benötigte Anzahl</target>
</segment>
</unit>
<unit id="UJpD7n6" name="assembly.import_bom">
<segment>
<source>assembly.import_bom</source>
<target>Importiere Parts für Baugruppe</target>
</segment>
</unit>
<unit id="WTasGao" name="assembly.bom.part">
<segment>
<source>assembly.bom.part</source>
<target>Bauteil</target>
</segment>
</unit>
<unit id="jHKh8Zp" name="assembly.bom.add_entry">
<segment>
<source>assembly.bom.add_entry</source>
<target>Eintrag hinzufügen</target>
</segment>
</unit>
<unit id="RsZ77df" name="assembly.bom.price">
<segment>
<source>assembly.bom.price</source>
<target>Preis</target>
</segment>
</unit>
<unit id="63adIrC" name="assembly.build.dont_check_quantity">
<segment state="translated">
<source>assembly.build.dont_check_quantity</source>
<target>Mengen nicht überprüfen</target>
</segment>
</unit>
<unit id="O0DP6tK" name="assembly.build.dont_check_quantity.help">
<segment state="translated">
<source>assembly.build.dont_check_quantity.help</source>
<target>Wenn diese Option gewählt wird, werden die gewählten Mengen aus dem Lager entfernt, egal ob mehr oder weniger Bauteile sind, als für den Bau der Baugruppe eigentlich benötigt werden.</target>
</segment>
</unit>
<unit id="9bc0nzK" name="assembly.build.add_builds_to_builds_part">
<segment state="translated">
<source>assembly.build.add_builds_to_builds_part</source>
<target>Gebaute Instanzen zum Bauteil der Baugruppe hinzufügen</target>
</segment>
</unit>
<unit id="nl.jtSx" name="assembly.bom_import.type">
<segment state="translated">
<source>assembly.bom_import.type</source>
<target>Typ</target>
</segment>
</unit>
<unit id="LtbSLHx" name="assembly.bom_import.type.json">
<segment state="translated">
<source>assembly.bom_import.type.json</source>
<target>JSON für eine Baugruppe</target>
</segment>
</unit>
<unit id="clXFAdN" name="assembly.bom_import.type.kicad_pcbnew">
<segment state="translated">
<source>assembly.bom_import.type.kicad_pcbnew</source>
<target>CSV (KiCAD Pcbnew BOM)</target>
</segment>
</unit>
<unit id="0IekETE" name="assembly.bom_import.clear_existing_bom">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom</source>
<target>Lösche existierende Bauteil-Einträge vor dem Import</target>
</segment>
</unit>
<unit id="S4QY6pA" name="assembly.bom_import.clear_existing_bom.help">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom.help</source>
<target>Wenn diese Option ausgewählt ist, werden alle bereits in der Baugruppe existierenden Bauteile gelöscht und mit den importierten Bauteildaten überschrieben.</target>
</segment>
</unit>
<unit id="RVNWPsh" name="assembly.import_bom.template.header.json">
<segment>
<source>assembly.import_bom.template.header.json</source>
<target>Import-Vorlage JSON für eine Baugruppe</target>
</segment>
</unit>
<unit id="eU4FfCr" name="assembly.import_bom.template.header.kicad_pcbnew">
<segment>
<source>assembly.import_bom.template.header.kicad_pcbnew</source>
<target>Import-Vorlage CSV (KiCAD Pcbnew BOM) für eine Baugruppe</target>
</segment>
</unit>
<unit id="aLomVVS" name="assembly.bom_import.template.entry.name">
<segment>
<source>assembly.bom_import.template.entry.name</source>
<target>Name des Bauteils in der Baugruppe</target>
</segment>
</unit>
<unit id="o4ZcLfV" name="assembly.bom_import.template.entry.part.mpnr">
<segment>
<source>assembly.bom_import.template.entry.part.mpnr</source>
<target>Eindeutige Produktnummer innerhalb des Herstellers</target>
</segment>
</unit>
<unit id="n3YbKeU" name="assembly.bom_import.template.entry.part.ipn">
<segment>
<source>assembly.bom_import.template.entry.part.ipn</source>
<target>Eideutige IPN des Bauteils</target>
</segment>
</unit>
<unit id="l2SYJP5" name="assembly.bom_import.template.entry.part.name">
<segment>
<source>assembly.bom_import.template.entry.part.name</source>
<target>Eindeutiger Name des Bauteils</target>
</segment>
</unit>
<unit id="VgXQ1xW" name="assembly.bom_import.template.entry.part.manufacturer.name">
<segment>
<source>assembly.bom_import.template.entry.part.manufacturer.name</source>
<target>Eindeutiger Name des Herstellers</target>
</segment>
</unit>
<unit id="MpB.o_L" name="assembly.bom_import.template.entry.part.category.name">
<segment>
<source>assembly.bom_import.template.entry.part.category.name</source>
<target>Eindeutiger Name der Kategorie</target>
</segment>
</unit>
<unit id="NIcfgj84" name="assembly.bom_import.template.json.table">
<segment>
<source>assembly.bom_import.template.json.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Feld</th>
<th>Bedingung</th>
<th>Datentyp</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td>quantity</td>
<td>Pflichtfeld</td>
<td>Gleitkommazahl (Float)</td>
<td>Muss gegeben sein und enthält einen Gleitkommawert (Float), der größer als 0.0 ist.</td>
</tr>
<tr>
<td>name</td>
<td>Optional</td>
<td>String</td>
<td>Falls vorhanden, muss es ein nicht-leerer String sein.</td>
</tr>
<tr>
<td>part</td>
<td>Optional</td>
<td>Objekt/Array</td>
<td>
Falls angegeben, muss es ein Objekt/Array sein und mindestens eines der Felder ausgefüllt sein:
<ul>
<li>part.id</li>
<li>part.name</li>
</ul>
</td>
</tr>
<tr>
<td>part.id</td>
<td>Optional</td>
<td>Ganzzahl (Integer)</td>
<td>Ganzzahl (Integer) > 0. Entspricht der Part-DB internen numerischen ID des Bauteils.</td>
</tr>
<tr>
<td>part.name</td>
<td>Optional</td>
<td>String</td>
<td>Nicht-leerer String, falls keine part.mpnr- bzw. part.ipn-Angabe gegeben ist.</td>
</tr>
<tr>
<td>part.mpnr</td>
<td>Optional</td>
<td>String</td>
<td>Nicht-leerer String, falls keine part.name- bzw. part-ipn-Angabe gegeben ist.</td>
</tr>
<tr>
<td>part.ipn</td>
<td>Optional</td>
<td>String</td>
<td>Nicht-leerer String, falls keine part.name- bzw. part.mpnr-Angabe gegeben ist.</td>
</tr>
<tr>
<td>part.description</td>
<td>Optional</td>
<td>String oder null</td>
<td>Falls vorhanden, muss es ein nicht-leerer String sein oder null.</td>
</tr>
<tr>
<td>part.manufacturer</td>
<td>Optional</td>
<td>Objekt/Array</td>
<td>
Falls vorhanden, muss es ein Objekt/Array sein und mindestens eines der Felder ausgefüllt sein:
<ul>
<li>manufacturer.id</li>
<li>manufacturer.name</li>
</ul>
</td>
</tr>
<tr>
<td>manufacturer.id</td>
<td>Optional</td>
<td>Ganzzahl (Integer)</td>
<td>Ganzzahl (Integer) > 0. Entspricht der internen numerischen ID des Herstellers.</td>
</tr>
<tr>
<td>manufacturer.name</td>
<td>Optional</td>
<td>String</td>
<td>Nicht-leerer String, falls keine manufacturer.id-Angabe gegeben ist.</td>
</tr>
<tr>
<td>part.category</td>
<td>Optional</td>
<td>Objekt/Array</td>
<td>
Falls vorhanden, muss es ein Objekt/Array sein und mindestens eines der Felder ausgefüllt sein:
<ul>
<li>category.id</li>
<li>category.name</li>
</ul>
</td>
</tr>
<tr>
<td>category.id</td>
<td>Optional</td>
<td>Ganzzahl (Integer)</td>
<td>Ganzzahl (Integer) > 0. Entspricht der internen numerischen ID der Kategorie des Bauteils.</td>
</tr>
<tr>
<td>category.name</td>
<td>Optional</td>
<td>String</td>
<td>Nicht-leerer String, falls keine category.id-Angabe gegeben ist.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="cU1bfDa" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns</source>
<target>Erwartete Spalten:</target>
</segment>
</unit>
<unit id="gvaB1sb" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns.note">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns.note</source>
<target>
<![CDATA[
<p><strong>Hinweis:</strong> Es findet keine Zuordnung zu konkreten Bauteilen aus der Kategorie-Verwaltung statt.</p>
]]>
</target>
</segment>
</unit>
<unit id="translationUnit3" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Feld</th>
<th>Bedingung</th>
<th>Datentyp</th>
<th>Beschreibung</th>
</tr>
</thead>
<tbody>
<tr>
<td>Id</td>
<td>Optional</td>
<td>Ganzzahl (Integer)</td>
<td>Offene Angabe. Eine eindeutige Identifikationsnummer für jedes Bauteil.</td>
</tr>
<tr>
<td>Designator</td>
<td>Optional</td>
<td>String</td>
<td>Offene Angabe. Ein eindeutiger Referenzbezeichner des Bauteils auf der Leiterplatte, z.B. „R1“ für Widerstand 1. Wird für den Bestückungsnamen des Bauteil-Eintrags innerhalb der Bauteilgruppe verwendet.</td>
</tr>
<tr>
<td>Package</td>
<td>Optional</td>
<td>String</td>
<td>Offene Angabe. Das Gehäuse oder die Bauform des Bauteils, z.B. „0805“ für SMD-Widerstände.</td>
</tr>
<tr>
<td>Quantity</td>
<td>Pflichtfeld</td>
<td>Ganzzahl (Integer)</td>
<td>Anzahl der identischen Bauteile, die benötigt werden, um eine Instanz der Baugruppe zu erstellen.</td>
</tr>
<tr>
<td>Designation</td>
<td>Pflichtfeld</td>
<td>String</td>
<td>Beschreibung oder Funktion des Bauteils, z.B. Widerstandswert „10kΩ“ oder Kondensatorwert „100nF“. Wird für den Namen des BOM-Eintrags verwendet.</td>
</tr>
<tr>
<td>Supplier and ref</td>
<td>Optional</td>
<td>String</td>
<td>Offene Angabe. Kann z.B. Distributor spezifischen Wert enthalten.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="QiZM3zY" name="typeahead.parts.part.name">
<segment>
<source>typeahead.parts.part.name</source>
<target>%name% (Bauteil)</target>
</segment>
</unit>
<unit id="WhYL2yX" name="typeahead.parts.assembly.name">
<segment>
<source>typeahead.parts.assembly.name</source>
<target>%name% (Baugruppe)</target>
</segment>
</unit>
<unit id="4cgba2c" name="projects.build.form.part">
<segment>
<source>projects.build.form.part</source>
<target>Bauteil "%name%"</target>
</segment>
</unit>
<unit id="1bCA1zb" name="projects.build.form.assembly">
<segment>
<source>projects.build.form.assembly</source>
<target>Baugruppe "%name%"</target>
</segment>
</unit>
<unit id="2cDB2ac" name="projects.build.form.assembly.bom.entry">
<segment>
<source>projects.build.form.assembly.bom.entry</source>
<target>%name% (%quantity% benötigt)</target>
</segment>
</unit>
<unit id="3dEC3bd" name="projects.build.form.assembly.bom.entry.no.stock">
<segment>
<source>projects.build.form.assembly.bom.entry.no.stock</source>
<target>nicht auf Lager</target>
</segment>
</unit>
<unit id="X9HUFrv" name="part.table.actions.error">
<segment state="translated">
<source>part.table.actions.error</source>

View file

@ -1535,5 +1535,693 @@
<target>Επεξεργασία</target>
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Μέρος)</target>
</segment>
</unit>
<unit id="GW8ZOX7" name="part.table.name.value.for_assembly">
<segment state="translated">
<source>part.table.name.value.for_assembly</source>
<target>%value% (Συναρμολόγηση)</target>
</segment>
</unit>
<unit id="KJuXVR5" name="project.bom.assembly">
<segment state="translated">
<source>project.bom.assembly</source>
<target>Συναρμολόγηση</target>
</segment>
</unit>
<unit id="LKvYWS6" name="project.bom.partOrAssembly">
<segment state="translated">
<source>project.bom.partOrAssembly</source>
<target>Επιλογή</target>
</segment>
</unit>
<unit id="hIIFtI1" name="assembly.edit.status">
<segment state="translated">
<source>assembly.edit.status</source>
<target>Κατάσταση</target>
</segment>
</unit>
<unit id="zRd.psv" name="assembly.status.draft">
<segment state="translated">
<source>assembly.status.draft</source>
<target>Προσχέδιο</target>
</segment>
</unit>
<unit id="_U8bb1t" name="assembly.status.planning">
<segment state="translated">
<source>assembly.status.planning</source>
<target>Υπό σχεδιασμό</target>
</segment>
</unit>
<unit id="GgUh7RT" name="assembly.status.in_production">
<segment state="translated">
<source>assembly.status.in_production</source>
<target>Σε παραγωγή</target>
</segment>
</unit>
<unit id="IluD8iU" name="assembly.status.finished">
<segment state="translated">
<source>assembly.status.finished</source>
<target>Ολοκληρώθηκε</target>
</segment>
</unit>
<unit id="Mybkd1s" name="assembly.status.archived">
<segment state="translated">
<source>assembly.status.archived</source>
<target>Αρχειοθετήθηκε</target>
</segment>
</unit>
<unit id="gHU1vgc" name="project.builds.no_stock">
<segment state="translated">
<source>project.builds.no_stock</source>
<target>δεν έχει καθοριστεί απόθεμα</target>
</segment>
</unit>
<unit id="OsmK1Iv" name="project.build.builds_part_lot_label">
<segment state="translated">
<source>project.build.builds_part_lot_label</source>
<target>%name% (%quantity% απαιτείται)</target>
</segment>
</unit>
<unit id="G2sXzh7" name="assembly.label">
<segment>
<source>assembly.label</source>
<target>Σύνολο</target>
</segment>
</unit>
<unit id="dL51AVa" name="assembly.caption">
<segment>
<source>assembly.caption</source>
<target>Σύνολο</target>
</segment>
</unit>
<unit id="G_rlE3w" name="perm.assemblies">
<segment>
<source>perm.assemblies</source>
<target>Συναρμολογήσεις</target>
</segment>
</unit>
<unit id="dipIGt4" name="assembly_bom_entry.label">
<segment>
<source>assembly_bom_entry.label</source>
<target>Μέρη</target>
</segment>
</unit>
<unit id="TvHlYfl" name="assembly.labelp">
<segment>
<source>assembly.labelp</source>
<target>Συναρμολογήσεις</target>
</segment>
</unit>
<unit id="gyRGdfv" name="assembly.edit">
<segment>
<source>assembly.edit</source>
<target>Επεξεργασία συνόλου</target>
</segment>
</unit>
<unit id="4Tdtoj_" name="assembly.new">
<segment>
<source>assembly.new</source>
<target>Νέο σύνολο</target>
</segment>
</unit>
<unit id="M51YUE." name="assembly.edit.associated_build_part">
<segment>
<source>assembly.edit.associated_build_part</source>
<target>Σχετικό μέρος</target>
</segment>
</unit>
<unit id="nH9R9f." name="assembly.edit.associated_build_part.add">
<segment>
<source>assembly.edit.associated_build_part.add</source>
<target>Προσθήκη μέρους</target>
</segment>
</unit>
<unit id="oVfOk.i" name="assembly.edit.associated_build.hint">
<segment>
<source>assembly.edit.associated_build.hint</source>
<target>Αυτό το μέρος αντιπροσωπεύει τις κατασκευασμένες εκδόσεις του συνόλου. Καταχωρίστε το εάν απαιτούνται κατασκευασμένες εκδόσεις. Εάν όχι, οι ποσότητες θα χρησιμοποιηθούν μόνο κατά την κατασκευή του εκάστοτε έργου.</target>
</segment>
</unit>
<unit id="_wZ_JZY" name="assembly.edit.bom.import_bom">
<segment>
<source>assembly.edit.bom.import_bom</source>
<target>Εισαγωγή μερών</target>
</segment>
</unit>
<unit id="vsmgKMw" name="log.database_updated.failed">
<segment>
<source>log.database_updated.failed</source>
<target>__log.database_updated.failed</target>
</segment>
</unit>
<unit id="GcQrTTE" name="log.database_updated.old_version">
<segment>
<source>log.database_updated.old_version</source>
<target>__log.database_updated.old_version</target>
</segment>
</unit>
<unit id="JJF47vK" name="log.database_updated.new_version">
<segment>
<source>log.database_updated.new_version</source>
<target>__log.database_updated.new_version</target>
</segment>
</unit>
<unit id="iVHS_sh" name="tree.tools.edit.assemblies">
<segment>
<source>tree.tools.edit.assemblies</source>
<target>Συναρμολογήσεις</target>
</segment>
</unit>
<unit id="naAMjcH" name="assembly.bom_import.flash.success">
<segment>
<source>assembly.bom_import.flash.success</source>
<target>%count% εγγραφές εξαρτημάτων εισήχθησαν με επιτυχία στο σύνολο.</target>
</segment>
</unit>
<unit id="ScQhV.o" name="assembly.bom_import.flash.invalid_entries">
<segment>
<source>assembly.bom_import.flash.invalid_entries</source>
<target>Σφάλμα επικύρωσης! Ελέγξτε το εισαγόμενο αρχείο!</target>
</segment>
</unit>
<unit id="luj_uCZ" name="assembly.bom_import.flash.invalid_file">
<segment>
<source>assembly.bom_import.flash.invalid_file</source>
<target>Το αρχείο δεν μπόρεσε να εισαχθεί. Ελέγξτε ότι έχετε επιλέξει τον σωστό τύπο αρχείου. Μήνυμα σφάλματος: %message%</target>
</segment>
</unit>
<unit id="u7SYWcB" name="assembly.bom.quantity">
<segment>
<source>assembly.bom.quantity</source>
<target>Ποσότητα</target>
</segment>
</unit>
<unit id="D7dPvPL" name="assembly.bom.mountnames">
<segment>
<source>assembly.bom.mountnames</source>
<target>Ονόματα συναρμολόγησης</target>
</segment>
</unit>
<unit id="rswC4eS" name="assembly.bom.instockAmount">
<segment state="translated">
<source>assembly.bom.instockAmount</source>
<target>Ποσότητα σε απόθεμα</target>
</segment>
</unit>
<unit id="oNutri3" name="assembly.info.title">
<segment>
<source>assembly.info.title</source>
<target>Πληροφορίες συναρμολόγησης</target>
</segment>
</unit>
<unit id="aO1rzVQ" name="assembly.info.info.label">
<segment>
<source>assembly.info.info.label</source>
<target>Πληροφορίες</target>
</segment>
</unit>
<unit id="9dOByT_" name="assembly.info.sub_assemblies.label">
<segment>
<source>assembly.info.sub_assemblies.label</source>
<target>Υποομάδες</target>
</segment>
</unit>
<unit id="Hmf0EwN" name="assembly.info.builds.label">
<segment>
<source>assembly.info.builds.label</source>
<target>Κατασκευές</target>
</segment>
</unit>
<unit id="z3F4Rcu" name="assembly.info.bom_add_parts">
<segment>
<source>assembly.info.bom_add_parts</source>
<target>Προσθήκη εξαρτημάτων</target>
</segment>
</unit>
<unit id="ZbDTUTS" name="assembly.builds.check_assembly_status">
<segment>
<source>assembly.builds.check_assembly_status</source>
<target><![CDATA[Η τρέχουσα κατάσταση συναρμολόγησης είναι <b>«%assembly_status%»</b>. Ελέγξτε εάν θέλετε πραγματικά να κατασκευάσετε τη συναρμολόγηση με αυτήν την κατάσταση!]]></target>
</segment>
</unit>
<unit id="xq1Soad" name="assembly.builds.build_not_possible">
<segment>
<source>assembly.builds.build_not_possible</source>
<target>Η κατασκευή δεν είναι δυνατή: Δεν υπάρχουν αρκετά διαθέσιμα εξαρτήματα</target>
</segment>
</unit>
<unit id="njRyDHQ" name="assembly.builds.following_bom_entries_miss_instock">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock</source>
<target>Δεν υπάρχουν αρκετά εξαρτήματα σε απόθεμα για να κατασκευαστεί αυτό το έργο %number_of_builds% φορές. Λείπουν τα ακόλουθα εξαρτήματα:</target>
</segment>
</unit>
<unit id="sp7Z0MS" name="assembly.builds.build_possible">
<segment>
<source>assembly.builds.build_possible</source>
<target>Κατασκευή δυνατή</target>
</segment>
</unit>
<unit id="1uZzdEl" name="assembly.builds.number_of_builds_possible">
<segment>
<source>assembly.builds.number_of_builds_possible</source>
<target><![CDATA[Διαθέτετε αρκετά εξαρτήματα σε απόθεμα για να κατασκευάσετε <b>%max_builds%</b> μονάδες αυτής της συναρμολόγησης.]]></target>
</segment>
</unit>
<unit id="pW6QLXf" name="assembly.builds.number_of_builds">
<segment>
<source>assembly.builds.number_of_builds</source>
<target>Αριθμός κατασκευών</target>
</segment>
</unit>
<unit id="mtJqT_a" name="assembly.build.btn_build">
<segment>
<source>assembly.build.btn_build</source>
<target>Κατασκευή</target>
</segment>
</unit>
<unit id="LFSVVcP" name="assembly.builds.no_stocked_builds">
<segment>
<source>assembly.builds.no_stocked_builds</source>
<target>Αποθηκευμένα κατασκευασμένα κομμάτια</target>
</segment>
</unit>
<unit id="dGFHutJ" name="assembly.info.bom_entries_count">
<segment>
<source>assembly.info.bom_entries_count</source>
<target>Εξαρτήματα</target>
</segment>
</unit>
<unit id="xJ7oBM4" name="assembly.info.sub_assemblies_count">
<segment>
<source>assembly.info.sub_assemblies_count</source>
<target>Υποομάδες</target>
</segment>
</unit>
<unit id="HZYhTlb" name="assembly.builds.stocked">
<segment>
<source>assembly.builds.stocked</source>
<target>σε απόθεμα</target>
</segment>
</unit>
<unit id="9EG0PLW" name="assembly.builds.needed">
<segment>
<source>assembly.builds.needed</source>
<target>απαιτούμενο</target>
</segment>
</unit>
<unit id="tgs_7u9" name="assembly.add_parts_to_assembly">
<segment>
<source>assembly.add_parts_to_assembly</source>
<target>Προσθήκη εξαρτημάτων στη συναρμολόγηση</target>
</segment>
</unit>
<unit id="PPsM0Dg" name="assembly.bom.name">
<segment>
<source>assembly.bom.name</source>
<target>Όνομα</target>
</segment>
</unit>
<unit id="nUEs.ld" name="assembly.bom.comment">
<segment>
<source>assembly.bom.comment</source>
<target>Σχόλια</target>
</segment>
</unit>
<unit id="87YpQ_u" name="assembly.builds.following_bom_entries_miss_instock_n">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock_n</source>
<target>Δεν υπάρχουν αρκετά εξαρτήματα σε απόθεμα για να κατασκευαστεί αυτή η συναρμολόγηση %number_of_builds% φορές. Λείπουν τα ακόλουθα εξαρτήματα:</target>
</segment>
</unit>
<unit id="JlQhDdS" name="assembly.build.help">
<segment>
<source>assembly.build.help</source>
<target>Επιλέξτε από ποια αποθέματα θα αφαιρεθούν τα αναγκαία για την κατασκευή εξαρτήματα (και σε ποια ποσότητα). Σημειώστε το πλαίσιο επιλογής για κάθε εξάρτημα όταν αφαιρέσετε τα εξαρτήματα ή χρησιμοποιήστε το ανώτερο πλαίσιο επιλογής για να τα ελέγξετε όλα ταυτόχρονα.</target>
</segment>
</unit>
<unit id="iP5_QVj" name="assembly.build.required_qty">
<segment>
<source>assembly.build.required_qty</source>
<target>Απαιτούμενη ποσότητα</target>
</segment>
</unit>
<unit id="UJpD7n6" name="assembly.import_bom">
<segment>
<source>assembly.import_bom</source>
<target>Εισαγωγή εξαρτημάτων συναρμολόγησης</target>
</segment>
</unit>
<unit id="WTasGao" name="assembly.bom.part">
<segment>
<source>assembly.bom.part</source>
<target>Εξάρτημα</target>
</segment>
</unit>
<unit id="jHKh8Zp" name="assembly.bom.add_entry">
<segment>
<source>assembly.bom.add_entry</source>
<target>Προσθήκη καταχώρησης</target>
</segment>
</unit>
<unit id="RsZ77df" name="assembly.bom.price">
<segment>
<source>assembly.bom.price</source>
<target>Τιμή</target>
</segment>
</unit>
<unit id="63adIrC" name="assembly.build.dont_check_quantity">
<segment state="translated">
<source>assembly.build.dont_check_quantity</source>
<target>Μην ελέγχετε την ποσότητα</target>
</segment>
</unit>
<unit id="O0DP6tK" name="assembly.build.dont_check_quantity.help">
<segment state="translated">
<source>assembly.build.dont_check_quantity.help</source>
<target>Εάν επιλεγεί αυτή η επιλογή, οι επιλεγμένες ποσότητες θα αφαιρεθούν από το απόθεμα ανεξάρτητα από το αν είναι περισσότερο ή λιγότερο από το απαιτούμενο για την κατασκευή της συναρμολόγησης.</target>
</segment>
</unit>
<unit id="9bc0nzK" name="assembly.build.add_builds_to_builds_part">
<segment state="translated">
<source>assembly.build.add_builds_to_builds_part</source>
<target>Προσθήκη κατασκευασμένων κομματιών στο τμήμα συναρμολόγησης</target>
</segment>
</unit>
<unit id="nl.jtSx" name="assembly.bom_import.type">
<segment state="translated">
<source>assembly.bom_import.type</source>
<target>Τύπος</target>
</segment>
</unit>
<unit id="LtbSLHx" name="assembly.bom_import.type.json">
<segment state="translated">
<source>assembly.bom_import.type.json</source>
<target>JSON για συναρμολόγηση</target>
</segment>
</unit>
<unit id="clXFAdN" name="assembly.bom_import.type.kicad_pcbnew">
<segment state="translated">
<source>assembly.bom_import.type.kicad_pcbnew</source>
<target>CSV (KiCAD Pcbnew BOM)</target>
</segment>
</unit>
<unit id="0IekETE" name="assembly.bom_import.clear_existing_bom">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom</source>
<target>Διαγραφή υπαρχόντων εξαρτημάτων πριν από την εισαγωγή</target>
</segment>
</unit>
<unit id="S4QY6pA" name="assembly.bom_import.clear_existing_bom.help">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom.help</source>
<target>Εάν επιλεγεί αυτή η επιλογή, όλα τα ήδη υπάρχοντα εξαρτήματα στη συναρμολόγηση θα διαγραφούν και θα αντικατασταθούν με τα δεδομένα εξαρτημάτων που εισάγονται.</target>
</segment>
</unit>
<unit id="RVNWPsh" name="assembly.import_bom.template.header.json">
<segment>
<source>assembly.import_bom.template.header.json</source>
<target>Πρότυπο εισαγωγής JSON για συναρμολόγηση</target>
</segment>
</unit>
<unit id="eU4FfCr" name="assembly.import_bom.template.header.kicad_pcbnew">
<segment>
<source>assembly.import_bom.template.header.kicad_pcbnew</source>
<target>Πρότυπο εισαγωγής CSV (KiCAD Pcbnew BOM) για συναρμολόγηση</target>
</segment>
</unit>
<unit id="aLomVVS" name="assembly.bom_import.template.entry.name">
<segment>
<source>assembly.bom_import.template.entry.name</source>
<target>Όνομα του εξαρτήματος στη συναρμολόγηση</target>
</segment>
</unit>
<unit id="o4ZcLfV" name="assembly.bom_import.template.entry.part.mpnr">
<segment>
<source>assembly.bom_import.template.entry.part.mpnr</source>
<target>Μοναδικός αριθμός προϊόντος από τον κατασκευαστή</target>
</segment>
</unit>
<unit id="n3YbKeU" name="assembly.bom_import.template.entry.part.ipn">
<segment>
<source>assembly.bom_import.template.entry.part.ipn</source>
<target>Μοναδικός IPN του εξαρτήματος</target>
</segment>
</unit>
<unit id="l2SYJP5" name="assembly.bom_import.template.entry.part.name">
<segment>
<source>assembly.bom_import.template.entry.part.name</source>
<target>Μοναδικό όνομα εξαρτήματος</target>
</segment>
</unit>
<unit id="VgXQ1xW" name="assembly.bom_import.template.entry.part.manufacturer.name">
<segment>
<source>assembly.bom_import.template.entry.part.manufacturer.name</source>
<target>Μοναδικό όνομα κατασκευαστή</target>
</segment>
</unit>
<unit id="MpB.o_L" name="assembly.bom_import.template.entry.part.category.name">
<segment>
<source>assembly.bom_import.template.entry.part.category.name</source>
<target>Μοναδικό όνομα κατηγορίας</target>
</segment>
</unit>
<unit id="NIcfgj84" name="assembly.bom_import.template.json.table">
<segment>
<source>assembly.bom_import.template.json.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Πεδίο</th>
<th>Προϋπόθεση</th>
<th>Τύπος Δεδομένων</th>
<th>Περιγραφή</th>
</tr>
</thead>
<tbody>
<tr>
<td>quantity</td>
<td>Υποχρεωτικό πεδίο</td>
<td>Αριθμός κινητής υποδιαστολής (Float)</td>
<td>Πρέπει να παρέχεται και να περιέχει τιμή κινητής υποδιαστολής (Float) μεγαλύτερη από 0.0.</td>
</tr>
<tr>
<td>name</td>
<td>Προαιρετικό</td>
<td>Κείμενο (String)</td>
<td>Εάν υπάρχει, πρέπει να είναι μη κενό κείμενο.</td>
</tr>
<tr>
<td>part</td>
<td>Προαιρετικό</td>
<td>Αντικείμενο/Πίνακας</td>
<td>
Εάν παρέχεται, πρέπει να είναι αντικείμενο/πίνακας και τουλάχιστον ένα από τα πεδία του να είναι συμπληρωμένο:
<ul>
<li>part.id</li>
<li>part.name</li>
</ul>
</td>
</tr>
<tr>
<td>part.id</td>
<td>Προαιρετικό</td>
<td>Ακέραιος αριθμός (Integer)</td>
<td>Ακέραιος (Integer) > 0. Αντιστοιχεί στην εσωτερική αριθμητική ταυτότητα (ID) του εξαρτήματος στη βάση δεδομένων.</td>
</tr>
<tr>
<td>part.name</td>
<td>Προαιρετικό</td>
<td>Κείμενο (String)</td>
<td>Μη κενό κείμενο, εάν δεν παρέχονται οι ενδείξεις part.mpnr ή part.ipn.</td>
</tr>
<tr>
<td>part.mpnr</td>
<td>Προαιρετικό</td>
<td>Κείμενο (String)</td>
<td>Μη κενό κείμενο, εάν δεν παρέχονται οι ενδείξεις part.name ή part.ipn.</td>
</tr>
<tr>
<td>part.ipn</td>
<td>Προαιρετικό</td>
<td>Κείμενο (String)</td>
<td>Μη κενό κείμενο, εάν δεν παρέχονται οι ενδείξεις part.name ή part.mpnr.</td>
</tr>
<tr>
<td>part.description</td>
<td>Προαιρετικό</td>
<td>Κείμενο ή null</td>
<td>Εάν υπάρχει, πρέπει να είναι μη κενό κείμενο, ή null.</td>
</tr>
<tr>
<td>part.manufacturer</td>
<td>Προαιρετικό</td>
<td>Αντικείμενο/Πίνακας</td>
<td>
Εάν υπάρχει, πρέπει να είναι αντικείμενο/πίνακας και τουλάχιστον ένα από τα πεδία του να είναι συμπληρωμένο:
<ul>
<li>manufacturer.id</li>
<li>manufacturer.name</li>
</ul>
</td>
</tr>
<tr>
<td>manufacturer.id</td>
<td>Προαιρετικό</td>
<td>Ακέραιος αριθμός (Integer)</td>
<td>Ακέραιος (Integer) > 0. Αντιστοιχεί στην εσωτερική αριθμητική ταυτότητα (ID) του κατασκευαστή.</td>
</tr>
<tr>
<td>manufacturer.name</td>
<td>Προαιρετικό</td>
<td>Κείμενο (String)</td>
<td>Μη κενό κείμενο, εάν δεν παρέχεται η ένδειξη manufacturer.id.</td>
</tr>
<tr>
<td>part.category</td>
<td>Προαιρετικό</td>
<td>Αντικείμενο/Πίνακας</td>
<td>
Εάν υπάρχει, πρέπει να είναι αντικείμενο/πίνακας και τουλάχιστον ένα από τα πεδία του να είναι συμπληρωμένο:
<ul>
<li>category.id</li>
<li>category.name</li>
</ul>
</td>
</tr>
<tr>
<td>category.id</td>
<td>Προαιρετικό</td>
<td>Ακέραιος αριθμός (Integer)</td>
<td>Ακέραιος (Integer) > 0. Αντιστοιχεί στην εσωτερική αριθμητική ταυτότητα (ID) της κατηγορίας του εξαρτήματος.</td>
</tr>
<tr>
<td>category.name</td>
<td>Προαιρετικό</td>
<td>Κείμενο (String)</td>
<td>Μη κενό κείμενο, εάν δεν παρέχεται η ένδειξη category.id.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="cU1bfDa" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns</source>
<target>Αναμενόμενες στήλες:</target>
</segment>
</unit>
<unit id="gvaB1sb" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns.note">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns.note</source>
<target>
<![CDATA[
<p><strong>Σημείωση:</strong> Δεν πραγματοποιείται αντιστοίχιση με συγκεκριμένα εξαρτήματα από τη διαχείριση κατηγοριών.</p>
]]>
</target>
</segment>
</unit>
<unit id="translationUnit4" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Πεδίο</th>
<th>Εκπλήρωση</th>
<th>Τύπος δεδομένων</th>
<th>Περιγραφή</th>
</tr>
</thead>
<tbody>
<tr>
<td>Id</td>
<td>Προαιρετικό</td>
<td>Ακέραιος αριθμός (Integer)</td>
<td>Ελεύθερη καταχώρηση. Μοναδικός αριθμός ταυτοποίησης για κάθε εξάρτημα.</td>
</tr>
<tr>
<td>Σχεδιαστής</td>
<td>Προαιρετικό</td>
<td>Συμβολοσειρά (String)</td>
<td>Ελεύθερη καταχώρηση. Μοναδικός αναγνωριστικός δείκτης του εξαρτήματος στην πλακέτα κυκλώματος, π.χ. "R1" για την αντίσταση 1. Χρησιμοποιείται για το όνομα του εξαρτήματος στο πλαίσιο της ομάδας εξαρτημάτων.</td>
</tr>
<tr>
<td>Συσκευασία</td>
<td>Προαιρετικό</td>
<td>Συμβολοσειρά (String)</td>
<td>Ελεύθερη καταχώρηση. Ο τύπος ή η μορφή του εξαρτήματος, π.χ. "0805" για αντιστάσεις SMD.</td>
</tr>
<tr>
<td>Ποσότητα</td>
<td>Υποχρεωτικό</td>
<td>Ακέραιος αριθμός (Integer)</td>
<td>Ο αριθμός των πανομοιότυπων εξαρτημάτων που απαιτούνται για τη δημιουργία μίας μονάδας του συνόλου.</td>
</tr>
<tr>
<td>Ορισμός</td>
<td>Υποχρεωτικό</td>
<td>Συμβολοσειρά (String)</td>
<td>Περιγραφή ή λειτουργία του εξαρτήματος, π.χ. αντίσταση "10kΩ" ή χωρητικότητα "100nF". Χρησιμοποιείται για το όνομα της εγγραφής στο BOM.</td>
</tr>
<tr>
<td>Προμηθευτής και παραπομπή</td>
<td>Προαιρετικό</td>
<td>Συμβολοσειρά (String)</td>
<td>Ελεύθερη καταχώρηση. Μπορεί να περιλαμβάνει, π.χ., ειδική τιμή από διανομέα.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="QiZM3zY" name="typeahead.parts.part.name">
<segment>
<source>typeahead.parts.part.name</source>
<target>%name% (Εξάρτημα)</target>
</segment>
</unit>
<unit id="WhYL2yX" name="typeahead.parts.assembly.name">
<segment>
<source>typeahead.parts.assembly.name</source>
<target>%name% (Συναρμολόγηση)</target>
</segment>
</unit>
<unit id="4cgba2c" name="projects.build.form.part">
<segment>
<source>projects.build.form.part</source>
<target>Εξάρτημα "%name%"</target>
</segment>
</unit>
<unit id="1bCA1zb" name="projects.build.form.assembly">
<segment>
<source>projects.build.form.assembly</source>
<target>Συναρμολόγηση "%name%"</target>
</segment>
</unit>
<unit id="2cDB2ac" name="projects.build.form.assembly.bom.entry">
<segment>
<source>projects.build.form.assembly.bom.entry</source>
<target>%name% (%quantity% απαιτείται)</target>
</segment>
</unit>
<unit id="3dEC3bd" name="projects.build.form.assembly.bom.entry.no.stock">
<segment>
<source>projects.build.form.assembly.bom.entry.no.stock</source>
<target>δεν υπάρχει στο απόθεμα</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -4741,6 +4741,18 @@ If you have done this incorrectly or if a computer is no longer trusted, you can
<target>Name</target>
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Part)</target>
</segment>
</unit>
<unit id="GW8ZOX7" name="part.table.name.value.for_assembly">
<segment state="translated">
<source>part.table.name.value.for_assembly</source>
<target>%value% (Assembly)</target>
</segment>
</unit>
<unit id="rW_SFJE" name="part.table.id">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:178</note>
@ -9861,6 +9873,18 @@ Element 1 -&gt; Element 1.2</target>
<target>Part</target>
</segment>
</unit>
<unit id="KJuXVR5" name="project.bom.assembly">
<segment state="translated">
<source>project.bom.assembly</source>
<target>Assembly</target>
</segment>
</unit>
<unit id="LKvYWS6" name="project.bom.partOrAssembly">
<segment state="translated">
<source>project.bom.partOrAssembly</source>
<target>Selection</target>
</segment>
</unit>
<unit id="kGqIirz" name="project.bom.add_entry">
<segment state="translated">
<source>project.bom.add_entry</source>
@ -9939,6 +9963,42 @@ Element 1 -&gt; Element 1.2</target>
<target>Archived</target>
</segment>
</unit>
<unit id="hIIFtI1" name="assembly.edit.status">
<segment state="translated">
<source>assembly.edit.status</source>
<target>Project status</target>
</segment>
</unit>
<unit id="zRd.psv" name="assembly.status.draft">
<segment state="translated">
<source>assembly.status.draft</source>
<target>Draft</target>
</segment>
</unit>
<unit id="_U8bb1t" name="assembly.status.planning">
<segment state="translated">
<source>assembly.status.planning</source>
<target>Planning</target>
</segment>
</unit>
<unit id="GgUh7RT" name="assembly.status.in_production">
<segment state="translated">
<source>assembly.status.in_production</source>
<target>In production</target>
</segment>
</unit>
<unit id="IluD8iU" name="assembly.status.finished">
<segment state="translated">
<source>assembly.status.finished</source>
<target>Finished</target>
</segment>
</unit>
<unit id="Mybkd1s" name="assembly.status.archived">
<segment state="translated">
<source>assembly.status.archived</source>
<target>Archived</target>
</segment>
</unit>
<unit id="9GtmqC1" name="part.new_build_part.error.build_part_already_exists">
<segment state="translated">
<source>part.new_build_part.error.build_part_already_exists</source>
@ -10215,6 +10275,12 @@ Element 1 -&gt; Element 1.2</target>
<target>stocked</target>
</segment>
</unit>
<unit id="gHU1vgc" name="project.builds.no_stock">
<segment state="translated">
<source>project.builds.no_stock</source>
<target>no stock specified</target>
</segment>
</unit>
<unit id="s5DQlqF" name="project.builds.needed">
<segment state="translated">
<source>project.builds.needed</source>
@ -10287,6 +10353,12 @@ Element 1 -&gt; Element 1.2</target>
<target>Target lot</target>
</segment>
</unit>
<unit id="OsmK1Iv" name="project.build.builds_part_lot_label">
<segment state="translated">
<source>project.build.builds_part_lot_label</source>
<target>%name% (%quantity% needed)</target>
</segment>
</unit>
<unit id="5DTAvWG" name="project.builds.number_of_builds">
<segment state="translated">
<source>project.builds.number_of_builds</source>
@ -12930,6 +13002,622 @@ Please note, that you can not impersonate a disabled user. If you try you will g
<target>View external version</target>
</segment>
</unit>
<unit id="G2sXzh7" name="assembly.label">
<segment>
<source>assembly.label</source>
<target>Assembly</target>
</segment>
</unit>
<unit id="dL51AVa" name="assembly.caption">
<segment>
<source>assembly.caption</source>
<target>Assembly</target>
</segment>
</unit>
<unit id="dipIGt4" name="assembly_bom_entry.label">
<segment>
<source>assembly_bom_entry.label</source>
<target>Parts</target>
</segment>
</unit>
<unit id="G_rlE3w" name="perm.assemblies">
<segment>
<source>perm.assemblies</source>
<target>Assemblies</target>
</segment>
</unit>
<unit id="TvHlYfl" name="assembly.labelp">
<segment>
<source>assembly.labelp</source>
<target>Assemblies</target>
</segment>
</unit>
<unit id="gyRGdfv" name="assembly.edit">
<segment>
<source>assembly.edit</source>
<target>Edit assembly</target>
</segment>
</unit>
<unit id="4Tdtoj_" name="assembly.new">
<segment>
<source>assembly.new</source>
<target>New assembly</target>
</segment>
</unit>
<unit id="M51YUE." name="assembly.edit.associated_build_part">
<segment>
<source>assembly.edit.associated_build_part</source>
<target>Associated builds part</target>
</segment>
</unit>
<unit id="nH9R9f." name="assembly.edit.associated_build_part.add">
<segment>
<source>assembly.edit.associated_build_part.add</source>
<target>Add builds part</target>
</segment>
</unit>
<unit id="oVfOk.i" name="assembly.edit.associated_build.hint">
<segment>
<source>assembly.edit.associated_build.hint</source>
<target>This part represents the builds of this assembly. To indicate if built instances are required. If not, the number of pieces regarding the assembly are only used for the build of the respective project.</target>
</segment>
</unit>
<unit id="_wZ_JZY" name="assembly.edit.bom.import_bom">
<segment>
<source>assembly.edit.bom.import_bom</source>
<target>Import BOM</target>
</segment>
</unit>
<unit id="vsmgKMw" name="log.database_updated.failed">
<segment>
<source>log.database_updated.failed</source>
<target>__log.database_updated.failed</target>
</segment>
</unit>
<unit id="GcQrTTE" name="log.database_updated.old_version">
<segment>
<source>log.database_updated.old_version</source>
<target>__log.database_updated.old_version</target>
</segment>
</unit>
<unit id="JJF47vK" name="log.database_updated.new_version">
<segment>
<source>log.database_updated.new_version</source>
<target>__log.database_updated.new_version</target>
</segment>
</unit>
<unit id="iVHS_sh" name="tree.tools.edit.assemblies">
<segment>
<source>tree.tools.edit.assemblies</source>
<target>Assemblies</target>
</segment>
</unit>
<unit id="naAMjcH" name="assembly.bom_import.flash.success">
<segment>
<source>assembly.bom_import.flash.success</source>
<target>Imported %count% parts in assembly successfully.</target>
</segment>
</unit>
<unit id="ScQhV.o" name="assembly.bom_import.flash.invalid_entries">
<segment>
<source>assembly.bom_import.flash.invalid_entries</source>
<target>Validation error! Please check your data!</target>
</segment>
</unit>
<unit id="luj_uCZ" name="assembly.bom_import.flash.invalid_file">
<segment>
<source>assembly.bom_import.flash.invalid_file</source>
<target>File could not be imported. Please check that you have selected the right file type. Error message: %message%</target>
</segment>
</unit>
<unit id="u7SYWcB" name="assembly.bom.quantity">
<segment>
<source>assembly.bom.quantity</source>
<target>Quantity</target>
</segment>
</unit>
<unit id="D7dPvPL" name="assembly.bom.mountnames">
<segment>
<source>assembly.bom.mountnames</source>
<target>Mount names</target>
</segment>
</unit>
<unit id="rswC4eS" name="assembly.bom.instockAmount">
<segment state="translated">
<source>assembly.bom.instockAmount</source>
<target>Stocked amount</target>
</segment>
</unit>
<unit id="oNutri3" name="assembly.info.title">
<segment>
<source>assembly.info.title</source>
<target>Assembly info</target>
</segment>
</unit>
<unit id="aO1rzVQ" name="assembly.info.info.label">
<segment>
<source>assembly.info.info.label</source>
<target>Info</target>
</segment>
</unit>
<unit id="9dOByT_" name="assembly.info.sub_assemblies.label">
<segment>
<source>assembly.info.sub_assemblies.label</source>
<target>Sub-assemblies</target>
</segment>
</unit>
<unit id="Hmf0EwN" name="assembly.info.builds.label">
<segment>
<source>assembly.info.builds.label</source>
<target>Build</target>
</segment>
</unit>
<unit id="z3F4Rcu" name="assembly.info.bom_add_parts">
<segment>
<source>assembly.info.bom_add_parts</source>
<target>Add part entries</target>
</segment>
</unit>
<unit id="ZbDTUTS" name="assembly.builds.check_assembly_status">
<segment>
<source>assembly.builds.check_assembly_status</source>
<target><![CDATA[The current assembly status is <b>"%assembly_status%"</b>. You should check if you really want to build the assembly with this status!]]></target>
</segment>
</unit>
<unit id="xq1Soad" name="assembly.builds.build_not_possible">
<segment>
<source>assembly.builds.build_not_possible</source>
<target>Build not possible: Parts not stocked</target>
</segment>
</unit>
<unit id="njRyDHQ" name="assembly.builds.following_bom_entries_miss_instock">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock</source>
<target>You do not have enough parts stocked to build this assembly %number_of_builds% times. The following parts have missing instock:</target>
</segment>
</unit>
<unit id="sp7Z0MS" name="assembly.builds.build_possible">
<segment>
<source>assembly.builds.build_possible</source>
<target>Build possible</target>
</segment>
</unit>
<unit id="1uZzdEl" name="assembly.builds.number_of_builds_possible">
<segment>
<source>assembly.builds.number_of_builds_possible</source>
<target><![CDATA[You have enough stocked to build <b>%max_builds%</b> builds of this assembly.]]></target>
</segment>
</unit>
<unit id="pW6QLXf" name="assembly.builds.number_of_builds">
<segment>
<source>assembly.builds.number_of_builds</source>
<target>Build amount</target>
</segment>
</unit>
<unit id="mtJqT_a" name="assembly.build.btn_build">
<segment>
<source>assembly.build.btn_build</source>
<target>Build</target>
</segment>
</unit>
<unit id="LFSVVcP" name="assembly.builds.no_stocked_builds">
<segment>
<source>assembly.builds.no_stocked_builds</source>
<target>Number of stocked builds</target>
</segment>
</unit>
<unit id="dGFHutJ" name="assembly.info.bom_entries_count">
<segment>
<source>assembly.info.bom_entries_count</source>
<target>Part entries</target>
</segment>
</unit>
<unit id="xJ7oBM4" name="assembly.info.sub_assemblies_count">
<segment>
<source>assembly.info.sub_assemblies_count</source>
<target>Sub-assemblies</target>
</segment>
</unit>
<unit id="HZYhTlb" name="assembly.builds.stocked">
<segment>
<source>assembly.builds.stocked</source>
<target>stocked</target>
</segment>
</unit>
<unit id="9EG0PLW" name="assembly.builds.needed">
<segment>
<source>assembly.builds.needed</source>
<target>needed</target>
</segment>
</unit>
<unit id="tgs_7u9" name="assembly.add_parts_to_assembly">
<segment>
<source>assembly.add_parts_to_assembly</source>
<target>Add parts to assembly</target>
</segment>
</unit>
<unit id="PPsM0Dg" name="assembly.bom.name">
<segment>
<source>assembly.bom.name</source>
<target>Name</target>
</segment>
</unit>
<unit id="nUEs.ld" name="assembly.bom.comment">
<segment>
<source>assembly.bom.comment</source>
<target>Notes</target>
</segment>
</unit>
<unit id="87YpQ_u" name="assembly.builds.following_bom_entries_miss_instock_n">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock_n</source>
<target>You do not have enough parts stocked to build this assembly %number_of_builds% times. The following parts have missing instock:</target>
</segment>
</unit>
<unit id="JlQhDdS" name="assembly.build.help">
<segment>
<source>assembly.build.help</source>
<target>Choose from which part lots the stock to build this assembly should be taken (and in which amount). Check the checkbox for each part, when you are finished withdrawing the parts, or use the top checkbox to check all boxes at once.</target>
</segment>
</unit>
<unit id="iP5_QVj" name="assembly.build.required_qty">
<segment>
<source>assembly.build.required_qty</source>
<target>Required quantity</target>
</segment>
</unit>
<unit id="UJpD7n6" name="assembly.import_bom">
<segment>
<source>assembly.import_bom</source>
<target>Import BOM for project</target>
</segment>
</unit>
<unit id="WTasGao" name="assembly.bom.part">
<segment>
<source>assembly.bom.part</source>
<target>Part</target>
</segment>
</unit>
<unit id="jHKh8Zp" name="assembly.bom.add_entry">
<segment>
<source>assembly.bom.add_entry</source>
<target>Add entry</target>
</segment>
</unit>
<unit id="RsZ77df" name="assembly.bom.price">
<segment>
<source>assembly.bom.price</source>
<target>Price</target>
</segment>
</unit>
<unit id="63adIrC" name="assembly.build.dont_check_quantity">
<segment state="translated">
<source>assembly.build.dont_check_quantity</source>
<target>Do not check quantities</target>
</segment>
</unit>
<unit id="O0DP6tK" name="assembly.build.dont_check_quantity.help">
<segment state="translated">
<source>assembly.build.dont_check_quantity.help</source>
<target>If this option is selected, the given withdraw quantities are used as given, no matter if more or less parts are actually required to build this assembly.</target>
</segment>
</unit>
<unit id="9bc0nzK" name="assembly.build.add_builds_to_builds_part">
<segment state="translated">
<source>assembly.build.add_builds_to_builds_part</source>
<target>Add builds to assembly builds part</target>
</segment>
</unit>
<unit id="nl.jtSx" name="assembly.bom_import.type">
<segment state="translated">
<source>assembly.bom_import.type</source>
<target>Type</target>
</segment>
</unit>
<unit id="LtbSLHx" name="assembly.bom_import.type.json">
<segment state="translated">
<source>assembly.bom_import.type.json</source>
<target>JSON for one assembly</target>
</segment>
</unit>
<unit id="clXFAdN" name="assembly.bom_import.type.kicad_pcbnew">
<segment state="translated">
<source>assembly.bom_import.type.kicad_pcbnew</source>
<target>CSV (KiCAD Pcbnew BOM)</target>
</segment>
</unit>
<unit id="0IekETE" name="assembly.bom_import.clear_existing_bom">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom</source>
<target>Clear existing part entries before importing</target>
</segment>
</unit>
<unit id="S4QY6pA" name="assembly.bom_import.clear_existing_bom.help">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom.help</source>
<target>Selecting this option will remove all existing part entries in the assembly and overwrite them with the imported part data!</target>
</segment>
</unit>
<unit id="RVNWPsh" name="assembly.import_bom.template.header.json">
<segment>
<source>assembly.import_bom.template.header.json</source>
<target>Import template JSON format for one assembly</target>
</segment>
</unit>
<unit id="RVNWPsh" name="assembly.import_bom.template.header.kicad_pcbnew">
<segment>
<source>assembly.import_bom.template.header.kicad_pcbnew</source>
<target>Import template CSV format (KiCAD Pcbnew BOM) for one assembly</target>
</segment>
</unit>
<unit id="aLomVVS" name="assembly.bom_import.template.entry.name">
<segment>
<source>assembly.bom_import.template.entry.name</source>
<target>Name of the part in the assembly</target>
</segment>
</unit>
<unit id="o4ZcLfV" name="assembly.bom_import.template.entry.part.mpnr">
<segment>
<source>assembly.bom_import.template.entry.part.mpnr</source>
<target>Unique product number within the manufacturer</target>
</segment>
</unit>
<unit id="n3YbKeU" name="assembly.bom_import.template.entry.part.ipn">
<segment>
<source>assembly.bom_import.template.entry.part.ipn</source>
<target>Unique IPN of the part</target>
</segment>
</unit>
<unit id="l2SYJP5" name="assembly.bom_import.template.entry.part.name">
<segment>
<source>assembly.bom_import.template.entry.part.name</source>
<target>Unique name of the part</target>
</segment>
</unit>
<unit id="VgXQ1xW" name="assembly.bom_import.template.entry.part.manufacturer.name">
<segment>
<source>assembly.bom_import.template.entry.part.manufacturer.name</source>
<target>Unique name of the manufacturer</target>
</segment>
</unit>
<unit id="MpB.o_L" name="assembly.bom_import.template.entry.part.category.name">
<segment>
<source>assembly.bom_import.template.entry.part.category.name</source>
<target>Unique name of the category</target>
</segment>
</unit>
<unit id="NIcfgj84" name="assembly.bom_import.template.json.table">
<segment>
<source>assembly.bom_import.template.json.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Field</th>
<th>Condition</th>
<th>Data type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>quantity</td>
<td>Required</td>
<td>Floating point (Float)</td>
<td>Must be provided and contains a floating-point value (Float) greater than 0.0.</td>
</tr>
<tr>
<td>name</td>
<td>Optional</td>
<td>String</td>
<td>If present, it must be a non-empty string.</td>
</tr>
<tr>
<td>part</td>
<td>Optional</td>
<td>Object/Array</td>
<td>
If provided, it must be an object/array and at least one of the fields must be filled:
<ul>
<li>part.id</li>
<li>part.name</li>
</ul>
</td>
</tr>
<tr>
<td>part.id</td>
<td>Optional</td>
<td>Integer</td>
<td>Integer > 0. Matches the Part-DB internal numeric ID of the component.</td>
</tr>
<tr>
<td>part.name</td>
<td>Optional</td>
<td>String</td>
<td>Non-empty string if no part.mpnr or part.ipn is provided.</td>
</tr>
<tr>
<td>part.mpnr</td>
<td>Optional</td>
<td>String</td>
<td>Non-empty string if no part.name or part.ipn is provided.</td>
</tr>
<tr>
<td>part.ipn</td>
<td>Optional</td>
<td>String</td>
<td>Non-empty string if no part.name or part.mpnr is provided.</td>
</tr>
<tr>
<td>part.description</td>
<td>Optional</td>
<td>String or null</td>
<td>If present, it must be a non-empty string or null.</td>
</tr>
<tr>
<td>part.manufacturer</td>
<td>Optional</td>
<td>Object/Array</td>
<td>
If present, it must be an object/array and at least one of the fields must be filled:
<ul>
<li>manufacturer.id</li>
<li>manufacturer.name</li>
</ul>
</td>
</tr>
<tr>
<td>manufacturer.id</td>
<td>Optional</td>
<td>Integer</td>
<td>Integer > 0. Matches the internal numeric ID of the manufacturer.</td>
</tr>
<tr>
<td>manufacturer.name</td>
<td>Optional</td>
<td>String</td>
<td>Non-empty string if no manufacturer.id is provided.</td>
</tr>
<tr>
<td>part.category</td>
<td>Optional</td>
<td>Object/Array</td>
<td>
If present, it must be an object/array and at least one of the fields must be filled:
<ul>
<li>category.id</li>
<li>category.name</li>
</ul>
</td>
</tr>
<tr>
<td>category.id</td>
<td>Optional</td>
<td>Integer</td>
<td>Integer > 0. Matches the internal numeric ID of the component's category.</td>
</tr>
<tr>
<td>category.name</td>
<td>Optional</td>
<td>String</td>
<td>Non-empty string if no category.id is provided.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="cU1bfDa" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns</source>
<target>Expected Columns:</target>
</segment>
</unit>
<unit id="gvaB1sb" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns.note">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns.note</source>
<target>
<![CDATA[
<p><strong>Note:</strong> No mapping is performed with specific components from category management.</p>
]]>
</target>
</segment>
</unit>
<unit id="translationUnit5" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Field</th>
<th>Condition</th>
<th>Data Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Id</td>
<td>Optional</td>
<td>Integer</td>
<td>Free-form field. A unique identification number for each component.</td>
</tr>
<tr>
<td>Designator</td>
<td>Optional</td>
<td>String</td>
<td>Free-form field. A unique reference designator of the component on the PCB, e.g., “R1” for resistor 1. Used for naming the placement in the component group.</td>
</tr>
<tr>
<td>Package</td>
<td>Optional</td>
<td>String</td>
<td>Free-form field. The casing or form factor of the component, e.g., “0805” for SMD resistors.</td>
</tr>
<tr>
<td>Quantity</td>
<td>Required</td>
<td>Integer</td>
<td>The number of identical components required to create a single instance of an assembly.</td>
</tr>
<tr>
<td>Designation</td>
<td>Required</td>
<td>String</td>
<td>The description or function of the component, e.g., resistor value “10kΩ” or capacitor value “100nF.” Used for the name in the BOM entry.</td>
</tr>
<tr>
<td>Supplier and ref</td>
<td>Optional</td>
<td>String</td>
<td>Free-form field. May include, for example, specific distributor information.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="QiZM3zY" name="typeahead.parts.part.name">
<segment>
<source>typeahead.parts.part.name</source>
<target>%name% (Part)</target>
</segment>
</unit>
<unit id="WhYL2yX" name="typeahead.parts.assembly.name">
<segment>
<source>typeahead.parts.assembly.name</source>
<target>%name% (Assembly)</target>
</segment>
</unit>
<unit id="4cgba2c" name="projects.build.form.part">
<segment>
<source>projects.build.form.part</source>
<target>Part "%name%"</target>
</segment>
</unit>
<unit id="1bCA1zb" name="projects.build.form.assembly">
<segment>
<source>projects.build.form.assembly</source>
<target>Assembly "%name%"</target>
</segment>
</unit>
<unit id="2cDB2ac" name="projects.build.form.assembly.bom.entry">
<segment>
<source>projects.build.form.assembly.bom.entry</source>
<target>%name% (%quantity% needed)</target>
</segment>
</unit>
<unit id="3dEC3bd" name="projects.build.form.assembly.bom.entry.no.stock">
<segment>
<source>projects.build.form.assembly.bom.entry.no.stock</source>
<target>not in stock</target>
</segment>
</unit>
<unit id="X9HUFrv" name="part.table.actions.error">
<segment state="translated">
<source>part.table.actions.error</source>

View file

@ -4740,6 +4740,18 @@ Subelementos serán desplazados hacia arriba.</target>
<target>Nombre</target>
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Componente)</target>
</segment>
</unit>
<unit id="GW8ZOX7" name="part.table.name.value.for_assembly">
<segment state="translated">
<source>part.table.name.value.for_assembly</source>
<target>%value% (Ensamblaje)</target>
</segment>
</unit>
<unit id="rW_SFJE" name="part.table.id">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:178</note>
@ -9804,6 +9816,18 @@ Elemento 3</target>
<target>Componente</target>
</segment>
</unit>
<unit id="KJuXVR5" name="project.bom.assembly">
<segment state="translated">
<source>project.bom.assembly</source>
<target>Baugruppe</target>
</segment>
</unit>
<unit id="LKvYWS6" name="project.bom.partOrAssembly">
<segment state="translated">
<source>project.bom.partOrAssembly</source>
<target>Auswahl</target>
</segment>
</unit>
<unit id="kGqIirz" name="project.bom.add_entry">
<segment state="translated">
<source>project.bom.add_entry</source>
@ -9882,6 +9906,42 @@ Elemento 3</target>
<target>Archivado</target>
</segment>
</unit>
<unit id="hIIFtI1" name="assembly.edit.status">
<segment state="translated">
<source>assembly.edit.status</source>
<target>Estatus</target>
</segment>
</unit>
<unit id="zRd.psv" name="assembly.status.draft">
<segment state="translated">
<source>assembly.status.draft</source>
<target>Esbozo</target>
</segment>
</unit>
<unit id="_U8bb1t" name="assembly.status.planning">
<segment state="translated">
<source>assembly.status.planning</source>
<target>En planificación</target>
</segment>
</unit>
<unit id="GgUh7RT" name="assembly.status.in_production">
<segment state="translated">
<source>assembly.status.in_production</source>
<target>En producción</target>
</segment>
</unit>
<unit id="IluD8iU" name="assembly.status.finished">
<segment state="translated">
<source>assembly.status.finished</source>
<target>Completado</target>
</segment>
</unit>
<unit id="Mybkd1s" name="assembly.status.archived">
<segment state="translated">
<source>assembly.status.archived</source>
<target>Archivado</target>
</segment>
</unit>
<unit id="9GtmqC1" name="part.new_build_part.error.build_part_already_exists">
<segment state="translated">
<source>part.new_build_part.error.build_part_already_exists</source>
@ -10158,6 +10218,12 @@ Elemento 3</target>
<target>Almacenado</target>
</segment>
</unit>
<unit id="gHU1vgc" name="project.builds.no_stock">
<segment state="translated">
<source>project.builds.no_stock</source>
<target>no se ha especificado stock</target>
</segment>
</unit>
<unit id="s5DQlqF" name="project.builds.needed">
<segment state="translated">
<source>project.builds.needed</source>
@ -10230,6 +10296,12 @@ Elemento 3</target>
<target>Lote objetivo</target>
</segment>
</unit>
<unit id="OsmK1Iv" name="project.build.builds_part_lot_label">
<segment state="translated">
<source>project.build.builds_part_lot_label</source>
<target>%name% (se requiere %quantity%)</target>
</segment>
</unit>
<unit id="5DTAvWG" name="project.builds.number_of_builds">
<segment state="translated">
<source>project.builds.number_of_builds</source>
@ -12368,5 +12440,621 @@ Por favor ten en cuenta que no puedes personificar a un usuario deshabilitado. S
<target>Este componente contiene más de un stock. Cambie la ubicación manualmente para seleccionar el stock deseado.</target>
</segment>
</unit>
<unit id="G2sXzh7" name="assembly.label">
<segment>
<source>assembly.label</source>
<target>Ensamblaje</target>
</segment>
</unit>
<unit id="dL51AVa" name="assembly.caption">
<segment>
<source>assembly.caption</source>
<target>Ensamblaje</target>
</segment>
</unit>
<unit id="G_rlE3w" name="perm.assemblies">
<segment>
<source>perm.assemblies</source>
<target>Ensamblajes</target>
</segment>
</unit>
<unit id="dipIGt4" name="assembly_bom_entry.label">
<segment>
<source>assembly_bom_entry.label</source>
<target>Componentes</target>
</segment>
</unit>
<unit id="TvHlYfl" name="assembly.labelp">
<segment>
<source>assembly.labelp</source>
<target>Ensamblajes</target>
</segment>
</unit>
<unit id="gyRGdfv" name="assembly.edit">
<segment>
<source>assembly.edit</source>
<target>Editar ensamblaje</target>
</segment>
</unit>
<unit id="4Tdtoj_" name="assembly.new">
<segment>
<source>assembly.new</source>
<target>Nuevo ensamblaje</target>
</segment>
</unit>
<unit id="M51YUE." name="assembly.edit.associated_build_part">
<segment>
<source>assembly.edit.associated_build_part</source>
<target>Componente asociado</target>
</segment>
</unit>
<unit id="nH9R9f." name="assembly.edit.associated_build_part.add">
<segment>
<source>assembly.edit.associated_build_part.add</source>
<target>Añadir componente</target>
</segment>
</unit>
<unit id="oVfOk.i" name="assembly.edit.associated_build.hint">
<segment>
<source>assembly.edit.associated_build.hint</source>
<target>Este componente representa las instancias fabricadas del ensamblaje. Indique si se necesitan instancias fabricadas. De lo contrario, las cantidades del componente solo se utilizarán cuando se construya el proyecto correspondiente.</target>
</segment>
</unit>
<unit id="_wZ_JZY" name="assembly.edit.bom.import_bom">
<segment>
<source>assembly.edit.bom.import_bom</source>
<target>Importar componentes</target>
</segment>
</unit>
<unit id="vsmgKMw" name="log.database_updated.failed">
<segment>
<source>log.database_updated.failed</source>
<target>__log.database_updated.failed</target>
</segment>
</unit>
<unit id="GcQrTTE" name="log.database_updated.old_version">
<segment>
<source>log.database_updated.old_version</source>
<target>__log.database_updated.old_version</target>
</segment>
</unit>
<unit id="JJF47vK" name="log.database_updated.new_version">
<segment>
<source>log.database_updated.new_version</source>
<target>__log.database_updated.new_version</target>
</segment>
</unit>
<unit id="iVHS_sh" name="tree.tools.edit.assemblies">
<segment>
<source>tree.tools.edit.assemblies</source>
<target>Ensamblajes</target>
</segment>
</unit>
<unit id="naAMjcH" name="assembly.bom_import.flash.success">
<segment>
<source>assembly.bom_import.flash.success</source>
<target>%count% componente(s) se importaron correctamente al ensamblaje.</target>
</segment>
</unit>
<unit id="ScQhV.o" name="assembly.bom_import.flash.invalid_entries">
<segment>
<source>assembly.bom_import.flash.invalid_entries</source>
<target>¡Error de validación! ¡Revisa el archivo importado!</target>
</segment>
</unit>
<unit id="luj_uCZ" name="assembly.bom_import.flash.invalid_file">
<segment>
<source>assembly.bom_import.flash.invalid_file</source>
<target>No se pudo importar el archivo. Asegúrate de haber seleccionado el tipo de archivo correcto. Mensaje de error: %message%</target>
</segment>
</unit>
<unit id="u7SYWcB" name="assembly.bom.quantity">
<segment>
<source>assembly.bom.quantity</source>
<target>Cantidad</target>
</segment>
</unit>
<unit id="D7dPvPL" name="assembly.bom.mountnames">
<segment>
<source>assembly.bom.mountnames</source>
<target>Nombres de montaje</target>
</segment>
</unit>
<unit id="rswC4eS" name="assembly.bom.instockAmount">
<segment state="translated">
<source>assembly.bom.instockAmount</source>
<target>Cantidad en stock</target>
</segment>
</unit>
<unit id="oNutri3" name="assembly.info.title">
<segment>
<source>assembly.info.title</source>
<target>Información del ensamblaje</target>
</segment>
</unit>
<unit id="aO1rzVQ" name="assembly.info.info.label">
<segment>
<source>assembly.info.info.label</source>
<target>Información</target>
</segment>
</unit>
<unit id="9dOByT_" name="assembly.info.sub_assemblies.label">
<segment>
<source>assembly.info.sub_assemblies.label</source>
<target>Subconjuntos</target>
</segment>
</unit>
<unit id="Hmf0EwN" name="assembly.info.builds.label">
<segment>
<source>assembly.info.builds.label</source>
<target>Construcciones</target>
</segment>
</unit>
<unit id="z3F4Rcu" name="assembly.info.bom_add_parts">
<segment>
<source>assembly.info.bom_add_parts</source>
<target>Añadir piezas</target>
</segment>
</unit>
<unit id="ZbDTUTS" name="assembly.builds.check_assembly_status">
<segment>
<source>assembly.builds.check_assembly_status</source>
<target><![CDATA[El estado actual del ensamblaje es <b>"%assembly_status%"</b>. ¡Por favor, verifica si realmente deseas construir el ensamblaje con este estado!]]></target>
</segment>
</unit>
<unit id="xq1Soad" name="assembly.builds.build_not_possible">
<segment>
<source>assembly.builds.build_not_possible</source>
<target>Construcción no posible: No hay suficientes componentes disponibles</target>
</segment>
</unit>
<unit id="njRyDHQ" name="assembly.builds.following_bom_entries_miss_instock">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock</source>
<target>No hay suficientes piezas en stock para construir este proyecto %number_of_builds% veces. Faltan las siguientes piezas:</target>
</segment>
</unit>
<unit id="sp7Z0MS" name="assembly.builds.build_possible">
<segment>
<source>assembly.builds.build_possible</source>
<target>Construcción posible</target>
</segment>
</unit>
<unit id="1uZzdEl" name="assembly.builds.number_of_builds_possible">
<segment>
<source>assembly.builds.number_of_builds_possible</source>
<target><![CDATA[Disponen de suficientes piezas en stock para construir <b>%max_builds%</b> unidades de este ensamblaje.]]></target>
</segment>
</unit>
<unit id="pW6QLXf" name="assembly.builds.number_of_builds">
<segment>
<source>assembly.builds.number_of_builds</source>
<target>Número de construcciones</target>
</segment>
</unit>
<unit id="mtJqT_a" name="assembly.build.btn_build">
<segment>
<source>assembly.build.btn_build</source>
<target>Construir</target>
</segment>
</unit>
<unit id="LFSVVcP" name="assembly.builds.no_stocked_builds">
<segment>
<source>assembly.builds.no_stocked_builds</source>
<target>Unidades construidas almacenadas</target>
</segment>
</unit>
<unit id="dGFHutJ" name="assembly.info.bom_entries_count">
<segment>
<source>assembly.info.bom_entries_count</source>
<target>Componentes</target>
</segment>
</unit>
<unit id="xJ7oBM4" name="assembly.info.sub_assemblies_count">
<segment>
<source>assembly.info.sub_assemblies_count</source>
<target>Subconjuntos</target>
</segment>
</unit>
<unit id="HZYhTlb" name="assembly.builds.stocked">
<segment>
<source>assembly.builds.stocked</source>
<target>en stock</target>
</segment>
</unit>
<unit id="9EG0PLW" name="assembly.builds.needed">
<segment>
<source>assembly.builds.needed</source>
<target>necesario</target>
</segment>
</unit>
<unit id="tgs_7u9" name="assembly.add_parts_to_assembly">
<segment>
<source>assembly.add_parts_to_assembly</source>
<target>Añadir piezas al ensamblaje</target>
</segment>
</unit>
<unit id="PPsM0Dg" name="assembly.bom.name">
<segment>
<source>assembly.bom.name</source>
<target>Nombre</target>
</segment>
</unit>
<unit id="nUEs.ld" name="assembly.bom.comment">
<segment>
<source>assembly.bom.comment</source>
<target>Comentarios</target>
</segment>
</unit>
<unit id="87YpQ_u" name="assembly.builds.following_bom_entries_miss_instock_n">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock_n</source>
<target>No hay suficientes piezas en stock para construir este ensamblaje %number_of_builds% veces. Faltan las siguientes piezas:</target>
</segment>
</unit>
<unit id="JlQhDdS" name="assembly.build.help">
<segment>
<source>assembly.build.help</source>
<target>Seleccione de qué almacenes se tomarán las piezas necesarias para la construcción (y en qué cantidad). Marque la casilla de cada entrada una vez que haya quitado las piezas, o use la casilla superior para marcarlas todas a la vez.</target>
</segment>
</unit>
<unit id="iP5_QVj" name="assembly.build.required_qty">
<segment>
<source>assembly.build.required_qty</source>
<target>Cantidad requerida</target>
</segment>
</unit>
<unit id="UJpD7n6" name="assembly.import_bom">
<segment>
<source>assembly.import_bom</source>
<target>Importar piezas para ensamblaje</target>
</segment>
</unit>
<unit id="WTasGao" name="assembly.bom.part">
<segment>
<source>assembly.bom.part</source>
<target>Pieza</target>
</segment>
</unit>
<unit id="jHKh8Zp" name="assembly.bom.add_entry">
<segment>
<source>assembly.bom.add_entry</source>
<target>Añadir entrada</target>
</segment>
</unit>
<unit id="RsZ77df" name="assembly.bom.price">
<segment>
<source>assembly.bom.price</source>
<target>Precio</target>
</segment>
</unit>
<unit id="63adIrC" name="assembly.build.dont_check_quantity">
<segment state="translated">
<source>assembly.build.dont_check_quantity</source>
<target>No verificar cantidades</target>
</segment>
</unit>
<unit id="O0DP6tK" name="assembly.build.dont_check_quantity.help">
<segment state="translated">
<source>assembly.build.dont_check_quantity.help</source>
<target>Si se selecciona esta opción, las cantidades seleccionadas se quitarán del inventario independientemente de si hay más o menos de lo necesario para construir el ensamblaje.</target>
</segment>
</unit>
<unit id="9bc0nzK" name="assembly.build.add_builds_to_builds_part">
<segment state="translated">
<source>assembly.build.add_builds_to_builds_part</source>
<target>Añadir unidades construidas a la parte del ensamblaje</target>
</segment>
</unit>
<unit id="nl.jtSx" name="assembly.bom_import.type">
<segment state="translated">
<source>assembly.bom_import.type</source>
<target>Tipo</target>
</segment>
</unit>
<unit id="LtbSLHx" name="assembly.bom_import.type.json">
<segment state="translated">
<source>assembly.bom_import.type.json</source>
<target>JSON para un ensamblaje</target>
</segment>
</unit>
<unit id="clXFAdN" name="assembly.bom_import.type.kicad_pcbnew">
<segment state="translated">
<source>assembly.bom_import.type.kicad_pcbnew</source>
<target>CSV (KiCAD Pcbnew BOM)</target>
</segment>
</unit>
<unit id="0IekETE" name="assembly.bom_import.clear_existing_bom">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom</source>
<target>Eliminar entradas de componentes existentes antes de la importación</target>
</segment>
</unit>
<unit id="S4QY6pA" name="assembly.bom_import.clear_existing_bom.help">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom.help</source>
<target>Si esta opción está seleccionada, se eliminarán todos los componentes existentes en el ensamblaje y serán reemplazados por los datos de los componentes importados.</target>
</segment>
</unit>
<unit id="RVNWPsh" name="assembly.import_bom.template.header.json">
<segment>
<source>assembly.import_bom.template.header.json</source>
<target>Plantilla de importación JSON para un ensamblaje</target>
</segment>
</unit>
<unit id="eU4FfCr" name="assembly.import_bom.template.header.kicad_pcbnew">
<segment>
<source>assembly.import_bom.template.header.kicad_pcbnew</source>
<target>Plantilla de importación CSV (KiCAD Pcbnew BOM) para un ensamblaje</target>
</segment>
</unit>
<unit id="aLomVVS" name="assembly.bom_import.template.entry.name">
<segment>
<source>assembly.bom_import.template.entry.name</source>
<target>Nombre del componente en el ensamblaje</target>
</segment>
</unit>
<unit id="o4ZcLfV" name="assembly.bom_import.template.entry.part.mpnr">
<segment>
<source>assembly.bom_import.template.entry.part.mpnr</source>
<target>Número de parte único dentro del fabricante</target>
</segment>
</unit>
<unit id="n3YbKeU" name="assembly.bom_import.template.entry.part.ipn">
<segment>
<source>assembly.bom_import.template.entry.part.ipn</source>
<target>IPN único del componente</target>
</segment>
</unit>
<unit id="l2SYJP5" name="assembly.bom_import.template.entry.part.name">
<segment>
<source>assembly.bom_import.template.entry.part.name</source>
<target>Nombre único del componente</target>
</segment>
</unit>
<unit id="VgXQ1xW" name="assembly.bom_import.template.entry.part.manufacturer.name">
<segment>
<source>assembly.bom_import.template.entry.part.manufacturer.name</source>
<target>Nombre único del fabricante</target>
</segment>
</unit>
<unit id="MpB.o_L" name="assembly.bom_import.template.entry.part.category.name">
<segment>
<source>assembly.bom_import.template.entry.part.category.name</source>
<target>Nombre único de la categoría</target>
</segment>
</unit>
<unit id="NIcfgj84" name="assembly.bom_import.template.json.table">
<segment>
<source>assembly.bom_import.template.json.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Campo</th>
<th>Condición</th>
<th>Tipo de dato</th>
<th>Descripción</th>
</tr>
</thead>
<tbody>
<tr>
<td>quantity</td>
<td>Obligatorio</td>
<td>Número decimal (Float)</td>
<td>Debe estar presente y contener un valor decimal (Float) mayor que 0.0.</td>
</tr>
<tr>
<td>name</td>
<td>Opcional</td>
<td>Cadena de texto (String)</td>
<td>Si está presente, debe ser una cadena de texto no vacía.</td>
</tr>
<tr>
<td>part</td>
<td>Opcional</td>
<td>Objeto/Array</td>
<td>
Si se proporciona, debe ser un objeto/array y al menos uno de los campos debe estar completado:
<ul>
<li>part.id</li>
<li>part.name</li>
</ul>
</td>
</tr>
<tr>
<td>part.id</td>
<td>Opcional</td>
<td>Entero (Integer)</td>
<td>Entero (Integer) &gt; 0. Corresponde al ID numérico interno del componente en la base de datos.</td>
</tr>
<tr>
<td>part.name</td>
<td>Opcional</td>
<td>Cadena de texto (String)</td>
<td>Cadena de texto no vacía, si no se proporciona part.mpnr o part.ipn.</td>
</tr>
<tr>
<td>part.mpnr</td>
<td>Opcional</td>
<td>Cadena de texto (String)</td>
<td>Cadena de texto no vacía, si no se proporciona part.name o part.ipn.</td>
</tr>
<tr>
<td>part.ipn</td>
<td>Opcional</td>
<td>Cadena de texto (String)</td>
<td>Cadena de texto no vacía, si no se proporciona part.name o part.mpnr.</td>
</tr>
<tr>
<td>part.description</td>
<td>Opcional</td>
<td>Cadena de texto (String) o null</td>
<td>Si está presente, debe ser una cadena de texto no vacía o null.</td>
</tr>
<tr>
<td>part.manufacturer</td>
<td>Opcional</td>
<td>Objeto/Array</td>
<td>
Si está presente, debe ser un objeto/array y al menos uno de los campos debe estar completado:
<ul>
<li>manufacturer.id</li>
<li>manufacturer.name</li>
</ul>
</td>
</tr>
<tr>
<td>manufacturer.id</td>
<td>Opcional</td>
<td>Entero (Integer)</td>
<td>Entero (Integer) &gt; 0. Corresponde al ID numérico interno del fabricante.</td>
</tr>
<tr>
<td>manufacturer.name</td>
<td>Opcional</td>
<td>Cadena de texto (String)</td>
<td>Cadena de texto no vacía, si no se proporciona manufacturer.id.</td>
</tr>
<tr>
<td>part.category</td>
<td>Opcional</td>
<td>Objeto/Array</td>
<td>
Si está presente, debe ser un objeto/array y al menos uno de los campos debe estar completado:
<ul>
<li>category.id</li>
<li>category.name</li>
</ul>
</td>
</tr>
<tr>
<td>category.id</td>
<td>Opcional</td>
<td>Entero (Integer)</td>
<td>Entero (Integer) &gt; 0. Corresponde al ID numérico interno de la categoría del componente.</td>
</tr>
<tr>
<td>category.name</td>
<td>Opcional</td>
<td>Cadena de texto (String)</td>
<td>Cadena de texto no vacía, si no se proporciona category.id.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="cU1bfDa" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns</source>
<target>Columnas esperadas:</target>
</segment>
</unit>
<unit id="gvaB1sb" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns.note">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns.note</source>
<target>
<![CDATA[
<p><strong>Nota:</strong> No se realiza una asociación con componentes específicos de la gestión de categorías.</p>
]]>
</target>
</segment>
</unit>
<unit id="translationUnit6" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Campo</th>
<th>Condición</th>
<th>Tipo de Datos</th>
<th>Descripción</th>
</tr>
</thead>
<tbody>
<tr>
<td>Id</td>
<td>Opcional</td>
<td>Entero</td>
<td>Campo libre. Un número de identificación único para cada componente.</td>
</tr>
<tr>
<td>Designador</td>
<td>Opcional</td>
<td>Cadena de texto</td>
<td>Campo libre. Un designador de referencia único para el componente en la PCB, p. ej., "R1" para la resistencia 1. Se utiliza para nombrar la colocación en el grupo de componentes.</td>
</tr>
<tr>
<td>Package</td>
<td>Opcional</td>
<td>Cadena de texto</td>
<td>Campo libre. El formato o tipo de encapsulado del componente, p. ej., "0805" para resistencias SMD.</td>
</tr>
<tr>
<td>Cantidad</td>
<td>Obligatorio</td>
<td>Entero</td>
<td>El número de componentes idénticos necesarios para crear una instancia única de un ensamblaje.</td>
</tr>
<tr>
<td>Designación</td>
<td>Obligatorio</td>
<td>Cadena de texto</td>
<td>La descripción o función del componente, p. ej., el valor de la resistencia "10kΩ" o el valor del condensador "100nF". Se utiliza para el nombre en la entrada del BOM.</td>
</tr>
<tr>
<td>Proveedor y referencia</td>
<td>Opcional</td>
<td>Cadena de texto</td>
<td>Campo libre. Puede incluir, por ejemplo, información específica del distribuidor.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="QiZM3zY" name="typeahead.parts.part.name">
<segment>
<source>typeahead.parts.part.name</source>
<target>%name% (Componente)</target>
</segment>
</unit>
<unit id="WhYL2yX" name="typeahead.parts.assembly.name">
<segment>
<source>typeahead.parts.assembly.name</source>
<target>%name% (Ensamblaje)</target>
</segment>
</unit>
<unit id="4cgba2c" name="projects.build.form.part">
<segment>
<source>projects.build.form.part</source>
<target>Componente "%name%"</target>
</segment>
</unit>
<unit id="1bCA1zb" name="projects.build.form.assembly">
<segment>
<source>projects.build.form.assembly</source>
<target>Ensamblaje "%name%"</target>
</segment>
</unit>
<unit id="2cDB2ac" name="projects.build.form.assembly.bom.entry">
<segment>
<source>projects.build.form.assembly.bom.entry</source>
<target>%name% (%quantity% necesario)</target>
</segment>
</unit>
<unit id="3dEC3bd" name="projects.build.form.assembly.bom.entry.no.stock">
<segment>
<source>projects.build.form.assembly.bom.entry.no.stock</source>
<target>sin stock</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -4703,6 +4703,18 @@ Si vous avez fait cela de manière incorrecte ou si un ordinateur n'est plus fia
<target>Nom</target>
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Componente)</target>
</segment>
</unit>
<unit id="GW8ZOX7" name="part.table.name.value.for_assembly">
<segment state="translated">
<source>part.table.name.value.for_assembly</source>
<target>%value% (Assemblaggio)</target>
</segment>
</unit>
<unit id="eshqdG." name="part.table.id">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:178</note>
@ -9097,5 +9109,681 @@ 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="KJuXVR5" name="project.bom.assembly">
<segment state="translated">
<source>project.bom.assembly</source>
<target>Assemblage</target>
</segment>
</unit>
<unit id="LKvYWS6" name="project.bom.partOrAssembly">
<segment state="translated">
<source>project.bom.partOrAssembly</source>
<target>Sélection</target>
</segment>
</unit>
<unit id="hIIFtI1" name="assembly.edit.status">
<segment state="translated">
<source>assembly.edit.status</source>
<target>Statut</target>
</segment>
</unit>
<unit id="zRd.psv" name="assembly.status.draft">
<segment state="translated">
<source>assembly.status.draft</source>
<target>Brouillon</target>
</segment>
</unit>
<unit id="_U8bb1t" name="assembly.status.planning">
<segment state="translated">
<source>assembly.status.planning</source>
<target>En planification</target>
</segment>
</unit>
<unit id="GgUh7RT" name="assembly.status.in_production">
<segment state="translated">
<source>assembly.status.in_production</source>
<target>En production</target>
</segment>
</unit>
<unit id="IluD8iU" name="assembly.status.finished">
<segment state="translated">
<source>assembly.status.finished</source>
<target>Terminé</target>
</segment>
</unit>
<unit id="Mybkd1s" name="assembly.status.archived">
<segment state="translated">
<source>assembly.status.archived</source>
<target>Archivé</target>
</segment>
</unit>
<unit id="gHU1vgc" name="project.builds.no_stock">
<segment state="translated">
<source>project.builds.no_stock</source>
<target>aucun stock indiqué</target>
</segment>
</unit>
<unit id="OsmK1Iv" name="project.build.builds_part_lot_label">
<segment state="translated">
<source>project.build.builds_part_lot_label</source>
<target>%name% (%quantity% requis)</target>
</segment>
</unit>
<unit id="G2sXzh7" name="assembly.label">
<segment>
<source>assembly.label</source>
<target>Assemblage</target>
</segment>
</unit>
<unit id="dL51AVa" name="assembly.caption">
<segment>
<source>assembly.caption</source>
<target>Assemblage</target>
</segment>
</unit>
<unit id="G_rlE3w" name="perm.assemblies">
<segment>
<source>perm.assemblies</source>
<target>Assemblages</target>
</segment>
</unit>
<unit id="dipIGt4" name="assembly_bom_entry.label">
<segment>
<source>assembly_bom_entry.label</source>
<target>Composants</target>
</segment>
</unit>
<unit id="TvHlYfl" name="assembly.labelp">
<segment>
<source>assembly.labelp</source>
<target>Assemblages</target>
</segment>
</unit>
<unit id="gyRGdfv" name="assembly.edit">
<segment>
<source>assembly.edit</source>
<target>Modifier l'assemblage</target>
</segment>
</unit>
<unit id="4Tdtoj_" name="assembly.new">
<segment>
<source>assembly.new</source>
<target>Nouvel assemblage</target>
</segment>
</unit>
<unit id="M51YUE." name="assembly.edit.associated_build_part">
<segment>
<source>assembly.edit.associated_build_part</source>
<target>Composant associé</target>
</segment>
</unit>
<unit id="nH9R9f." name="assembly.edit.associated_build_part.add">
<segment>
<source>assembly.edit.associated_build_part.add</source>
<target>Ajouter un composant</target>
</segment>
</unit>
<unit id="oVfOk.i" name="assembly.edit.associated_build.hint">
<segment>
<source>assembly.edit.associated_build.hint</source>
<target>Ce composant représente les instances fabriquées de l'assemblage. Indiquez si des instances fabriquées sont nécessaires. Sinon, les quantités des composants ne seront appliquées que lors de la construction du projet correspondant.</target>
</segment>
</unit>
<unit id="_wZ_JZY" name="assembly.edit.bom.import_bom">
<segment>
<source>assembly.edit.bom.import_bom</source>
<target>Importer des composants</target>
</segment>
</unit>
<unit id="vsmgKMw" name="log.database_updated.failed">
<segment>
<source>log.database_updated.failed</source>
<target>__log.database_updated.failed</target>
</segment>
</unit>
<unit id="GcQrTTE" name="log.database_updated.old_version">
<segment>
<source>log.database_updated.old_version</source>
<target>__log.database_updated.old_version</target>
</segment>
</unit>
<unit id="JJF47vK" name="log.database_updated.new_version">
<segment>
<source>log.database_updated.new_version</source>
<target>__log.database_updated.new_version</target>
</segment>
</unit>
<unit id="iVHS_sh" name="tree.tools.edit.assemblies">
<segment>
<source>tree.tools.edit.assemblies</source>
<target>Assemblages</target>
</segment>
</unit>
<unit id="naAMjcH" name="assembly.bom_import.flash.success">
<segment>
<source>assembly.bom_import.flash.success</source>
<target>%count% composant(s) ont été importé(s) avec succès dans l'assemblage.</target>
</segment>
</unit>
<unit id="ScQhV.o" name="assembly.bom_import.flash.invalid_entries">
<segment>
<source>assembly.bom_import.flash.invalid_entries</source>
<target>Erreur de validation ! Veuillez vérifier le fichier importé !</target>
</segment>
</unit>
<unit id="luj_uCZ" name="assembly.bom_import.flash.invalid_file">
<segment>
<source>assembly.bom_import.flash.invalid_file</source>
<target>Le fichier n'a pas pu être importé. Veuillez vérifier que vous avez sélectionné le bon type de fichier. Message d'erreur : %message%</target>
</segment>
</unit>
<unit id="u7SYWcB" name="assembly.bom.quantity">
<segment>
<source>assembly.bom.quantity</source>
<target>Quantité</target>
</segment>
</unit>
<unit id="D7dPvPL" name="assembly.bom.mountnames">
<segment>
<source>assembly.bom.mountnames</source>
<target>Noms de montage</target>
</segment>
</unit>
<unit id="rswC4eS" name="assembly.bom.instockAmount">
<segment state="translated">
<source>assembly.bom.instockAmount</source>
<target>Quantité en stock</target>
</segment>
</unit>
<unit id="oNutri3" name="assembly.info.title">
<segment>
<source>assembly.info.title</source>
<target>Informations sur l'assemblage</target>
</segment>
</unit>
<unit id="aO1rzVQ" name="assembly.info.info.label">
<segment>
<source>assembly.info.info.label</source>
<target>Informations</target>
</segment>
</unit>
<unit id="9dOByT_" name="assembly.info.sub_assemblies.label">
<segment>
<source>assembly.info.sub_assemblies.label</source>
<target>Sous-ensembles</target>
</segment>
</unit>
<unit id="Hmf0EwN" name="assembly.info.builds.label">
<segment>
<source>assembly.info.builds.label</source>
<target>Constructions</target>
</segment>
</unit>
<unit id="z3F4Rcu" name="assembly.info.bom_add_parts">
<segment>
<source>assembly.info.bom_add_parts</source>
<target>Ajouter des pièces</target>
</segment>
</unit>
<unit id="ZbDTUTS" name="assembly.builds.check_assembly_status">
<segment>
<source>assembly.builds.check_assembly_status</source>
<target><![CDATA[Le statut actuel de l'assemblage est <b>"%assembly_status%"</b>. Vérifiez bien si vous souhaitez construire l'assemblage avec ce statut !]]></target>
</segment>
</unit>
<unit id="xq1Soad" name="assembly.builds.build_not_possible">
<segment>
<source>assembly.builds.build_not_possible</source>
<target>Construction impossible : pièces insuffisantes disponibles</target>
</segment>
</unit>
<unit id="njRyDHQ" name="assembly.builds.following_bom_entries_miss_instock">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock</source>
<target>Il n'y a pas suffisamment de pièces en stock pour construire ce projet %number_of_builds% fois. Les pièces suivantes manquent en stock :</target>
</segment>
</unit>
<unit id="sp7Z0MS" name="assembly.builds.build_possible">
<segment>
<source>assembly.builds.build_possible</source>
<target>Construction possible</target>
</segment>
</unit>
<unit id="1uZzdEl" name="assembly.builds.number_of_builds_possible">
<segment>
<source>assembly.builds.number_of_builds_possible</source>
<target><![CDATA[Vous disposez de suffisamment de pièces en stock pour construire <b>%max_builds%</b> unités de cet assemblage.]]></target>
</segment>
</unit>
<unit id="pW6QLXf" name="assembly.builds.number_of_builds">
<segment>
<source>assembly.builds.number_of_builds</source>
<target>Nombre d'assemblages à construire</target>
</segment>
</unit>
<unit id="mtJqT_a" name="assembly.build.btn_build">
<segment>
<source>assembly.build.btn_build</source>
<target>Construire</target>
</segment>
</unit>
<unit id="LFSVVcP" name="assembly.builds.no_stocked_builds">
<segment>
<source>assembly.builds.no_stocked_builds</source>
<target>Nombre d'instances construites en stock</target>
</segment>
</unit>
<unit id="dGFHutJ" name="assembly.info.bom_entries_count">
<segment>
<source>assembly.info.bom_entries_count</source>
<target>Composants</target>
</segment>
</unit>
<unit id="xJ7oBM4" name="assembly.info.sub_assemblies_count">
<segment>
<source>assembly.info.sub_assemblies_count</source>
<target>Sous-ensembles</target>
</segment>
</unit>
<unit id="HZYhTlb" name="assembly.builds.stocked">
<segment>
<source>assembly.builds.stocked</source>
<target>en stock</target>
</segment>
</unit>
<unit id="9EG0PLW" name="assembly.builds.needed">
<segment>
<source>assembly.builds.needed</source>
<target>nécessaire</target>
</segment>
</unit>
<unit id="tgs_7u9" name="assembly.add_parts_to_assembly">
<segment>
<source>assembly.add_parts_to_assembly</source>
<target>Ajouter des pièces à l'assemblage</target>
</segment>
</unit>
<unit id="PPsM0Dg" name="assembly.bom.name">
<segment>
<source>assembly.bom.name</source>
<target>Nom</target>
</segment>
</unit>
<unit id="nUEs.ld" name="assembly.bom.comment">
<segment>
<source>assembly.bom.comment</source>
<target>Commentaires</target>
</segment>
</unit>
<unit id="87YpQ_u" name="assembly.builds.following_bom_entries_miss_instock_n">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock_n</source>
<target>Il n'y a pas suffisamment de pièces en stock pour construire cet assemblage %number_of_builds% fois. Les pièces suivantes manquent en stock :</target>
</segment>
</unit>
<unit id="JlQhDdS" name="assembly.build.help">
<segment>
<source>assembly.build.help</source>
<target>Sélectionnez les stocks à partir desquels les pièces nécessaires à la construction seront prises (et en quelle quantité). Vérifiez chaque pièce en les retirant, ou utilisez la case supérieure pour les sélectionner toutes à la fois.</target>
</segment>
</unit>
<unit id="iP5_QVj" name="assembly.build.required_qty">
<segment>
<source>assembly.build.required_qty</source>
<target>Quantité requise</target>
</segment>
</unit>
<unit id="UJpD7n6" name="assembly.import_bom">
<segment>
<source>assembly.import_bom</source>
<target>Importer des pièces pour l'assemblage</target>
</segment>
</unit>
<unit id="WTasGao" name="assembly.bom.part">
<segment>
<source>assembly.bom.part</source>
<target>Pièce</target>
</segment>
</unit>
<unit id="jHKh8Zp" name="assembly.bom.add_entry">
<segment>
<source>assembly.bom.add_entry</source>
<target>Ajouter une ligne</target>
</segment>
</unit>
<unit id="RsZ77df" name="assembly.bom.price">
<segment>
<source>assembly.bom.price</source>
<target>Prix</target>
</segment>
</unit>
<unit id="63adIrC" name="assembly.build.dont_check_quantity">
<segment state="translated">
<source>assembly.build.dont_check_quantity</source>
<target>Ne pas vérifier les quantités</target>
</segment>
</unit>
<unit id="O0DP6tK" name="assembly.build.dont_check_quantity.help">
<segment state="translated">
<source>assembly.build.dont_check_quantity.help</source>
<target>Si cette option est activée, les quantités sélectionnées seront retirées du stock, quelle que soit leur suffisance pour lassemblage.</target>
</segment>
</unit>
<unit id="9bc0nzK" name="assembly.build.add_builds_to_builds_part">
<segment state="translated">
<source>assembly.build.add_builds_to_builds_part</source>
<target>Ajouter les unités construites à la pièce assemblée</target>
</segment>
</unit>
<unit id="nl.jtSx" name="assembly.bom_import.type">
<segment state="translated">
<source>assembly.bom_import.type</source>
<target>Type</target>
</segment>
</unit>
<unit id="LtbSLHx" name="assembly.bom_import.type.json">
<segment state="translated">
<source>assembly.bom_import.type.json</source>
<target>JSON pour un assemblage</target>
</segment>
</unit>
<unit id="clXFAdN" name="assembly.bom_import.type.kicad_pcbnew">
<segment state="translated">
<source>assembly.bom_import.type.kicad_pcbnew</source>
<target>CSV (KiCAD Pcbnew BOM)</target>
</segment>
</unit>
<unit id="0IekETE" name="assembly.bom_import.clear_existing_bom">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom</source>
<target>Supprimer les entrées de pièces existantes avant limportation</target>
</segment>
</unit>
<unit id="S4QY6pA" name="assembly.bom_import.clear_existing_bom.help">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom.help</source>
<target>Si cette option est cochée, toutes les pièces existantes dans lassemblage seront supprimées et remplacées par les données importées.</target>
</segment>
</unit>
<unit id="RVNWPsh" name="assembly.import_bom.template.header.json">
<segment>
<source>assembly.import_bom.template.header.json</source>
<target>Modèle dimportation JSON pour un assemblage</target>
</segment>
</unit>
<unit id="eU4FfCr" name="assembly.import_bom.template.header.kicad_pcbnew">
<segment>
<source>assembly.import_bom.template.header.kicad_pcbnew</source>
<target>Modèle dimportation CSV (KiCAD Pcbnew BOM) pour un assemblage</target>
</segment>
</unit>
<unit id="aLomVVS" name="assembly.bom_import.template.entry.name">
<segment>
<source>assembly.bom_import.template.entry.name</source>
<target>Nom de la pièce dans lassemblage</target>
</segment>
</unit>
<unit id="o4ZcLfV" name="assembly.bom_import.template.entry.part.mpnr">
<segment>
<source>assembly.bom_import.template.entry.part.mpnr</source>
<target>Numéro unique de la pièce chez le fabricant</target>
</segment>
</unit>
<unit id="n3YbKeU" name="assembly.bom_import.template.entry.part.ipn">
<segment>
<source>assembly.bom_import.template.entry.part.ipn</source>
<target>Numéro IPN unique de la pièce</target>
</segment>
</unit>
<unit id="l2SYJP5" name="assembly.bom_import.template.entry.part.name">
<segment>
<source>assembly.bom_import.template.entry.part.name</source>
<target>Nom unique de la pièce</target>
</segment>
</unit>
<unit id="VgXQ1xW" name="assembly.bom_import.template.entry.part.manufacturer.name">
<segment>
<source>assembly.bom_import.template.entry.part.manufacturer.name</source>
<target>Nom unique du fabricant</target>
</segment>
</unit>
<unit id="MpB.o_L" name="assembly.bom_import.template.entry.part.category.name">
<segment>
<source>assembly.bom_import.template.entry.part.category.name</source>
<target>Nom unique de la catégorie</target>
</segment>
</unit>
<unit id="NIcfgj84" name="assembly.bom_import.template.json.table">
<segment>
<source>assembly.bom_import.template.json.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Champ</th>
<th>Condition</th>
<th>Type de données</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>quantity</td>
<td>Obligatoire</td>
<td>Nombre décimal (Float)</td>
<td>Doit être présent et contenir une valeur décimale (Float) supérieure à 0,0.</td>
</tr>
<tr>
<td>name</td>
<td>Optionnel</td>
<td>Chaîne (String)</td>
<td>Si présent, doit être une chaîne non vide.</td>
</tr>
<tr>
<td>part</td>
<td>Optionnel</td>
<td>Objet/Tableau</td>
<td>
Si fourni, doit être un objet/un tableau et au moins un des champs doit être rempli :
<ul>
<li>part.id</li>
<li>part.name</li>
</ul>
</td>
</tr>
<tr>
<td>part.id</td>
<td>Optionnel</td>
<td>Entier (Integer)</td>
<td>Entier (Integer) &gt; 0. Correspond à l'ID numérique interne dans Part-DB du composant.</td>
</tr>
<tr>
<td>part.name</td>
<td>Optionnel</td>
<td>Chaîne (String)</td>
<td>Chaîne non vide si part.mpnr ou part.ipn ne sont pas fournis.</td>
</tr>
<tr>
<td>part.mpnr</td>
<td>Optionnel</td>
<td>Chaîne (String)</td>
<td>Chaîne non vide si part.name ou part.ipn ne sont pas fournis.</td>
</tr>
<tr>
<td>part.ipn</td>
<td>Optionnel</td>
<td>Chaîne (String)</td>
<td>Chaîne non vide si part.name ou part.mpnr ne sont pas fournis.</td>
</tr>
<tr>
<td>part.description</td>
<td>Optionnel</td>
<td>Chaîne ou null</td>
<td>Si présent, doit être une chaîne non vide ou null.</td>
</tr>
<tr>
<td>part.manufacturer</td>
<td>Optionnel</td>
<td>Objet/Tableau</td>
<td>
Si présent, doit être un objet/un tableau et au moins un des champs doit être rempli :
<ul>
<li>manufacturer.id</li>
<li>manufacturer.name</li>
</ul>
</td>
</tr>
<tr>
<td>manufacturer.id</td>
<td>Optionnel</td>
<td>Entier (Integer)</td>
<td>Entier (Integer) &gt; 0. Correspond à l'ID numérique interne du fabricant.</td>
</tr>
<tr>
<td>manufacturer.name</td>
<td>Optionnel</td>
<td>Chaîne (String)</td>
<td>Chaîne non vide si manufacturer.id n'est pas fourni.</td>
</tr>
<tr>
<td>part.category</td>
<td>Optionnel</td>
<td>Objet/Tableau</td>
<td>
Si présent, doit être un objet/un tableau et au moins un des champs doit être rempli :
<ul>
<li>category.id</li>
<li>category.name</li>
</ul>
</td>
</tr>
<tr>
<td>category.id</td>
<td>Optionnel</td>
<td>Entier (Integer)</td>
<td>Entier (Integer) &gt; 0. Correspond à l'ID numérique interne de la catégorie du composant.</td>
</tr>
<tr>
<td>category.name</td>
<td>Optionnel</td>
<td>Chaîne (String)</td>
<td>Chaîne non vide si category.id n'est pas fourni.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="cU1bfDa" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns</source>
<target>Colonnes attendues :</target>
</segment>
</unit>
<unit id="gvaB1sb" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns.note">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns.note</source>
<target>
<![CDATA[
<p><strong>Remarque :</strong> Aucun mappage n'est effectué avec des composants spécifiques issus de la gestion des catégories.</p>
]]>
</target>
</segment>
</unit>
<unit id="translationUnit7" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Champ</th>
<th>Condition</th>
<th>Type de Données</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Id</td>
<td>Optionnel</td>
<td>Entier</td>
<td>Champ libre. Un numéro d'identification unique pour chaque composant.</td>
</tr>
<tr>
<td>Designeur</td>
<td>Optionnel</td>
<td>Chaîne</td>
<td>Champ libre. Une référence de désignation unique du composant sur le PCB, par exemple, "R1" pour la résistance 1. Utilisé pour nommer la position au sein du groupe de composants.</td>
</tr>
<tr>
<td>Boîtier</td>
<td>Optionnel</td>
<td>Chaîne</td>
<td>Champ libre. Le type ou format d'encapsulation du composant, par exemple, "0805" pour des résistances CMS.</td>
</tr>
<tr>
<td>Quantité</td>
<td>Obligatoire</td>
<td>Entier</td>
<td>Le nombre de composants identiques nécessaires pour créer une instance unique d'un ensemble.</td>
</tr>
<tr>
<td>Désignation</td>
<td>Obligatoire</td>
<td>Chaîne</td>
<td>La description ou la fonction du composant, par exemple, la valeur de résistance "10kΩ" ou la valeur de condensateur "100nF". Utilisé comme nom dans l'entrée de la nomenclature (BOM).</td>
</tr>
<tr>
<td>Fournisseur et réf</td>
<td>Optionnel</td>
<td>Chaîne</td>
<td>Champ libre. Peut inclure, par exemple, des informations spécifiques au distributeur.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="QiZM3zY" name="typeahead.parts.part.name">
<segment>
<source>typeahead.parts.part.name</source>
<target>%name% (pièce)</target>
</segment>
</unit>
<unit id="WhYL2yX" name="typeahead.parts.assembly.name">
<segment>
<source>typeahead.parts.assembly.name</source>
<target>%name% (assemblage)</target>
</segment>
</unit>
<unit id="4cgba2c" name="projects.build.form.part">
<segment>
<source>projects.build.form.part</source>
<target>Pièce "%name%"</target>
</segment>
</unit>
<unit id="1bCA1zb" name="projects.build.form.assembly">
<segment>
<source>projects.build.form.assembly</source>
<target>Assemblage "%name%"</target>
</segment>
</unit>
<unit id="2cDB2ac" name="projects.build.form.assembly.bom.entry">
<segment>
<source>projects.build.form.assembly.bom.entry</source>
<target>%name% (%quantity% nécessaires)</target>
</segment>
</unit>
<unit id="3dEC3bd" name="projects.build.form.assembly.bom.entry.no.stock">
<segment>
<source>projects.build.form.assembly.bom.entry.no.stock</source>
<target>Non disponible en stock</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -4742,6 +4742,18 @@ Se è stato fatto in modo errato o se un computer non è più attendibile, puoi
<target>Nome</target>
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Componente)</target>
</segment>
</unit>
<unit id="GW8ZOX7" name="part.table.name.value.for_assembly">
<segment state="translated">
<source>part.table.name.value.for_assembly</source>
<target>%value% (Assemblaggio)</target>
</segment>
</unit>
<unit id="rW_SFJE" name="part.table.id">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:178</note>
@ -9806,6 +9818,18 @@ Element 3</target>
<target>Componente</target>
</segment>
</unit>
<unit id="KJuXVR5" name="project.bom.assembly">
<segment state="translated">
<source>project.bom.assembly</source>
<target>Assemblaggio</target>
</segment>
</unit>
<unit id="LKvYWS6" name="project.bom.partOrAssembly">
<segment state="translated">
<source>project.bom.partOrAssembly</source>
<target>Selezione</target>
</segment>
</unit>
<unit id="kGqIirz" name="project.bom.add_entry">
<segment state="translated">
<source>project.bom.add_entry</source>
@ -9884,6 +9908,42 @@ Element 3</target>
<target>Archiviato</target>
</segment>
</unit>
<unit id="hIIFtI1" name="assembly.edit.status">
<segment state="translated">
<source>assembly.edit.status</source>
<target>Stato</target>
</segment>
</unit>
<unit id="zRd.psv" name="assembly.status.draft">
<segment state="translated">
<source>assembly.status.draft</source>
<target>Bozza</target>
</segment>
</unit>
<unit id="_U8bb1t" name="assembly.status.planning">
<segment state="translated">
<source>assembly.status.planning</source>
<target>In pianificazione</target>
</segment>
</unit>
<unit id="GgUh7RT" name="assembly.status.in_production">
<segment state="translated">
<source>assembly.status.in_production</source>
<target>In produzione</target>
</segment>
</unit>
<unit id="IluD8iU" name="assembly.status.finished">
<segment state="translated">
<source>assembly.status.finished</source>
<target>Completato</target>
</segment>
</unit>
<unit id="Mybkd1s" name="assembly.status.archived">
<segment state="translated">
<source>assembly.status.archived</source>
<target>Archiviato</target>
</segment>
</unit>
<unit id="9GtmqC1" name="part.new_build_part.error.build_part_already_exists">
<segment state="translated">
<source>part.new_build_part.error.build_part_already_exists</source>
@ -10160,6 +10220,12 @@ Element 3</target>
<target>a magazzino</target>
</segment>
</unit>
<unit id="gHU1vgc" name="project.builds.no_stock">
<segment state="translated">
<source>project.builds.no_stock</source>
<target>nessuna scorta specificata</target>
</segment>
</unit>
<unit id="s5DQlqF" name="project.builds.needed">
<segment state="translated">
<source>project.builds.needed</source>
@ -10232,6 +10298,12 @@ Element 3</target>
<target>Lotto target</target>
</segment>
</unit>
<unit id="OsmK1Iv" name="project.build.builds_part_lot_label">
<segment state="translated">
<source>project.build.builds_part_lot_label</source>
<target>%name% (%quantity% richiesti)</target>
</segment>
</unit>
<unit id="5DTAvWG" name="project.builds.number_of_builds">
<segment state="translated">
<source>project.builds.number_of_builds</source>
@ -12346,6 +12418,622 @@ Notare che non è possibile impersonare un utente disattivato. Quando si prova a
<target>Visualizza la versione esterna</target>
</segment>
</unit>
<unit id="G2sXzh7" name="assembly.label">
<segment>
<source>assembly.label</source>
<target>Assemblaggio</target>
</segment>
</unit>
<unit id="dL51AVa" name="assembly.caption">
<segment>
<source>assembly.caption</source>
<target>Assemblaggio</target>
</segment>
</unit>
<unit id="G_rlE3w" name="perm.assemblies">
<segment>
<source>perm.assemblies</source>
<target>Assemblaggi</target>
</segment>
</unit>
<unit id="dipIGt4" name="assembly_bom_entry.label">
<segment>
<source>assembly_bom_entry.label</source>
<target>Componenti</target>
</segment>
</unit>
<unit id="TvHlYfl" name="assembly.labelp">
<segment>
<source>assembly.labelp</source>
<target>Assemblaggi</target>
</segment>
</unit>
<unit id="gyRGdfv" name="assembly.edit">
<segment>
<source>assembly.edit</source>
<target>Modifica assemblaggio</target>
</segment>
</unit>
<unit id="4Tdtoj_" name="assembly.new">
<segment>
<source>assembly.new</source>
<target>Nuovo assemblaggio</target>
</segment>
</unit>
<unit id="M51YUE." name="assembly.edit.associated_build_part">
<segment>
<source>assembly.edit.associated_build_part</source>
<target>Componente associato</target>
</segment>
</unit>
<unit id="nH9R9f." name="assembly.edit.associated_build_part.add">
<segment>
<source>assembly.edit.associated_build_part.add</source>
<target>Aggiungi componente</target>
</segment>
</unit>
<unit id="oVfOk.i" name="assembly.edit.associated_build.hint">
<segment>
<source>assembly.edit.associated_build.hint</source>
<target>Questo componente rappresenta le istanze fabbricate dell'assemblaggio. Specificare se sono necessarie istanze fabbricate. In caso contrario, le quantità di componenti verranno utilizzate solo durante la costruzione del progetto corrispondente.</target>
</segment>
</unit>
<unit id="_wZ_JZY" name="assembly.edit.bom.import_bom">
<segment>
<source>assembly.edit.bom.import_bom</source>
<target>Importa componenti</target>
</segment>
</unit>
<unit id="vsmgKMw" name="log.database_updated.failed">
<segment>
<source>log.database_updated.failed</source>
<target>__log.database_updated.failed</target>
</segment>
</unit>
<unit id="GcQrTTE" name="log.database_updated.old_version">
<segment>
<source>log.database_updated.old_version</source>
<target>__log.database_updated.old_version</target>
</segment>
</unit>
<unit id="JJF47vK" name="log.database_updated.new_version">
<segment>
<source>log.database_updated.new_version</source>
<target>__log.database_updated.new_version</target>
</segment>
</unit>
<unit id="iVHS_sh" name="tree.tools.edit.assemblies">
<segment>
<source>tree.tools.edit.assemblies</source>
<target>Assemblaggi</target>
</segment>
</unit>
<unit id="naAMjcH" name="assembly.bom_import.flash.success">
<segment>
<source>assembly.bom_import.flash.success</source>
<target>%count% componente(i) importato(i) correttamente nell'assemblaggio.</target>
</segment>
</unit>
<unit id="ScQhV.o" name="assembly.bom_import.flash.invalid_entries">
<segment>
<source>assembly.bom_import.flash.invalid_entries</source>
<target>Errore di convalida! Controlla il file importato!</target>
</segment>
</unit>
<unit id="luj_uCZ" name="assembly.bom_import.flash.invalid_file">
<segment>
<source>assembly.bom_import.flash.invalid_file</source>
<target>Impossibile importare il file. Assicurati di aver selezionato il tipo di file corretto. Messaggio di errore: %message%</target>
</segment>
</unit>
<unit id="u7SYWcB" name="assembly.bom.quantity">
<segment>
<source>assembly.bom.quantity</source>
<target>Quantità</target>
</segment>
</unit>
<unit id="D7dPvPL" name="assembly.bom.mountnames">
<segment>
<source>assembly.bom.mountnames</source>
<target>Nomi di montaggio</target>
</segment>
</unit>
<unit id="rswC4eS" name="assembly.bom.instockAmount">
<segment state="translated">
<source>assembly.bom.instockAmount</source>
<target>Quantità in magazzino</target>
</segment>
</unit>
<unit id="oNutri3" name="assembly.info.title">
<segment>
<source>assembly.info.title</source>
<target>Informazioni sul gruppo</target>
</segment>
</unit>
<unit id="aO1rzVQ" name="assembly.info.info.label">
<segment>
<source>assembly.info.info.label</source>
<target>Info</target>
</segment>
</unit>
<unit id="9dOByT_" name="assembly.info.sub_assemblies.label">
<segment>
<source>assembly.info.sub_assemblies.label</source>
<target>Sotto-gruppi</target>
</segment>
</unit>
<unit id="Hmf0EwN" name="assembly.info.builds.label">
<segment>
<source>assembly.info.builds.label</source>
<target>Costruzioni</target>
</segment>
</unit>
<unit id="z3F4Rcu" name="assembly.info.bom_add_parts">
<segment>
<source>assembly.info.bom_add_parts</source>
<target>Aggiungi componenti</target>
</segment>
</unit>
<unit id="ZbDTUTS" name="assembly.builds.check_assembly_status">
<segment>
<source>assembly.builds.check_assembly_status</source>
<target><![CDATA[Lo stato attuale del gruppo è <b>"%assembly_status%"</b>. Controlla se vuoi davvero costruire il gruppo con questo stato!]]></target>
</segment>
</unit>
<unit id="xq1Soad" name="assembly.builds.build_not_possible">
<segment>
<source>assembly.builds.build_not_possible</source>
<target>Costruzione impossibile: componenti insufficienti disponibili</target>
</segment>
</unit>
<unit id="njRyDHQ" name="assembly.builds.following_bom_entries_miss_instock">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock</source>
<target>Non ci sono abbastanza componenti in magazzino per costruire questo progetto %number_of_builds% volte. Mancano i seguenti componenti:</target>
</segment>
</unit>
<unit id="sp7Z0MS" name="assembly.builds.build_possible">
<segment>
<source>assembly.builds.build_possible</source>
<target>Costruzione possibile</target>
</segment>
</unit>
<unit id="1uZzdEl" name="assembly.builds.number_of_builds_possible">
<segment>
<source>assembly.builds.number_of_builds_possible</source>
<target><![CDATA[Hai abbastanza pezzi in magazzino per costruire <b>%max_builds%</b> unità di questo gruppo.]]></target>
</segment>
</unit>
<unit id="pW6QLXf" name="assembly.builds.number_of_builds">
<segment>
<source>assembly.builds.number_of_builds</source>
<target>Numero di gruppi da costruire</target>
</segment>
</unit>
<unit id="mtJqT_a" name="assembly.build.btn_build">
<segment>
<source>assembly.build.btn_build</source>
<target>Costruire</target>
</segment>
</unit>
<unit id="LFSVVcP" name="assembly.builds.no_stocked_builds">
<segment>
<source>assembly.builds.no_stocked_builds</source>
<target>Numero di istanze costruite in magazzino</target>
</segment>
</unit>
<unit id="dGFHutJ" name="assembly.info.bom_entries_count">
<segment>
<source>assembly.info.bom_entries_count</source>
<target>Componenti</target>
</segment>
</unit>
<unit id="xJ7oBM4" name="assembly.info.sub_assemblies_count">
<segment>
<source>assembly.info.sub_assemblies_count</source>
<target>Sotto-gruppi</target>
</segment>
</unit>
<unit id="HZYhTlb" name="assembly.builds.stocked">
<segment>
<source>assembly.builds.stocked</source>
<target>disponibile</target>
</segment>
</unit>
<unit id="9EG0PLW" name="assembly.builds.needed">
<segment>
<source>assembly.builds.needed</source>
<target>necessari</target>
</segment>
</unit>
<unit id="tgs_7u9" name="assembly.add_parts_to_assembly">
<segment>
<source>assembly.add_parts_to_assembly</source>
<target>Aggiungi componenti al gruppo</target>
</segment>
</unit>
<unit id="PPsM0Dg" name="assembly.bom.name">
<segment>
<source>assembly.bom.name</source>
<target>Nome</target>
</segment>
</unit>
<unit id="nUEs.ld" name="assembly.bom.comment">
<segment>
<source>assembly.bom.comment</source>
<target>Commenti</target>
</segment>
</unit>
<unit id="87YpQ_u" name="assembly.builds.following_bom_entries_miss_instock_n">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock_n</source>
<target>Non ci sono abbastanza componenti in magazzino per costruire questo gruppo %number_of_builds% volte. Mancano i seguenti componenti:</target>
</segment>
</unit>
<unit id="JlQhDdS" name="assembly.build.help">
<segment>
<source>assembly.build.help</source>
<target>Seleziona i magazzini da cui prelevare i componenti necessari per la costruzione (e in che quantità). Spunta ciascun componente una volta prelevato, oppure utilizza la casella superiore per selezionare tutto in una volta.</target>
</segment>
</unit>
<unit id="iP5_QVj" name="assembly.build.required_qty">
<segment>
<source>assembly.build.required_qty</source>
<target>Quantità necessaria</target>
</segment>
</unit>
<unit id="UJpD7n6" name="assembly.import_bom">
<segment>
<source>assembly.import_bom</source>
<target>Importa componenti per il gruppo</target>
</segment>
</unit>
<unit id="WTasGao" name="assembly.bom.part">
<segment>
<source>assembly.bom.part</source>
<target>Componente</target>
</segment>
</unit>
<unit id="jHKh8Zp" name="assembly.bom.add_entry">
<segment>
<source>assembly.bom.add_entry</source>
<target>Aggiungi voce</target>
</segment>
</unit>
<unit id="RsZ77df" name="assembly.bom.price">
<segment>
<source>assembly.bom.price</source>
<target>Prezzo</target>
</segment>
</unit>
<unit id="63adIrC" name="assembly.build.dont_check_quantity">
<segment state="translated">
<source>assembly.build.dont_check_quantity</source>
<target>Non controllare le quantità</target>
</segment>
</unit>
<unit id="O0DP6tK" name="assembly.build.dont_check_quantity.help">
<segment state="translated">
<source>assembly.build.dont_check_quantity.help</source>
<target>Se abilitata, le quantità selezionate verranno rimosse dal magazzino indipendentemente dalla loro sufficienza per il gruppo.</target>
</segment>
</unit>
<unit id="9bc0nzK" name="assembly.build.add_builds_to_builds_part">
<segment state="translated">
<source>assembly.build.add_builds_to_builds_part</source>
<target>Aggiungi istanze costruite al gruppo componenti</target>
</segment>
</unit>
<unit id="nl.jtSx" name="assembly.bom_import.type">
<segment state="translated">
<source>assembly.bom_import.type</source>
<target>Tipo</target>
</segment>
</unit>
<unit id="LtbSLHx" name="assembly.bom_import.type.json">
<segment state="translated">
<source>assembly.bom_import.type.json</source>
<target>JSON per un gruppo</target>
</segment>
</unit>
<unit id="clXFAdN" name="assembly.bom_import.type.kicad_pcbnew">
<segment state="translated">
<source>assembly.bom_import.type.kicad_pcbnew</source>
<target>CSV (KiCAD Pcbnew)</target>
</segment>
</unit>
<unit id="0IekETE" name="assembly.bom_import.clear_existing_bom">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom</source>
<target>Elimina i componenti esistenti prima di importare</target>
</segment>
</unit>
<unit id="S4QY6pA" name="assembly.bom_import.clear_existing_bom.help">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom.help</source>
<target>Se abilitata, tutti i componenti esistenti verranno rimossi e sostituiti dai dati importati.</target>
</segment>
</unit>
<unit id="RVNWPsh" name="assembly.import_bom.template.header.json">
<segment>
<source>assembly.import_bom.template.header.json</source>
<target>Template di importazione JSON per un gruppo</target>
</segment>
</unit>
<unit id="eU4FfCr" name="assembly.import_bom.template.header.kicad_pcbnew">
<segment>
<source>assembly.import_bom.template.header.kicad_pcbnew</source>
<target>Template di importazione CSV (KiCAD Pcbnew BOM) per un gruppo</target>
</segment>
</unit>
<unit id="aLomVVS" name="assembly.bom_import.template.entry.name">
<segment>
<source>assembly.bom_import.template.entry.name</source>
<target>Nome del componente nel gruppo</target>
</segment>
</unit>
<unit id="o4ZcLfV" name="assembly.bom_import.template.entry.part.mpnr">
<segment>
<source>assembly.bom_import.template.entry.part.mpnr</source>
<target>Numero univoco del componente del produttore</target>
</segment>
</unit>
<unit id="n3YbKeU" name="assembly.bom_import.template.entry.part.ipn">
<segment>
<source>assembly.bom_import.template.entry.part.ipn</source>
<target>IPN univoco del componente</target>
</segment>
</unit>
<unit id="l2SYJP5" name="assembly.bom_import.template.entry.part.name">
<segment>
<source>assembly.bom_import.template.entry.part.name</source>
<target>Nome univoco del componente</target>
</segment>
</unit>
<unit id="VgXQ1xW" name="assembly.bom_import.template.entry.part.manufacturer.name">
<segment>
<source>assembly.bom_import.template.entry.part.manufacturer.name</source>
<target>Nome univoco del produttore</target>
</segment>
</unit>
<unit id="MpB.o_L" name="assembly.bom_import.template.entry.part.category.name">
<segment>
<source>assembly.bom_import.template.entry.part.category.name</source>
<target>Nome univoco della categoria</target>
</segment>
</unit>
<unit id="NIcfgj84" name="assembly.bom_import.template.json.table">
<segment>
<source>assembly.bom_import.template.json.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Campo</th>
<th>Condizione</th>
<th>Tipo di dato</th>
<th>Descrizione</th>
</tr>
</thead>
<tbody>
<tr>
<td>quantity</td>
<td>Obbligatorio</td>
<td>Numero decimale (Float)</td>
<td>Deve essere presente e contenere un valore decimale (Float) maggiore di 0,0.</td>
</tr>
<tr>
<td>name</td>
<td>Opzionale</td>
<td>Stringa (String)</td>
<td>Se presente, deve essere una stringa non vuota.</td>
</tr>
<tr>
<td>part</td>
<td>Opzionale</td>
<td>Oggetto/Array</td>
<td>
Se fornito, deve essere un oggetto/un array e almeno uno dei campi deve essere compilato:
<ul>
<li>part.id</li>
<li>part.name</li>
</ul>
</td>
</tr>
<tr>
<td>part.id</td>
<td>Opzionale</td>
<td>Intero (Integer)</td>
<td>Intero (Integer) &gt; 0. Corrisponde all'ID numerico interno di Part-DB per il componente.</td>
</tr>
<tr>
<td>part.name</td>
<td>Opzionale</td>
<td>Stringa (String)</td>
<td>Stringa non vuota se part.mpnr o part.ipn non sono forniti.</td>
</tr>
<tr>
<td>part.mpnr</td>
<td>Opzionale</td>
<td>Stringa (String)</td>
<td>Stringa non vuota se part.name o part.ipn non sono forniti.</td>
</tr>
<tr>
<td>part.ipn</td>
<td>Opzionale</td>
<td>Stringa (String)</td>
<td>Stringa non vuota se part.name o part.mpnr non sono forniti.</td>
</tr>
<tr>
<td>part.description</td>
<td>Opzionale</td>
<td>Stringa o null</td>
<td>Se presente, deve essere una stringa non vuota o null.</td>
</tr>
<tr>
<td>part.manufacturer</td>
<td>Opzionale</td>
<td>Oggetto/Array</td>
<td>
Se presente, deve essere un oggetto/un array e almeno uno dei campi deve essere compilato:
<ul>
<li>manufacturer.id</li>
<li>manufacturer.name</li>
</ul>
</td>
</tr>
<tr>
<td>manufacturer.id</td>
<td>Opzionale</td>
<td>Intero (Integer)</td>
<td>Intero (Integer) &gt; 0. Corrisponde all'ID numerico interno del produttore.</td>
</tr>
<tr>
<td>manufacturer.name</td>
<td>Opzionale</td>
<td>Stringa (String)</td>
<td>Stringa non vuota se manufacturer.id non è fornito.</td>
</tr>
<tr>
<td>part.category</td>
<td>Opzionale</td>
<td>Oggetto/Array</td>
<td>
Se presente, deve essere un oggetto/un array e almeno uno dei campi deve essere compilato:
<ul>
<li>category.id</li>
<li>category.name</li>
</ul>
</td>
</tr>
<tr>
<td>category.id</td>
<td>Opzionale</td>
<td>Intero (Integer)</td>
<td>Intero (Integer) &gt; 0. Corrisponde all'ID numerico interno della categoria del componente.</td>
</tr>
<tr>
<td>category.name</td>
<td>Opzionale</td>
<td>Stringa (String)</td>
<td>Stringa non vuota se category.id non è fornito.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="cU1bfDa" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns</source>
<target>Colonne previste:</target>
</segment>
</unit>
<unit id="gvaB1sb" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns.note">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns.note</source>
<target>
<![CDATA[
<p><strong>Nota:</strong> Non viene eseguita alcuna mappatura con componenti specifici dalla gestione delle categorie.</p>
]]>
</target>
</segment>
</unit>
<unit id="translationUnit8" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Campo</th>
<th>Condizione</th>
<th>Tipo di Dati</th>
<th>Descrizione</th>
</tr>
</thead>
<tbody>
<tr>
<td>Id</td>
<td>Opzionale</td>
<td>Intero</td>
<td>Campo libero. Un numero identificativo unico per ogni componente.</td>
</tr>
<tr>
<td>Designatore</td>
<td>Opzionale</td>
<td>Stringa</td>
<td>Campo libero. Un designatore di riferimento unico per il componente sul PCB, ad esempio, "R1" per il resistore 1. Utilizzato per nominare la posizione nel gruppo di componenti.</td>
</tr>
<tr>
<td>Package</td>
<td>Opzionale</td>
<td>Stringa</td>
<td>Campo libero. Il tipo o formato del contenitore del componente, ad esempio, "0805" per le resistenze SMD.</td>
</tr>
<tr>
<td>Quantità</td>
<td>Obbligatorio</td>
<td>Intero</td>
<td>Il numero di componenti identici necessari per creare una singola unità di assemblaggio.</td>
</tr>
<tr>
<td>Designazione</td>
<td>Obbligatorio</td>
<td>Stringa</td>
<td>La descrizione o la funzione del componente, ad esempio, il valore della resistenza "10kΩ" o il valore del condensatore "100nF". Utilizzato per il nome nell'entrata della lista dei materiali (BOM).</td>
</tr>
<tr>
<td>Fornitore e riferimento</td>
<td>Opzionale</td>
<td>Stringa</td>
<td>Campo libero. Può includere, ad esempio, informazioni specifiche del distributore.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="QiZM3zY" name="typeahead.parts.part.name">
<segment>
<source>typeahead.parts.part.name</source>
<target>%name% (componente)</target>
</segment>
</unit>
<unit id="WhYL2yX" name="typeahead.parts.assembly.name">
<segment>
<source>typeahead.parts.assembly.name</source>
<target>%name% (gruppo)</target>
</segment>
</unit>
<unit id="4cgba2c" name="projects.build.form.part">
<segment>
<source>projects.build.form.part</source>
<target>Componente "%name%"</target>
</segment>
</unit>
<unit id="1bCA1zb" name="projects.build.form.assembly">
<segment>
<source>projects.build.form.assembly</source>
<target>Gruppo "%name%"</target>
</segment>
</unit>
<unit id="2cDB2ac" name="projects.build.form.assembly.bom.entry">
<segment>
<source>projects.build.form.assembly.bom.entry</source>
<target>%name% (%quantity% necessari)</target>
</segment>
</unit>
<unit id="3dEC3bd" name="projects.build.form.assembly.bom.entry.no.stock">
<segment>
<source>projects.build.form.assembly.bom.entry.no.stock</source>
<target>Non disponibile in magazzino</target>
</segment>
</unit>
<unit id="X9HUFrv" name="part.table.actions.error">
<segment state="translated">
<source>part.table.actions.error</source>

View file

@ -4703,6 +4703,18 @@
<target>名称</target>
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value%(部品)</target>
</segment>
</unit>
<unit id="GW8ZOX7" name="part.table.name.value.for_assembly">
<segment state="translated">
<source>part.table.name.value.for_assembly</source>
<target>%value%(アセンブリ)</target>
</segment>
</unit>
<unit id="eshqdG." name="part.table.id">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:178</note>
@ -8834,5 +8846,645 @@ Exampletown</target>
<target>Part-DBについての質問は、&lt;a href="%href%" class="link-external" target="_blank"&gt;GitHub&lt;/a&gt; にスレッドがあります。</target>
</segment>
</unit>
<unit id="KJuXVR5" name="project.bom.assembly">
<segment state="translated">
<source>project.bom.assembly</source>
<target>アセンブリ</target>
</segment>
</unit>
<unit id="LKvYWS6" name="project.bom.partOrAssembly">
<segment state="translated">
<source>project.bom.partOrAssembly</source>
<target>選択</target>
</segment>
</unit>
<unit id="hIIFtI1" name="assembly.edit.status">
<segment state="translated">
<source>assembly.edit.status</source>
<target>ステータス</target>
</segment>
</unit>
<unit id="zRd.psv" name="assembly.status.draft">
<segment state="translated">
<source>assembly.status.draft</source>
<target>下書き</target>
</segment>
</unit>
<unit id="_U8bb1t" name="assembly.status.planning">
<segment state="translated">
<source>assembly.status.planning</source>
<target>計画中</target>
</segment>
</unit>
<unit id="GgUh7RT" name="assembly.status.in_production">
<segment state="translated">
<source>assembly.status.in_production</source>
<target>製作中</target>
</segment>
</unit>
<unit id="IluD8iU" name="assembly.status.finished">
<segment state="translated">
<source>assembly.status.finished</source>
<target>完成</target>
</segment>
</unit>
<unit id="Mybkd1s" name="assembly.status.archived">
<segment state="translated">
<source>assembly.status.archived</source>
<target>アーカイブ済み</target>
</segment>
</unit>
<unit id="gHU1vgc" name="project.builds.no_stock">
<segment state="translated">
<source>project.builds.no_stock</source>
<target>nessuna scorta specificata</target>
</segment>
</unit>
<unit id="OsmK1Iv" name="project.build.builds_part_lot_label">
<segment state="translated">
<source>project.build.builds_part_lot_label</source>
<target>%name% (必要数: %quantity%)</target>
</segment>
</unit>
<unit id="G2sXzh7" name="assembly.label">
<segment>
<source>assembly.label</source>
<target>アセンブリ</target>
</segment>
</unit>
<unit id="dL51AVa" name="assembly.caption">
<segment>
<source>assembly.caption</source>
<target>アセンブリ</target>
</segment>
</unit>
<unit id="G_rlE3w" name="perm.assemblies">
<segment>
<source>perm.assemblies</source>
<target>アセンブリ一覧</target>
</segment>
</unit>
<unit id="dipIGt4" name="assembly_bom_entry.label">
<segment>
<source>assembly_bom_entry.label</source>
<target>コンポーネント</target>
</segment>
</unit>
<unit id="TvHlYfl" name="assembly.labelp">
<segment>
<source>assembly.labelp</source>
<target>アセンブリ一覧</target>
</segment>
</unit>
<unit id="gyRGdfv" name="assembly.edit">
<segment>
<source>assembly.edit</source>
<target>アセンブリを編集</target>
</segment>
</unit>
<unit id="4Tdtoj_" name="assembly.new">
<segment>
<source>assembly.new</source>
<target>新しいアセンブリ</target>
</segment>
</unit>
<unit id="M51YUE." name="assembly.edit.associated_build_part">
<segment>
<source>assembly.edit.associated_build_part</source>
<target>関連コンポーネント</target>
</segment>
</unit>
<unit id="nH9R9f." name="assembly.edit.associated_build_part.add">
<segment>
<source>assembly.edit.associated_build_part.add</source>
<target>コンポーネントを追加</target>
</segment>
</unit>
<unit id="oVfOk.i" name="assembly.edit.associated_build.hint">
<segment>
<source>assembly.edit.associated_build.hint</source>
<target>このコンポーネントは、アセンブリの製造されたインスタンスを表します。製造されたインスタンスが必要な場合は登録してください。それ以外の場合、コンポーネントの数量は該当するプロジェクトを構築する際にのみ使用されます。</target>
</segment>
</unit>
<unit id="_wZ_JZY" name="assembly.edit.bom.import_bom">
<segment>
<source>assembly.edit.bom.import_bom</source>
<target>コンポーネントをインポート</target>
</segment>
</unit>
<unit id="vsmgKMw" name="log.database_updated.failed">
<segment>
<source>log.database_updated.failed</source>
<target>__log.database_updated.failed</target>
</segment>
</unit>
<unit id="GcQrTTE" name="log.database_updated.old_version">
<segment>
<source>log.database_updated.old_version</source>
<target>__log.database_updated.old_version</target>
</segment>
</unit>
<unit id="JJF47vK" name="log.database_updated.new_version">
<segment>
<source>log.database_updated.new_version</source>
<target>__log.database_updated.new_version</target>
</segment>
</unit>
<unit id="iVHS_sh" name="tree.tools.edit.assemblies">
<segment>
<source>tree.tools.edit.assemblies</source>
<target>アセンブリ一覧</target>
</segment>
</unit>
<unit id="naAMjcH" name="assembly.bom_import.flash.success">
<segment>
<source>assembly.bom_import.flash.success</source>
<target>%count% 個のコンポーネントが正常にアセンブリへインポートされました。</target>
</segment>
</unit>
<unit id="ScQhV.o" name="assembly.bom_import.flash.invalid_entries">
<segment>
<source>assembly.bom_import.flash.invalid_entries</source>
<target>検証エラー! インポートしたファイルを確認してください!</target>
</segment>
</unit>
<unit id="luj_uCZ" name="assembly.bom_import.flash.invalid_file">
<segment>
<source>assembly.bom_import.flash.invalid_file</source>
<target>ファイルをインポートできませんでした。正しいファイル形式を選択しているか確認してください。エラーメッセージ: %message%</target>
</segment>
</unit>
<unit id="u7SYWcB" name="assembly.bom.quantity">
<segment>
<source>assembly.bom.quantity</source>
<target>数量</target>
</segment>
</unit>
<unit id="D7dPvPL" name="assembly.bom.mountnames">
<segment>
<source>assembly.bom.mountnames</source>
<target>取り付け名</target>
</segment>
</unit>
<unit id="rswC4eS" name="assembly.bom.instockAmount">
<segment state="translated">
<source>assembly.bom.instockAmount</source>
<target>在庫数量</target>
</segment>
</unit>
<unit id="oNutri3" name="assembly.info.title">
<segment>
<source>assembly.info.title</source>
<target>アセンブリ情報</target>
</segment>
</unit>
<unit id="aO1rzVQ" name="assembly.info.info.label">
<segment>
<source>assembly.info.info.label</source>
<target>情報</target>
</segment>
</unit>
<unit id="9dOByT_" name="assembly.info.sub_assemblies.label">
<segment>
<source>assembly.info.sub_assemblies.label</source>
<target>サブアセンブリ</target>
</segment>
</unit>
<unit id="Hmf0EwN" name="assembly.info.builds.label">
<segment>
<source>assembly.info.builds.label</source>
<target>ビルド</target>
</segment>
</unit>
<unit id="z3F4Rcu" name="assembly.info.bom_add_parts">
<segment>
<source>assembly.info.bom_add_parts</source>
<target>部品を追加</target>
</segment>
</unit>
<unit id="ZbDTUTS" name="assembly.builds.check_assembly_status">
<segment>
<source>assembly.builds.check_assembly_status</source>
<target><![CDATA[アセンブリの現在の状態は<b>"%assembly_status%"</b>です。この状態でビルドを続行してよろしいですか?]]></target>
</segment>
</unit>
<unit id="xq1Soad" name="assembly.builds.build_not_possible">
<segment>
<source>assembly.builds.build_not_possible</source>
<target>ビルド不可能: 必要な部品が不足しています</target>
</segment>
</unit>
<unit id="njRyDHQ" name="assembly.builds.following_bom_entries_miss_instock">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock</source>
<target>%number_of_builds% 回のビルドを行うのに十分な部品在庫がありません。不足している部品:</target>
</segment>
</unit>
<unit id="sp7Z0MS" name="assembly.builds.build_possible">
<segment>
<source>assembly.builds.build_possible</source>
<target>ビルド可能</target>
</segment>
</unit>
<unit id="1uZzdEl" name="assembly.builds.number_of_builds_possible">
<segment>
<source>assembly.builds.number_of_builds_possible</source>
<target><![CDATA[現在の在庫では<b>%max_builds%</b> 回のアセンブリをビルドできます。]]></target>
</segment>
</unit>
<unit id="pW6QLXf" name="assembly.builds.number_of_builds">
<segment>
<source>assembly.builds.number_of_builds</source>
<target>ビルドするアセンブリ数</target>
</segment>
</unit>
<unit id="mtJqT_a" name="assembly.build.btn_build">
<segment>
<source>assembly.build.btn_build</source>
<target>ビルド</target>
</segment>
</unit>
<unit id="LFSVVcP" name="assembly.builds.no_stocked_builds">
<segment>
<source>assembly.builds.no_stocked_builds</source>
<target>在庫のビルド済みアセンブリ数</target>
</segment>
</unit>
<unit id="dGFHutJ" name="assembly.info.bom_entries_count">
<segment>
<source>assembly.info.bom_entries_count</source>
<target>部品</target>
</segment>
</unit>
<unit id="xJ7oBM4" name="assembly.info.sub_assemblies_count">
<segment>
<source>assembly.info.sub_assemblies_count</source>
<target>サブアセンブリ</target>
</segment>
</unit>
<unit id="HZYhTlb" name="assembly.builds.stocked">
<segment>
<source>assembly.builds.stocked</source>
<target>在庫あり</target>
</segment>
</unit>
<unit id="9EG0PLW" name="assembly.builds.needed">
<segment>
<source>assembly.builds.needed</source>
<target>必要数量</target>
</segment>
</unit>
<unit id="tgs_7u9" name="assembly.add_parts_to_assembly">
<segment>
<source>assembly.add_parts_to_assembly</source>
<target>アセンブリに部品を追加</target>
</segment>
</unit>
<unit id="rs_qty" name="assembly.build.required_qty">
<segment>
<source>assembly.build.required_qty</source>
<target>必要な数量</target>
</segment>
</unit>
<unit id="yes_button" name="assembly.build.yes_button">
<segment>
<source>assembly.build.yes_button</source>
<target>はい</target>
</segment>
</unit>
<unit id="no_button" name="assembly.build.no_button">
<segment>
<source>assembly.build.no_button</source>
<target>いいえ</target>
</segment>
</unit>
<unit id="JLhsU08" name="assembly.confirmation.required">
<segment>
<source>assembly.confirmation.required</source>
<target><![CDATA[続行してよろしいですか?]]></target>
</segment>
</unit>
<unit id="build_success" name="assembly.build.success">
<segment>
<source>assembly.build.success</source>
<target>ビルドが正常に完了しました!</target>
</segment>
</unit>
<unit id="build_cancelled" name="assembly.build.cancelled">
<segment>
<source>assembly.build.cancelled</source>
<target>ビルドがキャンセルされました。</target>
</segment>
</unit>
<unit id="nl.jtSx" name="assembly.bom_import.type">
<segment state="translated">
<source>assembly.bom_import.type</source>
<target>タイプ</target>
</segment>
</unit>
<unit id="LtbSLHx" name="assembly.bom_import.type.json">
<segment state="translated">
<source>assembly.bom_import.type.json</source>
<target>アセンブリ用 JSON</target>
</segment>
</unit>
<unit id="clXFAdN" name="assembly.bom_import.type.kicad_pcbnew">
<segment state="translated">
<source>assembly.bom_import.type.kicad_pcbnew</source>
<target>CSV (KiCAD Pcbnew)</target>
</segment>
</unit>
<unit id="0IekETE" name="assembly.bom_import.clear_existing_bom">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom</source>
<target>インポート前に既存の BOM をクリアする</target>
</segment>
</unit>
<unit id="S4QY6pA" name="assembly.bom_import.clear_existing_bom.help">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom.help</source>
<target>有効にすると、既存のすべての BOM エントリが削除され、インポートされたデータに置き換えられます。</target>
</segment>
</unit>
<unit id="RVNWPsh" name="assembly.import_bom.template.header.json">
<segment>
<source>assembly.import_bom.template.header.json</source>
<target>アセンブリ用 JSON テンプレート</target>
</segment>
</unit>
<unit id="eU4FfCr" name="assembly.import_bom.template.header.kicad_pcbnew">
<segment>
<source>assembly.import_bom.template.header.kicad_pcbnew</source>
<target>アセンブリ用 CSV テンプレートKiCAD Pcbnew BOM</target>
</segment>
</unit>
<unit id="aLomVVS" name="assembly.bom_import.template.entry.name">
<segment>
<source>assembly.bom_import.template.entry.name</source>
<target>アセンブリ内の部品名</target>
</segment>
</unit>
<unit id="o4ZcLfV" name="assembly.bom_import.template.entry.part.mpnr">
<segment>
<source>assembly.bom_import.template.entry.part.mpnr</source>
<target>メーカーの部品番号</target>
</segment>
</unit>
<unit id="n3YbKeU" name="assembly.bom_import.template.entry.part.ipn">
<segment>
<source>assembly.bom_import.template.entry.part.ipn</source>
<target>部品の一意の IPN</target>
</segment>
</unit>
<unit id="l2SYJP5" name="assembly.bom_import.template.entry.part.name">
<segment>
<source>assembly.bom_import.template.entry.part.name</source>
<target>部品名</target>
</segment>
</unit>
<unit id="VgXQ1xW" name="assembly.bom_import.template.entry.part.manufacturer.name">
<segment>
<source>assembly.bom_import.template.entry.part.manufacturer.name</source>
<target>メーカー名</target>
</segment>
</unit>
<unit id="MpB.o_L" name="assembly.bom_import.template.entry.part.category.name">
<segment>
<source>assembly.bom_import.template.entry.part.category.name</source>
<target>カテゴリ名</target>
</segment>
</unit>
<unit id="NIcfgj84" name="assembly.bom_import.template.json.table">
<segment>
<source>assembly.bom_import.template.json.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>フィールド</th>
<th>条件</th>
<th>データ型</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td>quantity</td>
<td>必須</td>
<td>浮動小数点数 (Float)</td>
<td>指定され、0.0より大きい浮動小数点値 (Float) を含む必要があります。</td>
</tr>
<tr>
<td>name</td>
<td>任意</td>
<td>文字列 (String)</td>
<td>指定されている場合、空でない文字列でなければなりません。</td>
</tr>
<tr>
<td>part</td>
<td>任意</td>
<td>オブジェクト/配列</td>
<td>
指定された場合、オブジェクト/配列であり、以下のフィールドのうち少なくとも1つが入力されている必要があります
<ul>
<li>part.id</li>
<li>part.name</li>
</ul>
</td>
</tr>
<tr>
<td>part.id</td>
<td>任意</td>
<td>整数 (Integer)</td>
<td>整数 (Integer) &gt; 0。部品の Part-DB 内部数値 ID に対応します。</td>
</tr>
<tr>
<td>part.name</td>
<td>任意</td>
<td>文字列 (String)</td>
<td>part.mpnr または part.ipn が指定されていない場合、空でない文字列でなければなりません。</td>
</tr>
<tr>
<td>part.mpnr</td>
<td>任意</td>
<td>文字列 (String)</td>
<td>part.name または part.ipn が指定されていない場合、空でない文字列でなければなりません。</td>
</tr>
<tr>
<td>part.ipn</td>
<td>任意</td>
<td>文字列 (String)</td>
<td>part.name または part.mpnr が指定されていない場合、空でない文字列でなければなりません。</td>
</tr>
<tr>
<td>part.description</td>
<td>任意</td>
<td>文字列または null</td>
<td>指定されている場合、空でない文字列または null でなければなりません。</td>
</tr>
<tr>
<td>part.manufacturer</td>
<td>任意</td>
<td>オブジェクト/配列</td>
<td>
指定されている場合、オブジェクト/配列であり、以下のフィールドのうち少なくとも1つが入力されている必要があります
<ul>
<li>manufacturer.id</li>
<li>manufacturer.name</li>
</ul>
</td>
</tr>
<tr>
<td>manufacturer.id</td>
<td>任意</td>
<td>整数 (Integer)</td>
<td>整数 (Integer) &gt; 0。製造元の内部数値 ID に対応します。</td>
</tr>
<tr>
<td>manufacturer.name</td>
<td>任意</td>
<td>文字列 (String)</td>
<td>manufacturer.id が指定されていない場合、空でない文字列でなければなりません。</td>
</tr>
<tr>
<td>part.category</td>
<td>任意</td>
<td>オブジェクト/配列</td>
<td>
指定されている場合、オブジェクト/配列であり、以下のフィールドのうち少なくとも1つが入力されている必要があります
<ul>
<li>category.id</li>
<li>category.name</li>
</ul>
</td>
</tr>
<tr>
<td>category.id</td>
<td>任意</td>
<td>整数 (Integer)</td>
<td>整数 (Integer) &gt; 0。コンポーネントカテゴリの内部数値 ID に対応します。</td>
</tr>
<tr>
<td>category.name</td>
<td>任意</td>
<td>文字列 (String)</td>
<td>category.id が指定されていない場合、空でない文字列でなければなりません。</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="cU1bfDa" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns</source>
<target>予想される列:</target>
</segment>
</unit>
<unit id="gvaB1sb" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns.note">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns.note</source>
<target>
<![CDATA[
<p><strong>注意:</strong> カテゴリ管理から特定のコンポーネントへのマッピングは行われません。</p>
]]>
</target>
</segment>
</unit>
<unit id="translationUnit9" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>フィールド</th>
<th>条件</th>
<th>データタイプ</th>
<th>説明</th>
</tr>
</thead>
<tbody>
<tr>
<td>ID</td>
<td>任意</td>
<td>整数</td>
<td>自由形式フィールド。各コンポーネントの一意の識別番号。</td>
</tr>
<tr>
<td>デジネータ</td>
<td>任意</td>
<td>文字列</td>
<td>自由形式フィールド。PCB上のコンポーネントの一意の参照デジネータ例: 抵抗1の「R1」。コンポーネントグループ内の配置の命名に使用されます。</td>
</tr>
<tr>
<td>パッケージ</td>
<td>任意</td>
<td>文字列</td>
<td>自由形式フィールド。コンポーネントのケースまたはフォームファクタ(例: SMD抵抗「0805」。</td>
</tr>
<tr>
<td>数量</td>
<td>必須</td>
<td>整数</td>
<td>アセンブリの単一インスタンスを作成するために必要な同一コンポーネントの数。</td>
</tr>
<tr>
<td>指定</td>
<td>必須</td>
<td>文字列</td>
<td>コンポーネントの説明または機能(例: 抵抗値「10kΩ」やコンデンサ値「100nF」。部品表BOMエントリ内の名前として使用されます。</td>
</tr>
<tr>
<td>サプライヤーと参照</td>
<td>任意</td>
<td>文字列</td>
<td>自由形式フィールド。たとえば、特定のディストリビューター情報を含む場合があります。</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="QiZM3zY" name="typeahead.parts.part.name">
<segment>
<source>typeahead.parts.part.name</source>
<target>%name%(部品)</target>
</segment>
</unit>
<unit id="WhYL2yX" name="typeahead.parts.assembly.name">
<segment>
<source>typeahead.parts.assembly.name</source>
<target>%name%(アセンブリ)</target>
</segment>
</unit>
<unit id="4cgba2c" name="projects.build.form.part">
<segment>
<source>projects.build.form.part</source>
<target>部品「%name%」</target>
</segment>
</unit>
<unit id="1bCA1zb" name="projects.build.form.assembly">
<segment>
<source>projects.build.form.assembly</source>
<target>アセンブリ「%name%」</target>
</segment>
</unit>
<unit id="2cDB2ac" name="projects.build.form.assembly.bom.entry">
<segment>
<source>projects.build.form.assembly.bom.entry</source>
<target>%name% (必要数量: %quantity%</target>
</segment>
</unit>
<unit id="3dEC3bd" name="projects.build.form.assembly.bom.entry.no.stock">
<segment>
<source>projects.build.form.assembly.bom.entry.no.stock</source>
<target>在庫なし</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -724,5 +724,729 @@
<target>Weet u zeker dat u wilt doorgaan?</target>
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Onderdeel)</target>
</segment>
</unit>
<unit id="GW8ZOX7" name="part.table.name.value.for_assembly">
<segment state="translated">
<source>part.table.name.value.for_assembly</source>
<target>%value% (Samenstelling)</target>
</segment>
</unit>
<unit id="KJuXVR5" name="project.bom.assembly">
<segment state="translated">
<source>project.bom.assembly</source>
<target>Assemblage</target>
</segment>
</unit>
<unit id="LKvYWS6" name="project.bom.partOrAssembly">
<segment state="translated">
<source>project.bom.partOrAssembly</source>
<target>Selectie</target>
</segment>
</unit>
<unit id="hIIFtI1" name="assembly.edit.status">
<segment state="translated">
<source>assembly.edit.status</source>
<target>Κατάσταση</target>
</segment>
</unit>
<unit id="zRd.psv" name="assembly.status.draft">
<segment state="translated">
<source>assembly.status.draft</source>
<target>Προσχέδιο</target>
</segment>
</unit>
<unit id="_U8bb1t" name="assembly.status.planning">
<segment state="translated">
<source>assembly.status.planning</source>
<target>Υπό σχεδιασμό</target>
</segment>
</unit>
<unit id="GgUh7RT" name="assembly.status.in_production">
<segment state="translated">
<source>assembly.status.in_production</source>
<target>Σε παραγωγή</target>
</segment>
</unit>
<unit id="IluD8iU" name="assembly.status.finished">
<segment state="translated">
<source>assembly.status.finished</source>
<target>Ολοκληρώθηκε</target>
</segment>
</unit>
<unit id="Mybkd1s" name="assembly.status.archived">
<segment state="translated">
<source>assembly.status.archived</source>
<target>Αρχειοθετήθηκε</target>
</segment>
</unit>
<unit id="gHU1vgc" name="project.builds.no_stock">
<segment state="translated">
<source>project.builds.no_stock</source>
<target>geen voorraad opgegeven</target>
</segment>
</unit>
<unit id="OsmK1Iv" name="project.build.builds_part_lot_label">
<segment state="translated">
<source>project.build.builds_part_lot_label</source>
<target>%name% (%quantity% vereist)</target>
</segment>
</unit>
<unit id="G2sXzh7" name="assembly.label">
<segment>
<source>assembly.label</source>
<target>Assemblage</target>
</segment>
</unit>
<unit id="dL51AVa" name="assembly.caption">
<segment>
<source>assembly.caption</source>
<target>Assemblage</target>
</segment>
</unit>
<unit id="G_rlE3w" name="perm.assemblies">
<segment>
<source>perm.assemblies</source>
<target>Assemblages</target>
</segment>
</unit>
<unit id="dipIGt4" name="assembly_bom_entry.label">
<segment>
<source>assembly_bom_entry.label</source>
<target>Componenten</target>
</segment>
</unit>
<unit id="TvHlYfl" name="assembly.labelp">
<segment>
<source>assembly.labelp</source>
<target>Assemblages</target>
</segment>
</unit>
<unit id="gyRGdfv" name="assembly.edit">
<segment>
<source>assembly.edit</source>
<target>Assemblage bewerken</target>
</segment>
</unit>
<unit id="4Tdtoj_" name="assembly.new">
<segment>
<source>assembly.new</source>
<target>Nieuwe assemblage</target>
</segment>
</unit>
<unit id="M51YUE." name="assembly.edit.associated_build_part">
<segment>
<source>assembly.edit.associated_build_part</source>
<target>Geassocieerd onderdeel</target>
</segment>
</unit>
<unit id="nH9R9f." name="assembly.edit.associated_build_part.add">
<segment>
<source>assembly.edit.associated_build_part.add</source>
<target>Onderdeel toevoegen</target>
</segment>
</unit>
<unit id="oVfOk.i" name="assembly.edit.associated_build.hint">
<segment>
<source>assembly.edit.associated_build.hint</source>
<target>Dit onderdeel vertegenwoordigt de vervaardigde exemplaren van de assemblage. Geef aan of vervaardigde exemplaren nodig zijn. Zo niet, dan worden de aantallen onderdelen alleen gebruikt bij het bouwen van het bijbehorende project.</target>
</segment>
</unit>
<unit id="_wZ_JZY" name="assembly.edit.bom.import_bom">
<segment>
<source>assembly.edit.bom.import_bom</source>
<target>Componenten importeren</target>
</segment>
</unit>
<unit id="vsmgKMw" name="log.database_updated.failed">
<segment>
<source>log.database_updated.failed</source>
<target>__log.database_updated.failed</target>
</segment>
</unit>
<unit id="GcQrTTE" name="log.database_updated.old_version">
<segment>
<source>log.database_updated.old_version</source>
<target>__log.database_updated.old_version</target>
</segment>
</unit>
<unit id="JJF47vK" name="log.database_updated.new_version">
<segment>
<source>log.database_updated.new_version</source>
<target>__log.database_updated.new_version</target>
</segment>
</unit>
<unit id="iVHS_sh" name="tree.tools.edit.assemblies">
<segment>
<source>tree.tools.edit.assemblies</source>
<target>Assemblages</target>
</segment>
</unit>
<unit id="naAMjcH" name="assembly.bom_import.flash.success">
<segment>
<source>assembly.bom_import.flash.success</source>
<target>%count% component(en) zijn succesvol geïmporteerd in de assemblage.</target>
</segment>
</unit>
<unit id="ScQhV.o" name="assembly.bom_import.flash.invalid_entries">
<segment>
<source>assembly.bom_import.flash.invalid_entries</source>
<target>Validatiefout! Controleer het geïmporteerde bestand!</target>
</segment>
</unit>
<unit id="luj_uCZ" name="assembly.bom_import.flash.invalid_file">
<segment>
<source>assembly.bom_import.flash.invalid_file</source>
<target>Het bestand kon niet worden geïmporteerd. Controleer of je het correcte bestandstype hebt geselecteerd. Foutmelding: %message%</target>
</segment>
</unit>
<unit id="u7SYWcB" name="assembly.bom.quantity">
<segment>
<source>assembly.bom.quantity</source>
<target>Aantal</target>
</segment>
</unit>
<unit id="D7dPvPL" name="assembly.bom.mountnames">
<segment>
<source>assembly.bom.mountnames</source>
<target>Montagenamen</target>
</segment>
</unit>
<unit id="rswC4eS" name="assembly.bom.instockAmount">
<segment state="translated">
<source>assembly.bom.instockAmount</source>
<target>Beschikbaar in voorraad</target>
</segment>
</unit>
<unit id="oNutri3" name="assembly.info.title">
<segment>
<source>assembly.info.title</source>
<target>Assemblage-informatie</target>
</segment>
</unit>
<unit id="aO1rzVQ" name="assembly.info.info.label">
<segment>
<source>assembly.info.info.label</source>
<target>Informatie</target>
</segment>
</unit>
<unit id="9dOByT_" name="assembly.info.sub_assemblies.label">
<segment>
<source>assembly.info.sub_assemblies.label</source>
<target>Subassemblages</target>
</segment>
</unit>
<unit id="Hmf0EwN" name="assembly.info.builds.label">
<segment>
<source>assembly.info.builds.label</source>
<target>Bouw</target>
</segment>
</unit>
<unit id="z3F4Rcu" name="assembly.info.bom_add_parts">
<segment>
<source>assembly.info.bom_add_parts</source>
<target>Onderdelen toevoegen</target>
</segment>
</unit>
<unit id="ZbDTUTS" name="assembly.builds.check_assembly_status">
<segment>
<source>assembly.builds.check_assembly_status</source>
<target><![CDATA[De huidige status van de assemblage is <b>"%assembly_status%"</b>. Bevestig dat je hiermee wilt doorgaan!]]></target>
</segment>
</unit>
<unit id="xq1Soad" name="assembly.builds.build_not_possible">
<segment>
<source>assembly.builds.build_not_possible</source>
<target>Bouwen is niet mogelijk: niet voldoende onderdelen beschikbaar</target>
</segment>
</unit>
<unit id="njRyDHQ" name="assembly.builds.following_bom_entries_miss_instock">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock</source>
<target>Er zijn niet voldoende onderdelen in voorraad om %number_of_builds% keer te bouwen. De volgende onderdelen ontbreken:</target>
</segment>
</unit>
<unit id="sp7Z0MS" name="assembly.builds.build_possible">
<segment>
<source>assembly.builds.build_possible</source>
<target>Bouwen mogelijk</target>
</segment>
</unit>
<unit id="1uZzdEl" name="assembly.builds.number_of_builds_possible">
<segment>
<source>assembly.builds.number_of_builds_possible</source>
<target><![CDATA[Er zijn genoeg onderdelen beschikbaar om <b>%max_builds%</b> assemblages te bouwen.]]></target>
</segment>
</unit>
<unit id="pW6QLXf" name="assembly.builds.number_of_builds">
<segment>
<source>assembly.builds.number_of_builds</source>
<target>Aantal te bouwen assemblages</target>
</segment>
</unit>
<unit id="mtJqT_a" name="assembly.build.btn_build">
<segment>
<source>assembly.build.btn_build</source>
<target>Bouwen</target>
</segment>
</unit>
<unit id="LFSVVcP" name="assembly.builds.no_stocked_builds">
<segment>
<source>assembly.builds.no_stocked_builds</source>
<target>Aantal geassembleerde onderdelen op voorraad</target>
</segment>
</unit>
<unit id="dGFHutJ" name="assembly.info.bom_entries_count">
<segment>
<source>assembly.info.bom_entries_count</source>
<target>Onderdelen</target>
</segment>
</unit>
<unit id="xJ7oBM4" name="assembly.info.sub_assemblies_count">
<segment>
<source>assembly.info.sub_assemblies_count</source>
<target>Subgroepen</target>
</segment>
</unit>
<unit id="HZYhTlb" name="assembly.builds.stocked">
<segment>
<source>assembly.builds.stocked</source>
<target>Op voorraad</target>
</segment>
</unit>
<unit id="9EG0PLW" name="assembly.builds.needed">
<segment>
<source>assembly.builds.needed</source>
<target>Nodig</target>
</segment>
</unit>
<unit id="tgs_7u9" name="assembly.add_parts_to_assembly">
<segment>
<source>assembly.add_parts_to_assembly</source>
<target>Onderdelen toevoegen aan assemblage</target>
</segment>
</unit>
<unit id="PPsM0Dg" name="assembly.bom.name">
<segment>
<source>assembly.bom.name</source>
<target>Naam</target>
</segment>
</unit>
<unit id="nUEs.ld" name="assembly.bom.comment">
<segment>
<source>assembly.bom.comment</source>
<target>Notities</target>
</segment>
</unit>
<unit id="87YpQ_u" name="assembly.builds.following_bom_entries_miss_instock_n">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock_n</source>
<target>Er zijn niet genoeg onderdelen op voorraad om deze assemblage %number_of_builds% keer te bouwen. Van de volgende onderdelen is er niet genoeg op voorraad:</target>
</segment>
</unit>
<unit id="JlQhDdS" name="assembly.build.help">
<segment>
<source>assembly.build.help</source>
<target>Selecteer uit welke voorraden de benodigde onderdelen voor de bouw gehaald moeten worden (en in welke hoeveelheid). Vink elk onderdeel afzonderlijk aan als het is verwijderd, of gebruik de bovenste selectievak om alle selectievakjes in één keer aan te vinken.</target>
</segment>
</unit>
<unit id="iP5_QVj" name="assembly.build.required_qty">
<segment>
<source>assembly.build.required_qty</source>
<target>Benodigde hoeveelheid</target>
</segment>
</unit>
<unit id="UJpD7n6" name="assembly.import_bom">
<segment>
<source>assembly.import_bom</source>
<target>Importeer onderdelen voor assemblage</target>
</segment>
</unit>
<unit id="WTasGao" name="assembly.bom.part">
<segment>
<source>assembly.bom.part</source>
<target>Onderdeel</target>
</segment>
</unit>
<unit id="jHKh8Zp" name="assembly.bom.add_entry">
<segment>
<source>assembly.bom.add_entry</source>
<target>Voer item in</target>
</segment>
</unit>
<unit id="RsZ77df" name="assembly.bom.price">
<segment>
<source>assembly.bom.price</source>
<target>Prijs</target>
</segment>
</unit>
<unit id="63adIrC" name="assembly.build.dont_check_quantity">
<segment state="translated">
<source>assembly.build.dont_check_quantity</source>
<target>Hoeveelheden niet controleren</target>
</segment>
</unit>
<unit id="O0DP6tK" name="assembly.build.dont_check_quantity.help">
<segment state="translated">
<source>assembly.build.dont_check_quantity.help</source>
<target>Als deze optie is geselecteerd, worden de geselecteerde hoeveelheden uit de voorraad verwijderd, ongeacht of er meer of minder onderdelen zijn dan nodig is voor de assemblage.</target>
</segment>
</unit>
<unit id="9bc0nzK" name="assembly.build.add_builds_to_builds_part">
<segment state="translated">
<source>assembly.build.add_builds_to_builds_part</source>
<target>Gemaakte instanties toevoegen aan onderdeel van assemblage</target>
</segment>
</unit>
<unit id="rs_qty" name="assembly.build.required_qty">
<segment>
<source>assembly.build.required_qty</source>
<target>Benodigd aantal</target>
</segment>
</unit>
<unit id="yes_button" name="assembly.build.yes_button">
<segment>
<source>assembly.build.yes_button</source>
<target>Ja</target>
</segment>
</unit>
<unit id="no_button" name="assembly.build.no_button">
<segment>
<source>assembly.build.no_button</source>
<target>Nee</target>
</segment>
</unit>
<unit id="JLhsU08" name="assembly.confirmation.required">
<segment>
<source>assembly.confirmation.required</source>
<target><![CDATA[Bevestig je dat je door wilt gaan?]]></target>
</segment>
</unit>
<unit id="build_success" name="assembly.build.success">
<segment>
<source>assembly.build.success</source>
<target>De assemblage is succesvol gebouwd!</target>
</segment>
</unit>
<unit id="build_cancelled" name="assembly.build.cancelled">
<segment>
<source>assembly.build.cancelled</source>
<target>De assemblage is geannuleerd.</target>
</segment>
</unit>
<unit id="nl.jtSx" name="assembly.bom_import.type">
<segment state="translated">
<source>assembly.bom_import.type</source>
<target>Type</target>
</segment>
</unit>
<unit id="LtbSLHx" name="assembly.bom_import.type.json">
<segment state="translated">
<source>assembly.bom_import.type.json</source>
<target>JSON voor assemblage</target>
</segment>
</unit>
<unit id="clXFAdN" name="assembly.bom_import.type.kicad_pcbnew">
<segment state="translated">
<source>assembly.bom_import.type.kicad_pcbnew</source>
<target>CSV (KiCAD Pcbnew)</target>
</segment>
</unit>
<unit id="0IekETE" name="assembly.bom_import.clear_existing_bom">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom</source>
<target>Bestaande BOM wissen vóór importeren</target>
</segment>
</unit>
<unit id="S4QY6pA" name="assembly.bom_import.clear_existing_bom.help">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom.help</source>
<target>Wanneer dit is ingeschakeld, worden alle bestaande BOM-items verwijderd en vervangen door de geïmporteerde gegevens.</target>
</segment>
</unit>
<unit id="RVNWPsh" name="assembly.import_bom.template.header.json">
<segment>
<source>assembly.import_bom.template.header.json</source>
<target>JSON-sjabloon voor assemblage</target>
</segment>
</unit>
<unit id="eU4FfCr" name="assembly.import_bom.template.header.kicad_pcbnew">
<segment>
<source>assembly.import_bom.template.header.kicad_pcbnew</source>
<target>CSV-sjabloon voor assemblage (KiCAD Pcbnew BOM)</target>
</segment>
</unit>
<unit id="aLomVVS" name="assembly.bom_import.template.entry.name">
<segment>
<source>assembly.bom_import.template.entry.name</source>
<target>Naam van onderdeel in de assemblage</target>
</segment>
</unit>
<unit id="o4ZcLfV" name="assembly.bom_import.template.entry.part.mpnr">
<segment>
<source>assembly.bom_import.template.entry.part.mpnr</source>
<target>Onderdeelnummer van de fabrikant</target>
</segment>
</unit>
<unit id="n3YbKeU" name="assembly.bom_import.template.entry.part.ipn">
<segment>
<source>assembly.bom_import.template.entry.part.ipn</source>
<target>Unieke IPN van het onderdeel</target>
</segment>
</unit>
<unit id="l2SYJP5" name="assembly.bom_import.template.entry.part.name">
<segment>
<source>assembly.bom_import.template.entry.part.name</source>
<target>Unieke naam van het onderdeel</target>
</segment>
</unit>
<unit id="VgXQ1xW" name="assembly.bom_import.template.entry.part.manufacturer.name">
<segment>
<source>assembly.bom_import.template.entry.part.manufacturer.name</source>
<target>Unieke naam van de fabrikant</target>
</segment>
</unit>
<unit id="MpB.o_L" name="assembly.bom_import.template.entry.part.category.name">
<segment>
<source>assembly.bom_import.template.entry.part.category.name</source>
<target>Unieke naam van de categorie</target>
</segment>
</unit>
<unit id="NIcfgj84" name="assembly.bom_import.template.json.table">
<segment>
<source>assembly.bom_import.template.json.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Veld</th>
<th>Voorwaarde</th>
<th>Gegevenstype</th>
<th>Beschrijving</th>
</tr>
</thead>
<tbody>
<tr>
<td>quantity</td>
<td>Verplicht veld</td>
<td>Kommagetal (Float)</td>
<td>Moet opgegeven zijn en bevat een kommagetal (Float) dat groter is dan 0,0.</td>
</tr>
<tr>
<td>name</td>
<td>Optioneel</td>
<td>String</td>
<td>Indien aanwezig, moet het een niet-lege string zijn.</td>
</tr>
<tr>
<td>part</td>
<td>Optioneel</td>
<td>Object/Array</td>
<td>
Indien opgegeven, moet het een object/array zijn en ten minste één van de velden ingevuld zijn:
<ul>
<li>part.id</li>
<li>part.name</li>
</ul>
</td>
</tr>
<tr>
<td>part.id</td>
<td>Optioneel</td>
<td>Geheel getal (Integer)</td>
<td>Geheel getal (Integer) > 0. Komt overeen met de interne numerieke ID van het onderdeel in de Part-DB.</td>
</tr>
<tr>
<td>part.name</td>
<td>Optioneel</td>
<td>String</td>
<td>Niet-lege string, indien geen part.mpnr- of part.ipn-vermelding is gegeven.</td>
</tr>
<tr>
<td>part.mpnr</td>
<td>Optioneel</td>
<td>String</td>
<td>Niet-lege string, indien geen part.name- of part.ipn-vermelding is gegeven.</td>
</tr>
<tr>
<td>part.ipn</td>
<td>Optioneel</td>
<td>String</td>
<td>Niet-lege string, indien geen part.name- of part.mpnr-vermelding is gegeven.</td>
</tr>
<tr>
<td>part.description</td>
<td>Optioneel</td>
<td>String of null</td>
<td>Indien aanwezig, moet het een niet-lege string zijn of null.</td>
</tr>
<tr>
<td>part.manufacturer</td>
<td>Optioneel</td>
<td>Object/Array</td>
<td>
Indien aanwezig, moet het een object/array zijn en ten minste één van de velden ingevuld zijn:
<ul>
<li>manufacturer.id</li>
<li>manufacturer.name</li>
</ul>
</td>
</tr>
<tr>
<td>manufacturer.id</td>
<td>Optioneel</td>
<td>Geheel getal (Integer)</td>
<td>Geheel getal (Integer) > 0. Komt overeen met de interne numerieke ID van de fabrikant.</td>
</tr>
<tr>
<td>manufacturer.name</td>
<td>Optioneel</td>
<td>String</td>
<td>Niet-lege string, indien geen manufacturer.id-vermelding is gegeven.</td>
</tr>
<tr>
<td>part.category</td>
<td>Optioneel</td>
<td>Object/Array</td>
<td>
Indien aanwezig, moet het een object/array zijn en ten minste één van de velden ingevuld zijn:
<ul>
<li>category.id</li>
<li>category.name</li>
</ul>
</td>
</tr>
<tr>
<td>category.id</td>
<td>Optioneel</td>
<td>Geheel getal (Integer)</td>
<td>Geheel getal (Integer) > 0. Komt overeen met de interne numerieke ID van de categorie van het onderdeel.</td>
</tr>
<tr>
<td>category.name</td>
<td>Optioneel</td>
<td>String</td>
<td>Niet-lege string, indien geen category.id-vermelding is gegeven.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="cU1bfDa" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns</source>
<target>Verwachte kolommen:</target>
</segment>
</unit>
<unit id="gvaB1sb" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns.note">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns.note</source>
<target>
<![CDATA[
<p><strong>Opmerking:</strong> Er wordt geen mapping uitgevoerd met specifieke componenten uit de categoriebeheer.</p>
]]>
</target>
</segment>
</unit>
<unit id="translationUnit10" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Veld</th>
<th>Voorwaarde</th>
<th>Gegevenstype</th>
<th>Beschrijving</th>
</tr>
</thead>
<tbody>
<tr>
<td>Id</td>
<td>Optioneel</td>
<td>Integer</td>
<td>Vrij veld. Een unieke identificatienummer voor elk onderdeel.</td>
</tr>
<tr>
<td>Ontwerper</td>
<td>Optioneel</td>
<td>Tekst</td>
<td>Vrij veld. Een unieke referentie-ontwerper voor het onderdeel op de PCB, bijvoorbeeld "R1" voor weerstand 1. Gebruikt voor de naamgeving van de plaatsing in de componentgroep.</td>
</tr>
<tr>
<td>Omhulsel</td>
<td>Optioneel</td>
<td>Tekst</td>
<td>Vrij veld. Het type of de vormfactor van het onderdeel, bijvoorbeeld "0805" voor SMD-weerstanden.</td>
</tr>
<tr>
<td>Aantal</td>
<td>Verplicht</td>
<td>Integer</td>
<td>Het aantal identieke onderdelen dat nodig is om een enkele instantie van een assemblage te maken.</td>
</tr>
<tr>
<td>Aanduiding</td>
<td>Verplicht</td>
<td>Tekst</td>
<td>De beschrijving of functie van het onderdeel, bijvoorbeeld de weerstandswaarde "10kΩ" of de condensatorwaarde "100nF". Wordt gebruikt als naam in de BOM-invoer.</td>
</tr>
<tr>
<td>Leverancier en referentie</td>
<td>Optioneel</td>
<td>Tekst</td>
<td>Vrij veld. Kan bijvoorbeeld informatie bevatten die specifiek is voor de distributeur.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="QiZM3zY" name="typeahead.parts.part.name">
<segment>
<source>typeahead.parts.part.name</source>
<target>%name% (Onderdeel)</target>
</segment>
</unit>
<unit id="WhYL2yX" name="typeahead.parts.assembly.name">
<segment>
<source>typeahead.parts.assembly.name</source>
<target>%name% (Assemblage)</target>
</segment>
</unit>
<unit id="4cgba2c" name="projects.build.form.part">
<segment>
<source>projects.build.form.part</source>
<target>Onderdelen "%name%"</target>
</segment>
</unit>
<unit id="1bCA1zb" name="projects.build.form.assembly">
<segment>
<source>projects.build.form.assembly</source>
<target>Assemblage "%name%"</target>
</segment>
</unit>
<unit id="2cDB2ac" name="projects.build.form.assembly.bom.entry">
<segment>
<source>projects.build.form.assembly.bom.entry</source>
<target>%name% (%quantity% benodigd)</target>
</segment>
</unit>
<unit id="3dEC3bd" name="projects.build.form.assembly.bom.entry.no.stock">
<segment>
<source>projects.build.form.assembly.bom.entry.no.stock</source>
<target>niet op voorraad</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -4745,6 +4745,18 @@ Jeśli zrobiłeś to niepoprawnie lub komputer nie jest już godny zaufania, mo
<target>Nazwa</target>
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value%(部品)</target>
</segment>
</unit>
<unit id="GW8ZOX7" name="part.table.name.value.for_assembly">
<segment state="translated">
<source>part.table.name.value.for_assembly</source>
<target>%value%(アセンブリ)</target>
</segment>
</unit>
<unit id="rW_SFJE" name="part.table.id">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:178</note>
@ -9809,6 +9821,18 @@ Element 3</target>
<target>Komponent</target>
</segment>
</unit>
<unit id="KJuXVR5" name="project.bom.assembly">
<segment state="translated">
<source>project.bom.assembly</source>
<target>Zespół</target>
</segment>
</unit>
<unit id="LKvYWS6" name="project.bom.partOrAssembly">
<segment state="translated">
<source>project.bom.partOrAssembly</source>
<target>Wybór</target>
</segment>
</unit>
<unit id="kGqIirz" name="project.bom.add_entry">
<segment state="translated">
<source>project.bom.add_entry</source>
@ -9887,6 +9911,42 @@ Element 3</target>
<target>Zarchiwizowany</target>
</segment>
</unit>
<unit id="hIIFtI1" name="assembly.edit.status">
<segment state="translated">
<source>assembly.edit.status</source>
<target>Status</target>
</segment>
</unit>
<unit id="zRd.psv" name="assembly.status.draft">
<segment state="translated">
<source>assembly.status.draft</source>
<target>Wersja robocza</target>
</segment>
</unit>
<unit id="_U8bb1t" name="assembly.status.planning">
<segment state="translated">
<source>assembly.status.planning</source>
<target>W planowaniu</target>
</segment>
</unit>
<unit id="GgUh7RT" name="assembly.status.in_production">
<segment state="translated">
<source>assembly.status.in_production</source>
<target>W produkcji</target>
</segment>
</unit>
<unit id="IluD8iU" name="assembly.status.finished">
<segment state="translated">
<source>assembly.status.finished</source>
<target>Zakończony</target>
</segment>
</unit>
<unit id="Mybkd1s" name="assembly.status.archived">
<segment state="translated">
<source>assembly.status.archived</source>
<target>Zarchiwizowany</target>
</segment>
</unit>
<unit id="9GtmqC1" name="part.new_build_part.error.build_part_already_exists">
<segment state="translated">
<source>part.new_build_part.error.build_part_already_exists</source>
@ -10163,6 +10223,12 @@ Element 3</target>
<target>dostępny</target>
</segment>
</unit>
<unit id="gHU1vgc" name="project.builds.no_stock">
<segment state="translated">
<source>project.builds.no_stock</source>
<target>brak podanego stanu magazynowego</target>
</segment>
</unit>
<unit id="s5DQlqF" name="project.builds.needed">
<segment state="translated">
<source>project.builds.needed</source>
@ -10235,6 +10301,12 @@ Element 3</target>
<target>Partia docelowa</target>
</segment>
</unit>
<unit id="OsmK1Iv" name="project.build.builds_part_lot_label">
<segment state="translated">
<source>project.build.builds_part_lot_label</source>
<target>%name% (%quantity% wymagane)</target>
</segment>
</unit>
<unit id="5DTAvWG" name="project.builds.number_of_builds">
<segment state="translated">
<source>project.builds.number_of_builds</source>
@ -12223,5 +12295,621 @@ Należy pamiętać, że nie możesz udawać nieaktywnych użytkowników. Jeśli
<target>Wygenerowany kod</target>
</segment>
</unit>
<unit id="G2sXzh7" name="assembly.label">
<segment>
<source>assembly.label</source>
<target>Zespół</target>
</segment>
</unit>
<unit id="dL51AVa" name="assembly.caption">
<segment>
<source>assembly.caption</source>
<target>Zespół</target>
</segment>
</unit>
<unit id="G_rlE3w" name="perm.assemblies">
<segment>
<source>perm.assemblies</source>
<target>Zespoły</target>
</segment>
</unit>
<unit id="dipIGt4" name="assembly_bom_entry.label">
<segment>
<source>assembly_bom_entry.label</source>
<target>Komponenty</target>
</segment>
</unit>
<unit id="TvHlYfl" name="assembly.labelp">
<segment>
<source>assembly.labelp</source>
<target>Zespoły</target>
</segment>
</unit>
<unit id="gyRGdfv" name="assembly.edit">
<segment>
<source>assembly.edit</source>
<target>Edytuj zespół</target>
</segment>
</unit>
<unit id="4Tdtoj_" name="assembly.new">
<segment>
<source>assembly.new</source>
<target>Nowy zespół</target>
</segment>
</unit>
<unit id="M51YUE." name="assembly.edit.associated_build_part">
<segment>
<source>assembly.edit.associated_build_part</source>
<target>Powiązany komponent</target>
</segment>
</unit>
<unit id="nH9R9f." name="assembly.edit.associated_build_part.add">
<segment>
<source>assembly.edit.associated_build_part.add</source>
<target>Dodaj komponent</target>
</segment>
</unit>
<unit id="oVfOk.i" name="assembly.edit.associated_build.hint">
<segment>
<source>assembly.edit.associated_build.hint</source>
<target>Ten komponent reprezentuje wyprodukowane instancje zespołu. Określ, czy są potrzebne wyprodukowane instancje. W przeciwnym razie ilości komponentów zostaną zastosowane tylko podczas budowy odpowiedniego projektu.</target>
</segment>
</unit>
<unit id="_wZ_JZY" name="assembly.edit.bom.import_bom">
<segment>
<source>assembly.edit.bom.import_bom</source>
<target>Importuj komponenty</target>
</segment>
</unit>
<unit id="vsmgKMw" name="log.database_updated.failed">
<segment>
<source>log.database_updated.failed</source>
<target>__log.database_updated.failed</target>
</segment>
</unit>
<unit id="GcQrTTE" name="log.database_updated.old_version">
<segment>
<source>log.database_updated.old_version</source>
<target>__log.database_updated.old_version</target>
</segment>
</unit>
<unit id="JJF47vK" name="log.database_updated.new_version">
<segment>
<source>log.database_updated.new_version</source>
<target>__log.database_updated.new_version</target>
</segment>
</unit>
<unit id="iVHS_sh" name="tree.tools.edit.assemblies">
<segment>
<source>tree.tools.edit.assemblies</source>
<target>Zespoły</target>
</segment>
</unit>
<unit id="naAMjcH" name="assembly.bom_import.flash.success">
<segment>
<source>assembly.bom_import.flash.success</source>
<target>Pomyślnie zaimportowano %count% komponent(ów) do zespołu.</target>
</segment>
</unit>
<unit id="ScQhV.o" name="assembly.bom_import.flash.invalid_entries">
<segment>
<source>assembly.bom_import.flash.invalid_entries</source>
<target>Błąd walidacji! Sprawdź zaimportowany plik!</target>
</segment>
</unit>
<unit id="luj_uCZ" name="assembly.bom_import.flash.invalid_file">
<segment>
<source>assembly.bom_import.flash.invalid_file</source>
<target>Nie udało się zaimportować pliku. Sprawdź, czy wybrano poprawny typ pliku. Komunikat błędu: %message%</target>
</segment>
</unit>
<unit id="u7SYWcB" name="assembly.bom.quantity">
<segment>
<source>assembly.bom.quantity</source>
<target>Ilość</target>
</segment>
</unit>
<unit id="D7dPvPL" name="assembly.bom.mountnames">
<segment>
<source>assembly.bom.mountnames</source>
<target>Nazwy montażu</target>
</segment>
</unit>
<unit id="rswC4eS" name="assembly.bom.instockAmount">
<segment state="translated">
<source>assembly.bom.instockAmount</source>
<target>Ilość na magazynie</target>
</segment>
</unit>
<unit id="oNutri3" name="assembly.info.title">
<segment>
<source>assembly.info.title</source>
<target>Informacje o zespole</target>
</segment>
</unit>
<unit id="aO1rzVQ" name="assembly.info.info.label">
<segment>
<source>assembly.info.info.label</source>
<target>Informacje</target>
</segment>
</unit>
<unit id="9dOByT_" name="assembly.info.sub_assemblies.label">
<segment>
<source>assembly.info.sub_assemblies.label</source>
<target>Podzespoły</target>
</segment>
</unit>
<unit id="Hmf0EwN" name="assembly.info.builds.label">
<segment>
<source>assembly.info.builds.label</source>
<target>Budowa</target>
</segment>
</unit>
<unit id="z3F4Rcu" name="assembly.info.bom_add_parts">
<segment>
<source>assembly.info.bom_add_parts</source>
<target>Dodaj części</target>
</segment>
</unit>
<unit id="ZbDTUTS" name="assembly.builds.check_assembly_status">
<segment>
<source>assembly.builds.check_assembly_status</source>
<target><![CDATA[Obecny status zespołu to <b>"%assembly_status%"</b>. Upewnij się, że chcesz zbudować zespół w tym statusie!]]></target>
</segment>
</unit>
<unit id="xq1Soad" name="assembly.builds.build_not_possible">
<segment>
<source>assembly.builds.build_not_possible</source>
<target>Budowa niemożliwa: niewystarczająca ilość części</target>
</segment>
</unit>
<unit id="njRyDHQ" name="assembly.builds.following_bom_entries_miss_instock">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock</source>
<target>Brakuje wystarczającej ilości części na magazynie, aby zbudować ten projekt %number_of_builds% razy. Brakujące części to:</target>
</segment>
</unit>
<unit id="sp7Z0MS" name="assembly.builds.build_possible">
<segment>
<source>assembly.builds.build_possible</source>
<target>Budowa możliwa</target>
</segment>
</unit>
<unit id="1uZzdEl" name="assembly.builds.number_of_builds_possible">
<segment>
<source>assembly.builds.number_of_builds_possible</source>
<target><![CDATA[Masz wystarczająco dużo części na magazynie, aby zbudować <b>%max_builds%</b> egzemplarzy tego zespołu.]]></target>
</segment>
</unit>
<unit id="pW6QLXf" name="assembly.builds.number_of_builds">
<segment>
<source>assembly.builds.number_of_builds</source>
<target>Liczba budowanych egzemplarzy</target>
</segment>
</unit>
<unit id="mtJqT_a" name="assembly.build.btn_build">
<segment>
<source>assembly.build.btn_build</source>
<target>Zbuduj</target>
</segment>
</unit>
<unit id="LFSVVcP" name="assembly.builds.no_stocked_builds">
<segment>
<source>assembly.builds.no_stocked_builds</source>
<target>Liczba zbudowanych i zmagazynowanych egzemplarzy</target>
</segment>
</unit>
<unit id="dGFHutJ" name="assembly.info.bom_entries_count">
<segment>
<source>assembly.info.bom_entries_count</source>
<target>Elementy</target>
</segment>
</unit>
<unit id="xJ7oBM4" name="assembly.info.sub_assemblies_count">
<segment>
<source>assembly.info.sub_assemblies_count</source>
<target>Podzespoły</target>
</segment>
</unit>
<unit id="HZYhTlb" name="assembly.builds.stocked">
<segment>
<source>assembly.builds.stocked</source>
<target>na magazynie</target>
</segment>
</unit>
<unit id="9EG0PLW" name="assembly.builds.needed">
<segment>
<source>assembly.builds.needed</source>
<target>potrzebne</target>
</segment>
</unit>
<unit id="tgs_7u9" name="assembly.add_parts_to_assembly">
<segment>
<source>assembly.add_parts_to_assembly</source>
<target>Dodaj części do zespołu</target>
</segment>
</unit>
<unit id="PPsM0Dg" name="assembly.bom.name">
<segment>
<source>assembly.bom.name</source>
<target>Nazwa</target>
</segment>
</unit>
<unit id="nUEs.ld" name="assembly.bom.comment">
<segment>
<source>assembly.bom.comment</source>
<target>Uwagi</target>
</segment>
</unit>
<unit id="87YpQ_u" name="assembly.builds.following_bom_entries_miss_instock_n">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock_n</source>
<target>Brakuje wystarczającej ilości części na magazynie, aby zbudować ten zespół %number_of_builds% razy. Brakujące części to:</target>
</segment>
</unit>
<unit id="JlQhDdS" name="assembly.build.help">
<segment>
<source>assembly.build.help</source>
<target>Wybierz, z których magazynów mają być pobrane części potrzebne do budowy (i w jakiej ilości). Zaznacz każdą pozycję, jeśli części zostały pobrane, lub użyj głównego pola wyboru, aby zaznaczyć wszystkie na raz.</target>
</segment>
</unit>
<unit id="iP5_QVj" name="assembly.build.required_qty">
<segment>
<source>assembly.build.required_qty</source>
<target>Wymagana ilość</target>
</segment>
</unit>
<unit id="UJpD7n6" name="assembly.import_bom">
<segment>
<source>assembly.import_bom</source>
<target>Importuj części dla zespołu</target>
</segment>
</unit>
<unit id="WTasGao" name="assembly.bom.part">
<segment>
<source>assembly.bom.part</source>
<target>Część</target>
</segment>
</unit>
<unit id="jHKh8Zp" name="assembly.bom.add_entry">
<segment>
<source>assembly.bom.add_entry</source>
<target>Dodaj pozycję</target>
</segment>
</unit>
<unit id="RsZ77df" name="assembly.bom.price">
<segment>
<source>assembly.bom.price</source>
<target>Cena</target>
</segment>
</unit>
<unit id="63adIrC" name="assembly.build.dont_check_quantity">
<segment state="translated">
<source>assembly.build.dont_check_quantity</source>
<target>Nie sprawdzaj ilości</target>
</segment>
</unit>
<unit id="O0DP6tK" name="assembly.build.dont_check_quantity.help">
<segment state="translated">
<source>assembly.build.dont_check_quantity.help</source>
<target>Jeśli opcja jest wybrana, zadeklarowana ilość zostanie odjęta z magazynu, niezależnie od tego, czy jest wystarczająca do budowy zespołu.</target>
</segment>
</unit>
<unit id="9bc0nzK" name="assembly.build.add_builds_to_builds_part">
<segment state="translated">
<source>assembly.build.add_builds_to_builds_part</source>
<target>Dodaj zbudowane egzemplarze jako część zespołu</target>
</segment>
</unit>
<unit id="nl.jtSx" name="assembly.bom_import.type">
<segment state="translated">
<source>assembly.bom_import.type</source>
<target>Typ</target>
</segment>
</unit>
<unit id="LtbSLHx" name="assembly.bom_import.type.json">
<segment state="translated">
<source>assembly.bom_import.type.json</source>
<target>JSON dla zespołu</target>
</segment>
</unit>
<unit id="clXFAdN" name="assembly.bom_import.type.kicad_pcbnew">
<segment state="translated">
<source>assembly.bom_import.type.kicad_pcbnew</source>
<target>CSV (KiCAD Pcbnew)</target>
</segment>
</unit>
<unit id="0IekETE" name="assembly.bom_import.clear_existing_bom">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom</source>
<target>Usuń istniejące dane przed importem</target>
</segment>
</unit>
<unit id="S4QY6pA" name="assembly.bom_import.clear_existing_bom.help">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom.help</source>
<target>Jeśli wybrano, wszystkie istniejące wpisy części zostaną usunięte i zastąpione danymi z importu.</target>
</segment>
</unit>
<unit id="RVNWPsh" name="assembly.import_bom.template.header.json">
<segment>
<source>assembly.import_bom.template.header.json</source>
<target>Szablon importu JSON dla zespołu</target>
</segment>
</unit>
<unit id="eU4FfCr" name="assembly.import_bom.template.header.kicad_pcbnew">
<segment>
<source>assembly.import_bom.template.header.kicad_pcbnew</source>
<target>Szablon importu CSV (KiCAD Pcbnew BOM) dla zespołu</target>
</segment>
</unit>
<unit id="aLomVVS" name="assembly.bom_import.template.entry.name">
<segment>
<source>assembly.bom_import.template.entry.name</source>
<target>Nazwa części w zespole</target>
</segment>
</unit>
<unit id="o4ZcLfV" name="assembly.bom_import.template.entry.part.mpnr">
<segment>
<source>assembly.bom_import.template.entry.part.mpnr</source>
<target>Unikalny numer katalogowy producenta</target>
</segment>
</unit>
<unit id="n3YbKeU" name="assembly.bom_import.template.entry.part.ipn">
<segment>
<source>assembly.bom_import.template.entry.part.ipn</source>
<target>Unikalny IPN części</target>
</segment>
</unit>
<unit id="l2SYJP5" name="assembly.bom_import.template.entry.part.name">
<segment>
<source>assembly.bom_import.template.entry.part.name</source>
<target>Unikalna nazwa części</target>
</segment>
</unit>
<unit id="VgXQ1xW" name="assembly.bom_import.template.entry.part.manufacturer.name">
<segment>
<source>assembly.bom_import.template.entry.part.manufacturer.name</source>
<target>Unikalna nazwa producenta</target>
</segment>
</unit>
<unit id="MpB.o_L" name="assembly.bom_import.template.entry.part.category.name">
<segment>
<source>assembly.bom_import.template.entry.part.category.name</source>
<target>Unikalna nazwa kategorii</target>
</segment>
</unit>
<unit id="NIcfgj84" name="assembly.bom_import.template.json.table">
<segment>
<source>assembly.bom_import.template.json.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Pole</th>
<th>Warunek</th>
<th>Typ danych</th>
<th>Opis</th>
</tr>
</thead>
<tbody>
<tr>
<td>quantity</td>
<td>Wymagane</td>
<td>Typ zmiennoprzecinkowy (Float)</td>
<td>Musi być obecne i zawierać wartość zmiennoprzecinkową (Float) większą niż 0,0.</td>
</tr>
<tr>
<td>name</td>
<td>Opcjonalne</td>
<td>Ciąg znaków (String)</td>
<td>Jeśli obecne, musi być niepustym ciągiem znaków.</td>
</tr>
<tr>
<td>part</td>
<td>Opcjonalne</td>
<td>Obiekt/Tablica</td>
<td>
Jeśli podane, musi być obiektem/tablicą i co najmniej jedno z poniższych pól musi być wypełnione:
<ul>
<li>part.id</li>
<li>part.name</li>
</ul>
</td>
</tr>
<tr>
<td>part.id</td>
<td>Opcjonalne</td>
<td>Liczba całkowita (Integer)</td>
<td>Liczba całkowita (Integer) &gt; 0. Odpowiada wewnętrznemu numerowi ID komponentu w Part-DB.</td>
</tr>
<tr>
<td>part.name</td>
<td>Opcjonalne</td>
<td>Ciąg znaków (String)</td>
<td>Niepusty ciąg znaków, jeśli part.mpnr ani part.ipn nie są podane.</td>
</tr>
<tr>
<td>part.mpnr</td>
<td>Opcjonalne</td>
<td>Ciąg znaków (String)</td>
<td>Niepusty ciąg znaków, jeśli part.name ani part.ipn nie są podane.</td>
</tr>
<tr>
<td>part.ipn</td>
<td>Opcjonalne</td>
<td>Ciąg znaków (String)</td>
<td>Niepusty ciąg znaków, jeśli part.name ani part.mpnr nie są podane.</td>
</tr>
<tr>
<td>part.description</td>
<td>Opcjonalne</td>
<td>Ciąg znaków lub null</td>
<td>Jeśli obecne, musi być niepustym ciągiem znaków lub null.</td>
</tr>
<tr>
<td>part.manufacturer</td>
<td>Opcjonalne</td>
<td>Obiekt/Tablica</td>
<td>
Jeśli obecne, musi być obiektem/tablicą i co najmniej jedno z poniższych pól musi być wypełnione:
<ul>
<li>manufacturer.id</li>
<li>manufacturer.name</li>
</ul>
</td>
</tr>
<tr>
<td>manufacturer.id</td>
<td>Opcjonalne</td>
<td>Liczba całkowita (Integer)</td>
<td>Liczba całkowita (Integer) &gt; 0. Odpowiada wewnętrznemu identyfikatorowi numerowemu producenta.</td>
</tr>
<tr>
<td>manufacturer.name</td>
<td>Opcjonalne</td>
<td>Ciąg znaków (String)</td>
<td>Niepusty ciąg znaków, jeśli manufacturer.id nie jest podane.</td>
</tr>
<tr>
<td>part.category</td>
<td>Opcjonalne</td>
<td>Obiekt/Tablica</td>
<td>
Jeśli obecne, musi być obiektem/tablicą i co najmniej jedno z poniższych pól musi być wypełnione:
<ul>
<li>category.id</li>
<li>category.name</li>
</ul>
</td>
</tr>
<tr>
<td>category.id</td>
<td>Opcjonalne</td>
<td>Liczba całkowita (Integer)</td>
<td>Liczba całkowita (Integer) &gt; 0. Odpowiada wewnętrznemu numerowi ID kategorii komponentu.</td>
</tr>
<tr>
<td>category.name</td>
<td>Opcjonalne</td>
<td>Ciąg znaków (String)</td>
<td>Niepusty ciąg znaków, jeśli category.id nie jest podane.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="cU1bfDa" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns</source>
<target>Oczekiwane kolumny:</target>
</segment>
</unit>
<unit id="gvaB1sb" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns.note">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns.note</source>
<target>
<![CDATA[
<p><strong>Uwaga:</strong> Nie wykonano mapowania z określonymi komponentami z zarządzania kategoriami.</p>
]]>
</target>
</segment>
</unit>
<unit id="translationUnit11" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Pole</th>
<th>Warunek</th>
<th>Typ Danych</th>
<th>Opis</th>
</tr>
</thead>
<tbody>
<tr>
<td>Id</td>
<td>Opcjonalne</td>
<td>Liczba całkowita</td>
<td>Pole dowolne. Unikalny numer identyfikacyjny dla każdego komponentu.</td>
</tr>
<tr>
<td>Designer</td>
<td>Opcjonalne</td>
<td>Łańcuch znaków</td>
<td>Pole dowolne. Unikalny oznacznik referencyjny komponentu na PCB, np. "R1" dla rezystora 1. Używany do nazewnictwa położenia w grupie komponentów.</td>
</tr>
<tr>
<td>Obudowa</td>
<td>Opcjonalne</td>
<td>Łańcuch znaków</td>
<td>Pole dowolne. Typ lub forma obudowy komponentu, np. "0805" dla rezystorów SMD.</td>
</tr>
<tr>
<td>Ilość</td>
<td>Wymagane</td>
<td>Liczba całkowita</td>
<td>Liczba identycznych komponentów potrzebnych do stworzenia jednej instancji złożenia.</td>
</tr>
<tr>
<td>Oznaczenie</td>
<td>Wymagane</td>
<td>Łańcuch znaków</td>
<td>Opis lub funkcja komponentu, np. wartość rezystora "10kΩ" lub wartość kondensatora "100nF". Używane jako nazwa w pozycji na liście materiałowej (BOM).</td>
</tr>
<tr>
<td>Dostawca i referencja</td>
<td>Opcjonalne</td>
<td>Łańcuch znaków</td>
<td>Pole dowolne. Może zawierać, np. informacje specyficzne dla dystrybutora.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="QiZM3zY" name="typeahead.parts.part.name">
<segment>
<source>typeahead.parts.part.name</source>
<target>%name% (część)</target>
</segment>
</unit>
<unit id="WhYL2yX" name="typeahead.parts.assembly.name">
<segment>
<source>typeahead.parts.assembly.name</source>
<target>%name% (zespół)</target>
</segment>
</unit>
<unit id="4cgba2c" name="projects.build.form.part">
<segment>
<source>projects.build.form.part</source>
<target>Część "%name%"</target>
</segment>
</unit>
<unit id="1bCA1zb" name="projects.build.form.assembly">
<segment>
<source>projects.build.form.assembly</source>
<target>Zespół "%name%"</target>
</segment>
</unit>
<unit id="2cDB2ac" name="projects.build.form.assembly.bom.entry">
<segment>
<source>projects.build.form.assembly.bom.entry</source>
<target>%name% (wymagana ilość: %quantity%)</target>
</segment>
</unit>
<unit id="3dEC3bd" name="projects.build.form.assembly.bom.entry.no.stock">
<segment>
<source>projects.build.form.assembly.bom.entry.no.stock</source>
<target>brak na magazynie</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -4751,6 +4751,18 @@
<target>Имя</target>
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value% (Часть)</target>
</segment>
</unit>
<unit id="GW8ZOX7" name="part.table.name.value.for_assembly">
<segment state="translated">
<source>part.table.name.value.for_assembly</source>
<target>%value% (Сборка)</target>
</segment>
</unit>
<unit id="rW_SFJE" name="part.table.id">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:178</note>
@ -9813,6 +9825,18 @@
<target>Компонент</target>
</segment>
</unit>
<unit id="KJuXVR5" name="project.bom.assembly">
<segment state="translated">
<source>project.bom.assembly</source>
<target>Сборка</target>
</segment>
</unit>
<unit id="LKvYWS6" name="project.bom.partOrAssembly">
<segment state="translated">
<source>project.bom.partOrAssembly</source>
<target>Выбор</target>
</segment>
</unit>
<unit id="kGqIirz" name="project.bom.add_entry">
<segment state="translated">
<source>project.bom.add_entry</source>
@ -9891,6 +9915,42 @@
<target>Архивный</target>
</segment>
</unit>
<unit id="hIIFtI1" name="assembly.edit.status">
<segment state="translated">
<source>assembly.edit.status</source>
<target>Статус</target>
</segment>
</unit>
<unit id="zRd.psv" name="assembly.status.draft">
<segment state="translated">
<source>assembly.status.draft</source>
<target>Черновик</target>
</segment>
</unit>
<unit id="_U8bb1t" name="assembly.status.planning">
<segment state="translated">
<source>assembly.status.planning</source>
<target>Планирование</target>
</segment>
</unit>
<unit id="GgUh7RT" name="assembly.status.in_production">
<segment state="translated">
<source>assembly.status.in_production</source>
<target>В производстве</target>
</segment>
</unit>
<unit id="IluD8iU" name="assembly.status.finished">
<segment state="translated">
<source>assembly.status.finished</source>
<target>Завершен</target>
</segment>
</unit>
<unit id="Mybkd1s" name="assembly.status.archived">
<segment state="translated">
<source>assembly.status.archived</source>
<target>Архивный</target>
</segment>
</unit>
<unit id="9GtmqC1" name="part.new_build_part.error.build_part_already_exists">
<segment state="translated">
<source>part.new_build_part.error.build_part_already_exists</source>
@ -10167,6 +10227,12 @@
<target>запасено</target>
</segment>
</unit>
<unit id="gHU1vgc" name="project.builds.no_stock">
<segment state="translated">
<source>project.builds.no_stock</source>
<target>склад не указан</target>
</segment>
</unit>
<unit id="s5DQlqF" name="project.builds.needed">
<segment state="translated">
<source>project.builds.needed</source>
@ -10239,6 +10305,12 @@
<target>Целевой лот</target>
</segment>
</unit>
<unit id="OsmK1Iv" name="project.build.builds_part_lot_label">
<segment state="translated">
<source>project.build.builds_part_lot_label</source>
<target>%name% (требуется: %quantity%)</target>
</segment>
</unit>
<unit id="5DTAvWG" name="project.builds.number_of_builds">
<segment state="translated">
<source>project.builds.number_of_builds</source>
@ -12323,5 +12395,621 @@
<target>Профиль сохранен!</target>
</segment>
</unit>
<unit id="G2sXzh7" name="assembly.label">
<segment>
<source>assembly.label</source>
<target>Сборка</target>
</segment>
</unit>
<unit id="dL51AVa" name="assembly.caption">
<segment>
<source>assembly.caption</source>
<target>Сборка</target>
</segment>
</unit>
<unit id="G_rlE3w" name="perm.assemblies">
<segment>
<source>perm.assemblies</source>
<target>Сборки</target>
</segment>
</unit>
<unit id="dipIGt4" name="assembly_bom_entry.label">
<segment>
<source>assembly_bom_entry.label</source>
<target>Компоненты</target>
</segment>
</unit>
<unit id="TvHlYfl" name="assembly.labelp">
<segment>
<source>assembly.labelp</source>
<target>Сборки</target>
</segment>
</unit>
<unit id="gyRGdfv" name="assembly.edit">
<segment>
<source>assembly.edit</source>
<target>Редактировать сборку</target>
</segment>
</unit>
<unit id="4Tdtoj_" name="assembly.new">
<segment>
<source>assembly.new</source>
<target>Новая сборка</target>
</segment>
</unit>
<unit id="M51YUE." name="assembly.edit.associated_build_part">
<segment>
<source>assembly.edit.associated_build_part</source>
<target>Связанный компонент</target>
</segment>
</unit>
<unit id="nH9R9f." name="assembly.edit.associated_build_part.add">
<segment>
<source>assembly.edit.associated_build_part.add</source>
<target>Добавить компонент</target>
</segment>
</unit>
<unit id="oVfOk.i" name="assembly.edit.associated_build.hint">
<segment>
<source>assembly.edit.associated_build.hint</source>
<target>Этот компонент представляет изготовленные экземпляры сборки. Укажите, нужны ли изготовленные экземпляры. В противном случае количество компонентов будет использоваться только при создании соответствующего проекта.</target>
</segment>
</unit>
<unit id="_wZ_JZY" name="assembly.edit.bom.import_bom">
<segment>
<source>assembly.edit.bom.import_bom</source>
<target>Импортировать компоненты</target>
</segment>
</unit>
<unit id="vsmgKMw" name="log.database_updated.failed">
<segment>
<source>log.database_updated.failed</source>
<target>__log.database_updated.failed</target>
</segment>
</unit>
<unit id="GcQrTTE" name="log.database_updated.old_version">
<segment>
<source>log.database_updated.old_version</source>
<target>__log.database_updated.old_version</target>
</segment>
</unit>
<unit id="JJF47vK" name="log.database_updated.new_version">
<segment>
<source>log.database_updated.new_version</source>
<target>__log.database_updated.new_version</target>
</segment>
</unit>
<unit id="iVHS_sh" name="tree.tools.edit.assemblies">
<segment>
<source>tree.tools.edit.assemblies</source>
<target>Сборки</target>
</segment>
</unit>
<unit id="naAMjcH" name="assembly.bom_import.flash.success">
<segment>
<source>assembly.bom_import.flash.success</source>
<target>%count% компонент(ов) успешно импортировано в сборку.</target>
</segment>
</unit>
<unit id="ScQhV.o" name="assembly.bom_import.flash.invalid_entries">
<segment>
<source>assembly.bom_import.flash.invalid_entries</source>
<target>Ошибка валидации! Проверьте импортированный файл!</target>
</segment>
</unit>
<unit id="luj_uCZ" name="assembly.bom_import.flash.invalid_file">
<segment>
<source>assembly.bom_import.flash.invalid_file</source>
<target>Не удалось импортировать файл. Убедитесь, что выбран правильный тип файла. Сообщение об ошибке: %message%</target>
</segment>
</unit>
<unit id="u7SYWcB" name="assembly.bom.quantity">
<segment>
<source>assembly.bom.quantity</source>
<target>Количество</target>
</segment>
</unit>
<unit id="D7dPvPL" name="assembly.bom.mountnames">
<segment>
<source>assembly.bom.mountnames</source>
<target>Названия монтажей</target>
</segment>
</unit>
<unit id="rswC4eS" name="assembly.bom.instockAmount">
<segment state="translated">
<source>assembly.bom.instockAmount</source>
<target>Количество на складе</target>
</segment>
</unit>
<unit id="oNutri3" name="assembly.info.title">
<segment>
<source>assembly.info.title</source>
<target>Информация о сборке</target>
</segment>
</unit>
<unit id="aO1rzVQ" name="assembly.info.info.label">
<segment>
<source>assembly.info.info.label</source>
<target>Информация</target>
</segment>
</unit>
<unit id="9dOByT_" name="assembly.info.sub_assemblies.label">
<segment>
<source>assembly.info.sub_assemblies.label</source>
<target>Подсборки</target>
</segment>
</unit>
<unit id="Hmf0EwN" name="assembly.info.builds.label">
<segment>
<source>assembly.info.builds.label</source>
<target>Сборка</target>
</segment>
</unit>
<unit id="z3F4Rcu" name="assembly.info.bom_add_parts">
<segment>
<source>assembly.info.bom_add_parts</source>
<target>Добавить детали</target>
</segment>
</unit>
<unit id="ZbDTUTS" name="assembly.builds.check_assembly_status">
<segment>
<source>assembly.builds.check_assembly_status</source>
<target><![CDATA[Текущий статус сборки: <b>"%assembly_status%"</b>. Убедитесь, что действительно хотите выполнить сборку с этим статусом!]]></target>
</segment>
</unit>
<unit id="xq1Soad" name="assembly.builds.build_not_possible">
<segment>
<source>assembly.builds.build_not_possible</source>
<target>Сборка невозможна: недостаточно деталей</target>
</segment>
</unit>
<unit id="njRyDHQ" name="assembly.builds.following_bom_entries_miss_instock">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock</source>
<target>Недостаточно деталей на складе для сборки %number_of_builds% экземпляров. Следующие детали отсутствуют в достаточном количестве:</target>
</segment>
</unit>
<unit id="sp7Z0MS" name="assembly.builds.build_possible">
<segment>
<source>assembly.builds.build_possible</source>
<target>Сборка возможна</target>
</segment>
</unit>
<unit id="1uZzdEl" name="assembly.builds.number_of_builds_possible">
<segment>
<source>assembly.builds.number_of_builds_possible</source>
<target><![CDATA[На складе имеется достаточно деталей для сборки <b>%max_builds%</b> экземпляров.]]></target>
</segment>
</unit>
<unit id="pW6QLXf" name="assembly.builds.number_of_builds">
<segment>
<source>assembly.builds.number_of_builds</source>
<target>Количество сборок</target>
</segment>
</unit>
<unit id="mtJqT_a" name="assembly.build.btn_build">
<segment>
<source>assembly.build.btn_build</source>
<target>Собрать</target>
</segment>
</unit>
<unit id="LFSVVcP" name="assembly.builds.no_stocked_builds">
<segment>
<source>assembly.builds.no_stocked_builds</source>
<target>Собранные экземпляры на складе</target>
</segment>
</unit>
<unit id="dGFHutJ" name="assembly.info.bom_entries_count">
<segment>
<source>assembly.info.bom_entries_count</source>
<target>Детали</target>
</segment>
</unit>
<unit id="xJ7oBM4" name="assembly.info.sub_assemblies_count">
<segment>
<source>assembly.info.sub_assemblies_count</source>
<target>Подсборки</target>
</segment>
</unit>
<unit id="HZYhTlb" name="assembly.builds.stocked">
<segment>
<source>assembly.builds.stocked</source>
<target>На складе</target>
</segment>
</unit>
<unit id="9EG0PLW" name="assembly.builds.needed">
<segment>
<source>assembly.builds.needed</source>
<target>Необходимо</target>
</segment>
</unit>
<unit id="tgs_7u9" name="assembly.add_parts_to_assembly">
<segment>
<source>assembly.add_parts_to_assembly</source>
<target>Добавить детали в сборку</target>
</segment>
</unit>
<unit id="PPsM0Dg" name="assembly.bom.name">
<segment>
<source>assembly.bom.name</source>
<target>Название</target>
</segment>
</unit>
<unit id="nUEs.ld" name="assembly.bom.comment">
<segment>
<source>assembly.bom.comment</source>
<target>Примечания</target>
</segment>
</unit>
<unit id="87YpQ_u" name="assembly.builds.following_bom_entries_miss_instock_n">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock_n</source>
<target>Недостаточно деталей на складе для сборки %number_of_builds% экземпляров. У следующих деталей недостаточное количество:</target>
</segment>
</unit>
<unit id="JlQhDdS" name="assembly.build.help">
<segment>
<source>assembly.build.help</source>
<target>Выберите, из каких запасов брать необходимые для сборки детали (и в каком количестве). Установите галочку для каждой позиции, если детали были взяты, или используйте основную галочку, чтобы отметить все позиции сразу.</target>
</segment>
</unit>
<unit id="iP5_QVj" name="assembly.build.required_qty">
<segment>
<source>assembly.build.required_qty</source>
<target>Необходимое количество</target>
</segment>
</unit>
<unit id="UJpD7n6" name="assembly.import_bom">
<segment>
<source>assembly.import_bom</source>
<target>Импортировать детали для сборки</target>
</segment>
</unit>
<unit id="WTasGao" name="assembly.bom.part">
<segment>
<source>assembly.bom.part</source>
<target>Компонент</target>
</segment>
</unit>
<unit id="jHKh8Zp" name="assembly.bom.add_entry">
<segment>
<source>assembly.bom.add_entry</source>
<target>Добавить запись</target>
</segment>
</unit>
<unit id="RsZ77df" name="assembly.bom.price">
<segment>
<source>assembly.bom.price</source>
<target>Цена</target>
</segment>
</unit>
<unit id="63adIrC" name="assembly.build.dont_check_quantity">
<segment state="translated">
<source>assembly.build.dont_check_quantity</source>
<target>Не проверять количество</target>
</segment>
</unit>
<unit id="O0DP6tK" name="assembly.build.dont_check_quantity.help">
<segment state="translated">
<source>assembly.build.dont_check_quantity.help</source>
<target>Если выбрано, указанные количества будут списаны со склада независимо от того, достаточно их или нет для указанной сборки.</target>
</segment>
</unit>
<unit id="9bc0nzK" name="assembly.build.add_builds_to_builds_part">
<segment state="translated">
<source>assembly.build.add_builds_to_builds_part</source>
<target>Добавить собранные экземпляры как компонент для подсборки</target>
</segment>
</unit>
<unit id="nl.jtSx" name="assembly.bom_import.type">
<segment state="translated">
<source>assembly.bom_import.type</source>
<target>Тип</target>
</segment>
</unit>
<unit id="LtbSLHx" name="assembly.bom_import.type.json">
<segment state="translated">
<source>assembly.bom_import.type.json</source>
<target>JSON для сборки</target>
</segment>
</unit>
<unit id="clXFAdN" name="assembly.bom_import.type.kicad_pcbnew">
<segment state="translated">
<source>assembly.bom_import.type.kicad_pcbnew</source>
<target>CSV (KiCAD Pcbnew)</target>
</segment>
</unit>
<unit id="0IekETE" name="assembly.bom_import.clear_existing_bom">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom</source>
<target>Очистить текущие данные перед импортом</target>
</segment>
</unit>
<unit id="S4QY6pA" name="assembly.bom_import.clear_existing_bom.help">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom.help</source>
<target>Если выбрано, все существующие записи о деталях будут удалены и заменены импортированными.</target>
</segment>
</unit>
<unit id="RVNWPsh" name="assembly.import_bom.template.header.json">
<segment>
<source>assembly.import_bom.template.header.json</source>
<target>Шаблон импорта JSON для сборки</target>
</segment>
</unit>
<unit id="eU4FfCr" name="assembly.import_bom.template.header.kicad_pcbnew">
<segment>
<source>assembly.import_bom.template.header.kicad_pcbnew</source>
<target>Шаблон импорта CSV (KiCAD Pcbnew BOM) для сборки</target>
</segment>
</unit>
<unit id="aLomVVS" name="assembly.bom_import.template.entry.name">
<segment>
<source>assembly.bom_import.template.entry.name</source>
<target>Название детали в сборке</target>
</segment>
</unit>
<unit id="o4ZcLfV" name="assembly.bom_import.template.entry.part.mpnr">
<segment>
<source>assembly.bom_import.template.entry.part.mpnr</source>
<target>Уникальный каталожный номер производителя</target>
</segment>
</unit>
<unit id="n3YbKeU" name="assembly.bom_import.template.entry.part.ipn">
<segment>
<source>assembly.bom_import.template.entry.part.ipn</source>
<target>Уникальный IPN компонента</target>
</segment>
</unit>
<unit id="l2SYJP5" name="assembly.bom_import.template.entry.part.name">
<segment>
<source>assembly.bom_import.template.entry.part.name</source>
<target>Уникальное имя компонента</target>
</segment>
</unit>
<unit id="VgXQ1xW" name="assembly.bom_import.template.entry.part.manufacturer.name">
<segment>
<source>assembly.bom_import.template.entry.part.manufacturer.name</source>
<target>Уникальное название производителя</target>
</segment>
</unit>
<unit id="MpB.o_L" name="assembly.bom_import.template.entry.part.category.name">
<segment>
<source>assembly.bom_import.template.entry.part.category.name</source>
<target>Уникальное название категории</target>
</segment>
</unit>
<unit id="NIcfgj84" name="assembly.bom_import.template.json.table">
<segment>
<source>assembly.bom_import.template.json.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Поле</th>
<th>Условие</th>
<th>Тип данных</th>
<th>Описание</th>
</tr>
</thead>
<tbody>
<tr>
<td>quantity</td>
<td>Обязательное</td>
<td>Дробное число (Float)</td>
<td>Поле должно быть заполнено и содержать дробное значение (Float), большее 0,0.</td>
</tr>
<tr>
<td>name</td>
<td>Опциональное</td>
<td>Строка (String)</td>
<td>Если присутствует, должно быть непустой строкой.</td>
</tr>
<tr>
<td>part</td>
<td>Опциональное</td>
<td>Объект/Массив</td>
<td>
Если указано, должно быть объектом/массивом, и хотя бы одно из следующих полей должно быть заполнено:
<ul>
<li>part.id</li>
<li>part.name</li>
</ul>
</td>
</tr>
<tr>
<td>part.id</td>
<td>Опциональное</td>
<td>Целое число (Integer)</td>
<td>Целое число (Integer) &gt; 0. Соответствует внутреннему цифровому идентификатору компонента в Part-DB.</td>
</tr>
<tr>
<td>part.name</td>
<td>Опциональное</td>
<td>Строка (String)</td>
<td>Непустая строка, если part.mpnr или part.ipn не указаны.</td>
</tr>
<tr>
<td>part.mpnr</td>
<td>Опциональное</td>
<td>Строка (String)</td>
<td>Непустая строка, если part.name или part.ipn не указаны.</td>
</tr>
<tr>
<td>part.ipn</td>
<td>Опциональное</td>
<td>Строка (String)</td>
<td>Непустая строка, если part.name или part.mpnr не указаны.</td>
</tr>
<tr>
<td>part.description</td>
<td>Опциональное</td>
<td>Строка или null</td>
<td>Если присутствует, должно быть непустой строкой или null.</td>
</tr>
<tr>
<td>part.manufacturer</td>
<td>Опциональное</td>
<td>Объект/Массив</td>
<td>
Если присутствует, должно быть объектом/массивом, и хотя бы одно из следующих полей должно быть заполнено:
<ul>
<li>manufacturer.id</li>
<li>manufacturer.name</li>
</ul>
</td>
</tr>
<tr>
<td>manufacturer.id</td>
<td>Опциональное</td>
<td>Целое число (Integer)</td>
<td>Целое число (Integer) &gt; 0. Соответствует внутреннему идентификатору производителя.</td>
</tr>
<tr>
<td>manufacturer.name</td>
<td>Опциональное</td>
<td>Строка (String)</td>
<td>Непустая строка, если manufacturer.id не указано.</td>
</tr>
<tr>
<td>part.category</td>
<td>Опциональное</td>
<td>Объект/Массив</td>
<td>
Если присутствует, должно быть объектом/массивом, и хотя бы одно из следующих полей должно быть заполнено:
<ul>
<li>category.id</li>
<li>category.name</li>
</ul>
</td>
</tr>
<tr>
<td>category.id</td>
<td>Опциональное</td>
<td>Целое число (Integer)</td>
<td>Целое число (Integer) &gt; 0. Соответствует внутреннему цифровому идентификатору категории компонента.</td>
</tr>
<tr>
<td>category.name</td>
<td>Опциональное</td>
<td>Строка (String)</td>
<td>Непустая строка, если category.id не указано.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="cU1bfDa" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns</source>
<target>Ожидаемые столбцы:</target>
</segment>
</unit>
<unit id="gvaB1sb" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns.note">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns.note</source>
<target>
<![CDATA[
<p><strong>Примечание:</strong> Сопоставление с конкретными компонентами из управления категориями не выполняется.</p>
]]>
</target>
</segment>
</unit>
<unit id="translationUnit12" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>Поле</th>
<th>Условие</th>
<th>Тип данных</th>
<th>Описание</th>
</tr>
</thead>
<tbody>
<tr>
<td>ID</td>
<td>Опционально</td>
<td>Целое число</td>
<td>Произвольное поле. Уникальный идентификационный номер для каждого компонента.</td>
</tr>
<tr>
<td>Дизигнатор</td>
<td>Опционально</td>
<td>Строка</td>
<td>Произвольное поле. Уникальный референсный обозначитель компонента на печатной плате, например, "R1" для резистора 1. Используется для именования позиции в группе компонентов.</td>
</tr>
<tr>
<td>Корпус</td>
<td>Опционально</td>
<td>Строка</td>
<td>Произвольное поле. Тип или форм-фактор корпуса компонента, например, "0805" для SMD-резисторов.</td>
</tr>
<tr>
<td>Количество</td>
<td>Обязательно</td>
<td>Целое число</td>
<td>Количество одинаковых компонентов, необходимое для создания одной единицы сборки.</td>
</tr>
<tr>
<td>Обозначение</td>
<td>Обязательно</td>
<td>Строка</td>
<td>Описание или функция компонента, например, номинал резистора "10kΩ" или номинал конденсатора "100nF". Используется в качестве имени в позиции списка материалов (BOM).</td>
</tr>
<tr>
<td>Поставщик и ссылка</td>
<td>Опционально</td>
<td>Строка</td>
<td>Произвольное поле. Может содержать, например, информацию, специфичную для дистрибьютора.</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="QiZM3zY" name="typeahead.parts.part.name">
<segment>
<source>typeahead.parts.part.name</source>
<target>%name% (Деталь)</target>
</segment>
</unit>
<unit id="WhYL2yX" name="typeahead.parts.assembly.name">
<segment>
<source>typeahead.parts.assembly.name</source>
<target>%name% (Сборка)</target>
</segment>
</unit>
<unit id="4cgba2c" name="projects.build.form.part">
<segment>
<source>projects.build.form.part</source>
<target>Компонент "%name%"</target>
</segment>
</unit>
<unit id="1bCA1zb" name="projects.build.form.assembly">
<segment>
<source>projects.build.form.assembly</source>
<target>Сборка "%name%"</target>
</segment>
</unit>
<unit id="2cDB2ac" name="projects.build.form.assembly.bom.entry">
<segment>
<source>projects.build.form.assembly.bom.entry</source>
<target>%name% (необходимо: %quantity%)</target>
</segment>
</unit>
<unit id="3dEC3bd" name="projects.build.form.assembly.bom.entry.no.stock">
<segment>
<source>projects.build.form.assembly.bom.entry.no.stock</source>
<target>Нет на складе</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -4749,6 +4749,18 @@
<target>名称</target>
</segment>
</unit>
<unit id="FV7YOW6" name="part.table.name.value.for_part">
<segment state="translated">
<source>part.table.name.value.for_part</source>
<target>%value%(部件)</target>
</segment>
</unit>
<unit id="GW8ZOX7" name="part.table.name.value.for_assembly">
<segment state="translated">
<source>part.table.name.value.for_assembly</source>
<target>%value%(组件)</target>
</segment>
</unit>
<unit id="eshqdG." name="part.table.id">
<notes>
<note category="file-source" priority="1">Part-DB1\src\DataTables\PartsDataTable.php:178</note>
@ -9812,6 +9824,18 @@ Element 3</target>
<target>部件</target>
</segment>
</unit>
<unit id="KJuXVR5" name="project.bom.assembly">
<segment state="translated">
<source>project.bom.assembly</source>
<target>装配</target>
</segment>
</unit>
<unit id="LKvYWS6" name="project.bom.partOrAssembly">
<segment state="translated">
<source>project.bom.partOrAssembly</source>
<target>选择</target>
</segment>
</unit>
<unit id="apnWXEq" name="project.bom.add_entry">
<segment state="translated">
<source>project.bom.add_entry</source>
@ -9890,6 +9914,42 @@ Element 3</target>
<target>已存档</target>
</segment>
</unit>
<unit id="hIIFtI1" name="assembly.edit.status">
<segment state="translated">
<source>assembly.edit.status</source>
<target>状态</target>
</segment>
</unit>
<unit id="zRd.psv" name="assembly.status.draft">
<segment state="translated">
<source>assembly.status.draft</source>
<target>草稿</target>
</segment>
</unit>
<unit id="_U8bb1t" name="assembly.status.planning">
<segment state="translated">
<source>assembly.status.planning</source>
<target>策划</target>
</segment>
</unit>
<unit id="GgUh7RT" name="assembly.status.in_production">
<segment state="translated">
<source>assembly.status.in_production</source>
<target>生产中</target>
</segment>
</unit>
<unit id="IluD8iU" name="assembly.status.finished">
<segment state="translated">
<source>assembly.status.finished</source>
<target>已完成</target>
</segment>
</unit>
<unit id="Mybkd1s" name="assembly.status.archived">
<segment state="translated">
<source>assembly.status.archived</source>
<target>已归档</target>
</segment>
</unit>
<unit id="jcf.5wX" name="part.new_build_part.error.build_part_already_exists">
<segment state="translated">
<source>part.new_build_part.error.build_part_already_exists</source>
@ -10166,6 +10226,12 @@ Element 3</target>
<target>在库</target>
</segment>
</unit>
<unit id="gHU1vgc" name="project.builds.no_stock">
<segment state="translated">
<source>project.builds.no_stock</source>
<target>未指定库存</target>
</segment>
</unit>
<unit id="mwL3d70" name="project.builds.needed">
<segment state="translated">
<source>project.builds.needed</source>
@ -10238,6 +10304,12 @@ Element 3</target>
<target>目标批次</target>
</segment>
</unit>
<unit id="OsmK1Iv" name="project.build.builds_part_lot_label">
<segment state="translated">
<source>project.build.builds_part_lot_label</source>
<target>%name% (需求数量: %quantity%)</target>
</segment>
</unit>
<unit id="pyv0k6b" name="project.builds.number_of_builds">
<segment state="translated">
<source>project.builds.number_of_builds</source>
@ -12208,5 +12280,621 @@ Element 3</target>
<target>成功创建 %COUNT% 个元素。</target>
</segment>
</unit>
<unit id="G2sXzh7" name="assembly.label">
<segment>
<source>assembly.label</source>
<target>装配</target>
</segment>
</unit>
<unit id="dL51AVa" name="assembly.caption">
<segment>
<source>assembly.caption</source>
<target>装配</target>
</segment>
</unit>
<unit id="G_rlE3w" name="perm.assemblies">
<segment>
<source>perm.assemblies</source>
<target>装配列表</target>
</segment>
</unit>
<unit id="dipIGt4" name="assembly_bom_entry.label">
<segment>
<source>assembly_bom_entry.label</source>
<target>组件</target>
</segment>
</unit>
<unit id="TvHlYfl" name="assembly.labelp">
<segment>
<source>assembly.labelp</source>
<target>装配列表</target>
</segment>
</unit>
<unit id="gyRGdfv" name="assembly.edit">
<segment>
<source>assembly.edit</source>
<target>编辑装配</target>
</segment>
</unit>
<unit id="4Tdtoj_" name="assembly.new">
<segment>
<source>assembly.new</source>
<target>新装配</target>
</segment>
</unit>
<unit id="M51YUE." name="assembly.edit.associated_build_part">
<segment>
<source>assembly.edit.associated_build_part</source>
<target>关联组件</target>
</segment>
</unit>
<unit id="nH9R9f." name="assembly.edit.associated_build_part.add">
<segment>
<source>assembly.edit.associated_build_part.add</source>
<target>添加组件</target>
</segment>
</unit>
<unit id="oVfOk.i" name="assembly.edit.associated_build.hint">
<segment>
<source>assembly.edit.associated_build.hint</source>
<target>此组件表示装配的生产实例。指定是否需要生产实例。如果不需要,则组件数量仅在构建相关项目时使用。</target>
</segment>
</unit>
<unit id="_wZ_JZY" name="assembly.edit.bom.import_bom">
<segment>
<source>assembly.edit.bom.import_bom</source>
<target>导入组件</target>
</segment>
</unit>
<unit id="vsmgKMw" name="log.database_updated.failed">
<segment>
<source>log.database_updated.failed</source>
<target>__log.database_updated.failed</target>
</segment>
</unit>
<unit id="GcQrTTE" name="log.database_updated.old_version">
<segment>
<source>log.database_updated.old_version</source>
<target>__log.database_updated.old_version</target>
</segment>
</unit>
<unit id="JJF47vK" name="log.database_updated.new_version">
<segment>
<source>log.database_updated.new_version</source>
<target>__log.database_updated.new_version</target>
</segment>
</unit>
<unit id="iVHS_sh" name="tree.tools.edit.assemblies">
<segment>
<source>tree.tools.edit.assemblies</source>
<target>装配列表</target>
</segment>
</unit>
<unit id="naAMjcH" name="assembly.bom_import.flash.success">
<segment>
<source>assembly.bom_import.flash.success</source>
<target>成功导入 %count% 个组件到装配中。</target>
</segment>
</unit>
<unit id="ScQhV.o" name="assembly.bom_import.flash.invalid_entries">
<segment>
<source>assembly.bom_import.flash.invalid_entries</source>
<target>验证错误!请检查导入的文件!</target>
</segment>
</unit>
<unit id="luj_uCZ" name="assembly.bom_import.flash.invalid_file">
<segment>
<source>assembly.bom_import.flash.invalid_file</source>
<target>文件导入失败。请确保选择了正确的文件格式。错误信息:%message%</target>
</segment>
</unit>
<unit id="u7SYWcB" name="assembly.bom.quantity">
<segment>
<source>assembly.bom.quantity</source>
<target>数量</target>
</segment>
</unit>
<unit id="D7dPvPL" name="assembly.bom.mountnames">
<segment>
<source>assembly.bom.mountnames</source>
<target>安装名称</target>
</segment>
</unit>
<unit id="rswC4eS" name="assembly.bom.instockAmount">
<segment state="translated">
<source>assembly.bom.instockAmount</source>
<target>库存数量</target>
</segment>
</unit>
<unit id="oNutri3" name="assembly.info.title">
<segment>
<source>assembly.info.title</source>
<target>装配信息</target>
</segment>
</unit>
<unit id="aO1rzVQ" name="assembly.info.info.label">
<segment>
<source>assembly.info.info.label</source>
<target>信息</target>
</segment>
</unit>
<unit id="9dOByT_" name="assembly.info.sub_assemblies.label">
<segment>
<source>assembly.info.sub_assemblies.label</source>
<target>子组件</target>
</segment>
</unit>
<unit id="Hmf0EwN" name="assembly.info.builds.label">
<segment>
<source>assembly.info.builds.label</source>
<target>构建</target>
</segment>
</unit>
<unit id="z3F4Rcu" name="assembly.info.bom_add_parts">
<segment>
<source>assembly.info.bom_add_parts</source>
<target>添加零件</target>
</segment>
</unit>
<unit id="ZbDTUTS" name="assembly.builds.check_assembly_status">
<segment>
<source>assembly.builds.check_assembly_status</source>
<target><![CDATA[当前装配状态为 <b>"%assembly_status%"</b>。请确认您是否要在该状态下构建组件!]]></target>
</segment>
</unit>
<unit id="xq1Soad" name="assembly.builds.build_not_possible">
<segment>
<source>assembly.builds.build_not_possible</source>
<target>无法构建:零件数量不足</target>
</segment>
</unit>
<unit id="njRyDHQ" name="assembly.builds.following_bom_entries_miss_instock">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock</source>
<target>库存中缺少足够的零件,无法构建 %number_of_builds% 次。缺少的零件包括:</target>
</segment>
</unit>
<unit id="sp7Z0MS" name="assembly.builds.build_possible">
<segment>
<source>assembly.builds.build_possible</source>
<target>可以构建</target>
</segment>
</unit>
<unit id="1uZzdEl" name="assembly.builds.number_of_builds_possible">
<segment>
<source>assembly.builds.number_of_builds_possible</source>
<target><![CDATA[您的库存中有足够的零件,可以构建 <b>%max_builds%</b> 个该组件。]]></target>
</segment>
</unit>
<unit id="pW6QLXf" name="assembly.builds.number_of_builds">
<segment>
<source>assembly.builds.number_of_builds</source>
<target>构建数量</target>
</segment>
</unit>
<unit id="mtJqT_a" name="assembly.build.btn_build">
<segment>
<source>assembly.build.btn_build</source>
<target>构建</target>
</segment>
</unit>
<unit id="LFSVVcP" name="assembly.builds.no_stocked_builds">
<segment>
<source>assembly.builds.no_stocked_builds</source>
<target>已构建并库存的数量</target>
</segment>
</unit>
<unit id="dGFHutJ" name="assembly.info.bom_entries_count">
<segment>
<source>assembly.info.bom_entries_count</source>
<target>条目</target>
</segment>
</unit>
<unit id="xJ7oBM4" name="assembly.info.sub_assemblies_count">
<segment>
<source>assembly.info.sub_assemblies_count</source>
<target>子组件</target>
</segment>
</unit>
<unit id="HZYhTlb" name="assembly.builds.stocked">
<segment>
<source>assembly.builds.stocked</source>
<target>库存中</target>
</segment>
</unit>
<unit id="9EG0PLW" name="assembly.builds.needed">
<segment>
<source>assembly.builds.needed</source>
<target>需要</target>
</segment>
</unit>
<unit id="tgs_7u9" name="assembly.add_parts_to_assembly">
<segment>
<source>assembly.add_parts_to_assembly</source>
<target>添加零件到组件</target>
</segment>
</unit>
<unit id="PPsM0Dg" name="assembly.bom.name">
<segment>
<source>assembly.bom.name</source>
<target>名称</target>
</segment>
</unit>
<unit id="nUEs.ld" name="assembly.bom.comment">
<segment>
<source>assembly.bom.comment</source>
<target>备注</target>
</segment>
</unit>
<unit id="87YpQ_u" name="assembly.builds.following_bom_entries_miss_instock_n">
<segment>
<source>assembly.builds.following_bom_entries_miss_instock_n</source>
<target>库存不足,无法构建 %number_of_builds% 次。缺少零件包括:</target>
</segment>
</unit>
<unit id="JlQhDdS" name="assembly.build.help">
<segment>
<source>assembly.build.help</source>
<target>选择部分库存零件及数量用于构建。每项零件使用复选框,如果零件已提取,也可以使用主复选框来选择所有项目。</target>
</segment>
</unit>
<unit id="iP5_QVj" name="assembly.build.required_qty">
<segment>
<source>assembly.build.required_qty</source>
<target>所需数量</target>
</segment>
</unit>
<unit id="UJpD7n6" name="assembly.import_bom">
<segment>
<source>assembly.import_bom</source>
<target>导入组件的零件</target>
</segment>
</unit>
<unit id="WTasGao" name="assembly.bom.part">
<segment>
<source>assembly.bom.part</source>
<target>零件</target>
</segment>
</unit>
<unit id="jHKh8Zp" name="assembly.bom.add_entry">
<segment>
<source>assembly.bom.add_entry</source>
<target>添加条目</target>
</segment>
</unit>
<unit id="RsZ77df" name="assembly.bom.price">
<segment>
<source>assembly.bom.price</source>
<target>价格</target>
</segment>
</unit>
<unit id="63adIrC" name="assembly.build.dont_check_quantity">
<segment state="translated">
<source>assembly.build.dont_check_quantity</source>
<target>不检查数量</target>
</segment>
</unit>
<unit id="O0DP6tK" name="assembly.build.dont_check_quantity.help">
<segment state="translated">
<source>assembly.build.dont_check_quantity.help</source>
<target>如果选中,即使库存不足,系统也会从库存中扣除声明的数量。</target>
</segment>
</unit>
<unit id="9bc0nzK" name="assembly.build.add_builds_to_builds_part">
<segment state="translated">
<source>assembly.build.add_builds_to_builds_part</source>
<target>将已构建的零件添加到组件</target>
</segment>
</unit>
<unit id="nl.jtSx" name="assembly.bom_import.type">
<segment state="translated">
<source>assembly.bom_import.type</source>
<target>类型</target>
</segment>
</unit>
<unit id="LtbSLHx" name="assembly.bom_import.type.json">
<segment state="translated">
<source>assembly.bom_import.type.json</source>
<target>JSON 文件(组件)</target>
</segment>
</unit>
<unit id="clXFAdN" name="assembly.bom_import.type.kicad_pcbnew">
<segment state="translated">
<source>assembly.bom_import.type.kicad_pcbnew</source>
<target>CSV 文件KiCAD Pcbnew</target>
</segment>
</unit>
<unit id="0IekETE" name="assembly.bom_import.clear_existing_bom">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom</source>
<target>在导入前清空现有数据</target>
</segment>
</unit>
<unit id="S4QY6pA" name="assembly.bom_import.clear_existing_bom.help">
<segment state="translated">
<source>assembly.bom_import.clear_existing_bom.help</source>
<target>如果选中,所有现有零件条目将被删除,新的导入数据将取而代之。</target>
</segment>
</unit>
<unit id="RVNWPsh" name="assembly.import_bom.template.header.json">
<segment>
<source>assembly.import_bom.template.header.json</source>
<target>装配 JSON 导入模板</target>
</segment>
</unit>
<unit id="eU4FfCr" name="assembly.import_bom.template.header.kicad_pcbnew">
<segment>
<source>assembly.import_bom.template.header.kicad_pcbnew</source>
<target>装配 CSV 模板KiCAD Pcbnew BOM</target>
</segment>
</unit>
<unit id="aLomVVS" name="assembly.bom_import.template.entry.name">
<segment>
<source>assembly.bom_import.template.entry.name</source>
<target>组件的零件名称</target>
</segment>
</unit>
<unit id="o4ZcLfV" name="assembly.bom_import.template.entry.part.mpnr">
<segment>
<source>assembly.bom_import.template.entry.part.mpnr</source>
<target>唯一制造商零件编号</target>
</segment>
</unit>
<unit id="n3YbKeU" name="assembly.bom_import.template.entry.part.ipn">
<segment>
<source>assembly.bom_import.template.entry.part.ipn</source>
<target>唯一 IPN 序列号</target>
</segment>
</unit>
<unit id="l2SYJP5" name="assembly.bom_import.template.entry.part.name">
<segment>
<source>assembly.bom_import.template.entry.part.name</source>
<target>零件名称</target>
</segment>
</unit>
<unit id="VgXQ1xW" name="assembly.bom_import.template.entry.part.manufacturer.name">
<segment>
<source>assembly.bom_import.template.entry.part.manufacturer.name</source>
<target>制造商名称</target>
</segment>
</unit>
<unit id="MpB.o_L" name="assembly.bom_import.template.entry.part.category.name">
<segment>
<source>assembly.bom_import.template.entry.part.category.name</source>
<target>类别名称</target>
</segment>
</unit>
<unit id="NIcfgj84" name="assembly.bom_import.template.json.table">
<segment>
<source>assembly.bom_import.template.json.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>字段</th>
<th>条件</th>
<th>数据类型</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>quantity</td>
<td>必填</td>
<td>浮点数 (Float)</td>
<td>必须存在,并包含大于 0.0 的浮点值 (Float)。</td>
</tr>
<tr>
<td>name</td>
<td>可选</td>
<td>字符串 (String)</td>
<td>如果存在,必须是非空字符串。</td>
</tr>
<tr>
<td>part</td>
<td>可选</td>
<td>对象/数组</td>
<td>
如果提供,则必须是对象/数组,并且以下字段中至少有一个被填写:
<ul>
<li>part.id</li>
<li>part.name</li>
</ul>
</td>
</tr>
<tr>
<td>part.id</td>
<td>可选</td>
<td>整数 (Integer)</td>
<td>整数 (Integer) &gt; 0。表示组件在 Part-DB 中的内部数字 ID。</td>
</tr>
<tr>
<td>part.name</td>
<td>可选</td>
<td>字符串 (String)</td>
<td>如果未提供 part.mpnr 或 part.ipn则必须是非空字符串。</td>
</tr>
<tr>
<td>part.mpnr</td>
<td>可选</td>
<td>字符串 (String)</td>
<td>如果未提供 part.name 或 part.ipn则必须是非空字符串。</td>
</tr>
<tr>
<td>part.ipn</td>
<td>可选</td>
<td>字符串 (String)</td>
<td>如果未提供 part.name 或 part.mpnr则必须是非空字符串。</td>
</tr>
<tr>
<td>part.description</td>
<td>可选</td>
<td>字符串或 null</td>
<td>如果存在,必须是非空字符串或 null。</td>
</tr>
<tr>
<td>part.manufacturer</td>
<td>可选</td>
<td>对象/数组</td>
<td>
如果存在,则必须是对象/数组,并且以下字段中至少有一个被填写:
<ul>
<li>manufacturer.id</li>
<li>manufacturer.name</li>
</ul>
</td>
</tr>
<tr>
<td>manufacturer.id</td>
<td>可选</td>
<td>整数 (Integer)</td>
<td>整数 (Integer) &gt; 0。表示制造商的内部数字 ID。</td>
</tr>
<tr>
<td>manufacturer.name</td>
<td>可选</td>
<td>字符串 (String)</td>
<td>如果未提供 manufacturer.id则必须是非空字符串。</td>
</tr>
<tr>
<td>part.category</td>
<td>可选</td>
<td>对象/数组</td>
<td>
如果存在,则必须是对象/数组,并且以下字段中至少有一个被填写:
<ul>
<li>category.id</li>
<li>category.name</li>
</ul>
</td>
</tr>
<tr>
<td>category.id</td>
<td>可选</td>
<td>整数 (Integer)</td>
<td>整数 (Integer) &gt; 0。表示组件类别的内部数字 ID。</td>
</tr>
<tr>
<td>category.name</td>
<td>可选</td>
<td>字符串 (String)</td>
<td>如果未提供 category.id则必须是非空字符串。</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="cU1bfDa" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns</source>
<target>预期的列:</target>
</segment>
</unit>
<unit id="gvaB1sb" name="assembly.bom_import.template.kicad_pcbnew.exptected_columns.note">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.exptected_columns.note</source>
<target>
<![CDATA[
<p><strong>注意:</strong> 未对类别管理中的特定组件进行映射。</p>
]]>
</target>
</segment>
</unit>
<unit id="translationUnit13" name="assembly.bom_import.template.kicad_pcbnew.table">
<segment>
<source>assembly.bom_import.template.kicad_pcbnew.table</source>
<target>
<![CDATA[
<table class="table table-bordered table-striped">
<thead class="thead-dark">
<tr>
<th>字段</th>
<th>条件</th>
<th>数据类型</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>ID</td>
<td>可选</td>
<td>整数</td>
<td>自由格式字段。每个组件的唯一标识号。</td>
</tr>
<tr>
<td>设计ator</td>
<td>可选</td>
<td>字符串</td>
<td>自由格式字段。PCB上组件的唯一参考标识符例如电阻1的"R1"。用于命名组件组中的位置。</td>
</tr>
<tr>
<td>封装</td>
<td>可选</td>
<td>字符串</td>
<td>自由格式字段。组件的封装类型或形式因子例如对于SMD电阻"0805"。</td>
</tr>
<tr>
<td>数量</td>
<td>必填</td>
<td>整数</td>
<td>创建一个组装实例所需的相同组件的数量。</td>
</tr>
<tr>
<td>描述</td>
<td>必填</td>
<td>字符串</td>
<td>组件的描述或功能,例如电阻值"10kΩ"或电容值"100nF"。在物料清单BOM条目中用作名称。</td>
</tr>
<tr>
<td>供应商和参考</td>
<td>可选</td>
<td>字符串</td>
<td>自由格式字段。例如,可以包含特定分销商的信息。</td>
</tr>
</tbody>
</table>
]]>
</target>
</segment>
</unit>
<unit id="QiZM3zY" name="typeahead.parts.part.name">
<segment>
<source>typeahead.parts.part.name</source>
<target>%name%(零件)</target>
</segment>
</unit>
<unit id="WhYL2yX" name="typeahead.parts.assembly.name">
<segment>
<source>typeahead.parts.assembly.name</source>
<target>%name%(组件)</target>
</segment>
</unit>
<unit id="4cgba2c" name="projects.build.form.part">
<segment>
<source>projects.build.form.part</source>
<target>零件“%name%”</target>
</segment>
</unit>
<unit id="1bCA1zb" name="projects.build.form.assembly">
<segment>
<source>projects.build.form.assembly</source>
<target>组件“%name%”</target>
</segment>
</unit>
<unit id="2cDB2ac" name="projects.build.form.assembly.bom.entry">
<segment>
<source>projects.build.form.assembly.bom.entry</source>
<target>%name%(需数量:%quantity%</target>
</segment>
</unit>
<unit id="3dEC3bd" name="projects.build.form.assembly.bom.entry.no.stock">
<segment>
<source>projects.build.form.assembly.bom.entry.no.stock</source>
<target>库存不足</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -245,6 +245,12 @@
<target>Musíte vybrat díl pro položku BOM dílu nebo nastavit název pro položku BOM bez dílu.</target>
</segment>
</unit>
<unit id="Q42Zh.e" name="validator.project.bom_entry.only_part_or_assembly_allowed">
<segment state="translated">
<source>validator.project.bom_entry.only_part_or_assembly_allowed</source>
<target>Je povoleno vybrat pouze jednu součástku nebo sestavu. Upravit prosím svůj výběr!</target>
</segment>
</unit>
<unit id="5CEup_N" name="project.bom_entry.name_already_in_bom">
<segment state="translated">
<source>project.bom_entry.name_already_in_bom</source>
@ -365,5 +371,23 @@
<target>Neplatný kód. Zkontrolujte, zda je vaše ověřovací aplikace správně nastavena a zda je čas správně nastaven jak na serveru, tak na ověřovacím zařízení.</target>
</segment>
</unit>
<unit id="qyoCAaP" name="assembly.bom_entry.part_already_in_bom">
<segment>
<source>assembly.bom_entry.part_already_in_bom</source>
<target>Tato součást již existuje ve skupině!</target>
</segment>
</unit>
<unit id="Pi60dJ9" name="assembly.bom_entry.name_already_in_bom">
<segment>
<source>assembly.bom_entry.name_already_in_bom</source>
<target>Již existuje součást s tímto názvem!</target>
</segment>
</unit>
<unit id="KWc.wZ4" name="validator.assembly.bom_entry.name_or_part_needed">
<segment>
<source>validator.assembly.bom_entry.name_or_part_needed</source>
<target>Musíte vybrat součást nebo nastavit název pro nesoučást!</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -245,6 +245,12 @@
<target>Du skal vælge en komponent eller angive et navn til en ikke-komponent styklistepost!</target>
</segment>
</unit>
<unit id="Q42Zh.e" name="validator.project.bom_entry.only_part_or_assembly_allowed">
<segment state="translated">
<source>validator.project.bom_entry.only_part_or_assembly_allowed</source>
<target>Det er kun tilladt at vælge én del eller en samling. Venligst tilpas dit valg!</target>
</segment>
</unit>
<unit id="WF_v4ih" name="project.bom_entry.name_already_in_bom">
<segment state="translated">
<source>project.bom_entry.name_already_in_bom</source>
@ -341,5 +347,23 @@
<target>Denne leverandørstregkodeværdi er allerede brugt til en anden beholdning. Stregkoden skal være unik!</target>
</segment>
</unit>
<unit id="qyoCAaP" name="assembly.bom_entry.part_already_in_bom">
<segment>
<source>assembly.bom_entry.part_already_in_bom</source>
<target>Denne del eksisterer allerede i gruppen!</target>
</segment>
</unit>
<unit id="Pi60dJ9" name="assembly.bom_entry.name_already_in_bom">
<segment>
<source>assembly.bom_entry.name_already_in_bom</source>
<target>Der findes allerede en del med dette navn!</target>
</segment>
</unit>
<unit id="KWc.wZ4" name="validator.assembly.bom_entry.name_or_part_needed">
<segment>
<source>validator.assembly.bom_entry.name_or_part_needed</source>
<target>Du skal vælge en del eller sætte et navn for en ikke-del!</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -242,7 +242,13 @@
<unit id="P31Yg.d" name="validator.project.bom_entry.name_or_part_needed">
<segment state="translated">
<source>validator.project.bom_entry.name_or_part_needed</source>
<target>Sie müssen ein Bauteil auswählen, oder einen Namen für ein nicht-Bauteil BOM-Eintrag setzen!</target>
<target>Sie müssen ein Bauteil bzw. eine Baugruppe auswählen, oder einen Namen für ein nicht-Bauteil BOM-Eintrag setzen!</target>
</segment>
</unit>
<unit id="Q42Zh.e" name="validator.project.bom_entry.only_part_or_assembly_allowed">
<segment state="translated">
<source>validator.project.bom_entry.only_part_or_assembly_allowed</source>
<target>Es darf nur ein Bauteil oder eine Baugruppe ausgewählt werden. Bitte passen Sie Ihre Auswahl an!</target>
</segment>
</unit>
<unit id="5CEup_N" name="project.bom_entry.name_already_in_bom">
@ -365,5 +371,23 @@
<target>Ungültiger Code. Überprüfen Sie, ob die Authenticator App korrekt eingerichtet ist und ob der Server und das Gerät beide die korrekte Uhrzeit eingestellt haben.</target>
</segment>
</unit>
<unit id="qyoCAaP" name="assembly.bom_entry.part_already_in_bom">
<segment>
<source>assembly.bom_entry.part_already_in_bom</source>
<target>Dieses Bauteil existiert bereits in der Gruppe!</target>
</segment>
</unit>
<unit id="Pi60dJ9" name="assembly.bom_entry.name_already_in_bom">
<segment>
<source>assembly.bom_entry.name_already_in_bom</source>
<target>Es gibt bereits einen Bauteil mit diesem Namen!</target>
</segment>
</unit>
<unit id="KWc.wZ4" name="validator.assembly.bom_entry.name_or_part_needed">
<segment>
<source>validator.assembly.bom_entry.name_or_part_needed</source>
<target>Sie müssen ein Bauteil auswählen, oder einen Namen für ein nicht-Bauteil setzen!</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -7,5 +7,29 @@
<target>Ο εσωτερικός αριθμός εξαρτήματος πρέπει να είναι μοναδικός. {{ value }} χρησιμοποιείται ήδη!</target>
</segment>
</unit>
<unit id="Q42Zh.e" name="validator.project.bom_entry.only_part_or_assembly_allowed">
<segment state="translated">
<source>validator.project.bom_entry.only_part_or_assembly_allowed</source>
<target>Det er kun tilladt at vælge én del eller en samling. Venligst tilpas dit valg!</target>
</segment>
</unit>
<unit id="qyoCAaP" name="assembly.bom_entry.part_already_in_bom">
<segment>
<source>assembly.bom_entry.part_already_in_bom</source>
<target>Αυτό το εξάρτημα υπάρχει ήδη στην ομάδα!</target>
</segment>
</unit>
<unit id="Pi60dJ9" name="assembly.bom_entry.name_already_in_bom">
<segment>
<source>assembly.bom_entry.name_already_in_bom</source>
<target>Υπάρχει ήδη ένα εξάρτημα με αυτό το όνομα!</target>
</segment>
</unit>
<unit id="KWc.wZ4" name="validator.assembly.bom_entry.name_or_part_needed">
<segment>
<source>validator.assembly.bom_entry.name_or_part_needed</source>
<target>Πρέπει να επιλέξετε ένα εξάρτημα ή να βάλετε ένα όνομα για ένα μη εξάρτημα!</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -242,7 +242,13 @@
<unit id="P31Yg.d" name="validator.project.bom_entry.name_or_part_needed">
<segment state="translated">
<source>validator.project.bom_entry.name_or_part_needed</source>
<target>You have to choose a part for a part BOM entry or set a name for a non-part BOM entry.</target>
<target>You have to select a part or assembly, or set a name for a non-component Bom entry!</target>
</segment>
</unit>
<unit id="Q42Zh.e" name="validator.project.bom_entry.only_part_or_assembly_allowed">
<segment state="translated">
<source>validator.project.bom_entry.only_part_or_assembly_allowed</source>
<target>Only one part or assembly may be selected. Please modify your selection!</target>
</segment>
</unit>
<unit id="5CEup_N" name="project.bom_entry.name_already_in_bom">
@ -365,5 +371,23 @@
<target>Invalid code. Check that your authenticator app is set up correctly and that both the server and authentication device has the time set correctly.</target>
</segment>
</unit>
<unit id="qyoCAaP" name="assembly.bom_entry.part_already_in_bom">
<segment>
<source>assembly.bom_entry.part_already_in_bom</source>
<target>__assembly.bom_entry.part_already_in_bom</target>
</segment>
</unit>
<unit id="Pi60dJ9" name="assembly.bom_entry.name_already_in_bom">
<segment>
<source>assembly.bom_entry.name_already_in_bom</source>
<target>__assembly.bom_entry.name_already_in_bom</target>
</segment>
</unit>
<unit id="KWc.wZ4" name="validator.assembly.bom_entry.name_or_part_needed">
<segment>
<source>validator.assembly.bom_entry.name_or_part_needed</source>
<target>__validator.assembly.bom_entry.name_or_part_needed</target>
</segment>
</unit>
</file>
</xliff>

Some files were not shown because too many files have changed in this diff Show more