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).
This commit is contained in:
Matthieu
2026-06-16 08:14:54 +02:00
parent d9313dbec8
commit 97d7cacd2c
9 changed files with 1040 additions and 23 deletions
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace App\Tests\Module\Transport\Api;
/**
* Archivage / restauration transporteur — trou 409 de restauration en conflit
* d'unicite (M4, RG-4.14). Le nominal (archive pose archivedAt) et le 422
* « archive + autre champ » sont couverts par CarrierWriteApiTest. Jumeau de
* SupplierArchiveTest (M2).
*
* @internal
*/
final class CarrierArchiveTest extends AbstractCarrierApiTestCase
{
/**
* RG-4.14 : restaurer un transporteur archive dont le nom a ete repris par un
* transporteur actif entre-temps doit echouer en 409 (index partiel
* uq_carrier_name_active : un seul actif portant ce nom).
*/
public function testRestoreConflictReturns409(): void
{
$client = $this->createAdminClient();
$archived = $this->seedCarrier('Acme Conflict', true);
$this->seedCarrier('Acme Conflict', false);
$client->request('PATCH', '/api/carriers/'.$archived->getId(), [
'headers' => ['Content-Type' => self::MERGE],
'json' => ['isArchived' => false],
]);
self::assertResponseStatusCodeSame(409);
}
}