feat(quotes,audit): rapport d'audit PDF + clauses contractuelles cochables#2
Merged
Merged
Conversation
…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>
15 tasks
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 :
lib/submissions/generate_audit_trail.rb)conditions_modal.vue, simplifié)Plan superpowers complet :
docs/superpowers/plans/2026-05-02-audit-trail-and-conditional-clauses.mdFeature 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 :
Fichiers clés :
packages/pdf/templates/audit-trail.typ(nouveau)packages/pdf/src/context-builder.ts—buildAuditTrailContextapps/desktop/src-tauri/src/pdf/render.rs—"audit-trail"ajoutéapps/desktop/src/routes/quotes/Detail.tsx— bouton + handlerFeature 2 — Clauses contractuelles cochables
Catalogue figé de 8 clauses pré-rédigées :
Quand l'utilisateur coche une option exclusive, l'autre se décoche automatiquement (pas de contradiction possible).
Stockées en JSON sur
quotes.clauses(migration0005_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.sqlpackages/db/src/queries/quotes.ts—parseClauses/serializeClausesapps/desktop/src/routes/quotes/QuoteForm.tsx—ClausesPanelpackages/pdf/templates/quote.typ— nouvelle sectionValidation
clauses(legal) — exclusions, hydrate, round-trip, catalogueaudit-trail-context(pdf) — tri chronologique, mapping FRquotes.clausesDetail— bouton audit visible/caché selon statusNewManual— panneau clauses + exclusionsaudit-trail.typréellement et vérifie%PDF-0005_quote_clauses.sql+ journal Drizzle +migrations-embedded.tsrégénéréTest plan
bun install(nouvelle dépendance@fakt/legaldans@fakt/db)bun run typecheck— zéro warning attendubun run lint— zéro warning attendubun run test— 1072 tests attendusquote.clausesdoit retourner[](NULL en DB → fail-safe)Hors scope (intentionnel)
clause_templatesuser-defined)conditionstexte libre)