Files
sam-api/app/Console/Commands/ValidateBendingItems.php
권혁성 855e806e42 refactor: 절곡 재고 마이그레이션 커맨드 리팩토링 및 검증/시더 추가
- Migrate5130BendingStock: BD-* 품목 초기 재고 셋팅으로 목적 변경, --min-stock 옵션 추가
- ValidateBendingItems: BD-* 품목 존재 여부 검증 커맨드 신규
- BendingItemSeeder: 경동 절곡 품목 시딩 신규

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 04:19:47 +09:00

138 lines
5.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Console\Commands;
use Illuminate\Console\Attributes\AsCommand;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
#[AsCommand(name: 'bending:validate-items', description: 'BD-* 절곡 세부품목 마스터 데이터 검증 (prefix × lengthCode 전 조합)')]
class ValidateBendingItems extends Command
{
protected $signature = 'bending:validate-items
{--tenant_id=287 : Target tenant ID (default: 287 경동기업)}';
/**
* prefix별 유효 길이코드 정의
*
* 가이드레일: 30, 35, 40, 43 (벽면/측면 공통)
* 하단마감재: 30, 40
* 셔터박스: 12, 24, 30, 35, 40, 41
* 연기차단재: 53, 54, 83, 84 (W50/W80 전용 코드)
* XX: 12, 24, 30, 35, 40, 41, 43 (하부BASE + 셔터 상부/마구리)
* YY: 30, 35, 40, 43 (별도 SUS 마감)
* HH: 30, 40 (보강평철)
*/
private function getPrefixLengthCodes(): array
{
$guideRailCodes = ['30', '35', '40', '43'];
$guideRailCodesWithExtra = ['24', '30', '35', '40', '43']; // RT/ST는 적은 종류
$bottomBarCodes = ['30', '40'];
$shutterBoxCodes = ['12', '24', '30', '35', '40', '41'];
return [
// 가이드레일 벽면형
'RS' => $guideRailCodes, // 벽면 SUS 마감재
'RM' => ['24', '30', '35', '40', '42', '43'], // 벽면 본체 (EGI)
'RC' => ['24', '30', '35', '40', '42', '43'], // 벽면 C형
'RD' => ['24', '30', '35', '40', '42', '43'], // 벽면 D형
'RT' => ['30', '43'], // 벽면 본체 (철재)
// 가이드레일 측면형
'SS' => ['30', '35', '40'], // 측면 SUS 마감재
'SM' => ['24', '30', '35', '40', '43'], // 측면 본체 (EGI)
'SC' => ['24', '30', '35', '40', '43'], // 측면 C형
'SD' => ['24', '30', '35', '40', '43'], // 측면 D형
'ST' => ['43'], // 측면 본체 (철재)
'SU' => ['30', '35', '40', '43'], // 측면 SUS (SUS2)
// 하단마감재
'BE' => $bottomBarCodes, // EGI 마감
'BS' => ['24', '30', '35', '40', '43'], // SUS 마감
'TS' => ['43'], // 철재 SUS
'LA' => $bottomBarCodes, // L-Bar
// 셔터박스
'CF' => $shutterBoxCodes, // 전면부
'CL' => $shutterBoxCodes, // 린텔부
'CP' => $shutterBoxCodes, // 점검구
'CB' => $shutterBoxCodes, // 후면코너부
// 연기차단재
'GI' => ['53', '54', '83', '84', '30', '35', '40'], // W50/W80 + 일반
// 공용/기타
'XX' => ['12', '24', '30', '35', '40', '41', '43'], // 하부BASE/셔터 상부/마구리
'YY' => ['30', '35', '40', '43'], // 별도 SUS 마감
'HH' => ['30', '40'], // 보강평철
];
}
public function handle(): int
{
$tenantId = (int) $this->option('tenant_id');
$this->info("=== BD-* 절곡 세부품목 마스터 검증 (tenant: {$tenantId}) ===");
$this->newLine();
// DB에서 전체 BD-* 품목 조회
$existingItems = DB::table('items')
->where('tenant_id', $tenantId)
->where('code', 'like', 'BD-%')
->whereNull('deleted_at')
->pluck('code')
->toArray();
$existingSet = array_flip($existingItems);
$this->info('현재 등록된 BD-* 품목: '.count($existingItems).'개');
$this->newLine();
$prefixMap = $this->getPrefixLengthCodes();
$totalExpected = 0;
$missing = [];
$found = 0;
foreach ($prefixMap as $prefix => $codes) {
$prefixMissing = [];
foreach ($codes as $code) {
$itemCode = "BD-{$prefix}-{$code}";
$totalExpected++;
if (isset($existingSet[$itemCode])) {
$found++;
} else {
$prefixMissing[] = $itemCode;
$missing[] = $itemCode;
}
}
$status = empty($prefixMissing) ? '✅' : '❌';
$countStr = count($codes) - count($prefixMissing).'/'.count($codes);
$this->line(" {$status} BD-{$prefix}: {$countStr}");
if (! empty($prefixMissing)) {
foreach ($prefixMissing as $m) {
$this->line(" ⚠️ 누락: {$m}");
}
}
}
$this->newLine();
$this->info('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
$this->info("검증 결과: {$found}/{$totalExpected} 등록 완료");
if (empty($missing)) {
$this->info('✅ All items registered — 누락 0건');
return self::SUCCESS;
}
$this->warn('❌ 누락 항목: '.count($missing).'건');
$this->newLine();
$this->table(['누락 품목코드'], array_map(fn ($m) => [$m], $missing));
return self::FAILURE;
}
}