178 lines
6.4 KiB
Python
178 lines
6.4 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.historical_learning_service import active_natures, ledger_mappings
|
|
from app.modules.accounting.ledger_learning_service import (
|
|
available_tally_guids,
|
|
learning_summary,
|
|
rank_suggestions,
|
|
recent_events,
|
|
record_review,
|
|
)
|
|
from app.modules.accounting.ui import _find_visible_client, _require_partner, _visible_clients
|
|
from app.modules.clients.business_profile_service import get_profile
|
|
from app.modules.core.rbac.deps import get_user_permissions, get_user_roles
|
|
|
|
router = APIRouter(prefix="/tools/accounting/ledger-learning", tags=["accounting-ledger-learning-ui"])
|
|
|
|
|
|
def _float_or_none(value):
|
|
try:
|
|
return float(value) if str(value or "").strip() else None
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
@router.get("")
|
|
def ledger_learning(
|
|
request: Request,
|
|
client_id: int | None = None,
|
|
tally_guid: str = "",
|
|
supplier_name: str = "",
|
|
supplier_gstin: str = "",
|
|
hsn_code: str = "",
|
|
description: str = "",
|
|
amount: str = "",
|
|
reviewed: int = 0,
|
|
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)
|
|
suggestions = []
|
|
mappings = []
|
|
tally_companies = []
|
|
profile = None
|
|
summary = []
|
|
events = []
|
|
|
|
if selected:
|
|
profile = get_profile(db, selected.id)
|
|
mappings = ledger_mappings(db, scope.tenant_id, selected.id, tally_guid)
|
|
tally_companies = available_tally_guids(db, scope.tenant_id, selected.id)
|
|
summary = learning_summary(db, scope.tenant_id, selected.id)
|
|
events = recent_events(db, scope.tenant_id, selected.id, 20)
|
|
if any([supplier_name.strip(), supplier_gstin.strip(), hsn_code.strip(), description.strip()]):
|
|
suggestions = rank_suggestions(
|
|
db,
|
|
tenant_id=scope.tenant_id,
|
|
client_id=selected.id,
|
|
tally_guid=tally_guid.strip(),
|
|
supplier_name=supplier_name,
|
|
supplier_gstin=supplier_gstin,
|
|
hsn_code=hsn_code,
|
|
description=description,
|
|
amount=_float_or_none(amount),
|
|
)
|
|
|
|
return templates.TemplateResponse("modules/accounting/templates/accounting/ledger_learning.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": "Self-improving Ledger Selection",
|
|
"clients": clients,
|
|
"selected_client": selected,
|
|
"profile": profile,
|
|
"tally_companies": tally_companies,
|
|
"mappings": mappings,
|
|
"natures": active_natures(db, scope.tenant_id),
|
|
"suggestions": suggestions,
|
|
"learning_summary": summary,
|
|
"events": events,
|
|
"query": {
|
|
"tally_guid": tally_guid,
|
|
"supplier_name": supplier_name,
|
|
"supplier_gstin": supplier_gstin,
|
|
"hsn_code": hsn_code,
|
|
"description": description,
|
|
"amount": amount,
|
|
},
|
|
"reviewed": bool(reviewed),
|
|
"error": error,
|
|
})
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@router.post("/review")
|
|
def review(
|
|
request: Request,
|
|
client_id: int = Form(...),
|
|
tally_guid: str = Form(""),
|
|
supplier_name: str = Form(""),
|
|
supplier_gstin: str = Form(""),
|
|
hsn_code: str = Form(""),
|
|
description: str = Form(""),
|
|
amount: str = Form(""),
|
|
suggested_nature_id: str = Form(""),
|
|
suggested_ledger_name: str = Form(""),
|
|
suggested_confidence: int = Form(0),
|
|
final_nature_id: int = Form(...),
|
|
final_ledger_name: str = Form(""),
|
|
explanation: str = Form(""),
|
|
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()
|
|
|
|
suggested_id = int(suggested_nature_id) if str(suggested_nature_id or "").isdigit() else None
|
|
explanations = [x.strip() for x in str(explanation or "").split("||") if x.strip()]
|
|
record_review(
|
|
db,
|
|
tenant_id=scope.tenant_id,
|
|
client_id=client.id,
|
|
tally_guid=tally_guid.strip(),
|
|
supplier_name=supplier_name,
|
|
supplier_gstin=supplier_gstin,
|
|
hsn_code=hsn_code,
|
|
description=description,
|
|
amount=_float_or_none(amount),
|
|
suggested_nature_id=suggested_id,
|
|
suggested_ledger_name=suggested_ledger_name,
|
|
suggested_confidence=suggested_confidence,
|
|
final_nature_id=final_nature_id,
|
|
final_ledger_name=final_ledger_name,
|
|
user_id=user.id,
|
|
explanation=explanations,
|
|
)
|
|
|
|
params = {
|
|
"client_id": client_id,
|
|
"tally_guid": tally_guid,
|
|
"supplier_name": supplier_name,
|
|
"supplier_gstin": supplier_gstin,
|
|
"hsn_code": hsn_code,
|
|
"description": description,
|
|
"amount": amount,
|
|
"reviewed": 1,
|
|
}
|
|
return RedirectResponse(url="/tools/accounting/ledger-learning?" + urlencode(params), status_code=303)
|
|
except Exception as exc:
|
|
db.rollback()
|
|
params = {"client_id": client_id, "error": str(exc)[:120]}
|
|
return RedirectResponse(url="/tools/accounting/ledger-learning?" + urlencode(params), status_code=303)
|
|
finally:
|
|
db.close()
|