Files
vexplor_dev/frontend/public/change-review.html
SeongHyun Kim 8c23f48996 feat: POP 재고관리 전면 구현 — 재고조정/재고이동/다중품목 공정
재고조정:
- fullBleed 좌우 분할, 숫자키패드 모달, 위치불일치 QR스캔+모달
- 임시저장 cart_items 상태관리 (saved/cancelled/confirmed)
- 조정이력 별도 페이지, DateRangePicker 통일
- popInventoryController 11개 API (adjust-batch, stock-detail, locations 등)

재고이동:
- 창고 탭: 탭 버튼 패턴 + flat 리스트 (아코디언 제거)
- 공정 탭: 공정명/설비 필터 모달 (작업지시번호 탭 제거)
- move-batch API: 창고→창고 + 공정→창고 (source_type 확장)
- 품목 이력 바텀시트 (transaction_type별 색상)

다중품목 공정실행:
- syncWorkInstructions LIMIT 1 제거 → detail 전체 순회
- batch_id 기반 품목별 공정 분리
- WorkOrderList/ProcessWork 품목 구분 표시

기타:
- PopShell fullBleed 모드 추가
- alert() → 토스트 메시지 교체
- MonitoringSettings import 수정
2026-04-10 17:17:23 +09:00

420 lines
18 KiB
HTML

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POP 변경사항 리뷰 — 공정실행 + 재고이동</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Pretendard', -apple-system, sans-serif; background: #0f172a; color: #e2e8f0; }
.header { background: linear-gradient(135deg, #1e293b, #0f172a); padding: 32px 40px; border-bottom: 2px solid #334155; }
.header h1 { font-size: 28px; font-weight: 800; color: #f8fafc; }
.header p { font-size: 14px; color: #94a3b8; margin-top: 8px; }
.section { padding: 32px 40px; }
.section-title { font-size: 22px; font-weight: 700; color: #f59e0b; margin-bottom: 8px; display: flex; align-items: center; gap: 12px; }
.section-title .badge { font-size: 12px; background: #f59e0b; color: #0f172a; padding: 4px 12px; border-radius: 20px; font-weight: 700; }
.section-desc { font-size: 14px; color: #94a3b8; margin-bottom: 24px; }
.compare { display: flex; gap: 24px; margin-bottom: 32px; }
.compare-box { flex: 1; background: #1e293b; border-radius: 16px; overflow: hidden; border: 2px solid #334155; }
.compare-box.before { border-color: #ef4444; }
.compare-box.after { border-color: #22c55e; }
.compare-label { padding: 12px 20px; font-size: 14px; font-weight: 700; text-align: center; }
.compare-box.before .compare-label { background: #7f1d1d; color: #fca5a5; }
.compare-box.after .compare-label { background: #14532d; color: #86efac; }
.compare-content { padding: 20px; }
.mock { background: #0f172a; border-radius: 12px; padding: 16px; font-size: 13px; line-height: 1.8; }
.mock .tab { display: inline-block; padding: 6px 16px; border-radius: 8px; font-size: 12px; font-weight: 700; margin: 2px 4px; }
.mock .tab.active { background: #f59e0b; color: #0f172a; }
.mock .tab.inactive { background: #334155; color: #94a3b8; }
.mock .tab.purple { background: #7c3aed; color: white; }
.mock .row { display: flex; justify-content: space-between; align-items: center; padding: 10px 12px; border-bottom: 1px solid #1e293b; }
.mock .row:last-child { border-bottom: none; }
.mock .row .name { font-weight: 600; color: #f8fafc; }
.mock .row .sub { font-size: 11px; color: #64748b; margin-top: 2px; }
.mock .row .qty { font-weight: 800; color: #f8fafc; font-size: 18px; }
.mock .row .btn { background: #f59e0b; color: #0f172a; padding: 4px 12px; border-radius: 8px; font-size: 12px; font-weight: 700; }
.mock .row .btn.green { background: #22c55e; }
.mock .row .btn.purple { background: #7c3aed; color: white; }
.mock .warn { background: #7f1d1d; color: #fca5a5; padding: 4px 10px; border-radius: 6px; font-size: 11px; font-weight: 700; display: inline-block; }
.mock .ok { background: #14532d; color: #86efac; padding: 4px 10px; border-radius: 6px; font-size: 11px; font-weight: 700; display: inline-block; }
.mock .wait { background: #78350f; color: #fde68a; padding: 4px 10px; border-radius: 6px; font-size: 11px; font-weight: 700; display: inline-block; }
.mock .group-header { background: #1e293b; padding: 8px 12px; font-weight: 700; color: #a78bfa; font-size: 13px; border-radius: 8px; margin: 8px 0 4px 0; }
.detail-box { background: #1e293b; border-radius: 12px; padding: 20px; margin-bottom: 16px; border-left: 4px solid #f59e0b; }
.detail-box h3 { font-size: 16px; font-weight: 700; color: #f8fafc; margin-bottom: 8px; }
.detail-box ul { padding-left: 20px; }
.detail-box li { font-size: 13px; color: #cbd5e1; margin-bottom: 6px; line-height: 1.6; }
.detail-box code { background: #334155; padding: 2px 6px; border-radius: 4px; font-size: 12px; color: #fde68a; }
.divider { height: 2px; background: linear-gradient(90deg, transparent, #334155, transparent); margin: 40px 0; }
.flow { display: flex; align-items: center; gap: 12px; margin: 16px 0; flex-wrap: wrap; }
.flow .step { background: #334155; padding: 8px 16px; border-radius: 8px; font-size: 13px; font-weight: 600; color: #e2e8f0; }
.flow .arrow { color: #f59e0b; font-size: 18px; font-weight: 700; }
.flow .step.highlight { background: #f59e0b; color: #0f172a; }
</style>
</head>
<body>
<div class="header">
<h1>POP 변경사항 리뷰</h1>
<p>2026-04-10 | 공정실행 다중품목 + 재고이동 리디자인</p>
</div>
<!-- ==================== 1. 공정실행 ==================== -->
<div class="section">
<div class="section-title">
1. 공정실행 — 다중품목 지원 <span class="badge">구현 완료</span>
</div>
<div class="section-desc">
PC에서 1개 작업지시에 여러 품목을 넣으면, POP에서도 품목별로 공정을 분리해서 보여줍니다.
</div>
<div class="compare">
<div class="compare-box before">
<div class="compare-label">BEFORE — 첫 번째 품목만 표시</div>
<div class="compare-content">
<div class="mock">
<div style="padding: 8px 12px; color: #94a3b8; font-size: 12px;">작업지시 CODE-00002</div>
<div class="row">
<div>
<div class="name">1. 제조반_계량</div>
<div class="sub">투입 100 → 양품 90</div>
</div>
<div class="qty">90 <span style="font-size:12px;color:#64748b">EA</span></div>
</div>
<div class="row">
<div>
<div class="name">2. 제조반_배합</div>
<div class="sub">투입 40 → 양품 20</div>
</div>
<div class="qty">20 <span style="font-size:12px;color:#64748b">EA</span></div>
</div>
<div class="row">
<div>
<div class="name">3. 제조반_포장</div>
<div class="sub">투입 20 → 양품 20</div>
</div>
<div class="qty">20 <span style="font-size:12px;color:#64748b">EA</span></div>
</div>
<div style="padding: 12px; text-align: center; color: #ef4444; font-size: 12px;">
⚠️ 욕조N_CTG28 (제품) 265개 + 원재료 2종이 있지만<br>첫 번째 품목만 공정 생성됨
</div>
</div>
</div>
</div>
<div class="compare-box after">
<div class="compare-label">AFTER — 품목별 공정 그룹</div>
<div class="compare-content">
<div class="mock">
<div style="padding: 8px 12px; color: #94a3b8; font-size: 12px;">작업지시 CODE-00002</div>
<div class="group-header">📦 욕조N_CTG28_반투명 (F_CRT01_265) — 제품 265개</div>
<div class="row">
<div>
<div class="name">1. 제조반_계량</div>
<div class="sub">투입 100 → 양품 90</div>
</div>
<div class="qty">90</div>
</div>
<div class="row">
<div>
<div class="name">2. 제조반_배합</div>
<div class="sub">투입 40 → 양품 20</div>
</div>
<div class="qty">20</div>
</div>
<div class="row">
<div>
<div class="name">3. 제조반_포장</div>
<div class="sub">투입 20 → 양품 20</div>
</div>
<div class="qty">20</div>
</div>
<div class="group-header">📦 반제품 가 (별도 라우팅 있을 경우)</div>
<div class="row">
<div>
<div class="name">1. 혼합</div>
<div class="sub">투입 50 → 양품 45</div>
</div>
<div class="qty">45</div>
</div>
<div style="padding: 8px 12px; color: #22c55e; font-size: 12px; text-align: center;">
✅ 각 품목이 독립된 공정 세트를 가짐 (batch_id로 구분)
</div>
</div>
</div>
</div>
</div>
<div class="detail-box">
<h3>기술 변경 요약</h3>
<ul>
<li><code>syncWorkInstructions</code>: <code>LIMIT 1</code> 제거 → work_instruction_detail 전체 순회</li>
<li>qty > 0 AND routing 있는 detail마다 공정 세트 독립 생성</li>
<li><code>work_order_process.batch_id</code> = item_number 저장 → 품목별 공정 구분</li>
<li>WorkOrderList: 카드에 품목명(품목코드) 표시, batch_id 기준 형제 공정 필터링</li>
<li>ProcessWork: 공정 상세에서 batch_id로 품목 구분 표시</li>
</ul>
</div>
</div>
<div class="divider"></div>
<!-- ==================== 2. 재고이동 ==================== -->
<div class="section">
<div class="section-title">
2. 재고이동 — 창고 탭 리디자인 <span class="badge">구현 완료</span>
</div>
<div class="section-desc">
아코디언 → 재고조정과 동일한 탭 버튼 패턴. 좌우 분할 + 이동 대기열.
</div>
<div class="compare">
<div class="compare-box before">
<div class="compare-label">BEFORE — 아코디언 접기/펼치기</div>
<div class="compare-content">
<div class="mock">
<div class="row" style="background:#1e293b; border-radius:8px; margin-bottom:4px;">
<div class="name">▶ 맹동창고</div>
<div style="color:#64748b">6건</div>
</div>
<div class="row" style="background:#1e293b; border-radius:8px; margin-bottom:4px;">
<div class="name">▶ 외주창고</div>
<div style="color:#64748b">3건</div>
</div>
<div class="row" style="background:#1e293b; border-radius:8px;">
<div class="name">▶ 테스트</div>
<div style="color:#64748b">4건</div>
</div>
<div style="padding: 12px; text-align: center; color: #ef4444; font-size: 12px;">
⚠️ 품목 많아지면 찾기 힘듦
</div>
</div>
</div>
</div>
<div class="compare-box after">
<div class="compare-label">AFTER — 탭 선택 + flat 리스트</div>
<div class="compare-content">
<div class="mock">
<div style="margin-bottom: 8px;">
<span class="tab active">전체</span>
<span class="tab inactive">맹동창고</span>
<span class="tab inactive">외주창고</span>
<span class="tab inactive">테스트</span>
</div>
<div class="row">
<div>
<div class="name">DEMO-PROD-001 / 데모페일 20L</div>
<div class="sub">맹동창고</div>
</div>
<div style="display:flex;align-items:center;gap:8px;">
<div class="qty">31</div>
<span class="btn"></span>
</div>
</div>
<div class="row">
<div>
<div class="name">793_CTG30_회색 / F_CRT01_040</div>
<div class="sub">25EA/BOX / 맹동창고</div>
</div>
<div style="display:flex;align-items:center;gap:8px;">
<div class="qty">400</div>
<span class="btn"></span>
</div>
</div>
<div class="row">
<div>
<div class="name">ESBC-500 / R_PLAST_024</div>
<div class="sub">180Kg/Drum / 테스트 · WH-001</div>
</div>
<div style="display:flex;align-items:center;gap:8px;">
<div class="qty">500</div>
<span class="btn"></span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="flow">
<div class="step">창고 탭 선택</div>
<div class="arrow"></div>
<div class="step">품목 [→] 터치</div>
<div class="arrow"></div>
<div class="step">도착 창고 모달</div>
<div class="arrow"></div>
<div class="step">수량 키패드</div>
<div class="arrow"></div>
<div class="step">대기열 추가</div>
<div class="arrow"></div>
<div class="step highlight">이동 확정</div>
</div>
</div>
<div class="divider"></div>
<!-- ==================== 3. 공정 탭 ==================== -->
<div class="section">
<div class="section-title">
3. 재고이동 공정 탭 — 공정/설비 필터 <span class="badge" style="background:#7c3aed;color:white;">구현 중</span>
</div>
<div class="section-desc">
"공정/설비 = 가상 창고" 개념. 공정명과 설비로 필터해서 해당 공정의 품목 현황을 봅니다.
</div>
<div class="compare">
<div class="compare-box before">
<div class="compare-label">BEFORE — 작업지시번호로 선택</div>
<div class="compare-content">
<div class="mock">
<div style="margin-bottom: 8px;">
<span class="tab purple">CODE-00002</span>
<span class="tab inactive">CODE-00005</span>
<span class="tab inactive">CODE-00006</span>
</div>
<div style="padding: 12px; text-align: center; color: #ef4444; font-size: 12px;">
⚠️ 작업지시 100개 쌓이면?<br>어떤 번호가 뭔지 모름
</div>
</div>
</div>
</div>
<div class="compare-box after">
<div class="compare-label">AFTER — 공정명 + 설비로 필터</div>
<div class="compare-content">
<div class="mock">
<div style="display:flex; gap:8px; margin-bottom:12px;">
<div style="flex:1; background:#334155; padding:10px 14px; border-radius:10px; text-align:center;">
<div style="font-size:11px; color:#a78bfa;">공정</div>
<div style="font-size:14px; font-weight:700; color:#f8fafc;">제조반_계량 ▼</div>
</div>
<div style="flex:1; background:#334155; padding:10px 14px; border-radius:10px; text-align:center;">
<div style="font-size:11px; color:#a78bfa;">설비</div>
<div style="font-size:14px; font-weight:700; color:#f8fafc;">전체 ▼</div>
</div>
</div>
<div style="font-size:11px; color:#64748b; padding:4px 8px;">제조반_계량 공정에 있는 품목들</div>
<div class="row">
<div>
<div class="name">페일_PE용기 / P_OTH06_038</div>
<div class="sub">WI: CODE-00002 · 투입100 · 양품90</div>
</div>
<div style="text-align:right;">
<div><span class="wait">대기 50</span></div>
<div style="margin-top:4px;"><span class="btn purple">→ 이동</span></div>
</div>
</div>
<div class="row">
<div>
<div class="name">페일_PE용기 / P_OTH06_038</div>
<div class="sub">WI: CODE-00005 · 투입50 · 양품50</div>
</div>
<div style="text-align:right;">
<div><span class="ok">입고완료</span></div>
</div>
</div>
<div class="row">
<div>
<div class="name">데모페일 20L / DEMO-PROD-001</div>
<div class="sub">WI: CODE-00006 · 투입30 · 양품30</div>
</div>
<div style="text-align:right;">
<div><span class="ok">입고완료</span></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="detail-box">
<h3>공정 탭 수량 계산법</h3>
<ul>
<li><strong>대기수량</strong> = N공정 양품 - (N+1)공정 투입. 예: 계량 양품90 - 배합 투입40 = <code>대기 50EA</code></li>
<li><strong>공정중</strong> = 투입 - 양품 - 불량. 예: 투입100 - 양품90 - 불량0 = <code>공정중 10EA</code></li>
<li><strong>미입고</strong> = 마지막 공정 양품 > 0 AND 창고 미배정 → <code>⚠️ 미입고</code> 경고</li>
<li><strong>입고완료</strong> = target_warehouse_id 있음 → 정상</li>
</ul>
</div>
<div class="detail-box" style="border-left-color: #7c3aed;">
<h3>"공정 = 가상 창고" 비유</h3>
<ul>
<li>창고를 선택하면 → 그 창고 안의 품목들이 보이듯</li>
<li>공정을 선택하면 → 그 공정에서 처리 중인 품목들이 보인다</li>
<li>설비를 추가 선택하면 → 더 좁혀짐 (1호기에서 계량 중인 것만)</li>
<li>대기수량이 있는 품목 = "아직 다음 공정으로 안 넘어간 것" → [→ 이동] 가능</li>
</ul>
</div>
</div>
<div class="divider"></div>
<!-- ==================== 4. 재고조정 ==================== -->
<div class="section">
<div class="section-title">
4. 재고조정 — 임시저장 + 상태관리 <span class="badge">구현 완료</span>
</div>
<div class="section-desc">
cart_items 테이블로 임시저장. 상태별 관리 (saved/cancelled/confirmed).
</div>
<div class="compare">
<div class="compare-box before">
<div class="compare-label">BEFORE — 임시저장 없음</div>
<div class="compare-content">
<div class="mock">
<div style="padding: 16px; text-align: center; color: #94a3b8;">
조정 작업 중 화면 이탈하면<br>모든 데이터 사라짐
</div>
</div>
</div>
</div>
<div class="compare-box after">
<div class="compare-label">AFTER — 임시저장 + 상태 관리</div>
<div class="compare-content">
<div class="mock">
<div class="row">
<div class="name">임시저장 버튼</div>
<div><span class="ok">saved</span></div>
</div>
<div class="row">
<div class="name">X 버튼 (개별 취소)</div>
<div><span class="warn">cancelled</span></div>
</div>
<div class="row">
<div class="name">초기화 버튼</div>
<div><span class="warn">전체 cancelled</span></div>
</div>
<div class="row">
<div class="name">일괄확정 버튼</div>
<div><span class="ok">confirmed</span></div>
</div>
<div style="padding: 8px 12px; color: #22c55e; font-size: 12px; text-align: center;">
✅ 페이지 새로고침해도 saved 데이터 복원됨
</div>
</div>
</div>
</div>
</div>
</div>
<div style="padding: 40px; text-align: center; color: #475569; font-size: 12px;">
Generated 2026-04-10 | POP 재고관리 리뷰
</div>
</body>
</html>