사용자 매뉴얼·번호알기·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:
@@ -202,11 +202,21 @@ class BagLotFlowBuilder
|
||||
return $this->resolvedFromPackRow($barcode, '박스', $first, 1, count($boxRows), $sheetQty);
|
||||
}
|
||||
|
||||
$exactSheet = $this->db->table('bag_receiving_pack_code')
|
||||
->where('brpc_lg_idx', $lgIdx)
|
||||
->where('brpc_sheet_start_code', $barcode)
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if (is_array($exactSheet) && $exactSheet !== []) {
|
||||
return $this->resolvedFromPackRow($barcode, '낱장', $exactSheet, 0, 0, 1);
|
||||
}
|
||||
|
||||
$sheetRows = $this->db->table('bag_receiving_pack_code')
|
||||
->where('brpc_lg_idx', $lgIdx)
|
||||
->where('brpc_sheet_start_code <=', $barcode)
|
||||
->where('brpc_sheet_end_code >=', $barcode)
|
||||
->limit(50)
|
||||
->where('brpc_sheet_start_code !=', '')
|
||||
->where('brpc_sheet_end_code !=', '')
|
||||
->limit(200)
|
||||
->get()
|
||||
->getResultArray();
|
||||
foreach ($sheetRows as $row) {
|
||||
@@ -217,9 +227,109 @@ class BagLotFlowBuilder
|
||||
}
|
||||
}
|
||||
|
||||
$fromScan = $this->resolveBarcodeFromScanTables($lgIdx, $barcode);
|
||||
if ($fromScan !== null) {
|
||||
return $fromScan;
|
||||
}
|
||||
|
||||
return ['ok' => false, 'message' => '등록되지 않은 바코드입니다.'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 판매·반품 스캔에만 있는 낱장 코드(입고 팩 테이블 미등록) 조회
|
||||
*
|
||||
* @return array{ok: true, barcode: string, unit: string, bag_code: string, bag_name: string, lot_no: string, box_code: string, pack_code: string, pack_ids: list<int>, qty_box: int, qty_pack: int, qty_sheet: int}|null
|
||||
*/
|
||||
private function resolveBarcodeFromScanTables(int $lgIdx, string $barcode): ?array
|
||||
{
|
||||
$bagCode = '';
|
||||
$bagName = '';
|
||||
if ($this->db->tableExists('bag_sale_scan_code')) {
|
||||
$sale = $this->db->table('bag_sale_scan_code')
|
||||
->select('bssc_bag_code, bssc_bag_name')
|
||||
->where('bssc_lg_idx', $lgIdx)
|
||||
->where('bssc_code', $barcode)
|
||||
->orderBy('bssc_regdate', 'DESC')
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if (is_array($sale) && $sale !== []) {
|
||||
$bagCode = (string) ($sale['bssc_bag_code'] ?? '');
|
||||
$bagName = (string) ($sale['bssc_bag_name'] ?? '');
|
||||
}
|
||||
}
|
||||
if ($bagCode === '' && $this->db->tableExists('bag_return_scan_code')) {
|
||||
$ret = $this->db->table('bag_return_scan_code')
|
||||
->select('brsc_bag_code, brsc_bag_name')
|
||||
->where('brsc_lg_idx', $lgIdx)
|
||||
->where('brsc_code', $barcode)
|
||||
->orderBy('brsc_regdate', 'DESC')
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if (is_array($ret) && $ret !== []) {
|
||||
$bagCode = (string) ($ret['brsc_bag_code'] ?? '');
|
||||
$bagName = (string) ($ret['brsc_bag_name'] ?? '');
|
||||
}
|
||||
}
|
||||
if ($bagCode === '' && $bagName === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$packRow = $this->findPackRowContainingSheet($lgIdx, $barcode);
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'barcode' => $barcode,
|
||||
'unit' => '낱장',
|
||||
'bag_code' => $bagCode,
|
||||
'bag_name' => $bagName,
|
||||
'lot_no' => (string) ($packRow['brpc_lot_no'] ?? ''),
|
||||
'box_code' => (string) ($packRow['brpc_box_code'] ?? ''),
|
||||
'pack_code' => (string) ($packRow['brpc_pack_code'] ?? ''),
|
||||
'pack_ids' => isset($packRow['brpc_idx']) ? [(int) $packRow['brpc_idx']] : [],
|
||||
'qty_box' => 0,
|
||||
'qty_pack' => 0,
|
||||
'qty_sheet' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function findPackRowContainingSheet(int $lgIdx, string $barcode): array
|
||||
{
|
||||
if (! $this->db->tableExists('bag_receiving_pack_code')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$exact = $this->db->table('bag_receiving_pack_code')
|
||||
->where('brpc_lg_idx', $lgIdx)
|
||||
->where('brpc_sheet_start_code', $barcode)
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if (is_array($exact) && $exact !== []) {
|
||||
return $exact;
|
||||
}
|
||||
|
||||
foreach ($this->db->table('bag_receiving_pack_code')
|
||||
->where('brpc_lg_idx', $lgIdx)
|
||||
->where('brpc_sheet_start_code !=', '')
|
||||
->where('brpc_sheet_end_code !=', '')
|
||||
->limit(200)
|
||||
->get()
|
||||
->getResultArray() as $row) {
|
||||
$start = (string) ($row['brpc_sheet_start_code'] ?? '');
|
||||
$end = (string) ($row['brpc_sheet_end_code'] ?? '');
|
||||
if ($this->barcodeInRange($barcode, $start, $end)) {
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $pack
|
||||
* @return array{ok: true, barcode: string, unit: string, bag_code: string, bag_name: string, lot_no: string, box_code: string, pack_code: string, pack_ids: list<int>, qty_box: int, qty_pack: int, qty_sheet: int}
|
||||
@@ -247,30 +357,62 @@ class BagLotFlowBuilder
|
||||
* @return list<array{flow_date: string, counterparty: string, flow_type: string, sort_ts: int}>
|
||||
*/
|
||||
private function collectFlowRows(int $lgIdx, array $resolved): array
|
||||
{
|
||||
$unit = (string) ($resolved['unit'] ?? '');
|
||||
|
||||
return match ($unit) {
|
||||
'낱장' => $this->collectFlowRowsForSheet($lgIdx, $resolved),
|
||||
'박스' => $this->collectFlowRowsForBox($lgIdx, $resolved),
|
||||
default => $this->collectFlowRowsForPack($lgIdx, $resolved),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 낱장: 해당 바코드 판매·반품만 + 소속 팩 입고 1건(발주·동일 LOT 전체 이력 제외)
|
||||
*
|
||||
* @param array<string, mixed> $resolved
|
||||
* @return list<array{flow_date: string, counterparty: string, flow_type: string, sort_ts: int}>
|
||||
*/
|
||||
private function collectFlowRowsForSheet(int $lgIdx, array $resolved): array
|
||||
{
|
||||
$rows = [];
|
||||
$codes = [$resolved['barcode']];
|
||||
if (($resolved['pack_code'] ?? '') !== '' && ! in_array($resolved['pack_code'], $codes, true)) {
|
||||
$codes[] = $resolved['pack_code'];
|
||||
}
|
||||
if (($resolved['box_code'] ?? '') !== '' && ! in_array($resolved['box_code'], $codes, true)) {
|
||||
$codes[] = $resolved['box_code'];
|
||||
$barcode = trim((string) ($resolved['barcode'] ?? ''));
|
||||
if ($barcode === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$brIdx = 0;
|
||||
if ($this->db->tableExists('bag_receiving_pack_code')) {
|
||||
$packCode = (string) ($resolved['pack_code'] ?? '');
|
||||
if ($packCode !== '') {
|
||||
$p = $this->db->table('bag_receiving_pack_code')
|
||||
->select('brpc_br_idx')
|
||||
->where('brpc_lg_idx', $lgIdx)
|
||||
->where('brpc_pack_code', $packCode)
|
||||
->get()
|
||||
->getRowArray();
|
||||
$brIdx = (int) ($p['brpc_br_idx'] ?? 0);
|
||||
$brIdx = $this->receivingBrIdxForPackCode($lgIdx, (string) ($resolved['pack_code'] ?? ''));
|
||||
if ($brIdx > 0) {
|
||||
foreach ($this->loadReceivingEventsByBrIdx($lgIdx, $brIdx) as $ev) {
|
||||
$rows[] = $ev;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->loadScanEventsForCodes($lgIdx, [$barcode]) as $ev) {
|
||||
$rows[] = $ev;
|
||||
}
|
||||
foreach ($this->loadReturnEventsForCodes($lgIdx, [$barcode]) as $ev) {
|
||||
$rows[] = $ev;
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* 팩: 팩·낱장 바코드 스캔 + 입고 + LOT 발주
|
||||
*
|
||||
* @param array<string, mixed> $resolved
|
||||
* @return list<array{flow_date: string, counterparty: string, flow_type: string, sort_ts: int}>
|
||||
*/
|
||||
private function collectFlowRowsForPack(int $lgIdx, array $resolved): array
|
||||
{
|
||||
$rows = [];
|
||||
$codes = array_values(array_unique(array_filter([
|
||||
(string) ($resolved['barcode'] ?? ''),
|
||||
(string) ($resolved['pack_code'] ?? ''),
|
||||
], static fn (string $c): bool => $c !== '')));
|
||||
|
||||
$brIdx = $this->receivingBrIdxForPackCode($lgIdx, (string) ($resolved['pack_code'] ?? ''));
|
||||
if ($brIdx > 0) {
|
||||
foreach ($this->loadReceivingEventsByBrIdx($lgIdx, $brIdx) as $ev) {
|
||||
$rows[] = $ev;
|
||||
@@ -294,6 +436,91 @@ class BagLotFlowBuilder
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* 박스: 박스·소속 팩 코드 스캔 + 입고(박스 내 팩) + LOT 발주
|
||||
*
|
||||
* @param array<string, mixed> $resolved
|
||||
* @return list<array{flow_date: string, counterparty: string, flow_type: string, sort_ts: int}>
|
||||
*/
|
||||
private function collectFlowRowsForBox(int $lgIdx, array $resolved): array
|
||||
{
|
||||
$rows = [];
|
||||
$boxCode = (string) ($resolved['box_code'] ?? '');
|
||||
$codes = array_values(array_unique(array_filter([
|
||||
(string) ($resolved['barcode'] ?? ''),
|
||||
$boxCode,
|
||||
(string) ($resolved['pack_code'] ?? ''),
|
||||
], static fn (string $c): bool => $c !== '')));
|
||||
|
||||
if ($boxCode !== '' && $this->db->tableExists('bag_receiving_pack_code')) {
|
||||
$packCodes = $this->db->table('bag_receiving_pack_code')
|
||||
->select('brpc_pack_code')
|
||||
->where('brpc_lg_idx', $lgIdx)
|
||||
->where('brpc_box_code', $boxCode)
|
||||
->get()
|
||||
->getResultArray();
|
||||
foreach ($packCodes as $p) {
|
||||
$pc = (string) ($p['brpc_pack_code'] ?? '');
|
||||
if ($pc !== '' && ! in_array($pc, $codes, true)) {
|
||||
$codes[] = $pc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$seenBr = [];
|
||||
if ($boxCode !== '' && $this->db->tableExists('bag_receiving_pack_code')) {
|
||||
$brRows = $this->db->table('bag_receiving_pack_code')
|
||||
->select('brpc_br_idx')
|
||||
->distinct()
|
||||
->where('brpc_lg_idx', $lgIdx)
|
||||
->where('brpc_box_code', $boxCode)
|
||||
->get()
|
||||
->getResultArray();
|
||||
foreach ($brRows as $brRow) {
|
||||
$brIdx = (int) ($brRow['brpc_br_idx'] ?? 0);
|
||||
if ($brIdx <= 0 || isset($seenBr[$brIdx])) {
|
||||
continue;
|
||||
}
|
||||
$seenBr[$brIdx] = true;
|
||||
foreach ($this->loadReceivingEventsByBrIdx($lgIdx, $brIdx) as $ev) {
|
||||
$rows[] = $ev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->loadScanEventsForCodes($lgIdx, $codes) as $ev) {
|
||||
$rows[] = $ev;
|
||||
}
|
||||
foreach ($this->loadReturnEventsForCodes($lgIdx, $codes) as $ev) {
|
||||
$rows[] = $ev;
|
||||
}
|
||||
|
||||
$lotNo = (string) ($resolved['lot_no'] ?? '');
|
||||
if ($lotNo !== '') {
|
||||
foreach ($this->loadOrderEventsForLot($lgIdx, $lotNo) as $ev) {
|
||||
$rows[] = $ev;
|
||||
}
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
private function receivingBrIdxForPackCode(int $lgIdx, string $packCode): int
|
||||
{
|
||||
if ($packCode === '' || ! $this->db->tableExists('bag_receiving_pack_code')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$p = $this->db->table('bag_receiving_pack_code')
|
||||
->select('brpc_br_idx')
|
||||
->where('brpc_lg_idx', $lgIdx)
|
||||
->where('brpc_pack_code', $packCode)
|
||||
->get()
|
||||
->getRowArray();
|
||||
|
||||
return (int) ($p['brpc_br_idx'] ?? 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<array{flow_date: string, counterparty: string, flow_type: string, sort_ts: int}>
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user