Files
dbx-main/app/static/malaysia_rack_view.js
T
king 0ce932f0e2 feat(malaysia): 창고 랙 전체 도면 A4 1장 'POG 출력' 추가
창고POG 출력 버튼 → Side A/B/Ground 전체 도면을 A4 가로 1장에
미리보기 후 인쇄. container-type:size + cqh/cqw 로 시트 높이에
맞춰 자동 축소. 기존 칸별 인쇄와 body 클래스(printing-cells/
printing-pog)로 분리해 인쇄 대상 충돌 방지.

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

294 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 (전체 도면 A4 가로 1장) 미리보기 ──
function openPOG() {
var board = document.getElementById("rv-board");
var pages = document.getElementById("pog-pages");
if (!board || !pages) return;
var sheet = document.createElement("div");
sheet.className = "pog-sheet";
// 머리말: 제목 + 창고/기준 재고조사
var head = document.createElement("div");
head.className = "pog-sheet-head";
var ttl = document.createElement("span");
ttl.className = "pog-t";
ttl.textContent = t("창고 POG", "Warehouse POG");
var meta = document.createElement("span");
meta.className = "pog-meta";
var wh = board.getAttribute("data-wh") || "";
var st = board.getAttribute("data-stocktake") || "";
meta.textContent = [wh, st].filter(Boolean).join(" | ");
head.appendChild(ttl);
head.appendChild(meta);
sheet.appendChild(head);
// 보드 통째로 복제 → 인터랙티브 요소 제거(체크박스/드래그)
var clone = board.cloneNode(true);
clone.removeAttribute("id");
clone.removeAttribute("style");
[].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.innerHTML = "";
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 = ""; }
});
});
})();