Files
2026-08-22 16:12:22 +05:30

202 lines
6.7 KiB
Python

from __future__ import annotations
from urllib.parse import urlencode
from fastapi import APIRouter, Form, Request
from fastapi.responses import RedirectResponse
from app.core.db.common import CommonSessionLocal
from app.core.security.csrf import get_or_create_csrf_token, validate_csrf
from app.core.templating import templates
from app.modules.accounting.purchase_posting_service import (
attempts_for_purchases,
preflight_choices,
queue_post,
queue_preflight,
sync_attempts,
visible_workstations,
)
from app.modules.accounting.purchase_review_service import review_queue
from app.modules.accounting.ui import _find_visible_client, _require_partner, _visible_clients
from app.modules.core.rbac.deps import get_user_permissions, get_user_roles
router = APIRouter(prefix="/tools/accounting/purchase-posting", tags=["accounting-purchase-posting-ui"])
def _redirect(client_id: int, *, message: str = "", error: str = "", page: int = 1, per_page: int = 25):
params = {"client_id": client_id, "page": page, "per_page": per_page}
if message:
params["message"] = message[:240]
if error:
params["error"] = error[:240]
return RedirectResponse(
url="/tools/accounting/purchase-posting?" + urlencode(params),
status_code=303,
)
@router.get("")
def page(
request: Request,
client_id: int | None = None,
page: int = 1,
per_page: int = 25,
message: str = "",
error: str = "",
):
db = CommonSessionLocal()
try:
user, response = _require_partner(request, db, "accounting.learning.view")
if response:
return response
clients, scope = _visible_clients(db, request, user)
selected = next((c for c in clients if client_id and int(c.id) == int(client_id)), None)
result = None
attempts = {}
choices = {}
workstations = []
if selected:
# Phase 10 intentionally starts only from reviewed purchases.
result = review_queue(
db,
tenant_id=scope.tenant_id,
client_id=selected.id,
status="reviewed",
page=page,
per_page=per_page,
)
ids = [row[0].id for row in result.rows]
attempts = attempts_for_purchases(
db, tenant_id=scope.tenant_id, client_id=selected.id, purchase_ids=ids
)
sync_attempts(db, list(attempts.values()))
attempts = attempts_for_purchases(
db, tenant_id=scope.tenant_id, client_id=selected.id, purchase_ids=ids
)
choices = {
pid: preflight_choices(attempt)
for pid, attempt in attempts.items()
if attempt.preflight_result_json
}
branch_id = getattr(user, "branch_id", None)
workstations = visible_workstations(
db, tenant_id=scope.tenant_id, branch_id=branch_id
)
return templates.TemplateResponse(
"modules/accounting/templates/accounting/purchase_posting.html",
{
"request": request,
"current_user": user,
"current_user_roles": get_user_roles(db, user.id),
"current_user_permissions": get_user_permissions(db, user.id),
"csrf_token": get_or_create_csrf_token(request),
"title": "Controlled Tally Purchase Posting",
"clients": clients,
"selected_client": selected,
"result": result,
"attempts": attempts,
"choices": choices,
"workstations": workstations,
"message": message,
"error": error,
},
)
finally:
db.close()
@router.post("/purchase/{purchase_id}/preflight")
def preflight(
request: Request,
purchase_id: int,
client_id: int = Form(...),
workstation_id: int = Form(...),
page: int = Form(1),
per_page: int = Form(25),
csrf_token: str = Form(...),
):
validate_csrf(request, csrf_token)
db = CommonSessionLocal()
try:
user, response = _require_partner(request, db, "accounting.learning.manage")
if response:
return response
client, _, scope = _find_visible_client(db, request, user, client_id)
if not client:
from app.core.http_responses import ui_access_denied
return ui_access_denied()
queue_preflight(
db,
tenant_id=scope.tenant_id,
client_id=client.id,
purchase_id=purchase_id,
workstation_id=workstation_id,
user_id=user.id,
)
return _redirect(
client.id,
message="Workstation preflight queued. Refresh this page after the agent processes the job.",
page=page,
per_page=per_page,
)
except Exception as exc:
db.rollback()
return _redirect(client_id, error=str(exc), page=page, per_page=per_page)
finally:
db.close()
@router.post("/purchase/{purchase_id}/post")
def post(
request: Request,
purchase_id: int,
client_id: int = Form(...),
party_ledger_name: str = Form(...),
input_igst_ledger_name: str = Form(""),
input_cgst_ledger_name: str = Form(""),
input_sgst_ledger_name: str = Form(""),
input_cess_ledger_name: str = Form(""),
page: int = Form(1),
per_page: int = Form(25),
csrf_token: str = Form(...),
):
validate_csrf(request, csrf_token)
db = CommonSessionLocal()
try:
user, response = _require_partner(request, db, "accounting.learning.manage")
if response:
return response
client, _, scope = _find_visible_client(db, request, user, client_id)
if not client:
from app.core.http_responses import ui_access_denied
return ui_access_denied()
queue_post(
db,
tenant_id=scope.tenant_id,
client_id=client.id,
purchase_id=purchase_id,
party_ledger_name=party_ledger_name,
input_igst_ledger_name=input_igst_ledger_name,
input_cgst_ledger_name=input_cgst_ledger_name,
input_sgst_ledger_name=input_sgst_ledger_name,
input_cess_ledger_name=input_cess_ledger_name,
user_id=user.id,
)
return _redirect(
client.id,
message="Controlled Purchase voucher job queued. Refresh to see the workstation/Tally result.",
page=page,
per_page=per_page,
)
except Exception as exc:
db.rollback()
return _redirect(client_id, error=str(exc), page=page, per_page=per_page)
finally:
db.close()