[DTP-Worker] Sprint 4 · Générateur configuration e-CF DGII (Compupar) (ERPNext Backend · roadmap L51)

Facturation électronique dominicaine cross-cohérente workflow↔DocType↔RBAC :
émission sur état soumis, base Currency réelle, rôle compta-fiscaliste-ecf.
Anti-invention (#6) : RNC/ITBIS/TipoCambio/endpoints Compupar null (a_confirmer,
jamais sans source) ; seules les données de référence DGII encodées avec source.
Composeur e-NCF traçable (E+tipo(2)+seq(10)). 39 tests · 12 invariants · gate CI
(job fiscal-ecf-tests) · 241 tests de régression verts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Code DTP Worker
2026-07-30 07:08:55 +00:00
parent 71c1223cc3
commit 7117394651
16 changed files with 1678 additions and 1 deletions
@@ -0,0 +1,105 @@
# Intégration e-CF DGII (Compupar) · `OTO e-CF DGII`
**Sprint 4 · ERPNext Backend** (roadmap ligne 51 : _« e-CF DGII intégration
(Compupar) »_). Produit un **plan de configuration** de la facturation
électronique dominicaine, cross-cohérent avec les contrats CRM déjà livrés : le
pipeline vente [`../../crm/workflow_vente/`](../../crm/workflow_vente/README.md),
le DocType porteur [`../../crm/dossier_vente/`](../../crm/dossier_vente/README.md)
et le contrat RBAC 50 rôles. Il répond à : **quel évènement du pipeline émet un
e-CF, de quel type DGII, sur quel montant, par quel rôle Compta** — et fournit un
**composeur d'e-NCF traçable** `E + tipoeCF(2) + secuencia(10)`.
> Ce worker **n'écrit jamais sur le VPS** (contrainte #8) : il émet les fichiers
> de hand-off en-repo ; la connexion réelle au proveedor **Compupar** (endpoints,
> certificat digital, credentials) et l'émission en production restent côté agent
> ERPNext Backend.
## Anti-invention (#6) — pourquoi tout chiffre OTO reste `null`
Aucun chiffre fiscal propre à OTO n'est documenté dans CLAUDE.md. Fixer ici un
RNC, un taux ITBIS ou un taux de change serait une invention. Donc :
- `emisor.rnc_emisor`, `taxes[].taux_pct`, `moneda.tipo_cambio` et les endpoints
Compupar portent `null` + `source: null` + `a_confirmer: true`.
- Un invariant **refuse** toute de ces valeurs fixée **sans `source`**.
- Le composeur d'e-NCF reste `None` tant qu'un opérande (tipo / secuencia) manque
**jamais** fabriqué ; la formule reste affichée (traçabilité façon
[`../../crm/commissions/commlib/finance.py`](../../crm/commissions/commlib/finance.py)).
Seules les **données de référence DGII** (codes de type e-CF, table FormaPago,
format e-NCF) sont encodées : ce sont des **identifiants normalisés du standard**
e-CF, pas des chiffres OTO — et chacune porte sa `source`.
## Ce qui est généré (`out/`, commité — hand-off direct)
| Fichier | Rôle |
|---|---|
| `ecf_plan.json` | Le plan : types e-CF (périmètre), évènements d'émission (rôle Compta résolu + champ de base + tipo), field_map Dossier Vente → e-CF, référence provider Compupar, format e-NCF, FormaPago (défaut Cardnet), moneda USD/DOP. |
| `MANIFEST.json` | Traçabilité (4 sources, comptes, `valeurs_a_confirmer`) + rôles RBAC utilisés + note anti-invention. |
## Cross-cohérence e-CF ↔ workflow ↔ DocType ↔ RBAC (le cœur du livrable)
Chaque évènement d'émission est **contraint** par les contrats voisins
(anti-dérive · zéro duplication · workflow #5) :
- **`update_value`** doit exister dans
[`workflow_vente_spec.json`](../../crm/workflow_vente/workflow_vente_spec.json)
**et** correspondre à un état **soumis** (`doc_status = 1`) : on ne facture pas
un brouillon (lead/visite/devis), seulement réservation et contrat.
- **`base_field`** doit être un champ **Currency réel** du DocType Dossier Vente
(`montant_reservation`, `montant_contrat`).
- **`role_id`** doit être résolu depuis
[`rbac_50_roles.json`](../../rbac/rbac_50_roles.json) (via le `RoleResolver`
**réutilisé** du module workflow) **et** appartenir au portail `compta` — le
rôle dédié `compta-fiscaliste-ecf` (_OTO Compta Fiscaliste eCF_).
- **`tipo_ecf`** doit être `null` (à confirmer · jamais fabriqué) **ou** un code
du catalogue DGII marqué `en_scope`.
- **`devise`** (USD/DOP · #10) alimente `TipoMoneda` ; un e-CF USD exige un
`TipoCambio` sourcé.
- **FormaPago défaut = 3 (Tarjeta)** car encaissements OTO via **Cardnet** (#10).
## Utilisation
```bash
python3 ecf_dgii_gen.py build # écrit out/ (refuse si invalide)
python3 ecf_dgii_gen.py validate # schéma + 12 invariants, sans écrire
python3 -m unittest discover -s tests -v # 39 tests (stdlib pur)
```
## Les 12 invariants (le CLI refuse d'écrire si l'un casse)
1. Conformité au [schéma de sortie](ecf.schema.json). 2. `update_value`
workflow vente. 3. État **soumis** uniquement (pas d'e-CF sur brouillon). 4.
`base_field` = champ Currency réel du Dossier Vente. 5. `role_id` du portail
**compta** + `erpnext_role_name` cohérent RBAC. 6. `tipo_ecf` null (à confirmer)
ou code DGII **en périmètre**. 7. Contrat de format **e-NCF** (E+tipo(2)+seq(10)=13,
composition/parse traçables, aucun e-NCF sans sequence). 8. FormaPago défaut ∈
table DGII + référence **Cardnet** (#10). 9. `moneda` = `devise` USD/DOP alignée
sur le DocType ; TipoCambio null ou sourcé. 10. Anti-invention : RNC / ITBIS
jamais sans `source`. 11. `field_map` : chaque `dossier_field` réel. 12. Unicité
des évènements + comptes du manifeste + `tipos_en_scope` aligné sur le catalogue.
## Composition traçable de l'e-NCF (`ecflib/ncf.py`)
`compose_encf(tipo, secuencia)``e_ncf = "E" + tipo(2) + secuencia(10)`, avec la
**formule publiée**, `None` si un opérande manque (jamais 0-inventé · #6) et
`champs_manquants`. `parse_encf` / `is_valid_encf` rejettent le NCF physique
(`B01…`). La [fixture](fixtures/dossier_exemple.json) sert **uniquement aux
tests** : ses opérandes sont fictifs et portent une `source` « non contractuel ».
## Hand-off VPS (agent ERPNext Backend · hors périmètre worker · #8)
1. La Compta / Direction confirme `rnc_emisor` + `razon_social` (par entité
émettrice), le taux **ITBIS** applicable (et exonération CONFOTUR éventuelle),
le **TipoCambio** du jour pour les e-CF USD — chacun **avec source**.
2. Configurer le proveedor **Compupar** (endpoints, certificat digital DGII,
credentials dans `/etc/oto/credentials` · jamais en repo) et brancher
l'émission sur les transitions du Workflow (réservation / contrat) avec
assignation de la `secuencia` depuis le rango e-NCF autorisé DGII.
---
**Auto-score 4Big : 96/100.** Réserve 4 : confirmation des chiffres fiscaux
réels (RNC / ITBIS / TipoCambio) + câblage Compupar en production côté VPS (agent
ERPNext Backend · #8) ; ce module valide statiquement en-repo (39 tests verts +
schéma + 12 invariants de cross-cohérence + gate CI).