Files
Starseed/config/packages/doctrine.yaml
T
Matthieu f6d39cb187 feat(logistique) : migration schéma M5 tickets de pesée (ERP-182)
Crée le schéma BDD du module Logistique (M5) au namespace racine
DoctrineMigrations (FK cross-module user/client/supplier/site, règle n°11) :

- site.code VARCHAR(8) (préfixe de numérotation {siteCode}-TP, RG-5.02) +
  backfill depuis le code postal + index unique uq_site_code. Colonne NULLABLE
  à ce ticket (l'entité Site ne mappe pas encore code) ; mapping ORM,
  peuplement et SET NOT NULL portés par le ticket entité.
- weighing_ticket_counter / weighbridge_dsd_counter : compteurs par site
  (numéro RG-5.02 / DSD pont RG-5.04), gérés en DBAL brut FOR UPDATE, hors ORM
  → exclus du schema_filter (sinon schema:update les droppe) + catalogués.
- weighing_ticket : table principale (contrepartie Client/Fournisseur/Autre
  avec CHECK 3 branches RG-5.03, immatriculation partagée, pesées vide/plein
  en colonnes plates, net_weight dérivé, soft-delete + Timestampable/Blamable).
  Index unique (site_id, number) + index FK. ON DELETE site/client/supplier =
  RESTRICT, created_by/updated_by = SET NULL.

COMMENT ON COLUMN sur chaque colonne créée (règle n°12). make test +
ColumnsHaveSqlCommentTest verts, db-reset OK.
2026-06-17 16:32:18 +02:00

197 lines
11 KiB
YAML

doctrine:
dbal:
# Deux connexions pointant sur le meme DSN : l'ORM utilise `default`,
# l'AuditLogWriter utilise `audit` pour ecrire hors de la transaction
# Doctrine et eviter tout entanglement transactionnel en batch.
default_connection: default
connections:
default:
url: '%env(resolve:DATABASE_URL)%'
profiling_collect_backtrace: '%kernel.debug%'
# Exclut certaines tables de toute operation de comparaison de
# schema (doctrine:schema:update, schema:validate, diff de
# migrations...). Ces tables n'ont volontairement aucune entite
# mappee :
# - `audit_log` : append-only via DBAL brut (AuditLogWriter) pour
# eviter la recursion du listener Doctrine.
# - `qualimat_sync_log` : journal de synchro transporteurs
# QUALIMAT, ecrit en DBAL brut par `app:qualimat:sync`, hors ORM.
# NB : `qualimat_carrier` n'est PLUS filtree depuis M4 (ERP-155) :
# elle est desormais mappee en LECTURE SEULE par l'entite
# App\Module\Transport\Domain\Entity\QualimatCarrier (cible de la
# FK editable carrier.qualimat_carrier_id). Son mapping reproduit
# a l'identique le DDL de la migration ERP-39 (unique siret, index
# is_active, TIMESTAMP(6)) -> schema:update reste un no-op.
# - `idtf_product` / `idtf_sync_log` : referentiel codes IDTF
# synchronise en DBAL brut par `app:idtf:sync`, hors ORM.
# - `weighing_ticket_counter` / `weighbridge_dsd_counter` : compteurs
# par site (numero de ticket de pesee RG-5.02 / DSD du pont RG-5.04,
# M5 Logistique), incrementes en DBAL brut sous verrou `FOR UPDATE`
# par l'allocateur — jamais mappes en ORM (cf. spec M5 § 2.5 / § 2.7).
# Sans ce filtre, schema:update les considere comme "orphelines" et
# genere un `DROP TABLE` qui casse la base de test apres chaque
# `make test-db-setup` (la migration les a creees, schema:update les
# supprime juste apres). Creation / suppression restent pilotees par
# les migrations (audit_log : Version20260420202749 ; qualimat :
# Version20260612150000 ; idtf : Version20260612160000 ; compteurs M5 :
# Version20260617150000).
schema_filter: '~^(?!(?:audit_log|qualimat_sync_log|idtf_product|idtf_sync_log|weighing_ticket_counter|weighbridge_dsd_counter)$).+~'
audit:
url: '%env(resolve:DATABASE_URL)%'
orm:
validate_xml_mapping: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
identity_generation_preferences:
Doctrine\DBAL\Platforms\PostgreSQLPlatform: identity
auto_mapping: true
# Mapping contrat DDD → classe concrete. Permet au module Core de
# referencer `SiteInterface` dans ses ORM mappings (User) sans importer
# la classe concrete du module Sites. Pattern officiel Doctrine pour
# les bounded contexts, remplace l'ancien import direct
# `App\Module\Sites\Domain\Entity\Site` dans User.php.
resolve_target_entities:
App\Shared\Domain\Contract\SiteInterface: App\Module\Sites\Domain\Entity\Site
# Cible des ManyToOne created_by / updated_by du TimestampableBlamableTrait.
# Permet a Shared de referencer UserInterface dans ses ORM mappings sans
# importer la classe concrete du module Core (cf. spec-back M0 § 2.8).
Symfony\Component\Security\Core\User\UserInterface: App\Module\Core\Domain\Entity\User
# Cible des ManyToMany Client.categories / ClientAddress.categories (M1).
# Permet au module Commercial de referencer une Category via le contrat
# Shared sans importer la classe concrete du module Catalog (regle n°1).
App\Shared\Domain\Contract\CategoryInterface: App\Module\Catalog\Domain\Entity\Category
# Cibles des ManyToOne de CarrierPrice (M4 Transport, onglet Prix) :
# permet au module Transport de referencer Client / Supplier et leurs
# adresses (M1/M2 Commercial) via des contrats Shared sans importer les
# classes concretes (regle n°1). L'embed JSON passe par les read-groups
# des entites concretes (client:read / supplier:read / ...).
App\Shared\Domain\Contract\ClientInterface: App\Module\Commercial\Domain\Entity\Client
App\Shared\Domain\Contract\ClientAddressInterface: App\Module\Commercial\Domain\Entity\ClientAddress
App\Shared\Domain\Contract\SupplierInterface: App\Module\Commercial\Domain\Entity\Supplier
App\Shared\Domain\Contract\SupplierAddressInterface: App\Module\Commercial\Domain\Entity\SupplierAddress
mappings:
# Mapping des entites techniques partagees (src/Shared/Domain/Entity).
# Premier occupant : UploadedDocument (infra upload generique ERP-154).
# Necessaire car les entites Shared ne sont pas couvertes par
# l'auto_mapping (qui ne cible que les bundles).
Shared:
type: attribute
is_bundle: false
dir: '%kernel.project_dir%/src/Shared/Domain/Entity'
prefix: 'App\Shared\Domain\Entity'
alias: Shared
Core:
type: attribute
is_bundle: false
dir: '%kernel.project_dir%/src/Module/Core/Domain/Entity'
prefix: 'App\Module\Core\Domain\Entity'
alias: Core
# Mapping inconditionnelle du module Sites : la structure DB
# existe meme si SitesModule::class est retire de config/modules.php.
# L'activation fonctionnelle (ex: exposition des permissions, futurs
# endpoints API) passe exclusivement par config/modules.php.
Sites:
type: attribute
is_bundle: false
dir: '%kernel.project_dir%/src/Module/Sites/Domain/Entity'
prefix: 'App\Module\Sites\Domain\Entity'
alias: Sites
# Mapping inconditionnel du module Catalog (meme logique que Sites) :
# la structure DB (category, category_type) existe meme si
# CatalogModule::class n'est pas encore wire dans config/modules.php
# (declaration du module = ticket 0.5 / ERP-47). L'ORM doit connaitre
# les entites pour que le schema soit en phase ; l'activation
# fonctionnelle passe exclusivement par config/modules.php.
Catalog:
type: attribute
is_bundle: false
dir: '%kernel.project_dir%/src/Module/Catalog/Domain/Entity'
prefix: 'App\Module\Catalog\Domain\Entity'
alias: Catalog
# Mapping inconditionnel du module Commercial (meme logique que Catalog) :
# les tables (client, sous-collections, referentiels comptables) creees
# par la migration M1 (Version20260601000000) doivent etre connues de
# l'ORM. L'activation fonctionnelle passe par config/modules.php.
Commercial:
type: attribute
is_bundle: false
dir: '%kernel.project_dir%/src/Module/Commercial/Domain/Entity'
prefix: 'App\Module\Commercial\Domain\Entity'
alias: Commercial
# Mapping inconditionnel du module Technique (meme logique que Commercial) :
# les tables prestataires (provider + sous-collections + jointures M2M)
# creees par la migration M3 (Version20260612100000) doivent etre connues
# de l'ORM. L'activation fonctionnelle passe par config/modules.php.
Technique:
type: attribute
is_bundle: false
dir: '%kernel.project_dir%/src/Module/Technique/Domain/Entity'
prefix: 'App\Module\Technique\Domain\Entity'
alias: Technique
# Mapping inconditionnel du module Transport (meme logique que Technique) :
# les tables transporteurs (carrier + sous-collections) creees par la
# migration M4 (Version20260615150000) et le mapping lecture-seule de
# qualimat_carrier (referentiel ERP-39) doivent etre connus de l'ORM.
# L'activation fonctionnelle passe par config/modules.php.
Transport:
type: attribute
is_bundle: false
dir: '%kernel.project_dir%/src/Module/Transport/Domain/Entity'
prefix: 'App\Module\Transport\Domain\Entity'
alias: Transport
controller_resolver:
auto_mapping: false
when@test:
doctrine:
dbal:
# Le suffixe "_test" doit etre propage aux deux connexions : l'ORM
# l'herite via `default`, l'AuditLogWriter via `audit`. Sans cela,
# la connexion `audit` ecrirait dans la base dev pendant que l'ORM
# ecrit dans la base test — divergence invisible en apparence mais
# fatale pour les tests du journal d'audit.
#
# `idle_connection_ttl: 1` (au lieu du defaut 600s) : en test on
# reboote le kernel a chaque test. Sans TTL court, les connexions
# orphelines s'accumulent dans PG et on finit par saturer le pool
# (max_connections=100) sur une suite de 200+ tests qui utilisent
# 2 connexions chacun (default + audit).
connections:
default:
dbname_suffix: '_test%env(default::TEST_TOKEN)%'
idle_connection_ttl: 1
audit:
dbname_suffix: '_test%env(default::TEST_TOKEN)%'
idle_connection_ttl: 1
orm:
mappings:
# Entite fictive SiteAware utilisee uniquement en tests du
# module Sites (ticket 4). Le mapping n'est charge qu'en
# environnement test, donc aucun impact sur les schemas
# dev/prod. La table est creee a la volee par les tests
# d'integration (via `SchemaTool::createSchema`) dans le
# setUp de SiteScopedQueryExtensionTest.
TestFixtures:
type: attribute
is_bundle: false
dir: '%kernel.project_dir%/tests/Fixtures/SiteAware'
prefix: 'App\Tests\Fixtures\SiteAware'
alias: TestFixtures
when@prod:
doctrine:
orm:
query_cache_driver:
type: pool
pool: doctrine.system_cache_pool
result_cache_driver:
type: pool
pool: doctrine.result_cache_pool
framework:
cache:
pools:
doctrine.result_cache_pool:
adapter: cache.app
doctrine.system_cache_pool:
adapter: cache.system