/* 쿠팡 밀크런 — 출고 폼 동적 라인 / 상품검색 / 박스계산. 서버가 저장 시 박스 수를 재계산하므로(store.compute_boxes) 여기 계산은 미리보기용. */ (function () { "use strict"; var CFG = window.CPG || {}; var body = document.getElementById("cpg-lines-body"); var form = document.getElementById("cpg-form"); if (!body || !form) return; // 입수량 규칙: product_code -> units_per_box var ruleMap = {}; try { var rules = JSON.parse(document.getElementById("cpg-box-rules").textContent || "[]"); rules.forEach(function (r) { if (r.active !== false) ruleMap[r.product_code] = r.units_per_box; }); } catch (e) {} var initLines = []; try { initLines = JSON.parse(document.getElementById("cpg-init-lines").textContent || "[]"); } catch (e) {} function ceilDiv(a, b) { return Math.ceil(a / b); } function recalc(row) { var qty = parseInt(row.querySelector(".cpg-qty").value, 10) || 0; var upbInput = row.querySelector(".cpg-upb"); var upb = parseInt(upbInput.value, 10); var cell = row.querySelector(".cpg-line-calc"); if (!upb || upb <= 0) { cell.textContent = "미설정"; cell.classList.add("cpg-warn"); return; } cell.classList.remove("cpg-warn"); if (qty <= 0) { cell.textContent = "—"; return; } var boxes = ceilDiv(qty, upb); var rem = qty % upb; cell.textContent = boxes + "박스" + (rem ? " +" + rem : " (딱맞음)"); } function makeRow(data) { data = data || {}; var tr = document.createElement("tr"); tr.innerHTML = '' + '' + '' + '' + '' + '' + '' + ''; tr.querySelector(".cpg-code").value = data.product_code || ""; tr.querySelector(".cpg-name").value = data.product_name_snapshot || data.product_name || ""; tr.querySelector(".cpg-qty").value = data.quantity || ""; var upb = data.units_per_box; if (upb == null && data.product_code && ruleMap[data.product_code] != null) upb = ruleMap[data.product_code]; tr.querySelector(".cpg-upb").value = (upb != null ? upb : ""); tr.querySelector(".cpg-line-del").addEventListener("click", function () { tr.remove(); renumber(); }); tr.querySelector(".cpg-qty").addEventListener("input", function () { recalc(tr); }); tr.querySelector(".cpg-upb").addEventListener("input", function () { recalc(tr); }); var codeInput = tr.querySelector(".cpg-code"); codeInput.addEventListener("input", function () { // 입수량 규칙 자동 채움 (수동값 없을 때) var code = codeInput.value.trim(); if (code && ruleMap[code] != null && !tr.querySelector(".cpg-upb").value) { tr.querySelector(".cpg-upb").value = ruleMap[code]; } if (CFG.searchEnabled) scheduleSearch(codeInput, tr); }); codeInput.addEventListener("blur", function () { setTimeout(hidePop, 150); }); body.appendChild(tr); recalc(tr); renumber(); return tr; } function renumber() { Array.prototype.forEach.call(body.querySelectorAll("tr"), function (tr, i) { tr.querySelector(".cpg-line-no").textContent = i + 1; }); } // ── 상품 검색 드롭다운 ── var pop = document.getElementById("cpg-search-pop"); var searchTimer = null; function hidePop() { if (pop) { pop.hidden = true; pop.innerHTML = ""; } } function scheduleSearch(input, tr) { if (!pop) return; clearTimeout(searchTimer); var q = input.value.trim(); if (q.length < 1) { hidePop(); return; } searchTimer = setTimeout(function () { doSearch(q, input, tr); }, 220); } function doSearch(q, input, tr) { fetch(CFG.searchUrl + "?q=" + encodeURIComponent(q)) .then(function (r) { return r.json(); }) .then(function (data) { var results = (data && data.results) || []; if (!results.length) { hidePop(); return; } pop.innerHTML = ""; results.forEach(function (item) { var div = document.createElement("div"); div.className = "cpg-search-item"; div.innerHTML = ''; div.querySelector(".cpg-sc").textContent = item.code; div.querySelector(".cpg-sn").textContent = item.name + (item.type ? " · " + item.type : ""); div.addEventListener("mousedown", function (e) { e.preventDefault(); input.value = item.code; tr.querySelector(".cpg-name").value = item.name || ""; var upb = item.units_per_box != null ? item.units_per_box : (ruleMap[item.code] != null ? ruleMap[item.code] : ""); if (upb !== "" && !tr.querySelector(".cpg-upb").value) tr.querySelector(".cpg-upb").value = upb; recalc(tr); hidePop(); }); pop.appendChild(div); }); var rect = input.getBoundingClientRect(); pop.style.left = (window.scrollX + rect.left) + "px"; pop.style.top = (window.scrollY + rect.bottom + 2) + "px"; pop.hidden = false; }) .catch(hidePop); } // ── 초기 라인 ── if (initLines.length) { initLines.forEach(makeRow); } else { makeRow(); } document.getElementById("cpg-add-line").addEventListener("click", function () { makeRow(); }); // ── 제출: 라인 직렬화 ── form.addEventListener("submit", function (e) { var lines = []; Array.prototype.forEach.call(body.querySelectorAll("tr"), function (tr) { var code = tr.querySelector(".cpg-code").value.trim(); var qty = parseInt(tr.querySelector(".cpg-qty").value, 10) || 0; if (!code || qty <= 0) return; // 빈 라인 스킵 (서버도 스킵) var upb = parseInt(tr.querySelector(".cpg-upb").value, 10); lines.push({ product_code: code, product_name_snapshot: tr.querySelector(".cpg-name").value.trim(), quantity: qty, units_per_box: (upb > 0 ? upb : null), memo: tr.querySelector(".cpg-memo").value.trim() }); }); if (!lines.length) { e.preventDefault(); alert("품목 라인을 최소 1개 입력하세요 (제품코드 + 수량)."); return; } document.getElementById("cpg-lines-json").value = JSON.stringify(lines); }); // ── 입고센터 select → 스냅샷 hidden 동기화 ── var centerSel = document.getElementById("cpg-center-select"); var centerName = document.getElementById("cpg-center-name"); if (centerSel && centerName) { function syncCenter() { var opt = centerSel.options[centerSel.selectedIndex]; if (opt && opt.value) centerName.value = opt.getAttribute("data-name") || opt.text; } centerSel.addEventListener("change", syncCenter); // 신규일 때만 자동 동기화 (수정 시 기존 스냅샷 보존) if (!centerName.value) syncCenter(); } })();