사용자 매뉴얼·번호알기·gov-portal 대시보드와 메뉴 동선·수불 리포트를 보강한다.
- 사용자 매뉴얼: league/commonmark 기반 bag/manual(로그인 전용), ManualRenderer + Config\Manual manifest, 콘텐츠 8종, E2E - 번호알기(봉투번호확인): bag/number-lookup, BagNumberLookup, E2E - gov-portal 대시보드 시안(기본/strip)·기본코드관리 화면 - 메뉴 관리: 등록·수정 후 메뉴 화면 유지, 수정 버튼 클릭 시 상단 스크롤 - 수불/분석 리포트(LOT 수불·반품/파기·수급계획·추이) 표시 보강 - .gitignore: docs/ → /docs/ 앵커링(최상위 개발문서만 제외, app/Docs는 추적) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -52,7 +52,7 @@ if ($bagName !== '' || $bagCode !== '') {
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm">조회</button>
|
||||
</form>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
팩·박스·낱장 바코드 또는 LOT 번호(보조: <code class="text-xs">lot_no</code> 파라미터)로 조회합니다.
|
||||
낱장 번호 조회 시 <strong>해당 장(바코드)의 판매·반품</strong>만 표시합니다. 팩·박스·LOT 조회는 해당 단위 이력입니다.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
@@ -128,6 +128,8 @@ if ($bagName !== '' || $bagCode !== '') {
|
||||
<div><dt class="text-gray-500 inline">LOT</dt>
|
||||
<dd class="font-mono text-xs break-all"><?= esc($lotLabel) ?></dd></div>
|
||||
<?php endif; ?>
|
||||
<div><dt class="text-gray-500 inline">봉투번호</dt>
|
||||
<dd class="font-mono text-xs break-all"><?= esc($barcode !== '' ? $barcode : '-') ?></dd></div>
|
||||
<?php if ($unit !== ''): ?>
|
||||
<div><dt class="text-gray-500 inline">조회단위</dt>
|
||||
<dd><?= esc($unit) ?></dd></div>
|
||||
|
||||
@@ -4,7 +4,15 @@ $endDate = (string) ($endDate ?? date('Y-m-d'));
|
||||
$ioType = (string) ($ioType ?? 'out');
|
||||
$result = is_array($result ?? null) ? $result : (array) ($result ?? []);
|
||||
$queried = (bool) ($queried ?? false);
|
||||
$exportQuery = (string) ($exportQuery ?? 'search=1');
|
||||
$exportParams = $queried ? array_filter([
|
||||
'search' => '1',
|
||||
'start_date' => $startDate,
|
||||
'end_date' => $endDate,
|
||||
'io_type' => $ioType,
|
||||
], static fn ($v) => $v !== null && $v !== '') : [];
|
||||
$excelUrl = $exportParams !== []
|
||||
? mgmt_url('reports/returns/export') . '?' . http_build_query($exportParams)
|
||||
: '';
|
||||
|
||||
$fmtKrDate = static function (string $ymd): string {
|
||||
$ts = strtotime($ymd);
|
||||
@@ -22,9 +30,10 @@ $printExtraLines = [
|
||||
|
||||
$typeLabel = static function (string $bsType): string {
|
||||
return match ($bsType) {
|
||||
'return' => '반품',
|
||||
'cancel' => '파기',
|
||||
default => $bsType,
|
||||
'return' => '반품',
|
||||
'dispose' => '파기',
|
||||
'cancel' => '파기',
|
||||
default => $bsType,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -42,6 +51,11 @@ $totalQty = 0;
|
||||
foreach ($result as $row) {
|
||||
$totalQty += (int) ($row->qty ?? 0);
|
||||
}
|
||||
|
||||
$tipPage = "지정판매소 반품·물류 입고분 파기 내역을 기간·입출고 구분으로 조회합니다.\n"
|
||||
. "· 출고: 지정판매소 반품 등록 화면에서 처리된 반품\n"
|
||||
. "· 입고: 물류 창고 입고분 파기 처리 내역\n"
|
||||
. "조회 후 표·엑셀·인쇄에 반영됩니다.";
|
||||
?>
|
||||
<?= view('components/print_header', [
|
||||
'printTitle' => '반품 / 파기 현황',
|
||||
@@ -50,10 +64,13 @@ foreach ($result as $row) {
|
||||
|
||||
<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>
|
||||
<span class="text-sm font-bold text-gray-700 inline-flex items-center gap-1">
|
||||
반품/파기 현황
|
||||
<?= view('components/field_tooltip', ['text' => $tipPage, 'placement' => 'below']) ?>
|
||||
</span>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<?php if ($queried && $exportQuery !== ''): ?>
|
||||
<a href="<?= mgmt_url('reports/returns/export?' . esc($exportQuery, 'attr')) ?>"
|
||||
<?php if ($excelUrl !== ''): ?>
|
||||
<a href="<?= esc($excelUrl, 'attr') ?>" target="_blank" rel="noopener noreferrer"
|
||||
class="bg-green-700 text-white px-3 py-1 rounded-sm text-sm hover:bg-green-800">엑셀저장</a>
|
||||
<?php else: ?>
|
||||
<span class="bg-gray-300 text-gray-600 px-3 py-1 rounded-sm text-sm cursor-not-allowed" title="조회 후 이용">엑셀저장</span>
|
||||
@@ -89,9 +106,6 @@ foreach ($result as $row) {
|
||||
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm">조회</button>
|
||||
</form>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
<strong>입고</strong> = 지정판매소 반품(재고 복귀), <strong>출고</strong> = 판매 취소·파기 처리. 조회 후 표·엑셀·인쇄에 반영됩니다.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<?php if (! $queried): ?>
|
||||
@@ -138,6 +152,26 @@ foreach ($result as $row) {
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.field-tip { position: relative; display: inline-flex; vertical-align: middle; }
|
||||
.field-tip-btn {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
width: 14px; height: 14px; font-size: 10px; font-weight: 700; line-height: 1;
|
||||
color: #6b7280; background: #f3f4f6; border: 1px solid #d1d5db; border-radius: 50%;
|
||||
cursor: help; user-select: none;
|
||||
}
|
||||
.field-tip-btn:hover, .field-tip-btn:focus { color: #1d4ed8; border-color: #93c5fd; background: #eff6ff; outline: none; }
|
||||
.field-tip-panel {
|
||||
position: absolute; z-index: 60; left: 50%; transform: translateX(-50%);
|
||||
bottom: calc(100% + 6px); width: max-content; max-width: 280px;
|
||||
padding: 0.35rem 0.5rem; border-radius: 4px;
|
||||
background: #1f2937; color: #f9fafb; font-size: 11px; font-weight: 500; line-height: 1.35;
|
||||
text-align: left; white-space: pre-line; box-shadow: 0 2px 8px rgba(0,0,0,.15);
|
||||
opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .12s, visibility .12s;
|
||||
}
|
||||
.field-tip--below .field-tip-panel { bottom: auto; top: calc(100% + 6px); }
|
||||
.field-tip:hover .field-tip-panel,
|
||||
.field-tip:focus-within .field-tip-panel { opacity: 1; visibility: visible; }
|
||||
|
||||
@media print {
|
||||
.no-print { display: none !important; }
|
||||
}
|
||||
|
||||
@@ -14,6 +14,17 @@ $fmtKrRef = static function (string $ymd): string {
|
||||
return $ts ? date('Y.m.d', $ts) . ' 현재' : $ymd;
|
||||
};
|
||||
|
||||
/** 툴팁: 의미 + 계산(간단) */
|
||||
$tipPage = "봉투 품목별로 재고가 며칠 버티는지, 언제·얼마나 발주할지 보는 수급·발주 계획표입니다.";
|
||||
$tipLead = "의미: 발주 후 입고까지 걸리는 제작기일(일). 재고 소진 전에 발주하려는 여유.\n계산: 발주예정일 = 기준일 + 소진일수 − 보유일수";
|
||||
$tipStock = "의미: 표에 넣을 현재고·총재고 범위.\n기존=바코드 미등록(수기), 바코드=등록 품목.";
|
||||
$tipSales = "의미: 소진일수에 쓸 판매 속도 범위.\n최근 12개월 순판매(또는 바코드 판매) 월평균.";
|
||||
$tipTotal = "의미: 지금·곧 쓸 수 있는 재고 합계.\n계산: 현재고 + 입고예정량";
|
||||
$tipMonth = "의미: 요즘 한 달 판매 규모(평균).\n최근 12개월 월평균 판매량.";
|
||||
$tipDepl = "의미: 이 판매 속도면 재고가 며칠 남는지.\n계산: (총재고 ÷ 월판매량) × 30";
|
||||
$tipSched = "의미: 발주를 넣기 좋은 날(제작기일 반영).\n계산: 기준일 + 소진일수 − 보유일수. 기한 지남=빨간색·긴급";
|
||||
$tipOrder = "의미: 그 시점에 맞춰 제안하는 추가 발주 장수.\n촉박하거나 발주예정일이 지난 품목만 표시.";
|
||||
|
||||
$printExtraLines = [
|
||||
$fmtKrRef($refDate),
|
||||
'적정재고 보유일수(제작기일): ' . $leadDays . '일',
|
||||
@@ -28,7 +39,10 @@ $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>
|
||||
<span class="text-sm font-bold text-gray-700 inline-flex items-center gap-1">
|
||||
쓰레기봉투 수급 계획
|
||||
<?= view('components/field_tooltip', ['text' => $tipPage, 'placement' => 'below']) ?>
|
||||
</span>
|
||||
<div class="flex flex-wrap items-center 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">인쇄</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>
|
||||
@@ -47,14 +61,19 @@ $printExtraLines = [
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<label class="font-bold text-gray-700 whitespace-nowrap">적정재고 보유일수</label>
|
||||
<label class="font-bold text-gray-700 whitespace-nowrap inline-flex items-center gap-0.5">
|
||||
적정재고 보유일수
|
||||
<?= view('components/field_tooltip', ['text' => $tipLead]) ?>
|
||||
</label>
|
||||
<input type="number" name="lead_days" value="<?= (int) $leadDays ?>" min="1" max="365"
|
||||
class="border border-gray-300 rounded px-2 py-1 w-20 text-right" title="제작기일(발주예정일 산정)"/>
|
||||
<span class="text-blue-700 text-xs">※ 제작기일 <?= (int) $leadDays ?>일 기준으로 발주예정일 산정</span>
|
||||
class="border border-gray-300 rounded px-2 py-1 w-20 text-right"/>
|
||||
</div>
|
||||
|
||||
<fieldset class="flex flex-wrap items-center gap-2 border-0 p-0 m-0">
|
||||
<legend class="font-bold text-gray-700 whitespace-nowrap mr-1">현재고 선택 옵션</legend>
|
||||
<legend class="font-bold text-gray-700 whitespace-nowrap mr-1 inline-flex items-center gap-0.5">
|
||||
현재고 선택 옵션
|
||||
<?= view('components/field_tooltip', ['text' => $tipStock]) ?>
|
||||
</legend>
|
||||
<?php foreach (['all' => 'ALL', 'legacy' => '기존 봉투', 'barcode' => '바코드 봉투'] as $val => $lab): ?>
|
||||
<label class="inline-flex items-center gap-1">
|
||||
<input type="radio" name="stock_scope" value="<?= esc($val) ?>" <?= $stockScope === $val ? 'checked' : '' ?>/>
|
||||
@@ -64,7 +83,10 @@ $printExtraLines = [
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="flex flex-wrap items-center gap-2 border-0 p-0 m-0">
|
||||
<legend class="font-bold text-gray-700 whitespace-nowrap mr-1">월 평균판매량 선택 옵션</legend>
|
||||
<legend class="font-bold text-gray-700 whitespace-nowrap mr-1 inline-flex items-center gap-0.5">
|
||||
월 평균판매량 선택 옵션
|
||||
<?= view('components/field_tooltip', ['text' => $tipSales]) ?>
|
||||
</legend>
|
||||
<?php foreach (['all' => 'ALL', 'legacy' => '기존 봉투', 'barcode' => '바코드 봉투'] as $val => $lab): ?>
|
||||
<label class="inline-flex items-center gap-1">
|
||||
<input type="radio" name="sales_scope" value="<?= esc($val) ?>" <?= $salesScope === $val ? 'checked' : '' ?>/>
|
||||
@@ -75,12 +97,6 @@ $printExtraLines = [
|
||||
|
||||
<button type="submit" class="bg-btn-search text-white px-4 py-1 rounded-sm">조회</button>
|
||||
</form>
|
||||
<p class="text-xs text-gray-500 mt-2 max-w-4xl">
|
||||
<strong>기존 봉투</strong> = 입고 팩 바코드 미등록 품목(수기 재고),
|
||||
<strong>바코드 봉투</strong> = <code class="text-xs">bag_receiving_pack_code</code> 등록 품목.
|
||||
월판매량은 최근 12개월 순판매(또는 바코드 판매 스캔)의 월평균입니다.
|
||||
소진일수 = (총재고÷월판매량)×30, 발주예정일 = 기준일+소진일수−보유일수, 과거일은 빨간색.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<?php if (! $queried): ?>
|
||||
@@ -105,11 +121,21 @@ $printExtraLines = [
|
||||
<th class="sp-col-num text-right">발주시재고</th>
|
||||
<th class="sp-col-num text-right border-l">현재고</th>
|
||||
<th class="sp-col-num text-right">입고예정량</th>
|
||||
<th class="sp-col-num text-right">총재고</th>
|
||||
<th class="sp-col-num text-right">월판매량</th>
|
||||
<th class="sp-col-num text-right">소진일수(일)</th>
|
||||
<th class="sp-col-date text-center border-l">발주예정일</th>
|
||||
<th class="sp-col-num text-right">발주수량</th>
|
||||
<th class="sp-col-num text-right">
|
||||
<span class="inline-flex items-center justify-end gap-0.5 w-full">총재고<?= view('components/field_tooltip', ['text' => $tipTotal, 'placement' => 'below']) ?></span>
|
||||
</th>
|
||||
<th class="sp-col-num text-right">
|
||||
<span class="inline-flex items-center justify-end gap-0.5 w-full">월판매량<?= view('components/field_tooltip', ['text' => $tipMonth, 'placement' => 'below']) ?></span>
|
||||
</th>
|
||||
<th class="sp-col-num text-right">
|
||||
<span class="inline-flex items-center justify-end gap-0.5 w-full">소진일수(일)<?= view('components/field_tooltip', ['text' => $tipDepl, 'placement' => 'below']) ?></span>
|
||||
</th>
|
||||
<th class="sp-col-date text-center border-l">
|
||||
<span class="inline-flex items-center justify-center gap-0.5">발주예정일<?= view('components/field_tooltip', ['text' => $tipSched, 'placement' => 'below']) ?></span>
|
||||
</th>
|
||||
<th class="sp-col-num text-right">
|
||||
<span class="inline-flex items-center justify-end gap-0.5 w-full">발주수량<?= view('components/field_tooltip', ['text' => $tipOrder, 'placement' => 'below']) ?></span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -154,7 +180,28 @@ $printExtraLines = [
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.supply-plan-table thead th { font-size: 0.75rem; line-height: 1.2; padding: 0.35rem 0.25rem; }
|
||||
.field-tip { position: relative; display: inline-flex; vertical-align: middle; }
|
||||
.field-tip-btn {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
width: 14px; height: 14px; font-size: 10px; font-weight: 700; line-height: 1;
|
||||
color: #6b7280; background: #f3f4f6; border: 1px solid #d1d5db; border-radius: 50%;
|
||||
cursor: help; user-select: none;
|
||||
}
|
||||
.field-tip-btn:hover, .field-tip-btn:focus { color: #1d4ed8; border-color: #93c5fd; background: #eff6ff; outline: none; }
|
||||
.field-tip-panel {
|
||||
position: absolute; z-index: 60; left: 50%; transform: translateX(-50%);
|
||||
bottom: calc(100% + 6px); width: max-content; max-width: 280px;
|
||||
padding: 0.35rem 0.5rem; border-radius: 4px;
|
||||
background: #1f2937; color: #f9fafb; font-size: 11px; font-weight: 500; line-height: 1.35;
|
||||
text-align: left; white-space: pre-line; box-shadow: 0 2px 8px rgba(0,0,0,.15);
|
||||
opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .12s, visibility .12s;
|
||||
}
|
||||
.field-tip--below .field-tip-panel { bottom: auto; top: calc(100% + 6px); }
|
||||
.field-tip:hover .field-tip-panel,
|
||||
.field-tip:focus-within .field-tip-panel { opacity: 1; visibility: visible; }
|
||||
|
||||
.supply-plan-table thead th { font-size: 0.75rem; line-height: 1.2; padding: 0.35rem 0.25rem; overflow: visible; }
|
||||
.supply-plan-table thead th .field-tip-panel { max-width: 260px; }
|
||||
.supply-plan-table tbody td { padding: 0.25rem 0.35rem; }
|
||||
|
||||
@media screen {
|
||||
|
||||
Reference in New Issue
Block a user