Aller au contenu

Dictionnaire de données

Dictionnaire de données généré depuis les commentaires de la base (COMMENT ON).

Name Columns Comment Type
journal.evenement 12 Journal d’audit en ajout seul : qui, quoi, quand, avant et après, pour chaque écriture sur une table du socle. Écrit par un déclencheur, jamais par l’API. BASE TABLE
app.organisation 16 Client de BioTrace (coopérative ou entreprise). Racine de l’isolation multi-clients ; liée à une organisation Keycloak. BASE TABLE
app.utilisateur 9 Utilisateur d’une organisation, lié au compte Keycloak. Un compte externe a une ligne par organisation où il intervient. BASE TABLE
app.ensemble_permissions 8 Ensemble de permissions configurable par organisation, attribuable à des utilisateurs. BASE TABLE
app.attribution_permissions 9 Attribution d’un ensemble de permissions à un utilisateur, avec sa période de validité. BASE TABLE
app.appareil 16 Téléphone enrôlé pour un agent. Une installation est enrôlée pour une seule organisation à la fois. BASE TABLE
app.code_enrolement 10 Code à usage unique permettant d’enrôler un appareil. Conservé en empreinte, jamais en clair. BASE TABLE
app.fichier 12 Référence d’un objet du stockage compatible S3 (EC-029). La base garde la référence et l’empreinte, pas le contenu. BASE TABLE
app.parametre 10 Valeur de paramètre versionnée par organisation, en ajout seul. Les anciennes versions restent lisibles pour les opérations passées. BASE TABLE
app.pack 6 Pack filière, table globale maintenue par l’équipe BioTrace et chargée depuis le dossier packs/. BASE TABLE
app.pack_version 11 Version d’un pack filière. Une version publiée est figée : seul le passage à « retiré » reste possible. BASE TABLE
app.taux_change 8 Taux de change daté, table globale en ajout seul. Exprimé en rapport de deux entiers, en unités majeures : 655957/1000 veut dire 1 EUR = 655,957 XOF (EC-028, EC-070). Pour convertir des montants en unités mineures, le domaine l’adapte avec les décimales de chaque devise (app.devise, packages/domain/devises). BASE TABLE
app.compte_matiere 6 Compte de la partie double de la matière : un lot, ou un compte technique (origine, intrant, perte, purge, expédition, écart d’inventaire). BASE TABLE
app.mouvement 10 Mouvement de matière, en ajout seul. Se compose de lignes signées dont la somme est nulle. Toute correction passe par une contre-passation. BASE TABLE
app.mouvement_ligne 7 Ligne d’un mouvement : quantité signée entrée dans un compte (positive) ou sortie (négative), en grammes. BASE TABLE
app.v_solde_compte 4 Solde en grammes de chaque compte de matière, par produit : somme des lignes de mouvement. Vue soumise à la RLS de l’appelant. VIEW
app.validation 9 Décision d’un validateur sur un objet, en ajout seul (EC-012). Les colonnes cibles s’ajoutent lot par lot. BASE TABLE
sync.commande 13 Commande envoyée par un téléphone, conservée telle que reçue, en ajout seul. Elle sert de trace et de clé d’idempotence. BASE TABLE
sync.commande_resultat 10 Issue du traitement d’une commande, descendue vers l’appareil par la synchronisation. BASE TABLE
sync.controle 14 File de contrôle unique : anomalies métier à examiner par un responsable. Modifiable jusqu’à sa levée. BASE TABLE
sync.rejet 11 Rejets de sécurité de la synchronisation (EC-043) : identité incohérente, identifiant ou séquence de commande déjà utilisés. Ajout seul. Aucune écriture métier ne découle d’un rejet. BASE TABLE
app.site 7 Site physique de l’organisation (dépôt, centre de collecte, atelier). Regroupe des emplacements de stockage. BASE TABLE
app.emplacement 10 Emplacement de stockage d’un site (magasin, cellule ou zone). Fonde la séparation physique des flux (RG-154). BASE TABLE
app.campagne 14 Campagne d’une filière. Toute opération appartient à une campagne. Plusieurs campagnes peuvent s’ouvrir la même année (EC-023). BASE TABLE
app.appel_administration 11 Journal des appels d’administration d’identité (comptes, permissions, appareils, organisation). Ajout seul : un appel refusé ou en échec est tracé aussi. BASE TABLE
app.facteur 7 Catalogue fermé des facteurs qui peuvent conditionner une valeur de règle (PC-02). Globale, ajout seul, chargée par le noyau et les packs. BASE TABLE
app.facteur_valeur 7 Valeurs permises d’un facteur de liste (pays, région, type de sol, opération, mode de culture). Globale, ajout seul : un pack publié ajoute ses valeurs, jamais n’en retire. BASE TABLE
app.unite 5 Unités de saisie et d’affichage des quantités. Le stock est toujours en grammes (EC-030) : l’unité ne fait que convertir. Globale, ajout seul. BASE TABLE
app.produit 13 Matière première ou produit fini de l’organisation, instancié depuis un modèle de pack publié ou créé à la main. Jamais supprimé. BASE TABLE
app.attribut 11 Attribut typé d’un produit (calibre, Brix…). Le schéma JSON décrit la valeur ; Ajv la valide côté client et côté serveur. BASE TABLE
app.modele_numerotation 7 Modèle de numérotation d’une organisation, par version. En ajout seul : une nouvelle version s’ajoute, les anciennes restent lisibles pour les campagnes qui les ont figées (EC-010). BASE TABLE
app.sequence_numero 8 Compteur du numéro officiel, par campagne, nature et produit. Le rang suivant est pris par app.attribuer_numero() dans la transaction de l’appelant (RG-141, RG-142). BASE TABLE
app.groupement 7 Groupement de producteurs. Unité de filtrage des données descendues sur le téléphone de l’agent. BASE TABLE
app.producteur 22 Producteur, objet pivot partagé avec le futur système de contrôle interne. Jamais supprimé, jamais réutilisé (RG-010). Un doublon de pièce d’identité ouvre un contrôle sans refuser la fiche (AV1-02). BASE TABLE
app.affectation_agent 8 Affectation d’un agent de terrain à un groupement. Historique : une affectation se termine par une date de fin, elle n’est jamais supprimée. BASE TABLE
app.fournisseur 13 Fournisseur de matière première. Tous les champs d’identité sont obligatoires (EC-053). Sa certification est portée par la table certificat (séquence A6, EC-025), pas par la fiche. BASE TABLE
app.client 15 Client acheteur. Tous les champs du cahier v1.2 §11 sont obligatoires (EC-053). BASE TABLE
app.parcelle 17 Parcelle d’un producteur. Son polygone vit dans app.parcelle_version, versionné et en ajout seul. Le téléphone filtre sur organisation_id et groupement_id. BASE TABLE
app.zone_cueillette 9 Zone de cueillette (karité, cajou…), cartographiée comme une parcelle : même table de versions de polygone (EC-050). BASE TABLE
app.parcelle_version 19 Version d’un polygone de parcelle ou de zone de cueillette. En ajout seul : une correction est un nouveau relevé, l’ancienne version reste lisible. Un polygone invalide est conservé tel quel, jamais corrigé (pas de ST_MakeValid). BASE TABLE
app.parcelle_version_en_vigueur 19 Version en vigueur (la plus récente) de chaque parcelle et de chaque zone de cueillette. Applique la RLS de l’appelant. VIEW
app.organisation_referentiel 8 Référentiels cochés par l’organisation (RG-301 ; bio UE, NOP, Naturland…), pris dans le catalogue global app.referentiel. Alimente le facteur dynamique referentiel (EC-065). Ancienne table app.referentiel, renommée sans changer ses identifiants (EC-101). BASE TABLE
app.certificat 15 Certificat d’un opérateur, d’un groupement ou d’un fournisseur pour un référentiel. « Expiré » n’est jamais stocké : il se calcule depuis date_fin à la date de l’opération (EC-025). Aucun texte libre (RG-013). BASE TABLE
app.statut_conformite 16 Statut de conformité d’une unité (producteur, parcelle ou zone de cueillette) pour un référentiel. En ajout seul : un changement ajoute une ligne, l’historique est conservé. La révision d’un statut exige un motif (RG-150). BASE TABLE
app.devise 5 Devise ISO 4217 connue de BioTrace, avec son nombre de décimales. Globale, en lecture seule pour l’API. Sert aux montants en unités mineures et au passage du taux publié au taux applicable (EC-070). BASE TABLE
app.contrat 16 Contrat d’achat, de vente ou de prestation. Fixe prix, volumes, tolérance et formule de réfaction par ligne. Modifiable en brouillon ; une fois actif, seul son statut change. BASE TABLE
app.contrat_ligne 12 Ligne d’un contrat : un produit, une quantité engagée, un prix par unité, une tolérance et la formule de réfaction. Un produit par contrat. BASE TABLE
app.prime 11 Prime d’une ligne de contrat : bio, équitable ou qualité, en montant par unité de prix ou en taux sur le prix de base. Un type par ligne. BASE TABLE
app.rendement_parcelle 10 Rendement de référence d’une parcelle ou d’une zone, en g/ha. En ajout seul : chaque révision ajoute une ligne avec son motif (RG-023). Aucune valeur par défaut : sans ligne, pas de capacité (EC-007). BASE TABLE
app.capacite_version 13 Capacité d’une parcelle ou d’une zone pour une campagne : surface mesurée × rendement. En ajout seul, une version par modification. Ne s’applique qu’une fois validée par la direction et le RSCI (EC-012). BASE TABLE
app.solde_capacite 12 Solde précalculé d’une parcelle ou d’une zone pour une campagne : capacité validée, quantité consommée, reste. Écrit par la base à la validation de la capacité ; le consommé reste à zéro jusqu’au Lot 2. Descendu sur le téléphone. BASE TABLE
app.activite 11 Catalogue fermé et versionné des types d’activité, sur trois niveaux (AM-04). Défini par BioTrace : un pack crée des instances typées, jamais de nouveau type. Les normes, les portées de certificat et les modules citent ces types. BASE TABLE
app.module 8 Modules du monolithe modulaire (AM-05). Toutes les tables sont déployées pour tous ; un module s’active par organisation. Le noyau est toujours actif. BASE TABLE
app.module_dependance 5 Dépendances entre modules, reflet des manifestes du domaine (packages/domain/src/modules/manifestes.ts) : un module ne s’active que si ses modules requis sont actifs, et un module requis ne se désactive pas tant qu’un module qui en dépend est actif. BASE TABLE
app.module_activite 5 Activités de premier niveau qui proposent un module. BioTrace propose, la direction décide : une proposition n’active rien. BASE TABLE
app.organisation_module 7 Activations et désactivations des modules d’une organisation, en ajout seul : date, auteur, motif. Un module désactivé passe en lecture seule ; ses données restent visibles et exportables. BASE TABLE
app.module_en_vigueur 6 État courant de chaque module d’une organisation : la ligne la plus récente de organisation_module. Un module absent est inactif, sauf le noyau. VIEW
app.organisation_activite 9 Activités déclarées par une organisation, et si besoin par site, en ajout seul : date, auteur, motif. Un nœud est une activité de premier niveau ou un nœud de l’arbre du pack (noeud_activite). Garde-fous et cascade par déclencheur. BASE TABLE
app.activite_en_vigueur 8 État courant de chaque nœud déclaré : la ligne la plus récente de organisation_activite par organisation, site et nœud. VIEW
app.variable_dictionnaire 10 Dictionnaire de données (AM-09, P5) : variables qu’une règle a le droit de lire, avec leur type, leur unité et leur disponibilité hors ligne. Généré depuis les manifestes des modules et, plus tard, les packs publiés. BASE TABLE
app.regle_decision 15 Lignes des tables de décision publiées par les packs référentiels (AM-09), en ajout seul : une correction passe par une nouvelle version du pack. Toutes les lignes vraies s’appliquent ; l’issue la plus forte décide. BASE TABLE
app.constat 25 Constat d’écart (AM-15) : chaque ligne vraie, ou impossible à évaluer, laisse une trace explicable (règle, version, source, valeurs). Ne se modifie que par changement de statut ; l’historique est dans le journal. BASE TABLE
app.referentiel 8 Catalogue global des référentiels (EC-089) : un code par référentiel, relié au pack référentiel qui porte ses états de qualité, ses grilles et ses tables de décision (AM-06). Écrit par la publication des packs et par les migrations. BASE TABLE
app.certificat_portee 13 Portée d’un certificat (AM-07) : ce que l’organisme certifie, une ligne par activité, produit, site ou unité, incluse ou exclue. En ajout seul. Un certificat sans aucune ligne a une portée non renseignée : aucun écart n’est alors calculé. L’écart de portée est jugé par le domaine, pas par la base. BASE TABLE
app.point_controle 18 Point de la grille de conformité d’un référentiel (AM-06), publié par une version de pack référentiel. Global, en ajout seul : une correction passe par une nouvelle version du pack. Un point automatique s’appuie sur une ligne de décision de la même version ; un point manuel est déclaré par une personne. BASE TABLE
app.evaluation_point 11 Évaluation d’un point de contrôle par une organisation : résultat d’une règle ou déclaration d’une personne. En ajout seul ; la plus récente fait foi (vue evaluation_point_en_vigueur). Le statut « non applicable » d’origine structurelle et « preuve manquante » se calculent dans le domaine. BASE TABLE
app.evaluation_point_en_vigueur 11 Évaluation en vigueur (la plus récente) de chaque point de contrôle, par objet évalué. Applique la RLS de l’appelant. VIEW
app.preuve 9 Pièce justificative : lien entre un fichier déposé, un point de contrôle et, facultativement, l’objet et l’évaluation qu’elle appuie. En ajout seul : une pièce n’est jamais détachée, le fichier lui-même est figé. BASE TABLE
app.noeud_activite 14 Arbre d’activités d’une organisation (AM-04) : les nœuds du pack filière choisi, copiés à l’ouverture du compte. Le type vient du catalogue fermé app.activite ; l’instance (chemin, libellé, caractère obligatoire ou critique) vient du pack. L’état actif ou non se lit dans organisation_activite. BASE TABLE
app.perimetre_module_agent 7 Périmètre des agents par module : une ligne par agent affecté (affectation en cours), par groupement et par module actif. Donnée dérivée, recalculée par déclencheur ; les règles de synchronisation la lisent pour ne descendre que les données des modules actifs (AM-05). BASE TABLE
app.lot 18 Lot : objet central de la traçabilité (cahier, entité Lot ; AM-03). Un lot a un compte de matière de nature lot et un mouvement de création obligatoire (I4). Sa quantité n’est pas stockée : elle se lit dans app.lot_solde. BASE TABLE
app.deplacement_lot 9 Déplacement d’un lot entre emplacements, en ajout seul. Le premier déplacement (départ vide) place le lot. Aucun gramme ne change de compte : ce n’est pas un mouvement de matière (RG-154). BASE TABLE
app.lot_emplacement_courant 3 Emplacement courant de chaque lot placé : celui de son dernier déplacement. VIEW
app.lot_solde 5 Quantités d’un lot, calculées depuis le compte du lot : total entré et disponible (solde), en grammes. VIEW
app.stock_emplacement 5 Stock de chaque emplacement : nombre de lots présents et somme de leurs quantités disponibles, en grammes, avec la capacité déclarée. VIEW
app.reception 22 Réception de matière (cahier §5.13, RG-170, RG-171). Rattache le lot créé (I4) à ses pesées, ses mesures et au calcul du poids net marchand, figé tel qu’imprimé. Seule la décision (validée ou rejetée) et la dérogation changent. BASE TABLE
app.reception_pesee 7 Pesée d’une réception, en ajout seul : poids brut et tare saisis séparément, le net est calculé et jamais saisi (RG-170). Plusieurs pesées s’additionnent (double pesée). BASE TABLE
app.reception_mesure 6 Mesures de qualité d’une réception, en ajout seul : humidité et impuretés, en centièmes de point. Une seule ligne par réception ; chaque valeur est facultative. BASE TABLE
app.quarantaine_lot 8 Mise en quarantaine et levée de quarantaine d’un lot, en ajout seul (RG-161, RG-172, I5). Le statut du lot suit l’événement ; la levée exige un motif. BASE TABLE
app.ordre_transformation 12 Ordre de transformation : instance d’un procédé (nœud de l’arbre d’activités) sur un site, pour une campagne (AM-12). Ses étapes sont celles du procédé. Seul son statut avance. BASE TABLE
app.etape_realisee 13 Étape réalisée d’un ordre de transformation : statut, mesures, auteur (AM-12). Chaque mouvement de l’étape la référence. Validée, elle ne change plus. BASE TABLE
app.sortie_etape 13 Sortie typée d’une étape : produit principal, coproduit, sous-produit (chacun un lot), rebut (avec sa destination) ou perte (avec son motif), en ajout seul (AM-12, RG-055). BASE TABLE
app.bilan_etape 8 Bilan matière d’une étape lu dans la partie double (EC-015) : entrées de matière et d’intrants, sorties en lots, rebuts purgés, pertes comptabilisées. Les pertes déclarées se comparent dans sortie_etape. VIEW
app.lot_qualite 7 Qualité d’un lot pour un référentiel (RG-152, AM-16) : un état du référentiel, en ajout seul. Base de l’intersection du mélange et du déclassement (RG-153). Un lot sans ligne n’a aucun référentiel : la qualité n’est jamais déduite (EC-120). BASE TABLE
app.vehicule 8 Véhicule de transport (analyse critique, « Vente et Expédition »). Porte les PV de nettoyage ; l’immatriculation est unique par organisation. BASE TABLE
app.pv_nettoyage_vehicule 8 PV de nettoyage d’un véhicule avant chargement (cahier §7), en ajout seul : date réelle du nettoyage, responsable, points de contrôle cochés, signataire. BASE TABLE
app.vente 17 Vente : engagement commercial envers un client (analyse critique, « Vente et Expédition », RG-080, RG-156). Sans contrat, la vente est « spot ». Figée dès sa validation ; le stock ne bouge qu’à la validation d’une expédition. BASE TABLE
app.expedition 19 Expédition : mouvement physique d’une vente (analyse critique, « Vente et Expédition »). Sa validation débite les lots expédiés (I1). Figée à l’enregistrement, seule la décision change. BASE TABLE
app.expedition_ligne 6 Ligne d’expédition (RG-080) : un lot validé et la quantité prélevée, en ajout seul. Ces lignes désignent la matière vendue. BASE TABLE
app.vente_solde 5 Solde d’une vente : quantité engagée, quantité expédiée (expéditions validées) et reste, calculés. VIEW
app.registre_annulation 12 Registre des dévalidations et des annulations (RG-032, RG-143, RG-144), en ajout seul : quel objet, quelle nature, quel motif, quel mouvement contre-passant, qui et quand. Fait partie du dossier d’audit. BASE TABLE
app.arete_filiation 3 Filiation des lots (RG-160) : un lot parent a servi à créer un lot enfant, par un mouvement de transformation non contre-passé. Dérivée des mouvements, rien n’est stocké en double. VIEW
app.livraison_client 6 Livraisons des lots aux clients (RG-160) : une ligne d’expédition validée, avec la vente et le client. Une expédition dévalidée n’y figure plus. VIEW
app.rappel 19 Rappel d’un lot (RG-160 à RG-163) : plan figé (lots à bloquer, lots d’origine, clients et quantités), clôture motivée, information de l’organisme certificateur. Une simulation (RG-163) archive son résultat sans rien modifier d’autre. BASE TABLE
app.rappel_evenement 7 Chronologie d’un rappel (RG-162), en ajout seul : ouverture, lots mis en quarantaine, organisme informé, clôture. BASE TABLE
app.dossier_audit 12 Dossier d’audit d’une campagne (RG-130), produit en une action par une tâche de fond (pg-boss) : preuves, constats, bilan, écarts ; jamais un verdict (AM-01). BASE TABLE
app.licence 8 Licence d’une organisation : modules ouverts pour une période. Ajout seul, la plus récente fait foi. Informative : rien n’est bloqué à l’échéance (EC-129). BASE TABLE
app.facture 12 Référence d’une facture émise par l’outil de facturation externe, saisie à la main. BioTrace ne facture pas (EC-129). BASE TABLE
app.action_equipe 7 Journal des actions du super administrateur sur les comptes de l’équipe BioTrace (création, rôle, désactivation, réinitialisation). Ajout seul, hors organisation. Les comptes eux-mêmes vivent dans Keycloak. BASE TABLE
app.organisation_onboarding 8 Parcours d’onboarding de la première connexion, en ajout seul : une ligne par étape (reporté, terminé, rouvert), avec les réponses, le pack chargé, l’auteur et la date. L’état courant est la ligne la plus récente. BASE TABLE
erDiagram

"app.organisation" }o--o| "app.fichier" : "FOREIGN KEY (logo_fichier_id) REFERENCES app.fichier(id)"
"app.utilisateur" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.ensemble_permissions" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.attribution_permissions" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, attribue_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.attribution_permissions" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, utilisateur_id) REFERENCES app.utilisateur(organisation_id, id)"
"app.attribution_permissions" }o--|| "app.ensemble_permissions" : "FOREIGN KEY (organisation_id, ensemble_id) REFERENCES app.ensemble_permissions(organisation_id, id)"
"app.appareil" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, revoque_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.appareil" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, utilisateur_id) REFERENCES app.utilisateur(organisation_id, id)"
"app.code_enrolement" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.code_enrolement" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, utilisateur_id) REFERENCES app.utilisateur(organisation_id, id)"
"app.code_enrolement" }o--|| "app.appareil" : "FOREIGN KEY (organisation_id, appareil_id) REFERENCES app.appareil(organisation_id, id)"
"app.fichier" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.fichier" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, depose_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.fichier" }o--|| "app.appareil" : "FOREIGN KEY (organisation_id, appareil_id) REFERENCES app.appareil(organisation_id, id)"
"app.parametre" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.parametre" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.pack_version" }o--|| "app.pack" : "FOREIGN KEY (pack_id) REFERENCES app.pack(id)"
"app.compte_matiere" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.mouvement" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.mouvement" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.mouvement" }o--|| "app.mouvement" : "FOREIGN KEY (organisation_id, contrepasse_mouvement_id) REFERENCES app.mouvement(organisation_id, id)"
"app.mouvement" }o--|| "app.etape_realisee" : "FOREIGN KEY (organisation_id, etape_realisee_id) REFERENCES app.etape_realisee(organisation_id, id)"
"app.mouvement_ligne" }o--|| "app.compte_matiere" : "FOREIGN KEY (organisation_id, compte_matiere_id) REFERENCES app.compte_matiere(organisation_id, id)"
"app.mouvement_ligne" }o--|| "app.mouvement" : "FOREIGN KEY (organisation_id, mouvement_id) REFERENCES app.mouvement(organisation_id, id)"
"app.mouvement_ligne" }o--|| "app.produit" : "FOREIGN KEY (organisation_id, produit_id) REFERENCES app.produit(organisation_id, id)"
"app.validation" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, auteur_objet_id) REFERENCES app.utilisateur(organisation_id, id)"
"app.validation" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, validateur_id) REFERENCES app.utilisateur(organisation_id, id)"
"app.validation" }o--|| "app.capacite_version" : "FOREIGN KEY (organisation_id, capacite_version_id) REFERENCES app.capacite_version(organisation_id, id)"
"sync.commande" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, utilisateur_id) REFERENCES app.utilisateur(organisation_id, id)"
"sync.commande" }o--|| "app.appareil" : "FOREIGN KEY (organisation_id, appareil_id) REFERENCES app.appareil(organisation_id, id)"
"sync.commande_resultat" }o--|| "app.appareil" : "FOREIGN KEY (organisation_id, appareil_id) REFERENCES app.appareil(organisation_id, id)"
"sync.commande_resultat" }o--|| "sync.commande" : "FOREIGN KEY (organisation_id, id) REFERENCES sync.commande(organisation_id, id)"
"sync.controle" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, decide_par) REFERENCES app.utilisateur(organisation_id, id)"
"sync.controle" }o--|| "sync.commande" : "FOREIGN KEY (organisation_id, commande_id) REFERENCES sync.commande(organisation_id, id)"
"sync.controle" }o--|| "app.producteur" : "FOREIGN KEY (organisation_id, producteur_id) REFERENCES app.producteur(organisation_id, id)"
"sync.controle" }o--|| "app.parcelle_version" : "FOREIGN KEY (organisation_id, parcelle_version_id) REFERENCES app.parcelle_version(organisation_id, id)"
"sync.rejet" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.site" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.emplacement" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.emplacement" }o--|| "app.site" : "FOREIGN KEY (organisation_id, site_id) REFERENCES app.site(organisation_id, id)"
"app.campagne" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.campagne" }o--|| "app.pack_version" : "FOREIGN KEY (pack_version_id) REFERENCES app.pack_version(id)"
"app.campagne" }o--|| "app.modele_numerotation" : "FOREIGN KEY (organisation_id, modele_numerotation_version) REFERENCES app.modele_numerotation(organisation_id, version)"
"app.appel_administration" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.appel_administration" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, auteur_id) REFERENCES app.utilisateur(organisation_id, id)"
"app.appel_administration" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cible_utilisateur_id) REFERENCES app.utilisateur(organisation_id, id)"
"app.appel_administration" }o--|| "app.appareil" : "FOREIGN KEY (organisation_id, cible_appareil_id) REFERENCES app.appareil(organisation_id, id)"
"app.facteur_valeur" }o--o| "app.pack" : "FOREIGN KEY (pack_id) REFERENCES app.pack(id)"
"app.facteur_valeur" }o--|| "app.facteur" : "FOREIGN KEY (facteur_code) REFERENCES app.facteur(code)"
"app.produit" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.produit" }o--o| "app.pack_version" : "FOREIGN KEY (pack_version_id) REFERENCES app.pack_version(id)"
"app.produit" }o--|| "app.unite" : "FOREIGN KEY (unite_code) REFERENCES app.unite(code)"
"app.produit" }o--|| "app.produit" : "FOREIGN KEY (organisation_id, matiere_premiere_liee_id) REFERENCES app.produit(organisation_id, id)"
"app.attribut" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.attribut" }o--|| "app.produit" : "FOREIGN KEY (organisation_id, produit_id) REFERENCES app.produit(organisation_id, id)"
"app.modele_numerotation" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.modele_numerotation" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.sequence_numero" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.sequence_numero" }o--|| "app.campagne" : "FOREIGN KEY (organisation_id, campagne_id) REFERENCES app.campagne(organisation_id, id)"
"app.sequence_numero" }o--|| "app.produit" : "FOREIGN KEY (organisation_id, produit_id) REFERENCES app.produit(organisation_id, id)"
"app.groupement" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.producteur" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.producteur" }o--|| "app.fichier" : "FOREIGN KEY (organisation_id, photo_fichier_id) REFERENCES app.fichier(organisation_id, id)"
"app.producteur" }o--|| "app.fichier" : "FOREIGN KEY (organisation_id, piece_fichier_id) REFERENCES app.fichier(organisation_id, id)"
"app.producteur" }o--|| "app.groupement" : "FOREIGN KEY (organisation_id, groupement_id) REFERENCES app.groupement(organisation_id, id)"
"app.producteur" }o--|| "app.producteur" : "FOREIGN KEY (organisation_id, fusionnee_dans_id) REFERENCES app.producteur(organisation_id, id)"
"app.affectation_agent" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.affectation_agent" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, utilisateur_id) REFERENCES app.utilisateur(organisation_id, id)"
"app.affectation_agent" }o--|| "app.groupement" : "FOREIGN KEY (organisation_id, groupement_id) REFERENCES app.groupement(organisation_id, id)"
"app.fournisseur" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.client" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.parcelle" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.parcelle" }o--|| "app.facteur_valeur" : "FOREIGN KEY (mode_culture_facteur, mode_culture) REFERENCES app.facteur_valeur(facteur_code, code)"
"app.parcelle" }o--|| "app.facteur_valeur" : "FOREIGN KEY (region_facteur, region) REFERENCES app.facteur_valeur(facteur_code, code)"
"app.parcelle" }o--|| "app.facteur_valeur" : "FOREIGN KEY (type_sol_facteur, type_sol) REFERENCES app.facteur_valeur(facteur_code, code)"
"app.parcelle" }o--|| "app.produit" : "FOREIGN KEY (organisation_id, produit_id) REFERENCES app.produit(organisation_id, id)"
"app.parcelle" }o--|| "app.producteur" : "FOREIGN KEY (organisation_id, groupement_id, producteur_id) REFERENCES app.producteur(organisation_id, groupement_id, id)"
"app.zone_cueillette" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.zone_cueillette" }o--|| "app.produit" : "FOREIGN KEY (organisation_id, produit_id) REFERENCES app.produit(organisation_id, id)"
"app.zone_cueillette" }o--|| "app.groupement" : "FOREIGN KEY (organisation_id, groupement_id) REFERENCES app.groupement(organisation_id, id)"
"app.parcelle_version" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.parcelle_version" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.parcelle_version" }o--|| "app.fichier" : "FOREIGN KEY (organisation_id, trace_fichier_id) REFERENCES app.fichier(organisation_id, id)"
"app.parcelle_version" }o--|| "app.parcelle" : "FOREIGN KEY (organisation_id, groupement_id, parcelle_id) REFERENCES app.parcelle(organisation_id, groupement_id, id)"
"app.parcelle_version" }o--|| "app.zone_cueillette" : "FOREIGN KEY (organisation_id, groupement_id, zone_cueillette_id) REFERENCES app.zone_cueillette(organisation_id, groupement_id, id)"
"app.organisation_referentiel" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.organisation_referentiel" }o--|| "app.referentiel" : "FOREIGN KEY (code) REFERENCES app.referentiel(code)"
"app.certificat" }o--o| "app.organisation" : "FOREIGN KEY (operateur_id) REFERENCES app.organisation(id)"
"app.certificat" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.certificat" }o--|| "app.fichier" : "FOREIGN KEY (organisation_id, fichier_id) REFERENCES app.fichier(organisation_id, id)"
"app.certificat" }o--|| "app.groupement" : "FOREIGN KEY (organisation_id, groupement_id) REFERENCES app.groupement(organisation_id, id)"
"app.certificat" }o--|| "app.fournisseur" : "FOREIGN KEY (organisation_id, fournisseur_id) REFERENCES app.fournisseur(organisation_id, id)"
"app.certificat" }o--|| "app.organisation_referentiel" : "FOREIGN KEY (organisation_id, referentiel_id) REFERENCES app.organisation_referentiel(organisation_id, id)"
"app.statut_conformite" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.statut_conformite" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.statut_conformite" }o--|| "app.producteur" : "FOREIGN KEY (organisation_id, groupement_id, producteur_id) REFERENCES app.producteur(organisation_id, groupement_id, id)"
"app.statut_conformite" }o--|| "app.parcelle" : "FOREIGN KEY (organisation_id, groupement_id, parcelle_id) REFERENCES app.parcelle(organisation_id, groupement_id, id)"
"app.statut_conformite" }o--|| "app.zone_cueillette" : "FOREIGN KEY (organisation_id, groupement_id, zone_cueillette_id) REFERENCES app.zone_cueillette(organisation_id, groupement_id, id)"
"app.statut_conformite" }o--|| "app.organisation_referentiel" : "FOREIGN KEY (organisation_id, referentiel_id) REFERENCES app.organisation_referentiel(organisation_id, id)"
"app.contrat" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.contrat" }o--|| "app.fichier" : "FOREIGN KEY (organisation_id, fichier_id) REFERENCES app.fichier(organisation_id, id)"
"app.contrat" }o--|| "app.campagne" : "FOREIGN KEY (organisation_id, campagne_id) REFERENCES app.campagne(organisation_id, id)"
"app.contrat" }o--|| "app.groupement" : "FOREIGN KEY (organisation_id, groupement_id) REFERENCES app.groupement(organisation_id, id)"
"app.contrat" }o--|| "app.producteur" : "FOREIGN KEY (organisation_id, producteur_id) REFERENCES app.producteur(organisation_id, id)"
"app.contrat" }o--|| "app.fournisseur" : "FOREIGN KEY (organisation_id, fournisseur_id) REFERENCES app.fournisseur(organisation_id, id)"
"app.contrat" }o--|| "app.client" : "FOREIGN KEY (organisation_id, client_id) REFERENCES app.client(organisation_id, id)"
"app.contrat_ligne" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.contrat_ligne" }o--|| "app.unite" : "FOREIGN KEY (unite_prix) REFERENCES app.unite(code)"
"app.contrat_ligne" }o--|| "app.produit" : "FOREIGN KEY (organisation_id, produit_id) REFERENCES app.produit(organisation_id, id)"
"app.contrat_ligne" }o--|| "app.devise" : "FOREIGN KEY (devise) REFERENCES app.devise(code)"
"app.contrat_ligne" }o--|| "app.contrat" : "FOREIGN KEY (organisation_id, contrat_id) REFERENCES app.contrat(organisation_id, id)"
"app.prime" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.prime" }o--|| "app.contrat" : "FOREIGN KEY (organisation_id, contrat_id) REFERENCES app.contrat(organisation_id, id)"
"app.prime" }o--|| "app.contrat_ligne" : "FOREIGN KEY (organisation_id, contrat_ligne_id) REFERENCES app.contrat_ligne(organisation_id, id)"
"app.prime" }o--|| "app.contrat_ligne" : "FOREIGN KEY (organisation_id, contrat_ligne_id, devise) REFERENCES app.contrat_ligne(organisation_id, id, devise)"
"app.rendement_parcelle" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.rendement_parcelle" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.rendement_parcelle" }o--|| "app.parcelle" : "FOREIGN KEY (organisation_id, groupement_id, parcelle_id) REFERENCES app.parcelle(organisation_id, groupement_id, id)"
"app.rendement_parcelle" }o--|| "app.zone_cueillette" : "FOREIGN KEY (organisation_id, groupement_id, zone_cueillette_id) REFERENCES app.zone_cueillette(organisation_id, groupement_id, id)"
"app.capacite_version" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.capacite_version" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.capacite_version" }o--|| "app.campagne" : "FOREIGN KEY (organisation_id, campagne_id) REFERENCES app.campagne(organisation_id, id)"
"app.capacite_version" }o--|| "app.parcelle" : "FOREIGN KEY (organisation_id, groupement_id, parcelle_id) REFERENCES app.parcelle(organisation_id, groupement_id, id)"
"app.capacite_version" }o--|| "app.zone_cueillette" : "FOREIGN KEY (organisation_id, groupement_id, zone_cueillette_id) REFERENCES app.zone_cueillette(organisation_id, groupement_id, id)"
"app.capacite_version" }o--|| "app.parcelle_version" : "FOREIGN KEY (organisation_id, parcelle_version_id) REFERENCES app.parcelle_version(organisation_id, id)"
"app.solde_capacite" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.solde_capacite" }o--|| "app.campagne" : "FOREIGN KEY (organisation_id, campagne_id) REFERENCES app.campagne(organisation_id, id)"
"app.solde_capacite" }o--|| "app.parcelle" : "FOREIGN KEY (organisation_id, groupement_id, parcelle_id) REFERENCES app.parcelle(organisation_id, groupement_id, id)"
"app.solde_capacite" }o--|| "app.zone_cueillette" : "FOREIGN KEY (organisation_id, groupement_id, zone_cueillette_id) REFERENCES app.zone_cueillette(organisation_id, groupement_id, id)"
"app.solde_capacite" }o--|| "app.capacite_version" : "FOREIGN KEY (organisation_id, capacite_version_id) REFERENCES app.capacite_version(organisation_id, id)"
"app.activite" }o--o| "app.activite" : "FOREIGN KEY (parent_code) REFERENCES app.activite(code)"
"app.module_dependance" }o--|| "app.module" : "FOREIGN KEY (module_code) REFERENCES app.module(code)"
"app.module_dependance" }o--|| "app.module" : "FOREIGN KEY (requis_code) REFERENCES app.module(code)"
"app.module_activite" }o--|| "app.activite" : "FOREIGN KEY (activite_code) REFERENCES app.activite(code)"
"app.module_activite" }o--|| "app.module" : "FOREIGN KEY (module_code) REFERENCES app.module(code)"
"app.organisation_module" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.organisation_module" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.organisation_module" }o--|| "app.module" : "FOREIGN KEY (module_code) REFERENCES app.module(code)"
"app.organisation_activite" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.organisation_activite" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.organisation_activite" }o--|| "app.site" : "FOREIGN KEY (organisation_id, site_id) REFERENCES app.site(organisation_id, id)"
"app.organisation_activite" }o--|| "app.activite" : "FOREIGN KEY (activite_code) REFERENCES app.activite(code)"
"app.variable_dictionnaire" }o--o| "app.pack_version" : "FOREIGN KEY (pack_version_id) REFERENCES app.pack_version(id)"
"app.variable_dictionnaire" }o--o| "app.module" : "FOREIGN KEY (module_code) REFERENCES app.module(code)"
"app.regle_decision" }o--|| "app.pack_version" : "FOREIGN KEY (pack_version_id) REFERENCES app.pack_version(id)"
"app.constat" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.constat" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.constat" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, statut_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.constat" }o--o| "sync.controle" : "FOREIGN KEY (controle_id) REFERENCES sync.controle(id)"
"app.constat" }o--o| "app.regle_decision" : "FOREIGN KEY (regle_decision_id) REFERENCES app.regle_decision(id)"
"app.referentiel" }o--o| "app.pack" : "FOREIGN KEY (pack_id) REFERENCES app.pack(id)"
"app.certificat_portee" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.certificat_portee" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.certificat_portee" }o--|| "app.site" : "FOREIGN KEY (organisation_id, site_id) REFERENCES app.site(organisation_id, id)"
"app.certificat_portee" }o--|| "app.produit" : "FOREIGN KEY (organisation_id, produit_id) REFERENCES app.produit(organisation_id, id)"
"app.certificat_portee" }o--|| "app.producteur" : "FOREIGN KEY (organisation_id, producteur_id) REFERENCES app.producteur(organisation_id, id)"
"app.certificat_portee" }o--|| "app.parcelle" : "FOREIGN KEY (organisation_id, parcelle_id) REFERENCES app.parcelle(organisation_id, id)"
"app.certificat_portee" }o--|| "app.zone_cueillette" : "FOREIGN KEY (organisation_id, zone_cueillette_id) REFERENCES app.zone_cueillette(organisation_id, id)"
"app.certificat_portee" }o--|| "app.certificat" : "FOREIGN KEY (organisation_id, certificat_id) REFERENCES app.certificat(organisation_id, id)"
"app.certificat_portee" }o--o| "app.activite" : "FOREIGN KEY (activite_code) REFERENCES app.activite(code)"
"app.point_controle" }o--|| "app.pack_version" : "FOREIGN KEY (pack_version_id) REFERENCES app.pack_version(id)"
"app.point_controle" }o--o| "app.module" : "FOREIGN KEY (module_code) REFERENCES app.module(code)"
"app.point_controle" }o--|| "app.regle_decision" : "FOREIGN KEY (pack_version_id, regle_code) REFERENCES app.regle_decision(pack_version_id, code)"
"app.evaluation_point" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.evaluation_point" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, evalue_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.evaluation_point" }o--|| "app.constat" : "FOREIGN KEY (organisation_id, constat_id) REFERENCES app.constat(organisation_id, id)"
"app.evaluation_point" }o--|| "app.point_controle" : "FOREIGN KEY (point_controle_id) REFERENCES app.point_controle(id)"
"app.preuve" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.preuve" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, depose_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.preuve" }o--|| "app.fichier" : "FOREIGN KEY (organisation_id, fichier_id) REFERENCES app.fichier(organisation_id, id)"
"app.preuve" }o--|| "app.point_controle" : "FOREIGN KEY (point_controle_id) REFERENCES app.point_controle(id)"
"app.preuve" }o--|| "app.evaluation_point" : "FOREIGN KEY (organisation_id, evaluation_point_id) REFERENCES app.evaluation_point(organisation_id, id)"
"app.noeud_activite" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.noeud_activite" }o--|| "app.pack_version" : "FOREIGN KEY (pack_version_id) REFERENCES app.pack_version(id)"
"app.noeud_activite" }o--|| "app.activite" : "FOREIGN KEY (activite_code) REFERENCES app.activite(code)"
"app.perimetre_module_agent" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.perimetre_module_agent" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, utilisateur_id) REFERENCES app.utilisateur(organisation_id, id)"
"app.perimetre_module_agent" }o--|| "app.groupement" : "FOREIGN KEY (organisation_id, groupement_id) REFERENCES app.groupement(organisation_id, id)"
"app.perimetre_module_agent" }o--|| "app.module" : "FOREIGN KEY (module_code) REFERENCES app.module(code)"
"app.lot" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.lot" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, annule_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.lot" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.lot" }o--|| "app.compte_matiere" : "FOREIGN KEY (organisation_id, compte_matiere_id) REFERENCES app.compte_matiere(organisation_id, id)"
"app.lot" }o--|| "app.mouvement" : "FOREIGN KEY (organisation_id, mouvement_creation_id) REFERENCES app.mouvement(organisation_id, id)"
"app.lot" }o--|| "app.campagne" : "FOREIGN KEY (organisation_id, campagne_id) REFERENCES app.campagne(organisation_id, id)"
"app.lot" }o--|| "app.produit" : "FOREIGN KEY (organisation_id, produit_id) REFERENCES app.produit(organisation_id, id)"
"app.deplacement_lot" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.deplacement_lot" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.deplacement_lot" }o--|| "app.emplacement" : "FOREIGN KEY (organisation_id, de_emplacement_id) REFERENCES app.emplacement(organisation_id, id)"
"app.deplacement_lot" }o--|| "app.emplacement" : "FOREIGN KEY (organisation_id, emplacement_id) REFERENCES app.emplacement(organisation_id, id)"
"app.deplacement_lot" }o--|| "app.lot" : "FOREIGN KEY (organisation_id, lot_id) REFERENCES app.lot(organisation_id, id)"
"app.reception" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.reception" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.reception" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, decide_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.reception" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, derogation_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.reception" }o--|| "app.fichier" : "FOREIGN KEY (organisation_id, ticket_fichier_id) REFERENCES app.fichier(organisation_id, id)"
"app.reception" }o--|| "app.campagne" : "FOREIGN KEY (organisation_id, campagne_id) REFERENCES app.campagne(organisation_id, id)"
"app.reception" }o--|| "app.produit" : "FOREIGN KEY (organisation_id, produit_id) REFERENCES app.produit(organisation_id, id)"
"app.reception" }o--|| "app.producteur" : "FOREIGN KEY (organisation_id, producteur_id) REFERENCES app.producteur(organisation_id, id)"
"app.reception" }o--|| "app.fournisseur" : "FOREIGN KEY (organisation_id, fournisseur_id) REFERENCES app.fournisseur(organisation_id, id)"
"app.reception" }o--|| "app.contrat" : "FOREIGN KEY (organisation_id, contrat_id) REFERENCES app.contrat(organisation_id, id)"
"app.reception" }o--|| "app.lot" : "FOREIGN KEY (organisation_id, lot_id) REFERENCES app.lot(organisation_id, id)"
"app.reception_pesee" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.reception_pesee" }o--|| "app.reception" : "FOREIGN KEY (organisation_id, reception_id) REFERENCES app.reception(organisation_id, id)"
"app.reception_mesure" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.reception_mesure" }o--|| "app.reception" : "FOREIGN KEY (organisation_id, reception_id) REFERENCES app.reception(organisation_id, id)"
"app.quarantaine_lot" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.quarantaine_lot" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.quarantaine_lot" }o--|| "app.constat" : "FOREIGN KEY (organisation_id, constat_id) REFERENCES app.constat(organisation_id, id)"
"app.quarantaine_lot" }o--|| "app.lot" : "FOREIGN KEY (organisation_id, lot_id) REFERENCES app.lot(organisation_id, id)"
"app.ordre_transformation" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.ordre_transformation" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.ordre_transformation" }o--|| "app.site" : "FOREIGN KEY (organisation_id, site_id) REFERENCES app.site(organisation_id, id)"
"app.ordre_transformation" }o--|| "app.campagne" : "FOREIGN KEY (organisation_id, campagne_id) REFERENCES app.campagne(organisation_id, id)"
"app.ordre_transformation" }o--|| "app.noeud_activite" : "FOREIGN KEY (organisation_id, noeud_id) REFERENCES app.noeud_activite(organisation_id, id)"
"app.etape_realisee" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.etape_realisee" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.etape_realisee" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, valide_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.etape_realisee" }o--|| "app.noeud_activite" : "FOREIGN KEY (organisation_id, noeud_etape_id) REFERENCES app.noeud_activite(organisation_id, id)"
"app.etape_realisee" }o--|| "app.ordre_transformation" : "FOREIGN KEY (organisation_id, ordre_id) REFERENCES app.ordre_transformation(organisation_id, id)"
"app.sortie_etape" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.sortie_etape" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.sortie_etape" }o--|| "app.mouvement" : "FOREIGN KEY (organisation_id, mouvement_id) REFERENCES app.mouvement(organisation_id, id)"
"app.sortie_etape" }o--|| "app.produit" : "FOREIGN KEY (organisation_id, produit_id) REFERENCES app.produit(organisation_id, id)"
"app.sortie_etape" }o--|| "app.lot" : "FOREIGN KEY (organisation_id, lot_id) REFERENCES app.lot(organisation_id, id)"
"app.sortie_etape" }o--|| "app.etape_realisee" : "FOREIGN KEY (organisation_id, etape_realisee_id) REFERENCES app.etape_realisee(organisation_id, id)"
"app.lot_qualite" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.lot_qualite" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.lot_qualite" }o--|| "app.referentiel" : "FOREIGN KEY (referentiel_code) REFERENCES app.referentiel(code)"
"app.lot_qualite" }o--|| "app.lot" : "FOREIGN KEY (organisation_id, lot_id) REFERENCES app.lot(organisation_id, id)"
"app.vehicule" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.pv_nettoyage_vehicule" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.pv_nettoyage_vehicule" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, signataire_id) REFERENCES app.utilisateur(organisation_id, id)"
"app.pv_nettoyage_vehicule" }o--|| "app.vehicule" : "FOREIGN KEY (organisation_id, vehicule_id) REFERENCES app.vehicule(organisation_id, id)"
"app.vente" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.vente" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.vente" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, valide_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.vente" }o--|| "app.unite" : "FOREIGN KEY (unite_prix) REFERENCES app.unite(code)"
"app.vente" }o--|| "app.produit" : "FOREIGN KEY (organisation_id, produit_id) REFERENCES app.produit(organisation_id, id)"
"app.vente" }o--|| "app.client" : "FOREIGN KEY (organisation_id, client_id) REFERENCES app.client(organisation_id, id)"
"app.vente" }o--|| "app.devise" : "FOREIGN KEY (devise) REFERENCES app.devise(code)"
"app.vente" }o--|| "app.contrat" : "FOREIGN KEY (organisation_id, contrat_id) REFERENCES app.contrat(organisation_id, id)"
"app.vente" }o--o| "app.referentiel" : "FOREIGN KEY (referentiel_code) REFERENCES app.referentiel(code)"
"app.expedition" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.expedition" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.expedition" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, decide_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.expedition" }o--|| "app.mouvement" : "FOREIGN KEY (organisation_id, mouvement_id) REFERENCES app.mouvement(organisation_id, id)"
"app.expedition" }o--|| "app.vehicule" : "FOREIGN KEY (organisation_id, vehicule_id) REFERENCES app.vehicule(organisation_id, id)"
"app.expedition" }o--|| "app.pv_nettoyage_vehicule" : "FOREIGN KEY (organisation_id, pv_nettoyage_id, vehicule_id) REFERENCES app.pv_nettoyage_vehicule(organisation_id, id, vehicule_id)"
"app.expedition" }o--|| "app.vente" : "FOREIGN KEY (organisation_id, vente_id) REFERENCES app.vente(organisation_id, id)"
"app.expedition_ligne" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.expedition_ligne" }o--|| "app.lot" : "FOREIGN KEY (organisation_id, lot_id) REFERENCES app.lot(organisation_id, id)"
"app.expedition_ligne" }o--|| "app.expedition" : "FOREIGN KEY (organisation_id, expedition_id) REFERENCES app.expedition(organisation_id, id)"
"app.registre_annulation" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.registre_annulation" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.registre_annulation" }o--|| "app.mouvement" : "FOREIGN KEY (organisation_id, mouvement_id) REFERENCES app.mouvement(organisation_id, id)"
"app.registre_annulation" }o--|| "app.campagne" : "FOREIGN KEY (organisation_id, campagne_id) REFERENCES app.campagne(organisation_id, id)"
"app.registre_annulation" }o--|| "app.constat" : "FOREIGN KEY (organisation_id, constat_id) REFERENCES app.constat(organisation_id, id)"
"app.registre_annulation" }o--|| "app.lot" : "FOREIGN KEY (organisation_id, lot_id) REFERENCES app.lot(organisation_id, id)"
"app.rappel" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.rappel" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, clos_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.rappel" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.rappel" }o--|| "app.lot" : "FOREIGN KEY (organisation_id, lot_depart_id) REFERENCES app.lot(organisation_id, id)"
"app.rappel_evenement" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.rappel_evenement" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.rappel_evenement" }o--|| "app.rappel" : "FOREIGN KEY (organisation_id, rappel_id) REFERENCES app.rappel(organisation_id, id)"
"app.dossier_audit" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.dossier_audit" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, demande_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.dossier_audit" }o--|| "app.campagne" : "FOREIGN KEY (organisation_id, campagne_id) REFERENCES app.campagne(organisation_id, id)"
"app.licence" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.facture" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.facture" }o--|| "app.devise" : "FOREIGN KEY (devise) REFERENCES app.devise(code)"
"app.organisation_onboarding" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"
"app.organisation_onboarding" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.organisation_onboarding" }o--o| "app.pack_version" : "FOREIGN KEY (pack_version_id) REFERENCES app.pack_version(id)"

"journal.evenement" {
  uuid id "Identifiant de l'événement (UUID v7)."
  uuid organisation_id "Organisation concernée. Nulle pour les tables globales (packs, taux de change)."
  text table_nom "Table modifiée, préfixée de son schéma (ex. app.utilisateur)."
  uuid ligne_id "Identifiant de la ligne modifiée."
  text operation "Nature de l'écriture : INSERT, UPDATE ou DELETE."
  jsonb avant "Contenu de la ligne avant l'écriture, en JSON. Nul pour un INSERT."
  jsonb apres "Contenu de la ligne après l'écriture, en JSON. Nul pour un DELETE."
  uuid utilisateur_id "Auteur de l'écriture, lu dans la variable de transaction app.utilisateur_id."
  uuid appareil_id "Appareil d'origine si la commande vient du terrain (app.appareil_id)."
  uuid commande_id "Commande traitée par l'écriture, s'il y en a une (app.commande_id)."
  bigint transaction_id "Numéro de la transaction PostgreSQL, pour regrouper les événements d'une même opération."
  timestamp_with_time_zone horodatage "Date et heure exactes de l'écriture."
}
"app.organisation" {
  uuid id "Identifiant de l'organisation (UUID v7)."
  text code "Code court en majuscules. Immuable : il figure dans les identifiants Keycloak (coopa.agent01). « EXT » est réservé."
  text nom "Nom de l'organisation."
  text pays "Pays, code ISO 3166-1 alpha-2."
  text langue "Langue de l'organisation : fr ou en."
  text fuseau_horaire "Fuseau horaire IANA utilisé pour l'affichage des dates."
  text devise_defaut "Devise par défaut des montants, code ISO 4217."
  text keycloak_organisation_id "Identifiant de l'organisation dans Keycloak."
  text organisme_certificateur "Organisme de certification de l'organisation. Aucun contrôle de format (EC-027)."
  text code_operateur "Code d'opérateur attribué par l'organisme certificateur."
  uuid logo_fichier_id FK "Fichier du logo (EC-029)."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de la dernière modification, tenue par un déclencheur."
  text source_conformite "Source des décisions de conformité : `interne` (contrôle interne de BioTrace) ou `externe` (outil tiers). Une seule source active à la fois ; app.statut_conformite.source doit lui être égale."
  text statut "État du compte client : `active` ou `suspendue`. Une organisation suspendue est refusée par la garde d'authentification (`organisation_suspendue`) ; ses données et ses comptes sont conservés. Décidé par l'équipe de l'éditeur (EC-126)."
  timestamp_with_time_zone suspendue_le "Date de la suspension en cours ; nulle tant que l'organisation est active."
}
"app.utilisateur" {
  uuid id "Identifiant de l'utilisateur (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire de la ligne. Sert à l'isolation par RLS (EC-032) et au filtrage de la synchronisation."
  uuid keycloak_sub "Identifiant du compte Keycloak (revendication sub). Identique d'une organisation à l'autre pour un compte externe."
  text identifiant "Identifiant local de connexion, sans le code de l'organisation : 3 à 32 caractères parmi a-z, 0-9, _ et -. Unique dans l'organisation."
  text nom_affiche "Nom affiché dans l'application."
  text telephone "Numéro de téléphone. Attribut du compte, jamais son identifiant."
  text statut "Statut du compte : actif, suspendu ou desactive."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de la dernière modification, tenue par un déclencheur."
}
"app.ensemble_permissions" {
  uuid id "Identifiant de l'ensemble (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire de la ligne. Sert à l'isolation par RLS (EC-032) et au filtrage de la synchronisation."
  text code "Code de l'ensemble, unique dans l'organisation."
  text libelle "Libellé affiché."
  text__ permissions "Permissions accordées, validées par l'API contre le catalogue du code."
  boolean sensible "Vrai si l'ensemble impose le second facteur d'authentification."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de la dernière modification, tenue par un déclencheur."
}
"app.attribution_permissions" {
  uuid id "Identifiant de l'attribution (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire de la ligne. Sert à l'isolation par RLS (EC-032) et au filtrage de la synchronisation."
  uuid utilisateur_id FK "Utilisateur bénéficiaire."
  uuid ensemble_id FK "Ensemble de permissions attribué."
  timestamp_with_time_zone debut "Début de la période d'attribution."
  timestamp_with_time_zone fin "Fin de la période. Nulle si l'attribution est sans limite."
  uuid attribue_par FK "Utilisateur qui a attribué l'ensemble."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de la dernière modification, tenue par un déclencheur."
}
"app.appareil" {
  uuid id "Identifiant de l'appareil (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire de la ligne. Sert à l'isolation par RLS (EC-032) et au filtrage de la synchronisation."
  text code "Code court unique de l'appareil, utilisé dans les références provisoires (EC-009)."
  uuid utilisateur_id FK "Agent titulaire de l'appareil."
  text modele "Modèle du téléphone."
  boolean gnss_bifrequence "Vrai si le téléphone dispose d'un récepteur GNSS bifréquence (GPS-03, GPS-05)."
  text version_application "Dernière version de l'application terrain connue."
  text statut "Statut : actif, revoque ou remplace."
  timestamp_with_time_zone enrole_le "Date d'enrôlement."
  timestamp_with_time_zone revoque_le "Date de révocation. Nulle si l'appareil n'est pas révoqué."
  uuid revoque_par FK "Utilisateur qui a révoqué l'appareil."
  text motif_revocation "Motif de la révocation, obligatoire dès qu'elle a lieu."
  bigint derniere_sequence "Dernière séquence de commande traitée pour cet appareil. Ne diminue jamais."
  timestamp_with_time_zone derniere_synchro "Date de la dernière synchronisation."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de la dernière modification, tenue par un déclencheur."
}
"app.code_enrolement" {
  uuid id "Identifiant du code (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire de la ligne. Sert à l'isolation par RLS (EC-032) et au filtrage de la synchronisation."
  uuid utilisateur_id FK "Agent pour lequel le code a été généré."
  text empreinte_code "Empreinte SHA-256 du code, en hexadécimal."
  timestamp_with_time_zone expire_le "Date d'expiration du code (24 heures)."
  timestamp_with_time_zone utilise_le "Date d'utilisation. Nulle tant que le code n'a pas servi."
  uuid appareil_id FK "Appareil enrôlé avec ce code."
  uuid cree_par FK "Administrateur qui a généré le code. Différent de l'agent."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de la dernière modification, tenue par un déclencheur."
}
"app.fichier" {
  uuid id "Identifiant du fichier (UUID v7, généré sur l'appareil ou par l'API)."
  uuid organisation_id FK "Organisation propriétaire de la ligne. Sert à l'isolation par RLS (EC-032) et au filtrage de la synchronisation."
  text cle_objet "Clé de l'objet dans le stockage."
  text type_mime "Type MIME du contenu."
  bigint taille_octets "Taille en octets."
  text sha256 "Empreinte SHA-256 du contenu, en hexadécimal."
  text statut "Statut : attendu, recu ou empreinte_invalide. Un fichier reçu n'est plus modifiable."
  uuid appareil_id FK "Appareil d'origine du dépôt."
  uuid depose_par FK "Utilisateur qui a déposé le fichier."
  timestamp_with_time_zone depose_le "Date de réception du contenu par le stockage."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de la dernière modification, tenue par un déclencheur."
}
"app.parametre" {
  uuid id "Identifiant de la version (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire de la ligne. Sert à l'isolation par RLS (EC-032) et au filtrage de la synchronisation."
  text cle "Clé du paramètre, en notation pointée (ex. tolerance.rendement)."
  integer version "Numéro de version, à partir de 1."
  jsonb valeur "Valeur du paramètre, en JSON."
  text schema_ref "JSON Schema de la valeur, dans packages/schemas."
  timestamp_with_time_zone applicable_du "Date à partir de laquelle la version s'applique."
  text motif "Motif du changement."
  uuid cree_par FK "Utilisateur qui a créé la version."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
}
"app.pack" {
  uuid id "Identifiant du pack (UUID v7)."
  text code "Code du pack (ex. fruits)."
  text libelle "Libellé du pack."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de la dernière modification, tenue par un déclencheur."
  text type "Type de pack (AM-06) : filiere (produits, paramètres, arbre d'activités) ou referentiel (états de qualité, grilles, tables de décision)."
}
"app.pack_version" {
  uuid id "Identifiant de la version (UUID v7)."
  uuid pack_id FK "Pack concerné."
  text version "Version sémantique (majeure.mineure.correctif)."
  jsonb contenu "Contenu du pack, en JSON."
  text sha256 "Empreinte SHA-256 du contenu."
  boolean calibre "Vrai si les rendements du pack ont été calibrés (EC-007)."
  text statut "Statut : brouillon, publie ou retire."
  timestamp_with_time_zone publie_le "Date de publication. Nulle pour un brouillon."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de la dernière modification, tenue par un déclencheur."
  uuid publie_par "Identifiant Keycloak (sub) du membre de l'équipe de l'éditeur qui a publié la version depuis le cockpit. Nul pour les versions publiées par le pipeline ou la ligne de commande avant CP-5."
}
"app.taux_change" {
  uuid id "Identifiant du taux (UUID v7)."
  text devise_source "Devise de départ, code ISO 4217."
  text devise_cible "Devise d'arrivée, code ISO 4217."
  bigint numerateur "Numérateur du taux publié, en unités majeures : taux = numérateur / dénominateur unités de la devise cible pour une unité de la devise source."
  bigint denominateur "Dénominateur du taux publié, en unités majeures."
  date applicable_du "Date à partir de laquelle le taux s'applique."
  text source "Origine du taux."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
}
"app.compte_matiere" {
  uuid id "Identifiant du compte (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire de la ligne. Sert à l'isolation par RLS (EC-032) et au filtrage de la synchronisation."
  text nature "Nature du compte. Un seul compte technique par nature et par organisation ; les lots sont illimités."
  text libelle "Libellé du compte."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de la dernière modification, tenue par un déclencheur."
}
"app.mouvement" {
  uuid id "Identifiant du mouvement (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire de la ligne. Sert à l'isolation par RLS (EC-032) et au filtrage de la synchronisation."
  text nature "Nature : reception, transfert, transformation, expedition, perte, inventaire ou contrepassation."
  timestamp_with_time_zone date_operation "Date de l'opération sur le terrain ou à la balance."
  uuid contrepasse_mouvement_id FK "Mouvement annulé par cette contre-passation. Un mouvement n'est contre-passé qu'une fois."
  text motif "Motif, obligatoire pour une contre-passation."
  uuid commande_id "Commande de synchronisation à l'origine du mouvement."
  uuid cree_par FK "Utilisateur auteur du mouvement."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  uuid etape_realisee_id FK "Étape réalisée à laquelle se rattache un mouvement de transformation (AM-12) ; obligatoire pour ce type de mouvement, vide pour les autres."
}
"app.mouvement_ligne" {
  uuid id "Identifiant de la ligne (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire de la ligne. Sert à l'isolation par RLS (EC-032) et au filtrage de la synchronisation."
  uuid mouvement_id FK "Mouvement auquel appartient la ligne."
  uuid compte_matiere_id FK "Compte débité ou crédité."
  uuid produit_id FK "Produit concerné, de la même organisation (clé étrangère posée au Lot 1, séquence A2)."
  bigint quantite_g "Quantité en grammes, signée et non nulle."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
}
"app.v_solde_compte" {
  uuid organisation_id "Organisation propriétaire du compte."
  uuid compte_matiere_id "Compte de matière."
  uuid produit_id "Produit."
  numeric solde_g "Solde en grammes."
}
"app.validation" {
  uuid id "Identifiant de la validation (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire de la ligne. Sert à l'isolation par RLS (EC-032) et au filtrage de la synchronisation."
  text role_validation "Rôle sous lequel la validation est donnée."
  uuid validateur_id FK "Utilisateur qui valide. Différent de l'auteur de l'objet."
  uuid auteur_objet_id FK "Auteur de l'objet validé, renseigné par le déclencheur du lot, jamais par l'API."
  text decision "Décision : approuve ou refuse."
  text commentaire "Commentaire, obligatoire en cas de refus."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  uuid capacite_version_id FK "Capacité validée par cette ligne (de la même organisation). Une cible au plus ; une capacité se valide une fois par rôle, `direction` et `rsci`, par deux personnes différentes."
}
"sync.commande" {
  uuid id "Identifiant de la commande (UUID v7 généré sur l'appareil). Clé d'idempotence."
  uuid organisation_id FK "Organisation propriétaire de la ligne. Sert à l'isolation par RLS (EC-032) et au filtrage de la synchronisation."
  uuid appareil_id FK "Appareil émetteur."
  uuid utilisateur_id FK "Agent auteur."
  bigint sequence "Séquence locale de l'appareil, à partir de 1. Unique par appareil."
  text type "Type de commande (ex. collecte.enregistrer)."
  integer version_schema "Version du JSON Schema de la charge utile."
  timestamp_with_time_zone cree_le_appareil "Heure du téléphone à la saisie."
  timestamp_with_time_zone date_operation "Date d'opération retenue par le serveur, jamais postérieure à la réception (protocole §6.4)."
  timestamp_with_time_zone recue_le "Heure de réception par le serveur."
  text reference_provisoire "Référence provisoire du document de terrain (EC-009)."
  text empreinte "Empreinte SHA-256 de la commande canonique."
  jsonb enveloppe "Commande complète en JSON, telle que reçue."
}
"sync.commande_resultat" {
  uuid id FK "Identifiant de la commande concernée (PowerSync exige une colonne id)."
  uuid organisation_id FK "Organisation propriétaire de la ligne. Sert à l'isolation par RLS (EC-032) et au filtrage de la synchronisation."
  uuid appareil_id FK "Appareil destinataire."
  text issue "Issue : acceptee, acceptee_controle ou rejetee."
  text__ motifs "Motifs de contrôle ou de rejet. Vide si la commande est acceptée."
  text__ alertes "Alertes non bloquantes."
  text numero_officiel "Numéro officiel attribué par le serveur."
  jsonb entites "Entités créées, par identifiant, en JSON."
  timestamp_with_time_zone date_operation "Date d'opération retenue."
  timestamp_with_time_zone traitee_le "Date de traitement par le serveur."
}
"sync.controle" {
  uuid id "Identifiant du contrôle (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire de la ligne. Sert à l'isolation par RLS (EC-032) et au filtrage de la synchronisation."
  uuid commande_id FK "Commande à l'origine du contrôle."
  text motif "Motif du contrôle, parmi le catalogue du protocole (§9). « module_inactif » (EC-096) : commande d'un module inactif, conservée sans écriture métier."
  jsonb details "Éléments de contexte, en JSON."
  text statut "Statut : ouvert, en_cours ou leve."
  text decision "Décision de levée. Renseignée si et seulement si le contrôle est levé."
  text motif_decision "Motif de la décision."
  uuid decide_par FK "Utilisateur qui a levé le contrôle. Jamais l'auteur de la commande."
  timestamp_with_time_zone decide_le "Date de la levée."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de la dernière modification, tenue par un déclencheur."
  uuid producteur_id FK "Producteur concerné par le contrôle (de la même organisation), par exemple pour un doublon de pièce d'identité (AV1-02)."
  uuid parcelle_version_id FK "Version de polygone concernée par le contrôle (de la même organisation) : polygone invalide ou chevauchement."
}
"sync.rejet" {
  uuid id "Identifiant du rejet (UUID v7)."
  uuid organisation_id FK "Organisation du jeton qui a envoyé la commande. Sert à l'isolation par RLS (EC-032)."
  uuid commande_id "Identifiant de commande annoncé dans l'enveloppe reçue. Sans clé étrangère : il peut désigner une autre commande."
  uuid appareil_id "Appareil annoncé dans l'enveloppe reçue. Sans clé étrangère : il peut être incohérent avec le jeton."
  uuid utilisateur_id "Utilisateur annoncé dans l'enveloppe reçue. Sans clé étrangère : il peut être incohérent avec le jeton."
  bigint sequence "Séquence annoncée dans l'enveloppe reçue."
  text code "Code du rejet : identite_incoherente, identifiant_reutilise ou sequence_reutilisee."
  text empreinte_recue "Empreinte annoncée dans l'enveloppe reçue."
  jsonb details "Éléments de comparaison : valeurs attendues et reçues, empreinte de la commande déjà enregistrée."
  jsonb enveloppe "Enveloppe reçue, telle quelle."
  timestamp_with_time_zone recu_le "Heure de réception du rejet par le serveur."
}
"app.site" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  text code "Code du site, unique par organisation."
  text nom "Nom du site."
  boolean actif "Faux quand le site n'est plus utilisé. Un site n'est jamais supprimé."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.emplacement" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid site_id FK "Site qui porte l'emplacement (de la même organisation)."
  text code "Code de l'emplacement, unique dans son site."
  text nom "Nom de l'emplacement."
  text__ qualites_autorisees "Qualités biologiques que l'emplacement accepte. Vocabulaire à arrêter avec la certification (séquence A6, EC-055) ; non contrôlé pour l'instant."
  bigint capacite_g "Capacité de stockage en grammes (EC-030). Absente : non limitée."
  boolean actif "Faux quand l'emplacement n'est plus utilisé. Un emplacement n'est jamais supprimé."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.campagne" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  text code "Code de campagne inséré dans le numéro officiel. Unique par organisation, toutes années confondues (EC-031)."
  text nom "Nom de la campagne."
  date date_ouverture_prevue "Date d'ouverture prévue. Détermine « AA » tant que la campagne est en préparation."
  date date_cloture_prevue "Date de clôture prévue. Jamais avant la date d'ouverture prévue."
  date date_ouverture "Date d'ouverture réelle, renseignée à l'ouverture puis figée."
  date date_cloture "Date de clôture réelle, renseignée à la clôture."
  smallint annee "Année d'ouverture (réelle, sinon prévue), calculée. Ses deux derniers chiffres forment « AA » du numéro (EC-023)."
  uuid pack_version_id FK "Version du pack de la filière. Figée à l'ouverture, qui exige une version publiée."
  integer modele_numerotation_version FK "Version du modèle de numérotation de l'organisation. Obligatoire et figée à l'ouverture ; la clé vers le modèle est posée à la séquence A3."
  text statut "`preparation`, `ouverte` ou `cloturee`. Pas de retour en préparation."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.appel_administration" {
  uuid id "Identifiant de l'appel (UUID v7)."
  uuid organisation_id FK "Organisation concernée : l'organisation active, ou celle qui vient d'être créée. Sert à l'isolation par RLS (EC-032)."
  uuid auteur_sub "Identifiant Keycloak de l'auteur de l'appel."
  uuid auteur_id FK "Ligne app.utilisateur de l'auteur dans l'organisation. Nul pour un membre de l'équipe BioTrace qui crée une organisation."
  text action "Action demandée, ex. utilisateur.creer, utilisateur.desactiver, appareil.revoquer, organisation.creer."
  uuid cible_utilisateur_id FK "Utilisateur visé, de la même organisation."
  uuid cible_keycloak_sub "Identifiant Keycloak du compte visé, conservé même si la ligne app.utilisateur n'existe pas ou plus."
  uuid cible_appareil_id FK "Appareil visé, de la même organisation."
  text resultat "`reussi`, `refuse` (droit manquant, cible d'une autre organisation) ou `echec` (Keycloak ou base indisponible)."
  jsonb details "Éléments utiles à l'audit : motif, code d'erreur, ensemble de permissions. Jamais de mot de passe ni de jeton."
  timestamp_with_time_zone cree_le "Date de l'appel."
}
"app.facteur" {
  uuid id "Identifiant stable (UUID v7)."
  text code "Code du facteur, unique, cible des clés étrangères : produit, operation, pays, region, mode_culture, type_sol, referentiel, classe_altitude."
  text libelle_fr "Libellé en français."
  text libelle_en "Libellé en anglais."
  text nature "`liste` (valeurs fermées) ou `classes` (facteur numérique découpé en classes par le pack, PC-03)."
  text source "Origine des valeurs : `noyau` (livrées avec BioTrace), `pack` (déclarées par un pack), `dynamique` (tirées des tables de l'organisation : produits, référentiels)."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
}
"app.facteur_valeur" {
  uuid id "Identifiant stable (UUID v7), cible des clés étrangères des parcelles (séquence A5)."
  text facteur_code FK "Facteur dont la valeur fait partie."
  text code "Code de la valeur, unique dans son facteur : TG, lateritique, irrigue…"
  text libelle_fr "Libellé en français."
  text libelle_en "Libellé en anglais."
  uuid pack_id FK "Pack qui a déclaré la valeur. Nul pour une valeur du noyau."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
}
"app.unite" {
  uuid id "Identifiant stable (UUID v7)."
  text code "Code de l'unité : g, kg, t."
  text libelle_fr "Libellé en français."
  text libelle_en "Libellé en anglais."
  bigint grammes_par_unite "Nombre de grammes dans une unité (entier) : kg 1 000, t 1 000 000."
}
"app.produit" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  text code "Code du produit, inséré dans le numéro officiel (segment « produit »). Unique par organisation (EC-063)."
  text nom_fr "Nom en français."
  text nom_en "Nom en anglais."
  text unite_code FK "Unité de saisie et d'affichage."
  boolean est_matiere_premiere "Vrai pour une matière première, faux pour un produit fini."
  uuid matiere_premiere_liee_id FK "Matière première dont le produit fini est issu. Réservé aux produits finis, de la même organisation."
  text code_modele "Code du modèle de produit du pack qui a servi à créer la ligne. Nul pour un produit créé à la main."
  uuid pack_version_id FK "Version de pack d'origine. Nulle pour un produit créé à la main ; nulle exactement quand code_modele l'est."
  boolean actif "Faux quand le produit n'est plus utilisé. Un produit n'est jamais supprimé."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.attribut" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid produit_id FK "Produit décrit, de la même organisation."
  text code "Code de l'attribut, unique pour le produit : calibre, brix…"
  text libelle_fr "Libellé en français, affiché au-dessus du champ."
  text libelle_en "Libellé en anglais."
  jsonb schema "JSON Schema de la valeur de l'attribut (un objet). Entiers seulement pour les mesures (calculs en entiers)."
  boolean obligatoire "Vrai si la valeur doit être fournie à la saisie."
  integer ordre "Rang d'affichage dans le formulaire."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.modele_numerotation" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  integer version "Numéro de version, à partir de 1, unique par organisation. Une campagne ouverte en fige un."
  jsonb segments "Segments du numéro, dans l'ordre, au format de numeroOfficiel (packages/domain) : annee_campagne, code_campagne, nature, produit, prefixe_entreprise, texte, sequence. Exactement un segment de séquence."
  text motif "Raison de cette version."
  uuid cree_par FK "Utilisateur qui a publié la version. Absent pour la version créée avec l'organisation."
  timestamp_with_time_zone cree_le "Date de création de la version."
}
"app.sequence_numero" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid campagne_id FK "Campagne de la séquence (de la même organisation)."
  text nature "Nature du numéro : MP (matière première), REC (réception), NET (nettoyage), PF (produit fini). « Vente » n'existe plus (avenant n°1). Codes à confirmer, EC-068."
  uuid produit_id FK "Produit de la séquence (de la même organisation)."
  bigint derniere_valeur "Dernier rang attribué ; 0 tant qu'aucun numéro n'est sorti. Ne revient jamais en arrière."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.groupement" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  text code "Code du groupement, unique par organisation."
  text nom "Nom du groupement."
  boolean actif "Faux quand le groupement n'est plus utilisé. Un groupement n'est jamais supprimé."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.producteur" {
  uuid id "Identifiant stable (UUID v7), clé d'échange avec le système de contrôle interne."
  uuid organisation_id FK "Organisation qui enrôle le producteur. Sert à l'isolation par RLS (EC-032)."
  uuid groupement_id FK "Groupement d'appartenance (de la même organisation). Le téléphone filtre sur ce groupement."
  text code "Code du producteur, unique par organisation et définitif (RG-010, EC-031)."
  text nom_complet "Nom complet du producteur."
  text telephone "Téléphone. Donnée personnelle : accès réservé aux rôles qui en ont l'usage (EX-063)."
  text village "Village."
  text canton "Canton."
  text zone_production "Zone de production."
  date date_inscription "Date d'inscription du producteur, transmise au système de contrôle interne."
  text type_piece_identite "Type de la pièce d'identité (carte nationale, passeport…). Vocabulaire non contrôlé pour l'instant. Renseigné avec le numéro, ou absent avec lui."
  text numero_piece_identite "Numéro de la pièce d'identité, tel que saisi. Le doublon ne bloque rien : il ouvre un contrôle (AV1-02)."
  text numero_piece_normalise "Numéro de pièce en majuscules, sans espace ni tiret, calculé. Sert à rapprocher deux saisies du même numéro."
  uuid photo_fichier_id FK "Photo du producteur (fichier de la même organisation)."
  uuid piece_fichier_id FK "Image de la pièce d'identité (fichier de la même organisation)."
  bigint chiffre_affaires_valeur "Chiffre d'affaires annuel, en unités mineures de sa devise (EC-028). Sert à l'éligibilité au groupe (EC-014)."
  text chiffre_affaires_devise "Devise ISO 4217 du chiffre d'affaires. Renseignée avec la valeur, ou absente avec elle."
  boolean actif "Faux quand le producteur est retiré des listes de saisie sans altérer l'historique (RG-011). Toujours faux pour une fiche fusionnée."
  text statut "`actif` ou `fusionnee`. Une fiche fusionnée renvoie à la fiche retenue et ne se réactive pas."
  uuid fusionnee_dans_id FK "Fiche retenue quand celle-ci a été absorbée par une fusion. Renseignée si et seulement si le statut est `fusionnee`."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.affectation_agent" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid groupement_id FK "Groupement confié à l'agent (de la même organisation)."
  uuid utilisateur_id FK "Agent affecté (utilisateur de la même organisation)."
  date debut "Premier jour de l'affectation."
  date fin "Dernier jour de l'affectation. Absent : affectation en cours."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.fournisseur" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  text code "Code du fournisseur, unique par organisation."
  text nom "Nom du fournisseur."
  text pays "Pays, code ISO 3166-1 alpha-2."
  text ville "Ville."
  text adresse "Adresse postale."
  text telephone "Téléphone du fournisseur."
  text email "Adresse électronique du fournisseur."
  text contact "Personne à contacter."
  boolean actif "Faux quand le fournisseur n'est plus utilisé. Un fournisseur n'est jamais supprimé."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.client" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  text code "Code du client, unique par organisation."
  text nom "Nom du client."
  text pays "Pays, code ISO 3166-1 alpha-2."
  text adresse_livraison "Adresse de livraison habituelle."
  text contact "Personne à contacter."
  text telephone "Téléphone du client."
  text email "Adresse électronique du client."
  text incoterm_habituel "Incoterm 2020 habituel du client (EXW, FCA, CPT, CIP, DAP, DPU, DDP, FAS, FOB, CFR, CIF)."
  text devise "Devise de facturation, code ISO 4217 (EC-028)."
  text conditions_reglement "Conditions de règlement habituelles, en texte."
  boolean actif "Faux quand le client n'est plus utilisé. Un client n'est jamais supprimé."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.parcelle" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid groupement_id FK "Groupement de la parcelle, identique à celui de son producteur (clé composée) : un producteur qui a des parcelles ne change pas de groupement."
  uuid producteur_id FK "Producteur propriétaire de la parcelle (de la même organisation)."
  uuid produit_id FK "Produit cultivé (de la même organisation)."
  text code "Code de la parcelle, unique par organisation."
  text type_surface "`cultures`, `serres` ou `prairies` : sert aux seuils d'éligibilité au groupe (RG-024)."
  bigint surface_declaree_m2 "Surface déclarée par le producteur, en m² (EC-030). Jamais la base d'une capacité : c'est la surface mesurée (AV1-01)."
  text region_facteur FK "Constante `region` : nom du facteur de la clé étrangère composée vers facteur_valeur."
  text region FK "Région (valeur du facteur `region`), facultative."
  text mode_culture_facteur FK "Constante `mode_culture` : nom du facteur de la clé étrangère composée vers facteur_valeur."
  text mode_culture FK "Mode de culture (`pluvial` ou `irrigue`), facultatif."
  text type_sol_facteur FK "Constante `type_sol` : nom du facteur de la clé étrangère composée vers facteur_valeur."
  text type_sol FK "Type de sol (valeur du facteur `type_sol`), facultatif."
  boolean actif "Faux quand la parcelle n'est plus utilisée. Une parcelle n'est jamais supprimée."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.zone_cueillette" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid groupement_id FK "Groupement qui exploite la zone (de la même organisation)."
  uuid produit_id FK "Produit cueilli (de la même organisation)."
  text code "Code de la zone, unique par organisation."
  text nom "Nom de la zone."
  boolean actif "Faux quand la zone n'est plus utilisée. Une zone n'est jamais supprimée."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.parcelle_version" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid groupement_id FK "Groupement de la cible, repris d'elle quand il n'est pas donné. Le téléphone filtre dessus."
  uuid parcelle_id FK "Parcelle cible. Exactement une cible entre parcelle et zone de cueillette."
  uuid zone_cueillette_id FK "Zone de cueillette cible. Exactement une cible entre parcelle et zone de cueillette."
  integer version "Numéro de version de la cible, à partir de 1, attribué par la base quand il n'est pas donné. La plus élevée est la version en vigueur."
  geometry_Polygon_4326_ geom "Polygone en WGS 84 (SRID 4326). Peut être invalide : il est alors signalé, jamais réparé."
  jsonb geojson "Le même polygone en GeoJSON, tenu par la base : c'est ce que le téléphone reçoit."
  boolean valide "Résultat de ST_IsValid sur le polygone, calculé par la base."
  text motif_invalidite "Raison donnée par ST_IsValidReason quand le polygone est invalide, sinon vide."
  bigint surface_mesuree_m2 "Surface mesurée en m² (ST_Area en géographie, arrondie). Vide si le polygone est invalide. Base de toute capacité (AV1-01)."
  bigint perimetre_m "Périmètre en mètres (ST_Perimeter en géographie, arrondi). Vide si le polygone est invalide."
  text methode "Méthode de relevé (GPS-03) : `sommets`, `marche`, `dessin` ou `import` (EC-048)."
  integer precision_mediane_cm "Précision médiane déclarée du relevé, en centimètres (GPS-03). Pour un import, la précision lue dans le fichier."
  text appareil "Appareil ou récepteur utilisé pour le relevé (GPS-03), en texte libre."
  uuid trace_fichier_id FK "Trace brute du relevé (fichier de la même organisation), conservée pour l'audit (GPS-03)."
  bigint incertitude_m2 "Incertitude de la surface mesurée (GPS-04) : périmètre × précision médiane, calculée. Un écart avec la surface déclarée plus petit n'est pas une anomalie."
  uuid cree_par FK "Utilisateur qui a importé ou relevé la version."
  timestamp_with_time_zone cree_le "Date de création de la version : début de sa période de vigueur."
}
"app.parcelle_version_en_vigueur" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid groupement_id "Groupement de la cible, repris d'elle quand il n'est pas donné. Le téléphone filtre dessus."
  uuid parcelle_id "Parcelle cible. Exactement une cible entre parcelle et zone de cueillette."
  uuid zone_cueillette_id "Zone de cueillette cible. Exactement une cible entre parcelle et zone de cueillette."
  integer version "Numéro de version de la cible, à partir de 1, attribué par la base quand il n'est pas donné. La plus élevée est la version en vigueur."
  geometry_Polygon_4326_ geom "Polygone en WGS 84 (SRID 4326). Peut être invalide : il est alors signalé, jamais réparé."
  jsonb geojson "Le même polygone en GeoJSON, tenu par la base : c'est ce que le téléphone reçoit."
  boolean valide "Résultat de ST_IsValid sur le polygone, calculé par la base."
  text motif_invalidite "Raison donnée par ST_IsValidReason quand le polygone est invalide, sinon vide."
  bigint surface_mesuree_m2 "Surface mesurée en m² (ST_Area en géographie, arrondie). Vide si le polygone est invalide. Base de toute capacité (AV1-01)."
  bigint perimetre_m "Périmètre en mètres (ST_Perimeter en géographie, arrondi). Vide si le polygone est invalide."
  text methode "Méthode de relevé (GPS-03) : `sommets`, `marche`, `dessin` ou `import` (EC-048)."
  integer precision_mediane_cm "Précision médiane déclarée du relevé, en centimètres (GPS-03). Pour un import, la précision lue dans le fichier."
  text appareil "Appareil ou récepteur utilisé pour le relevé (GPS-03), en texte libre."
  uuid trace_fichier_id "Trace brute du relevé (fichier de la même organisation), conservée pour l'audit (GPS-03)."
  bigint incertitude_m2 "Incertitude de la surface mesurée (GPS-04) : périmètre × précision médiane, calculée. Un écart avec la surface déclarée plus petit n'est pas une anomalie."
  uuid cree_par "Utilisateur qui a importé ou relevé la version."
  timestamp_with_time_zone cree_le "Date de création de la version : début de sa période de vigueur."
}
"app.organisation_referentiel" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  text code FK "Code du référentiel coché, pris dans le catalogue global (clé étrangère), unique par organisation."
  text libelle_fr "Libellé en français, propre à l'organisation."
  text libelle_en "Libellé en anglais, propre à l'organisation."
  boolean actif "Faux quand l'organisation ne revendique plus ce référentiel. Une ligne n'est jamais supprimée."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.certificat" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation qui enregistre le certificat. Sert à l'isolation par RLS (EC-032)."
  uuid referentiel_id FK "Référentiel coché par l'organisation (app.organisation_referentiel) que le certificat atteste (EC-101)."
  uuid operateur_id FK "Titulaire opérateur : l'organisation elle-même. Exactement un titulaire entre opérateur, groupement et fournisseur (EC-025)."
  uuid groupement_id FK "Titulaire groupement (de la même organisation). Un certificat de groupe couvre ses membres."
  uuid fournisseur_id FK "Titulaire fournisseur (de la même organisation). Un fournisseur ne se crée pas sans son certificat (EC-053)."
  text organisme_nom "Nom de l'organisme certificateur."
  text organisme_code "Code de l'organisme certificateur, sans contrôle de format (EC-027)."
  text numero "Numéro du certificat, sans contrôle de format."
  date date_debut "Premier jour de validité, inclus."
  date date_fin "Dernier jour de validité, inclus. Jamais avant date_debut. L'expiration se calcule depuis cette date."
  text statut "`valide`, `suspendu` ou `retire`. Pas de valeur « expiré » : il se calcule. La date d'une suspension ou d'un retrait n'est pas stockée (EC-072) ; le journal d'audit en garde la trace."
  uuid fichier_id FK "PDF du certificat (fichier de la même organisation). Obligatoire pour un fournisseur (EC-053)."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.statut_conformite" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid groupement_id FK "Groupement de l'unité, repris d'elle par la base. Le téléphone filtre dessus."
  uuid producteur_id FK "Unité producteur. Exactement une unité entre producteur, parcelle et zone de cueillette (EC-025)."
  uuid parcelle_id FK "Unité parcelle."
  uuid zone_cueillette_id FK "Unité zone de cueillette."
  uuid referentiel_id FK "Référentiel coché par l'organisation (app.organisation_referentiel) pour lequel le statut vaut (EC-101)."
  text etat "État de conformité, défini par le pack référentiel (AM-16) : bio, conversion_annee_2_et_plus, conversion_annee_1, conventionnel, suspendu, retiré pour l'UE. Contrôlé par la base contre les états publiés."
  date debut_conversion "Début de la conversion. Obligatoire pour les deux états de conversion."
  date date_eligibilite "Date à laquelle l'unité devient éligible (fin de conversion prévue), facultative."
  date date_effet "Date d'effet du statut : il vaut à partir de ce jour, jusqu'à la ligne suivante."
  text source "`interne` ou `externe`. Doit égaler organisation.source_conformite."
  text reference_decision "Référence de la décision de la source (par exemple du contrôle interne)."
  text motif "Justification écrite. Absente pour la première ligne d'une unité, obligatoire pour toute révision (RG-150)."
  timestamp_with_time_zone mis_a_jour_le "Date de réception de la mise à jour par BioTrace. Départage deux lignes de même date d'effet."
  uuid cree_par FK "Utilisateur qui a enregistré ou révisé le statut."
}
"app.devise" {
  uuid id "Identifiant stable (UUID v7)."
  text code "Code ISO 4217 en majuscules (XOF, EUR, USD), cible des clés étrangères de montants."
  text libelle_fr "Libellé en français."
  text libelle_en "Libellé en anglais."
  smallint decimales "Nombre de décimales de la devise : l'unité mineure vaut 10^-décimales unité majeure (EUR 2, XOF 0)."
}
"app.contrat" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  text code "Code du contrat, unique par organisation."
  text type "`achat` (producteur, groupement ou fournisseur), `vente` (client) ou `prestation` (client ou fournisseur, EC-052)."
  uuid producteur_id FK "Contrepartie producteur (achat). Exactement une contrepartie entre producteur, groupement, fournisseur et client."
  uuid groupement_id FK "Contrepartie groupement (achat)."
  uuid fournisseur_id FK "Contrepartie fournisseur (achat ou prestation)."
  uuid client_id FK "Contrepartie client (vente ou prestation)."
  uuid campagne_id FK "Campagne du contrat (de la même organisation)."
  date date_debut "Premier jour de la période du contrat."
  date date_fin "Dernier jour de la période du contrat. Jamais avant le début."
  text statut "`brouillon`, `actif`, `solde` ou `resilie`. Pas de retour en arrière ; l'activation exige une ligne."
  text conditions_paiement "Conditions de paiement, en texte, facultatives."
  uuid fichier_id FK "Pièce jointe du contrat (fichier de la même organisation), facultative."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.contrat_ligne" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid contrat_id FK "Contrat de la ligne (de la même organisation)."
  uuid produit_id FK "Produit de la ligne (de la même organisation), unique dans le contrat."
  bigint quantite_engagee_g "Quantité engagée en grammes (EC-030), strictement positive."
  bigint prix_unite_mineure "Prix de base en unités mineures de la devise, par unité de prix (par exemple 150 XOF le kilo). Jamais sans devise."
  text devise FK "Devise du prix, code ISO 4217 connu de app.devise (EC-028)."
  text unite_prix FK "Unité du prix (`g`, `kg`, `t`) : le prix vaut par cette unité. Le stock reste en grammes (EC-030)."
  integer tolerance_cp "Tolérance sur la quantité engagée, en centièmes de point. L'alerte de solde au-delà est calculée au Lot 2."
  jsonb formule_refaction "Formule de réfaction en JSON (aucune, excedent_lineaire, lineaire_simple, bilan_de_masse), forme validée par FormuleRefaction (Ajv) ; le mode d'arrondi en fait partie (EC-051). Lue par calculerPesee à la réception."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.prime" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid contrat_ligne_id FK "Ligne de contrat concernée (de la même organisation)."
  text type "`bio`, `equitable` ou `qualite`."
  text mode "`montant` (par unité de prix de la ligne, en devise) ou `taux` (centièmes de point du prix de base)."
  bigint montant_unite_mineure "Montant par unité de prix, en unités mineures. Renseigné si et seulement si le mode est `montant`."
  text devise FK "Devise du montant, égale à celle de la ligne (clé composée). Renseignée si et seulement si le mode est `montant` : aucun montant sans devise."
  integer taux_cp "Taux en centièmes de point du prix de base. Renseigné si et seulement si le mode est `taux`."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
  uuid contrat_id FK "Contrat de la ligne, repris d'elle par la base (jamais une autre valeur). Dénormalisé pour que les règles de synchronisation filtrent les primes d'un contrat sans jointure (protocole §7.3)."
}
"app.rendement_parcelle" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid groupement_id FK "Groupement de la cible, repris d'elle par la base. Le téléphone filtre dessus."
  uuid parcelle_id FK "Parcelle cible. Exactement une cible entre parcelle et zone de cueillette."
  uuid zone_cueillette_id FK "Zone de cueillette cible."
  integer version "Numéro de version de la cible, à partir de 1, attribué par la base. La plus élevée est en vigueur."
  bigint valeur_g_ha "Rendement de référence en grammes par hectare (EC-030), strictement positif."
  text motif "Justification écrite de la valeur, obligatoire à chaque ligne (RG-023)."
  uuid cree_par FK "Utilisateur qui a fixé ou révisé le rendement."
  timestamp_with_time_zone cree_le "Date de création de la version."
}
"app.capacite_version" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid groupement_id FK "Groupement de la cible, repris d'elle par la base. Le téléphone filtre dessus."
  uuid parcelle_id FK "Parcelle cible. Exactement une cible entre parcelle et zone de cueillette."
  uuid zone_cueillette_id FK "Zone de cueillette cible (EC-050)."
  uuid campagne_id FK "Campagne de la capacité. Deux campagnes la même année portent chacune la leur."
  integer version "Numéro de version pour la cible et la campagne, attribué par la base."
  uuid parcelle_version_id FK "Version de polygone sur laquelle la capacité est calculée, posée par la base. Si le polygone change, la capacité n'est plus applicable (EC-012)."
  bigint surface_mesuree_m2 "Surface mesurée de cette version de polygone, en m², posée par la base. La surface déclarée n'entre jamais dans le calcul (AV1-01)."
  bigint rendement_g_ha "Rendement de référence en vigueur lors de la saisie, en g/ha, posé par la base."
  bigint capacite_g "Capacité en grammes, calculée : surface mesurée × rendement / 10 000, arrondie au plus proche (RG-020). Seule formule du projet pour la capacité."
  uuid cree_par FK "Auteur de la proposition : il ne peut pas la valider (SB-11)."
  timestamp_with_time_zone cree_le "Date de création de la version."
}
"app.solde_capacite" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid groupement_id FK "Groupement de la cible. Le téléphone filtre dessus."
  uuid parcelle_id FK "Parcelle du solde. Exactement une cible entre parcelle et zone de cueillette."
  uuid zone_cueillette_id FK "Zone de cueillette du solde."
  uuid campagne_id FK "Campagne du solde."
  uuid capacite_version_id FK "Version de capacité validée qui fonde le solde."
  bigint capacite_g "Capacité validée, en grammes."
  bigint consomme_g "Quantité déjà collectée, en grammes. Jamais écrasée par une nouvelle capacité."
  bigint reste_g "Capacité moins consommé, en grammes. Négatif en cas de dépassement : un dépassement n'est jamais masqué (RG-022)."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.activite" {
  uuid id "Identifiant stable (UUID v7)."
  text code "Code stable du type, cité par les packs, les grilles et les modules."
  smallint niveau "Niveau dans l'arbre : 1 activité, 2 sous-activité, 3 étape. Trois niveaux au plus."
  text parent_code FK "Type parent indicatif du catalogue (affichage) ; non vérifié contre l'arbre réel d'une organisation (app.noeud_activite), où seul le niveau du type doit correspondre à la profondeur du chemin."
  text regroupement "Regroupement affiché à l'écran : les cinq activités élémentaires du cahier v1.2, §2.2."
  text libelle_fr "Libellé en français."
  text libelle_en "Libellé en anglais."
  smallint ordre "Ordre d'affichage parmi les types du même parent."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification (par migration seulement)."
  boolean provisoire "Vrai pour un type de niveau 2 ou 3 posé avant la validation du catalogue avec l'organisme de certification (Q1, EC-108). Le code reste stable ; le libellé et le rangement peuvent changer."
}
"app.module" {
  uuid id "Identifiant stable (UUID v7)."
  text code "Code du module ; préfixe de ses permissions (EC-092)."
  text libelle_fr "Libellé en français."
  text libelle_en "Libellé en anglais."
  boolean toujours_actif "Vrai pour le noyau : il ne s'active ni ne se désactive."
  smallint ordre "Ordre d'affichage."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification (par migration seulement)."
}
"app.module_dependance" {
  uuid id "Identifiant stable (UUID v7)."
  text module_code FK "Module qui dépend."
  text requis_code FK "Module requis."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification (par migration seulement)."
}
"app.module_activite" {
  uuid id "Identifiant stable (UUID v7)."
  text module_code FK "Module proposé."
  text activite_code FK "Activité déclarée qui le propose."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification (par migration seulement)."
}
"app.organisation_module" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation concernée. Sert à l'isolation par RLS (EC-032)."
  text module_code FK "Module activé ou désactivé ; jamais le noyau."
  text etat "Nouvel état : actif ou inactif. Un changement sans effet est refusé (etat_inchange)."
  text motif "Motif du changement, obligatoire."
  uuid cree_par FK "Utilisateur qui a fait le changement."
  timestamp_with_time_zone cree_le "Horodatage du changement, qui vaut date d'effet."
}
"app.module_en_vigueur" {
  uuid organisation_id "Organisation."
  text module_code "Module."
  text etat "État courant."
  text motif "Motif du dernier changement."
  uuid cree_par "Auteur du dernier changement."
  timestamp_with_time_zone cree_le "Date du dernier changement."
}
"app.organisation_activite" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation concernée. Sert à l'isolation par RLS (EC-032)."
  uuid site_id FK "Site concerné ; vide pour toute l'organisation."
  text noeud "Nœud de l'arbre d'activités : code d'activité au premier niveau, chemin pointé du pack (noeud_activite.chemin) aux niveaux 2 et 3."
  text activite_code FK "Type du nœud, pris dans le catalogue fermé."
  text etat "Nouvel état : active ou désactivée. Un changement sans effet est refusé (etat_inchange)."
  text motif "Motif du changement, obligatoire."
  uuid cree_par FK "Utilisateur qui a fait le changement."
  timestamp_with_time_zone cree_le "Horodatage du changement, qui vaut date d'effet."
}
"app.activite_en_vigueur" {
  uuid organisation_id "Organisation."
  uuid site_id "Site, ou vide pour toute l'organisation."
  text noeud "Nœud."
  text activite_code "Type du nœud."
  text etat "État courant."
  text motif "Motif du dernier changement."
  uuid cree_par "Auteur du dernier changement."
  timestamp_with_time_zone cree_le "Date du dernier changement."
}
"app.variable_dictionnaire" {
  uuid id "Identifiant stable (UUID v7)."
  text nom "Nom pointé de la variable, lu par les conditions (reception.quantite_g)."
  text type "Type : entier, texte, booléen, liste de textes, table texte → texte. Jamais de décimal."
  text unite "Unité de la valeur (g, cp…) ; vide pour un texte ou un booléen."
  text origine "Origine : noyau (code), pack (mesure déclarée par une étape), indicateur (calcul du noyau)."
  boolean hors_ligne "Vrai si la variable est disponible sur le téléphone ; sinon la ligne qui la lit est évaluée au serveur (AM-10)."
  text module_code FK "Module qui déclare la variable ; exclusif de pack_version_id."
  uuid pack_version_id FK "Version de pack qui déclare la variable ; exclusif de module_code."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification (par migration seulement)."
}
"app.regle_decision" {
  uuid id "Identifiant stable (UUID v7)."
  uuid pack_version_id FK "Version du pack référentiel qui publie la ligne."
  text code "Identifiant de la ligne dans le pack, stable d'une version à l'autre."
  text accroche "Point d'accroche où la ligne est évaluée (reception.avant_validation…), déclaré par un manifeste de module."
  jsonb contexte "Facteurs cités et leurs valeurs acceptées (produit, référentiel…) ; {} : tous les contextes."
  text condition "Condition CEL booléenne sur les variables du dictionnaire ; les seuils passent par param('clé'), jamais en dur."
  text__ parametres "Clés de paramètres citées par la condition, résolues par le moteur de paramètres."
  text issue "Issue d'une condition vraie, parmi six, de la plus faible à la plus forte : signaler, exiger une note, exiger une pièce, créer une non-conformité, mettre en quarantaine, bloquer l'action."
  text source "Texte, article ou règle d'où vient la ligne. Obligatoire (PC-06)."
  date date_effet "Date d'effet, incluse (AM-11)."
  date date_fin "Date de fin, exclue ; vide si la ligne reste en vigueur."
  text date_reference "Date de l'objet comparée à la période d'effet : opération, récolte, création du lot ou émission du certificat (AM-11)."
  text evaluation "locale (téléphone et serveur) ou serveur (données absentes du téléphone ; issue connue à la synchronisation, AM-10)."
  jsonb mesure "Pour un écart chiffré : variable constatée et paramètre attendu (EC-098)."
  timestamp_with_time_zone cree_le "Date de publication de la ligne."
}
"app.constat" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation concernée. Sert à l'isolation par RLS (EC-032)."
  uuid regle_decision_id FK "Ligne de décision évaluée."
  text regle_code "Code de la ligne, recopié pour la lecture (instantané, PC-08)."
  text version "Version du pack de la ligne, recopiée (instantané, PC-08)."
  text source "Source de la ligne, recopiée (instantané, PC-08)."
  text accroche "Point d'accroche de l'évaluation."
  text objet_type "Nature de l'objet concerné (reception, lot, collecte…)."
  uuid objet_id "Identifiant de l'objet concerné."
  text resultat "ecart (condition vraie) ou a_verifier (variable, paramètre ou date absents, erreur d'évaluation : jamais de valeur par défaut, PC-07)."
  text issue "Issue de la ligne pour un écart ; vide pour « à vérifier »."
  text motif "Raison d'un « à vérifier » ; vide pour un écart."
  jsonb valeurs "Valeurs lues par la ligne et paramètres utilisés, au moment de l'évaluation."
  bigint constatee "Valeur constatée, si la ligne déclare une mesure (entier, unité de la variable)."
  bigint attendue "Valeur attendue, si la ligne déclare une mesure."
  bigint ecart "Écart constatée − attendue, si la ligne déclare une mesure."
  uuid controle_id FK "Entrée de la file de contrôle liée, quand l'issue met une commande en contrôle (EC-091)."
  text statut "ouvert, justifie (note exigée), leve, clos (final)."
  text note "Justification écrite (RG-308), obligatoire pour « justifié »."
  uuid statut_par FK "Auteur du dernier changement de statut."
  timestamp_with_time_zone statut_le "Date du dernier changement de statut, posée par la base."
  uuid cree_par FK "Utilisateur à l'origine de l'évaluation ; vide pour une évaluation du serveur."
  timestamp_with_time_zone cree_le "Date du constat."
  timestamp_with_time_zone modifie_le "Date de la dernière modification, tenue par un déclencheur."
  text origine "Origine de la règle : pack (ligne d'une table de décision, regle_decision_id renseigné) ou processus (règle codée comme RG-014 ou RG-172, sans ligne de décision)."
}
"app.referentiel" {
  uuid id "Identifiant stable (UUID v7)."
  text code "Code du référentiel (UE, NOP, CCPB…), valeur du facteur `referentiel`."
  text nature "Nature : norme publique, organisme de certification, standard privé ; vide tant que le pack n'est pas publié."
  text libelle_fr "Libellé en français."
  text libelle_en "Libellé en anglais."
  uuid pack_id FK "Pack référentiel qui porte les états, les grilles et les tables de décision ; vide tant qu'aucun pack n'est publié."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.certificat_portee" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid certificat_id FK "Certificat dont la ligne précise la portée (de la même organisation)."
  text dimension "`activite`, `produit`, `site` ou `unite`. Détermine la seule colonne d'élément renseignée."
  text activite_code FK "Activité du catalogue fermé (dimension `activite`)."
  uuid produit_id FK "Produit du catalogue de l'organisation (dimension `produit`). Les produits sont listés un à un : le catalogue n'a pas de catégorie (EC-102)."
  uuid site_id FK "Site de l'organisation (dimension `site`)."
  uuid producteur_id FK "Unité producteur (dimension `unite`). Exactement une unité entre producteur, parcelle et zone de cueillette."
  uuid parcelle_id FK "Unité parcelle (dimension `unite`)."
  uuid zone_cueillette_id FK "Unité zone de cueillette (dimension `unite`)."
  boolean inclus "Vrai : l'élément est couvert par le certificat. Faux : il est exclu expressément, et l'exclusion l'emporte sur une inclusion plus large."
  uuid cree_par FK "Utilisateur qui a déclaré la ligne."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
}
"app.point_controle" {
  uuid id "Identifiant stable (UUID v7)."
  uuid pack_version_id FK "Version du pack référentiel qui publie le point."
  text code "Code du point, unique dans la version du pack (par exemple CER-39)."
  text libelle_fr "Libellé en français."
  text libelle_en "Libellé en anglais."
  text theme "Thème d'affichage du tableau « prêt pour l'audit ». Texte libre fourni par le pack, provisoire (EC-104)."
  text source "Texte, article ou citation courte dont le point est tiré."
  text__ activites "Activités du catalogue `app.activite` auxquelles le point s'applique. Vide : toutes les activités. Le point est « non applicable » si l'organisation n'en exerce aucune."
  text objet "Ce sur quoi porte le point : opération, lot, site, organisation…"
  text preuve_attendue "Type de pièce attendue, en texte. Vide : le point n'exige aucune pièce. Sans pièce liée, un point autrement satisfait est « preuve manquante »."
  text echeance "Échéance ou fréquence, en texte. Stockée, non évaluée avant un besoin avéré (EC-104)."
  text mode "`automatique` : une règle du moteur le juge. `manuel` : une personne le coche, avec pièce."
  text regle_code FK "Ligne de décision de la même version de pack qui juge le point. Obligatoire si le mode est automatique, interdit sinon."
  text module_code FK "Module dont dépend le point. Vide : noyau. Si le module est inactif, le point est « non applicable »."
  date date_effet "Premier jour où le point s'applique (AM-11)."
  date date_fin "Premier jour où le point ne s'applique plus. La date de fin est exclue."
  text date_reference "Date à laquelle le point se juge : opération, récolte, création du lot ou émission du certificat (AM-11)."
  timestamp_with_time_zone cree_le "Date de publication du point."
}
"app.evaluation_point" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid point_controle_id FK "Point évalué."
  text objet_type "Type de l'objet évalué (lot, site…). Vide : le point est évalué pour toute l'organisation."
  uuid objet_id "Identifiant de l'objet évalué. Renseigné avec `objet_type`, ou pas du tout."
  text statut "`satisfait`, `preuve_manquante`, `ecart_ouvert`, `a_verifier` ou `non_applicable`. Une personne ne déclare jamais « preuve manquante » : il se déduit des pièces."
  text source "`regle` : résultat d'une règle du moteur. `manuelle` : déclaration d'une personne."
  uuid constat_id FK "Constat d'écart ouvert par la règle (AM-15), pour une évaluation issue d'une règle."
  text motif "Justification de la déclaration. Obligatoire pour déclarer un point satisfait ou non applicable."
  uuid evalue_par FK "Utilisateur qui a déclaré. Vide pour un résultat de règle."
  timestamp_with_time_zone evalue_le "Date de l'évaluation."
}
"app.evaluation_point_en_vigueur" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid point_controle_id "Point évalué."
  text objet_type "Type de l'objet évalué (lot, site…). Vide : le point est évalué pour toute l'organisation."
  uuid objet_id "Identifiant de l'objet évalué. Renseigné avec `objet_type`, ou pas du tout."
  text statut "`satisfait`, `preuve_manquante`, `ecart_ouvert`, `a_verifier` ou `non_applicable`. Une personne ne déclare jamais « preuve manquante » : il se déduit des pièces."
  text source "`regle` : résultat d'une règle du moteur. `manuelle` : déclaration d'une personne."
  uuid constat_id "Constat d'écart ouvert par la règle (AM-15), pour une évaluation issue d'une règle."
  text motif "Justification de la déclaration. Obligatoire pour déclarer un point satisfait ou non applicable."
  uuid evalue_par "Utilisateur qui a déclaré. Vide pour un résultat de règle."
  timestamp_with_time_zone evalue_le "Date de l'évaluation."
}
"app.preuve" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  uuid fichier_id FK "Fichier déposé (de la même organisation)."
  uuid point_controle_id FK "Point de contrôle que la pièce appuie."
  text objet_type "Type de l'objet appuyé. Vide : toute l'organisation."
  uuid objet_id "Identifiant de l'objet appuyé. Renseigné avec `objet_type`, ou pas du tout."
  uuid evaluation_point_id FK "Évaluation que la pièce a motivée, si elle a été liée à ce moment."
  uuid depose_par FK "Utilisateur qui a lié la pièce."
  timestamp_with_time_zone cree_le "Date de liaison."
}
"app.noeud_activite" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire. Sert à l'isolation par RLS (EC-032)."
  text chemin "Chemin pointé du nœud, un segment par niveau, trois niveaux au plus. Au premier niveau, il vaut le code de l'activité."
  text activite_code FK "Type du nœud, pris dans le catalogue fermé app.activite ; sa profondeur est celle du nœud."
  text libelle_fr "Libellé en français, défini par le pack."
  text libelle_en "Libellé en anglais, défini par le pack."
  boolean obligatoire "Vrai : le nœud ne se désactive pas tant que son parent est actif."
  boolean critique "Vrai : étape critique (OSP-05), donc obligatoire."
  boolean sous_traitable "Vrai : le nœud peut être confié à un sous-traitant."
  smallint ordre "Rang parmi les nœuds frères."
  uuid pack_version_id FK "Version du pack d'où le nœud a été copié."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification de la ligne."
  jsonb contrat "Contrat facultatif du nœud (AM-04, EC-118) : entrées et sorties typées (codes produit), mesures (code, libellé, unité, type) et clés de paramètres, tel que publié par le pack et validé par packs.validerArbre. Absent : le nœud accepte tout (pas de contrat défini). Les intrants cités par AM-04 restent en écart (EC-119)."
}
"app.perimetre_module_agent" {
  uuid id "Identifiant de la ligne."
  uuid organisation_id FK "Organisation."
  uuid groupement_id FK "Groupement où l'agent est affecté."
  uuid utilisateur_id FK "Agent affecté."
  text module_code FK "Module actif de l'organisation (le noyau, toujours actif, n'y figure pas)."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.lot" {
  uuid id "Identifiant du lot (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid campagne_id FK "Campagne du lot."
  text reference_provisoire "Référence provisoire, tant que le lot n'est pas validé (BR-AAAA-MM-JJ-NNNN dans le cahier)."
  bigint numero "Rang du numéro officiel, pris par app.attribuer_numero à la validation, jamais réutilisé (I3). Vide avant la validation."
  text nature "Nature de la séquence de numérotation : MP matière première, REC réception, NET nettoyé, PF produit fini (EC-068)."
  uuid produit_id FK "Produit du lot."
  text qualite "Catégorie d'affichage du lot (EC-090) : saisie à la réception, calculée par le mélange (la plus basse catégorie des entrées et des états du résultat, EC-120). La qualité par référentiel est dans app.lot_qualite."
  text statut "Statut : brouillon, en_attente, valide, rejete, annule, consomme, quarantaine, archive. Transitions contrôlées par app.tg_lot_controle."
  uuid compte_matiere_id FK "Compte de matière du lot (nature lot) : son solde est la quantité du lot."
  uuid mouvement_creation_id FK "Mouvement qui crée le lot (I4) : il crédite le compte du lot et n'est pas une contre-passation."
  timestamp_with_time_zone date_operation "Date de l'opération qui crée le lot, saisie sur le terrain."
  text motif_annulation "Motif de l'annulation ; renseigné si et seulement si le statut est annule."
  uuid annule_par FK "Utilisateur qui a annulé le lot."
  timestamp_with_time_zone date_annulation "Date de l'annulation."
  uuid cree_par FK "Utilisateur qui a créé le lot."
  timestamp_with_time_zone cree_le "Date d'enregistrement du lot."
  timestamp_with_time_zone modifie_le "Date de la dernière modification (statut ou numéro)."
}
"app.deplacement_lot" {
  uuid id "Identifiant du déplacement (UUID v7, ordonné dans le temps)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid lot_id FK "Lot déplacé."
  uuid de_emplacement_id FK "Emplacement de départ ; vide pour le premier placement. Doit être l'emplacement courant du lot."
  uuid emplacement_id FK "Emplacement d'arrivée : actif, et qui accepte la qualité du lot (RG-154)."
  timestamp_with_time_zone date_operation "Date du déplacement, saisie sur le terrain."
  text motif "Motif du déplacement, facultatif."
  uuid cree_par FK "Utilisateur qui a enregistré le déplacement."
  timestamp_with_time_zone cree_le "Date d'enregistrement du déplacement."
}
"app.lot_emplacement_courant" {
  uuid organisation_id "Organisation propriétaire."
  uuid lot_id "Lot placé."
  uuid emplacement_id "Emplacement où se trouve le lot."
}
"app.lot_solde" {
  uuid organisation_id "Organisation propriétaire."
  uuid lot_id "Lot concerné."
  uuid produit_id "Produit du lot."
  bigint quantite_totale_g "Total des entrées dans le compte du lot, en grammes : ce que le lot a reçu depuis sa création."
  bigint quantite_disponible_g "Solde du compte du lot, en grammes : ce qui reste disponible."
}
"app.stock_emplacement" {
  uuid organisation_id "Organisation propriétaire."
  uuid emplacement_id "Emplacement de stockage."
  bigint capacite_g "Capacité déclarée de l'emplacement, en grammes ; vide si elle n'est pas limitée."
  integer nombre_lots "Nombre de lots présents dans l'emplacement."
  bigint stock_g "Somme des quantités disponibles des lots présents, en grammes."
}
"app.reception" {
  uuid id "Identifiant de la réception (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid campagne_id FK "Campagne de la réception."
  uuid produit_id FK "Produit reçu."
  uuid producteur_id FK "Producteur qui livre ; vide si la matière vient d'un fournisseur (l'un ou l'autre, jamais les deux)."
  uuid fournisseur_id FK "Fournisseur qui livre ; vide si la matière vient d'un producteur."
  uuid contrat_id FK "Contrat dont la formule de réfaction s'applique ; vide si aucun contrat (aucune réfaction)."
  uuid lot_id FK "Lot créé par la réception, avec son mouvement de création (I4). Un lot naît d'une seule réception."
  timestamp_with_time_zone date_operation "Date de la réception, saisie sur le terrain : c'est elle qui sert à apprécier l'état du certificat (RG-014)."
  text statut "Statut : en_attente, validee ou rejetee. Ne revient jamais en arrière ; une erreur se corrige par annulation (L2-6)."
  jsonb formule_refaction "Formule de réfaction retenue, copiée du contrat à l'enregistrement : c'est elle qui est imprimée sur le bon de réception (RG-171)."
  jsonb detail_calcul "Détail du calcul du poids net marchand (brut, tare, net, excédent, réfaction), figé tel qu'imprimé (RG-171)."
  bigint net_marchand_g "Poids net marchand en grammes : net de pesée moins la réfaction du contrat. Copie du détail pour les lectures."
  uuid ticket_fichier_id FK "Ticket de pesée conservé (RG-170), facultatif tant que le stockage objet n'est pas en place."
  text derogation_motif "Justification de la dérogation de la direction quand une règle bloque la réception (RG-014) ; vide sans dérogation."
  uuid derogation_par FK "Utilisateur de la direction qui a donné la dérogation."
  timestamp_with_time_zone derogation_le "Date de la dérogation."
  uuid decide_par FK "Utilisateur qui a validé ou rejeté la réception ; vide tant qu'elle est en attente."
  timestamp_with_time_zone decide_le "Date de la décision ; vide tant que la réception est en attente."
  uuid cree_par FK "Utilisateur qui a enregistré la réception."
  timestamp_with_time_zone cree_le "Date d'enregistrement de la réception."
  timestamp_with_time_zone modifie_le "Date de la dernière modification (décision ou dérogation)."
}
"app.reception_pesee" {
  uuid id "Identifiant de la pesée (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid reception_id FK "Réception pesée."
  integer rang "Rang de la pesée dans la réception, à partir de 1."
  bigint brut_g "Poids brut en grammes, véhicule et chargement."
  bigint tare_g "Tare en grammes, jamais supérieure au brut."
  timestamp_with_time_zone cree_le "Date d'enregistrement de la pesée."
}
"app.reception_mesure" {
  uuid id "Identifiant de la mesure (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid reception_id FK "Réception mesurée."
  integer humidite_cp "Humidité mesurée en centièmes de point (1 250 = 12,50 %) ; vide si elle n'a pas été mesurée."
  integer impuretes_cp "Impuretés mesurées en centièmes de point ; vide si elles n'ont pas été mesurées."
  timestamp_with_time_zone cree_le "Date d'enregistrement de la mesure."
}
"app.quarantaine_lot" {
  uuid id "Identifiant de l'événement (UUID v7, ordonné dans le temps)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid lot_id FK "Lot concerné."
  text evenement "Événement : mise (le lot validé passe en quarantaine) ou levee (il redevient validé)."
  text motif "Motif de la mise ou de la levée ; toujours renseigné."
  uuid constat_id FK "Constat de la règle qui a déclenché la mise en quarantaine ; vide pour une mise ou une levée manuelle."
  uuid cree_par FK "Utilisateur qui a mis le lot en quarantaine ou levé la quarantaine."
  timestamp_with_time_zone cree_le "Date de l'événement."
}
"app.ordre_transformation" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid campagne_id FK "Campagne des lots produits par l'ordre."
  uuid site_id FK "Site où le procédé est réalisé ; le procédé doit y être actif."
  uuid noeud_id FK "Procédé : nœud de l'arbre d'activités de l'organisation, de profondeur 2 ou 3, sous une activité du regroupement transformation."
  text niveau_suivi "Niveau de suivi retenu à la création (réglage de l'organisation, par procédé) : global (une saisie couvre le procédé) ou detaille (une saisie par étape)."
  text statut "brouillon, en_cours (dès qu'une étape démarre), termine (toutes les étapes validées) ou annule (aucune étape validée)."
  timestamp_with_time_zone date_operation "Date de l'opération, saisie par l'opérateur."
  text motif_annulation "Motif de l'annulation ; renseigné si et seulement si l'ordre est annulé."
  uuid cree_par FK "Utilisateur qui a créé l'ordre."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.etape_realisee" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid ordre_id FK "Ordre de transformation concerné."
  uuid noeud_etape_id FK "Étape du procédé (nœud enfant du procédé). Vide pour la saisie qui couvre tout le procédé en suivi global."
  text statut "a_faire, en_cours, realisee ou validee ; une seule marche à la fois."
  jsonb mesures "Mesures saisies à l'étape (objet JSON) ; modifiables tant que l'étape n'est pas validée."
  timestamp_with_time_zone debut_le "Début de l'étape."
  timestamp_with_time_zone fin_le "Fin de l'étape ; jamais avant le début."
  uuid cree_par FK "Utilisateur qui a ouvert l'étape."
  uuid valide_par FK "Utilisateur qui a validé l'étape ; renseigné si et seulement si elle est validée."
  timestamp_with_time_zone valide_le "Date de validation de l'étape."
  timestamp_with_time_zone cree_le "Date de création de la ligne."
  timestamp_with_time_zone modifie_le "Date de dernière modification."
}
"app.sortie_etape" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid etape_realisee_id FK "Étape qui produit la sortie ; elle doit être en cours ou réalisée, pas encore validée."
  text nature "produit_principal (au plus un par étape), coproduit, sous_produit, rebut ou perte."
  uuid produit_id FK "Produit de la sortie."
  bigint quantite_g "Quantité en grammes, strictement positive."
  uuid lot_id FK "Lot créé par un mouvement de l'étape, pour la même quantité ; vide pour un rebut ou une perte, qui ne deviennent jamais un lot."
  text destination "Destination du rebut (obligatoire pour un rebut)."
  text motif "Motif de la perte déclarée (obligatoire pour une perte)."
  uuid cree_par FK "Utilisateur qui a déclaré la sortie."
  timestamp_with_time_zone cree_le "Date de la déclaration."
  boolean reintegre_flux_bio "Vrai si l'opérateur déclare que ce rebut réintègre un flux biologique (EC-120) ; réservé à la nature rebut. Lu par la variable etape.rebut_reintegre_flux_bio des normes."
  uuid mouvement_id FK "Mouvement de transformation qui a produit la sortie. Une sortie dont le mouvement est contre-passé (étape dévalidée) ne compte plus dans le bilan ; vide pour les sorties antérieures à L2-6 sans mouvement retrouvé."
}
"app.bilan_etape" {
  uuid organisation_id "Organisation propriétaire."
  uuid etape_realisee_id "Étape réalisée concernée."
  uuid ordre_id "Ordre de transformation de l'étape."
  bigint entrees_matiere_g "Matière consommée : débits des comptes de lot par les mouvements de l'étape, en grammes."
  bigint intrants_g "Intrants consommés : débits des comptes d'intrant par les mouvements de l'étape, en grammes (EC-015)."
  bigint sorties_lots_g "Quantité créée en lots (crédits des comptes de lot), en grammes."
  bigint rebuts_g "Quantité purgée, sortie des flux biologiques (crédits du compte de purge), en grammes."
  bigint pertes_g "Perte comptabilisée (crédits du compte de perte), en grammes ; les pertes déclarées s'en retranchent pour donner l'écart inexpliqué."
}
"app.lot_qualite" {
  uuid id "Identifiant de la ligne (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire du lot (isolation par ligne)."
  uuid lot_id FK "Lot dont la qualité est décrite ; se pose tant que le lot est en brouillon ou en attente."
  text referentiel_code FK "Code du référentiel du catalogue global (UE, NOP…), coché par l'organisation."
  text etat "Code d'un état de qualité du référentiel (bio, conversion_annee_1…), pris dans les états de la dernière version publiée de son pack ; un état « hors statut » est refusé."
  uuid cree_par FK "Utilisateur qui a posé la qualité."
  timestamp_with_time_zone cree_le "Date de pose de la qualité."
}
"app.vehicule" {
  uuid id "Identifiant du véhicule (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  text immatriculation "Immatriculation du véhicule, unique par organisation."
  text type "Type de véhicule (camion, semi-remorque, conteneur routier…), texte libre : aucune liste n'est donnée par les sources."
  text transporteur "Transporteur qui exploite le véhicule ; vide si le véhicule est celui de l'organisation."
  boolean actif "Faux quand le véhicule ne se charge plus ; une expédition refuse un véhicule inactif."
  timestamp_with_time_zone cree_le "Date d'enregistrement du véhicule."
  timestamp_with_time_zone modifie_le "Date de la dernière modification du véhicule."
}
"app.pv_nettoyage_vehicule" {
  uuid id "Identifiant du PV (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid vehicule_id FK "Véhicule nettoyé."
  date date_nettoyage "Date réelle du nettoyage, saisie par le responsable : elle ne dépasse pas la date de l'expédition qui s'en sert."
  text responsable "Nom de la personne qui a nettoyé le véhicule."
  jsonb points_controle "Points de contrôle du PV : liste d'objets { code, libelle, controle }. Un PV n'est valable pour une expédition que si tous les points sont contrôlés."
  uuid signataire_id FK "Utilisateur qui signe le PV."
  timestamp_with_time_zone cree_le "Date d'enregistrement du PV."
}
"app.vente" {
  uuid id "Identifiant de la vente (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid client_id FK "Client acheteur."
  uuid contrat_id FK "Contrat de vente actif du même client, facultatif : vide pour une vente « spot ». Sa tolérance s'ajoute à la quantité engagée."
  uuid produit_id FK "Produit vendu : celui de tous les lots expédiés."
  text referentiel_code FK "Référentiel revendiqué sur la vente (RG-156, AV1-03) ; vide si la vente ne revendique aucun référentiel."
  bigint quantite_engagee_g "Quantité vendue en grammes. Le total expédié ne la dépasse pas, tolérance du contrat comprise."
  bigint prix_unite_mineure "Prix par unité de prix, en unités mineures de la devise."
  text unite_prix FK "Unité à laquelle le prix s'applique (kilogramme, tonne…)."
  text devise FK "Devise du prix."
  date date_operation "Date de la vente."
  text statut "Statut : brouillon, validee, soldee ou annulee. Une vente annulée ne peut plus avoir d'expédition validée."
  uuid valide_par FK "Utilisateur qui a validé la vente ; vide tant qu'elle est en brouillon."
  timestamp_with_time_zone valide_le "Date de validation de la vente."
  uuid cree_par FK "Utilisateur qui a saisi la vente."
  timestamp_with_time_zone cree_le "Date de saisie de la vente."
  timestamp_with_time_zone modifie_le "Date de la dernière modification de la vente."
}
"app.expedition" {
  uuid id "Identifiant de l'expédition (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid vente_id FK "Vente validée qui donne lieu à l'expédition ; une vente en a une ou plusieurs."
  uuid vehicule_id FK "Véhicule qui charge la matière."
  uuid pv_nettoyage_id FK "PV de nettoyage du véhicule avant chargement ; il doit être valable à la date de l'expédition."
  timestamp_with_time_zone date_operation "Date de l'expédition : c'est elle qui sert à apprécier les certificats de la chaîne (AV1-03)."
  text conteneur "Numéro du conteneur ; vide hors transport conteneurisé."
  text__ scelles "Numéros des scellés posés."
  text destination "Destination de l'expédition."
  text coi_reference "Référence du certificat d'inspection (COI) ; facultative, donnée seulement tant qu'aucun module documents n'existe."
  text nop_import_certificate "Référence du NOP Import Certificate ; facultative."
  text eudr_reference "Référence de la déclaration EUDR ; facultative."
  text statut "Statut : brouillon, validee ou annulee. Ne revient jamais en arrière."
  uuid mouvement_id FK "Mouvement de matière (nature expedition) écrit à la validation : débit des lots, crédit du compte technique d'expédition. Vide hors expédition validée."
  uuid decide_par FK "Utilisateur qui a validé ou annulé l'expédition."
  timestamp_with_time_zone decide_le "Date de la décision."
  uuid cree_par FK "Utilisateur qui a saisi l'expédition."
  timestamp_with_time_zone cree_le "Date de saisie de l'expédition."
  timestamp_with_time_zone modifie_le "Date de la dernière modification de l'expédition."
}
"app.expedition_ligne" {
  uuid id "Identifiant de la ligne (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid expedition_id FK "Expédition à laquelle la ligne appartient."
  uuid lot_id FK "Lot validé prélevé (RG-081) ; un lot ne figure qu'une fois par expédition."
  bigint quantite_g "Quantité prélevée en grammes, strictement positive, jamais supérieure à ce que contient le lot."
  timestamp_with_time_zone cree_le "Date d'enregistrement de la ligne."
}
"app.vente_solde" {
  uuid organisation_id "Organisation propriétaire."
  uuid vente_id "Vente concernée."
  bigint quantite_engagee_g "Quantité engagée de la vente, en grammes."
  bigint expedie_g "Total des lignes des expéditions validées, en grammes."
  bigint reste_g "Quantité engagée moins quantité expédiée ; négatif quand la tolérance du contrat est utilisée."
}
"app.registre_annulation" {
  uuid id "Identifiant de la ligne du registre (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  text objet_type "Type d'objet défait : reception, etape_realisee, expedition, vente ou lot."
  uuid objet_id "Identifiant de l'objet défait."
  text nature "devalidation (retour à l'état précédent : réception, étape, expédition) ou annulation (clôture : vente, lot). Deux mécanismes distincts (EC-008)."
  text motif "Justification écrite, obligatoire."
  uuid lot_id FK "Lot annulé, avec son numéro conservé (RG-143) ; pour une dévalidation de réception, le lot d'origine annulé. Vide pour une vente, une étape ou une expédition."
  uuid campagne_id FK "Campagne concernée, pour l'édition du registre par campagne (RG-144) ; vide pour une vente ou une expédition."
  uuid mouvement_id FK "Mouvement de contre-passation écrit ; vide pour l'annulation d'une vente, qui n'a pas encore bougé la matière."
  uuid constat_id FK "Constat consigné pour la dévalidation (AM-15) ; vide pour une annulation."
  uuid cree_par FK "Utilisateur qui défait l'objet (direction)."
  timestamp_with_time_zone cree_le "Date de la dévalidation ou de l'annulation."
}
"app.arete_filiation" {
  uuid organisation_id "Organisation propriétaire."
  uuid parent_lot_id "Lot dont la matière a été consommée par la transformation."
  uuid enfant_lot_id "Lot créé par la même transformation."
}
"app.livraison_client" {
  uuid organisation_id "Organisation propriétaire."
  uuid lot_id "Lot expédié."
  uuid client_id "Client de la vente."
  uuid vente_id "Vente servie par l'expédition."
  uuid expedition_id "Expédition validée qui a livré le lot."
  bigint quantite_g "Quantité du lot livrée par cette expédition, en grammes."
}
"app.rappel" {
  uuid id "Identifiant du rappel (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid lot_depart_id FK "Lot à partir duquel le rappel est lancé."
  text motif "Raison du rappel, obligatoire."
  boolean simule "Vrai pour un exercice à blanc (RG-163) : aucun lot n'est mis en quarantaine."
  text statut "ouvert, clos (décision de clôture consignée) ou simule (exercice à blanc, archivé tel quel)."
  jsonb plan "Plan figé à l'ouverture : lots descendants et ascendants avec leur profondeur, clients touchés, quantités livrées en grammes."
  integer duree_calcul_ms "Durée mesurée du calcul du plan, en millisecondes (RG-160, RG-163 : délai de réponse)."
  text decision_cloture "Décision de clôture, obligatoire pour clore (RG-162)."
  timestamp_with_time_zone organisme_informe_le "Date à laquelle l'organisme certificateur a été informé (RG-162) ; vide s'il ne l'est pas."
  text organisme_reference "Référence de l'échange avec l'organisme certificateur (texte libre)."
  bigint quantite_retournee_g "Quantité retournée par les clients, en grammes (RG-162)."
  bigint quantite_detruite_g "Quantité détruite, en grammes (RG-162)."
  bigint quantite_declassee_g "Quantité déclassée, en grammes (RG-162)."
  uuid clos_par FK "Utilisateur qui clôt le rappel."
  timestamp_with_time_zone clos_le "Date de la clôture."
  uuid cree_par FK "Utilisateur qui ouvre le rappel (direction)."
  timestamp_with_time_zone cree_le "Date d'ouverture."
  timestamp_with_time_zone modifie_le "Dernière modification (clôture)."
}
"app.rappel_evenement" {
  uuid id "Identifiant de l'événement (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid rappel_id FK "Rappel concerné."
  text type "ouverture, lot_quarantaine, organisme_informe ou cloture."
  jsonb detail "Données de l'événement (lots touchés, décision, référence)."
  uuid cree_par FK "Utilisateur à l'origine de l'événement."
  timestamp_with_time_zone cree_le "Date de l'événement."
}
"app.dossier_audit" {
  uuid id "Identifiant du dossier (UUID v7)."
  uuid organisation_id FK "Organisation propriétaire (isolation par ligne)."
  uuid campagne_id FK "Campagne couverte."
  text statut "en_attente (tâche inscrite), genere ou echec."
  jsonb contenu "Dossier structuré (JSON), en forme canonique."
  text edition_html "Édition imprimable du dossier (HTML). Le PDF n'est pas fourni : voir ecarts.md."
  text empreinte "Empreinte SHA-256 (hexadécimal) de la forme canonique du contenu : même contenu, même empreinte."
  text erreur "Message d'échec de la tâche ; vide sinon."
  uuid demande_par FK "Utilisateur qui demande le dossier."
  timestamp_with_time_zone demande_le "Date de la demande."
  timestamp_with_time_zone genere_le "Date de génération."
  timestamp_with_time_zone modifie_le "Dernière modification."
}
"app.licence" {
  uuid id "Identifiant (UUID v7)."
  uuid organisation_id FK "Organisation licenciée. Sert à l'isolation par RLS."
  date debut "Premier jour de la licence, inclus."
  date fin "Dernier jour de la licence, inclus. « Échue » se calcule à partir de cette date."
  text__ modules "Codes des modules ouverts, hors noyau (toujours actif), sans doublon."
  text motif "Motif écrit de la licence (nouvelle licence, renouvellement, correction)."
  uuid cree_par "Identifiant Keycloak (sub) du membre de l'équipe de l'éditeur qui a saisi la licence."
  timestamp_with_time_zone cree_le "Date de saisie."
}
"app.facture" {
  uuid id "Identifiant (UUID v7)."
  uuid organisation_id FK "Organisation facturée. Sert à l'isolation par RLS."
  text numero "Numéro de la facture dans l'outil externe, unique par organisation."
  bigint montant "Montant en unités mineures de la devise."
  text devise FK "Code de la devise (app.devise)."
  date emise_le "Date d'émission."
  date echeance_le "Dernier jour pour payer, inclus. « En retard » se calcule : émise, non payée, échéance dépassée."
  text statut "`emise`, `payee` ou `annulee`. Payée et annulée sont définitifs."
  date payee_le "Date de paiement ; nulle tant que la facture n'est pas payée."
  uuid cree_par "Identifiant Keycloak (sub) du membre de l'équipe de l'éditeur qui a saisi la référence."
  timestamp_with_time_zone cree_le "Date de saisie."
  timestamp_with_time_zone modifie_le "Dernière modification, tenue par un déclencheur."
}
"app.action_equipe" {
  uuid id "Identifiant de l'action (UUID v7)."
  timestamp_with_time_zone cree_le "Moment de l'action."
  text auteur_sub "Identifiant Keycloak (sub) du super administrateur qui a agi."
  text action "Action : `equipe.creer`, `equipe.changer_role`, `equipe.desactiver`, `equipe.reactiver` ou `equipe.reinitialiser`."
  text sujet_sub "Identifiant Keycloak (sub) du compte concerné."
  text sujet_identifiant "Identifiant de connexion du compte concerné au moment de l'action, conservé même si le compte disparaît de Keycloak."
  jsonb details "Précisions : rôle attribué, ancien rôle. Jamais de mot de passe."
}
"app.organisation_onboarding" {
  uuid id "Identifiant stable (UUID v7)."
  uuid organisation_id FK "Organisation concernée. Sert à l'isolation par RLS (EC-032)."
  text etat "reporte : la personne a choisi « Plus tard » ; termine : les modules, le pack et les activités ont été appliqués ; rouvert : le parcours a été rejoué (tests, reprise après un changement d'activité)."
  jsonb reponses "Réponses au questionnaire : famille de culture, activités, exigences, familles souhaitées mais pas encore disponibles. Sert de préremplissage et de demande à l'équipe de l'éditeur (EC-133)."
  uuid pack_version_id FK "Version du pack filière chargée à la fin du parcours. Nulle tant que le parcours n'est pas terminé."
  text motif "Motif de la ligne, obligatoire (« Onboarding » en usage normal)."
  uuid cree_par FK "Utilisateur qui a reporté, terminé ou rouvert le parcours."
  timestamp_with_time_zone cree_le "Horodatage de la ligne."
}

Generated by tbls