Files
Starseed/src/Module/Commercial/Infrastructure/DataFixtures/CommercialReferentialFixtures.php
T
matthieu b495e4030a
Auto Tag Develop / tag (push) Failing after 28s
[ERP-54] Créer les entités Client + sous-entités + référentiels (#29)
## Contexte

Ticket Lesstime **#54** (1.1 / Backend / M) — spec `docs/specs/M1-clients/spec-back.md` § 3.4 / § 3.5.

> 🔗 **MR stackée sur ERP-53** — cible `feature/ERP-53-migrer-tables-client-m1`, **pas** `develop`. À repointer vers `develop` quand ERP-53 sera mergé (cf. `STACK-BRANCHES-PROCEDURE.md`). Le diff ne montre que les fichiers d'ERP-54.

## Contenu

**9 entités** (`src/Module/Commercial/Domain/Entity/`) :
- Métier : `Client`, `ClientContact`, `ClientAddress`, `ClientRib` — `#[Auditable]` + Timestampable/Blamable.
- Référentiels statiques lecture seule : `TvaMode`, `PaymentDelay`, `PaymentType`, `Bank` — whitelistés dans `EntitiesAreTimestampableBlamableTest::EXCLUDED`.

**8 repositories** interfaces (`Domain/Repository/`) + impl Doctrine (`Infrastructure/Doctrine/`).

> La spec § 3.5 ne définit que 8 entités (4 métier + 4 référentiels) ; pas de 9ᵉ entité malgré la formulation « 9 paires » du ticket.

## Décisions

- **Aucun `#[ApiResource]` dans ce ticket** : le bloc ApiResource du `Client` (§ 3.4) référence `ClientProvider`/`ClientProcessor` = périmètre **ERP-55**. L'inclure casserait `cache:clear`/`make test`/`schema:validate`. Les entités sont des entités Doctrine pures (ORM + Assert + Groups). Endpoints lecture seule des référentiels → ticket dédié.
- **Q4** : `Client` sans `#[ORM\UniqueConstraint]` — unicité du nom de société portée par l'index partiel Postgres `uq_client_company_name_active` (inexprimable en attribut ORM).
- **Audit RIB (29/05)** : aucun `#[AuditIgnore]` sur `ClientRib.iban`/`bic` (tous champs audités, audit admin-only).
- **Cross-module (règle n°1)** : M2M `Category` via le contrat `Shared\Domain\Contract\CategoryInterface` + `resolve_target_entities` (pas d'import direct Catalog→Commercial) ; `ClientAddress.sites` via `SiteInterface` existant.

## Infra nécessaire (découvert pendant le dev)

- `doctrine.yaml` : mapping ORM du module `Commercial` (mappings explicites par module) + résolution `CategoryInterface → Category`.
- `CommercialReferentialFixtures` **créée** (n'existait pas — ERP-53 avait seedé les CategoryType côté Catalog) : re-seed idempotent des 4 référentiels, sinon vidés au `db-reset` (désormais tables mappées).
- `ColumnCommentsCatalog` étendu (colonnes M1) pour le chemin `schema:update`/test — sinon `ColumnsHaveSqlCommentTest` (garde-fou n°12) échoue.
- Migration retrofit `Version20260528120000` (ERP-67) rendue résiliente (`$schema->hasTable()`) : elle rejouait tout le catalogue mais s'exécute avant la création des tables M1 → `relation tva_mode does not exist`. Conforme à son docblock (« les futures migrations posent leurs propres COMMENT »).
- `makefile test-db-setup` : recréation de l'index partiel `uq_client_company_name_active` (analogue de la ligne existante pour `category`).

## Vérifications

- `make php-cs-fixer-allow-risky` ✓
- `make db-reset` ✓ (bout en bout ; 4 référentiels + 4 CategoryType présents, 2 index partiels créés)
- `make test` ✓ **312/312** (Architecture vert, 0 régression M0)
- `doctrine:schema:validate` : Mapping **OK** ; « not in sync » = bruit cosmétique pré-existant du projet (clear COMMENT hors-ORM, drop index partiels, renommages d'index). Seul diff introduit : renommage cosmétique de l'index M2M `idx_client_category_category` (même colonne) — aucun écart de type/colonne/FK vs migration ERP-53.

---------

Co-authored-by: admin malio <malio@yuno.malio.fr>
Co-authored-by: Matthieu <contact@malio.fr>
Co-authored-by: Matthieu <mtholot19@gmail.com>
Reviewed-on: #29
Co-authored-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
Co-committed-by: THOLOT DECHENE Matthieu <matthieu@yuno.malio.fr>
2026-06-01 15:20:22 +00:00

95 lines
3.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Module\Commercial\Infrastructure\DataFixtures;
use App\Module\Commercial\Domain\Entity\Bank;
use App\Module\Commercial\Domain\Entity\PaymentDelay;
use App\Module\Commercial\Domain\Entity\PaymentType;
use App\Module\Commercial\Domain\Entity\TvaMode;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
/**
* Fixtures du module Commercial : re-seed des 4 referentiels comptables
* (tva_mode, payment_delay, payment_type, bank) seedes par la migration M1
* (Version20260601000000).
*
* Pourquoi cette fixture EN PLUS du seed de la migration : depuis ERP-54 ces
* 4 tables sont des entites managees par l'ORM, donc le purger Doctrine les
* vide avant chaque `doctrine:fixtures:load`. Sans cette fixture, les
* referentiels seedes par la migration disparaitraient apres `make db-reset`
* (0 ligne en dev/test) — cassant les FK Client -> referentiels et les tests
* RG-1.12/1.13. Le seed migration couvre la prod (ou les fixtures ne tournent
* pas) ; cette fixture re-aligne dev et test. Memes valeurs des deux cotes.
*
* Idempotence : lookup par `code` avant insertion (sur le modele de
* CategoryTypeFixtures). Rejouable sans doublon meme si le purger est desactive.
*/
class CommercialReferentialFixtures extends Fixture
{
/**
* Source unique des referentiels : classe d'entite => [code => [label, position]].
* Doit rester aligne sur le seed de la migration Version20260601000000.
*
* @var array<class-string, array<string, array{string, int}>>
*/
private const REFERENTIALS = [
TvaMode::class => [
'FRANCE_VENTES' => ['France (ventes)', 10],
'EXPORT_VENTES' => ['Export (ventes)', 20],
'INTRACOM_VENTES' => ['Intracom (ventes)', 30],
],
PaymentDelay::class => [
'J15' => ['15 jours', 10],
'J30' => ['30 jours', 20],
'A_RECEPTION' => ['À réception', 30],
],
PaymentType::class => [
'VIREMENT' => ['Virement', 10],
'LCR' => ['LCR', 20],
'NON_SOUMISE' => ['Non soumise', 30],
'CHEQUE' => ['Chèque', 40],
],
Bank::class => [
'SG' => ['Société Générale', 10],
'CIC' => ['CIC', 20],
'CA' => ['Crédit Agricole', 30],
],
];
public function load(ObjectManager $manager): void
{
foreach (self::REFERENTIALS as $entityClass => $rows) {
$this->seedReferential($manager, $entityClass, $rows);
}
$manager->flush();
}
/**
* Upsert idempotent d'un referentiel : indexe l'existant par code puis
* cree/met a jour chaque entree. Les 4 entites partagent le meme contrat
* setCode/setLabel/setPosition.
*
* @param class-string $entityClass
* @param array<string, array{string, int}> $rows
*/
private function seedReferential(ObjectManager $manager, string $entityClass, array $rows): void
{
$existingByCode = [];
foreach ($manager->getRepository($entityClass)->findAll() as $entity) {
$existingByCode[$entity->getCode()] = $entity;
}
foreach ($rows as $code => [$label, $position]) {
$entity = $existingByCode[$code] ?? new $entityClass();
$entity->setCode($code);
$entity->setLabel($label);
$entity->setPosition($position);
$manager->persist($entity);
}
}
}