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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user