645 lines
18 KiB
Python
645 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: 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]
|