Files
Starseed/src/Module/Transport/Application/Service/CarrierFieldNormalizer.php
T
Matthieu 13d4a08bc9 feat(transport) : CarrierProcessor + champs conditionnels formulaire principal (ERP-158)
Ecriture du formulaire principal transporteur (M4, WT4) : POST/PATCH via
CarrierProcessor + CarrierFieldNormalizer, contraintes conditionnelles sur
l'entite Carrier.

- RG-4.01 : POST qualimatCarrier -> certificationType=QUALIMAT + FK persistee ;
  cas LIOT (name=LIOT) -> certification non requise, liotPlates accepte.
- RG-4.02 : certificationType=AUTRE sans dischargeDocument -> 422 (Assert\Callback).
- RG-4.03 : isChartered=true sans indexationRate/containerType/volumeM3 -> 422.
- RG-4.12 : doublon de nom (parmi actifs) -> 409 (index partiel uq_carrier_name_active).
- RG-4.13 : normalisation serveur (name UPPER, liotPlates ;-split/trim/UPPER) +
  methodes personne/telephone/email pour les sous-ressources Contact (WT7).
- RG-4.14 : PATCH isArchived exige transport.carriers.archive (Admin seul),
  mode strict -> 403 + 422 si autre champ ; restauration en conflit -> 409.

Operations Post/Patch ajoutees a l'ApiResource (lecture posee au WT3 conservee).
RG conditionnelles portees par validateMainFormConsistency (Assert\Callback +
->atPath()) pour un propertyPath mappable inline (useFormErrors, ERP-101).

certificationType / containerType whitelistes dans EXCLUDED_LENGTH_MIRROR (Choice
borne deja les valeurs, miroir SupplierAddress::addressType).

Tests : CarrierWriteApiTest (RG-4.01->4.03/4.12->4.14), CarrierRBACMatrixTest
(matrice bureau/compta/commerciale/usine), CarrierArchiveTest (409 restauration),
CarrierFieldNormalizerTest (RG-4.13). make test vert (750).
2026-06-16 15:13:10 +02:00

106 lines
3.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Module\Transport\Application\Service;
/**
* Normalisation serveur des champs texte d'un Carrier / CarrierContact, appliquee
* par le CarrierProcessor (et les futurs processors de sous-ressources, WT6/7/8)
* AVANT persistance. Cf. spec-back M4 § 2.10 + RG-4.13. Jumeau de
* SupplierFieldNormalizer (M2), enrichi du cas LIOT (immatriculations).
*
* - name : UPPERCASE integral (RG-4.13)
* - firstName / lastName (personnes, sur CarrierContact) : Title Case (RG-4.13)
* - phone* : chiffres uniquement, ex "06.12.34.56.78" -> "0612345678" (RG-4.13).
* Le formatage d'affichage "XX XX XX XX XX" est de la responsabilite du front.
* - email : lowercase integral (RG-4.13)
* - liotPlates : liste « ; » -> split, trim, UPPER, rejoin "; " (cas LIOT RG-4.01).
*
* Toutes les methodes sont null-safe et trim-ent l'entree ; une chaine vide apres
* trim devient null (evite de persister "" dans des colonnes nullable).
*/
final class CarrierFieldNormalizer
{
/**
* Raison sociale en majuscules (RG-4.13). Conserve null tel quel ; une chaine
* non vide est trim + upper. Une chaine vide reste "" (champ obligatoire :
* c'est l'Assert\NotBlank qui rejette, pas le normalizer).
*/
public function normalizeName(?string $value): ?string
{
if (null === $value) {
return null;
}
return mb_strtoupper(trim($value), 'UTF-8');
}
/**
* Nom/prenom de personne en Title Case (RG-4.13) : "JEAN dupont" ->
* "Jean Dupont". Une chaine vide apres trim devient null.
*/
public function normalizePersonName(?string $value): ?string
{
if (null === $value) {
return null;
}
$value = trim($value);
return '' === $value ? null : mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
}
/**
* Email en minuscules (RG-4.13). Une chaine vide apres trim devient null.
*/
public function normalizeEmail(?string $value): ?string
{
if (null === $value) {
return null;
}
$value = trim($value);
return '' === $value ? null : mb_strtolower($value, 'UTF-8');
}
/**
* Telephone reduit aux chiffres (RG-4.13) : "06.12.34.56.78" -> "0612345678".
* Une valeur sans aucun chiffre devient null.
*/
public function normalizePhone(?string $value): ?string
{
if (null === $value) {
return null;
}
$digits = preg_replace('/\D+/', '', $value) ?? '';
return '' === $digits ? null : $digits;
}
/**
* Immatriculations LIOT (RG-4.01 / RG-4.13) : la saisie « ; »-separee est
* decoupee, chaque plaque trim + UPPER, les segments vides ecartes, puis
* recomposee avec le separateur canonique "; ". Une saisie sans aucune plaque
* exploitable devient null.
*/
public function normalizeLiotPlates(?string $value): ?string
{
if (null === $value) {
return null;
}
$plates = [];
foreach (explode(';', $value) as $plate) {
$plate = trim($plate);
if ('' !== $plate) {
$plates[] = mb_strtoupper($plate, 'UTF-8');
}
}
return [] === $plates ? null : implode('; ', $plates);
}
}