d9313dbec8
Schéma BDD du répertoire transporteurs (M4) + entités + contrat de lecture (liste + détail), socle du front. - Migration Version20260615150000 : tables carrier / carrier_address / carrier_contact / carrier_price (FK cross-module, CHECK enum, index partiel uq_carrier_name_active, COMMENT ON COLUMN). uploaded_document et qualimat_carrier réutilisées (non recréées). - Entités Carrier* (#[Auditable], Timestampable/Blamable) + ApiResource LECTURE seule (GetCollection + Get via CarrierProvider, anti-N+1, exclusion archivés + ?includeArchived). Écriture (POST/PATCH + Processor) reportée WT4+. - QualimatCarrier : mapping ORM lecture seule sur la table référentielle existante (sortie du schema_filter, mapping aligné DDL ERP-39, schema:update no-op) + endpoint de recherche read-only (§ 4.7). - Relations cross-module des prix (Client/Supplier/adresses) via contrats Shared (ClientInterface, SupplierInterface, ClientAddressInterface, SupplierAddressInterface) + resolve_target_entities — sans import inter-module (règle n°1). Ajout du groupe supplier_address:read aux champs de SupplierAddress pour l'embed. - Garde-fous : ColumnCommentsCatalog (carrier* + qualimat_carrier), makefile test-db-setup (index partiel carrier), i18n audit (transport_carrier*), EntitiesAreTimestampableBlamableTest (QualimatCarrier whitelisté). - CarrierSerializationContractTest : contrat JSON liste + détail vérifié (embeds objet, booléens, enveloppe Hydra) ; JSON réel capturé dans spec-back § 4.0.bis. make db-reset OK, make test vert (731), make nuxt-test vert (480), php-cs-fixer OK.
357 lines
23 KiB
PHP
357 lines
23 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace DoctrineMigrations;
|
|
|
|
use App\Shared\Infrastructure\Database\ColumnCommentsCatalog;
|
|
use Doctrine\DBAL\Schema\Schema;
|
|
use Doctrine\Migrations\AbstractMigration;
|
|
|
|
/**
|
|
* M4 — Repertoire transporteurs (ERP-155/157) : creation du schema BDD du
|
|
* repertoire transporteurs sous le module Transport (jumeau des M2/M3).
|
|
*
|
|
* Tables creees :
|
|
* - carrier : table principale (formulaire + lien QUALIMAT + archive + soft-delete
|
|
* + Timestampable/Blamable) ;
|
|
* - carrier_address / carrier_contact / carrier_price : sous-collections 1:n.
|
|
*
|
|
* Tables NON recrees (reutilisees) :
|
|
* - qualimat_carrier (ERP-39, Version20260612150000) : cible de la FK editable
|
|
* carrier.qualimat_carrier_id (§ 2.5) ;
|
|
* - uploaded_document (ERP-154, Version20260615130000) : cible de la FK
|
|
* carrier.discharge_document_id (Decharge, § 2.7) ;
|
|
* - client / client_address / supplier / supplier_address (M1/M2) et site (Sites) :
|
|
* cibles des FK de carrier_price (onglet Prix, RG-4.10/4.11).
|
|
*
|
|
* Namespace racine `DoctrineMigrations` (regle ABSOLUE n°11) et NON modulaire :
|
|
* FK cross-module (user, client, client_address, supplier, supplier_address, site,
|
|
* qualimat_carrier, uploaded_document). Le tri par timestamp au sein du namespace
|
|
* racine garantit l'ordre apres la creation de ces tables sur base vide.
|
|
*
|
|
* Decision IDs (spec § 2.2, tranchee a ce ticket) : carrier et ses sous-tables
|
|
* utilisent `INT GENERATED BY DEFAULT AS IDENTITY` (homogeneite globale Starseed
|
|
* M1/M2/M3, evite la friction bigint->string de l'ORM). Seule
|
|
* carrier.qualimat_carrier_id est BIGINT pour matcher qualimat_carrier.id (existant).
|
|
* Horodatages `TIMESTAMP(0) WITHOUT TIME ZONE` (le TimestampableBlamableTrait mappe
|
|
* `datetime_immutable`), pour que `schema:update --force` reste un no-op.
|
|
*
|
|
* Chaque colonne porte son `COMMENT ON COLUMN` (regle ABSOLUE n°12). Les 4
|
|
* tables carrier* etant mappees par l'ORM des ce ticket, elles sont aussi ajoutees
|
|
* a ColumnCommentsCatalog : `app:apply-column-comments` (test-db-setup) rejoue ces
|
|
* COMMENT apres le `schema:update --force` qui les droperait sinon.
|
|
*/
|
|
final class Version20260615150000 extends AbstractMigration
|
|
{
|
|
public function getDescription(): string
|
|
{
|
|
return 'ERP-155/157 (M4) : tables carrier + carrier_address + carrier_contact + carrier_price (repertoire transporteurs).';
|
|
}
|
|
|
|
public function up(Schema $schema): void
|
|
{
|
|
$this->createCarrierTable();
|
|
$this->createCarrierAddress();
|
|
$this->createCarrierContact();
|
|
$this->createCarrierPrice();
|
|
}
|
|
|
|
public function down(Schema $schema): void
|
|
{
|
|
// Ordre inverse des dependances FK : sous-collections d'abord, puis carrier.
|
|
$this->addSql('DROP TABLE IF EXISTS carrier_price');
|
|
$this->addSql('DROP TABLE IF EXISTS carrier_contact');
|
|
$this->addSql('DROP TABLE IF EXISTS carrier_address');
|
|
$this->addSql('DROP TABLE IF EXISTS carrier');
|
|
}
|
|
|
|
// =================================================================
|
|
// Table principale `carrier`
|
|
// =================================================================
|
|
|
|
private function createCarrierTable(): void
|
|
{
|
|
$this->addSql(<<<'SQL'
|
|
CREATE TABLE carrier (
|
|
id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
qualimat_carrier_id BIGINT DEFAULT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
certification_type VARCHAR(20) DEFAULT NULL,
|
|
is_chartered BOOLEAN DEFAULT FALSE NOT NULL,
|
|
indexation_rate NUMERIC(5, 2) DEFAULT NULL,
|
|
container_type VARCHAR(12) DEFAULT NULL,
|
|
volume_m3 NUMERIC(10, 2) DEFAULT NULL,
|
|
discharge_document_id INT DEFAULT NULL,
|
|
liot_plates TEXT DEFAULT NULL,
|
|
is_archived BOOLEAN DEFAULT FALSE NOT NULL,
|
|
archived_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL,
|
|
deleted_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL,
|
|
created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
updated_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
created_by INT DEFAULT NULL,
|
|
updated_by INT DEFAULT NULL,
|
|
PRIMARY KEY (id),
|
|
CONSTRAINT chk_carrier_certification_type
|
|
CHECK (certification_type IS NULL OR certification_type IN ('QUALIMAT', 'GMP_PLUS', 'OVOCOM', 'COMPTE_PROPRE', 'AUTRE')),
|
|
CONSTRAINT chk_carrier_container_type
|
|
CHECK (container_type IS NULL OR container_type IN ('BENNE', 'FOND_MOUVANT')),
|
|
CONSTRAINT fk_carrier_qualimat
|
|
FOREIGN KEY (qualimat_carrier_id) REFERENCES qualimat_carrier (id) ON DELETE SET NULL,
|
|
CONSTRAINT fk_carrier_discharge_document
|
|
FOREIGN KEY (discharge_document_id) REFERENCES uploaded_document (id) ON DELETE SET NULL,
|
|
CONSTRAINT fk_carrier_created_by
|
|
FOREIGN KEY (created_by) REFERENCES "user" (id) ON DELETE SET NULL,
|
|
CONSTRAINT fk_carrier_updated_by
|
|
FOREIGN KEY (updated_by) REFERENCES "user" (id) ON DELETE SET NULL
|
|
)
|
|
SQL);
|
|
|
|
$this->addSql('CREATE INDEX idx_carrier_is_archived ON carrier (is_archived)');
|
|
$this->addSql('CREATE INDEX idx_carrier_deleted_at ON carrier (deleted_at)');
|
|
$this->addSql('CREATE INDEX idx_carrier_qualimat ON carrier (qualimat_carrier_id)');
|
|
$this->addSql('CREATE INDEX idx_carrier_discharge_document ON carrier (discharge_document_id)');
|
|
$this->addSql('CREATE INDEX idx_carrier_created_by ON carrier (created_by)');
|
|
$this->addSql('CREATE INDEX idx_carrier_updated_by ON carrier (updated_by)');
|
|
|
|
// Unicite metier partielle : nom insensible a la casse, parmi les
|
|
// non-archives ET non soft-deletes uniquement (§ 2.6). Inexprimable en ORM.
|
|
$this->addSql(<<<'SQL'
|
|
CREATE UNIQUE INDEX uq_carrier_name_active
|
|
ON carrier (LOWER(name))
|
|
WHERE is_archived = FALSE AND deleted_at IS NULL
|
|
SQL);
|
|
|
|
$this->comment('carrier', '_table', 'Repertoire transporteurs (M4 Transport) — entites editables, archivables (is_archived) et soft-deletables (deleted_at). Distinct du referentiel qualimat_carrier.');
|
|
$this->comment('carrier', 'id', 'Identifiant interne auto-incremente.');
|
|
$this->comment('carrier', 'qualimat_carrier_id', 'Lien editable vers le referentiel QUALIMAT (saisie assistee RG-4.01). FK -> qualimat_carrier.id, ON DELETE SET NULL : transporteur conserve si la ligne QUALIMAT disparait.');
|
|
$this->comment('carrier', 'name', 'Raison sociale du transporteur (stockee en MAJUSCULES). Unique case-insensitive parmi les non-archives/non-supprimes (uq_carrier_name_active, RG-4.12 / § 2.6).');
|
|
$this->comment('carrier', 'certification_type', 'Type de certification : QUALIMAT (si lie, lecture seule) ou GMP_PLUS/OVOCOM/COMPTE_PROPRE/AUTRE. AUTRE declenche le champ Decharge (RG-4.02). Null en cas LIOT (RG-4.01).');
|
|
$this->comment('carrier', 'is_chartered', '« Affreter » coche : declenche indexation/benne-fond mouvant/volume, obligatoires (RG-4.03). Faux par defaut.');
|
|
$this->comment('carrier', 'indexation_rate', 'Taux d indexation en pourcentage (NUMERIC 5,2) — renseigne si affrete (RG-4.03).');
|
|
$this->comment('carrier', 'container_type', 'Type de contenant BENNE|FOND_MOUVANT (chk_carrier_container_type) — renseigne si affrete (RG-4.03).');
|
|
$this->comment('carrier', 'volume_m3', 'Volume en m3 (NUMERIC 10,2) — renseigne si affrete (RG-4.03).');
|
|
$this->comment('carrier', 'discharge_document_id', 'Document de Decharge (visible si certification_type = AUTRE, RG-4.02). FK -> uploaded_document.id (infra Shared § 2.7), ON DELETE SET NULL.');
|
|
$this->comment('carrier', 'liot_plates', 'Immatriculations LIOT separees par « ; » (cas special nom=LIOT, RG-4.01). Les autres champs sont masques dans ce cas.');
|
|
$this->comment('carrier', 'is_archived', 'Drapeau fonctionnel d archivage — masque par defaut dans la liste. Bascule via permission transport.carriers.archive (Admin seul).');
|
|
$this->comment('carrier', 'archived_at', 'Horodatage de l archivage — pose quand is_archived passe a vrai, remis a null a la restauration.');
|
|
$this->comment('carrier', 'deleted_at', 'Horodatage du soft-delete technique — non expose par l API au M4. Null = ligne active.');
|
|
$this->addTimestampableBlamableComments('carrier');
|
|
}
|
|
|
|
// =================================================================
|
|
// Sous-collection : adresses (1:n)
|
|
// =================================================================
|
|
|
|
private function createCarrierAddress(): void
|
|
{
|
|
$this->addSql(<<<'SQL'
|
|
CREATE TABLE carrier_address (
|
|
id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
carrier_id INT NOT NULL,
|
|
country VARCHAR(80) DEFAULT 'France' NOT NULL,
|
|
postal_code VARCHAR(20) DEFAULT NULL,
|
|
city VARCHAR(120) DEFAULT NULL,
|
|
street VARCHAR(255) DEFAULT NULL,
|
|
street_complement VARCHAR(255) DEFAULT NULL,
|
|
position INT DEFAULT 0 NOT NULL,
|
|
created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
updated_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
created_by INT DEFAULT NULL,
|
|
updated_by INT DEFAULT NULL,
|
|
PRIMARY KEY (id),
|
|
CONSTRAINT fk_carrier_address_carrier
|
|
FOREIGN KEY (carrier_id) REFERENCES carrier (id) ON DELETE CASCADE,
|
|
CONSTRAINT fk_carrier_address_created_by
|
|
FOREIGN KEY (created_by) REFERENCES "user" (id) ON DELETE SET NULL,
|
|
CONSTRAINT fk_carrier_address_updated_by
|
|
FOREIGN KEY (updated_by) REFERENCES "user" (id) ON DELETE SET NULL
|
|
)
|
|
SQL);
|
|
$this->addSql('CREATE INDEX idx_carrier_address_carrier ON carrier_address (carrier_id)');
|
|
$this->addSql('CREATE INDEX idx_carrier_address_created_by ON carrier_address (created_by)');
|
|
$this->addSql('CREATE INDEX idx_carrier_address_updated_by ON carrier_address (updated_by)');
|
|
|
|
$this->comment('carrier_address', '_table', 'Adresses d un transporteur (1:n) — onglet Adresse (M4). Pre-remplie depuis QUALIMAT si applicable (RG-4.05).');
|
|
$this->comment('carrier_address', 'id', 'Identifiant interne auto-incremente.');
|
|
$this->comment('carrier_address', 'carrier_id', 'FK -> carrier.id, ON DELETE CASCADE — transporteur proprietaire de l adresse.');
|
|
$this->comment('carrier_address', 'country', 'Pays de l adresse — defaut France.');
|
|
$this->comment('carrier_address', 'postal_code', 'Code postal (saisie assistee BAN cote front, RG-4.06).');
|
|
$this->comment('carrier_address', 'city', 'Ville — preremplie depuis le code postal via API BAN cote front.');
|
|
$this->comment('carrier_address', 'street', 'Numero et voie de l adresse.');
|
|
$this->comment('carrier_address', 'street_complement', 'Complement d adresse (etage, batiment...) — optionnel.');
|
|
$this->comment('carrier_address', 'position', 'Ordre d affichage de l adresse dans la liste du transporteur (croissant).');
|
|
$this->addTimestampableBlamableComments('carrier_address');
|
|
}
|
|
|
|
// =================================================================
|
|
// Sous-collection : contacts (1:n)
|
|
// =================================================================
|
|
|
|
private function createCarrierContact(): void
|
|
{
|
|
$this->addSql(<<<'SQL'
|
|
CREATE TABLE carrier_contact (
|
|
id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
carrier_id INT NOT NULL,
|
|
first_name VARCHAR(120) DEFAULT NULL,
|
|
last_name VARCHAR(120) DEFAULT NULL,
|
|
job_title VARCHAR(120) DEFAULT NULL,
|
|
phone_primary VARCHAR(20) DEFAULT NULL,
|
|
phone_secondary VARCHAR(20) DEFAULT NULL,
|
|
email VARCHAR(180) DEFAULT NULL,
|
|
position INT DEFAULT 0 NOT NULL,
|
|
created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
updated_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
created_by INT DEFAULT NULL,
|
|
updated_by INT DEFAULT NULL,
|
|
PRIMARY KEY (id),
|
|
CONSTRAINT chk_carrier_contact_filled
|
|
CHECK (first_name IS NOT NULL OR last_name IS NOT NULL OR job_title IS NOT NULL
|
|
OR phone_primary IS NOT NULL OR email IS NOT NULL),
|
|
CONSTRAINT fk_carrier_contact_carrier
|
|
FOREIGN KEY (carrier_id) REFERENCES carrier (id) ON DELETE CASCADE,
|
|
CONSTRAINT fk_carrier_contact_created_by
|
|
FOREIGN KEY (created_by) REFERENCES "user" (id) ON DELETE SET NULL,
|
|
CONSTRAINT fk_carrier_contact_updated_by
|
|
FOREIGN KEY (updated_by) REFERENCES "user" (id) ON DELETE SET NULL
|
|
)
|
|
SQL);
|
|
$this->addSql('CREATE INDEX idx_carrier_contact_carrier ON carrier_contact (carrier_id)');
|
|
$this->addSql('CREATE INDEX idx_carrier_contact_created_by ON carrier_contact (created_by)');
|
|
$this->addSql('CREATE INDEX idx_carrier_contact_updated_by ON carrier_contact (updated_by)');
|
|
|
|
$this->comment('carrier_contact', '_table', 'Contacts d un transporteur (1:n) — onglet Contact (M4). Au moins un champ rempli (RG-4.08, chk_carrier_contact_filled), max 2 telephones.');
|
|
$this->comment('carrier_contact', 'id', 'Identifiant interne auto-incremente.');
|
|
$this->comment('carrier_contact', 'carrier_id', 'FK -> carrier.id, ON DELETE CASCADE — transporteur proprietaire du contact.');
|
|
$this->comment('carrier_contact', 'first_name', 'Prenom du contact (capitalise serveur). Au moins un champ du contact est requis (RG-4.08).');
|
|
$this->comment('carrier_contact', 'last_name', 'Nom du contact (capitalise serveur). Au moins un champ du contact est requis (RG-4.08).');
|
|
$this->comment('carrier_contact', 'job_title', 'Fonction / intitule de poste du contact (≤ 120 caracteres).');
|
|
$this->comment('carrier_contact', 'phone_primary', 'Telephone principal — chiffres uniquement (normalisation serveur).');
|
|
$this->comment('carrier_contact', 'phone_secondary', 'Telephone secondaire — chiffres uniquement (max 2 telephones, RG-4.08).');
|
|
$this->comment('carrier_contact', 'email', 'Email du contact (lowercase serveur).');
|
|
$this->comment('carrier_contact', 'position', 'Ordre d affichage du contact dans la liste du transporteur (croissant).');
|
|
$this->addTimestampableBlamableComments('carrier_contact');
|
|
}
|
|
|
|
// =================================================================
|
|
// Sous-collection : prix (1:n) — onglet Prix (RG-4.09 -> RG-4.11)
|
|
// =================================================================
|
|
|
|
private function createCarrierPrice(): void
|
|
{
|
|
$this->addSql(<<<'SQL'
|
|
CREATE TABLE carrier_price (
|
|
id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
|
carrier_id INT NOT NULL,
|
|
direction VARCHAR(12) NOT NULL,
|
|
client_id INT DEFAULT NULL,
|
|
client_delivery_address_id INT DEFAULT NULL,
|
|
departure_site_id INT DEFAULT NULL,
|
|
supplier_id INT DEFAULT NULL,
|
|
supplier_supply_address_id INT DEFAULT NULL,
|
|
delivery_site_id INT DEFAULT NULL,
|
|
container_type VARCHAR(12) NOT NULL,
|
|
pricing_unit VARCHAR(8) NOT NULL,
|
|
price NUMERIC(12, 2) NOT NULL,
|
|
price_state VARCHAR(12) NOT NULL,
|
|
position INT DEFAULT 0 NOT NULL,
|
|
created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
updated_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
|
|
created_by INT DEFAULT NULL,
|
|
updated_by INT DEFAULT NULL,
|
|
PRIMARY KEY (id),
|
|
CONSTRAINT chk_carrier_price_direction CHECK (direction IN ('CLIENT', 'FOURNISSEUR')),
|
|
CONSTRAINT chk_carrier_price_container CHECK (container_type IN ('BENNE', 'FOND_MOUVANT')),
|
|
CONSTRAINT chk_carrier_price_unit CHECK (pricing_unit IN ('FORFAIT', 'TONNE')),
|
|
CONSTRAINT chk_carrier_price_state CHECK (price_state IN ('EN_COURS', 'VALIDE', 'NON_VALIDE')),
|
|
CONSTRAINT chk_carrier_price_client_branch
|
|
CHECK (direction <> 'CLIENT' OR (client_id IS NOT NULL AND supplier_id IS NULL)),
|
|
CONSTRAINT chk_carrier_price_supplier_branch
|
|
CHECK (direction <> 'FOURNISSEUR' OR (supplier_id IS NOT NULL AND client_id IS NULL)),
|
|
CONSTRAINT fk_carrier_price_carrier
|
|
FOREIGN KEY (carrier_id) REFERENCES carrier (id) ON DELETE CASCADE,
|
|
CONSTRAINT fk_carrier_price_client
|
|
FOREIGN KEY (client_id) REFERENCES client (id) ON DELETE RESTRICT,
|
|
CONSTRAINT fk_carrier_price_client_address
|
|
FOREIGN KEY (client_delivery_address_id) REFERENCES client_address (id) ON DELETE RESTRICT,
|
|
CONSTRAINT fk_carrier_price_departure_site
|
|
FOREIGN KEY (departure_site_id) REFERENCES site (id) ON DELETE RESTRICT,
|
|
CONSTRAINT fk_carrier_price_supplier
|
|
FOREIGN KEY (supplier_id) REFERENCES supplier (id) ON DELETE RESTRICT,
|
|
CONSTRAINT fk_carrier_price_supplier_address
|
|
FOREIGN KEY (supplier_supply_address_id) REFERENCES supplier_address (id) ON DELETE RESTRICT,
|
|
CONSTRAINT fk_carrier_price_delivery_site
|
|
FOREIGN KEY (delivery_site_id) REFERENCES site (id) ON DELETE RESTRICT,
|
|
CONSTRAINT fk_carrier_price_created_by
|
|
FOREIGN KEY (created_by) REFERENCES "user" (id) ON DELETE SET NULL,
|
|
CONSTRAINT fk_carrier_price_updated_by
|
|
FOREIGN KEY (updated_by) REFERENCES "user" (id) ON DELETE SET NULL
|
|
)
|
|
SQL);
|
|
$this->addSql('CREATE INDEX idx_carrier_price_carrier ON carrier_price (carrier_id)');
|
|
$this->addSql('CREATE INDEX idx_carrier_price_client ON carrier_price (client_id)');
|
|
$this->addSql('CREATE INDEX idx_carrier_price_client_address ON carrier_price (client_delivery_address_id)');
|
|
$this->addSql('CREATE INDEX idx_carrier_price_departure_site ON carrier_price (departure_site_id)');
|
|
$this->addSql('CREATE INDEX idx_carrier_price_supplier ON carrier_price (supplier_id)');
|
|
$this->addSql('CREATE INDEX idx_carrier_price_supplier_address ON carrier_price (supplier_supply_address_id)');
|
|
$this->addSql('CREATE INDEX idx_carrier_price_delivery_site ON carrier_price (delivery_site_id)');
|
|
$this->addSql('CREATE INDEX idx_carrier_price_created_by ON carrier_price (created_by)');
|
|
$this->addSql('CREATE INDEX idx_carrier_price_updated_by ON carrier_price (updated_by)');
|
|
|
|
$this->comment('carrier_price', '_table', 'Prix d un transporteur (1:n) — onglet Prix (M4). Branche CLIENT ou FOURNISSEUR selon direction (RG-4.09→4.11, CHECK chk_carrier_price_*).');
|
|
$this->comment('carrier_price', 'id', 'Identifiant interne auto-incremente.');
|
|
$this->comment('carrier_price', 'carrier_id', 'FK -> carrier.id, ON DELETE CASCADE — transporteur proprietaire du prix.');
|
|
$this->comment('carrier_price', 'direction', 'Sens du prix : CLIENT ou FOURNISSEUR (RG-4.09). Pilote l affichage et l obligation des colonnes client_*/supplier_* (RG-4.10/4.11).');
|
|
$this->comment('carrier_price', 'client_id', 'Branche CLIENT (RG-4.10) : client concerne. FK -> client.id, ON DELETE RESTRICT. Requis ssi direction = CLIENT.');
|
|
$this->comment('carrier_price', 'client_delivery_address_id', 'Branche CLIENT : adresse de livraison du client. FK -> client_address.id, ON DELETE RESTRICT.');
|
|
$this->comment('carrier_price', 'departure_site_id', 'Branche CLIENT : adresse de depart = un des 3 sites (86/17/82). FK -> site.id, ON DELETE RESTRICT.');
|
|
$this->comment('carrier_price', 'supplier_id', 'Branche FOURNISSEUR (RG-4.11) : fournisseur concerne. FK -> supplier.id, ON DELETE RESTRICT. Requis ssi direction = FOURNISSEUR.');
|
|
$this->comment('carrier_price', 'supplier_supply_address_id', 'Branche FOURNISSEUR : adresse d approvisionnement du fournisseur. FK -> supplier_address.id, ON DELETE RESTRICT.');
|
|
$this->comment('carrier_price', 'delivery_site_id', 'Branche FOURNISSEUR : adresse de livraison = un des 3 sites (86/17/82). FK -> site.id, ON DELETE RESTRICT.');
|
|
$this->comment('carrier_price', 'container_type', 'Type de contenant BENNE|FOND_MOUVANT (chk_carrier_price_container).');
|
|
$this->comment('carrier_price', 'pricing_unit', 'Unite de tarification FORFAIT|TONNE (chk_carrier_price_unit).');
|
|
$this->comment('carrier_price', 'price', 'Montant du prix (NUMERIC 12,2).');
|
|
$this->comment('carrier_price', 'price_state', 'Etat du prix : EN_COURS, VALIDE ou NON_VALIDE (chk_carrier_price_state). Affiche dans le tableau Prix.');
|
|
$this->comment('carrier_price', 'position', 'Ordre d affichage du prix dans la liste du transporteur (croissant).');
|
|
$this->addTimestampableBlamableComments('carrier_price');
|
|
}
|
|
|
|
// =================================================================
|
|
// Helpers (identiques au M2 Version20260605130000)
|
|
// =================================================================
|
|
|
|
/**
|
|
* Pose les 4 commentaires standardises Timestampable/Blamable sur une table,
|
|
* en reutilisant le catalogue partage (source unique, ERP-67).
|
|
*/
|
|
private function addTimestampableBlamableComments(string $table): void
|
|
{
|
|
foreach (ColumnCommentsCatalog::timestampableBlamableComments() as $column => $description) {
|
|
$this->comment($table, $column, $description);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Emet un `COMMENT ON TABLE` (colonne speciale `_table`) ou `COMMENT ON COLUMN`
|
|
* en dollar-quoting Postgres ($_$...$_$) pour eviter tout echappement d apostrophe.
|
|
*/
|
|
private function comment(string $table, string $column, string $description): void
|
|
{
|
|
$quotedTable = '"'.str_replace('"', '""', $table).'"';
|
|
|
|
if ('_table' === $column) {
|
|
$this->addSql(sprintf('COMMENT ON TABLE %s IS $_$%s$_$', $quotedTable, $description));
|
|
|
|
return;
|
|
}
|
|
|
|
$this->addSql(sprintf(
|
|
'COMMENT ON COLUMN %s.%s IS $_$%s$_$',
|
|
$quotedTable,
|
|
'"'.str_replace('"', '""', $column).'"',
|
|
$description,
|
|
));
|
|
}
|
|
}
|