Fix live Purchase and Sales historical Tally sync
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
from sqlalchemy import select
|
||||
|
||||
from app.modules.documents.models import ERPWorkstationAgent
|
||||
|
||||
|
||||
ONLINE_WINDOW = timedelta(minutes=5)
|
||||
|
||||
|
||||
def workstation_companies(workstation) -> list[dict]:
|
||||
try:
|
||||
rows = json.loads(workstation.tally_companies_json or "[]")
|
||||
except Exception:
|
||||
rows = []
|
||||
if not isinstance(rows, list):
|
||||
return []
|
||||
|
||||
result = []
|
||||
seen = set()
|
||||
for row in rows:
|
||||
if not isinstance(row, dict):
|
||||
continue
|
||||
guid = str(row.get("guid") or "").strip()
|
||||
name = str(row.get("name") or "").strip()
|
||||
if not guid or guid in seen:
|
||||
continue
|
||||
seen.add(guid)
|
||||
result.append(
|
||||
{
|
||||
"guid": guid,
|
||||
"name": name,
|
||||
"gstin": str(row.get("gstin") or "").strip(),
|
||||
"gstins": list(row.get("gstins") or []),
|
||||
}
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def workstation_is_recent(workstation) -> bool:
|
||||
seen = getattr(workstation, "last_seen_at_utc", None)
|
||||
if not seen:
|
||||
return False
|
||||
if seen.tzinfo is None:
|
||||
seen = seen.replace(tzinfo=timezone.utc)
|
||||
return datetime.now(timezone.utc) - seen <= ONLINE_WINDOW
|
||||
|
||||
|
||||
def eligible_tally_workstations(db, tenant_id: int, branch_id: int | None = None):
|
||||
stmt = select(ERPWorkstationAgent).where(
|
||||
ERPWorkstationAgent.tenant_id == int(tenant_id),
|
||||
ERPWorkstationAgent.is_active.is_(True),
|
||||
ERPWorkstationAgent.tally_connected.is_(True),
|
||||
)
|
||||
if branch_id is not None:
|
||||
stmt = stmt.where(ERPWorkstationAgent.branch_id == int(branch_id))
|
||||
|
||||
rows = list(
|
||||
db.execute(
|
||||
stmt.order_by(
|
||||
ERPWorkstationAgent.last_seen_at_utc.desc(),
|
||||
ERPWorkstationAgent.machine_name.asc(),
|
||||
)
|
||||
).scalars().all()
|
||||
)
|
||||
return [
|
||||
row
|
||||
for row in rows
|
||||
if workstation_is_recent(row) and workstation_companies(row)
|
||||
]
|
||||
|
||||
|
||||
def resolve_workstation_company(
|
||||
db,
|
||||
*,
|
||||
workstation_id: int,
|
||||
tenant_id: int,
|
||||
branch_id: int | None,
|
||||
tally_guid: str,
|
||||
):
|
||||
workstation = db.get(ERPWorkstationAgent, int(workstation_id))
|
||||
if (
|
||||
not workstation
|
||||
or int(workstation.tenant_id) != int(tenant_id)
|
||||
or not workstation.is_active
|
||||
or not workstation.tally_connected
|
||||
or not workstation_is_recent(workstation)
|
||||
or (
|
||||
branch_id is not None
|
||||
and int(workstation.branch_id or 0) != int(branch_id)
|
||||
)
|
||||
):
|
||||
raise ValueError(
|
||||
"Selected ERP Local Agent workstation is offline, stale, or not connected to Tally."
|
||||
)
|
||||
|
||||
guid = str(tally_guid or "").strip()
|
||||
company = next(
|
||||
(
|
||||
row
|
||||
for row in workstation_companies(workstation)
|
||||
if str(row.get("guid") or "").strip() == guid
|
||||
),
|
||||
None,
|
||||
)
|
||||
if not company:
|
||||
raise ValueError(
|
||||
"Selected Tally company is not currently advertised by this workstation. "
|
||||
"Open the company in TallyPrime and allow the Local Agent to refresh."
|
||||
)
|
||||
return workstation, company
|
||||
Reference in New Issue
Block a user