P2-15~18, P5-04~11, CT-05~06 웹 미구현 기능 전체 구현
P2-15: 지정판매소 다조건 조회 (이름/구군/상태 필터) P2-17: 지정판매소 지도 표시 (Kakao Maps) P2-18: 지정판매소 현황 (연도별 신규/취소 통계) P5-04: 년 판매 현황 (월별 피벗 테이블) P5-05: 지정판매소별 판매현황 (판매소별 수량/금액) P5-06: 홈택스 세금계산서 엑셀 내보내기 P5-08: 반품/파기 현황 (기간별 조회) P5-10: LOT 수불 조회 (LOT 번호 검색) P5-11: 기타 입출고 (등록 + 재고 연동) CT-05: CRUD 로깅 (activity_log 테이블 + audit_helper) CT-06: 대시보드 실 데이터 (발주/판매/재고/불출 통계) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,118 @@
|
||||
<div class="border border-gray-300 p-4">
|
||||
<p class="text-sm text-gray-600">관리자 메인 화면입니다. 상단 메뉴에서 기능을 선택하세요.</p>
|
||||
<?php $s = $stats ?? []; ?>
|
||||
|
||||
<?php if (!($lgIdx ?? null)): ?>
|
||||
<div class="border border-orange-300 bg-orange-50 p-4 text-sm text-orange-700">
|
||||
작업할 지자체가 선택되지 않았습니다. 상단에서 지자체를 선택해 주세요.
|
||||
</div>
|
||||
<?php else: ?>
|
||||
|
||||
<!-- 통계 카드 -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-3 mb-4">
|
||||
<div class="border border-gray-300 p-4 bg-white">
|
||||
<div class="text-xs text-gray-500">총 발주 건수</div>
|
||||
<div class="text-2xl font-bold text-blue-700"><?= number_format($s['order_count'] ?? 0) ?></div>
|
||||
<div class="text-xs text-gray-400 mt-1">금액: <?= number_format($s['order_amount'] ?? 0) ?>원</div>
|
||||
</div>
|
||||
<div class="border border-gray-300 p-4 bg-white">
|
||||
<div class="text-xs text-gray-500">총 판매 건수</div>
|
||||
<div class="text-2xl font-bold text-green-700"><?= number_format($s['sale_count'] ?? 0) ?></div>
|
||||
<div class="text-xs text-gray-400 mt-1">금액: <?= number_format($s['sale_amount'] ?? 0) ?>원</div>
|
||||
</div>
|
||||
<div class="border border-gray-300 p-4 bg-white">
|
||||
<div class="text-xs text-gray-500">재고 품목 수</div>
|
||||
<div class="text-2xl font-bold text-purple-700"><?= number_format($s['inventory_count'] ?? 0) ?></div>
|
||||
<div class="text-xs text-gray-400 mt-1">현재 재고가 있는 봉투 품목</div>
|
||||
</div>
|
||||
<div class="border border-gray-300 p-4 bg-white">
|
||||
<div class="text-xs text-gray-500">이번 달 불출</div>
|
||||
<div class="text-2xl font-bold text-orange-700"><?= number_format($s['issue_count_month'] ?? 0) ?></div>
|
||||
<div class="text-xs text-gray-400 mt-1"><?= date('Y년 n월') ?> 무료용 불출</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 최근 내역 -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<!-- 최근 발주 -->
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-1">
|
||||
<h3 class="text-sm font-bold text-gray-700">최근 발주 5건</h3>
|
||||
<a href="<?= base_url('admin/bag-orders') ?>" class="text-xs text-blue-600 hover:underline">전체보기</a>
|
||||
</div>
|
||||
<div class="border border-gray-300 overflow-auto">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>LOT번호</th>
|
||||
<th>발주일</th>
|
||||
<th>상태</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-center">
|
||||
<?php
|
||||
$statusMap = ['normal' => '정상', 'cancelled' => '취소', 'deleted' => '삭제'];
|
||||
foreach (($s['recent_orders'] ?? []) as $order):
|
||||
?>
|
||||
<tr>
|
||||
<td class="font-mono text-sm">
|
||||
<a href="<?= base_url('admin/bag-orders/detail/' . (int) $order->bo_idx) ?>" class="text-blue-600 hover:underline"><?= esc($order->bo_lot_no) ?></a>
|
||||
</td>
|
||||
<td><?= esc($order->bo_order_date) ?></td>
|
||||
<td>
|
||||
<?php
|
||||
$stClass = match($order->bo_status) {
|
||||
'cancelled' => 'text-red-600',
|
||||
'deleted' => 'text-gray-400',
|
||||
default => 'text-green-600',
|
||||
};
|
||||
?>
|
||||
<span class="<?= $stClass ?>"><?= esc($statusMap[$order->bo_status] ?? $order->bo_status) ?></span>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($s['recent_orders'])): ?>
|
||||
<tr><td colspan="3" class="text-gray-400 py-3">발주 내역이 없습니다.</td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 최근 판매 -->
|
||||
<div>
|
||||
<div class="flex items-center justify-between mb-1">
|
||||
<h3 class="text-sm font-bold text-gray-700">최근 판매 5건</h3>
|
||||
<a href="<?= base_url('admin/bag-sales') ?>" class="text-xs text-blue-600 hover:underline">전체보기</a>
|
||||
</div>
|
||||
<div class="border border-gray-300 overflow-auto">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>판매소</th>
|
||||
<th>봉투명</th>
|
||||
<th>수량</th>
|
||||
<th>금액</th>
|
||||
<th>구분</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-right">
|
||||
<?php
|
||||
$typeMap = ['sale' => '판매', 'return' => '반품', 'cancel' => '취소'];
|
||||
foreach (($s['recent_sales'] ?? []) as $sale):
|
||||
?>
|
||||
<tr>
|
||||
<td class="text-left pl-2"><?= esc($sale->bs_ds_name) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($sale->bs_bag_name) ?></td>
|
||||
<td><?= number_format(abs((int) $sale->bs_qty)) ?></td>
|
||||
<td><?= number_format((int) $sale->bs_amount) ?></td>
|
||||
<td class="text-center"><?= esc($typeMap[$sale->bs_type] ?? $sale->bs_type) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($s['recent_sales'])): ?>
|
||||
<tr><td colspan="5" class="text-center text-gray-400 py-3">판매 내역이 없습니다.</td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -9,6 +9,29 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- P2-15: 다조건 검색 -->
|
||||
<section class="p-2 bg-white border-b border-gray-200 no-print">
|
||||
<form method="GET" action="<?= base_url('admin/designated-shops') ?>" class="flex flex-wrap items-center gap-2">
|
||||
<label class="text-sm text-gray-600">상호명</label>
|
||||
<input type="text" name="ds_name" value="<?= esc($dsName ?? '') ?>" placeholder="상호명 검색" class="border border-gray-300 rounded px-2 py-1 text-sm w-40"/>
|
||||
<label class="text-sm text-gray-600">구군코드</label>
|
||||
<select name="ds_gugun_code" class="border border-gray-300 rounded px-2 py-1 text-sm">
|
||||
<option value="">전체</option>
|
||||
<?php foreach (($gugunCodes ?? []) as $gc): ?>
|
||||
<option value="<?= esc($gc->ds_gugun_code) ?>" <?= ($dsGugunCode ?? '') === $gc->ds_gugun_code ? 'selected' : '' ?>><?= esc($gc->ds_gugun_code) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<label class="text-sm text-gray-600">상태</label>
|
||||
<select name="ds_state" class="border border-gray-300 rounded px-2 py-1 text-sm">
|
||||
<option value="">전체</option>
|
||||
<option value="1" <?= ($dsState ?? '') === '1' ? 'selected' : '' ?>>정상</option>
|
||||
<option value="2" <?= ($dsState ?? '') === '2' ? 'selected' : '' ?>>폐업</option>
|
||||
<option value="3" <?= ($dsState ?? '') === '3' ? 'selected' : '' ?>>직권해지</option>
|
||||
</select>
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm text-sm">조회</button>
|
||||
<a href="<?= base_url('admin/designated-shops') ?>" class="border border-gray-300 text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">초기화</a>
|
||||
</form>
|
||||
</section>
|
||||
<div class="border border-gray-300 overflow-auto mt-2">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
|
||||
56
app/Views/admin/designated_shop/map.php
Normal file
56
app/Views/admin/designated_shop/map.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?= view('components/print_header', ['printTitle' => '지정판매소 지도']) ?>
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<span class="text-sm font-bold text-gray-700">지정판매소 지도</span>
|
||||
<a href="<?= base_url('admin/designated-shops') ?>" class="border border-gray-300 text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">목록으로</a>
|
||||
</div>
|
||||
</section>
|
||||
<div id="kakao-map" class="w-full border border-gray-300 mt-2" style="height:600px;"></div>
|
||||
<div class="mt-2 text-sm text-gray-500">총 <?= count($shops) ?>개 판매소 표시</div>
|
||||
|
||||
<script src="//dapi.kakao.com/v2/maps/sdk.js?appkey=KAKAO_APP_KEY&libraries=services"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var mapContainer = document.getElementById('kakao-map');
|
||||
if (typeof kakao === 'undefined' || typeof kakao.maps === 'undefined') {
|
||||
mapContainer.innerHTML = '<div class="flex items-center justify-center h-full text-gray-400">카카오맵 API 키를 설정해 주세요.</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
var mapOption = {
|
||||
center: new kakao.maps.LatLng(35.8714, 128.6014), // 대구 기본 좌표
|
||||
level: 8
|
||||
};
|
||||
var map = new kakao.maps.Map(mapContainer, mapOption);
|
||||
|
||||
var geocoder = new kakao.maps.services.Geocoder();
|
||||
var shops = <?= json_encode(array_map(function($s) {
|
||||
return ['name' => $s->ds_name, 'addr' => $s->ds_addr ?? '', 'rep' => $s->ds_rep_name ?? '', 'tel' => $s->ds_tel ?? ''];
|
||||
}, $shops), JSON_UNESCAPED_UNICODE) ?>;
|
||||
|
||||
var bounds = new kakao.maps.LatLngBounds();
|
||||
var markerCount = 0;
|
||||
|
||||
shops.forEach(function(shop) {
|
||||
if (!shop.addr) return;
|
||||
geocoder.addressSearch(shop.addr, function(result, status) {
|
||||
if (status === kakao.maps.services.Status.OK) {
|
||||
var coords = new kakao.maps.LatLng(result[0].y, result[0].x);
|
||||
var marker = new kakao.maps.Marker({ map: map, position: coords });
|
||||
var infoContent = '<div style="padding:5px;font-size:12px;min-width:150px;">' +
|
||||
'<strong>' + shop.name + '</strong><br/>' +
|
||||
(shop.rep ? '대표: ' + shop.rep + '<br/>' : '') +
|
||||
(shop.tel ? 'TEL: ' + shop.tel + '<br/>' : '') +
|
||||
'<span style="color:#888;">' + shop.addr + '</span></div>';
|
||||
var infowindow = new kakao.maps.InfoWindow({ content: infoContent });
|
||||
kakao.maps.event.addListener(marker, 'click', function() {
|
||||
infowindow.open(map, marker);
|
||||
});
|
||||
bounds.extend(coords);
|
||||
markerCount++;
|
||||
if (markerCount > 0) map.setBounds(bounds);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
80
app/Views/admin/designated_shop/status.php
Normal file
80
app/Views/admin/designated_shop/status.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?= view('components/print_header', ['printTitle' => '지정판매소 현황']) ?>
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<span class="text-sm font-bold text-gray-700">지정판매소 현황 (신규/취소)</span>
|
||||
<div class="flex items-center gap-2">
|
||||
<button onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button>
|
||||
<a href="<?= base_url('admin/designated-shops') ?>" class="border border-gray-300 text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">목록으로</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 전체 현황 요약 -->
|
||||
<div class="flex gap-4 mt-2 mb-2">
|
||||
<div class="border border-gray-300 p-3 flex-1 text-center">
|
||||
<div class="text-sm text-gray-500">활성 판매소</div>
|
||||
<div class="text-2xl font-bold text-green-600"><?= number_format($totalActive) ?></div>
|
||||
</div>
|
||||
<div class="border border-gray-300 p-3 flex-1 text-center">
|
||||
<div class="text-sm text-gray-500">비활성/취소 판매소</div>
|
||||
<div class="text-2xl font-bold text-red-600"><?= number_format($totalInactive) ?></div>
|
||||
</div>
|
||||
<div class="border border-gray-300 p-3 flex-1 text-center">
|
||||
<div class="text-sm text-gray-500">전체</div>
|
||||
<div class="text-2xl font-bold text-gray-700"><?= number_format($totalActive + $totalInactive) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-2">
|
||||
<!-- 연도별 신규등록 -->
|
||||
<div>
|
||||
<h3 class="text-sm font-bold text-gray-700 mb-1">연도별 신규등록 건수</h3>
|
||||
<div class="border border-gray-300 overflow-auto">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>연도</th>
|
||||
<th>신규등록 건수</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-right">
|
||||
<?php foreach ($newByYear as $row): ?>
|
||||
<tr>
|
||||
<td class="text-center"><?= esc($row->yr) ?>년</td>
|
||||
<td><?= number_format((int) $row->cnt) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($newByYear)): ?>
|
||||
<tr><td colspan="2" class="text-center text-gray-400 py-4">데이터가 없습니다.</td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 연도별 취소/비활성 -->
|
||||
<div>
|
||||
<h3 class="text-sm font-bold text-gray-700 mb-1">연도별 취소/비활성 건수</h3>
|
||||
<div class="border border-gray-300 overflow-auto">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>연도</th>
|
||||
<th>취소/비활성 건수</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-right">
|
||||
<?php foreach ($cancelByYear as $row): ?>
|
||||
<tr>
|
||||
<td class="text-center"><?= esc($row->yr) ?>년</td>
|
||||
<td><?= number_format((int) $row->cnt) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($cancelByYear)): ?>
|
||||
<tr><td colspan="2" class="text-center text-gray-400 py-4">데이터가 없습니다.</td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
99
app/Views/admin/sales_report/lot_flow.php
Normal file
99
app/Views/admin/sales_report/lot_flow.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?= view('components/print_header', ['printTitle' => 'LOT 수불 조회']) ?>
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<span class="text-sm font-bold text-gray-700">LOT 수불 조회</span>
|
||||
<button onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button>
|
||||
</div>
|
||||
</section>
|
||||
<section class="p-2 bg-white border-b border-gray-200">
|
||||
<form method="GET" action="<?= base_url('admin/reports/lot-flow') ?>" class="flex flex-wrap items-center gap-2">
|
||||
<label class="text-sm text-gray-600">LOT 번호</label>
|
||||
<input type="text" name="lot_no" value="<?= esc($lotNo ?? '') ?>" placeholder="LOT-YYYYMMDD-XXXXXX" class="border border-gray-300 rounded px-2 py-1 text-sm w-64"/>
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm text-sm">조회</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<?php if ($lotNo !== '' && $order): ?>
|
||||
<!-- 발주 정보 -->
|
||||
<div class="border border-gray-300 p-3 mt-2 bg-gray-50">
|
||||
<h3 class="text-sm font-bold text-gray-700 mb-2">발주 정보</h3>
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-2 text-sm">
|
||||
<div><span class="text-gray-500">LOT번호:</span> <span class="font-mono"><?= esc($order->bo_lot_no) ?></span></div>
|
||||
<div><span class="text-gray-500">발주일:</span> <?= esc($order->bo_order_date) ?></div>
|
||||
<div><span class="text-gray-500">상태:</span>
|
||||
<?php $statusMap = ['normal' => '정상', 'cancelled' => '취소', 'deleted' => '삭제']; ?>
|
||||
<?= esc($statusMap[$order->bo_status] ?? $order->bo_status) ?>
|
||||
</div>
|
||||
<div><span class="text-gray-500">등록일:</span> <?= esc($order->bo_regdate) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 발주 품목 -->
|
||||
<h3 class="text-sm font-bold text-gray-700 mt-3 mb-1">발주 품목</h3>
|
||||
<div class="border border-gray-300 overflow-auto">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>봉투코드</th>
|
||||
<th>봉투명</th>
|
||||
<th>발주수량(박스)</th>
|
||||
<th>발주수량(매)</th>
|
||||
<th>단가</th>
|
||||
<th>금액</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-right">
|
||||
<?php foreach ($items as $item): ?>
|
||||
<tr>
|
||||
<td class="text-center font-mono"><?= esc($item->boi_bag_code) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($item->boi_bag_name) ?></td>
|
||||
<td><?= number_format((int) $item->boi_qty_box) ?></td>
|
||||
<td><?= number_format((int) $item->boi_qty_sheet) ?></td>
|
||||
<td><?= number_format((int) $item->boi_unit_price) ?></td>
|
||||
<td><?= number_format((int) $item->boi_amount) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($items)): ?>
|
||||
<tr><td colspan="6" class="text-center text-gray-400 py-4">품목이 없습니다.</td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 입고 내역 -->
|
||||
<h3 class="text-sm font-bold text-gray-700 mt-3 mb-1">입고 내역</h3>
|
||||
<div class="border border-gray-300 overflow-auto">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>입고일</th>
|
||||
<th>봉투코드</th>
|
||||
<th>봉투명</th>
|
||||
<th>입고수량(박스)</th>
|
||||
<th>입고수량(매)</th>
|
||||
<th>납품자</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-right">
|
||||
<?php foreach ($receivings as $recv): ?>
|
||||
<tr>
|
||||
<td class="text-center"><?= esc($recv->br_receive_date) ?></td>
|
||||
<td class="text-center font-mono"><?= esc($recv->br_bag_code) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($recv->br_bag_name) ?></td>
|
||||
<td><?= number_format((int) $recv->br_qty_box) ?></td>
|
||||
<td><?= number_format((int) $recv->br_qty_sheet) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($recv->br_sender_name ?? '') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($receivings)): ?>
|
||||
<tr><td colspan="6" class="text-center text-gray-400 py-4">입고 내역이 없습니다.</td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php elseif ($lotNo !== '' && !$order): ?>
|
||||
<div class="border border-gray-300 p-4 mt-2 text-center text-gray-400">해당 LOT 번호의 발주를 찾을 수 없습니다.</div>
|
||||
<?php else: ?>
|
||||
<div class="border border-gray-300 p-4 mt-2 text-center text-gray-400">LOT 번호를 입력하고 조회해 주세요.</div>
|
||||
<?php endif; ?>
|
||||
84
app/Views/admin/sales_report/misc_flow.php
Normal file
84
app/Views/admin/sales_report/misc_flow.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?= view('components/print_header', ['printTitle' => '기타 입출고']) ?>
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<span class="text-sm font-bold text-gray-700">기타 입출고</span>
|
||||
<button onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<?php if (!($tableExists ?? false)): ?>
|
||||
<div class="border border-orange-300 bg-orange-50 p-3 mt-2 text-sm text-orange-700">
|
||||
bag_misc_flow 테이블이 생성되지 않았습니다. <code>writable/database/bag_misc_flow_tables.sql</code>을 실행해 주세요.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- 등록 폼 -->
|
||||
<section class="p-2 bg-white border-b border-gray-200 no-print">
|
||||
<form method="POST" action="<?= base_url('admin/reports/misc-flow') ?>" class="flex flex-wrap items-center gap-2">
|
||||
<?= csrf_field() ?>
|
||||
<label class="text-sm text-gray-600">구분</label>
|
||||
<select name="bmf_type" class="border border-gray-300 rounded px-2 py-1 text-sm" required>
|
||||
<option value="in">입고</option>
|
||||
<option value="out">출고</option>
|
||||
</select>
|
||||
<label class="text-sm text-gray-600">봉투</label>
|
||||
<select name="bmf_bag_code" class="border border-gray-300 rounded px-2 py-1 text-sm" required>
|
||||
<option value="">선택</option>
|
||||
<?php foreach ($bagCodes as $bc): ?>
|
||||
<option value="<?= esc($bc->cd_code) ?>"><?= esc($bc->cd_code . ' - ' . $bc->cd_name) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<label class="text-sm text-gray-600">수량</label>
|
||||
<input type="number" name="bmf_qty" min="1" class="border border-gray-300 rounded px-2 py-1 text-sm w-24" required/>
|
||||
<label class="text-sm text-gray-600">일자</label>
|
||||
<input type="date" name="bmf_date" value="<?= date('Y-m-d') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm" required/>
|
||||
<label class="text-sm text-gray-600">사유</label>
|
||||
<input type="text" name="bmf_reason" placeholder="입출고 사유" class="border border-gray-300 rounded px-2 py-1 text-sm w-48" required/>
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm text-sm">등록</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<!-- 조회 필터 -->
|
||||
<section class="p-2 bg-white border-b border-gray-200 no-print">
|
||||
<form method="GET" action="<?= base_url('admin/reports/misc-flow') ?>" class="flex flex-wrap items-center gap-2">
|
||||
<label class="text-sm text-gray-600">시작일</label>
|
||||
<input type="date" name="start_date" value="<?= esc($startDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
|
||||
<label class="text-sm text-gray-600">~</label>
|
||||
<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 rounded-sm text-sm">조회</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<div class="border border-gray-300 overflow-auto mt-2">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>번호</th>
|
||||
<th>구분</th>
|
||||
<th>일자</th>
|
||||
<th>봉투코드</th>
|
||||
<th>봉투명</th>
|
||||
<th>수량</th>
|
||||
<th>사유</th>
|
||||
<th>등록일</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-right">
|
||||
<?php foreach ($result as $row): ?>
|
||||
<tr>
|
||||
<td class="text-center"><?= (int) $row->bmf_idx ?></td>
|
||||
<td class="text-center"><?= $row->bmf_type === 'in' ? '<span class="text-blue-600">입고</span>' : '<span class="text-red-600">출고</span>' ?></td>
|
||||
<td class="text-center"><?= esc($row->bmf_date) ?></td>
|
||||
<td class="text-center font-mono"><?= esc($row->bmf_bag_code) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->bmf_bag_name) ?></td>
|
||||
<td><?= number_format((int) $row->bmf_qty) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->bmf_reason) ?></td>
|
||||
<td class="text-center"><?= esc($row->bmf_regdate) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($result)): ?>
|
||||
<tr><td colspan="8" class="text-center text-gray-400 py-4">조회된 데이터가 없습니다.</td></tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
59
app/Views/admin/sales_report/returns.php
Normal file
59
app/Views/admin/sales_report/returns.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?= view('components/print_header', ['printTitle' => '반품/파기 현황']) ?>
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<span class="text-sm font-bold text-gray-700">반품/파기 현황</span>
|
||||
<button onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button>
|
||||
</div>
|
||||
</section>
|
||||
<section class="p-2 bg-white border-b border-gray-200">
|
||||
<form method="GET" action="<?= base_url('admin/reports/returns') ?>" class="flex flex-wrap items-center gap-2">
|
||||
<label class="text-sm text-gray-600">시작일</label>
|
||||
<input type="date" name="start_date" value="<?= esc($startDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
|
||||
<label class="text-sm text-gray-600">~</label>
|
||||
<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 rounded-sm text-sm">조회</button>
|
||||
</form>
|
||||
</section>
|
||||
<div class="border border-gray-300 overflow-auto mt-2">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>일자</th>
|
||||
<th>판매소</th>
|
||||
<th>봉투코드</th>
|
||||
<th>봉투명</th>
|
||||
<th>구분</th>
|
||||
<th>수량</th>
|
||||
<th>금액</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-right">
|
||||
<?php
|
||||
$totalQty = 0; $totalAmt = 0;
|
||||
$typeMap = ['return' => '반품', 'cancel' => '취소/파기'];
|
||||
foreach ($result as $row):
|
||||
$totalQty += (int) $row->qty;
|
||||
$totalAmt += (int) $row->amount;
|
||||
?>
|
||||
<tr>
|
||||
<td class="text-center"><?= esc($row->bs_sale_date) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->bs_ds_name) ?></td>
|
||||
<td class="text-center font-mono"><?= esc($row->bs_bag_code) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->bs_bag_name) ?></td>
|
||||
<td class="text-center"><?= esc($typeMap[$row->bs_type] ?? $row->bs_type) ?></td>
|
||||
<td><?= number_format((int) $row->qty) ?></td>
|
||||
<td><?= number_format((int) $row->amount) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($result)): ?>
|
||||
<tr><td colspan="7" class="text-center text-gray-400 py-4">조회된 데이터가 없습니다.</td></tr>
|
||||
<?php else: ?>
|
||||
<tr class="font-bold bg-gray-100">
|
||||
<td colspan="5" class="text-center">합계</td>
|
||||
<td><?= number_format($totalQty) ?></td>
|
||||
<td><?= number_format($totalAmt) ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
64
app/Views/admin/sales_report/shop_sales.php
Normal file
64
app/Views/admin/sales_report/shop_sales.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?= view('components/print_header', ['printTitle' => '지정판매소별 판매현황']) ?>
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<span class="text-sm font-bold text-gray-700">지정판매소별 판매현황</span>
|
||||
<button onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button>
|
||||
</div>
|
||||
</section>
|
||||
<section class="p-2 bg-white border-b border-gray-200">
|
||||
<form method="GET" action="<?= base_url('admin/reports/shop-sales') ?>" class="flex flex-wrap items-center gap-2">
|
||||
<label class="text-sm text-gray-600">시작일</label>
|
||||
<input type="date" name="start_date" value="<?= esc($startDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
|
||||
<label class="text-sm text-gray-600">~</label>
|
||||
<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 rounded-sm text-sm">조회</button>
|
||||
</form>
|
||||
</section>
|
||||
<div class="border border-gray-300 overflow-auto mt-2">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>판매소명</th>
|
||||
<th>판매수량</th>
|
||||
<th>판매금액</th>
|
||||
<th>반품수량</th>
|
||||
<th>반품금액</th>
|
||||
<th>순판매수량</th>
|
||||
<th>순판매금액</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-right">
|
||||
<?php
|
||||
$totSaleQty = 0; $totSaleAmt = 0; $totRetQty = 0; $totRetAmt = 0;
|
||||
foreach ($result as $row):
|
||||
$totSaleQty += (int) $row->sale_qty;
|
||||
$totSaleAmt += (int) $row->sale_amount;
|
||||
$totRetQty += (int) $row->return_qty;
|
||||
$totRetAmt += (int) $row->return_amount;
|
||||
?>
|
||||
<tr>
|
||||
<td class="text-left pl-2"><?= esc($row->bs_ds_name) ?></td>
|
||||
<td><?= number_format((int) $row->sale_qty) ?></td>
|
||||
<td><?= number_format((int) $row->sale_amount) ?></td>
|
||||
<td><?= number_format((int) $row->return_qty) ?></td>
|
||||
<td><?= number_format((int) $row->return_amount) ?></td>
|
||||
<td class="font-bold"><?= number_format((int) $row->sale_qty - (int) $row->return_qty) ?></td>
|
||||
<td class="font-bold"><?= number_format((int) $row->sale_amount - (int) $row->return_amount) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($result)): ?>
|
||||
<tr><td colspan="7" class="text-center text-gray-400 py-4">조회된 데이터가 없습니다.</td></tr>
|
||||
<?php else: ?>
|
||||
<tr class="font-bold bg-gray-100">
|
||||
<td class="text-center">합계</td>
|
||||
<td><?= number_format($totSaleQty) ?></td>
|
||||
<td><?= number_format($totSaleAmt) ?></td>
|
||||
<td><?= number_format($totRetQty) ?></td>
|
||||
<td><?= number_format($totRetAmt) ?></td>
|
||||
<td><?= number_format($totSaleQty - $totRetQty) ?></td>
|
||||
<td><?= number_format($totSaleAmt - $totRetAmt) ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
62
app/Views/admin/sales_report/yearly_sales.php
Normal file
62
app/Views/admin/sales_report/yearly_sales.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?= view('components/print_header', ['printTitle' => '년 판매 현황']) ?>
|
||||
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel">
|
||||
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
||||
<span class="text-sm font-bold text-gray-700">년 판매 현황 (월별)</span>
|
||||
<button onclick="window.print()" class="no-print border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50 transition">인쇄</button>
|
||||
</div>
|
||||
</section>
|
||||
<section class="p-2 bg-white border-b border-gray-200">
|
||||
<form method="GET" action="<?= base_url('admin/reports/yearly-sales') ?>" class="flex flex-wrap items-center gap-2">
|
||||
<label class="text-sm text-gray-600">연도</label>
|
||||
<select name="year" class="border border-gray-300 rounded px-2 py-1 text-sm">
|
||||
<?php for ($y = (int) date('Y'); $y >= 2020; $y--): ?>
|
||||
<option value="<?= $y ?>" <?= (int)($year ?? date('Y')) === $y ? 'selected' : '' ?>><?= $y ?>년</option>
|
||||
<?php endfor; ?>
|
||||
</select>
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm text-sm">조회</button>
|
||||
</form>
|
||||
</section>
|
||||
<div class="border border-gray-300 overflow-auto mt-2">
|
||||
<table class="w-full data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>봉투코드</th>
|
||||
<th>봉투명</th>
|
||||
<th>1월</th><th>2월</th><th>3월</th><th>4월</th><th>5월</th><th>6월</th>
|
||||
<th>7월</th><th>8월</th><th>9월</th><th>10월</th><th>11월</th><th>12월</th>
|
||||
<th class="bg-gray-100">합계</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-right">
|
||||
<?php
|
||||
$grandTotal = array_fill(1, 13, 0); // 1~12 + 13=total
|
||||
foreach ($result as $row):
|
||||
?>
|
||||
<tr>
|
||||
<td class="text-center font-mono"><?= esc($row->bs_bag_code) ?></td>
|
||||
<td class="text-left pl-2"><?= esc($row->bs_bag_name) ?></td>
|
||||
<?php for ($m = 1; $m <= 12; $m++):
|
||||
$key = 'm' . sprintf('%02d', $m);
|
||||
$val = (int) $row->$key;
|
||||
$grandTotal[$m] += $val;
|
||||
?>
|
||||
<td><?= $val > 0 ? number_format($val) : '-' ?></td>
|
||||
<?php endfor; ?>
|
||||
<?php $grandTotal[13] += (int) $row->total; ?>
|
||||
<td class="font-bold bg-gray-50"><?= number_format((int) $row->total) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($result)): ?>
|
||||
<tr><td colspan="15" class="text-center text-gray-400 py-4">조회된 데이터가 없습니다.</td></tr>
|
||||
<?php else: ?>
|
||||
<tr class="font-bold bg-gray-100">
|
||||
<td colspan="2" class="text-center">합계</td>
|
||||
<?php for ($m = 1; $m <= 12; $m++): ?>
|
||||
<td><?= $grandTotal[$m] > 0 ? number_format($grandTotal[$m]) : '-' ?></td>
|
||||
<?php endfor; ?>
|
||||
<td class="bg-gray-200"><?= number_format($grandTotal[13]) ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
Reference in New Issue
Block a user