feat: 新增预算后端服务与差旅风险规则库
后端新增预算模型、端点和服务模块,支持预算 CRUD 和余额 查询,清理旧生成规则文件并替换为按严重等级分类的差旅风 险规则库,优化认证权限和报销单访问策略,新增财务规则目 录和演示数据构建脚本,前端预算中心增加对话框交互,完善 审计页面运行时模型和元数据展示,补充单元测试。
This commit is contained in:
120
server/src/app/schemas/budget.py
Normal file
120
server/src/app/schemas/budget.py
Normal file
@@ -0,0 +1,120 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field
|
||||
|
||||
|
||||
class BudgetAllocationCreate(BaseModel):
|
||||
fiscal_year: int = Field(ge=2000, le=2100)
|
||||
period_type: str = Field(default="quarter", max_length=20)
|
||||
period_key: str = Field(min_length=1, max_length=30)
|
||||
department_id: str | None = Field(default=None, max_length=36)
|
||||
department_name: str = Field(min_length=1, max_length=100)
|
||||
cost_center: str | None = Field(default=None, max_length=50)
|
||||
project_code: str | None = Field(default=None, max_length=50)
|
||||
subject_code: str = Field(min_length=1, max_length=50)
|
||||
subject_name: str = Field(min_length=1, max_length=100)
|
||||
original_amount: Decimal = Field(ge=0)
|
||||
warning_threshold: Decimal = Field(default=Decimal("80.00"), ge=0, le=100)
|
||||
control_action: str = Field(default="block", max_length=30)
|
||||
description: str | None = Field(default=None, max_length=500)
|
||||
|
||||
|
||||
class BudgetCheckRequest(BaseModel):
|
||||
fiscal_year: int | None = Field(default=None, ge=2000, le=2100)
|
||||
period_key: str | None = Field(default=None, max_length=30)
|
||||
department_id: str | None = Field(default=None, max_length=36)
|
||||
department_name: str | None = Field(default=None, max_length=100)
|
||||
cost_center: str | None = Field(default=None, max_length=50)
|
||||
project_code: str | None = Field(default=None, max_length=50)
|
||||
subject_code: str = Field(min_length=1, max_length=50)
|
||||
amount: Decimal = Field(ge=0)
|
||||
|
||||
|
||||
class BudgetOperationRequest(BudgetCheckRequest):
|
||||
source_type: str = Field(min_length=1, max_length=40)
|
||||
source_id: str = Field(min_length=1, max_length=64)
|
||||
source_no: str = Field(min_length=1, max_length=80)
|
||||
reason: str | None = Field(default=None, max_length=500)
|
||||
|
||||
|
||||
class BudgetBalanceRead(BaseModel):
|
||||
total_amount: Decimal
|
||||
reserved_amount: Decimal
|
||||
consumed_amount: Decimal
|
||||
available_amount: Decimal
|
||||
usage_rate: Decimal
|
||||
|
||||
|
||||
class BudgetAllocationRead(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: str
|
||||
budget_no: str
|
||||
fiscal_year: int
|
||||
period_type: str
|
||||
period_key: str
|
||||
department_id: str | None
|
||||
department_name: str
|
||||
cost_center: str | None
|
||||
project_code: str | None
|
||||
subject_code: str
|
||||
subject_name: str
|
||||
original_amount: Decimal
|
||||
adjusted_amount: Decimal
|
||||
status: str
|
||||
warning_threshold: Decimal
|
||||
control_action: str
|
||||
description: str | None = None
|
||||
balance: BudgetBalanceRead
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class BudgetTransactionRead(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: str
|
||||
transaction_no: str
|
||||
allocation_id: str
|
||||
reservation_id: str | None
|
||||
source_type: str
|
||||
source_id: str
|
||||
source_no: str
|
||||
transaction_type: str
|
||||
amount: Decimal
|
||||
before_available_amount: Decimal
|
||||
after_available_amount: Decimal
|
||||
operator: str | None
|
||||
reason: str | None
|
||||
created_at: datetime
|
||||
|
||||
|
||||
class BudgetSummaryRead(BaseModel):
|
||||
fiscal_year: int | None = None
|
||||
period_key: str | None = None
|
||||
total_amount: Decimal
|
||||
reserved_amount: Decimal
|
||||
consumed_amount: Decimal
|
||||
available_amount: Decimal
|
||||
warning_count: int
|
||||
over_budget_count: int
|
||||
allocations: list[BudgetAllocationRead] = Field(default_factory=list)
|
||||
|
||||
|
||||
class BudgetCheckRead(BaseModel):
|
||||
passed: bool
|
||||
blocking_reasons: list[str] = Field(default_factory=list)
|
||||
flags: list[dict] = Field(default_factory=list)
|
||||
allocation: BudgetAllocationRead | None = None
|
||||
|
||||
|
||||
class BudgetOperationRead(BaseModel):
|
||||
ok: bool
|
||||
message: str
|
||||
reservation_id: str | None = None
|
||||
allocation: BudgetAllocationRead | None = None
|
||||
transaction: BudgetTransactionRead | None = None
|
||||
flags: list[dict] = Field(default_factory=list)
|
||||
@@ -23,7 +23,7 @@ class SettingsCompanyForm(BaseModel):
|
||||
|
||||
class SettingsAdminForm(BaseModel):
|
||||
adminAccount: str = Field(min_length=1, max_length=120)
|
||||
adminEmail: str = Field(min_length=1, max_length=255)
|
||||
adminEmail: str = Field(default="", max_length=255)
|
||||
newPassword: str = Field(default="", max_length=128)
|
||||
confirmPassword: str = Field(default="", max_length=128)
|
||||
sessionTimeout: int = Field(default=30, ge=5, le=240)
|
||||
|
||||
Reference in New Issue
Block a user