+
@@ -82,7 +55,7 @@
카드를 ③ 센터로 끌어다 놓거나, 카드를 클릭해 담습니다.
-
왼쪽에서 제품과 수량을 넣고 계산하세요.
+
① 에서 쿠팡 발주 파일을 업로드하면 상자가 계산됩니다.
@@ -137,6 +110,21 @@
+
+
+
+
+
상자 변경 필요
+
+ 아래 혼합 상자는 채움률이 60% 이하입니다. 더 작은 상자로 바꾸거나 내용물을 합치세요.
+
+
+
+
+
+
+
+
@@ -182,12 +170,8 @@
// ③ 선택한 센터에만 분배. 담은 수량만큼 ② 의 잔여가 즉시 줄어든다.
// 분배 내역은 화면 안에서만 유지된다(DB 저장 없음).
(function () {
- var tbody = document.getElementById("cpg-calc-rows");
- if (!tbody) return;
- var runBtn = document.getElementById("cpg-calc-run");
- var addBtn = document.getElementById("cpg-calc-add");
- var resetBtn = document.getElementById("cpg-calc-reset");
var msg = document.getElementById("cpg-calc-msg");
+ if (!msg) return;
var sumEmpty = document.getElementById("cpg-sum-empty");
var sumBody = document.getElementById("cpg-sum-body");
var sumKpis = document.getElementById("cpg-sum-kpis");
@@ -268,42 +252,22 @@
return hit ? hit.name : cid;
}
- // ── ① 입력 표 ─────────────────────────────────────
- function addRow(code, qty) {
- var tr = document.createElement("tr");
- tr.className = "cpg-calc-row";
- tr.innerHTML =
- '
" +
- '
' +
- '
';
- tbody.appendChild(tr);
- if (code) tr.querySelector(".cpg-calc-name").value = code;
- return tr;
- }
-
- function rowsArr() {
- return Array.prototype.slice.call(tbody.querySelectorAll(".cpg-calc-row"));
- }
-
- function collect() {
- var items = [];
- rowsArr().forEach(function (tr) {
- var code = tr.querySelector(".cpg-calc-name").value;
- if (!code) return;
- items.push({ product_code: code, quantity: parseInt(tr.querySelector(".cpg-calc-qty").value, 10) || 0 });
+ // ── 계산 ─────────────────────────────────────────
+ // 품목은 업로드(또는 임시 저장 불러오기)로만 들어온다. 직접 입력 표는 없앴다.
+ function currentItems() {
+ return calc.results.map(function (r) {
+ return { product_code: r.product_code, quantity: r.quantity };
});
- return items;
}
- function run(restore) {
- var items = collect();
- if (!items.length) { msg.textContent = "제품명을 하나 이상 선택하세요."; return; }
+ function run(restore, items) {
+ var list = items || currentItems();
+ if (!list.length) { msg.textContent = "먼저 쿠팡 발주 파일을 업로드하세요."; return; }
msg.textContent = "계산 중…";
- runBtn.disabled = true;
fetch("/cupang/api/box-calc", {
method: "POST",
headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ items: items })
+ body: JSON.stringify({ items: list })
})
.then(function (r) { if (!r.ok) throw new Error("http " + r.status); return r.json(); })
.then(function (data) {
@@ -329,21 +293,20 @@
});
});
});
- var had = Object.keys(alloc).some(function (k) { return alloc[k].length; });
alloc = {};
stamped = {};
allStamped = false; // 계산이 바뀌면 이전 배분은 근거가 사라지므로 비운다.
var skipped = restore ? applyRestore(restore) : 0;
render();
- if (restore) {
- msg.textContent = "불러오기 완료 (" + calc.results.length + "건)" +
- (skipped ? " — 담을 수 없는 " + skipped + "건은 제외" : "");
- } else {
- msg.textContent = "계산 완료 (" + calc.results.length + "건)" + (had ? " — 센터 분배 초기화됨" : "");
- }
+ if (activeDate) { saveActive(); }
+ renderTabs();
+ msg.textContent = restore
+ ? "불러오기 완료 (" + calc.results.length + "건)" +
+ (skipped ? " — 담을 수 없는 " + skipped + "건은 제외" : "")
+ : "계산 완료 (" + calc.results.length + "건)";
+ warnLowMixes();
})
- .catch(function () { msg.textContent = "계산 실패 — 새로고침 후 다시 시도하세요."; })
- .then(function () { runBtn.disabled = false; });
+ .catch(function () { msg.textContent = "계산 실패 — 새로고침 후 다시 시도하세요."; });
}
// ── 쿠팡 발주 엑셀 업로드 ─────────────────────────
@@ -371,8 +334,8 @@
var sets = {}; // "YYYY-MM-DD" -> 상태 스냅샷
var dateOrder = []; // 탭 순서
var activeDate = ""; // "" = 업로드 전(수동 입력 모드)
- var tabsBar = document.getElementById("cpg-date-tabs");
- var tabsList = document.getElementById("cpg-date-tabs-list");
+ var tabsList = document.getElementById("cpg-date-list");
+ var tabsEmpty = document.getElementById("cpg-date-empty");
function centersOf(allocObj) {
return Object.keys(allocObj || {}).filter(function (cid) {
@@ -388,18 +351,29 @@
return n;
}
+ var DOW = ["일", "월", "화", "수", "목", "금", "토"];
+
+ function dateLabel(d) {
+ var p = (d || "").split("-");
+ if (p.length !== 3) return d || "날짜 미확인";
+ var dt = new Date(+p[0], +p[1] - 1, +p[2]);
+ return d + "(" + DOW[dt.getDay()] + ")";
+ }
+
function renderTabs() {
- if (!tabsBar) return;
- if (dateOrder.length < 1) { tabsBar.hidden = true; tabsList.innerHTML = ""; return; }
- tabsBar.hidden = false;
+ if (!tabsList) return;
+ if (tabsEmpty) tabsEmpty.hidden = dateOrder.length > 0;
+ if (dateOrder.length < 1) { tabsList.innerHTML = ""; return; }
tabsList.innerHTML = dateOrder.map(function (d) {
var st = sets[d] || {};
var cn = centersOf(st.alloc).length;
var bx = boxesOf(st.alloc);
+ var qty = 0;
+ (st.items || []).forEach(function (it) { qty += it.quantity; });
return '";
}).join("");
}
@@ -408,7 +382,7 @@
function saveActive() {
if (!activeDate) return;
sets[activeDate] = {
- items: collect(),
+ items: currentItems(),
calc: { results: calc.results, mixes: calc.mixes },
units: units, labels: labels, contents: contents,
openCenters: openCenters.slice(), alloc: alloc, methods: methods,
@@ -431,12 +405,9 @@
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); });
- if (!(st.items || []).length) addRow();
-
render();
renderTabs();
+ warnLowMixes();
}
if (tabsList) {
@@ -838,6 +809,65 @@
return '배분 완료';
}
+ // 자투리 혼합 상자가 이 비율 이하로 차면 더 작은 상자로 바꾸는 편이 낫다.
+ var LOW_FILL = 60;
+
+ function fillPercentOf(key) { return Math.round(mixFill(key) * 1000) / 10; }
+ function isLowMix(key) { return fillPercentOf(key) <= LOW_FILL; }
+
+ // 배분 완료 대신 찍는 경고 도장(오렌지).
+ function stampLowHtml(key) {
+ var fresh = !stamped[key];
+ stamped[key] = true;
+ return '상자 변경 필요';
+ }
+
+ // ── 채움률이 낮은 상자 안내 ───────────────────────
+ var lowDlg = document.getElementById("cpg-low-dlg");
+ var lowList = document.getElementById("cpg-low-list");
+ var lowSeen = ""; // 같은 상태로 팝업을 반복해서 띄우지 않도록
+
+ function lowMixes() {
+ var out = [];
+ calc.mixes.forEach(function (m) {
+ m.boxes.forEach(function (b, i) {
+ var key = mixKeyFor(b);
+ if (!isLowMix(key)) return;
+ out.push({
+ key: key,
+ name: mixTypeOf(key) + " 혼합 #" + (i + 1) + (mixOwner[key] ? " · " + mixOwner[key] : ""),
+ fill: fillPercentOf(key),
+ units: units[key] || 0
+ });
+ });
+ });
+ return out;
+ }
+
+ function warnLowMixes() {
+ if (!lowDlg || !lowList) return;
+ var rows = lowMixes();
+ var sign = rows.map(function (r) { return r.key + ":" + r.fill; }).join("|");
+ if (!rows.length) { lowSeen = ""; return; }
+ if (sign === lowSeen) return; // 이미 안내한 그대로면 다시 띄우지 않는다
+ lowSeen = sign;
+ lowList.innerHTML = rows.map(function (r) {
+ return "