Unify accounting analysis on Client FY mirror registry

This commit is contained in:
A R R R Associates
2026-09-20 16:36:14 +05:30
parent 24b13e5d6e
commit c63fc2773c
11 changed files with 103 additions and 45 deletions
+39 -6
View File
@@ -190,6 +190,39 @@ def _accounting_storage_payload(client, financial_year: str | None = None, *, db
own_db.close()
def _analysis_accounting_storage_payload(
client,
financial_year: str | None = None,
*,
db=None,
tenant_id: int | None = None,
) -> dict:
"""Return the authoritative registered Client/FY mirror payload for analysis jobs.
Analytical procedures must never silently fall back to a deterministic folder.
The fallback remains available to bootstrap/sync/write workflows through
``_accounting_storage_payload``. Requiring a registry row here guarantees that
every analysis is tied to the same Client/FY/version selected by
``AccountingMirrorRegistry``.
"""
payload = _accounting_storage_payload(
client,
financial_year,
db=db,
tenant_id=tenant_id,
)
if not bool(payload.get("mirror_registered")):
fy_value = str(payload.get("financial_year") or financial_year or "").strip()
raise ValueError(
f"No registered Accounting Mirror is available for FY {fy_value}. "
"Create or register the Client/FY mirror from Tally Accounting first."
)
if not str(payload.get("accounting_relative_dir") or "").strip():
raise ValueError("The registered Accounting Mirror has no storage path.")
payload["analysis_source"] = "accounting_mirror_registry"
return payload
def _client_registrations(db, client, tenant_id: int):
rows = db.execute(
select(ClientRegistration, RegistrationType)
@@ -535,7 +568,7 @@ def download_analysis_report(
node.node_code,
"accounting_analysis_get",
{
**_accounting_storage_payload(client, financial_year),
**_analysis_accounting_storage_payload(client, financial_year, db=db, tenant_id=scope.tenant_id),
"run_id": int(analysis_run_id),
},
timeout_seconds=30,
@@ -1109,7 +1142,7 @@ def depreciation_it_tool(
if selected_client and node and online:
selected_fy=_financial_year_for_date(date.fromisoformat(start_text))
storage_payload=_accounting_storage_payload(selected_client,selected_fy)
storage_payload=_analysis_accounting_storage_payload(selected_client,selected_fy,db=db,tenant_id=scope.tenant_id)
try:
status_response=request_agent_command(
node.node_code,"accounting_mirror_status",storage_payload,timeout_seconds=20
@@ -1124,7 +1157,7 @@ def depreciation_it_tool(
prior_start=date(prior_start_year,4,1)
prior_end=date(prior_start_year+1,3,31)
prior_fy=_financial_year_for_date(prior_start)
prior_payload=_accounting_storage_payload(selected_client,prior_fy)
prior_payload=_analysis_accounting_storage_payload(selected_client,prior_fy,db=db,tenant_id=scope.tenant_id)
prior_response=request_agent_command(
node.node_code,"accounting_mirror_status",prior_payload,timeout_seconds=20
)
@@ -1222,7 +1255,7 @@ async def calculate_it_depreciation(request: Request):
node=get_active_storage_node_for_branch(db,scope.tenant_id,scope.branch_id)
if not node or not _node_online(node): return RedirectResponse(url=f"/tools/tally/depreciation?client_id={client.id}&fy_start={start.isoformat()}&fy_end={end.isoformat()}&error={quote('ERP Local Agent is offline for the active branch.')}",status_code=303)
try:
result=request_agent_command(node.node_code,"accounting_calculate_it_depreciation",{**_accounting_storage_payload(client, _financial_year_for_date(start)),"fy_start":start.isoformat(),"fy_end":end.isoformat(),"assignments":assignments,"depreciation_expense_ledger":str(form.get("depreciation_expense_ledger") or ""),"depreciation_reserve_ledger":str(form.get("depreciation_reserve_ledger") or ""),"financial_year":_financial_year_for_date(start),"requested_by_user_id":int(user.id)},timeout_seconds=120)
result=request_agent_command(node.node_code,"accounting_calculate_it_depreciation",{**_analysis_accounting_storage_payload(client, _financial_year_for_date(start), db=db, tenant_id=scope.tenant_id),"fy_start":start.isoformat(),"fy_end":end.isoformat(),"assignments":assignments,"depreciation_expense_ledger":str(form.get("depreciation_expense_ledger") or ""),"depreciation_reserve_ledger":str(form.get("depreciation_reserve_ledger") or ""),"financial_year":_financial_year_for_date(start),"requested_by_user_id":int(user.id)},timeout_seconds=120)
if not result.get("ok"): raise RuntimeError(str(result.get("error") or "Income-tax depreciation calculation failed."))
dep=(result.get("result") or {}).get("depreciation") or {}; rid=int(dep.get("run_id"))
except Exception as exc:
@@ -1255,7 +1288,7 @@ async def approve_it_depreciation(request: Request):
try:
result = request_agent_command(
node.node_code, "accounting_approve_it_depreciation",
{**_accounting_storage_payload(client, _financial_year_for_date(approval_fy_start)), "run_id": run_id, "approved_by_user_id": int(user.id), "approval_note": str(form.get("approval_note") or "")},
{**_analysis_accounting_storage_payload(client, _financial_year_for_date(approval_fy_start), db=db, tenant_id=scope.tenant_id), "run_id": run_id, "approved_by_user_id": int(user.id), "approval_note": str(form.get("approval_note") or "")},
timeout_seconds=30,
)
if not result.get("ok"): raise RuntimeError(str(result.get("error") or "Approval failed."))
@@ -1293,7 +1326,7 @@ async def post_it_depreciation_to_tally(request: Request):
try:
result = request_agent_command(
node.node_code, "accounting_post_it_depreciation",
{**_accounting_storage_payload(client, _financial_year_for_date(posting_fy_start)), "run_id": run_id, "posted_by_user_id": int(user.id)},
{**_analysis_accounting_storage_payload(client, _financial_year_for_date(posting_fy_start), db=db, tenant_id=scope.tenant_id), "run_id": run_id, "posted_by_user_id": int(user.id)},
timeout_seconds=120,
)
if not result.get("ok"): raise RuntimeError(str(result.get("error") or "Tally write-back failed."))