feat(server): add PUT /attachment/config with removal guard
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.orm import Session
|
||||
@@ -10,6 +11,7 @@ from app.models.attachment import (
|
||||
FinishedGoodsAttachmentLocation,
|
||||
FinishedGoodsBoxAttachmentItem,
|
||||
)
|
||||
from app.schemas.attachment import AttachmentConfigRequest
|
||||
from app.services.box_service import InvalidZongpaiError
|
||||
|
||||
ZONGPAI_PATTERN = re.compile(r"^(\d{2}(B|C|T)\d+|\d{2}(BW|CW)\d{4})$")
|
||||
@@ -130,3 +132,69 @@ def get_attachment_status(db: Session, zongpai_no: str) -> dict:
|
||||
"all_complete": all_complete,
|
||||
"items": items,
|
||||
}
|
||||
|
||||
|
||||
def _ensure_categories_exist(db: Session, category_ids: list[int]) -> dict[int, str]:
|
||||
names = _category_name_map(db, category_ids)
|
||||
missing = [c for c in category_ids if c not in names]
|
||||
if missing:
|
||||
raise CategoryNotFoundError(missing[0])
|
||||
return names
|
||||
|
||||
|
||||
def configure_attachment(db: Session, req: AttachmentConfigRequest) -> dict:
|
||||
zongpai_no = _validate_zongpai(req.zongpai_no)
|
||||
|
||||
# Validate categories referenced in the submitted list
|
||||
submitted_ids = [it.category_id for it in req.items]
|
||||
_ensure_categories_exist(db, submitted_ids)
|
||||
|
||||
# Upsert order-level determination row
|
||||
att = (
|
||||
db.query(FinishedGoodsAttachment)
|
||||
.filter(FinishedGoodsAttachment.zongpai_no == zongpai_no)
|
||||
.first()
|
||||
)
|
||||
if att is None:
|
||||
att = FinishedGoodsAttachment(zongpai_no=zongpai_no, determination=req.determination)
|
||||
db.add(att)
|
||||
else:
|
||||
att.determination = req.determination
|
||||
att.updated_at = datetime.now()
|
||||
db.flush()
|
||||
|
||||
# Load existing items
|
||||
existing = (
|
||||
db.query(FinishedGoodsAttachmentItem)
|
||||
.filter(FinishedGoodsAttachmentItem.zongpai_no == zongpai_no)
|
||||
.all()
|
||||
)
|
||||
existing_by_cat = {it.category_id: it for it in existing}
|
||||
submitted_set = set(submitted_ids)
|
||||
boxed = _boxed_qty_map(db, zongpai_no)
|
||||
names = _category_name_map(db, list(existing_by_cat.keys()) + submitted_ids)
|
||||
|
||||
# Remove items not in the submitted list; block those with boxed > 0
|
||||
for cat_id, it in existing_by_cat.items():
|
||||
if cat_id not in submitted_set:
|
||||
if boxed.get(cat_id, 0) > 0:
|
||||
raise CategoryHasBoxesError(names.get(cat_id, str(cat_id)))
|
||||
db.delete(it)
|
||||
|
||||
# Upsert submitted items
|
||||
for submitted in req.items:
|
||||
it = existing_by_cat.get(submitted.category_id)
|
||||
if it is None:
|
||||
db.add(
|
||||
FinishedGoodsAttachmentItem(
|
||||
zongpai_no=zongpai_no,
|
||||
category_id=submitted.category_id,
|
||||
expected_qty=submitted.expected_qty,
|
||||
)
|
||||
)
|
||||
else:
|
||||
it.expected_qty = submitted.expected_qty
|
||||
it.updated_at = datetime.now()
|
||||
|
||||
db.commit()
|
||||
return get_attachment_status(db, zongpai_no)
|
||||
|
||||
Reference in New Issue
Block a user