diff --git a/app/Views/bag/order_phone_manage.php b/app/Views/bag/order_phone_manage.php
index dc231dc..e57685c 100644
--- a/app/Views/bag/order_phone_manage.php
+++ b/app/Views/bag/order_phone_manage.php
@@ -121,26 +121,25 @@
@@ -464,9 +493,6 @@
// 현재 이 주문을 보고 있으면 상세·포장명세 즉시 갱신
if (String(selectedId) === String(data.so_idx)) {
renderPacking(order);
- // 새로 스캔한 봉투가 항상 맨 위(최신)에 보이도록 스크롤을 맨 위로 이동
- const packingScrollEl = document.getElementById('packing-scroll');
- if (packingScrollEl) packingScrollEl.scrollTop = 0;
const cell = detailBody.querySelector('.item-packed-cell[data-bag-code="' + (window.CSS && CSS.escape ? CSS.escape(String(data.bag_code)) : String(data.bag_code)) + '"]');
if (cell) {
cell.textContent = nf(Number(data.packed_qty || 0));
@@ -580,32 +606,93 @@
applySplit(currentPct());
}
- // ── 포장 명세 탭: 주문 품목을 박스/팩/낱장 단위 행으로 펼침 ──────────
- const packingBody = document.getElementById('packing-body');
+ // ── 스캔 현황(최근 2건 + 앞으로 찍을 목록) + 포장 명세 팝업 ──────────
const packingTotal = document.getElementById('packing-total');
+ const recentScansEl = document.getElementById('recent-scans');
+ const remainingListEl = document.getElementById('remaining-list');
+ const btnPackingModal = document.getElementById('btn-packing-modal');
+ const packingModalOverlay = document.getElementById('packing-modal-overlay');
+ const packingModalBody = document.getElementById('packing-modal-body');
+ const packingModalTitle = document.getElementById('packing-modal-title');
+ const packingModalCount = document.getElementById('packing-modal-count');
+ const packingModalTotal = document.getElementById('packing-modal-total');
+ let packingModalOrderId = null;
function packEsc(s) { return String(s == null ? '' : s).replace(/[&<>]/g, (c) => ({ '&': '&', '<': '<', '>': '>' }[c])); }
- // 포장 명세 = 실제 스캔(=판매/포장)된 봉투코드 내역. 최근 스캔이 위로 오도록 역순 표시.
- function renderPacking(order) {
- if (!packingBody) return;
+
+ // 스캔 코드 전체 → 표 행(HTML) + 건수/합계 (팝업용). 최근이 위로.
+ function scanFullRows(order) {
const scans = Array.isArray(order.scans) ? order.scans.slice().reverse() : [];
- let no = 0;
- let totalQty = 0;
+ let no = 0, total = 0;
const rows = scans.map((s) => {
no++;
- const code = s.bag_code || '';
- const name = s.bag_name || '';
- const qty = parseInt(s.qty || 0, 10) || 0;
- totalQty += qty;
+ const code = s.bag_code || '', name = s.bag_name || '', qty = parseInt(s.qty || 0, 10) || 0;
+ total += qty;
return '
| ' + no + ' | '
+ '' + packEsc(code) + ' ' + packEsc(name) + ' | '
+ '' + packEsc(s.code || '') + ' | '
+ '' + nf(qty) + ' | '
+ '' + packEsc(s.unit || '') + ' |
';
});
- packingBody.innerHTML = rows.length ? rows.join('')
- : '
| 스캔된 포장 내역이 없습니다. |
';
- if (packingTotal) packingTotal.textContent = nf(totalQty);
+ return { html: rows.join(''), count: no, total: total };
}
+ function fillPackingModal(order) {
+ const full = scanFullRows(order);
+ if (packingModalBody) packingModalBody.innerHTML = full.count ? full.html
+ : '
| 스캔된 포장 내역이 없습니다. |
';
+ if (packingModalCount) packingModalCount.textContent = nf(full.count);
+ if (packingModalTotal) packingModalTotal.textContent = nf(full.total);
+ }
+ // 앞으로 찍을 목록 = 품목별 남은(접수량 − 포장량)
+ function renderRemaining(order) {
+ if (!remainingListEl) return;
+ const items = Array.isArray(order.items) ? order.items : [];
+ const rows = items.map((it) => {
+ const qty = parseInt(it.soi_qty || 0, 10) || 0;
+ const packed = parseInt(it.soi_packed_qty || 0, 10) || 0;
+ if (qty <= 0) return '';
+ const remain = Math.max(0, qty - packed);
+ const name = (String(it.soi_bag_code || '') + ' ' + String(it.soi_bag_name || '')).trim();
+ if (remain <= 0) {
+ return '
' + packEsc(name) + '✓ 완료
';
+ }
+ return '
' + packEsc(name) + '' + nf(remain) + ' 매
';
+ }).filter(Boolean);
+ remainingListEl.innerHTML = rows.length ? rows.join('') : '
남은 수량이 없습니다.';
+ }
+ // 스캔 현황 갱신(최근 2건 + 남은 목록 + 포장량 + 열린 팝업)
+ function renderPacking(order) {
+ const scans = Array.isArray(order.scans) ? order.scans : [];
+ const full = scanFullRows(order);
+ if (packingTotal) packingTotal.textContent = nf(full.total);
+ if (btnPackingModal) btnPackingModal.disabled = false;
+ if (recentScansEl) {
+ const last = scans.slice(-2).reverse();
+ recentScansEl.innerHTML = last.length
+ ? last.map((s, i) => '
'
+ + '' + (i === 0 ? '▶ ' : '') + packEsc(s.code || '') + ''
+ + '' + packEsc(s.bag_name || '') + ' · ' + nf(parseInt(s.qty || 0, 10) || 0) + '
').join('')
+ : '
스캔 내역이 없습니다.';
+ }
+ renderRemaining(order);
+ if (packingModalOverlay && !packingModalOverlay.classList.contains('hidden') && String(packingModalOrderId) === String(order.so_idx)) {
+ fillPackingModal(order);
+ }
+ }
+ // 포장 명세 팝업 열기/닫기
+ function openPackingModal() {
+ const order = orderMap.get(String(selectedId));
+ if (!order) return;
+ packingModalOrderId = selectedId;
+ if (packingModalTitle) packingModalTitle.textContent = '접수 #' + displayNo(order) + ' · ' + (order.so_ds_name || '');
+ fillPackingModal(order);
+ packingModalOverlay.classList.remove('hidden');
+ packingModalOverlay.classList.add('flex');
+ }
+ function closePackingModal() { packingModalOverlay.classList.add('hidden'); packingModalOverlay.classList.remove('flex'); }
+ if (btnPackingModal) btnPackingModal.addEventListener('click', openPackingModal);
+ document.getElementById('packing-modal-close')?.addEventListener('click', closePackingModal);
+ document.getElementById('packing-modal-close2')?.addEventListener('click', closePackingModal);
+ if (packingModalOverlay) packingModalOverlay.addEventListener('click', (e) => { if (e.target === packingModalOverlay) closePackingModal(); });
// ── 접수 리스트 렌더 + 헤더 클릭 정렬 ──────────────────────────
let sortKey = 'so_idx', sortDir = 'desc', selectedId = null;
@@ -668,6 +755,7 @@
listBody.querySelectorAll('.order-list-row').forEach((row) => row.classList.remove('bg-blue-100'));
tr.classList.add('bg-blue-100');
renderDetail(selectedId);
+ openPackingModal(); // 행 클릭 시 해당 주문 봉투코드 팝업 즉시 표시
});
detailBody?.addEventListener('input', (e) => {