115 lines
3.2 KiB
Python
115 lines
3.2 KiB
Python
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
|