Files
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

80 lines
5.2 KiB
Python

"""READ-ONLY dry-run of the deterministic cClassTrib resolver over real Firebird databases. Writes NOTHING to any database.
python scripts/rtc_resolution_dryrun.py --db TESTESOFICIAIS CONSTRUCAO ... [--out docs/runs/rtc_resolution_dryrun.json]
For each database, three passes over the same read-only snapshot:
AS_FOUND the ERP's own RTC switch and the company's real CRT (what the Agent would report today)
WHATIF_RTC_ON RTC forced ON (pure evaluator; PARAMETROS is never touched), real CRT
WHATIF_NORMAL_ON RTC forced ON and regime normal (CRT 3): what the rules WOULD resolve once an official orientation exists for Simples
NCM validity = the code exists in the active official NCM table exported from the Hub (docs/runs/ncm_valid_codes.txt).
"""
import argparse
import csv
import json
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 gti_fiscal_agent import firebird_reader, unified_fiscal # noqa: E402
from unified_coverage_e2e import resolved_crt # noqa: E402
NCM_OK = set((ROOT / "docs" / "runs" / "ncm_valid_codes.txt").read_text().split())
def digits(value):
return "".join(ch for ch in str(value or "") if ch.isdigit())
def one_pass(env, company, override, ncm_valid):
conn = firebird_reader._connect(env) # READ COMMITTED READ ONLY
try:
return unified_fiscal.assess_from_connection(conn, company, rtc_override=override, ncm_valid=ncm_valid)
finally:
conn.close()
def main():
ap = argparse.ArgumentParser(); ap.add_argument("--db", nargs="+", required=True); ap.add_argument("--out", default="docs/runs/rtc_resolution_dryrun.json"); ap.add_argument("--sample", type=int, default=100)
a = ap.parse_args()
report, sample = {}, []
for name in a.db:
path = lc.allowlist()[name]; env = lc.env_for(path)
crt = resolved_crt(name, path)
products = firebird_reader.read_products(env)
ncm_valid = {str(p["CODIGO"]): digits(p.get("CLASSIFICACAO_FISCAL")) in NCM_OK for p in products}
passes = {}
for label, override, company in (("AS_FOUND", None, {"crt": crt}), ("WHATIF_RTC_ON", True, {"crt": crt}), ("WHATIF_NORMAL_ON", True, {"crt": "3"})):
out = one_pass(env, company, override, ncm_valid)
summ = out["context"].get("rtc_resolution")
findings = out["findings"]
by_status = Counter((f.get("rtc_resolution") or {}).get("status", "NOT_EVALUATED") for f in findings.values())
conflicts = Counter(b["type"] for f in findings.values() for b in f["blockers"] if b["category"] == "RTC")
passes[label] = {"crt": company["crt"], "rtc_enabled": out["context"]["rtc_enabled"], "products": len(findings), "resolution_summary": summ, "by_status": dict(by_status), "rtc_blocker_types": dict(conflicts),
"AUTO_APPLY_CANDIDATES": sum(1 for f in findings.values() if (f.get("rtc_resolution") or {}).get("auto_apply_candidate") and not [b for b in f["blockers"] if b["type"] in ("CCLASSTRIB_CONFLICT",)])}
if label == "WHATIF_NORMAL_ON" and len(sample) < a.sample:
by_code = {str(p["CODIGO"]): p for p in products}
for code, f in findings.items():
res = f.get("rtc_resolution")
if res and len(sample) < a.sample and (res["auto_apply_candidate"] or res["status"] == "AMBIGUOUS") and (len([s for s in sample if s["database"] == name]) < max(1, a.sample // len(a.db))):
p = by_code.get(code, {})
sample.append({"database": name, "product": code, "ncm": digits(p.get("CLASSIFICACAO_FISCAL")), "current_cclasstrib": (p.get("CCLASSTRIB") or "").strip() if p.get("CCLASSTRIB") else "", "proposed_cclasstrib": res["resolved_cclasstrib"] or "",
"status": res["status"], "rule_id": res["rule_id"], "reason": res["reason"], "confidence_type": res["confidence_type"], "scenario": "WHATIF_NORMAL_ON (regime normal, RTC on)"})
passes[label]["context"] = {k: out["context"][k] for k in ("rtc_param", "rtc_source", "rtc_observed_at", "local_catalog_hash", "official_catalog", "catalog_integrity")}
report[name] = {"crt": crt, "total_products": len(products), "passes": passes}
print(name, "crt", crt, {k: (v["rtc_enabled"], v["by_status"], v["AUTO_APPLY_CANDIDATES"]) for k, v in passes.items()}, flush=True)
doc = {"mode": "READ_ONLY_DRY_RUN", "FIREBIRD_WRITES": 0, "WRITE_ATTEMPTS": 0, "WRITE_SUCCESSES": 0, "databases": report}
(ROOT / a.out).write_text(json.dumps(doc, ensure_ascii=False, indent=1, default=str), encoding="utf-8")
with open(ROOT / "docs" / "runs" / "rtc_resolution_audit_sample.csv", "w", newline="", encoding="utf-8-sig") as fh:
w = csv.DictWriter(fh, fieldnames=list(sample[0]) if sample else ["database"]); w.writeheader(); w.writerows(sample)
print("sample rows:", len(sample))
if __name__ == "__main__":
main()