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.0: chandj.KDunitprice (601건) → items, prices
|
||||||
* Phase 1.1: chandj.models (18건) → items (FG), prices
|
* Phase 1.1: chandj.models (18건) → items (FG), prices
|
||||||
* Phase 1.2: chandj.item_list (9건) → items (PT), 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
|
* @see docs/plans/kd-items-migration-plan.md
|
||||||
*/
|
*/
|
||||||
@@ -60,14 +62,21 @@ public function run(): void
|
|||||||
// Phase 1.2: item_list → items (PT)
|
// Phase 1.2: item_list → items (PT)
|
||||||
$itemListCount = $this->migrateItemList($tenantId, $userId);
|
$itemListCount = $this->migrateItemList($tenantId, $userId);
|
||||||
|
|
||||||
|
// Phase 2.1: BDmodels.seconditem → items (PT) 누락 부품
|
||||||
|
$bdPartsCount = $this->migrateBDmodelsParts($tenantId, $userId);
|
||||||
|
|
||||||
// prices 생성 (모든 items 기반)
|
// prices 생성 (모든 items 기반)
|
||||||
$priceCount = $this->migratePrices($tenantId, $userId);
|
$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('✅ 마이그레이션 완료:');
|
$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(" → prices: {$priceCount}건");
|
||||||
|
$this->command->info(" → BOM 연결: {$bomCount}건");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -382,4 +391,193 @@ private function mapItemType(?string $itemDiv): string
|
|||||||
{
|
{
|
||||||
return self::ITEM_TYPE_MAP[$itemDiv] ?? 'SM';
|
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