mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-01-13 13:49:33 +00:00
Erweitere IPN-Suggest um Bauteilbeschreibung.
Die Implementierung berücksichtigt nun zusätzlich die Bauteilbeschreibung zu maximal 150 Zeichen Länge für die Generierung von IPN-Vorschlägen und Inkrementen.
This commit is contained in:
parent
9c565cbaed
commit
0dc7dbdb03
20 changed files with 361 additions and 35 deletions
|
|
@ -464,7 +464,7 @@ final class PartController extends AbstractController
|
|||
$template,
|
||||
[
|
||||
'part' => $new_part,
|
||||
'ipnSuggestions' => $partRepository->autoCompleteIpn($data, $this->autocompletePartDigits),
|
||||
'ipnSuggestions' => $partRepository->autoCompleteIpn($data, base64_encode($data->getDescription()), $this->autocompletePartDigits),
|
||||
'form' => $form,
|
||||
'merge_old_name' => $merge_infos['tname_before'] ?? null,
|
||||
'merge_other' => $merge_infos['other_part'] ?? null,
|
||||
|
|
|
|||
|
|
@ -283,6 +283,7 @@ class TypeaheadController extends AbstractController
|
|||
$partId = null;
|
||||
}
|
||||
$categoryId = $request->query->getInt('categoryId');
|
||||
$description = $request->query->getString('description');
|
||||
|
||||
/** @var Part $part */
|
||||
$part = $partId !== null ? $entityManager->getRepository(Part::class)->find($partId) : new Part();
|
||||
|
|
@ -292,7 +293,7 @@ class TypeaheadController extends AbstractController
|
|||
$clonedPart->setCategory($category);
|
||||
|
||||
$partRepository = $entityManager->getRepository(Part::class);
|
||||
$ipnSuggestions = $partRepository->autoCompleteIpn($clonedPart, $this->autocompletePartDigits);
|
||||
$ipnSuggestions = $partRepository->autoCompleteIpn($clonedPart, $description, $this->autocompletePartDigits);
|
||||
|
||||
return new JsonResponse($ipnSuggestions);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ class PartBaseType extends AbstractType
|
|||
'attr' => [
|
||||
'placeholder' => 'part.edit.description.placeholder',
|
||||
'rows' => 2,
|
||||
'data-ipn-suggestion' => 'descriptionField',
|
||||
],
|
||||
])
|
||||
->add('minAmount', SIUnitType::class, [
|
||||
|
|
|
|||
|
|
@ -109,10 +109,30 @@ class PartRepository extends NamedDBElementRepository
|
|||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
public function autoCompleteIpn(Part $part, int $autocompletePartDigits): array
|
||||
/**
|
||||
* Provides IPN (Internal Part Number) suggestions for a given part based on its category, description,
|
||||
* and configured autocomplete digit length.
|
||||
*
|
||||
* This function generates suggestions for common prefixes and incremented prefixes based on
|
||||
* the part's current category and its hierarchy. If the part is unsaved, a default "n.a." prefix is returned.
|
||||
*
|
||||
* @param Part $part The part for which autocomplete suggestions are generated.
|
||||
* @param string $description Base64-encoded description to assist in generating suggestions.
|
||||
* @param int $autocompletePartDigits The number of digits used in autocomplete increments.
|
||||
*
|
||||
* @return array An associative array containing the following keys:
|
||||
* - 'commonPrefixes': List of common prefixes found for the part.
|
||||
* - 'prefixesPartIncrement': Increments for the generated prefixes, including hierarchical prefixes.
|
||||
*/
|
||||
public function autoCompleteIpn(Part $part, string $description, int $autocompletePartDigits): array
|
||||
{
|
||||
$category = $part->getCategory();
|
||||
$ipnSuggestions = ['commonPrefixes' => [], 'prefixesPartIncrement' => []];
|
||||
$description = base64_decode($description);
|
||||
|
||||
if (strlen($description) > 150) {
|
||||
$description = substr($description, 0, 150);
|
||||
}
|
||||
|
||||
// Validate the category and ensure it's an instance of Category
|
||||
if ($category instanceof Category) {
|
||||
|
|
@ -127,6 +147,22 @@ class PartRepository extends NamedDBElementRepository
|
|||
'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')
|
||||
];
|
||||
|
||||
$suggestionByDescription = $this->getIpnSuggestByDescription($description);
|
||||
|
||||
if ($suggestionByDescription !== null && $suggestionByDescription !== $part->getIpn() && $part->getIpn() !== null && $part->getIpn() !== '') {
|
||||
$ipnSuggestions['prefixesPartIncrement'][] = [
|
||||
'title' => $part->getIpn(),
|
||||
'description' => $this->translator->trans('part.edit.tab.advanced.ipn.prefix.description.current-increment')
|
||||
];
|
||||
}
|
||||
|
||||
if ($suggestionByDescription !== null) {
|
||||
$ipnSuggestions['prefixesPartIncrement'][] = [
|
||||
'title' => $suggestionByDescription,
|
||||
'description' => $this->translator->trans('part.edit.tab.advanced.ipn.prefix.description.increment')
|
||||
];
|
||||
}
|
||||
|
||||
$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')
|
||||
|
|
@ -165,7 +201,62 @@ class PartRepository extends NamedDBElementRepository
|
|||
return $ipnSuggestions;
|
||||
}
|
||||
|
||||
public function generateNextPossiblePartIncrement(string $currentPath, Part $currentPart, int $autocompletePartDigits): string
|
||||
/**
|
||||
* Suggests the next IPN (Internal Part Number) based on the provided part description.
|
||||
*
|
||||
* Searches for parts with similar descriptions and retrieves their existing IPNs to calculate the next suggestion.
|
||||
* Returns null if the description is empty or no suggestion can be generated.
|
||||
*
|
||||
* @param string $description The part description to search for.
|
||||
*
|
||||
* @return string|null The suggested IPN, or null if no suggestion is possible.
|
||||
*
|
||||
* @throws NonUniqueResultException
|
||||
*/
|
||||
public function getIpnSuggestByDescription(string $description): ?string
|
||||
{
|
||||
if ($description === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$qb = $this->createQueryBuilder('part');
|
||||
|
||||
$qb->select('part')
|
||||
->where('part.description LIKE :descriptionPattern')
|
||||
->setParameter('descriptionPattern', $description.'%')
|
||||
->orderBy('part.id', 'ASC');
|
||||
|
||||
$partsBySameDescription = $qb->getQuery()->getResult();
|
||||
$givenIpnsWithSameDescription = [];
|
||||
|
||||
foreach ($partsBySameDescription as $part) {
|
||||
if ($part->getIpn() === null || $part->getIpn() === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$givenIpnsWithSameDescription[] = $part->getIpn();
|
||||
}
|
||||
|
||||
return $this->getNextIpnSuggestion($givenIpnsWithSameDescription);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the next possible increment for a part within a given category, while ensuring uniqueness.
|
||||
*
|
||||
* This method calculates the next available increment for a part's identifier (`ipn`) based on the current path
|
||||
* and the number of digits specified for the autocomplete feature. It ensures that the generated identifier
|
||||
* aligns with the expected length and does not conflict with already existing identifiers in the same category.
|
||||
*
|
||||
* @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 int $autocompletePartDigits 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.
|
||||
*
|
||||
* @throws NonUniqueResultException If the query returns non-unique results.
|
||||
* @throws NoResultException If the query fails to return a result.
|
||||
*/
|
||||
private function generateNextPossiblePartIncrement(string $currentPath, Part $currentPart, int $autocompletePartDigits): ?string
|
||||
{
|
||||
$qb = $this->createQueryBuilder('part');
|
||||
|
||||
|
|
@ -213,4 +304,41 @@ class PartRepository extends NamedDBElementRepository
|
|||
|
||||
return str_pad((string) $nextIncrement, $autocompletePartDigits, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the next IPN suggestion based on the maximum numeric suffix found in the given IPNs.
|
||||
*
|
||||
* The new IPN is constructed using the base format of the first provided IPN,
|
||||
* incremented by the next free numeric suffix. If no base IPNs are found,
|
||||
* returns null.
|
||||
*
|
||||
* @param array $givenIpns List of IPNs to analyze.
|
||||
*
|
||||
* @return string|null The next suggested IPN, or null if no base IPNs can be derived.
|
||||
*/
|
||||
private function getNextIpnSuggestion(array $givenIpns): ?string {
|
||||
$maxSuffix = 0;
|
||||
|
||||
foreach ($givenIpns as $ipn) {
|
||||
// Check whether the IPN contains a suffix "_ <number>"
|
||||
if (preg_match('/_(\d+)$/', $ipn, $matches)) {
|
||||
$suffix = (int)$matches[1];
|
||||
if ($suffix > $maxSuffix) {
|
||||
$maxSuffix = $suffix; // Höchste Nummer speichern
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the basic format (the IPN without suffix) from the first IPN
|
||||
$baseIpn = $givenIpns[0] ?? '';
|
||||
$baseIpn = preg_replace('/_\d+$/', '', $baseIpn); // Entferne vorhandene "_<Zahl>"
|
||||
|
||||
if ($baseIpn === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Generate next free possible IPN
|
||||
return $baseIpn . '_' . ($maxSuffix + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue