- 문서관리 1단계 변경사항 (20260128_document_management_phase1_1.md) - FCM 사용자 타겟 알림 계획 - 수입검사 문서 통합 계획 - 품목 마이그레이션 계획 (경동) - 수주 마이그레이션 계획 (경동) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
34 KiB
34 KiB
경동기업(5130) 입고/재고/주문 마이그레이션 계획
작성일: 2026-01-28 목적: 경동기업 레거시 시스템(5130/)의 입고(instock), 재고(stocks), 주문(output) 데이터를 SAM으로 이관 기준 문서:
5130/폴더 분석 결과 상태: ⏳ 대기 (품목 마이그레이션 선행 필요) 데이터 규모: ~78,000 레코드 (입고 2,286 + 재고 ~500 + 주문 75,000+) 선행 조건:kd-items-migration-plan.md완료 필수
🚀 새 세션 시작 가이드 (Quick Start)
이 문서만 보고 작업을 재개하려면:
# 1. Docker 서비스 확인
docker ps | grep sam
# 2. 선행 조건 확인 (items 마이그레이션 완료 여부)
docker exec sam-mysql-1 mysql -uroot -proot samdb -e "SELECT COUNT(*) FROM items WHERE tenant_id=287;"
# → 최소 600건 이상이어야 함
# 3. 레거시 DB 테스트
docker exec sam-mysql-1 mysql -uroot -proot chandj -e "SELECT COUNT(*) FROM output;"
# 4. 현재 진행 상태 확인
# → 아래 "📍 현재 진행 상태" 섹션 참조
환경 정보
| 항목 | 값 |
|---|---|
| 프로젝트 루트 | /Users/kent/Works/@KD_SAM/SAM |
| 레거시 소스 | 5130/ (프로젝트 루트 직하) |
| API 프로젝트 | api/ |
| Docker 컨테이너 | sam-mysql-1 |
| 레거시 DB | chandj (MySQL) |
| SAM DB | samdb (MySQL) ⚠️ |
| 대상 테넌트 ID | 287 (경동기업) |
| 생성자 사용자 ID | 1 |
DB 접속 명령어
# 레거시 DB (chandj) 접속
docker exec -it sam-mysql-1 mysql -uroot -proot chandj
# SAM DB 접속
docker exec -it sam-mysql-1 mysql -uroot -proot samdb
# 입고 기록 확인
docker exec sam-mysql-1 mysql -uroot -proot chandj -e "SELECT COUNT(*) FROM instock;"
# 주문 기록 확인
docker exec sam-mysql-1 mysql -uroot -proot chandj -e "SELECT COUNT(*) FROM output;"
전제 조건 (작업 전 확인)
- Docker 서비스 실행 중
sam-mysql-1컨테이너 실행 중- chandj 데이터베이스 접근 가능
- ⚠️ 품목 마이그레이션 완료 (
kd-items-migration-plan.md) - SAM orders 마이그레이션 실행 완료 (
php artisan migrate) - SAM item_receipts 마이그레이션 실행 완료
📍 현재 진행 상태
| 항목 | 내용 |
|---|---|
| 마지막 완료 작업 | 문서 분리 완료 (items + orders 분리) |
| 다음 작업 | ⏳ 품목 마이그레이션 완료 대기 |
| 진행률 | 0/2 (0%) - 대기 중 |
| 마지막 업데이트 | 2026-01-28 |
시작 조건
이 문서의 작업을 시작하기 전:
- ✅
kd-items-migration-plan.mdPhase 1~4 완료 - ✅ SAM items 테이블에 ~800건 이상 존재
- ✅ SAM prices 테이블에 ~500건 이상 존재
-- 시작 조건 확인 쿼리
SELECT
(SELECT COUNT(*) FROM items WHERE tenant_id=287) AS items_count,
(SELECT COUNT(*) FROM prices WHERE tenant_id=287) AS prices_count;
-- items_count >= 700, prices_count >= 400 이어야 시작 가능
0. 성공 기준
| 기준 | 목표값 | 확인 방법 |
|---|---|---|
| item_receipts 합계 | ~2,300건 | SELECT COUNT(*) FROM item_receipts WHERE tenant_id=287 |
| stocks 합계 | ~500건 | SELECT COUNT(*) FROM stocks WHERE tenant_id=287 |
| lots 합계 | ~200건 | SELECT COUNT(*) FROM lots WHERE tenant_id=287 |
| lot_sales 합계 | ~300건 | SELECT COUNT(*) FROM lot_sales WHERE tenant_id=287 |
| orders 합계 | ~25,000건 | SELECT COUNT(*) FROM orders WHERE tenant_id=287 |
| order_items 합계 | ~50,000건 | SELECT COUNT(*) FROM order_items WHERE tenant_id=287 |
| item_id 연결율 | 100% | SELECT COUNT(*) FROM item_receipts WHERE item_id IS NULL (0건) |
| API 테스트 | 100% | /api/v1/orders 목록 조회 성공 |
1. 개요
1.1 배경
경동기업 레거시 시스템의 입고/재고/주문 데이터를 SAM으로 이관. 이 작업은 품목(items) 마이그레이션 완료 후 진행해야 함 (item_id FK 참조 필요).
1.2 핵심 차이점
┌────────────────────────────────────────────────────────────────────────────┐
│ 레거시 (chandj) → SAM (samdb) │
├────────────────────────────────────────────────────────────────────────────┤
│ 📥 입고/재고 │
│ ───────────────────────────────────────────────────────────────────────── │
│ instock (2,286건) → item_receipts + stocks │
│ lot, lot_sales → lots + lot_sales │
│ │
│ 📋 주문/출고 │
│ ───────────────────────────────────────────────────────────────────────── │
│ output (24,564건) → orders + order_items │
│ output.iList (JSON 파일 참조) → orders.options │
│ estimate → orders (type=견적) │
└────────────────────────────────────────────────────────────────────────────┘
1.3 output.iList JSON 파일 구조 ⭐
-- output 테이블의 iList 컬럼
-- 값: "../output/i_json/22545.json" (파일 경로!)
-- 실제 파일 위치: 5130/output/i_json/{output_id}.json
JSON 파일 내용 예시 (5130/output/i_json/22545.json):
{
"inputValue": [
"2024-12-03", // 날짜
"명보에스티", // 거래처명
"KWE01 전체적인 테스트", // 모델/설명
// ... 추가 입력값들
],
"beforeWidth": ["8000", "7000"], // 변경전 폭
"beforeHeight": ["4000", "3500"], // 변경전 높이
"afterWidth": ["8000", "7000"], // 변경후 폭
"afterHeight": ["4000", "3500"], // 변경후 높이
"pages": [
{
"page": "1",
"inputItems": {
"openWidth": "8000",
"openHeight": "4000",
// ... 기타 치수 정보
},
"checkboxData": [...]
}
],
"approval": {
"writer": {"name": "개발자", "date": "25/01/02"},
"approver": {"name": "관리자", "date": "25/01/03"}
}
}
SAM 매핑:
inputValue→orders.options(JSON)pages→order_items.options(JSON)approval→orders.approved_by,orders.approved_atbeforeWidth/Height,afterWidth/Height→order_items.options.dimensions
2. 레거시 DB 구조 분석
2.1 핵심 테이블 및 레코드 수
📥 입고/재고 테이블
| 테이블 | 레코드 수 | 역할 | SAM 매핑 |
|---|---|---|---|
instock ⭐ |
2,286 | 입고 기록 | item_receipts + stocks |
lot |
~200 | 로트 관리 | lots |
lot_sales |
~300 | 로트 소진 | lot_sales |
📋 주문/출고 테이블
| 테이블 | 레코드 수 | 역할 | SAM 매핑 |
|---|---|---|---|
output ⭐ |
24,564 | 주문/출고 기록 | orders + order_items |
estimate |
~500 | 견적 | orders (type=견적) |
2.2 instock 테이블 구조 ⭐
-- instock: 입고 기록 (2,286건)
-- ⚠️ 실제 컬럼명 (2026-01-28 확인됨)
num INT PRIMARY KEY, -- PK ⭐
is_deleted INT, -- 삭제 여부
item_name VARCHAR(255), -- 품목명
prodcode VARCHAR(50), -- items.code와 매칭 ⭐
iList TEXT, -- 관련 정보 (JSON?)
lot_no VARCHAR(100), -- 로트번호
lotDone INT, -- 로트 완료 여부
inspection_date DATE, -- 검수일 (입고일로 사용) ⭐
supplier VARCHAR(255), -- 공급업체
specification VARCHAR(255), -- 규격
unit VARCHAR(20), -- 단위
received_qty DECIMAL, -- 입고 수량 ⭐
material_no VARCHAR(100), -- 자재번호
manufacturer VARCHAR(255), -- 제조사
remarks TEXT, -- 비고 ⭐
purchase_price_excl_vat DECIMAL, -- 단가 (부가세 제외) ⭐
weight_kg DECIMAL, -- 중량
searchtag TEXT, -- 검색 태그
update_log TEXT -- 변경 이력
2.3 output 테이블 구조 ⭐
-- output: 주문/출고 기록 (24,564건)
-- ⚠️ 실제 컬럼명 (2026-01-28 확인됨) - 70+ 컬럼 중 주요 컬럼만 표시
num INT PRIMARY KEY, -- PK ⭐ (output_id 대신)
secondordnum VARCHAR(50), -- 2차 주문번호
iList VARCHAR(255), -- JSON 파일 경로 (../output/i_json/xxx.json) ⭐
COD VARCHAR(50), -- COD 코드
con_num VARCHAR(50), -- 계약번호
is_deleted INT, -- 삭제 여부
outdate DATE, -- 출고일 (order_date 대신) ⭐
indate DATE, -- 입고일/등록일
outworkplace VARCHAR(255), -- 출고처/거래처 ⭐
orderman VARCHAR(100), -- 주문자
outputplace VARCHAR(255), -- 출력처
receiver VARCHAR(100), -- 수령자
phone VARCHAR(50), -- 전화번호
comment TEXT, -- 비고 (memo 대신) ⭐
-- ... 이하 70+ 컬럼 (상세 분석 필요)
-- 참고: 전체 컬럼 목록 확인 필요
-- docker exec sam-mysql-1 mysql -uroot -proot chandj -e "DESCRIBE output;"
output 테이블 전체 컬럼 확인 명령:
docker exec sam-mysql-1 mysql -uroot -proot chandj -e "DESCRIBE output;" | head -80
3. SAM 테이블 구조 (Target)
3.1 item_receipts 테이블
CREATE TABLE item_receipts (
id BIGINT PRIMARY KEY,
tenant_id BIGINT NOT NULL, -- 287 (경동기업)
item_id BIGINT NOT NULL, -- items.id FK ⭐
receipt_date DATE NOT NULL, -- 입고일
quantity DECIMAL(15,4) NOT NULL, -- 수량
unit_price DECIMAL(15,4), -- 단가
total_amount DECIMAL(15,4), -- 금액
supplier_id BIGINT, -- 공급업체 ID
lot_id BIGINT, -- 로트 ID
note TEXT,
created_by, updated_by, deleted_by, timestamps, soft_deletes
);
3.2 stocks 테이블
CREATE TABLE stocks (
id BIGINT PRIMARY KEY,
tenant_id BIGINT NOT NULL,
item_id BIGINT NOT NULL, -- items.id FK
warehouse_id BIGINT, -- 창고 ID
quantity DECIMAL(15,4) NOT NULL, -- 현재고
reserved_qty DECIMAL(15,4) DEFAULT 0, -- 예약수량
available_qty DECIMAL(15,4), -- 가용재고
last_movement_at TIMESTAMP,
created_by, updated_by, timestamps
);
3.3 orders 테이블
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
tenant_id BIGINT NOT NULL,
order_no VARCHAR(50) NOT NULL, -- 주문번호
order_type VARCHAR(20) NOT NULL, -- 주문/견적
order_date DATE NOT NULL,
delivery_date DATE,
client_id BIGINT, -- 거래처 ID
status VARCHAR(30), -- 상태
total_amount DECIMAL(15,4),
options JSON, -- iList JSON 데이터 ⭐
approved_by BIGINT,
approved_at TIMESTAMP,
note TEXT,
created_by, updated_by, deleted_by, timestamps, soft_deletes
);
3.4 order_items 테이블
CREATE TABLE order_items (
id BIGINT PRIMARY KEY,
tenant_id BIGINT NOT NULL,
order_id BIGINT NOT NULL, -- orders.id FK
item_id BIGINT, -- items.id FK (nullable - 신규품목 가능)
seq_no INT NOT NULL, -- 순번
item_code VARCHAR(100),
item_name VARCHAR(255),
quantity DECIMAL(15,4) NOT NULL,
unit_price DECIMAL(15,4),
amount DECIMAL(15,4),
options JSON, -- pages[n] JSON 데이터 ⭐
note TEXT,
created_by, updated_by, timestamps
);
4. 대상 범위
4.1 Phase 5: 입고/재고 데이터 이관 ⭐
| # | 작업 항목 | 상태 | 비고 |
|---|---|---|---|
| 5.1 | instock 테이블 구조 분석 | ⏳ | 컬럼 확인 필요 |
| 5.2 | instock → item_receipts 매핑 설계 | ⏳ | item_code → item_id |
| 5.3 | instock → item_receipts INSERT | ⏳ | 2,286건 |
| 5.4 | instock 재고 집계 → stocks | ⏳ | 품목별 현재고 |
| 5.5 | lot → lots | ⏳ | 로트 관리 |
| 5.6 | lot_sales → lot_sales | ⏳ | 로트 소진 |
| 5.7 | ⚠️ 사용자 승인: 입고/재고 INSERT 실행 | ⏳ |
4.2 Phase 6: 주문/출고 데이터 이관 ⭐
| # | 작업 항목 | 상태 | 비고 |
|---|---|---|---|
| 6.1 | output 테이블 구조 분석 | ⏳ | 컬럼 확인 필요 |
| 6.2 | output → orders 헤더 INSERT | ⏳ | 24,564건 |
| 6.3 | output.iList JSON 파일 파싱 | ⏳ | 파일 경로 → JSON 읽기 |
| 6.4 | JSON → order_items 생성 | ⏳ | pages 배열 처리 |
| 6.5 | JSON.approval → orders 승인 정보 | ⏳ | approved_by, approved_at |
| 6.6 | estimate → orders (type=견적) | ⏳ | 견적 데이터 |
| 6.7 | ⚠️ 사용자 승인: 주문/출고 INSERT 실행 | ⏳ |
5. SQL 쿼리 / 스크립트
5.1 instock → item_receipts
-- 입고 데이터 이관 (prodcode로 item_id 조회)
-- ⚠️ 실제 컬럼명 사용 (2026-01-28 확인됨)
INSERT INTO samdb.item_receipts (
tenant_id, item_id, receipt_date, quantity,
unit_price, total_amount, note,
created_by, created_at, updated_at
)
SELECT
287 AS tenant_id,
i.id AS item_id,
ins.inspection_date AS receipt_date, -- ⭐ inspection_date 사용
ins.received_qty AS quantity, -- ⭐ received_qty 사용
ins.purchase_price_excl_vat AS unit_price, -- ⭐ purchase_price_excl_vat 사용
(ins.received_qty * COALESCE(ins.purchase_price_excl_vat, 0)) AS total_amount, -- 계산
CONCAT_WS(' | ',
ins.remarks,
CONCAT('supplier:', ins.supplier),
CONCAT('manufacturer:', ins.manufacturer),
CONCAT('material_no:', ins.material_no)
) AS note, -- ⭐ remarks + 추가 정보
1 AS created_by,
NOW(), NOW()
FROM chandj.instock ins
JOIN samdb.items i ON i.code = ins.prodcode AND i.tenant_id = 287 -- ⭐ prodcode 사용
WHERE ins.is_deleted = 0
AND ins.prodcode IS NOT NULL AND ins.prodcode != '';
-- 결과 확인
SELECT COUNT(*) FROM samdb.item_receipts WHERE tenant_id = 287;
-- item_id 연결 실패 레코드 확인
SELECT ins.prodcode, ins.item_name, COUNT(*) AS cnt
FROM chandj.instock ins
LEFT JOIN samdb.items i ON i.code = ins.prodcode AND i.tenant_id = 287
WHERE ins.is_deleted = 0 AND i.id IS NULL
GROUP BY ins.prodcode, ins.item_name;
5.2 재고 집계 → stocks
-- 입고 데이터 기반 현재고 집계
INSERT INTO samdb.stocks (
tenant_id, item_id, quantity, available_qty,
last_movement_at, created_by, created_at, updated_at
)
SELECT
287 AS tenant_id,
ir.item_id,
SUM(ir.quantity) AS quantity,
SUM(ir.quantity) AS available_qty,
MAX(ir.receipt_date) AS last_movement_at,
1 AS created_by,
NOW(), NOW()
FROM samdb.item_receipts ir
WHERE ir.tenant_id = 287
GROUP BY ir.item_id;
5.3 output → orders + order_items [PHP 스크립트]
<?php
/**
* output → orders + order_items 마이그레이션 * ⚠️ 실제 컬럼명 사용 (2026-01-28 확인됨)
*
* 1단계: output 레코드 → orders 헤더 생성
* 2단계: iList JSON 파일 파싱 → order_items 생성
*/
$tenantId = 287;
$userId = 1;
$basePath = '/Users/kent/Works/@KD_SAM/SAM/5130';
// output 레코드 조회 (실제 컬럼명 사용)
$stmt = $pdo->query("
SELECT num, secondordnum, iList, COD, con_num,
outdate, indate, outworkplace, orderman,
outputplace, receiver, phone, comment
FROM output
WHERE is_deleted = 0
ORDER BY num
");
$outputs = $stmt->fetchAll(PDO::FETCH_ASSOC);
$orderCount = 0;
$itemCount = 0;
foreach ($outputs as $output) {
// 1단계: orders INSERT
// ⭐ num을 사용 (output_id 대신)
$orderNo = 'ORD-' . str_pad($output['num'], 8, '0', STR_PAD_LEFT);
// iList JSON 파일 읽기
$iListPath = $output['iList']; // "../output/i_json/22545.json"
if (empty($iListPath)) {
continue; // iList 없으면 스킵
}
$jsonFile = str_replace('../', '', $iListPath);
$fullPath = $basePath . '/' . $jsonFile;
$options = null;
$approvedBy = null;
$approvedAt = null;
$jsonContent = null;
if (file_exists($fullPath)) {
$jsonContent = json_decode(file_get_contents($fullPath), true);
// options에 전체 JSON 저장
$options = json_encode([
'inputValue' => $jsonContent['inputValue'] ?? [],
'beforeWidth' => $jsonContent['beforeWidth'] ?? [],
'beforeHeight' => $jsonContent['beforeHeight'] ?? [],
'afterWidth' => $jsonContent['afterWidth'] ?? [],
'afterHeight' => $jsonContent['afterHeight'] ?? [],
]);
// 승인 정보 추출
if (isset($jsonContent['approval']['approver'])) {
$approver = $jsonContent['approval']['approver'];
// approver.name으로 사용자 ID 조회 필요
$approvedAt = $approver['date'] ?? null;
}
}
$orderStmt = $pdo->prepare("
INSERT INTO orders (
tenant_id, order_no, order_type, order_date, delivery_date,
status, total_amount, options, approved_at, note,
created_by, created_at, updated_at
) VALUES (?, ?, 'order', ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
");
$orderStmt->execute([
$tenantId,
$orderNo,
$output['outdate'], // ⭐ outdate 사용 (order_date 대신)
$output['indate'], // ⭐ indate 사용 (delivery_date 대신?)
'completed', // 상태 - output 테이블에서 확인 필요
0, // total_amount - output 테이블에서 확인 필요
$options,
$approvedAt,
$output['comment'], // ⭐ comment 사용 (memo 대신)
$userId,
]);
$orderId = $pdo->lastInsertId();
$orderCount++;
// 2단계: order_items INSERT (pages 배열 처리)
if ($jsonContent && isset($jsonContent['pages']) && is_array($jsonContent['pages'])) {
foreach ($jsonContent['pages'] as $seqNo => $page) {
$itemOptions = json_encode([
'inputItems' => $page['inputItems'] ?? [],
'checkboxData' => $page['checkboxData'] ?? [],
]);
$itemStmt = $pdo->prepare("
INSERT INTO order_items (
tenant_id, order_id, seq_no, item_code, item_name,
quantity, options,
created_by, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, 1, ?, ?, NOW(), NOW())
");
$itemStmt->execute([
$tenantId,
$orderId,
$seqNo + 1,
null, // item_code - JSON에서 추출 필요
$output['outworkplace'] ?? '', // ⭐ outworkplace 사용 (거래처명)
$itemOptions,
$userId
]);
$itemCount++;
}
}
if ($orderCount % 1000 === 0) {
echo "진행중: {$orderCount} orders, {$itemCount} items\n";
}
}
echo "완료: {$orderCount} orders, {$itemCount} items\n";
6. 기준 원칙
┌─────────────────────────────────────────────────────────────────────────┐
│ 🎯 핵심 원칙 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 📦 데이터 전략 │
│ ───────────────────────────────────────────────────────────────────── │
│ - item_code → item_id 변환 (items 테이블 참조) │
│ - JSON 파일은 options 컬럼에 통째로 저장 (파싱 + 원본 보존) │
│ - 재고는 입고 기록 집계로 계산 │
│ │
│ ⚠️ 선행 조건 │
│ ───────────────────────────────────────────────────────────────────── │
│ - 반드시 items 마이그레이션 완료 후 진행 │
│ - item_code가 없는 레코드는 스킵하고 로그 기록 │
│ │
│ ✅ 필수 사항 │
│ ───────────────────────────────────────────────────────────────────── │
│ - 전체 이관 (instock 2,286건, output 24,564건) │
│ - JSON 파일 파싱 (5130/output/i_json/*.json) │
│ - 로컬 검증 완료 후 개발서버 배포 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
6.1 변경 승인 정책
| 분류 | 예시 | 승인 |
|---|---|---|
| ✅ 즉시 가능 | SELECT 쿼리, 분석, 매핑 설계 | 불필요 |
| ⚠️ 컨펌 필요 | INSERT 실행, TRUNCATE, 개발서버 배포 | 필수 |
| 🔴 금지 | 운영서버 직접 작업 | 별도 협의 |
7. 데이터 규모 예상
7.1 입고/재고 테이블 예상
| 소스 | 레코드 수 | SAM 테이블 | 예상 건수 |
|---|---|---|---|
| instock | 2,286 | item_receipts | ~2,286 |
| instock (집계) | - | stocks | ~500 (품목별 현재고) |
| lot | ~200 | lots | ~200 |
| lot_sales | ~300 | lot_sales | ~300 |
| 합계 | - | - | ~3,300건 |
7.2 주문/출고 테이블 예상
| 소스 | 레코드 수 | SAM 테이블 | 예상 건수 |
|---|---|---|---|
| output | 24,564 | orders | ~24,564 |
| output.iList (JSON) | ~24,564 파일 | order_items | ~50,000 (주문당 2건 평균) |
| estimate | ~500 | orders (type=견적) | ~500 |
| 합계 | - | - | ~75,000건 |
7.3 전체 마이그레이션 요약 (이 문서 범위)
| SAM 테이블 | 예상 건수 | 비고 |
|---|---|---|
| item_receipts | ~2,300 | 입고 기록 |
| stocks | ~500 | 현재고 |
| lots | ~200 | 로트 |
| lot_sales | ~300 | 로트 소진 |
| orders | ~25,000 | 주문 헤더 |
| order_items | ~50,000 | 주문 상세 |
| 총계 | ~78,000건 |
8. 체크리스트
Phase 5: 입고/재고 데이터 이관 ⭐
- instock 테이블 구조 분석 (컬럼명 확인)
- instock → item_receipts 매핑 설계
- item_code → item_id 변환 쿼리 작성
- 마이그레이션 스크립트 작성
- 재고 집계 → stocks 쿼리 작성
- lot/lot_sales 구조 분석 및 매핑
- ⚠️ 사용자 승인: 입고/재고 INSERT 실행
Phase 6: 주문/출고 데이터 이관 ⭐
- output 테이블 구조 분석 (컬럼명 확인)
- output → orders 매핑 설계
- iList JSON 파일 구조 분석 (완료)
- JSON → order_items 매핑 설계
- estimate → orders 매핑 설계
- 마이그레이션 스크립트 작성 (24,564건)
- JSON 파일 파싱 로직 구현
- ⚠️ 사용자 승인: 주문/출고 INSERT 실행
9. 참고 문서
- 레거시 소스:
5130/폴더 - JSON 파일 경로:
5130/output/i_json/*.json - 선행 문서:
docs/plans/kd-items-migration-plan.md(품목/단가 마이그레이션) - SAM orders 마이그레이션:
api/database/migrations/*_create_orders_table.php - SAM item_receipts 마이그레이션:
api/database/migrations/*_create_item_receipts_table.php - DummyDataSeeder:
api/database/seeders/DummyDataSeeder.php(TENANT_ID=287, USER_ID=1)
10. 세션 및 메모리 관리 정책
10.1 세션 시작 시 (Load Strategy)
# 1. Docker 확인
docker ps | grep sam
# 2. 선행 조건 확인
docker exec sam-mysql-1 mysql -uroot -proot samdb -e "SELECT COUNT(*) FROM items WHERE tenant_id=287;"
# → 최소 600건 이상이어야 시작 가능
# 3. 현재 진행 상태 확인
# → 이 문서의 "📍 현재 진행 상태" 섹션 참조
10.2 작업 중 관리
| 작업 완료 시 | 조치 |
|---|---|
| Phase 완료 | "📍 현재 진행 상태" 업데이트 |
| INSERT 실행 | "12. 변경 이력" 추가 |
| 오류 발생 | 체크리스트에 메모 추가 |
11. 자기완결성 점검 결과
11.1 핵심 정보 요약 (새 세션용)
┌─────────────────────────────────────────────────────────────────────────┐
│ 📋 핵심 정보 요약 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 🎯 목표: 경동기업 레거시(chandj) → SAM(samdb) 입고/재고/주문 이관 │
│ │
│ 📊 데이터 규모 (총 ~78,000건): │
│ - item_receipts: ~2,300건 (입고) │
│ - stocks: ~500건 (현재고) │
│ - orders: ~25,000건 (주문 헤더) │
│ - order_items: ~50,000건 (주문 상세) │
│ │
│ 🔑 핵심 상수: │
│ - tenant_id = 287 (경동기업) │
│ - user_id = 1 (생성자) │
│ - Docker: sam-mysql-1 │
│ - 레거시 DB: chandj / SAM DB: samdb ⚠️ │
│ - JSON 파일: 5130/output/i_json/*.json │
│ │
│ ⭐ instock 실제 컬럼명 (2026-01-28 확인): │
│ - prodcode (품목코드) → items.code 매칭용 │
│ - item_name (품목명) │
│ - received_qty (입고수량) │
│ - purchase_price_excl_vat (단가) │
│ - inspection_date (입고일) │
│ - remarks (비고) │
│ │
│ ⭐ output 실제 컬럼명 (2026-01-28 확인): │
│ - num (PK, output_id 대신) │
│ - outdate (출고일, order_date 대신) │
│ - iList (JSON 파일 경로) │
│ - outworkplace (거래처) │
│ - comment (비고, memo 대신) │
│ │
│ ⚠️ 선행 조건: │
│ - kd-items-migration-plan.md 완료 필수! │
│ - SAM items 테이블에 ~800건 이상 존재해야 함 │
│ │
│ ⭐ 마이그레이션 순서: │
│ 1. instock → item_receipts (2,286건) │
│ 2. 재고 집계 → stocks (~500건) │
│ 3. output → orders + order_items (24,564건 + ~50,000건) │
│ │
│ 📍 현재 상태: ⏳ 대기 (품목 마이그레이션 완료 대기) │
│ │
│ 📎 선행 문서: docs/plans/kd-items-migration-plan.md (품목/단가) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
12. 변경 이력
| 날짜 | 항목 | 변경 내용 | 파일 | 승인 |
|---|---|---|---|---|
| 2026-01-28 | 문서 분리 | items-migration-kyungdong-plan.md에서 입고/재고/주문 부분 분리 | - | - |
| 2026-01-28 | 문서 생성 | kd-orders-migration-plan.md 신규 생성 | - | - |
| 2026-01-28 | 컬럼명 수정 | 실제 DB 컬럼명으로 업데이트 (item_code→prodcode, output_id→num 등) | - | - |
13. 트러블슈팅 가이드
13.1 일반적인 문제
| 문제 | 원인 | 해결책 |
|---|---|---|
| item_id 연결 실패 | items 마이그레이션 미완료 | kd-items-migration-plan.md 먼저 완료 |
| JSON 파일 없음 | 파일 경로 오류 | 5130/output/i_json/ 폴더 확인 |
| 대량 INSERT 느림 | 단건 INSERT | 배치 INSERT (1000건씩) 사용 |
| 외래키 오류 | item_id 없음 | item_code → item_id 매핑 확인 |
13.2 output.iList JSON 파일 처리
// output.iList 값 예시: "../output/i_json/22545.json"
$iListPath = $output['iList']; // "../output/i_json/22545.json"
// 실제 파일 경로로 변환
$basePath = '/Users/kent/Works/@KD_SAM/SAM/5130';
$jsonFile = str_replace('../', '', $iListPath);
$fullPath = $basePath . '/' . $jsonFile;
// JSON 파일 읽기
if (file_exists($fullPath)) {
$jsonContent = json_decode(file_get_contents($fullPath), true);
// $jsonContent['inputValue'], $jsonContent['pages'] 등 사용
} else {
// 파일 없음 - 로그 기록 후 스킵
error_log("JSON file not found: {$fullPath}");
}
13.3 prodcode → item_id 매칭 실패
-- 매칭 실패 레코드 확인 (⭐ prodcode 사용)
SELECT ins.prodcode, ins.item_name, COUNT(*) AS cnt
FROM chandj.instock ins
LEFT JOIN samdb.items i ON i.code = ins.prodcode AND i.tenant_id = 287
WHERE ins.is_deleted = 0 AND i.id IS NULL
GROUP BY ins.prodcode, ins.item_name;
-- 해결 방법:
-- 1. 매칭 실패한 prodcode를 items 테이블에 추가
-- 2. 또는 스킵하고 로그 기록
-- items에 없는 품목 신규 생성 쿼리 (필요시)
INSERT INTO samdb.items (tenant_id, item_type, code, name, unit, attributes, is_active, created_by, created_at, updated_at)
SELECT DISTINCT
287 AS tenant_id,
'SM' AS item_type, -- 기본값: 부자재
ins.prodcode AS code,
ins.item_name AS name,
ins.unit AS unit,
JSON_OBJECT('legacy_source', 'instock', 'specification', ins.specification) AS attributes,
1 AS is_active,
1 AS created_by,
NOW(), NOW()
FROM chandj.instock ins
LEFT JOIN samdb.items i ON i.code = ins.prodcode AND i.tenant_id = 287
WHERE ins.is_deleted = 0
AND ins.prodcode IS NOT NULL AND ins.prodcode != ''
AND i.id IS NULL;
이 문서는 /sc:plan 스킬로 생성되었습니다.