Files
fastAPI/app/schemas/box.py
Misaka 39b8e5de6a feat: extend boxing module to support accessory items
- Add PendingAccessory schema and pending_accessories to BoxInfoResponse
- Add item_type field to BoxItemDetail, CurrentZongpaiBox, BoxSaveRequest, BoxSaveResponse
- Query pending (unboxed) accessories in get_box_info
- Branch save_box_record to skip ERP validation for accessory items

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-24 17:43:15 +08:00

139 lines
3.0 KiB
Python

import re
from pydantic import BaseModel, Field, field_validator
ZONGPAI_PATTERN = re.compile(r"^(\d{2}(B|C|T)\d+|\d{2}(BW|CW)\d{4})$")
class PendingAccessory(BaseModel):
"""待装箱的配件"""
accessory_id: int
accessory_type: str
quantity: int
location_code: str | None = None
class BoxItemDetail(BaseModel):
"""箱号下某个总排号的明细"""
box_item_id: int | None = None
zongpai_no: str
work_order_no: str | None = None
quantity: int
total_quantity: int | None = None
item_type: str = "product"
class CurrentZongpaiBox(BaseModel):
"""当前总排号已经分配的箱号明细"""
box_item_id: int
box_no: int
quantity: int
item_type: str = "product"
class BoxDetail(BaseModel):
"""一个箱号的完整信息"""
box_no: int
items: list[BoxItemDetail]
class BoxInfoResponse(BaseModel):
"""GET /box/info 响应"""
zongpai_no: str
paichan_no: str
work_order_no: str | None = None
quantity: int
current_zongpai_boxes: list[CurrentZongpaiBox] = Field(default_factory=list)
existing_boxes: list[BoxDetail]
pending_accessories: list[PendingAccessory] = Field(default_factory=list)
max_box_no: int
suggested_box_no: int
class BoxSaveRequest(BaseModel):
"""POST /box 请求"""
zongpai_no: str
box_no: int
quantity: int
item_type: str = "product"
accessory_id: int | None = None
@field_validator("zongpai_no")
@classmethod
def validate_zongpai_no(cls, v: str) -> str:
v = v.strip().upper()
if not ZONGPAI_PATTERN.match(v):
raise ValueError("INVALID_ZONGPAI")
return v
@field_validator("box_no")
@classmethod
def validate_box_no(cls, v: int) -> int:
if v <= 0:
raise ValueError("INVALID_BOX_NO")
return v
@field_validator("quantity")
@classmethod
def validate_quantity(cls, v: int) -> int:
if v <= 0:
raise ValueError("INVALID_QUANTITY")
return v
class BoxSaveResponse(BaseModel):
"""POST /box 响应"""
box_item_id: int
paichan_no: str
box_no: int
zongpai_no: str
quantity: int
item_type: str = "product"
created_at: str
class BoxUpdateRequest(BaseModel):
"""PATCH /box/{box_item_id} 请求"""
box_no: int
quantity: int
@field_validator("box_no")
@classmethod
def validate_box_no(cls, v: int) -> int:
if v <= 0:
raise ValueError("INVALID_BOX_NO")
return v
@field_validator("quantity")
@classmethod
def validate_quantity(cls, v: int) -> int:
if v <= 0:
raise ValueError("INVALID_QUANTITY")
return v
class BoxUpdateResponse(BaseModel):
"""PATCH /box/{box_item_id} 响应"""
box_item_id: int
paichan_no: str
box_no: int
zongpai_no: str
quantity: int
updated_at: str
class BoxDeleteResponse(BaseModel):
"""DELETE /box/{box_item_id} 响应"""
box_item_id: int
deleted: bool