fix: RTC observed live with provenance, non-circular PIS/COFINS applicability, canonical CST normalization (1.1.8)

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

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
gti
2026-09-24 17:46:45 -03:00
co-authored by Claude Sonnet 5
parent 1430042322
commit 88f4318072
18 changed files with 679 additions and 814 deletions
+11 -2
View File
@@ -162,9 +162,18 @@ def priority_label(value):
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)."""
"""Discreet Reforma Tributaria status from the last completed job's `fiscal_context`, exactly as the Agent OBSERVED it in that installation's own database.
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
observed = None
if context.get("rtc_observed_at"):
try:
observed = format_ui_datetime(datetime.fromisoformat(str(context["rtc_observed_at"]).replace("Z", "+00:00")))
except ValueError:
observed = None
source = context.get("rtc_source") or "PARAMETROS.USAR_REFORMA_TRIBUTARIA"
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"))}
"catalog_valid": bool(context.get("rtc_catalog_valid")), "observed_at": observed, "conflict": bool(context.get("rtc_param_conflict")),
"title": f"Lido no banco do cliente ({source})" + (f" em {observed}" if observed else "") + (" - linhas de PARAMETROS divergentes: RTC tratada como desativada" if context.get("rtc_param_conflict") else "")}
+63 -30
View File
@@ -54,17 +54,25 @@ def incidence_kind(value) -> str:
return "UNKNOWN"
def canonical_cst(value) -> str | None:
"""The official 2-digit CST this text stands for, or None. Exactly two official digits, or ONE digit whose zero-padded form is an official code
("4" -> "04"). Nothing else is ever normalised ("049", "000", "2x" stay invalid)."""
code = _text(value)
if code in CST_DOMAIN:
return code
if len(code) == 1 and code.isdigit() and code.zfill(2) in CST_DOMAIN:
return code.zfill(2)
return None
def validate_cst(value) -> str:
"""EMPTY | VALID | VALID_AFTER_NORMALIZATION | INVALID."""
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
if code in CST_DOMAIN:
return "VALID"
return "VALID_AFTER_NORMALIZATION" if canonical_cst(code) else "INVALID"
def validate_rate(value) -> str:
@@ -82,56 +90,71 @@ def validate_rate(value) -> str:
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.
Decided ONLY from signals that are independent of the six CST/rate fields - an empty field is a data state, never proof of "not applicable":
* the company regime (CRT);
* the product's own declaration: PISCOFINS flag and INCIDENCIA_PISCOFINS;
* official rules (LC 123/2006 art. 13 IV/V and art. 18 par. 4-A I; Leis 9.718/98, 10.637/02, 10.833/03; SPED 4.3.x for the CST domain/semantics).
REQUIRED the product declares a special treatment (flag S, or incidence MONOFASICA / SUBSTITUICAO): the exit CST must be valid, whatever the fields hold.
NOT_APPLICABLE Simples family (CRT 1/2/4) with no special treatment declared: PIS/Cofins are collected inside the DAS (LC 123 art. 13), so the product carries no
obligation. The same verdict holds whether the fields are empty or typed.
OPTIONAL taxed regime (CRT 3 or other) AND the product explicitly declares "no special treatment" (flag N + incidence NENHUMA): the tax applies but the
company default covers it, so the product default is not mandatory; anything typed must still be valid.
NEEDS_REVIEW the declaration cannot be read: incidence text outside the SGV vocabulary, or a taxed regime with a blank flag/incidence.
"""
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)
raw_incidence = _text(product.get("INCIDENCIA_PISCOFINS"))
kind = incidence_kind(raw_incidence)
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"}
if _text(crt) in SIMPLES_FAMILY:
return {"state": "NOT_APPLICABLE", "basis": "SIMPLES_DAS_NO_SPECIAL_TREATMENT"}
if flag == "N" and raw_incidence:
return {"state": "OPTIONAL", "basis": "TAXED_REGIME_COMPANY_DEFAULT_COVERS"}
return {"state": "NEEDS_REVIEW", "basis": "NO_DECLARATION_IN_TAXED_REGIME"}
def evaluate_pis_cofins(product: dict, crt) -> dict:
"""{"applicability": {...}, "blockers": [(type, reason)]}. Entry and exit are separate; a missing OPTIONAL value never blocks."""
"""{"applicability": {...}, "blockers": [(type, reason)], "warnings": [(type, reason)]}. Entry and exit are separate; PIS and COFINS independent.
Blockers exist only where the field applies (REQUIRED / OPTIONAL). Non-blocking warnings report data-quality facts (non-canonical or ignored values)."""
app = pis_cofins_applicability(product, crt)
blockers: list[tuple[str, str]] = []
if app["state"] in ("NOT_APPLICABLE", "NEEDS_REVIEW"):
return {"applicability": app, "blockers": blockers}
warnings: list[tuple[str, str]] = []
kind = incidence_kind(product.get("INCIDENCIA_PISCOFINS"))
flag = _text(product.get("PISCOFINS")).upper()
applies = app["state"] in ("REQUIRED", "OPTIONAL")
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 "")))
for slot, value, state in (("ENTRY", entry, entry_state), ("EXIT", exit_, exit_state)):
if state == "VALID_AFTER_NORMALIZATION":
warnings.append((f"CST_{tax}_NON_CANONICAL_FORMAT", f"{slot}:{_text(value)}->{canonical_cst(value)}"))
elif state == "INVALID":
if applies:
blockers.append((f"{tax}_CST_INVALID", slot))
else:
warnings.append((f"CST_{tax}_INVALID_IGNORED", f"{slot}:{_text(value)}"))
if not applies:
continue
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_)
code = canonical_cst(exit_)
if code:
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":
if applies and 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):
if applies and 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)}
return {"applicability": app, "blockers": _dedupe(blockers), "warnings": _dedupe(warnings)}
def _dedupe(items):
@@ -201,11 +224,21 @@ def evaluate_product(product: dict, company: dict | None, rtc: dict | None, toda
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"]]
warnings = [{"type": kind, "reason": reason} for kind, reason in pis["warnings"]]
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,
"basis": {"PIS_COFINS": pis["applicability"]["basis"], "RTC": rtc_result["applicability"]["basis"]}, "mandatory_fields": mandatory, "warnings": warnings,
"needs_review": pis["applicability"]["state"] == "NEEDS_REVIEW"}
def legal_effective_context(crt) -> dict:
"""INFORMATIONAL ONLY (never a blocker, never part of completeness). The operational rule stays ERP_RTC_ENABLED; this records what the legislation
says about IBS/CBS in documents for the company regime so a difference in timing can be explained without inventing a rule."""
simples = _text(crt) in SIMPLES_FAMILY
return {"crt": _text(crt) or None, "informational_only": True,
"ibs_cbs_in_documents": "FROM_2027_FOR_SIMPLES_ORIENTATION_PENDING" if simples else ("MANDATORY_FROM_2026-01-01" if _text(crt) == "3" else "UNKNOWN_REGIME"),
"sources": ["NT 2025.002-RTC v1.51 (Portal NF-e, 04/08/2026): CRT 1/2/4 orientations in a future NT; IBS/CBS taxation of these taxpayers only from 2027", "LC 214/2025 art. 348"]}
+1 -1
View File
@@ -8,7 +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>{% 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>
</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" }}" title="{{ ops.rtc.title }}">{{ 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>
+12
View File
@@ -575,3 +575,15 @@ def test_pis_cofins_and_rtc_blockers_appear_in_the_same_queue_and_dashboard_brea
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
def test_rtc_status_shows_where_and_when_the_agent_observed_it():
client, agent_id, _ = _panel_client()
_complete_sync(client, agent_id, {"rtc_enabled": True, "rtc_supported": True, "rtc_catalog_valid": True, "rtc_source": "PARAMETROS.USAR_REFORMA_TRIBUTARIA", "rtc_observed_at": "2026-09-24T20:00:00+00:00"})
text = client.get(f"/agents/{agent_id}").text
assert 'title="Lido no banco do cliente (PARAMETROS.USAR_REFORMA_TRIBUTARIA) em ' in text and ">Ativa<" in text
_complete_sync(client, agent_id, {"rtc_enabled": False, "rtc_param_conflict": True, "rtc_source": "PARAMETROS.USAR_REFORMA_TRIBUTARIA"})
conflict = client.get(f"/agents/{agent_id}").text
assert "linhas de PARAMETROS divergentes" in conflict and ">Desativada<" in conflict
for legacy in ("Escopo anterior", "Escopo ampliado", "Legacy coverage", "Expanded coverage"):
assert legacy not in conflict
+89 -27
View File
@@ -1,19 +1,25 @@
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)
from central.app.unified_fiscal_coverage import (blocker_category, canonical_cst, evaluate_product, incidence_kind, legal_effective_context, pis_cofins_applicability, rtc_enabled,
validate_cclasstrib, validate_cst)
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"}
FIELDS = ("COD_ENTRADA_PIS", "COD_SAIDA_PIS", "ALIQ_PIS", "COD_ENTRADA_COFINS", "COD_SAIDA_COFINS", "ALIQ_COFINS")
def kinds(result):
return {b["type"] for b in result["blockers"]}
def warns(result):
return {w["type"] for w in result["warnings"]}
# ---------------------------------------------------------------- RTC on / off
def test_rtc_off_ignores_cclasstrib_completely():
for value in (None, "", "12x", "999999", "000001"):
@@ -48,14 +54,85 @@ def test_rtc_enabled_needs_schema_and_erp_switch():
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_legal_context_is_informational_and_never_creates_a_blocker():
simples, normal = legal_effective_context("1"), legal_effective_context("3")
assert simples["informational_only"] and "2027" in simples["ibs_cbs_in_documents"] and normal["ibs_cbs_in_documents"] == "MANDATORY_FROM_2026-01-01"
assert legal_effective_context(None)["ibs_cbs_in_documents"] == "UNKNOWN_REGIME"
# the operational rule is the ERP switch only: a Simples company with RTC on is validated, one with RTC off is not, whatever the legal timing says
assert kinds(evaluate_product({**EMPTY, "CCLASSTRIB": None}, SIMPLES, ON)) == {"CCLASSTRIB_MISSING"} and kinds(evaluate_product({**EMPTY, "CCLASSTRIB": None}, SIMPLES, OFF)) == set()
# ---------------------------------------------------------------- PIS / COFINS applicability is NOT circular
def test_applicability_never_depends_on_whether_the_six_fields_are_empty():
typed = {f: "49" if "COD" in f else "0" for f in FIELDS}
for crt in ("1", "2", "4", "3", None):
for base in (EMPTY, {"CODIGO": "1", "PISCOFINS": "S", "INCIDENCIA_PISCOFINS": "MONOFÁSICA"}, {"CODIGO": "1", "PISCOFINS": "", "INCIDENCIA_PISCOFINS": ""}):
empty_state = pis_cofins_applicability(base, crt)
filled_state = pis_cofins_applicability({**base, **typed}, crt)
assert empty_state == filled_state, (crt, base) # same verdict with the fields empty or typed: emptiness proves nothing
def test_empty_fields_with_applicable_context_is_a_missing_blocker():
for crt in ("1", "3"):
r = evaluate_product({**EMPTY, "PISCOFINS": "S", "INCIDENCIA_PISCOFINS": "MONOFÁSICA"}, {"crt": crt}, OFF)
assert r["applicability"]["PIS_COFINS"] == "REQUIRED" and kinds(r) == {"PIS_CST_MISSING", "COFINS_CST_MISSING"}
def test_empty_fields_with_provably_not_applicable_context_is_not_applicable():
for crt in ("1", "2", "4"):
r = evaluate_product(EMPTY, {"crt": crt}, OFF)
assert r["applicability"]["PIS_COFINS"] == "NOT_APPLICABLE" and r["basis"]["PIS_COFINS"] == "SIMPLES_DAS_NO_SPECIAL_TREATMENT" and r["blockers"] == [] and not r["needs_review"]
assert evaluate_product({"CODIGO": "1"}, SIMPLES, OFF)["blockers"] == [] # columns absent in an old schema, Simples: nothing to require
def test_empty_fields_with_insufficient_context_need_review_and_never_default_to_complete():
for company in (NORMAL, {"crt": None}, None):
r = evaluate_product({"CODIGO": "1", "PISCOFINS": "", "INCIDENCIA_PISCOFINS": ""}, company, OFF)
assert r["applicability"]["PIS_COFINS"] == "NEEDS_REVIEW" and r["needs_review"] and r["blockers"] == []
state = classify_product_state({"CODIGO": "1"}, {"status": "EXACT_NCM"}, fiscal=r)["state"]
assert state == "NEEDS_REVIEW" # counted as UNCLASSIFIED, never inflating coverage
def test_taxed_regime_with_explicit_no_special_treatment_is_optional_and_typed_values_must_be_valid():
r = evaluate_product(EMPTY, NORMAL, OFF)
assert r["applicability"]["PIS_COFINS"] == "OPTIONAL" and r["blockers"] == []
assert kinds(evaluate_product({**EMPTY, "COD_SAIDA_PIS": "2x"}, NORMAL, OFF)) == {"PIS_CST_INVALID"}
def test_unknown_incidence_text_needs_review_in_every_regime():
for crt in ("1", "3"):
r = evaluate_product({**EMPTY, "INCIDENCIA_PISCOFINS": "OUTRA COISA"}, {"crt": crt}, OFF)
assert r["applicability"]["PIS_COFINS"] == "NEEDS_REVIEW" and r["needs_review"] and r["blockers"] == []
def test_simples_typed_garbage_is_ignored_as_a_warning_not_a_blocker():
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"] == "NOT_APPLICABLE" and r["blockers"] == []
assert warns(r) == {"CST_PIS_NON_CANONICAL_FORMAT", "CST_COFINS_NON_CANONICAL_FORMAT"} # "2" is one digit of official "02": normalisable
bad = evaluate_product({**EMPTY, "COD_ENTRADA_COFINS": "000", "COD_SAIDA_COFINS": "060"}, SIMPLES, OFF)
assert bad["blockers"] == [] and warns(bad) == {"CST_COFINS_INVALID_IGNORED"}
# ---------------------------------------------------------------- CST normalisation
def test_canonical_cst_accepts_only_one_digit_with_an_official_zero_padded_form():
assert canonical_cst("4") == "04" and canonical_cst("04") == "04" and canonical_cst(" 4 ") == "04" and canonical_cst("9") == "09" and canonical_cst("1") == "01"
for text in ("0", "049", "000", "060", "2x", "x", "", None, "10", "12", "00"):
assert canonical_cst(text) is None, text
assert validate_cst("4") == "VALID_AFTER_NORMALIZATION" and validate_cst("04") == "VALID" and validate_cst("049") == "INVALID" and validate_cst("") == "EMPTY"
def test_unpadded_cst_is_semantically_valid_warns_and_is_used_canonically_for_the_rules():
row = {**EMPTY, "COD_SAIDA_PIS": "4", "COD_SAIDA_COFINS": "4"}
r = evaluate_product(row, NORMAL, OFF)
assert r["blockers"] == [] and warns(r) == {"CST_PIS_NON_CANONICAL_FORMAT", "CST_COFINS_NON_CANONICAL_FORMAT"}
mono = {**EMPTY, "PISCOFINS": "S", "INCIDENCIA_PISCOFINS": "MONOFÁSICA", "COD_SAIDA_PIS": "4", "COD_SAIDA_COFINS": "4"}
assert kinds(evaluate_product(mono, NORMAL, OFF)) == set() # "4" -> "04" is compatible with MONOFASICA
assert kinds(evaluate_product({**mono, "COD_SAIDA_PIS": "1", "ALIQ_PIS": "1,65"}, NORMAL, OFF)) == {"PIS_CST_INCIDENCE_MISMATCH"} # "1" -> "01" is not
assert kinds(evaluate_product({**EMPTY, "COD_SAIDA_PIS": "049"}, NORMAL, OFF)) == {"PIS_CST_INVALID"} # ambiguous: still a blocker
# ---------------------------------------------------------------- PIS / COFINS rules
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)
@@ -69,13 +146,6 @@ def test_st_incidence_with_taxable_cst_is_a_mismatch_and_pis_cofins_are_independ
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"}
@@ -85,17 +155,15 @@ def test_rate_rules_follow_the_cst():
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():
def test_inconsistent_flags():
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"
assert incidence_kind("SUBST.TRIBUTÁRIA") == "SUBSTITUICAO" and incidence_kind(None) == "NONE" and incidence_kind(" nenhuma ") == "NONE" and incidence_kind("???") == "UNKNOWN"
# ---------------------------------------------------------------- ONE definition of complete, ONE queue
@@ -105,7 +173,8 @@ def test_blocker_categories_feed_the_single_dashboard_breakdown():
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": "???"}]
products = [{**EMPTY, "CODIGO": "1"}, {**EMPTY, "CODIGO": "2", "PISCOFINS": "S", "INCIDENCIA_PISCOFINS": "MONOFÁSICA"}, {**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]
@@ -118,14 +187,7 @@ def test_unified_coverage_invariant_and_rtc_toggle_changes_only_rtc_products():
def test_fiscal_blockers_never_hide_or_override_a_decision_blocker():
fiscal = evaluate_product({**EMPTY, "COD_SAIDA_PIS": "2"}, SIMPLES, OFF)
fiscal = evaluate_product({**EMPTY, "PISCOFINS": "S", "INCIDENCIA_PISCOFINS": "MONOFÁSICA"}, 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
+28
View File
@@ -236,3 +236,31 @@ def test_partial_reprocess_never_closes_fiscal_blockers_of_other_products():
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
def test_rtc_on_then_off_and_pis_fix_leave_no_stale_open_blockers_and_never_duplicate():
from central.app.models import BlockedProduct
client, engine = _client()
hb(client)
rtc_missing = _fiscal_blocker("20", "RTC", "CCLASSTRIB_MISSING", "r1")
pis_invalid = _fiscal_blocker("21", "PIS", "PIS_CST_INVALID", "p1")
op = {**OPERATIONAL, "BLOCKED_MISSING_EVIDENCE": 0, "BLOCKED_CONFLICT": 0}
finish(client, engine, {"operational": {**op, "COMPLETE": 519, "BLOCKED_FISCAL_DATA": 2, "SAFE_BLOCKED": 2}, "blocked_products": [rtc_missing, pis_invalid, dict(rtc_missing)], "resolved_products": [],
"fiscal_context": {"rtc_enabled": True, "evaluated_all": True}})
with Session(engine) as s:
assert len(s.scalars(select(BlockedProduct)).all()) == 2 # the repeated blocker is one row
age_last_sync(engine, 4000)
hb(client)
# RTC switched off in the ERP AND the PIS value was normalised/fixed: the next full pass reports nothing
finish(client, engine, {"operational": {**op, "COMPLETE": 521}, "blocked_products": [], "resolved_products": [], "fiscal_context": {"rtc_enabled": False, "evaluated_all": True}})
with Session(engine) as s:
rows = s.scalars(select(BlockedProduct)).all()
assert [r.resolved_at is not None for r in rows] == [True, True] and {r.resolution_source for r in rows} == {"AUTO_REEVALUATION"}
assert s.scalars(select(BlockedProduct).where(BlockedProduct.resolved_at.is_(None))).all() == []
age_last_sync(engine, 4000)
hb(client)
# RTC comes back on: the SAME blocker (same fingerprint) reopens instead of being duplicated
finish(client, engine, {"operational": {**op, "COMPLETE": 520, "BLOCKED_FISCAL_DATA": 1, "SAFE_BLOCKED": 1}, "blocked_products": [rtc_missing], "resolved_products": [], "fiscal_context": {"rtc_enabled": True, "evaluated_all": True}})
with Session(engine) as s:
rows = s.scalars(select(BlockedProduct)).all()
assert len(rows) == 2 and [(r.product_code, r.resolved_at is None) for r in rows] == [("20", True), ("21", False)]