Hide & exclude inactive e-taxes rows; drop Status column

Inactive rows (objects/cash registers/POS with status != A; bank accounts
with status = C) are no longer counted, previewed, or kept in the DB.

- Single active-status source of truth: _extract_items / _is_active /
  _active_items, mirroring _purge_inactive_etaxes_records.
- fetch_all_etaxes summary counts active rows only (drop _count_items).
- _preview_* filter inactive and remove the Status column (4 lists).
- Presented certificates (audit records) left untouched.

DB side was already covered by the post-load purge; this makes the
wizard list/counter consistent with what survives finalize.

Bump version 0.1.28 -> 0.1.29.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ali 2026-06-12 12:43:21 +00:00
parent 68ea3dce38
commit e1ea661855
3 changed files with 58 additions and 54 deletions

View File

@ -1 +1 @@
__version__ = "0.1.28" __version__ = "0.1.29"

View File

@ -98,7 +98,8 @@ def fetch_all_etaxes():
try: try:
data = _fetch_endpoint(method, url, payload, paginated) data = _fetch_endpoint(method, url, payload, paginated)
cache.set(field, json.dumps(data, ensure_ascii=False)) cache.set(field, json.dumps(data, ensure_ascii=False))
summary[field] = _count_items(data, paginated) # Count only active rows so the digit matches the (filtered) preview.
summary[field] = len(_active_items(field, data))
except Exception as exc: except Exception as exc:
errors[field] = str(exc) errors[field] = str(exc)
frappe.log_error( frappe.log_error(
@ -260,45 +261,81 @@ def _flatten_pages(data, list_key):
return [] return []
# Active-status semantics — single source of truth for counts AND preview.
# Mirrors _purge_inactive_etaxes_records on the materialize side, so what the
# user sees in the wizard list matches what actually survives into the
# E-Taxes doctypes after finalize. Inactive rows are never counted or shown.
_ACTIVE_STATUS = {"A", "ACTIVE"}
def _extract_items(field, data):
"""Pull the raw item list out of a cached endpoint payload, handling the
paginated list-of-pages vs single-dict shapes."""
if field == "objects_json":
return (data.get("objectInfoShortList") or data.get("objects") or []) if isinstance(data, dict) else []
if field == "cash_registers_json":
return _flatten_pages(data, "cashRegisters")
if field == "pos_terminals_json":
return _flatten_pages(data, "posTerminals")
if field == "bank_accounts_json":
if isinstance(data, dict):
return data.get("bankAccounts") or []
if isinstance(data, list):
return data
return []
if field == "presented_certs_json":
return (data.get("certificateList") or []) if isinstance(data, dict) else []
return []
def _is_active(field, item):
"""Active-row predicate per endpoint. Presented certificates are audit
records with no active/inactive notion always kept."""
if not isinstance(item, dict):
return False
if field == "bank_accounts_json":
# 'C' = closed; anything else (incl. blank) counts as open.
return (item.get("status") or "").strip().upper() != "C"
if field in ("objects_json", "cash_registers_json", "pos_terminals_json"):
return (item.get("status") or "").strip().upper() in _ACTIVE_STATUS
return True
def _active_items(field, data):
"""Extracted items minus the inactive ones — the list the wizard counts,
previews, and (indirectly) loads."""
return [it for it in _extract_items(field, data) if _is_active(field, it)]
def _empty_preview(headers): def _empty_preview(headers):
return {"headers": headers, "rows": []} return {"headers": headers, "rows": []}
def _preview_objects(raw): def _preview_objects(raw):
headers = [_("Code"), _("Name"), _("Status"), _("Address")] headers = [_("Code"), _("Name"), _("Address")]
data = _safe_parse(raw) data = _safe_parse(raw)
if data is None: if data is None:
return _empty_preview(headers) return _empty_preview(headers)
items = []
if isinstance(data, dict):
items = data.get("objectInfoShortList") or data.get("objects") or []
rows = [] rows = []
for obj in items[:PREVIEW_ROW_LIMIT]: for obj in _active_items("objects_json", data)[:PREVIEW_ROW_LIMIT]:
if not isinstance(obj, dict):
continue
rows.append([ rows.append([
obj.get("objectCode") or "", obj.get("objectCode") or "",
(obj.get("objectName") or "").strip(), (obj.get("objectName") or "").strip(),
obj.get("status") or "",
obj.get("address") or "", obj.get("address") or "",
]) ])
return {"headers": headers, "rows": rows} return {"headers": headers, "rows": rows}
def _preview_cash_registers(raw): def _preview_cash_registers(raw):
headers = [_("Number"), _("Model"), _("Status"), _("Object"), _("Operator")] headers = [_("Number"), _("Model"), _("Object"), _("Operator")]
data = _safe_parse(raw) data = _safe_parse(raw)
if data is None: if data is None:
return _empty_preview(headers) return _empty_preview(headers)
items = _flatten_pages(data, "cashRegisters")
rows = [] rows = []
for reg in items[:PREVIEW_ROW_LIMIT]: for reg in _active_items("cash_registers_json", data)[:PREVIEW_ROW_LIMIT]:
if not isinstance(reg, dict):
continue
rows.append([ rows.append([
reg.get("number") or "", reg.get("number") or "",
reg.get("model") or "", reg.get("model") or "",
reg.get("status") or "",
reg.get("object") or "", reg.get("object") or "",
reg.get("operatorName") or "", reg.get("operatorName") or "",
]) ])
@ -306,18 +343,14 @@ def _preview_cash_registers(raw):
def _preview_pos_terminals(raw): def _preview_pos_terminals(raw):
headers = [_("Serial number"), _("Status"), _("Registration date"), _("Bank")] headers = [_("Serial number"), _("Registration date"), _("Bank")]
data = _safe_parse(raw) data = _safe_parse(raw)
if data is None: if data is None:
return _empty_preview(headers) return _empty_preview(headers)
items = _flatten_pages(data, "posTerminals")
rows = [] rows = []
for term in items[:PREVIEW_ROW_LIMIT]: for term in _active_items("pos_terminals_json", data)[:PREVIEW_ROW_LIMIT]:
if not isinstance(term, dict):
continue
rows.append([ rows.append([
term.get("serialNumber") or "", term.get("serialNumber") or "",
term.get("status") or "",
(term.get("registrationDate") or "")[:10], (term.get("registrationDate") or "")[:10],
term.get("bankName") or "", term.get("bankName") or "",
]) ])
@ -325,23 +358,15 @@ def _preview_pos_terminals(raw):
def _preview_bank_accounts(raw): def _preview_bank_accounts(raw):
headers = [_("IBAN"), _("Currency"), _("Status"), _("Bank")] headers = [_("IBAN"), _("Currency"), _("Bank")]
data = _safe_parse(raw) data = _safe_parse(raw)
if data is None: if data is None:
return _empty_preview(headers) return _empty_preview(headers)
items = []
if isinstance(data, dict):
items = data.get("bankAccounts") or []
elif isinstance(data, list):
items = data
rows = [] rows = []
for acc in items[:PREVIEW_ROW_LIMIT]: for acc in _active_items("bank_accounts_json", data)[:PREVIEW_ROW_LIMIT]:
if not isinstance(acc, dict):
continue
rows.append([ rows.append([
acc.get("number") or "", acc.get("number") or "",
acc.get("currency") or "", acc.get("currency") or "",
acc.get("status") or "",
acc.get("bankName") or "", acc.get("bankName") or "",
]) ])
return {"headers": headers, "rows": rows} return {"headers": headers, "rows": rows}
@ -1364,27 +1389,6 @@ def _auth_headers():
return headers return headers
def _count_items(data, paginated):
"""Lightweight count for UI summary. Digs one level deep to find the list."""
if paginated and isinstance(data, list):
total = 0
for page in data:
if not isinstance(page, dict):
continue
for value in page.values():
if isinstance(value, list):
total += len(value)
break
return total
if isinstance(data, dict):
for value in data.values():
if isinstance(value, list):
return len(value)
if isinstance(data, list):
return len(data)
return 0
def _find_default_warehouse(company): def _find_default_warehouse(company):
"""Pick a sensible default warehouse for Stock Settings. ERPNext creates """Pick a sensible default warehouse for Stock Settings. ERPNext creates
'Stores' for every new company (is_group=0); under language=az the 'Stores' for every new company (is_group=0); under language=az the

View File

@ -10,7 +10,7 @@ frappe.provide("jey_wizard");
// Bump this string in every commit that changes wizard code. Displayed in the badge so // Bump this string in every commit that changes wizard code. Displayed in the badge so
// we can tell at a glance which version is actually running on a given machine. Kept in // we can tell at a glance which version is actually running on a given machine. Kept in
// sync with __version__ in jey_wizard/__init__.py. // sync with __version__ in jey_wizard/__init__.py.
const JEY_WIZARD_VERSION = "0.1.28"; const JEY_WIZARD_VERSION = "0.1.29";
// Wipe Frappe + ERPNext default slides so their `before_load`/`after_load` listeners // Wipe Frappe + ERPNext default slides so their `before_load`/`after_load` listeners
// don't try to mutate a wizard that isn't slide-based anymore. // don't try to mutate a wizard that isn't slide-based anymore.