Le champ que `Encabezado.TipoMoneda` porte dans chaque e-CF DGII — `moneda.options = ["USD","DOP"]` — vit dans `ecf_spec.json` ET, byte-gaté par check_artifacts, dans `out/ecf_plan.json`. C'est une valeur CANONIQUE de CLAUDE.md #10 (« **USD + DOP** devises ») dont le README se réclame « #10 » (README:37/57/77). Or byte-gater une constante prouve la REPRODUCTIBILITÉ, PAS l'ANCRAGE : check_artifacts prouve ecf_plan==build depuis le spec, JAMAIS que spec[moneda]==#10. Les blocs fiscaux amont gatent le FormaPago (Cardnet · #10) + états/champs émetteurs, AVEUGLES au jeu de devises ; le bloc « Fiches #10 » ancre les fiches ERPNext/CRM, AVEUGLE au module fiscal. Piège #6 : RENOMMER/ÉTENDRE les devises dans CLAUDE.md #10 (`USD + DOP`→`USD + EUR`, ou +`EUR`) ET dans le spec de façon cohérente reste byte-VERT tout en faisant émettre à l'e-CF un `TipoMoneda` d'une devise qui CONTREDIT le mandat — « vert trompeur » de la classe de la marque SEO §Entités, la persona Chat OTOIA ou les tokens branding #4, qu'aucune suite tests/ (FONCTIONS de génération, jamais l'ancre à CLAUDE.md) n'attrape. L'agent ERPNext Backend câblerait TipoMoneda hors mandat. Correctif de déclaration : nouveau champ `moneda.options_source` (ecf_spec.json + requis dans ecf.schema.json) citant « CLAUDE.md #10 — devises **USD + DOP** », DISTINCT de `source` (taux TipoCambio, reste null/a_confirmer côté VPS · #8). Artefact régénéré (byte-gate re-verte · 39 tests module verts). Gate ajouté (ci/check_readme_claims.sh, bloc « Fiscal · ecf_dgii — le JEU DE DEVISES `moneda` … ANCRÉ sur CLAUDE.md #10 ») : RÉUTILISE exp["devises"] déjà recomputé depuis #10 (zéro duplication) ; (a) spec[moneda].options == devises #10 ORDRE exact ; (b) artefact byte-gaté idem ; (c) options_source cite CLAUDE.md #10 + chaque devise ; (d) prose README qui se réclame de #10 == jeu exact + toute mention moneda/devise d'une paire concordante (scan ancré sur contexte, évite le faux positif `RNC / ITBIS`). 7 morsures vérifiées (CLAUDE.md #10 rename = 4 bites en cascade · spec options régénéré = le vrai silent green · artefact seul · reorder · options_source sans ancre · README ligne #10 · README table), restauré vert, 7 gates re-verts. ci/README.md (clause récap + paragraphe « 4e surface du MÊME README ») mis à jour. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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/,
le DocType porteur ../../crm/dossier_vente/
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_cambioet les endpoints Compupar portentnull+source: null+a_confirmer: true.- Un invariant refuse toute de ces valeurs fixée sans
source. - Le composeur d'e-NCF reste
Nonetant qu'un opérande (tipo / secuencia) manque — jamais fabriqué ; la formule reste affichée (traçabilité façon../../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_valuedoit exister dansworkflow_vente_spec.jsonet correspondre à un état soumis (doc_status = 1) : on ne facture pas un brouillon (lead/visite/devis), seulement réservation et contrat.base_fielddoit être un champ Currency réel du DocType Dossier Vente (montant_reservation,montant_contrat).role_iddoit être résolu depuisrbac_50_roles.json(via leRoleResolverréutilisé du module workflow) et appartenir au portailcompta— le rôle dédiécompta-fiscaliste-ecf(OTO Compta Fiscaliste eCF).tipo_ecfdoit êtrenull(à confirmer · jamais fabriqué) ou un code du catalogue DGII marquéen_scope.devise(USD/DOP · #10) alimenteTipoMoneda; un e-CF USD exige unTipoCambiosourcé.- FormaPago défaut = 3 (Tarjeta) car encaissements OTO via Cardnet (#10).
Utilisation
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)
- Conformité au schéma de sortie. 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_iddu portail compta +erpnext_role_namecohérent RBAC. 6.tipo_ecfnull (à 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=deviseUSD/DOP alignée sur le DocType ; TipoCambio null ou sourcé. 10. Anti-invention : RNC / ITBIS jamais sanssource. 11.field_map: chaquedossier_fieldréel. 12. Unicité des évènements + comptes du manifeste +tipos_en_scopealigné 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 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)
- 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. - 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 lasecuenciadepuis 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).