Files
menchuang/src/huamenchuang/api/contracts.py
T

103 lines
3.8 KiB
Python

"""合同路由:意向单/正式单管理。"""
from fastapi import APIRouter
from pydantic import BaseModel, Field
from sqlalchemy import select
from huamenchuang.core.deps import CurrentUser, DbSession
from huamenchuang.core.response import fail, ok, paginated
from huamenchuang.repositories.contract_repo import status_label
from huamenchuang.services.contract_service import ContractService
router = APIRouter(prefix="/contracts", tags=["合同"])
class ContractBody(BaseModel):
customer_id: int | None = None
customer_name: str = ""
phone: str = ""
address: str = ""
area: float = 0
count: int = 0
money: float = 0
note: str = ""
class ConvertBody(BaseModel):
contract_id: int
def _to_out(c) -> dict:
return {
"id": c.id, "contract_no": c.contract_no, "customer_id": c.customer_id,
"customer_name": c.customer_name, "phone": c.phone, "address": c.address,
"area": float(c.area), "count": c.count, "money": float(c.money),
"status": c.status, "status_label": status_label(c.status),
"sign_status": c.sign_status, "note": c.note,
"created_at": str(c.created_at), "updated_at": str(c.updated_at),
}
@router.post("")
async def create_contract(body: ContractBody, user: CurrentUser, db: DbSession):
"""创建合同(默认意向单)。"""
contract = await ContractService(db).create(user, body.model_dump())
return ok(data=_to_out(contract), message="创建成功")
@router.get("")
async def list_contracts(user: CurrentUser, db: DbSession, page: int = 1,
page_size: int = 15, keyword: str = "", status: int | None = None):
"""合同分页列表(结构对齐目标站 {"contracts": {...}},含已收金额)。"""
items, total = await ContractService(db).repo.search(
user.company_id, page, page_size, keyword, status)
# 当页合同的有效收款聚合(一次 GROUP BY,避免逐行查询)
received_map: dict[int, float] = {}
if items:
from sqlalchemy import func
from huamenchuang.models.business import ContractReceipt
ids = [c.id for c in items]
rows = (await db.execute(
select(ContractReceipt.contract_id, func.sum(ContractReceipt.amount))
.where(ContractReceipt.contract_id.in_(ids), ContractReceipt.status == 1)
.group_by(ContractReceipt.contract_id))).all()
received_map = {cid: float(v or 0) for cid, v in rows}
outs = []
for c in items:
out = _to_out(c)
out["received"] = round(received_map.get(c.id, 0), 2)
out["unreceived"] = round(float(c.money) - out["received"], 2)
outs.append(out)
return ok(data={"contracts": paginated(outs, page, page_size, total)})
@router.put("/{contract_id}")
async def update_contract(contract_id: int, body: ContractBody, user: CurrentUser, db: DbSession):
"""编辑合同。"""
try:
c = await ContractService(db).update(user, contract_id, body.model_dump())
except KeyError as e:
return fail(str(e))
return ok(data=_to_out(c), message="更新成功")
@router.post("/convert")
async def convert_contract(body: ConvertBody, user: CurrentUser, db: DbSession):
"""意向单转正式单(对齐目标站 convert-contract/)。"""
try:
c = await ContractService(db).convert_to_formal(user, body.contract_id)
except KeyError as e:
return fail(str(e))
except ValueError as e:
return fail(str(e))
return ok(data=_to_out(c), message="已转为正式单")
@router.delete("/{contract_id}")
async def delete_contract(contract_id: int, user: CurrentUser, db: DbSession):
"""删除合同。"""
try:
await ContractService(db).delete(user, contract_id)
except KeyError as e:
return fail(str(e))
return ok(message="删除成功")