feat: add one-to-one box mode with duplicate bind check
Add box_mode field to BoxSaveRequest and DuplicateZongpaiBindError exception. In one-to-one mode, prevent the same zongpai from being packed into multiple boxes, returning 409 with existing box info. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
16
app/main.py
16
app/main.py
@@ -19,7 +19,7 @@ except Exception as e:
|
|||||||
from app.api.v1.location import router as location_router
|
from app.api.v1.location import router as location_router
|
||||||
from app.api.v1.box import router as box_router
|
from app.api.v1.box import router as box_router
|
||||||
from app.services.location_service import DuplicateLocationError
|
from app.services.location_service import DuplicateLocationError
|
||||||
from app.services.box_service import ZongpaiNotFoundError, DuplicateBoxItemError, InvalidZongpaiError
|
from app.services.box_service import ZongpaiNotFoundError, DuplicateBoxItemError, InvalidZongpaiError, DuplicateZongpaiBindError
|
||||||
|
|
||||||
app = FastAPI(title="CargoTrace API", version="0.1.0")
|
app = FastAPI(title="CargoTrace API", version="0.1.0")
|
||||||
|
|
||||||
@@ -106,6 +106,20 @@ async def duplicate_box_handler(request: Request, exc: DuplicateBoxItemError):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@app.exception_handler(DuplicateZongpaiBindError)
|
||||||
|
async def duplicate_zongpai_bind_handler(request: Request, exc: DuplicateZongpaiBindError):
|
||||||
|
return JSONResponse(
|
||||||
|
status_code=409,
|
||||||
|
content={
|
||||||
|
"error_code": "DUPLICATE_ZONGPAI_BIND",
|
||||||
|
"message": f"总排号 {exc.zongpai_no} 已绑定箱号 {exc.box_no},请勿重复装箱",
|
||||||
|
"paichan_no": exc.paichan_no,
|
||||||
|
"box_no": exc.box_no,
|
||||||
|
"zongpai_no": exc.zongpai_no,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
async def root():
|
async def root():
|
||||||
return {"message": "Welcome to CargoTrace API"}
|
return {"message": "Welcome to CargoTrace API"}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ class BoxSaveRequest(BaseModel):
|
|||||||
zongpai_no: str
|
zongpai_no: str
|
||||||
box_no: int
|
box_no: int
|
||||||
quantity: int
|
quantity: int
|
||||||
|
box_mode: str | None = None # one-to-one | one-to-many | many-to-one
|
||||||
|
|
||||||
@field_validator("zongpai_no")
|
@field_validator("zongpai_no")
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -23,6 +23,13 @@ class DuplicateBoxItemError(Exception):
|
|||||||
self.box_no = box_no
|
self.box_no = box_no
|
||||||
|
|
||||||
|
|
||||||
|
class DuplicateZongpaiBindError(Exception):
|
||||||
|
def __init__(self, paichan_no: str, box_no: int, zongpai_no: str):
|
||||||
|
self.paichan_no = paichan_no
|
||||||
|
self.box_no = box_no
|
||||||
|
self.zongpai_no = zongpai_no
|
||||||
|
|
||||||
|
|
||||||
def query_erp_info(db: Session, zongpai_no: str) -> dict:
|
def query_erp_info(db: Session, zongpai_no: str) -> dict:
|
||||||
"""从 ERP 视图查询总排号对应的排产号和数量。"""
|
"""从 ERP 视图查询总排号对应的排产号和数量。"""
|
||||||
sql = text(_erp_info_sql(db))
|
sql = text(_erp_info_sql(db))
|
||||||
@@ -101,6 +108,19 @@ def save_box_record(db: Session, req: BoxSaveRequest) -> dict:
|
|||||||
erp = query_erp_info(db, req.zongpai_no)
|
erp = query_erp_info(db, req.zongpai_no)
|
||||||
paichan_no = erp["paichan_no"]
|
paichan_no = erp["paichan_no"]
|
||||||
|
|
||||||
|
if req.box_mode == "one-to-one":
|
||||||
|
existing_box_no = (
|
||||||
|
db.query(FinishedGoodsBox.box_no)
|
||||||
|
.join(FinishedGoodsBoxItem, FinishedGoodsBox.id == FinishedGoodsBoxItem.box_id)
|
||||||
|
.filter(
|
||||||
|
FinishedGoodsBox.paichan_no == paichan_no,
|
||||||
|
FinishedGoodsBoxItem.zongpai_no == req.zongpai_no,
|
||||||
|
)
|
||||||
|
.scalar()
|
||||||
|
)
|
||||||
|
if existing_box_no is not None:
|
||||||
|
raise DuplicateZongpaiBindError(paichan_no, existing_box_no, req.zongpai_no)
|
||||||
|
|
||||||
box = (
|
box = (
|
||||||
db.query(FinishedGoodsBox)
|
db.query(FinishedGoodsBox)
|
||||||
.filter(
|
.filter(
|
||||||
|
|||||||
Reference in New Issue
Block a user