Move existing accounting writebacks to SQLite .NET batches
This commit is contained in:
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
from datetime import date, datetime
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from urllib.parse import quote
|
||||
import hashlib
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Request
|
||||
@@ -635,7 +636,8 @@ def cash_payment_allocation(
|
||||
saved=False,
|
||||
command_error=error or "",
|
||||
allocation_plan=None,
|
||||
allocation_input={"total_amount": "", "party_name": "", "expense_ledger": "", "source_payments": ""},
|
||||
allocation_input={"total_amount": "", "party_name": "", "expense_ledger": "", "source_payments": "", "cash_ledger": "Cash"},
|
||||
posting_result=None,
|
||||
page_mode="allocation",
|
||||
)
|
||||
finally:
|
||||
@@ -762,8 +764,111 @@ async def cash_payment_allocation_plan(request: Request):
|
||||
"party_name": party_name,
|
||||
"expense_ledger": expense_ledger,
|
||||
"source_payments": source_payments,
|
||||
"cash_ledger": str(form.get("cash_ledger") or "Cash"),
|
||||
},
|
||||
posting_result=None,
|
||||
page_mode="allocation",
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@router.post("/allocation/post")
|
||||
async def cash_payment_allocation_post(request: Request):
|
||||
form = await request.form()
|
||||
validate_csrf(request, str(form.get("csrf_token") or ""))
|
||||
db = CommonSessionLocal()
|
||||
try:
|
||||
user, response = _require_partner(request, db, "accounting.tally.writeback")
|
||||
if response:
|
||||
return response
|
||||
clients, scope = _visible_clients(db, request, user)
|
||||
client_id = int(form.get("client_id") or 0)
|
||||
selected_client = next((row for row in clients if int(row.id) == client_id), None)
|
||||
if not selected_client:
|
||||
return _denied()
|
||||
setting = _setting(db, _tenant_id(request, user))
|
||||
total_amount_text = str(form.get("total_amount") or "")
|
||||
party_name = str(form.get("party_name") or "").strip()
|
||||
expense_ledger = str(form.get("expense_ledger") or "").strip()
|
||||
cash_ledger = str(form.get("cash_ledger") or "Cash").strip()
|
||||
source_payments = str(form.get("source_payments") or "")
|
||||
total_amount = _parse_money(total_amount_text, "Total cash payment")
|
||||
plan = _allocation_plan(source_payments, total_amount, float(setting.cash_limit), int(setting.max_allocation_days))
|
||||
if plan.get("status") != "ready":
|
||||
raise ValueError("The source-date allocation must pass validation before posting to Tally.")
|
||||
if not expense_ledger:
|
||||
raise ValueError("Expense / purchase ledger is required for Tally posting.")
|
||||
if not cash_ledger:
|
||||
raise ValueError("Cash ledger is required for Tally posting.")
|
||||
if str(form.get("confirm_posting") or "") != "yes" or str(form.get("confirm_text") or "").strip().upper() != "POST":
|
||||
raise ValueError("Confirm the reviewed allocation and type POST exactly before Tally write-back.")
|
||||
|
||||
first_date = date.fromisoformat(str(plan["entries"][0]["date"]))
|
||||
source_material = "|".join([str(client_id), expense_ledger, cash_ledger, party_name, source_payments, f"{total_amount:.2f}"])
|
||||
source_key = "CASHALLOC-" + hashlib.sha256(source_material.encode("utf-8")).hexdigest()[:20].upper()
|
||||
node = get_active_storage_node_for_branch(db, scope.tenant_id, scope.branch_id)
|
||||
if not node or not _node_online(node):
|
||||
raise ValueError("ERP Local Agent is offline for the active branch.")
|
||||
result = request_agent_command(
|
||||
node.node_code,
|
||||
"accounting_post_cash_allocation",
|
||||
{
|
||||
**_accounting_storage_payload(selected_client, _financial_year_for_date(first_date)),
|
||||
"client_id": int(selected_client.id),
|
||||
"source_key": source_key,
|
||||
"party_name": party_name,
|
||||
"debit_ledger": expense_ledger,
|
||||
"cash_ledger": cash_ledger,
|
||||
"entries": list(plan.get("entries") or []),
|
||||
"posted_by_user_id": int(user.id),
|
||||
"narration": f"Cash payment allocation · {party_name or expense_ledger}",
|
||||
},
|
||||
timeout_seconds=180,
|
||||
)
|
||||
if not result.get("ok"):
|
||||
raise ValueError(str(result.get("error") or "Cash allocation batch posting failed."))
|
||||
body = result.get("result") or {}
|
||||
return _render(
|
||||
request, db, user, title="Cash Payment Compliance Review",
|
||||
clients=clients, selected_client=selected_client, storage_node=node,
|
||||
agent_online=_node_online(node), live_result=None, selected_tally_guid="",
|
||||
date_from="", date_to="", setting=setting, review=None, cache_job=None,
|
||||
active_job_id="", saved=False, command_error="", allocation_plan=plan,
|
||||
allocation_input={
|
||||
"total_amount": total_amount_text, "party_name": party_name,
|
||||
"expense_ledger": expense_ledger, "source_payments": source_payments,
|
||||
"cash_ledger": cash_ledger,
|
||||
},
|
||||
posting_result=body, page_mode="allocation",
|
||||
)
|
||||
except Exception as exc:
|
||||
clients, scope = _visible_clients(db, request, user) if 'user' in locals() else ([], None)
|
||||
selected_client = next((row for row in clients if int(row.id) == int(form.get("client_id") or 0)), None) if clients else None
|
||||
setting = _setting(db, _tenant_id(request, user)) if 'user' in locals() else None
|
||||
plan = None
|
||||
try:
|
||||
total_amount_text = str(form.get("total_amount") or "")
|
||||
plan = _allocation_plan(str(form.get("source_payments") or ""), _parse_money(total_amount_text, "Total cash payment"), float(setting.cash_limit), int(setting.max_allocation_days)) if setting else None
|
||||
except Exception:
|
||||
pass
|
||||
if 'user' not in locals():
|
||||
raise
|
||||
node = get_active_storage_node_for_branch(db, scope.tenant_id, scope.branch_id) if scope else None
|
||||
return _render(
|
||||
request, db, user, title="Cash Payment Compliance Review", clients=clients,
|
||||
selected_client=selected_client, storage_node=node, agent_online=_node_online(node),
|
||||
live_result=None, selected_tally_guid="", date_from="", date_to="", setting=setting,
|
||||
review=None, cache_job=None, active_job_id="", saved=False, command_error=str(exc),
|
||||
allocation_plan=plan,
|
||||
allocation_input={
|
||||
"total_amount": str(form.get("total_amount") or ""),
|
||||
"party_name": str(form.get("party_name") or ""),
|
||||
"expense_ledger": str(form.get("expense_ledger") or ""),
|
||||
"source_payments": str(form.get("source_payments") or ""),
|
||||
"cash_ledger": str(form.get("cash_ledger") or "Cash"),
|
||||
},
|
||||
posting_result=None, page_mode="allocation",
|
||||
)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
Reference in New Issue
Block a user