Added feature for part IPN suggest with category prefixes (#1054)

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

* Umstellung Migrationen bzgl. Multi-Plattform-Support.
Zunächst MySQL, SQLite Statements integrieren.

* Postgre Statements integrieren

* SQL-Formatierung in Migration verbessern

* 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.

* Anpassungen aus Analyse vornehmen

* IPN-Validierung für Parts überarbeiten

* IPN-Vorschlagslogik um Konfiguration erweitert

* Anpassungen aus phpstan Analyse

* IPN-Vorschlagslogik erweitert und Bauteil-IPN vereindeutigt

Die IPN-Logik wurde um eine Konfiguration zur automatischen Suffix-Anfügung und die Berücksichtigung von doppelten Beschreibungen bei Bedarf ergänzt. Zudem wurde das Datenmodell angepasst, um eine eindeutige Speicherung der IPN zu gewährleisten.

* Regex-Konfigurationsmöglichkeit für IPN-Vorschläge einführen

Die Einstellungen für die IPN-Vorschlagslogik wurden um eine Regex-Validierung und eine Hilfetext-Konfiguration erweitert. Tests und Änderungen an den Formularoptionen wurden implementiert.

* Match range assert and form limits in suggestPartDigits

* Keep existing behavior with autoAppend suffix by default

* Show the regex hint in the browser validation notice.

* Improved translations

* Removed unnecessary service definition

* Removed german comments

---------

Co-authored-by: Marcel Diegelmann <marcel.diegelmann@gmail.com>
Co-authored-by: Jan Böhmer <mail@jan-boehmer.de>
This commit is contained in:
web-devinition.de 2025-11-03 00:31:47 +01:00 committed by GitHub
parent 14a4f1f437
commit 771857e014
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
34 changed files with 2791 additions and 115 deletions

View file

@ -0,0 +1,97 @@
<?php
namespace App\EventSubscriber\UserSystem;
use App\Entity\Parts\Part;
use App\Settings\MiscSettings\IpnSuggestSettings;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Events;
use Doctrine\ORM\Event\OnFlushEventArgs;
class PartUniqueIpnSubscriber implements EventSubscriber
{
public function __construct(
private IpnSuggestSettings $ipnSuggestSettings
) {
}
public function getSubscribedEvents(): array
{
return [
Events::onFlush,
];
}
public function onFlush(OnFlushEventArgs $args): void
{
if (!$this->ipnSuggestSettings->autoAppendSuffix) {
return;
}
$em = $args->getObjectManager();
$uow = $em->getUnitOfWork();
$meta = $em->getClassMetadata(Part::class);
// Collect all IPNs already reserved in the current flush (so new entities do not collide with each other)
$reservedIpns = [];
// Helper to assign a collision-free IPN for a Part entity
$ensureUnique = function (Part $part) use ($em, $uow, $meta, &$reservedIpns) {
$ipn = $part->getIpn();
if ($ipn === null || $ipn === '') {
return;
}
// Check against IPNs already reserved in the current flush (except itself)
$originalIpn = $ipn;
$candidate = $originalIpn;
$increment = 1;
$conflicts = function (string $candidate) use ($em, $part, $reservedIpns) {
// Collision within the current flush session?
if (isset($reservedIpns[$candidate]) && $reservedIpns[$candidate] !== $part) {
return true;
}
// Collision with an existing DB row?
$existing = $em->getRepository(Part::class)->findOneBy(['ipn' => $candidate]);
return $existing !== null && $existing->getId() !== $part->getId();
};
while ($conflicts($candidate)) {
$candidate = $originalIpn . '_' . $increment;
$increment++;
}
if ($candidate !== $ipn) {
$before = $part->getIpn();
$part->setIpn($candidate);
// Recompute the change set so Doctrine writes the change
$uow->recomputeSingleEntityChangeSet($meta, $part);
$reservedIpns[$candidate] = $part;
// If the old IPN was reserved already, clean it up
if ($before !== null && isset($reservedIpns[$before]) && $reservedIpns[$before] === $part) {
unset($reservedIpns[$before]);
}
} else {
// Candidate unchanged, but reserve it so subsequent entities see it
$reservedIpns[$candidate] = $part;
}
};
// 1) Iterate over new entities
foreach ($uow->getScheduledEntityInsertions() as $entity) {
if ($entity instanceof Part) {
$ensureUnique($entity);
}
}
// 2) Iterate over updates (if IPN changed, ensure uniqueness again)
foreach ($uow->getScheduledEntityUpdates() as $entity) {
if ($entity instanceof Part) {
$ensureUnique($entity);
}
}
}
}