Add Phase 10 controlled Tally purchase posting
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user