Poser les modèles de documents et le dictionnaire RGPD

Deux livrables de WP-10 manquaient, et le second n'existait nulle part.

Les modèles de documents produisent attestations et courriers types en
résolvant les variables du dossier. Le rendu vit dans le domaine, avec une règle
qui n'est pas négociable : une variable citée sans valeur fait échouer la
génération. Une attestation trouée est un document faux, pas un document
incomplet — le blanc se remarque à la relecture une fois sur deux, et la pièce
part signée. De même, une variable inconnue est refusée à l'enregistrement du
modèle, par l'administrateur qui l'écrit, plutôt que découverte par le
gestionnaire devant le salarié qui attend.

Le corps reste de l'HTML rédigé par un administrateur ; ce sont les valeurs
venues du dossier qui sont échappées à l'insertion. Un nom de famille importé
d'un autre outil ne doit pas pouvoir ouvrir une balise.

`Document.templateId` est en ON DELETE SET NULL : une pièce déjà remise ne
disparaît pas parce qu'on a effacé le modèle qui l'a produite.

L'écran RGPD livre le dictionnaire donnée → finalité → base légale →
destinataire → durée qu'exige la matrice n° 14. Le dictionnaire vit dans le code
parce qu'il décrit le schéma ; les durées viennent de RetentionPolicy parce
qu'elles se paramètrent et s'auditent. Une catégorie sans durée déclarée est
signalée en rouge : le plan interdit d'appliquer une durée par défaut.

L'affectation des bases légales porte un avertissement explicite. C'est une
qualification juridique, pas un réglage : elle doit être confirmée par le
responsable de traitement avant d'être opposée à quiconque.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Opus 5 committed 2026-08-11 12:44:44 +02:00
1 parent 5b60e7ddb6
commit db03768025
10 files changed
+1090 -1

No files matched your search

+32 -1
View File
@@ -34,6 +34,7 @@ model Account {
jobTitles JobTitle[]
labels Label[]
absenceTypes AbsenceType[]
documentTemplates DocumentTemplate[]
legalConfig LegalConfigEntry[]
installation Installation?
}
@@ -792,6 +793,33 @@ model DailyNote {
/// pouvoir reproduire à l'identique une paie antérieure après un changement de
/// règle. Une mise à jour en place rendrait cette reproduction impossible, et
/// l'impossibilité ne se découvrirait qu'au premier contrôle.
/// Modèle de document — PLAN.md §4.7 et §9.
///
/// `bodyHtml` porte le corps avec ses variables sous la forme `{{cle}}`.
/// `availableFields` déclare les variables offertes à la rédaction : elles sont
/// **scopées par établissement**, si bien qu'un modèle rendu pour un salarié
/// résout ses variables dans le contexte de l'établissement de son contrat. Une
/// même attestation ne porte donc pas la même raison sociale d'un magasin à
/// l'autre.
///
/// Archivé plutôt que supprimé : un document généré garde `templateId`, et
/// perdre le modèle rendrait inexplicable la forme d'une pièce déjà remise.
model DocumentTemplate {
id String @id @default(cuid())
accountId String
name String
bodyHtml String
availableFields Json
archivedAt DateTime?
createdAt DateTime @default(now())
account Account @relation(fields: [accountId], references: [id], onDelete: Cascade)
documents Document[]
@@unique([accountId, name])
@@index([accountId])
}
/// Pièce du dossier salarié — PLAN.md §4.7.
///
/// Le contenu vit sur disque, **chiffré**, jamais dans la base : une pièce
@@ -811,6 +839,8 @@ model Document {
/// Empreinte du clair, pour détecter une altération du fichier stocké.
checksum String
/// Donnée de santé : sa lecture est journalisée (§3.6).
/// Modèle dont la pièce est issue. Nul pour un fichier déposé à la main.
templateId String?
isSensitive Boolean @default(false)
/// Échéance de conservation. Nulle tant qu'aucune politique ne l'a fixée :
/// le plan interdit d'appliquer une durée par défaut (§12.5).
@@ -820,7 +850,8 @@ model Document {
deletedAt DateTime?
deletedBy String?
membership Membership? @relation(fields: [membershipId], references: [id], onDelete: Cascade)
membership Membership? @relation(fields: [membershipId], references: [id], onDelete: Cascade)
template DocumentTemplate? @relation(fields: [templateId], references: [id], onDelete: SetNull)
@@index([accountId])
@@index([membershipId])