Add Phase 9 purchase review board
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
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.historical_learning_service import active_natures
|
||||
from app.modules.accounting.ledger_learning_service import available_tally_guids
|
||||
from app.modules.accounting.purchase_review_service import (
|
||||
review_queue, review_counts, return_periods, nature_maps_for_rows,
|
||||
explanations_for_rows, mappings_by_nature, review_one, bulk_confirm,
|
||||
)
|
||||
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-review",tags=["accounting-purchase-review-ui"])
|
||||
|
||||
|
||||
def _qs(client_id, filters, page):
|
||||
params={"client_id":client_id,"tally_guid":filters.get("tally_guid", ""),"status":filters.get("status","all"),"confidence":filters.get("confidence","all"),"source":filters.get("source","all"),"supplier":filters.get("supplier",""),"return_period":filters.get("return_period",""),"per_page":filters.get("per_page",25),"page":page}
|
||||
return "/tools/accounting/purchase-review?"+urlencode(params)
|
||||
|
||||
|
||||
def _redirect(client_id, filters, *, message="", error="", page=None):
|
||||
params=dict(filters); params["client_id"]=client_id; params["page"]=page or filters.get("page",1)
|
||||
if message: params["message"]=message[:220]
|
||||
if error: params["error"]=error[:220]
|
||||
return RedirectResponse(url="/tools/accounting/purchase-review?"+urlencode(params),status_code=303)
|
||||
|
||||
|
||||
@router.get("")
|
||||
def board(request:Request,client_id:int|None=None,tally_guid:str="",status:str="all",confidence:str="all",source:str="all",supplier:str="",return_period:str="",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=counts=None; natures=[]; nature_by_id={}; mappings={}; explanations={}; periods=[]; companies=[]; prev_url=next_url=""
|
||||
filters={"tally_guid":tally_guid,"status":status,"confidence":confidence,"source":source,"supplier":supplier,"return_period":return_period,"per_page":per_page,"page":page}
|
||||
if selected:
|
||||
result=review_queue(db,tenant_id=scope.tenant_id,client_id=selected.id,tally_guid=tally_guid,status=status,confidence=confidence,source=source,supplier=supplier,return_period=return_period,page=page,per_page=per_page)
|
||||
counts=review_counts(db,tenant_id=scope.tenant_id,client_id=selected.id)
|
||||
natures=active_natures(db,scope.tenant_id)
|
||||
nature_by_id=nature_maps_for_rows(db,result["rows"])
|
||||
mappings=mappings_by_nature(db,scope.tenant_id,selected.id,tally_guid)
|
||||
explanations=explanations_for_rows(result["rows"])
|
||||
periods=return_periods(db,tenant_id=scope.tenant_id,client_id=selected.id)
|
||||
companies=available_tally_guids(db,scope.tenant_id,selected.id)
|
||||
if result["page"]>1:prev_url=_qs(selected.id,filters,result["page"]-1)
|
||||
if result["page"]<result["pages"]:next_url=_qs(selected.id,filters,result["page"]+1)
|
||||
return templates.TemplateResponse("modules/accounting/templates/accounting/purchase_review.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":"Purchase Review Board","clients":clients,"selected_client":selected,"result":result,"counts":counts,"natures":natures,"nature_by_id":nature_by_id,"mappings_by_nature":mappings,"explanations":explanations,"return_periods":periods,"tally_companies":companies,"filters":filters,"prev_url":prev_url,"next_url":next_url,"message":message,"error":error})
|
||||
finally:db.close()
|
||||
|
||||
|
||||
@router.post("/purchase/{purchase_id}/review")
|
||||
def review_purchase_row(request:Request,purchase_id:int,client_id:int=Form(...),final_nature_id:int=Form(...),final_ledger_name:str=Form(""),tally_guid:str=Form(""),status:str=Form("all"),confidence:str=Form("all"),source:str=Form("all"),supplier:str=Form(""),return_period:str=Form(""),page:int=Form(1),per_page:int=Form(25),csrf_token:str=Form(...)):
|
||||
validate_csrf(request,csrf_token); db=CommonSessionLocal(); filters={"tally_guid":tally_guid,"status":status,"confidence":confidence,"source":source,"supplier":supplier,"return_period":return_period,"page":page,"per_page":per_page}
|
||||
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()
|
||||
row=review_one(db,tenant_id=scope.tenant_id,client_id=client.id,purchase_id=purchase_id,final_nature_id=final_nature_id,final_ledger_name=final_ledger_name,user_id=user.id)
|
||||
return _redirect(client.id,filters,message=f"Invoice {row.invoice_number} reviewed.")
|
||||
except Exception as exc:
|
||||
db.rollback(); return _redirect(client_id,filters,error=str(exc))
|
||||
finally:db.close()
|
||||
|
||||
|
||||
@router.post("/bulk-confirm")
|
||||
async def bulk_confirm_rows(request:Request):
|
||||
form=await request.form(); validate_csrf(request,str(form.get("csrf_token") or ""))
|
||||
client_id=int(form.get("client_id") or 0); filters={"tally_guid":str(form.get("tally_guid") or ""),"status":str(form.get("status") or "all"),"confidence":str(form.get("confidence") or "all"),"source":str(form.get("source") or "all"),"supplier":str(form.get("supplier") or ""),"return_period":str(form.get("return_period") or ""),"page":int(form.get("page") or 1),"per_page":int(form.get("per_page") or 25)}
|
||||
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()
|
||||
count=bulk_confirm(db,tenant_id=scope.tenant_id,client_id=client.id,purchase_ids=form.getlist("purchase_ids"),user_id=user.id)
|
||||
return _redirect(client.id,filters,message=f"Confirmed {count} high-confidence purchase(s).")
|
||||
except Exception as exc:
|
||||
db.rollback(); return _redirect(client_id,filters,error=str(exc))
|
||||
finally:db.close()
|
||||
Reference in New Issue
Block a user