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
+1
View File
@@ -44,3 +44,4 @@ scripts/_*
*.gz
*.pkl
.pytmp/
sources_cache/legal/
+37 -16
View File
@@ -30,8 +30,8 @@ sys.path.insert(0, str(AGENT_ROOT / "src"))
sys.path.insert(0, str(PROJECT_ROOT)) # ships central/app engine modules read-only (central.app.*)
from gti_fiscal_agent.client import HubClient # noqa: E402
from gti_fiscal_agent.firebird_reader import _connect as _open_read_only, check_connection, read_products # noqa: E402
from gti_fiscal_agent.rtc_pis_cofins import assess_from_connection # noqa: E402
from gti_fiscal_agent import firebird_reader as _firebird_reader, unified_fiscal as _unified # noqa: E402
from gti_fiscal_agent.firebird_reader import check_connection, read_products # noqa: E402
from gti_fiscal_agent.pipeline import run_pipeline # noqa: E402
from gti_fiscal_agent.runner import REFERENCE_SCHEMA_V1, product_fingerprint, to_hub_product # noqa: E402
from central.app.operational_state import classify_product_state, summarize_operational_states # noqa: E402
@@ -187,16 +187,27 @@ def open_write_connection(env, database_path, firebird_writes=True):
)
def _expanded_scope(env, legacy_states):
"""RTC + PIS/COFINS assessment (read-only, SELECT only). Isolated: a failure here never fails the job or changes legacy coverage."""
def _unified_fiscal(env, company):
"""PIS/COFINS applicability + RTC (only when the ERP has the reform on), read-only (SELECT only). Fail-closed: if it cannot be assessed the job fails
instead of reporting a coverage that ignores these fields."""
connection = _firebird_reader._connect(env)
try:
connection = _open_read_only(env)
try:
return assess_from_connection(connection, legacy_states)
finally:
connection.close()
except Exception as error: # noqa: BLE001
return {"scope_version": "expanded-v1", "unavailable": type(error).__name__}
return _unified.assess_from_connection(connection, company)
finally:
connection.close()
def _fiscal_blocked_products(products, findings):
"""One queue entry per (product, blocker). Same shape as every other blocker; `field` is the dashboard category (PIS/COFINS/RTC)."""
out = []
for product in products:
code = str(product.get("CODIGO") or "")
found = findings.get(code) or {}
for blocker in found.get("blockers") or []:
out.append({"company": "", "product_code": code, "gtin": product.get("GTIN") or product.get("EAN_GTIN"), "description": product.get("PRODUTO"), "blocker_type": "BLOCKED_FISCAL_DATA",
"field": blocker["category"], "reason_code": blocker["type"], "evidence_summary": {"reason": blocker["reason"], "applicability": found.get("applicability")},
"input_fingerprint": found.get("input_fingerprint", "")})
return out
def execute_job(job, config, executor, state, env):
@@ -238,7 +249,8 @@ def execute_job(job, config, executor, state, env):
if write_connection is not None:
write_connection.close()
operational = coverage = expanded_scope = None
operational = coverage = fiscal_context = None
fiscal = None
if sync_kind:
if cache:
for product, decision in zip(to_process, decisions):
@@ -247,14 +259,23 @@ def execute_job(job, config, executor, state, env):
all_decisions = [state.cached_decision(p["CODIGO"]) or {} for p in products]
else:
all_decisions = decisions
product_states = [classify_product_state(p, d) for p, d in zip(products, all_decisions)]
operational = summarize_operational_states(product_states)
coverage = build_job_coverage(env, client, products, all_decisions)
expanded_scope = _expanded_scope(env, {s["product_code"]: s["state"] for s in product_states})
company = {"crt": ((coverage or {}).get("company_context") or {}).get("company_crt")}
fiscal = _unified_fiscal(env, company)
findings = fiscal["findings"]
product_states = [classify_product_state(p, d, fiscal=findings.get(str(p["CODIGO"]))) for p, d in zip(products, all_decisions)]
operational = summarize_operational_states(product_states)
fiscal_context = {**fiscal["context"], "evaluated_all": kind != "REPROCESS_PRODUCTS"}
else:
fiscal = _unified_fiscal(env, None)
fiscal_blocked = _fiscal_blocked_products(products, fiscal["findings"])
fiscal_codes = {b["product_code"] for b in fiscal_blocked}
summary["blocked_products"] = summary["blocked_products"] + fiscal_blocked
summary["resolved_products"] = [c for c in summary["resolved_products"] if str(c) not in fiscal_codes]
result_summary = {
"coverage": coverage,
"operational": operational,
"expanded_scope": expanded_scope,
"fiscal_context": fiscal_context,
"fiscal_mode": getattr(config, "fiscal_mode", None),
"release_version": env.get("AGENT_VERSION") or "1.1.7",
"products_total": len(products),
@@ -1,277 +0,0 @@
"""READ-ONLY RTC (cClassTrib / CST IBS-CBS) and PIS/COFINS assessment of SGV products.
Everything here is observation: capability detection, catalog integrity, deterministic validators, applicability and an
EXPANDED coverage view that sits next to (never replaces) the legacy scope coverage. Nothing writes to Firebird, nothing
fills a value, and no fiscal rule is invented: where local data cannot prove that a field applies, the answer is
UNKNOWN_APPLICABILITY, not a blocker.
Vocabulary kept apart on purpose: SCHEMA_SUPPORT (the column exists) / DATA_PRESENT (it has a value) /
APPLICABILITY (the field applies to this product) / MANDATORYNESS (a missing value is a defect). Only the last one
may block, and only when applicability is proven from local, reliable data.
"""
from __future__ import annotations
import re
LOCAL_CATALOG_ONLY = True # the RTC catalog used is the one stored in the client's own SGV database; no official dataset is integrated here
PRODUCT_TABLE = "C000025"
RTC_CCLASSTRIB_TABLE = "RTC_CCLASSTRIB"
RTC_CST_TABLE = "RTC_CST"
PIS_COFINS_COLUMNS = ("COD_ENTRADA_PIS", "COD_SAIDA_PIS", "ALIQ_PIS", "COD_ENTRADA_COFINS", "COD_SAIDA_COFINS", "ALIQ_COFINS", "PISCOFINS", "INCIDENCIA_PISCOFINS")
PRODUCT_COLUMNS = ("CODIGO", "CCLASSTRIB") + PIS_COFINS_COLUMNS
# Official CST PIS/COFINS codes (2 digits). Used for FORMAT/membership only, never to pick a value.
OFFICIAL_PIS_COFINS_CST = frozenset([f"{n:02d}" for n in (*range(1, 10), 49, *range(50, 57), *range(60, 68), *range(70, 76), 98, 99)])
# ---------------------------------------------------------------- capabilities -------------------------------------------------
def detect_rtc_capabilities(schema: dict, *, param_value=None, catalog: dict | None = None, physical_fk: bool | None = None) -> dict:
"""schema: {TABLE: {COLUMN, ...}}. Fail-closed: anything not proven is False. Old databases without RTC simply report False."""
product_cols = set(schema.get(PRODUCT_TABLE, ()))
class_cols = set(schema.get(RTC_CCLASSTRIB_TABLE, ()))
cst_cols = set(schema.get(RTC_CST_TABLE, ()))
has_product = "CCLASSTRIB" in product_cols
has_class = {"CCLASSTRIB", "CSTIBSCBS"} <= class_cols
has_cst = "CSTIBSCBS" in cst_cols
has_relationship = bool(has_product and has_class and has_cst) # column-level relationship: product -> class -> cst
caps = {
"HAS_PRODUCT_CCLASSTRIB": has_product,
"HAS_RTC_CCLASSTRIB_TABLE": has_class,
"HAS_RTC_CST_TABLE": has_cst,
"HAS_RTC_RELATIONSHIP": has_relationship,
"HAS_PHYSICAL_RTC_FK": bool(physical_fk),
"RTC_ENABLED_BY_PARAM": None if param_value is None else str(param_value).strip() in {"1", "S", "T", "TRUE"},
"RTC_CATALOG_VALID": False,
"LOCAL_CATALOG_ONLY": LOCAL_CATALOG_ONLY,
}
if catalog is not None and has_class and has_cst:
caps["RTC_CATALOG_VALID"] = catalog_integrity(catalog)["valid"]
return caps
def catalog_integrity(catalog: dict) -> dict:
"""catalog: {"classes": {cClassTrib: cst_or_None}, "cst": {code, ...}, "duplicates": int}. Pure."""
classes, cst = catalog.get("classes", {}), set(catalog.get("cst", ()))
bad_format = [c for c in classes if not re.fullmatch(r"\d{6}", str(c))]
bad_cst_format = [c for c in cst if not re.fullmatch(r"\d{3}", str(c))]
orphans = [c for c, s in classes.items() if s is None or str(s) not in cst]
return {"classes": len(classes), "cst": len(cst), "orphan_classes": len(orphans), "bad_class_format": len(bad_format),
"bad_cst_format": len(bad_cst_format), "duplicates": int(catalog.get("duplicates", 0)),
"valid": bool(classes and cst and not orphans and not bad_format and not bad_cst_format and not catalog.get("duplicates"))}
# ---------------------------------------------------------------- validators ---------------------------------------------------
def _text(value) -> str:
return "" if value is None else str(value).strip()
def validate_cclasstrib(value, catalog: dict | None) -> str:
"""EMPTY | INVALID_FORMAT | NOT_IN_LOCAL_CATALOG | VALID_LOCAL | CST_MISSING | VALID_WITH_CST (deterministic, no inference)."""
code = _text(value)
if not code:
return "EMPTY"
if not re.fullmatch(r"\d{6}", code):
return "INVALID_FORMAT"
if not catalog or code not in catalog.get("classes", {}):
return "NOT_IN_LOCAL_CATALOG"
if not catalog.get("cst"):
return "VALID_LOCAL" # code exists, but there is no CST table to verify its CST
cst = catalog["classes"][code]
return "VALID_WITH_CST" if cst is not None and str(cst) in catalog["cst"] else "CST_MISSING"
def validate_pis_cofins_cst(value) -> str:
code = _text(value)
if not code:
return "EMPTY"
return "VALID" if code in OFFICIAL_PIS_COFINS_CST else "INVALID_FORMAT"
def validate_pis_cofins_rate(value) -> str:
text = _text(value)
if not text:
return "EMPTY"
if not re.fullmatch(r"\d{1,3}([.,]\d{1,4})?", text):
return "INVALID_RATE"
return "VALID" if float(text.replace(",", ".")) <= 100 else "INVALID_RATE"
def validate_pis_cofins(product: dict) -> dict:
"""Field-level validation for one product row. Flag consistency compares the ERP's own flag/incidence with its own codes."""
out = {}
for tax in ("PIS", "COFINS"):
out[f"{tax}_ENTRY_CST"] = validate_pis_cofins_cst(product.get(f"COD_ENTRADA_{tax}"))
out[f"{tax}_EXIT_CST"] = validate_pis_cofins_cst(product.get(f"COD_SAIDA_{tax}"))
out[f"{tax}_RATE"] = validate_pis_cofins_rate(product.get(f"ALIQ_{tax}"))
flag = _text(product.get("PISCOFINS")).upper()
incidence = _text(product.get("INCIDENCIA_PISCOFINS")).upper()
has_codes = any(_text(product.get(c)) for c in PIS_COFINS_COLUMNS[:6])
special = incidence not in ("", "NENHUMA")
inconsistent = (flag == "N" and (special or has_codes)) or (flag == "S" and incidence in ("", "NENHUMA"))
out["FLAG_CONSISTENCY"] = "INCONSISTENT" if inconsistent else ("EMPTY" if not flag else "CONSISTENT")
return out
# ---------------------------------------------------------------- applicability -----------------------------------------------
def rtc_applicability(caps: dict) -> str:
"""APPLICABLE only when the ERP itself is switched to the reform (PARAMETROS.USAR_REFORMA_TRIBUTARIA) AND the model exists.
NOT_ENABLED_BY_ERP when the ERP says it does not use RTC; NOT_SUPPORTED when the schema cannot hold it; else UNKNOWN."""
if not (caps.get("HAS_PRODUCT_CCLASSTRIB") and caps.get("HAS_RTC_RELATIONSHIP")):
return "NOT_SUPPORTED"
enabled = caps.get("RTC_ENABLED_BY_PARAM")
if enabled is None:
return "UNKNOWN_APPLICABILITY"
return "APPLICABLE" if enabled else "NOT_ENABLED_BY_ERP"
def pis_cofins_applicability(product: dict, schema_ok: bool) -> str:
"""Product-level PIS/COFINS defaults are treated as applicable only when the product itself carries evidence of them
(a code, or a special incidence/flag). A product with no data and flag N / NENHUMA gives NO proof either way (documents
take their CST from elsewhere), so it is UNKNOWN_APPLICABILITY - not complete and not blocked."""
if not schema_ok:
return "NOT_SUPPORTED"
evidence = any(_text(product.get(c)) for c in PIS_COFINS_COLUMNS[:6]) or _text(product.get("PISCOFINS")).upper() == "S" \
or _text(product.get("INCIDENCIA_PISCOFINS")).upper() not in ("", "NENHUMA")
return "APPLICABLE" if evidence else "UNKNOWN_APPLICABILITY"
# ---------------------------------------------------------------- blockers + expanded coverage ------------------------------
def expanded_findings(product: dict, caps: dict, catalog: dict | None) -> dict:
"""{"blockers": [(TYPE, reason)], "unknown": [area], "notes": [...]}: specific blockers only where applicability is proven."""
blockers, unknown = [], []
rtc = rtc_applicability(caps)
if rtc == "APPLICABLE":
state = validate_cclasstrib(product.get("CCLASSTRIB"), catalog)
if state == "EMPTY":
blockers.append(("CCLASSTRIB_MISSING", "EMPTY"))
elif state == "INVALID_FORMAT":
blockers.append(("CCLASSTRIB_INVALID", state))
elif state == "NOT_IN_LOCAL_CATALOG":
blockers.append(("CCLASSTRIB_NOT_IN_CATALOG", state))
elif state == "CST_MISSING":
blockers.append(("RTC_CST_INVALID", state))
elif rtc == "UNKNOWN_APPLICABILITY":
unknown.append("RTC")
schema_ok = caps.get("_PIS_COFINS_SCHEMA", False)
pis_cofins = pis_cofins_applicability(product, schema_ok)
if pis_cofins == "APPLICABLE":
checks = validate_pis_cofins(product)
for tax in ("PIS", "COFINS"):
for slot, label in (("ENTRY_CST", "ENTRY"), ("EXIT_CST", "EXIT")):
result = checks[f"{tax}_{slot}"]
if result == "INVALID_FORMAT":
blockers.append((f"{tax}_CST_INVALID", f"{label}:{result}"))
if checks[f"{tax}_RATE"] == "INVALID_RATE":
blockers.append((f"{tax}_RATE_INVALID", "INVALID_RATE"))
exit_state = checks[f"{tax}_EXIT_CST"]
if exit_state == "EMPTY" and checks[f"{tax}_RATE"] != "EMPTY":
blockers.append((f"{tax}_CST_MISSING", "EXIT:EMPTY_WITH_RATE"))
if checks["FLAG_CONSISTENCY"] == "INCONSISTENT":
blockers.append(("PIS_COFINS_FLAG_INCONSISTENT", "FLAG_VS_DATA"))
elif pis_cofins == "UNKNOWN_APPLICABILITY":
unknown.append("PIS_COFINS")
return {"blockers": blockers, "unknown": unknown, "rtc_applicability": rtc, "pis_cofins_applicability": pis_cofins}
def expanded_scope_summary(products: list[dict], legacy_states: dict, caps: dict, catalog: dict | None) -> dict:
"""Legacy coverage is reported unchanged (LEGACY_SCOPE_COVERAGE); EXPANDED_FISCAL_SCOPE_COVERAGE adds RTC + PIS/COFINS.
legacy_states: {product_code: state} from classify_product_state (COMPLETE/CORRECTED/BLOCKED_*/FAILED_TECHNICAL)."""
legacy = {"complete": 0, "safe_blocked": 0, "failed": 0}
expanded = {"complete": 0, "safe_blocked": 0, "failed": 0, "unknown_applicability": 0}
blockers: dict[str, int] = {}
blocked_rows = []
caps = dict(caps)
for product in products:
code = str(product.get("CODIGO") or "")
state = legacy_states.get(code, "COMPLETE")
if state == "FAILED_TECHNICAL":
legacy["failed"] += 1
expanded["failed"] += 1
continue
if state.startswith("BLOCKED_"):
legacy["safe_blocked"] += 1
expanded["safe_blocked"] += 1 # blocked in the legacy scope stays blocked: the expanded scope only ADDS conditions
continue
legacy["complete"] += 1
found = expanded_findings(product, caps, catalog)
if found["blockers"]:
expanded["safe_blocked"] += 1
for kind, reason in found["blockers"]:
blockers[kind] = blockers.get(kind, 0) + 1
if len(blocked_rows) < 200:
blocked_rows.append({"product_code": code, "type": kind, "reason": reason})
elif found["unknown"]:
expanded["unknown_applicability"] += 1
else:
expanded["complete"] += 1
total = len(products)
return {"scope_version": "expanded-v1", "local_catalog_only": LOCAL_CATALOG_ONLY, "total_products": total,
"legacy_scope": legacy, "expanded_scope": expanded, "blockers_by_type": dict(sorted(blockers.items())),
"blocker_samples": blocked_rows, "invariant_pass": sum(expanded.values()) == total and sum(legacy.values()) == total,
"capabilities": {k: v for k, v in caps.items() if not k.startswith("_")}}
# ---------------------------------------------------------------- read-only loader -------------------------------------------
def load_rtc_context(cursor) -> tuple[dict, dict | None, dict]:
"""Read schema + catalog + ERP parameter through an already open READ ONLY cursor (SELECT only).
Returns (schema, catalog|None, extras). Never raises for a database that lacks RTC."""
schema: dict[str, set] = {}
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}
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]
except Exception: # noqa: BLE001 - unreadable parameter => applicability stays unknown
extras["param_value"] = None
try:
cursor.execute("SELECT COUNT(*) FROM RDB$RELATION_CONSTRAINTS WHERE RDB$RELATION_NAME = 'C000025' AND RDB$CONSTRAINT_TYPE = 'FOREIGN KEY' AND RDB$CONSTRAINT_NAME CONTAINING 'CCLASSTRIB'")
extras["physical_fk"] = cursor.fetchone()[0] > 0
except Exception: # noqa: BLE001
pass
catalog = None
if {"CCLASSTRIB", "CSTIBSCBS"} <= schema.get(RTC_CCLASSTRIB_TABLE, set()) and "CSTIBSCBS" in schema.get(RTC_CST_TABLE, set()):
cursor.execute("SELECT TRIM(CCLASSTRIB), TRIM(CSTIBSCBS) FROM RTC_CCLASSTRIB")
rows = cursor.fetchall()
classes = {c: (s or None) for c, s in rows}
cursor.execute("SELECT TRIM(CSTIBSCBS) FROM RTC_CST")
cst = {r[0] for r in cursor.fetchall()}
catalog = {"classes": classes, "cst": cst, "duplicates": len(rows) - len(classes)}
return schema, catalog, extras
def read_products_rtc_pis_cofins(cursor, schema: dict) -> list[dict]:
"""SELECT of only the columns that exist (old schemas keep working)."""
have = schema.get(PRODUCT_TABLE, set())
columns = [c for c in PRODUCT_COLUMNS if c in have]
if "CODIGO" not in columns:
return []
cursor.execute(f"SELECT {', '.join(columns)} FROM {PRODUCT_TABLE} ORDER BY CODIGO")
return [dict(zip(columns, row)) for row in cursor.fetchall()]
def assess_from_connection(conn, legacy_states: dict) -> dict:
"""One read-only pass: capabilities -> catalog integrity -> expanded scope. Any failure degrades to an explicit unavailable
marker; it never raises into the job and never touches the database beyond SELECT."""
try:
cursor = conn.cursor()
schema, catalog, extras = load_rtc_context(cursor)
caps = detect_rtc_capabilities(schema, param_value=extras["param_value"], catalog=catalog, physical_fk=extras["physical_fk"])
caps["_PIS_COFINS_SCHEMA"] = all(c in schema.get(PRODUCT_TABLE, ()) for c in PIS_COFINS_COLUMNS[:6])
products = read_products_rtc_pis_cofins(cursor, schema)
summary = expanded_scope_summary(products, legacy_states, caps, catalog)
summary["catalog_integrity"] = catalog_integrity(catalog) if catalog else None
summary["capabilities"]["PIS_COFINS_SCHEMA_VALID"] = caps["_PIS_COFINS_SCHEMA"]
summary["data_present"] = {c: sum(1 for p in products if _text(p.get(c))) for c in ("CCLASSTRIB",) + PIS_COFINS_COLUMNS if any(c in p for p in products[:1])}
return summary
except Exception as error: # noqa: BLE001
return {"scope_version": "expanded-v1", "unavailable": type(error).__name__}
@@ -0,0 +1,102 @@
"""READ-ONLY ERP loader for the unified fiscal completeness (PIS/COFINS applicability + RTC when the ERP has the reform on).
The rules live in central/app/unified_fiscal_coverage.py (pure). This module only observes the ERP through an already open
READ ONLY connection (SELECT only): schema capabilities, RTC catalog, PARAMETROS.USAR_REFORMA_TRIBUTARIA and the product columns.
Nothing is written and no value is ever filled in.
"""
from __future__ import annotations
import hashlib
import json
from datetime import date
from central.app.unified_fiscal_coverage import PIS_COFINS_FIELDS, catalog_integrity, evaluate_product, rtc_enabled
PRODUCT_TABLE = "C000025"
RTC_CCLASSTRIB_TABLE = "RTC_CCLASSTRIB"
RTC_CST_TABLE = "RTC_CST"
PRODUCT_COLUMNS = ("CODIGO", "CCLASSTRIB") + PIS_COFINS_FIELDS + ("PISCOFINS", "INCIDENCIA_PISCOFINS")
TRUTHY = {"1", "S", "T", "TRUE"}
def detect_rtc_capabilities(schema: dict, *, param_value=None, catalog: dict | None = None, physical_fk: bool | None = None) -> dict:
"""schema: {TABLE: {COLUMN, ...}}. Fail-closed: anything not proven is False. Old databases without RTC simply report False."""
product_cols = set(schema.get(PRODUCT_TABLE, ()))
class_cols = set(schema.get(RTC_CCLASSTRIB_TABLE, ()))
cst_cols = set(schema.get(RTC_CST_TABLE, ()))
has_product = "CCLASSTRIB" in product_cols
has_class = {"CCLASSTRIB", "CSTIBSCBS"} <= class_cols
has_cst = "CSTIBSCBS" in cst_cols
caps = {
"HAS_PRODUCT_CCLASSTRIB": has_product, "HAS_RTC_CCLASSTRIB_TABLE": has_class, "HAS_RTC_CST_TABLE": has_cst,
"HAS_RTC_RELATIONSHIP": bool(has_product and has_class and has_cst), "HAS_PHYSICAL_RTC_FK": bool(physical_fk),
"RTC_ENABLED_BY_PARAM": False if param_value is None else str(param_value).strip().upper() in TRUTHY,
"RTC_CATALOG_VALID": False,
}
if catalog is not None and has_class and has_cst:
caps["RTC_CATALOG_VALID"] = catalog_integrity(catalog)["valid"]
return caps
def load_rtc_context(cursor) -> tuple[dict, dict | None, dict]:
"""Schema + catalog + ERP parameter through a READ ONLY cursor. Never raises for a database that lacks RTC."""
schema: dict[str, set] = {}
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}
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]
except Exception: # noqa: BLE001 - unreadable parameter => RTC stays off (not enabled by the ERP)
extras["param_value"] = None
try:
cursor.execute("SELECT COUNT(*) FROM RDB$RELATION_CONSTRAINTS WHERE RDB$RELATION_NAME = 'C000025' AND RDB$CONSTRAINT_TYPE = 'FOREIGN KEY' AND RDB$CONSTRAINT_NAME CONTAINING 'CCLASSTRIB'")
extras["physical_fk"] = cursor.fetchone()[0] > 0
except Exception: # noqa: BLE001
pass
catalog = None
class_cols = schema.get(RTC_CCLASSTRIB_TABLE, set())
if {"CCLASSTRIB", "CSTIBSCBS"} <= class_cols and "CSTIBSCBS" in schema.get(RTC_CST_TABLE, set()):
validity = "INICIO_VIGENCIA" in class_cols and "FIM_VIGENCIA" in class_cols
cursor.execute("SELECT TRIM(CCLASSTRIB), TRIM(CSTIBSCBS)" + (", INICIO_VIGENCIA, FIM_VIGENCIA" if validity else "") + " FROM RTC_CCLASSTRIB")
rows = cursor.fetchall()
classes = {r[0]: (r[1] or None) for r in rows}
cursor.execute("SELECT TRIM(CSTIBSCBS) FROM RTC_CST")
catalog = {"classes": classes, "cst": {r[0] for r in cursor.fetchall()}, "duplicates": len(rows) - len(classes)}
if validity:
catalog["validity"] = {r[0]: (r[2], r[3]) for r in rows}
return schema, catalog, extras
def read_products(cursor, schema: dict) -> list[dict]:
"""SELECT of only the columns that exist (old schemas keep working)."""
have = schema.get(PRODUCT_TABLE, set())
columns = [c for c in PRODUCT_COLUMNS if c in have]
if "CODIGO" not in columns:
return []
cursor.execute(f"SELECT {', '.join(columns)} FROM {PRODUCT_TABLE} ORDER BY CODIGO")
return [dict(zip(columns, row)) for row in cursor.fetchall()]
def assess_from_connection(conn, company: dict | None, *, today: date | None = None, rtc_override: bool | None = None) -> dict:
"""One read-only pass. Returns {"context": {...}, "findings": {product_code: evaluate_product(...)}}.
rtc_override forces RTC on/off for simulations WITHOUT touching the ERP parameter (tests / what-if only)."""
cursor = conn.cursor()
schema, catalog, extras = load_rtc_context(cursor)
caps = detect_rtc_capabilities(schema, param_value=extras["param_value"], catalog=catalog, physical_fk=extras["physical_fk"])
enabled = rtc_enabled(caps) if rtc_override is None else bool(rtc_override and caps["HAS_RTC_RELATIONSHIP"])
rtc = {"enabled": enabled, "catalog": catalog, "catalog_valid": caps["RTC_CATALOG_VALID"]}
today = today or date.today()
products = read_products(cursor, schema)
findings = {}
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
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)},
"findings": findings}
+21
View File
@@ -1,3 +1,24 @@
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parents[1] / "src"))
import pytest
@pytest.fixture(autouse=True)
def _no_real_firebird_for_unified_fiscal(monkeypatch, request):
"""The unified fiscal assessment opens its own READ ONLY connection; unit tests have no Firebird, so it is neutral here unless a test opts in."""
if request.node.get_closest_marker("real_unified_fiscal"):
return
from gti_fiscal_agent import firebird_reader, unified_fiscal
class _Conn:
def close(self):
pass
monkeypatch.setattr(firebird_reader, "_connect", lambda env: _Conn())
monkeypatch.setattr(unified_fiscal, "assess_from_connection", lambda conn, company, **kw: {"context": {"rtc_enabled": False, "rtc_supported": False, "rtc_catalog_valid": False, "products_evaluated": 0}, "findings": {}})
def pytest_configure(config):
config.addinivalue_line("markers", "real_unified_fiscal: use the real unified fiscal assessment (with a fake cursor) instead of the neutral stub")
-134
View File
@@ -1,134 +0,0 @@
"""RTC (cClassTrib / CST IBS-CBS) and PIS/COFINS: capabilities, validators, applicability, legacy vs expanded coverage. Read-only."""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parents[1] / "src"))
from gti_fiscal_agent import rtc_pis_cofins as r # noqa: E402
FULL_SCHEMA = {"C000025": {"CODIGO", "CCLASSTRIB", *r.PIS_COFINS_COLUMNS}, "RTC_CCLASSTRIB": {"CCLASSTRIB", "CSTIBSCBS"}, "RTC_CST": {"CSTIBSCBS"}}
CATALOG = {"classes": {"000001": "000", "200003": "200", "999999": None}, "cst": {"000", "200"}, "duplicates": 0}
def caps(schema=FULL_SCHEMA, param=0, catalog=CATALOG):
c = r.detect_rtc_capabilities(schema, param_value=param, catalog=catalog, physical_fk=True)
c["_PIS_COFINS_SCHEMA"] = all(col in schema.get("C000025", ()) for col in r.PIS_COFINS_COLUMNS[:6])
return c
def test_database_without_rtc_fails_closed_and_is_not_blocked():
c = caps({"C000025": {"CODIGO"}}, param=None, catalog=None)
assert not any(c[k] for k in ("HAS_PRODUCT_CCLASSTRIB", "HAS_RTC_CCLASSTRIB_TABLE", "HAS_RTC_CST_TABLE", "HAS_RTC_RELATIONSHIP", "RTC_CATALOG_VALID"))
assert r.rtc_applicability(c) == "NOT_SUPPORTED"
found = r.expanded_findings({"CODIGO": 1}, c, None)
assert found["blockers"] == [] and found["unknown"] == []
def test_partial_rtc_column_without_catalog_tables_is_not_a_relationship():
c = caps({"C000025": {"CODIGO", "CCLASSTRIB"}}, param=1, catalog=None)
assert c["HAS_PRODUCT_CCLASSTRIB"] and not c["HAS_RTC_RELATIONSHIP"] and not c["RTC_CATALOG_VALID"]
assert r.rtc_applicability(c) == "NOT_SUPPORTED"
def test_full_model_and_valid_catalog_capabilities():
good = {"classes": {"000001": "000"}, "cst": {"000"}, "duplicates": 0}
c = caps(catalog=good)
assert c["HAS_RTC_RELATIONSHIP"] and c["RTC_CATALOG_VALID"] and c["HAS_PHYSICAL_RTC_FK"] and c["RTC_ENABLED_BY_PARAM"] is False and c["LOCAL_CATALOG_ONLY"]
assert not caps(catalog=CATALOG)["RTC_CATALOG_VALID"] # a class pointing to a missing CST makes the catalog invalid
def test_cclasstrib_validator_states():
assert r.validate_cclasstrib(None, CATALOG) == "EMPTY" and r.validate_cclasstrib(" ", CATALOG) == "EMPTY"
assert r.validate_cclasstrib("12A456", CATALOG) == "INVALID_FORMAT" and r.validate_cclasstrib("12345", CATALOG) == "INVALID_FORMAT"
assert r.validate_cclasstrib("123456", CATALOG) == "NOT_IN_LOCAL_CATALOG"
assert r.validate_cclasstrib("200003", CATALOG) == "VALID_WITH_CST"
assert r.validate_cclasstrib("999999", CATALOG) == "CST_MISSING"
assert r.validate_cclasstrib("000001", {"classes": {"000001": None}, "cst": set()}) == "VALID_LOCAL"
assert r.validate_cclasstrib("000001", None) == "NOT_IN_LOCAL_CATALOG"
def test_pis_cofins_validators():
assert r.validate_pis_cofins_cst("") == "EMPTY" and r.validate_pis_cofins_cst(None) == "EMPTY"
assert r.validate_pis_cofins_cst("01") == "VALID" and r.validate_pis_cofins_cst("73") == "VALID"
assert r.validate_pis_cofins_cst("2") == "INVALID_FORMAT" and r.validate_pis_cofins_cst("10") == "INVALID_FORMAT"
assert r.validate_pis_cofins_rate("0,65") == "VALID" and r.validate_pis_cofins_rate("3") == "VALID" and r.validate_pis_cofins_rate("") == "EMPTY"
assert r.validate_pis_cofins_rate("abc") == "INVALID_RATE" and r.validate_pis_cofins_rate("150") == "INVALID_RATE"
ok = r.validate_pis_cofins({"PISCOFINS": "S", "INCIDENCIA_PISCOFINS": "MONOFASICA", "COD_SAIDA_PIS": "04", "COD_SAIDA_COFINS": "04"})
assert ok["FLAG_CONSISTENCY"] == "CONSISTENT"
bad = r.validate_pis_cofins({"PISCOFINS": "N", "INCIDENCIA_PISCOFINS": "NENHUMA", "COD_SAIDA_PIS": "01"})
assert bad["FLAG_CONSISTENCY"] == "INCONSISTENT"
def test_empty_pis_cofins_is_unknown_applicability_not_a_blocker():
found = r.expanded_findings({"CODIGO": 1, "PISCOFINS": "N", "INCIDENCIA_PISCOFINS": "NENHUMA"}, caps(), CATALOG)
assert found["blockers"] == [] and found["unknown"] == ["PIS_COFINS"] and found["rtc_applicability"] == "NOT_ENABLED_BY_ERP"
def test_populated_pis_cofins_is_applicable_and_valid_data_completes():
row = {"CODIGO": 1, "PISCOFINS": "S", "INCIDENCIA_PISCOFINS": "SUBST.TRIBUTARIA", "COD_ENTRADA_PIS": "73", "COD_SAIDA_PIS": "01", "ALIQ_PIS": "0,65",
"COD_ENTRADA_COFINS": "73", "COD_SAIDA_COFINS": "01", "ALIQ_COFINS": "3"}
found = r.expanded_findings(row, caps(), CATALOG)
assert found["blockers"] == [] and found["unknown"] == [] and found["pis_cofins_applicability"] == "APPLICABLE"
def test_invalid_values_where_applicable_create_specific_blockers():
row = {"CODIGO": 2, "PISCOFINS": "N", "INCIDENCIA_PISCOFINS": "NENHUMA", "COD_ENTRADA_PIS": "2", "COD_SAIDA_PIS": "2", "ALIQ_PIS": "2",
"COD_ENTRADA_COFINS": "2", "COD_SAIDA_COFINS": "2", "ALIQ_COFINS": "x"}
kinds = {k for k, _ in r.expanded_findings(row, caps(), CATALOG)["blockers"]}
assert {"PIS_CST_INVALID", "COFINS_CST_INVALID", "COFINS_RATE_INVALID", "PIS_COFINS_FLAG_INCONSISTENT"} <= kinds
def test_rtc_becomes_applicable_only_when_the_erp_enables_it():
on = caps(param=1)
assert r.rtc_applicability(on) == "APPLICABLE"
assert {k for k, _ in r.expanded_findings({"CODIGO": 1}, on, CATALOG)["blockers"]} == {"CCLASSTRIB_MISSING"}
assert {k for k, _ in r.expanded_findings({"CODIGO": 1, "CCLASSTRIB": "123456"}, on, CATALOG)["blockers"]} == {"CCLASSTRIB_NOT_IN_CATALOG"}
assert {k for k, _ in r.expanded_findings({"CODIGO": 1, "CCLASSTRIB": "999999"}, on, CATALOG)["blockers"]} == {"RTC_CST_INVALID"}
assert {k for k, _ in r.expanded_findings({"CODIGO": 1, "CCLASSTRIB": "12x"}, on, CATALOG)["blockers"]} == {"CCLASSTRIB_INVALID"}
assert not [k for k, _ in r.expanded_findings({"CODIGO": 1, "CCLASSTRIB": "200003"}, on, CATALOG)["blockers"] if k.startswith(("CCLASS", "RTC"))]
assert r.rtc_applicability(caps(param=None)) == "UNKNOWN_APPLICABILITY"
def test_legacy_and_expanded_coverage_are_reported_side_by_side_and_close():
products = [{"CODIGO": 1}, {"CODIGO": 2}, {"CODIGO": 3},
{"CODIGO": 4, "PISCOFINS": "S", "INCIDENCIA_PISCOFINS": "MONOFASICA", "COD_SAIDA_PIS": "01", "COD_SAIDA_COFINS": "01", "COD_ENTRADA_PIS": "73", "COD_ENTRADA_COFINS": "73"},
{"CODIGO": 5, "COD_SAIDA_PIS": "2", "PISCOFINS": "S"}]
states = {"1": "COMPLETE", "2": "BLOCKED_MISSING_EVIDENCE", "3": "FAILED_TECHNICAL", "4": "COMPLETE", "5": "CORRECTED"}
out = r.expanded_scope_summary(products, states, caps(), CATALOG)
assert out["legacy_scope"] == {"complete": 3, "safe_blocked": 1, "failed": 1}
# 1 unknown (no PIS/COFINS evidence), 1 complete (#4 valid), blocked = legacy-blocked #2 + #5 (invalid CST), failed #3
assert out["expanded_scope"] == {"complete": 1, "safe_blocked": 2, "failed": 1, "unknown_applicability": 1}
assert out["invariant_pass"] and out["blockers_by_type"].get("PIS_CST_INVALID") == 1 and out["local_catalog_only"]
class FakeCursor:
def __init__(self, tables, rows):
self.tables, self.rows, self.result = tables, rows, []
def execute(self, sql):
assert sql.lstrip().upper().startswith("SELECT") # read-only by construction
if "RDB$RELATION_FIELDS" in sql:
self.result = [(t, c) for t, cols in self.tables.items() for c in cols]
elif "USAR_REFORMA_TRIBUTARIA" in sql:
self.result = [(0,)]
elif "RDB$RELATION_CONSTRAINTS" in sql:
self.result = [(1,)]
elif "FROM RTC_CCLASSTRIB" in sql:
self.result = [("000001", "000")]
elif "FROM RTC_CST" in sql:
self.result = [("000",)]
elif "FROM C000025" in sql:
self.result = self.rows
def fetchall(self):
return self.result
def fetchone(self):
return self.result[0] if self.result else None
def test_loader_reads_schema_catalog_and_param_with_select_only():
cur = FakeCursor({"C000025": ["CODIGO", "CCLASSTRIB"], "RTC_CCLASSTRIB": ["CCLASSTRIB", "CSTIBSCBS"], "RTC_CST": ["CSTIBSCBS"], "PARAMETROS": ["USAR_REFORMA_TRIBUTARIA"]}, [(1, None)])
schema, catalog, extras = r.load_rtc_context(cur)
assert catalog == {"classes": {"000001": "000"}, "cst": {"000"}, "duplicates": 0} and extras == {"param_value": 0, "physical_fk": True}
assert r.read_products_rtc_pis_cofins(cur, schema) == [{"CODIGO": 1, "CCLASSTRIB": None}]
+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)
+39
View File
@@ -146,3 +146,42 @@ def test_heartbeats_keep_flowing_while_a_long_job_runs(monkeypatch, tmp_path):
monkeypatch.setattr(service, "execute_job", lambda *a, **k: (time.sleep(0.4), ({"ok": 1}, None))[1])
service.run(_config(agent_id="2", poll_interval_seconds=0, state_db=str(tmp_path / "a.db")), {"AGENT_ID": "2"}, stopper=stopper)
assert beats.count("BUSY") >= 3 # heartbeats during the job, not only before it
def _findings(**by_code):
out = {}
for code, kinds in by_code.items():
out[code] = {"blockers": [{"type": t, "category": c, "reason": "R"} for t, c in kinds], "applicability": {"PIS_COFINS": "REQUIRED", "RTC": "NOT_APPLICABLE"}, "input_fingerprint": "fp" + code, "needs_review": False}
return out
def test_unified_fiscal_blockers_change_the_single_coverage_and_the_single_queue(env_setup, monkeypatch):
from gti_fiscal_agent import unified_fiscal
findings = _findings(**{"2": [("PIS_CST_INVALID", "PIS"), ("COFINS_CST_INVALID", "COFINS")]})
monkeypatch.setattr(unified_fiscal, "assess_from_connection", lambda conn, company, **kw: {"context": {"rtc_enabled": False, "rtc_supported": True, "rtc_catalog_valid": True}, "findings": findings})
hub, state = env_setup
result, error = run_job("INITIAL_SYNC", [product("1"), product("2"), product("3", ncm="")], state, monkeypatch)
assert error is None
op = result["operational"]
assert op["TOTAL_PRODUCTS"] == 3 and op["COMPLETE_PRODUCTS"] == 1 and op["SAFE_BLOCKED"] == 2 and op["BLOCKED_FISCAL_DATA"] == 1 # one number, one definition
assert op["COMPLETE_PRODUCTS"] + op["SAFE_BLOCKED"] + op["FAILED_TECHNICAL"] + op["UNCLASSIFIED"] == 3
queue = sorted((b["product_code"], b["field"], b["reason_code"]) for b in result["blocked_products"])
assert queue == [("2", "COFINS", "COFINS_CST_INVALID"), ("2", "PIS", "PIS_CST_INVALID"), ("3", "NCM", "INVALID_NCM")] # ONE queue: NCM and fiscal blockers together
assert result["resolved_products"] == ["1"] # a fiscal blocker keeps the product out of resolved
assert result["fiscal_context"]["rtc_enabled"] is False and result["fiscal_context"]["evaluated_all"] is True
assert "expanded_scope" not in result
def test_rtc_context_is_reported_but_blockers_only_exist_when_the_evaluator_produced_them(env_setup, monkeypatch):
from gti_fiscal_agent import unified_fiscal
monkeypatch.setattr(unified_fiscal, "assess_from_connection", lambda conn, company, **kw: {"context": {"rtc_enabled": True, "rtc_supported": True, "rtc_catalog_valid": True}, "findings": _findings(**{"1": [("CCLASSTRIB_MISSING", "RTC")]})})
hub, state = env_setup
result, _ = run_job("REPROCESS_ALL", [product("1"), product("2")], state, monkeypatch)
assert [b["reason_code"] for b in result["blocked_products"]] == ["CCLASSTRIB_MISSING"] and result["blocked_products"][0]["field"] == "RTC"
assert result["fiscal_context"]["rtc_enabled"] is True and result["operational"]["BLOCKED_FISCAL_DATA"] == 1
def test_partial_reprocess_does_not_claim_a_full_fiscal_evaluation(env_setup, monkeypatch):
hub, state = env_setup
result, _ = run_job("REPROCESS_PRODUCTS", [product("1")], state, monkeypatch)
assert result["fiscal_context"] is None and result["operational"] is None
+4 -1
View File
@@ -11,7 +11,7 @@ from .classification import ENGINE_VERSION, Classifier
from .config import settings
from .datasets.service import DatasetService
from .jobs import SYNC_KINDS, claim_next_job, complete_job, create_job, fail_job, schedule_automatic_sync
from .blocked_products import list_blockers, resolve_blocker, resolve_product_blockers, upsert_blocker
from .blocked_products import list_blockers, resolve_blocker, resolve_product_blockers, resolve_stale_fiscal_blockers, upsert_blocker
from .context_keys import validate_entry
from .models import AgentSnapshot, BlockedProduct, FiscalContextEntry, ClassificationCache, ClassificationRequest, ClassificationResult, Dataset, Installation, Job
from .events import record_event
@@ -422,10 +422,13 @@ def router(session_factory, bootstrap_token):
if job is None or job.agent_id != agent.id: raise HTTPException(404, "job not found")
result_summary = payload.get("result_summary", {})
installation = session.scalar(select(Installation).where(Installation.agent_id == agent.id))
pass_started = datetime.now(timezone.utc)
for blocker in result_summary.get("blocked_products", []):
upsert_blocker(session, agent.tenant_id, blocker, installation_id=installation.id if installation else None)
for product_code in result_summary.get("resolved_products", []):
resolve_product_blockers(session, agent.tenant_id, product_code, installation_id=installation.id if installation else None)
if installation is not None and (result_summary.get("fiscal_context") or {}).get("evaluated_all"):
resolve_stale_fiscal_blockers(session, agent.tenant_id, installation.id, pass_started)
if not complete_job(session, job, result_summary): raise HTTPException(409, "job is not RUNNING")
if installation is not None:
_apply_job_outcome(session, agent, installation, job, result_summary, failed=False)
+18 -1
View File
@@ -18,7 +18,7 @@ def upsert_blocker(session, tenant_id: int, payload: dict, *, installation_id: i
row = session.scalar(select(BlockedProduct).where(BlockedProduct.blocker_fingerprint == fingerprint))
now = datetime.now(timezone.utc)
if row is None:
row = BlockedProduct(tenant_id=tenant_id, installation_id=installation_id, company=payload.get("company", ""), product_code=str(payload.get("product_code", "")), gtin=payload.get("gtin"), description=payload.get("description"), blocker_type=payload.get("blocker_type", "BLOCKED_MISSING_EVIDENCE"), field=payload.get("field", ""), reason_code=payload.get("reason_code", ""), evidence_summary=payload.get("evidence_summary") or {}, input_fingerprint=payload.get("input_fingerprint", ""), blocker_fingerprint=fingerprint)
row = BlockedProduct(tenant_id=tenant_id, installation_id=installation_id, company=payload.get("company", ""), product_code=str(payload.get("product_code", "")), gtin=payload.get("gtin"), description=payload.get("description"), blocker_type=payload.get("blocker_type", "BLOCKED_MISSING_EVIDENCE"), field=payload.get("field", ""), reason_code=payload.get("reason_code", ""), evidence_summary=payload.get("evidence_summary") or {}, input_fingerprint=payload.get("input_fingerprint", ""), blocker_fingerprint=fingerprint, first_seen_at=now, last_seen_at=now)
session.add(row)
else:
row.last_seen_at = now
@@ -73,3 +73,20 @@ def resolve_product_blockers(session, tenant_id: int, product_code: str, source:
if rows:
session.commit()
return len(rows)
FISCAL_BLOCKER_FIELDS = ("PIS", "COFINS", "RTC")
def resolve_stale_fiscal_blockers(session, tenant_id: int, installation_id: int, since: datetime) -> int:
"""After a FULL evaluation of the unified fiscal fields, every open PIS/COFINS/RTC blocker of the installation that this pass did not
re-report (last_seen_at < since) no longer holds: the ERP data changed, or RTC was switched off. Other blocker fields are untouched."""
rows = session.scalars(select(BlockedProduct).where(BlockedProduct.tenant_id == tenant_id, BlockedProduct.installation_id == installation_id,
BlockedProduct.resolved_at.is_(None), BlockedProduct.field.in_(FISCAL_BLOCKER_FIELDS), BlockedProduct.last_seen_at < since)).all()
now = datetime.now(timezone.utc)
for row in rows:
row.resolved_at = now
row.resolution_source = "AUTO_REEVALUATION"
if rows:
session.commit()
return len(rows)
+1 -1
View File
@@ -16,7 +16,7 @@ from .panel_presentation import health_reason_label, blocker_reason_label, block
HEALTH_STATES = ("ONLINE", "DEGRADED", "OFFLINE", "FAILED", "UNKNOWN")
BLOCKER_FIELDS = ("CFOP", "ST", "ICMS", "CSOSN", "NCM", "OTHER")
BLOCKER_FIELDS = ("NCM", "CEST", "CFOP", "ST", "ICMS", "CSOSN", "PIS", "COFINS", "RTC", "OTHER")
ATTENTION_CANDIDATE_LIMIT = 10
INACTIVE_ENROLLMENT_STATES = ("REVOKED", "FAILED", "EXPIRED")
+12 -3
View File
@@ -6,7 +6,7 @@ import json
STATES = {
"COMPLETE", "CORRECTED", "BLOCKED_MISSING_EVIDENCE", "BLOCKED_CONFLICT",
"BLOCKED_COMPANY_CONTEXT", "BLOCKED_SCHEMA", "FAILED_TECHNICAL",
"BLOCKED_COMPANY_CONTEXT", "BLOCKED_SCHEMA", "BLOCKED_FISCAL_DATA", "FAILED_TECHNICAL", "NEEDS_REVIEW",
}
@@ -17,7 +17,8 @@ def blocker_fingerprint(tenant: str, company: str, product_code: str, field: str
return hashlib.sha256(json.dumps(raw, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
def classify_product_state(product: dict, decision: dict, *, write_status: str | None = None) -> dict:
def classify_product_state(product: dict, decision: dict, *, write_status: str | None = None, fiscal: dict | None = None) -> dict:
"""`fiscal` = unified_fiscal_coverage.evaluate_product(...): PIS/COFINS + (only when the ERP has RTC on) cClassTrib. One definition of COMPLETE."""
status = str(decision.get("status") or "").upper()
if write_status == "FAILED":
state = "FAILED_TECHNICAL"
@@ -33,7 +34,13 @@ def classify_product_state(product: dict, decision: dict, *, write_status: str |
state = "CORRECTED"
else:
state = "COMPLETE"
return {"product_code": str(product.get("CODIGO") or ""), "state": state, "decision_status": status}
fiscal = fiscal or {}
if state in ("COMPLETE", "CORRECTED"):
if fiscal.get("blockers"):
state = "BLOCKED_FISCAL_DATA"
elif fiscal.get("needs_review"):
state = "NEEDS_REVIEW"
return {"product_code": str(product.get("CODIGO") or ""), "state": state, "decision_status": status, "fiscal_blockers": list(fiscal.get("blockers") or [])}
def should_retry_blocker(previous_input_fingerprint: str | None, current_input_fingerprint: str) -> bool:
@@ -44,6 +51,8 @@ def summarize_operational_states(states: list[dict]) -> dict:
counts = Counter(item.get("state") for item in states)
result = {state: counts.get(state, 0) for state in sorted(STATES)}
result["TOTAL_PRODUCTS"] = len(states)
result["UNCLASSIFIED"] = result["NEEDS_REVIEW"]
result["COMPLETE_PRODUCTS"] = result["COMPLETE"] + result["CORRECTED"]
result["SAFE_BLOCKED"] = sum(result[state] for state in STATES if state.startswith("BLOCKED_"))
result["AUTONOMOUSLY_PROCESSABLE_PRODUCTS"] = result["TOTAL_PRODUCTS"] - result["FAILED_TECHNICAL"]
result["AGENT_HEALTH"] = "HEALTHY" if result["FAILED_TECHNICAL"] == 0 else "DEGRADED"
+15 -16
View File
@@ -80,7 +80,7 @@ def operational_priority(blocker) -> str:
return "CRITICAL"
if blocker_type in {"BLOCKED_MISSING_EVIDENCE", "MISSING_EVIDENCE"}:
return "HIGH"
if field in {"CFOP", "ST", "ICMS", "CSOSN", "NCM"} or reason in {"INSUFFICIENT_EVIDENCE", "NO_EVIDENCE"}:
if field in {"CFOP", "ST", "ICMS", "CSOSN", "NCM", "CEST", "PIS", "COFINS", "RTC"} or reason in {"INSUFFICIENT_EVIDENCE", "NO_EVIDENCE"}:
return "NORMAL"
return "LOW"
@@ -120,16 +120,17 @@ def job_kind_label(value):
return _human_label(value, {"SYNC_INCREMENTAL": "Sincronização incremental", "INITIAL_SYNC": "Sincronização inicial", "REPROCESS_ALL": "Reprocessar tudo", "REPROCESS_PRODUCTS": "Reprocessar produtos"})
_RTC_LABELS = {
"CCLASSTRIB_MISSING": "cClassTrib ausente", "CCLASSTRIB_INVALID": "cClassTrib inválido", "CCLASSTRIB_NOT_IN_CATALOG": "cClassTrib fora do catálogo local",
"RTC_CST_INVALID": "CST IBS/CBS inválido", "PIS_CST_MISSING": "CST PIS ausente", "PIS_CST_INVALID": "CST PIS inválido", "PIS_RATE_INVALID": "Alíquota PIS inválida",
"COFINS_CST_MISSING": "CST COFINS ausente", "COFINS_CST_INVALID": "CST COFINS inválido", "COFINS_RATE_INVALID": "Alíquota COFINS inválida",
"PIS_COFINS_FLAG_INCONSISTENT": "Flag PIS/COFINS inconsistente",
_FISCAL_LABELS = {
"CCLASSTRIB_MISSING": "cClassTrib ausente", "CCLASSTRIB_INVALID_FORMAT": "cClassTrib com formato inválido", "CCLASSTRIB_NOT_IN_CATALOG": "cClassTrib fora do catálogo local",
"RTC_CST_MISSING": "CST IBS/CBS ausente no catálogo", "RTC_CATALOG_INVALID": "Catálogo RTC local inválido",
"PIS_CST_MISSING": "CST PIS ausente", "PIS_CST_INVALID": "CST PIS inválido", "PIS_RATE_MISSING": "Alíquota PIS ausente", "PIS_RATE_INVALID": "Alíquota PIS inválida", "PIS_CST_INCIDENCE_MISMATCH": "CST PIS incompatível com a incidência",
"COFINS_CST_MISSING": "CST COFINS ausente", "COFINS_CST_INVALID": "CST COFINS inválido", "COFINS_RATE_MISSING": "Alíquota COFINS ausente", "COFINS_RATE_INVALID": "Alíquota COFINS inválida", "COFINS_CST_INCIDENCE_MISMATCH": "CST COFINS incompatível com a incidência",
"PIS_COFINS_FLAG_INCONSISTENT": "Flag PIS/COFINS inconsistente", "BLOCKED_FISCAL_DATA": "Dado fiscal do cadastro",
}
def blocker_type_label(value):
return _human_label(value, {"BLOCKED_MISSING_EVIDENCE": "Evidência fiscal ausente", "MISSING_EVIDENCE": "Evidência fiscal ausente", "TECHNICAL_FAILURE": "Falha técnica", "CONNECTION_ERROR": "Erro de conexão", "OTHER": "Outro", **_RTC_LABELS})
return _human_label(value, {"BLOCKED_MISSING_EVIDENCE": "Evidência fiscal ausente", "MISSING_EVIDENCE": "Evidência fiscal ausente", "TECHNICAL_FAILURE": "Falha técnica", "CONNECTION_ERROR": "Erro de conexão", "OTHER": "Outro", **_FISCAL_LABELS})
_REASON_LABELS = {
@@ -143,7 +144,7 @@ _REASON_LABELS = {
def blocker_reason_label(value):
return _human_label(value, _REASON_LABELS)
return _human_label(value, {**_REASON_LABELS, **_FISCAL_LABELS})
def health_reason_label(value):
@@ -160,12 +161,10 @@ def priority_label(value):
return _human_label(value, {"CRITICAL": "Crítica", "HIGH": "Alta", "NORMAL": "Média", "MEDIUM": "Média", "LOW": "Baixa"})
def expanded_scope_view(summary):
"""Panel-ready view of the Agent's `expanded_scope` job summary (legacy and expanded coverage side by side). None when absent/unavailable."""
if not isinstance(summary, dict) or "expanded_scope" not in summary:
def rtc_status_view(summary):
"""Discreet Reforma Tributária status from the last completed job's `fiscal_context`. None when the Agent did not report it (older Agents)."""
context = (summary or {}).get("fiscal_context") if isinstance(summary, dict) else None
if not isinstance(context, dict) or "rtc_enabled" not in context:
return None
legacy, expanded, total = summary.get("legacy_scope") or {}, summary["expanded_scope"] or {}, summary.get("total_products") or 0
percent = lambda part: round(100 * part / total, 1) if total else None
return {"total": total, "legacy": legacy, "expanded": expanded, "legacy_percent": percent(legacy.get("complete", 0)), "expanded_percent": percent(expanded.get("complete", 0)),
"blockers": [{"type": key, "label": blocker_type_label(key), "count": count} for key, count in (summary.get("blockers_by_type") or {}).items()],
"capabilities": summary.get("capabilities") or {}, "local_catalog_only": bool(summary.get("local_catalog_only"))}
return {"enabled": bool(context["rtc_enabled"]), "label": "Ativa" if context["rtc_enabled"] else "Desativada", "supported": bool(context.get("rtc_supported")),
"catalog_valid": bool(context.get("rtc_catalog_valid"))}
+211
View File
@@ -0,0 +1,211 @@
"""UNIFIED fiscal completeness: ONE definition of "complete" for a product (pure, no I/O).
mandatory(product, company, rtc) = base fiscal fields (decided elsewhere: NCM/CEST/CFOP/ICMS/IPI ...)
+ PIS/COFINS when they APPLY to the product
+ RTC (cClassTrib -> CST IBS/CBS) ONLY when the ERP has the tax reform switched on.
There is no "legacy scope" and no "expanded scope": every finding here becomes a normal blocker in the single queue and
changes the single coverage number. Nothing here writes to the ERP or invents a fiscal value.
Sources (see docs/pis_cofins_applicability.md): SPED EFD-Contribuicoes table 4.3.3/4.3.4 (CST domain), 4.3.10-4.3.15 (special regimes),
LC 123/2006 art. 13/18 (PIS/Cofins inside the Simples DAS), Leis 9.718/98, 10.637/2002, 10.833/2003 (cumulative / non-cumulative),
NT 2025.002-RTC v1.51 (IBS/CBS fields), LC 214/2025 art. 348.
"""
from __future__ import annotations
import re
import unicodedata
from datetime import date
# Official CST PIS / CST COFINS domain (SPED tables 4.3.3 / 4.3.4). Membership only - never used to pick a value.
CST_DOMAIN = frozenset([f"{n:02d}" for n in (*range(1, 10), 49, *range(50, 57), *range(60, 68), *range(70, 76), 98, 99)])
RATE_REQUIRED_CST = frozenset({"01", "02"}) # taxable by percentage: a rate is needed
RATE_NOT_APPLICABLE_CST = frozenset({"03", "04", "05", "06", "07", "08", "09"}) # per-unit amount / monophasic / ST / zero / exempt / no incidence / suspended
INCIDENCE_CST = {"MONOFASICA": frozenset({"02", "03", "04"}), "SUBSTITUICAO": frozenset({"05"})} # 4.3.10/4.3.11 (02,03,04) and 4.3.12 (05)
SIMPLES_FAMILY = frozenset({"1", "2", "4"}) # CRT 1 Simples, 2 Simples excess sublimit, 4 MEI (LC 123/2006): PIS/Cofins are collected inside the DAS
PIS_COFINS_FIELDS = ("COD_ENTRADA_PIS", "COD_SAIDA_PIS", "ALIQ_PIS", "COD_ENTRADA_COFINS", "COD_SAIDA_COFINS", "ALIQ_COFINS")
FIELD_BY_BLOCKER_PREFIX = (("PIS", "PIS"), ("COFINS", "COFINS"), ("CCLASSTRIB", "RTC"), ("RTC", "RTC"))
def _text(value) -> str:
return "" if value is None else str(value).strip()
def blocker_category(blocker_type: str) -> str:
"""Dashboard category of a blocker type: PIS | COFINS | RTC | OTHER."""
for prefix, category in FIELD_BY_BLOCKER_PREFIX:
if blocker_type.startswith(prefix):
return category
return "OTHER"
def incidence_kind(value) -> str:
"""NONE | MONOFASICA | SUBSTITUICAO | UNKNOWN (accent/case-insensitive, encoding tolerant). Anything the SGV does not define is UNKNOWN, never NONE."""
raw = _text(value)
text = re.sub(r"[^A-Z]", "", unicodedata.normalize("NFKD", raw).encode("ascii", "ignore").decode().upper())
if not raw or text == "NENHUMA":
return "NONE"
if text.startswith("MONOF"):
return "MONOFASICA"
if text.startswith("SUBST"):
return "SUBSTITUICAO"
return "UNKNOWN"
def validate_cst(value) -> str:
code = _text(value)
if not code:
return "EMPTY"
return "VALID" if code in CST_DOMAIN else "INVALID"
def cst_suggestion(value) -> str | None:
"""A short numeric code that becomes an official CST once zero-padded to two digits ('4' -> '04'). Only a hint for the operator; never applied."""
code = _text(value)
return code.zfill(2) if code.isdigit() and len(code) == 1 and code.zfill(2) in CST_DOMAIN else None
def validate_rate(value) -> str:
text = _text(value)
if not text:
return "EMPTY"
if not re.fullmatch(r"\d{1,3}([.,]\d{1,4})?", text):
return "INVALID"
return "VALID" if float(text.replace(",", ".")) <= 100 else "INVALID"
# ------------------------------------------------------------------------------------------------- PIS / COFINS
def pis_cofins_applicability(product: dict, crt) -> dict:
"""{"state": NOT_APPLICABLE | REQUIRED | OPTIONAL | NEEDS_REVIEW, "basis": ...}.
NOT_APPLICABLE the product carries no product-level PIS/COFINS treatment at all (flag N/blank, incidence NENHUMA/blank, all six fields blank).
The tax is then settled elsewhere: inside the DAS (Simples family) or from the company default (regime normal).
REQUIRED the product itself claims a special treatment (flag S, or incidence MONOFASICA/SUBSTITUICAO): the exit CST (and a rate when the CST needs one) must be valid.
OPTIONAL no special claim, but some CST/rate was typed: it is not mandatory, yet whatever is there must be valid.
NEEDS_REVIEW the incidence text is not one the SGV defines (NENHUMA / MONOFASICA / SUBST.TRIBUTARIA): applicability cannot be decided.
"""
flag = _text(product.get("PISCOFINS")).upper()
kind = incidence_kind(product.get("INCIDENCIA_PISCOFINS"))
has_values = any(_text(product.get(c)) for c in PIS_COFINS_FIELDS)
if kind == "UNKNOWN":
return {"state": "NEEDS_REVIEW", "basis": "INCIDENCE_NOT_DEFINED_BY_SGV"}
if flag == "S" or kind in ("MONOFASICA", "SUBSTITUICAO"):
return {"state": "REQUIRED", "basis": "PRODUCT_DECLARES_SPECIAL_TREATMENT"}
if has_values:
return {"state": "OPTIONAL", "basis": "VALUES_TYPED_WITHOUT_SPECIAL_CLAIM"}
return {"state": "NOT_APPLICABLE", "basis": "SIMPLES_DAS" if _text(crt) in SIMPLES_FAMILY else "COMPANY_DEFAULT"}
def evaluate_pis_cofins(product: dict, crt) -> dict:
"""{"applicability": {...}, "blockers": [(type, reason)]}. Entry and exit are separate; a missing OPTIONAL value never blocks."""
app = pis_cofins_applicability(product, crt)
blockers: list[tuple[str, str]] = []
if app["state"] in ("NOT_APPLICABLE", "NEEDS_REVIEW"):
return {"applicability": app, "blockers": blockers}
kind = incidence_kind(product.get("INCIDENCIA_PISCOFINS"))
flag = _text(product.get("PISCOFINS")).upper()
for tax in ("PIS", "COFINS"):
entry, exit_, rate = product.get(f"COD_ENTRADA_{tax}"), product.get(f"COD_SAIDA_{tax}"), product.get(f"ALIQ_{tax}")
entry_state, exit_state, rate_state = validate_cst(entry), validate_cst(exit_), validate_rate(rate)
if entry_state == "INVALID":
blockers.append((f"{tax}_CST_INVALID", "ENTRY" + (f":UNPADDED_USE_{cst_suggestion(entry)}" if cst_suggestion(entry) else "")))
if exit_state == "INVALID":
blockers.append((f"{tax}_CST_INVALID", "EXIT" + (f":UNPADDED_USE_{cst_suggestion(exit_)}" if cst_suggestion(exit_) else "")))
if app["state"] == "REQUIRED" and exit_state == "EMPTY":
blockers.append((f"{tax}_CST_MISSING", "EXIT"))
if rate_state == "INVALID":
blockers.append((f"{tax}_RATE_INVALID", "RATE"))
if exit_state == "VALID":
code = _text(exit_)
if code in RATE_REQUIRED_CST and rate_state == "EMPTY":
blockers.append((f"{tax}_RATE_MISSING", f"CST_{code}_NEEDS_RATE"))
if kind in INCIDENCE_CST and code not in INCIDENCE_CST[kind]:
blockers.append((f"{tax}_CST_INCIDENCE_MISMATCH", f"{kind}:CST_{code}"))
if kind == "NONE" and app["state"] == "REQUIRED" and code in RATE_NOT_APPLICABLE_CST - {"03"} and flag == "S":
blockers.append((f"{tax}_CST_INCIDENCE_MISMATCH", f"NENHUMA:CST_{code}"))
if flag == "N" and kind != "NONE":
blockers.append(("PIS_COFINS_FLAG_INCONSISTENT", "FLAG_N_WITH_SPECIAL_INCIDENCE"))
if flag == "S" and kind == "NONE" and not any(_text(product.get(c)) for c in PIS_COFINS_FIELDS):
blockers.append(("PIS_COFINS_FLAG_INCONSISTENT", "FLAG_S_WITHOUT_INCIDENCE_OR_CODES"))
return {"applicability": app, "blockers": _dedupe(blockers)}
def _dedupe(items):
seen, out = set(), []
for item in items:
if item not in seen:
seen.add(item)
out.append(item)
return out
# ------------------------------------------------------------------------------------------------- RTC
def rtc_enabled(caps: dict) -> bool:
"""RTC is in scope ONLY when the ERP is switched to the reform (PARAMETROS.USAR_REFORMA_TRIBUTARIA truthy) AND its schema can hold it."""
return bool(caps.get("HAS_PRODUCT_CCLASSTRIB") and caps.get("HAS_RTC_RELATIONSHIP") and caps.get("RTC_ENABLED_BY_PARAM"))
def catalog_integrity(catalog: dict) -> dict:
"""catalog: {"classes": {cClassTrib: cst_or_None}, "cst": {code, ...}, "duplicates": int}."""
classes, cst = catalog.get("classes", {}), set(catalog.get("cst", ()))
bad_format = [c for c in classes if not re.fullmatch(r"\d{6}", str(c))]
bad_cst_format = [c for c in cst if not re.fullmatch(r"\d{3}", str(c))]
orphans = [c for c, s in classes.items() if s is None or str(s) not in cst]
return {"classes": len(classes), "cst": len(cst), "orphan_classes": len(orphans), "bad_class_format": len(bad_format), "bad_cst_format": len(bad_cst_format),
"duplicates": int(catalog.get("duplicates", 0)), "valid": bool(classes and cst and not orphans and not bad_format and not bad_cst_format and not catalog.get("duplicates"))}
def validate_cclasstrib(value, catalog: dict | None, today: date | None = None) -> str:
"""EMPTY | INVALID_FORMAT | NOT_IN_CATALOG | EXPIRED | CST_MISSING | VALID (CST IBS/CBS derived through the catalog, never stored on the product)."""
code = _text(value)
if not code:
return "EMPTY"
if not re.fullmatch(r"\d{6}", code):
return "INVALID_FORMAT"
if not catalog or code not in catalog.get("classes", {}):
return "NOT_IN_CATALOG"
validity = (catalog.get("validity") or {}).get(code)
if validity and today is not None:
start, end = validity
if (start and start > today) or (end and end < today):
return "EXPIRED"
cst = catalog["classes"][code]
return "VALID" if cst is not None and str(cst) in set(catalog.get("cst", ())) else "CST_MISSING"
def evaluate_rtc(product: dict, rtc: dict | None, today: date | None = None) -> dict:
"""rtc: {"enabled": bool, "catalog": {...}|None, "catalog_valid": bool}. Disabled => NOT_APPLICABLE and NO blocker, whatever CCLASSTRIB holds."""
if not rtc or not rtc.get("enabled"):
return {"applicability": {"state": "NOT_APPLICABLE", "basis": "RTC_DISABLED_IN_ERP"}, "blockers": []}
app = {"state": "REQUIRED", "basis": "RTC_ENABLED_IN_ERP"}
if not rtc.get("catalog_valid"):
return {"applicability": app, "blockers": [("RTC_CATALOG_INVALID", "LOCAL_CATALOG_FAILED_INTEGRITY")]}
state = validate_cclasstrib(product.get("CCLASSTRIB"), rtc.get("catalog"), today)
mapping = {"EMPTY": "CCLASSTRIB_MISSING", "INVALID_FORMAT": "CCLASSTRIB_INVALID_FORMAT", "NOT_IN_CATALOG": "CCLASSTRIB_NOT_IN_CATALOG", "EXPIRED": "CCLASSTRIB_NOT_IN_CATALOG", "CST_MISSING": "RTC_CST_MISSING"}
return {"applicability": app, "blockers": [(mapping[state], state)] if state in mapping else []}
# ------------------------------------------------------------------------------------------------- unified
def evaluate_product(product: dict, company: dict | None, rtc: dict | None, today: date | None = None) -> dict:
"""The single fiscal-completeness verdict for the fields this module owns.
Returns {"blockers": [{"type","category","reason"}], "applicability": {"PIS_COFINS": state, "RTC": state}, "mandatory_fields": [...], "needs_review": bool}."""
crt = (company or {}).get("crt")
pis = evaluate_pis_cofins(product, crt)
rtc_result = evaluate_rtc(product, rtc, today)
blockers = [{"type": kind, "category": blocker_category(kind), "reason": reason} for kind, reason in pis["blockers"] + rtc_result["blockers"]]
mandatory = []
if pis["applicability"]["state"] == "REQUIRED":
mandatory += ["COD_SAIDA_PIS", "COD_SAIDA_COFINS"]
if rtc_result["applicability"]["state"] == "REQUIRED":
mandatory.append("CCLASSTRIB")
return {"blockers": blockers, "applicability": {"PIS_COFINS": pis["applicability"]["state"], "RTC": rtc_result["applicability"]["state"]},
"basis": {"PIS_COFINS": pis["applicability"]["basis"], "RTC": rtc_result["applicability"]["basis"]}, "mandatory_fields": mandatory,
"needs_review": pis["applicability"]["state"] == "NEEDS_REVIEW"}
+2 -2
View File
@@ -15,7 +15,7 @@ from .config import settings
from .blocker_resolution import BlockerResolutionUnavailable, _company_key, resolve_blocker_with_context
from .context_keys import ALLOWED_KEYS, ENUMS
from .installation_identity import reference_agent_version
from .panel_presentation import expanded_scope_view, job_kind_label, job_status_label
from .panel_presentation import job_kind_label, job_status_label, rtc_status_view
from .control_plane import blocker_detail, blocker_filter_summary, dashboard_summary, installation_view, jobs_status_summary, list_blockers, list_installations, list_jobs, list_panel_events
from .human_auth import create_session, current_session, require_csrf, verify_password
from .panel_presentation import format_ui_datetime
@@ -296,7 +296,7 @@ def router(session_factory):
if installation.processing_enabled and last_sync is not None:
next_sync = last_sync + timedelta(seconds=installation.sync_interval_seconds)
last_summary = session.scalar(select(Job.result_summary).where(Job.agent_id == agent.id, Job.status == "COMPLETED", Job.kind.in_(("INITIAL_SYNC", "SYNC_INCREMENTAL", "REPROCESS_ALL"))).order_by(Job.id.desc()).limit(1))
return {"expanded": expanded_scope_view(last_summary), "enabled": bool(installation.processing_enabled), "interval_minutes": installation.sync_interval_seconds // 60,
return {"rtc": rtc_status_view(last_summary), "enabled": bool(installation.processing_enabled), "interval_minutes": installation.sync_interval_seconds // 60,
"last_sync": format_ui_datetime(last_sync) if last_sync else None, "next_sync": format_ui_datetime(next_sync) if next_sync else None,
"last_changed": installation.last_sync_changed, "last_job": {"kind": last_job.kind, "status": last_job.status, "id": last_job.id, "kind_label": job_kind_label(last_job.kind), "status_label": job_status_label(last_job.status)} if last_job else None}
+1 -5
View File
@@ -8,11 +8,7 @@
<form method="post" action="/agents/{{ detail.agent_id }}/sync-now" style="display:inline"><input type="hidden" name="csrf_token" value="{{ csrf_token }}"><button type="submit" class="primary-action" {% if not ops.enabled %}disabled title="Processamento pausado: retome para sincronizar"{% else %}title="Verifica apenas as alterações desde a última sincronização"{% endif %}>Sincronizar agora</button></form>
<a class="botao secondary" href="/agents/{{ detail.agent_id }}/reprocess-confirmation" title="Recalcula todos os produtos (pede confirmação)">Reprocessar tudo</a>
{% if ops.enabled %}<form method="post" action="/agents/{{ detail.agent_id }}/pause" style="display:inline"><input type="hidden" name="csrf_token" value="{{ csrf_token }}"><button type="submit" class="caution" title="Mantém o heartbeat, mas não executa jobs">Pausar processamento</button></form>{% else %}<form method="post" action="/agents/{{ detail.agent_id }}/resume" style="display:inline"><input type="hidden" name="csrf_token" value="{{ csrf_token }}"><button type="submit" class="primary-action" title="Volta a executar jobs">Retomar processamento</button></form>{% endif %}
</div></div><dl class="facts"><dt>Processamento</dt><dd><span class="badge {{ '' if ops.enabled else 'critical' }}">{{ "Ativo" if ops.enabled else "Pausado" }}</span> · sincronização automática a cada {{ ops.interval_minutes }} min</dd><dt>Última sincronização</dt><dd>{{ ops.last_sync or "ainda não sincronizado" }}{% if ops.last_changed is not none %} · {{ ops.last_changed }} produto(s) alterado(s){% endif %}</dd><dt>Sincronização agendada</dt><dd>{{ ops.next_sync or ("aguardando o primeiro sync" if ops.enabled else "pausado") }}</dd><dt>Último job</dt><dd>{% if ops.last_job %}<a href="/jobs?scope=all&amp;job_id={{ ops.last_job.id }}">{{ ops.last_job.kind_label }}</a> · {{ ops.last_job.status_label }}{% else %}nenhum{% endif %}</dd></dl></section>
{% if ops.expanded %}<section class="panel"><div class="panel-title"><div><h2>Cobertura fiscal</h2><p class="muted">Escopo anterior e escopo ampliado (cClassTrib e PIS/COFINS quando aplicáveis). Catálogo RTC local do ERP.</p></div></div>
<dl class="facts"><dt>Escopo anterior</dt><dd>{{ ops.expanded.legacy.complete }} completos · {{ ops.expanded.legacy.safe_blocked }} bloqueados · {{ ops.expanded.legacy.failed }} falhas{% if ops.expanded.legacy_percent is not none %} · {{ ops.expanded.legacy_percent|string|replace(".", ",") }}%{% endif %}</dd>
<dt>Escopo ampliado</dt><dd>{{ ops.expanded.expanded.complete }} completos · {{ ops.expanded.expanded.safe_blocked }} bloqueados · {{ ops.expanded.expanded.failed }} falhas · {{ ops.expanded.expanded.unknown_applicability }} com aplicabilidade desconhecida</dd>
<dt>Novos bloqueios</dt><dd>{% for b in ops.expanded.blockers %}<span class="badge blocked">{{ b.label }} · {{ b.count }}</span> {% else %}nenhum{% endfor %}</dd></dl></section>{% endif %}
</div></div><dl class="facts"><dt>Processamento</dt><dd><span class="badge {{ '' if ops.enabled else 'critical' }}">{{ "Ativo" if ops.enabled else "Pausado" }}</span> · sincronização automática a cada {{ ops.interval_minutes }} min</dd><dt>Última sincronização</dt><dd>{{ ops.last_sync or "ainda não sincronizado" }}{% if ops.last_changed is not none %} · {{ ops.last_changed }} produto(s) alterado(s){% endif %}</dd><dt>Sincronização agendada</dt><dd>{{ ops.next_sync or ("aguardando o primeiro sync" if ops.enabled else "pausado") }}</dd>{% if ops.rtc %}<dt>Reforma Tributária</dt><dd><span class="badge {{ "" if ops.rtc.enabled else "muted" }}">{{ ops.rtc.label }}</span></dd>{% endif %}<dt>Último job</dt><dd>{% if ops.last_job %}<a href="/jobs?scope=all&amp;job_id={{ ops.last_job.id }}">{{ ops.last_job.kind_label }}</a> · {{ ops.last_job.status_label }}{% else %}nenhum{% endif %}</dd></dl></section>
<nav class="tabs" aria-label="Seções da instalação">{% for name,label in [("overview","Resumo"),("fiscal","Fiscal"),("blockers","Blockers"),("jobs","Jobs"),("events","Eventos")] %}<a class="{% if tab==name %}active{% endif %}" href="?tab={{ name }}">{{ label }}</a>{% endfor %}</nav>
<section class="split"><article class="panel"><div class="panel-title"><h2>Contexto da empresa</h2></div><dl class="facts"><dt>Cliente</dt><dd>{{ detail.client or "Não informado" }}</dd><dt>Nome de exibição</dt><dd>{{ detail.display_name or "Não informado" }}</dd><dt>Hostname</dt><dd>{{ detail.hostname or "Não informado" }}</dd><dt>Empresa</dt><dd>{{ detail.company_name or detail.company_code or "Não informado" }}</dd><dt>UF</dt><dd>{{ detail.company_uf or "Não informado" }}</dd><dt>CRT</dt><dd>{{ detail.company_crt or "Não informado" }}</dd><dt>Versão do agent</dt><dd>{{ detail.agent_version_display }}</dd><dt>Último heartbeat</dt><dd title="{{ detail.last_seen_at }}">{{ detail.last_seen_display }} · {{ detail.last_seen_age }}</dd><dt>Firebird</dt><dd>{{ detail.firebird_version or "Não informado" }} · ODS {{ detail.firebird_ods or "Não informado" }}</dd><dt>IDs técnicos</dt><dd>Agent {{ detail.technical_ids.agent_id }} · instalação {{ detail.technical_ids.installation_db_id }}</dd></dl></article>
<article class="panel"><div class="panel-title"><h2>Eventos recentes</h2><a href="#eventos">Ver eventos</a></div>{% for event in events[:8] %}<div class="event-row"><span class="event-time">{{ event.occurred_display }}</span><span><b>{{ event.event_type }}</b><small>{{ event.message }}{% if event.job_id %} · <a href="/jobs?job_id={{ event.job_id }}">Job {{ event.job_id }}</a>{% endif %}{% if event.product_code %} · <a href="/blockers?agent_id={{ detail.agent_id }}&amp;product_code={{ event.product_code|urlencode }}">Produto {{ event.product_code }}</a>{% endif %}</small></span><em class="severity {{ event.severity|lower }}">Severidade: {{ event.severity }}</em></div>{% else %}<p class="empty-state">Nenhum evento.</p>{% endfor %}</article></section>
+2 -2
View File
@@ -15,7 +15,7 @@
{% if reason_code_filter %}<input type="hidden" name="reason_code" value="{{ reason_code_filter }}">{% endif %}
<label>Cliente<input name="client" value="{{ client_filter }}"></label>
<label>Instalação / servidor<input name="installation" value="{{ installation_filter }}"></label>
<label>Campo<select name="field"><option value="">Todos</option>{% for value in ["CFOP","ST","ICMS","CSOSN","NCM","OTHER"] %}<option value="{{ value }}" {% if field_filter==value %}selected{% endif %}>{{ value if value!="OTHER" else "Outros" }}</option>{% endfor %}</select></label>
<label>Campo<select name="field"><option value="">Todos</option>{% for value in ["NCM","CEST","CFOP","ST","ICMS","CSOSN","PIS","COFINS","RTC","OTHER"] %}<option value="{{ value }}" {% if field_filter==value %}selected{% endif %}>{{ value if value!="OTHER" else "Outros" }}</option>{% endfor %}</select></label>
<label>Tipo de blocker<input name="blocker_type" value="{{ blocker_type_filter }}" placeholder="Tipo técnico ou nome"></label>
<label>Status<select name="status"><option value="OPEN" {% if status_filter=="OPEN" %}selected{% endif %}>Abertos</option><option value="RESOLVED" {% if status_filter=="RESOLVED" %}selected{% endif %}>Resolvidos</option><option value="ALL" {% if status_filter=="ALL" %}selected{% endif %}>Todos</option></select></label>
<label>Visualização<select name="scope"><option value="active" {% if scope_filter=="active" %}selected{% endif %}>Operação ativa</option><option value="all" {% if scope_filter=="all" %}selected{% endif %}>Histórico e inativos</option></select></label>
@@ -26,7 +26,7 @@
</form>
{% if field_filter or reason_code_filter or scope_filter=="all" or status_filter!="OPEN" %}<p class="active-filters" aria-live="polite">Filtros ativos: {% if scope_filter=="all" %}<span class="filter-chip">Histórico e inativos</span>{% endif %}{% if field_filter %}<span class="filter-chip">Campo: {{ field_filter }}</span>{% endif %}{% if reason_code_filter %}<span class="filter-chip">Motivo: {{ reason_code_filter }}</span>{% endif %}{% if status_filter!="OPEN" %}<span class="filter-chip">Status: {{ "Resolvidos" if status_filter=="RESOLVED" else "Todos" }}</span>{% endif %}</p>{% endif %}
</section>
<section class="panel"><div class="panel-title"><h2>Distribuição dos blockers filtrados</h2></div><div class="bar-grid">{% for key in ["CFOP","ST","ICMS","CSOSN","NCM","OTHER"] %}{% if summary.blocker_breakdown[key] > 0 %}<a class="bar-row bar-link" href="/blockers?{{ field_query_base|e }}&amp;field={{ key }}"><span>{{ key }}</span><b>{{ summary.blocker_breakdown[key] }}</b><span aria-hidden="true">→</span></a>{% else %}<div class="bar-row muted"><span>{{ key }}</span><b>0</b></div>{% endif %}{% endfor %}</div></section>
<section class="panel"><div class="panel-title"><h2>Distribuição dos blockers filtrados</h2></div><div class="bar-grid">{% for key in ["NCM","CEST","CFOP","ST","ICMS","CSOSN","PIS","COFINS","RTC","OTHER"] %}{% if summary.blocker_breakdown[key] > 0 %}<a class="bar-row bar-link" href="/blockers?{{ field_query_base|e }}&amp;field={{ key }}"><span>{{ key }}</span><b>{{ summary.blocker_breakdown[key] }}</b><span aria-hidden="true">→</span></a>{% else %}<div class="bar-row muted"><span>{{ key }}</span><b>0</b></div>{% endif %}{% endfor %}</div></section>
<div class="panel table-wrap blockers-table-wrap"><table class="blockers-table"><caption>Fila de blockers · {{ result.total }} resultados</caption><thead><tr><th>Cliente / servidor</th><th>Produto</th><th>Campo / tipo</th><th>Prioridade</th><th>Problema</th><th>Última ocorrência</th><th>Ação</th></tr></thead><tbody>
{% for item in result["items"] %}<tr><td data-label="Cliente / servidor"><b>{{ item.client or "Cliente não identificado" }}</b><small>{{ item.hostname or item.display_name or "Instalação não informada" }}</small></td><td data-label="Produto"><a class="row-primary-link" href="{{ item.link }}"><b>{{ item.product_code }}</b><small>{{ item.description or "Sem descrição" }}</small></a></td><td data-label="Campo / tipo">{{ item.field or "Outros" }}<small>{{ item.blocker_type_label }}</small></td><td data-label="Prioridade"><span class="badge priority-{{ item.priority|lower }}">{{ item.priority_label }}</span></td><td data-label="Problema">{{ item.reason_label }}<small>{{ item.age }}</small></td><td data-label="Última ocorrência" title="{{ item.last_seen_at }}">{{ item.last_seen_display }}<small>{{ item.retry_count }} tentativas</small></td><td data-label="Ação"><a class="secondary" href="{{ item.link }}">Ver detalhes</a></td></tr>{% else %}<tr><td colspan="7" class="empty-state">Nenhum blocker corresponde aos filtros. <a href="/blockers?scope=all&amp;status=ALL">Consultar histórico</a></td></tr>{% endfor %}
</tbody></table></div>
+1 -1
View File
@@ -25,7 +25,7 @@
</article>
<article class="panel">
<div class="panel-title"><h2>Breakdown dos blockers</h2><a href="/blockers?scope=active&amp;status=OPEN">Ver blockers</a></div>
{% for key in ["CFOP","ST","ICMS","CSOSN","NCM","OTHER"] %}
{% for key in ["NCM","CEST","CFOP","ST","ICMS","CSOSN","PIS","COFINS","RTC","OTHER"] %}
{% if summary.open_blocker_breakdown[key] > 0 %}<a class="bar-row bar-link" href="/blockers?scope=active&amp;status=OPEN&amp;field={{ key }}" aria-label="{{ key }}: {{ summary.open_blocker_breakdown[key] }} blockers abertos"><span>{{ key }}</span><b>{{ summary.open_blocker_breakdown[key] }}</b><span aria-hidden="true">→</span></a>
{% else %}<div class="bar-row muted"><span>{{ key }}</span><b>0</b></div>{% endif %}
{% endfor %}
+39
View File
@@ -536,3 +536,42 @@ def test_agent_detail_processing_hierarchy_and_status_badges():
assert "Mantém o heartbeat, mas não executa jobs" in page and "Recalcula todos os produtos" in page
assert "Saúde técnica: " not in page and "Status fiscal: " not in page # context lives in the small label, not in the badge
assert '<small class="status-label">Saúde técnica</small>' in page
def _complete_sync(client, agent_id, fiscal_context):
with client._session_factory() as session:
session.add(Job(agent_id=agent_id, kind="SYNC_INCREMENTAL", status="COMPLETED", payload={}, result_summary={"fiscal_context": fiscal_context}, created_at=datetime(2026, 9, 22, 19, 0, tzinfo=timezone.utc),
finished_at=datetime(2026, 9, 22, 19, 5, tzinfo=timezone.utc)))
session.commit()
def test_agent_detail_has_one_coverage_and_shows_rtc_status_only_when_reported():
client, agent_id, _ = _panel_client()
plain = client.get(f"/agents/{agent_id}").text
assert "Reforma Tributária" not in plain # older Agents do not report it
for text in (plain,):
assert "Escopo anterior" not in text and "Escopo ampliado" not in text and "aplicabilidade desconhecida" not in text
_complete_sync(client, agent_id, {"rtc_enabled": False, "rtc_supported": True, "rtc_catalog_valid": True})
off = client.get(f"/agents/{agent_id}").text
assert re.search(r"Reforma Tributária</dt><dd><span[^>]*>Desativada</span>", off) and "Escopo anterior" not in off and "Escopo ampliado" not in off
assert "cClassTrib" not in off # RTC off never surfaces RTC as a problem
_complete_sync(client, agent_id, {"rtc_enabled": True, "rtc_supported": True, "rtc_catalog_valid": True})
on = client.get(f"/agents/{agent_id}").text
assert re.search(r"Reforma Tributária</dt><dd><span[^>]*>Ativa</span>", on)
def test_pis_cofins_and_rtc_blockers_appear_in_the_same_queue_and_dashboard_breakdown():
client, agent_id, _ = _panel_client()
with client._session_factory() as session:
installation = session.scalar(select(Installation).where(Installation.agent_id == agent_id))
for code, field, reason in (("P-010", "PIS", "PIS_CST_INVALID"), ("P-011", "COFINS", "COFINS_CST_MISSING"), ("P-012", "RTC", "CCLASSTRIB_MISSING")):
upsert_blocker(session, installation.tenant_id, {"company": "Acme", "product_code": code, "description": "x", "blocker_type": "BLOCKED_FISCAL_DATA", "field": field, "reason_code": reason, "input_fingerprint": code},
installation_id=installation.id)
dash = client.get("/").text
for key in ("PIS", "COFINS", "RTC", "CEST"):
assert f"<span>{key}</span>" in dash
queue = client.get("/blockers?scope=active&status=OPEN").text
assert "CST PIS inválido" in queue and "CST COFINS ausente" in queue and "cClassTrib ausente" in queue
assert "fila RTC" not in queue.lower()
filtered = client.get("/blockers?scope=active&status=OPEN&field=RTC").text
assert "cClassTrib ausente" in filtered and "CST PIS inválido" not in filtered
+9 -10
View File
@@ -108,7 +108,7 @@ def test_dashboard_reconciles_five_health_states_and_counts_distinct_safe_produc
assert summary["agents_total"] == sum(summary["health_counts"].values())
assert summary["safe_blocked_products"] == 2
assert summary["open_blockers"] == 3
assert summary["open_blocker_breakdown"] == {"CFOP": 1, "ST": 0, "ICMS": 1, "CSOSN": 0, "NCM": 1, "OTHER": 0}
assert summary["open_blocker_breakdown"] == {"NCM": 1, "CEST": 0, "CFOP": 1, "ST": 0, "ICMS": 1, "CSOSN": 0, "PIS": 0, "COFINS": 0, "RTC": 0, "OTHER": 0}
assert summary["online"] == 1 and summary["safe_blocked"] == 2
@@ -576,12 +576,11 @@ def test_remaining_technical_enums_have_human_labels():
assert blocker_reason_label("SOMETHING_NEW") == "Something new" # unknown codes degrade readably, never crash
def test_expanded_scope_view_labels_and_absence():
from central.app.panel_presentation import expanded_scope_view
assert expanded_scope_view(None) is None and expanded_scope_view({"coverage": {}}) is None
view = expanded_scope_view({"total_products": 10, "legacy_scope": {"complete": 8, "safe_blocked": 2, "failed": 0},
"expanded_scope": {"complete": 1, "safe_blocked": 3, "failed": 0, "unknown_applicability": 6},
"blockers_by_type": {"PIS_CST_INVALID": 1, "CCLASSTRIB_MISSING": 2}, "local_catalog_only": True})
assert view["legacy_percent"] == 80.0 and view["expanded_percent"] == 10.0 and view["local_catalog_only"]
assert {b["label"] for b in view["blockers"]} == {"CST PIS inválido", "cClassTrib ausente"}
assert blocker_type_label("RTC_CST_INVALID") == "CST IBS/CBS inválido" and blocker_type_label("COFINS_RATE_INVALID") == "Alíquota COFINS inválida"
def test_rtc_status_view_is_discreet_and_optional():
from central.app.panel_presentation import rtc_status_view
assert rtc_status_view(None) is None and rtc_status_view({"coverage": {}}) is None
assert rtc_status_view({"fiscal_context": {"rtc_enabled": False}})["label"] == "Desativada"
on = rtc_status_view({"fiscal_context": {"rtc_enabled": True, "rtc_supported": True, "rtc_catalog_valid": True}})
assert on["label"] == "Ativa" and on["enabled"] and on["catalog_valid"]
assert blocker_type_label("RTC_CST_MISSING") == "CST IBS/CBS ausente no catálogo" and blocker_type_label("COFINS_RATE_INVALID") == "Alíquota COFINS inválida"
assert blocker_type_label("PIS_CST_INCIDENCE_MISMATCH") == "CST PIS incompatível com a incidência"
@@ -0,0 +1,131 @@
from datetime import date
from central.app.operational_state import classify_product_state, summarize_operational_states
from central.app.unified_fiscal_coverage import (blocker_category, evaluate_product, incidence_kind, rtc_enabled, validate_cclasstrib)
CATALOG = {"classes": {"000001": "000", "200003": "200", "410004": "410", "999999": None, "555555": "555"}, "cst": {"000", "200", "410"}, "duplicates": 0}
ON = {"enabled": True, "catalog": CATALOG, "catalog_valid": True}
OFF = {"enabled": False, "catalog": CATALOG, "catalog_valid": True}
SIMPLES, NORMAL = {"crt": "1"}, {"crt": "3"}
EMPTY = {"CODIGO": "1", "PISCOFINS": "N", "INCIDENCIA_PISCOFINS": "NENHUMA"}
def kinds(result):
return {b["type"] for b in result["blockers"]}
# ---------------------------------------------------------------- RTC on / off
def test_rtc_off_ignores_cclasstrib_completely():
for value in (None, "", "12x", "999999", "000001"):
r = evaluate_product({**EMPTY, "CCLASSTRIB": value}, SIMPLES, OFF)
assert r["blockers"] == [] and r["applicability"]["RTC"] == "NOT_APPLICABLE" and "CCLASSTRIB" not in r["mandatory_fields"]
assert evaluate_product(EMPTY, SIMPLES, None)["blockers"] == []
def test_rtc_on_requires_and_validates_cclasstrib():
assert kinds(evaluate_product({**EMPTY, "CCLASSTRIB": None}, SIMPLES, ON)) == {"CCLASSTRIB_MISSING"}
assert kinds(evaluate_product({**EMPTY, "CCLASSTRIB": "12x"}, SIMPLES, ON)) == {"CCLASSTRIB_INVALID_FORMAT"}
assert kinds(evaluate_product({**EMPTY, "CCLASSTRIB": "123456"}, SIMPLES, ON)) == {"CCLASSTRIB_NOT_IN_CATALOG"}
assert kinds(evaluate_product({**EMPTY, "CCLASSTRIB": "999999"}, SIMPLES, ON)) == {"RTC_CST_MISSING"} # class without CST
assert kinds(evaluate_product({**EMPTY, "CCLASSTRIB": "555555"}, SIMPLES, ON)) == {"RTC_CST_MISSING"} # class points to a CST that is not in RTC_CST (orphan)
ok = evaluate_product({**EMPTY, "CCLASSTRIB": "200003"}, SIMPLES, ON)
assert ok["blockers"] == [] and ok["applicability"]["RTC"] == "REQUIRED" and "CCLASSTRIB" in ok["mandatory_fields"]
def test_rtc_on_with_invalid_catalog_blocks_with_a_single_clear_reason():
r = evaluate_product({**EMPTY, "CCLASSTRIB": "200003"}, SIMPLES, {"enabled": True, "catalog": CATALOG, "catalog_valid": False})
assert kinds(r) == {"RTC_CATALOG_INVALID"}
def test_rtc_vigencia_is_used_only_when_the_catalog_carries_it():
cat = {**CATALOG, "validity": {"200003": (date(2027, 1, 1), None), "410004": (date(2025, 1, 1), date(2025, 12, 31))}}
assert validate_cclasstrib("200003", cat, date(2026, 9, 24)) == "EXPIRED" and validate_cclasstrib("410004", cat, date(2026, 9, 24)) == "EXPIRED"
assert validate_cclasstrib("000001", cat, date(2026, 9, 24)) == "VALID" and validate_cclasstrib("200003", CATALOG, date(2026, 9, 24)) == "VALID"
def test_rtc_enabled_needs_schema_and_erp_switch():
caps = {"HAS_PRODUCT_CCLASSTRIB": True, "HAS_RTC_RELATIONSHIP": True, "RTC_ENABLED_BY_PARAM": True}
assert rtc_enabled(caps) and not rtc_enabled({**caps, "RTC_ENABLED_BY_PARAM": False}) and not rtc_enabled({**caps, "HAS_RTC_RELATIONSHIP": False}) and not rtc_enabled({})
# ---------------------------------------------------------------- PIS / COFINS applicability
def test_empty_product_is_not_applicable_in_every_regime_and_never_blocks():
for company, basis in ((SIMPLES, "SIMPLES_DAS"), ({"crt": "2"}, "SIMPLES_DAS"), ({"crt": "4"}, "SIMPLES_DAS"), (NORMAL, "COMPANY_DEFAULT"), ({"crt": None}, "COMPANY_DEFAULT"), (None, "COMPANY_DEFAULT")):
r = evaluate_product(EMPTY, company, OFF)
assert r["applicability"]["PIS_COFINS"] == "NOT_APPLICABLE" and r["basis"]["PIS_COFINS"] == basis and r["blockers"] == []
assert evaluate_product({"CODIGO": "1"}, SIMPLES, OFF)["blockers"] == [] # columns absent in an old schema: same answer
def test_declared_monophasic_product_requires_a_valid_exit_cst():
good = {**EMPTY, "PISCOFINS": "S", "INCIDENCIA_PISCOFINS": "MONOFÁSICA", "COD_SAIDA_PIS": "04", "COD_SAIDA_COFINS": "04"}
r = evaluate_product(good, SIMPLES, OFF)
assert r["blockers"] == [] and r["applicability"]["PIS_COFINS"] == "REQUIRED" and set(r["mandatory_fields"]) == {"COD_SAIDA_PIS", "COD_SAIDA_COFINS"}
assert kinds(evaluate_product({**good, "COD_SAIDA_PIS": None}, SIMPLES, OFF)) == {"PIS_CST_MISSING"}
assert kinds(evaluate_product({**good, "COD_SAIDA_COFINS": "01", "ALIQ_COFINS": "7,6"}, SIMPLES, OFF)) == {"COFINS_CST_INCIDENCE_MISMATCH"}
def test_st_incidence_with_taxable_cst_is_a_mismatch_and_pis_cofins_are_independent():
row = {**EMPTY, "PISCOFINS": "S", "INCIDENCIA_PISCOFINS": "SUBST.TRIBUTÁRIA", "COD_ENTRADA_PIS": "73", "COD_SAIDA_PIS": "01", "ALIQ_PIS": "0,65", "COD_ENTRADA_COFINS": "73", "COD_SAIDA_COFINS": "05"}
assert kinds(evaluate_product(row, NORMAL, OFF)) == {"PIS_CST_INCIDENCE_MISMATCH"} # only PIS is wrong; COFINS 05 is right
def test_value_2_is_invalid_format_not_a_test_product_and_not_silently_ignored():
row = {**EMPTY, "COD_ENTRADA_PIS": "2", "COD_SAIDA_PIS": "2", "ALIQ_PIS": "2", "COD_ENTRADA_COFINS": "2", "COD_SAIDA_COFINS": "2", "ALIQ_COFINS": "2"}
r = evaluate_product(row, SIMPLES, OFF)
assert r["applicability"]["PIS_COFINS"] == "OPTIONAL" and kinds(r) == {"PIS_CST_INVALID", "COFINS_CST_INVALID"}
assert "PIS_RATE_INVALID" not in kinds(r) # rate 2 is a valid number; only the CST codes are malformed
def test_rate_rules_follow_the_cst():
base = {**EMPTY, "PISCOFINS": "S", "INCIDENCIA_PISCOFINS": "NENHUMA"}
assert kinds(evaluate_product({**base, "COD_SAIDA_PIS": "01", "COD_SAIDA_COFINS": "01"}, NORMAL, OFF)) == {"PIS_RATE_MISSING", "COFINS_RATE_MISSING"}
assert kinds(evaluate_product({**base, "COD_SAIDA_PIS": "01", "ALIQ_PIS": "1,65", "COD_SAIDA_COFINS": "01", "ALIQ_COFINS": "7,6"}, NORMAL, OFF)) == set()
assert kinds(evaluate_product({**base, "COD_SAIDA_PIS": "49", "COD_SAIDA_COFINS": "49"}, SIMPLES, OFF)) == set()
assert kinds(evaluate_product({**base, "COD_SAIDA_PIS": "49", "ALIQ_PIS": "abc", "COD_SAIDA_COFINS": "49"}, SIMPLES, OFF)) == {"PIS_RATE_INVALID"}
assert kinds(evaluate_product({**base, "COD_SAIDA_PIS": "49", "ALIQ_PIS": "150", "COD_SAIDA_COFINS": "49"}, SIMPLES, OFF)) == {"PIS_RATE_INVALID"}
def test_inconsistent_flags_and_unknown_incidence():
assert "PIS_COFINS_FLAG_INCONSISTENT" in kinds(evaluate_product({**EMPTY, "PISCOFINS": "N", "INCIDENCIA_PISCOFINS": "MONOFÁSICA", "COD_SAIDA_PIS": "04", "COD_SAIDA_COFINS": "04"}, SIMPLES, OFF))
assert "PIS_COFINS_FLAG_INCONSISTENT" in kinds(evaluate_product({**EMPTY, "PISCOFINS": "S"}, SIMPLES, OFF))
unknown = evaluate_product({**EMPTY, "INCIDENCIA_PISCOFINS": "OUTRA COISA"}, SIMPLES, OFF)
assert unknown["needs_review"] and unknown["blockers"] == [] and unknown["applicability"]["PIS_COFINS"] == "NEEDS_REVIEW"
def test_incidence_text_is_encoding_and_accent_tolerant():
for text in ("MONOFÁSICA", "MONOFASICA", "MONOF�SICA", "monofásica"):
assert incidence_kind(text) == "MONOFASICA"
assert incidence_kind("SUBST.TRIBUTÁRIA") == "SUBSTITUICAO" and incidence_kind(None) == "NONE" and incidence_kind(" nenhuma ") == "NONE"
# ---------------------------------------------------------------- ONE definition of complete, ONE queue
def test_blocker_categories_feed_the_single_dashboard_breakdown():
assert {blocker_category(t) for t in ("PIS_CST_INVALID", "PIS_COFINS_FLAG_INCONSISTENT")} == {"PIS"} and blocker_category("COFINS_RATE_MISSING") == "COFINS"
assert {blocker_category(t) for t in ("CCLASSTRIB_MISSING", "RTC_CST_MISSING", "RTC_CATALOG_INVALID")} == {"RTC"} and blocker_category("XYZ") == "OTHER"
def test_unified_coverage_invariant_and_rtc_toggle_changes_only_rtc_products():
products = [{**EMPTY, "CODIGO": "1"}, {**EMPTY, "CODIGO": "2", "COD_SAIDA_PIS": "2"}, {**EMPTY, "CODIGO": "3", "CCLASSTRIB": "200003"}, {**EMPTY, "CODIGO": "4", "INCIDENCIA_PISCOFINS": "???"}]
def run(rtc):
states = [classify_product_state(p, {"status": "EXACT_NCM"}, fiscal=evaluate_product(p, SIMPLES, rtc)) for p in products]
return summarize_operational_states(states)
off, on = run(OFF), run(ON)
for s in (off, on):
assert s["TOTAL_PRODUCTS"] == s["COMPLETE_PRODUCTS"] + s["SAFE_BLOCKED"] + s["FAILED_TECHNICAL"] + s["UNCLASSIFIED"] == 4
assert off["COMPLETE_PRODUCTS"] == 2 and off["BLOCKED_FISCAL_DATA"] == 1 and off["UNCLASSIFIED"] == 1 # RTC off: cClassTrib never counts
assert on["COMPLETE_PRODUCTS"] == 1 and on["BLOCKED_FISCAL_DATA"] == 3 and on["UNCLASSIFIED"] == 0 # RTC on: only product 3 has a valid cClassTrib; a real blocker outranks NEEDS_REVIEW
def test_fiscal_blockers_never_hide_or_override_a_decision_blocker():
fiscal = evaluate_product({**EMPTY, "COD_SAIDA_PIS": "2"}, SIMPLES, OFF)
state = classify_product_state({"CODIGO": "1"}, {"status": "INVALID_NCM"}, fiscal=fiscal)
assert state["state"] == "BLOCKED_MISSING_EVIDENCE" # the NCM blocker stays the primary state
assert classify_product_state({"CODIGO": "1"}, {"status": "EXACT_NCM"}, fiscal=None)["state"] == "COMPLETE"
def test_unpadded_single_digit_cst_is_flagged_with_the_official_code_as_a_hint_only():
row = {**EMPTY, "COD_SAIDA_PIS": "4", "COD_SAIDA_COFINS": "4"}
r = evaluate_product(row, NORMAL, OFF)
assert kinds(r) == {"PIS_CST_INVALID", "COFINS_CST_INVALID"} and any("UNPADDED_USE_04" in b["reason"] for b in r["blockers"])
assert evaluate_product({**EMPTY, "COD_SAIDA_PIS": "049"}, NORMAL, OFF)["blockers"][0]["reason"] == "EXIT" # 3 digits: ambiguous, no hint
+42
View File
@@ -194,3 +194,45 @@ def test_panel_controls_require_login():
anon = TestClient(create_app(database_probe=lambda: True, session_factory=factory, bootstrap_token=None))
for action in ("sync-now", "pause", "resume"):
assert anon.post(f"/agents/{agent_id}/{action}", data={}, follow_redirects=False).status_code == 303 # bounced to login, nothing executed
def _fiscal_blocker(code, field, reason, fp):
return {"company": "", "product_code": code, "gtin": None, "description": "P" + code, "blocker_type": "BLOCKED_FISCAL_DATA", "field": field, "reason_code": reason, "evidence_summary": {}, "input_fingerprint": fp}
def test_fiscal_blockers_share_the_single_queue_and_are_resolved_by_the_next_full_pass():
"""PIS/COFINS/RTC blockers are ordinary blockers; when a later FULL pass no longer reports them (data fixed / RTC switched off) they close automatically."""
from central.app.models import BlockedProduct
client, engine = _client()
hb(client)
operational = {**OPERATIONAL, "COMPLETE": 519, "BLOCKED_MISSING_EVIDENCE": 0, "BLOCKED_CONFLICT": 0, "BLOCKED_FISCAL_DATA": 2, "SAFE_BLOCKED": 2, "TOTAL_PRODUCTS": 521}
first = {"operational": operational, "blocked_products": [_fiscal_blocker("10", "PIS", "PIS_CST_INVALID", "a"), _fiscal_blocker("11", "RTC", "CCLASSTRIB_MISSING", "b")], "resolved_products": [],
"fiscal_context": {"rtc_enabled": True, "evaluated_all": True}}
finish(client, engine, first)
with Session(engine) as s:
rows = s.scalars(select(BlockedProduct).where(BlockedProduct.resolved_at.is_(None))).all()
assert sorted((r.product_code, r.field) for r in rows) == [("10", "PIS"), ("11", "RTC")]
snap = s.scalars(select(AgentSnapshot)).one()
assert (snap.fiscally_complete, snap.safe_blocked, snap.total_products) == (519, 2, 521)
age_last_sync(engine, 4000)
hb(client)
second = {"operational": {**OPERATIONAL, "COMPLETE": 520, "BLOCKED_MISSING_EVIDENCE": 0, "BLOCKED_CONFLICT": 0, "BLOCKED_FISCAL_DATA": 1, "SAFE_BLOCKED": 1},
"blocked_products": [_fiscal_blocker("10", "PIS", "PIS_CST_INVALID", "a")], "resolved_products": [], "fiscal_context": {"rtc_enabled": False, "evaluated_all": True}}
finish(client, engine, second)
with Session(engine) as s:
open_rows = s.scalars(select(BlockedProduct).where(BlockedProduct.resolved_at.is_(None))).all()
assert [(r.product_code, r.field) for r in open_rows] == [("10", "PIS")] # RTC blocker closed because RTC is off now
closed = s.scalars(select(BlockedProduct).where(BlockedProduct.resolved_at.is_not(None))).all()
assert [(r.product_code, r.resolution_source) for r in closed] == [("11", "AUTO_REEVALUATION")]
def test_partial_reprocess_never_closes_fiscal_blockers_of_other_products():
from central.app.models import BlockedProduct
client, engine = _client()
hb(client)
finish(client, engine, {"operational": OPERATIONAL, "blocked_products": [_fiscal_blocker("10", "PIS", "PIS_CST_INVALID", "a")], "resolved_products": [], "fiscal_context": {"evaluated_all": True}})
age_last_sync(engine, 4000)
hb(client)
finish(client, engine, {"operational": OPERATIONAL, "blocked_products": [], "resolved_products": [], "fiscal_context": {"evaluated_all": False}})
with Session(engine) as s:
assert len(s.scalars(select(BlockedProduct).where(BlockedProduct.resolved_at.is_(None))).all()) == 1
+52
View File
@@ -0,0 +1,52 @@
# PIS/COFINS — aplicabilidade no cadastro de produto (SGV)
Implementação: `central/app/unified_fiscal_coverage.py` (`pis_cofins_applicability`, `evaluate_pis_cofins`). Somente leitura; nenhum valor é preenchido.
`ACCESSED_AT` de todas as fontes: **2026-09-24** (hashes em `docs/sources/SOURCES_MANIFEST_UNIFIED_FISCAL_V1.json` e `docs/sources/SOURCES_MANIFEST_V3.json`).
## 1. O que o campo do produto é (e o que não é)
Campos: `COD_ENTRADA_PIS`, `COD_SAIDA_PIS`, `ALIQ_PIS`, `COD_ENTRADA_COFINS`, `COD_SAIDA_COFINS`, `ALIQ_COFINS`, `PISCOFINS` (N/S), `INCIDENCIA_PISCOFINS` (NENHUMA / MONOFÁSICA / SUBST.TRIBUTÁRIA).
- São *defaults de cadastro* (tela Produtos > Config. Impostos > CST PIS/COFINS; binding provado por DFM em `docs/SGV_PRODUCT_FISCAL_UI_MAP_V1.json`). Os campos CST usam um seletor com a lista oficial "NN - descrição" (2 dígitos).
- O SGV copia esses defaults para os itens de documentos de entrada (SQL de inserção do item no executável: `COD_SAIDA_PIS, COD_ENTRADA_PIS, COD_SAIDA_COFINS, COD_ENTRADA_COFINS, ... cst_ipi`).
- Não são a tributação efetiva do documento. **DOCUMENT_PIS_COFINS_SOURCE**: em `NFCE_ITENS.CST_PIS_COFINS` o CST **não vem do cadastro do produto**. Evidência (TESTESOFICIAIS): os 3.336 itens com CST `01` são todos de dezembro/2022 (dia 01 a 31, sequência contínua de notas) e independentes do produto (produtos com `PISCOFINS=N`, campos vazios); antes (jun–nov/2022) e depois (2023–2026) os itens saem com CST em branco; o produto com CST `04`/MONOFÁSICA cadastrado emitiu em branco. Nas 4 bases de clientes (~570 mil itens) 100% saem em branco, mesmo para produtos com CST `04` no cadastro (RINCAO). O código do emissor de NFC-e não está disponível localmente (o executável SGV 5.0.6/5.1.0.4 não contém `CST_PIS_COFINS`), então a regra exata do documento **não é comprovável** aqui; o que é comprovado é que ela não lê o cadastro do produto.
## 2. Modelo de aplicabilidade (por produto)
| Estado | Condição (dados do próprio produto) | Efeito |
|---|---|---|
| `NOT_APPLICABLE` | flag N/vazio, incidência NENHUMA/vazia, os 6 campos vazios | não entra na completude; sem blocker |
| `REQUIRED` | flag `S`, ou incidência MONOFÁSICA / SUBST.TRIBUTÁRIA | CST de **saída** válido obrigatório; alíquota conforme CST |
| `OPTIONAL` | sem alegação especial, mas algum CST/alíquota digitado | nada é obrigatório; o que existir tem de ser válido |
| `NEEDS_REVIEW` | incidência fora do vocabulário do SGV | único caso não decidível → "Não classificado" |
Base de `NOT_APPLICABLE`: `SIMPLES_DAS` para CRT 1/2/4; `COMPANY_DEFAULT` para CRT 3 e demais (o produto sem tratamento próprio herda o padrão da empresa).
### Por que é assim (fontes)
1. **Simples Nacional (CRT 1, 2, 4)** — SOURCE Planalto; DOCUMENT LC 123/2006 compilada; ARTICLES art. 13, IV (Cofins) e V (PIS/Pasep): recolhidos mensalmente no DAS; art. 18, §4º-A, I: o contribuinte deve segregar a receita sujeita à tributação monofásica. Logo o produto sem regime especial não tem PIS/COFINS de documento a calcular; só a monofasia exige segregação — e ela é *declarada* pelo produto (flag S / incidência).
2. **Regime normal (CRT 3)** — Lei 9.718/1998 art. 2º-3º (cumulativo, faturamento), Lei 10.637/2002 art. 1º (PIS não cumulativo), Lei 10.833/2003 art. 1º (Cofins não cumulativa). O regime (cumulativo/não cumulativo) é fato da empresa e alimenta o padrão da empresa, não o produto; por isso o produto vazio é `NOT_APPLICABLE (COMPANY_DEFAULT)` e o perfil da empresa continua sendo pergunta de empresa (`OPERATOR_COMPANY_QUESTIONS_V1.md`).
3. **Regimes especiais por NCM** — SPED EFD-Contribuições, tabelas 4.3.10/4.3.11 (monofásica, CST 02/03/04), 4.3.12 (ST, CST 05), 4.3.13 (alíquota zero, CST 06), 4.3.14 (isenção, CST 07), 4.3.15 (sem incidência, CST 08) — versões 2026 baixadas de `sped.rfb.gov.br/arquivo/download/{1638,5786,1642,1643,1646,1651}` (`SPED_PISCOFINS_TABLES_V3.json`; 4.3.13 v1.34 de 16/04/2026). Lei 10.147/2000 (monofasia de medicamentos/perfumaria). Servem para *validar coerência* (CST × incidência) e para o motor de correção de produto; **não** tornam obrigatório preencher um produto que não declara nada, porque no SGV o campo não alimenta o documento (item 1).
4. **Domínio de CST** — SPED tabelas 4.3.3 (CST-PIS) e 4.3.4 (CST-COFINS): 01–09, 49, 50–56, 60–67, 70–75, 98, 99. O seletor do SGV grava 2 dígitos.
5. **Documento fiscal** — NT 2025.002-RTC v1.51 (Portal NF-e, publicada 04/08/2026), Grupos Q (PIS) e S (COFINS): o grupo é obrigatório no item da NF-e (rejeições 745/748), com exceções por finalidade. Isso vale para o **documento**; não obriga o cadastro do produto a ter o default preenchido.
## 3. Regras de validação (blockers)
Entrada e saída são avaliadas separadamente; PIS e COFINS são independentes.
| Blocker | Quando |
|---|---|
| `{PIS,COFINS}_CST_INVALID` | CST de entrada ou saída digitado que não é um código oficial de 2 dígitos (ex.: `2`, `4`, `049`). Dica no motivo quando um dígito vira código oficial com zero à esquerda (`4` → `04`); nunca aplicada |
| `{PIS,COFINS}_CST_MISSING` | produto `REQUIRED` sem CST de saída |
| `{PIS,COFINS}_RATE_MISSING` | CST de saída 01/02 sem alíquota |
| `{PIS,COFINS}_RATE_INVALID` | alíquota não numérica ou > 100 |
| `{PIS,COFINS}_CST_INCIDENCE_MISMATCH` | incidência MONOFÁSICA com CST fora de {02,03,04}; SUBST.TRIBUTÁRIA com CST ≠ 05 |
| `PIS_COFINS_FLAG_INCONSISTENT` | flag N com incidência especial; flag S sem incidência e sem códigos |
Alíquota: CST 01/02 exigem percentual; 03 (valor por unidade) e 04–09 não têm percentual; demais CSTs, opcional (validada se existir).
## 4. Regimes no corpus SGV
Encontrados: CRT 1 (Simples: CONSTRUCAO, MMMODAS, UNIAO, TESTESOFICIAIS) e CRT 3 (RINCAO, via EMISSOR). O motor não assume CRT 1: a base de `NOT_APPLICABLE` e as regras de validade dependem apenas dos dados do produto; o CRT só escolhe a *base* (DAS × padrão da empresa) e será usado pelos perfis de empresa.
## 5. Campos ignorados quando não aplicáveis
Produto `NOT_APPLICABLE`: os 6 campos, `PISCOFINS` e `INCIDENCIA_PISCOFINS` não contam. Candidatos a *write target* futuro (não usados nesta versão, sem escrita): `COD_SAIDA_PIS`, `COD_SAIDA_COFINS` (a partir das tabelas 4.3.10–4.3.15, já aplicadas na missão Local Completion V2 nas cópias autorizadas).
+30 -48
View File
@@ -1,60 +1,42 @@
# Modelo RTC + PIS/COFINS no SGV (evidência: TESTESOFICIAIS.FDB)
# Reforma Tributária (RTC) no cadastro de produto — modelo condicional
Tudo abaixo é observação read-only. Nenhum valor é preenchido, nenhum write é habilitado (`FIREBIRD_WRITES=0`).
Código: `agent/src/gti_fiscal_agent/rtc_pis_cofins.py` (capabilities, validadores, aplicabilidade, cobertura ampliada),
integrado ao job de sync do Agent (`expanded_scope` no `result_summary`) e exibido no detalhe do Agent.
Substitui o modelo anterior de "escopo ampliado". Não existe segundo escopo, segundo painel ou segunda fila: RTC é **uma condição** da definição única de completude (`docs/unified_fiscal_completeness.md`).
## 1. Modelo real do RTC
## Regra definitiva
`RTC_ENABLED = PARAMETROS.USAR_REFORMA_TRIBUTARIA truthy (1/S/T/TRUE) E o schema comporta RTC`
(`C000025.CCLASSTRIB`, `RTC_CCLASSTRIB(CCLASSTRIB, CSTIBSCBS)`, `RTC_CST(CSTIBSCBS)`).
`C000025.CCLASSTRIB` (VARCHAR(6)) → `RTC_CCLASSTRIB.CCLASSTRIB` (CHAR(6), PK) → `RTC_CCLASSTRIB.CSTIBSCBS` (CHAR(3)) → `RTC_CST.CSTIBSCBS` (CHAR(3), PK).
- `RTC_ENABLED=false` (parâmetro 0, nulo, ausente ou schema antigo): `CCLASSTRIB` e CST IBS/CBS **não entram** na completude; nenhum blocker RTC; o painel só mostra "Reforma Tributária: Desativada".
- `RTC_ENABLED=true`: RTC passa a ser obrigatório para todo produto.
- **Há FK física** nos dois elos: `FK_C000025_CCLASSTRIB` (RESTRICT/RESTRICT) e `FK_RTC_CCLASSTRIB_CST`.
- Catálogo local: 145 cClassTrib (6 dígitos, únicos, 0 duplicados, 0 órfãos) para 18 CST IBS/CBS (3 dígitos); nenhum registro com `FIM_VIGENCIA`; `INICIO_VIGENCIA` de 2025-05-05 a 2025-11-19. Cada CST tem ao menos um cClassTrib.
- Atributos do catálogo (não são valores do produto): `TIPO_ALIQUOTA`, `PRED_IBS`/`PRED_CBS` (redução), `IND_TRIB_REGULAR`, `IND_CREDPRES_OPER`, `IND_ESTORNO_CRED`, monofasia, `IND_NFE/NFCE/...` (documentos aplicáveis), `ANEXO`, `LINK`. `RTC_CST` descreve o CST (`IND_IBSCBS`, `IND_REDBC`, `IND_REDALIQ`, `IND_DIF`, `IND_CREDPRES_IBSZFM`...).
- **Não existem no produto**: CST IBS/CBS, alíquotas IBS/CBS, cCredPres. O CST IBS/CBS só se obtém **do catálogo, a partir do cClassTrib informado**.
- `PARAMETROS.USAR_REFORMA_TRIBUTARIA = 0`: o ERP não usa RTC neste banco. Documentos (`C000062`, `C000032`, `C000088`, `NFCE_ITENS`) não têm campos RTC.
- `LOCAL_CATALOG_ONLY=true`: comparamos apenas com o catálogo do próprio banco. Não há dataset oficial integrado; o ponto de extensão é `catalog` em `expanded_scope_summary`/`validate_cclasstrib`.
Não há estado "desconhecido": um parâmetro ilegível conta como não habilitado pelo ERP (fail-closed para *escopo*, sem inflar cobertura porque o campo não é exigido).
## 2. PIS/COFINS no produto (`C000025`)
## Validação com RTC ativa
`C000025.CCLASSTRIB → RTC_CCLASSTRIB.CSTIBSCBS → RTC_CST` (o CST IBS/CBS **não** é coluna do produto; é derivado pela relação).
| Campo | Tipo | Semântica observada |
|---|---|---|
| `COD_ENTRADA_PIS` / `COD_ENTRADA_COFINS` | VARCHAR(3) | CST de entrada (ex.: `73`); 2/521 preenchidos |
| `COD_SAIDA_PIS` / `COD_SAIDA_COFINS` | VARCHAR(3) | CST de saída (ex.: `01`, `04`); 3/521 |
| `ALIQ_PIS` / `ALIQ_COFINS` | VARCHAR(8) | percentual **textual** com vírgula (`0,65`, `3`); 2 não vazios |
| `PISCOFINS` | VARCHAR(1) | flag S/N (N=519, S=2) |
| `INCIDENCIA_PISCOFINS` | VARCHAR(30) | regime adicional: NENHUMA=519, MONOFÁSICA=1, SUBST.TRIBUTÁRIA=1 (coerente com o flag) |
| Blocker (PT-BR) | Condição |
|---|---|
| `CCLASSTRIB_MISSING` cClassTrib ausente | vazio |
| `CCLASSTRIB_INVALID_FORMAT` cClassTrib com formato inválido | não tem exatamente 6 dígitos |
| `CCLASSTRIB_NOT_IN_CATALOG` cClassTrib fora do catálogo local | inexistente em `RTC_CCLASSTRIB`, ou fora da vigência (`INICIO_VIGENCIA` futura / `FIM_VIGENCIA` passada — colunas do próprio catálogo) |
| `RTC_CST_MISSING` CST IBS/CBS ausente no catálogo | a classe não aponta CST ou o CST não existe em `RTC_CST` |
| `RTC_CATALOG_INVALID` Catálogo RTC local inválido | catálogo com órfãos, duplicatas ou formatos inválidos (todo produto, enquanto RTC ativa) |
- CST oficial PIS/COFINS tem 2 dígitos; um produto traz `2` em todos os campos (formato inválido, aparenta dado de teste) e é o único com flag inconsistente (`N` com códigos).
- Não existe catálogo PIS/COFINS no SGV (nenhuma tabela com PIS/COFINS/CST no nome além de `RTC_CST`). Os códigos são texto livre.
- **Produto × documento.** `C000062` (itens de saída) grava `COD_*_PIS/COFINS` e alíquotas por item (7 de 693 com `20`/`41`/`12`); `NFCE_ITENS.CST_PIS_COFINS` tem `01` em 3.336 itens, inclusive de produtos com `INCIDENCIA=NENHUMA` e sem código no produto. Conclusão: o documento **não** depende do cadastro do produto para o CST. `PRODUCT_DEFAULT_MODEL` = campos esparsos em `C000025` (2–3 produtos); `DOCUMENT_ITEM_MODEL` = campos por item; `CANONICAL_SOURCE_FOR_PRODUCT_DEFAULT` = `C000025` somente onde preenchido, sem fonte canônica para os demais.
Integridade do catálogo: sem órfãos (classe → CST inexistente), sem duplicatas, classes com 6 dígitos, CST com 3 dígitos. TESTESOFICIAIS: 145 classes, 18 CSTs, 0 órfãos, 0 duplicatas.
## 3. Aplicabilidade (conservadora)
## O que NÃO é validado
O schema do produto não modela alíquota IBS/CBS, valor IBS/CBS nem `cCredPres`. Não existem blockers desses campos e nenhum write target foi inventado. A validação do produto reflete só o que o SGV guarda como default: `CCLASSTRIB`.
Quatro coisas separadas: SCHEMA_SUPPORT, DATA_PRESENT, APPLICABILITY, MANDATORYNESS. Só a última bloqueia, e só com aplicabilidade provada por dado local.
## Fontes (ACCESSED_AT 2026-09-24)
- NT 2025.002-RTC v1.51, Portal Nacional da NF-e (publicada 04/08/2026): campos IBS/CBS (grupo UB) obrigatórios em 2026; para emitentes CRT 1/2/4 as orientações "serão publicadas em NT futura" porque a tributação IBS/CBS/IS desses contribuintes ocorre a partir de 2027 (art. 348 da LC 214/2025).
- LC 214/2025 (Planalto, compilada): art. 348 (2026: IBS/CBS compensados com PIS/Cofins; alíquotas de teste).
- Catálogo local do ERP (`RTC_CCLASSTRIB`/`RTC_CST`, `PUBLICACAO`/`INICIO_VIGENCIA`) e cClassTrib oficial no Hub (`cClassTrib 2026-06-22.xlsx`).
- `PARAMETROS.USAR_REFORMA_TRIBUTARIA`: parâmetro do próprio SGV (executável 5.1.0.4 o lê; o 5.0.6.124 não tem RTC).
- **RTC**: `NOT_SUPPORTED` (schema sem o modelo) · `NOT_ENABLED_BY_ERP` (parâmetro do ERP = 0; não bloqueia nem conta como desconhecido) · `UNKNOWN_APPLICABILITY` (parâmetro ilegível) · `APPLICABLE` (parâmetro = 1 e modelo existe → cClassTrib passa a ser exigido/validado).
- **PIS/COFINS**: `APPLICABLE` só quando o próprio produto traz evidência (algum código/alíquota, `PISCOFINS='S'` ou incidência especial). Produto vazio com `N`/`NENHUMA` é `UNKNOWN_APPLICABILITY`: não é completo nem bloqueado.
Nota fiscal vs. regra do produto: para CRT 1 a legislação só exige IBS/CBS no documento a partir de 2027; a regra deste motor segue o **interruptor do ERP** — quando o cliente liga a reforma no SGV, o cadastro passa a ser cobrado.
## 4. Validadores (determinísticos, sem preenchimento)
## Achado de corpus (somente leitura)
A cópia local `TESTESOFICIAIS.FDB` tem `USAR_REFORMA_TRIBUTARIA=1` e 0 produtos com `CCLASSTRIB`, enquanto o piloto confirmado pelo operador tem `0`. Nada foi alterado no arquivo; o resultado do piloto é reproduzido por simulação pura (`RTC_ENABLED=false`, `scripts/unified_coverage_e2e.py`).
- cClassTrib: `EMPTY`, `INVALID_FORMAT` (≠ 6 dígitos), `NOT_IN_LOCAL_CATALOG`, `VALID_LOCAL`, `CST_MISSING`, `VALID_WITH_CST`.
- CST PIS/COFINS: `EMPTY`, `VALID`, `INVALID_FORMAT` (fora do conjunto oficial 01–09, 49, 50–56, 60–67, 70–75, 98, 99). Alíquota: `EMPTY`, `VALID` (0–100, vírgula ou ponto), `INVALID_RATE`. Flag: `CONSISTENT`/`INCONSISTENT`/`EMPTY`.
- Blockers específicos (rótulos PT-BR no painel): `CCLASSTRIB_MISSING/INVALID/NOT_IN_CATALOG`, `RTC_CST_INVALID`, `PIS|COFINS_CST_MISSING/INVALID`, `PIS|COFINS_RATE_INVALID`, `PIS_COFINS_FLAG_INCONSISTENT`. Eles ficam em `expanded_scope.blockers_by_type`, **separados** dos blockers legados (não alteram o invariante 495+26+0=521 do escopo anterior).
## 5. Cobertura: legado × ampliada
`legacy_scope` (LEGACY_SCOPE_COVERAGE) é preservado sem alteração. `expanded_scope` (EXPANDED_FISCAL_SCOPE_COVERAGE) só **acrescenta** condições: bloqueado/falha legado continua assim; completo legado vira completo ampliado, bloqueado (blocker específico) ou `unknown_applicability`. Invariante: soma = total.
Resultado no TESTESOFICIAIS (521 produtos): legado 495 completos / 26 bloqueados / 0 falhas; ampliado 2 completos / 27 bloqueados / 0 falhas / 492 com aplicabilidade desconhecida; blockers novos: `PIS_CST_INVALID` 2, `COFINS_CST_INVALID` 2, `PIS_COFINS_FLAG_INCONSISTENT` 1 (mesmo produto). Os 492 desconhecidos **não** são defeitos: faltam evidências para dizer se o cadastro do produto deve trazer CST de PIS/COFINS.
## 6. Limitações
- Sem catálogo oficial RTC nem regra tributária: nenhuma correspondência é inventada.
- A aplicabilidade de PIS/COFINS por regime (CRT 1 aqui) não está determinada por dado local confiável.
- Vigência do cClassTrib não é usada como bloqueio (o catálogo local não tem `FIM_VIGENCIA`).
## 7. Campos candidatos a write futuro (NÃO autorizado)
`CANDIDATE`, sem write habilitado e sem autorização: `C000025.CCLASSTRIB`, `COD_ENTRADA_PIS`, `COD_SAIDA_PIS`, `ALIQ_PIS`, `COD_ENTRADA_COFINS`, `COD_SAIDA_COFINS`, `ALIQ_COFINS`.
Não devem ser escritos: `RTC_CCLASSTRIB`, `RTC_CST` (catálogos), `PARAMETROS.USAR_REFORMA_TRIBUTARIA`, `PISCOFINS`/`INCIDENCIA_PISCOFINS` sem evidência de regime, campos de documentos fiscais, e qualquer CST IBS/CBS "inventado" no produto.
## Testes
RTC off/on, `CCLASSTRIB` vazio/válido/inválido/órfão/expirado, catálogo inválido: `central/tests/test_unified_fiscal_coverage.py`, `agent/tests/test_unified_fiscal.py`.
+630
View File
@@ -0,0 +1,630 @@
{
"database": "TESTESOFICIAIS",
"crt": "1",
"products": 521,
"fiscal_products_read": 521,
"BEFORE": {
"COMPLETE_PRODUCTS": 495,
"SAFE_BLOCKED_PRODUCTS": 26,
"COVERAGE_PERCENT": 95.01,
"UNKNOWN_APPLICABILITY_previous_model": 518
},
"runs": {
"ERP_AS_FOUND": {
"rtc_enabled": true,
"TOTAL_PRODUCTS": 521,
"COMPLETE_PRODUCTS": 0,
"SAFE_BLOCKED_PRODUCTS": 521,
"FAILED_PRODUCTS": 0,
"UNCLASSIFIED_PRODUCTS": 0,
"COVERAGE_PERCENT": 0.0,
"INVARIANT_PASS": true,
"PIS_BLOCKERS": 2,
"COFINS_BLOCKERS": 2,
"RTC_BLOCKERS": 521,
"OTHER_BLOCKERS": 0,
"blockers_by_type": {
"CCLASSTRIB_MISSING": 521,
"COFINS_CST_INCIDENCE_MISMATCH": 1,
"COFINS_CST_INVALID": 1,
"PIS_CST_INCIDENCE_MISMATCH": 1,
"PIS_CST_INVALID": 1
},
"applicability": {
"NOT_APPLICABLE": 518,
"REQUIRED": 2,
"OPTIONAL": 1
},
"rtc_applicability": {
"REQUIRED": 521
},
"changed_products": 495,
"examples": [
{
"product": "000004",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000005",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000006",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000008",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000009",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000010",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000011",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000013",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000015",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000017",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000018",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000019",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000021",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000022",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000023",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000024",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000025",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000026",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000027",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000028",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000029",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000032",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000033",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000034",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000035",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000036",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000038",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000040",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000041",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000042",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
}
],
"context": {
"rtc_enabled": true,
"rtc_supported": true,
"rtc_catalog_valid": true,
"rtc_param": 1,
"catalog_integrity": {
"classes": 145,
"cst": 18,
"orphan_classes": 0,
"bad_class_format": 0,
"bad_cst_format": 0,
"duplicates": 0,
"valid": true
},
"crt": "1",
"products_evaluated": 521
}
},
"RTC_OFF_SIMULATION": {
"rtc_enabled": false,
"TOTAL_PRODUCTS": 521,
"COMPLETE_PRODUCTS": 493,
"SAFE_BLOCKED_PRODUCTS": 28,
"FAILED_PRODUCTS": 0,
"UNCLASSIFIED_PRODUCTS": 0,
"COVERAGE_PERCENT": 94.63,
"INVARIANT_PASS": true,
"PIS_BLOCKERS": 2,
"COFINS_BLOCKERS": 2,
"RTC_BLOCKERS": 0,
"OTHER_BLOCKERS": 0,
"blockers_by_type": {
"COFINS_CST_INCIDENCE_MISMATCH": 1,
"COFINS_CST_INVALID": 1,
"PIS_CST_INCIDENCE_MISMATCH": 1,
"PIS_CST_INVALID": 1
},
"applicability": {
"NOT_APPLICABLE": 518,
"REQUIRED": 2,
"OPTIONAL": 1
},
"rtc_applicability": {
"NOT_APPLICABLE": 521
},
"changed_products": 2,
"examples": [
{
"product": "000700",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INCIDENCE_MISMATCH",
"PIS_CST_INCIDENCE_MISMATCH"
]
},
{
"product": "001434",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
}
]
},
"RTC_ON_SIMULATION": {
"rtc_enabled": true,
"TOTAL_PRODUCTS": 521,
"COMPLETE_PRODUCTS": 0,
"SAFE_BLOCKED_PRODUCTS": 521,
"FAILED_PRODUCTS": 0,
"UNCLASSIFIED_PRODUCTS": 0,
"COVERAGE_PERCENT": 0.0,
"INVARIANT_PASS": true,
"PIS_BLOCKERS": 2,
"COFINS_BLOCKERS": 2,
"RTC_BLOCKERS": 521,
"OTHER_BLOCKERS": 0,
"blockers_by_type": {
"CCLASSTRIB_MISSING": 521,
"COFINS_CST_INCIDENCE_MISMATCH": 1,
"COFINS_CST_INVALID": 1,
"PIS_CST_INCIDENCE_MISMATCH": 1,
"PIS_CST_INVALID": 1
},
"applicability": {
"NOT_APPLICABLE": 518,
"REQUIRED": 2,
"OPTIONAL": 1
},
"rtc_applicability": {
"REQUIRED": 521
},
"changed_products": 495,
"examples": [
{
"product": "000004",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000005",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000006",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000008",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000009",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000010",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000011",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000013",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000015",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000017",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000018",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000019",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000021",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000022",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000023",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000024",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000025",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000026",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000027",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000028",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000029",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000032",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000033",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000034",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000035",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000036",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000038",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000040",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000041",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000042",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
}
]
}
},
"SAFETY": {
"FIREBIRD_WRITES": 0,
"WRITE_ATTEMPTS": 0,
"WRITE_SUCCESSES": 0,
"connection_mode": "READ COMMITTED READ ONLY (Agent reader); no write-capable connection opened"
}
}
@@ -0,0 +1,593 @@
{
"mode": "FISCAL_ONLY (no Hub decision)",
"database": "CONSTRUCAO",
"crt": "1",
"products": 3712,
"fiscal_products_read": 3712,
"BEFORE": {
"COMPLETE_PRODUCTS": 3712,
"SAFE_BLOCKED_PRODUCTS": 0,
"COVERAGE_PERCENT": 100.0,
"UNKNOWN_APPLICABILITY_previous_model": 3712
},
"runs": {
"ERP_AS_FOUND": {
"rtc_enabled": true,
"TOTAL_PRODUCTS": 3712,
"COMPLETE_PRODUCTS": 0,
"SAFE_BLOCKED_PRODUCTS": 3712,
"FAILED_PRODUCTS": 0,
"UNCLASSIFIED_PRODUCTS": 0,
"COVERAGE_PERCENT": 0.0,
"INVARIANT_PASS": true,
"PIS_BLOCKERS": 0,
"COFINS_BLOCKERS": 0,
"RTC_BLOCKERS": 3712,
"OTHER_BLOCKERS": 0,
"blockers_by_type": {
"CCLASSTRIB_MISSING": 3712
},
"applicability": {
"NOT_APPLICABLE": 3712
},
"rtc_applicability": {
"REQUIRED": 3712
},
"changed_products": 3712,
"examples": [
{
"product": "000001",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000002",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000003",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000004",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000005",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000006",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000007",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000008",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000009",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000010",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000011",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000012",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000013",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000014",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000015",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000016",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000017",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000018",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000019",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000020",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000021",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000022",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000023",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000024",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000025",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000026",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000027",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000028",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000029",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000030",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
}
],
"context": {
"rtc_enabled": true,
"rtc_supported": true,
"rtc_catalog_valid": true,
"rtc_param": 1,
"catalog_integrity": {
"classes": 145,
"cst": 18,
"orphan_classes": 0,
"bad_class_format": 0,
"bad_cst_format": 0,
"duplicates": 0,
"valid": true
},
"crt": "1",
"products_evaluated": 3712
}
},
"RTC_OFF_SIMULATION": {
"rtc_enabled": false,
"TOTAL_PRODUCTS": 3712,
"COMPLETE_PRODUCTS": 3712,
"SAFE_BLOCKED_PRODUCTS": 0,
"FAILED_PRODUCTS": 0,
"UNCLASSIFIED_PRODUCTS": 0,
"COVERAGE_PERCENT": 100.0,
"INVARIANT_PASS": true,
"PIS_BLOCKERS": 0,
"COFINS_BLOCKERS": 0,
"RTC_BLOCKERS": 0,
"OTHER_BLOCKERS": 0,
"blockers_by_type": {},
"applicability": {
"NOT_APPLICABLE": 3712
},
"rtc_applicability": {
"NOT_APPLICABLE": 3712
},
"changed_products": 0,
"examples": []
},
"RTC_ON_SIMULATION": {
"rtc_enabled": true,
"TOTAL_PRODUCTS": 3712,
"COMPLETE_PRODUCTS": 0,
"SAFE_BLOCKED_PRODUCTS": 3712,
"FAILED_PRODUCTS": 0,
"UNCLASSIFIED_PRODUCTS": 0,
"COVERAGE_PERCENT": 0.0,
"INVARIANT_PASS": true,
"PIS_BLOCKERS": 0,
"COFINS_BLOCKERS": 0,
"RTC_BLOCKERS": 3712,
"OTHER_BLOCKERS": 0,
"blockers_by_type": {
"CCLASSTRIB_MISSING": 3712
},
"applicability": {
"NOT_APPLICABLE": 3712
},
"rtc_applicability": {
"REQUIRED": 3712
},
"changed_products": 3712,
"examples": [
{
"product": "000001",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000002",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000003",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000004",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000005",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000006",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000007",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000008",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000009",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000010",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000011",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000012",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000013",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000014",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000015",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000016",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000017",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000018",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000019",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000020",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000021",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000022",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000023",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000024",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000025",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000026",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000027",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000028",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000029",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000030",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
}
]
}
},
"SAFETY": {
"FIREBIRD_WRITES": 0,
"WRITE_ATTEMPTS": 0,
"WRITE_SUCCESSES": 0,
"connection_mode": "READ COMMITTED READ ONLY (Agent reader); no write-capable connection opened"
}
}
+353
View File
@@ -0,0 +1,353 @@
{
"mode": "FISCAL_ONLY (no Hub decision)",
"database": "MMMODAS",
"crt": "2",
"products": 20997,
"fiscal_products_read": 20997,
"BEFORE": {
"COMPLETE_PRODUCTS": 20997,
"SAFE_BLOCKED_PRODUCTS": 0,
"COVERAGE_PERCENT": 100.0,
"UNKNOWN_APPLICABILITY_previous_model": 20996
},
"runs": {
"ERP_AS_FOUND": {
"rtc_enabled": false,
"TOTAL_PRODUCTS": 20997,
"COMPLETE_PRODUCTS": 20997,
"SAFE_BLOCKED_PRODUCTS": 0,
"FAILED_PRODUCTS": 0,
"UNCLASSIFIED_PRODUCTS": 0,
"COVERAGE_PERCENT": 100.0,
"INVARIANT_PASS": true,
"PIS_BLOCKERS": 0,
"COFINS_BLOCKERS": 0,
"RTC_BLOCKERS": 0,
"OTHER_BLOCKERS": 0,
"blockers_by_type": {},
"applicability": {
"OPTIONAL": 1,
"NOT_APPLICABLE": 20995
},
"rtc_applicability": {
"NOT_APPLICABLE": 20996
},
"changed_products": 0,
"examples": [],
"context": {
"rtc_enabled": false,
"rtc_supported": true,
"rtc_catalog_valid": true,
"rtc_param": 0,
"catalog_integrity": {
"classes": 145,
"cst": 18,
"orphan_classes": 0,
"bad_class_format": 0,
"bad_cst_format": 0,
"duplicates": 0,
"valid": true
},
"crt": "2",
"products_evaluated": 20997
}
},
"RTC_OFF_SIMULATION": {
"rtc_enabled": false,
"TOTAL_PRODUCTS": 20997,
"COMPLETE_PRODUCTS": 20997,
"SAFE_BLOCKED_PRODUCTS": 0,
"FAILED_PRODUCTS": 0,
"UNCLASSIFIED_PRODUCTS": 0,
"COVERAGE_PERCENT": 100.0,
"INVARIANT_PASS": true,
"PIS_BLOCKERS": 0,
"COFINS_BLOCKERS": 0,
"RTC_BLOCKERS": 0,
"OTHER_BLOCKERS": 0,
"blockers_by_type": {},
"applicability": {
"OPTIONAL": 1,
"NOT_APPLICABLE": 20995
},
"rtc_applicability": {
"NOT_APPLICABLE": 20996
},
"changed_products": 0,
"examples": []
},
"RTC_ON_SIMULATION": {
"rtc_enabled": true,
"TOTAL_PRODUCTS": 20997,
"COMPLETE_PRODUCTS": 0,
"SAFE_BLOCKED_PRODUCTS": 20997,
"FAILED_PRODUCTS": 0,
"UNCLASSIFIED_PRODUCTS": 0,
"COVERAGE_PERCENT": 0.0,
"INVARIANT_PASS": true,
"PIS_BLOCKERS": 0,
"COFINS_BLOCKERS": 0,
"RTC_BLOCKERS": 20996,
"OTHER_BLOCKERS": 0,
"blockers_by_type": {
"CCLASSTRIB_MISSING": 20996
},
"applicability": {
"OPTIONAL": 1,
"NOT_APPLICABLE": 20995
},
"rtc_applicability": {
"REQUIRED": 20996
},
"changed_products": 20997,
"examples": [
{
"product": "000002",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000003",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000006",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000008",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000011",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000013",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000014",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000016",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000017",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000018",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000019",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000020",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000022",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000023",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000024",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000025",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000026",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000027",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000028",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000029",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000030",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000031",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000032",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000033",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000034",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000038",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000039",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000040",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000041",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000042",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
}
]
}
},
"SAFETY": {
"FIREBIRD_WRITES": 0,
"WRITE_ATTEMPTS": 0,
"WRITE_SUCCESSES": 0,
"connection_mode": "READ COMMITTED READ ONLY (Agent reader); no write-capable connection opened"
}
}
+902
View File
@@ -0,0 +1,902 @@
{
"database": "RINCAO",
"crt": null,
"products": 894,
"fiscal_products_read": 894,
"BEFORE": {
"COMPLETE_PRODUCTS": 894,
"SAFE_BLOCKED_PRODUCTS": 0,
"COVERAGE_PERCENT": 100.0,
"UNKNOWN_APPLICABILITY_previous_model": 815
},
"runs": {
"ERP_AS_FOUND": {
"rtc_enabled": false,
"TOTAL_PRODUCTS": 894,
"COMPLETE_PRODUCTS": 815,
"SAFE_BLOCKED_PRODUCTS": 79,
"FAILED_PRODUCTS": 0,
"UNCLASSIFIED_PRODUCTS": 0,
"COVERAGE_PERCENT": 91.16,
"INVARIANT_PASS": true,
"PIS_BLOCKERS": 79,
"COFINS_BLOCKERS": 79,
"RTC_BLOCKERS": 0,
"OTHER_BLOCKERS": 0,
"blockers_by_type": {
"COFINS_CST_INVALID": 79,
"PIS_CST_INVALID": 79
},
"applicability": {
"NOT_APPLICABLE": 815,
"OPTIONAL": 79
},
"rtc_applicability": {
"NOT_APPLICABLE": 894
},
"changed_products": 79,
"examples": [
{
"product": "000139",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000140",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000141",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000142",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000143",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000144",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000145",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000146",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000147",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000148",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000149",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000150",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000156",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000157",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000158",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000159",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000160",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000161",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000165",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000166",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000167",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000168",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000169",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000170",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000172",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000173",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000174",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000175",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000176",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000177",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
}
],
"context": {
"rtc_enabled": false,
"rtc_supported": true,
"rtc_catalog_valid": true,
"rtc_param": 0,
"catalog_integrity": {
"classes": 145,
"cst": 18,
"orphan_classes": 0,
"bad_class_format": 0,
"bad_cst_format": 0,
"duplicates": 0,
"valid": true
},
"crt": null,
"products_evaluated": 894
}
},
"RTC_OFF_SIMULATION": {
"rtc_enabled": false,
"TOTAL_PRODUCTS": 894,
"COMPLETE_PRODUCTS": 815,
"SAFE_BLOCKED_PRODUCTS": 79,
"FAILED_PRODUCTS": 0,
"UNCLASSIFIED_PRODUCTS": 0,
"COVERAGE_PERCENT": 91.16,
"INVARIANT_PASS": true,
"PIS_BLOCKERS": 79,
"COFINS_BLOCKERS": 79,
"RTC_BLOCKERS": 0,
"OTHER_BLOCKERS": 0,
"blockers_by_type": {
"COFINS_CST_INVALID": 79,
"PIS_CST_INVALID": 79
},
"applicability": {
"NOT_APPLICABLE": 815,
"OPTIONAL": 79
},
"rtc_applicability": {
"NOT_APPLICABLE": 894
},
"changed_products": 79,
"examples": [
{
"product": "000139",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000140",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000141",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000142",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000143",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000144",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000145",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000146",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000147",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000148",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000149",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000150",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000156",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000157",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000158",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000159",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000160",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000161",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000165",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000166",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000167",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000168",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000169",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000170",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000172",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000173",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000174",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000175",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000176",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
},
{
"product": "000177",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID",
"PIS_CST_INVALID"
]
}
]
},
"RTC_ON_SIMULATION": {
"rtc_enabled": true,
"TOTAL_PRODUCTS": 894,
"COMPLETE_PRODUCTS": 0,
"SAFE_BLOCKED_PRODUCTS": 894,
"FAILED_PRODUCTS": 0,
"UNCLASSIFIED_PRODUCTS": 0,
"COVERAGE_PERCENT": 0.0,
"INVARIANT_PASS": true,
"PIS_BLOCKERS": 79,
"COFINS_BLOCKERS": 79,
"RTC_BLOCKERS": 894,
"OTHER_BLOCKERS": 0,
"blockers_by_type": {
"CCLASSTRIB_MISSING": 894,
"COFINS_CST_INVALID": 79,
"PIS_CST_INVALID": 79
},
"applicability": {
"NOT_APPLICABLE": 815,
"OPTIONAL": 79
},
"rtc_applicability": {
"REQUIRED": 894
},
"changed_products": 894,
"examples": [
{
"product": "000001",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000004",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000005",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000006",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000007",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000008",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000009",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000010",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000011",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000012",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000013",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000014",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000015",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000016",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000017",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000018",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000019",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000020",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000021",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000022",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000023",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000024",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000025",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000026",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000027",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000028",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000029",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000030",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000031",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000032",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
}
]
}
},
"SAFETY": {
"FIREBIRD_WRITES": 0,
"WRITE_ATTEMPTS": 0,
"WRITE_SUCCESSES": 0,
"connection_mode": "READ COMMITTED READ ONLY (Agent reader); no write-capable connection opened"
}
}
+392
View File
@@ -0,0 +1,392 @@
{
"mode": "FISCAL_ONLY (no Hub decision)",
"database": "UNIAO",
"crt": "3",
"products": 8942,
"fiscal_products_read": 8942,
"BEFORE": {
"COMPLETE_PRODUCTS": 8942,
"SAFE_BLOCKED_PRODUCTS": 0,
"COVERAGE_PERCENT": 100.0,
"UNKNOWN_APPLICABILITY_previous_model": 8170
},
"runs": {
"ERP_AS_FOUND": {
"rtc_enabled": false,
"TOTAL_PRODUCTS": 8942,
"COMPLETE_PRODUCTS": 8940,
"SAFE_BLOCKED_PRODUCTS": 2,
"FAILED_PRODUCTS": 0,
"UNCLASSIFIED_PRODUCTS": 0,
"COVERAGE_PERCENT": 99.98,
"INVARIANT_PASS": true,
"PIS_BLOCKERS": 0,
"COFINS_BLOCKERS": 2,
"RTC_BLOCKERS": 0,
"OTHER_BLOCKERS": 0,
"blockers_by_type": {
"COFINS_CST_INVALID": 2
},
"applicability": {
"NOT_APPLICABLE": 8170,
"OPTIONAL": 772
},
"rtc_applicability": {
"NOT_APPLICABLE": 8942
},
"changed_products": 2,
"examples": [
{
"product": "000304",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID"
]
},
{
"product": "003090",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID"
]
}
],
"context": {
"rtc_enabled": false,
"rtc_supported": true,
"rtc_catalog_valid": true,
"rtc_param": 0,
"catalog_integrity": {
"classes": 145,
"cst": 18,
"orphan_classes": 0,
"bad_class_format": 0,
"bad_cst_format": 0,
"duplicates": 0,
"valid": true
},
"crt": "3",
"products_evaluated": 8942
}
},
"RTC_OFF_SIMULATION": {
"rtc_enabled": false,
"TOTAL_PRODUCTS": 8942,
"COMPLETE_PRODUCTS": 8940,
"SAFE_BLOCKED_PRODUCTS": 2,
"FAILED_PRODUCTS": 0,
"UNCLASSIFIED_PRODUCTS": 0,
"COVERAGE_PERCENT": 99.98,
"INVARIANT_PASS": true,
"PIS_BLOCKERS": 0,
"COFINS_BLOCKERS": 2,
"RTC_BLOCKERS": 0,
"OTHER_BLOCKERS": 0,
"blockers_by_type": {
"COFINS_CST_INVALID": 2
},
"applicability": {
"NOT_APPLICABLE": 8170,
"OPTIONAL": 772
},
"rtc_applicability": {
"NOT_APPLICABLE": 8942
},
"changed_products": 2,
"examples": [
{
"product": "000304",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID"
]
},
{
"product": "003090",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"COFINS_CST_INVALID"
]
}
]
},
"RTC_ON_SIMULATION": {
"rtc_enabled": true,
"TOTAL_PRODUCTS": 8942,
"COMPLETE_PRODUCTS": 0,
"SAFE_BLOCKED_PRODUCTS": 8942,
"FAILED_PRODUCTS": 0,
"UNCLASSIFIED_PRODUCTS": 0,
"COVERAGE_PERCENT": 0.0,
"INVARIANT_PASS": true,
"PIS_BLOCKERS": 0,
"COFINS_BLOCKERS": 2,
"RTC_BLOCKERS": 8942,
"OTHER_BLOCKERS": 0,
"blockers_by_type": {
"CCLASSTRIB_MISSING": 8942,
"COFINS_CST_INVALID": 2
},
"applicability": {
"NOT_APPLICABLE": 8170,
"OPTIONAL": 772
},
"rtc_applicability": {
"REQUIRED": 8942
},
"changed_products": 8942,
"examples": [
{
"product": "000008",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000009",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000014",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000015",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000016",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000017",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000018",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000019",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000020",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000021",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000022",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000023",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000024",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000025",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000026",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000027",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000028",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000029",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000030",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000031",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000032",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000033",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000034",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000035",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000036",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000037",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000038",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000039",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000040",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
},
{
"product": "000041",
"BEFORE_STATUS": "COMPLETE",
"AFTER_STATUS": "BLOCKED_FISCAL_DATA",
"REASON": [
"CCLASSTRIB_MISSING"
]
}
]
}
},
"SAFETY": {
"FIREBIRD_WRITES": 0,
"WRITE_ATTEMPTS": 0,
"WRITE_SUCCESSES": 0,
"connection_mode": "READ COMMITTED READ ONLY (Agent reader); no write-capable connection opened"
}
}
@@ -0,0 +1,70 @@
{
"generated": "2026-09-24",
"items": [
{
"name": "LC 123/2006 (Simples Nacional), compilada",
"source_url": "https://www.planalto.gov.br/ccivil_03/leis/lcp/lcp123.htm",
"accessed_at": "2026-09-24",
"sha256": "8c446540a798b1fe5cfff380cf1a736394ea5bfe0a8ce7351151dca77d02eee4",
"bytes": 1622252,
"note": "art. 13 IV/V (Cofins e PIS no DAS), art. 18 par. 4-A I (segregacao da receita monofasica). Obtida via Internet Archive (planalto recusa conexao direta)"
},
{
"name": "Lei 9.718/1998, compilada",
"source_url": "https://www.planalto.gov.br/ccivil_03/leis/l9718compilada.htm",
"accessed_at": "2026-09-24",
"sha256": "79b8c471f84ec9d702e6e6ef8cb28198143c0cbc4db59a14714c5c5d1b188906",
"bytes": 75858,
"note": "art. 2-3: PIS/Cofins sobre faturamento (regime cumulativo)"
},
{
"name": "Lei 10.637/2002, compilada",
"source_url": "https://www.planalto.gov.br/ccivil_03/leis/2002/l10637.htm",
"accessed_at": "2026-09-24",
"sha256": "eaa97c5448184815c96fb68ffcd6e136721d66965b609af60b6832fdd741382e",
"bytes": 238160,
"note": "art. 1: PIS/Pasep nao cumulativo"
},
{
"name": "Lei 10.833/2003, compilada",
"source_url": "https://www.planalto.gov.br/ccivil_03/leis/2003/l10.833.htm",
"accessed_at": "2026-09-24",
"sha256": "4d562e534472c620c404e55e56544b1b17ead935fd9fcc767fe5a19eb6062a3a",
"bytes": 533449,
"note": "art. 1: Cofins nao cumulativa"
},
{
"name": "Lei 10.147/2000, compilada",
"source_url": "https://www.planalto.gov.br/ccivil_03/leis/l10147.htm",
"accessed_at": "2026-09-24",
"sha256": "4b5d3c4c62ae967516b42be56c7cc8fb6315590fd98c4202c91fb2877f59c703",
"bytes": 35624,
"note": "incidencia monofasica (medicamentos, perfumaria/higiene)"
},
{
"name": "LC 214/2025, compilada",
"source_url": "https://www.planalto.gov.br/ccivil_03/leis/lcp/lcp214.htm",
"accessed_at": "2026-09-24",
"sha256": "5c7d59b3c49e5b7c066c2ad28bedf32791193a676ec1a163bec14bea5fa2f680",
"bytes": 5400654,
"note": "art. 348: transicao 2026 (IBS/CBS compensados); Simples so a partir de 2027"
},
{
"name": "NT 2025.002-RTC v1.51 (NF-e/NFC-e IBS/CBS/IS)",
"source_url": "https://www.nfe.fazenda.gov.br/portal/exibirArquivo.aspx?conteudo=AKD/muSmiIY=",
"accessed_at": "2026-09-24",
"sha256": "a4aaaa181522b43cd90b502f8ccb9e4bdabea30fed838e2d0790be5ba77254a4",
"bytes": 2182361,
"note": "publicada 04/08/2026; CRT 1/2/4: orientacoes IBS/CBS em NT futura, tributacao so a partir de 2027 (art. 348 LC 214); grupos Q/S (PIS/COFINS) obrigatorios no documento (rejeicoes 745/748)"
},
{
"name": "NT 2026.002 v1.10a",
"source_url": "https://www.nfe.fazenda.gov.br/portal/exibirArquivo.aspx?conteudo=8zOzIahvn8I=",
"accessed_at": "2026-09-24",
"sha256": "705f771830565c209d815659347ac4ea251901c77064cc163ce5c93f311e6e64",
"bytes": 767698,
"note": "publicada 25/08/2026 (consultada; sem regra adicional de PIS/COFINS de produto)"
}
],
"sped_tables": "docs/sources/SOURCES_MANIFEST_V3.json + SPED_PISCOFINS_TABLES_V3.json (4.3.3/4.3.4 CST domain; 4.3.10-4.3.15 special regimes)"
}
+64
View File
@@ -0,0 +1,64 @@
# Completude fiscal unificada (UNIFIED_FISCAL_COVERAGE)
Uma definição de "completo", um painel, uma fila de blockers, uma cobertura.
## Definição
```
campos obrigatórios(produto, empresa) =
campos fiscais base aplicáveis (NCM, CEST, CFOP, ICMS/CST/CSOSN, IPI ... — decididos pelo motor de decisão do Hub)
+ PIS/COFINS aplicáveis (docs/pis_cofins_applicability.md)
+ RTC (cClassTrib → CST IBS/CBS) SOMENTE quando o ERP tem a Reforma Tributária ligada (docs/rtc_pis_cofins_model.md)
```
Campos `NOT_APPLICABLE` não contam contra a completude. Nenhum campo é exigido só porque a coluna existe.
## Onde vive (nenhuma cópia paralela)
| Peça | Arquivo |
|---|---|
| regras puras (PIS/COFINS, RTC, `evaluate_product`) | `central/app/unified_fiscal_coverage.py` |
| estado do produto (`COMPLETE`/`CORRECTED`/`BLOCKED_*`/`BLOCKED_FISCAL_DATA`/`FAILED_TECHNICAL`/`NEEDS_REVIEW`) | `central/app/operational_state.py` (`classify_product_state(..., fiscal=)`) |
| leitura do ERP (somente SELECT) | `agent/src/gti_fiscal_agent/unified_fiscal.py` |
| integração no job do Agent | `agent/scripts/run_agent_service.py` (`_unified_fiscal`, `_fiscal_blocked_products`) |
| snapshot / cobertura no Hub | `central/app/api.py` (`_apply_job_outcome` — inalterado: usa o mesmo `operational`) |
| fila única + fechamento automático | `central/app/blocked_products.py` (`resolve_stale_fiscal_blockers`) |
| painel | `central/app/panel_presentation.py`, `central/templates/{dashboard,blockers,agent_detail}.html` |
## Estados finais do produto
`COMPLETE` (+`CORRECTED`) · `SAFE_BLOCKED` (`BLOCKED_*`, incluindo `BLOCKED_FISCAL_DATA` para PIS/COFINS/RTC) · `FAILED_TECHNICAL` · `NEEDS_REVIEW` (não classificado; só incidência PIS/COFINS fora do vocabulário do SGV).
Invariante: `TOTAL = COMPLETE + SAFE_BLOCKED + FAILED + UNCLASSIFIED` (o Hub calcula "não classificados" como `total − completos − bloqueados − falhas`, então a igualdade fecha por construção e é reverificada em `coverage_invariant_pass`).
Precedência: um blocker de decisão (NCM, conflito, contexto) permanece o estado principal; blockers fiscais só rebaixam produto que seria completo. Um blocker real vale mais que `NEEDS_REVIEW`.
## Fila única e fechamento
Cada blocker fiscal é um blocker comum: `blocker_type=BLOCKED_FISCAL_DATA`, `field ∈ {PIS, COFINS, RTC}`, `reason_code` = tipo (ex.: `PIS_CST_INVALID`). A cada job de varredura completa (`fiscal_context.evaluated_all=true`) o Agent reenvia todos os blockers vigentes e o Hub fecha (`AUTO_REEVALUATION`) os blockers PIS/COFINS/RTC da instalação não reenviados (dado corrigido, ou RTC desligada). Reprocesso parcial não fecha nada de outros produtos. O fingerprint do blocker inclui os valores fiscais lidos.
## Painel
- Dashboard: um único bloco "Cobertura dos produtos" (Produtos, Completos, Bloqueados com segurança, Falhas técnicas, Não classificados, Cobertura fiscal). Breakdown de blockers: `NCM, CEST, CFOP, ST, ICMS, CSOSN, PIS, COFINS, RTC, OTHER` (categoria com 0 fica esmaecida).
- Detalhe do Agent: a mesma cobertura + uma linha discreta "Reforma Tributária: Ativa/Desativada" (do último job). RTC desativada nunca aparece como problema. O bloco "Escopo anterior/ampliado" foi removido.
- Removidos do código: `expanded_scope_summary`, `expanded_scope_view`, `LEGACY_SCOPE_COVERAGE`, `EXPANDED_SCOPE_COVERAGE`, `UNKNOWN_APPLICABILITY` (como estado em massa).
## Segurança
Somente leitura. `FIREBIRD_WRITES=0`: a avaliação abre uma conexão `READ COMMITTED READ ONLY`, executa apenas SELECT e nunca preenche `CCLASSTRIB`, PIS ou COFINS. Testes provam que só SELECT é emitido.
## Validação no banco real (somente leitura)
`scripts/unified_coverage_e2e.py` → `docs/runs/unified_coverage_e2e*.json`. TESTESOFICIAIS (521 produtos, CRT 1):
| | Antes (só decisão) | Depois — RTC desligada (estado do piloto) |
|---|---|---|
| Completos | 495 (95,01%) | 493 (94,63%) |
| Bloqueados | 26 | 28 |
| Falhas / Não classificados | 0 / 0 | 0 / 0 |
| RTC blockers | — | 0 |
| PIS / COFINS blockers | — | 2 / 2 (2 produtos) |
Aplicabilidade PIS/COFINS: 518 `NOT_APPLICABLE`, 2 `REQUIRED`, 1 `OPTIONAL`, 0 `NEEDS_REVIEW` (antes: 492 `UNKNOWN_APPLICABILITY`). Os 2 produtos bloqueados são legítimos: um com CST `2`/`2` (formato inválido) e um com incidência SUBST.TRIBUTÁRIA e CST 01 (incompatível com CST 05).
### Corpus de clientes (RTC desligada, somente PIS/COFINS; `--fiscal-only`, sem decisão do Hub)
| Banco | CRT | Produtos | NOT_APPLICABLE | OPTIONAL | Blockers PIS/COFINS | Cobertura da parte fiscal |
|---|---|---|---|---|---|---|
| CONSTRUCAO | 1 | 3.712 | 3.712 | 0 | 0 | 100% |
| MMMODAS | 1 | 20.997 | 20.995 | 1 | 0 | 100% |
| UNIAO | 1 | 8.942 | 8.170 | 772 | 2 produtos (COFINS `000`/`060`, estilo CST de ICMS) | 99,98% |
| RINCAO | 3 | 894 | 815 | 79 | 79 produtos (78 com CST `4` sem zero à esquerda + 1) | 91,16% |
| TESTESOFICIAIS | 1 | 521 | 518 | 1 | 2 produtos | 99,62% (94,63% com a decisão do Hub) |
`OPTIONAL` = valor digitado sem flag/incidência especial. RINCAO: `4` deve ser `04` (o seletor do SGV grava 2 dígitos); o motivo do blocker traz a dica `UNPADDED_USE_04`, nunca aplicada.
+95
View File
@@ -0,0 +1,95 @@
"""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 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 (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]}
if label == "ERP_AS_FOUND":
runs[label]["context"] = {k: v for k, v in out["context"].items() if k != "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")})
print(" applicability", r["applicability"], "blockers", r["blockers_by_type"])
if __name__ == "__main__":
main()