feat: unified fiscal coverage - PIS/COFINS applicability + conditional RTC in one definition, one queue, one panel

- 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>
This commit is contained in:
gti
2026-09-24 16:39:53 -03:00
co-authored by Claude Sonnet 5
parent d897cceebb
commit b15dbea213
32 changed files with 3979 additions and 517 deletions
+110
View File
@@ -0,0 +1,110 @@
import re
from datetime import date
import pytest
from gti_fiscal_agent import unified_fiscal as uf
pytestmark = pytest.mark.real_unified_fiscal
SCHEMA_ROWS = ([("C000025", c) for c in ("CODIGO", "CCLASSTRIB", "COD_ENTRADA_PIS", "COD_SAIDA_PIS", "ALIQ_PIS", "COD_ENTRADA_COFINS", "COD_SAIDA_COFINS", "ALIQ_COFINS", "PISCOFINS", "INCIDENCIA_PISCOFINS")]
+ [("RTC_CCLASSTRIB", c) for c in ("CCLASSTRIB", "CSTIBSCBS", "INICIO_VIGENCIA", "FIM_VIGENCIA")] + [("RTC_CST", "CSTIBSCBS")] + [("PARAMETROS", "USAR_REFORMA_TRIBUTARIA")])
PRODUCT_COLS = ("CODIGO", "CCLASSTRIB", "COD_ENTRADA_PIS", "COD_SAIDA_PIS", "ALIQ_PIS", "COD_ENTRADA_COFINS", "COD_SAIDA_COFINS", "ALIQ_COFINS", "PISCOFINS", "INCIDENCIA_PISCOFINS")
PRODUCTS = [("1", None, None, None, None, None, None, None, "N", "NENHUMA"), ("2", "200003", None, "04", None, None, "04", None, "S", "MONOFÁSICA"), ("3", None, "2", "2", "2", "2", "2", "2", "N", "NENHUMA")]
CLASSES = [("000001", "000", date(2025, 5, 5), None), ("200003", "200", date(2025, 5, 5), None)]
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 execute(self, sql, *args):
self.statements.append(sql)
text = " ".join(sql.split()).upper()
assert text.startswith("SELECT"), f"non-SELECT statement: {sql}"
if "FROM RDB$RELATION_FIELDS" in text:
self._rows = list(self.schema)
elif "FROM PARAMETROS" in text:
self._rows = [(self.param,)]
elif "RDB$RELATION_CONSTRAINTS" in text:
self._rows = [(1,)]
elif "FROM RTC_CCLASSTRIB" in text:
self._rows = list(self.classes)
elif "FROM RTC_CST" in text:
self._rows = list(self.cst)
elif "FROM C000025" in text:
self._rows = list(self.products)
else:
raise AssertionError(sql)
def fetchall(self):
return self._rows
def fetchone(self):
return self._rows[0] if self._rows else None
class FakeConn:
def __init__(self, **kw):
self.cur = FakeCursor(**kw)
def cursor(self):
return self.cur
def kinds(found):
return sorted({b["type"] for b in found["blockers"]})
def test_rtc_off_in_the_erp_is_out_of_scope_and_never_blocks():
conn = FakeConn(param=0)
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 all(s.strip().upper().startswith("SELECT") for s in conn.cur.statements) # read-only: SELECT statements only
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
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)
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")]
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"]
@pytest.mark.parametrize("value,expected", [(None, False), (0, False), ("0", False), (1, True), ("1", True), ("S", True), ("N", False)])
def test_parameter_values_are_read_fail_closed(value, expected):
caps = uf.detect_rtc_capabilities({"C000025": {"CCLASSTRIB"}, "RTC_CCLASSTRIB": {"CCLASSTRIB", "CSTIBSCBS"}, "RTC_CST": {"CSTIBSCBS"}}, param_value=value)
assert caps["RTC_ENABLED_BY_PARAM"] is expected
def test_catalog_with_orphan_or_duplicate_is_invalid_and_flagged_only_when_rtc_is_on():
bad = FakeConn(param=1, classes=CLASSES + [("200003", "200", date(2025, 5, 5), None)]) # duplicate class
on = uf.assess_from_connection(bad, {"crt": "1"})
assert on["context"]["rtc_catalog_valid"] is False and kinds(on["findings"]["2"]) == ["RTC_CATALOG_INVALID"]
off = uf.assess_from_connection(FakeConn(param=0, classes=CLASSES + [("200003", "200", date(2025, 5, 5), None)]), {"crt": "1"})
assert not [b for f in off["findings"].values() for b in f["blockers"] if b["category"] == "RTC"]
def test_input_fingerprint_changes_with_the_fiscal_values_and_the_rtc_switch():
a = uf.assess_from_connection(FakeConn(param=0), {"crt": "1"})["findings"]["1"]["input_fingerprint"]
b = uf.assess_from_connection(FakeConn(param=1), {"crt": "1"})["findings"]["1"]["input_fingerprint"]
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)