|
|
@@ -5,10 +5,20 @@ from typing import Any, List, Optional
|
|
|
from urllib.parse import quote
|
|
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, status, Query
|
|
|
+from pydantic import Field
|
|
|
from fastapi.responses import StreamingResponse
|
|
|
from sqlalchemy import or_
|
|
|
from sqlalchemy.orm import Session
|
|
|
|
|
|
+from app.api.bx.export_common import (
|
|
|
+ EXPORT_MAX_ROWS,
|
|
|
+ ExportColumn,
|
|
|
+ ExportValidationError,
|
|
|
+ BxApplyListExportBody,
|
|
|
+ dump_export_row,
|
|
|
+ issue_status_label,
|
|
|
+ respond_xlsx,
|
|
|
+)
|
|
|
from app.core.bx_auth import BxUserContext, get_bx_current_user
|
|
|
from app.core.database import get_db
|
|
|
from app.core.tenant_context import get_tenant_no
|
|
|
@@ -251,6 +261,161 @@ def advance_issue_stats(
|
|
|
return ApiResponse(data=AdvanceFinanceIssueStatsOut.model_validate(data))
|
|
|
|
|
|
|
|
|
+
|
|
|
+PAYMENT_EXPORT_COLUMNS = [
|
|
|
+ ExportColumn(title="申请单号", field="applyCode"),
|
|
|
+ ExportColumn(title="申请时间", field="applyTime"),
|
|
|
+ ExportColumn(title="申请人", field="applicantName"),
|
|
|
+ ExportColumn(title="报销主题", field="title"),
|
|
|
+ ExportColumn(title="金额(元)", field="amount"),
|
|
|
+ ExportColumn(title="冲抵金额(元)", field="offsetAmount"),
|
|
|
+ ExportColumn(title="实付金额(元)", field="payAmount"),
|
|
|
+ ExportColumn(title="处理状态", field="status"),
|
|
|
+ ExportColumn(title="支付时间", field="payTime"),
|
|
|
+]
|
|
|
+
|
|
|
+ADVANCE_ISSUE_EXPORT_COLUMNS = [
|
|
|
+ ExportColumn(title="申请单号", field="applyCode"),
|
|
|
+ ExportColumn(title="申请时间", field="applyTime"),
|
|
|
+ ExportColumn(title="申请人", field="applicantName"),
|
|
|
+ ExportColumn(title="申请原因", field="reason"),
|
|
|
+ ExportColumn(title="申请金额(元)", field="amount"),
|
|
|
+ ExportColumn(title="实际发放金额(元)", field="actualAmount"),
|
|
|
+ ExportColumn(title="冲抵总金额(元)", field="offsetAmount"),
|
|
|
+ ExportColumn(title="审批通过时间", field="approveTime"),
|
|
|
+ ExportColumn(title="发放时间", field="issueTime"),
|
|
|
+ ExportColumn(title="发放人", field="issueUserName"),
|
|
|
+ ExportColumn(title="发放状态", field="issueStatus"),
|
|
|
+]
|
|
|
+
|
|
|
+
|
|
|
+class PaymentExportBody(BxApplyListExportBody):
|
|
|
+ issue_status: Optional[str] = Field(default=None, alias="issueStatus")
|
|
|
+
|
|
|
+
|
|
|
+@router.post("/pending/export")
|
|
|
+def export_pending_payments(
|
|
|
+ body: BxApplyListExportBody,
|
|
|
+ db: Session = Depends(get_db),
|
|
|
+ user: BxUserContext = Depends(get_bx_current_user),
|
|
|
+):
|
|
|
+ try:
|
|
|
+ tenant_no = _tenant(db, user)
|
|
|
+ filters = [BxReimburseApply.status == "approved"]
|
|
|
+ if body.keyword:
|
|
|
+ filters.append(
|
|
|
+ or_(
|
|
|
+ BxReimburseApply.apply_code.contains(body.keyword),
|
|
|
+ BxReimburseApply.applicant_name.contains(body.keyword),
|
|
|
+ )
|
|
|
+ )
|
|
|
+ if body.applicant_id:
|
|
|
+ filters.append(BxReimburseApply.applicant_id == str(body.applicant_id).strip())
|
|
|
+ if body.ids:
|
|
|
+ filters.append(BxReimburseApply.id.in_([str(i) for i in body.ids]))
|
|
|
+ append_create_time_filters(
|
|
|
+ filters, BxReimburseApply,
|
|
|
+ apply_date=body.apply_date, apply_date_from=body.apply_date_from, apply_date_to=body.apply_date_to,
|
|
|
+ )
|
|
|
+ rows, _ = ReimburseService.get_paged(
|
|
|
+ db, tenant_no, page=1, page_size=EXPORT_MAX_ROWS, filters=filters, user=user
|
|
|
+ )
|
|
|
+ outs = ReimburseService.out_rows_with_attachment_counts(db, tenant_no, list(rows))
|
|
|
+ export_rows = [dump_export_row(o) for o in outs]
|
|
|
+ return respond_xlsx(
|
|
|
+ db, tenant_no=tenant_no, sheet_title="待付款", columns=PAYMENT_EXPORT_COLUMNS,
|
|
|
+ rows=export_rows, filename_stem="报销待付款", requested_columns=body.columns,
|
|
|
+ )
|
|
|
+ except ExportValidationError as e:
|
|
|
+ raise HTTPException(status_code=400, detail=str(e)) from e
|
|
|
+
|
|
|
+
|
|
|
+@router.post("/processed/export")
|
|
|
+def export_processed_payments(
|
|
|
+ body: BxApplyListExportBody,
|
|
|
+ db: Session = Depends(get_db),
|
|
|
+ user: BxUserContext = Depends(get_bx_current_user),
|
|
|
+):
|
|
|
+ try:
|
|
|
+ tenant_no = _tenant(db, user)
|
|
|
+ filters = [BxReimburseApply.status.in_(["processed", "paid", "offset"])]
|
|
|
+ if body.keyword:
|
|
|
+ filters.append(
|
|
|
+ or_(
|
|
|
+ BxReimburseApply.apply_code.contains(body.keyword),
|
|
|
+ BxReimburseApply.applicant_name.contains(body.keyword),
|
|
|
+ )
|
|
|
+ )
|
|
|
+ if body.applicant_id:
|
|
|
+ filters.append(BxReimburseApply.applicant_id == str(body.applicant_id).strip())
|
|
|
+ if body.ids:
|
|
|
+ filters.append(BxReimburseApply.id.in_([str(i) for i in body.ids]))
|
|
|
+ append_create_time_filters(
|
|
|
+ filters, BxReimburseApply,
|
|
|
+ apply_date=body.apply_date, apply_date_from=body.apply_date_from, apply_date_to=body.apply_date_to,
|
|
|
+ )
|
|
|
+ rows, _ = ReimburseService.get_paged(
|
|
|
+ db, tenant_no, page=1, page_size=EXPORT_MAX_ROWS, filters=filters, user=user
|
|
|
+ )
|
|
|
+ outs = ReimburseService.out_rows_with_attachment_counts(db, tenant_no, list(rows))
|
|
|
+ export_rows = [dump_export_row(o) for o in outs]
|
|
|
+ return respond_xlsx(
|
|
|
+ db, tenant_no=tenant_no, sheet_title="已付款", columns=PAYMENT_EXPORT_COLUMNS,
|
|
|
+ rows=export_rows, filename_stem="报销已付款", requested_columns=body.columns,
|
|
|
+ )
|
|
|
+ except ExportValidationError as e:
|
|
|
+ raise HTTPException(status_code=400, detail=str(e)) from e
|
|
|
+
|
|
|
+
|
|
|
+@router.post("/advance-issues/export-xlsx")
|
|
|
+def export_advance_issues_xlsx(
|
|
|
+ body: PaymentExportBody,
|
|
|
+ db: Session = Depends(get_db),
|
|
|
+ user: BxUserContext = Depends(get_bx_current_user),
|
|
|
+):
|
|
|
+ try:
|
|
|
+ tenant_no = _tenant(db, user)
|
|
|
+ filters = []
|
|
|
+ issue_status = body.issue_status
|
|
|
+ if issue_status == "pending":
|
|
|
+ filters.append(BxAdvanceApply.status == "approved")
|
|
|
+ filters.append(or_(BxAdvanceApply.issue_status.is_(None), BxAdvanceApply.issue_status != "issued"))
|
|
|
+ elif issue_status == "issued":
|
|
|
+ filters.append(BxAdvanceApply.status == "issued")
|
|
|
+ else:
|
|
|
+ filters.append(or_(BxAdvanceApply.status == "approved", BxAdvanceApply.status == "issued"))
|
|
|
+ if body.keyword:
|
|
|
+ filters.append(
|
|
|
+ or_(
|
|
|
+ BxAdvanceApply.apply_code.contains(body.keyword),
|
|
|
+ BxAdvanceApply.applicant_name.contains(body.keyword),
|
|
|
+ )
|
|
|
+ )
|
|
|
+ if body.applicant_id:
|
|
|
+ filters.append(BxAdvanceApply.applicant_id == str(body.applicant_id).strip())
|
|
|
+ if body.ids:
|
|
|
+ filters.append(BxAdvanceApply.id.in_([str(i) for i in body.ids]))
|
|
|
+ append_create_time_filters(
|
|
|
+ filters, BxAdvanceApply,
|
|
|
+ apply_date=body.apply_date, apply_date_from=body.apply_date_from, apply_date_to=body.apply_date_to,
|
|
|
+ )
|
|
|
+ rows, _ = AdvanceService.get_paged(
|
|
|
+ db, tenant_no, page=1, page_size=EXPORT_MAX_ROWS, filters=filters, user=user
|
|
|
+ )
|
|
|
+ out_rows = [AdvanceIssueOut.model_validate(r) for r in rows]
|
|
|
+ export_rows = []
|
|
|
+ for o in out_rows:
|
|
|
+ data = dump_export_row(o, status_fields=())
|
|
|
+ data["issueStatus"] = issue_status_label(getattr(o, "issue_status", None) or data.get("issueStatus"))
|
|
|
+ export_rows.append(data)
|
|
|
+ return respond_xlsx(
|
|
|
+ db, tenant_no=tenant_no, sheet_title="备用金发放", columns=ADVANCE_ISSUE_EXPORT_COLUMNS,
|
|
|
+ rows=export_rows, filename_stem="备用金发放", requested_columns=body.columns,
|
|
|
+ )
|
|
|
+ except ExportValidationError as e:
|
|
|
+ raise HTTPException(status_code=400, detail=str(e)) from e
|
|
|
+
|
|
|
+
|
|
|
@router.get("/advance-issues/export")
|
|
|
def export_advance_issues(
|
|
|
issue_status: Optional[str] = None,
|