Fix GST reconciliation server imports and Docker healthcheck

This commit is contained in:
A R R R Associates
2026-09-10 22:32:25 +05:30
parent 3b3b9c5ebe
commit 2ad8e44312
2 changed files with 117 additions and 2 deletions
+3 -1
View File
@@ -24,4 +24,6 @@ RUN mkdir -p /app/data/storage /app/data/year_backups
EXPOSE 8000 EXPOSE 8000
CMD ["sh", "-c", "alembic upgrade head && uvicorn app.main:app --host 0.0.0.0 --port 8000"] HEALTHCHECK --interval=10s --timeout=5s --start-period=10s --retries=10 CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/', timeout=3)"
CMD ["sh", "-c", "alembic upgrade head && uvicorn app.main:app --host 0.0.0.0 --port 8000"]
+114 -1
View File
@@ -27,9 +27,122 @@ from app.modules.credential_vault.crypto import decrypt_value
from app.modules.credential_vault.models import CredentialVaultEntry from app.modules.credential_vault.models import CredentialVaultEntry
from app.modules.credential_vault.service import can_view_entry, log_access from app.modules.credential_vault.service import can_view_entry, log_access
from app.modules.documents.services import client_folder_parts, get_active_storage_node_for_branch, sanitize_segment from app.modules.documents.services import client_folder_parts, get_active_storage_node_for_branch, sanitize_segment
from app.modules.documents.local_agent_runtime.erp_local_agent.gst_portal_runtime import extract_invoice_rows, extract_gstr3b_itc
from app.modules.registrations.models import ClientRegistration, RegistrationType from app.modules.registrations.models import ClientRegistration, RegistrationType
def _gst_parse_date(value: str) -> str:
text = str(value or "").strip()
for fmt in ("%d-%m-%Y", "%d/%m/%Y", "%Y-%m-%d", "%d-%b-%Y", "%d/%m/%y"):
try:
return datetime.strptime(text, fmt).date().isoformat()
except Exception:
pass
return text
def _gst_float(value) -> float:
try:
return float(value or 0)
except Exception:
return 0.0
def _gst_doc_key(value: str) -> str:
return "".join(ch for ch in str(value or "").upper() if ch.isalnum())
def _gst_walk(obj):
if isinstance(obj, dict):
yield obj
for value in obj.values():
yield from _gst_walk(value)
elif isinstance(obj, list):
for value in obj:
yield from _gst_walk(value)
def _gst_tax_from_invoice(inv: dict) -> dict[str, float]:
out = {"taxable": 0.0, "igst": 0.0, "cgst": 0.0, "sgst": 0.0, "cess": 0.0}
for item in inv.get("itms") or inv.get("items") or []:
detail = item.get("itm_det") or item.get("item_det") or item
out["taxable"] += _gst_float(detail.get("txval"))
out["igst"] += _gst_float(detail.get("iamt"))
out["cgst"] += _gst_float(detail.get("camt"))
out["sgst"] += _gst_float(detail.get("samt"))
out["cess"] += _gst_float(detail.get("csamt"))
if not any(out.values()):
out["taxable"] = _gst_float(inv.get("txval") or inv.get("taxable_value"))
out["igst"] = _gst_float(inv.get("iamt") or inv.get("igst"))
out["cgst"] = _gst_float(inv.get("camt") or inv.get("cgst"))
out["sgst"] = _gst_float(inv.get("samt") or inv.get("sgst"))
out["cess"] = _gst_float(inv.get("csamt") or inv.get("cess"))
return out
def extract_invoice_rows(data) -> list[dict]:
"""Normalize invoice-like rows from GST JSON without importing workstation runtime code."""
rows: list[dict] = []
seen: set[tuple[str, str, str]] = set()
for obj in _gst_walk(data):
invno = str(
obj.get("inum")
or obj.get("inv_num")
or obj.get("doc_no")
or obj.get("invoiceNumber")
or ""
).strip()
if not invno:
continue
gstin = str(
obj.get("ctin")
or obj.get("stin")
or obj.get("supplier_gstin")
or obj.get("recipientGstin")
or ""
).strip().upper()
invoice_date = _gst_parse_date(
str(
obj.get("idt")
or obj.get("inv_date")
or obj.get("doc_date")
or obj.get("invoiceDate")
or ""
)
)
key = (gstin, _gst_doc_key(invno), invoice_date)
if key in seen:
continue
seen.add(key)
tax = _gst_tax_from_invoice(obj)
rows.append(
{
"gstin": gstin,
"invoice_no": invno,
"invoice_key": _gst_doc_key(invno),
"invoice_date": invoice_date,
"invoice_value": _gst_float(
obj.get("val") or obj.get("invoice_value") or obj.get("invoiceValue")
),
**tax,
}
)
return rows
def extract_gstr3b_itc(data) -> dict[str, float]:
"""Normalize GSTR-3B eligible-ITC totals from uploaded portal JSON."""
totals = {"igst": 0.0, "cgst": 0.0, "sgst": 0.0, "cess": 0.0}
if not isinstance(data, dict):
return totals
itc = data.get("itc_elg") or {}
for item in itc.get("itc_avl") or []:
totals["igst"] += _gst_float(item.get("iamt"))
totals["cgst"] += _gst_float(item.get("camt"))
totals["sgst"] += _gst_float(item.get("samt"))
totals["cess"] += _gst_float(item.get("csamt"))
return totals
router = APIRouter(prefix="/tools/accounting/gst-reconciliation", tags=["accounting-gst-reconciliation-ui"]) router = APIRouter(prefix="/tools/accounting/gst-reconciliation", tags=["accounting-gst-reconciliation-ui"])
_TOKEN_PURPOSE = "gst_operator_browser_v1" _TOKEN_PURPOSE = "gst_operator_browser_v1"
_TOKEN_MINUTES = 30 _TOKEN_MINUTES = 30