feat(malaysia): 입출고 화면 통합 — 낱개(좌)·세트(우) 한 폼, 버튼 하나로 일괄 적용
- /movements/bulk + /movements/set-out-bulk → /movements/apply 단일 엔드포인트 - 낱개는 선택 종류(IN/OUT/ADJUST), 세트는 항상 OUT(BOM 분해) - 2컬럼 그리드 한 폼, "일괄 적용" 버튼 하나 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -170,12 +170,16 @@ async def movements_page(request: Request) -> HTMLResponse:
|
||||
return render_template(request, "malaysia/movements.html", ctx)
|
||||
|
||||
|
||||
@router.post("/movements/bulk")
|
||||
async def movements_bulk(request: Request, user: dict[str, Any] = Depends(_require_user)) -> RedirectResponse:
|
||||
"""낱개 아이템 그리드 일괄 등록. form: movement_type, movement_date,
|
||||
warehouse_code, ref_type, ref_no, memo, qty_<ITEMCODE>...
|
||||
@router.post("/movements/apply")
|
||||
async def movements_apply(request: Request, user: dict[str, Any] = Depends(_require_user)) -> RedirectResponse:
|
||||
"""낱개 + 세트 한 화면 일괄 적용 (버튼 하나).
|
||||
|
||||
IN/OUT 은 양수만 등록(0/빈칸 스킵), ADJUST 는 0 아닌 값만 등록(±).
|
||||
form: movement_type(낱개용 IN/OUT/ADJUST), movement_date, warehouse_code,
|
||||
ref_type, ref_no, memo, qty_<CODE>...
|
||||
|
||||
- 낱개(individual): 선택한 movement_type 으로 등록. IN/OUT 양수만, ADJUST ±(0 스킵).
|
||||
- 세트(set): 항상 OUT 으로 처리 — BOM(itemcode_db) 분해해 구성품별 OUT 생성.
|
||||
빈칸/0 은 스킵. 코드별 에러는 모아서 부분 적용 후 안내.
|
||||
"""
|
||||
st = _store(request)
|
||||
if st is None:
|
||||
@@ -189,9 +193,12 @@ async def movements_bulk(request: Request, user: dict[str, Any] = Depends(_requi
|
||||
ref_type = str(form.get("ref_type") or "").strip()
|
||||
ref_no = str(form.get("ref_no") or "").strip()
|
||||
memo = str(form.get("memo") or "").strip()
|
||||
bmap = _bom_map(request)
|
||||
|
||||
created = 0
|
||||
applied = 0
|
||||
errors: list[str] = []
|
||||
|
||||
# 낱개 — 선택한 종류로
|
||||
for it in st.list_items(kind="individual"):
|
||||
code = it["item_code"]
|
||||
raw = str(form.get(f"qty_{code}") or "").strip()
|
||||
@@ -210,31 +217,11 @@ async def movements_bulk(request: Request, user: dict[str, Any] = Depends(_requi
|
||||
movement_type=mtype, qty=n, ref_type=ref_type, ref_no=ref_no,
|
||||
memo=memo, created_by=user["email"],
|
||||
)
|
||||
created += 1
|
||||
applied += 1
|
||||
except ValueError as exc:
|
||||
errors.append(f"{code}: {exc}")
|
||||
if errors and created == 0:
|
||||
raise HTTPException(status_code=400, detail="; ".join(errors[:10]))
|
||||
return RedirectResponse(url=f"/malaysia/movements?wh={wh}&type={mtype}", status_code=303)
|
||||
|
||||
|
||||
@router.post("/movements/set-out-bulk")
|
||||
async def set_out_bulk(request: Request, user: dict[str, Any] = Depends(_require_user)) -> RedirectResponse:
|
||||
"""세트 그리드 일괄 출고. form: movement_date, warehouse_code, ref_no, memo,
|
||||
qty_<SETCODE>... → 각 세트를 BOM 분해해 구성품별 OUT 생성.
|
||||
"""
|
||||
st = _store(request)
|
||||
if st is None:
|
||||
raise HTTPException(status_code=503, detail="malaysia_stock_db 미설정")
|
||||
form = await request.form()
|
||||
wh = str(form.get("warehouse_code") or "").strip()
|
||||
mdate = str(form.get("movement_date") or "").strip()
|
||||
ref_no = str(form.get("ref_no") or "").strip()
|
||||
memo = str(form.get("memo") or "").strip()
|
||||
bmap = _bom_map(request)
|
||||
|
||||
done = 0
|
||||
errors: list[str] = []
|
||||
# 세트 — 항상 OUT(BOM 분해)
|
||||
for it in st.list_items(kind="set"):
|
||||
code = it["item_code"]
|
||||
raw = str(form.get(f"qty_{code}") or "").strip()
|
||||
@@ -243,6 +230,7 @@ async def set_out_bulk(request: Request, user: dict[str, Any] = Depends(_require
|
||||
try:
|
||||
n = int(raw)
|
||||
except ValueError:
|
||||
errors.append(f"{code}: 숫자 아님")
|
||||
continue
|
||||
if n <= 0:
|
||||
continue
|
||||
@@ -252,12 +240,13 @@ async def set_out_bulk(request: Request, user: dict[str, Any] = Depends(_require
|
||||
bom_map=bmap, ref_type="set", ref_no=ref_no, memo=memo,
|
||||
created_by=user["email"],
|
||||
)
|
||||
done += 1
|
||||
applied += 1
|
||||
except ValueError as exc:
|
||||
errors.append(f"{code}: {exc}")
|
||||
if errors and done == 0:
|
||||
|
||||
if errors and applied == 0:
|
||||
raise HTTPException(status_code=400, detail="; ".join(errors[:10]))
|
||||
return RedirectResponse(url=f"/malaysia/movements?wh={wh}&type=OUT", status_code=303)
|
||||
return RedirectResponse(url=f"/malaysia/movements?wh={wh}&type={mtype}", status_code=303)
|
||||
|
||||
|
||||
# ════════════════════════════════════════════════════════════
|
||||
|
||||
Reference in New Issue
Block a user