import re from sqlalchemy import text from sqlalchemy.orm import Session from app.models.finished_goods import FinishedGoodsBox, FinishedGoodsBoxItem from app.schemas.box import BoxSaveRequest ZONGPAI_PATTERN = re.compile(r"^(\d{2}(B|C|T)\d+|\d{2}(BW|CW)\d{4})$") class InvalidZongpaiError(Exception): pass class ZongpaiNotFoundError(Exception): pass class DuplicateBoxItemError(Exception): def __init__(self, paichan_no: str, box_no: int): self.paichan_no = paichan_no self.box_no = box_no def query_erp_info(db: Session, zongpai_no: str) -> dict: """从 ERP 视图查询总排号对应的排产号和数量。""" sql = text( "SELECT TOP 1 [总排号], [排产号], [数量] " "FROM [ERPAuto].[vw_productionContractData] " "WHERE [总排号] = :zongpai_no" ) row = db.execute(sql, {"zongpai_no": zongpai_no}).fetchone() if not row: raise ZongpaiNotFoundError() return { "zongpai_no": row[0], "paichan_no": row[1], "quantity": int(row[2]), } def get_box_info(db: Session, zongpai_no: str) -> dict: """获取装箱信息:排产号、数量、已有箱号明细。""" zongpai_no = zongpai_no.strip().upper() if not ZONGPAI_PATTERN.match(zongpai_no): raise InvalidZongpaiError() erp = query_erp_info(db, zongpai_no) paichan_no = erp["paichan_no"] quantity = erp["quantity"] boxes = ( db.query(FinishedGoodsBox) .filter(FinishedGoodsBox.paichan_no == paichan_no) .order_by(FinishedGoodsBox.box_no) .all() ) existing_boxes = [] max_box_no = 0 for box in boxes: items = ( db.query(FinishedGoodsBoxItem) .filter(FinishedGoodsBoxItem.box_id == box.id) .all() ) existing_boxes.append({ "box_no": box.box_no, "items": [ {"zongpai_no": item.zongpai_no, "quantity": int(item.quantity or 0)} for item in items ], }) if box.box_no > max_box_no: max_box_no = box.box_no return { "zongpai_no": zongpai_no, "paichan_no": paichan_no, "quantity": quantity, "existing_boxes": existing_boxes, "max_box_no": max_box_no, "suggested_box_no": max_box_no + 1, } def save_box_record(db: Session, req: BoxSaveRequest) -> dict: """保存装箱记录。箱已存在时仅追加明细(多码一箱场景)。""" erp = query_erp_info(db, req.zongpai_no) paichan_no = erp["paichan_no"] box = ( db.query(FinishedGoodsBox) .filter( FinishedGoodsBox.paichan_no == paichan_no, FinishedGoodsBox.box_no == req.box_no, ) .first() ) if box: existing_item = ( db.query(FinishedGoodsBoxItem) .filter( FinishedGoodsBoxItem.box_id == box.id, FinishedGoodsBoxItem.zongpai_no == req.zongpai_no, ) .first() ) if existing_item: raise DuplicateBoxItemError(paichan_no, req.box_no) else: box = FinishedGoodsBox( paichan_no=paichan_no, box_no=req.box_no, ) db.add(box) db.flush() item = FinishedGoodsBoxItem( box_id=box.id, zongpai_no=req.zongpai_no, quantity=req.quantity, ) db.add(item) db.commit() db.refresh(item) return { "paichan_no": paichan_no, "box_no": req.box_no, "zongpai_no": req.zongpai_no, "quantity": req.quantity, "created_at": item.created_at.isoformat(), }