IPN-Vorschlagslogik um Konfiguration erweitert

This commit is contained in:
Marcel Diegelmann 2025-09-25 10:26:34 +02:00
parent 3100c83246
commit a2f53290f4
14 changed files with 163 additions and 51 deletions

5
.env
View file

@ -50,8 +50,6 @@ EMAIL_SENDER_EMAIL=noreply@partdb.changeme
EMAIL_SENDER_NAME="Part-DB Mailer" EMAIL_SENDER_NAME="Part-DB Mailer"
# Set this to 1 to allow reset of a password per email # Set this to 1 to allow reset of a password per email
ALLOW_EMAIL_PW_RESET=0 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 # Error pages settings
@ -118,9 +116,6 @@ REDIRECT_TO_HTTPS=0
# 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) # 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 DISABLE_YEAR2038_BUG_CHECK=0
# Define the number of digits used for the incremental numbering of parts in the IPN (Internal Part Number) autocomplete system.
AUTOCOMPLETE_PART_DIGITS=4
# Set the trusted IPs here, when using an reverse proxy # Set the trusted IPs here, when using an reverse proxy
#TRUSTED_PROXIES=127.0.0.0/8,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 #TRUSTED_PROXIES=127.0.0.0/8,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
#TRUSTED_HOSTS='^(localhost|example\.com)$' #TRUSTED_HOSTS='^(localhost|example\.com)$'

View file

@ -9,7 +9,6 @@ parameters:
# This is used as workaround for places where we can not access the settings directly (like the 2FA application names) # 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.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.locale_menu: ['en', 'de', 'it', 'fr', 'ru', 'ja', 'cs', 'da', 'zh', 'pl'] # The languages that are shown in user drop down menu
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 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
@ -20,7 +19,6 @@ parameters:
###################################################################################################################### ######################################################################################################################
partdb.gdpr_compliance: true # If this option is activated, IP addresses are anonymized to be GDPR compliant 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.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 # Mail settings

View file

@ -231,28 +231,16 @@ services:
tags: tags:
- { name: 'doctrine.fixtures.purger_factory', alias: 'reset_autoincrement_purger' } - { 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: App\Repository\PartRepository:
arguments: arguments:
$translator: '@translator' $translator: '@translator'
tags: ['doctrine.repository_service'] tags: ['doctrine.repository_service']
App\EventSubscriber\UserSystem\PartUniqueIpnSubscriber: App\EventSubscriber\UserSystem\PartUniqueIpnSubscriber:
arguments:
$enforceUniqueIpn: '%partdb.users.enforce_unique_ipn%'
tags: tags:
- { name: doctrine.event_subscriber } - { name: doctrine.event_subscriber }
App\Validator\Constraints\UniquePartIpnValidator: App\Validator\Constraints\UniquePartIpnValidator:
arguments:
$enforceUniqueIpn: '%partdb.users.enforce_unique_ipn%'
tags: [ 'validator.constraint_validator' ] 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. # 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.

View file

@ -116,7 +116,9 @@ 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. 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 * `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 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_ENABLE_UNIQUE_CHECK`: 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.
* `IPN_SUGGEST_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. 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 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. unique increments for parts within a category hierarchy, ensuring consistency and uniqueness in IPN generation.
@ -132,8 +134,6 @@ bundled with Part-DB. Set `DATABASE_MYSQL_SSL_VERIFY_CERT` if you want to accept
sent from. sent from.
* `ALLOW_EMAIL_PW_RESET`: Set this value to true, if you want to allow users to reset their password via an email * `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. 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 ### Table related settings

View file

@ -47,6 +47,7 @@ use App\Services\Parts\PartLotWithdrawAddHelper;
use App\Services\Parts\PricedetailHelper; use App\Services\Parts\PricedetailHelper;
use App\Services\ProjectSystem\ProjectBuildPartHelper; use App\Services\ProjectSystem\ProjectBuildPartHelper;
use App\Settings\BehaviorSettings\PartInfoSettings; use App\Settings\BehaviorSettings\PartInfoSettings;
use App\Settings\MiscSettings\IpnSuggestSettings;
use DateTime; use DateTime;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Exception; use Exception;
@ -74,7 +75,7 @@ final class PartController extends AbstractController
private readonly EntityManagerInterface $em, private readonly EntityManagerInterface $em,
private readonly EventCommentHelper $commentHelper, private readonly EventCommentHelper $commentHelper,
private readonly PartInfoSettings $partInfoSettings, private readonly PartInfoSettings $partInfoSettings,
private readonly int $autocompletePartDigits, private readonly IpnSuggestSettings $ipnSuggestSettings,
) { ) {
} }
@ -451,7 +452,7 @@ final class PartController extends AbstractController
$template, $template,
[ [
'part' => $new_part, 'part' => $new_part,
'ipnSuggestions' => $partRepository->autoCompleteIpn($data, base64_encode($data->getDescription()), $this->autocompletePartDigits), 'ipnSuggestions' => $partRepository->autoCompleteIpn($data, base64_encode($data->getDescription()), $this->ipnSuggestSettings->suggestPartDigits),
'form' => $form, 'form' => $form,
'merge_old_name' => $merge_infos['tname_before'] ?? null, 'merge_old_name' => $merge_infos['tname_before'] ?? null,
'merge_other' => $merge_infos['other_part'] ?? null, 'merge_other' => $merge_infos['other_part'] ?? null,

View file

@ -23,6 +23,7 @@ declare(strict_types=1);
namespace App\Controller; namespace App\Controller;
use App\Entity\Parameters\AbstractParameter; use App\Entity\Parameters\AbstractParameter;
use App\Settings\MiscSettings\IpnSuggestSettings;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use App\Entity\Attachments\Attachment; use App\Entity\Attachments\Attachment;
use App\Entity\Parts\Category; use App\Entity\Parts\Category;
@ -63,7 +64,7 @@ class TypeaheadController extends AbstractController
public function __construct( public function __construct(
protected AttachmentURLGenerator $urlGenerator, protected AttachmentURLGenerator $urlGenerator,
protected Packages $assets, protected Packages $assets,
protected int $autocompletePartDigits protected IpnSuggestSettings $ipnSuggestSettings,
) { ) {
} }
@ -207,7 +208,7 @@ class TypeaheadController extends AbstractController
$clonedPart->setCategory($category); $clonedPart->setCategory($category);
$partRepository = $entityManager->getRepository(Part::class); $partRepository = $entityManager->getRepository(Part::class);
$ipnSuggestions = $partRepository->autoCompleteIpn($clonedPart, $description, $this->autocompletePartDigits); $ipnSuggestions = $partRepository->autoCompleteIpn($clonedPart, $description, $this->ipnSuggestSettings->suggestPartDigits);
return new JsonResponse($ipnSuggestions); return new JsonResponse($ipnSuggestions);
} }

View file

@ -3,6 +3,7 @@
namespace App\EventSubscriber\UserSystem; namespace App\EventSubscriber\UserSystem;
use App\Entity\Parts\Part; use App\Entity\Parts\Part;
use App\Settings\MiscSettings\IpnSuggestSettings;
use Doctrine\Common\EventSubscriber; use Doctrine\Common\EventSubscriber;
use Doctrine\Persistence\Event\LifecycleEventArgs; use Doctrine\Persistence\Event\LifecycleEventArgs;
use Doctrine\ORM\Events; use Doctrine\ORM\Events;
@ -12,7 +13,7 @@ class PartUniqueIpnSubscriber implements EventSubscriber
{ {
public function __construct( public function __construct(
private EntityManagerInterface $entityManager, private EntityManagerInterface $entityManager,
private readonly bool $enforceUniqueIpn = false private IpnSuggestSettings $ipnSuggestSettings
) { ) {
} }
@ -53,7 +54,7 @@ class PartUniqueIpnSubscriber implements EventSubscriber
->findOneBy(['ipn' => $part->getIpn()]); ->findOneBy(['ipn' => $part->getIpn()]);
if ($existingPart && $existingPart->getId() !== $part->getId()) { if ($existingPart && $existingPart->getId() !== $part->getId()) {
if ($this->enforceUniqueIpn) { if ($this->ipnSuggestSettings->enableUniqueCheck) {
return; return;
} }
@ -70,4 +71,4 @@ class PartUniqueIpnSubscriber implements EventSubscriber
$part->setIpn($originalIpn . "_$increment"); $part->setIpn($originalIpn . "_$increment");
} }
} }
} }

View file

@ -118,13 +118,13 @@ class PartRepository extends NamedDBElementRepository
* *
* @param Part $part The part for which autocomplete suggestions are generated. * @param Part $part The part for which autocomplete suggestions are generated.
* @param string $description Base64-encoded description to assist in generating suggestions. * @param string $description Base64-encoded description to assist in generating suggestions.
* @param int $autocompletePartDigits The number of digits used in autocomplete increments. * @param int $suggestPartDigits The number of digits used in autocomplete increments.
* *
* @return array An associative array containing the following keys: * @return array An associative array containing the following keys:
* - 'commonPrefixes': List of common prefixes found for the part. * - 'commonPrefixes': List of common prefixes found for the part.
* - 'prefixesPartIncrement': Increments for the generated prefixes, including hierarchical prefixes. * - 'prefixesPartIncrement': Increments for the generated prefixes, including hierarchical prefixes.
*/ */
public function autoCompleteIpn(Part $part, string $description, int $autocompletePartDigits): array public function autoCompleteIpn(Part $part, string $description, int $suggestPartDigits): array
{ {
$category = $part->getCategory(); $category = $part->getCategory();
$ipnSuggestions = ['commonPrefixes' => [], 'prefixesPartIncrement' => []]; $ipnSuggestions = ['commonPrefixes' => [], 'prefixesPartIncrement' => []];
@ -140,7 +140,7 @@ class PartRepository extends NamedDBElementRepository
$directIpnPrefixEmpty = $category->getPartIpnPrefix() === ''; $directIpnPrefixEmpty = $category->getPartIpnPrefix() === '';
$currentPath = $currentPath === '' ? 'n.a.' : $currentPath; $currentPath = $currentPath === '' ? 'n.a.' : $currentPath;
$increment = $this->generateNextPossiblePartIncrement($currentPath, $part, $autocompletePartDigits); $increment = $this->generateNextPossiblePartIncrement($currentPath, $part, $suggestPartDigits);
$ipnSuggestions['commonPrefixes'][] = [ $ipnSuggestions['commonPrefixes'][] = [
'title' => $currentPath . '-', 'title' => $currentPath . '-',
@ -181,7 +181,7 @@ class PartRepository extends NamedDBElementRepository
'description' => $this->translator->trans('part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment') 'description' => $this->translator->trans('part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment')
]; ];
$increment = $this->generateNextPossiblePartIncrement($currentPath, $part, $autocompletePartDigits); $increment = $this->generateNextPossiblePartIncrement($currentPath, $part, $suggestPartDigits);
$ipnSuggestions['prefixesPartIncrement'][] = [ $ipnSuggestions['prefixesPartIncrement'][] = [
'title' => $currentPath . '-' . $increment, 'title' => $currentPath . '-' . $increment,
@ -249,18 +249,18 @@ class PartRepository extends NamedDBElementRepository
* *
* @param string $currentPath The base path or prefix for the part's identifier. * @param string $currentPath The base path or prefix for the part's identifier.
* @param Part $currentPart The part entity for which the increment is being generated. * @param Part $currentPart The part entity for which the increment is being generated.
* @param int $autocompletePartDigits The number of digits reserved for the increment. * @param int $suggestPartDigits The number of digits reserved for the increment.
* *
* @return string|null The next possible increment as a zero-padded string, or null if it cannot be generated. * @return string|null The next possible increment as a zero-padded string, or null if it cannot be generated.
* *
* @throws NonUniqueResultException If the query returns non-unique results. * @throws NonUniqueResultException If the query returns non-unique results.
* @throws NoResultException If the query fails to return a result. * @throws NoResultException If the query fails to return a result.
*/ */
private function generateNextPossiblePartIncrement(string $currentPath, Part $currentPart, int $autocompletePartDigits): ?string private function generateNextPossiblePartIncrement(string $currentPath, Part $currentPart, int $suggestPartDigits): ?string
{ {
$qb = $this->createQueryBuilder('part'); $qb = $this->createQueryBuilder('part');
$expectedLength = strlen($currentPath) + 1 + $autocompletePartDigits; // Path + '-' + $autocompletePartDigits digits $expectedLength = strlen($currentPath) + 1 + $suggestPartDigits; // Path + '-' + $suggestPartDigits digits
// Fetch all parts in the given category, sorted by their ID in ascending order // Fetch all parts in the given category, sorted by their ID in ascending order
$qb->select('part') $qb->select('part')
@ -281,14 +281,14 @@ class PartRepository extends NamedDBElementRepository
if ($part->getId() === $currentPart->getId()) { if ($part->getId() === $currentPart->getId()) {
// Extract and return the current part's increment directly // Extract and return the current part's increment directly
$incrementPart = substr($part->getIpn(), -$autocompletePartDigits); $incrementPart = substr($part->getIpn(), -$suggestPartDigits);
if (is_numeric($incrementPart)) { if (is_numeric($incrementPart)) {
return str_pad((string) $incrementPart, $autocompletePartDigits, '0', STR_PAD_LEFT); return str_pad((string) $incrementPart, $suggestPartDigits, '0', STR_PAD_LEFT);
} }
} }
// Extract last $autocompletePartDigits digits for possible available part increment // Extract last $autocompletePartDigits digits for possible available part increment
$incrementPart = substr($part->getIpn(), -$autocompletePartDigits); $incrementPart = substr($part->getIpn(), -$suggestPartDigits);
if (is_numeric($incrementPart)) { if (is_numeric($incrementPart)) {
$usedIncrements[] = (int) $incrementPart; $usedIncrements[] = (int) $incrementPart;
} }
@ -302,7 +302,7 @@ class PartRepository extends NamedDBElementRepository
$nextIncrement++; $nextIncrement++;
} }
return str_pad((string) $nextIncrement, $autocompletePartDigits, '0', STR_PAD_LEFT); return str_pad((string) $nextIncrement, $suggestPartDigits, '0', STR_PAD_LEFT);
} }
/** /**

View file

@ -0,0 +1,54 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 Jan Böhmer (https://github.com/jbtronics)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Settings\MiscSettings;
use App\Settings\SettingsIcon;
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
use Symfony\Component\Translation\TranslatableMessage as TM;
use Symfony\Component\Validator\Constraints as Assert;
#[Settings(label: new TM("settings.misc.ipn_suggest"))]
#[SettingsIcon("fa-list")]
class IpnSuggestSettings
{
use SettingsTrait;
#[SettingsParameter(
label: new TM("settings.misc.ipn_suggest.enableUniqueCheck"),
envVar: "bool:IPN_ENABLE_UNIQUE_CHECK", envVarMode: EnvVarMode::OVERWRITE,
)]
public bool $enableUniqueCheck = true;
#[SettingsParameter(label: new TM("settings.misc.ipn_suggest.suggestPartDigits"),
description: new TM("settings.misc.ipn_suggest.suggestPartDigits.help"),
formOptions: ['attr' => ['min' => 1, 'max' => 100]],
envVar: "int:IPN_SUGGEST_PART_DIGITS", envVarMode: EnvVarMode::OVERWRITE
)]
#[Assert\Range(min: 1, max: 6)]
public int $suggestPartDigits = 4;
}

View file

@ -34,4 +34,7 @@ class MiscSettings
#[EmbeddedSettings] #[EmbeddedSettings]
public ?ExchangeRateSettings $exchangeRate = null; public ?ExchangeRateSettings $exchangeRate = null;
}
#[EmbeddedSettings]
public ?IpnSuggestSettings $ipnSuggestSettings = null;
}

View file

@ -3,6 +3,7 @@
namespace App\Validator\Constraints; namespace App\Validator\Constraints;
use App\Entity\Parts\Part; use App\Entity\Parts\Part;
use App\Settings\MiscSettings\IpnSuggestSettings;
use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\ConstraintValidator;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
@ -10,12 +11,12 @@ use Doctrine\ORM\EntityManagerInterface;
class UniquePartIpnValidator extends ConstraintValidator class UniquePartIpnValidator extends ConstraintValidator
{ {
private EntityManagerInterface $entityManager; private EntityManagerInterface $entityManager;
private bool $enforceUniqueIpn; private IpnSuggestSettings $ipnSuggestSettings;
public function __construct(EntityManagerInterface $entityManager, bool $enforceUniqueIpn) public function __construct(EntityManagerInterface $entityManager, IpnSuggestSettings $ipnSuggestSettings)
{ {
$this->entityManager = $entityManager; $this->entityManager = $entityManager;
$this->enforceUniqueIpn = $enforceUniqueIpn; $this->ipnSuggestSettings = $ipnSuggestSettings;
} }
public function validate($value, Constraint $constraint) public function validate($value, Constraint $constraint)
@ -24,7 +25,7 @@ class UniquePartIpnValidator extends ConstraintValidator
return; return;
} }
if (!$this->enforceUniqueIpn) { if (!$this->ipnSuggestSettings->enableUniqueCheck) {
return; return;
} }
@ -40,12 +41,10 @@ class UniquePartIpnValidator extends ConstraintValidator
foreach ($existingParts as $existingPart) { foreach ($existingParts as $existingPart) {
if ($currentPart->getId() !== $existingPart->getId()) { if ($currentPart->getId() !== $existingPart->getId()) {
if ($this->enforceUniqueIpn) { $this->context->buildViolation($constraint->message)
$this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $value)
->setParameter('{{ value }}', $value) ->addViolation();
->addViolation();
}
} }
} }
} }
} }

View file

@ -13053,6 +13053,30 @@ Vezměte prosím na vědomí, že se nemůžete vydávat za uživatele se zakáz
<target>Pokud potřebujete směnné kurzy mezi měnami mimo eurozónu, můžete zde zadat API klíč z fixer.io.</target> <target>Pokud potřebujete směnné kurzy mezi měnami mimo eurozónu, můžete zde zadat API klíč z fixer.io.</target>
</segment> </segment>
</unit> </unit>
<unit id="jd7tEu3" name="settings.misc.ipn_suggest">
<segment state="translated">
<source>settings.misc.ipn_suggest</source>
<target>Seznam návrhů IPN součástek</target>
</segment>
</unit>
<unit id="kdi8mT4" name="settings.misc.ipn_suggest.enableUniqueCheck">
<segment state="translated">
<source>settings.misc.ipn_suggest.enableUniqueCheck</source>
<target>Kontrola jedinečnosti IPN aktivní. Odznačte, pokud chcete při opětovném zadání existujícího IPN při ukládání přidat k uživatelskému vstupu inkrementální číslo.</target>
</segment>
</unit>
<unit id="rociEg6" name="settings.misc.ipn_suggest.suggestPartDigits">
<segment state="translated">
<source>settings.misc.ipn_suggest.suggestPartDigits</source>
<target>Počet čísel pro inkrement</target>
</segment>
</unit>
<unit id="judfiK3" name="settings.misc.ipn_suggest.suggestPartDigits.help">
<segment state="translated">
<source>settings.misc.ipn_suggest.suggestPartDigits.help</source>
<target>Počet číslic použitých pro inkrementální číslování součástí v návrhovém systému IPN (Interní číslo součástky).</target>
</segment>
</unit>
<unit id="Ffr5xYM" name="settings.behavior.part_info"> <unit id="Ffr5xYM" name="settings.behavior.part_info">
<segment state="translated"> <segment state="translated">
<source>settings.behavior.part_info</source> <source>settings.behavior.part_info</source>

View file

@ -13133,6 +13133,30 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön
<target>Wenn Sie Wechselkurse zwischen Nicht-Euro-Währungen benötigen, können Sie hier einen API-Schlüssel von fixer.io eingeben.</target> <target>Wenn Sie Wechselkurse zwischen Nicht-Euro-Währungen benötigen, können Sie hier einen API-Schlüssel von fixer.io eingeben.</target>
</segment> </segment>
</unit> </unit>
<unit id="jd7tEu3" name="settings.misc.ipn_suggest">
<segment state="translated">
<source>settings.misc.ipn_suggest</source>
<target>Bauteil IPN-Vorschlagsliste</target>
</segment>
</unit>
<unit id="kdi8mT4" name="settings.misc.ipn_suggest.enableUniqueCheck">
<segment state="translated">
<source>settings.misc.ipn_suggest.enableUniqueCheck</source>
<target>Check auf Eindeutigkeit der IPN aktiv. Deselektieren Sie, wenn Sie bei erneuter Eingabe einer vorhandenen IPN eine inkrementelle Zahl an die Benutzereingabe beim Speichern erhalten möchten.</target>
</segment>
</unit>
<unit id="rociEg6" name="settings.misc.ipn_suggest.suggestPartDigits">
<segment state="translated">
<source>settings.misc.ipn_suggest.suggestPartDigits</source>
<target>Stellen für numerisches Inkrement</target>
</segment>
</unit>
<unit id="judfiK3" name="settings.misc.ipn_suggest.suggestPartDigits.help">
<segment state="translated">
<source>settings.misc.ipn_suggest.suggestPartDigits.help</source>
<target>Die Anzahl der Ziffern, die für die inkrementale Nummerierung von Teilen im IPN-Vorschlagssystem verwendet werden.</target>
</segment>
</unit>
<unit id="Ffr5xYM" name="settings.behavior.part_info"> <unit id="Ffr5xYM" name="settings.behavior.part_info">
<segment state="translated"> <segment state="translated">
<source>settings.behavior.part_info</source> <source>settings.behavior.part_info</source>

View file

@ -13134,6 +13134,30 @@ Please note, that you can not impersonate a disabled user. If you try you will g
<target>If you need exchange rates between non-euro currencies, you can input an API key from fixer.io here.</target> <target>If you need exchange rates between non-euro currencies, you can input an API key from fixer.io here.</target>
</segment> </segment>
</unit> </unit>
<unit id="jd7tEu3" name="settings.misc.ipn_suggest">
<segment state="translated">
<source>settings.misc.ipn_suggest</source>
<target>Part IPN Suggest</target>
</segment>
</unit>
<unit id="kdi8mT4" name="settings.misc.ipn_suggest.enableUniqueCheck">
<segment state="translated">
<source>settings.misc.ipn_suggest.enableUniqueCheck</source>
<target>IPN uniqueness check active. Deselect if you want an incremental number to be added to the user input when entering an existing IPN again upon saving.</target>
</segment>
</unit>
<unit id="rociEg6" name="settings.misc.ipn_suggest.suggestPartDigits">
<segment state="translated">
<source>settings.misc.ipn_suggest.suggestPartDigits</source>
<target>Increment Digits</target>
</segment>
</unit>
<unit id="judfiK3" name="settings.misc.ipn_suggest.suggestPartDigits.help">
<segment state="translated">
<source>settings.misc.ipn_suggest.suggestPartDigits.help</source>
<target>The number of digits used for the incremental numbering of parts in the IPN (Internal Part Number) suggestion system.</target>
</segment>
</unit>
<unit id="Ffr5xYM" name="settings.behavior.part_info"> <unit id="Ffr5xYM" name="settings.behavior.part_info">
<segment state="translated"> <segment state="translated">
<source>settings.behavior.part_info</source> <source>settings.behavior.part_info</source>