Files
dbx-main/app/static/malaysia_rack_view.js
T
king fb114a222c feat(malaysia): 창고 POG를 면별 A4 1장(Side A/B/Ground)으로 분리
전체 1장 → 면마다 한 페이지로 출력. 행을 flex 균등 분배해
칸·글자 크게(cqh 2배) 한눈에 보이게. 시트별 page-break 적용.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 00:16:15 +09:00

297 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 말레이시아 창고 랙 보기 — 드래그 재배치 + 저장 + 인쇄 미리보기.
*
* - 각 .rv-cell 의 data-entries(JSON)가 진실원천. 드래그 드롭으로 두 칸의
* 구성(entries)을 맞교환(빈 칸이면 이동). 칸 번호(cell)는 그대로.
* - 저장: 모든 칸의 현재 구성을 rk_cell/rk_sku/rk_upb/rk_box 병렬배열 hidden
* 으로 만들어 POST /malaysia/rack/save (서버 reposition_rack).
* - 인쇄: 체크한 칸을 칸마다 A4 가로 1장으로 미리보기 후 window.print().
* - 언어: localStorage('mys_lang'). 셀/미리보기 텍스트를 현재 언어로 렌더.
*/
(function () {
"use strict";
function lang() { return localStorage.getItem("mys_lang") === "en" ? "en" : "ko"; }
function t(ko, en) { return lang() === "en" ? en : ko; }
function fmt(n) { return Number(n || 0).toLocaleString(); }
function nameOf(e) { return (lang() === "en" ? e.en : e.ko) || e.sku; }
// 상품명 끝의 용량 토큰(숫자 포함, 예 750ml)을 분리 → {main, size}
function splitName(full) {
var m = String(full || "").match(/^(.*\S)\s+(\S*\d\S*)$/);
if (m) return { main: m[1], size: m[2] };
return { main: full || "", size: "" };
}
function getEntries(cell) {
try { return JSON.parse(cell.getAttribute("data-entries") || "[]"); }
catch (_) { return []; }
}
function setEntries(cell, arr) {
cell.setAttribute("data-entries", JSON.stringify(arr));
}
function renderCell(cell) {
var items = cell.querySelector(".rv-items");
if (!items) return;
var arr = getEntries(cell);
items.innerHTML = "";
if (!arr.length) {
cell.classList.add("is-empty");
var s = document.createElement("span");
s.className = "rv-empty-note";
s.textContent = "—";
items.appendChild(s);
return;
}
cell.classList.remove("is-empty");
arr.forEach(function (e) {
var total = (Number(e.upb) || 0) * (Number(e.box) || 0);
var wrap = document.createElement("div");
wrap.className = "rv-item";
var nm = document.createElement("span");
nm.className = "rv-name";
nm.textContent = nameOf(e);
var calc = document.createElement("span");
calc.className = "rv-calc";
calc.innerHTML = fmt(e.upb) + t("개", "ea") + " × " + fmt(e.box) + t("박스", "box")
+ " = <span class=\"rv-qty\">" + fmt(total) + "</span>";
wrap.appendChild(nm);
wrap.appendChild(document.createElement("br"));
wrap.appendChild(calc);
items.appendChild(wrap);
});
}
function renderAll() {
document.querySelectorAll(".rv-cell").forEach(renderCell);
}
// ── 드래그 재배치 ──
var dragSrc = null;
var dirty = false;
function markDirty() {
dirty = true;
var b = document.getElementById("rv-save-btn");
if (b) b.classList.add("is-dirty");
}
function cleanup() {
if (dragSrc) dragSrc.classList.remove("dragging");
dragSrc = null;
document.querySelectorAll(".rv-cell.is-dragover")
.forEach(function (c) { c.classList.remove("is-dragover"); });
}
function onDragStart(e) {
var cell = e.target.closest(".rv-cell");
if (!cell) return;
dragSrc = cell;
cell.classList.add("dragging");
e.dataTransfer.effectAllowed = "move";
try { e.dataTransfer.setData("text/plain", cell.getAttribute("data-cell")); } catch (_) {}
}
function onDragOver(e) {
if (!dragSrc) return;
e.preventDefault();
e.dataTransfer.dropEffect = "move";
var cell = e.target.closest(".rv-cell");
if (cell && cell !== dragSrc) cell.classList.add("is-dragover");
}
function onDragLeave(e) {
var cell = e.target.closest(".rv-cell");
if (cell) cell.classList.remove("is-dragover");
}
function onDrop(e) {
e.preventDefault();
var dst = e.target.closest(".rv-cell");
if (!dst || !dragSrc || dst === dragSrc) { cleanup(); return; }
var a = getEntries(dragSrc);
var b = getEntries(dst);
setEntries(dragSrc, b);
setEntries(dst, a);
renderCell(dragSrc);
renderCell(dst);
markDirty();
cleanup();
}
// ── 인쇄 선택(체크박스) → 셀 강조 ──
function onChange(e) {
var cb = e.target;
if (!cb.classList || !cb.classList.contains("rv-pick")) return;
var cell = cb.closest(".rv-cell");
if (cell) cell.classList.toggle("is-selected", cb.checked);
}
// ── 저장 ──
function addHidden(parent, name, val) {
var i = document.createElement("input");
i.type = "hidden";
i.name = name;
i.value = val;
parent.appendChild(i);
}
function save() {
var fields = document.getElementById("rv-save-fields");
var form = document.getElementById("rv-save-form");
if (!fields || !form) return;
fields.innerHTML = "";
document.querySelectorAll(".rv-cell").forEach(function (cell) {
var cc = cell.getAttribute("data-cell");
getEntries(cell).forEach(function (e) {
addHidden(fields, "rk_cell", cc);
addHidden(fields, "rk_sku", e.sku);
addHidden(fields, "rk_upb", e.upb);
addHidden(fields, "rk_box", e.box);
});
});
dirty = false;
var b = document.getElementById("rv-save-btn");
if (b) b.classList.remove("is-dirty");
alert(t("저장되었습니다.", "Saved."));
form.submit();
}
// ── 인쇄 미리보기 ──
function openPreview() {
var picked = [].slice.call(document.querySelectorAll(".rv-cell")).filter(function (c) {
var cb = c.querySelector(".rv-pick");
return cb && cb.checked;
});
if (!picked.length) {
alert(t("인쇄할 칸을 먼저 선택하세요.", "Select cells to print first."));
return;
}
// 빈 칸은 인쇄 대상에서 제외. 내용 있는 칸만 인쇄.
var withItems = picked.filter(function (c) { return getEntries(c).length > 0; });
if (!withItems.length) {
alert(t("인쇄할 문서가 없습니다. (선택한 칸이 모두 비어 있습니다)",
"Nothing to print. (All selected cells are empty.)"));
return;
}
var pages = document.getElementById("rvp-pages");
pages.innerHTML = "";
withItems.forEach(function (cell) {
var arr = getEntries(cell);
var sheet = document.createElement("div");
sheet.className = "rvp-sheet";
// 인쇄 시 칸에 담긴 상품 수에 따라 글자 크기 단계 지정(한 장에 다 들어가게)
if (arr.length >= 3) sheet.classList.add("rvp-many");
else if (arr.length === 2) sheet.classList.add("rvp-multi");
var list = document.createElement("div");
list.className = "rvp-list";
{
arr.forEach(function (e) {
var total = (Number(e.upb) || 0) * (Number(e.box) || 0);
var nm = splitName(nameOf(e));
var line = document.createElement("div");
line.className = "rvp-line";
var main = document.createElement("span");
main.className = "rvp-name";
main.textContent = nm.main;
line.appendChild(main);
if (nm.size) {
var size = document.createElement("span");
size.className = "rvp-size";
size.textContent = nm.size;
line.appendChild(size);
}
var calc = document.createElement("span");
calc.className = "rvp-calc";
calc.innerHTML = fmt(e.upb) + t("개", "ea") + " &times; "
+ "<b class=\"rvp-box\">" + fmt(e.box) + t("박스", "box") + "</b>"
+ " = " + fmt(total);
line.appendChild(calc);
list.appendChild(line);
});
}
sheet.appendChild(list);
pages.appendChild(sheet);
});
document.body.classList.add("printing-cells");
document.getElementById("rvp-overlay").classList.add("is-open");
}
function closePreview() {
document.getElementById("rvp-overlay").classList.remove("is-open");
document.body.classList.remove("printing-cells");
}
// ── 창고 POG: 면(Side A/B/Ground)마다 A4 가로 1장 미리보기 ──
function openPOG() {
var board = document.getElementById("rv-board");
var pages = document.getElementById("pog-pages");
if (!board || !pages) return;
var wh = board.getAttribute("data-wh") || "";
var st = board.getAttribute("data-stocktake") || "";
var metaTxt = [wh, st].filter(Boolean).join(" | ");
pages.innerHTML = "";
[].slice.call(board.querySelectorAll(".rv-side")).forEach(function (side) {
var sheet = document.createElement("div");
sheet.className = "pog-sheet";
// 머리말: 면 이름(Side A/B/Ground) + 창고/기준 재고조사
var head = document.createElement("div");
head.className = "pog-sheet-head";
var ttl = document.createElement("span");
ttl.className = "pog-t";
var h3 = side.querySelector("h3");
ttl.textContent = h3 ? h3.textContent.trim() : t("창고 POG", "Warehouse POG");
var meta = document.createElement("span");
meta.className = "pog-meta";
meta.textContent = metaTxt;
head.appendChild(ttl);
head.appendChild(meta);
sheet.appendChild(head);
// 면 복제 → 인터랙티브 요소 제거(체크박스/드래그)
var clone = side.cloneNode(true);
[].slice.call(clone.querySelectorAll(".rv-cell")).forEach(function (c) {
c.removeAttribute("draggable");
c.classList.remove("is-selected", "is-dragover", "dragging");
});
[].slice.call(clone.querySelectorAll(".rv-pick")).forEach(function (cb) {
cb.parentNode && cb.parentNode.removeChild(cb);
});
sheet.appendChild(clone);
pages.appendChild(sheet);
});
document.body.classList.add("printing-pog");
document.getElementById("pog-overlay").classList.add("is-open");
}
function closePOG() {
document.getElementById("pog-overlay").classList.remove("is-open");
document.body.classList.remove("printing-pog");
}
document.addEventListener("DOMContentLoaded", function () {
renderAll();
var board = document.getElementById("rv-board");
if (board) {
board.addEventListener("dragstart", onDragStart);
board.addEventListener("dragover", onDragOver);
board.addEventListener("dragleave", onDragLeave);
board.addEventListener("drop", onDrop);
board.addEventListener("dragend", cleanup);
board.addEventListener("change", onChange);
}
var sb = document.getElementById("rv-save-btn");
if (sb) sb.addEventListener("click", save);
var pb = document.getElementById("rv-print-btn");
if (pb) pb.addEventListener("click", openPreview);
var pc = document.getElementById("rvp-close");
if (pc) pc.addEventListener("click", closePreview);
var pp = document.getElementById("rvp-print");
if (pp) pp.addEventListener("click", function () { window.print(); });
var gb = document.getElementById("rv-pog-btn");
if (gb) gb.addEventListener("click", openPOG);
var gc = document.getElementById("pog-close");
if (gc) gc.addEventListener("click", closePOG);
var gp = document.getElementById("pog-print");
if (gp) gp.addEventListener("click", function () { window.print(); });
var lt = document.getElementById("mys-lang-toggle");
if (lt) lt.addEventListener("click", function () { setTimeout(renderAll, 0); });
window.addEventListener("beforeunload", function (ev) {
if (dirty) { ev.preventDefault(); ev.returnValue = ""; }
});
});
})();