통계 분석(전년대비·월별·계절별), 수급계획·LOT 수불, 지정판매소·실사·메뉴 링크 등을 포함한다. Co-authored-by: Cursor <cursoragent@cursor.com>
323 lines
11 KiB
PHP
323 lines
11 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
/** @var string $startDate */
|
|
/** @var string $endDate */
|
|
/** @var string $writeDate */
|
|
/** @var bool $searched */
|
|
/** @var list<string> $headers */
|
|
/** @var list<list<string>> $displayRows */
|
|
/** @var int $totalCount */
|
|
/** @var float $totalSupplyAmount */
|
|
/** @var float $totalTaxAmount */
|
|
/** @var int $missingBizCount */
|
|
/** @var string $lgName */
|
|
/** @var list<string> $printExtraLines */
|
|
/** @var list<array{label: string, sheet_name: string, cols: list<int>}> $hometaxPrintPages */
|
|
/** @var list<int> $hometaxColMinPx */
|
|
|
|
helper('admin');
|
|
|
|
$baseParams = [
|
|
'start_date' => $startDate ?? '',
|
|
'end_date' => $endDate ?? '',
|
|
'write_date' => $writeDate ?? '',
|
|
];
|
|
$searchParams = array_merge($baseParams, ['search' => '1']);
|
|
$exportParams = array_merge($searchParams, ['export' => '1']);
|
|
|
|
$searchUrl = mgmt_url('reports/hometax-export?' . http_build_query($searchParams));
|
|
$excelUrl = mgmt_url('reports/hometax-export?' . http_build_query($exportParams));
|
|
|
|
$totalGrand = (float) ($totalSupplyAmount ?? 0) + (float) ($totalTaxAmount ?? 0);
|
|
$colCount = max(1, count($headers ?? []));
|
|
|
|
/** 홈택스 28열 — 주소·상호·이메일 등 텍스트 열을 넓게 (합계 100%) */
|
|
$hometaxColWidths = [
|
|
'4.5%', '4%', '4%', '6%', '3%', '6.5%', '3.5%', '11%', '3%', '3%', '5.5%',
|
|
'6%', '3%', '6.5%', '3.5%', '11%', '3%', '3%', '5.5%',
|
|
'4%', '4%', '3%', '5.5%', '3%', '3%', '3.5%', '3.5%', '3.5%',
|
|
];
|
|
$hometaxColMinPx = $hometaxColMinPx ?? [];
|
|
$hometaxPrintPages = $hometaxPrintPages ?? [];
|
|
|
|
$hometaxWrapColIdx = [7, 15, 5, 6, 13, 14, 10, 18, 22, 27];
|
|
$hometaxNumColIdx = [19, 20, 24, 25, 26, 27];
|
|
|
|
$hometaxNormalizeColWidths = static function (array $colIndices) use ($hometaxColWidths): array {
|
|
$sum = 0.0;
|
|
foreach ($colIndices as $ci) {
|
|
$sum += (float) str_replace('%', '', (string) ($hometaxColWidths[$ci] ?? '3'));
|
|
}
|
|
$normalized = [];
|
|
foreach ($colIndices as $ci) {
|
|
$pct = (float) str_replace('%', '', (string) ($hometaxColWidths[$ci] ?? '3'));
|
|
$normalized[$ci] = ($sum > 0 ? round($pct / $sum * 100, 2) : round(100 / max(1, count($colIndices)), 2)) . '%';
|
|
}
|
|
|
|
return $normalized;
|
|
};
|
|
|
|
$hometaxCellClass = static function (int $ci) use ($hometaxWrapColIdx, $hometaxNumColIdx): string {
|
|
$class = 'text-left px-1 py-1';
|
|
if (in_array($ci, $hometaxWrapColIdx, true)) {
|
|
$class .= ' ht-wrap';
|
|
}
|
|
if (in_array($ci, $hometaxNumColIdx, true)) {
|
|
$class .= ' ht-num';
|
|
}
|
|
|
|
return $class;
|
|
};
|
|
|
|
/**
|
|
* @param list<int> $colIndices
|
|
*/
|
|
$hometaxRenderTable = static function (
|
|
array $colIndices,
|
|
string $tableExtraClass,
|
|
string $tableId,
|
|
bool $forPrint
|
|
) use (
|
|
$headers,
|
|
$displayRows,
|
|
$searched,
|
|
$colCount,
|
|
$hometaxColWidths,
|
|
$hometaxColMinPx,
|
|
$hometaxCellClass,
|
|
$hometaxNormalizeColWidths
|
|
): void {
|
|
$sliceCount = count($colIndices);
|
|
$widthsForSet = $hometaxNormalizeColWidths($colIndices);
|
|
?>
|
|
<table
|
|
class="w-full data-table text-xs <?= esc($tableExtraClass, 'attr') ?>"
|
|
id="<?= esc($tableId, 'attr') ?>"
|
|
<?= $forPrint ? 'data-hometax-print="1"' : '' ?>
|
|
>
|
|
<colgroup>
|
|
<?php foreach ($colIndices as $ci):
|
|
$wPct = $widthsForSet[$ci] ?? (string) round(100 / max(1, $sliceCount), 2) . '%';
|
|
$wPx = (int) ($hometaxColMinPx[$ci] ?? 56);
|
|
?>
|
|
<col style="width: <?= esc($wPct, 'attr') ?>;<?= $forPrint ? '' : ' min-width: ' . $wPx . 'px' ?>"/>
|
|
<?php endforeach; ?>
|
|
</colgroup>
|
|
<thead>
|
|
<tr>
|
|
<?php foreach ($colIndices as $ci): ?>
|
|
<th class="<?= esc($hometaxCellClass($ci), 'attr') ?>"><?= esc((string) ($headers[$ci] ?? '')) ?></th>
|
|
<?php endforeach; ?>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="text-right">
|
|
<?php if (! ($searched ?? true)): ?>
|
|
<tr>
|
|
<td colspan="<?= (int) $sliceCount ?>" class="text-center text-gray-500 py-8">조회를 건너뛴 상태입니다. <strong>조회</strong>를 눌러 주세요.</td>
|
|
</tr>
|
|
<?php elseif (($displayRows ?? []) === []): ?>
|
|
<tr>
|
|
<td colspan="<?= (int) $sliceCount ?>" class="text-center text-gray-500 py-8">조회된 판매 내역이 없습니다.</td>
|
|
</tr>
|
|
<?php else: ?>
|
|
<?php foreach ($displayRows as $row): ?>
|
|
<tr>
|
|
<?php foreach ($colIndices as $ci):
|
|
$tdClass = 'tabular-nums text-left px-1 py-0.5 border-t border-gray-100 ' . $hometaxCellClass($ci);
|
|
?>
|
|
<td class="<?= esc(trim($tdClass), 'attr') ?>"><?= esc((string) ($row[$ci] ?? '')) ?></td>
|
|
<?php endforeach; ?>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
<?php
|
|
};
|
|
?>
|
|
<?= view('components/print_header', [
|
|
'printTitle' => '홈택스 처리',
|
|
'printExtraLines' => $printExtraLines ?? [],
|
|
]) ?>
|
|
|
|
<section class="border-b border-gray-300 p-3 shrink-0 bg-control-panel no-print">
|
|
<div class="flex flex-wrap items-center justify-between gap-2 mb-2">
|
|
<h1 class="text-base font-bold text-gray-800">홈택스 처리</h1>
|
|
<div class="flex flex-wrap gap-2 items-center">
|
|
<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>
|
|
</div>
|
|
|
|
<form method="get" action="<?= esc(mgmt_url('reports/hometax-export'), 'attr') ?>" id="hometax-process-form" class="flex flex-wrap items-end gap-3 text-sm">
|
|
<input type="hidden" name="search" value="1"/>
|
|
<div>
|
|
<label class="block text-gray-600 mb-0.5">판매일자</label>
|
|
<div class="flex items-center gap-1">
|
|
<input type="date" name="start_date" value="<?= esc($startDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
|
|
<span class="text-gray-500">~</span>
|
|
<input type="date" name="end_date" value="<?= esc($endDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label class="block text-gray-600 mb-0.5">작성일자</label>
|
|
<input type="date" name="write_date" value="<?= esc($writeDate ?? '') ?>" class="border border-gray-300 rounded px-2 py-1 text-sm"/>
|
|
</div>
|
|
<div class="pt-5">
|
|
<button type="submit" class="border border-blue-600 bg-blue-50 text-blue-800 px-4 py-1 rounded-sm text-sm font-medium hover:bg-blue-100 transition">조회</button>
|
|
</div>
|
|
</form>
|
|
</section>
|
|
|
|
<section class="p-3 bg-white border-b border-gray-200 hometax-report-section">
|
|
<style>
|
|
.hometax-print-only { display: none; }
|
|
@media print {
|
|
@page {
|
|
size: A4 landscape;
|
|
margin: 4mm 5mm;
|
|
}
|
|
.hometax-report-section {
|
|
padding: 0 !important;
|
|
border: none !important;
|
|
}
|
|
.hometax-screen-only {
|
|
display: none !important;
|
|
}
|
|
.hometax-print-only {
|
|
display: block !important;
|
|
}
|
|
.hometax-print-page {
|
|
page-break-after: always;
|
|
break-after: page;
|
|
}
|
|
.hometax-print-page:last-child {
|
|
page-break-after: auto;
|
|
break-after: auto;
|
|
}
|
|
.hometax-print-page-label {
|
|
font-size: 8pt;
|
|
font-weight: 700;
|
|
margin: 0 0 4px;
|
|
}
|
|
.hometax-print-scroll {
|
|
overflow: visible !important;
|
|
border: 1px solid #333 !important;
|
|
}
|
|
.hometax-print-table {
|
|
font-size: 8pt !important;
|
|
width: 100% !important;
|
|
table-layout: fixed !important;
|
|
}
|
|
.hometax-print-table th,
|
|
.hometax-print-table td {
|
|
padding: 3px 5px !important;
|
|
white-space: normal !important;
|
|
word-break: break-word;
|
|
overflow-wrap: break-word;
|
|
line-height: 1.3;
|
|
vertical-align: top;
|
|
}
|
|
.hometax-print-table th {
|
|
font-size: 7.5pt !important;
|
|
font-weight: 600;
|
|
}
|
|
.hometax-print-table .ht-num {
|
|
white-space: nowrap !important;
|
|
word-break: normal !important;
|
|
}
|
|
.hometax-print-table thead {
|
|
display: table-header-group;
|
|
}
|
|
.hometax-print-table tr {
|
|
page-break-inside: avoid;
|
|
break-inside: avoid;
|
|
}
|
|
}
|
|
@media screen {
|
|
#hometax-result-table {
|
|
width: max(100%, 4200px);
|
|
min-width: 4200px;
|
|
table-layout: fixed;
|
|
}
|
|
#hometax-result-table th,
|
|
#hometax-result-table td {
|
|
white-space: nowrap;
|
|
padding: 4px 6px !important;
|
|
}
|
|
#hometax-result-table .ht-wrap {
|
|
white-space: normal;
|
|
word-break: break-word;
|
|
max-width: 220px;
|
|
}
|
|
}
|
|
</style>
|
|
<div class="text-sm font-semibold text-gray-700 mb-2 no-print">조회결과</div>
|
|
<div class="hometax-screen-only hometax-scroll-wrap overflow-x-auto border border-gray-300" style="max-width: 100%;">
|
|
<?php
|
|
$hometaxRenderTable(
|
|
range(0, max(0, $colCount - 1)),
|
|
'',
|
|
'hometax-result-table',
|
|
false
|
|
);
|
|
?>
|
|
</div>
|
|
|
|
<div class="hometax-print-only" aria-hidden="true">
|
|
<?php foreach ($hometaxPrintPages as $ppi => $page):
|
|
$pageCols = $page['cols'];
|
|
if ($pageCols === []) {
|
|
continue;
|
|
}
|
|
?>
|
|
<section class="hometax-print-page">
|
|
<p class="hometax-print-page-label"><?= esc((string) ($page['label'] ?? '')) ?></p>
|
|
<div class="hometax-print-scroll">
|
|
<?php
|
|
$hometaxRenderTable(
|
|
$pageCols,
|
|
'hometax-print-table',
|
|
'hometax-print-table-' . (int) $ppi,
|
|
true
|
|
);
|
|
?>
|
|
</div>
|
|
</section>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<div class="mt-4 flex flex-wrap gap-6 text-sm border-t border-gray-200 pt-3 no-print">
|
|
<div><span class="text-gray-600">총 건수</span> <strong class="tabular-nums"><?= (int) ($totalCount ?? 0) ?></strong> 건</div>
|
|
<div><span class="text-gray-600">총 금액</span> <strong class="tabular-nums"><?= esc(number_format((int) round($totalGrand))) ?></strong> 원 <span class="text-gray-400 text-xs">(공급가액+세액)</span></div>
|
|
<div><span class="text-gray-600">사업자등록번호 없음</span> <strong class="tabular-nums text-amber-800"><?= (int) ($missingBizCount ?? 0) ?></strong> 건</div>
|
|
</div>
|
|
|
|
<div class="mt-2 text-xs text-gray-500 no-print print:hidden">
|
|
인쇄·엑셀저장은 동일하게 2쪽 열 구성입니다(1쪽: 공급자·공급받는자, 2쪽: 금액·품목). 요약·결재란은 인쇄용 헤더에 포함됩니다.
|
|
</div>
|
|
</section>
|
|
|
|
<style media="print">
|
|
.no-print { display: none !important; }
|
|
</style>
|
|
|
|
<script>
|
|
(function () {
|
|
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 = '홈택스처리_' + stamp();
|
|
});
|
|
window.addEventListener('afterprint', function () {
|
|
document.title = savedTitle;
|
|
});
|
|
})();
|
|
</script>
|