From a3f3e9cd64983f592d3b84495ebe7cceaf87037e Mon Sep 17 00:00:00 2001 From: taekyoungc Date: Wed, 15 Jul 2026 14:59:13 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=8B=A4=EC=82=AC=20=EC=9E=AC=EA=B3=A0?= =?UTF-8?q?=20=EB=B0=94=EC=BD=94=EB=93=9C=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=E2=80=94=20=EC=B0=BD=EA=B3=A0=EC=97=90=20=EC=9E=88=EC=96=B4?= =?UTF-8?q?=EC=95=BC=20=ED=95=98=EB=8A=94=20=EB=B2=88=ED=98=B8=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C/=EC=97=91=EC=85=80/=EC=9D=B8=EC=87=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 문의 #34: 실사 시 현재 창고에 남아있어야 하는(재고 상태) 봉투 번호(바코드) 목록을 뽑아 실물과 대조할 수 있게 신규 화면 추가. - bag/inventory/stock-barcode-list 라우트 + Bag::stockBarcodeList() - bag_receiving_pack_code 중 brpc_state='in_stock' 기준(기존 실사 스냅샷과 동일) - 품목/번호(박스·팩·시트·LOT) 필터, 인쇄, 엑셀저장(전체) - 화면 표시는 상위 2,000건 상한 + 안내 배너(합계·엑셀은 전체 기준) - 재고 관리 메뉴에 전 지자체 등록 Co-Authored-By: Claude Opus 4.8 --- app/Config/Routes.php | 1 + app/Controllers/Bag.php | 105 ++++++++++++++ .../bag/inventory_stock_barcode_list.php | 129 ++++++++++++++++++ 3 files changed, 235 insertions(+) create mode 100644 app/Views/bag/inventory_stock_barcode_list.php diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 01a1f76..e3af57e 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -38,6 +38,7 @@ $routes->get('bag/issue', 'Bag::issueLegacy'); $routes->get('bag/issue/cancel', 'Bag::issue'); $routes->get('bag/inventory', 'Bag::inventory'); $routes->get('bag/inventory/export', 'Bag::inventoryExport'); +$routes->get('bag/inventory/stock-barcode-list', 'Bag::stockBarcodeList'); // 실사 재고 바코드 리스트(창고에 있어야 하는 번호) $routes->get('bag/inventory/inspection-select', 'Bag::inspectionSelect'); $routes->get('bag/inventory/inspection-work', 'Bag::inspectionWork'); $routes->post('bag/inventory/inspection-run', 'Bag::inspectionRun'); diff --git a/app/Controllers/Bag.php b/app/Controllers/Bag.php index 440ff7e..c2517bb 100644 --- a/app/Controllers/Bag.php +++ b/app/Controllers/Bag.php @@ -1312,6 +1312,111 @@ SQL); ); } + /** + * 실사 재고 바코드 리스트 — 현재 창고에 있어야 하는(재고로 남아있는) 봉투 번호(바코드) 목록. + * bag_receiving_pack_code 중 brpc_state='in_stock' 인 팩/박스 코드를 품목·박스·팩·시트번호(시작~끝)·매수로 뽑아 + * 실사 전 대조용으로 조회/엑셀/인쇄한다. (기존 실사 스냅샷과 동일한 in_stock 기준) + */ + public function stockBarcodeList(): string|ResponseInterface|RedirectResponse + { + $lgIdx = $this->lgIdx(); + if (! $lgIdx) { + return redirect()->to(site_url('bag/inventory'))->with('error', '지자체를 선택해 주세요.'); + } + + $db = \Config\Database::connect(); + $bagCode = trim((string) ($this->request->getGet('bag_code') ?? '')); + $keyword = trim((string) ($this->request->getGet('keyword') ?? '')); + + // 품목 선택 옵션 — 현재 재고로 남아있는 품목만 + $itemOptions = $db->table('bag_receiving_pack_code') + ->select('brpc_bag_code, MAX(brpc_bag_name) AS bag_name, COUNT(*) AS pack_cnt, SUM(brpc_sheet_qty) AS sheet_qty', false) + ->where('brpc_lg_idx', $lgIdx) + ->where('brpc_state', 'in_stock') + ->where('brpc_bag_code !=', '') + ->groupBy('brpc_bag_code') + ->orderBy('brpc_bag_code', 'ASC') + ->get() + ->getResultArray(); + + $builder = $db->table('bag_receiving_pack_code') + ->select('brpc_bag_code, brpc_bag_name, brpc_lot_no, brpc_box_code, brpc_pack_code, brpc_sheet_start_code, brpc_sheet_end_code, brpc_sheet_qty') + ->where('brpc_lg_idx', $lgIdx) + ->where('brpc_state', 'in_stock'); + if ($bagCode !== '') { + $builder->where('brpc_bag_code', $bagCode); + } + if ($keyword !== '') { + $builder->groupStart() + ->like('brpc_box_code', $keyword) + ->orLike('brpc_pack_code', $keyword) + ->orLike('brpc_sheet_start_code', $keyword) + ->orLike('brpc_sheet_end_code', $keyword) + ->orLike('brpc_lot_no', $keyword) + ->groupEnd(); + } + $rows = $builder + ->orderBy('brpc_bag_code', 'ASC') + ->orderBy('brpc_box_code', 'ASC') + ->orderBy('brpc_pack_code', 'ASC') + ->get() + ->getResultArray(); + + // 합계 — 박스 수(고유), 팩 수, 총 매수 + $totalPacks = count($rows); + $totalSheets = 0; + $boxSet = []; + foreach ($rows as $r) { + $totalSheets += (int) ($r['brpc_sheet_qty'] ?? 0); + $bx = trim((string) ($r['brpc_box_code'] ?? '')); + if ($bx !== '') { + $boxSet[$bx] = true; + } + } + $totalBoxes = count($boxSet); + + // 엑셀 다운로드 — 현재 필터 그대로 + if ((string) ($this->request->getGet('export') ?? '') === '1') { + helper('export'); + $exportRows = []; + foreach ($rows as $r) { + $exportRows[] = [ + (string) ($r['brpc_bag_name'] ?? '') !== '' ? (string) $r['brpc_bag_name'] : (string) ($r['brpc_bag_code'] ?? ''), + (string) ($r['brpc_lot_no'] ?? ''), + (string) ($r['brpc_box_code'] ?? ''), + (string) ($r['brpc_pack_code'] ?? ''), + (string) ($r['brpc_sheet_start_code'] ?? ''), + (string) ($r['brpc_sheet_end_code'] ?? ''), + number_format((int) ($r['brpc_sheet_qty'] ?? 0)), + ]; + } + $exportRows[] = ['합계', '', '박스 ' . number_format($totalBoxes), '팩 ' . number_format($totalPacks), '', '', number_format($totalSheets)]; + export_xlsx( + '실사재고바코드리스트_' . date('Ymd') . '.xlsx', + '창고재고바코드', + ['품목', 'LOT', '박스코드', '팩코드', '시작번호', '끝번호', '매수'], + $exportRows + ); + } + + // 화면 표시는 성능을 위해 상위 N건까지만(합계·엑셀 저장은 항상 전체 기준) + $displayLimit = 2000; + $displayTruncated = count($rows) > $displayLimit; + $displayRows = $displayTruncated ? array_slice($rows, 0, $displayLimit) : $rows; + + return $this->render('실사 재고 바코드 리스트', 'bag/inventory_stock_barcode_list', [ + 'bagCode' => $bagCode, + 'keyword' => $keyword, + 'itemOptions' => $itemOptions, + 'rows' => $displayRows, + 'totalBoxes' => $totalBoxes, + 'totalPacks' => $totalPacks, + 'totalSheets' => $totalSheets, + 'displayTruncated' => $displayTruncated, + 'displayLimit' => $displayLimit, + ]); + } + /** * @return array{ * rows: list, diff --git a/app/Views/bag/inventory_stock_barcode_list.php b/app/Views/bag/inventory_stock_barcode_list.php new file mode 100644 index 0000000..b28de78 --- /dev/null +++ b/app/Views/bag/inventory_stock_barcode_list.php @@ -0,0 +1,129 @@ +> $itemOptions + * @var list> $rows + * @var int $totalBoxes + * @var int $totalPacks + * @var int $totalSheets + */ +$bagCode = (string) ($bagCode ?? ''); +$keyword = (string) ($keyword ?? ''); +$itemOptions = is_array($itemOptions ?? null) ? $itemOptions : []; +$rows = is_array($rows ?? null) ? $rows : []; +$totalBoxes = (int) ($totalBoxes ?? 0); +$totalPacks = (int) ($totalPacks ?? 0); +$totalSheets = (int) ($totalSheets ?? 0); +$displayTruncated = (bool) ($displayTruncated ?? false); +$displayLimit = (int) ($displayLimit ?? 2000); +$exportQs = http_build_query(['bag_code' => $bagCode, 'keyword' => $keyword, 'export' => 1]); +?> +
+
+
+
+

실사 재고 바코드 리스트

+

현재 창고에 남아있어야 하는(재고 상태) 봉투 번호를 뽑아 실사 시 실물과 대조하세요.

+
+
+ + 엑셀저장 +
+
+ +
+
+ + +
+
+ + +
+ + + 초기화 + +
+
+ +
+ + + +
+ 전체 건 중 화면에는 상위 건만 표시됩니다. 전체는 엑셀저장으로 받거나 품목을 선택해 좁혀 보세요. +
+ + +
+ + + + + + + + + + + + + + + + + + $r): ?> + + + + + + + + + + + + + + + + + + + + + + + +
No품목LOT박스코드팩코드시작번호끝번호매수
재고로 남아있는 바코드가 없습니다.
합계박스
+
+
+
+ +