사용자 매뉴얼·번호알기·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:
225
app/Libraries/BagNumberLookup.php
Normal file
225
app/Libraries/BagNumberLookup.php
Normal file
@@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Libraries;
|
||||
|
||||
/**
|
||||
* 봉투번호확인(번호알기) — 코드 입력 → 바코드·인쇄숫자·인식번호 분해.
|
||||
* LOT-입고PK-팩/박스-낱장 형식 및 DB 등록 바코드 지원.
|
||||
*/
|
||||
class BagNumberLookup
|
||||
{
|
||||
/**
|
||||
* @return array{
|
||||
* ok: bool,
|
||||
* message: string,
|
||||
* input: string,
|
||||
* barcode_text: string,
|
||||
* print_text: string,
|
||||
* recognition_text: string,
|
||||
* unit: string,
|
||||
* bag_code: string,
|
||||
* bag_name: string
|
||||
* }
|
||||
*/
|
||||
public function resolve(string $raw, ?int $lgIdx = null): array
|
||||
{
|
||||
$input = trim($raw);
|
||||
if ($input === '') {
|
||||
return $this->fail('코드를 입력해 주세요.', $input);
|
||||
}
|
||||
|
||||
$normalized = strtoupper(preg_replace('/\s+/', '', $input) ?? $input);
|
||||
$parsed = $this->parseStructuredCode($normalized);
|
||||
|
||||
if ($parsed === null && $lgIdx !== null) {
|
||||
$parsed = $this->resolveFromDatabase($lgIdx, $normalized);
|
||||
}
|
||||
|
||||
if ($parsed === null) {
|
||||
return $this->fail('인식할 수 없는 코드입니다. 봉투 바코드(LOT-입고번호-팩/박스-낱장) 형식을 확인해 주세요.', $input);
|
||||
}
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'message' => '',
|
||||
'input' => $input,
|
||||
'barcode_text' => $this->formatRow($parsed['barcode'], 4),
|
||||
'print_text' => $this->formatRow($parsed['print'], 3),
|
||||
'recognition_text' => $this->formatRow($parsed['recognition'], 2),
|
||||
'unit' => (string) ($parsed['unit'] ?? ''),
|
||||
'bag_code' => (string) ($parsed['bag_code'] ?? ''),
|
||||
'bag_name' => (string) ($parsed['bag_name'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<string> $parts
|
||||
*/
|
||||
private function formatRow(array $parts, int $slots): string
|
||||
{
|
||||
$cells = array_pad($parts, $slots, '-');
|
||||
foreach ($cells as $i => $cell) {
|
||||
if ($cell === '' || $cell === null) {
|
||||
$cells[$i] = '-';
|
||||
}
|
||||
}
|
||||
|
||||
return implode(' ', $cells);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{barcode:list<string>,print:list<string>,recognition:list<string>,unit:string,bag_code?:string,bag_name?:string}|null
|
||||
*/
|
||||
private function parseStructuredCode(string $code): ?array
|
||||
{
|
||||
if (preg_match('/^([A-Z0-9]+)-(\d{6})-P(\d{3})-S(\d+)$/i', $code, $m) === 1) {
|
||||
$sheet = str_pad((string) $m[4], 5, '0', STR_PAD_LEFT);
|
||||
|
||||
return [
|
||||
'barcode' => [(string) $m[1], (string) $m[2], 'P' . $m[3], 'S' . $sheet],
|
||||
'print' => [(string) (int) $m[2], (string) (int) $m[3], (string) (int) $sheet],
|
||||
'recognition' => [(string) $m[2], 'P' . $m[3]],
|
||||
'unit' => '낱장',
|
||||
];
|
||||
}
|
||||
|
||||
if (preg_match('/^([A-Z0-9]+)-(\d{6})-P(\d{3})$/i', $code, $m) === 1) {
|
||||
return [
|
||||
'barcode' => [(string) $m[1], (string) $m[2], 'P' . $m[3], '-'],
|
||||
'print' => [(string) (int) $m[2], (string) (int) $m[3], '-'],
|
||||
'recognition' => [(string) $m[2], 'P' . $m[3]],
|
||||
'unit' => '팩',
|
||||
];
|
||||
}
|
||||
|
||||
if (preg_match('/^([A-Z0-9]+)-(\d{6})-B(\d{3})$/i', $code, $m) === 1) {
|
||||
return [
|
||||
'barcode' => [(string) $m[1], (string) $m[2], 'B' . $m[3], '-'],
|
||||
'print' => [(string) (int) $m[2], (string) (int) $m[3], '-'],
|
||||
'recognition' => [(string) $m[2], 'B' . $m[3]],
|
||||
'unit' => '박스',
|
||||
];
|
||||
}
|
||||
|
||||
if (preg_match('/^([A-Z0-9]{4,8})$/i', $code) === 1) {
|
||||
return [
|
||||
'barcode' => [$code, '-', '-', '-'],
|
||||
'print' => ['-', '-', '-'],
|
||||
'recognition' => [$code, '-'],
|
||||
'unit' => 'LOT',
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{barcode:list<string>,print:list<string>,recognition:list<string>,unit:string,bag_code?:string,bag_name?:string}|null
|
||||
*/
|
||||
private function resolveFromDatabase(int $lgIdx, string $code): ?array
|
||||
{
|
||||
$db = \Config\Database::connect();
|
||||
if (! $db->tableExists('bag_receiving_pack_code')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$row = $db->table('bag_receiving_pack_code')
|
||||
->where('brpc_lg_idx', $lgIdx)
|
||||
->where('brpc_pack_code', $code)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if (is_array($row) && $row !== []) {
|
||||
return $this->parsedFromPackRow($code, $row, '팩');
|
||||
}
|
||||
|
||||
$boxRows = $db->table('bag_receiving_pack_code')
|
||||
->where('brpc_lg_idx', $lgIdx)
|
||||
->where('brpc_box_code', $code)
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if (is_array($boxRows) && $boxRows !== []) {
|
||||
return $this->parsedFromPackRow($code, $boxRows, '박스');
|
||||
}
|
||||
|
||||
$sheetRow = $db->table('bag_receiving_pack_code')
|
||||
->where('brpc_lg_idx', $lgIdx)
|
||||
->where('brpc_sheet_start_code <=', $code)
|
||||
->where('brpc_sheet_end_code >=', $code)
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if (is_array($sheetRow) && $sheetRow !== []) {
|
||||
$parsed = $this->parseStructuredCode($code);
|
||||
if ($parsed !== null) {
|
||||
$parsed['bag_code'] = (string) ($sheetRow['brpc_bag_code'] ?? '');
|
||||
$parsed['bag_name'] = (string) ($sheetRow['brpc_bag_name'] ?? '');
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
}
|
||||
|
||||
$exactSheet = $db->table('bag_receiving_pack_code')
|
||||
->where('brpc_lg_idx', $lgIdx)
|
||||
->groupStart()
|
||||
->where('brpc_sheet_start_code', $code)
|
||||
->orWhere('brpc_sheet_end_code', $code)
|
||||
->groupEnd()
|
||||
->limit(1)
|
||||
->get()
|
||||
->getRowArray();
|
||||
if (is_array($exactSheet) && $exactSheet !== []) {
|
||||
$parsed = $this->parseStructuredCode($code);
|
||||
if ($parsed !== null) {
|
||||
$parsed['bag_code'] = (string) ($exactSheet['brpc_bag_code'] ?? '');
|
||||
$parsed['bag_name'] = (string) ($exactSheet['brpc_bag_name'] ?? '');
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
* @return array{barcode:list<string>,print:list<string>,recognition:list<string>,unit:string,bag_code:string,bag_name:string}
|
||||
*/
|
||||
private function parsedFromPackRow(string $code, array $row, string $unit): array
|
||||
{
|
||||
$parsed = $this->parseStructuredCode($code);
|
||||
if ($parsed === null) {
|
||||
$parsed = [
|
||||
'barcode' => [$code, '-', '-', '-'],
|
||||
'print' => ['-', '-', '-'],
|
||||
'recognition' => ['-', '-'],
|
||||
'unit' => $unit,
|
||||
];
|
||||
}
|
||||
$parsed['unit'] = $unit;
|
||||
$parsed['bag_code'] = (string) ($row['brpc_bag_code'] ?? '');
|
||||
$parsed['bag_name'] = (string) ($row['brpc_bag_name'] ?? '');
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{ok:bool,message:string,input:string,barcode_text:string,print_text:string,recognition_text:string,unit:string,bag_code:string,bag_name:string}
|
||||
*/
|
||||
private function fail(string $message, string $input): array
|
||||
{
|
||||
return [
|
||||
'ok' => false,
|
||||
'message' => $message,
|
||||
'input' => $input,
|
||||
'barcode_text' => $this->formatRow([], 4),
|
||||
'print_text' => $this->formatRow([], 3),
|
||||
'recognition_text' => $this->formatRow([], 2),
|
||||
'unit' => '',
|
||||
'bag_code' => '',
|
||||
'bag_name' => '',
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user