Link Accounting Mirror metadata and Tally paths to Client FY

This commit is contained in:
A R R R Associates
2026-09-20 14:59:14 +05:30
parent 827231a135
commit 0e12ccfe4e
8 changed files with 318 additions and 171 deletions
+72 -3
View File
@@ -24,6 +24,8 @@ from app.modules.documents.services import build_document_scope, get_active_stor
from app.modules.accounting.agent_bridge import request_agent_command
from app.modules.accounting.accounting_mirror_service import (
get_registered_mirror,
get_current_accounting_mirror,
registered_mirror_payload,
list_registered_mirrors,
list_accounting_mirror_versions,
next_accounting_mirror_version,
@@ -106,20 +108,87 @@ def _financial_year_options(reference_fy: str | None = None, years_back: int = 7
]
def _accounting_storage_payload(client, financial_year: str | None = None) -> dict:
"""Use the same FY + client-folder policy as existing Engagement storage."""
def _accounting_storage_payload(client, financial_year: str | None = None, *, db=None, tenant_id: int | None = None) -> dict:
"""Resolve the Accounting Mirror path for one Client/FY.
The deterministic FY/client path remains the backward-compatible fallback. If
a current mirror metadata row exists, its registered path/version becomes the
authoritative payload for every Accounting tool importing this helper. This
keeps old tools working while removing repeated mirror/path selection.
"""
fy_value = str(financial_year or _financial_year_for_date()).strip()
fy_folder = sanitize_segment(f"FY{fy_value}", "FY")
letter, client_folder = client_folder_parts(client, int(client.id))
relative_dir = Path(fy_folder) / "Clients" / letter / client_folder / "Accounting"
return {
fallback = {
"client_id": int(client.id),
"client_name": str(client.client_name or "").strip(),
"client_code": str(getattr(client, "client_code", "") or "").strip(),
"financial_year": fy_value,
"accounting_relative_dir": relative_dir.as_posix(),
"mirror_registered": False,
}
own_db = None
try:
session = db
if session is None:
own_db = CommonSessionLocal()
session = own_db
resolved_tenant_id = int(tenant_id or getattr(client, "tenant_id", 0) or 0)
if not resolved_tenant_id:
return fallback
resolved = registered_mirror_payload(
session,
tenant_id=resolved_tenant_id,
client_id=int(client.id),
financial_year=fy_value,
fallback=fallback,
)
if resolved.get("mirror_registered") and str(resolved.get("mirror_local_path") or "").strip():
return resolved
# Backfill mirrors created before the registry existed, and enrich older
# registry rows that pre-date mirror_local_path metadata. This is deliberately
# read-only on the workstation: the agent only lists canonical .act files;
# sync_discovered_mirrors creates missing metadata rows in PostgreSQL.
try:
branch_id = getattr(client, "branch_id", None)
node = get_active_storage_node_for_branch(session, resolved_tenant_id, branch_id)
if node and _node_online(node):
response = request_agent_command(
node.node_code,
"accounting_mirror_list",
{"client_id": int(client.id)},
timeout_seconds=8,
)
if response.get("ok"):
sync_discovered_mirrors(
session,
tenant_id=resolved_tenant_id,
client_id=int(client.id),
storage_node_id=int(node.id),
discovered=(response.get("result") or {}).get("mirrors") or [],
requested_by_user_id=None,
)
resolved = registered_mirror_payload(
session,
tenant_id=resolved_tenant_id,
client_id=int(client.id),
financial_year=fy_value,
fallback=fallback,
)
except Exception:
pass
return resolved
except Exception:
# Registry lookup must never break an existing Accounting tool; legacy
# deterministic path resolution remains available until backfill occurs.
return fallback
finally:
if own_db is not None:
own_db.close()
def _client_registrations(db, client, tenant_id: int):
rows = db.execute(