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.
| Name |
Type |
Default |
Nullable |
Children |
Parents |
Comment |
| id |
uuid |
app.uuid_v7() |
false |
app.contrat_ligne app.prime app.reception app.vente |
|
Identifiant stable (UUID v7). |
| organisation_id |
uuid |
|
false |
app.contrat_ligne app.prime app.reception app.vente |
app.organisation app.fichier app.campagne app.groupement app.producteur app.fournisseur app.client |
Organisation propriétaire. Sert à l’isolation par RLS (EC-032). |
| code |
text |
|
false |
|
|
Code du contrat, unique par organisation. |
| type |
text |
|
false |
|
|
achat (producteur, groupement ou fournisseur), vente (client) ou prestation (client ou fournisseur, EC-052). |
| producteur_id |
uuid |
|
true |
|
app.producteur |
Contrepartie producteur (achat). Exactement une contrepartie entre producteur, groupement, fournisseur et client. |
| groupement_id |
uuid |
|
true |
|
app.groupement |
Contrepartie groupement (achat). |
| fournisseur_id |
uuid |
|
true |
|
app.fournisseur |
Contrepartie fournisseur (achat ou prestation). |
| client_id |
uuid |
|
true |
|
app.client |
Contrepartie client (vente ou prestation). |
| campagne_id |
uuid |
|
false |
|
app.campagne |
Campagne du contrat (de la même organisation). |
| date_debut |
date |
|
false |
|
|
Premier jour de la période du contrat. |
| date_fin |
date |
|
false |
|
|
Dernier jour de la période du contrat. Jamais avant le début. |
| statut |
text |
‘brouillon’::text |
false |
|
|
brouillon, actif, solde ou resilie. Pas de retour en arrière ; l’activation exige une ligne. |
| conditions_paiement |
text |
|
true |
|
|
Conditions de paiement, en texte, facultatives. |
| fichier_id |
uuid |
|
true |
|
app.fichier |
Pièce jointe du contrat (fichier de la même organisation), facultative. |
| 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 dernière modification. |
| Name |
Type |
Definition |
| contrat_check |
CHECK |
CHECK (((((((producteur_id IS NOT NULL))::integer + ((groupement_id IS NOT NULL))::integer) + ((fournisseur_id IS NOT NULL))::integer) + ((client_id IS NOT NULL))::integer) = 1)) |
| contrat_check1 |
CHECK |
CHECK ((((type = ‘achat’::text) AND (client_id IS NULL)) OR ((type = ‘vente’::text) AND (client_id IS NOT NULL)) OR ((type = ‘prestation’::text) AND (producteur_id IS NULL) AND (groupement_id IS NULL)))) |
| contrat_check2 |
CHECK |
CHECK ((date_fin >= date_debut)) |
| contrat_code_check |
CHECK |
CHECK ((code <> ‘’::text)) |
| contrat_conditions_paiement_check |
CHECK |
CHECK ((conditions_paiement <> ‘’::text)) |
| contrat_statut_check |
CHECK |
CHECK ((statut = ANY (ARRAY[‘brouillon’::text, ‘actif’::text, ‘solde’::text, ‘resilie’::text]))) |
| contrat_type_check |
CHECK |
CHECK ((type = ANY (ARRAY[‘achat’::text, ‘vente’::text, ‘prestation’::text]))) |
| contrat_organisation_id_fkey |
FOREIGN KEY |
FOREIGN KEY (organisation_id) REFERENCES app.organisation(id) |
| contrat_organisation_id_fichier_id_fkey |
FOREIGN KEY |
FOREIGN KEY (organisation_id, fichier_id) REFERENCES app.fichier(organisation_id, id) |
| contrat_organisation_id_campagne_id_fkey |
FOREIGN KEY |
FOREIGN KEY (organisation_id, campagne_id) REFERENCES app.campagne(organisation_id, id) |
| contrat_organisation_id_groupement_id_fkey |
FOREIGN KEY |
FOREIGN KEY (organisation_id, groupement_id) REFERENCES app.groupement(organisation_id, id) |
| contrat_organisation_id_producteur_id_fkey |
FOREIGN KEY |
FOREIGN KEY (organisation_id, producteur_id) REFERENCES app.producteur(organisation_id, id) |
| contrat_organisation_id_fournisseur_id_fkey |
FOREIGN KEY |
FOREIGN KEY (organisation_id, fournisseur_id) REFERENCES app.fournisseur(organisation_id, id) |
| contrat_organisation_id_client_id_fkey |
FOREIGN KEY |
FOREIGN KEY (organisation_id, client_id) REFERENCES app.client(organisation_id, id) |
| contrat_pkey |
PRIMARY KEY |
PRIMARY KEY (id) |
| contrat_organisation_id_code_key |
UNIQUE |
UNIQUE (organisation_id, code) |
| contrat_organisation_id_id_key |
UNIQUE |
UNIQUE (organisation_id, id) |
| Name |
Definition |
| contrat_pkey |
CREATE UNIQUE INDEX contrat_pkey ON app.contrat USING btree (id) |
| contrat_organisation_id_code_key |
CREATE UNIQUE INDEX contrat_organisation_id_code_key ON app.contrat USING btree (organisation_id, code) |
| contrat_organisation_id_id_key |
CREATE UNIQUE INDEX contrat_organisation_id_id_key ON app.contrat USING btree (organisation_id, id) |
| Name |
Definition |
| journal |
CREATE TRIGGER journal AFTER INSERT OR DELETE OR UPDATE ON app.contrat FOR EACH ROW EXECUTE FUNCTION journal.tg_journaliser() |
| modifie_le |
CREATE TRIGGER modifie_le BEFORE UPDATE ON app.contrat FOR EACH ROW EXECUTE FUNCTION app.tg_modifie_le() |
| cycle |
CREATE TRIGGER cycle BEFORE UPDATE ON app.contrat FOR EACH ROW EXECUTE FUNCTION app.tg_contrat_cycle() |
erDiagram
"app.contrat_ligne" }o--|| "app.contrat" : "FOREIGN KEY (organisation_id, contrat_id) REFERENCES app.contrat(organisation_id, id)"
"app.prime" }o--|| "app.contrat" : "FOREIGN KEY (organisation_id, contrat_id) REFERENCES app.contrat(organisation_id, id)"
"app.reception" }o--|| "app.contrat" : "FOREIGN KEY (organisation_id, contrat_id) REFERENCES app.contrat(organisation_id, id)"
"app.vente" }o--|| "app.contrat" : "FOREIGN KEY (organisation_id, contrat_id) REFERENCES app.contrat(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" {
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.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.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.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.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.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.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.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."
}
Generated by tbls