diff --git a/app/modules/malaysia/router.py b/app/modules/malaysia/router.py index 455e6f3..a2b1c93 100644 --- a/app/modules/malaysia/router.py +++ b/app/modules/malaysia/router.py @@ -464,6 +464,80 @@ async def movements_export(request: Request) -> StreamingResponse: ) +@router.get("/rack/export") +async def rack_export(request: Request) -> StreamingResponse: + """창고 랙 보기 데이터를 엑셀로 내보낸다. + + 랙 보기와 동일 기준(latest_rack_stocktake)의 모든 칸 항목을 한 행씩. + 컬럼: 아이템 코드 / 이름 / 수량 / 랙번호. 표 전체에 자동필터, + 각 열은 내용 길이에 맞춰 너비 자동. + """ + import io # noqa: WPS433 + + guard = _guard(request) + if not isinstance(guard, tuple): + return guard # type: ignore[return-value] + st, _user = guard + wh = _selected_wh(request, st) + latest = st.latest_rack_stocktake(warehouse_code=wh) + names = {**st.item_name_map(), **store.lid_name_map()} + + rows: list[tuple[str, str, int, str]] = [] + if latest: + for e in st.list_rack_entries(stocktake_id=latest["id"]): + code = e["sku_code"] + qty = int(e["units_per_box"]) * int(e["box_count"]) + rows.append((code, names.get(code, code), qty, e["cell_code"])) + # 랙번호 → 아이템코드 순 정렬(보기 흐름과 동일하게 칸 기준). + rows.sort(key=lambda r: (r[3], r[0])) + + from openpyxl import Workbook # noqa: WPS433 + from openpyxl.styles import Alignment, Font, PatternFill # noqa: WPS433 + from openpyxl.utils import get_column_letter # noqa: WPS433 + + wb = Workbook() + ws = wb.active + ws.title = "rack" + header = ["아이템 코드", "이름", "수량", "랙번호"] + ws.append(header) + for code, name, qty, cell in rows: + ws.append([code, name, qty, cell]) + + # 제목행: 파랑 배경 + 흰색 굵은글씨 + 가운데 정렬 + fill = PatternFill(start_color="FF4472C4", end_color="FF4472C4", fill_type="solid") + font = Font(color="FFFFFFFF", bold=True) + center = Alignment(horizontal="center", vertical="center") + for col in range(1, len(header) + 1): + cell = ws.cell(row=1, column=col) + cell.fill = fill + cell.font = font + cell.alignment = center + + # 표 전체 자동필터 + ws.auto_filter.ref = ws.dimensions + + # 열너비 자동(내용 길이 기준, 한글 폭 보정). 최소 8 ~ 최대 50. + for col in range(1, len(header) + 1): + letter = get_column_letter(col) + longest = 0 + for cell in ws[letter]: + v = "" if cell.value is None else str(cell.value) + width = sum(2 if ord(ch) > 0x1100 else 1 for ch in v) + longest = max(longest, width) + ws.column_dimensions[letter].width = max(8, min(50, longest + 2)) + + buf = io.BytesIO() + wb.save(buf) + buf.seek(0) + sdate = (latest or {}).get("stocktake_date", today_kst().isoformat()) + fname = f"malaysia_rack_{wh}_{sdate}.xlsx" + return StreamingResponse( + buf, + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + headers={"Content-Disposition": f'attachment; filename="{fname}"'}, + ) + + # ════════════════════════════════════════════════════════════ # 일일 재고조사 # ════════════════════════════════════════════════════════════ diff --git a/app/modules/malaysia/templates/malaysia/rack_view.html b/app/modules/malaysia/templates/malaysia/rack_view.html index a69c119..3316e7a 100644 --- a/app/modules/malaysia/templates/malaysia/rack_view.html +++ b/app/modules/malaysia/templates/malaysia/rack_view.html @@ -140,6 +140,7 @@ {% if stocktake %}
칸을 드래그해 다른 칸으로 옮기면 구성이 이동(채워진 칸끼리는 맞교환)됩니다. 칸 번호는 그대로입니다. 인쇄할 칸은 체크 후 인쇄하세요. + 다운로드