diff --git a/app/modules/malaysia/router.py b/app/modules/malaysia/router.py index e4cb38b..86921f9 100644 --- a/app/modules/malaysia/router.py +++ b/app/modules/malaysia/router.py @@ -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_... +@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_... + + - 낱개(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_... → 각 세트를 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) # ════════════════════════════════════════════════════════════ diff --git a/app/modules/malaysia/templates/malaysia/movements.html b/app/modules/malaysia/templates/malaysia/movements.html index 2d0cfff..054dbf1 100644 --- a/app/modules/malaysia/templates/malaysia/movements.html +++ b/app/modules/malaysia/templates/malaysia/movements.html @@ -5,14 +5,13 @@ {% set active_tab = 'move' %} {% include "malaysia/_nav.html" %} - -
-

입고 / 출고 / 조정 — 낱개 일괄

- 전체 낱개 아이템에 수량만 입력. 빈칸/0은 제외. IN/OUT 양수, ADJUST는 ±. (세트는 아래에서 출고) -
- -
-
- -
-

세트 출고 — BOM 분해 (itemcode_db)

- 세트 수량 입력 시 set_components 기준으로 구성품별 OUT 자동 생성. 빈칸/0 제외. -
- -
- - - +
+ +
+

낱개 아이템

+
+ + + + {% for it in individual_items %} + + + + + + {% endfor %} + +
CodeNameQty
{{ it.item_code }}{{ it.item_name }}
+
-
- - - - {% for it in set_items %} - - - - - - {% endfor %} - -
Set CodeSet NameSet Qty (OUT)
{{ it.item_code }}{{ it.item_name }}
+ + +
+

세트 아이템 — 출고(OUT) · BOM 분해

+ itemcode_db set_components 기준 구성품별 OUT 자동 생성 +
+ + + + {% for it in set_items %} + + + + + + {% endfor %} + +
Set CodeSet NameQty (OUT)
{{ it.item_code }}{{ it.item_name }}
+
-
- -
+
+ +
+ +
+