93 lines
6.0 KiB
Python
93 lines
6.0 KiB
Python
"""Read-only fiscal coverage report for a set of products (used by the Agent's REPROCESS_ALL result and by the multi-database tooling).
|
|
|
|
Pure computation over Hub decisions + product rows + local evidence. It reuses the shipped pure engines in central.app
|
|
(fiscal_decision, product_defaults, icms_context) — no fiscal rule lives here, only guards for the company context:
|
|
the BA Anexo 1 ST/MVA dataset is applied to UF=BA only and CSOSN logic only under CRT=1.
|
|
"""
|
|
from collections import Counter
|
|
|
|
PRODUCT_COLUMNS = ("CODIGO", "PRODUTO", "CLASSIFICACAO_FISCAL", "CFOP", "CSOSN", "CST", "CEST", "IPI", "EX_TIPI", "TIPO", "PROD_COZINHA", "PRODUTO_SERVICO", "SITUACAO_TRIBUTARIA")
|
|
|
|
|
|
def _s(v):
|
|
return None if v is None else str(v).strip()
|
|
|
|
|
|
def compute_coverage(products, decisions, company, company_default, refs, evidence):
|
|
"""products/decisions aligned lists. company: {"uf","crt"} (resolved). evidence: {"recipes","composed","purchases","stock_entry"}."""
|
|
from central.app.fiscal_decision import decide_product
|
|
from central.app.icms_context import csosn_v2, product_role, st_status, withheld_status
|
|
from central.app.product_defaults import cfop_default
|
|
uf_ba = company.get("uf") == "BA"
|
|
crt1 = company.get("crt") == "1"
|
|
recipes, composed = evidence.get("recipes", set()), evidence.get("composed", set())
|
|
purchases, stock_entry = evidence.get("purchases", {}), evidence.get("stock_entry", {})
|
|
m, cfop_values, situ = Counter(), Counter(), Counter()
|
|
for p, d in zip(products, decisions):
|
|
p = {c: p.get(c) for c in PRODUCT_COLUMNS}
|
|
code = _s(p["CODIGO"]); p["CODIGO"] = code
|
|
cls = d.get("classification") or {}
|
|
rec = decide_product(p, d, {"uf": company.get("uf") or "", "crt": company.get("crt") or ""}, refs)
|
|
ncm_status = d.get("status"); raw_ncm = _s(p["CLASSIFICACAO_FISCAL"])
|
|
m["NCM_MISSING" if not raw_ncm else ("NCM_VALID" if ncm_status == "EXACT_NCM" else ("NCM_LEGACY" if ncm_status == "LEGACY_NCM" else "NCM_INVALID"))] += 1
|
|
ipi = rec["IPI"]; rk = (cls.get("ipi") or {}).get("rate_kind"); ist = d.get("ipi_status")
|
|
if ist == "IPI_RESOLVED":
|
|
m[{"NUMERIC": "IPI_NUMERIC", "NON_NUMERIC": "IPI_NON_NUMERIC", "BLANK": "IPI_SOURCE_BLANK"}.get(rk, "IPI_NO_MATCH")] += 1
|
|
elif ist == "IPI_CANDIDATES":
|
|
m["IPI_AMBIGUOUS"] += 1
|
|
elif ist != "LEGACY_NCM" and raw_ncm:
|
|
m["IPI_NO_MATCH"] += 1
|
|
if ipi["action_class"] == "ALREADY_CORRECT":
|
|
m["IPI_ALREADY_CORRECT"] += 1
|
|
if ipi["write_eligible"]:
|
|
m["IPI_CHANGE_REQUIRED"] += 1; m["IPI_WRITE_ELIGIBLE"] += 1
|
|
cs_ = d.get("cest_status")
|
|
if cs_ == "CEST_CONFIRMED":
|
|
m["CEST_CONFIRMED"] += 1
|
|
elif cs_ == "CEST_CANDIDATES":
|
|
m["CEST_CANDIDATES"] += 1
|
|
cf = cfop_default(p, ncm_status, d.get("ba_st_mva_status") if uf_ba else None, refs["cfop_valid"])
|
|
cfop_values[_s(p["CFOP"])] += 1
|
|
m["CFOP_RESOLVED" if cf["status"] == "CFOP_DEFAULT_RESOLVED" else "CFOP_CONTEXT_REQUIRED"] += 1
|
|
ba = cls.get("ba_st_mva") or {}
|
|
if uf_ba:
|
|
st, _ = st_status(ncm_status, d.get("ba_st_mva_status"), cs_, p["CEST"], ba.get("cest"), None)
|
|
else:
|
|
st = "ST_UNKNOWN"
|
|
m[st] += 1
|
|
wh, _ = withheld_status(purchases.get(code, []))
|
|
m[{"ST_ALREADY_WITHHELD_CONFIRMED": "ST_WITHHOLDING_CONFIRMED", "ST_ALREADY_WITHHELD_NOT_CONFIRMED": "ST_WITHHOLDING_NOT_CONFIRMED"}.get(wh, "ST_WITHHOLDING_UNKNOWN")] += 1
|
|
role, _ = product_role(p["TIPO"], p["PROD_COZINHA"] == 1, code in recipes, code in composed, bool(_s(p["PRODUTO_SERVICO"])), bool(purchases.get(code)) or stock_entry.get(code, False))
|
|
m["PRODUCT_ROLE_KNOWN" if role != "UNKNOWN" else "PRODUCT_ROLE_UNKNOWN"] += 1
|
|
m["ROLE_" + role] += 1
|
|
state = csosn_v2(p["CSOSN"], company_default, ncm_status, st if uf_ba else "ST_UNKNOWN", wh, role, refs["csosn"])["status"] if crt1 else "CSOSN_CONTEXT_REQUIRED"
|
|
m["CSOSN_RESOLVED" if state == "CSOSN_RESOLVED" else ("CSOSN_CANDIDATES" if state == "CSOSN_CANDIDATE" else "CSOSN_CONTEXT_REQUIRED")] += 1
|
|
situ[_s(p["SITUACAO_TRIBUTARIA"])] += 1
|
|
n = len(products)
|
|
fill = lambda f: sum(1 for p in products if _s(p.get(f)) not in (None, "")) # noqa: E731
|
|
return {"TOTAL_PRODUCTS": n, "metrics": dict(m),
|
|
"IPI_RESOLVED": m["IPI_NUMERIC"] + m["IPI_NON_NUMERIC"] + m["IPI_SOURCE_BLANK"],
|
|
"quality": {"ncm_valid_share": round(m["NCM_VALID"] / n, 4) if n else None, "cest_filled": fill("CEST"), "csosn_filled": fill("CSOSN"), "cfop_filled": fill("CFOP"), "ipi_filled": fill("IPI"),
|
|
"cfop_distinct": len(cfop_values), "cfop_top": cfop_values.most_common(6), "situacao_tributaria": dict(situ.most_common(6)),
|
|
"purchase_items_C000088": sum(len(v) for v in purchases.values()), "products_with_purchase_evidence": len(purchases), "recipes": len(recipes), "composed_products": len(composed)},
|
|
"guards_applied": {"st_dataset_ba_only": not uf_ba, "csosn_requires_crt1": not crt1}}
|
|
|
|
|
|
def build_refs(get, products, company_uf):
|
|
"""Reference-data snapshot from the Hub (`get(path) -> json`). Used by the engines for domain validity only."""
|
|
refs = {}
|
|
for key, domain in (("csosn", "CSOSN"), ("cst_icms", "CST_ICMS"), ("cst_pis", "CST_PIS"), ("cst_cofins", "CST_COFINS"), ("origem", "ORIGEM_MERCADORIA")):
|
|
refs[key] = set(get(f"/v1/reference/fiscal_codes/{domain}")["codes"])
|
|
valid = set()
|
|
for code in {_s(p.get(f)) for p in products for f in ("CFOP", "CFOP_ENTRADA") if _s(p.get(f))}:
|
|
if get(f"/v1/reference/cfop/{code}")["found"]:
|
|
valid.add(code)
|
|
refs["cfop_valid"] = valid
|
|
refs["fcp_uf"] = {}
|
|
fcp = get(f"/v1/reference/fcp/{company_uf}")
|
|
if fcp.get("found"):
|
|
refs["fcp_uf"][company_uf] = fcp
|
|
refs["cbs_rate_2026"] = get("/v1/reference/rtc_cbs_rate/2026").get("rate")
|
|
refs["cclasstrib_count"] = sum(d["record_count"] for d in get("/v1/datasets/status") if d["kind"] == "RTC_CCLASSTRIB" and d["status"] == "ACTIVE")
|
|
return refs
|