2026-06-01 16:15:15 +09:00
|
|
|
<?php
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
/** @var int $year */
|
|
|
|
|
/** @var string $gugunCode */
|
|
|
|
|
/** @var int $saIdx */
|
|
|
|
|
/** @var list<object> $agencies */
|
|
|
|
|
/** @var list<array{code: string, name: string}> $gugunOptions */
|
|
|
|
|
/** @var list<array{id: string, label: string}> $colSpec */
|
|
|
|
|
/** @var list<array{name: string, lines: list<array<string,mixed>>}> $itemBlocks */
|
|
|
|
|
/** @var array{name: string, lines: list<array<string,mixed>>} $footerBlock */
|
|
|
|
|
/** @var bool $hasBsFee */
|
|
|
|
|
/** @var string $lgName */
|
|
|
|
|
/** @var string $gugunLabel */
|
|
|
|
|
/** @var string $agencyLabel */
|
|
|
|
|
/** @var list<string> $printExtraLines */
|
|
|
|
|
/** @var bool $hasYearlyData */
|
|
|
|
|
|
|
|
|
|
$yMax = (int) date('Y') + 1;
|
|
|
|
|
$yMin = 2020;
|
|
|
|
|
|
|
|
|
|
$exportParams = array_merge([
|
|
|
|
|
'year' => (string) ($year ?? date('Y')),
|
|
|
|
|
'export' => '1',
|
|
|
|
|
], array_filter([
|
|
|
|
|
'gugun_code' => (string) ($gugunCode ?? ''),
|
|
|
|
|
'sa_idx' => (int) ($saIdx ?? 0) > 0 ? (string) (int) ($saIdx ?? 0) : '',
|
|
|
|
|
], static fn ($v): bool => $v !== '' && $v !== null && $v !== 0));
|
|
|
|
|
$excelUrl = mgmt_url('reports/yearly-sales?' . http_build_query($exportParams));
|
|
|
|
|
|
|
|
|
|
$colCount = 2 + count($colSpec ?? []);
|
|
|
|
|
$nMetricCols = max(1, count($colSpec ?? []));
|
|
|
|
|
$metricColPct = round(86 / $nMetricCols, 4);
|
|
|
|
|
|
|
|
|
|
$fmtMeasureCell = static function (array $cell, string $measureKey, bool $hasBsFee): string {
|
|
|
|
|
if ($measureKey === 'fee' && ! $hasBsFee) {
|
|
|
|
|
return '—';
|
|
|
|
|
}
|
|
|
|
|
if ($measureKey === 'qty') {
|
|
|
|
|
return number_format((int) ($cell['qty'] ?? 0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return number_format((int) round((float) ($cell[$measureKey] ?? 0)));
|
|
|
|
|
};
|
|
|
|
|
?>
|
|
|
|
|
<?= view('components/print_header', [
|
|
|
|
|
'printTitle' => ((int) ($year ?? date('Y'))) . '년 판매 현황',
|
|
|
|
|
'printExtraLines' => $printExtraLines ?? [],
|
|
|
|
|
]) ?>
|
|
|
|
|
|
|
|
|
|
<section class="border-b border-gray-300 p-2 shrink-0 bg-control-panel no-print">
|
2026-03-26 16:50:28 +09:00
|
|
|
<div class="flex flex-wrap items-center justify-between gap-y-2">
|
2026-06-01 16:15:15 +09:00
|
|
|
<span class="text-sm font-bold text-gray-700">년 판매 현황</span>
|
|
|
|
|
<div class="flex flex-wrap gap-2">
|
|
|
|
|
<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 transition">인쇄</button>
|
|
|
|
|
<a href="<?= esc($excelUrl, 'attr') ?>" class="inline-flex items-center border border-green-600 text-green-700 px-3 py-1 rounded-sm text-sm hover:bg-green-50 transition">엑셀저장</a>
|
|
|
|
|
</div>
|
2026-03-26 16:50:28 +09:00
|
|
|
</div>
|
|
|
|
|
</section>
|
2026-06-01 16:15:15 +09:00
|
|
|
|
|
|
|
|
<section class="p-3 bg-white border-b border-gray-200 no-print">
|
|
|
|
|
<form method="GET" action="<?= mgmt_url('reports/yearly-sales') ?>" class="flex flex-wrap items-end gap-3 text-sm">
|
|
|
|
|
<div>
|
|
|
|
|
<label class="block text-gray-600 mb-0.5">조회 년도</label>
|
|
|
|
|
<select name="year" class="border border-gray-300 rounded px-2 py-1 text-sm min-w-[7rem]">
|
|
|
|
|
<?php for ($y = $yMax; $y >= $yMin; $y--): ?>
|
|
|
|
|
<option value="<?= $y ?>" <?= (int) ($year ?? date('Y')) === $y ? 'selected' : '' ?>><?= $y ?>년</option>
|
|
|
|
|
<?php endfor; ?>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label class="block text-gray-600 mb-0.5">구·군</label>
|
|
|
|
|
<select name="gugun_code" class="border border-gray-300 rounded px-2 py-1 text-sm min-w-[10rem] max-w-[18rem]">
|
|
|
|
|
<option value="">전체</option>
|
|
|
|
|
<?php foreach ($gugunOptions ?? [] as $g): ?>
|
|
|
|
|
<?php $gc = (string) ($g['code'] ?? ''); ?>
|
|
|
|
|
<option value="<?= esc($gc, 'attr') ?>" <?= ($gugunCode ?? '') === $gc ? 'selected' : '' ?>><?= esc((string) ($g['name'] ?? $gc)) ?></option>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<label class="block text-gray-600 mb-0.5">대행소</label>
|
|
|
|
|
<select name="sa_idx" class="border border-gray-300 rounded px-2 py-1 text-sm min-w-[12rem] max-w-[20rem]">
|
|
|
|
|
<option value="0">전체</option>
|
|
|
|
|
<?php foreach ($agencies ?? [] as $agency): ?>
|
|
|
|
|
<?php $aid = (int) ($agency->sa_idx ?? 0); ?>
|
|
|
|
|
<option value="<?= esc((string) $aid) ?>" <?= (int) ($saIdx ?? 0) === $aid ? 'selected' : '' ?>>
|
|
|
|
|
<?= esc(trim((string) ($agency->sa_name ?? ''))) ?>
|
|
|
|
|
</option>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
<button type="submit" class="bg-btn-search text-white px-4 py-1.5 rounded-sm text-sm">조회</button>
|
2026-03-26 16:50:28 +09:00
|
|
|
</form>
|
|
|
|
|
</section>
|
2026-06-01 16:15:15 +09:00
|
|
|
|
|
|
|
|
<section class="p-3 bg-white yearly-sales-report-section">
|
|
|
|
|
<style>
|
|
|
|
|
/* 화면: 가로 스크롤. 인쇄: 가로 용지 + 작은 글자 + 셀 줄바꿈으로 한 페이지에 맞춤 */
|
|
|
|
|
@media print {
|
|
|
|
|
@page {
|
|
|
|
|
size: A4 landscape;
|
|
|
|
|
margin: 5mm 6mm;
|
|
|
|
|
}
|
|
|
|
|
.yearly-sales-report-section {
|
|
|
|
|
padding: 0 !important;
|
|
|
|
|
}
|
|
|
|
|
.yearly-sales-scroll-wrap {
|
|
|
|
|
overflow: visible !important;
|
|
|
|
|
border: 1px solid #333 !important;
|
|
|
|
|
max-width: none !important;
|
|
|
|
|
}
|
|
|
|
|
#yearly-sales-table {
|
|
|
|
|
font-size: 7pt !important;
|
|
|
|
|
width: 100% !important;
|
|
|
|
|
max-width: 100% !important;
|
|
|
|
|
table-layout: fixed !important;
|
|
|
|
|
}
|
|
|
|
|
#yearly-sales-table th,
|
|
|
|
|
#yearly-sales-table td {
|
|
|
|
|
min-width: 0 !important;
|
|
|
|
|
max-width: none !important;
|
|
|
|
|
padding: 1px 2px !important;
|
|
|
|
|
white-space: normal !important;
|
|
|
|
|
word-break: break-word;
|
|
|
|
|
overflow-wrap: anywhere;
|
|
|
|
|
line-height: 1.15;
|
|
|
|
|
vertical-align: top;
|
|
|
|
|
}
|
|
|
|
|
#yearly-sales-table th {
|
|
|
|
|
font-size: 6.5pt !important;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@media screen {
|
|
|
|
|
#yearly-sales-table td.tabular-nums {
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
<div class="mb-2 text-center no-print">
|
|
|
|
|
<h1 class="text-lg font-bold m-0"><?= (int) ($year ?? date('Y')) ?>년 판매 현황</h1>
|
|
|
|
|
<p class="text-sm text-gray-700 m-1"><?= esc(trim(($lgName ?? '') . ' · 구·군: ' . ($gugunLabel ?? '') . ' · 대행소: ' . ($agencyLabel ?? ''))) ?></p>
|
|
|
|
|
<p class="text-xs text-gray-500 m-0">(단위: 매 / 원)</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="yearly-sales-scroll-wrap border border-gray-300 overflow-x-auto">
|
|
|
|
|
<table class="w-full data-table text-xs sm:text-sm" id="yearly-sales-table">
|
|
|
|
|
<colgroup>
|
|
|
|
|
<col style="width: 9%;"/>
|
|
|
|
|
<col style="width: 5%;"/>
|
|
|
|
|
<?php foreach (($colSpec ?? []) as $_): ?>
|
|
|
|
|
<col style="width: <?= esc((string) $metricColPct, 'attr') ?>%;"/>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
</colgroup>
|
|
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th class="align-middle min-w-0 sm:min-w-[7rem] max-w-[10rem] sm:max-w-[12rem] text-left pl-2">품목</th>
|
|
|
|
|
<th class="align-middle min-w-0 sm:min-w-[4.5rem]">구분</th>
|
|
|
|
|
<?php foreach ($colSpec ?? [] as $col): ?>
|
|
|
|
|
<th class="align-middle text-center min-w-0 sm:min-w-[4.5rem] border-l border-gray-200"><?= esc((string) ($col['label'] ?? '')) ?></th>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody class="text-right">
|
|
|
|
|
<?php if (! ($hasYearlyData ?? false)): ?>
|
|
|
|
|
<tr>
|
|
|
|
|
<td colspan="<?= (int) $colCount ?>" class="text-center text-gray-400 py-6">조회된 데이터가 없습니다.</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<?php else: ?>
|
|
|
|
|
<?php foreach ($itemBlocks ?? [] as $block): ?>
|
|
|
|
|
<?php $lines = $block['lines'] ?? []; ?>
|
|
|
|
|
<?php foreach ($lines as $liIdx => $li): ?>
|
|
|
|
|
<tr class="odd:bg-white even:bg-gray-50/80">
|
|
|
|
|
<?php if ($liIdx === 0): ?>
|
|
|
|
|
<td rowspan="4" class="text-left align-top pl-2 pt-1 font-medium border-r border-gray-200"><?= esc((string) ($block['name'] ?? '')) ?></td>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
<td class="text-left pl-2 border-r border-gray-100"><?= esc((string) ($li['measure'] ?? '')) ?></td>
|
|
|
|
|
<?php
|
|
|
|
|
$cells = (array) ($li['cells'] ?? []);
|
|
|
|
|
$mk = (string) ($li['measureKey'] ?? '');
|
|
|
|
|
?>
|
|
|
|
|
<?php foreach ($colSpec ?? [] as $col): ?>
|
|
|
|
|
<?php $cid = (string) ($col['id'] ?? ''); ?>
|
|
|
|
|
<?php $cell = (array) ($cells[$cid] ?? ['qty' => 0, 'amt' => 0.0, 'fee' => 0.0, 'levy' => 0.0]); ?>
|
|
|
|
|
<td class="border-l border-gray-100 tabular-nums"><?= $fmtMeasureCell($cell, $mk, (bool) ($hasBsFee ?? false)) ?></td>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
</tr>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
|
|
|
|
|
<?php $fLines = $footerBlock['lines'] ?? []; ?>
|
|
|
|
|
<?php foreach ($fLines as $fIdx => $li): ?>
|
|
|
|
|
<tr class="bg-amber-50 font-semibold border-t-2 border-amber-200">
|
|
|
|
|
<?php if ($fIdx === 0): ?>
|
|
|
|
|
<td rowspan="4" class="text-center align-middle text-amber-900 border-r border-amber-200"><?= esc((string) ($footerBlock['name'] ?? '전체 합계')) ?></td>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
<td class="text-left pl-2 border-r border-amber-100"><?= esc((string) ($li['measure'] ?? '')) ?></td>
|
|
|
|
|
<?php
|
|
|
|
|
$cells = (array) ($li['cells'] ?? []);
|
|
|
|
|
$mk = (string) ($li['measureKey'] ?? '');
|
|
|
|
|
?>
|
|
|
|
|
<?php foreach ($colSpec ?? [] as $col): ?>
|
|
|
|
|
<?php $cid = (string) ($col['id'] ?? ''); ?>
|
|
|
|
|
<?php $cell = (array) ($cells[$cid] ?? ['qty' => 0, 'amt' => 0.0, 'fee' => 0.0, 'levy' => 0.0]); ?>
|
|
|
|
|
<td class="border-l border-amber-100 tabular-nums"><?= $fmtMeasureCell($cell, $mk, (bool) ($hasBsFee ?? false)) ?></td>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
</tr>
|
|
|
|
|
<?php endforeach; ?>
|
|
|
|
|
<?php endif; ?>
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
</section>
|
|
|
|
|
<script>
|
|
|
|
|
(function () {
|
|
|
|
|
const year = <?= json_encode((int) ($year ?? (int) date('Y')), JSON_THROW_ON_ERROR) ?>;
|
|
|
|
|
let savedTitle = document.title;
|
|
|
|
|
function stamp() {
|
|
|
|
|
const d = new Date();
|
|
|
|
|
const p = (n) => String(n).padStart(2, '0');
|
|
|
|
|
return d.getFullYear() + p(d.getMonth() + 1) + p(d.getDate()) + '_' + p(d.getHours()) + p(d.getMinutes()) + p(d.getSeconds());
|
|
|
|
|
}
|
|
|
|
|
window.addEventListener('beforeprint', function () {
|
|
|
|
|
savedTitle = document.title;
|
|
|
|
|
document.title = '년판매현황_' + year + '_' + stamp();
|
|
|
|
|
});
|
|
|
|
|
window.addEventListener('afterprint', function () {
|
|
|
|
|
document.title = savedTitle;
|
|
|
|
|
});
|
|
|
|
|
})();
|
|
|
|
|
</script>
|