feat: Phase 2 BOM 마이그레이션 추가
- Phase 2.1: BDmodels.seconditem → PT items 6건 추가 - 누락 부품: L-BAR, 보강평철, 케이스, 하단마감재 등 - Phase 2.2: items.bom JSON 연결 18건 - FG items (models) ↔ PT items (seconditem) BOM 관계 설정 - 최종: items 634건, prices 634건, BOM 18건 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,8 @@
|
||||
* Phase 1.0: chandj.KDunitprice (601건) → items, prices
|
||||
* Phase 1.1: chandj.models (18건) → items (FG), prices
|
||||
* Phase 1.2: chandj.item_list (9건) → items (PT), prices
|
||||
* Phase 2.1: chandj.BDmodels.seconditem → items (PT) 누락 부품 추가
|
||||
* Phase 2.2: chandj.BDmodels → items.bom JSON (FG ↔ PT 연결)
|
||||
*
|
||||
* @see docs/plans/kd-items-migration-plan.md
|
||||
*/
|
||||
@@ -60,14 +62,21 @@ public function run(): void
|
||||
// Phase 1.2: item_list → items (PT)
|
||||
$itemListCount = $this->migrateItemList($tenantId, $userId);
|
||||
|
||||
// Phase 2.1: BDmodels.seconditem → items (PT) 누락 부품
|
||||
$bdPartsCount = $this->migrateBDmodelsParts($tenantId, $userId);
|
||||
|
||||
// prices 생성 (모든 items 기반)
|
||||
$priceCount = $this->migratePrices($tenantId, $userId);
|
||||
|
||||
$totalItems = $itemCount + $modelCount + $itemListCount;
|
||||
// Phase 2.2: BDmodels → items.bom JSON
|
||||
$bomCount = $this->migrateBom($tenantId);
|
||||
|
||||
$totalItems = $itemCount + $modelCount + $itemListCount + $bdPartsCount;
|
||||
$this->command->info('');
|
||||
$this->command->info('✅ 마이그레이션 완료:');
|
||||
$this->command->info(" → items: {$totalItems}건 (KDunitprice {$itemCount} + models {$modelCount} + item_list {$itemListCount})");
|
||||
$this->command->info(" → items: {$totalItems}건 (KDunitprice {$itemCount} + models {$modelCount} + item_list {$itemListCount} + BDmodels부품 {$bdPartsCount})");
|
||||
$this->command->info(" → prices: {$priceCount}건");
|
||||
$this->command->info(" → BOM 연결: {$bomCount}건");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -382,4 +391,193 @@ private function mapItemType(?string $itemDiv): string
|
||||
{
|
||||
return self::ITEM_TYPE_MAP[$itemDiv] ?? 'SM';
|
||||
}
|
||||
|
||||
/**
|
||||
* Phase 2.1: BDmodels.seconditem → items (PT) 누락 부품 추가
|
||||
*
|
||||
* item_list에 없는 BDmodels.seconditem을 PT items로 생성
|
||||
*/
|
||||
private function migrateBDmodelsParts(int $tenantId, int $userId): int
|
||||
{
|
||||
$this->command->info('');
|
||||
$this->command->info('📦 [Phase 2.1] BDmodels.seconditem → items (PT) 누락 부품...');
|
||||
|
||||
// BDmodels에서 고유한 seconditem 목록 조회
|
||||
$bdSecondItems = DB::connection('chandj')
|
||||
->table('BDmodels')
|
||||
->where(function ($q) {
|
||||
$q->where('is_deleted', 0)->orWhereNull('is_deleted');
|
||||
})
|
||||
->whereNotNull('seconditem')
|
||||
->where('seconditem', '!=', '')
|
||||
->distinct()
|
||||
->pluck('seconditem');
|
||||
|
||||
// 이미 존재하는 PT items 코드 조회
|
||||
$existingPtCodes = DB::table('items')
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('item_type', 'PT')
|
||||
->pluck('code')
|
||||
->map(fn ($code) => str_starts_with($code, 'PT-') ? substr($code, 3) : $code)
|
||||
->toArray();
|
||||
|
||||
$items = [];
|
||||
$now = now();
|
||||
|
||||
foreach ($bdSecondItems as $secondItem) {
|
||||
// 이미 PT items에 있으면 스킵
|
||||
if (in_array($secondItem, $existingPtCodes)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$code = "PT-{$secondItem}";
|
||||
|
||||
$items[] = [
|
||||
'tenant_id' => $tenantId,
|
||||
'item_type' => 'PT',
|
||||
'code' => $code,
|
||||
'name' => $secondItem,
|
||||
'unit' => 'EA',
|
||||
'category_id' => null,
|
||||
'process_type' => null,
|
||||
'item_category' => null,
|
||||
'bom' => null,
|
||||
'attributes' => json_encode([
|
||||
'legacy_source' => 'BDmodels_seconditem',
|
||||
]),
|
||||
'attributes_archive' => null,
|
||||
'options' => null,
|
||||
'description' => null,
|
||||
'is_active' => true,
|
||||
'created_by' => $userId,
|
||||
'updated_by' => $userId,
|
||||
'created_at' => $now,
|
||||
'updated_at' => $now,
|
||||
];
|
||||
}
|
||||
|
||||
if (! empty($items)) {
|
||||
DB::table('items')->insert($items);
|
||||
}
|
||||
|
||||
$this->command->info(" → 소스 데이터: {$bdSecondItems->count()}건 (중복 제외 ".count($items).'건 신규)');
|
||||
$this->command->info(' ✓ items (PT): '.count($items).'건 생성 완료');
|
||||
|
||||
return count($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Phase 2.2: BDmodels → items.bom JSON (FG ↔ PT 연결)
|
||||
*
|
||||
* models 기반 FG items에 BOM 연결
|
||||
* bom: [{child_item_id: X, quantity: Y}, ...]
|
||||
*/
|
||||
private function migrateBom(int $tenantId): int
|
||||
{
|
||||
$this->command->info('');
|
||||
$this->command->info('🔗 [Phase 2.2] BDmodels → items.bom JSON 연결...');
|
||||
|
||||
// PT items 조회 (code → id 매핑)
|
||||
$ptItems = DB::table('items')
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('item_type', 'PT')
|
||||
->pluck('id', 'code')
|
||||
->toArray();
|
||||
|
||||
// PT- prefix 없는 버전도 매핑 추가
|
||||
$ptItemsByName = [];
|
||||
foreach ($ptItems as $code => $id) {
|
||||
$name = str_starts_with($code, 'PT-') ? substr($code, 3) : $code;
|
||||
$ptItemsByName[$name] = $id;
|
||||
}
|
||||
|
||||
// FG items 조회 (models 기반)
|
||||
$fgItems = DB::table('items')
|
||||
->where('tenant_id', $tenantId)
|
||||
->where('item_type', 'FG')
|
||||
->whereNotNull('attributes')
|
||||
->get(['id', 'code', 'attributes']);
|
||||
|
||||
// BDmodels 데이터 조회
|
||||
$bdModels = DB::connection('chandj')
|
||||
->table('BDmodels')
|
||||
->where(function ($q) {
|
||||
$q->where('is_deleted', 0)->orWhereNull('is_deleted');
|
||||
})
|
||||
->whereNotNull('model_name')
|
||||
->where('model_name', '!=', '')
|
||||
->get(['model_name', 'seconditem', 'savejson']);
|
||||
|
||||
// model_name → seconditems 그룹핑
|
||||
$modelBomMap = [];
|
||||
foreach ($bdModels as $bd) {
|
||||
if (empty($bd->seconditem)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$modelName = $bd->model_name;
|
||||
if (! isset($modelBomMap[$modelName])) {
|
||||
$modelBomMap[$modelName] = [];
|
||||
}
|
||||
|
||||
// savejson에서 수량 파싱 (col8이 수량)
|
||||
$quantity = 1;
|
||||
if (! empty($bd->savejson)) {
|
||||
$json = json_decode($bd->savejson, true);
|
||||
if (is_array($json) && ! empty($json)) {
|
||||
// 첫 번째 항목의 col8(수량) 사용
|
||||
$quantity = (int) ($json[0]['col8'] ?? 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 중복 체크 후 추가
|
||||
$found = false;
|
||||
foreach ($modelBomMap[$modelName] as &$existing) {
|
||||
if ($existing['seconditem'] === $bd->seconditem) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! $found) {
|
||||
$modelBomMap[$modelName][] = [
|
||||
'seconditem' => $bd->seconditem,
|
||||
'quantity' => $quantity,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$updatedCount = 0;
|
||||
|
||||
foreach ($fgItems as $fgItem) {
|
||||
$attributes = json_decode($fgItem->attributes, true) ?? [];
|
||||
$modelName = $attributes['model_name'] ?? null;
|
||||
|
||||
if (empty($modelName) || ! isset($modelBomMap[$modelName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$bomArray = [];
|
||||
foreach ($modelBomMap[$modelName] as $bomItem) {
|
||||
$childItemId = $ptItemsByName[$bomItem['seconditem']] ?? null;
|
||||
if ($childItemId) {
|
||||
$bomArray[] = [
|
||||
'child_item_id' => $childItemId,
|
||||
'quantity' => $bomItem['quantity'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($bomArray)) {
|
||||
DB::table('items')
|
||||
->where('id', $fgItem->id)
|
||||
->update(['bom' => json_encode($bomArray)]);
|
||||
$updatedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->command->info(' → BDmodels 모델: '.count($modelBomMap).'개');
|
||||
$this->command->info(" ✓ items.bom 연결: {$updatedCount}건 완료");
|
||||
|
||||
return $updatedCount;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user