diff --git a/app/modules/cupang/router.py b/app/modules/cupang/router.py index 8ae27ae..7405682 100644 --- a/app/modules/cupang/router.py +++ b/app/modules/cupang/router.py @@ -129,7 +129,11 @@ async def index(request: Request) -> HTMLResponse: store, user = guard year, month = _ym(request) - shipments = store.list_shipments(year=year, month=month) + # 취소된 묶음은 달력·목록 어디에도 보이지 않는다(soft delete = 삭제로 취급). + shipments = [ + s for s in store.list_shipments(year=year, month=month) + if s.get("status") != "취소" + ] # 달력 칸에는 출고 건수와 센터 수만 보여준다. counts: dict[str, dict[str, Any]] = {} @@ -340,10 +344,19 @@ async def update( return RedirectResponse(url=f"/cupang/{shipment_id}", status_code=303) +def _safe_next(raw: str, fallback: str) -> str: + """열린 리다이렉트 방지 — /cupang/ 안쪽 경로만 허용.""" + nxt = (raw or "").strip() + if nxt.startswith("/cupang/") and "//" not in nxt[1:]: + return nxt + return fallback + + @router.post("/{shipment_id:int}/delete") async def delete( request: Request, shipment_id: int, + next: str = Form(""), user: dict[str, Any] = Depends(_require_user), ) -> RedirectResponse: """운영 안전: 기본은 status='취소' soft delete.""" @@ -354,7 +367,9 @@ async def delete( store.soft_delete(shipment_id=shipment_id) except KeyError: raise HTTPException(status_code=404, detail="출고 묶음을 찾을 수 없습니다.") - return RedirectResponse(url=f"/cupang/{shipment_id}", status_code=303) + return RedirectResponse( + url=_safe_next(next, f"/cupang/{shipment_id}"), status_code=303 + ) @router.post("/{shipment_id:int}/hard-delete") @@ -374,6 +389,36 @@ async def hard_delete( return RedirectResponse(url="/cupang/", status_code=303) +@router.post("/day-delete") +async def day_delete( + request: Request, + date: str = Form(...), + next: str = Form(""), + user: dict[str, Any] = Depends(_require_user), +) -> RedirectResponse: + """선택한 출고일의 묶음을 한 번에 취소 처리한다(soft delete).""" + store = _store(request) + if store is None: + raise HTTPException(status_code=503, detail="cupang_db 미설정") + + day = (date or "").strip() + try: + _date.fromisoformat(day) + except ValueError: + raise HTTPException(status_code=400, detail="날짜 형식이 올바르지 않습니다.") + + for ship in store.list_shipments(date_from=day, date_to=day): + if ship.get("status") == "취소": + continue + try: + store.soft_delete(shipment_id=ship["id"]) + except KeyError: + continue + return RedirectResponse( + url=_safe_next(next, f"/cupang/?date={day}"), status_code=303 + ) + + # ════════════════════════════════════════════════════════════ # 입고센터 관리 # ════════════════════════════════════════════════════════════ diff --git a/app/modules/cupang/templates/cupang/box_calc.html b/app/modules/cupang/templates/cupang/box_calc.html index cd220a3..ebfc39e 100644 --- a/app/modules/cupang/templates/cupang/box_calc.html +++ b/app/modules/cupang/templates/cupang/box_calc.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
diff --git a/app/modules/cupang/templates/cupang/box_rules.html b/app/modules/cupang/templates/cupang/box_rules.html index db9d195..8f3f934 100644 --- a/app/modules/cupang/templates/cupang/box_rules.html +++ b/app/modules/cupang/templates/cupang/box_rules.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
diff --git a/app/modules/cupang/templates/cupang/centers.html b/app/modules/cupang/templates/cupang/centers.html index 2fbba99..5d9830f 100644 --- a/app/modules/cupang/templates/cupang/centers.html +++ b/app/modules/cupang/templates/cupang/centers.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
diff --git a/app/modules/cupang/templates/cupang/detail.html b/app/modules/cupang/templates/cupang/detail.html index c40a34e..6fc87cf 100644 --- a/app/modules/cupang/templates/cupang/detail.html +++ b/app/modules/cupang/templates/cupang/detail.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
diff --git a/app/modules/cupang/templates/cupang/form.html b/app/modules/cupang/templates/cupang/form.html index dd607b5..ab52fc7 100644 --- a/app/modules/cupang/templates/cupang/form.html +++ b/app/modules/cupang/templates/cupang/form.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
diff --git a/app/modules/cupang/templates/cupang/index.html b/app/modules/cupang/templates/cupang/index.html index b67d753..698a67b 100644 --- a/app/modules/cupang/templates/cupang/index.html +++ b/app/modules/cupang/templates/cupang/index.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
@@ -60,16 +60,22 @@

{{ selected_date }} 출고

{{ sel_shipments|length }}건 + {% if sel_shipments %} + + {% endif %}
{% if sel_shipments %} - + {% endfor %} @@ -94,7 +100,78 @@ + + + + +
{% endblock %} +{% block scripts %} + +{% endblock %} + diff --git a/app/modules/cupang/templates/cupang/products.html b/app/modules/cupang/templates/cupang/products.html index 1f1b343..b2fac22 100644 --- a/app/modules/cupang/templates/cupang/products.html +++ b/app/modules/cupang/templates/cupang/products.html @@ -1,6 +1,6 @@ {% extends "erp_base.html" %} -{% block head_extra %}{% endblock %} +{% block head_extra %}{% endblock %} {% block content %}
diff --git a/app/static/cupang.css b/app/static/cupang.css index eef83cd..14df58c 100644 --- a/app/static/cupang.css +++ b/app/static/cupang.css @@ -836,6 +836,21 @@ body.cpg-dragging { cursor: grabbing; user-select: none; } .cpg-ship-iqty { color: var(--color-midtone-gray); } .cpg-ship-ibox { font-weight: 600; } +/* 달력 오른쪽 — 삭제 버튼 / 확인 팝업 */ +.cpg-list-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } +.cpg-list-head > h2 { flex: 0 0 auto; } +.cpg-list-head > .erp-muted { flex: 1 1 auto; } +.cpg-day-del { flex: 0 0 auto; } +.cpg-ship-name { + flex: 1 1 auto; min-width: 0; + font-size: 14px; font-weight: 700; text-decoration: none; color: inherit; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} +.cpg-ship-name:hover { text-decoration: underline; } +.cpg-del-box { width: min(400px, calc(100vw - 32px)); } +.cpg-del-msg { margin: 0; font-size: 14px; line-height: 1.5; word-break: keep-all; } +.cpg-del-act { display: flex; gap: 6px; justify-content: flex-end; margin-top: 4px; } + /* ③ 헤더의 분배 확정 버튼 */ .cpg-dist-head-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } .cpg-dist-head-bar > .erp-muted { flex: 1 1 auto; min-width: 0; }