eacde5b322
- 입수량/박스 입력을 한 줄로(.rk-line) - 칸 ∑ 합계 제거 — 서로 다른 상품을 합산해 의미 없음(입력·랙보기 양쪽) - 상품별 행 소계(합계)는 유지(개별 SKU 기준 유효) - malaysia_rack.js: 칸 합계 로직 삭제, 행 소계만 갱신 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
199 lines
9.4 KiB
HTML
199 lines
9.4 KiB
HTML
{% extends "erp_base.html" %}
|
||
|
||
{% macro rack_row(cell, items, entry=None, disabled=False) %}
|
||
<div class="rk-row">
|
||
<input type="hidden" name="rk_cell" value="{{ cell }}">
|
||
<select name="rk_sku" class="erp-input rk-sku" {% if disabled %}disabled{% endif %}>
|
||
<option value="">— 선택 —</option>
|
||
{% for it in items %}
|
||
<option value="{{ it.item_code }}" {% if entry and entry.sku_code == it.item_code %}selected{% endif %}>{{ it.item_name }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
<div class="rk-line">
|
||
<label class="rk-f"><span>입수량</span>
|
||
<input class="erp-input rk-upb" type="number" min="1" name="rk_upb"
|
||
value="{{ entry.units_per_box if entry else '' }}" {% if disabled %}disabled{% endif %} /></label>
|
||
<label class="rk-f"><span>박스</span>
|
||
<input class="erp-input rk-box" type="number" min="1" name="rk_box"
|
||
value="{{ entry.box_count if entry else '' }}" {% if disabled %}disabled{% endif %} /></label>
|
||
</div>
|
||
<div class="rk-foot">
|
||
<span><span class="i18n">합계</span> <b class="rk-sub">{{ "{:,}".format(entry.units_per_box * entry.box_count) if entry else 0 }}</b></span>
|
||
{% if not disabled %}<button type="button" class="rk-del" title="행 삭제">× <span class="i18n">삭제</span></button>{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endmacro %}
|
||
|
||
{% block head_extra %}
|
||
<style>
|
||
.mys-compact .erp-table th,
|
||
.mys-compact .erp-table td { padding-top:4px; padding-bottom:4px; }
|
||
/* 상단 정보 바 — 한 줄 */
|
||
.mys-info-bar {
|
||
display:flex; align-items:center; gap:16px; flex-wrap:nowrap;
|
||
border:1px solid #e2e8f0; border-radius:10px; background:#fff; padding:10px 14px;
|
||
}
|
||
.mys-info-bar h2 { margin:0; white-space:nowrap; }
|
||
.mys-info-bar > div { white-space:nowrap; }
|
||
.mys-info-bar .mys-hint {
|
||
flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis;
|
||
white-space:nowrap; font-size:12px;
|
||
}
|
||
.mys-info-bar > button { flex:0 0 auto; }
|
||
|
||
/* 랙 보드 — 두 면을 세로로 적층(각 면 풀폭 → 칸 넓게) */
|
||
.rack-board { display:flex; flex-direction:column; gap:18px; }
|
||
.rack-side { width:100%; }
|
||
.rack-side h3 { text-align:center; margin:0 0 8px; }
|
||
.rack-row { display:grid; grid-template-columns:repeat(6, 1fr); gap:6px; margin-bottom:6px; }
|
||
.rack-cell {
|
||
border:1px solid #cbd5e1; border-radius:6px; padding:4px;
|
||
background:#f8fafc; min-height:70px; display:flex; flex-direction:column; min-width:0;
|
||
}
|
||
.rack-cell-head {
|
||
display:flex; align-items:center; gap:4px; justify-content:space-between;
|
||
font-size:11px; font-weight:600; color:#475569; margin-bottom:4px;
|
||
}
|
||
.rack-cell-head .rk-add {
|
||
border:1px solid #94a3b8; background:#fff; border-radius:4px;
|
||
width:20px; height:20px; line-height:1; padding:0; cursor:pointer; font-weight:700; flex:0 0 auto;
|
||
}
|
||
.rk-rows { display:flex; flex-direction:column; gap:6px; }
|
||
/* 입력란 세로 배치 — 좁은 칸에서도 레이블이 보이게 */
|
||
.rk-row {
|
||
display:flex; flex-direction:column; gap:3px;
|
||
border:1px solid #e2e8f0; border-radius:5px; background:#fff; padding:4px;
|
||
}
|
||
.rk-row .rk-sku { font-size:12px; padding:2px 4px; width:100%; min-width:0; }
|
||
.rk-row .rk-line { display:flex; gap:6px; }
|
||
.rk-row .rk-f { display:flex; align-items:center; gap:3px; font-size:11px; margin:0; flex:1 1 0; min-width:0; }
|
||
.rk-row .rk-f > span { flex:0 0 auto; color:#64748b; }
|
||
.rk-row .rk-upb, .rk-row .rk-box {
|
||
flex:1 1 auto; min-width:0; font-size:12px; padding:2px 4px; text-align:right;
|
||
}
|
||
.rk-row .rk-foot {
|
||
display:flex; align-items:center; justify-content:space-between;
|
||
font-size:11px; color:#475569; margin-top:1px;
|
||
}
|
||
.rk-row .rk-sub { color:#2563eb; font-weight:600; }
|
||
.rk-row .rk-del {
|
||
border:none; background:transparent; color:#dc2626; cursor:pointer;
|
||
font-size:11px; line-height:1; padding:0;
|
||
}
|
||
</style>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<section class="mys mys-compact">
|
||
{% include "malaysia/_i18n.html" %}
|
||
<div class="erp-page-actions">
|
||
<a class="erp-btn erp-btn-primary" href="/malaysia/stocktakes?wh={{ stocktake.warehouse_code }}">◀◀ 조사 목록</a>
|
||
</div>
|
||
|
||
<form method="post" action="/malaysia/stocktakes/{{ stocktake.id }}/rack/bulk">
|
||
<!-- 상단 정보 스트립 (한 줄) -->
|
||
<div class="mys-info-bar">
|
||
<h2><span class="i18n">재고조사</span> #{{ stocktake.id }}</h2>
|
||
<div><span class="erp-muted">날짜</span> {{ stocktake.stocktake_date }}</div>
|
||
<div><span class="erp-muted">창고</span> {{ stocktake.warehouse_code }}</div>
|
||
<div><span class="erp-muted">상태</span>
|
||
{% if stocktake.status=='finalized' %}<span class="erp-badge erp-badge-success">확정</span>
|
||
{% elif stocktake.status=='cancelled' %}<span class="erp-badge erp-badge-neutral">취소</span>
|
||
{% else %}<span class="erp-badge erp-badge-inverse">작성중</span>{% endif %}
|
||
</div>
|
||
{% if stocktake.finalized_at %}<div><span class="erp-muted">확정시각</span> {{ stocktake.finalized_at }}</div>{% endif %}
|
||
{% if missing_bom %}<span style="color:#b91c1c;font-weight:600;white-space:nowrap;">⚠ BOM 누락: {{ missing_bom|join(', ') }}</span>{% endif %}
|
||
<span class="erp-muted i18n mys-hint">랙 칸에 아이템·입수량(박스당 개수)·박스 수를 입력하면 낱개/콤보 수량이 자동 집계됩니다. 한 칸에 여러 아이템은 + 로 추가하세요.</span>
|
||
{% if is_draft %}<button type="submit" class="erp-btn erp-btn-primary">입력 저장</button>{% endif %}
|
||
</div>
|
||
|
||
<!-- 랙 보드 -->
|
||
<div class="erp-card" style="margin-top:12px;">
|
||
<div class="cpg-card-head"><h2>랙 입력 (박스 단위)</h2></div>
|
||
<div class="rack-board" style="margin-top:10px;">
|
||
{% for side in rack_layout %}
|
||
<div class="rack-side">
|
||
<h3>Side {{ side.side }}</h3>
|
||
{% for row in side.rows %}
|
||
<div class="rack-row">
|
||
{% for col in row.cols %}
|
||
{% for cell in col %}
|
||
<div class="rack-cell" data-cell="{{ cell }}">
|
||
<div class="rack-cell-head">
|
||
<span>{{ cell }}</span>
|
||
{% if is_draft %}<button type="button" class="rk-add" title="아이템 추가">+</button>{% endif %}
|
||
</div>
|
||
<div class="rk-rows">
|
||
{% set entries = rack_by_cell.get(cell, []) %}
|
||
{% for e in entries %}
|
||
{{ rack_row(cell, rack_items, entry=e, disabled=(not is_draft)) }}
|
||
{% endfor %}
|
||
{% if is_draft and not entries %}
|
||
{{ rack_row(cell, rack_items) }}
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
{% endfor %}
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
</form>
|
||
|
||
<!-- JS 클론용 빈 행 템플릿(옵션 1벌만 보유) -->
|
||
<template id="rk-row-tpl">
|
||
{{ rack_row('', rack_items) }}
|
||
</template>
|
||
|
||
<!-- 확정/취소/삭제 -->
|
||
<div class="erp-page-actions" style="margin-top:12px;display:flex;gap:8px;flex-wrap:wrap;">
|
||
{% if is_draft %}
|
||
{% if is_admin %}
|
||
<form method="post" action="/malaysia/stocktakes/{{ stocktake.id }}/finalize"
|
||
onsubmit="return confirm('확정하면 전산 재고와의 차이가 STOCKTAKE 이동으로 기록되고, 이후 수정 불가합니다. 계속?');">
|
||
<button type="submit" class="erp-btn erp-btn-primary" {% if missing_bom %}disabled title="BOM 누락 콤보 해결 필요"{% endif %}>확정 (전산 반영)</button>
|
||
</form>
|
||
{% endif %}
|
||
<form method="post" action="/malaysia/stocktakes/{{ stocktake.id }}/cancel" onsubmit="return confirm('이 조사를 취소합니다. 계속?');">
|
||
<button type="submit" class="erp-btn erp-btn-danger">취소</button>
|
||
</form>
|
||
{% endif %}
|
||
{% if is_super %}
|
||
<form method="post" action="/malaysia/stocktakes/{{ stocktake.id }}/delete"
|
||
onsubmit="return confirm('이 재고조사를 완전 삭제합니다(되돌릴 수 없음). 계속?');">
|
||
<button type="submit" class="erp-btn erp-btn-danger">삭제 (슈퍼관리자)</button>
|
||
</form>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<!-- 최종 계산 결과 (낱개 기준) -->
|
||
<div class="erp-card" style="margin-top:16px;">
|
||
<div class="cpg-card-head"><h2>최종 계산 (낱개 기준)</h2></div>
|
||
<span class="erp-muted">total_qty = direct_qty(낱개 직접) + from_set_qty(콤보 분해)</span>
|
||
<div class="erp-table-wrap">
|
||
<table class="erp-table">
|
||
<thead><tr><th>Item Code</th><th>Product Name</th><th style="text-align:right">Direct</th><th style="text-align:right">From Set</th><th style="text-align:right">Total</th></tr></thead>
|
||
<tbody>
|
||
{% for r in result_rows %}
|
||
<tr>
|
||
<td>{{ r.item_code }}</td>
|
||
<td>{{ r.item_name }}</td>
|
||
<td style="text-align:right">{{ r.direct_qty }}</td>
|
||
<td style="text-align:right">{{ r.from_set_qty }}</td>
|
||
<td style="text-align:right;font-weight:600;">{{ r.total_qty }}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
{% if not result_rows %}
|
||
<tr><td colspan="5" class="erp-muted">입력된 수량이 없습니다.</td></tr>
|
||
{% endif %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
<script src="/static/malaysia_rack.js?v=20260614c"></script>
|
||
{% endblock %}
|