chore: establish project baseline
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
"""Product Fiscal Defaults V1 runner: CFOP/CSOSN/CST over all C000025 products (read-only).
|
||||
|
||||
Writes docs/PRODUCT_FISCAL_DEFAULTS_MATRIX_V1.json, ..._DRY_RUN_V1.json, FISCAL_CONTEXT_REQUIREMENTS_V2.json,
|
||||
NEXT_FISCAL_FIELD_IMPLEMENTATION_V2.md. No Firebird writes: the engine can only emit write_eligible=False
|
||||
(see central/app/product_defaults.py); this runner refuses to write anything.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from collections import Counter, defaultdict
|
||||
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"))
|
||||
import run_fiscal_decision_v1 as base # noqa: E402
|
||||
from central.app.product_defaults import PRODUCT_DEFAULTS_ENGINE_VERSION, cfop_default, csosn_default, cst_status, validate_against_history # noqa: E402
|
||||
from gti_fiscal_agent.firebird_reader import _connect # noqa: E402
|
||||
|
||||
|
||||
def main():
|
||||
token = sys.argv[1]
|
||||
hub = "http://localhost:8000"
|
||||
base.load_dotenv(ROOT / ".env")
|
||||
conn = _connect(os.environ); cur = conn.cursor()
|
||||
cur.execute("select CODIGO, PRODUTO, CLASSIFICACAO_FISCAL, CFOP, CSOSN, CST from C000025 order by CODIGO")
|
||||
products = [dict(zip(("CODIGO", "PRODUTO", "CLASSIFICACAO_FISCAL", "CFOP", "CSOSN", "CST"), r)) for r in cur.fetchall()]
|
||||
cur.execute("select ICMS_CSOSN, CRT, UF from C000004"); company_default, crt, uf = [str(x).strip() for x in cur.fetchone()]
|
||||
conn.close()
|
||||
refs = base.build_refs(hub, token, products, uf)
|
||||
|
||||
decisions = []
|
||||
for i in range(0, len(products), 100):
|
||||
chunk = products[i:i + 100]
|
||||
decisions += base.http(f"{hub}/v1/classify/batch", token, {"products": [{"current_ncm": p["CLASSIFICACAO_FISCAL"], "description": p["PRODUTO"] or ""} for p in chunk]})["results"]
|
||||
|
||||
trusted = json.loads((ROOT / "docs" / "runs" / "trusted_historical_items.json").read_text(encoding="utf-8"))
|
||||
hist = defaultdict(lambda: {"cfop": [], "csosn": []})
|
||||
for t in trusted:
|
||||
h = hist[str(t["cod"]).strip()]
|
||||
if (t.get("cfop") or "").strip(): h["cfop"].append(t["cfop"].strip())
|
||||
if (t.get("csosn") is not None) and str(t["csosn"]).strip(): h["csosn"].append(str(t["csosn"]).strip())
|
||||
|
||||
rows = []
|
||||
for p, d in zip(products, decisions):
|
||||
code = str(p["CODIGO"]).strip()
|
||||
ncm_status = d.get("status"); mva = d.get("ba_st_mva_status")
|
||||
cf = cfop_default(p, ncm_status, mva, refs["cfop_valid"])
|
||||
cs = csosn_default(p, ncm_status, mva, refs["csosn"], company_default)
|
||||
ct = cst_status(p, refs["cst_icms"], refs["origem"])
|
||||
h = hist.get(code, {"cfop": [], "csosn": []})
|
||||
cf["historical_validation"] = validate_against_history(cf["proposed_value"], h["cfop"])
|
||||
cs["historical_validation"] = validate_against_history(cs["proposed_value"], h["csosn"])
|
||||
cf_cur_share = (sum(1 for v in h["cfop"] if v == cf["current_value"]) / len(h["cfop"])) if h["cfop"] else None
|
||||
cs_cur_share = (sum(1 for v in h["csosn"] if v == cs["current_value"]) / len(h["csosn"])) if h["csosn"] else None
|
||||
rows.append({
|
||||
"CODIGO": code, "DESCRIPTION": p["PRODUTO"], "NCM": p["CLASSIFICACAO_FISCAL"],
|
||||
"CFOP_CURRENT": cf["current_value"], "CFOP_PROPOSED": cf["proposed_value"], "CFOP_STATUS": cf["status"], "CFOP_REASON": cf["reason_code"], "CFOP_WRITE_ELIGIBLE": cf["write_eligible"],
|
||||
"CSOSN_CURRENT": cs["current_value"], "CSOSN_PROPOSED": cs["proposed_value"], "CSOSN_STATUS": cs["status"], "CSOSN_REASON": cs["reason_code"], "CSOSN_WRITE_ELIGIBLE": cs["write_eligible"],
|
||||
"CST_CURRENT": ct["current_value"], "CST_STATUS": ct["status"],
|
||||
"HISTORICAL_OCCURRENCES": {"trusted_cfop_items": len(h["cfop"]), "trusted_csosn_items": len(h["csosn"])},
|
||||
"HISTORICAL_CONSISTENCY": {"cfop_current_equals_emitted_share": cf_cur_share, "csosn_current_equals_emitted_share": cs_cur_share,
|
||||
"cfop_validation": cf["historical_validation"], "csosn_validation": cs["historical_validation"]},
|
||||
"PROVENANCE": {"engine": PRODUCT_DEFAULTS_ENGINE_VERSION, "cfop_dataset": d.get("dataset_versions", {}).get("cfop"), "moc": d.get("dataset_versions", {}).get("nfe_moc_tables"),
|
||||
"ba_st_mva": d.get("dataset_versions", {}).get("ba_st_mva"), "company": {"crt": crt, "uf": uf, "icms_csosn": company_default}},
|
||||
"_cfop": cf, "_csosn": cs,
|
||||
})
|
||||
|
||||
def count(field, key):
|
||||
c = Counter(r[key] for r in rows)
|
||||
return c
|
||||
cf_states = Counter(r["CFOP_STATUS"] for r in rows); cs_states = Counter(r["CSOSN_STATUS"] for r in rows)
|
||||
cf_act = Counter(r["_cfop"]["action_class"] for r in rows); cs_act = Counter(r["_csosn"]["action_class"] for r in rows)
|
||||
cfv = Counter(r["HISTORICAL_CONSISTENCY"]["cfop_validation"] for r in rows if r["CFOP_PROPOSED"] is not None)
|
||||
csv_ = Counter(r["HISTORICAL_CONSISTENCY"]["csosn_validation"] for r in rows if r["CSOSN_PROPOSED"] is not None)
|
||||
cf_conflicts = [r for r in rows if r["CFOP_STATUS"] == "CFOP_DEFAULT_CANDIDATE"]
|
||||
conflict_hist = Counter()
|
||||
for r in cf_conflicts:
|
||||
share = r["HISTORICAL_CONSISTENCY"]["cfop_current_equals_emitted_share"]
|
||||
conflict_hist["no_trusted_history" if share is None else ("history_emitted_current_value" if share == 1.0 else "history_differs_from_current")] += 1
|
||||
summary = {
|
||||
"TOTAL_PRODUCTS": len(rows), "company": {"crt": crt, "uf": uf, "icms_csosn": company_default},
|
||||
"CFOP": {"RESOLVED": cf_states["CFOP_DEFAULT_RESOLVED"], "ALREADY_CORRECT": cf_act["ALREADY_CORRECT"], "CHANGE_REQUIRED": 0,
|
||||
"REVIEW": cf_act["REVIEW_REQUIRED"], "CONTEXT_REQUIRED": cf_states["CFOP_OPERATION_CONTEXT_REQUIRED"], "UNRESOLVED": cf_states["CFOP_UNRESOLVED"],
|
||||
"CANDIDATE": cf_states["CFOP_DEFAULT_CANDIDATE"], "AUTO_APPLY_SAFE": 0, "states": dict(cf_states), "historical_validation_of_proposals": dict(cfv),
|
||||
"conflict_candidates_vs_history": dict(conflict_hist)},
|
||||
"CSOSN": {"RESOLVED": cs_states["CSOSN_DEFAULT_RESOLVED"], "ALREADY_CORRECT": 0, "CHANGE_REQUIRED": 0, "REVIEW": cs_act["REVIEW_REQUIRED"],
|
||||
"CONTEXT_REQUIRED": cs_states["CSOSN_OPERATION_CONTEXT_REQUIRED"], "UNRESOLVED": cs_states["CSOSN_UNRESOLVED"], "CANDIDATE": cs_states["CSOSN_DEFAULT_CANDIDATE"],
|
||||
"AUTO_APPLY_SAFE": 0, "states": dict(cs_states), "historical_validation_of_proposals": dict(csv_),
|
||||
"st_conflicts": dict(Counter(r["_csosn"].get("consistency_flag") for r in rows if r["_csosn"].get("consistency_flag")))},
|
||||
"CST": dict(Counter(r["CST_STATUS"] for r in rows)),
|
||||
}
|
||||
for r in rows:
|
||||
r.pop("_cfop"); r.pop("_csosn")
|
||||
dm = {"artifact": "PRODUCT_FISCAL_DEFAULTS_MATRIX_V1", "engine": PRODUCT_DEFAULTS_ENGINE_VERSION, "products": rows}
|
||||
(ROOT / "docs" / "PRODUCT_FISCAL_DEFAULTS_MATRIX_V1.json").write_text(json.dumps(dm, ensure_ascii=False, indent=1, default=str), encoding="utf-8")
|
||||
dry = {"artifact": "PRODUCT_FISCAL_DEFAULTS_DRY_RUN_V1", "firebird_writes": 0, "write_candidates": [], **summary,
|
||||
"note": "No proposal is write-eligible: CFOP conflicts need ST-retention/resale context; CSOSN needs product ICMS-treatment and retention context."}
|
||||
(ROOT / "docs" / "PRODUCT_FISCAL_DEFAULTS_DRY_RUN_V1.json").write_text(json.dumps(dry, ensure_ascii=False, indent=1), encoding="utf-8")
|
||||
|
||||
# context requirements V2 (exact inputs still blocking)
|
||||
st_unknown = sum(1 for r in rows if r["CFOP_REASON"] == "ST_APPLICABILITY_UNCONFIRMED_NEEDS_CEST_CONFIRMATION")
|
||||
st_subject = sum(1 for r in rows if r["CSOSN_REASON"] == "ST_RETENTION_BY_SUPPLIER_UNKNOWN")
|
||||
ncm_bad = sum(1 for r in rows if r["CFOP_REASON"] == "NCM_NOT_VALID_CANNOT_ASSESS_ST")
|
||||
csosn_notst = sum(1 for r in rows if r["CSOSN_STATUS"] == "CSOSN_DEFAULT_CANDIDATE")
|
||||
blank_cfop = sum(1 for r in rows if r["CFOP_REASON"] == "NO_DECLARED_RESALE_OR_PRODUCTION_FAMILY")
|
||||
other_family = sum(1 for r in rows if r["CFOP_REASON"] in ("NO_RULE_FOR_DECLARED_CFOP_FAMILY", "DIRECTION_OR_UF_SCOPE_NOT_INTRA_UF_SAIDA"))
|
||||
conflicts = len(cf_conflicts)
|
||||
req = [
|
||||
{"INPUT": "valid_ncm", "SOURCE_EXPECTED": "product master (C000025.CLASSIFICACAO_FISCAL)", "AVAILABLE": "PARTIAL (495/521 valid)", "AFFECTED_PRODUCTS": ncm_bad, "AFFECTED_FIELD": "CFOP,CSOSN", "NEXT_ACTION": "Fix/review invalid+legacy NCMs (never auto-change)."},
|
||||
{"INPUT": "st_applicability_confirmed (CEST confirmation)", "SOURCE_EXPECTED": "CEST dataset + BA Anexo 1 item", "AVAILABLE": "PARTIAL (31 resolved, 237 candidates)", "AFFECTED_PRODUCTS": st_unknown, "AFFECTED_FIELD": "CFOP,CSOSN", "NEXT_ACTION": "Confirm CEST per product (description evidence/manual review) to settle ST applicability."},
|
||||
{"INPUT": "st_retention_by_supplier", "SOURCE_EXPECTED": "purchase document per product (entrada) or supplier flag", "AVAILABLE": False, "AFFECTED_PRODUCTS": st_subject, "AFFECTED_FIELD": "CSOSN(500 vs 102),CFOP(5405 vs 5102)", "NEXT_ACTION": "Locate structured retention evidence in purchase items (C000032/C000062 CST 060/CSOSN 500 history) or ask vendor."},
|
||||
{"INPUT": "product_icms_treatment (taxed / exempt / not taxed)", "SOURCE_EXPECTED": "state ICMS rules per NCM (no acquired dataset)", "AVAILABLE": False, "AFFECTED_PRODUCTS": csosn_notst, "AFFECTED_FIELD": "CSOSN(102 vs 400/300/103)", "NEXT_ACTION": "Needs an official per-NCM ICMS treatment source; ERP CST=040 with CSOSN=102 coexists, so ERP alone is ambiguous."},
|
||||
{"INPUT": "declared_resale_vs_production", "SOURCE_EXPECTED": "product nature/type (C000025.TIPO_ITEM unused) or company activity", "AVAILABLE": False, "AFFECTED_PRODUCTS": blank_cfop + other_family, "AFFECTED_FIELD": "CFOP", "NEXT_ACTION": "Confirm meaning of FOOD_ATRIBUICAO_VALORES/TIPO_ITEM with vendor before deriving 5101 vs 5102."},
|
||||
{"INPUT": "st_conflict_resolution (which of ERP CFOP or BA Anexo 1 is wrong)", "SOURCE_EXPECTED": "human/fiscal review of conflicting products", "AVAILABLE": False, "AFFECTED_PRODUCTS": conflicts, "AFFECTED_FIELD": "CFOP", "NEXT_ACTION": "Review queue; do not auto-apply."},
|
||||
{"INPUT": "destination_uf / consumer_final / direction", "SOURCE_EXPECTED": "NFC-e document model 65 + chave cUF (proven 1499/1499) and C000004.UF", "AVAILABLE": True, "AFFECTED_PRODUCTS": 0, "AFFECTED_FIELD": "CFOP", "NEXT_ACTION": "Already satisfied for NFC-e scope; NF-e needs customer data."},
|
||||
]
|
||||
(ROOT / "docs" / "FISCAL_CONTEXT_REQUIREMENTS_V2.json").write_text(json.dumps({"artifact": "FISCAL_CONTEXT_REQUIREMENTS_V2", "requirements": req}, ensure_ascii=False, indent=1), encoding="utf-8")
|
||||
|
||||
# priorities V2 (mechanical)
|
||||
prio = [
|
||||
("CFOP", "CONFIRMED (HIGH)", "PARTIAL", "YES (ST family)", summary["CFOP"]["RESOLVED"] + conflicts, "trusted history available", "AUTO_APPLY_SAFE=0; review the ST conflicts."),
|
||||
("CSOSN", "CONFIRMED (HIGH)", "NO", "NO (treatment/retention sources missing)", summary["CSOSN"]["CANDIDATE"] + summary["CSOSN"]["CONTEXT_REQUIRED"], "trusted history available", "Acquire retention/treatment evidence."),
|
||||
("CST", "CONFIRMED (MEDIUM)", "YES", "n/a (reference only, CRT=1)", 521, "trusted history 100%", "No write needed."),
|
||||
("CEST", "CONFIRMED (HIGH)", "YES", "PARTIAL", 15, "weak (1/15 supported)", "Description-evidence review of 15 candidates."),
|
||||
("IPI", "CONFIRMED (HIGH)", "YES", "YES", 107, "n/a", "Maintained; already reconciled."),
|
||||
]
|
||||
md = ["# NEXT_FISCAL_FIELD_IMPLEMENTATION_V2", "",
|
||||
"Mechanical ordering: mapping confirmed, rule available, context available, products affected, history validation, auto-apply possible (post PRODUCT DEFAULTS V1).", "",
|
||||
"| FIELD | ERP_MAPPING | CONTEXT_AVAILABLE | RULE_AVAILABLE | POTENTIAL_PRODUCTS | HISTORICAL_VALIDATION | NEXT_ACTION |", "|---|---|---|---|---|---|---|"]
|
||||
md += [f"| {a} | {b} | {c} | {d_} | {e} | {f} | {g} |" for a, b, c, d_, e, f, g in prio]
|
||||
md += ["", "Blocking inputs and affected counts: `docs/FISCAL_CONTEXT_REQUIREMENTS_V2.json`."]
|
||||
(ROOT / "docs" / "NEXT_FISCAL_FIELD_IMPLEMENTATION_V2.md").write_text("\n".join(md) + "\n", encoding="utf-8")
|
||||
print(json.dumps(summary, ensure_ascii=True, indent=1))
|
||||
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user