Skip to content

feat(quotes,audit): rapport d'audit PDF + clauses contractuelles cochables#2

Merged
Seeyko merged 1 commit into
mainfrom
feat/audit-trail-and-conditional-clauses
May 3, 2026
Merged

feat(quotes,audit): rapport d'audit PDF + clauses contractuelles cochables#2
Seeyko merged 1 commit into
mainfrom
feat/audit-trail-and-conditional-clauses

Conversation

@Seeyko

@Seeyko Seeyko commented May 3, 2026

Copy link
Copy Markdown
Collaborator

Contexte

Suite à l'analyse comparative FAKT vs DocuSeal (2026-05-02), deux features mode-1 compatibles ont été identifiées comme à plus haute valeur ajoutée et alignées avec la roadmap V0.1.x de stabilisation pré-V0.2 :

  1. Rapport d'audit PDF lisible (inspiré lib/submissions/generate_audit_trail.rb)
  2. Clauses contractuelles cochables (inspiré conditions_modal.vue, simplifié)

Plan superpowers complet : docs/superpowers/plans/2026-05-02-audit-trail-and-conditional-clauses.md

Feature 1 — Rapport d'audit PDF

Avant : la chaîne signature_events était intègre côté DB (SHA-256 chaînés, append-only via triggers) mais invisible pour un tiers. En cas de litige, aucun document à présenter.

Maintenant : un bouton « Rapport d'audit » apparaît sur chaque devis signé. Au clic, génération d'un PDF Typst présentable juge / avocat :

  • Header + métadonnées document (type, n°, montant, client, dates)
  • Section Signatures électroniques : signataire, IP, UA, niveau eIDAS, fournisseur TSA, hashes SHA-256 complets (jamais tronqués — valeur juridique)
  • Journal d'événements : chronologie fusionnée signatures + activity (created, sent, viewed, signed, paid…)
  • Footer : mention vérification PAdES (Adobe Reader, pyHanko, recalcul manuel SHA-256)

Fichiers clés :

  • packages/pdf/templates/audit-trail.typ (nouveau)
  • packages/pdf/src/context-builder.tsbuildAuditTrailContext
  • apps/desktop/src-tauri/src/pdf/render.rs"audit-trail" ajouté
  • apps/desktop/src/routes/quotes/Detail.tsx — bouton + handler

Feature 2 — Clauses contractuelles cochables

Catalogue figé de 8 clauses pré-rédigées :

Catégorie Clauses
Paiement Acompte 30 % | Acompte 50 % (mutuellement exclusifs)
Garantie 6 mois | 12 mois (mutuellement exclusifs)
PI Cession après paiement | Licence d'utilisation (mutuellement exclusifs)
Responsabilité Limitation au montant du devis
Juridiction Juridiction française + résolution amiable

Quand l'utilisateur coche une option exclusive, l'autre se décoche automatiquement (pas de contradiction possible).

Stockées en JSON sur quotes.clauses (migration 0005_quote_clauses.sql). Insérées dans le PDF avant les CGV légales.

Fichiers clés :

  • packages/legal/src/clauses.ts (nouveau catalogue + helpers)
  • packages/db/src/migrations/0005_quote_clauses.sql
  • packages/db/src/queries/quotes.tsparseClauses / serializeClauses
  • apps/desktop/src/routes/quotes/QuoteForm.tsxClausesPanel
  • packages/pdf/templates/quote.typ — nouvelle section

Validation

  • 1072 tests verts (+30 nouveaux)
    • 22 tests clauses (legal) — exclusions, hydrate, round-trip, catalogue
    • 7 tests audit-trail-context (pdf) — tri chronologique, mapping FR
    • 3 tests DB round-trip quotes.clauses
    • 2 tests UI Detail — bouton audit visible/caché selon status
    • 2 tests UI NewManual — panneau clauses + exclusions
      • tests templates Typst, mocks
  • typecheck zéro warning sur 13 packages
  • lint Biome zéro warning sur 515 fichiers
  • Test d'intégration Typst CLI : compile audit-trail.typ réellement et vérifie %PDF-
  • Migration DB 0005_quote_clauses.sql + journal Drizzle + migrations-embedded.ts régénéré

Test plan

  • bun install (nouvelle dépendance @fakt/legal dans @fakt/db)
  • bun run typecheck — zéro warning attendu
  • bun run lint — zéro warning attendu
  • bun run test — 1072 tests attendus
  • Smoke test manuel desktop : créer un devis avec clauses cochées → vérifier rendu PDF (clauses avant CGV) → signer → vérifier que « Rapport d'audit » apparaît → télécharger → vérifier hashes complets et chronologie
  • Compatibilité ascendante : ouvrir un devis créé avant la migration → quote.clauses doit retourner [] (NULL en DB → fail-safe)

Hors scope (intentionnel)

  • Édition utilisateur du catalogue de clauses (V0.4+ via clause_templates user-defined)
  • Rapport d'audit sur les factures (V0.3+ avec sceau auto à l'émission)
  • Intégration IA pour suggérer des clauses (V0.4+, l'IA continue de remplir conditions texte libre)
  • Webhooks sortants / lien public de signature (V0.2 self-host minimum)

…ables

Inspirées du comparatif FAKT vs DocuSeal, deux features mode-1 compatibles
ajoutées en V0.1.x :

1. Rapport d'audit PDF lisible (audit-trail.typ)
   - Bouton « Rapport d'audit » sur les devis signés
   - PDF présentable juge / avocat : métadonnées document, chaîne SHA-256
     complète, journal d'événements, mention vérification PAdES
   - Inspiration : DocuSeal lib/submissions/generate_audit_trail.rb

2. Clauses contractuelles cochables (CLAUSE_CATALOG)
   - 8 clauses pré-rédigées : acompte 30/50, garantie 6/12, IP cession/
     licence, limitation de responsabilité, juridiction FR
   - Exclusions mutuelles automatiques (deposit-30 vs deposit-50, etc.)
   - Persistées en JSON sur quotes.clauses (migration 0005)
   - Insérées dans le PDF avant les CGV légales

Plan : docs/superpowers/plans/2026-05-02-audit-trail-and-conditional-clauses.md

Validation : 1072 tests verts, typecheck zéro warning, lint zéro warning,
migration DB 0005 + embedded migrations régénérées.

Signed-off-by: Tom Andrieu <andrieutom30@gmail.com>
@Seeyko Seeyko merged commit 71b231b into main May 3, 2026
4 of 6 checks passed
@Seeyko Seeyko deleted the feat/audit-trail-and-conditional-clauses branch May 3, 2026 15:11
Seeyko added a commit that referenced this pull request May 3, 2026
Workflow V0.2 acté dans `docs/roadmap-post-v0.1.md` §2 (Option B).

À l'émission d'un devis, FAKT calcule maintenant le SHA-256 du texte
normalisé du PDF officiel (via `pdf-extract` Rust + commande Tauri
`compute_pdf_text_hash`) et le persiste sur `quotes.original_text_hash`.

À l'import retour, un nouveau bouton « Importer signature client » sur
les devis `sent` ouvre un modal :
- Sélection du PDF retourné par le client (file picker)
- Champ email/nom du signataire
- Vérification du hash texte du PDF importé contre celui stocké
- Si match → store_signed_pdf + signature event chaîné + activity event
  `quote_signed_by_client_imported` + transition sent → signed
- Si mismatch → modal de confirmation forcée affichant les deux hashes
  tronqués (8…8) avec avertissement (annotation/altération). Force OK :
  l'audit event consigne la divergence.

La chaîne d'audit reste cryptographiquement intègre : le calcul de
`previousEventHash` passe par la commande Rust
`compute_signature_event_self_hash` (pas de réimplémentation TS).

Plan : docs/superpowers/plans/2026-05-03-import-signed-quote.md

Validation :
- 1045+ tests verts (sidecar original-text-hash, DB round-trip, UI bouton,
  Rust normalize_text + audit chain hash)
- typecheck zéro warning sur 13 packages
- lint Biome zéro warning sur 516 fichiers
- cargo check OK (nouvelle dep `pdf-extract = "0.7"`)

Note merge : conflit attendu avec PR #2 (clauses) sur :
- `0005_*.sql` — renommer en `0006_quote_original_text_hash.sql` au rebase
- `_journal.json` + `migrations-embedded.ts` — régénérer
- `Quote` interface (shared) — merge naturel

Signed-off-by: Tom Andrieu <andrieutom30@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant