feat: Phase 1.1-1.2 추가 (models, item_list 마이그레이션)

- migrateModels(): chandj.models → items (FG) 18건
- migrateItemList(): chandj.item_list → items (PT) 9건
- migratePrices(): 다양한 소스 단가 처리 로직 개선
- 코드 포맷: FG-{model}-{type}-{finish}, PT-{name}

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-28 20:38:29 +09:00
parent 2bce30056d
commit ec47c26ea8

View File

@@ -9,8 +9,9 @@
/**
* 경동기업 품목/단가 마이그레이션 Seeder
*
* 소스: chandj.KDunitprice (603건)
* 타겟: samdb.items, samdb.prices
* 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
*
* @see docs/plans/kd-items-migration-plan.md
*/
@@ -28,6 +29,14 @@ class KyungdongItemSeeder extends Seeder
'[무형상품]' => 'CS',
];
/**
* finishing_type 약어 매핑
*/
private const FINISHING_MAP = [
'SUS마감' => 'SUS',
'EGI마감' => 'EGI',
];
/**
* 경동기업 품목/단가 마이그레이션 실행
*/
@@ -42,14 +51,23 @@ public function run(): void
// 1. 기존 데이터 삭제
$this->cleanupExistingData($tenantId);
// 2. KDunitprice → items
// Phase 1.0: KDunitprice → items
$itemCount = $this->migrateItems($tenantId, $userId);
// 3. items 기반 → prices
// Phase 1.1: models → items (FG)
$modelCount = $this->migrateModels($tenantId, $userId);
// Phase 1.2: item_list → items (PT)
$itemListCount = $this->migrateItemList($tenantId, $userId);
// prices 생성 (모든 items 기반)
$priceCount = $this->migratePrices($tenantId, $userId);
$totalItems = $itemCount + $modelCount + $itemListCount;
$this->command->info('');
$this->command->info("✅ 마이그레이션 완료: items {$itemCount}건, prices {$priceCount}");
$this->command->info('✅ 마이그레이션 완료:');
$this->command->info(" → items: {$totalItems}건 (KDunitprice {$itemCount} + models {$modelCount} + item_list {$itemListCount})");
$this->command->info(" → prices: {$priceCount}");
}
/**
@@ -148,11 +166,12 @@ private function migratePrices(int $tenantId, int $userId): int
$this->command->info('');
$this->command->info('💰 items → prices 마이그레이션...');
// 생성된 items와 KDunitprice 조인하여 prices 생성
// 생성된 items 조회
$items = DB::table('items')
->where('tenant_id', $tenantId)
->get(['id', 'code', 'item_type']);
->get(['id', 'code', 'item_type', 'attributes']);
// KDunitprice 단가 (code → unitprice)
$kdPrices = DB::connection('chandj')
->table('KDunitprice')
->whereNull('is_deleted')
@@ -160,12 +179,31 @@ private function migratePrices(int $tenantId, int $userId): int
->where('prodcode', '!=', '')
->pluck('unitprice', 'prodcode');
// item_list 단가 (item_name → col13)
$itemListPrices = DB::connection('chandj')
->table('item_list')
->pluck('col13', 'item_name');
$prices = [];
$now = now();
$batchCount = 0;
foreach ($items as $item) {
$unitPrice = $kdPrices[$item->code] ?? 0;
$attributes = json_decode($item->attributes, true) ?? [];
$legacySource = $attributes['legacy_source'] ?? '';
// 소스별 단가 결정
$unitPrice = match ($legacySource) {
'KDunitprice' => $kdPrices[$item->code] ?? 0,
'item_list' => $itemListPrices[$attributes['legacy_num'] ? $this->getItemListName($item->code) : ''] ?? $attributes['base_price'] ?? 0,
'models' => 0, // models는 단가 없음
default => 0,
};
// item_list의 경우 attributes에 저장된 base_price 사용
if ($legacySource === 'item_list' && isset($attributes['base_price'])) {
$unitPrice = $attributes['base_price'];
}
$prices[] = [
'tenant_id' => $tenantId,
@@ -182,7 +220,7 @@ private function migratePrices(int $tenantId, int $userId): int
'supplier' => null,
'effective_from' => now()->toDateString(),
'effective_to' => null,
'note' => 'KDunitprice 마이그레이션',
'note' => "{$legacySource} 마이그레이션",
'status' => 'active',
'is_final' => false,
'created_by' => $userId,
@@ -211,6 +249,132 @@ private function migratePrices(int $tenantId, int $userId): int
return $batchCount;
}
/**
* PT-{name} 코드에서 name 추출
*/
private function getItemListName(string $code): string
{
return str_starts_with($code, 'PT-') ? substr($code, 3) : '';
}
/**
* Phase 1.1: models → items (FG) 마이그레이션
*/
private function migrateModels(int $tenantId, int $userId): int
{
$this->command->info('');
$this->command->info('📦 [Phase 1.1] models → items (FG) 마이그레이션...');
$models = DB::connection('chandj')
->table('models')
->where(function ($q) {
$q->where('is_deleted', 0)->orWhereNull('is_deleted');
})
->get();
$this->command->info(" → 소스 데이터: {$models->count()}");
$items = [];
$now = now();
foreach ($models as $model) {
$finishingShort = self::FINISHING_MAP[$model->finishing_type] ?? 'STD';
$code = "FG-{$model->model_name}-{$model->guiderail_type}-{$finishingShort}";
$name = "{$model->model_name} {$model->major_category} {$model->finishing_type} {$model->guiderail_type}";
$items[] = [
'tenant_id' => $tenantId,
'item_type' => 'FG',
'code' => $code,
'name' => trim($name),
'unit' => 'EA',
'category_id' => null,
'process_type' => null,
'item_category' => $model->major_category,
'bom' => null,
'attributes' => json_encode([
'model_name' => $model->model_name,
'major_category' => $model->major_category,
'finishing_type' => $model->finishing_type,
'guiderail_type' => $model->guiderail_type,
'legacy_source' => 'models',
'legacy_model_id' => $model->model_id,
]),
'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(" ✓ items (FG): {$models->count()}건 생성 완료");
return $models->count();
}
/**
* Phase 1.2: item_list → items (PT) 마이그레이션
*/
private function migrateItemList(int $tenantId, int $userId): int
{
$this->command->info('');
$this->command->info('📦 [Phase 1.2] item_list → items (PT) 마이그레이션...');
$itemList = DB::connection('chandj')
->table('item_list')
->get();
$this->command->info(" → 소스 데이터: {$itemList->count()}");
$items = [];
$now = now();
foreach ($itemList as $item) {
$code = "PT-{$item->item_name}";
$items[] = [
'tenant_id' => $tenantId,
'item_type' => 'PT',
'code' => $code,
'name' => $item->item_name,
'unit' => 'EA',
'category_id' => null,
'process_type' => null,
'item_category' => null,
'bom' => null,
'attributes' => json_encode([
'base_price' => $item->col13,
'legacy_source' => 'item_list',
'legacy_num' => $item->num,
]),
'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(" ✓ items (PT): {$itemList->count()}건 생성 완료");
return $itemList->count();
}
/**
* item_div → item_type 매핑
*/