76e7a59ba7
Logique métier d'écriture et de lecture du ticket de pesée (M5).
Processor (POST/PATCH) :
- résolution du site courant (CurrentSiteProvider) + attribution du numéro
{siteCode}-TP-{NNNN} à la création, immuables ensuite (RG-5.02 / RG-5.09) ;
- exclusivité de la contrepartie CLIENT/FOURNISSEUR/AUTRE — null-ification des
champs hors-branche (RG-5.03, garde-fou CHECK Postgres) ;
- normalisation immatriculation trim/UPPER + masque XX-000-XX hors « Tout
format », 422 inline sur le champ si invalide (RG-5.01 / RG-5.10) ;
- DSD autoritaire pour les pesées AUTO via DsdAllocator (verrou), MANUEL conservé
(RG-5.04) ;
- poids net = plein − vide recalculé (RG-5.05).
Provider (GET) : liste paginée (Paginator ORM, règle n°13), recherche ?search=,
tri ?order[displayDate], cloisonnement par site courant appliqué dans le provider
(le SiteScopedQueryExtension ne traverse pas un provider custom), fetch-join
client/supplier/site anti-N+1, 404 hors périmètre / soft-delete.
Ajouts : WeighingTicketNumberAllocator (compteur weighing_ticket_counter,
SELECT FOR UPDATE), WeighingTicketFieldNormalizer, InvalidImmatriculationException
+ alias DI.
make test vert (811), Architecture vert (CollectionsArePaginatedTest).
88 lines
3.2 KiB
PHP
88 lines
3.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Module\Logistique\Application\Service;
|
|
|
|
use App\Module\Logistique\Domain\Exception\InvalidImmatriculationException;
|
|
|
|
/**
|
|
* Normalisation serveur des champs texte d'un WeighingTicket, appliquee par le
|
|
* WeighingTicketProcessor AVANT persistance. Cf. spec-back M5 § 6 + RG-5.01 /
|
|
* RG-5.10. Jumeau leger de CarrierFieldNormalizer (M4).
|
|
*
|
|
* - immatriculation (RG-5.01 / RG-5.10) : trim + UPPER. Si « Tout format » N'EST
|
|
* PAS coche (freeFormat = false), la saisie est ramenee au masque SIV
|
|
* canonique XX-000-XX (separateurs/espaces ignores a la saisie, re-poses) ; une
|
|
* plaque qui ne s'y conforme pas leve InvalidImmatriculationException (-> 422
|
|
* par le Processor). En « Tout format » (anciennes plaques, etranger, engins),
|
|
* seul le trim + UPPER s'applique.
|
|
* - otherLabel (RG-5.03) : trim ; une chaine vide apres trim devient null (evite
|
|
* de persister "" dans une colonne nullable).
|
|
*
|
|
* Methodes null-safe : une entree null ressort null (l'obligation eventuelle est
|
|
* portee par les Assert de l'entite / la coherence contrepartie, pas ici).
|
|
*/
|
|
final class WeighingTicketFieldNormalizer
|
|
{
|
|
/**
|
|
* Plaque SIV « nue » (sans separateurs) : 2 lettres, 3 chiffres, 2 lettres.
|
|
* Les lettres interdites du SIV (I, O, U + SS) ne sont pas filtrees ici : le
|
|
* masque de saisie reste volontairement simple (le metier accepte ces cas via
|
|
* « Tout format » si besoin).
|
|
*/
|
|
private const string SIV_BARE_PATTERN = '/^[A-Z]{2}[0-9]{3}[A-Z]{2}$/';
|
|
|
|
/**
|
|
* Normalise l'immatriculation (RG-5.01 / RG-5.10).
|
|
*
|
|
* @param bool $freeFormat « Tout format » coche -> masque SIV desactive
|
|
*
|
|
* @throws InvalidImmatriculationException si !freeFormat et la plaque ne
|
|
* respecte pas le masque XX-000-XX
|
|
*/
|
|
public function normalizeImmatriculation(?string $value, bool $freeFormat): ?string
|
|
{
|
|
if (null === $value) {
|
|
return null;
|
|
}
|
|
|
|
$value = mb_strtoupper(trim($value), 'UTF-8');
|
|
if ('' === $value) {
|
|
return null;
|
|
}
|
|
|
|
// « Tout format » : aucune contrainte de masque (RG-5.01).
|
|
if ($freeFormat) {
|
|
return $value;
|
|
}
|
|
|
|
// Masque SIV : on ignore tout ce qui n'est pas alphanumerique (l'operateur
|
|
// peut saisir « ab123cd », « AB 123 CD » ou « AB-123-CD ») puis on valide
|
|
// le squelette 2-3-2 et on repose les separateurs canoniques.
|
|
$bare = preg_replace('/[^A-Z0-9]/', '', $value) ?? '';
|
|
|
|
if (1 !== preg_match(self::SIV_BARE_PATTERN, $bare)) {
|
|
throw new InvalidImmatriculationException(
|
|
'Format d\'immatriculation invalide : attendu XX-000-XX (cochez « Tout format » pour une plaque libre).',
|
|
);
|
|
}
|
|
|
|
return sprintf('%s-%s-%s', substr($bare, 0, 2), substr($bare, 2, 3), substr($bare, 5, 2));
|
|
}
|
|
|
|
/**
|
|
* Trim du libelle « Autre » (RG-5.03). Une chaine vide apres trim devient null.
|
|
*/
|
|
public function normalizeOtherLabel(?string $value): ?string
|
|
{
|
|
if (null === $value) {
|
|
return null;
|
|
}
|
|
|
|
$value = trim($value);
|
|
|
|
return '' === $value ? null : $value;
|
|
}
|
|
}
|