diff --git a/app/modules/malaysia/router.py b/app/modules/malaysia/router.py index a978844..c2d6a84 100644 --- a/app/modules/malaysia/router.py +++ b/app/modules/malaysia/router.py @@ -556,20 +556,11 @@ async def stocktake_lines_bulk( return RedirectResponse(url=f"/malaysia/stocktakes/{stocktake_id}", status_code=303) -@router.post("/stocktakes/{stocktake_id:int}/rack/bulk") -async def stocktake_rack_bulk( - request: Request, stocktake_id: int, user: dict[str, Any] = Depends(_require_user) -) -> RedirectResponse: - """랙 입력 일괄 저장. +def _parse_rack_form(form: Any) -> list[dict[str, Any]]: + """랙 폼 병렬배열(rk_cell/rk_sku/rk_upb/rk_box) → 입력 항목 리스트. - form 병렬 배열: rk_cell[], rk_sku[], rk_upb[], rk_box[] (DOM 순서 유지). SKU 미선택/입수량·박스수 ≤ 0 인 행은 스킵(미입력 행 취급). - 저장 시 랙 항목 교체 + SKU별 집계로 daily_stocktake_line 재생성. """ - st = _store(request) - if st is None: - raise HTTPException(status_code=503, detail="malaysia_stock_db 미설정") - form = await request.form() cells = form.getlist("rk_cell") skus = form.getlist("rk_sku") upbs = form.getlist("rk_upb") @@ -589,6 +580,22 @@ async def stocktake_rack_bulk( entries.append( {"cell_code": cell, "sku_code": code, "units_per_box": u, "box_count": b} ) + return entries + + +@router.post("/stocktakes/{stocktake_id:int}/rack/bulk") +async def stocktake_rack_bulk( + request: Request, stocktake_id: int, user: dict[str, Any] = Depends(_require_user) +) -> RedirectResponse: + """랙 입력 일괄 저장. + + 저장 시 랙 항목 교체 + SKU별 집계로 daily_stocktake_line 재생성. + """ + st = _store(request) + if st is None: + raise HTTPException(status_code=503, detail="malaysia_stock_db 미설정") + form = await request.form() + entries = _parse_rack_form(form) try: st.replace_rack(stocktake_id=stocktake_id, entries=entries) except KeyError: @@ -598,6 +605,34 @@ async def stocktake_rack_bulk( return RedirectResponse(url=f"/malaysia/stocktakes/{stocktake_id}", status_code=303) +@router.post("/stocktakes/{stocktake_id:int}/rack/finalize") +async def stocktake_rack_finalize( + request: Request, stocktake_id: int, user: dict[str, Any] = Depends(_require_user) +) -> RedirectResponse: + """랙 입력 저장 + 즉시 확정(한 번에). + + 확정 버튼이 랙 폼과 별개라 랙 데이터가 누락되는 문제를 막기 위해, + 화면의 랙 입력을 먼저 저장(replace_rack)한 뒤 finalize 한다. + """ + from app.store import is_admin # noqa: WPS433 + + if not is_admin(user): + raise HTTPException(status_code=403, detail="전산 재고 반영은 관리자만 가능합니다.") + st = _store(request) + if st is None: + raise HTTPException(status_code=503, detail="malaysia_stock_db 미설정") + form = await request.form() + entries = _parse_rack_form(form) + try: + st.replace_rack(stocktake_id=stocktake_id, entries=entries) + st.finalize_stocktake(stocktake_id=stocktake_id, bom_map=_bom_map(request)) + except KeyError: + raise HTTPException(status_code=404, detail="재고조사를 찾을 수 없습니다.") + except ValueError as exc: + raise HTTPException(status_code=400, detail=str(exc)) + return RedirectResponse(url=f"/malaysia/stocktakes/{stocktake_id}", status_code=303) + + @router.post("/stocktakes/{stocktake_id:int}/lines/{line_id:int}/delete") async def stocktake_line_delete( request: Request, stocktake_id: int, line_id: int, diff --git a/app/modules/malaysia/templates/malaysia/stocktake_detail.html b/app/modules/malaysia/templates/malaysia/stocktake_detail.html index fb7b3e4..b6dc36e 100644 --- a/app/modules/malaysia/templates/malaysia/stocktake_detail.html +++ b/app/modules/malaysia/templates/malaysia/stocktake_detail.html @@ -103,8 +103,13 @@ {% if stocktake.finalized_at %}
확정시각 {{ stocktake.finalized_at }}
{% endif %} {% if missing_bom %}⚠ BOM 누락: {{ missing_bom|join(', ') }}{% endif %} - 랙 칸에 아이템·입수량(박스당 개수)·박스 수를 입력하면 낱개/콤보 수량이 자동 집계됩니다. 한 칸에 여러 아이템은 + 로 추가하세요. - {% if is_draft %}{% endif %} + 랙 칸에 아이템·입수량(박스당 개수)·박스 수를 입력하면 낱개/콤보 수량이 자동 집계됩니다. 한 칸에 여러 아이템은 + 로 추가하세요. 저장 또는 확정 시 화면 입력이 함께 반영됩니다. + {% if is_draft %}{% endif %} + {% if is_draft and is_admin %} + + {% endif %} @@ -151,12 +156,6 @@
{% if is_draft %} - {% if is_admin %} -
- -
- {% endif %}