Files
GTI_GERENCIADOR_FISCAL/scripts/unified_coverage_e2e.py
T
gtiandClaude Sonnet 5 88f4318072 fix: RTC observed live with provenance, non-circular PIS/COFINS applicability, canonical CST normalization (1.1.8)
- RTC: Agent reads ALL PARAMETROS rows every sync (conflict => off), reports rtc_source/rtc_observed_at/rtc_param_rows; legal_effective_context is informational only
- PIS/COFINS: applicability from CRT + PISCOFINS flag + incidence only (never from empty fields); Simples w/o special treatment NOT_APPLICABLE, taxed regime with explicit N OPTIONAL, unreadable declaration NEEDS_REVIEW
- CST: one digit with an official zero-padded form is VALID_AFTER_NORMALIZATION (non-blocking warning), nothing is written to the ERP
- tests: RTC toggle 0->1->0 across syncs (Agent + Hub stale closing), non-circularity, normalization, panel provenance

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 17:46:45 -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()