Files
dbx-main/app/modules/malaysia/templates/malaysia/stocktake_detail.html
T
king fa396bd237 feat(malaysia): 재고조사 랙에 Side B 아래 Ground(G1~G6) 6칸 추가
Side B 다음에 Ground 영역을 추가하고 단일 파렛트 6칸(G1~G6)을
한 줄로 배치. rack_cell_codes 검증 목록과 rack_layout 렌더 구조에
반영. 면 제목을 side.label 로 바꿔 A/B는 'Side A/B', Ground는
'Ground'로 표기. 입력/이동 저장 검증 모두 G1~G6 허용.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 14:23:40 +09:00

204 lines
9.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% 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 and has_prev_rack %}
<button type="submit" class="erp-btn erp-btn-outline"
formaction="/malaysia/stocktakes/{{ stocktake.id }}/rack/load-previous"
formnovalidate
onclick="return confirm('이전 재고조사의 저장값을 불러옵니다. 현재 화면에 입력한 내용은 사라지고 이전값으로 대체됩니다. 계속?');">이전값 불러오기</button>
{% endif %}
{% if is_draft %}<button type="submit" class="erp-btn erp-btn-outline">입력 저장</button>{% endif %}
{% if is_draft and is_admin %}
<button type="submit" class="erp-btn erp-btn-primary"
formaction="/malaysia/stocktakes/{{ stocktake.id }}/rack/finalize"
onclick="return confirm('현재 입력한 랙 내용을 저장하고 확정합니다. 확정하면 전산 재고와의 차이가 STOCKTAKE 이동으로 기록되고 이후 수정 불가합니다. 계속?');">확정 (전산 반영)</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.label }}</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 %}
<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">{{ "{:,}".format(r.direct_qty) }}</td>
<td style="text-align:right">{{ "{:,}".format(r.from_set_qty) }}</td>
<td style="text-align:right;font-weight:600;">{{ "{:,}".format(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 %}