Enrich ERP and Tally GSTIN display

This commit is contained in:
A R R R Associates
2026-08-20 21:24:35 +05:30
parent de8b32b06d
commit fa0f681106
6 changed files with 117 additions and 8 deletions
@@ -261,7 +261,9 @@
<th class="px-4 py-3 text-left font-semibold text-slate-600">ERP Scope</th>
<th class="px-4 py-3 text-left font-semibold text-slate-600">Tally Company</th>
<th class="px-4 py-3 text-left font-semibold text-slate-600">GUID</th>
<th class="px-4 py-3 text-left font-semibold text-slate-600">GSTIN</th>
<th class="px-4 py-3 text-left font-semibold text-slate-600">ERP GSTIN</th>
<th class="px-4 py-3 text-left font-semibold text-slate-600">Tally GSTIN</th>
<th class="px-4 py-3 text-left font-semibold text-slate-600">GSTIN Match</th>
<th class="px-4 py-3 text-left font-semibold text-slate-600">Loaded</th>
<th class="px-4 py-3 text-right font-semibold text-slate-600">Action</th>
</tr>
@@ -280,7 +282,25 @@
</td>
<td class="px-4 py-3 font-medium text-slate-900">{{ mapping.company_name }}</td>
<td class="px-4 py-3 font-mono text-xs text-slate-600">{{ mapping.tally_guid }}</td>
<td class="px-4 py-3 text-slate-600">{{ mapping.gstin or '-' }}</td>
<td class="px-4 py-3 text-slate-600">
{% if mapping.erp_gstins %}
{% for gstin in mapping.erp_gstins %}<div class="font-mono text-xs">{{ gstin }}</div>{% endfor %}
{% else %}-{% endif %}
</td>
<td class="px-4 py-3 font-mono text-xs text-slate-600">{{ mapping.tally_gstin_display or '-' }}</td>
<td class="px-4 py-3">
{% if mapping.gstin_match_status == 'matched' %}
<span class="rounded-full bg-emerald-50 px-2 py-1 text-xs font-semibold text-emerald-700">Matched</span>
{% elif mapping.gstin_match_status == 'mismatch' %}
<span class="rounded-full bg-red-50 px-2 py-1 text-xs font-semibold text-red-700">Mismatch</span>
{% elif mapping.gstin_match_status == 'tally_missing' %}
<span class="rounded-full bg-amber-50 px-2 py-1 text-xs font-semibold text-amber-700">Tally GSTIN unavailable</span>
{% elif mapping.gstin_match_status == 'erp_missing' %}
<span class="rounded-full bg-amber-50 px-2 py-1 text-xs font-semibold text-amber-700">ERP GSTIN unavailable</span>
{% else %}
<span class="rounded-full bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-600">Unavailable</span>
{% endif %}
</td>
<td class="px-4 py-3">
<span class="rounded-full px-2 py-1 text-xs font-semibold {% if mapping.currently_loaded %}bg-emerald-50 text-emerald-700{% else %}bg-slate-100 text-slate-600{% endif %}">
{{ 'Yes' if mapping.currently_loaded else 'No' }}
@@ -314,7 +334,7 @@
{% if tally.companies %}
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-slate-200 text-sm">
<thead class="bg-slate-50"><tr><th class="px-4 py-3 text-left font-semibold text-slate-600">Company</th><th class="px-4 py-3 text-left font-semibold text-slate-600">GUID</th><th class="px-4 py-3 text-left font-semibold text-slate-600">GSTIN</th></tr></thead>
<thead class="bg-slate-50"><tr><th class="px-4 py-3 text-left font-semibold text-slate-600">Company</th><th class="px-4 py-3 text-left font-semibold text-slate-600">GUID</th><th class="px-4 py-3 text-left font-semibold text-slate-600">Tally GSTIN</th></tr></thead>
<tbody class="divide-y divide-slate-100">
{% for company in tally.companies %}
<tr>
+55
View File
@@ -2,6 +2,7 @@ from __future__ import annotations
from datetime import date, datetime, timezone
from pathlib import Path
import re
from urllib.parse import quote
from fastapi import APIRouter, Form, Request
@@ -104,6 +105,59 @@ def _registration_payload(registration, registration_type) -> dict:
}
def _normalise_gstin(value: str) -> str:
return re.sub(r"\s+", "", str(value or "").strip().upper())
def _erp_client_gstins(registrations) -> list[str]:
values: list[str] = []
for registration, registration_type in registrations or []:
if str(getattr(registration_type, "code", "") or "").strip().upper() != "GSTIN":
continue
number = _normalise_gstin(getattr(registration, "registration_number", ""))
if number and number not in values:
values.append(number)
return values
def _enrich_gstin_display(live_result: dict | None, registrations) -> None:
"""Attach ERP/Tally GSTIN display metadata without mutating stored mappings."""
if not isinstance(live_result, dict):
return
accounting = live_result.get("accounting") or {}
tally = live_result.get("tally") or {}
mappings = accounting.get("mappings") or []
companies = tally.get("companies") or []
client_gstins = _erp_client_gstins(registrations)
company_by_guid = {str(row.get("guid") or "").strip(): row for row in companies if isinstance(row, dict)}
for mapping in mappings:
if not isinstance(mapping, dict):
continue
registration_type = str(mapping.get("registration_type_code") or "").strip().upper()
registration_number = _normalise_gstin(mapping.get("registration_number"))
if mapping.get("registration_id") and registration_type == "GSTIN" and registration_number:
erp_gstins = [registration_number]
elif not mapping.get("registration_id"):
erp_gstins = list(client_gstins)
else:
erp_gstins = []
live_company = company_by_guid.get(str(mapping.get("tally_guid") or "").strip()) or {}
tally_gstin = _normalise_gstin(live_company.get("gstin") or mapping.get("gstin"))
mapping["erp_gstins"] = erp_gstins
mapping["tally_gstin_display"] = tally_gstin
if erp_gstins and tally_gstin:
mapping["gstin_match_status"] = "matched" if tally_gstin in erp_gstins else "mismatch"
elif erp_gstins and not tally_gstin:
mapping["gstin_match_status"] = "tally_missing"
elif tally_gstin and not erp_gstins:
mapping["gstin_match_status"] = "erp_missing"
else:
mapping["gstin_match_status"] = "unavailable"
def _node_online(node) -> bool:
if not node or not node.last_seen_at_utc:
return False
@@ -175,6 +229,7 @@ def tally_tool(
)
if response_data.get("ok"):
live_result = response_data.get("result") or {}
_enrich_gstin_display(live_result, registrations)
else:
command_error = str(response_data.get("error") or "Local agent command failed.")
except Exception as exc: