Files
jongryangje/app/Views/bag/flow.php
taekyoungc 0f1d414f37 사이트·관리자 봉투 물류 기능(수불·통계·레포트·재고·발주)과 DB·메뉴·E2E를 운영 반영한다.
통계 분석(전년대비·월별·계절별), 수급계획·LOT 수불, 지정판매소·실사·메뉴 링크 등을 포함한다.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 16:15:15 +09:00

289 lines
11 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
$startDate = (string) ($startDate ?? date('Y-m-01'));
$endDate = (string) ($endDate ?? date('Y-m-d'));
$aggMode = (string) ($aggMode ?? 'period');
$bagCode = (string) ($bagCode ?? '');
$bagKind = (string) ($bagKind ?? '');
$saIdx = (int) ($saIdx ?? 0);
$rows = is_array($rows ?? null) ? $rows : [];
$bagProducts = is_array($bagProducts ?? null) ? $bagProducts : [];
$bagKindOptions = is_array($bagKindOptions ?? null) ? $bagKindOptions : [];
$agencies = is_array($agencies ?? null) ? $agencies : [];
$exportQuery = (string) ($exportQuery ?? 'search=1');
$queried = (bool) ($queried ?? false);
$fmt = static fn ($n): string => number_format((int) $n);
$printExtraLines = [];
if ($queried) {
$aggLabel = $aggMode === 'daily' ? '일자별' : '기간별';
$printExtraLines[] = '조회기간: ' . $startDate . ' ~ ' . $endDate . ' (' . $aggLabel . ')';
}
?>
<div class="flow-print-sheet">
<?= view('components/print_header', [
'printTitle' => '기간별 봉투 수불 현황',
'printExtraLines' => $printExtraLines,
]) ?>
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel no-print">
<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 flex-wrap items-center gap-2">
<a href="<?= base_url('bag/flow/export?' . esc($exportQuery, 'attr')) ?>" class="bg-green-700 text-white px-3 py-1 rounded-sm text-sm hover:bg-green-800">엑셀저장</a>
<button type="button" onclick="window.print()" class="border border-btn-print-border text-gray-600 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">인쇄</button>
<a href="<?= base_url('dashboard') ?>" class="border border-gray-400 text-gray-700 px-3 py-1 rounded-sm text-sm hover:bg-gray-50">종료</a>
</div>
</div>
</section>
<section class="p-2 bg-white border-b border-gray-200 no-print">
<form method="get" action="<?= base_url('bag/flow') ?>" class="flex flex-wrap items-end gap-x-3 gap-y-2 text-sm">
<input type="hidden" name="search" value="1"/>
<div class="flex flex-wrap items-center gap-2">
<label class="font-bold text-gray-700 whitespace-nowrap">조회기간</label>
<input type="date" name="start_date" value="<?= esc($startDate) ?>" class="border border-gray-300 rounded px-2 py-1" required/>
<span>~</span>
<input type="date" name="end_date" value="<?= esc($endDate) ?>" class="border border-gray-300 rounded px-2 py-1" required/>
</div>
<div class="flex flex-wrap items-center gap-2">
<label class="font-bold text-gray-700 whitespace-nowrap">봉투형식</label>
<select name="bag_code" class="border border-gray-300 rounded px-2 py-1 min-w-[11rem]">
<option value="">전체 봉투</option>
<?php foreach ($bagProducts as $bp): ?>
<option value="<?= esc((string) $bp['code']) ?>" <?= $bagCode === (string) $bp['code'] ? 'selected' : '' ?>>
<?= esc((string) $bp['code']) ?> — <?= esc((string) $bp['name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="flex flex-wrap items-center gap-2">
<label class="font-bold text-gray-700 whitespace-nowrap">봉투구분</label>
<select name="bag_kind" class="border border-gray-300 rounded px-2 py-1 min-w-[8rem]">
<option value="">전체</option>
<?php foreach ($bagKindOptions as $opt): ?>
<option value="<?= esc((string) $opt->cd_code) ?>" <?= $bagKind === (string) $opt->cd_code ? 'selected' : '' ?>>
<?= esc((string) $opt->cd_name) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="flex flex-wrap items-center gap-2">
<label class="font-bold text-gray-700 whitespace-nowrap">대행소</label>
<select name="sa_idx" class="border border-gray-300 rounded px-2 py-1 min-w-[10rem]">
<option value="0">전체</option>
<?php foreach ($agencies as $agency): ?>
<?php
$aid = (int) ($agency->sa_idx ?? 0);
$label = (string) ($agency->sa_name ?? '');
if (isset($agency->sa_kind) && (string) $agency->sa_kind !== '') {
$label = (string) $agency->sa_kind . ' — ' . $label;
}
?>
<option value="<?= $aid ?>" <?= $saIdx === $aid ? 'selected' : '' ?>><?= esc($label) ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="flex flex-wrap items-center gap-3">
<span class="font-bold text-gray-700 whitespace-nowrap">집계방식</span>
<label class="inline-flex items-center gap-1">
<input type="radio" name="agg_mode" value="daily" <?= $aggMode === 'daily' ? 'checked' : '' ?>/>
일자별
</label>
<label class="inline-flex items-center gap-1">
<input type="radio" name="agg_mode" value="period" <?= $aggMode === 'period' ? 'checked' : '' ?>/>
기간별
</label>
</div>
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm">조회</button>
<a href="<?= base_url('bag/flow') ?>" class="text-gray-500 hover:text-gray-800 px-2">초기화</a>
</form>
<p class="text-xs text-gray-500 mt-1">전일재고 = 조회 시작일 전날 기준 품목별 재고(입고·반품·기타 출고 누적). 대행소 선택 시 <strong>판매</strong>만 해당 대행소 소속 판매소 기준입니다.</p>
</section>
<?php if (! $queried): ?>
<div class="m-2 p-3 border border-blue-200 bg-blue-50 text-sm text-blue-900 no-print">
조회 조건을 설정한 뒤 <strong>조회</strong> 버튼을 눌러 주세요.
</div>
<?php endif; ?>
<?php if ($queried): ?>
<div class="p-2 overflow-auto flow-report-wrap">
<table class="w-full data-table text-sm flow-report-table">
<thead>
<tr>
<th rowspan="2" class="flow-col-date">일자</th>
<th rowspan="2" class="flow-col-item">품목</th>
<th rowspan="2" class="flow-col-num">
<span class="flow-lbl-screen">전일재고</span><span class="flow-lbl-print">전일</span>
</th>
<th colspan="4">입고</th>
<th colspan="6">출고</th>
<th rowspan="2" class="flow-col-num">잔량</th>
</tr>
<tr>
<th class="flow-col-num">입고</th>
<th class="flow-col-num">반품</th>
<th class="flow-col-num">기타</th>
<th class="flow-col-num">
<span class="flow-lbl-screen">입고계</span><span class="flow-lbl-print">입계</span>
</th>
<th class="flow-col-num">판매</th>
<th class="flow-col-num">
<span class="flow-lbl-screen">일반불출</span><span class="flow-lbl-print">일반</span>
</th>
<th class="flow-col-num">
<span class="flow-lbl-screen">무료불출</span><span class="flow-lbl-print">무료</span>
</th>
<th class="flow-col-num">반품</th>
<th class="flow-col-num">기타</th>
<th class="flow-col-num">
<span class="flow-lbl-screen">출고계</span><span class="flow-lbl-print">출계</span>
</th>
</tr>
</thead>
<tbody class="text-right">
<?php if ($rows !== []): ?>
<?php foreach ($rows as $row): ?>
<?php
$rowType = (string) ($row['row_type'] ?? 'data');
$trClass = match ($rowType) {
'subtotal', 'grand' => 'bg-amber-50 font-semibold',
default => '',
};
?>
<tr class="<?= esc($trClass) ?>">
<td class="flow-col-date text-center"><?= esc((string) ($row['date'] ?? '')) ?></td>
<td class="flow-col-item text-left pl-2"><?= esc((string) ($row['item_name'] ?? '')) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['prev_stock'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['recv_in'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['recv_return'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['recv_misc'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['recv_total'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['out_sale'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['out_issue_gen'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['out_issue_free'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['out_return'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['out_misc'] ?? 0) ?></td>
<td class="flow-col-num tabular-nums"><?= $fmt($row['out_total'] ?? 0) ?></td>
<td class="flow-col-num font-semibold tabular-nums"><?= $fmt($row['balance'] ?? 0) ?></td>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr>
<td colspan="15" class="text-center text-gray-400 py-8">조회 결과가 없습니다.</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<?php endif; ?>
</div>
<style>
.flow-lbl-print { display: none; }
@media screen {
.flow-report-wrap { overflow-x: auto; }
.flow-report-table { min-width: 1200px; }
}
@media print {
@page {
size: A4 portrait;
margin: 10mm 8mm;
}
html { font-size: 12px !important; }
.flow-print-sheet {
width: 100% !important;
max-width: 100% !important;
box-sizing: border-box;
}
.print-header,
.print-header table,
.print-header hr {
width: 100% !important;
max-width: 100% !important;
box-sizing: border-box;
}
.print-header table td[style*="width:45%"] table {
width: 160px !important;
max-width: 38% !important;
font-size: 9px !important;
}
.flow-report-wrap {
overflow: hidden !important;
padding: 0 !important;
width: 100% !important;
max-width: 100% !important;
}
.flow-report-table.data-table {
min-width: 0 !important;
width: 100% !important;
max-width: 100% !important;
table-layout: fixed !important;
font-size: 6px !important;
}
.flow-report-table.data-table th,
.flow-report-table.data-table td {
white-space: normal !important;
word-break: keep-all;
overflow-wrap: anywhere;
padding: 1px 1px !important;
line-height: 1.1;
vertical-align: middle;
}
.flow-lbl-screen { display: none !important; }
.flow-lbl-print { display: inline !important; }
/* 세로 A4: 일자 10% + 품목 14% + 수치 12열 각 6.33% ≈ 100% */
.flow-report-table .flow-col-date {
width: 10%;
font-size: 5px !important;
text-align: center;
}
.flow-report-table .flow-col-item {
width: 14%;
text-align: left;
font-size: 5px !important;
padding-top: 3px !important;
padding-bottom: 3px !important;
line-height: 1.25;
}
.flow-report-table .flow-col-num {
width: 6.33%;
white-space: nowrap !important;
font-size: 6px !important;
text-align: right;
padding-left: 0 !important;
padding-right: 1px !important;
}
.flow-report-table thead th {
font-size: 5px !important;
font-weight: 700;
padding: 1px 0 !important;
}
.flow-report-table tbody tr {
break-inside: avoid;
page-break-inside: avoid;
}
}
</style>