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

@ -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;
}