Files
GTI_GERENCIADOR_FISCAL/fiscal/scripts/unified_coverage_e2e.py
T
gti d21a9d1140 Add 'fiscal/' from commit '102ba84500dbd9655a0471288c3e278652593ca9'
git-subtree-dir: fiscal
git-subtree-mainline: d95165fc8f
git-subtree-split: 102ba84500
2026-09-29 14:43:23 -03:00

108 lines
7.5 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 resolved_crt(name, path):
"""Company regime exactly as the Agent resolves it (deterministic company-context resolver)."""
from gti_fiscal_agent import company_context as cc
conn = lc.connect(path)
try:
cur = conn.cursor()
result = cc.resolve(cc.collect_evidence(cur), cc.config_from_env(lc._cfg_env(name)))
return result.get("company_crt") if result.get("status") == "PASS" else None
finally:
conn.close()
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 resolved_crt(a.db, path) 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], "non_canonical_warnings": out["context"]["non_canonical_warnings"]["products"]}
if label == "ERP_AS_FOUND":
runs[label]["context"] = {k: v for k, v in out["context"].items() if k not in ("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", "non_canonical_warnings")})
print(" applicability", r["applicability"], "blockers", r["blockers_by_type"])
if __name__ == "__main__":
main()