303 lines
10 KiB
Python
303 lines
10 KiB
Python
"""
|
|
发票数据API路由
|
|
"""
|
|
from typing import Any, List, Optional
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
|
from sqlalchemy import select, func, and_, or_
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from loguru import logger
|
|
|
|
from app.database import get_async_session
|
|
from app.models.invoice import Invoice
|
|
from app.schemas.invoice import (
|
|
InvoiceCreate,
|
|
InvoiceUpdate,
|
|
InvoiceResponse,
|
|
InvoiceListResponse,
|
|
)
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("", response_model=InvoiceListResponse)
|
|
async def list_invoices(
|
|
page: int = Query(1, ge=1, description="页码"),
|
|
size: int = Query(10, ge=1, le=100, description="每页数量"),
|
|
invoice_id: str = Query(None, description="发票ID"),
|
|
invoice_type: str = Query(None, description="发票类型"),
|
|
seller_name: str = Query(None, description="销售方名称"),
|
|
invoice_status: str = Query(None, description="发票状态"),
|
|
db: AsyncSession = Depends(get_async_session),
|
|
):
|
|
"""
|
|
获取发票数据列表(分页查询)
|
|
"""
|
|
logger.info(f"获取发票数据列表: page={page}, size={size}")
|
|
|
|
# 构建查询
|
|
query = select(Invoice)
|
|
|
|
# 添加过滤条件
|
|
conditions = []
|
|
if invoice_id:
|
|
conditions.append(Invoice.invoice_id.ilike(f"%{invoice_id}%"))
|
|
if invoice_type:
|
|
conditions.append(Invoice.invoice_type == invoice_type)
|
|
if seller_name:
|
|
conditions.append(Invoice.seller_name.ilike(f"%{seller_name}%"))
|
|
if invoice_status:
|
|
conditions.append(Invoice.invoice_status == invoice_status)
|
|
|
|
if conditions:
|
|
query = query.where(and_(*conditions))
|
|
|
|
# 获取总数
|
|
count_query = select(func.count()).select_from(Invoice)
|
|
if conditions:
|
|
count_query = count_query.where(and_(*conditions))
|
|
|
|
total_result = await db.execute(count_query)
|
|
total = total_result.scalar()
|
|
|
|
# 分页
|
|
query = query.offset((page - 1) * size).limit(size)
|
|
|
|
# 执行查询
|
|
result = await db.execute(query)
|
|
records = result.scalars().all()
|
|
|
|
# 转换为响应格式
|
|
response_records = []
|
|
for invoice in records:
|
|
response_records.append({
|
|
"id": invoice.id,
|
|
"invoice_id": invoice.invoice_id,
|
|
"invoice_code": invoice.invoice_code,
|
|
"invoice_no": invoice.invoice_no,
|
|
"invoice_type": invoice.invoice_type,
|
|
"direction": invoice.direction,
|
|
"invoice_date": invoice.invoice_date.isoformat() if invoice.invoice_date else None,
|
|
"purchaser_name": invoice.purchaser_name,
|
|
"purchaser_tax_no": invoice.purchaser_tax_no,
|
|
"seller_name": invoice.seller_name,
|
|
"seller_tax_no": invoice.seller_tax_no,
|
|
"total_amount": invoice.total_amount,
|
|
"total_tax": invoice.total_tax,
|
|
"total_amount_with_tax": invoice.total_amount_with_tax,
|
|
"amount_in_words": invoice.amount_in_words,
|
|
"invoice_status": invoice.invoice_status,
|
|
"is_verified": invoice.is_verified,
|
|
"verified_time": invoice.verified_time.isoformat() if invoice.verified_time else None,
|
|
"is_red_invoice": invoice.is_red_invoice,
|
|
"red_reason": invoice.red_reason,
|
|
"remark": invoice.remark,
|
|
"pdf_url": invoice.pdf_url,
|
|
})
|
|
|
|
return InvoiceListResponse(
|
|
records=response_records,
|
|
total=total,
|
|
page=page,
|
|
size=size,
|
|
)
|
|
|
|
|
|
@router.get("/{invoice_id}", response_model=InvoiceResponse)
|
|
async def get_invoice(invoice_id: str, db: AsyncSession = Depends(get_async_session)):
|
|
"""
|
|
根据ID获取发票数据详细信息
|
|
"""
|
|
logger.info(f"获取发票数据详情: {invoice_id}")
|
|
|
|
query = select(Invoice).where(Invoice.invoice_id == invoice_id)
|
|
result = await db.execute(query)
|
|
invoice = result.scalar_one_or_none()
|
|
|
|
if not invoice:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=f"发票数据不存在: {invoice_id}",
|
|
)
|
|
|
|
# 转换为响应格式
|
|
response = {
|
|
"id": invoice.id,
|
|
"invoice_id": invoice.invoice_id,
|
|
"invoice_code": invoice.invoice_code,
|
|
"invoice_no": invoice.invoice_no,
|
|
"invoice_type": invoice.invoice_type,
|
|
"direction": invoice.direction,
|
|
"invoice_date": invoice.invoice_date.isoformat() if invoice.invoice_date else None,
|
|
"purchaser_name": invoice.purchaser_name,
|
|
"purchaser_tax_no": invoice.purchaser_tax_no,
|
|
"seller_name": invoice.seller_name,
|
|
"seller_tax_no": invoice.seller_tax_no,
|
|
"total_amount": invoice.total_amount,
|
|
"total_tax": invoice.total_tax,
|
|
"total_amount_with_tax": invoice.total_amount_with_tax,
|
|
"amount_in_words": invoice.amount_in_words,
|
|
"invoice_status": invoice.invoice_status,
|
|
"is_verified": invoice.is_verified,
|
|
"verified_time": invoice.verified_time.isoformat() if invoice.verified_time else None,
|
|
"is_red_invoice": invoice.is_red_invoice,
|
|
"red_reason": invoice.red_reason,
|
|
"remark": invoice.remark,
|
|
"pdf_url": invoice.pdf_url,
|
|
}
|
|
|
|
return response
|
|
|
|
|
|
@router.post("", response_model=InvoiceResponse, status_code=status.HTTP_201_CREATED)
|
|
async def create_invoice(invoice: InvoiceCreate, db: AsyncSession = Depends(get_async_session)):
|
|
"""
|
|
创建新的发票数据
|
|
"""
|
|
logger.info(f"创建发票数据: {invoice.invoice_no}")
|
|
|
|
# 生成发票ID
|
|
query = select(func.count()).select_from(Invoice)
|
|
result = await db.execute(query)
|
|
count = result.scalar()
|
|
invoice_id = f"INV{2024}{(count + 1):06d}"
|
|
|
|
# 创建新发票
|
|
new_invoice = Invoice(
|
|
invoice_id=invoice_id,
|
|
invoice_code=invoice.invoice_code,
|
|
invoice_no=invoice.invoice_no,
|
|
invoice_type=invoice.invoice_type,
|
|
direction=invoice.direction,
|
|
invoice_date=invoice.invoice_date,
|
|
purchaser_name=invoice.purchaser_name,
|
|
purchaser_tax_no=invoice.purchaser_tax_no,
|
|
seller_name=invoice.seller_name,
|
|
seller_tax_no=invoice.seller_tax_no,
|
|
total_amount=invoice.total_amount,
|
|
total_tax=invoice.total_tax,
|
|
total_amount_with_tax=invoice.total_amount_with_tax,
|
|
amount_in_words=invoice.amount_in_words,
|
|
invoice_status=invoice.invoice_status,
|
|
is_verified=invoice.is_verified,
|
|
verified_time=invoice.verified_time,
|
|
is_red_invoice=invoice.is_red_invoice,
|
|
red_reason=invoice.red_reason,
|
|
remark=invoice.remark,
|
|
pdf_url=invoice.pdf_url,
|
|
)
|
|
|
|
db.add(new_invoice)
|
|
await db.commit()
|
|
await db.refresh(new_invoice)
|
|
|
|
# 转换为响应格式
|
|
response = {
|
|
"id": new_invoice.id,
|
|
"invoice_id": new_invoice.invoice_id,
|
|
"invoice_code": new_invoice.invoice_code,
|
|
"invoice_no": new_invoice.invoice_no,
|
|
"invoice_type": new_invoice.invoice_type,
|
|
"direction": new_invoice.direction,
|
|
"invoice_date": new_invoice.invoice_date.isoformat() if new_invoice.invoice_date else None,
|
|
"purchaser_name": new_invoice.purchaser_name,
|
|
"purchaser_tax_no": new_invoice.purchaser_tax_no,
|
|
"seller_name": new_invoice.seller_name,
|
|
"seller_tax_no": new_invoice.seller_tax_no,
|
|
"total_amount": new_invoice.total_amount,
|
|
"total_tax": new_invoice.total_tax,
|
|
"total_amount_with_tax": new_invoice.total_amount_with_tax,
|
|
"amount_in_words": new_invoice.amount_in_words,
|
|
"invoice_status": new_invoice.invoice_status,
|
|
"is_verified": new_invoice.is_verified,
|
|
"verified_time": new_invoice.verified_time.isoformat() if new_invoice.verified_time else None,
|
|
"is_red_invoice": new_invoice.is_red_invoice,
|
|
"red_reason": new_invoice.red_reason,
|
|
"remark": new_invoice.remark,
|
|
"pdf_url": new_invoice.pdf_url,
|
|
}
|
|
|
|
return response
|
|
|
|
|
|
@router.put("/{invoice_id}", response_model=InvoiceResponse)
|
|
async def update_invoice(
|
|
invoice_id: str,
|
|
invoice_update: InvoiceUpdate,
|
|
db: AsyncSession = Depends(get_async_session),
|
|
):
|
|
"""
|
|
更新发票数据
|
|
"""
|
|
logger.info(f"更新发票数据: {invoice_id}")
|
|
|
|
query = select(Invoice).where(Invoice.invoice_id == invoice_id)
|
|
result = await db.execute(query)
|
|
invoice = result.scalar_one_or_none()
|
|
|
|
if not invoice:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=f"发票数据不存在: {invoice_id}",
|
|
)
|
|
|
|
# 更新字段
|
|
update_data = invoice_update.model_dump(exclude_unset=True)
|
|
for key, value in update_data.items():
|
|
if hasattr(invoice, key):
|
|
setattr(invoice, key, value)
|
|
|
|
await db.commit()
|
|
await db.refresh(invoice)
|
|
|
|
# 转换为响应格式
|
|
response = {
|
|
"id": invoice.id,
|
|
"invoice_id": invoice.invoice_id,
|
|
"invoice_code": invoice.invoice_code,
|
|
"invoice_no": invoice.invoice_no,
|
|
"invoice_type": invoice.invoice_type,
|
|
"direction": invoice.direction,
|
|
"invoice_date": invoice.invoice_date.isoformat() if invoice.invoice_date else None,
|
|
"purchaser_name": invoice.purchaser_name,
|
|
"purchaser_tax_no": invoice.purchaser_tax_no,
|
|
"seller_name": invoice.seller_name,
|
|
"seller_tax_no": invoice.seller_tax_no,
|
|
"total_amount": invoice.total_amount,
|
|
"total_tax": invoice.total_tax,
|
|
"total_amount_with_tax": invoice.total_amount_with_tax,
|
|
"amount_in_words": invoice.amount_in_words,
|
|
"invoice_status": invoice.invoice_status,
|
|
"is_verified": invoice.is_verified,
|
|
"verified_time": invoice.verified_time.isoformat() if invoice.verified_time else None,
|
|
"is_red_invoice": invoice.is_red_invoice,
|
|
"red_reason": invoice.red_reason,
|
|
"remark": invoice.remark,
|
|
"pdf_url": invoice.pdf_url,
|
|
}
|
|
|
|
return response
|
|
|
|
|
|
@router.delete("/{invoice_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
async def delete_invoice(invoice_id: str, db: AsyncSession = Depends(get_async_session)):
|
|
"""
|
|
删除发票数据(软删除)
|
|
"""
|
|
logger.info(f"删除发票数据: {invoice_id}")
|
|
|
|
query = select(Invoice).where(Invoice.invoice_id == invoice_id)
|
|
result = await db.execute(query)
|
|
invoice = result.scalar_one_or_none()
|
|
|
|
if not invoice:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail=f"发票数据不存在: {invoice_id}",
|
|
)
|
|
|
|
# 软删除 - 设置为已作废状态
|
|
invoice.invoice_status = "cancelled"
|
|
await db.commit()
|
|
|
|
return None
|