dingdanquanliucheng/backend/app/api/reconciliation.py
wsb1224 0bda0bbaa0 feat: 新增客户对账单功能
管理员后台新增"客户对账单"页面,按客户+时间段汇总订单和欠款明细,
支持逾期高亮和 CSV 导出。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-04 13:37:52 +08:00

101 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
对账模块路由模块
提供客户对账单查询接口,按客户+时间段汇总订单和欠款明细。
支持 CSV 导出对账明细。
URL 前缀:/api/reconciliation
权限要求manager/admin 角色
"""
from datetime import date
from fastapi import APIRouter, Depends, Query
from sqlalchemy.orm import Session
from backend.app.api.deps import require_permissions, require_roles
from backend.app.db import get_db_session
from backend.app.repositories.reminder_repository import ReminderRepository
from backend.app.schemas.common import success_payload
router = APIRouter(prefix="/api/reconciliation", tags=["reconciliation"])
reminder_repository = ReminderRepository()
@router.get("/customer-statement")
def get_customer_statement(
customer_id: int = Query(..., gt=0),
start_date: date | None = Query(default=None),
end_date: date | None = Query(default=None),
session: Session = Depends(get_db_session),
current_user: dict = Depends(require_roles("manager", "admin")),
_permission_user: dict = Depends(require_permissions("order:list")),
) -> dict:
"""查询客户对账单:指定客户在时间段内的订单和欠款明细。"""
from datetime import date as _date
customer = reminder_repository.get_customer(session, customer_id)
if customer is None:
from backend.app.core.error_codes import ErrorCode
from backend.app.core.exceptions import AppException
raise AppException(code=ErrorCode.NOT_FOUND, message="客户不存在", status_code=404)
rows = reminder_repository.get_customer_statement(session, customer_id, start_date, end_date)
orders = []
total_amount = 0.0
total_arrears = 0.0
overdue_amount = 0.0
overdue_count = 0
today = _date.today()
for order, arrears in rows:
amount = float(order.contract_amount or 0)
arrears_amount = float(arrears.arrears_amount or 0) if arrears else 0.0
arrears_status = arrears.status if arrears else None
due_date = arrears.due_date if arrears else None
# 逾期判定:状态为 overdue 或 pending 但已过到期日
is_overdue = False
if arrears and arrears_status == "overdue":
is_overdue = True
elif arrears and arrears_status == "pending" and due_date and due_date <= today:
is_overdue = True
total_amount += amount
if arrears_amount > 0:
total_arrears += arrears_amount
if is_overdue:
overdue_amount += arrears_amount
overdue_count += 1
orders.append({
"order_id": order.id,
"order_no": order.order_no,
"order_status": order.order_status,
"contract_amount": amount,
"created_at": order.created_at.strftime("%Y-%m-%d %H:%M") if order.created_at else "",
"arrears_amount": arrears_amount,
"due_date": due_date.strftime("%Y-%m-%d") if due_date else "",
"arrears_status": arrears_status,
"is_overdue": is_overdue,
})
return success_payload({
"customer": {
"id": customer.id,
"customer_name": customer.customer_name,
"mobile": customer.mobile,
"settlement_type": customer.settlement_type or "",
"settlement_days": customer.settlement_days or 0,
},
"summary": {
"total_orders": len(orders),
"total_amount": round(total_amount, 2),
"total_arrears": round(total_arrears, 2),
"overdue_amount": round(overdue_amount, 2),
"overdue_count": overdue_count,
},
"orders": orders,
})