Aller au contenu

app.utilisateur

Utilisateur d’une organisation, lié au compte Keycloak. Un compte externe a une ligne par organisation où il intervient.

Name Type Default Nullable Children Parents Comment
id uuid app.uuid_v7() false app.attribution_permissions app.appareil app.code_enrolement app.fichier app.parametre app.mouvement app.validation sync.commande sync.controle app.appel_administration app.modele_numerotation app.affectation_agent app.parcelle_version app.statut_conformite app.rendement_parcelle app.capacite_version app.organisation_module app.organisation_activite app.constat app.certificat_portee app.evaluation_point app.preuve app.perimetre_module_agent app.lot app.deplacement_lot app.reception app.quarantaine_lot app.ordre_transformation app.etape_realisee app.sortie_etape app.lot_qualite app.pv_nettoyage_vehicule app.vente app.expedition app.registre_annulation app.rappel app.rappel_evenement app.dossier_audit app.organisation_onboarding Identifiant de l’utilisateur (UUID v7).
organisation_id uuid false app.attribution_permissions app.appareil app.code_enrolement app.fichier app.parametre app.mouvement app.validation sync.commande sync.controle app.appel_administration app.modele_numerotation app.affectation_agent app.parcelle_version app.statut_conformite app.rendement_parcelle app.capacite_version app.organisation_module app.organisation_activite app.constat app.certificat_portee app.evaluation_point app.preuve app.perimetre_module_agent app.lot app.deplacement_lot app.reception app.quarantaine_lot app.ordre_transformation app.etape_realisee app.sortie_etape app.lot_qualite app.pv_nettoyage_vehicule app.vente app.expedition app.registre_annulation app.rappel app.rappel_evenement app.dossier_audit app.organisation_onboarding app.organisation Organisation propriétaire de la ligne. Sert à l’isolation par RLS (EC-032) et au filtrage de la synchronisation.
keycloak_sub uuid false Identifiant du compte Keycloak (revendication sub). Identique d’une organisation à l’autre pour un compte externe.
identifiant text false Identifiant local de connexion, sans le code de l’organisation : 3 à 32 caractères parmi a-z, 0-9, _ et -. Unique dans l’organisation.
nom_affiche text false Nom affiché dans l’application.
telephone text true Numéro de téléphone. Attribut du compte, jamais son identifiant.
statut text ‘actif’::text false Statut du compte : actif, suspendu ou desactive.
cree_le timestamp with time zone now() false Date de création de la ligne.
modifie_le timestamp with time zone now() false Date de la dernière modification, tenue par un déclencheur.
Name Type Definition
utilisateur_identifiant_check CHECK CHECK ((identifiant ~ ‘^[a-z0-9_-]{3,32}$’::text))
utilisateur_statut_check CHECK CHECK ((statut = ANY (ARRAY[‘actif’::text, ‘suspendu’::text, ‘desactive’::text])))
utilisateur_organisation_id_fkey FOREIGN KEY FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)
utilisateur_pkey PRIMARY KEY PRIMARY KEY (id)
utilisateur_organisation_id_id_key UNIQUE UNIQUE (organisation_id, id)
utilisateur_organisation_id_keycloak_sub_key UNIQUE UNIQUE (organisation_id, keycloak_sub)
utilisateur_organisation_id_identifiant_key UNIQUE UNIQUE (organisation_id, identifiant)
Name Definition
utilisateur_pkey CREATE UNIQUE INDEX utilisateur_pkey ON app.utilisateur USING btree (id)
utilisateur_organisation_id_id_key CREATE UNIQUE INDEX utilisateur_organisation_id_id_key ON app.utilisateur USING btree (organisation_id, id)
utilisateur_organisation_id_keycloak_sub_key CREATE UNIQUE INDEX utilisateur_organisation_id_keycloak_sub_key ON app.utilisateur USING btree (organisation_id, keycloak_sub)
utilisateur_organisation_id_identifiant_key CREATE UNIQUE INDEX utilisateur_organisation_id_identifiant_key ON app.utilisateur USING btree (organisation_id, identifiant)
Name Definition
journal CREATE TRIGGER journal AFTER INSERT OR DELETE OR UPDATE ON app.utilisateur FOR EACH ROW EXECUTE FUNCTION journal.tg_journaliser()
modifie_le CREATE TRIGGER modifie_le BEFORE UPDATE ON app.utilisateur FOR EACH ROW EXECUTE FUNCTION app.tg_modifie_le()
erDiagram

"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.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.fichier" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, depose_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.parametre" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.mouvement" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(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)"
"sync.commande" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, utilisateur_id) REFERENCES app.utilisateur(organisation_id, id)"
"sync.controle" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, decide_par) REFERENCES app.utilisateur(organisation_id, 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.modele_numerotation" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.affectation_agent" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, utilisateur_id) REFERENCES app.utilisateur(organisation_id, id)"
"app.parcelle_version" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.statut_conformite" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.rendement_parcelle" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.capacite_version" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.organisation_module" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.organisation_activite" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, 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.certificat_portee" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.evaluation_point" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, evalue_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.preuve" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, depose_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.perimetre_module_agent" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, utilisateur_id) REFERENCES app.utilisateur(organisation_id, 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.deplacement_lot" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, 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.quarantaine_lot" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.ordre_transformation" }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, 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.sortie_etape" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.lot_qualite" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.pv_nettoyage_vehicule" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, signataire_id) REFERENCES app.utilisateur(organisation_id, 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.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.registre_annulation" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, 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_evenement" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.dossier_audit" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, demande_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.organisation_onboarding" }o--|| "app.utilisateur" : "FOREIGN KEY (organisation_id, cree_par) REFERENCES app.utilisateur(organisation_id, id)"
"app.utilisateur" }o--|| "app.organisation" : "FOREIGN KEY (organisation_id) REFERENCES app.organisation(id)"

"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.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.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.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.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."
}
"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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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."
}
"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."
}

Generated by tbls