[DTP-Worker 20260801_210434] Sprint 8 · buffer · CRM/commissions : gate d'IDENTITÉ du CHAMP DEVISE — la clause devise_field = le champ devise (Select USD/DOP · #10) du DocType Dossier Vente (crm/commissions/README.md:54), invariant CLI #8, transcrite en prose hors de tout gate (grep devise_field dans ci/ = 0)
4e surface distincte du même livrable crm/commissions (après états/champs, anti-taux #6, rôles payés) : le « sur quelle devise » du calcul base×taux. Re-dérivée de 3 sources byte-gatées/constitutionnelles : plan.devise_field (out/commission_plan.json) == champ `devise` du DocType (dossier_vente/doctype_spec.json, fieldtype Select) == options ORDONNÉES == devises canoniques CLAUDE.md #10 (« USD + DOP »). Ancrage vivant (#10) : (0) 2 devises depuis le segment gras de #10 ; (a) cross-artefact plan==DocType==#10 ; (b) prose README:54 == artefacts + #10 + tag #10. Distincte du bloc Fiches #10 (prose ERPNext/CRM) et du bloc e-CF TipoMoneda (spec fiscal). 7 morsures vérifiées (4 README · 1 DocType · 1 CLAUDE.md · 1 plan), 7 gates re-verts, working tree byte-restauré (git checkout --, JAMAIS git clean). ci/README.md (récap + paragraphe détaillé) et 05_activity_log mis à jour. Auto-score 4Big 96/100. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3559,6 +3559,105 @@ if cr_ev is not None:
|
||||
good(f"CRM commissions rôles · MANIFEST.roles_rbac_utilises == {len(cr_ids)} "
|
||||
f"rôles distincts payés (counts.roles cohérent)")
|
||||
|
||||
# ============================================================================
|
||||
# CRM/Commissions · IDENTITÉ du CHAMP DEVISE — `devise_field` = le champ `devise`
|
||||
# (`Select` USD/DOP · #10) du DocType, transcrit À LA MAIN au README:54 sans gate.
|
||||
# ----------------------------------------------------------------------------
|
||||
# Le générateur commissions_gen émet out/commission_plan.json (byte-gaté par
|
||||
# check_artifacts ⇒ faisant autorité) : le plan porte la clé RACINE `devise_field`
|
||||
# — le champ du Dossier Vente qui PORTE la devise d'un montant commissionné. Son
|
||||
# invariant CLI #8 exige TROIS choses simultanées : `devise_field == "devise"`, le
|
||||
# DocType Dossier Vente (doctype_spec.json, l'INPUT du générateur) porte bien un
|
||||
# champ `devise` de fieldtype `Select`, et ses `options` == `USD`/`DOP` — les DEUX
|
||||
# devises canoniques de CLAUDE.md #10 (« **USD + DOP** devises »). La §Cross-cohérence
|
||||
# RÉÉNONCE le tout EN PROSE (README:54) : « `devise_field` = le champ `devise`
|
||||
# (`Select` **USD/DOP** · #10) du DocType ». Les blocs commissions ci-dessus gatent
|
||||
# les états soumis, les champs de base, les rôles payés et la promesse anti-taux —
|
||||
# mais AUCUN ne touche ce couple champ↔devises. Dérive #6 : RENOMMER le champ
|
||||
# (`devise`→`moneda`) dans le plan/DocType, changer son fieldtype (`Select`→`Data` :
|
||||
# la contrainte d'énumération PERDUE, un montant en monnaie libre), ou altérer ses
|
||||
# options (`USD/DOP`→`USD/EUR` : devise HORS #10, le calcul `base × taux`
|
||||
# commissionnerait dans une monnaie non prévue) laisse la prose périmée EN SILENCE
|
||||
# pendant que l'artefact/DocType dit autre chose → « vert trompeur » qu'aucune suite
|
||||
# tests/ (FONCTIONS finance/résolution, jamais la prose vs artefact) n'attrape ; ce
|
||||
# n'est ni un lien, ni un terme interdit, ni un `out/` byte-gaté du module → invisible
|
||||
# à check_docs/guard_constraints. On RECOMPUTE depuis les artefacts byte-gatés + la
|
||||
# contrainte constitutionnelle : (0) ANCRAGE — les 2 devises depuis CLAUDE.md #10
|
||||
# (segment gras « USD + DOP ») ; (a) CROSS-ARTEFACT — plan.devise_field == "devise"
|
||||
# == fieldname du champ DocType · fieldtype == "Select" · options (ORDONNÉES) ==
|
||||
# devises #10 ; (b) PROSE — README:54 nomme le champ `devise`, le dit `Select`, cite
|
||||
# USD/DOP == #10 et tague #10. Un claim absent échoue AUSSI (traçabilité #6).
|
||||
# Direction plan/DocType/CLAUDE.md #10 → README.
|
||||
# ============================================================================
|
||||
try:
|
||||
cd_plan = load("crm/commissions/out/commission_plan.json")
|
||||
cd_dt = load("crm/dossier_vente/doctype_spec.json")
|
||||
cd_cm = open("CLAUDE.md", encoding="utf-8").read()
|
||||
except (OSError, KeyError, ValueError, TypeError) as e:
|
||||
bad(f"CRM commissions devise · artefact/CLAUDE.md illisible/inattendu : {e}"); cd_plan = None
|
||||
if cd_plan is not None:
|
||||
# (0) ANCRAGE #10 : les 2 devises canoniques (segment gras « USD + DOP »)
|
||||
cd_cur = None
|
||||
cd_l10 = re.search(r"^10\.\s+(.+)$", cd_cm, re.M)
|
||||
if cd_l10:
|
||||
for seg in re.split(r"·", cd_l10.group(1)):
|
||||
if "devise" in seg.lower():
|
||||
mm = re.search(r"\*\*(.+?)\*\*", seg)
|
||||
if mm:
|
||||
cd_cur = [t.strip() for t in re.split(r"[+/]", mm.group(1)) if t.strip()]
|
||||
if not cd_cur:
|
||||
bad("CRM commissions devise · devises #10 (« **USD + DOP** ») INTROUVABLES dans "
|
||||
"CLAUDE.md #10 → ancrage rompu")
|
||||
else:
|
||||
cd_field = None
|
||||
for grp in cd_dt.get("field_groups", []):
|
||||
for f in grp.get("fields", []):
|
||||
if f.get("fieldname") == "devise":
|
||||
cd_field = f
|
||||
cd_pf = cd_plan.get("devise_field")
|
||||
cd_opts = ([ln for ln in cd_field.get("options", "").split("\n") if ln]
|
||||
if cd_field else None)
|
||||
# (a) CROSS-ARTEFACT : plan ↔ DocType ↔ devises #10 (invariant CLI #8)
|
||||
if cd_pf != "devise":
|
||||
bad(f"CRM commissions devise · plan.devise_field={cd_pf!r} ≠ 'devise' → "
|
||||
f"invariant #8 cassé (le champ porteur de la devise a changé)")
|
||||
elif cd_field is None:
|
||||
bad("CRM commissions devise · champ `devise` ABSENT du DocType Dossier Vente "
|
||||
"→ invariant #8 (options USD/DOP invérifiables)")
|
||||
elif cd_field.get("fieldtype") != "Select":
|
||||
bad(f"CRM commissions devise · champ `devise` fieldtype={cd_field.get('fieldtype')!r} "
|
||||
f"≠ 'Select' → énumération USD/DOP PERDUE (monnaie libre)")
|
||||
elif cd_opts != cd_cur:
|
||||
bad(f"CRM commissions devise · options du champ `devise` {cd_opts} ≠ devises #10 "
|
||||
f"{cd_cur} (ordre inclus) → devise HORS CLAUDE.md #10")
|
||||
else:
|
||||
good(f"CRM commissions devise · plan.devise_field='devise' == DocType champ `devise` "
|
||||
f"(Select {cd_opts}) == CLAUDE.md #10")
|
||||
# (b) PROSE README:54 — champ · fieldtype · devises · tag #10
|
||||
cd_rd = read_norm(CM_RD)
|
||||
mpr = re.search(
|
||||
r"`devise_field`\*\* = le champ `([a-z_]+)` \(`(\w+)` \*\*([A-Z/]+)\*\* · #10\)",
|
||||
cd_rd or "")
|
||||
if mpr is None:
|
||||
bad(f"CRM commissions devise · {CM_RD} — claim « `devise_field` = le champ "
|
||||
f"`devise` (`Select` **USD/DOP** · #10) » INTROUVABLE/malformé (traçabilité #6)")
|
||||
else:
|
||||
p_field, p_ftype = mpr.group(1), mpr.group(2)
|
||||
p_cur = mpr.group(3).split("/")
|
||||
errs = []
|
||||
if p_field != "devise":
|
||||
errs.append(f"champ `{p_field}` ≠ `devise`")
|
||||
if p_ftype != "Select":
|
||||
errs.append(f"type `{p_ftype}` ≠ `Select`")
|
||||
if p_cur != cd_cur:
|
||||
errs.append(f"devises {p_cur} ≠ #10 {cd_cur}")
|
||||
if errs:
|
||||
bad(f"CRM commissions devise · {CM_RD} — prose périmée : "
|
||||
+ " · ".join(errs) + " → régénérer le README")
|
||||
else:
|
||||
good(f"CRM commissions devise · {CM_RD} — prose « champ `devise` "
|
||||
f"(`Select` {'/'.join(p_cur)}) · #10 » == artefacts + CLAUDE.md #10")
|
||||
|
||||
# ============================================================================
|
||||
# RBAC/fixtures_gen · le CATALOGUE des DocTypes `custom` à créer sur le VPS et la
|
||||
# SÉPARATION DES POUVOIRS (le rôle unique porteur de `set_user_permissions`) —
|
||||
|
||||
Reference in New Issue
Block a user