feat(cupang): 자투리 혼합 박스 상자 종류 선택
박스 요약 카드에서 쿠팡상자 / 3호상자 / 6호상자 중 하나를 고른다. 고른 종류는 카드 제목·박스 그림·③ 센터 분배 라벨에 반영되고, 출고일 탭 전환과 임시 저장/불러오기에도 함께 보관된다. 드롭다운 조작 시에는 드래그·담기 대화상자가 열리지 않게 예외 처리. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901j" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901k" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
@@ -264,6 +264,27 @@
|
||||
function prodKey(code) { return "p:" + code; }
|
||||
function mixKey(boxName, idx) { return "m:" + boxName + "#" + idx; }
|
||||
|
||||
// 자투리 혼합 박스에 쓸 상자 종류 — 요약 카드에서 고른다(표시용).
|
||||
var MIX_TYPES = ["쿠팡상자", "3호상자", "6호상자"];
|
||||
var mixType = {}; // mixKey -> 고른 상자 종류
|
||||
var mixOwner = {}; // mixKey -> 센터명(업로드로 만들어진 경우)
|
||||
|
||||
function mixTypeOf(key) { return mixType[key] || MIX_TYPES[0]; }
|
||||
|
||||
function mixLabel(key, idx) {
|
||||
return mixTypeOf(key) + " 혼합 #" + (idx + 1) +
|
||||
(mixOwner[key] ? " · " + mixOwner[key] : "");
|
||||
}
|
||||
|
||||
function mixTypeSelect(key) {
|
||||
var cur = mixTypeOf(key);
|
||||
return '<select class="erp-select cpg-box-type" data-type-for="' + esc(key) + '"' +
|
||||
' aria-label="상자 종류">' +
|
||||
MIX_TYPES.map(function (t) {
|
||||
return '<option value="' + esc(t) + '"' + (t === cur ? " selected" : "") + ">" + esc(t) + "</option>";
|
||||
}).join("") + "</select>";
|
||||
}
|
||||
|
||||
function centerName(cid) {
|
||||
var hit = centers.filter(function (c) { return String(c.id) === String(cid); })[0];
|
||||
return hit ? hit.name : cid;
|
||||
@@ -317,13 +338,15 @@
|
||||
units[prodKey(r.product_code)] = r.units_per_box;
|
||||
labels[prodKey(r.product_code)] = r.product_name;
|
||||
});
|
||||
mixOwner = {};
|
||||
calc.mixes.forEach(function (m) {
|
||||
m.boxes.forEach(function (b, i) {
|
||||
var k = mixKey(m.box_name, i);
|
||||
var n = 0;
|
||||
b.items.forEach(function (it) { n += it.quantity; });
|
||||
units[mixKey(m.box_name, i)] = n;
|
||||
labels[mixKey(m.box_name, i)] = m.box_name + " 혼합 #" + (i + 1);
|
||||
contents[mixKey(m.box_name, i)] = b.items.map(function (it) {
|
||||
units[k] = n;
|
||||
labels[k] = mixLabel(k, i);
|
||||
contents[k] = b.items.map(function (it) {
|
||||
return { product_code: it.product_code, product_name: it.product_name, quantity: it.quantity };
|
||||
});
|
||||
});
|
||||
@@ -412,7 +435,8 @@
|
||||
calc: { results: calc.results, mixes: calc.mixes },
|
||||
units: units, labels: labels, contents: contents,
|
||||
openCenters: openCenters.slice(), alloc: alloc, methods: methods,
|
||||
stamped: stamped, allStamped: allStamped
|
||||
stamped: stamped, allStamped: allStamped,
|
||||
mixType: mixType, mixOwner: mixOwner
|
||||
};
|
||||
}
|
||||
|
||||
@@ -428,6 +452,7 @@
|
||||
openCenters = st.openCenters.slice();
|
||||
alloc = st.alloc; methods = st.methods;
|
||||
stamped = st.stamped; allStamped = st.allStamped;
|
||||
mixType = st.mixType || {}; mixOwner = st.mixOwner || {};
|
||||
|
||||
tbody.innerHTML = "";
|
||||
(st.items || []).forEach(function (it) { addRow(it.product_code, it.quantity); });
|
||||
@@ -458,9 +483,9 @@
|
||||
items: [], calc: { results: [], mixes: [] },
|
||||
units: {}, labels: {}, contents: {},
|
||||
openCenters: [], alloc: {}, methods: {},
|
||||
stamped: {}, allStamped: false
|
||||
stamped: {}, allStamped: false,
|
||||
mixType: {}, mixOwner: {}
|
||||
};
|
||||
var mixOwner = {};
|
||||
|
||||
function put(cid, key, count) {
|
||||
if (count <= 0) return;
|
||||
@@ -498,7 +523,7 @@
|
||||
var idx = bucket.boxes.length;
|
||||
bucket.boxes.push(b);
|
||||
var k = mixKey(mx.box_name, idx);
|
||||
mixOwner[k] = g.center_name;
|
||||
st.mixOwner[k] = g.center_name;
|
||||
put(cid, k, 1);
|
||||
});
|
||||
});
|
||||
@@ -517,8 +542,9 @@
|
||||
b.items.forEach(function (it) { n += it.quantity; });
|
||||
var k = mixKey(m.box_name, i);
|
||||
st.units[k] = n;
|
||||
st.labels[k] = m.box_name + " 혼합 #" + (i + 1) +
|
||||
(mixOwner[k] ? " · " + mixOwner[k] : "");
|
||||
st.mixType[k] = MIX_TYPES[0];
|
||||
st.labels[k] = st.mixType[k] + " 혼합 #" + (i + 1) +
|
||||
(st.mixOwner[k] ? " · " + st.mixOwner[k] : "");
|
||||
st.contents[k] = b.items.map(function (it) {
|
||||
return { product_code: it.product_code, product_name: it.product_name, quantity: it.quantity };
|
||||
});
|
||||
@@ -691,7 +717,7 @@
|
||||
'<path class="cpg-box-flapL" d="M10 20 L2 8 H30 L36 20 Z" />' +
|
||||
'<path class="cpg-box-flapR" d="M62 20 L70 8 H42 L36 20 Z" />' +
|
||||
'<line class="cpg-box-seam" x1="36" y1="20" x2="36" y2="54" />' +
|
||||
'<text class="cpg-box-label" x="36" y="42" text-anchor="middle">쿠팡박스</text>' +
|
||||
'<text class="cpg-box-label" x="36" y="42" text-anchor="middle">' + esc(label || "") + "</text>" +
|
||||
"</svg>";
|
||||
}
|
||||
|
||||
@@ -792,10 +818,11 @@
|
||||
bh += '<div class="cpg-box-card' + (remain ? " is-draggable" : " is-done") + '"' +
|
||||
' data-key="' + esc(key) + '"' +
|
||||
' style="animation-delay:' + (n * 60) + 'ms">' +
|
||||
'<div class="cpg-box-art">' + boxSvg(b.fill_percent, m.box_name) + "</div>" +
|
||||
'<div class="cpg-box-art">' + boxSvg(b.fill_percent, mixTypeOf(key)) + "</div>" +
|
||||
'<div class="cpg-box-info">' +
|
||||
'<div class="cpg-box-title">' + esc(m.box_name) + " 혼합 #" + (i + 1) +
|
||||
'<div class="cpg-box-title">' + esc(mixTypeOf(key)) + " 혼합 #" + (i + 1) +
|
||||
(remain ? "" : stampHtml(key)) + "</div>" +
|
||||
'<div class="cpg-box-typerow">' + mixTypeSelect(key) + "</div>" +
|
||||
'<ul class="cpg-box-items">' + items + "</ul>" +
|
||||
'<div class="cpg-box-bar"><span style="width:' + b.fill_percent + '%"></span></div>' +
|
||||
'<div class="cpg-box-fillnum">' + b.fill_percent + "% 채움 · " + units[key] + "개</div>" +
|
||||
@@ -1001,6 +1028,7 @@
|
||||
|
||||
sumBody.addEventListener("mousedown", function (e) {
|
||||
if (e.button !== 0) return;
|
||||
if (e.target.closest(".cpg-box-type")) return; // 상자 종류 고르는 중
|
||||
var card = e.target.closest("[data-key]");
|
||||
if (!card || card.classList.contains("is-done")) return;
|
||||
drag = { key: card.getAttribute("data-key"), card: card, startX: e.clientX, startY: e.clientY, moved: false, ghost: null };
|
||||
@@ -1032,12 +1060,29 @@
|
||||
// 드래그하지 않고 클릭한 경우 — 첫 센터를 기본값으로 대화상자를 연다.
|
||||
// (드래그를 끝낸 카드에서 브라우저가 한 번 더 쏘는 click 은 1회만 무시)
|
||||
sumBody.addEventListener("click", function (e) {
|
||||
if (e.target.closest(".cpg-box-type")) return; // 상자 종류 고르는 중
|
||||
var card = e.target.closest("[data-key]");
|
||||
if (!card) return;
|
||||
if (card === suppressClickOn) { suppressClickOn = null; return; }
|
||||
openDialog(card.getAttribute("data-key"), openCenters[0]);
|
||||
});
|
||||
|
||||
// 상자 종류 변경 — 라벨(②③)만 바뀌고 배분 수량은 그대로 유지된다.
|
||||
sumBody.addEventListener("change", function (e) {
|
||||
var sel = e.target.closest(".cpg-box-type");
|
||||
if (!sel) return;
|
||||
var key = sel.getAttribute("data-type-for");
|
||||
mixType[key] = sel.value;
|
||||
calc.mixes.forEach(function (m) {
|
||||
m.boxes.forEach(function (b, i) {
|
||||
var k = mixKey(m.box_name, i);
|
||||
if (k === key) labels[k] = mixLabel(k, i);
|
||||
});
|
||||
});
|
||||
render();
|
||||
msg.textContent = "상자 종류를 " + sel.value + " 로 바꿨습니다.";
|
||||
});
|
||||
|
||||
if (distList) {
|
||||
distList.addEventListener("input", function (e) {
|
||||
if (!e.target.classList.contains("cpg-dist-qty")) return;
|
||||
@@ -1367,7 +1412,8 @@
|
||||
alloc: alloc,
|
||||
methods: methods,
|
||||
stamped: stamped,
|
||||
allStamped: allStamped
|
||||
allStamped: allStamped,
|
||||
mixType: mixType
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1391,6 +1437,17 @@
|
||||
methods = snap.methods && typeof snap.methods === "object" ? snap.methods : {};
|
||||
stamped = snap.stamped && typeof snap.stamped === "object" ? snap.stamped : {};
|
||||
allStamped = !!snap.allStamped;
|
||||
if (snap.mixType && typeof snap.mixType === "object") {
|
||||
Object.keys(snap.mixType).forEach(function (k) {
|
||||
if (k in units) mixType[k] = snap.mixType[k];
|
||||
});
|
||||
calc.mixes.forEach(function (m) {
|
||||
m.boxes.forEach(function (b, i) {
|
||||
var k = mixKey(m.box_name, i);
|
||||
labels[k] = mixLabel(k, i);
|
||||
});
|
||||
});
|
||||
}
|
||||
return skipped;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901j" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901k" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901j" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901k" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901j" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901k" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901j" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901k" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901j" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901k" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901j" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260901k" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
|
||||
@@ -1335,3 +1335,11 @@ body.cpg-dragging { cursor: grabbing; user-select: none; }
|
||||
}
|
||||
.cpg-po-dates > li > b { font-family: var(--font-geist-mono); }
|
||||
.cpg-po-dates > li > span { color: var(--color-midtone-gray); }
|
||||
|
||||
/* 자투리 혼합 박스 — 상자 종류 선택 */
|
||||
.cpg-box-typerow { margin: 2px 0 6px; }
|
||||
.cpg-box-type {
|
||||
width: auto; min-width: 108px;
|
||||
padding: 2px 8px; font-size: 12px; line-height: 1.6;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user