增加财务模块
This commit is contained in:
@@ -24,6 +24,7 @@ from .stock_movement_routes import router as stock_movement_router
|
||||
from .purchase_order_routes import router as purchase_order_router
|
||||
from .sales_order_routes import router as sales_order_router
|
||||
from .dashboard_routes import router as dashboard_router
|
||||
from .finance_routes import router as finance_router
|
||||
|
||||
inventory_router = APIRouter(prefix="/api", tags=["进销存"])
|
||||
|
||||
@@ -36,5 +37,6 @@ inventory_router.include_router(stock_movement_router)
|
||||
inventory_router.include_router(purchase_order_router)
|
||||
inventory_router.include_router(sales_order_router)
|
||||
inventory_router.include_router(dashboard_router)
|
||||
inventory_router.include_router(finance_router)
|
||||
|
||||
__all__ = ["inventory_router"]
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, func
|
||||
from sqlalchemy.orm import selectinload
|
||||
from typing import Optional, List
|
||||
from datetime import datetime
|
||||
|
||||
from database.database import get_db_session
|
||||
from services.auth_service import get_current_active_user
|
||||
from models.database import (
|
||||
User,
|
||||
Customer,
|
||||
Supplier,
|
||||
SalesOrder,
|
||||
PurchaseOrder,
|
||||
FinanceTransaction,
|
||||
FinanceAllocation,
|
||||
)
|
||||
from .schemas import (
|
||||
ReceiptCreate,
|
||||
PaymentCreate,
|
||||
FinanceTransactionResponse,
|
||||
FinanceSummaryResponse,
|
||||
ReceivableItemResponse,
|
||||
PayableItemResponse,
|
||||
)
|
||||
from .utils import generate_order_no
|
||||
|
||||
router = APIRouter(prefix="/finance", tags=["财务管理"])
|
||||
|
||||
|
||||
def _build_transaction_response(txn: FinanceTransaction) -> FinanceTransactionResponse:
|
||||
allocations = [
|
||||
{
|
||||
"id": item.id,
|
||||
"order_type": item.order_type,
|
||||
"order_id": item.order_id,
|
||||
"allocated_amount": item.allocated_amount,
|
||||
}
|
||||
for item in txn.allocations
|
||||
]
|
||||
return FinanceTransactionResponse(
|
||||
id=txn.id,
|
||||
txn_no=txn.txn_no,
|
||||
txn_type=txn.txn_type,
|
||||
partner_type=txn.partner_type,
|
||||
partner_id=txn.partner_id,
|
||||
amount=txn.amount,
|
||||
txn_date=txn.txn_date,
|
||||
method=txn.method,
|
||||
account_name=txn.account_name,
|
||||
status=txn.status,
|
||||
remark=txn.remark,
|
||||
created_at=txn.created_at,
|
||||
allocations=allocations,
|
||||
)
|
||||
|
||||
|
||||
def _validate_allocation_total(transaction_amount: float, allocation_amounts: List[float]):
|
||||
allocated_total = sum(allocation_amounts)
|
||||
if allocated_total - transaction_amount > 1e-6:
|
||||
raise HTTPException(status_code=400, detail="核销总额不能大于单据金额")
|
||||
|
||||
|
||||
@router.post("/receipts", response_model=FinanceTransactionResponse, status_code=201)
|
||||
async def create_receipt(
|
||||
payload: ReceiptCreate,
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
customer_result = await db_session.execute(
|
||||
select(Customer).where(Customer.id == payload.customer_id, Customer.is_active == True)
|
||||
)
|
||||
customer = customer_result.scalar_one_or_none()
|
||||
if not customer:
|
||||
raise HTTPException(status_code=404, detail="客户不存在")
|
||||
|
||||
_validate_allocation_total(payload.amount, [item.allocated_amount for item in payload.allocations])
|
||||
|
||||
txn = FinanceTransaction(
|
||||
txn_no=generate_order_no("RC"),
|
||||
txn_type="receipt",
|
||||
partner_type="customer",
|
||||
partner_id=payload.customer_id,
|
||||
amount=payload.amount,
|
||||
txn_date=payload.txn_date or datetime.now(),
|
||||
method=payload.method,
|
||||
account_name=payload.account_name,
|
||||
status="confirmed",
|
||||
remark=payload.remark,
|
||||
operator_id=current_user.id,
|
||||
)
|
||||
db_session.add(txn)
|
||||
await db_session.flush()
|
||||
|
||||
for allocation in payload.allocations:
|
||||
if allocation.order_type != "sales":
|
||||
raise HTTPException(status_code=400, detail="收款单只允许核销销售订单")
|
||||
|
||||
order_result = await db_session.execute(
|
||||
select(SalesOrder).where(SalesOrder.id == allocation.order_id, SalesOrder.customer_id == payload.customer_id)
|
||||
)
|
||||
sales_order = order_result.scalar_one_or_none()
|
||||
if not sales_order:
|
||||
raise HTTPException(status_code=404, detail=f"销售订单不存在: {allocation.order_id}")
|
||||
|
||||
remaining = (sales_order.total_amount or 0) - (sales_order.received_amount or 0)
|
||||
if allocation.allocated_amount - remaining > 1e-6:
|
||||
raise HTTPException(status_code=400, detail=f"销售订单核销超额: {sales_order.order_no}")
|
||||
|
||||
db_session.add(
|
||||
FinanceAllocation(
|
||||
transaction_id=txn.id,
|
||||
order_type="sales",
|
||||
order_id=sales_order.id,
|
||||
allocated_amount=allocation.allocated_amount,
|
||||
)
|
||||
)
|
||||
sales_order.received_amount = (sales_order.received_amount or 0) + allocation.allocated_amount
|
||||
|
||||
await db_session.commit()
|
||||
result = await db_session.execute(
|
||||
select(FinanceTransaction)
|
||||
.options(selectinload(FinanceTransaction.allocations))
|
||||
.where(FinanceTransaction.id == txn.id)
|
||||
)
|
||||
created = result.scalar_one()
|
||||
return _build_transaction_response(created)
|
||||
|
||||
|
||||
@router.post("/payments", response_model=FinanceTransactionResponse, status_code=201)
|
||||
async def create_payment(
|
||||
payload: PaymentCreate,
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
supplier_result = await db_session.execute(
|
||||
select(Supplier).where(Supplier.id == payload.supplier_id, Supplier.is_active == True)
|
||||
)
|
||||
supplier = supplier_result.scalar_one_or_none()
|
||||
if not supplier:
|
||||
raise HTTPException(status_code=404, detail="供应商不存在")
|
||||
|
||||
_validate_allocation_total(payload.amount, [item.allocated_amount for item in payload.allocations])
|
||||
|
||||
txn = FinanceTransaction(
|
||||
txn_no=generate_order_no("PY"),
|
||||
txn_type="payment",
|
||||
partner_type="supplier",
|
||||
partner_id=payload.supplier_id,
|
||||
amount=payload.amount,
|
||||
txn_date=payload.txn_date or datetime.now(),
|
||||
method=payload.method,
|
||||
account_name=payload.account_name,
|
||||
status="confirmed",
|
||||
remark=payload.remark,
|
||||
operator_id=current_user.id,
|
||||
)
|
||||
db_session.add(txn)
|
||||
await db_session.flush()
|
||||
|
||||
for allocation in payload.allocations:
|
||||
if allocation.order_type != "purchase":
|
||||
raise HTTPException(status_code=400, detail="付款单只允许核销采购订单")
|
||||
|
||||
order_result = await db_session.execute(
|
||||
select(PurchaseOrder).where(PurchaseOrder.id == allocation.order_id, PurchaseOrder.supplier_id == payload.supplier_id)
|
||||
)
|
||||
purchase_order = order_result.scalar_one_or_none()
|
||||
if not purchase_order:
|
||||
raise HTTPException(status_code=404, detail=f"采购订单不存在: {allocation.order_id}")
|
||||
|
||||
remaining = (purchase_order.total_amount or 0) - (purchase_order.paid_amount or 0)
|
||||
if allocation.allocated_amount - remaining > 1e-6:
|
||||
raise HTTPException(status_code=400, detail=f"采购订单核销超额: {purchase_order.order_no}")
|
||||
|
||||
db_session.add(
|
||||
FinanceAllocation(
|
||||
transaction_id=txn.id,
|
||||
order_type="purchase",
|
||||
order_id=purchase_order.id,
|
||||
allocated_amount=allocation.allocated_amount,
|
||||
)
|
||||
)
|
||||
purchase_order.paid_amount = (purchase_order.paid_amount or 0) + allocation.allocated_amount
|
||||
|
||||
await db_session.commit()
|
||||
result = await db_session.execute(
|
||||
select(FinanceTransaction)
|
||||
.options(selectinload(FinanceTransaction.allocations))
|
||||
.where(FinanceTransaction.id == txn.id)
|
||||
)
|
||||
created = result.scalar_one()
|
||||
return _build_transaction_response(created)
|
||||
|
||||
|
||||
@router.get("/transactions", response_model=List[FinanceTransactionResponse])
|
||||
async def list_transactions(
|
||||
txn_type: Optional[str] = None,
|
||||
status: Optional[str] = "confirmed",
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(20, ge=1, le=100),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
query = (
|
||||
select(FinanceTransaction)
|
||||
.options(selectinload(FinanceTransaction.allocations))
|
||||
.order_by(FinanceTransaction.created_at.desc())
|
||||
)
|
||||
if txn_type:
|
||||
query = query.where(FinanceTransaction.txn_type == txn_type)
|
||||
if status:
|
||||
query = query.where(FinanceTransaction.status == status)
|
||||
query = query.offset(skip).limit(limit)
|
||||
result = await db_session.execute(query)
|
||||
rows = result.scalars().all()
|
||||
return [_build_transaction_response(item) for item in rows]
|
||||
|
||||
|
||||
@router.post("/transactions/{transaction_id}/void")
|
||||
async def void_transaction(
|
||||
transaction_id: int,
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
result = await db_session.execute(
|
||||
select(FinanceTransaction)
|
||||
.options(selectinload(FinanceTransaction.allocations))
|
||||
.where(FinanceTransaction.id == transaction_id)
|
||||
)
|
||||
txn = result.scalar_one_or_none()
|
||||
if not txn:
|
||||
raise HTTPException(status_code=404, detail="财务单据不存在")
|
||||
if txn.status == "voided":
|
||||
return {"message": "单据已作废"}
|
||||
|
||||
for allocation in txn.allocations:
|
||||
if allocation.order_type == "sales":
|
||||
sales_result = await db_session.execute(select(SalesOrder).where(SalesOrder.id == allocation.order_id))
|
||||
sales_order = sales_result.scalar_one_or_none()
|
||||
if sales_order:
|
||||
sales_order.received_amount = max((sales_order.received_amount or 0) - allocation.allocated_amount, 0)
|
||||
elif allocation.order_type == "purchase":
|
||||
purchase_result = await db_session.execute(select(PurchaseOrder).where(PurchaseOrder.id == allocation.order_id))
|
||||
purchase_order = purchase_result.scalar_one_or_none()
|
||||
if purchase_order:
|
||||
purchase_order.paid_amount = max((purchase_order.paid_amount or 0) - allocation.allocated_amount, 0)
|
||||
|
||||
txn.status = "voided"
|
||||
await db_session.commit()
|
||||
return {"message": "单据已作废"}
|
||||
|
||||
|
||||
@router.get("/receivables", response_model=List[ReceivableItemResponse])
|
||||
async def list_receivables(
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
result = await db_session.execute(
|
||||
select(SalesOrder, Customer)
|
||||
.join(Customer, SalesOrder.customer_id == Customer.id)
|
||||
.where((SalesOrder.total_amount - SalesOrder.received_amount) > 0)
|
||||
.order_by(SalesOrder.created_at.desc())
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
)
|
||||
rows = []
|
||||
for order, customer in result.all():
|
||||
receivable_amount = (order.total_amount or 0) - (order.received_amount or 0)
|
||||
rows.append(
|
||||
ReceivableItemResponse(
|
||||
order_id=order.id,
|
||||
order_no=order.order_no,
|
||||
customer_id=customer.id,
|
||||
customer_name=customer.name,
|
||||
order_date=order.order_date,
|
||||
total_amount=order.total_amount or 0,
|
||||
received_amount=order.received_amount or 0,
|
||||
receivable_amount=receivable_amount,
|
||||
status=order.status,
|
||||
)
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
@router.get("/payables", response_model=List[PayableItemResponse])
|
||||
async def list_payables(
|
||||
skip: int = Query(0, ge=0),
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
result = await db_session.execute(
|
||||
select(PurchaseOrder, Supplier)
|
||||
.join(Supplier, PurchaseOrder.supplier_id == Supplier.id)
|
||||
.where((PurchaseOrder.total_amount - PurchaseOrder.paid_amount) > 0)
|
||||
.order_by(PurchaseOrder.created_at.desc())
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
)
|
||||
rows = []
|
||||
for order, supplier in result.all():
|
||||
payable_amount = (order.total_amount or 0) - (order.paid_amount or 0)
|
||||
rows.append(
|
||||
PayableItemResponse(
|
||||
order_id=order.id,
|
||||
order_no=order.order_no,
|
||||
supplier_id=supplier.id,
|
||||
supplier_name=supplier.name,
|
||||
order_date=order.order_date,
|
||||
total_amount=order.total_amount or 0,
|
||||
paid_amount=order.paid_amount or 0,
|
||||
payable_amount=payable_amount,
|
||||
status=order.status,
|
||||
)
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
@router.get("/summary", response_model=FinanceSummaryResponse)
|
||||
async def get_finance_summary(
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
current_user: User = Depends(get_current_active_user),
|
||||
):
|
||||
receivable_total = await db_session.scalar(
|
||||
select(func.coalesce(func.sum(SalesOrder.total_amount - SalesOrder.received_amount), 0))
|
||||
.where((SalesOrder.total_amount - SalesOrder.received_amount) > 0)
|
||||
) or 0
|
||||
payable_total = await db_session.scalar(
|
||||
select(func.coalesce(func.sum(PurchaseOrder.total_amount - PurchaseOrder.paid_amount), 0))
|
||||
.where((PurchaseOrder.total_amount - PurchaseOrder.paid_amount) > 0)
|
||||
) or 0
|
||||
|
||||
now = datetime.now()
|
||||
month_start = datetime(now.year, now.month, 1)
|
||||
|
||||
monthly_receipt_total = await db_session.scalar(
|
||||
select(func.coalesce(func.sum(FinanceTransaction.amount), 0))
|
||||
.where(FinanceTransaction.txn_type == "receipt")
|
||||
.where(FinanceTransaction.status == "confirmed")
|
||||
.where(FinanceTransaction.txn_date >= month_start)
|
||||
) or 0
|
||||
monthly_payment_total = await db_session.scalar(
|
||||
select(func.coalesce(func.sum(FinanceTransaction.amount), 0))
|
||||
.where(FinanceTransaction.txn_type == "payment")
|
||||
.where(FinanceTransaction.status == "confirmed")
|
||||
.where(FinanceTransaction.txn_date >= month_start)
|
||||
) or 0
|
||||
|
||||
return FinanceSummaryResponse(
|
||||
receivable_total=round(float(receivable_total), 2),
|
||||
payable_total=round(float(payable_total), 2),
|
||||
monthly_receipt_total=round(float(monthly_receipt_total), 2),
|
||||
monthly_payment_total=round(float(monthly_payment_total), 2),
|
||||
overdue_receivable_count=0,
|
||||
overdue_payable_count=0,
|
||||
)
|
||||
|
||||
@@ -14,6 +14,17 @@ from .sales_order_schemas import (
|
||||
SalesOrderResponse,
|
||||
SalesOrderItemCreate
|
||||
)
|
||||
from .finance_schemas import (
|
||||
FinanceAllocationCreate,
|
||||
FinanceTransactionCreate,
|
||||
ReceiptCreate,
|
||||
PaymentCreate,
|
||||
FinanceAllocationResponse,
|
||||
FinanceTransactionResponse,
|
||||
FinanceSummaryResponse,
|
||||
ReceivableItemResponse,
|
||||
PayableItemResponse
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"ProductCreate", "ProductResponse",
|
||||
@@ -24,4 +35,8 @@ __all__ = [
|
||||
"StockMovementCreate", "StockMovementResponse",
|
||||
"PurchaseOrderCreate", "PurchaseOrderResponse", "PurchaseOrderItemCreate",
|
||||
"SalesOrderCreate", "SalesOrderResponse", "SalesOrderItemCreate",
|
||||
"FinanceAllocationCreate", "FinanceTransactionCreate",
|
||||
"ReceiptCreate", "PaymentCreate",
|
||||
"FinanceAllocationResponse", "FinanceTransactionResponse",
|
||||
"FinanceSummaryResponse", "ReceivableItemResponse", "PayableItemResponse",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import Optional, List, Literal
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
TxnType = Literal["receipt", "payment"]
|
||||
PartnerType = Literal["customer", "supplier"]
|
||||
OrderType = Literal["sales", "purchase"]
|
||||
TxnStatus = Literal["confirmed", "voided"]
|
||||
|
||||
|
||||
class FinanceAllocationCreate(BaseModel):
|
||||
order_type: OrderType
|
||||
order_id: int
|
||||
allocated_amount: float = Field(gt=0)
|
||||
|
||||
|
||||
class FinanceTransactionCreate(BaseModel):
|
||||
amount: float = Field(gt=0)
|
||||
txn_date: Optional[datetime] = None
|
||||
method: str = "bank"
|
||||
account_name: Optional[str] = None
|
||||
remark: Optional[str] = None
|
||||
allocations: List[FinanceAllocationCreate]
|
||||
|
||||
|
||||
class ReceiptCreate(FinanceTransactionCreate):
|
||||
customer_id: int
|
||||
|
||||
|
||||
class PaymentCreate(FinanceTransactionCreate):
|
||||
supplier_id: int
|
||||
|
||||
|
||||
class FinanceAllocationResponse(BaseModel):
|
||||
id: int
|
||||
order_type: str
|
||||
order_id: int
|
||||
allocated_amount: float
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class FinanceTransactionResponse(BaseModel):
|
||||
id: int
|
||||
txn_no: str
|
||||
txn_type: TxnType
|
||||
partner_type: PartnerType
|
||||
partner_id: int
|
||||
amount: float
|
||||
txn_date: datetime
|
||||
method: str
|
||||
account_name: Optional[str]
|
||||
status: TxnStatus
|
||||
remark: Optional[str]
|
||||
created_at: datetime
|
||||
allocations: List[FinanceAllocationResponse] = []
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class FinanceSummaryResponse(BaseModel):
|
||||
receivable_total: float
|
||||
payable_total: float
|
||||
monthly_receipt_total: float
|
||||
monthly_payment_total: float
|
||||
overdue_receivable_count: int = 0
|
||||
overdue_payable_count: int = 0
|
||||
|
||||
|
||||
class ReceivableItemResponse(BaseModel):
|
||||
order_id: int
|
||||
order_no: str
|
||||
customer_id: int
|
||||
customer_name: str
|
||||
order_date: datetime
|
||||
total_amount: float
|
||||
received_amount: float
|
||||
receivable_amount: float
|
||||
status: str
|
||||
|
||||
|
||||
class PayableItemResponse(BaseModel):
|
||||
order_id: int
|
||||
order_no: str
|
||||
supplier_id: int
|
||||
supplier_name: str
|
||||
order_date: datetime
|
||||
total_amount: float
|
||||
paid_amount: float
|
||||
payable_amount: float
|
||||
status: str
|
||||
@@ -29,15 +29,19 @@ DEFAULT_PERMISSIONS = [
|
||||
{"code": "manage_suppliers", "name": "管理供应商", "module": "inventory"},
|
||||
{"code": "view_customers", "name": "查看客户", "module": "inventory"},
|
||||
{"code": "manage_customers", "name": "管理客户", "module": "inventory"},
|
||||
{"code": "view_finance", "name": "查看财务", "module": "finance"},
|
||||
{"code": "manage_receipts", "name": "管理收款", "module": "finance"},
|
||||
{"code": "manage_payments", "name": "管理付款", "module": "finance"},
|
||||
{"code": "void_finance_transaction", "name": "作废财务单据", "module": "finance"},
|
||||
{"code": "view_users", "name": "查看用户", "module": "admin"},
|
||||
{"code": "manage_users", "name": "管理用户", "module": "admin"},
|
||||
{"code": "manage_roles", "name": "管理角色", "module": "admin"},
|
||||
]
|
||||
|
||||
DEFAULT_ROLES = [
|
||||
{"code": "admin", "name": "管理员", "description": "系统管理员,拥有所有权限", "is_system": True, "permissions": ["view_dashboard", "view_moldinsight", "upload_file", "view_history", "view_inventory", "manage_inventory", "view_products", "manage_products", "view_suppliers", "manage_suppliers", "view_customers", "manage_customers", "view_users", "manage_users", "manage_roles"]},
|
||||
{"code": "user", "name": "普通用户", "description": "普通用户,可使用模具分析和查看库存", "is_system": False, "permissions": ["view_dashboard", "view_moldinsight", "upload_file", "view_history", "view_inventory", "view_products", "view_suppliers", "view_customers"]},
|
||||
{"code": "viewer", "name": "只读用户", "description": "只读用户,只能查看数据", "is_system": False, "permissions": ["view_dashboard", "view_moldinsight", "view_history", "view_inventory", "view_products", "view_suppliers", "view_customers"]},
|
||||
{"code": "admin", "name": "管理员", "description": "系统管理员,拥有所有权限", "is_system": True, "permissions": ["view_dashboard", "view_moldinsight", "upload_file", "view_history", "view_inventory", "manage_inventory", "view_products", "manage_products", "view_suppliers", "manage_suppliers", "view_customers", "manage_customers", "view_finance", "manage_receipts", "manage_payments", "void_finance_transaction", "view_users", "manage_users", "manage_roles"]},
|
||||
{"code": "user", "name": "普通用户", "description": "普通用户,可使用模具分析和查看库存", "is_system": False, "permissions": ["view_dashboard", "view_moldinsight", "upload_file", "view_history", "view_inventory", "view_products", "view_suppliers", "view_customers", "view_finance", "manage_receipts", "manage_payments"]},
|
||||
{"code": "viewer", "name": "只读用户", "description": "只读用户,只能查看数据", "is_system": False, "permissions": ["view_dashboard", "view_moldinsight", "view_history", "view_inventory", "view_products", "view_suppliers", "view_customers", "view_finance"]},
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -681,6 +681,45 @@ class SalesOrder(Base):
|
||||
return f"<SalesOrder(order_no='{self.order_no}', status='{self.status}')>"
|
||||
|
||||
|
||||
class FinanceTransaction(Base):
|
||||
__tablename__ = "finance_transactions"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
txn_no = Column(String(50), unique=True, index=True, nullable=False)
|
||||
txn_type = Column(String(20), nullable=False, index=True)
|
||||
partner_type = Column(String(20), nullable=False, index=True)
|
||||
partner_id = Column(Integer, nullable=False, index=True)
|
||||
amount = Column(Float, nullable=False)
|
||||
txn_date = Column(DateTime, default=func.now(), index=True)
|
||||
method = Column(String(30), default="bank")
|
||||
account_name = Column(String(100), nullable=True)
|
||||
status = Column(String(20), default="confirmed", index=True)
|
||||
remark = Column(Text, nullable=True)
|
||||
operator_id = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
created_at = Column(DateTime, default=func.now(), index=True)
|
||||
|
||||
allocations = relationship("FinanceAllocation", back_populates="transaction", cascade="all, delete-orphan")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<FinanceTransaction(txn_no='{self.txn_no}', txn_type='{self.txn_type}', amount={self.amount})>"
|
||||
|
||||
|
||||
class FinanceAllocation(Base):
|
||||
__tablename__ = "finance_allocations"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
transaction_id = Column(Integer, ForeignKey("finance_transactions.id"), nullable=False, index=True)
|
||||
order_type = Column(String(20), nullable=False, index=True)
|
||||
order_id = Column(Integer, nullable=False, index=True)
|
||||
allocated_amount = Column(Float, nullable=False)
|
||||
created_at = Column(DateTime, default=func.now(), index=True)
|
||||
|
||||
transaction = relationship("FinanceTransaction", back_populates="allocations")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<FinanceAllocation(transaction_id={self.transaction_id}, order_type='{self.order_type}', amount={self.allocated_amount})>"
|
||||
|
||||
|
||||
class AnalysisMetrics(Base):
|
||||
"""分析指标表"""
|
||||
__tablename__ = "analysis_metrics"
|
||||
|
||||
Reference in New Issue
Block a user