[DTP-Worker] Sprint 8 · buffer · Fiscal/ecf_dgii (2e surface) : la CROSS-COHÉRENCE e-CF ↔ workflow ↔ DocType ↔ RBAC — le CŒUR du livrable — énumérait EN PROSE trois données data-derived (états qui ÉMETTENT un e-CF · champs Currency de base · FormaPago défaut ancrée sur CLAUDE.md #10/Cardnet) sans AUCUN gate d'IDENTITÉ. Le bloc e-NCF existant ne gate QUE le FORMAT de l'identifiant (E+tipo(2)+seq(10)=13) — jamais ces trois ensembles/valeur. Ils sont DATA-DERIVED de out/ecf_plan.json (byte-gaté par check_artifacts) : README:48 « seulement réservation et contrat » = {emission_events[].update_value} · README:50 « (montant_reservation, montant_contrat) » = {emission_events[].base_field} · README:59 « FormaPago défaut = 3 (Tarjeta) … via Cardnet (#10) » = forma_pago_defaut (ancré sur CLAUDE.md #10 : Cardnet = carte, pas Stripe). PIÈGE : check_artifacts ne prouve QUE ecf_plan==build (byte-for-byte) et le bloc racine ne gate QUE le compte de tests e-CF (« 39 ») → AJOUTER un évènement d'émission (facturer un devis) · en RETIRER un · RENOMMER un base_field · CHANGER forma_pago_defaut (Cardnet abandonné → code 2 Transfert) laissait la prose périmée pendant que l'artefact dit autre chose → l'agent ERPNext Backend câblerait le mauvais évènement / la mauvaise FormaPago (le risque même que la cross-cohérence veut prévenir) — « vert trompeur » qu'aucune suite tests/ (qui teste des FONCTIONS ncf/résolution, pas la prose) n'attrape. Même patron EXACT que la cross-cohérence CRM/Commissions déjà gatée (base_field + états soumis). Nouveau bloc « Fiscal cross-cohérence » dans ci/check_readme_claims.sh (après le bloc e-NCF) : (1) états émetteurs recomputés de emission_events[].update_value, prose exigée EXACTE (set-diff · accents NFKD) ; (2) champs de base recomputés de emission_events[].base_field, prose EXACTE ; (3) FormaPago défaut = forma_pago_defaut.code + libellé (⊆ catalogue) + ancrage Cardnet à proximité. Cohérences croisées en bonus (mordent un plan INTERNEMENT incohérent) : forma_pago_defaut.code ∈ catalogue formas_pago · libellé prose ⊆ libellé catalogue du code · ensembles NON VIDES. Un claim absent échoue AUSSI. 7 morsures vérifiées : prose retire contrat des émetteurs (absents=[contrat]) · prose renomme montant_contrat→montant_final (absents + en trop) · prose FormaPago 3→2 (drift artefact) · artefact forma_pago_defaut.code 3→2 (prose périmée) · artefact ajoute l'évènement devis (états ET base_field mordus · absents=[devis]) · FormaPago prose supprimée (INTROUVABLE) · artefact code→99 hors catalogue (incohérence interne) ; restauré = green : émetteurs [contrat,reservation] · base [montant_contrat,montant_reservation] · FormaPago 3 (Tarjeta) ancré Cardnet · exit 0. État courant : aucune valeur périmée (anti-invention #6, rien à réécrire) — le défaut est la surface ungated. ci/README.md (table + détail « 2e surface e-CF DGII ») mis à jour · working tree byte-restauré (git clean) · 7 gates re-verts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+28
-1
File diff suppressed because one or more lines are too long
@@ -1675,6 +1675,112 @@ if encf is not None:
|
||||
re.search(r"`([A-Z])\s*\+\s*tipoeCF\((\d+)\)\s*\+\s*"
|
||||
r"secuencia\((\d+)\)`", ecf_fi), False)
|
||||
|
||||
# ============================================================================
|
||||
# Fiscal · e-CF DGII (2e surface du MÊME README) · la CROSS-COHÉRENCE
|
||||
# (« le cœur du livrable ») — ENSEMBLES/valeur data-derived transcrits À LA MAIN
|
||||
# sans AUCUN gate d'IDENTITÉ. Le bloc e-NCF ci-dessus ne gate QUE le FORMAT de
|
||||
# l'identifiant ; la section « Cross-cohérence e-CF ↔ workflow ↔ DocType ↔ RBAC »
|
||||
# ÉNUMÈRE trois données dérivées de out/ecf_plan.json (byte-gaté par
|
||||
# check_artifacts ⇒ faisant autorité) :
|
||||
# • README:48 « seulement réservation et contrat » — l'ensemble des états qui
|
||||
# ÉMETTENT un e-CF = {emission_events[].update_value}.
|
||||
# • README:50 « (`montant_reservation`, `montant_contrat`) » — l'ensemble des
|
||||
# champs Currency de base = {emission_events[].base_field}.
|
||||
# • README:59 « FormaPago défaut = 3 (Tarjeta) … via Cardnet (#10) » — le code +
|
||||
# libellé de forma_pago_defaut, ANCRÉ sur CLAUDE.md #10 (Cardnet = carte).
|
||||
# Aucun gate ne comparait ces valeurs à l'artefact : AJOUTER un évènement
|
||||
# d'émission (facturer un devis) · en RETIRER un · RENOMMER un base_field ·
|
||||
# CHANGER forma_pago_defaut (Cardnet abandonné → code 2 Transfert) ferait dériver
|
||||
# la prose en silence pendant que l'ecf_plan byte-gaté dit autre chose — l'agent
|
||||
# ERPNext Backend câblerait le mauvais évènement / la mauvaise FormaPago (le risque
|
||||
# même que la cross-cohérence veut prévenir) — « vert trompeur » qu'aucune suite
|
||||
# tests/ (qui teste des FONCTIONS ncf/résolution, pas la prose) n'attrape. Même
|
||||
# patron EXACT que la cross-cohérence CRM/Commissions (base_field + états soumis).
|
||||
# On RECOMPUTE chaque ensemble/valeur depuis l'artefact (zéro duplication du
|
||||
# contrat du générateur #6) et on exige que la prose l'encode EXACTEMENT (absent ET
|
||||
# en trop via set-diff · accents/casse normalisés). Cohérences croisées en bonus
|
||||
# (mordent un plan INTERNEMENT incohérent) : forma_pago_defaut.code ∈ catalogue
|
||||
# formas_pago · libellé prose ⊆ libellé catalogue du code · ensembles NON VIDES.
|
||||
# Un claim absent échoue AUSSI (traçabilité).
|
||||
# ============================================================================
|
||||
ECF_RD2 = f"{D}/fiscal/ecf_dgii/README.md"
|
||||
def _ecf_acc(s):
|
||||
return "".join(c for c in unicodedata.normalize("NFKD", s)
|
||||
if unicodedata.category(c) != "Mn").lower()
|
||||
try:
|
||||
ecfp = load("fiscal/ecf_dgii/out/ecf_plan.json")
|
||||
ecf_ev = ecfp["emission_events"]
|
||||
ecf_uv = {_ecf_acc(e["update_value"]) for e in ecf_ev}
|
||||
ecf_bf = {e["base_field"] for e in ecf_ev}
|
||||
ecf_fpd = ecfp["forma_pago_defaut"]
|
||||
ecf_fp_code = str(ecf_fpd["code"])
|
||||
ecf_fp_cat = {str(f["code"]): f["label"] for f in ecfp["formas_pago"]}
|
||||
except (OSError, KeyError, ValueError, TypeError) as e:
|
||||
bad(f"Fiscal cross-cohérence · artefact ecf_plan illisible/inattendu : {e}"); ecf_ev = None
|
||||
if ecf_ev is not None:
|
||||
# bonus : plan INTERNEMENT cohérent (ensembles non vides · défaut ∈ catalogue)
|
||||
if not ecf_uv or not ecf_bf:
|
||||
bad("Fiscal cross-cohérence · aucun évènement d'émission (plan vide) → vérifier le plan")
|
||||
elif ecf_fp_code not in ecf_fp_cat:
|
||||
bad(f"Fiscal cross-cohérence · forma_pago_defaut.code={ecf_fp_code} ABSENT du "
|
||||
f"catalogue formas_pago {sorted(ecf_fp_cat)} → plan incohérent")
|
||||
else:
|
||||
good(f"Fiscal cross-cohérence · forma_pago_defaut {ecf_fp_code} "
|
||||
f"({ecf_fp_cat[ecf_fp_code]}) ∈ catalogue formas_pago · émission {sorted(ecf_uv)} non vide")
|
||||
ecf_rd2 = read_norm(ECF_RD2)
|
||||
if ecf_rd2 is not None:
|
||||
# 1) README:48 — états émetteurs « seulement réservation et contrat »
|
||||
me = re.search(r"seulement\s+([^.]+)\.", ecf_rd2)
|
||||
if me is None:
|
||||
bad(f"Fiscal cross-cohérence · {ECF_RD2} — énumération des états émetteurs "
|
||||
f"INTROUVABLE (attendu {sorted(ecf_uv)})")
|
||||
else:
|
||||
es = {_ecf_acc(t.strip()) for t in re.split(r"\bet\b|[,/]", me.group(1)) if t.strip()}
|
||||
if es == ecf_uv:
|
||||
good(f"Fiscal cross-cohérence · {ECF_RD2} — états émetteurs {sorted(es)} == "
|
||||
f"ecf_plan.emission_events[].update_value")
|
||||
else:
|
||||
miss = sorted(ecf_uv - es); extra = sorted(es - ecf_uv)
|
||||
detail = (f" · absents={miss}" if miss else "") + (f" · en trop={extra}" if extra else "")
|
||||
bad(f"Fiscal cross-cohérence · {ECF_RD2} — énumère {sorted(es)} MAIS plan "
|
||||
f"{sorted(ecf_uv)}{detail} → régénérer le README")
|
||||
# 2) README:50 — champs de base « (`montant_reservation`, `montant_contrat`) »
|
||||
mf = re.search(r"Currency réel\*\* du DocType Dossier Vente\s*\(([^)]*)\)", ecf_rd2)
|
||||
if mf is None:
|
||||
bad(f"Fiscal cross-cohérence · {ECF_RD2} — énumération des champs de base "
|
||||
f"INTROUVABLE (attendu {sorted(ecf_bf)})")
|
||||
else:
|
||||
bs = set(re.findall(r"`([a-z_]+)`", mf.group(1)))
|
||||
if bs == ecf_bf:
|
||||
good(f"Fiscal cross-cohérence · {ECF_RD2} — champs de base {sorted(bs)} == "
|
||||
f"ecf_plan.emission_events[].base_field")
|
||||
else:
|
||||
miss = sorted(ecf_bf - bs); extra = sorted(bs - ecf_bf)
|
||||
detail = (f" · absents={miss}" if miss else "") + (f" · en trop={extra}" if extra else "")
|
||||
bad(f"Fiscal cross-cohérence · {ECF_RD2} — énumère {sorted(bs)} MAIS plan "
|
||||
f"{sorted(ecf_bf)}{detail} → régénérer le README")
|
||||
# 3) README:59 — FormaPago défaut « = 3 (Tarjeta) … via Cardnet (#10) »
|
||||
mp = re.search(r"FormaPago défaut\s*=\s*(\d+)\s*\(([^)]*)\)", ecf_rd2)
|
||||
if mp is None:
|
||||
bad(f"Fiscal cross-cohérence · {ECF_RD2} — FormaPago défaut INTROUVABLE "
|
||||
f"(attendu code {ecf_fp_code} · {ecf_fp_cat.get(ecf_fp_code,'?')})")
|
||||
else:
|
||||
got_code = mp.group(1); got_lbl = _ecf_acc(mp.group(2).strip())
|
||||
cat_lbl = _ecf_acc(ecf_fp_cat.get(ecf_fp_code, ""))
|
||||
has_cardnet = "cardnet" in _ecf_acc(ecf_rd2[mp.start():mp.start() + 160])
|
||||
if got_code == ecf_fp_code and got_lbl and got_lbl in cat_lbl and has_cardnet:
|
||||
good(f"Fiscal cross-cohérence · {ECF_RD2} — FormaPago défaut {got_code} "
|
||||
f"({mp.group(2).strip()}) == forma_pago_defaut · ancré Cardnet (#10)")
|
||||
elif got_code != ecf_fp_code:
|
||||
bad(f"Fiscal cross-cohérence · {ECF_RD2} — FormaPago défaut = {got_code} MAIS "
|
||||
f"forma_pago_defaut.code = {ecf_fp_code} → régénérer le README")
|
||||
elif not (got_lbl and got_lbl in cat_lbl):
|
||||
bad(f"Fiscal cross-cohérence · {ECF_RD2} — libellé FormaPago {mp.group(2).strip()!r} "
|
||||
f"≠ catalogue code {ecf_fp_code} ({ecf_fp_cat.get(ecf_fp_code)!r}) → régénérer")
|
||||
else:
|
||||
bad(f"Fiscal cross-cohérence · {ECF_RD2} — FormaPago défaut sans ancrage "
|
||||
f"Cardnet (#10) à proximité → régénérer le README")
|
||||
|
||||
# ============================================================================
|
||||
# CRM/Commissions · barème vendeurs — la CROSS-COHÉRENCE (« cœur du livrable »)
|
||||
# transcrite À LA MAIN dans le README sans AUCUN gate d'IDENTITÉ.
|
||||
|
||||
Reference in New Issue
Block a user