Files
fastAPI/app/schemas/accessory.py
Misaka b9f76b03fa feat: add accessory Pydantic schemas
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-24 17:31:20 +08:00

136 lines
3.3 KiB
Python

# app/schemas/accessory.py
import re
from pydantic import BaseModel, Field, field_validator
PAICHAN_PATTERN = re.compile(r"^[A-Z]{1,2}\d{5}(-J)?$")
LOCATION_NORMAL_PATTERN = re.compile(r"^[A-Z]+\d+-\d+-\d+$")
LOCATION_TRANS_PATTERN = re.compile(r"^TRANS-\d+")
# ── Work order query ──
class WorkOrderGroup(BaseModel):
work_order_no: str
zongpai_nos: list[str]
class WorkOrderQueryResponse(BaseModel):
paichan_no: str
work_orders: list[WorkOrderGroup]
# ── Accessory CRUD ──
class AccessoryCreateRequest(BaseModel):
paichan_no: str
zongpai_no: str
accessory_type: str
quantity: int
location_code: str | None = None
@field_validator("paichan_no")
@classmethod
def validate_paichan_no(cls, v: str) -> str:
v = v.strip().upper()
if not PAICHAN_PATTERN.match(v):
raise ValueError("INVALID_PAICHAN")
return v
@field_validator("zongpai_no")
@classmethod
def validate_zongpai_no(cls, v: str) -> str:
v = v.strip().upper()
if not re.match(r"^(\d{2}(B|C|T)\d+|\d{2}(BW|CW)\d{4})$", v):
raise ValueError("INVALID_ZONGPAI")
return v
@field_validator("quantity")
@classmethod
def validate_quantity(cls, v: int) -> int:
if v <= 0:
raise ValueError("INVALID_QUANTITY")
return v
@field_validator("location_code")
@classmethod
def validate_location_code(cls, v: str | None) -> str | None:
if v is None:
return v
v = v.strip().upper()
if not LOCATION_NORMAL_PATTERN.match(v) and not LOCATION_TRANS_PATTERN.match(v):
raise ValueError("INVALID_LOCATION")
return v
class AccessoryResponse(BaseModel):
id: int
paichan_no: str
zongpai_no: str
accessory_type: str
quantity: int
location_code: str | None = None
is_boxed: bool = False
created_at: str
class AccessoryListResponse(BaseModel):
paichan_no: str
items: list[AccessoryResponse]
class AccessoryUpdateRequest(BaseModel):
accessory_type: str | None = None
quantity: int | None = None
location_code: str | None = None
@field_validator("quantity")
@classmethod
def validate_quantity(cls, v: int | None) -> int | None:
if v is not None and v <= 0:
raise ValueError("INVALID_QUANTITY")
return v
@field_validator("location_code")
@classmethod
def validate_location_code(cls, v: str | None) -> str | None:
if v is None:
return v
v = v.strip().upper()
if not LOCATION_NORMAL_PATTERN.match(v) and not LOCATION_TRANS_PATTERN.match(v):
raise ValueError("INVALID_LOCATION")
return v
# ── Accessory type management ──
class AccessoryTypeItem(BaseModel):
id: int
name: str
sort_order: int
class AccessoryTypeListResponse(BaseModel):
types: list[AccessoryTypeItem]
class AccessoryTypeCreateRequest(BaseModel):
name: str
sort_order: int = 0
@field_validator("name")
@classmethod
def validate_name(cls, v: str) -> str:
v = v.strip()
if not v:
raise ValueError("INVALID_TYPE_NAME")
return v
class AccessoryTypeUpdateRequest(BaseModel):
name: str | None = None
sort_order: int | None = None