Files
X-Financial/server/src/app/schemas/reimbursement.py

216 lines
5.7 KiB
Python
Raw Normal View History

from __future__ import annotations
from datetime import date, datetime
from decimal import Decimal
from typing import Any
from pydantic import BaseModel, ConfigDict, Field
class ReimbursementCreate(BaseModel):
request_no: str
employee_id: str
title: str
category: str
amount: Decimal
reason: str | None = None
class ReimbursementRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
request_no: str
employee_id: str
title: str
category: str
status: str
amount: Decimal
reason: str | None
created_at: datetime
updated_at: datetime
class ExpenseClaimItemRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
item_date: date
item_type: str
item_reason: str
item_location: str
item_amount: Decimal
invoice_id: str | None
is_system_generated: bool = False
created_at: datetime
updated_at: datetime
class ExpenseClaimAttachmentAnalysisRead(BaseModel):
severity: str
label: str
headline: str
summary: str
points: list[str] = Field(default_factory=list)
rule_basis: list[str] = Field(default_factory=list)
suggestion: str = ""
class ExpenseClaimAttachmentDocumentFieldRead(BaseModel):
key: str
label: str
value: str
class ExpenseClaimAttachmentDocumentInfoRead(BaseModel):
document_type: str = "other"
document_type_label: str = "其他单据"
scene_code: str = "other"
scene_label: str = "其他票据"
fields: list[ExpenseClaimAttachmentDocumentFieldRead] = Field(default_factory=list)
class ExpenseClaimAttachmentRequirementRead(BaseModel):
matches: bool = False
current_expense_type: str = "other"
current_expense_type_label: str = "其他"
allowed_scene_labels: list[str] = Field(default_factory=list)
recognized_scene_code: str = "other"
recognized_scene_label: str = "其他票据"
recognized_document_type: str = "other"
recognized_document_type_label: str = "其他单据"
message: str = ""
class ExpenseClaimAttachmentRead(BaseModel):
file_name: str
storage_key: str
media_type: str
size_bytes: int
uploaded_at: datetime | None = None
previewable: bool = True
preview_kind: str = ""
preview_url: str = ""
analysis: ExpenseClaimAttachmentAnalysisRead | None = None
document_info: ExpenseClaimAttachmentDocumentInfoRead | None = None
requirement_check: ExpenseClaimAttachmentRequirementRead | None = None
class ExpenseClaimItemUpdate(BaseModel):
item_date: date | None = None
item_type: str | None = None
item_reason: str | None = None
item_location: str | None = None
item_amount: Decimal | None = None
invoice_id: str | None = None
class ExpenseClaimItemCreate(BaseModel):
item_date: date | None = None
item_type: str | None = None
item_reason: str | None = None
item_location: str | None = None
item_amount: Decimal | None = None
invoice_id: str | None = None
class ExpenseClaimUpdate(BaseModel):
reason: str | None = Field(default=None, max_length=500)
class ExpenseClaimRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: str
claim_no: str
employee_id: str | None
employee_name: str
department_id: str | None
department_name: str
employee_position: str | None = None
employee_grade: str | None = None
manager_name: str | None = None
budget_approver_name: str | None = None
budget_approver_grade: str | None = None
budget_approver_role_code: str | None = None
role_labels: list[str] = Field(default_factory=list)
project_code: str | None
expense_type: str
reason: str
location: str
amount: Decimal
currency: str
invoice_count: int
occurred_at: datetime
submitted_at: datetime | None
status: str
approval_stage: str | None
risk_flags_json: list[Any] = Field(default_factory=list)
created_at: datetime
updated_at: datetime
items: list[ExpenseClaimItemRead] = Field(default_factory=list)
class ExpenseClaimActionResponse(BaseModel):
message: str
claim_id: str
status: str | None = None
class ExpenseClaimReturnPayload(BaseModel):
reason: str | None = Field(default=None, max_length=500)
reason_codes: list[str] = Field(default_factory=list, max_length=10)
class ExpenseClaimApprovalPayload(BaseModel):
opinion: str | None = Field(default=None, max_length=500)
class TravelReimbursementCalculatorRequest(BaseModel):
days: int = Field(ge=1, le=365)
location: str = Field(min_length=1, max_length=120)
grade: str | None = Field(default=None, max_length=30)
class TravelReimbursementCalculatorResponse(BaseModel):
days: int
location: str
matched_city: str
city_tier: str
grade: str
grade_band: str
grade_band_label: str
hotel_rate: Decimal
hotel_amount: Decimal
allowance_region: str
meal_allowance_rate: Decimal
basic_allowance_rate: Decimal
total_allowance_rate: Decimal
allowance_amount: Decimal
total_amount: Decimal
rule_name: str
rule_version: str
formula_text: str
summary_text: str
class ExpenseClaimAttachmentActionResponse(BaseModel):
message: str
claim_id: str
item_id: str
invoice_id: str | None = None
item_date: date | None = None
item_type: str | None = None
item_reason: str | None = None
item_location: str | None = None
item_amount: Decimal | None = None
claim_amount: Decimal | None = None
claim_risk_flags: list[Any] = Field(default_factory=list)
attachment: ExpenseClaimAttachmentRead | None = None
class ExpenseClaimItemActionResponse(BaseModel):
message: str
claim_id: str
item_id: str