feat: unified fiscal coverage - PIS/COFINS applicability + conditional RTC in one definition, one queue, one panel
- central/app/unified_fiscal_coverage.py: pure rules (PIS/COFINS NOT_APPLICABLE/REQUIRED/OPTIONAL/NEEDS_REVIEW, RTC only when the ERP switch is on) - agent: read-only loader (unified_fiscal.py) replaces the legacy/expanded module; findings feed the single operational state, coverage and blocker queue - hub: BLOCKED_FISCAL_DATA blockers (PIS/COFINS/RTC) share the queue; stale ones close on the next full pass; dashboard breakdown gains CEST/PIS/COFINS/RTC - panel: legacy/expanded coverage block removed; discreet 'Reforma Tributaria: Ativa/Desativada' - docs: unified_fiscal_completeness, pis_cofins_applicability, rtc_pis_cofins_model + official source manifests; read-only E2E script Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
"""READ-ONLY end-to-end of the UNIFIED fiscal coverage over a real Firebird database (default: TESTESOFICIAIS.FDB).
|
||||
|
||||
python scripts/unified_coverage_e2e.py [--db TESTESOFICIAIS] [--out docs/runs/unified_coverage_e2e.json]
|
||||
|
||||
Reads the products with the Agent's own reader (SELECT only), classifies them through the Hub, then computes the operational states
|
||||
BEFORE (decision only) and AFTER (decision + PIS/COFINS + RTC when enabled). The ERP's RTC switch is reported as found; a what-if run
|
||||
with RTC forced ON / OFF uses the pure evaluator only (nothing is written, PARAMETROS is never touched).
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT)); sys.path.insert(0, str(ROOT / "scripts")); sys.path.insert(0, str(ROOT / "agent" / "src"))
|
||||
from dotenv import load_dotenv # noqa: E402
|
||||
|
||||
load_dotenv(ROOT / ".env")
|
||||
import local_completion as lc # noqa: E402
|
||||
from central.app.operational_state import classify_product_state, summarize_operational_states # noqa: E402
|
||||
from gti_fiscal_agent import firebird_reader, unified_fiscal # noqa: E402
|
||||
from gti_fiscal_agent.client import HubClient # noqa: E402
|
||||
from gti_fiscal_agent.runner import to_hub_product # noqa: E402
|
||||
|
||||
PIS_COLS = ("COD_ENTRADA_PIS", "COD_SAIDA_PIS", "ALIQ_PIS", "COD_ENTRADA_COFINS", "COD_SAIDA_COFINS", "ALIQ_COFINS")
|
||||
|
||||
|
||||
def old_unknown(product):
|
||||
"""The previous model's UNKNOWN_APPLICABILITY: no product-level evidence of PIS/COFINS at all."""
|
||||
text = lambda v: "" if v is None else str(v).strip() # noqa: E731
|
||||
evidence = any(text(product.get(c)) for c in PIS_COLS) or text(product.get("PISCOFINS")).upper() == "S" or text(product.get("INCIDENCIA_PISCOFINS")).upper() not in ("", "NENHUMA")
|
||||
return not evidence
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(); ap.add_argument("--db", default="TESTESOFICIAIS"); ap.add_argument("--out", default="docs/runs/unified_coverage_e2e.json"); ap.add_argument("--crt", default=None); ap.add_argument("--fiscal-only", action="store_true", help="skip the Hub classification (decisions = EXACT_NCM); measures only the PIS/COFINS + RTC contribution")
|
||||
a = ap.parse_args()
|
||||
path = lc.allowlist()[a.db]; env = lc.env_for(path)
|
||||
products = firebird_reader.read_products(env)
|
||||
connection = firebird_reader._connect(env) # READ COMMITTED READ ONLY (Agent reader)
|
||||
try:
|
||||
cursor = connection.cursor()
|
||||
schema, _catalog, extras = unified_fiscal.load_rtc_context(cursor)
|
||||
fiscal_products = unified_fiscal.read_products(cursor, schema)
|
||||
cursor.execute("SELECT DISTINCT TRIM(CRT) FROM C000004"); crts = [r[0] for r in cursor.fetchall()]
|
||||
finally:
|
||||
connection.close()
|
||||
crt = a.crt or (crts[0] if len(crts) == 1 else None)
|
||||
client = HubClient(os.environ.get("HUB_URL", lc.HUB), os.environ["AGENT_DEV_TOKEN"])
|
||||
decisions = [{"status": "EXACT_NCM"} for _ in products] if a.fiscal_only else client.classify_batch([to_hub_product(p) for p in products])
|
||||
before_states = [classify_product_state(p, d) for p, d in zip(products, decisions)]
|
||||
before = summarize_operational_states(before_states)
|
||||
company = {"crt": crt}
|
||||
runs = {}
|
||||
for label, override in (("ERP_AS_FOUND", None), ("RTC_OFF_SIMULATION", False), ("RTC_ON_SIMULATION", True)):
|
||||
conn = firebird_reader._connect(env)
|
||||
try:
|
||||
out = unified_fiscal.assess_from_connection(conn, company, rtc_override=override)
|
||||
finally:
|
||||
conn.close()
|
||||
findings = out["findings"]
|
||||
states = [classify_product_state(p, d, fiscal=findings.get(str(p["CODIGO"]))) for p, d in zip(products, decisions)]
|
||||
summary = summarize_operational_states(states)
|
||||
by_type, by_category = Counter(), Counter()
|
||||
for f in findings.values():
|
||||
for kind in {(b["type"], b["category"]) for b in f["blockers"]}:
|
||||
by_type[kind[0]] += 1; by_category[kind[1]] += 1
|
||||
changed = [{"product": s["product_code"], "BEFORE_STATUS": b["state"], "AFTER_STATUS": s["state"], "REASON": sorted({x["type"] for x in s["fiscal_blockers"]}) or ("NEEDS_REVIEW" if s["state"] == "NEEDS_REVIEW" else "")}
|
||||
for b, s in zip(before_states, states) if b["state"] != s["state"]]
|
||||
total = summary["TOTAL_PRODUCTS"]
|
||||
runs[label] = {"rtc_enabled": out["context"]["rtc_enabled"], "TOTAL_PRODUCTS": total, "COMPLETE_PRODUCTS": summary["COMPLETE_PRODUCTS"], "SAFE_BLOCKED_PRODUCTS": summary["SAFE_BLOCKED"],
|
||||
"FAILED_PRODUCTS": summary["FAILED_TECHNICAL"], "UNCLASSIFIED_PRODUCTS": summary["UNCLASSIFIED"],
|
||||
"COVERAGE_PERCENT": round(100 * summary["COMPLETE_PRODUCTS"] / total, 2) if total else 0.0,
|
||||
"INVARIANT_PASS": total == summary["COMPLETE_PRODUCTS"] + summary["SAFE_BLOCKED"] + summary["FAILED_TECHNICAL"] + summary["UNCLASSIFIED"],
|
||||
"PIS_BLOCKERS": by_category["PIS"], "COFINS_BLOCKERS": by_category["COFINS"], "RTC_BLOCKERS": by_category["RTC"], "OTHER_BLOCKERS": by_category["OTHER"], "blockers_by_type": dict(sorted(by_type.items())),
|
||||
"applicability": dict(Counter(f["applicability"]["PIS_COFINS"] for f in findings.values())), "rtc_applicability": dict(Counter(f["applicability"]["RTC"] for f in findings.values())),
|
||||
"changed_products": len(changed), "examples": changed[:30]}
|
||||
if label == "ERP_AS_FOUND":
|
||||
runs[label]["context"] = {k: v for k, v in out["context"].items() if k != "capabilities"}
|
||||
total = before["TOTAL_PRODUCTS"]
|
||||
report = {"mode": "FISCAL_ONLY (no Hub decision)" if a.fiscal_only else "FULL (Hub decision + fiscal)", "database": a.db, "crt": crt, "products": len(products), "fiscal_products_read": len(fiscal_products),
|
||||
"BEFORE": {"COMPLETE_PRODUCTS": before["COMPLETE_PRODUCTS"], "SAFE_BLOCKED_PRODUCTS": before["SAFE_BLOCKED"], "COVERAGE_PERCENT": round(100 * before["COMPLETE_PRODUCTS"] / total, 2),
|
||||
"UNKNOWN_APPLICABILITY_previous_model": sum(1 for p in fiscal_products if old_unknown(p))}, "runs": runs,
|
||||
"SAFETY": {"FIREBIRD_WRITES": 0, "WRITE_ATTEMPTS": 0, "WRITE_SUCCESSES": 0, "connection_mode": "READ COMMITTED READ ONLY (Agent reader); no write-capable connection opened"}}
|
||||
Path(ROOT / a.out).write_text(json.dumps(report, ensure_ascii=False, indent=1, default=str), encoding="utf-8")
|
||||
print(json.dumps({k: report[k] for k in ("database", "crt", "products", "BEFORE")}, ensure_ascii=False))
|
||||
for label, r in runs.items():
|
||||
print(label, {k: r[k] for k in ("rtc_enabled", "TOTAL_PRODUCTS", "COMPLETE_PRODUCTS", "SAFE_BLOCKED_PRODUCTS", "FAILED_PRODUCTS", "UNCLASSIFIED_PRODUCTS", "COVERAGE_PERCENT", "INVARIANT_PASS", "PIS_BLOCKERS", "COFINS_BLOCKERS", "RTC_BLOCKERS", "OTHER_BLOCKERS")})
|
||||
print(" applicability", r["applicability"], "blockers", r["blockers_by_type"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user