feat(server): add POST /attachment/box with real-evidence flip + cap
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -11,8 +11,13 @@ from app.models.attachment import (
|
||||
FinishedGoodsAttachmentLocation,
|
||||
FinishedGoodsBoxAttachmentItem,
|
||||
)
|
||||
from app.models.finished_goods import FinishedGoodsBox
|
||||
from app.schemas.attachment import AttachmentConfigRequest
|
||||
from app.services.box_service import InvalidZongpaiError
|
||||
from app.services.box_service import (
|
||||
InvalidQuantityError,
|
||||
InvalidZongpaiError,
|
||||
query_erp_info,
|
||||
)
|
||||
|
||||
ZONGPAI_PATTERN = re.compile(r"^(\d{2}(B|C|T)\d+|\d{2}(BW|CW)\d{4})$")
|
||||
|
||||
@@ -257,3 +262,112 @@ def register_attachment_location(db: Session, req) -> FinishedGoodsAttachmentLoc
|
||||
db.commit()
|
||||
db.refresh(record)
|
||||
return record
|
||||
|
||||
|
||||
def _boxed_for_category(
|
||||
db: Session, zongpai_no: str, category_id: int, exclude_item_id: int | None = None
|
||||
) -> int:
|
||||
q = db.query(FinishedGoodsBoxAttachmentItem).filter(
|
||||
FinishedGoodsBoxAttachmentItem.zongpai_no == zongpai_no,
|
||||
FinishedGoodsBoxAttachmentItem.category_id == category_id,
|
||||
)
|
||||
if exclude_item_id is not None:
|
||||
q = q.filter(FinishedGoodsBoxAttachmentItem.id != exclude_item_id)
|
||||
return sum(int(r.quantity or 0) for r in q.all())
|
||||
|
||||
|
||||
def _ensure_attachment_item(db: Session, zongpai_no: str, category_id: int, erp_qty: int):
|
||||
"""实物证据:不存在则建 item(expected 兜底 = ERP 数量),并保证 determination=has。"""
|
||||
if not _category_name_map(db, [category_id]):
|
||||
raise CategoryNotFoundError(category_id)
|
||||
|
||||
att = (
|
||||
db.query(FinishedGoodsAttachment)
|
||||
.filter(FinishedGoodsAttachment.zongpai_no == zongpai_no)
|
||||
.first()
|
||||
)
|
||||
if att is None:
|
||||
att = FinishedGoodsAttachment(zongpai_no=zongpai_no, determination="has")
|
||||
db.add(att)
|
||||
elif att.determination in ("undetermined", "none"):
|
||||
att.determination = "has"
|
||||
att.updated_at = datetime.now()
|
||||
db.flush()
|
||||
|
||||
item = (
|
||||
db.query(FinishedGoodsAttachmentItem)
|
||||
.filter(
|
||||
FinishedGoodsAttachmentItem.zongpai_no == zongpai_no,
|
||||
FinishedGoodsAttachmentItem.category_id == category_id,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if item is None:
|
||||
item = FinishedGoodsAttachmentItem(
|
||||
zongpai_no=zongpai_no,
|
||||
category_id=category_id,
|
||||
expected_qty=erp_qty,
|
||||
)
|
||||
db.add(item)
|
||||
db.flush()
|
||||
return item
|
||||
|
||||
|
||||
def save_attachment_box(db: Session, req) -> dict:
|
||||
zongpai_no = _validate_zongpai(req.zongpai_no)
|
||||
|
||||
erp = query_erp_info(db, zongpai_no) # raises ZongpaiNotFoundError if missing
|
||||
paichan_no = erp["paichan_no"]
|
||||
erp_qty = erp["quantity"]
|
||||
|
||||
item = _ensure_attachment_item(db, zongpai_no, req.category_id, erp_qty)
|
||||
|
||||
box = (
|
||||
db.query(FinishedGoodsBox)
|
||||
.filter(
|
||||
FinishedGoodsBox.paichan_no == paichan_no,
|
||||
FinishedGoodsBox.box_no == req.box_no,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if box is not None:
|
||||
existing_in_box = (
|
||||
db.query(FinishedGoodsBoxAttachmentItem)
|
||||
.filter(
|
||||
FinishedGoodsBoxAttachmentItem.box_id == box.id,
|
||||
FinishedGoodsBoxAttachmentItem.zongpai_no == zongpai_no,
|
||||
FinishedGoodsBoxAttachmentItem.category_id == req.category_id,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if existing_in_box is not None:
|
||||
raise DuplicateAttachmentBoxItemError(paichan_no, req.box_no)
|
||||
else:
|
||||
box = FinishedGoodsBox(paichan_no=paichan_no, box_no=req.box_no)
|
||||
db.add(box)
|
||||
db.flush()
|
||||
|
||||
# Cap check against expected_qty (mirrors product _ensure_quantity_within_erp_total)
|
||||
already_boxed = _boxed_for_category(db, zongpai_no, req.category_id)
|
||||
if already_boxed + req.quantity > item.expected_qty:
|
||||
raise InvalidQuantityError()
|
||||
|
||||
record = FinishedGoodsBoxAttachmentItem(
|
||||
box_id=box.id,
|
||||
zongpai_no=zongpai_no,
|
||||
category_id=req.category_id,
|
||||
quantity=req.quantity,
|
||||
)
|
||||
db.add(record)
|
||||
db.commit()
|
||||
db.refresh(record)
|
||||
|
||||
return {
|
||||
"box_item_id": record.id,
|
||||
"paichan_no": paichan_no,
|
||||
"box_no": req.box_no,
|
||||
"zongpai_no": zongpai_no,
|
||||
"category_id": req.category_id,
|
||||
"quantity": req.quantity,
|
||||
"created_at": record.created_at.isoformat(),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user