Allow to configure seperators, category fallback and a global prefix for IPN generation
Some checks failed
Build assets artifact / Build assets artifact (push) Has been cancelled
Docker Image Build / docker (push) Has been cancelled
Docker Image Build (FrankenPHP) / docker (push) Has been cancelled
Static analysis / Static analysis (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, mysql) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, postgres) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.2, sqlite) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.3, sqlite) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.4, sqlite) (push) Has been cancelled
PHPUnit Tests / PHPUnit and coverage Test (PHP 8.5, sqlite) (push) Has been cancelled

Translations still missing
This commit is contained in:
Jan Böhmer 2025-12-01 23:56:36 +01:00
parent 9a1961bcd7
commit 023d38d170
4 changed files with 102 additions and 26 deletions

View file

@ -1548,6 +1548,11 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
* dump_destination?: scalar|null, // A stream URL where dumps should be written to. // Default: null
* theme?: "dark"|"light", // Changes the color of the dump() output when rendered directly on the templating. "dark" (default) or "light". // Default: "dark"
* }
* @psalm-type MakerConfig = array{
* root_namespace?: scalar|null, // Default: "App"
* generate_final_classes?: bool, // Default: true
* generate_final_entities?: bool, // Default: false
* }
* @psalm-type WebpackEncoreConfig = array{
* output_path: scalar|null, // The path where Encore is building the assets - i.e. Encore.setOutputPath()
* crossorigin?: false|"anonymous"|"use-credentials", // crossorigin value when Encore.enableIntegrityHashes() is used, can be false (default), anonymous or use-credentials // Default: false
@ -1672,12 +1677,6 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
* post_processors?: array<string, array<string, mixed>>,
* },
* }
* @psalm-type DamaDoctrineTestConfig = array{
* enable_static_connection?: mixed, // Default: true
* enable_static_meta_data_cache?: bool, // Default: true
* enable_static_query_cache?: bool, // Default: true
* connection_keys?: list<mixed>,
* }
* @psalm-type TwigExtraConfig = array{
* cache?: bool|array{
* enabled?: bool, // Default: false
@ -2373,6 +2372,13 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
* invalidate_on_env_change?: bool, // Default: true
* },
* }
* @psalm-type JbtronicsTranslationEditorConfig = array{
* translations_path?: scalar|null, // Default: "%translator.default_path%"
* format?: scalar|null, // Default: "xlf"
* xliff_version?: scalar|null, // Default: "2.0"
* use_intl_icu_format?: bool, // Default: false
* writer_options?: list<scalar|null>,
* }
* @psalm-type ApiPlatformConfig = array{
* title?: scalar|null, // The title of the API. // Default: ""
* description?: scalar|null, // The description of the API. // Default: ""
@ -2628,17 +2634,11 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator;
* ...<mixed>
* },
* }
* @psalm-type MakerConfig = array{
* root_namespace?: scalar|null, // Default: "App"
* generate_final_classes?: bool, // Default: true
* generate_final_entities?: bool, // Default: false
* }
* @psalm-type JbtronicsTranslationEditorConfig = array{
* translations_path?: scalar|null, // Default: "%translator.default_path%"
* format?: scalar|null, // Default: "xlf"
* xliff_version?: scalar|null, // Default: "2.0"
* use_intl_icu_format?: bool, // Default: false
* writer_options?: list<scalar|null>,
* @psalm-type DamaDoctrineTestConfig = array{
* enable_static_connection?: mixed, // Default: true
* enable_static_meta_data_cache?: bool, // Default: true
* enable_static_query_cache?: bool, // Default: true
* connection_keys?: list<mixed>,
* }
* @psalm-type ConfigType = array{
* imports?: ImportsConfig,

View file

@ -132,6 +132,20 @@ class PartRepository extends NamedDBElementRepository
$category = $part->getCategory();
$ipnSuggestions = ['commonPrefixes' => [], 'prefixesPartIncrement' => []];
//Show global prefix first if configured
if ($this->ipnSuggestSettings->globalPrefix !== null && $this->ipnSuggestSettings->globalPrefix !== '') {
$ipnSuggestions['commonPrefixes'][] = [
'title' => $this->ipnSuggestSettings->globalPrefix,
'description' => $this->translator->trans('part.edit.tab.advanced.ipn.prefix.global_prefix')
];
$increment = $this->generateNextPossibleGlobalIncrement();
$ipnSuggestions['prefixesPartIncrement'][] = [
'title' => $this->ipnSuggestSettings->globalPrefix . $increment,
'description' => $this->translator->trans('part.edit.tab.advanced.ipn.prefix.global_prefix')
];
}
if (strlen($description) > 150) {
$description = substr($description, 0, 150);
}
@ -160,17 +174,17 @@ class PartRepository extends NamedDBElementRepository
if ($category instanceof Category) {
$currentPath = $category->getPartIpnPrefix();
$directIpnPrefixEmpty = $category->getPartIpnPrefix() === '';
$currentPath = $currentPath === '' ? 'n.a.' : $currentPath;
$currentPath = $currentPath === '' ? $this->ipnSuggestSettings->fallbackPrefix : $currentPath;
$increment = $this->generateNextPossiblePartIncrement($currentPath, $part, $suggestPartDigits);
$ipnSuggestions['commonPrefixes'][] = [
'title' => $currentPath . '-',
'title' => $currentPath . $this->ipnSuggestSettings->numberSeparator,
'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,
'title' => $currentPath . $this->ipnSuggestSettings->numberSeparator . $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')
];
@ -179,18 +193,19 @@ class PartRepository extends NamedDBElementRepository
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;
$effectiveIPNPrefix = $parentCategory->getPartIpnPrefix() === '' ? $this->ipnSuggestSettings->fallbackPrefix : $parentCategory->getPartIpnPrefix();
$currentPath = $effectiveIPNPrefix . $this->ipnSuggestSettings->categorySeparator . $currentPath;
$ipnSuggestions['commonPrefixes'][] = [
'title' => $currentPath . '-',
'title' => $currentPath . $this->ipnSuggestSettings->numberSeparator,
'description' => $this->translator->trans('part.edit.tab.advanced.ipn.prefix.hierarchical.no_increment')
];
$increment = $this->generateNextPossiblePartIncrement($currentPath, $part, $suggestPartDigits);
$ipnSuggestions['prefixesPartIncrement'][] = [
'title' => $currentPath . '-' . $increment,
'title' => $currentPath . $this->ipnSuggestSettings->numberSeparator . $increment,
'description' => $this->translator->trans('part.edit.tab.advanced.ipn.prefix.hierarchical.increment')
];
@ -199,7 +214,7 @@ class PartRepository extends NamedDBElementRepository
}
} elseif ($part->getID() === null) {
$ipnSuggestions['commonPrefixes'][] = [
'title' => 'n.a.',
'title' => $this->ipnSuggestSettings->fallbackPrefix,
'description' => $this->translator->trans('part.edit.tab.advanced.ipn.prefix.not_saved')
];
}
@ -246,6 +261,33 @@ class PartRepository extends NamedDBElementRepository
return $this->getNextIpnSuggestion($givenIpnsWithSameDescription);
}
private function generateNextPossibleGlobalIncrement(): string
{
$qb = $this->createQueryBuilder('part');
$qb->select('part.ipn')
->where('REGEXP(part.ipn, :ipnPattern) = TRUE')
->setParameter('ipnPattern', '^' . preg_quote($this->ipnSuggestSettings->globalPrefix, '/') . '\d+$')
->orderBy('NATSORT(part.ipn)', 'DESC')
->setMaxResults(1)
;
$highestIPN = $qb->getQuery()->getOneOrNullResult();
if ($highestIPN !== null) {
//Remove the prefix and extract the increment part
$incrementPart = substr($highestIPN['ipn'], strlen($this->ipnSuggestSettings->globalPrefix));
//Extract a number using regex
preg_match('/(\d+)$/', $incrementPart, $matches);
$incrementInt = isset($matches[1]) ? (int) $matches[1] + 1 : 0;
} else {
$incrementInt = 1;
}
return str_pad((string) $incrementInt, $this->ipnSuggestSettings->suggestPartDigits, '0', STR_PAD_LEFT);
}
/**
* Generates the next possible increment for a part within a given category, while ensuring uniqueness.
*
@ -266,7 +308,7 @@ class PartRepository extends NamedDBElementRepository
{
$qb = $this->createQueryBuilder('part');
$expectedLength = strlen($currentPath) + 1 + $suggestPartDigits; // Path + '-' + $suggestPartDigits digits
$expectedLength = strlen($currentPath) + strlen($this->ipnSuggestSettings->categorySeparator) + $suggestPartDigits; // Path + '-' + $suggestPartDigits digits
// Fetch all parts in the given category, sorted by their ID in ascending order
$qb->select('part')

View file

@ -78,4 +78,32 @@ class IpnSuggestSettings
envVar: "bool:IPN_USE_DUPLICATE_DESCRIPTION", envVarMode: EnvVarMode::OVERWRITE,
)]
public bool $useDuplicateDescription = false;
#[SettingsParameter(
label: new TM("settings.misc.ipn_suggest.fallbackPrefix"),
description: new TM("settings.misc.ipn_suggest.fallbackPrefix.help"),
options: ['type' => StringType::class],
)]
public string $fallbackPrefix = 'N.A.';
#[SettingsParameter(
label: new TM("settings.misc.ipn_suggest.numberSeparator"),
description: new TM("settings.misc.ipn_suggest.numberSeparator.help"),
options: ['type' => StringType::class],
)]
public ?string $numberSeparator = '-';
#[SettingsParameter(
label: new TM("settings.misc.ipn_suggest.categorySeparator"),
description: new TM("settings.misc.ipn_suggest.categorySeparator.help"),
options: ['type' => StringType::class],
)]
public ?string $categorySeparator = '-';
#[SettingsParameter(
label: new TM("settings.misc.ipn_suggest.globalPrefix"),
description: new TM("settings.misc.ipn_suggest.globalPrefix.help"),
options: ['type' => StringType::class],
)]
public ?string $globalPrefix = null;
}

View file

@ -14478,5 +14478,11 @@ Please note that this system is currently experimental, and the synonyms defined
<target>e.g. Format: 34 alphanumeric segments (any number) separated by "-", followed by "-" and 4 digits, e.g., PCOM-RES-0001</target>
</segment>
</unit>
<unit id="M5Q_eZW" name="part.edit.tab.advanced.ipn.prefix.global_prefix">
<segment>
<source>part.edit.tab.advanced.ipn.prefix.global_prefix</source>
<target>The global IPN prefix, common across all parts</target>
</segment>
</unit>
</file>
</xliff>