ForgeFlow-ERP/backend/app/schemas/database.py
2026-06-12 16:29:05 +08:00

652 lines
18 KiB
Python

from __future__ import annotations
from datetime import date, datetime
from pydantic import BaseModel, ConfigDict, Field
class DataCountItem(BaseModel):
code: str
label: str
count: int
class BroadcastMessageCreate(BaseModel):
title: str = Field(min_length=1, max_length=100)
content: str = Field(min_length=1, max_length=500)
attendance_point_names: list[str] = Field(default_factory=list)
tone: str = "INFO"
status: str = "ACTIVE"
starts_at: datetime | None = None
ends_at: datetime | None = None
sort_no: int = 0
class BroadcastMessageRead(BroadcastMessageCreate):
broadcast_id: int
created_by: int | str | None = None
created_at: datetime
updated_at: datetime
class AiAssistantConfigUpdate(BaseModel):
assistant_name: str = Field(default="嘉恒工艺助手", min_length=1, max_length=100)
provider_name: str | None = None
api_base_url: str | None = None
api_key: str | None = None
model_name: str | None = None
temperature: float = Field(default=0.3, ge=0, le=2)
system_prompt: str | None = None
enabled: bool = False
class AiAssistantConfigRead(AiAssistantConfigUpdate):
config_id: int | None = None
api_key_configured: bool = False
updated_at: datetime | None = None
class SystemConfigUpdate(BaseModel):
config_value: str = Field(min_length=1, max_length=500)
remark: str | None = Field(default=None, max_length=500)
class SystemConfigRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
config_code: str
config_name: str
config_value: str
remark: str | None = None
updated_at: datetime | None = None
class SmartOperationReportConfigUpdate(BaseModel):
enabled: bool
remark: str | None = Field(default=None, max_length=500)
class SmartOperationReportConfigRead(BaseModel):
config_code: str = "SMART_OPERATION_REPORT_ENABLED"
config_name: str = "对接智能报工小程序"
enabled: bool = True
config_value: str = "开启"
remark: str | None = None
updated_at: datetime | None = None
class LoginUser(BaseModel):
user_id: int
username: str
employee_id: int
mobile: str | None = None
display_name: str
title: str
dept_name: str | None = None
role_codes: list[str]
role_names: list[str]
permission_codes: list[str]
class AuthLoginRequest(BaseModel):
username: str
password_ciphertext: str = Field(min_length=1)
login_key_id: str = Field(min_length=1)
class AuthLoginKeyResponse(BaseModel):
key_id: str
public_key_pem: str
algorithm: str
class AuthChangePasswordRequest(BaseModel):
old_password: str = Field(min_length=1)
new_password: str = Field(min_length=6, max_length=128)
class AuthResetPasswordRequest(BaseModel):
username: str = Field(min_length=1, max_length=50)
new_password: str = Field(min_length=6, max_length=128)
class AuthSession(LoginUser):
access_token: str
token_type: str
expires_at: datetime
class DepartmentRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
dept_code: str
dept_name: str
dept_type: str
manager_name: str | None = None
status: str
class EmployeeRead(BaseModel):
id: int
employee_code: str
employee_name: str
dept_id: int
dept_name: str | None = None
mobile: str | None = None
gender: str | None = None
hire_date: date | None = None
job_title: str | None = None
shift_code: str | None = None
is_operator: bool
is_workshop_staff: bool
status: str
remark: str | None = None
username: str | None = None
role_names: list[str] = Field(default_factory=list)
class EmployeeCreate(BaseModel):
employee_name: str
dept_id: int = Field(gt=0)
mobile: str | None = None
gender: str | None = None
hire_date: date | None = None
job_title: str | None = None
shift_code: str | None = None
manager_employee_id: int | None = None
is_operator: bool = False
is_workshop_staff: bool = False
status: str = "ACTIVE"
remark: str | None = None
class EmployeeUpdate(BaseModel):
employee_name: str
dept_id: int = Field(gt=0)
mobile: str | None = None
gender: str | None = None
hire_date: date | None = None
job_title: str | None = None
shift_code: str | None = None
manager_employee_id: int | None = None
is_operator: bool = False
is_workshop_staff: bool = False
status: str = "ACTIVE"
remark: str | None = None
class MiniAppPersonnelRead(BaseModel):
phone: str
name: str
role: str | None = None
role_name: str | None = None
roles: list[str] = Field(default_factory=list)
role_names: list[str] = Field(default_factory=list)
attendance_point_names: list[str] = Field(default_factory=list)
is_temporary: bool = False
temporary_expires_at: datetime | None = None
temporary_expired: bool = False
created_at: datetime | None = None
updated_at: datetime | None = None
class MiniAppPersonnelUpsert(BaseModel):
phone: str = Field(min_length=1, max_length=20)
name: str = Field(min_length=1, max_length=64)
roles: list[str] = Field(default_factory=lambda: ["worker"])
attendance_point_names: list[str] = Field(default_factory=list)
is_temporary: bool = False
temporary_expires_at: datetime | None = None
class MiniAppAttendancePointRead(BaseModel):
name: str
latitude: float | None = None
longitude: float | None = None
radius_meters: int = 500
remark: str | None = None
is_active: bool = True
created_at: datetime | None = None
updated_at: datetime | None = None
class MiniAppProductRead(BaseModel):
attendance_point_name: str
project_no: str
product_name: str
profile_no: str | None = None
material_code: str | None = None
material_name: str | None = None
supplier: str | None = None
product_net_weight_kg: float | None = None
product_gross_weight_kg: float | None = None
allowed_scrap_rate: float | None = None
scrap_loss_rate: float | None = None
waste_price_yuan_per_kg: float | None = None
device_no: str
process_name: str | None = None
stamping_method: str | None = None
operator_count: float = 1
standard_beat: float = 0
standard_workload: float = 0
display_name: str
created_at: datetime | None = None
updated_at: datetime | None = None
class MiniAppProductUpsert(BaseModel):
attendance_point_name: str = Field(min_length=1, max_length=128)
project_no: str = Field(min_length=1, max_length=64)
product_name: str = Field(min_length=1, max_length=255)
profile_no: str | None = None
material_code: str | None = None
material_name: str | None = None
supplier: str | None = None
product_net_weight_kg: float | None = Field(default=None, ge=0)
product_gross_weight_kg: float | None = Field(default=None, ge=0)
allowed_scrap_rate: float | None = Field(default=None, ge=0)
scrap_loss_rate: float | None = Field(default=None, ge=0)
waste_price_yuan_per_kg: float | None = Field(default=None, ge=0)
device_no: str = Field(default="", max_length=64)
process_name: str = Field(default="", max_length=128)
stamping_method: str | None = None
operator_count: float = Field(default=1, ge=0)
standard_beat: float = Field(default=0, ge=0)
standard_workload: float = Field(default=0, ge=0)
original_attendance_point_name: str | None = None
original_project_no: str | None = None
original_product_name: str | None = None
original_device_no: str | None = None
original_process_name: str | None = None
class MiniAppProductImportResult(BaseModel):
imported: int = 0
updated: int = 0
skipped: int = 0
errors: list[str] = Field(default_factory=list)
material_sync: dict[str, object] = Field(default_factory=dict)
product_specs: dict[str, int] = Field(default_factory=dict)
message: str
class MiniAppMaterialWastePriceSync(BaseModel):
material_code: str | None = None
material_name: str | None = None
waste_price_yuan_per_kg: float = Field(default=0, ge=0)
class WarehouseRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
warehouse_code: str
warehouse_name: str
warehouse_type: str
status: str
class ItemRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
item_code: str
item_name: str
item_type: str
specification: str | None = None
material_grade: str | None = None
unit_weight_kg: float
safety_stock_weight_kg: float
status: str
class MaterialRead(BaseModel):
item_id: int
item_code: str
item_name: str
specification: str | None = None
material_grade: str | None = None
unit_weight_kg: float = 0
safety_stock_weight_kg: float = 0
scrap_sale_price: float = 0
default_supplier_id: int | None = None
supplier_name: str | None = None
purchase_calc_mode: str
min_purchase_qty: float
purchase_multiple_qty: float
purchase_multiple_weight_kg: float
lead_time_days: int
quality_rule: str | None = None
remark: str | None = None
status: str = "ACTIVE"
class ProductRead(BaseModel):
item_id: int
item_code: str
item_name: str
specification: str | None = None
drawing_no: str | None = None
product_family: str | None = None
version_no: str
unit_weight_kg: float
allowed_scrap_rate: float
standard_output_qty: float = 0
quality_standard: str | None = None
safety_stock_weight_kg: float = 0
status: str
class ProductCreate(BaseModel):
item_code: str | None = None
item_name: str
specification: str | None = None
drawing_no: str | None = None
product_family: str | None = None
version_no: str = "V1.0"
unit_weight_kg: float = Field(gt=0)
allowed_scrap_rate: float = Field(default=0, ge=0)
standard_output_qty: float = Field(default=0, ge=0)
quality_standard: str | None = None
safety_stock_weight_kg: float = Field(default=0, ge=0)
status: str = "ACTIVE"
class ProductUpdate(BaseModel):
item_code: str | None = None
item_name: str
specification: str | None = None
drawing_no: str | None = None
product_family: str | None = None
version_no: str
unit_weight_kg: float = Field(gt=0)
allowed_scrap_rate: float = Field(default=0, ge=0)
standard_output_qty: float = Field(default=0, ge=0)
quality_standard: str | None = None
safety_stock_weight_kg: float = Field(default=0, ge=0)
status: str = "ACTIVE"
class MaterialCreate(BaseModel):
item_code: str | None = None
item_name: str
specification: str
material_grade: str
unit_weight_kg: float = Field(gt=0)
safety_stock_weight_kg: float = Field(default=0, ge=0)
scrap_sale_price: float = Field(default=0, ge=0)
default_supplier_id: int | None = None
purchase_calc_mode: str = "BY_WEIGHT"
min_purchase_qty: float = Field(default=0, ge=0)
purchase_multiple_qty: float = Field(default=0, ge=0)
purchase_multiple_weight_kg: float = Field(default=0, ge=0)
lead_time_days: int = Field(default=0, ge=0)
quality_rule: str | None = None
remark: str | None = None
status: str = "ACTIVE"
class MaterialUpdate(MaterialCreate):
pass
class BomItemPayload(BaseModel):
seq_no: int = Field(gt=0)
material_item_id: int = Field(gt=0)
usage_type: str = "WEIGHT"
qty_per_piece: float = Field(default=1, gt=0)
net_weight_per_piece_kg: float = Field(default=0, ge=0)
gross_weight_per_piece_kg: float = Field(default=0, ge=0)
loss_rate: float = Field(default=0, ge=0)
issue_over_tolerance_rate: float = Field(default=0, ge=0)
remark: str | None = None
class BomItemRead(BomItemPayload):
bom_item_id: int
material_code: str
material_name: str
seq_no: int
material_item_id: int
usage_type: str
qty_per_piece: float
net_weight_per_piece_kg: float
gross_weight_per_piece_kg: float
loss_rate: float
issue_over_tolerance_rate: float
remark: str | None = None
class BomRead(BaseModel):
bom_id: int
bom_code: str
product_item_id: int
product_code: str
product_name: str
version_no: str
is_default: bool
status: str
effective_date: date | None = None
expire_date: date | None = None
remark: str | None = None
item_count: int = 0
items: list[BomItemRead] = Field(default_factory=list)
class BomUpsert(BaseModel):
bom_code: str | None = None
product_item_id: int
version_no: str = "V1.0"
is_default: bool = True
status: str = "ACTIVE"
effective_date: date | None = None
expire_date: date | None = None
remark: str | None = None
items: list[BomItemPayload]
class ProcessRead(BaseModel):
process_id: int
process_code: str
process_name: str
process_type: str
is_report_required: bool
is_quality_gate: bool
status: str
class RouteOperationPayload(BaseModel):
seq_no: int = Field(gt=0)
process_id: int = Field(gt=0)
work_center_id: int = Field(gt=0)
operation_name: str
std_setup_minutes: float = Field(default=0, ge=0)
std_cycle_seconds: float = Field(default=0, ge=0)
std_labor_cost: float = Field(default=0, ge=0)
std_machine_cost: float = Field(default=0, ge=0)
std_outsource_cost: float = Field(default=0, ge=0)
allowed_scrap_rate: float = Field(default=0, ge=0)
report_required: bool = True
is_key_operation: bool = False
status: str = "ACTIVE"
remark: str | None = None
class RouteOperationRead(RouteOperationPayload):
route_operation_id: int
process_code: str
process_name: str
stamping_method: str | None = None
work_center_code: str
work_center_name: str
seq_no: int
process_id: int
work_center_id: int
operation_name: str
std_setup_minutes: float
std_cycle_seconds: float
std_labor_cost: float
std_machine_cost: float
std_outsource_cost: float
allowed_scrap_rate: float
report_required: bool
is_key_operation: bool
status: str
remark: str | None = None
class ProcessRouteRead(BaseModel):
route_id: int
route_code: str
route_name: str
product_item_id: int
product_code: str
product_name: str
version_no: str
is_default: bool
status: str
effective_date: date | None = None
expire_date: date | None = None
remark: str | None = None
operation_count: int = 0
operations: list[RouteOperationRead] = Field(default_factory=list)
class ProcessRouteUpsert(BaseModel):
route_code: str | None = None
route_name: str
product_item_id: int
version_no: str = "V1.0"
is_default: bool = True
status: str = "ACTIVE"
effective_date: date | None = None
expire_date: date | None = None
remark: str | None = None
operations: list[RouteOperationPayload]
class SalesOrderRead(BaseModel):
order_id: int
order_no: str
customer_id: int
customer_code: str
customer_name: str
sales_employee_id: int | None = None
sales_employee_name: str | None = None
order_date: date
promised_date: date | None = None
delivery_address: str | None = None
total_amount: float
status: str
line_count: int
mrp_count: int = 0
purchase_order_count: int = 0
delivery_count: int = 0
delivery_item_count: int = 0
work_order_count: int = 0
can_delete: bool = False
archive_status: str = "未生成"
archive_version: int | None = None
archive_created_at: datetime | None = None
archive_error_message: str | None = None
class CustomerRead(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
customer_code: str
customer_name: str
short_name: str | None = None
contact_name: str | None = None
contact_phone: str | None = None
contacts: list[dict[str, str]] = Field(default_factory=list, max_length=3)
address: str | None = None
credit_days: int = 0
status: str
class CustomerCreate(BaseModel):
customer_name: str
short_name: str | None = None
contact_name: str | None = None
contact_phone: str | None = None
contacts: list[dict[str, str]] = Field(default_factory=list, max_length=3)
address: str | None = None
tax_no: str | None = None
credit_days: int = 30
class SalesOrderItemCreate(BaseModel):
product_item_id: int
order_qty: float
unit_price: float
promised_date: date | None = None
customer_part_no: str | None = None
class SalesOrderCreate(BaseModel):
customer_id: int
sales_employee_id: int | None = None
promised_date: date | None = None
delivery_address: str | None = None
tax_rate: float = 0
remark: str | None = None
items: list[SalesOrderItemCreate]
class SalesOrderItemRead(BaseModel):
sales_order_item_id: int
sales_order_id: int
order_no: str
line_no: int
product_item_id: int
product_code: str
product_name: str
customer_part_no: str | None = None
order_qty: float
delivered_qty: float
unit_price: float
line_amount: float
promised_date: date | None = None
status: str
class SalesOrderCreateResult(BaseModel):
order_id: int
order_no: str
total_amount: float
item_count: int
status: str
archive_status: str = "未生成"
archive_version: int | None = None
archive_error_message: str | None = None
class MaterialDemandRead(BaseModel):
demand_id: int
demand_no: str
order_no: str
material_item_id: int
product_code: str
product_name: str
material_code: str
material_name: str
purchase_calc_mode: str
order_qty: float
theoretical_weight_kg: float
planned_weight_kg: float
shortage_weight_kg: float
suggested_purchase_qty: float
suggested_purchase_weight_kg: float
status: str
generated_at: datetime | None = None
class MrpGenerationResult(BaseModel):
sales_order_id: int
order_no: str
generated_count: int
demand_rows: list[MaterialDemandRead]