- 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>
96 lines
6.9 KiB
Python
96 lines
6.9 KiB
Python
"""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()
|