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, ai_assist_one, internal_model_predict_one, ) 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"]