2ab38c7dbd
- set_movement(콤보 출고 원장)을 이동이력에 병합, 구분 컬럼(낱개/콤보) 추가 - db.list_set_movements (동일 필터: 창고/종류/날짜) - 콤보 줄은 set_code 를 Item 으로 표시 → 콤보 몇 개 나갔는지 확인 가능 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
175 lines
8.1 KiB
HTML
175 lines
8.1 KiB
HTML
{% extends "erp_base.html" %}
|
|
|
|
{% block head_extra %}
|
|
<style>
|
|
.mys-compact .erp-table th,
|
|
.mys-compact .erp-table td { padding-top:4px; padding-bottom:4px; }
|
|
.mys-compact .erp-input { padding-top:4px; padding-bottom:4px; }
|
|
.mys-compact td.nm { white-space:nowrap; }
|
|
.mys-compact th.ccol, .mys-compact td.ccol { white-space:nowrap; width:88px; }
|
|
.mys-compact .qcol { width:110px; }
|
|
.mys-compact .qcol .erp-input { width:100px; }
|
|
.mys-compact .optfields .erp-field { width:100%; }
|
|
.mys-compact .optfields .erp-input,
|
|
.mys-compact .optfields .erp-select { width:100%; box-sizing:border-box; }
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<section class="mys mys-compact">
|
|
{% include "malaysia/_i18n.html" %}
|
|
{% set active_tab = 'move' %}
|
|
{% include "malaysia/_nav.html" %}
|
|
|
|
<!-- 3분할: 좌(옵션) · 중(낱개) · 우(세트), 버튼 하나 -->
|
|
<form method="post" action="/malaysia/movements/apply" onsubmit="return mysCheckComboIn(this)">
|
|
<input type="hidden" name="warehouse_code" value="{{ selected_wh }}" />
|
|
|
|
<div class="mys-grid" style="display:grid;grid-template-columns:280px 1fr 1fr;gap:16px;align-items:start;">
|
|
|
|
<!-- 좌: 종류 / 날짜 / 메모 -->
|
|
<div class="erp-card">
|
|
<div class="cpg-card-head"><h2>등록 옵션</h2></div>
|
|
<div class="optfields" style="display:flex;flex-direction:column;gap:10px;margin-top:8px;">
|
|
<label class="erp-field"><span>이동 종류 (낱개)</span>
|
|
<select class="erp-select" name="movement_type" required>
|
|
<option value="IN">IN (입고)</option>
|
|
<option value="OUT">OUT (출고)</option>
|
|
<option value="ADJUST">ADJUST (조정 ±)</option>
|
|
</select></label>
|
|
<label class="erp-field"><span>날짜</span>
|
|
<input class="erp-input" type="date" name="movement_date" value="{{ today }}" required /></label>
|
|
<label class="erp-field"><span>메모</span>
|
|
<input class="erp-input" type="text" name="memo" /></label>
|
|
</div>
|
|
<div class="erp-page-actions" style="margin-top:12px;display:flex;gap:8px;">
|
|
<button type="submit" class="erp-btn erp-btn-primary">입력</button>
|
|
<button type="button" class="erp-btn erp-btn-outline" onclick="mysDownloadMovements(this)">다운로드</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 중: 낱개 -->
|
|
<div class="erp-card">
|
|
<div class="cpg-card-head"><h2>낱개 상품</h2></div>
|
|
<div class="erp-table-wrap" style="margin-top:8px;">
|
|
<table class="erp-table">
|
|
<thead><tr><th class="ccol">Code</th><th>Name</th><th class="qcol">Qty</th></tr></thead>
|
|
<tbody>
|
|
{% for it in individual_items %}
|
|
<tr>
|
|
<td class="ccol">{{ it.item_code }}</td>
|
|
<td class="nm" data-en="{{ it.item_name }}" data-ko="{{ ko_names.get(it.item_code, it.item_name) }}">{{ it.item_name }}</td>
|
|
<td class="qcol"><input class="erp-input" type="number" name="qty_{{ it.item_code }}" placeholder="—" /></td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 우: 세트 -->
|
|
<div class="erp-card">
|
|
<div class="cpg-card-head"><h2>콤보 상품</h2></div>
|
|
<div class="erp-table-wrap" style="margin-top:8px;">
|
|
<table class="erp-table">
|
|
<thead><tr><th class="ccol">Set Code</th><th>Set Name</th><th class="qcol">Qty</th></tr></thead>
|
|
<tbody>
|
|
{% for it in set_items %}
|
|
<tr>
|
|
<td class="ccol">{{ it.item_code }}</td>
|
|
<td class="nm" data-en="{{ it.item_name }}" data-ko="{{ ko_names.get(it.item_code, it.item_name) }}">{{ it.item_name }}</td>
|
|
<td class="qcol"><input class="erp-input" type="number" min="0" name="qty_{{ it.item_code }}" placeholder="—" /></td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<script>
|
|
// 콤보(MY-)는 입고 불가 — IN 선택 + 콤보 수량 입력 시 안내 후 제출 차단
|
|
function mysCheckComboIn(form) {
|
|
var type = form.querySelector('[name=movement_type]').value;
|
|
if (type !== 'IN') return true;
|
|
var keyed = [];
|
|
form.querySelectorAll('input[name^="qty_MY-"]').forEach(function (inp) {
|
|
var v = (inp.value || '').trim();
|
|
if (v !== '' && v !== '0') keyed.push(inp.name.replace('qty_', ''));
|
|
});
|
|
if (keyed.length) {
|
|
var en = localStorage.getItem('mys_lang') === 'en';
|
|
alert(en
|
|
? 'Combo products cannot be received.\nReceive them as separate single items.\n\nNot allowed: ' + keyed.join(', ')
|
|
: '콤보 상품은 입고할 수 없습니다.\n낱개로 분리해서 입고하세요.\n\n입고 불가 콤보: ' + keyed.join(', '));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function mysDownloadMovements(btn) {
|
|
var form = btn.closest('form');
|
|
var type = form.querySelector('[name=movement_type]').value;
|
|
var date = form.querySelector('[name=movement_date]').value;
|
|
var wh = form.querySelector('[name=warehouse_code]').value;
|
|
if (type !== 'IN' && type !== 'OUT') {
|
|
alert('다운로드는 IN(입고) 또는 OUT(출고)만 가능합니다.');
|
|
return;
|
|
}
|
|
if (!date) { alert('날짜를 선택하세요.'); return; }
|
|
var url = '/malaysia/movements/export?wh=' + encodeURIComponent(wh)
|
|
+ '&type=' + encodeURIComponent(type)
|
|
+ '&date=' + encodeURIComponent(date);
|
|
window.location.href = url;
|
|
}
|
|
</script>
|
|
|
|
<!-- 이동 이력 -->
|
|
<div class="erp-card" style="margin-top:16px;">
|
|
<div class="cpg-card-head" style="display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:8px;">
|
|
<h2><span class="i18n">이동 이력</span>{% if filter_date %} · {{ filter_date }}{% endif %}</h2>
|
|
<span style="display:flex;gap:6px;align-items:center;flex-wrap:wrap;">
|
|
<form method="get" style="display:flex;gap:6px;align-items:center;">
|
|
<input type="hidden" name="wh" value="{{ selected_wh }}" />
|
|
{% if filter_type %}<input type="hidden" name="type" value="{{ filter_type }}" />{% endif %}
|
|
<input class="erp-input" type="date" name="date" value="{{ filter_date }}" onchange="this.form.submit()" />
|
|
{% if filter_date %}<a class="erp-btn erp-btn-outline" href="/malaysia/movements?wh={{ selected_wh }}{% if filter_type %}&type={{ filter_type }}{% endif %}">날짜해제</a>{% endif %}
|
|
</form>
|
|
<a class="erp-btn erp-btn-outline" href="/malaysia/movements?wh={{ selected_wh }}{% if filter_date %}&date={{ filter_date }}{% endif %}">전체</a>
|
|
{% for t in ['IN','OUT','ADJUST','STOCKTAKE'] %}
|
|
<a class="erp-btn {% if filter_type==t %}erp-btn-primary{% else %}erp-btn-outline{% endif %}" href="/malaysia/movements?wh={{ selected_wh }}&type={{ t }}{% if filter_date %}&date={{ filter_date }}{% endif %}">{{ t }}</a>
|
|
{% endfor %}
|
|
</span>
|
|
</div>
|
|
<div class="erp-table-wrap">
|
|
<table class="erp-table">
|
|
<thead><tr><th>Date</th><th>구분</th><th>Type</th><th>Item</th><th style="text-align:right">Qty</th><th>Memo</th><th>By</th></tr></thead>
|
|
<tbody>
|
|
{% for m in movements %}
|
|
<tr>
|
|
<td>{{ m.date_label }}</td>
|
|
<td>
|
|
{% if m.kind == 'set' %}
|
|
<span class="erp-badge" style="background:#fef3c7;color:#92400e;">콤보</span>
|
|
{% else %}
|
|
<span class="erp-badge" style="background:#e0e7ff;color:#3730a3;">낱개</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>{{ m.movement_type }}</td>
|
|
<td>{{ m.item_code }}</td>
|
|
<td style="text-align:right">{{ m.qty }}</td>
|
|
<td>{{ m.memo or '—' }}</td>
|
|
<td>{{ m.created_by or '—' }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
{% if not movements %}
|
|
<tr><td colspan="7" class="erp-muted">이동 이력이 없습니다.</td></tr>
|
|
{% endif %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
{% endblock %}
|