847 lines
24 KiB
Python
847 lines
24 KiB
Python
from datetime import date, datetime
|
|
from typing import Any, Literal
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
|
|
from app.models import ReportStatus, Role
|
|
|
|
|
|
class Page(BaseModel):
|
|
page: int = 1
|
|
page_size: int = 10
|
|
total: int = 0
|
|
total_pages: int = 1
|
|
|
|
|
|
class PageResponse(BaseModel):
|
|
page: int
|
|
page_size: int
|
|
total: int
|
|
total_pages: int
|
|
rows: list[Any]
|
|
|
|
|
|
class TokenResponse(BaseModel):
|
|
access_token: str
|
|
token_type: str = "bearer"
|
|
user: "PersonnelOut"
|
|
|
|
|
|
class AttendancePointBase(BaseModel):
|
|
name: str = Field(min_length=1, max_length=128)
|
|
latitude: float | None = None
|
|
longitude: float | None = None
|
|
radius_meters: int = Field(default=500, ge=1)
|
|
day_start: str = Field(default="08:00", pattern=r"^\d{1,2}:\d{2}$")
|
|
day_end: str = Field(default="17:20", pattern=r"^\d{1,2}:\d{2}$")
|
|
lunch_start: str = Field(default="11:40", pattern=r"^\d{1,2}:\d{2}$")
|
|
lunch_end: str = Field(default="12:40", pattern=r"^\d{1,2}:\d{2}$")
|
|
dinner_start: str = Field(default="17:20", pattern=r"^\d{1,2}:\d{2}$")
|
|
dinner_end: str = Field(default="18:00", pattern=r"^\d{1,2}:\d{2}$")
|
|
overtime_start: str = Field(default="18:00", pattern=r"^\d{1,2}:\d{2}$")
|
|
overtime_end: str = Field(default="20:00", pattern=r"^\d{1,2}:\d{2}$")
|
|
night_start: str = Field(default="20:00", pattern=r"^\d{1,2}:\d{2}$")
|
|
night_end: str = Field(default="06:00", pattern=r"^\d{1,2}:\d{2}$")
|
|
remark: str | None = Field(default=None, max_length=500)
|
|
is_active: bool = True
|
|
|
|
|
|
class AttendancePointCreate(AttendancePointBase):
|
|
original_name: str | None = None
|
|
|
|
|
|
class AttendancePointOut(AttendancePointBase):
|
|
created_at: datetime | None = None
|
|
updated_at: datetime | None = None
|
|
|
|
|
|
class LoginSelectionResponse(BaseModel):
|
|
needs_selection: bool
|
|
matched_people: list["PersonnelOut"] = []
|
|
selection_token: str | None = None
|
|
access_token: str | None = None
|
|
token_type: str = "bearer"
|
|
user: "PersonnelOut | None" = None
|
|
needs_temporary_worker: bool = False
|
|
temporary_token: str | None = None
|
|
temporary_phone: str | None = None
|
|
temporary_expires_at: datetime | None = None
|
|
|
|
|
|
class WechatLoginRequest(BaseModel):
|
|
phone_code: str | None = None
|
|
phone: str | None = None
|
|
phone_candidates: list[str] = []
|
|
selected_role: Role | None = None
|
|
create_temporary_worker: bool = False
|
|
temporary_token: str | None = None
|
|
temporary_name: str | None = None
|
|
temporary_attendance_point_name: str | None = None
|
|
temporary_latitude: float | None = None
|
|
temporary_longitude: float | None = None
|
|
|
|
|
|
class RoleSelectionRequest(BaseModel):
|
|
selection_token: str
|
|
role: Role
|
|
|
|
|
|
class SwitchRoleRequest(BaseModel):
|
|
role: Role
|
|
|
|
|
|
class PersonnelBase(BaseModel):
|
|
phone: str = Field(min_length=5, max_length=20)
|
|
name: str = Field(min_length=1, max_length=64)
|
|
role: Role
|
|
attendance_point_names: list[str] = []
|
|
|
|
|
|
class PersonnelCreate(PersonnelBase):
|
|
original_phone: str | None = None
|
|
|
|
|
|
class PersonnelOut(PersonnelBase):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
role_name: str
|
|
roles: list[Role] = Field(default_factory=list)
|
|
role_names: list[str] = Field(default_factory=list)
|
|
attendance_points: list[AttendancePointOut] = Field(default_factory=list)
|
|
is_temporary: bool = False
|
|
temporary_expires_at: datetime | None = None
|
|
temporary_expired: bool = False
|
|
|
|
|
|
class ProductBase(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 = None
|
|
product_gross_weight_kg: float | None = None
|
|
scrap_loss_rate: float | None = None
|
|
waste_price_yuan_per_kg: float | None = None
|
|
device_no: str = ""
|
|
process_name: str = Field(min_length=1, max_length=128)
|
|
stamping_method: str | None = None
|
|
operator_count: float = 1
|
|
process_unit_price_yuan: float = Field(default=0, ge=0)
|
|
standard_beat: float = Field(default=0, ge=0)
|
|
standard_workload: float | None = None
|
|
|
|
|
|
class ProductCreate(ProductBase):
|
|
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 ProductOut(ProductBase):
|
|
standard_workload: float
|
|
display_name: str
|
|
|
|
|
|
class MoldNameOut(BaseModel):
|
|
attendance_point_name: str = ""
|
|
name: str
|
|
process_name: str = ""
|
|
stamping_method: str | None = None
|
|
operator_count: float = 1
|
|
is_cleaning: bool = False
|
|
is_misc: bool = False
|
|
is_continuous_die: bool = False
|
|
is_multi_person: bool = False
|
|
display_name: str = ""
|
|
|
|
|
|
class DeviceQrCreate(BaseModel):
|
|
attendance_point_name: str = Field(min_length=1, max_length=128)
|
|
product_name: str = Field(min_length=1, max_length=255)
|
|
process_name: str = Field(min_length=1, max_length=128)
|
|
|
|
|
|
class DeviceQrBatchCreate(BaseModel):
|
|
items: list[DeviceQrCreate] = Field(min_length=1, max_length=500)
|
|
|
|
|
|
class DeviceQrBatchOut(BaseModel):
|
|
count: int
|
|
file_name: str
|
|
zip_url: str
|
|
|
|
|
|
class DeviceQrBatchTaskOut(BaseModel):
|
|
id: int
|
|
file_name: str
|
|
status: str
|
|
status_name: str
|
|
item_count: int
|
|
completed_count: int
|
|
failed_count: int = 0
|
|
progress_percent: float = 0
|
|
zip_url: str | None = None
|
|
error_message: str | None = None
|
|
started_at: datetime | None = None
|
|
finished_at: datetime | None = None
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class EquipmentBase(BaseModel):
|
|
attendance_point_name: str = Field(min_length=1, max_length=128)
|
|
device_no: str = Field(min_length=1, max_length=64)
|
|
device_type: Literal["冲压设备", "清洗设备"] = "冲压设备"
|
|
remark: str | None = Field(default=None, max_length=500)
|
|
|
|
|
|
class EquipmentCreate(EquipmentBase):
|
|
original_attendance_point_name: str | None = None
|
|
original_device_no: str | None = None
|
|
|
|
|
|
class EquipmentOut(EquipmentBase):
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class WorkScheduleBase(BaseModel):
|
|
day_start: str = Field(pattern=r"^\d{1,2}:\d{2}$")
|
|
day_end: str = Field(pattern=r"^\d{1,2}:\d{2}$")
|
|
lunch_start: str = Field(pattern=r"^\d{1,2}:\d{2}$")
|
|
lunch_end: str = Field(pattern=r"^\d{1,2}:\d{2}$")
|
|
dinner_start: str = Field(pattern=r"^\d{1,2}:\d{2}$")
|
|
dinner_end: str = Field(pattern=r"^\d{1,2}:\d{2}$")
|
|
overtime_start: str = Field(pattern=r"^\d{1,2}:\d{2}$")
|
|
overtime_end: str = Field(pattern=r"^\d{1,2}:\d{2}$")
|
|
night_start: str = Field(pattern=r"^\d{1,2}:\d{2}$")
|
|
night_end: str = Field(pattern=r"^\d{1,2}:\d{2}$")
|
|
attendance_latitude: float | None = None
|
|
attendance_longitude: float | None = None
|
|
attendance_radius_meters: int | None = Field(default=500, ge=1)
|
|
auto_submit_hours: float = Field(default=15, ge=1, le=168)
|
|
|
|
|
|
class WorkScheduleUpdate(WorkScheduleBase):
|
|
pass
|
|
|
|
|
|
class WorkScheduleSettingsUpdate(WorkScheduleBase):
|
|
attendance_points: list[AttendancePointCreate] = Field(default_factory=list)
|
|
|
|
|
|
class WorkScheduleOut(WorkScheduleBase):
|
|
id: int
|
|
attendance_points: list[AttendancePointOut] = Field(default_factory=list)
|
|
updated_by: str | None = None
|
|
updated_at: datetime
|
|
|
|
|
|
class ImportResult(BaseModel):
|
|
imported: int
|
|
updated: int
|
|
skipped: int
|
|
errors: list[str]
|
|
|
|
|
|
class ClockState(BaseModel):
|
|
action: Literal["start", "switch", "switch_or_finish", "finish", "report", "refinish"]
|
|
action_text: str
|
|
attendance_point_name: str = ""
|
|
device_no: str
|
|
mold_name: str = ""
|
|
process_name: str = ""
|
|
stamping_method: str | None = None
|
|
display_name: str = ""
|
|
is_cleaning: bool = False
|
|
is_misc: bool = False
|
|
current_mold_name: str = ""
|
|
current_process_name: str = ""
|
|
session_id: int | None = None
|
|
start_at: datetime | None = None
|
|
end_at: datetime | None = None
|
|
devices: list[str] = []
|
|
auto_submit_hours: float = 15
|
|
|
|
|
|
class MoldLockConflictDetail(BaseModel):
|
|
code: str = "mold_occupied"
|
|
message: str
|
|
lock_id: int
|
|
attendance_point_name: str = ""
|
|
mold_name: str = ""
|
|
process_name: str = ""
|
|
mold_display_name: str = ""
|
|
occupied_phone: str = ""
|
|
occupied_name: str = ""
|
|
|
|
|
|
class ClockRequest(BaseModel):
|
|
attendance_point_name: str = ""
|
|
device_no: str = ""
|
|
mold_name: str = ""
|
|
process_name: str = ""
|
|
latitude: float | None = None
|
|
longitude: float | None = None
|
|
|
|
|
|
class ContinueWorkRequest(BaseModel):
|
|
session_id: int
|
|
|
|
|
|
class ClockSessionOut(BaseModel):
|
|
id: int
|
|
attendance_point_name: str = ""
|
|
employee_phone: str
|
|
start_at: datetime
|
|
end_at: datetime | None
|
|
status: str
|
|
devices: list[str]
|
|
|
|
|
|
class ProductOption(BaseModel):
|
|
attendance_point_name: str = ""
|
|
project_no: str
|
|
product_name: str
|
|
material_code: str | None = None
|
|
material_name: str | None = None
|
|
device_no: str
|
|
process_name: str | None = None
|
|
stamping_method: str | None = None
|
|
operator_count: float = 1
|
|
process_unit_price_yuan: float = 0
|
|
standard_beat: float
|
|
standard_workload: float
|
|
is_cleaning: bool = False
|
|
is_misc: bool = False
|
|
is_continuous_die: bool = False
|
|
is_multi_person: bool = False
|
|
display_name: str
|
|
|
|
|
|
class EquipmentOption(BaseModel):
|
|
attendance_point_name: str = ""
|
|
device_no: str
|
|
device_type: str = "冲压设备"
|
|
remark: str | None = None
|
|
display_name: str
|
|
|
|
|
|
class ReportDraftItem(BaseModel):
|
|
attendance_point_name: str = ""
|
|
device_no: str = ""
|
|
project_no: str = ""
|
|
product_name: str = ""
|
|
material_code: str | None = None
|
|
material_name: str | None = None
|
|
raw_material_batch_no: str | None = None
|
|
raw_material_batch_options: list[str] = []
|
|
process_name: str | None = None
|
|
stamping_method: str | None = None
|
|
operator_count: float = 1
|
|
process_unit_price_yuan: float = 0
|
|
changeover_count: float = 0
|
|
standard_beat: float = 0
|
|
standard_workload: float = 0
|
|
good_qty: float = 0
|
|
defect_qty: float = 0
|
|
scrap_qty: float = 0
|
|
is_misc: bool = False
|
|
is_continuous_die: bool = False
|
|
remark: str | None = None
|
|
|
|
|
|
class ReportDraftBlock(BaseModel):
|
|
attendance_point_name: str = ""
|
|
device_no: str
|
|
mold_name: str = ""
|
|
process_name: str = ""
|
|
stamping_method: str | None = None
|
|
display_name: str = ""
|
|
start_at: datetime
|
|
end_at: datetime
|
|
duration_minutes: float = 0
|
|
options: list[ProductOption]
|
|
equipment_options: list[EquipmentOption] = []
|
|
items: list[ReportDraftItem]
|
|
is_misc: bool = False
|
|
is_continuous_die: bool = False
|
|
|
|
|
|
class ReportDraftOut(BaseModel):
|
|
session_id: int
|
|
attendance_point_name: str = ""
|
|
employee_phone: str
|
|
employee_name: str
|
|
start_at: datetime
|
|
end_at: datetime
|
|
duration_minutes: float
|
|
device_blocks: list[ReportDraftBlock]
|
|
|
|
|
|
class ReportSubmitItem(BaseModel):
|
|
attendance_point_name: str = ""
|
|
device_no: str = ""
|
|
project_no: str
|
|
product_name: str
|
|
material_code: str | None = None
|
|
material_name: str | None = None
|
|
raw_material_batch_no: str | None = None
|
|
process_name: str | None = None
|
|
stamping_method: str | None = None
|
|
operator_count: float = 1
|
|
process_unit_price_yuan: float = 0
|
|
changeover_count: float = Field(default=0, ge=0)
|
|
standard_beat: float
|
|
standard_workload: float = 0
|
|
good_qty: float = Field(default=0, ge=0)
|
|
defect_qty: float = Field(default=0, ge=0)
|
|
scrap_qty: float = Field(default=0, ge=0)
|
|
started_at: datetime | None = None
|
|
is_misc: bool = False
|
|
is_continuous_die: bool = False
|
|
remark: str | None = None
|
|
|
|
|
|
class ReportSubmitBlock(BaseModel):
|
|
attendance_point_name: str = ""
|
|
device_no: str
|
|
process_name: str = ""
|
|
items: list[ReportSubmitItem]
|
|
|
|
|
|
class ReportSubmitRequest(BaseModel):
|
|
session_id: int
|
|
device_blocks: list[ReportSubmitBlock]
|
|
|
|
|
|
class CleaningReportSubmitItem(BaseModel):
|
|
attendance_point_name: str = ""
|
|
device_no: str = ""
|
|
device_nos: list[str] = Field(default_factory=list)
|
|
product_name: str = Field(min_length=1, max_length=255)
|
|
process_name: str = Field(min_length=1, max_length=128)
|
|
stamping_method: str | None = None
|
|
quantity: float = Field(gt=0)
|
|
|
|
|
|
class CleaningReportSubmitRequest(BaseModel):
|
|
items: list[CleaningReportSubmitItem]
|
|
latitude: float | None = None
|
|
longitude: float | None = None
|
|
|
|
|
|
class ReportMetrics(BaseModel):
|
|
effective_minutes: float
|
|
shift_distribution_text: str = ""
|
|
shift_day_minutes: float = 0
|
|
shift_overtime_minutes: float = 0
|
|
shift_night_minutes: float = 0
|
|
shift_other_minutes: float = 0
|
|
shift_meal_break_minutes: float = 0
|
|
total_good_qty: float
|
|
total_output_qty: float
|
|
actual_beat: float
|
|
standard_beat: float
|
|
expected_workload: float
|
|
pace_rate: float
|
|
workload_rate: float
|
|
|
|
|
|
class ReportAllocationOut(BaseModel):
|
|
allocation_date: date
|
|
day_minutes: float = 0
|
|
overtime_minutes: float = 0
|
|
night_minutes: float = 0
|
|
effective_minutes: float = 0
|
|
good_qty: float = 0
|
|
defect_qty: float = 0
|
|
scrap_qty: float = 0
|
|
changeover_count: float = 0
|
|
reference_wage: float = 0
|
|
|
|
|
|
class ReportItemOut(BaseModel):
|
|
id: int | None = None
|
|
attendance_point_name: str = ""
|
|
device_no: str
|
|
project_no: str
|
|
product_name: str
|
|
material_code: str | None = None
|
|
material_name: str | None = None
|
|
raw_material_batch_no: str | None = None
|
|
process_name: str | None = None
|
|
stamping_method: str | None = None
|
|
operator_count: float = 1
|
|
process_unit_price_yuan: float = 0
|
|
changeover_count: float = 0
|
|
standard_beat: float
|
|
standard_workload: float
|
|
good_qty: float
|
|
defect_qty: float
|
|
scrap_qty: float
|
|
allocated_minutes: float = 0
|
|
started_at: datetime | None = None
|
|
is_misc: bool = False
|
|
is_continuous_die: bool = False
|
|
is_multi_person: bool = False
|
|
remark: str | None = None
|
|
over_limit_status: str = "not_checked"
|
|
over_limit_type: str | None = None
|
|
over_limit_reason: str | None = None
|
|
over_limit_checked_at: datetime | None = None
|
|
over_limit_check_source: str | None = None
|
|
over_limit_batch_no: str | None = None
|
|
over_limit_product_gross_weight_kg: float | None = None
|
|
over_limit_issued_weight_kg: float | None = None
|
|
over_limit_max_reportable_qty: float | None = None
|
|
over_limit_previous_good_qty: float | None = None
|
|
over_limit_current_cumulative_qty: float | None = None
|
|
over_limit_current_report_qty: float | None = None
|
|
allocations: list[ReportAllocationOut] = Field(default_factory=list)
|
|
corrections: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class ReportDeviceSegmentOut(BaseModel):
|
|
id: int
|
|
attendance_point_name: str = ""
|
|
device_no: str
|
|
mold_name: str = ""
|
|
process_name: str = ""
|
|
stamping_method: str | None = None
|
|
display_name: str = ""
|
|
scanned_at: datetime
|
|
released_at: datetime | None = None
|
|
sort_order: int = 0
|
|
corrections: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class ReportOut(BaseModel):
|
|
id: int
|
|
attendance_point_name: str = ""
|
|
session_id: int
|
|
employee_phone: str
|
|
employee_name: str
|
|
report_date: date
|
|
start_at: datetime
|
|
end_at: datetime
|
|
duration_minutes: float
|
|
break_minutes: float
|
|
status: ReportStatus
|
|
status_name: str
|
|
reviewer_phone: str | None = None
|
|
reviewer_name: str | None = None
|
|
reviewed_at: datetime | None = None
|
|
reject_reason: str | None = None
|
|
review_remark: str | None = None
|
|
submitted_at: datetime
|
|
is_system_auto_submitted: bool = False
|
|
auto_submit_reason: str | None = None
|
|
has_over_limit: bool = False
|
|
over_limit_summary: str | None = None
|
|
is_multi_person_assistant: bool = False
|
|
multi_person_source_report_id: int | None = None
|
|
is_voided: bool = False
|
|
voided_at: datetime | None = None
|
|
voided_by: str | None = None
|
|
voided_by_name: str | None = None
|
|
unvoid_deadline_at: datetime | None = None
|
|
can_void: bool = False
|
|
can_unvoid: bool = False
|
|
can_edit: bool = False
|
|
is_modified: bool = False
|
|
device_segments: list[ReportDeviceSegmentOut] = Field(default_factory=list)
|
|
items: list[ReportItemOut]
|
|
metrics: ReportMetrics
|
|
result_text: str
|
|
allocation_summary_text: str = ""
|
|
corrections: dict[str, Any] = Field(default_factory=dict)
|
|
|
|
|
|
class ReportItemCorrection(BaseModel):
|
|
id: int
|
|
device_no: str | None = None
|
|
project_no: str | None = None
|
|
product_name: str | None = None
|
|
process_name: str | None = None
|
|
raw_material_batch_no: str | None = None
|
|
changeover_count: float | None = Field(default=None, ge=0)
|
|
good_qty: float | None = Field(default=None, ge=0)
|
|
defect_qty: float | None = Field(default=None, ge=0)
|
|
scrap_qty: float | None = Field(default=None, ge=0)
|
|
remark: str | None = None
|
|
|
|
|
|
class ReportDeviceCorrection(BaseModel):
|
|
id: int
|
|
scanned_at: datetime | None = None
|
|
|
|
|
|
class ApproveReportRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
start_at: datetime | None = None
|
|
end_at: datetime | None = None
|
|
device_corrections: list[ReportDeviceCorrection] = Field(default_factory=list)
|
|
item_corrections: list[ReportItemCorrection] = []
|
|
review_remark: str | None = Field(default=None, max_length=500)
|
|
remark: str | None = None
|
|
|
|
|
|
class RejectReportRequest(BaseModel):
|
|
reason: str = "需重新填报"
|
|
|
|
|
|
class DeviceQrOut(BaseModel):
|
|
attendance_point_name: str = ""
|
|
device_no: str
|
|
process_name: str = ""
|
|
stamping_method: str | None = None
|
|
is_cleaning: bool = False
|
|
is_misc: bool = False
|
|
is_continuous_die: bool = False
|
|
mold_name: str = ""
|
|
display_name: str = ""
|
|
scene: str
|
|
page: str
|
|
qr_url: str | None = None
|
|
|
|
|
|
class DashboardRow(BaseModel):
|
|
id: str
|
|
attendance_point_name: str = ""
|
|
report_date: date
|
|
source_report_date: date | None = None
|
|
source_report_dates: list[date] = Field(default_factory=list)
|
|
employee_phone: str
|
|
employee_name: str
|
|
project_no: str
|
|
product_name: str
|
|
process_name: str | None = None
|
|
report_count: int
|
|
effective_minutes: float
|
|
shift_distribution_text: str = ""
|
|
shift_day_minutes: float = 0
|
|
shift_overtime_minutes: float = 0
|
|
shift_night_minutes: float = 0
|
|
shift_other_minutes: float = 0
|
|
total_good_qty: float
|
|
total_defect_qty: float
|
|
total_output_qty: float
|
|
actual_beat: float
|
|
standard_beat: float
|
|
expected_workload: float
|
|
pace_rate: float
|
|
workload_rate: float
|
|
pace_text: str
|
|
workload_text: str
|
|
profile_no: str | None = None
|
|
material_code: str | None = None
|
|
material_name: str | None = None
|
|
raw_material_batch_no: str | None = None
|
|
device_no: str | None = None
|
|
stamping_method: str | None = None
|
|
operator_count: float | None = None
|
|
process_unit_price_yuan: float = 0
|
|
changeover_count: float = 0
|
|
reference_wage: float = 0
|
|
worker_type: str = "正式工"
|
|
is_multi_person: bool = False
|
|
is_multi_person_assistant: bool = False
|
|
is_misc: bool = False
|
|
is_continuous_die: bool = False
|
|
is_system_auto_submitted: bool = False
|
|
is_voided: bool = False
|
|
review_remark: str | None = None
|
|
has_over_limit: bool = False
|
|
over_limit_summary: str | None = None
|
|
over_limit_reasons: str | None = None
|
|
correction_pairs: list[dict[str, Any]] = Field(default_factory=list)
|
|
|
|
|
|
class UsageStatsRow(BaseModel):
|
|
id: str
|
|
category: str
|
|
attendance_point_name: str = ""
|
|
name: str
|
|
object_type: str
|
|
metric_kind: str
|
|
value: float = 0
|
|
report_count: int = 0
|
|
product_name: str = ""
|
|
process_name: str = ""
|
|
stamping_method: str = ""
|
|
tags: list[str] = Field(default_factory=list)
|
|
|
|
|
|
class UsageStatsDailyRow(BaseModel):
|
|
report_date: date
|
|
value: float = 0
|
|
report_count: int = 0
|
|
|
|
|
|
class UsageStatsReportRow(BaseModel):
|
|
report_id: int
|
|
report_date: date
|
|
employee_phone: str = ""
|
|
employee_name: str = ""
|
|
display_name: str = ""
|
|
value: float = 0
|
|
report_count: int = 1
|
|
|
|
|
|
class UsageStatsDetailOut(BaseModel):
|
|
id: str = ""
|
|
category: str
|
|
attendance_point_name: str = ""
|
|
name: str = ""
|
|
object_type: str = ""
|
|
metric_kind: str = ""
|
|
value: float = 0
|
|
report_count: int = 0
|
|
product_name: str = ""
|
|
process_name: str = ""
|
|
stamping_method: str = ""
|
|
tags: list[str] = Field(default_factory=list)
|
|
daily_rows: list[UsageStatsDailyRow] = Field(default_factory=list)
|
|
report_rows: list[UsageStatsReportRow] = Field(default_factory=list)
|
|
|
|
|
|
class NoticeCreate(BaseModel):
|
|
title: str = Field(min_length=1, max_length=120)
|
|
content: str = Field(min_length=1, max_length=1000)
|
|
attendance_point_names: list[str] = []
|
|
sort_order: int = 0
|
|
is_active: bool = True
|
|
|
|
|
|
class NoticeUpdate(BaseModel):
|
|
title: str | None = Field(default=None, min_length=1, max_length=120)
|
|
content: str | None = Field(default=None, min_length=1, max_length=1000)
|
|
attendance_point_names: list[str] | None = None
|
|
sort_order: int | None = None
|
|
is_active: bool | None = None
|
|
|
|
|
|
class NoticeOut(BaseModel):
|
|
id: int
|
|
title: str
|
|
content: str
|
|
attendance_point_names: list[str] = []
|
|
sort_order: int
|
|
is_active: bool
|
|
created_by: str
|
|
creator_name: str
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
|
|
class ReconciliationMonthCell(BaseModel):
|
|
month: int
|
|
reported_good_qty: float = 0
|
|
reconciled_good_qty: float = 0
|
|
return_qty: float = 0
|
|
|
|
|
|
class ReconciliationRow(BaseModel):
|
|
attendance_point_name: str = ""
|
|
product_name: str
|
|
months: list[ReconciliationMonthCell]
|
|
|
|
|
|
class ReconciliationLedgerOut(BaseModel):
|
|
year: int
|
|
current_year: int
|
|
current_month: int
|
|
rows: list[ReconciliationRow]
|
|
|
|
|
|
class ReconciliationYearsOut(BaseModel):
|
|
years: list[int]
|
|
current_year: int
|
|
|
|
|
|
class MonitorLockOut(BaseModel):
|
|
id: int
|
|
attendance_point_name: str = ""
|
|
mold_name: str
|
|
process_name: str = ""
|
|
stamping_method: str | None = None
|
|
mold_display_name: str
|
|
occupied_phone: str
|
|
occupied_name: str
|
|
occupied_start_at: datetime
|
|
occupied_minutes: float
|
|
|
|
|
|
class MoldLockFeedbackOut(BaseModel):
|
|
id: int
|
|
attendance_point_name: str = ""
|
|
mold_name: str
|
|
process_name: str = ""
|
|
stamping_method: str | None = None
|
|
mold_display_name: str
|
|
lock_id: int
|
|
reporter_phone: str
|
|
reporter_name: str
|
|
occupied_phone: str
|
|
occupied_name: str
|
|
feedback_at: datetime
|
|
read_at: datetime | None = None
|
|
handled_at: datetime | None = None
|
|
status: str
|
|
status_name: str
|
|
can_release: bool = True
|
|
|
|
|
|
class MoldLockFeedbackCreate(BaseModel):
|
|
lock_id: int
|
|
|
|
|
|
class ReconciliationEntryUpdate(BaseModel):
|
|
attendance_point_name: str = Field(min_length=1, max_length=128)
|
|
year: int = Field(ge=2000, le=2100)
|
|
month: int = Field(ge=1, le=12)
|
|
product_name: str = Field(min_length=1, max_length=255)
|
|
reconciled_good_qty: float | None = Field(default=None, ge=0)
|
|
return_qty: float | None = Field(default=None, ge=0)
|
|
|
|
|
|
class ErpLoginQrcodeRequest(BaseModel):
|
|
ticket: str = Field(min_length=1, max_length=16)
|
|
session_id: int = Field(ge=1)
|
|
|
|
|
|
class ErpLoginQrcodeResponse(BaseModel):
|
|
qr_url: str | None = None
|
|
page: str
|
|
scene: str
|
|
|
|
|
|
class ErpLoginPreviewResponse(BaseModel):
|
|
session_id: int
|
|
system_name: str
|
|
started_at: datetime
|
|
expires_at: datetime
|
|
request_ip_hint: str | None = None
|
|
user_agent_hint: str | None = None
|
|
|
|
|
|
class ErpLoginConfirmRequest(BaseModel):
|
|
phone_code: str = Field(min_length=1, max_length=256)
|
|
|
|
|
|
class ErpLoginActionResponse(BaseModel):
|
|
status: str
|
|
failure_reason: str | None = None
|
|
|
|
|
|
PersonnelOut.model_rebuild()
|
|
TokenResponse.model_rebuild()
|
|
LoginSelectionResponse.model_rebuild()
|