169 lines
8.1 KiB
PHP
169 lines
8.1 KiB
PHP
|
|
<?php
|
||
|
|
/**
|
||
|
|
* [개발용 임시] 판매관리·전화접수 화면 하단에 끼우는 "전체 처리 흐름" 표.
|
||
|
|
*
|
||
|
|
* - 통합 데이터 출처:
|
||
|
|
* · shop_order + shop_order_item (단계: order) — 전화/일반 주문 접수
|
||
|
|
* · bag_sale_scan_code state=sold (단계: sale) — 지정판매소 판매 처리
|
||
|
|
* · bag_sale_scan_code state=in_stock (단계: returned) — 반품으로 재고 복귀
|
||
|
|
* - 호출 API: GET /bag/sale/dev-all-sales-history
|
||
|
|
* - 운영 배포 시 본 파일과 라우트/컨트롤러(`Bag::devAllSalesHistory`) 함께 제거.
|
||
|
|
*
|
||
|
|
* 다수 페이지에 동일하게 include 되므로 ID 충돌 회피를 위해 dev-all-sales- 접두어 사용.
|
||
|
|
*/
|
||
|
|
?>
|
||
|
|
<section id="dev-all-sales-panel" class="border border-amber-400 bg-amber-50/50 p-3 mt-3 rounded-sm">
|
||
|
|
<div class="flex flex-wrap items-center justify-between gap-2 mb-2">
|
||
|
|
<p class="text-xs text-amber-900 leading-relaxed m-0">
|
||
|
|
<strong class="text-amber-950">[개발용 임시 — 전체 처리 흐름]</strong>
|
||
|
|
현재 지자체의 <strong>주문 접수(order) · 판매 처리(sale) · 반품 복귀(returned)</strong>를 모두 모아
|
||
|
|
<strong>일시 역순 최근 500건</strong>으로 표시합니다. (일시는 DB UTC 기준을 <strong>한국 표준시(KST)</strong>로 변환)
|
||
|
|
운영 배포 시 이 블록을 제거해 주세요.
|
||
|
|
</p>
|
||
|
|
<div class="flex items-center gap-2">
|
||
|
|
<span id="dev-all-sales-count" class="text-[13px] text-amber-900"></span>
|
||
|
|
<button type="button" id="dev-all-sales-refresh" class="text-[13px] border border-amber-500 text-amber-800 bg-white rounded px-2 py-0.5 hover:bg-amber-100">새로고침</button>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
<div class="max-h-80 overflow-auto border border-amber-300 bg-white">
|
||
|
|
<table class="w-full data-table text-xs">
|
||
|
|
<thead class="sticky top-0 bg-amber-100 z-10">
|
||
|
|
<tr>
|
||
|
|
<th class="w-40">일시</th>
|
||
|
|
<th class="w-24">단계</th>
|
||
|
|
<th>지정판매소</th>
|
||
|
|
<th class="w-14">주문</th>
|
||
|
|
<th>봉투 종류</th>
|
||
|
|
<th class="w-44">봉투 바코드</th>
|
||
|
|
<th class="w-14">포장</th>
|
||
|
|
<th class="w-14">수량</th>
|
||
|
|
</tr>
|
||
|
|
</thead>
|
||
|
|
<tbody id="dev-all-sales-tbody">
|
||
|
|
<tr><td colspan="8" class="text-center text-gray-400 py-4">불러오는 중…</td></tr>
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
(function () {
|
||
|
|
// base_url() 은 .env 의 app.baseURL 기준 절대 URL을 만든다. 사용자가 다른 host(예: localhost)로
|
||
|
|
// 접속한 경우 cross-origin 으로 가 세션 쿠키가 빠진다. 페이지 origin과 동일하도록 path 부분만 사용.
|
||
|
|
const apiPath = '<?= parse_url(base_url('bag/sale/dev-all-sales-history'), PHP_URL_PATH) ?>';
|
||
|
|
const api = apiPath || '/bag/sale/dev-all-sales-history';
|
||
|
|
const tbody = document.getElementById('dev-all-sales-tbody');
|
||
|
|
const countEl = document.getElementById('dev-all-sales-count');
|
||
|
|
const refreshBtn = document.getElementById('dev-all-sales-refresh');
|
||
|
|
if (!tbody) return;
|
||
|
|
|
||
|
|
const nf = new Intl.NumberFormat('ko-KR');
|
||
|
|
const escapeHtml = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => ({
|
||
|
|
'&': '&', '<': '<', '>': '>', '"': '"', "'": ''',
|
||
|
|
}[c]));
|
||
|
|
|
||
|
|
function stageBadge(type) {
|
||
|
|
const t = String(type || '').toLowerCase();
|
||
|
|
if (t === 'order') {
|
||
|
|
return '<span class="text-[12px] px-1.5 py-0.5 rounded bg-sky-100 text-sky-800">주문 접수</span>';
|
||
|
|
}
|
||
|
|
if (t === 'sale') {
|
||
|
|
return '<span class="text-[12px] px-1.5 py-0.5 rounded bg-rose-100 text-rose-800">판매</span>';
|
||
|
|
}
|
||
|
|
if (t === 'returned') {
|
||
|
|
return '<span class="text-[12px] px-1.5 py-0.5 rounded bg-emerald-100 text-emerald-800">반품 복귀</span>';
|
||
|
|
}
|
||
|
|
return `<span class="text-[12px] px-1.5 py-0.5 rounded bg-gray-100 text-gray-700">${escapeHtml(type)}</span>`;
|
||
|
|
}
|
||
|
|
|
||
|
|
function rowClass(type) {
|
||
|
|
const t = String(type || '').toLowerCase();
|
||
|
|
if (t === 'returned') return 'text-gray-500';
|
||
|
|
return '';
|
||
|
|
}
|
||
|
|
|
||
|
|
function dash(s) {
|
||
|
|
return (s == null || String(s) === '') ? '<span class="text-gray-300">-</span>' : escapeHtml(s);
|
||
|
|
}
|
||
|
|
|
||
|
|
function renderRows(rows) {
|
||
|
|
if (!Array.isArray(rows) || rows.length === 0) {
|
||
|
|
tbody.innerHTML = '<tr><td colspan="8" class="text-center text-gray-400 py-4">처리 내역이 없습니다.</td></tr>';
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const html = rows.map((r) => {
|
||
|
|
const shopText = (r.ds_name && String(r.ds_name).trim() !== '')
|
||
|
|
? `${escapeHtml(r.ds_shop_no || '')} ${escapeHtml(r.ds_name)}`.trim()
|
||
|
|
: `판매소#${escapeHtml(r.ds_idx || '0')}`;
|
||
|
|
const bagText = (r.bag_code || r.bag_name)
|
||
|
|
? `${escapeHtml(r.bag_code || '')} ${escapeHtml(r.bag_name || '')}`.trim()
|
||
|
|
: '<span class="text-gray-300">-</span>';
|
||
|
|
return `
|
||
|
|
<tr class="${rowClass(r.event_type)}">
|
||
|
|
<td class="text-center whitespace-nowrap">${escapeHtml(r.event_time || '')}</td>
|
||
|
|
<td class="text-center">${stageBadge(r.event_type)}</td>
|
||
|
|
<td class="text-left pl-1">${shopText}</td>
|
||
|
|
<td class="text-center">${escapeHtml(r.so_idx || '')}</td>
|
||
|
|
<td class="text-left pl-1">${bagText}</td>
|
||
|
|
<td class="text-center font-mono">${dash(r.code)}</td>
|
||
|
|
<td class="text-center">${dash(r.unit)}</td>
|
||
|
|
<td class="text-right pr-1 tabular-nums">${Number(r.qty || 0) ? nf.format(Number(r.qty)) : '<span class="text-gray-300">-</span>'}</td>
|
||
|
|
</tr>`;
|
||
|
|
}).join('');
|
||
|
|
tbody.innerHTML = html;
|
||
|
|
}
|
||
|
|
|
||
|
|
async function load() {
|
||
|
|
tbody.innerHTML = '<tr><td colspan="8" class="text-center text-gray-400 py-4">불러오는 중…</td></tr>';
|
||
|
|
countEl.textContent = '';
|
||
|
|
try {
|
||
|
|
const url = api + (api.indexOf('?') >= 0 ? '&' : '?') + '_=' + Date.now();
|
||
|
|
const res = await fetch(url, {
|
||
|
|
credentials: 'same-origin',
|
||
|
|
cache: 'no-store',
|
||
|
|
headers: { 'Accept': 'application/json', 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' },
|
||
|
|
});
|
||
|
|
const data = await res.json();
|
||
|
|
if (!data || data.ok === false) {
|
||
|
|
const msg = (data && data.message) ? data.message : '내역을 불러오지 못했습니다.';
|
||
|
|
tbody.innerHTML = `<tr><td colspan="8" class="text-center text-red-500 py-4">${escapeHtml(msg)}</td></tr>`;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
// ok=false (예: 지자체 미선택)일 때는 진단을 표 자리에 띄운다.
|
||
|
|
if (data.ok === false) {
|
||
|
|
const sess = data.session || {};
|
||
|
|
const lines = [
|
||
|
|
(data.message || '지자체를 선택해 주세요.'),
|
||
|
|
`세션: mb_idx=${sess.mb_idx ?? '-'} · mb_level=${sess.mb_level ?? '-'} · admin_selected_lg_idx=${sess.admin_selected_lg_idx ?? '-'} · mb_lg_idx=${sess.mb_lg_idx ?? '-'}`,
|
||
|
|
];
|
||
|
|
tbody.innerHTML = `<tr><td colspan="8" class="text-left px-2 py-4 text-amber-900"><div class="mb-1">${escapeHtml(lines[0])}</div><div class="text-[12px] text-gray-500 font-mono">${escapeHtml(lines[1])}</div></td></tr>`;
|
||
|
|
countEl.textContent = `lg_idx=- · 주문 0 / 판매 0 / 반품복귀 0 · 표시 0건`;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
renderRows(data.rows || []);
|
||
|
|
const orders = Number(data.orders ?? 0);
|
||
|
|
const sold = Number(data.sold ?? 0);
|
||
|
|
const ret = Number(data.returned ?? 0);
|
||
|
|
const shown = Array.isArray(data.rows) ? data.rows.length : 0;
|
||
|
|
const lg = data.lg_idx == null ? '-' : data.lg_idx;
|
||
|
|
const sess = data.session || {};
|
||
|
|
const sessText = `[mb_level=${sess.mb_level ?? '-'}, admin=${sess.admin_selected_lg_idx ?? '-'}, mb_lg=${sess.mb_lg_idx ?? '-'}]`;
|
||
|
|
countEl.textContent = `lg_idx=${lg} ${sessText} · 주문 ${nf.format(orders)} / 판매 ${nf.format(sold)} / 반품복귀 ${nf.format(ret)} · 표시 ${nf.format(shown)}건`;
|
||
|
|
} catch (e) {
|
||
|
|
tbody.innerHTML = '<tr><td colspan="8" class="text-center text-red-500 py-4">통신 오류로 불러오지 못했습니다.</td></tr>';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
refreshBtn?.addEventListener('click', () => load());
|
||
|
|
|
||
|
|
// 폼 제출(주문 저장·판매 저장 등) 직후 redirect 되어 새로 로드된 경우에도
|
||
|
|
// 항상 최신 상태를 보장한다. pageshow(BFCache 복원 포함)에서 한 번 더 호출.
|
||
|
|
window.addEventListener('pageshow', (ev) => {
|
||
|
|
if (ev.persisted) load();
|
||
|
|
});
|
||
|
|
|
||
|
|
load();
|
||
|
|
})();
|
||
|
|
</script>
|