From 5542748539c471bfc861133a95a72c350dfd0b5e Mon Sep 17 00:00:00 2001 From: king Date: Tue, 1 Sep 2026 13:56:10 +0900 Subject: [PATCH] =?UTF-8?q?feat(cupang):=20=EC=9E=90=ED=88=AC=EB=A6=AC=20?= =?UTF-8?q?=ED=98=BC=ED=95=A9=20=EB=B0=95=EC=8A=A4=20=EC=83=81=EC=9E=90=20?= =?UTF-8?q?=EC=A2=85=EB=A5=98=20=EC=84=A0=ED=83=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 박스 요약 카드에서 쿠팡상자 / 3호상자 / 6호상자 중 하나를 고른다. 고른 종류는 카드 제목·박스 그림·③ 센터 분배 라벨에 반영되고, 출고일 탭 전환과 임시 저장/불러오기에도 함께 보관된다. 드롭다운 조작 시에는 드래그·담기 대화상자가 열리지 않게 예외 처리. Co-Authored-By: Claude Opus 5 --- .../cupang/templates/cupang/box_calc.html | 85 ++++++++++++++++--- .../cupang/templates/cupang/box_rules.html | 2 +- .../cupang/templates/cupang/centers.html | 2 +- .../cupang/templates/cupang/detail.html | 2 +- app/modules/cupang/templates/cupang/form.html | 2 +- .../cupang/templates/cupang/index.html | 2 +- .../cupang/templates/cupang/products.html | 2 +- app/static/cupang.css | 8 ++ 8 files changed, 85 insertions(+), 20 deletions(-) diff --git a/app/modules/cupang/templates/cupang/box_calc.html b/app/modules/cupang/templates/cupang/box_calc.html index e8eb600..c577fee 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 %}
@@ -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 '"; + } + 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 @@ '' + '' + '' + - '쿠팡박스' + + '' + esc(label || "") + "" + ""; } @@ -792,10 +818,11 @@ bh += '
' + - '
' + boxSvg(b.fill_percent, m.box_name) + "
" + + '
' + boxSvg(b.fill_percent, mixTypeOf(key)) + "
" + '
' + - '
' + esc(m.box_name) + " 혼합 #" + (i + 1) + + '
' + esc(mixTypeOf(key)) + " 혼합 #" + (i + 1) + (remain ? "" : stampHtml(key)) + "
" + + '
' + mixTypeSelect(key) + "
" + '
    ' + items + "
" + '
' + '
' + b.fill_percent + "% 채움 · " + units[key] + "개
" + @@ -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; } diff --git a/app/modules/cupang/templates/cupang/box_rules.html b/app/modules/cupang/templates/cupang/box_rules.html index 7703d05..43ce413 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 aaf034b..8bdd4dc 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 7be3f8d..37b0d56 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 675582d..227dc06 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 cdf93f4..31c5c39 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 %}
diff --git a/app/modules/cupang/templates/cupang/products.html b/app/modules/cupang/templates/cupang/products.html index 9a917f2..8607ded 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 c155254..5b42ca0 100644 --- a/app/static/cupang.css +++ b/app/static/cupang.css @@ -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; +}