feat(logistique) : entité WeighingTicket + dette site.code (ERP-183)

Entité WeighingTicket
- Entité métier complète (#[Auditable], TimestampableBlamableTrait, relations
  ORM Client/Supplier/Site) + contrat de sérialisation à 3 maillons
  (weighing_ticket:read / :item:read + contextes par opération).
- Getters calculés displayDate et plateFreeFormat (#[SerializedName]),
  sécurité view/manage, pas de Delete/archive.
- Validation #[Assert\*] messages FR + #[Assert\Callback] RG-5.03 (->atPath()),
  libellé i18n audit.entity.logistique_weighingticket.
- Repository : interface Domain + DoctrineWeighingTicketRepository
  (recherche + tri number DESC, deletedAt IS NULL).

Dette site.code
- Site.code mappé VARCHAR(8) (groupes read/write), dérivation auto au
  PrePersist (2 premiers chiffres du CP), UniqueConstraint uq_site_code.
- Migration Version20260617160000 : ALTER COLUMN code SET NOT NULL + COMMENT.
- Fixtures (codes 86/17/82) et SiteApiTest ajustés.

Câblage
- doctrine.yaml : mapping ORM du module Logistique (absent du scaffold ERP-181).
- ColumnCommentsCatalog : site.code + table weighing_ticket.

Specs M5 versionnées (spec-back / spec-front / prompts).
This commit is contained in:
Matthieu
2026-06-17 17:46:20 +02:00
parent f6d39cb187
commit 4369c71706
20 changed files with 2006 additions and 10 deletions
+8 -6
View File
@@ -79,7 +79,9 @@ final class SiteApiTest extends AbstractApiTestCase
'name' => 'Test-New-Site',
'street' => '1 rue du Test',
'complement' => null,
'postalCode' => '86000',
// CP 75xxx -> code derive 75 : evite la collision uq_site_code
// avec la fixture Chatellerault (code 86) — RG-5.02 (ERP-183).
'postalCode' => '75000',
'city' => 'Poitiers',
'color' => '#AABBCC',
],
@@ -94,7 +96,7 @@ final class SiteApiTest extends AbstractApiTestCase
public function testAdminCanPatchSite(): void
{
$em = $this->getEm();
$site = new Site('Test-Patch-Site', '1 rue Test', null, '86000', 'Poitiers', '#000000');
$site = new Site('Test-Patch-Site', '1 rue Test', null, '75000', 'Poitiers', '#000000');
$em->persist($site);
$em->flush();
@@ -112,7 +114,7 @@ final class SiteApiTest extends AbstractApiTestCase
public function testAdminCanDeleteSite(): void
{
$em = $this->getEm();
$site = new Site('Test-Delete-Site', '1 rue Test', null, '86000', 'Poitiers', '#000000');
$site = new Site('Test-Delete-Site', '1 rue Test', null, '75000', 'Poitiers', '#000000');
$em->persist($site);
$em->flush();
$siteId = $site->getId();
@@ -129,7 +131,7 @@ final class SiteApiTest extends AbstractApiTestCase
public function testUserWithViewButNotManageCannotDelete(): void
{
$em = $this->getEm();
$site = new Site('Test-Protected', '1 rue Test', null, '86000', 'Poitiers', '#000000');
$site = new Site('Test-Protected', '1 rue Test', null, '75000', 'Poitiers', '#000000');
$em->persist($site);
$em->flush();
@@ -189,7 +191,7 @@ final class SiteApiTest extends AbstractApiTestCase
'json' => [
'name' => 'Test-FullAddress-Ignored',
'street' => '1 rue Test',
'postalCode' => '86000',
'postalCode' => '75000',
'city' => 'Poitiers',
'color' => '#000000',
'fullAddress' => 'Adresse arbitraire envoyee par le client',
@@ -200,7 +202,7 @@ final class SiteApiTest extends AbstractApiTestCase
$data = $response->toArray();
// Le getter computed prevaut sur ce qu'envoie le client : street
// determine la 1re ligne, jamais la valeur "Adresse arbitraire...".
self::assertSame("1 rue Test\n86000 Poitiers", $data['fullAddress']);
self::assertSame("1 rue Test\n75000 Poitiers", $data['fullAddress']);
}
public function testCreateSiteWithInvalidPostalCodeReturns422(): void