Files
sam-docs/dev/dev_plans/kd-orders-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

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

시작 조건

이 문서의 작업을 시작하기 전:

  1. kd-items-migration-plan.md Phase 1~4 완료
  2. SAM items 테이블에 ~800건 이상 존재
  3. 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 매핑:

  • inputValueorders.options (JSON)
  • pagesorder_items.options (JSON)
  • approvalorders.approved_by, orders.approved_at
  • beforeWidth/Height, afterWidth/Heightorder_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/dev_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/dev_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 스킬로 생성되었습니다.