Erweiterungstätigkeiten zur IPN-Vorschlagsliste anhand von Präfixen aus den Kategorien

This commit is contained in:
Marcel Diegelmann 2025-04-01 16:10:10 +02:00
parent cbfe1d4cc8
commit 7162199e61
32 changed files with 1482 additions and 6 deletions

18
.env
View file

@ -53,6 +53,8 @@ EMAIL_SENDER_EMAIL=noreply@partdb.changeme
EMAIL_SENDER_NAME="Part-DB Mailer"
# Set this to 1 to allow reset of a password per email
ALLOW_EMAIL_PW_RESET=0
# Set this to 0 to allow to enter already available IPN. In this case a unique increment is appended to the user input.
ENFORCE_UNIQUE_IPN=1
###################################################################################
# Error pages settings
@ -126,6 +128,22 @@ NO_URL_REWRITE_AVAILABLE=0
# Set to 1, if Part-DB should redirect all HTTP requests to HTTPS. You dont need to configure this, if your webserver already does this.
REDIRECT_TO_HTTPS=0
# If you want to use fixer.io for currency conversion, you have to set this to your API key
FIXER_API_KEY=CHANGEME
# Override value if you want to show to show a given text on homepage.
# When this is empty the content of config/banner.md is used as banner
BANNER=""
# Enable the part image overlay which shows name and filename of the picture
SHOW_PART_IMAGE_OVERLAY=1
# Define the number of digits used for the incremental numbering of parts in the IPN (Internal Part Number) autocomplete system.
AUTOCOMPLETE_PART_DIGITS=4
APP_ENV=prod
APP_SECRET=a03498528f5a5fc089273ec9ae5b2849
# Set this to zero, if you want to disable the year 2038 bug check on 32-bit systems (it will cause errors with current 32-bit PHP versions)
DISABLE_YEAR2038_BUG_CHECK=0

View file

@ -0,0 +1,220 @@
import { Controller } from "@hotwired/stimulus";
import "../../css/components/autocomplete_bootstrap_theme.css";
export default class extends Controller {
static targets = ["input"];
static values = {
partId: Number,
partCategoryId: Number,
suggestions: Object,
commonSectionHeader: String, // Dynamic header for common Prefixes
partIncrementHeader: String, // Dynamic header for new possible part increment
suggestUrl: String,
};
connect() {
this.configureAutocomplete();
this.watchCategoryChanges();
}
templates = {
commonSectionHeader({ title, html }) {
return html`
<section class="aa-Source">
<div class="aa-SourceHeader">
<span class="aa-SourceHeaderTitle">${title}</span>
<div class="aa-SourceHeaderLine"></div>
</div>
</section>
`;
},
partIncrementHeader({ title, html }) {
return html`
<section class="aa-Source">
<div class="aa-SourceHeader">
<span class="aa-SourceHeaderTitle">${title}</span>
<div class="aa-SourceHeaderLine"></div>
</div>
</section>
`;
},
list({ html }) {
return html`
<ul class="aa-List" role="listbox"></ul>
`;
},
item({ suggestion, description, html }) {
return html`
<li class="aa-Item" role="option" data-suggestion="${suggestion}" aria-selected="false">
<div class="aa-ItemWrapper">
<div class="aa-ItemContent">
<div class="aa-ItemIcon aa-ItemIcon--noBorder">
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M12 21c4.971 0 9-4.029 9-9s-4.029-9-9-9-9 4.029-9 9 4.029 9 9 9z"></path>
</svg>
</div>
<div class="aa-ItemContentBody">
<div class="aa-ItemContentTitle">${suggestion}</div>
<div class="aa-ItemContentDescription">${description}</div>
</div>
</div>
</div>
</li>
`;
},
};
configureAutocomplete() {
const inputField = this.inputTarget;
const commonPrefixes = this.suggestionsValue.commonPrefixes || [];
const prefixesPartIncrement = this.suggestionsValue.prefixesPartIncrement || [];
const commonHeader = this.commonSectionHeaderValue;
const partIncrementHeader = this.partIncrementHeaderValue;
if (!inputField || (!commonPrefixes.length && !prefixesPartIncrement.length)) return;
// Check whether the panel should be created at the update
if (this.isPanelInitialized) {
const existingPanel = inputField.parentNode.querySelector(".aa-Panel");
if (existingPanel) {
// Only remove the panel in the update phase
existingPanel.remove();
}
}
// Create panel
const panel = document.createElement("div");
panel.classList.add("aa-Panel");
panel.style.display = "none";
// Create panel layout
const panelLayout = document.createElement("div");
panelLayout.classList.add("aa-PanelLayout", "aa-Panel--scrollable");
// Section for common prefixes
if (commonPrefixes.length) {
const commonSection = document.createElement("section");
commonSection.classList.add("aa-Source");
const commonSectionHeader = this.templates.commonSectionHeader({
title: commonHeader,
html: String.raw,
});
commonSection.innerHTML += commonSectionHeader;
const commonList = document.createElement("ul");
commonList.classList.add("aa-List");
commonList.setAttribute("role", "listbox");
commonPrefixes.forEach((prefix) => {
const itemHTML = this.templates.item({
suggestion: prefix.title,
description: prefix.description,
html: String.raw,
});
commonList.innerHTML += itemHTML;
});
commonSection.appendChild(commonList);
panelLayout.appendChild(commonSection);
}
// Section for prefixes part increment
if (prefixesPartIncrement.length) {
const partIncrementSection = document.createElement("section");
partIncrementSection.classList.add("aa-Source");
const partIncrementHeaderHtml = this.templates.partIncrementHeader({
title: partIncrementHeader,
html: String.raw,
});
partIncrementSection.innerHTML += partIncrementHeaderHtml;
const partIncrementList = document.createElement("ul");
partIncrementList.classList.add("aa-List");
partIncrementList.setAttribute("role", "listbox");
prefixesPartIncrement.forEach((prefix) => {
const itemHTML = this.templates.item({
suggestion: prefix.title,
description: prefix.description,
html: String.raw,
});
partIncrementList.innerHTML += itemHTML;
});
partIncrementSection.appendChild(partIncrementList);
panelLayout.appendChild(partIncrementSection);
}
panel.appendChild(panelLayout);
inputField.parentNode.appendChild(panel);
inputField.addEventListener("focus", () => {
panel.style.display = "block";
});
inputField.addEventListener("blur", () => {
setTimeout(() => {
panel.style.display = "none";
}, 100);
});
// Selection of an item
panelLayout.addEventListener("mousedown", (event) => {
const target = event.target.closest("li");
if (target) {
inputField.value = target.dataset.suggestion;
panel.style.display = "none";
}
});
this.isPanelInitialized = true;
};
watchCategoryChanges() {
const categoryField = document.querySelector('[data-ipn-suggestion="categoryField"]');
this.previousCategoryId = Number(this.partCategoryIdValue);
if (categoryField) {
categoryField.addEventListener("change", () => {
const categoryId = Number(categoryField.value);
// Check whether the category has changed compared to the previous ID
if (categoryId !== this.previousCategoryId) {
this.fetchNewSuggestions(categoryId);
this.previousCategoryId = categoryId;
}
});
}
}
fetchNewSuggestions(categoryId) {
const baseUrl = this.suggestUrlValue;
const partId = this.partIdValue;
const url = `${baseUrl}?partId=${partId}&categoryId=${categoryId}`;
fetch(url, {
method: "GET",
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
},
})
.then((response) => {
if (!response.ok) {
throw new Error(`Error when calling up the IPN-suggestions: ${response.status}`);
}
return response.json();
})
.then((data) => {
this.suggestionsValue = data;
this.configureAutocomplete();
})
.catch((error) => {
console.error("Errors when loading the new IPN-suggestions:", error);
});
};
}

View file

@ -9,6 +9,8 @@ parameters:
# This is used as workaround for places where we can not access the settings directly (like the 2FA application names)
partdb.title: '%env(string:settings:customization:instanceName)%' # The title shown inside of Part-DB (e.g. in the navbar and on homepage)
partdb.locale_menu: ['en', 'de', 'it', 'fr', 'ru', 'ja', 'cs', 'da', 'zh', 'pl'] # The languages that are shown in user drop down menu
partdb.enforce_change_comments_for: '%env(csv:ENFORCE_CHANGE_COMMENTS_FOR)%' # The actions for which a change comment is required (e.g. "part_edit", "part_create", etc.). If this is empty, change comments are not required at all.
partdb.autocomplete_part_digits: '%env(trim:string:AUTOCOMPLETE_PART_DIGITS)%' # The number of digits used for the incremental numbering of parts in the IPN (Internal Part Number) autocomplete system.
partdb.default_uri: '%env(string:DEFAULT_URI)%' # The default URI to use for the Part-DB instance (e.g. https://part-db.example.com/). This is used for generating links in emails
@ -35,6 +37,7 @@ parameters:
######################################################################################################################
partdb.gdpr_compliance: true # If this option is activated, IP addresses are anonymized to be GDPR compliant
partdb.users.email_pw_reset: '%env(bool:ALLOW_EMAIL_PW_RESET)%' # Config if users are able, to reset their password by email. By default this enabled, when a mail server is configured.
partdb.users.enforce_unique_ipn: '%env(bool:ENFORCE_UNIQUE_IPN)%' # Config if users are able, to enter an already available IPN. In this case a unique increment is appended to the user input.
######################################################################################################################
# Mail settings

View file

@ -263,6 +263,30 @@ services:
tags:
- { name: 'doctrine.fixtures.purger_factory', alias: 'reset_autoincrement_purger' }
App\Controller\PartController:
bind:
$autocompletePartDigits: '%partdb.autocomplete_part_digits%'
App\Controller\TypeaheadController:
bind:
$autocompletePartDigits: '%partdb.autocomplete_part_digits%'
App\Repository\PartRepository:
arguments:
$translator: '@translator'
tags: ['doctrine.repository_service']
App\EventSubscriber\UserSystem\PartUniqueIpnSubscriber:
arguments:
$enforceUniqueIpn: '%partdb.users.enforce_unique_ipn%'
tags:
- { name: doctrine.event_subscriber }
App\Validator\Constraints\UniquePartIpnValidator:
arguments:
$enforceUniqueIpn: '%partdb.users.enforce_unique_ipn%'
tags: [ 'validator.constraint_validator' ]
# We are needing this service inside a migration, where only the container is injected. So we need to define it as public, to access it from the container.
App\Services\UserSystem\PermissionPresetsHelper:
public: true

View file

@ -116,6 +116,10 @@ bundled with Part-DB. Set `DATABASE_MYSQL_SSL_VERIFY_CERT` if you want to accept
value should be handled as confidential data and not shared publicly.
* `SHOW_PART_IMAGE_OVERLAY`: Set to 0 to disable the part image overlay, which appears if you hover over an image in the
part image gallery
* `AUTOCOMPLETE_PART_DIGITS`: Defines the fixed number of digits used as the increment at the end of an IPN (Internal Part Number).
IPN prefixes, maintained within part categories and their hierarchy, form the foundation for suggesting complete IPNs.
These suggestions become accessible during IPN input of a part. The constant specifies the digits used to calculate and assign
unique increments for parts within a category hierarchy, ensuring consistency and uniqueness in IPN generation.
### E-Mail settings (all env only)
@ -128,6 +132,8 @@ bundled with Part-DB. Set `DATABASE_MYSQL_SSL_VERIFY_CERT` if you want to accept
sent from.
* `ALLOW_EMAIL_PW_RESET`: Set this value to true, if you want to allow users to reset their password via an email
notification. You have to configure the mail provider first before via the MAILER_DSN setting.
* `ENFORCE_UNIQUE_IPN`: Set this value to false, if you want to allow users to enter a already available IPN for a part entry.
In this case a unique increment is appended to the user input.
### Table related settings

View file

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20250325073036 extends AbstractMigration
{
public function up(Schema $schema): void
{
$this->addSql('ALTER TABLE categories ADD part_ipn_prefix VARCHAR(255) NOT NULL AFTER partname_regex');
$this->addSql('DROP INDEX UNIQ_6940A7FE3D721C14 ON parts');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE `categories` DROP part_ipn_prefix');
$this->addSql('CREATE UNIQUE INDEX UNIQ_6940A7FE3D721C14 ON `parts` (ipn)');
}
}

View file

@ -76,6 +76,7 @@ final class PartController extends AbstractController
private readonly EntityManagerInterface $em,
private readonly EventCommentHelper $commentHelper,
private readonly PartInfoSettings $partInfoSettings,
private readonly int $autocompletePartDigits,
) {
}
@ -457,10 +458,13 @@ final class PartController extends AbstractController
$template = 'parts/edit/update_from_ip.html.twig';
}
$partRepository = $this->em->getRepository(Part::class);
return $this->render(
$template,
[
'part' => $new_part,
'ipnSuggestions' => $partRepository->autoCompleteIpn($data, $this->autocompletePartDigits),
'form' => $form,
'merge_old_name' => $merge_infos['tname_before'] ?? null,
'merge_other' => $merge_infos['other_part'] ?? null,
@ -470,7 +474,6 @@ final class PartController extends AbstractController
);
}
#[Route(path: '/{id}/add_withdraw', name: 'part_add_withdraw', methods: ['POST'])]
public function withdrawAddHandler(Part $part, Request $request, EntityManagerInterface $em, PartLotWithdrawAddHelper $withdrawAddHelper): Response
{

View file

@ -69,7 +69,8 @@ class TypeaheadController extends AbstractController
public function __construct(
protected AttachmentURLGenerator $urlGenerator,
protected Packages $assets,
protected TranslatorInterface $translator
protected TranslatorInterface $translator,
protected int $autocompletePartDigits,
) {
}
@ -271,4 +272,28 @@ class TypeaheadController extends AbstractController
return new JsonResponse($data, Response::HTTP_OK, [], true);
}
#[Route(path: '/parts/ipn-suggestions', name: 'ipn_suggestions', methods: ['GET'])]
public function ipnSuggestions(
Request $request,
EntityManagerInterface $entityManager
): JsonResponse {
$partId = $request->query->get('partId');
if ($partId === '0' || $partId === 'undefined' || $partId === 'null') {
$partId = null;
}
$categoryId = $request->query->getInt('categoryId');
/** @var Part $part */
$part = $partId !== null ? $entityManager->getRepository(Part::class)->find($partId) : new Part();
$category = $entityManager->getRepository(Category::class)->find($categoryId);
$clonedPart = clone $part;
$clonedPart->setCategory($category);
$partRepository = $entityManager->getRepository(Part::class);
$ipnSuggestions = $partRepository->autoCompleteIpn($clonedPart, $this->autocompletePartDigits);
return new JsonResponse($ipnSuggestions);
}
}

View file

@ -118,6 +118,13 @@ class Category extends AbstractPartsContainingDBElement
#[ORM\Column(type: Types::TEXT)]
protected string $partname_regex = '';
/**
* @var string The prefix for ipn generation for created parts in this category.
*/
#[Groups(['full', 'import', 'category:read', 'category:write'])]
#[ORM\Column(type: Types::STRING, length: 255, nullable: false)]
protected string $part_ipn_prefix = '';
/**
* @var bool Set to true, if the footprints should be disabled for parts this category (not implemented yet).
*/
@ -225,6 +232,16 @@ class Category extends AbstractPartsContainingDBElement
return $this;
}
public function getPartIpnPrefix(): string
{
return $this->part_ipn_prefix;
}
public function setPartIpnPrefix(string $part_ipn_prefix): void
{
$this->part_ipn_prefix = $part_ipn_prefix;
}
public function isDisableFootprints(): bool
{
return $this->disable_footprints;

View file

@ -62,7 +62,6 @@ use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
@ -76,7 +75,6 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
* @extends AttachmentContainingDBElement<PartAttachment>
* @template-use ParametersTrait<PartParameter>
*/
#[UniqueEntity(fields: ['ipn'], message: 'part.ipn.must_be_unique')]
#[ORM\Entity(repositoryClass: PartRepository::class)]
#[ORM\EntityListeners([TreeCacheInvalidationListener::class])]
#[ORM\Table('`parts`')]

View file

@ -29,6 +29,7 @@ use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Constraints\Length;
use App\Validator\Constraints\UniquePartIpnConstraint;
/**
* Advanced properties of a part, not related to a more specific group.
@ -62,8 +63,9 @@ trait AdvancedPropertyTrait
*/
#[Assert\Length(max: 100)]
#[Groups(['extended', 'full', 'import', 'part:read', 'part:write'])]
#[ORM\Column(type: Types::STRING, length: 100, unique: true, nullable: true)]
#[ORM\Column(type: Types::STRING, length: 100, nullable: true)]
#[Length(max: 100)]
#[UniquePartIpnConstraint]
protected ?string $ipn = null;
/**

View file

@ -0,0 +1,73 @@
<?php
namespace App\EventSubscriber\UserSystem;
use App\Entity\Parts\Part;
use Doctrine\Common\EventSubscriber;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use Doctrine\ORM\Events;
use Doctrine\ORM\EntityManagerInterface;
class PartUniqueIpnSubscriber implements EventSubscriber
{
public function __construct(
private EntityManagerInterface $entityManager,
private readonly bool $enforceUniqueIpn = false
) {
}
public function getSubscribedEvents(): array
{
return [
Events::prePersist,
Events::preUpdate,
];
}
public function prePersist(LifecycleEventArgs $args): void
{
$entity = $args->getObject();
if ($entity instanceof Part) {
$this->ensureUniqueIpn($entity);
}
}
public function preUpdate(LifecycleEventArgs $args): void
{
$entity = $args->getObject();
if ($entity instanceof Part) {
$this->ensureUniqueIpn($entity);
}
}
private function ensureUniqueIpn(Part $part): void
{
if ($part->getIpn() === null || $part->getIpn() === '') {
return;
}
$existingPart = $this->entityManager
->getRepository(Part::class)
->findOneBy(['ipn' => $part->getIpn()]);
if ($existingPart && $existingPart->getId() !== $part->getId()) {
if ($this->enforceUniqueIpn) {
return;
}
// Anhang eines Inkrements bis ein einzigartiger Wert gefunden wird
$increment = 1;
$originalIpn = $part->getIpn();
while ($this->entityManager
->getRepository(Part::class)
->findOneBy(['ipn' => $originalIpn . "_$increment"])) {
$increment++;
}
$part->setIpn($originalIpn . "_$increment");
}
}
}

View file

@ -84,6 +84,17 @@ class CategoryAdminForm extends BaseEntityAdminForm
'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity),
]);
$builder->add('part_ipn_prefix', TextType::class, [
'required' => false,
'empty_data' => '',
'label' => 'category.edit.part_ipn_prefix',
'help' => 'category.edit.part_ipn_prefix.help',
'attr' => [
'placeholder' => 'category.edit.part_ipn_prefix.placeholder',
],
'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity),
]);
$builder->add('default_description', RichTextEditorType::class, [
'required' => false,
'empty_data' => '',

View file

@ -104,6 +104,9 @@ class PartBaseType extends AbstractType
'disable_not_selectable' => true,
//Do not require category for new parts, so that the user must select the category by hand and cannot forget it (the requirement is handled by the constraint in the entity)
'required' => !$new_part,
'attr' => [
'data-ipn-suggestion' => 'categoryField',
]
])
->add('footprint', StructuralEntityType::class, [
'class' => Footprint::class,
@ -175,6 +178,11 @@ class PartBaseType extends AbstractType
'required' => false,
'empty_data' => null,
'label' => 'part.edit.ipn',
'attr' => [
'class' => 'ipn-suggestion-field',
'data-elements--ipn-suggestion-target' => 'input',
'autocomplete' => 'off',
]
]);
//Comment section

View file

@ -22,17 +22,31 @@ declare(strict_types=1);
namespace App\Repository;
use App\Entity\Parts\Category;
use App\Entity\Parts\Part;
use App\Entity\Parts\PartLot;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\QueryBuilder;
use Symfony\Contracts\Translation\TranslatorInterface;
use Doctrine\ORM\EntityManagerInterface;
/**
* @extends NamedDBElementRepository<Part>
*/
class PartRepository extends NamedDBElementRepository
{
private TranslatorInterface $translator;
public function __construct(
EntityManagerInterface $em,
TranslatorInterface $translator
) {
parent::__construct($em, $em->getClassMetadata(Part::class));
$this->translator = $translator;
}
/**
* Gets the summed up instock of all parts (only parts without a measurement unit).
*
@ -94,4 +108,109 @@ class PartRepository extends NamedDBElementRepository
return $qb->getQuery()->getResult();
}
public function autoCompleteIpn(Part $part, int $autocompletePartDigits): array
{
$category = $part->getCategory();
$ipnSuggestions = ['commonPrefixes' => [], 'prefixesPartIncrement' => []];
// Validate the category and ensure it's an instance of Category
if ($category instanceof Category) {
$currentPath = $category->getPartIpnPrefix();
$directIpnPrefixEmpty = $category->getPartIpnPrefix() === '';
$currentPath = $currentPath === '' ? 'n.a.' : $currentPath;
$increment = $this->generateNextPossiblePartIncrement($currentPath, $part, $autocompletePartDigits);
$ipnSuggestions['commonPrefixes'][] = [
'title' => $currentPath . '-',
'description' => $directIpnPrefixEmpty ? $this->translator->trans('part.edit.tab.advanced.ipn.prefix_empty.direct_category', ['%name%' => $category->getName()]) : $this->translator->trans('part.edit.tab.advanced.ipn.prefix.direct_category')
];
$ipnSuggestions['prefixesPartIncrement'][] = [
'title' => $currentPath . '-' . $increment,
'description' => $directIpnPrefixEmpty ? $this->translator->trans('part.edit.tab.advanced.ipn.prefix_empty.direct_category', ['%name%' => $category->getName()]) : $this->translator->trans('part.edit.tab.advanced.ipn.prefix.direct_category.increment')
];
// Process parent categories
$parentCategory = $category->getParent();
while ($parentCategory instanceof Category) {
// Prepend the parent category's prefix to the current path
$currentPath = $parentCategory->getPartIpnPrefix() . '-' . $currentPath;
$currentPath = $parentCategory->getPartIpnPrefix() === '' ? 'n.a.-' . $currentPath : $currentPath;
$ipnSuggestions['commonPrefixes'][] = [
'title' => $currentPath . '-',
'description' => $this->translator->trans('part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment')
];
$increment = $this->generateNextPossiblePartIncrement($currentPath, $part, $autocompletePartDigits);
$ipnSuggestions['prefixesPartIncrement'][] = [
'title' => $currentPath . '-' . $increment,
'description' => $this->translator->trans('part.edit.tab.advanced.ipn.prefix.hierarchical.increment')
];
// Move to the next parent category
$parentCategory = $parentCategory->getParent();
}
} elseif ($part->getID() === null) {
$ipnSuggestions['commonPrefixes'][] = [
'title' => 'n.a.',
'description' => $this->translator->trans('part.edit.tab.advanced.ipn.prefix.not_saved')
];
}
return $ipnSuggestions;
}
public function generateNextPossiblePartIncrement(string $currentPath, Part $currentPart, int $autocompletePartDigits): string
{
$qb = $this->createQueryBuilder('part');
$expectedLength = strlen($currentPath) + 1 + $autocompletePartDigits; // Path + '-' + $autocompletePartDigits digits
// Fetch all parts in the given category, sorted by their ID in ascending order
$qb->select('part')
->where('part.ipn LIKE :ipnPattern')
->andWhere('LENGTH(part.ipn) = :expectedLength')
->setParameter('ipnPattern', $currentPath . '%')
->setParameter('expectedLength', $expectedLength)
->orderBy('part.id', 'ASC');
$parts = $qb->getQuery()->getResult();
// Collect all used increments in the category
$usedIncrements = [];
foreach ($parts as $part) {
if ($part->getIpn() === null || $part->getIpn() === '') {
continue;
}
if ($part->getId() === $currentPart->getId()) {
// Extract and return the current part's increment directly
$incrementPart = substr($part->getIpn(), -$autocompletePartDigits);
if (is_numeric($incrementPart)) {
return str_pad((string) $incrementPart, $autocompletePartDigits, '0', STR_PAD_LEFT);
}
}
// Extract last $autocompletePartDigits digits for possible available part increment
$incrementPart = substr($part->getIpn(), -$autocompletePartDigits);
if (is_numeric($incrementPart)) {
$usedIncrements[] = (int) $incrementPart;
}
}
// Generate the next free $autocompletePartDigits-digit increment
$nextIncrement = 1; // Start at the beginning
while (in_array($nextIncrement, $usedIncrements)) {
$nextIncrement++;
}
return str_pad((string) $nextIncrement, $autocompletePartDigits, '0', STR_PAD_LEFT);
}
}

View file

@ -0,0 +1,20 @@
<?php
namespace App\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
/**
* @Annotation
* @Target({"PROPERTY"})
*/
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class UniquePartIpnConstraint extends Constraint
{
public string $message = 'part.ipn.must_be_unique';
public function validatedBy(): string
{
return UniquePartIpnValidator::class;
}
}

View file

@ -0,0 +1,38 @@
<?php
namespace App\Validator\Constraints;
use App\Entity\Parts\Part;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Doctrine\ORM\EntityManagerInterface;
class UniquePartIpnValidator extends ConstraintValidator
{
private EntityManagerInterface $entityManager;
private bool $enforceUniqueIpn;
public function __construct(EntityManagerInterface $entityManager, bool $enforceUniqueIpn)
{
$this->entityManager = $entityManager;
$this->enforceUniqueIpn = $enforceUniqueIpn;
}
public function validate($value, Constraint $constraint)
{
if (null === $value || '' === $value) {
return;
}
$repository = $this->entityManager->getRepository(Part::class);
$existingPart = $repository->findOneBy(['ipn' => $value]);
if ($existingPart) {
if ($this->enforceUniqueIpn) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $value)
->addViolation();
}
}
}
}

View file

@ -33,6 +33,7 @@
<hr>
{{ form_row(form.partname_regex) }}
{{ form_row(form.partname_hint) }}
{{ form_row(form.part_ipn_prefix) }}
<hr>
{{ form_row(form.default_description) }}
{{ form_row(form.default_comment) }}

View file

@ -1,5 +1,14 @@
{{ form_row(form.needsReview) }}
{{ form_row(form.favorite) }}
{{ form_row(form.mass) }}
{{ form_row(form.ipn) }}
<div {{ stimulus_controller('elements/ipn_suggestion', {
partId: part.id,
partCategoryId: part.category ? part.category.id : null,
suggestions: ipnSuggestions,
'commonSectionHeader': 'part.edit.tab.advanced.ipn.commonSectionHeader'|trans,
'partIncrementHeader': 'part.edit.tab.advanced.ipn.partIncrementHeader'|trans,
'suggestUrl': url('ipn_suggestions')
}) }}>
{{ form_row(form.ipn) }}
</div>
{{ form_row(form.partUnit) }}

View file

@ -1842,6 +1842,54 @@ Související prvky budou přesunuty nahoru.</target>
<target>Pokročilé</target>
</segment>
</unit>
<unit id="a1k7Blf" name="part.edit.tab.advanced.ipn.commonSectionHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.commonSectionHeader</source>
<target>Návrhy bez přírůstku části</target>
</segment>
</unit>
<unit id="2achA.b" name="part.edit.tab.advanced.ipn.partIncrementHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.partIncrementHeader</source>
<target>Návrhy s číselnými přírůstky částí</target>
</segment>
</unit>
<unit id="dB1ChKc" name="part.edit.tab.advanced.ipn.prefix_empty.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix_empty.direct_category</source>
<target>IPN předpona přímé kategorie je prázdná, zadejte ji v kategorii „%name%“</target>
</segment>
</unit>
<unit id="ec2DiJd" name="part.edit.tab.advanced.ipn.prefix.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category</source>
<target>IPN prefix přímé kategorie</target>
</segment>
</unit>
<unit id="2e.kJb4" name="part.edit.tab.advanced.ipn.prefix.direct_category.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category.increment</source>
<target>IPN prefix přímé kategorie a specifického přírůstku pro část</target>
</segment>
</unit>
<unit id="ba1GCoq" name="part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment</source>
<target>IPN prefixy s hierarchickým pořadím kategorií rodičovských prefixů</target>
</segment>
</unit>
<unit id="d1uVFa3" name="part.edit.tab.advanced.ipn.prefix.hierarchical.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.increment</source>
<target>IPN prefixy s hierarchickým pořadím kategorií rodičovských prefixů a specifickým přírůstkem pro část</target>
</segment>
</unit>
<unit id="a4rPagW" name="part.edit.tab.advanced.ipn.prefix.not_saved">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.not_saved</source>
<target>Nejprve vytvořte součást a přiřaďte ji do kategorie: s dostupnými kategoriemi a jejich vlastními IPN prefixy lze automaticky navrhnout IPN označení pro danou součást</target>
</segment>
</unit>
<unit id="c3S4jwK" name="part.edit.tab.part_lots">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40</note>
@ -6959,6 +7007,12 @@ Pokud jste to provedli nesprávně nebo pokud počítač již není důvěryhodn
<target>Filtr názvů</target>
</segment>
</unit>
<unit id="dsG4xTQ" name="category.edit.part_ipn_prefix">
<segment state="translated">
<source>category.edit.part_ipn_prefix</source>
<target>Předpona součásti IPN</target>
</segment>
</unit>
<unit id="yY_ld.7" name="category.edit.default_description">
<notes>
<note priority="1">obsolete</note>
@ -10296,12 +10350,24 @@ Element 3</target>
<target>např. "/Kondenzátor \d+ nF/i"</target>
</segment>
</unit>
<unit id="OXeGz1A" name="category.edit.part_ipn_prefix.placeholder">
<segment state="translated">
<source>category.edit.part_ipn_prefix.placeholder</source>
<target>např. "B12A"</target>
</segment>
</unit>
<unit id="DL.TreI" name="category.edit.partname_regex.help">
<segment state="translated">
<source>category.edit.partname_regex.help</source>
<target>Regulární výraz kompatibilní s PCRE, kterému musí název dílu odpovídat.</target>
</segment>
</unit>
<unit id="a1bUsfJ" name="category.edit.part_ipn_prefix.help">
<segment state="translated">
<source>category.edit.part_ipn_prefix.help</source>
<target>Předpona navrhovaná při zadávání IPN části.</target>
</segment>
</unit>
<unit id="GzqIwHH" name="entity.select.add_hint">
<segment state="translated">
<source>entity.select.add_hint</source>

View file

@ -1850,6 +1850,54 @@ Underelementer vil blive flyttet opad.</target>
<target>Advanceret</target>
</segment>
</unit>
<unit id="a1k7Blf" name="part.edit.tab.advanced.ipn.commonSectionHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.commonSectionHeader</source>
<target>Forslag uden del-inkrement</target>
</segment>
</unit>
<unit id="2achA.b" name="part.edit.tab.advanced.ipn.partIncrementHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.partIncrementHeader</source>
<target>Forslag med numeriske deleforøgelser</target>
</segment>
</unit>
<unit id="dB1ChKc" name="part.edit.tab.advanced.ipn.prefix_empty.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix_empty.direct_category</source>
<target>IPN-præfikset for den direkte kategori er tomt, angiv det i kategorien "%name%"</target>
</segment>
</unit>
<unit id="ec2DiJd" name="part.edit.tab.advanced.ipn.prefix.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category</source>
<target>IPN-præfiks for direkte kategori</target>
</segment>
</unit>
<unit id="2e.kJb4" name="part.edit.tab.advanced.ipn.prefix.direct_category.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category.increment</source>
<target>IPN-præfiks for den direkte kategori og en delspecifik inkrement</target>
</segment>
</unit>
<unit id="ba1GCoq" name="part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment</source>
<target>IPN-præfikser med hierarkisk rækkefølge af overordnede præfikser</target>
</segment>
</unit>
<unit id="d1uVFa3" name="part.edit.tab.advanced.ipn.prefix.hierarchical.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.increment</source>
<target>IPN-præfikser med hierarkisk rækkefølge af overordnede præfikser og en del-specifik inkrement</target>
</segment>
</unit>
<unit id="a4rPagW" name="part.edit.tab.advanced.ipn.prefix.not_saved">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.not_saved</source>
<target>Opret først en komponent, og tildel den en kategori: med eksisterende kategorier og deres egne IPN-præfikser kan IPN-betegnelsen for komponenten foreslås automatisk</target>
</segment>
</unit>
<unit id="uc9NwcF" name="part.edit.tab.part_lots">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40</note>
@ -6966,6 +7014,12 @@ Bemærk også, at uden to-faktor-godkendelse er din konto ikke længere så godt
<target>Navnefilter</target>
</segment>
</unit>
<unit id="dsG4xTQ" name="category.edit.part_ipn_prefix">
<segment state="translated">
<source>category.edit.part_ipn_prefix</source>
<target>IPN-komponentförstavelse</target>
</segment>
</unit>
<unit id="UH78POJ" name="category.edit.default_description">
<notes>
<note priority="1">obsolete</note>
@ -10322,12 +10376,24 @@ Element 3</target>
<target>f.eks. "/Kondensator \d+ nF/i"</target>
</segment>
</unit>
<unit id="OXeGz1A" name="category.edit.part_ipn_prefix.placeholder">
<segment state="translated">
<source>category.edit.part_ipn_prefix.placeholder</source>
<target>f.eks. "B12A"</target>
</segment>
</unit>
<unit id="QFgP__5" name="category.edit.partname_regex.help">
<segment state="translated">
<source>category.edit.partname_regex.help</source>
<target>Et PCRE-kompatibelt regulært udtryk, som delnavnet skal opfylde.</target>
</segment>
</unit>
<unit id="a1bUsfJ" name="category.edit.part_ipn_prefix.help">
<segment state="translated">
<source>category.edit.part_ipn_prefix.help</source>
<target>Et prefix foreslået, når IPN for en del indtastes.</target>
</segment>
</unit>
<unit id="vr7oZKL" name="entity.select.add_hint">
<segment state="translated">
<source>entity.select.add_hint</source>

View file

@ -1841,6 +1841,54 @@ Subelemente werden beim Löschen nach oben verschoben.</target>
<target>Erweiterte Optionen</target>
</segment>
</unit>
<unit id="a1k7Blf" name="part.edit.tab.advanced.ipn.commonSectionHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.commonSectionHeader</source>
<target>Vorschläge ohne Teil-Inkrement</target>
</segment>
</unit>
<unit id="2achA.b" name="part.edit.tab.advanced.ipn.partIncrementHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.partIncrementHeader</source>
<target>Vorschläge mit numerischen Teil-Inkrement</target>
</segment>
</unit>
<unit id="dB1ChKc" name="part.edit.tab.advanced.ipn.prefix_empty.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix_empty.direct_category</source>
<target>IPN-Präfix der direkten Kategorie leer, geben Sie einen Präfix in Kategorie "%name%" an</target>
</segment>
</unit>
<unit id="ec2DiJd" name="part.edit.tab.advanced.ipn.prefix.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category</source>
<target>IPN-Präfix der direkten Kategorie</target>
</segment>
</unit>
<unit id="2e.kJb4" name="part.edit.tab.advanced.ipn.prefix.direct_category.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category.increment</source>
<target>IPN-Präfix der direkten Kategorie und eines teilspezifischen Inkrements</target>
</segment>
</unit>
<unit id="ba1GCoq" name="part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment</source>
<target>IPN-Präfixe mit hierarchischer Kategorienreihenfolge der Elternpräfixe</target>
</segment>
</unit>
<unit id="d1uVFa3" name="part.edit.tab.advanced.ipn.prefix.hierarchical.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.increment</source>
<target>IPN-Präfixe mit hierarchischer Kategorienreihenfolge der Elternpräfixe und ein teilsspezifisches Inkrement</target>
</segment>
</unit>
<unit id="a4rPagW" name="part.edit.tab.advanced.ipn.prefix.not_saved">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.not_saved</source>
<target>Bitte erstellen Sie zuerst ein Bauteil und weisen Sie dieses einer Kategorie zu: mit vorhandenen Kategorien und derene eigenen IPN-Präfix kann die IPN-Angabe für das jeweilige Teil automatisch vorgeschlagen werden</target>
</segment>
</unit>
<unit id="c3S4jwK" name="part.edit.tab.part_lots">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40</note>
@ -6976,6 +7024,12 @@ Wenn Sie dies fehlerhafterweise gemacht haben oder ein Computer nicht mehr vertr
<target>Namensfilter</target>
</segment>
</unit>
<unit id="dsG4xTQ" name="category.edit.part_ipn_prefix">
<segment state="translated">
<source>category.edit.part_ipn_prefix</source>
<target>Bauteil IPN-Präfix</target>
</segment>
</unit>
<unit id="yY_ld.7" name="category.edit.default_description">
<notes>
<note priority="1">obsolete</note>
@ -10388,12 +10442,24 @@ Element 1 -&gt; Element 1.2</target>
<target>z.B. "/Kondensator \d+ nF/i"</target>
</segment>
</unit>
<unit id="OXeGz1A" name="category.edit.part_ipn_prefix.placeholder">
<segment state="translated">
<source>category.edit.part_ipn_prefix.placeholder</source>
<target>z.B. "B12A"</target>
</segment>
</unit>
<unit id="DL.TreI" name="category.edit.partname_regex.help">
<segment state="translated">
<source>category.edit.partname_regex.help</source>
<target>Ein PCRE-kompatibler regulärer Ausdruck, den der Bauteilename erfüllen muss.</target>
</segment>
</unit>
<unit id="a1bUsfJ" name="category.edit.part_ipn_prefix.help">
<segment state="translated">
<source>category.edit.part_ipn_prefix.help</source>
<target>Ein Präfix, der bei der IPN-Eingabe eines Bauteils vorgeschlagen wird.</target>
</segment>
</unit>
<unit id="GzqIwHH" name="entity.select.add_hint">
<segment state="translated">
<source>entity.select.add_hint</source>

View file

@ -2476,5 +2476,71 @@
<target>%name% (Το συνώνυμό σας: %synonym%)</target>
</segment>
</unit>
<unit id="dsG4xTQ" name="category.edit.part_ipn_prefix">
<segment state="translated">
<source>category.edit.part_ipn_prefix</source>
<target>Πρόθεμα εξαρτήματος IPN</target>
</segment>
</unit>
<unit id="OXeGz1A" name="category.edit.part_ipn_prefix.placeholder">
<segment state="translated">
<source>category.edit.part_ipn_prefix.placeholder</source>
<target>π.χ. "B12A"</target>
</segment>
</unit>
<unit id="a1bUsfJ" name="category.edit.part_ipn_prefix.help">
<segment state="translated">
<source>category.edit.part_ipn_prefix.help</source>
<target>Μια προτεινόμενη πρόθεμα κατά την εισαγωγή του IPN ενός τμήματος.</target>
</segment>
</unit>
<unit id="a1k7Blf" name="part.edit.tab.advanced.ipn.commonSectionHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.commonSectionHeader</source>
<target>Προτάσεις χωρίς αύξηση μέρους</target>
</segment>
</unit>
<unit id="2achA.b" name="part.edit.tab.advanced.ipn.partIncrementHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.partIncrementHeader</source>
<target>Προτάσεις με αριθμητικές αυξήσεις μερών</target>
</segment>
</unit>
<unit id="dB1ChKc" name="part.edit.tab.advanced.ipn.prefix_empty.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix_empty.direct_category</source>
<target>Το IPN πρόθεμα της άμεσης κατηγορίας είναι κενό, καθορίστε το στην κατηγορία "%name%"</target>
</segment>
</unit>
<unit id="ec2DiJd" name="part.edit.tab.advanced.ipn.prefix.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category</source>
<target>Πρόθεμα IPN για την άμεση κατηγορία</target>
</segment>
</unit>
<unit id="2e.kJb4" name="part.edit.tab.advanced.ipn.prefix.direct_category.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category.increment</source>
<target>Πρόθεμα IPN της άμεσης κατηγορίας και μιας ειδικής για μέρος αύξησης</target>
</segment>
</unit>
<unit id="ba1GCoq" name="part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment</source>
<target>Προθέματα IPN με ιεραρχική σειρά κατηγοριών των προθέτων γονέων</target>
</segment>
</unit>
<unit id="d1uVFa3" name="part.edit.tab.advanced.ipn.prefix.hierarchical.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.increment</source>
<target>Προθέματα IPN με ιεραρχική σειρά κατηγοριών των προθέτων γονέων και συγκεκριμένη αύξηση για το μέρος</target>
</segment>
</unit>
<unit id="a4rPagW" name="part.edit.tab.advanced.ipn.prefix.not_saved">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.not_saved</source>
<target>Δημιουργήστε πρώτα ένα εξάρτημα και αντιστοιχίστε το σε μια κατηγορία: με τις υπάρχουσες κατηγορίες και τα δικά τους προθέματα IPN, η ονομασία IPN για το εξάρτημα μπορεί να προταθεί αυτόματα</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -1842,6 +1842,54 @@ Sub elements will be moved upwards.</target>
<target>Advanced</target>
</segment>
</unit>
<unit id="a1k7Blf" name="part.edit.tab.advanced.ipn.commonSectionHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.commonSectionHeader</source>
<target>Suggestions without part increment</target>
</segment>
</unit>
<unit id="2achA.b" name="part.edit.tab.advanced.ipn.partIncrementHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.partIncrementHeader</source>
<target>Suggestions with numeric part increment</target>
</segment>
</unit>
<unit id="dB1ChKc" name="part.edit.tab.advanced.ipn.prefix_empty.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix_empty.direct_category</source>
<target>IPN prefix of direct category empty, specify one in category "%name%"</target>
</segment>
</unit>
<unit id="ec2DiJd" name="part.edit.tab.advanced.ipn.prefix.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category</source>
<target>IPN prefix of direct category</target>
</segment>
</unit>
<unit id="2e.kJb4" name="part.edit.tab.advanced.ipn.prefix.direct_category.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category.increment</source>
<target>IPN prefix of direct category and part-specific increment</target>
</segment>
</unit>
<unit id="ba1GCoq" name="part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment</source>
<target>IPN prefixes with hierarchical category order of parent-prefix(es)</target>
</segment>
</unit>
<unit id="d1uVFa3" name="part.edit.tab.advanced.ipn.prefix.hierarchical.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.increment</source>
<target>IPN prefixes with hierarchical category order of parent-prefix(es) and part-specific increment</target>
</segment>
</unit>
<unit id="a4rPagW" name="part.edit.tab.advanced.ipn.prefix.not_saved">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.not_saved</source>
<target>Please create part at first and assign it to a category: with existing categories and their own IPN prefix, the IPN for the part can be suggested automatically</target>
</segment>
</unit>
<unit id="c3S4jwK" name="part.edit.tab.part_lots">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40</note>
@ -6977,6 +7025,12 @@ If you have done this incorrectly or if a computer is no longer trusted, you can
<target>Name filter</target>
</segment>
</unit>
<unit id="dsG4xTQ" name="category.edit.part_ipn_prefix">
<segment state="translated">
<source>category.edit.part_ipn_prefix</source>
<target>Part IPN Prefix</target>
</segment>
</unit>
<unit id="yY_ld.7" name="category.edit.default_description">
<notes>
<note priority="1">obsolete</note>
@ -10389,12 +10443,24 @@ Element 1 -&gt; Element 1.2</target>
<target>e.g "/Capacitor \d+ nF/i"</target>
</segment>
</unit>
<unit id="OXeGz1A" name="category.edit.part_ipn_prefix.placeholder">
<segment state="translated">
<source>category.edit.part_ipn_prefix.placeholder</source>
<target>e.g "B12A"</target>
</segment>
</unit>
<unit id="DL.TreI" name="category.edit.partname_regex.help">
<segment state="translated">
<source>category.edit.partname_regex.help</source>
<target>A PCRE-compatible regular expression, which a part name have to match.</target>
</segment>
</unit>
<unit id="a1bUsfJ" name="category.edit.part_ipn_prefix.help">
<segment state="translated">
<source>category.edit.part_ipn_prefix.help</source>
<target>A prefix suggested when entering the IPN of a part.</target>
</segment>
</unit>
<unit id="GzqIwHH" name="entity.select.add_hint">
<segment state="translated">
<source>entity.select.add_hint</source>

View file

@ -1842,6 +1842,54 @@ Subelementos serán desplazados hacia arriba.</target>
<target>Avanzado</target>
</segment>
</unit>
<unit id="a1k7Blf" name="part.edit.tab.advanced.ipn.commonSectionHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.commonSectionHeader</source>
<target>Sugerencias sin incremento de parte</target>
</segment>
</unit>
<unit id="2achA.b" name="part.edit.tab.advanced.ipn.partIncrementHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.partIncrementHeader</source>
<target>Sugerencias con incrementos numéricos de partes</target>
</segment>
</unit>
<unit id="dB1ChKc" name="part.edit.tab.advanced.ipn.prefix_empty.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix_empty.direct_category</source>
<target>El prefijo IPN de la categoría directa está vacío, especifíquelo en la categoría "%name%"</target>
</segment>
</unit>
<unit id="ec2DiJd" name="part.edit.tab.advanced.ipn.prefix.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category</source>
<target>Prefijo IPN de la categoría directa</target>
</segment>
</unit>
<unit id="2e.kJb4" name="part.edit.tab.advanced.ipn.prefix.direct_category.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category.increment</source>
<target>Prefijo IPN de la categoría directa y un incremento específico de la pieza</target>
</segment>
</unit>
<unit id="ba1GCoq" name="part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment</source>
<target>Prefijos IPN con orden jerárquico de categorías de prefijos principales</target>
</segment>
</unit>
<unit id="d1uVFa3" name="part.edit.tab.advanced.ipn.prefix.hierarchical.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.increment</source>
<target>Prefijos IPN con orden jerárquico de categorías de prefijos principales y un incremento específico para la parte</target>
</segment>
</unit>
<unit id="a4rPagW" name="part.edit.tab.advanced.ipn.prefix.not_saved">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.not_saved</source>
<target>Primero cree un componente y asígnele una categoría: con las categorías existentes y sus propios prefijos IPN, el identificador IPN para el componente puede ser sugerido automáticamente</target>
</segment>
</unit>
<unit id="c3S4jwK" name="part.edit.tab.part_lots">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40</note>
@ -6976,6 +7024,12 @@ Subelementos serán desplazados hacia arriba.</target>
<target>Filtro de nombre</target>
</segment>
</unit>
<unit id="dsG4xTQ" name="category.edit.part_ipn_prefix">
<segment state="translated">
<source>category.edit.part_ipn_prefix</source>
<target>Prefijo de IPN de la pieza</target>
</segment>
</unit>
<unit id="yY_ld.7" name="category.edit.default_description">
<notes>
<note priority="1">obsolete</note>
@ -10332,12 +10386,24 @@ Elemento 3</target>
<target>p.ej. "/Condensador \d+ nF/i"</target>
</segment>
</unit>
<unit id="OXeGz1A" name="category.edit.part_ipn_prefix.placeholder">
<segment state="translated">
<source>category.edit.part_ipn_prefix.placeholder</source>
<target>p.ej. "B12A"</target>
</segment>
</unit>
<unit id="DL.TreI" name="category.edit.partname_regex.help">
<segment state="translated">
<source>category.edit.partname_regex.help</source>
<target>Una expresión regular compatible con PCRE, la cual debe coincidir con el nombre de un componente.</target>
</segment>
</unit>
<unit id="a1bUsfJ" name="category.edit.part_ipn_prefix.help">
<segment state="translated">
<source>category.edit.part_ipn_prefix.help</source>
<target>Un prefijo sugerido al ingresar el IPN de una parte.</target>
</segment>
</unit>
<unit id="GzqIwHH" name="entity.select.add_hint">
<segment state="translated">
<source>entity.select.add_hint</source>

View file

@ -1820,6 +1820,54 @@ Show/Hide sidebar</target>
<target>Avancé</target>
</segment>
</unit>
<unit id="a1k7Blf" name="part.edit.tab.advanced.ipn.commonSectionHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.commonSectionHeader</source>
<target>Suggestions sans incrément de partie</target>
</segment>
</unit>
<unit id="2achA.b" name="part.edit.tab.advanced.ipn.partIncrementHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.partIncrementHeader</source>
<target>Propositions avec incréments numériques de parties</target>
</segment>
</unit>
<unit id="dB1ChKc" name="part.edit.tab.advanced.ipn.prefix_empty.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix_empty.direct_category</source>
<target>Le préfixe IPN de la catégorie directe est vide, veuillez le spécifier dans la catégorie "%name%"</target>
</segment>
</unit>
<unit id="ec2DiJd" name="part.edit.tab.advanced.ipn.prefix.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category</source>
<target>Préfixe IPN de la catégorie directe</target>
</segment>
</unit>
<unit id="2e.kJb4" name="part.edit.tab.advanced.ipn.prefix.direct_category.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category.increment</source>
<target>Préfixe IPN de la catégorie directe et d'un incrément spécifique à la partie</target>
</segment>
</unit>
<unit id="ba1GCoq" name="part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment</source>
<target>Préfixes IPN avec un ordre hiérarchique des catégories des préfixes parents</target>
</segment>
</unit>
<unit id="d1uVFa3" name="part.edit.tab.advanced.ipn.prefix.hierarchical.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.increment</source>
<target>Préfixes IPN avec un ordre hiérarchique des catégories des préfixes parents et un incrément spécifique à la pièce</target>
</segment>
</unit>
<unit id="a4rPagW" name="part.edit.tab.advanced.ipn.prefix.not_saved">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.not_saved</source>
<target>Créez d'abord une pièce et assignez-la à une catégorie : avec les catégories existantes et leurs propres préfixes IPN, l'identifiant IPN pour la pièce peut être proposé automatiquement</target>
</segment>
</unit>
<unit id="uc9NwcF" name="part.edit.tab.part_lots">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40</note>
@ -6918,6 +6966,12 @@ Si vous avez fait cela de manière incorrecte ou si un ordinateur n'est plus fia
<target>Filtre de nom</target>
</segment>
</unit>
<unit id="dsG4xTQ" name="category.edit.part_ipn_prefix">
<segment state="translated">
<source>category.edit.part_ipn_prefix</source>
<target>Préfixe de pièce IPN</target>
</segment>
</unit>
<unit id="UH78POJ" name="category.edit.default_description">
<notes>
<note priority="1">obsolete</note>
@ -10050,5 +10104,17 @@ exemple de ville</target>
<target>%name% (Votre synonyme : %synonym%)</target>
</segment>
</unit>
<unit id="OXeGz1A" name="category.edit.part_ipn_prefix.placeholder">
<segment state="translated">
<source>category.edit.part_ipn_prefix.placeholder</source>
<target>par ex. "B12A"</target>
</segment>
</unit>
<unit id="a1bUsfJ" name="category.edit.part_ipn_prefix.help">
<segment state="translated">
<source>category.edit.part_ipn_prefix.help</source>
<target>Un préfixe suggéré lors de la saisie de l'IPN d'une pièce.</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -1842,6 +1842,54 @@ I sub elementi saranno spostati verso l'alto.</target>
<target>Avanzate</target>
</segment>
</unit>
<unit id="a1k7Blf" name="part.edit.tab.advanced.ipn.commonSectionHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.commonSectionHeader</source>
<target>Suggerimenti senza incremento di parte</target>
</segment>
</unit>
<unit id="2achA.b" name="part.edit.tab.advanced.ipn.partIncrementHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.partIncrementHeader</source>
<target>Suggerimenti con incrementi numerici delle parti</target>
</segment>
</unit>
<unit id="dB1ChKc" name="part.edit.tab.advanced.ipn.prefix_empty.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix_empty.direct_category</source>
<target>Il prefisso IPN della categoria diretta è vuoto, specificarlo nella categoria "%name%"</target>
</segment>
</unit>
<unit id="ec2DiJd" name="part.edit.tab.advanced.ipn.prefix.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category</source>
<target>Prefisso IPN della categoria diretta</target>
</segment>
</unit>
<unit id="2e.kJb4" name="part.edit.tab.advanced.ipn.prefix.direct_category.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category.increment</source>
<target>Prefisso IPN della categoria diretta e di un incremento specifico della parte</target>
</segment>
</unit>
<unit id="ba1GCoq" name="part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment</source>
<target>Prefissi IPN con ordine gerarchico delle categorie dei prefissi padre</target>
</segment>
</unit>
<unit id="d1uVFa3" name="part.edit.tab.advanced.ipn.prefix.hierarchical.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.increment</source>
<target>Prefissi IPN con ordine gerarchico delle categorie dei prefissi padre e un incremento specifico per il pezzo</target>
</segment>
</unit>
<unit id="a4rPagW" name="part.edit.tab.advanced.ipn.prefix.not_saved">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.not_saved</source>
<target>Crea prima un componente e assegnagli una categoria: con le categorie esistenti e i loro propri prefissi IPN, l'identificativo IPN per il componente può essere suggerito automaticamente</target>
</segment>
</unit>
<unit id="c3S4jwK" name="part.edit.tab.part_lots">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40</note>
@ -6978,6 +7026,12 @@ Se è stato fatto in modo errato o se un computer non è più attendibile, puoi
<target>Filtro nome</target>
</segment>
</unit>
<unit id="dsG4xTQ" name="category.edit.part_ipn_prefix">
<segment state="translated">
<source>category.edit.part_ipn_prefix</source>
<target>Prefisso parte IPN</target>
</segment>
</unit>
<unit id="yY_ld.7" name="category.edit.default_description">
<notes>
<note priority="1">obsolete</note>
@ -10334,12 +10388,24 @@ Element 3</target>
<target>es. "/Condensatore \d+ nF/i"</target>
</segment>
</unit>
<unit id="OXeGz1A" name="category.edit.part_ipn_prefix.placeholder">
<segment state="translated">
<source>category.edit.part_ipn_prefix.placeholder</source>
<target>es. "B12A"</target>
</segment>
</unit>
<unit id="DL.TreI" name="category.edit.partname_regex.help">
<segment state="translated">
<source>category.edit.partname_regex.help</source>
<target>Un'espressione regolare compatibile con PCRE che il nome del componente deve soddisfare.</target>
</segment>
</unit>
<unit id="a1bUsfJ" name="category.edit.part_ipn_prefix.help">
<segment state="translated">
<source>category.edit.part_ipn_prefix.help</source>
<target>Un prefisso suggerito durante l'inserimento dell'IPN di una parte.</target>
</segment>
</unit>
<unit id="GzqIwHH" name="entity.select.add_hint">
<segment state="translated">
<source>entity.select.add_hint</source>

View file

@ -1820,6 +1820,54 @@
<target>詳細</target>
</segment>
</unit>
<unit id="a1k7Blf" name="part.edit.tab.advanced.ipn.commonSectionHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.commonSectionHeader</source>
<target>部品の増加なしの提案。</target>
</segment>
</unit>
<unit id="2achA.b" name="part.edit.tab.advanced.ipn.partIncrementHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.partIncrementHeader</source>
<target>パーツの数値インクリメントを含む提案</target>
</segment>
</unit>
<unit id="dB1ChKc" name="part.edit.tab.advanced.ipn.prefix_empty.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix_empty.direct_category</source>
<target>直接カテゴリの IPN プレフィックスが空です。「%name%」カテゴリで指定してください</target>
</segment>
</unit>
<unit id="ec2DiJd" name="part.edit.tab.advanced.ipn.prefix.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category</source>
<target>直接カテゴリのIPNプレフィックス</target>
</segment>
</unit>
<unit id="2e.kJb4" name="part.edit.tab.advanced.ipn.prefix.direct_category.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category.increment</source>
<target>直接カテゴリのIPNプレフィックスと部品特有のインクリメント</target>
</segment>
</unit>
<unit id="ba1GCoq" name="part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment</source>
<target>親プレフィックスの階層カテゴリ順のIPNプレフィックス</target>
</segment>
</unit>
<unit id="d1uVFa3" name="part.edit.tab.advanced.ipn.prefix.hierarchical.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.increment</source>
<target>親プレフィックスの階層カテゴリ順とパーツ固有の増分のIPNプレフィックス</target>
</segment>
</unit>
<unit id="a4rPagW" name="part.edit.tab.advanced.ipn.prefix.not_saved">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.not_saved</source>
<target>まずはコンポーネントを作成し、それをカテゴリに割り当ててください既存のカテゴリとそれぞれのIPNプレフィックスを基に、コンポーネントのIPNを自動的に提案できます</target>
</segment>
</unit>
<unit id="uc9NwcF" name="part.edit.tab.part_lots">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40</note>
@ -6919,6 +6967,12 @@
<target>名前のフィルター</target>
</segment>
</unit>
<unit id="dsG4xTQ" name="category.edit.part_ipn_prefix">
<segment state="translated">
<source>category.edit.part_ipn_prefix</source>
<target>部品 IPN 接頭辞</target>
</segment>
</unit>
<unit id="UH78POJ" name="category.edit.default_description">
<notes>
<note priority="1">obsolete</note>
@ -9763,5 +9817,17 @@ Exampletown</target>
<target>%name% (あなたの同義語: %synonym%)</target>
</segment>
</unit>
<unit id="OXeGz1A" name="category.edit.part_ipn_prefix.placeholder">
<segment state="translated">
<source>category.edit.part_ipn_prefix.placeholder</source>
<target>例: "B12A"</target>
</segment>
</unit>
<unit id="a1bUsfJ" name="category.edit.part_ipn_prefix.help">
<segment state="translated">
<source>category.edit.part_ipn_prefix.help</source>
<target>部品のIPN入力時に提案される接頭辞。</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -1701,5 +1701,71 @@
<target>%name% (Uw synoniem: %synonym%)</target>
</segment>
</unit>
<unit id="dsG4xTQ" name="category.edit.part_ipn_prefix">
<segment state="translated">
<source>category.edit.part_ipn_prefix</source>
<target>IPN-voorvoegsel van onderdeel</target>
</segment>
</unit>
<unit id="OXeGz1A" name="category.edit.part_ipn_prefix.placeholder">
<segment state="translated">
<source>category.edit.part_ipn_prefix.placeholder</source>
<target>bijv. "B12A"</target>
</segment>
</unit>
<unit id="a1bUsfJ" name="category.edit.part_ipn_prefix.help">
<segment state="translated">
<source>category.edit.part_ipn_prefix.help</source>
<target>Een voorgesteld voorvoegsel bij het invoeren van de IPN van een onderdeel.</target>
</segment>
</unit>
<unit id="a1k7Blf" name="part.edit.tab.advanced.ipn.commonSectionHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.commonSectionHeader</source>
<target>Suggesties zonder toename van onderdelen</target>
</segment>
</unit>
<unit id="2achA.b" name="part.edit.tab.advanced.ipn.partIncrementHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.partIncrementHeader</source>
<target>Suggesties met numerieke verhogingen van onderdelen</target>
</segment>
</unit>
<unit id="dB1ChKc" name="part.edit.tab.advanced.ipn.prefix_empty.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix_empty.direct_category</source>
<target>Het IPN-prefix van de directe categorie is leeg, geef het op in de categorie "%name%"</target>
</segment>
</unit>
<unit id="ec2DiJd" name="part.edit.tab.advanced.ipn.prefix.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category</source>
<target>IPN-prefix van de directe categorie</target>
</segment>
</unit>
<unit id="2e.kJb4" name="part.edit.tab.advanced.ipn.prefix.direct_category.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category.increment</source>
<target>IPN-voorvoegsel van de directe categorie en een onderdeel specifiek increment</target>
</segment>
</unit>
<unit id="ba1GCoq" name="part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment</source>
<target>IPN-prefixen met een hiërarchische volgorde van hoofdcategorieën</target>
</segment>
</unit>
<unit id="d1uVFa3" name="part.edit.tab.advanced.ipn.prefix.hierarchical.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.increment</source>
<target>IPN-prefixen met een hiërarchische volgorde van hoofdcategorieën en een specifieke toename voor het onderdeel</target>
</segment>
</unit>
<unit id="a4rPagW" name="part.edit.tab.advanced.ipn.prefix.not_saved">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.not_saved</source>
<target>Maak eerst een component en wijs het toe aan een categorie: met de bestaande categorieën en hun eigen IPN-prefixen kan de IPN voor het component automatisch worden voorgesteld</target>
</segment>
</unit>
</file>
</xliff>

View file

@ -1847,6 +1847,54 @@ Po usunięciu pod elementy zostaną przeniesione na górę.</target>
<target>Zaawansowane</target>
</segment>
</unit>
<unit id="a1k7Blf" name="part.edit.tab.advanced.ipn.commonSectionHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.commonSectionHeader</source>
<target>Sugestie bez zwiększenia części</target>
</segment>
</unit>
<unit id="2achA.b" name="part.edit.tab.advanced.ipn.partIncrementHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.partIncrementHeader</source>
<target>Propozycje z numerycznymi przyrostami części</target>
</segment>
</unit>
<unit id="dB1ChKc" name="part.edit.tab.advanced.ipn.prefix_empty.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix_empty.direct_category</source>
<target>Prefiks IPN kategorii bezpośredniej jest pusty, podaj go w kategorii "%name%".</target>
</segment>
</unit>
<unit id="ec2DiJd" name="part.edit.tab.advanced.ipn.prefix.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category</source>
<target>Prefiks IPN kategorii bezpośredniej</target>
</segment>
</unit>
<unit id="2e.kJb4" name="part.edit.tab.advanced.ipn.prefix.direct_category.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category.increment</source>
<target>Prefiks IPN bezpośredniej kategorii i specyficzny dla części przyrost</target>
</segment>
</unit>
<unit id="ba1GCoq" name="part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment</source>
<target>Prefiksy IPN z hierarchiczną kolejnością kategorii prefiksów nadrzędnych</target>
</segment>
</unit>
<unit id="d1uVFa3" name="part.edit.tab.advanced.ipn.prefix.hierarchical.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.increment</source>
<target>Prefiksy IPN z hierarchiczną kolejnością kategorii prefiksów nadrzędnych i specyficznym przyrostem dla części</target>
</segment>
</unit>
<unit id="a4rPagW" name="part.edit.tab.advanced.ipn.prefix.not_saved">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.not_saved</source>
<target>Najpierw utwórz komponent i przypisz go do kategorii: dzięki istniejącym kategoriom i ich własnym prefiksom IPN identyfikator IPN dla komponentu może być proponowany automatycznie</target>
</segment>
</unit>
<unit id="c3S4jwK" name="part.edit.tab.part_lots">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40</note>
@ -6981,6 +7029,12 @@ Jeśli zrobiłeś to niepoprawnie lub komputer nie jest już godny zaufania, mo
<target>Filtr nazwy</target>
</segment>
</unit>
<unit id="dsG4xTQ" name="category.edit.part_ipn_prefix">
<segment state="translated">
<source>category.edit.part_ipn_prefix</source>
<target>Prefiks IPN części</target>
</segment>
</unit>
<unit id="yY_ld.7" name="category.edit.default_description">
<notes>
<note priority="1">obsolete</note>
@ -10337,12 +10391,24 @@ Element 3</target>
<target>np. "/Kondensator \d+ nF/i"</target>
</segment>
</unit>
<unit id="OXeGz1A" name="category.edit.part_ipn_prefix.placeholder">
<segment state="translated">
<source>category.edit.part_ipn_prefix.placeholder</source>
<target>np. "B12A"</target>
</segment>
</unit>
<unit id="DL.TreI" name="category.edit.partname_regex.help">
<segment state="translated">
<source>category.edit.partname_regex.help</source>
<target>Wyrażenie regularne zgodne z PCRE, do którego musi pasować nazwa komponentu.</target>
</segment>
</unit>
<unit id="a1bUsfJ" name="category.edit.part_ipn_prefix.help">
<segment state="translated">
<source>category.edit.part_ipn_prefix.help</source>
<target>Een voorgesteld voorvoegsel bij het invoeren van de IPN van een onderdeel.</target>
</segment>
</unit>
<unit id="GzqIwHH" name="entity.select.add_hint">
<segment state="translated">
<source>entity.select.add_hint</source>

View file

@ -1850,6 +1850,54 @@
<target>Расширенные</target>
</segment>
</unit>
<unit id="a1k7Blf" name="part.edit.tab.advanced.ipn.commonSectionHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.commonSectionHeader</source>
<target>Предложения без увеличения частей.</target>
</segment>
</unit>
<unit id="2achA.b" name="part.edit.tab.advanced.ipn.partIncrementHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.partIncrementHeader</source>
<target>Предложения с числовыми приращениями частей</target>
</segment>
</unit>
<unit id="dB1ChKc" name="part.edit.tab.advanced.ipn.prefix_empty.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix_empty.direct_category</source>
<target>Префикс IPN для прямой категории пуст, укажите его в категории «%name%».</target>
</segment>
</unit>
<unit id="ec2DiJd" name="part.edit.tab.advanced.ipn.prefix.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category</source>
<target>Префикс IPN для прямой категории</target>
</segment>
</unit>
<unit id="2e.kJb4" name="part.edit.tab.advanced.ipn.prefix.direct_category.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category.increment</source>
<target>Префикс IPN прямой категории и специфическое для части приращение</target>
</segment>
</unit>
<unit id="ba1GCoq" name="part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment</source>
<target>IPN-префиксы с иерархическим порядком категорий родительских префиксов</target>
</segment>
</unit>
<unit id="d1uVFa3" name="part.edit.tab.advanced.ipn.prefix.hierarchical.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.increment</source>
<target>IPN-префиксы с иерархическим порядком категорий родительских префиксов и специфическим увеличением для компонента</target>
</segment>
</unit>
<unit id="a4rPagW" name="part.edit.tab.advanced.ipn.prefix.not_saved">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.not_saved</source>
<target>Сначала создайте компонент и назначьте ему категорию: на основе существующих категорий и их собственных IPN-префиксов идентификатор IPN для компонента может быть предложен автоматически</target>
</segment>
</unit>
<unit id="c3S4jwK" name="part.edit.tab.part_lots">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40</note>
@ -6988,6 +7036,12 @@
<target>Фильтр по имени</target>
</segment>
</unit>
<unit id="dsG4xTQ" name="category.edit.part_ipn_prefix">
<segment state="translated">
<source>category.edit.part_ipn_prefix</source>
<target>Префикс IPN детали</target>
</segment>
</unit>
<unit id="yY_ld.7" name="category.edit.default_description">
<notes>
<note priority="1">obsolete</note>
@ -10341,12 +10395,24 @@
<target>e.g "/Конденсатор \d+ nF/i"</target>
</segment>
</unit>
<unit id="OXeGz1A" name="category.edit.part_ipn_prefix.placeholder">
<segment state="translated">
<source>category.edit.part_ipn_prefix.placeholder</source>
<target>e.g "B12A"</target>
</segment>
</unit>
<unit id="DL.TreI" name="category.edit.partname_regex.help">
<segment state="translated">
<source>category.edit.partname_regex.help</source>
<target>PCRE-совместимое регулярное выражение которому должно соответствовать имя компонента.</target>
</segment>
</unit>
<unit id="a1bUsfJ" name="category.edit.part_ipn_prefix.help">
<segment state="translated">
<source>category.edit.part_ipn_prefix.help</source>
<target>Предлагаемый префикс при вводе IPN детали.</target>
</segment>
</unit>
<unit id="GzqIwHH" name="entity.select.add_hint">
<segment state="translated">
<source>entity.select.add_hint</source>

View file

@ -1850,6 +1850,54 @@
<target>高级</target>
</segment>
</unit>
<unit id="a1k7Blf" name="part.edit.tab.advanced.ipn.commonSectionHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.commonSectionHeader</source>
<target>Sugestie bez zwiększenia części</target>
</segment>
</unit>
<unit id="2achA.b" name="part.edit.tab.advanced.ipn.partIncrementHeader">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.partIncrementHeader</source>
<target>包含部件数值增量的建议</target>
</segment>
</unit>
<unit id="dB1ChKc" name="part.edit.tab.advanced.ipn.prefix_empty.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix_empty.direct_category</source>
<target>直接类别的 IPN 前缀为空,请在类别“%name%”中指定。</target>
</segment>
</unit>
<unit id="ec2DiJd" name="part.edit.tab.advanced.ipn.prefix.direct_category">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category</source>
<target>直接类别的IPN前缀</target>
</segment>
</unit>
<unit id="2e.kJb4" name="part.edit.tab.advanced.ipn.prefix.direct_category.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.direct_category.increment</source>
<target>直接类别的IPN前缀和部件特定的增量</target>
</segment>
</unit>
<unit id="ba1GCoq" name="part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment</source>
<target>具有父级前缀层级类别顺序的IPN前缀</target>
</segment>
</unit>
<unit id="d1uVFa3" name="part.edit.tab.advanced.ipn.prefix.hierarchical.increment">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.hierarchical.increment</source>
<target>具有父级前缀层级类别顺序和组件特定增量的IPN前缀</target>
</segment>
</unit>
<unit id="a4rPagW" name="part.edit.tab.advanced.ipn.prefix.not_saved">
<segment state="translated">
<source>part.edit.tab.advanced.ipn.prefix.not_saved</source>
<target>请先创建组件并将其分配到类别基于现有类别及其专属的IPN前缀可以自动建议组件的IPN</target>
</segment>
</unit>
<unit id="uc9NwcF" name="part.edit.tab.part_lots">
<notes>
<note category="file-source" priority="1">Part-DB1\templates\Parts\edit\edit_part_info.html.twig:40</note>
@ -6985,6 +7033,12 @@
<target>名称过滤器</target>
</segment>
</unit>
<unit id="dsG4xTQ" name="category.edit.part_ipn_prefix">
<segment state="translated">
<source>category.edit.part_ipn_prefix</source>
<target>部件 IPN 前缀</target>
</segment>
</unit>
<unit id="UH78POJ" name="category.edit.default_description">
<notes>
<note priority="1">obsolete</note>
@ -10340,12 +10394,24 @@ Element 3</target>
<target> </target>
</segment>
</unit>
<unit id="OXeGz1A" name="category.edit.part_ipn_prefix.placeholder">
<segment state="translated">
<source>category.edit.part_ipn_prefix.placeholder</source>
<target>例如:"B12A"</target>
</segment>
</unit>
<unit id="QFgP__5" name="category.edit.partname_regex.help">
<segment state="translated">
<source>category.edit.partname_regex.help</source>
<target>与PCRE兼容的正则表达式部分名称必须匹配。</target>
</segment>
</unit>
<unit id="a1bUsfJ" name="category.edit.part_ipn_prefix.help">
<segment state="translated">
<source>category.edit.part_ipn_prefix.help</source>
<target>输入零件IPN时建议的前缀。</target>
</segment>
</unit>
<unit id="vr7oZKL" name="entity.select.add_hint">
<segment state="translated">
<source>entity.select.add_hint</source>