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>
This commit is contained in:
gti
2026-09-24 17:46:45 -03:00
co-authored by Claude Sonnet 5
parent 1430042322
commit 88f4318072
18 changed files with 679 additions and 814 deletions
+30 -9
View File
@@ -8,9 +8,10 @@ from __future__ import annotations
import hashlib
import json
from datetime import date
from collections import Counter
from datetime import date, datetime, timezone
from central.app.unified_fiscal_coverage import PIS_COFINS_FIELDS, catalog_integrity, evaluate_product, rtc_enabled
from central.app.unified_fiscal_coverage import PIS_COFINS_FIELDS, catalog_integrity, evaluate_product, legal_effective_context, rtc_enabled
PRODUCT_TABLE = "C000025"
RTC_CCLASSTRIB_TABLE = "RTC_CCLASSTRIB"
@@ -44,12 +45,17 @@ def load_rtc_context(cursor) -> tuple[dict, dict | None, dict]:
cursor.execute("SELECT TRIM(rf.RDB$RELATION_NAME), TRIM(rf.RDB$FIELD_NAME) FROM RDB$RELATION_FIELDS rf WHERE rf.RDB$RELATION_NAME IN ('C000025','RTC_CCLASSTRIB','RTC_CST','PARAMETROS')")
for table, column in cursor.fetchall():
schema.setdefault(table, set()).add(column)
extras: dict = {"param_value": None, "physical_fk": False}
extras: dict = {"param_value": None, "param_rows": 0, "param_conflict": False, "physical_fk": False}
if "USAR_REFORMA_TRIBUTARIA" in schema.get("PARAMETROS", ()):
try:
cursor.execute("SELECT FIRST 1 USAR_REFORMA_TRIBUTARIA FROM PARAMETROS")
row = cursor.fetchone()
extras["param_value"] = None if row is None or row[0] is None else row[0]
# ALL rows are read: the source of truth is what THIS installation's database says NOW (never a cached or Hub-side value).
cursor.execute("SELECT FIRST 20 USAR_REFORMA_TRIBUTARIA FROM PARAMETROS")
values = [None if r[0] is None else str(r[0]).strip().upper() for r in cursor.fetchall()]
extras["param_rows"] = len(values)
if len(set(values)) > 1:
extras["param_conflict"] = True # rows disagree: never guess "on" (RTC stays out of scope, and the conflict is reported)
elif values:
extras["param_value"] = values[0]
except Exception: # noqa: BLE001 - unreadable parameter => RTC stays off (not enabled by the ERP)
extras["param_value"] = None
try:
@@ -92,11 +98,26 @@ def assess_from_connection(conn, company: dict | None, *, today: date | None = N
today = today or date.today()
products = read_products(cursor, schema)
findings = {}
warn_by_type: Counter = Counter()
warn_products: set = set()
samples: list = []
applicability: Counter = Counter()
for p in products:
found = evaluate_product(p, company, rtc, today)
found["input_fingerprint"] = hashlib.sha256(json.dumps({k: (None if v is None else str(v).strip()) for k, v in p.items()} | {"rtc": enabled}, sort_keys=True).encode()).hexdigest()
findings[str(p.get("CODIGO") or "")] = found
code = str(p.get("CODIGO") or "")
findings[code] = found
applicability[found["applicability"]["PIS_COFINS"]] += 1
for w in found.get("warnings", []):
warn_by_type[w["type"]] += 1
warn_products.add(code)
if len(samples) < 10:
samples.append({"product_code": code, **w})
integrity = catalog_integrity(catalog) if catalog else None
return {"context": {"rtc_enabled": enabled, "rtc_supported": caps["HAS_RTC_RELATIONSHIP"], "rtc_catalog_valid": caps["RTC_CATALOG_VALID"], "rtc_param": extras["param_value"],
"catalog_integrity": integrity, "crt": (company or {}).get("crt"), "capabilities": caps, "products_evaluated": len(products)},
return {"context": {"rtc_enabled": enabled, "erp_rtc_enabled": enabled, "rtc_supported": caps["HAS_RTC_RELATIONSHIP"], "rtc_catalog_valid": caps["RTC_CATALOG_VALID"], "rtc_param": extras["param_value"],
"rtc_source": "SIMULATION" if rtc_override is not None else "PARAMETROS.USAR_REFORMA_TRIBUTARIA", "rtc_param_rows": extras["param_rows"], "rtc_param_conflict": extras["param_conflict"],
"rtc_observed_at": datetime.now(timezone.utc).isoformat(timespec="seconds"), "legal_effective_context": legal_effective_context((company or {}).get("crt")),
"catalog_integrity": integrity, "crt": (company or {}).get("crt"), "capabilities": caps, "products_evaluated": len(products),
"pis_cofins_applicability": dict(applicability),
"non_canonical_warnings": {"products": len(warn_products), "by_type": dict(sorted(warn_by_type.items())), "samples": samples}},
"findings": findings}
+47 -7
View File
@@ -16,8 +16,8 @@ CLASSES = [("000001", "000", date(2025, 5, 5), None), ("200003", "200", date(202
class FakeCursor:
"""READ ONLY fake: answers only SELECTs and records every statement so tests can prove nothing else ran."""
def __init__(self, param=0, schema=SCHEMA_ROWS, products=PRODUCTS, classes=CLASSES, cst=(("000",), ("200",))):
self.param, self.schema, self.products, self.classes, self.cst, self.statements, self._rows = param, schema, products, classes, cst, [], []
def __init__(self, param=0, params=None, schema=SCHEMA_ROWS, products=PRODUCTS, classes=CLASSES, cst=(("000",), ("200",))):
self.param, self.params, self.schema, self.products, self.classes, self.cst, self.statements, self._rows = param, params, schema, products, classes, cst, [], []
def execute(self, sql, *args):
self.statements.append(sql)
@@ -26,7 +26,7 @@ class FakeCursor:
if "FROM RDB$RELATION_FIELDS" in text:
self._rows = list(self.schema)
elif "FROM PARAMETROS" in text:
self._rows = [(self.param,)]
self._rows = [(v,) for v in (self.params if self.params is not None else [self.param])]
elif "RDB$RELATION_CONSTRAINTS" in text:
self._rows = [(1,)]
elif "FROM RTC_CCLASSTRIB" in text:
@@ -62,7 +62,7 @@ def test_rtc_off_in_the_erp_is_out_of_scope_and_never_blocks():
out = uf.assess_from_connection(conn, {"crt": "1"}, today=date(2026, 9, 24))
assert out["context"]["rtc_enabled"] is False and out["context"]["rtc_supported"] is True and out["context"]["rtc_catalog_valid"] is True
assert not [b for f in out["findings"].values() for b in f["blockers"] if b["category"] == "RTC"]
assert kinds(out["findings"]["1"]) == [] and kinds(out["findings"]["2"]) == [] and kinds(out["findings"]["3"]) == ["COFINS_CST_INVALID", "PIS_CST_INVALID"]
assert kinds(out["findings"]["1"]) == [] and kinds(out["findings"]["2"]) == [] and kinds(out["findings"]["3"]) == [] # Simples + explicit "no special treatment": not applicable, typed "2" is only a warning
assert all(s.strip().upper().startswith("SELECT") for s in conn.cur.statements) # read-only: SELECT statements only
@@ -70,19 +70,19 @@ def test_rtc_on_in_the_erp_brings_cclasstrib_into_the_same_findings():
out = uf.assess_from_connection(FakeConn(param=1), {"crt": "1"}, today=date(2026, 9, 24))
assert out["context"]["rtc_enabled"] is True
assert kinds(out["findings"]["1"]) == ["CCLASSTRIB_MISSING"] and kinds(out["findings"]["2"]) == []
assert kinds(out["findings"]["3"]) == ["CCLASSTRIB_MISSING", "COFINS_CST_INVALID", "PIS_CST_INVALID"] # one queue, one verdict per product
assert kinds(out["findings"]["3"]) == ["CCLASSTRIB_MISSING"] # one queue, one verdict per product
def test_simulation_flag_never_touches_the_erp_parameter():
conn = FakeConn(param=0)
on = uf.assess_from_connection(conn, {"crt": "1"}, rtc_override=True)
assert on["context"]["rtc_enabled"] is True and kinds(on["findings"]["1"]) == ["CCLASSTRIB_MISSING"]
assert on["context"]["rtc_param"] == 0 and not any("UPDATE" in s.upper() for s in conn.cur.statements)
assert on["context"]["rtc_param"] == "0" and on["context"]["rtc_source"] == "SIMULATION" and not any("UPDATE" in s.upper() for s in conn.cur.statements)
def test_old_schema_without_rtc_columns_reports_unsupported_and_still_evaluates_pis_cofins():
old = [("C000025", c) for c in ("CODIGO", "COD_SAIDA_PIS", "PISCOFINS", "INCIDENCIA_PISCOFINS")]
rows = [("1", None, "N", "NENHUMA"), ("2", "9", "N", "NENHUMA")]
rows = [("1", None, "N", "NENHUMA"), ("2", "049", "N", "NENHUMA")]
out = uf.assess_from_connection(FakeConn(schema=old, products=rows, classes=[], cst=()), {"crt": "3"})
assert out["context"]["rtc_supported"] is False and out["context"]["rtc_enabled"] is False
assert kinds(out["findings"]["1"]) == [] and kinds(out["findings"]["2"]) == ["PIS_CST_INVALID"]
@@ -108,3 +108,43 @@ def test_input_fingerprint_changes_with_the_fiscal_values_and_the_rtc_switch():
changed = [("1", None, None, "99", None, None, None, None, "N", "NENHUMA")] + PRODUCTS[1:]
c = uf.assess_from_connection(FakeConn(param=0, products=changed), {"crt": "1"})["findings"]["1"]["input_fingerprint"]
assert len({a, b, c}) == 3 and re.fullmatch(r"[0-9a-f]{64}", a)
def test_rtc_state_is_read_from_this_database_now_with_provenance_and_timestamp():
out = uf.assess_from_connection(FakeConn(param=1), {"crt": "1"})
ctx = out["context"]
assert ctx["rtc_enabled"] is True and ctx["erp_rtc_enabled"] is True and ctx["rtc_source"] == "PARAMETROS.USAR_REFORMA_TRIBUTARIA" and ctx["rtc_param"] == "1" and ctx["rtc_param_rows"] == 1
assert ctx["rtc_param_conflict"] is False and ctx["rtc_observed_at"].endswith("+00:00")
assert ctx["legal_effective_context"]["informational_only"] is True and ctx["legal_effective_context"]["crt"] == "1"
def test_toggle_between_syncs_is_followed_in_both_directions_without_any_cache():
conn = FakeConn(param=0)
first = uf.assess_from_connection(conn, {"crt": "1"})
assert first["context"]["rtc_enabled"] is False and not [b for f in first["findings"].values() for b in f["blockers"] if b["category"] == "RTC"]
conn.cur.param = 1 # the operator switches the reform on in the ERP between two syncs
second = uf.assess_from_connection(conn, {"crt": "1"})
assert second["context"]["rtc_enabled"] is True and kinds(second["findings"]["1"]) == ["CCLASSTRIB_MISSING"]
assert second["findings"]["1"]["input_fingerprint"] != first["findings"]["1"]["input_fingerprint"] # a new blocker fingerprint: the old (RTC off) state is not reused
conn.cur.param = 0 # and off again
third = uf.assess_from_connection(conn, {"crt": "1"})
assert third["context"]["rtc_enabled"] is False and not [b for f in third["findings"].values() for b in f["blockers"] if b["category"] == "RTC"]
assert third["findings"]["1"]["input_fingerprint"] == first["findings"]["1"]["input_fingerprint"]
def test_conflicting_or_repeated_parameter_rows_never_guess_on():
same = uf.assess_from_connection(FakeConn(params=[1, 1]), {"crt": "1"})["context"]
assert same["rtc_enabled"] is True and same["rtc_param_rows"] == 2 and same["rtc_param_conflict"] is False
conflict = uf.assess_from_connection(FakeConn(params=[1, 0]), {"crt": "1"})
assert conflict["context"]["rtc_enabled"] is False and conflict["context"]["rtc_param_conflict"] is True
assert not [b for f in conflict["findings"].values() for b in f["blockers"] if b["category"] == "RTC"]
assert uf.assess_from_connection(FakeConn(params=[]), {"crt": "1"})["context"]["rtc_enabled"] is False
def test_non_canonical_cst_warnings_are_reported_without_blocking():
rows = [("1", None, None, "4", None, None, "4", None, "N", "NENHUMA"), ("2", None, None, None, None, None, None, None, "N", "NENHUMA")]
out = uf.assess_from_connection(FakeConn(products=rows), {"crt": "3"})
assert kinds(out["findings"]["1"]) == [] and kinds(out["findings"]["2"]) == []
w = out["context"]["non_canonical_warnings"]
assert w["products"] == 1 and w["by_type"] == {"CST_COFINS_NON_CANONICAL_FORMAT": 1, "CST_PIS_NON_CANONICAL_FORMAT": 1} and w["samples"][0]["product_code"] == "1"
assert out["context"]["pis_cofins_applicability"] == {"OPTIONAL": 2}
+23
View File
@@ -185,3 +185,26 @@ def test_partial_reprocess_does_not_claim_a_full_fiscal_evaluation(env_setup, mo
hub, state = env_setup
result, _ = run_job("REPROCESS_PRODUCTS", [product("1")], state, monkeypatch)
assert result["fiscal_context"] is None and result["operational"] is None
def test_rtc_toggle_between_two_syncs_is_followed_by_the_next_job(env_setup, monkeypatch):
"""The Agent asks the database EVERY sync: 0 -> 1 makes the next job report RTC blockers, 1 -> 0 makes the following job report none (Hub then closes the old ones)."""
from gti_fiscal_agent import unified_fiscal
switch = {"on": False}
def assess(conn, company, **kw):
found = _findings(**{"1": [("CCLASSTRIB_MISSING", "RTC")]}) if switch["on"] else {}
return {"context": {"rtc_enabled": switch["on"], "rtc_supported": True, "rtc_catalog_valid": True, "rtc_source": "PARAMETROS.USAR_REFORMA_TRIBUTARIA", "rtc_observed_at": "2026-09-24T20:00:00+00:00"}, "findings": found}
monkeypatch.setattr(unified_fiscal, "assess_from_connection", assess)
hub, state = env_setup
products = [product("1"), product("2")]
off1, _ = run_job("INITIAL_SYNC", products, state, monkeypatch)
switch["on"] = True
on, _ = run_job("SYNC_INCREMENTAL", products, state, monkeypatch) # nothing changed in C000025: the delta cache must not hide the RTC change
switch["on"] = False
off2, _ = run_job("SYNC_INCREMENTAL", products, state, monkeypatch)
assert off1["blocked_products"] == [] and off1["fiscal_context"]["rtc_enabled"] is False
assert [b["field"] for b in on["blocked_products"]] == ["RTC"] and on["fiscal_context"]["rtc_enabled"] is True and on["operational"]["BLOCKED_FISCAL_DATA"] == 1
assert on["fiscal_context"]["rtc_observed_at"] and on["fiscal_context"]["rtc_source"] == "PARAMETROS.USAR_REFORMA_TRIBUTARIA"
assert off2["blocked_products"] == [] and off2["fiscal_context"]["rtc_enabled"] is False and off2["fiscal_context"]["evaluated_all"] is True
assert off2["operational"]["COMPLETE_PRODUCTS"] == 2