Link Accounting Mirror metadata and Tally paths to Client FY
This commit is contained in:
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user