fix: gate cClassTrib applicability by CRT and date
This commit is contained in:
@@ -168,12 +168,38 @@ def _dedupe(items):
|
||||
|
||||
# ------------------------------------------------------------------------------------------------- RTC
|
||||
|
||||
CCLASSTRIB_EFFECTIVE_FROM = {
|
||||
"3": date(2026, 8, 3),
|
||||
"1": date(2027, 1, 1),
|
||||
"2": date(2027, 1, 1),
|
||||
"4": date(2027, 1, 1),
|
||||
}
|
||||
|
||||
|
||||
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 cclasstrib_applicability(crt, rtc_enabled: bool, today: date | None = None) -> dict:
|
||||
"""Company-level cClassTrib requiredness from live CRT and its effective date.
|
||||
|
||||
The ERP RTC switch scopes the feature; it does not make cClassTrib mandatory
|
||||
for every company/product. Unknown regimes fail closed to NOT_APPLICABLE.
|
||||
"""
|
||||
today = today or date.today()
|
||||
regime = _text(crt)
|
||||
if not rtc_enabled:
|
||||
return {"state": "NOT_APPLICABLE", "basis": "RTC_DISABLED_IN_ERP", "effective_from": None, "source": "PARAMETROS.USAR_REFORMA_TRIBUTARIA"}
|
||||
effective_from = CCLASSTRIB_EFFECTIVE_FROM.get(regime)
|
||||
if effective_from is None:
|
||||
return {"state": "NOT_APPLICABLE", "basis": "COMPANY_CRT_UNRESOLVED", "effective_from": None, "source": "C000004.CRT"}
|
||||
if today < effective_from:
|
||||
basis = "SIMPLES_CCLASSTRIB_NOT_EFFECTIVE" if regime in SIMPLES_FAMILY else "CCLASSTRIB_NOT_YET_EFFECTIVE"
|
||||
return {"state": "NOT_APPLICABLE", "basis": basis, "effective_from": effective_from.isoformat(), "source": "C000004.CRT + RTC effective-date rules"}
|
||||
return {"state": "REQUIRED", "basis": "CCLASSTRIB_EFFECTIVE_FOR_CRT", "effective_from": effective_from.isoformat(), "source": "C000004.CRT + RTC effective-date rules"}
|
||||
|
||||
|
||||
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", ()))
|
||||
@@ -202,11 +228,13 @@ def validate_cclasstrib(value, catalog: dict | None, today: date | None = None)
|
||||
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"}
|
||||
def evaluate_rtc(product: dict, rtc: dict | None, today: date | None = None, crt=None) -> dict:
|
||||
"""Validate cClassTrib only when RTC is on and the company's regime/date gate applies."""
|
||||
app = rtc.get("cclasstrib_applicability") if isinstance(rtc, dict) else None
|
||||
if not isinstance(app, dict):
|
||||
app = cclasstrib_applicability(crt, bool(rtc and rtc.get("enabled")), today)
|
||||
if app["state"] != "REQUIRED":
|
||||
return {"applicability": app, "blockers": []}
|
||||
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)
|
||||
@@ -222,7 +250,7 @@ def evaluate_product(product: dict, company: dict | None, rtc: dict | None, toda
|
||||
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)
|
||||
rtc_result = evaluate_rtc(product, rtc, today, crt)
|
||||
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 = []
|
||||
@@ -236,8 +264,7 @@ def evaluate_product(product: dict, company: dict | None, rtc: dict | None, toda
|
||||
|
||||
|
||||
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 official acts say about the
|
||||
obligation to issue DF-e with IBS/CBS for the company regime, so a timing difference can be explained without inventing a rule.
|
||||
"""Summarize the broad IBS/CBS document timeline; cClassTrib requiredness has its own CRT/date gate above.
|
||||
Source: Ato Conjunto RFB/CGIBS n 4/2026 (DOU 31/07/2026) art. 1: NF-e (55) and NFC-e (65) from 2026-08-03; par. 1: Simples optantes from 2027-01-01."""
|
||||
simples = _text(crt) in SIMPLES_FAMILY
|
||||
return {"crt": _text(crt) or None, "informational_only": True,
|
||||
|
||||
Reference in New Issue
Block a user