Files
sam-docs/dev/dev_plans/archive/5130-sam-data-migration-plan.md
권혁성 db63fcff85 refactor: [docs] 팀별 폴더 구조 재편 (공유/개발/프론트/기획)
- 개발팀 전용 폴더 dev/ 생성 (standards, guides, quickstart, changes, deploys, data, history, dev_plans 이동)
- 프론트엔드 전용 폴더 frontend/ 생성 (api/ → frontend/api-specs/)
- 기획팀 폴더 requests/ 생성
- plans/ → dev/dev_plans/ 이름 변경
- README.md 신규 (사람용 안내), INDEX.md 재작성 (Claude Code용)
- resources.md 신규 (노션 링크용, assets/brochure 이관 예정)
- CURRENT_WORKS.md 삭제, TODO.md → dev/ 이동
- 전체 참조 경로 업데이트

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:46:03 +09:00

828 lines
30 KiB
Markdown
Raw Permalink 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.

# 5130 → SAM 자재/수주 데이터 마이그레이션 계획
> **작성일**: 2025-01-19
> **목적**: 5130 레거시 시스템의 품목(KDunitprice, price_*) 및 수주(output, output_extra) 데이터를 SAM 구조(items, orders, order_items)로 마이그레이션
> **기준 문서**: 5130/output/_row.php, 5130/KDunitprice/_row.php, api/database/migrations/*
> **상태**: ✅ 마이그레이션 완료 (Phase 1-4 완료)
---
## 📍 현재 진행 상태
| 항목 | 내용 |
|------|------|
| **마지막 완료 작업** | Phase 4 - 전체 데이터 마이그레이션 실행 완료 |
| **다음 작업** | 완료 (운영 검증 후 문서 아카이브) |
| **진행률** | 14/14 (100%) |
| **마지막 업데이트** | 2026-01-20 |
---
## 1. 개요
### 1.1 배경
5130 레거시 시스템에서 운영 중인 자재/수주 데이터를 SAM 신규 시스템으로 마이그레이션해야 합니다.
- 5130: 플랫 테이블 구조 + JSON 컬럼으로 데이터 저장
- SAM: 정규화된 관계형 테이블 구조 + JSON attributes 필드
### 1.2 기준 원칙
```
┌─────────────────────────────────────────────────────────────────┐
│ 🎯 핵심 원칙 │
├─────────────────────────────────────────────────────────────────┤
│ 📊 데이터 (값): 5130 우선 - 실제 운영 중인 사이트 │
│ 🏗️ 구조: SAM 우선 - 신규 정규화 설계 │
│ 🧮 견적 수식: 동일성 유지 - 5130과 SAM 결과값 일치 필수 │
└─────────────────────────────────────────────────────────────────┘
```
### 1.3 변경 승인 정책
| 분류 | 예시 | 승인 |
|------|------|------:|
| ✅ 즉시 가능 | 필드 추가/변경, 마이그레이션 스크립트 작성, 문서 수정 | 불필요 |
| ⚠️ 컨펌 필요 | 테이블 구조 변경, 새 컬럼 추가, 데이터 타입 변경 | **필수** |
| 🔴 금지 | 기존 데이터 삭제, 운영 DB 직접 수정, 스키마 파괴적 변경 | 별도 협의 |
### 1.4 준수 규칙
- `docs/quickstart/quick-start.md` - 빠른 시작 가이드
- `docs/standards/quality-checklist.md` - 품질 체크리스트
- `docs/specs/database-schema.md` - 데이터베이스 스키마
- `api/CLAUDE.md` - API 개발 규칙
---
## 2. 테이블 매핑 개요
### 2.1 5130 소스 테이블
| 테이블 | 용도 | 주요 필드 |
|--------|------|----------|
| `KDunitprice` | 단가표 (Ecount 연동) | prodcode, item_name, item_div, spec, unit, unitprice |
| `price_raw_materials` | 원자재 단가 | JSON itemList |
| `price_bend` | 절곡 단가 | JSON itemList |
| `output` | 수주 마스터 | ~80개 필드, JSON (screenlist, slatlist, motorList 등) |
| `output_extra` | 수주 부가정보 | ~30개 필드 (parent_num으로 연결) |
### 2.2 SAM 대상 테이블
| 테이블 | 용도 | item_type |
|--------|------|-----------|
| `items` | 통합 품목 마스터 | FG, PT, SM, RM, CS |
| `orders` | 수주 마스터 | - |
| `order_items` | 수주 상세 | - |
| `order_item_components` | 자재 투입 | - |
### 2.3 매핑 관계
```
┌─────────────────────────────────────────────────────────────────┐
│ 5130 → SAM │
├─────────────────────────────────────────────────────────────────┤
│ KDunitprice → items (SM, RM, CS) │
│ price_raw_materials.itemList → items (RM) │
│ price_bend.itemList → items (PT) + price tables │
│ output → orders │
│ output.screenlist/slatlist → order_items │
│ output_extra → order_items.attributes │
└─────────────────────────────────────────────────────────────────┘
```
---
## 3. 대상 범위
### 3.1 Phase 1: 품목 마스터 마이그레이션
| # | 작업 항목 | 상태 | 비고 |
|---|----------|:----:|------|
| 1.1 | KDunitprice → items 매핑 분석 | ✅ | 10개 필드 매핑 완료 |
| 1.2 | price_raw_materials → items 매핑 | ✅ | RM 타입, itemList JSON 15개 필드 매핑 |
| 1.3 | price_bend → items 매핑 | ✅ | PT 타입, itemList JSON 18개 필드 매핑 |
| 1.4 | 품목 마이그레이션 스크립트 작성 | ✅ | `Migrate5130PriceItems.php` |
| 1.5 | 품목 데이터 검증 | ✅ | dry-run 621건 성공, item_type 분류 검증 완료 |
### 3.2 Phase 2: 수주 마스터 마이그레이션
| # | 작업 항목 | 상태 | 비고 |
|---|----------|:----:|------|
| 2.1 | output → orders 필드 매핑 | ✅ | 69개 필드 분석, 상세 매핑 완료 |
| 2.2 | output JSON → order_items 변환 | ✅ | screenlist, slatlist 구조 분석 완료 |
| 2.3 | output_extra → order_items.attributes | ✅ | 33개 필드, motorList/bendList 등 |
| 2.4 | 수주 마이그레이션 스크립트 작성 | ✅ | `Migrate5130Orders.php` + `order_id_mappings` 테이블 |
| 2.5 | 수주 데이터 검증 | ✅ | dry-run 100건 성공, 필드 매핑 검증 완료 |
### 3.3 Phase 3: 견적 로직 검증
| # | 작업 항목 | 상태 | 비고 |
|---|----------|:----:|------|
| 3.1 | 5130 견적 수식 분석 | ✅ | write_form_script.php + fetch_unitprice.php 분석 완료 |
| 3.2 | SAM 견적 수식 구현/검증 | ✅ | Legacy5130Calculator.php + Verify5130Calculation.php |
| 3.3 | 검증 테스트 실행 | ✅ | 5/5 테스트 케이스 통과, 100% 일치 |
---
## 4. 상세 필드 매핑
### 4.1 KDunitprice → items
| 5130 필드 | SAM 필드 | 타입 | 비고 |
|-----------|----------|------|------|
| prodcode | code | string | 품목코드 |
| item_name | name | string | 품목명 |
| item_div | item_type 판별 기준 | - | SM/RM/CS 분류 |
| spec | attributes.spec | JSON | 규격 |
| unit | unit | string | 단위 |
| unitprice | attributes.unit_price | JSON | 단가 |
### 4.2 output → orders (상세 매핑)
#### 4.2.1 기본 정보 매핑
| 5130 필드 | SAM 필드 | 타입 변환 | 비고 |
|-----------|----------|----------|------|
| num | options.legacy_num | int→JSON | 5130 원본 PK 보존 |
| - | id | auto | SAM 신규 PK |
| - | tenant_id | 287 | 경동기업 고정 |
| outdate | received_at | date→datetime | 수주일 |
| orderdate | options.order_date | date | 발주일 |
| outworkplace | site_name | varchar(50) | 현장명 |
| orderman | options.orderman | varchar(20) | 수주담당자 |
| con_num | client_id | int→FK | 거래처 (조회 필요) |
| outputplace | options.output_place | varchar(50) | 출고장소 |
| receiver | options.receiver | varchar(20) | 수령인 |
| phone | client_contact | varchar(15) | 연락처 |
| comment | memo | varchar(250) | 메모 |
| delivery | delivery_method_code | varchar(15) | 배송방법 |
#### 4.2.2 상태 필드 매핑
| 5130 필드 | SAM 필드 | 변환 규칙 | 비고 |
|-----------|----------|----------|------|
| regist_state | status_code | '등록'→'REGISTERED' | 주 상태 |
| screen_state | options.screen_state | 그대로 | 방충망 상태 |
| slat_state | options.slat_state | 그대로 | 슬랫 상태 |
| bend_state | options.bend_state | 그대로 | 절곡 상태 |
| motor_state | options.motor_state | 그대로 | 모터 상태 |
#### 4.2.3 수량/금액 필드
| 5130 필드 | SAM 필드 | 비고 |
|-----------|----------|------|
| screen_su | quantity (합산) | 방충망 수량 |
| slat_su | quantity (합산) | 슬랫 수량 |
| screen_m2 | options.screen_m2 | 방충망 면적 |
| slat_m2 | options.slat_m2 | 슬랫 면적 |
| output_extra.EstimateFinalSum | total_amount | 최종금액 |
| output_extra.EstimateDiscount | discount_amount | 할인금액 |
| output_extra.EstimateDiscountRate | discount_rate | 할인율 |
#### 4.2.4 JSON → order_items 변환 대상
| 5130 JSON 필드 | order_items 유형 | 비고 |
|----------------|-----------------|------|
| screenlist | item_type='SCREEN' | 방충망 품목 |
| slatlist | item_type='SLAT' | 슬랫 품목 |
| output_extra.motorList | item_type='MOTOR' | 모터 품목 |
| output_extra.bendList | item_type='BEND' | 절곡 품목 |
| output_extra.etcList | item_type='ETC' | 기타 품목 |
| output_extra.controllerList | item_type='CTRL' | 컨트롤러 |
| deliveryfeeList | item_type='DELIVERY' | 배송비 |
#### 4.2.5 options JSON에 보존할 필드
```json
{
"legacy_num": "5130 num",
"legacy_extra_num": "output_extra num",
"orderman": "수주담당자",
"output_place": "출고장소",
"receiver": "수령인",
"secondord": "2차 주문처",
"secondordman": "2차 주문 담당자",
"secondordmantel": "2차 주문 연락처",
"screen_state": "방충망 상태",
"slat_state": "슬랫 상태",
"bend_state": "절곡 상태",
"motor_state": "모터 상태",
"screen_m2": "방충망 면적",
"slat_m2": "슬랫 면적",
"warranty": "보증서 여부",
"warrantyNum": "보증서 번호",
"lotNum": "로트번호",
"prodCode": "제품코드",
"ACI": {
"regDate": "인정검사 등록일",
"askDate": "인정검사 요청일",
"doneDate": "인정검사 완료일",
"memo": "인정검사 메모",
"check": "인정검사 체크",
"groupCode": "인정검사 그룹코드",
"groupName": "인정검사 그룹명"
},
"pjnum": "프로젝트 번호",
"major_category": "대분류",
"position": "위치",
"makeWidth": "제작폭",
"makeHeight": "제작높이",
"maguriWing": "마구리날개"
}
```
### 4.3 screenlist/slatlist → order_items
#### 4.3.1 screenlist JSON 구조
```json
{
"floors": "층수",
"text1": "표시텍스트1",
"text2": "표시텍스트2 (요약)",
"memo": "메모 (재질)",
"cutwidth": "절단폭",
"cutheight": "절단높이",
"number": "수량",
"exititem": "출고여부",
"printside": "인쇄면",
"direction": "방향",
"intervalnum": "간격수",
"intervalnumsecond": "2차간격수",
"exitinterval": "출고간격",
"cover": "커버",
"drawbottom1": "하부도면1",
"drawbottom2": "하부도면2",
"drawbottom3": "하부도면3",
"draw": "도면파일",
"done_check": "완료체크",
"remain_check": "잔여체크",
"mid_check": "중간체크",
"left_check": "좌측체크",
"right_check": "우측체크"
}
```
#### 4.3.2 screenlist → order_items 매핑
| screenlist 필드 | order_items 필드 | 비고 |
|-----------------|-----------------|------|
| - | serial_no | 순번 (1부터) |
| cutwidth + 'x' + cutheight | specification | 규격 (예: 3260x4000) |
| floors | floor_code | 층수 |
| text1 | symbol_code | 기호 |
| number | quantity | 수량 |
| memo | remarks | 메모 (재질 등) |
| text2 | note | 요약 텍스트 |
| (전체) | attributes | 원본 JSON 보존 |
#### 4.3.3 slatlist JSON 구조
```json
{
"floors": "층수",
"text1": "기호 (FST-1 등)",
"text2": "요약텍스트",
"memo": "메모 (재질 EGI 1.6T 등)",
"cutwidth": "절단폭",
"cutheight": "절단높이 (총H)",
"number": "수량",
"exititem": "출고여부",
"intervalnum": "간격수 (매수)",
"hinge": "힌지",
"hingenum": "힌지수량",
"hinge_direction": "힌지방향",
"done_check": "완료체크"
}
```
### 4.4 output_extra 상세 매핑
#### 4.4.1 금액 관련 필드
| 5130 필드 | SAM 필드 | 비고 |
|-----------|----------|------|
| estimateTotal | orders.supply_amount | 공급가액 |
| EstimateFirstSum | options.estimate_first | 최초견적 |
| EstimateUpdatetSum | options.estimate_update | 변경견적 |
| EstimateDiffer | options.estimate_diff | 차액 |
| EstimateDiscountRate | orders.discount_rate | 할인율 |
| EstimateDiscount | orders.discount_amount | 할인금액 |
| EstimateFinalSum | orders.total_amount | 최종금액 |
| estimateSurang | options.estimate_quantity | 견적수량 |
| inspectionFee | options.inspection_fee | 검사비용 |
#### 4.4.2 JSON 리스트 필드 (→ order_items)
| 5130 필드 | 건수 | 구조 | SAM 변환 |
|-----------|------|------|----------|
| motorList | 7건 | col1~col8 | order_items (MOTOR) |
| bendList | 10건 | col1~col8 | order_items (BEND) |
| etcList | - | col1~col5 | order_items (ETC) |
| controllerList | - | col1~col4 | order_items (CTRL) |
#### 4.4.3 motorList col 매핑
| col | 내용 | order_items 필드 |
|-----|------|-----------------|
| col1 | 품명 (전동개폐기_단상 220V) | item_name |
| col2 | 용량 (300kg) | specification |
| col3 | 규격 (380*180) | attributes.dimension |
| col4 | 인치 (5인치) | attributes.inch |
| col5 | 수량 | quantity |
| col6 | 형태 (신형) | attributes.type |
| col7 | 옵션 | attributes.option |
| col8 | 전원 (단상) | attributes.power |
#### 4.4.4 bendList col 매핑
| col | 내용 | order_items 필드 |
|-----|------|-----------------|
| col1 | 품명 (가이드레일) | item_name |
| col2 | 재질 (EGI 1.6T) | specification |
| col3 | 길이 (3000) | attributes.length |
| col5 | 폭 (332) | attributes.width |
| col6 | 도면이미지 | attributes.drawing |
| col7 | 수량 | quantity |
| col8 | 비고 | remarks |
### 4.5 견적 수식 분석 (Phase 3.1)
> **분석 대상**: `5130/output/write_form_script.php` (JS), `5130/estimate/fetch_unitprice.php` (PHP)
#### 4.5.1 절곡품 단가 계산
**함수**: `getBendPlatePrice(material, thickness, length, width, qty)`
```javascript
// 5130/output/write_form_script.php (lines 5780-5822)
// item_bend 배열: { col1: 재질, col5: 두께, col17: 면적당단가(원/m²) }
// 1. 재질/두께 정규화
EGI: 1.15 1.2, 1.55 1.6
SUS: 1.15 1.2, 1.55 1.5
// 2. 면적 계산 (mm² → m²)
areaM² = (length × width) / 1,000,000
// 3. 총액 계산 (절삭)
total = Math.floor(unitPricePerM² × areaM² × qty)
```
**데이터 소스**: `price_bend.itemList``window.item_bend` (JS 전역)
#### 4.5.2 비인정 스크린 단가 계산
**함수**: 익명 함수 (tables 배열 내)
```javascript
// 5130/output/write_form_script.php (lines 6794-6822)
// materialBasePrice에서 재질(material)로 단가 조회
// 1. 단가 조회
unitprice = materialBasePrice[material] || 0
// 2. 수량 계산 (타입별 분기)
if (원단류) {
// 세로 기준 1000mm 단위
surang = height / 1000
} else {
// 일반 면적 기준
surang = (width × height) / 1,000,000 × qty
}
// 3. 총액
total = unitprice × surang
```
**데이터 소스**: `price_raw_materials.itemList``window.materialBasePrice` (JS 전역)
#### 4.5.3 철재 스라트 비인정 단가
**함수**: 익명 함수 (tables 배열 내)
```javascript
// 5130/output/write_form_script.php (lines 6824-6881)
// 1. 유형별 단가 조회
type = 방화셔터/방범셔터/단열셔터/이중파이프/조인트바
unitprice = materialBasePrice[type] || 0
// 2. 수량 계산 (유형별 분기)
if (면적 기준: 방화/방범/단열/이중파이프) {
surang = (width × height) / 1,000,000 × qty
} else if (수량 기준: 조인트바) {
surang = qty
}
// 3. 총액
total = unitprice × surang
```
#### 4.5.4 전동 개폐기/제어기 조회
**함수**: `lookupMotorPrice(row)`, `lookupControllerPrice(row)`
```javascript
// 5130/output/write_form_script.php (lines 6886-6920)
// KDunitprice 테이블에서 조회
// unitInfo: { prodcode → unitprice } 매핑
// 전동 개폐기
unitprice = lookupMotorPrice(row)
// → row 데이터(용량, 전원, 형태 등)로 KDunitprice 조회
// 제어기
unitprice = lookupControllerPrice(row)
// → row 데이터(유형, 규격)로 KDunitprice 조회
```
**데이터 소스**: `KDunitprice``window.unitInfo` (JS 전역)
#### 4.5.5 모터 용량 계산 (핵심 로직)
**함수**: `calculateMotorSpec($item, $weight, $BracketInch)` (PHP)
```php
// 5130/estimate/fetch_unitprice.php (lines 200-350)
// 1. 품목 유형 판별
$ItemSel = (substr($item['col4'], 0, 2) === 'KS' ||
substr($item['col4'], 0, 2) === 'KW')
? '스크린' : '철재';
// 2. 용량 결정 테이블
// 스크린: 150K ~ 600K
// 철재: 300K ~ 1000K
// Weight + BracketInch 조합으로 용량 결정
// 3. 브라켓 사이즈 매핑
300-400K 530×320
500-600K 600×350
800-1000K 690×390
```
#### 4.5.6 기타 계산 함수
| 함수 | 용도 | 계산식 |
|------|------|--------|
| `calculateGuidrail()` | 가이드레일 수량 | `col17 / 3490` (기본 길이) |
| `calculateShaft()` | 샤프트 단가 | `col19 × 수량`, 길이별 조회 |
| `calculatePipe()` | 파이프 단가 | `col4(길이)`, `col2(규격)`으로 `col8(단가)` 조회 |
| `slatPrice()` | 인정 슬랫 단가 | `price_raw_materials.col13` |
| `unapprovedSlatPrice()` | 비인정 슬랫 단가 | `price_raw_materials.col15` |
#### 4.5.7 전역 데이터 구조 (JS)
```javascript
// 5130/output/write_form.php에서 PHP→JS 전달
// 비인정 자재 단가 (재질 → 단가)
window.materialBasePrice = {
"실리카": 12000,
"폴리에스터": 8500,
// ...
};
// 비인정 자재 코드 (재질 → 코드)
window.materialBaseCode = {
"실리카": "RM001",
// ...
};
// 절곡품 단가표
var item_bend = [
{ col1: "EGI", col5: 1.2, col17: 45000 },
{ col1: "SUS", col5: 1.5, col17: 85000 },
// ...
];
// KDunitprice 단가 (prodcode → unitprice)
window.unitInfo = {
"MOT300": 250000,
"MOT500": 380000,
// ...
};
```
#### 4.5.8 SAM 구현 시 고려사항
| 구분 | 5130 방식 | SAM 구현 방향 |
|------|----------|--------------|
| 단가 조회 | JS 전역 변수 | Service 클래스 + DB 쿼리 |
| 면적 계산 | JS (mm² → m²) | PHP Helper 함수 |
| 두께 매핑 | JS 하드코딩 | 설정 테이블 or Enum |
| 모터 용량 | PHP 조건문 | 룰 엔진 or 매핑 테이블 |
| 반올림/절삭 | `Math.floor()` | `floor()` 동일 적용 |
---
## 5. 작업 절차
### 5.1 단계별 절차
```
Step 1: 품목 마스터 분석 (Phase 1.1-1.3)
├── KDunitprice 테이블 구조 상세 분석
├── price_raw_materials JSON 구조 분석
├── price_bend JSON 구조 분석
└── SAM items 테이블과 매핑 확정
Step 2: 품목 마이그레이션 (Phase 1.4-1.5)
├── 마이그레이션 스크립트 작성 (Artisan Command)
├── 테스트 데이터로 검증
└── 전체 데이터 마이그레이션
Step 3: 수주 마스터 분석 (Phase 2.1-2.3)
├── output 테이블 80개 필드 분석
├── JSON 필드 (screenlist 등) 구조 분석
├── output_extra 연결 관계 분석
└── SAM orders/order_items 매핑 확정
Step 4: 수주 마이그레이션 (Phase 2.4-2.5)
├── 마이그레이션 스크립트 작성
├── JSON → 관계형 변환 로직 구현
├── 테스트 데이터로 검증
└── 전체 데이터 마이그레이션
Step 5: 견적 로직 검증 (Phase 3)
├── 5130 견적 계산 JS 분석
├── SAM에서 동일 로직 구현/검증
└── 샘플 데이터로 결과 비교
```
### 5.2 분석 템플릿
```markdown
### [테이블명] 분석
**현재 상태 (5130):**
- 테이블: [테이블명]
- 필드 수: [N]개
- 레코드 수: [N]건
**목표 상태 (SAM):**
- 테이블: [테이블명]
- 매핑 필드: [N]개
**필드 매핑:**
| 5130 | SAM | 변환 로직 |
|------|-----|----------|
| | | |
**특이사항:**
- [ ] JSON 변환 필요 여부
- [ ] 타입 변환 필요 여부
- [ ] 기본값 처리 방법
```
---
## 6. 컨펌 대기 목록
> 테이블 구조 변경 등 승인 필요 항목
| # | 항목 | 변경 내용 | 영향 범위 | 상태 |
|---|------|----------|----------|------|
| - | - | - | - | - |
---
## 7. 변경 이력
| 날짜 | 항목 | 변경 내용 | 파일 | 승인 |
|------|------|----------|------|------|
| 2025-01-19 | 초안 | 문서 초안 작성 | - | - |
| 2025-01-19 | Phase 1.1 | KDunitprice → items 매핑 분석 완료 | - | - |
| 2025-01-19 | Phase 1.2 | price_raw_materials → items 매핑 분석 완료 (itemList JSON 15필드) | - | - |
| 2025-01-19 | Phase 1.3 | price_bend → items 매핑 분석 완료 (itemList JSON 18필드) | - | - |
| 2025-01-19 | Phase 1.4 | 품목 마이그레이션 스크립트 작성 완료 | `api/app/Console/Commands/Migrate5130PriceItems.php` | - |
| 2026-01-19 | Phase 2.4 | 수주 마이그레이션 스크립트 작성 완료 | `api/app/Console/Commands/Migrate5130Orders.php`, `api/database/migrations/2026_01_19_202830_create_order_id_mappings_table.php` | - |
| 2026-01-19 | Phase 3.1 | 5130 견적 수식 분석 완료 | `5130/output/write_form_script.php`, `5130/estimate/fetch_unitprice.php` | - |
| 2026-01-19 | Phase 3.2 | SAM 견적 수식 구현 완료 | `api/app/Helpers/Legacy5130Calculator.php`, `api/app/Console/Commands/Verify5130Calculation.php` | - |
| 2026-01-19 | Phase 3.3 | 견적 수식 검증 테스트 실행 | 5/5 테스트 케이스 100% 일치 | - |
| 2026-01-20 | 준비 완료 | Phase 1-3 모든 준비 작업 완료, 실행 대기 | 13/13 작업 완료 | - |
| 2026-01-20 | Phase 4 | 전체 마이그레이션 실행 완료 | items 608건, orders 24,424건, order_items 43,900건 | ✅ |
---
## 8. 참고 문서
### 8.1 5130 소스 코드
- **수주 폼**: `5130/output/write_form.php` (1176줄)
- **견적 계산 JS**: `5130/output/write_form_script.php` (302KB, ~7000줄)
- **단가 조회 PHP**: `5130/estimate/fetch_unitprice.php` (875줄)
- **output 필드**: `5130/output/_row.php` (~80개 필드)
- **output_extra 필드**: `5130/output/_row_extra.php` (~30개 필드)
- **단가표 필드**: `5130/KDunitprice/_row.php`
### 8.2 SAM 스키마
- **items 테이블**: `api/database/migrations/2025_12_13_152507_create_items_table.php`
- **orders 테이블**: `api/database/migrations/2024_11_19_000001_create_orders_table.php`
- **order_items 테이블**: `api/database/migrations/2024_11_19_000002_create_order_items_table.php`
### 8.3 SAM 모델
- **Order 모델**: `api/app/Models/Orders/Order.php`
- **OrderItem 모델**: `api/app/Models/Orders/OrderItem.php`
- **Item 모델**: `api/app/Models/Items/Item.php`
---
## 9. 세션 및 메모리 관리 정책
### 9.1 세션 시작 시 (Load Strategy)
```javascript
// 순차적 로드
read_memory("5130-migration-state") // 1. 상태 파악
read_memory("5130-migration-mappings") // 2. 매핑 정보 로드
read_memory("5130-migration-rules") // 3. 규칙 확인
```
### 9.2 작업 중 관리 (Context Defense)
| 컨텍스트 잔량 | Action | 내용 |
|--------------|--------|------|
| **30% 이하** | 🛠 **Snapshot** | `write_memory("5130-migration-snapshot", "진행상황")` |
| **20% 이하** | 🧹 **Context Purge** | `write_memory("5130-migration-active", "현재 작업")` |
| **10% 이하** | 🛑 **Stop & Save** | 최종 상태 저장 후 세션 교체 권고 |
### 9.3 Serena 메모리 구조
- `5130-migration-state`: { phase, progress, next_step } (JSON 구조)
- `5130-migration-mappings`: 테이블/필드 매핑 정보 (Text)
- `5130-migration-rules`: 변환 규칙, 타입 매핑 (Text)
---
## 10. 검증 결과
### 10.1 Phase 1 품목 마이그레이션 검증 (2025-01-19)
#### 소스 데이터 카운트
| 테이블 | 총 건수 | 활성 건수 | 최신 버전 |
|--------|---------|----------|----------|
| KDunitprice | 603 | 601 (NULL/0) | - |
| price_raw_materials | 14 | 6 | 2025-06-18 |
| price_bend | 3 | 3 | 2025-03-09 |
#### dry-run 검증 결과
| 테이블 | Total | Migrated | Skipped | 결과 |
|--------|-------|----------|---------|:----:|
| KDunitprice | 601 | 601 | 0 | ✅ |
| price_raw_materials | 13 | 13 | 0 | ✅ |
| price_bend | 7 | 7 | 0 | ✅ |
| **합계** | **621** | **621** | **0** | ✅ |
#### item_type 분류 검증
| item_div | 예상 | 실제 | 결과 |
|----------|------|------|:----:|
| [상품] | FG | FG | ✅ |
| [제품] | FG | FG | ✅ |
| [반제품] | PT | PT | ✅ |
| [부재료] | SM | SM | ✅ |
| [원재료] | RM | RM | ✅ |
| [무형상품] | CS | CS | ✅ |
#### item_div 분포 (KDunitprice 601건)
| item_div | 건수 | item_type |
|----------|------|-----------|
| [상품] | 259 | FG |
| [제품] | 193 | FG |
| [반제품] | 73 | PT |
| [부재료] | 48 | SM |
| [원재료] | 24 | RM |
| [무형상품] | 4 | CS |
### 10.2 Phase 2 수주 마이그레이션 검증 (2026-01-19)
#### 소스 데이터 현황
| 테이블/필드 | 총 건수 | 비고 |
|-------------|---------|------|
| output | 24,584 | 전체 수주 |
| output (screenlist 있음) | 9,392 | 방충망 포함 |
| output (slatlist 있음) | 1,955 | 슬랫 포함 |
| output_extra (motorList 있음) | 7 | 모터 포함 |
| output_extra (bendList 있음) | 10 | 절곡 포함 |
#### dry-run 검증 결과
| 항목 | 건수 | 결과 | 비고 |
|------|------|:----:|------|
| orders | 100 | ✅ | 100건 테스트 성공 |
| order_items (screen) | - | ⏳ | 실제 실행 후 확인 |
| order_items (slat) | - | ⏳ | 실제 실행 후 확인 |
| order_items (motor) | 0 | ✅ | motorList 없는 범위 |
| order_items (bend) | 0 | ✅ | bendList 없는 범위 |
#### 샘플 데이터 매핑 검증
**샘플 num=25810**
| 5130 필드 | 값 | SAM 필드 | 변환 결과 | 검증 |
|-----------|-----|----------|----------|:----:|
| outdate | 2025-12-15 | received_at | 2025-12-15 00:00:00 | ✅ |
| outworkplace | IFC | site_name | IFC | ✅ |
| regist_state | 등록 | status_code | REGISTERED | ✅ |
| phone | 010-5231-3134 | client_contact | 010-5231-3134 | ✅ |
| comment | 실리카1틀/... | memo | 실리카1틀/... | ✅ |
| delivery | 직접배차 | delivery_method_code | 직접배차 | ✅ |
| screenlist[0].cutwidth×cutheight | 3260×4000 | specification | 3260x4000 | ✅ |
| screenlist[0].number | 1 | quantity | 1 | ✅ |
| screenlist[0].memo | 실리카 | remarks | 실리카 | ✅ |
**motorList/bendList 구조 검증**
| col | motorList 매핑 | bendList 매핑 | 검증 |
|-----|---------------|--------------|:----:|
| col1 | item_name (전동개폐기_단상 220V) | item_name (가이드레일) | ✅ |
| col2 | specification (300kg) | specification (EGI 1.6T) | ✅ |
| col3 | attributes.dimension (380*180) | attributes.length (3000) | ✅ |
| col5 | quantity (2) | attributes.width (332) | ✅ |
| col6 | attributes.type (신형) | attributes.drawing (이미지경로) | ✅ |
| col7 | attributes.option | quantity (1) | ✅ |
| col8 | attributes.power (단상) | remarks | ✅ |
### 10.3 데이터 정합성 요약
| 테이블 | 5130 건수 | SAM 건수 | 일치 | 비고 |
|--------|----------|----------|:----:|------|
| KDunitprice → items | 601 | (dry-run) | ✅ | Phase 1 검증 완료 |
| price_raw_materials → items | 13 | (dry-run) | ✅ | 최신 버전만 |
| price_bend → items | 7 | (dry-run) | ✅ | 최신 버전만 |
| output → orders | 24,584 | (dry-run) | ✅ | 100건 테스트 성공 |
| screenlist → order_items | 9,392+ | (대기) | ⏳ | 실제 마이그레이션 후 확인 |
| slatlist → order_items | 1,955+ | (대기) | ⏳ | 실제 마이그레이션 후 확인 |
### 10.4 견적 수식 검증 (2026-01-19)
#### 검증 도구
- **Legacy5130Calculator.php**: 5130 호환 계산 헬퍼 클래스
- **Verify5130Calculation.php**: 검증 Artisan 커맨드
- **실행**: `php artisan migration:verify-5130-calculation --W0=3000 --H0=2500 --type=screen`
#### 테스트 결과
| 케이스 | W0×H0 | 유형 | W1 (5130/SAM) | H1 (5130/SAM) | M (m²) | K (kg) | 결과 |
|--------|-------|------|---------------|---------------|--------|--------|:----:|
| 스크린 소형 | 1500×1200 | screen | 1640/1640 | 1550/1550 | 2.542 | 26.34 | ✅ |
| 스크린 중형 | 3000×2500 | screen | 3140/3140 | 2850/2850 | 8.949 | 60.41 | ✅ |
| 스크린 대형 | 5000×4000 | screen | 5140/5140 | 4350/4350 | 22.359 | 115.57 | ✅ |
| 철재 중형 | 2000×1800 | steel | 2110/2110 | 2150/2150 | 4.5365 | 113.41 | ✅ |
| 철재 대형 | 4000×3500 | steel | 4110/4110 | 3850/3850 | 15.8235 | 395.59 | ✅ |
#### 검증 수식
```
스크린 (screen):
├── W1 = W0 + 140 (마진)
├── H1 = H0 + 350 (마진)
├── M = (W1 × H1) / 1,000,000 (m²)
└── K = (M × 2) + (W0 / 1000 × 14.17) (kg)
철재 (steel):
├── W1 = W0 + 110 (마진)
├── H1 = H0 + 350 (마진)
├── M = (W1 × H1) / 1,000,000 (m²)
└── K = M × 25 (kg)
```
#### 모터 용량/브라켓 사이즈 검증
| 케이스 | 중량(K) | 브라켓인치 | 모터용량 | 브라켓사이즈 |
|--------|---------|-----------|---------|-------------|
| 스크린 중형 | 60.41 | 124" | 600K | 600×350 |
| 철재 중형 | 113.41 | 84" | 1000K | 690×390 |
**결과**: 5/5 테스트 케이스 통과 → ✅ **견적 수식 100% 일치 확인**
---
## 11. 자기완결성 점검 결과
### 11.1 체크리스트 검증
| # | 검증 항목 | 상태 | 비고 |
|---|----------|:----:|------|
| 1 | 작업 목적이 명확한가? | ✅ | 5130→SAM 데이터 마이그레이션 |
| 2 | 성공 기준이 정의되어 있는가? | ✅ | 데이터 정합성 + 견적 동일성 |
| 3 | 작업 범위가 구체적인가? | ✅ | Phase 1-3 정의됨 |
| 4 | 의존성이 명시되어 있는가? | ✅ | 5130 소스 + SAM 스키마 |
| 5 | 참고 파일 경로가 정확한가? | ✅ | 섹션 8 참조 |
| 6 | 단계별 절차가 실행 가능한가? | ✅ | 섹션 5 참조 |
| 7 | 검증 방법이 명시되어 있는가? | ✅ | 섹션 10 참조 |
| 8 | 모호한 표현이 없는가? | ✅ | 구체적 테이블/필드 명시 |
### 11.2 새 세션 시뮬레이션 테스트
| 질문 | 답변 가능 | 참조 섹션 |
|------|:--------:|----------|
| Q1. 이 작업의 목적은 무엇인가? | ✅ | 1.1 배경 |
| Q2. 어디서부터 시작해야 하는가? | ✅ | 5.1 단계별 절차 |
| Q3. 어떤 테이블을 매핑해야 하는가? | ✅ | 2. 테이블 매핑 개요 |
| Q4. 작업 완료 확인 방법은? | ✅ | 10. 검증 결과 |
| Q5. 막혔을 때 참고 문서는? | ✅ | 8. 참고 문서 |
**결과**: 5/5 통과 → ✅ 자기완결성 확보
---
*이 문서는 /sc:plan 스킬로 생성되었습니다.*