# 5130 → SAM BOM 데이터 마이그레이션 계획 > **작성일**: 2025-01-20 > **목적**: 5130 레거시 시스템의 BOM 데이터를 SAM items 테이블의 bom 컬럼에 마이그레이션 > **기준 문서**: `api/app/Services/Quote/FormulaEvaluatorService.php` > **상태**: ✅ 완료 (Serena ID: 5130-bom-migration-state) --- ## 📍 현재 진행 상태 | 항목 | 내용 | |------|------| | **마지막 완료 작업** | BOM 마이그레이션 실행 완료 (61건) | | **다음 작업** | 견적 페이지에서 실제 테스트 (사용자 수동 확인) | | **진행률** | 4/4 (100%) | | **마지막 업데이트** | 2025-01-20 | --- ## 1. 개요 ### 1.1 배경 5130 레거시 시스템에서 SAM으로 품목(items) 마이그레이션이 완료되었으나, 완제품(FG)의 BOM 데이터가 마이그레이션되지 않아 다음 문제가 발생: ``` 문제 현상: - 견적 페이지에서 "국민방화스크린 (일체형) (S0001)" 선택 후 자동 견적 산출 → 합계 0원 - 원인: S0001의 bom 컬럼이 NULL - items 테이블에서 확인: SELECT bom FROM items WHERE code = 'S0001' → NULL ``` **기존 마이그레이션 상태:** - Items: 608건 (KDunitprice → items) - Orders: 24,424건 - Order Items: 43,900건 - ❌ BOM 데이터: 마이그레이션 안됨 ### 1.2 기준 원칙 ``` ┌─────────────────────────────────────────────────────────────────┐ │ 🎯 핵심 원칙 │ ├─────────────────────────────────────────────────────────────────┤ │ 1. FormulaEvaluatorService 호환 BOM JSON 형식 생성 │ │ 2. 동적 수량 계산을 위한 quantityFormula 필드 지원 │ │ 3. childItemCode 기반 참조 (child_item_id 아님) │ │ 4. 기존 SAM BOM 패턴과 일관성 유지 │ └─────────────────────────────────────────────────────────────────┘ ``` ### 1.3 변경 승인 정책 | 분류 | 예시 | 승인 | |------|------|------| | ✅ 즉시 가능 | BOM JSON 데이터 추가, 매핑 테이블 생성 | 불필요 | | ⚠️ 컨펌 필요 | 기존 items 데이터 수정, 새 마이그레이션 스크립트 | **필수** | | 🔴 금지 | items 테이블 구조 변경, 기존 BOM 삭제 | 별도 협의 | ### 1.4 준수 규칙 - `docs/quickstart/quick-start.md` - 빠른 시작 가이드 - `docs/standards/quality-checklist.md` - 품질 체크리스트 - `api/app/Services/Quote/FormulaEvaluatorService.php` - BOM 계산 로직 --- ## 2. 데이터 구조 분석 ### 2.1 5130 BOM 구조 ``` 5130 DB (chandj) ├── KDunitprice (품목 마스터) │ ├── prodcode: 품목 코드 │ ├── item_name: 품목명 │ └── item_div: [제품], [상품], [부재료], [원재료], [반제품] │ ├── models (모델 마스터) │ ├── model_id: PK │ ├── model_name: KSS01, KSE01, KWE01... (모델 코드) │ ├── major_category: 스크린 | 철재 │ ├── finishing_type: SUS마감 | EGI마감 │ └── guiderail_type: 벽면형 | 측면형 │ ├── parts (1단계 BOM - 모델별 부품) │ ├── part_id: PK │ ├── model_id: FK → models │ ├── part_name: 가이드레일, 하단마감재 등 │ ├── spec: 120*70, 60*40 등 │ ├── quantity: 수량 │ ├── unit: SET, EA 등 │ └── unitprice: 단가 (문자열, 콤마 포함) │ └── parts_sub (2단계 BOM - 부품별 원자재) ├── subpart_id: PK ├── part_id: FK → parts ├── subpart_name: 1번(마감제), 2번(본체) 등 ├── material: SUS 1.2T, EGI 1.55T 등 ├── quantity: 수량 ├── bendSum, plateSum, finalSum: 가공 관련 └── unitPrice, computedPrice, lineTotal: 금액 ``` **5130 model_id별 데이터 현황:** | model_id | model_name | category | finishing | guiderail | parts 수 | |----------|------------|----------|-----------|-----------|----------| | 12 | KSS01 | 스크린 | SUS마감 | 벽면형 | 2 | | 13 | KSS01 | 스크린 | SUS마감 | 측면형 | 2 | | 14 | KSE01 | 스크린 | SUS마감 | 벽면형 | 2 | | ... | ... | ... | ... | ... | ... | **5130 KDunitprice item_div 분포:** | item_div | 건수 | SAM item_type 매핑 | |----------|------|-------------------| | [제품] | 194건 | FG (완제품) | | [상품] | 260건 | SM (부자재) | | [부재료] | 48건 | SM (부자재) | | [원재료] | 24건 | RM (원자재) | | [반제품] | 73건 | SF (반제품) | | [무형상품] | 4건 | CS (서비스) | ### 2.2 SAM BOM 구조 ```sql -- SAM items 테이블 BOM 컬럼 items.bom: JSON ``` **SAM BOM JSON 형식 (FormulaEvaluatorService 호환):** ```json [ { "childItemCode": "SF-SCR-F01", // 필수: 하위 품목 코드 "quantity": 1, // 필수: 기본 수량 "quantityFormula": "W*H/1000000", // 선택: 동적 수량 계산식 "unit": "M2", // 선택: 단위 "note": "스크린 원단" // 선택: 비고 }, { "childItemCode": "SF-SCR-M01", "quantity": 1, "quantityFormula": "", "unit": "EA", "note": "소형용 모터" } ] ``` **기존 SAM BOM 예시 (FG-SCR-001):** ```json [ {"unit":"M2","quantity":1,"childItemCode":"SF-SCR-F01","quantityFormula":"W*H/1000000"}, {"unit":"M","quantity":1,"childItemCode":"SF-SCR-F02","quantityFormula":"H/1000"}, {"unit":"EA","quantity":1,"childItemCode":"SF-SCR-M01","quantityFormula":"","note":"소형용"}, {"unit":"EA","quantity":20,"childItemCode":"SM-B002","quantityFormula":"","note":"조립용"} ] ``` ### 2.3 핵심 차이점 | 항목 | 5130 | SAM | |------|------|-----| | **BOM 저장 위치** | parts/parts_sub 테이블 | items.bom JSON 컬럼 | | **연결 기준** | model_id (모델 기준) | childItemCode (품목 코드 기준) | | **수량 계산** | 고정값 + estimate.detailJson | quantityFormula 동적 계산 | | **단가 계산** | parts.unitprice 고정 | FormulaEvaluatorService 동적 | | **계층 구조** | 2단계 (parts → parts_sub) | 1단계 (flat JSON array) | --- ## 3. 마이그레이션 전략 ### 3.1 접근 방식: 수동 매핑 + 템플릿 기반 5130의 BOM 구조와 SAM의 BOM 구조가 근본적으로 다르기 때문에, 자동 변환이 아닌 **수동 매핑 + 템플릿 기반** 접근 필요: ``` ┌─────────────────────────────────────────────────────────────────┐ │ 전략: 완제품(FG) 유형별 BOM 템플릿 정의 │ ├─────────────────────────────────────────────────────────────────┤ │ 1. SCREEN 완제품 → screen_bom_template │ │ 2. STEEL 완제품 → steel_bom_template │ │ 3. BENDING 완제품 → bending_bom_template │ │ │ │ 각 템플릿은 FormulaEvaluatorService 호환 JSON 형식으로 정의 │ └─────────────────────────────────────────────────────────────────┘ ``` ### 3.2 완제품-모델 매핑 **매핑 대상 (SAM items WHERE item_type='FG' AND source='5130'):** ```sql -- SAM에서 5130에서 마이그레이션된 완제품 목록 SELECT id, code, name, item_category FROM items WHERE item_type = 'FG' AND (legacy_code IS NOT NULL OR code LIKE 'S%'); ``` **주요 완제품 매핑 예시:** | SAM code | SAM name | item_category | 5130 model | |----------|----------|---------------|------------| | S0001 | 국민방화스크린(일체형) | SCREEN | KSS01 (스크린/SUS/벽면형) | | S0002 | 국민방화스크린(분리형) | SCREEN | KSE01 (스크린/SUS/벽면형) | | ... | ... | ... | ... | ### 3.3 BOM 템플릿 정의 **SCREEN 완제품 BOM 템플릿:** ```json [ {"childItemCode": "RM-SCR-FABRIC", "quantity": 1, "quantityFormula": "W*H/1000000", "unit": "M2", "note": "스크린 원단"}, {"childItemCode": "PT-SCR-GUIDE", "quantity": 1, "quantityFormula": "H/1000", "unit": "M", "note": "가이드레일"}, {"childItemCode": "PT-SCR-BOTTOM", "quantity": 1, "quantityFormula": "W/1000", "unit": "M", "note": "하단바"}, {"childItemCode": "PT-SCR-CASE", "quantity": 1, "quantityFormula": "W/1000", "unit": "M", "note": "케이스"}, {"childItemCode": "PT-SCR-MOTOR", "quantity": 1, "quantityFormula": "", "unit": "EA", "note": "모터"} ] ``` --- ## 4. 작업 절차 ### 4.1 Phase 1: 하위 품목 확인 및 생성 | # | 작업 항목 | 상태 | 비고 | |---|----------|:----:|------| | 1.1 | BOM에 필요한 하위 품목(SF, PT, RM) 목록 정의 | ✅ | 52개 품목 정의됨 | | 1.2 | SAM items 테이블에 하위 품목 존재 여부 확인 | ✅ | 52개 모두 존재 확인 | | 1.3 | 누락된 하위 품목 생성 (필요시) | ✅ | 누락 품목 없음 (생성 불필요) | ### 4.2 Phase 2: BOM 템플릿 정의 | # | 작업 항목 | 상태 | 비고 | |---|----------|:----:|------| | 2.1 | SCREEN 완제품용 BOM 템플릿 정의 | ✅ | FG-SCR-001 (14개 항목) | | 2.2 | STEEL 완제품용 BOM 템플릿 정의 | ✅ | FG-STL-001 (12개 항목) | | 2.3 | BENDING 완제품용 BOM 템플릿 정의 | ✅ | FG-BND-001 (6개 항목) | ### 4.3 Phase 3: 마이그레이션 스크립트 작성 | # | 작업 항목 | 상태 | 비고 | |---|----------|:----:|------| | 3.1 | Migrate5130Bom 커맨드 생성 | ✅ | `api/app/Console/Commands/Migrate5130Bom.php` | | 3.2 | 완제품-템플릿 매핑 로직 구현 | ✅ | item_category 기반 매핑 | | 3.3 | items.bom 컬럼 업데이트 로직 구현 | ✅ | DB::table 직접 업데이트 | | 3.4 | 검증 로직 구현 | ✅ | dry-run, verbose 옵션 지원 | ### 4.4 Phase 4: 검증 및 테스트 | # | 작업 항목 | 상태 | 비고 | |---|----------|:----:|------| | 4.1 | Migrate5130Bom 커맨드 실행 | ✅ | 61건 처리 완료 | | 4.2 | 견적 페이지에서 실제 테스트 | ⏳ | 사용자 수동 확인 필요 | | 4.3 | 결과 문서화 | ✅ | 본 문서 업데이트 | --- ## 5. 기술 상세 ### 5.1 FormulaEvaluatorService BOM 처리 로직 ```php // api/app/Services/Quote/FormulaEvaluatorService.php // BOM JSON 필드 사용 위치: // 1. getBomItems() - bom JSON 파싱 // 2. calculateBomQuantity() - quantityFormula 평가 // 3. childItemCode로 하위 품목 조회 // 주요 변수: // - W0, H0: 개구부 치수 (입력값) // - W1, H1: 제작 치수 (계산값) // - W, H: W1, H1과 동일 // - M: 면적 (m²) // - K: 중량 (kg) ``` ### 5.2 마이그레이션 스크립트 구조 ```php // api/app/Console/Commands/Migrate5130Bom.php class Migrate5130Bom extends Command { protected $signature = 'migration:migrate-5130-bom {--dry-run : 실제 변경 없이 시뮬레이션} {--code= : 특정 품목 코드만 처리}'; // 1. item_category별 BOM 템플릿 정의 private array $bomTemplates = [ 'SCREEN' => [...], 'STEEL' => [...], 'BENDING' => [...] ]; // 2. 완제품 조회 (5130 마이그레이션된 FG) // 3. 템플릿 기반 BOM JSON 생성 // 4. items.bom 컬럼 업데이트 } ``` ### 5.3 검증 쿼리 ```sql -- 마이그레이션 전: BOM이 NULL인 완제품 SELECT code, name, item_category FROM items WHERE item_type = 'FG' AND item_category IN ('SCREEN', 'STEEL', 'BENDING') AND (bom IS NULL OR bom = '[]'); -- 마이그레이션 후: BOM이 있는 완제품 SELECT code, name, item_category, JSON_LENGTH(bom) as bom_count FROM items WHERE item_type = 'FG' AND item_category IN ('SCREEN', 'STEEL', 'BENDING') AND bom IS NOT NULL AND JSON_LENGTH(bom) > 0; ``` --- ## 6. 컨펌 대기 목록 > 모든 승인 항목 완료 | # | 항목 | 변경 내용 | 영향 범위 | 상태 | |---|------|----------|----------|------| | 1 | BOM 템플릿 확정 | SCREEN/STEEL/BENDING별 템플릿 | 견적 계산 | ✅ 완료 | | 2 | 하위 품목 코드 확정 | childItemCode 명명 규칙 | items 테이블 | ✅ 완료 | | 3 | 마이그레이션 실행 | items.bom 업데이트 | 완제품 61건 | ✅ 완료 | --- ## 7. 변경 이력 | 날짜 | 항목 | 변경 내용 | 파일 | 승인 | |------|------|----------|------|------| | 2025-01-20 | 초안 | 계획 문서 작성 | - | - | | 2025-01-20 | 분석 | 5130/SAM BOM 구조 분석 완료 | - | - | | 2025-01-20 | 스크립트 | Migrate5130Bom 커맨드 생성 | `api/app/Console/Commands/Migrate5130Bom.php` | ✅ | | 2025-01-20 | 실행 | BOM 마이그레이션 실행 (61건) | items.bom 컬럼 | ✅ | | 2025-01-20 | 문서화 | 결과 문서화 완료 | 본 문서 | ✅ | --- ## 8. 참고 문서 - **FormulaEvaluatorService**: `api/app/Services/Quote/FormulaEvaluatorService.php` - **기존 마이그레이션**: `api/app/Console/Commands/Migrate5130PriceItems.php` - **검증 커맨드**: `api/app/Console/Commands/Verify5130Calculation.php` - **품질 체크리스트**: `docs/standards/quality-checklist.md` --- ## 9. 세션 및 메모리 관리 정책 (Serena Optimized) ### 9.1 세션 시작 시 (Load Strategy) ```javascript // 순차적 로드 read_memory("5130-bom-migration-state") // 1. 상태 파악 read_memory("5130-bom-migration-rules") // 2. 규칙 확인 read_memory("5130-bom-migration-mappings") // 3. 매핑 확인 ``` ### 9.2 Serena 메모리 구조 - `5130-bom-migration-state`: { phase, progress, next_step, last_decision } - `5130-bom-migration-rules`: BOM 템플릿 정의, 변환 규칙 - `5130-bom-migration-mappings`: 완제품-모델 매핑 테이블 --- ## 10. 검증 결과 > 2025-01-20 마이그레이션 실행 완료 ### 10.1 마이그레이션 실행 결과 ``` 📊 카테고리별 BOM 적용 현황 (tenant_id=287): SCREEN: 35건 STEEL: 11건 BENDING: 15건 ✅ BOM 적용 완료: 61건 ⏳ BOM 미적용: 0건 ``` ### 10.2 테스트 케이스 | 입력값 | 예상 결과 | 실제 결과 | 상태 | |--------|----------|----------|------| | S0001 BOM JSON 확인 | childItemCode 5개 이상 | 14개 항목 적용됨 | ✅ | | S0001 + W0=2500, H0=2000 | 견적 금액 > 0 | 사용자 확인 필요 | ⏳ | ### 10.3 성공 기준 달성 현황 | 기준 | 달성 | 비고 | |------|------|------| | 완제품 BOM NULL → JSON 변환 | ✅ | 61건 변환 완료 | | BOM JSON 형식 호환 | ✅ | FormulaEvaluatorService 호환 형식 | | 견적 계산 정상 동작 | ⏳ | 사용자 수동 확인 필요 | ### 10.4 BOM 템플릿 상세 | 카테고리 | 소스 템플릿 | BOM 항목 수 | 적용 완제품 수 | |----------|------------|------------|--------------| | SCREEN | FG-SCR-001 | 14개 | 35건 | | STEEL | FG-STL-001 | 12개 | 11건 | | BENDING | FG-BND-001 | 6개 | 15건 | --- ## 11. 자기완결성 점검 결과 > Phase 5.5에서 수행된 자기완결성 점검 결과 ### 11.1 체크리스트 검증 | # | 검증 항목 | 상태 | 비고 | |---|----------|:----:|------| | 1 | 작업 목적이 명확한가? | ✅ | S0001 등 BOM NULL → 견적 0원 문제 해결 | | 2 | 성공 기준이 정의되어 있는가? | ✅ | 섹션 10.2 참조 | | 3 | 작업 범위가 구체적인가? | ✅ | SCREEN/STEEL/BENDING 완제품 대상 | | 4 | 의존성이 명시되어 있는가? | ✅ | FormulaEvaluatorService, 하위 품목 | | 5 | 참고 파일 경로가 정확한가? | ✅ | 섹션 8 참조 | | 6 | 단계별 절차가 실행 가능한가? | ✅ | 섹션 4 참조 | | 7 | 검증 방법이 명시되어 있는가? | ✅ | 섹션 10.1 참조 | | 8 | 모호한 표현이 없는가? | ✅ | 구체적 수치/조건 명시 | ### 11.2 새 세션 시뮬레이션 테스트 | 질문 | 답변 가능 | 참조 섹션 | |------|:--------:|----------| | Q1. 이 작업의 목적은 무엇인가? | ✅ | 1.1 배경 | | Q2. 어디서부터 시작해야 하는가? | ✅ | 4. 작업 절차 | | Q3. 어떤 파일을 수정해야 하는가? | ✅ | 5.2 마이그레이션 스크립트 | | Q4. 작업 완료 확인 방법은? | ✅ | 10. 검증 결과 | | Q5. 막혔을 때 참고 문서는? | ✅ | 8. 참고 문서 | **결과**: 5/5 통과 → ✅ 자기완결성 확보 --- *이 문서는 /plan 스킬로 생성되었습니다.*