bmc_hub/app/modules/also/backend/router.py
2026-07-03 20:20:22 +02:00

191 lines
5.8 KiB
Python

from typing import Optional
from fastapi import APIRouter, File, Query, Request, UploadFile
from app.modules.also.backend.service import also_service
from app.modules.also.models.schemas import (
AlsoApproveResult,
AlsoCompanyMappingUpsert,
AlsoDifferenceItem,
AlsoDashboardSummaryResponse,
AlsoImportJobCreate,
AlsoImportJobResponse,
AlsoImportLinesRequest,
AlsoManualCompanyMapRequest,
AlsoManualProductMapRequest,
AlsoProductMappingUpsert,
AlsoQueueApproveRequest,
AlsoQueueLineResponse,
AlsoQueueProcessRequest,
AlsoQueueProcessResult,
)
router = APIRouter()
def _user_id_from_request(request: Request) -> Optional[int]:
raw_user_id = getattr(request.state, "user_id", None)
if raw_user_id is None:
return None
try:
return int(raw_user_id)
except (TypeError, ValueError):
return None
@router.get("/also/config")
async def also_config() -> dict:
return also_service.get_config()
@router.post("/also/import-jobs", response_model=AlsoImportJobResponse)
async def create_import_job(payload: AlsoImportJobCreate, request: Request):
return also_service.create_import_job(payload, _user_id_from_request(request))
@router.get("/also/import-jobs", response_model=list[AlsoImportJobResponse])
async def list_import_jobs(
status: Optional[str] = Query(default=None),
limit: int = Query(default=100, ge=1, le=500),
):
return also_service.list_import_jobs(status=status, limit=limit)
@router.get("/also/import-jobs/{job_id}")
async def get_import_job(job_id: int):
return also_service.get_import_job(job_id)
@router.delete("/also/import-jobs/{job_id}")
async def delete_import_job(
job_id: int,
delete_order_drafts: bool = Query(default=False),
):
return also_service.delete_import_job(job_id=job_id, delete_order_drafts=delete_order_drafts)
@router.post("/also/import-jobs/{job_id}/auto-map")
async def auto_map_import_job(job_id: int):
return also_service.auto_map_import_job(job_id=job_id)
@router.post("/also/import-lines/{line_id}/map-company")
async def manual_map_company_for_line(
line_id: int,
payload: AlsoManualCompanyMapRequest,
request: Request,
):
return also_service.manual_map_company_for_line(
line_id=line_id,
customer_id=payload.customer_id,
notes=payload.notes,
updated_by_user_id=_user_id_from_request(request),
)
@router.post("/also/import-lines/{line_id}/map-product")
async def manual_map_product_for_line(
line_id: int,
payload: AlsoManualProductMapRequest,
request: Request,
):
return also_service.manual_map_product_for_line(
line_id=line_id,
product_id=payload.product_id,
notes=payload.notes,
updated_by_user_id=_user_id_from_request(request),
)
@router.post("/also/import-upload")
async def upload_import_file(
request: Request,
file: UploadFile = File(...),
):
payload = await file.read()
return also_service.import_billing_upload(
file_name=file.filename or "also-billing.xlsx",
file_bytes=payload,
imported_by_user_id=_user_id_from_request(request),
source_label="Manual upload",
)
@router.post("/also/import-jobs/{job_id}/lines")
async def import_lines(job_id: int, payload: AlsoImportLinesRequest):
return also_service.import_lines(job_id=job_id, payload=payload)
@router.get("/also/import-jobs/{job_id}/lines")
async def get_import_job_lines(
job_id: int,
status: Optional[str] = Query(default=None),
limit: int = Query(default=500, ge=1, le=2000),
):
return also_service.get_import_job_lines(job_id=job_id, status=status, limit=limit)
@router.get("/also/queue", response_model=list[AlsoQueueLineResponse])
async def get_queue(
status: Optional[str] = Query(default=None),
limit: int = Query(default=200, ge=1, le=1000),
):
return also_service.get_queue(status=status, limit=limit)
@router.get("/also/dashboard/summary", response_model=AlsoDashboardSummaryResponse)
async def get_dashboard_summary():
return also_service.get_dashboard_summary()
@router.get("/also/dashboard/differences", response_model=list[AlsoDifferenceItem])
async def get_dashboard_differences(limit: int = Query(default=50, ge=1, le=200)):
return also_service.get_monthly_differences(limit=limit)
@router.get("/also/dashboard/status-breakdown")
async def get_status_breakdown():
return also_service.get_status_breakdown()
@router.get("/also/dashboard/monthly-history")
async def get_monthly_history(months: int = Query(default=6, ge=1, le=24)):
return also_service.get_monthly_history(months=months)
@router.post("/also/queue/run-matching", response_model=AlsoQueueProcessResult)
async def run_matching(payload: AlsoQueueProcessRequest):
return also_service.run_matching(
import_job_id=payload.import_job_id,
line_ids=payload.line_ids,
limit=payload.limit,
)
@router.post("/also/queue/run-validation", response_model=AlsoQueueProcessResult)
async def run_validation(payload: AlsoQueueProcessRequest):
return also_service.run_validation(
import_job_id=payload.import_job_id,
line_ids=payload.line_ids,
limit=payload.limit,
)
@router.post("/also/queue/approve", response_model=AlsoApproveResult)
async def approve_queue(payload: AlsoQueueApproveRequest, request: Request):
return also_service.approve_lines_to_drafts(
import_job_id=payload.import_job_id,
line_ids=payload.line_ids,
approved_by_user_id=_user_id_from_request(request),
)
@router.put("/also/mappings/company")
async def upsert_company_mapping(payload: AlsoCompanyMappingUpsert):
return also_service.upsert_company_mapping(payload)
@router.put("/also/mappings/product")
async def upsert_product_mapping(payload: AlsoProductMappingUpsert):
return also_service.upsert_product_mapping(payload)