사이트 메뉴 /bag/* 10개 페이지 구현 + E2E 테스트 timeout 보강

- Bag 컨트롤러 신규 (기본정보/발주입고/불출/재고/판매/판매현황/수불/통계/창/도움말)
- 사이트 공통 레이아웃 bag/layout/main.php 추출
- /bag/* 라우트 10개 등록 (Routes.php)
- bag-site.spec.js E2E 테스트 11개 추가
- Playwright timeout 30s→60s, waitForURL 15s→30s
- P4 지자체관리자 접근 테스트 3개로 분리

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
javamon1174
2026-03-26 14:30:45 +09:00
parent 466f6fe085
commit a0103eb95d
27 changed files with 951 additions and 18 deletions

View File

@@ -0,0 +1,9 @@
<div class="flex items-center justify-center h-full text-gray-400">
<div class="text-center">
<svg class="w-16 h-16 mx-auto mb-4 text-gray-300" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 13.125C3 12.504 3.504 12 4.125 12h2.25c.621 0 1.125.504 1.125 1.125v6.75C7.5 20.496 6.996 21 6.375 21h-2.25A1.125 1.125 0 013 19.875v-6.75zM9.75 8.625c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125v11.25c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V8.625zM16.5 4.125c0-.621.504-1.125 1.125-1.125h2.25C20.496 3 21 3.504 21 4.125v15.75c0 .621-.504 1.125-1.125 1.125h-2.25a1.125 1.125 0 01-1.125-1.125V4.125z"/>
</svg>
<p class="text-lg font-medium">통계 분석 관리</p>
<p class="text-sm mt-1">Phase 6에서 구현 예정입니다.</p>
</div>
</div>

View File

@@ -0,0 +1,83 @@
<div class="space-y-6">
<!-- 기본코드 종류 -->
<section>
<h3 class="text-base font-bold text-gray-700 mb-2 border-b pb-1">기본코드 종류</h3>
<table class="data-table">
<thead><tr>
<th class="w-16">번호</th><th>코드</th><th>코드명</th><th>상태</th>
</tr></thead>
<tbody>
<?php if (! empty($codeKinds)): ?>
<?php foreach ($codeKinds as $i => $row): ?>
<tr>
<td class="text-center"><?= $i + 1 ?></td>
<td class="text-center"><?= esc($row->ck_code) ?></td>
<td><?= esc($row->ck_name) ?></td>
<td class="text-center"><?= ($row->ck_status ?? 'active') === 'active' ? '사용' : '미사용' ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="4" class="text-center text-gray-400 py-4">등록된 코드 종류가 없습니다.</td></tr>
<?php endif; ?>
</tbody>
</table>
</section>
<!-- 봉투 단가 -->
<section>
<h3 class="text-base font-bold text-gray-700 mb-2 border-b pb-1">봉투 단가</h3>
<table class="data-table">
<thead><tr>
<th class="w-16">번호</th><th>봉투코드</th><th>봉투명</th><th>발주단가</th><th>도매가</th><th>소비자가</th><th>적용시작</th><th>적용종료</th><th>상태</th>
</tr></thead>
<tbody>
<?php if (! empty($bagPrices)): ?>
<?php foreach ($bagPrices as $i => $row): ?>
<tr>
<td class="text-center"><?= $i + 1 ?></td>
<td class="text-center"><?= esc($row->bp_bag_code) ?></td>
<td><?= esc($row->bp_bag_name ?? '') ?></td>
<td class="text-right"><?= number_format((float)($row->bp_order_price ?? 0)) ?></td>
<td class="text-right"><?= number_format((float)($row->bp_wholesale_price ?? 0)) ?></td>
<td class="text-right"><?= number_format((float)($row->bp_consumer_price ?? 0)) ?></td>
<td class="text-center"><?= esc($row->bp_start_date ?? '') ?></td>
<td class="text-center"><?= ($row->bp_end_date ?? '') ?: '현재' ?></td>
<td class="text-center"><?= ($row->bp_status ?? 'active') === 'active' ? '사용' : '만료' ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="9" class="text-center text-gray-400 py-4">등록된 단가 정보가 없습니다.</td></tr>
<?php endif; ?>
</tbody>
</table>
</section>
<!-- 포장 단위 -->
<section>
<h3 class="text-base font-bold text-gray-700 mb-2 border-b pb-1">포장 단위</h3>
<table class="data-table">
<thead><tr>
<th class="w-16">번호</th><th>봉투코드</th><th>봉투명</th><th>박스당 팩 수</th><th>팩당 낱장 수</th><th>1박스 총 낱장</th><th>적용시작</th><th>적용종료</th><th>상태</th>
</tr></thead>
<tbody>
<?php if (! empty($packagingUnits)): ?>
<?php foreach ($packagingUnits as $i => $row): ?>
<tr>
<td class="text-center"><?= $i + 1 ?></td>
<td class="text-center"><?= esc($row->pu_bag_code) ?></td>
<td><?= esc($row->pu_bag_name ?? '') ?></td>
<td class="text-right"><?= number_format((int)($row->pu_packs_per_box ?? 0)) ?></td>
<td class="text-right"><?= number_format((int)($row->pu_sheets_per_pack ?? 0)) ?></td>
<td class="text-right"><?= number_format((int)($row->pu_packs_per_box ?? 0) * (int)($row->pu_sheets_per_pack ?? 0)) ?></td>
<td class="text-center"><?= esc($row->pu_start_date ?? '') ?></td>
<td class="text-center"><?= ($row->pu_end_date ?? '') ?: '현재' ?></td>
<td class="text-center"><?= ($row->pu_status ?? 'active') === 'active' ? '사용' : '만료' ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="9" class="text-center text-gray-400 py-4">등록된 포장 단위가 없습니다.</td></tr>
<?php endif; ?>
</tbody>
</table>
</section>
</div>

88
app/Views/bag/flow.php Normal file
View File

@@ -0,0 +1,88 @@
<div class="space-y-1">
<form method="get" class="flex items-center gap-3 text-sm mb-3">
<label class="font-bold text-gray-700">조회기간</label>
<input type="date" name="start_date" value="<?= esc($startDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
<span>~</span>
<input type="date" name="end_date" value="<?= esc($endDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm">조회</button>
<a href="<?= base_url('bag/flow') ?>" class="text-sm text-gray-500 hover:text-gray-700">초기화</a>
</form>
<!-- 수불 요약 -->
<table class="data-table">
<thead>
<tr>
<th rowspan="2">봉투코드</th>
<th rowspan="2">봉투명</th>
<th rowspan="2">현재재고</th>
<th colspan="2">입고</th>
<th colspan="2">출고</th>
</tr>
<tr>
<th>입고수량</th><th>반품수량</th>
<th>판매수량</th><th>불출수량</th>
</tr>
</thead>
<tbody>
<?php
// 봉투코드별 수불 집계
$summary = [];
// 재고
foreach ($inventory as $inv) {
$code = $inv->bi_bag_code ?? '';
if (! isset($summary[$code])) {
$summary[$code] = ['name' => $inv->bi_bag_name ?? '', 'stock' => 0, 'recv' => 0, 'return' => 0, 'sale' => 0, 'issue' => 0];
}
$summary[$code]['stock'] += (int)($inv->bi_qty_sheet ?? 0);
}
// 입고
foreach ($receiving as $r) {
$code = $r->br_bag_code ?? '';
if (! isset($summary[$code])) {
$summary[$code] = ['name' => $r->br_bag_name ?? '', 'stock' => 0, 'recv' => 0, 'return' => 0, 'sale' => 0, 'issue' => 0];
}
$summary[$code]['recv'] += (int)($r->br_qty_sheet ?? 0);
}
// 판매/반품
foreach ($sales as $s) {
$code = $s->bs_bag_code ?? '';
if (! isset($summary[$code])) {
$summary[$code] = ['name' => $s->bs_bag_name ?? '', 'stock' => 0, 'recv' => 0, 'return' => 0, 'sale' => 0, 'issue' => 0];
}
$type = $s->bs_type ?? 'sale';
if ($type === 'return') {
$summary[$code]['return'] += (int)($s->bs_qty ?? 0);
} else {
$summary[$code]['sale'] += (int)($s->bs_qty ?? 0);
}
}
// 불출
foreach ($issues as $iss) {
$code = $iss->bi2_bag_code ?? '';
if (! isset($summary[$code])) {
$summary[$code] = ['name' => $iss->bi2_bag_name ?? '', 'stock' => 0, 'recv' => 0, 'return' => 0, 'sale' => 0, 'issue' => 0];
}
if (($iss->bi2_status ?? 'normal') === 'normal') {
$summary[$code]['issue'] += (int)($iss->bi2_qty ?? 0);
}
}
ksort($summary);
?>
<?php if (! empty($summary)): ?>
<?php $idx = 0; foreach ($summary as $code => $s): $idx++; ?>
<tr>
<td class="text-center"><?= esc($code) ?></td>
<td><?= esc($s['name']) ?></td>
<td class="text-right"><?= number_format($s['stock']) ?></td>
<td class="text-right"><?= number_format($s['recv']) ?></td>
<td class="text-right"><?= number_format($s['return']) ?></td>
<td class="text-right"><?= number_format($s['sale']) ?></td>
<td class="text-right"><?= number_format($s['issue']) ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="7" class="text-center text-gray-400 py-4">수불 데이터가 없습니다.</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>

31
app/Views/bag/help.php Normal file
View File

@@ -0,0 +1,31 @@
<div class="max-w-3xl mx-auto py-4">
<h2 class="text-lg font-bold text-gray-700 mb-4">도움말</h2>
<div class="space-y-4 text-sm text-gray-600">
<section>
<h3 class="font-bold text-gray-700 mb-1">시스템 개요</h3>
<p>쓰레기봉투 물류시스템은 지자체 종량제 쓰레기봉투의 발주, 입고, 재고, 판매, 불출 전체 물류 프로세스를 관리합니다.</p>
</section>
<section>
<h3 class="font-bold text-gray-700 mb-1">메뉴 안내</h3>
<table class="data-table">
<thead><tr><th>메뉴</th><th>설명</th></tr></thead>
<tbody>
<tr><td class="font-medium">기본정보관리</td><td>코드 종류, 봉투 단가, 포장 단위 기본 데이터 조회</td></tr>
<tr><td class="font-medium">발주 입고 관리</td><td>봉투 발주 현황 입고 현황 조회</td></tr>
<tr><td class="font-medium">불출 관리</td><td>무료용 봉투 불출 내역 조회</td></tr>
<tr><td class="font-medium">재고 관리</td><td>봉투별 현재 재고(낱장) 조회</td></tr>
<tr><td class="font-medium">판매 관리</td><td>주문 접수, 판매/반품 내역 조회</td></tr>
<tr><td class="font-medium">판매 현황</td><td>기간별 판매 데이터 조회</td></tr>
<tr><td class="font-medium">봉투 수불 관리</td><td>봉투코드별 입출고 수불 요약 조회</td></tr>
</tbody>
</table>
</section>
<section>
<h3 class="font-bold text-gray-700 mb-1">문의</h3>
<p>시스템 사용 문의사항은 소속 지자체 담당자에게 연락하시기 바랍니다.</p>
</section>
</div>
</div>

View File

@@ -0,0 +1,20 @@
<table class="data-table">
<thead><tr>
<th class="w-16">번호</th><th>봉투코드</th><th>봉투명</th><th>현재재고(낱장)</th><th>최종갱신</th>
</tr></thead>
<tbody>
<?php if (! empty($list)): ?>
<?php foreach ($list as $i => $row): ?>
<tr>
<td class="text-center"><?= $i + 1 ?></td>
<td class="text-center"><?= esc($row->bi_bag_code ?? '') ?></td>
<td><?= esc($row->bi_bag_name ?? '') ?></td>
<td class="text-right"><?= number_format((int)($row->bi_qty_sheet ?? 0)) ?></td>
<td class="text-center"><?= esc($row->bi_updated_at ?? $row->updated_at ?? '') ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="5" class="text-center text-gray-400 py-4">재고 데이터가 없습니다.</td></tr>
<?php endif; ?>
</tbody>
</table>

41
app/Views/bag/issue.php Normal file
View File

@@ -0,0 +1,41 @@
<div class="space-y-1">
<form method="get" class="flex items-center gap-3 text-sm mb-3">
<label class="font-bold text-gray-700">불출일</label>
<input type="date" name="start_date" value="<?= esc($startDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
<span>~</span>
<input type="date" name="end_date" value="<?= esc($endDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm">조회</button>
<a href="<?= base_url('bag/issue') ?>" class="text-sm text-gray-500 hover:text-gray-700">초기화</a>
</form>
<table class="data-table">
<thead><tr>
<th class="w-16">번호</th><th>연도</th><th>분기</th><th>구분</th><th>불출일</th><th>불출처</th><th>봉투코드</th><th>봉투명</th><th>수량</th><th>상태</th>
</tr></thead>
<tbody>
<?php if (! empty($list)): ?>
<?php foreach ($list as $i => $row): ?>
<tr>
<td class="text-center"><?= $i + 1 ?></td>
<td class="text-center"><?= esc($row->bi2_year ?? '') ?></td>
<td class="text-center"><?= esc($row->bi2_quarter ?? '') ?></td>
<td class="text-center"><?= esc($row->bi2_type ?? '') ?></td>
<td class="text-center"><?= esc($row->bi2_issue_date ?? '') ?></td>
<td><?= esc($row->bi2_destination ?? '') ?></td>
<td class="text-center"><?= esc($row->bi2_bag_code ?? '') ?></td>
<td><?= esc($row->bi2_bag_name ?? '') ?></td>
<td class="text-right"><?= number_format((int)($row->bi2_qty ?? 0)) ?></td>
<td class="text-center">
<?php
$st = $row->bi2_status ?? 'normal';
echo match($st) { 'normal' => '정상', 'cancelled' => '<span class="text-orange-600">취소</span>', default => esc($st) };
?>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="10" class="text-center text-gray-400 py-4">등록된 불출이 없습니다.</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>

View File

@@ -0,0 +1,123 @@
<?php
declare(strict_types=1);
helper('admin');
$siteNavTree = get_site_nav_tree();
$uriObj = service('request')->getUri();
$currentPath = trim((string) $uriObj->getPath(), '/');
if (str_starts_with($currentPath, 'index.php/')) {
$currentPath = substr($currentPath, strlen('index.php/'));
}
$mbLevel = (int) session()->get('mb_level');
$isAdmin = ($mbLevel === \Config\Roles::LEVEL_SUPER_ADMIN || $mbLevel === \Config\Roles::LEVEL_LOCAL_ADMIN);
$effectiveLgIdx = admin_effective_lg_idx();
$effectiveLgName = null;
if ($effectiveLgIdx) {
$lgRow = model(\App\Models\LocalGovernmentModel::class)->find($effectiveLgIdx);
$effectiveLgName = $lgRow ? $lgRow->lg_name : null;
}
?>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title><?= esc($title ?? '쓰레기봉투 물류시스템') ?></title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700&amp;display=swap" rel="stylesheet"/>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: { sans: ['"Malgun Gothic"', '"Noto Sans KR"', 'sans-serif'] },
colors: {
'system-header': '#ffffff',
'title-bar': '#2c3e50',
'control-panel': '#f8f9fa',
'btn-search': '#1c4e80',
'btn-excel-border': '#28a745',
'btn-excel-text': '#28a745',
'btn-print-border': '#ced4da',
'btn-exit': '#d9534f',
},
fontSize: { 'xxs': '0.65rem' }
}
}
}
</script>
<style data-purpose="table-layout">
.data-table { width: 100%; border-collapse: collapse; font-family: 'Malgun Gothic', 'Noto Sans KR', sans-serif; }
.data-table th, .data-table td { border: 1px solid #ccc; padding: 4px 8px; white-space: nowrap; font-size: 13px; }
.data-table th { background-color: #e9ecef; text-align: center; vertical-align: middle; font-weight: bold; color: #333; }
.data-table tbody tr:nth-child(even) td { background-color: #f9f9f9; }
.data-table tbody tr:hover td { background-color: #e6f7ff !important; }
.main-content-area { height: calc(100vh - 130px); overflow: auto; }
body { overflow: hidden; }
</style>
</head>
<body class="bg-gray-100 text-gray-800 flex flex-col h-screen font-sans antialiased select-none">
<!-- BEGIN: Top Navigation -->
<header class="bg-white border-b border-gray-300 h-12 flex items-center justify-between px-4 shrink-0 z-20">
<div class="flex items-center gap-4">
<div class="flex items-center gap-2">
<div class="w-6 h-6 flex items-center justify-center shrink-0">
<svg class="h-5 w-5" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<rect width="16" height="16" fill="#2563eb"/><rect x="2" y="2" width="7" height="7" fill="white"/><rect x="5" y="5" width="9" height="9" fill="white"/>
</svg>
</div>
<a href="<?= base_url() ?>" class="text-base font-semibold text-gray-800 tracking-tight hover:text-blue-600">쓰레기봉투 물류시스템</a>
</div>
</div>
<nav class="hidden md:flex gap-5 text-sm font-medium text-gray-600">
<?php if (! empty($siteNavTree)): ?>
<?php foreach ($siteNavTree as $navItem): ?>
<?php $isActive = ($currentPath === trim((string) $navItem->mm_link, '/')); ?>
<div class="relative group">
<a class="<?= $isActive ? 'text-blue-700 font-bold border-b-2 border-blue-700 pb-3 -mb-3' : 'hover:text-blue-600' ?>"
href="<?= base_url($navItem->mm_link) ?>">
<?= esc($navItem->mm_name) ?>
</a>
<?php if (! empty($navItem->children)): ?>
<div class="absolute left-0 top-full hidden group-hover:block bg-white border border-gray-200 rounded shadow-lg min-w-[10rem] z-30">
<?php foreach ($navItem->children as $child): ?>
<a href="<?= base_url($child->mm_link) ?>"
class="block px-3 py-1.5 text-sm text-gray-700 hover:bg-blue-50 whitespace-nowrap">
<?= esc($child->mm_name) ?>
</a>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
<?php endif; ?>
</nav>
<div class="flex items-center gap-2">
<?php if ($effectiveLgName !== null): ?>
<span class="text-sm text-gray-600" title="현재 작업 지자체"><?= esc($effectiveLgName) ?></span>
<?php endif; ?>
<?php if ($isAdmin): ?>
<a href="<?= base_url('admin') ?>" class="text-gray-500 hover:text-blue-600 text-sm">관리자</a>
<?php endif; ?>
<a href="<?= base_url('logout') ?>" class="text-gray-500 hover:text-red-600 transition-colors inline-block p-1 rounded hover:bg-red-50" title="로그아웃">
<svg class="h-5 w-5 inline" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M6 18L18 6M6 6l12 12" stroke-linecap="round" stroke-linejoin="round"/></svg> 종료
</a>
</div>
</header>
<!-- END: Top Navigation -->
<div class="bg-title-bar text-white px-4 py-2 text-sm font-medium shrink-0">
<?= esc($title ?? '') ?>
</div>
<?php if (session()->getFlashdata('success')): ?>
<div class="mx-4 mt-2 p-3 rounded-lg bg-green-50 text-green-700 text-sm" role="alert"><?= esc(session()->getFlashdata('success')) ?></div>
<?php endif; ?>
<?php if (session()->getFlashdata('error')): ?>
<div class="mx-4 mt-2 p-3 rounded-lg bg-red-50 text-red-700 text-sm" role="alert"><?= esc(session()->getFlashdata('error')) ?></div>
<?php endif; ?>
<main class="main-content-area flex-grow bg-white p-4">
<?= $content ?>
</main>
<footer class="bg-gray-200 border-t border-gray-300 px-4 py-1 text-xs text-gray-600 flex items-center justify-between shrink-0">
<span>쓰레기봉투 물류시스템</span>
<span><?= date('Y.m.d (D) g:i:sA') ?></span>
</footer>
</body>
</html>

View File

@@ -0,0 +1,71 @@
<div class="space-y-1">
<!-- 필터 -->
<form method="get" class="flex items-center gap-3 text-sm mb-3">
<label class="font-bold text-gray-700">조회기간</label>
<input type="date" name="start_date" value="<?= esc($startDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
<span>~</span>
<input type="date" name="end_date" value="<?= esc($endDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm">조회</button>
<a href="<?= base_url('bag/purchase-inbound') ?>" class="text-sm text-gray-500 hover:text-gray-700">초기화</a>
</form>
<!-- 발주 현황 -->
<section>
<h3 class="text-base font-bold text-gray-700 mb-2 border-b pb-1">발주 현황</h3>
<table class="data-table">
<thead><tr>
<th class="w-16">번호</th><th>LOT번호</th><th>발주일</th><th>품목수</th><th>총수량(낱장)</th><th>총금액</th><th>상태</th>
</tr></thead>
<tbody>
<?php if (! empty($orders)): ?>
<?php foreach ($orders as $i => $row): ?>
<?php $summary = $itemSummary[$row->bo_idx] ?? ['qty' => 0, 'amount' => 0, 'count' => 0]; ?>
<tr>
<td class="text-center"><?= $i + 1 ?></td>
<td class="text-center"><?= esc($row->bo_lot_number ?? '') ?></td>
<td class="text-center"><?= esc($row->bo_order_date ?? '') ?></td>
<td class="text-right"><?= number_format($summary['count']) ?></td>
<td class="text-right"><?= number_format($summary['qty']) ?></td>
<td class="text-right"><?= number_format($summary['amount']) ?></td>
<td class="text-center">
<?php
$st = $row->bo_status ?? 'normal';
echo match($st) { 'normal' => '정상', 'cancelled' => '<span class="text-orange-600">취소</span>', 'deleted' => '<span class="text-red-600">삭제</span>', default => esc($st) };
?>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="7" class="text-center text-gray-400 py-4">등록된 발주가 없습니다.</td></tr>
<?php endif; ?>
</tbody>
</table>
</section>
<!-- 입고 현황 -->
<section class="mt-4">
<h3 class="text-base font-bold text-gray-700 mb-2 border-b pb-1">입고 현황</h3>
<table class="data-table">
<thead><tr>
<th class="w-16">번호</th><th>봉투코드</th><th>봉투명</th><th>박스수</th><th>낱장수</th><th>입고일</th><th>구분</th>
</tr></thead>
<tbody>
<?php if (! empty($receivings)): ?>
<?php foreach ($receivings as $i => $row): ?>
<tr>
<td class="text-center"><?= $i + 1 ?></td>
<td class="text-center"><?= esc($row->br_bag_code ?? '') ?></td>
<td><?= esc($row->br_bag_name ?? '') ?></td>
<td class="text-right"><?= number_format((int)($row->br_qty_box ?? 0)) ?></td>
<td class="text-right"><?= number_format((int)($row->br_qty_sheet ?? 0)) ?></td>
<td class="text-center"><?= esc($row->br_receive_date ?? '') ?></td>
<td class="text-center"><?= esc($row->br_type ?? '정상입고') ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="7" class="text-center text-gray-400 py-4">등록된 입고가 없습니다.</td></tr>
<?php endif; ?>
</tbody>
</table>
</section>
</div>

76
app/Views/bag/sales.php Normal file
View File

@@ -0,0 +1,76 @@
<div class="space-y-1">
<form method="get" class="flex items-center gap-3 text-sm mb-3">
<label class="font-bold text-gray-700">조회기간</label>
<input type="date" name="start_date" value="<?= esc($startDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
<span>~</span>
<input type="date" name="end_date" value="<?= esc($endDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm">조회</button>
<a href="<?= base_url('bag/sales') ?>" class="text-sm text-gray-500 hover:text-gray-700">초기화</a>
</form>
<!-- 주문 접수 -->
<section>
<h3 class="text-base font-bold text-gray-700 mb-2 border-b pb-1">주문 접수</h3>
<table class="data-table">
<thead><tr>
<th class="w-16">번호</th><th>판매소</th><th>접수일</th><th>배달일</th><th>수량</th><th>금액</th><th>상태</th>
</tr></thead>
<tbody>
<?php if (! empty($orderList)): ?>
<?php foreach ($orderList as $i => $row): ?>
<tr>
<td class="text-center"><?= $i + 1 ?></td>
<td><?= esc($row->so_shop_name ?? '') ?></td>
<td class="text-center"><?= esc($row->so_order_date ?? '') ?></td>
<td class="text-center"><?= esc($row->so_delivery_date ?? '') ?></td>
<td class="text-right"><?= number_format((int)($row->so_qty ?? 0)) ?></td>
<td class="text-right"><?= number_format((float)($row->so_amount ?? 0)) ?></td>
<td class="text-center">
<?php
$st = $row->so_status ?? 'normal';
echo match($st) { 'normal' => '정상', 'cancelled' => '<span class="text-orange-600">취소</span>', default => esc($st) };
?>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="7" class="text-center text-gray-400 py-4">등록된 주문이 없습니다.</td></tr>
<?php endif; ?>
</tbody>
</table>
</section>
<!-- 판매/반품 -->
<section class="mt-4">
<h3 class="text-base font-bold text-gray-700 mb-2 border-b pb-1">판매/반품</h3>
<table class="data-table">
<thead><tr>
<th class="w-16">번호</th><th>판매소</th><th>판매일</th><th>봉투코드</th><th>봉투명</th><th>수량</th><th>단가</th><th>금액</th><th>구분</th>
</tr></thead>
<tbody>
<?php if (! empty($salesList)): ?>
<?php foreach ($salesList as $i => $row): ?>
<tr>
<td class="text-center"><?= $i + 1 ?></td>
<td><?= esc($row->bs_shop_name ?? '') ?></td>
<td class="text-center"><?= esc($row->bs_sale_date ?? '') ?></td>
<td class="text-center"><?= esc($row->bs_bag_code ?? '') ?></td>
<td><?= esc($row->bs_bag_name ?? '') ?></td>
<td class="text-right"><?= number_format((int)($row->bs_qty ?? 0)) ?></td>
<td class="text-right"><?= number_format((float)($row->bs_unit_price ?? 0)) ?></td>
<td class="text-right"><?= number_format((float)($row->bs_amount ?? 0)) ?></td>
<td class="text-center">
<?php
$t = $row->bs_type ?? 'sale';
echo match($t) { 'sale' => '판매', 'return' => '<span class="text-blue-600">반품</span>', 'cancel' => '<span class="text-red-600">취소</span>', default => esc($t) };
?>
</td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="9" class="text-center text-gray-400 py-4">등록된 판매/반품이 없습니다.</td></tr>
<?php endif; ?>
</tbody>
</table>
</section>
</div>

View File

@@ -0,0 +1,34 @@
<div class="space-y-1">
<form method="get" class="flex items-center gap-3 text-sm mb-3">
<label class="font-bold text-gray-700">조회기간</label>
<input type="date" name="start_date" value="<?= esc($startDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
<span>~</span>
<input type="date" name="end_date" value="<?= esc($endDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm">조회</button>
<a href="<?= base_url('bag/sales-stats') ?>" class="text-sm text-gray-500 hover:text-gray-700">초기화</a>
</form>
<table class="data-table">
<thead><tr>
<th class="w-16">번호</th><th>판매소</th><th>판매일</th><th>봉투코드</th><th>봉투명</th><th>수량</th><th>단가</th><th>금액</th>
</tr></thead>
<tbody>
<?php if (! empty($result)): ?>
<?php foreach ($result as $i => $row): ?>
<tr>
<td class="text-center"><?= $i + 1 ?></td>
<td><?= esc($row->bs_shop_name ?? '') ?></td>
<td class="text-center"><?= esc($row->bs_sale_date ?? '') ?></td>
<td class="text-center"><?= esc($row->bs_bag_code ?? '') ?></td>
<td><?= esc($row->bs_bag_name ?? '') ?></td>
<td class="text-right"><?= number_format((int)($row->bs_qty ?? 0)) ?></td>
<td class="text-right"><?= number_format((float)($row->bs_unit_price ?? 0)) ?></td>
<td class="text-right"><?= number_format((float)($row->bs_amount ?? 0)) ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr><td colspan="8" class="text-center text-gray-400 py-4">판매 데이터가 없습니다.</td></tr>
<?php endif; ?>
</tbody>
</table>
</div>

9
app/Views/bag/window.php Normal file
View File

@@ -0,0 +1,9 @@
<div class="flex items-center justify-center h-full text-gray-400">
<div class="text-center">
<svg class="w-16 h-16 mx-auto mb-4 text-gray-300" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 8.25V18a2.25 2.25 0 002.25 2.25h13.5A2.25 2.25 0 0021 18V8.25m-18 0V6a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 6v2.25m-18 0h18M5.25 6h.008v.008H5.25V6zM7.5 6h.008v.008H7.5V6zm2.25 0h.008v.008H9.75V6z"/>
</svg>
<p class="text-lg font-medium"> 관리</p>
<p class="text-sm mt-1">Phase 6에서 구현 예정입니다.</p>
</div>
</div>