Files
sam-docs/plans/product-code-traceability-plan.md
권혁성 666c80c350 docs: [통합계획] 제품코드 추적성 + 검사 단위 구조 통합 계획 수립
- integrated-master-plan.md: 7 Phase 통합 마스터 (의존성 맵, 진행 관리)
- integrated-phase-0-1.md: 사전 조사 + product_code 전파 수정 상세
- integrated-phase-2.md: 절곡 분석/설계 + 견적/품질 개선 상세
- integrated-phase-3.md: 절곡 검사 동적 구현 상세
- 원본 2개 문서 아카이브 전환 (통합 문서 링크 추가)
- INDEX.md 통합 문서 등록
2026-02-27 10:15:19 +09:00

33 KiB

품목(제품코드) 연결 구조 개선 계획

⚠️ 이 문서는 아카이브 참조용입니다. 통합 계획은 integrated-master-plan.md를 참조하세요.

작성일: 2026-02-25 목적: 견적 → 수주 → 생산 → 출하 → 품질 전 단계에서 제품코드(모델코드) 추적성 확보 상태: 📦 통합 계획으로 이관 (2026-02-27) 리뷰: v2 - SuperClaude 3개 페르소나 리뷰 반영 (Backend Architect, System Architect, Quality Engineer)


📍 현재 진행 상태

항목 내용
마지막 완료 작업 전체 데이터 흐름 분석 + 페르소나 리뷰 반영
다음 작업 Phase 0 - 사전 데이터 조사
진행률 0/4 Phase (0%)
마지막 업데이트 2026-02-25

1. 개요

1.1 배경

SAM ERP에서 1개소(1틀) = 1셔터 = 1제품모델이 기본 추적 단위이다. 제품코드(모델코드, 예: FG-KQTS01-측면형-SUS)는 견적 단계에서 생성되어 수주 → 생산 → 출하 → 품질까지 일관되게 추적되어야 한다.

현재 문제: 제품코드가 order_nodes.options까지만 전달되고, 그 이후 단계(작업지시, 출하, 품질)로 흐르지 않아 추적성이 끊어진 상태이다.

1.2 용어 정의

용어 설명 예시 네이밍 규칙
product_code 완제품 모델코드 (제품 추적 단위) FG-KQTS01-측면형-SUS Backend JSON: product_code (snake_case), Frontend: productCode (camelCase)
item_code 품목 마스터 코드 (원자재/부품) EST-RAW-슬랫-방화, SUS304 items.code 컬럼
product_name 완제품명 측면형 스크린 셔터 Backend JSON: product_name, Frontend: productName
개소(틀) 1셔터 = 1제품모델 단위 order_nodes 1행 = 1개소 -

주의: item_code(원자재)와 product_code(완제품)는 완전히 다른 데이터. 혼동 금지.

1.3 핵심 데이터 흐름 (AS-IS)

견적(quotes)
 └─ calculation_inputs JSON → items[].productCode (camelCase)
 └─ product_code 컬럼 → ❌ 비어있음 (미활용)
    │
    ▼
수주(orders)
 └─ item_id → ❌ NULL (미설정)
 └─ order_nodes.options → ✅ product_code (snake_case) 존재
    │
    ▼
작업지시(work_orders)
 └─ work_order_items.options → ❌ product_code 누락 (복사 안됨)
 └─ work_results → ❌ product_code 없음
    │
    ▼
출하(shipments)
 └─ shipment_items.item_code → 원자재 코드만 (제품코드 아님)
    │
    ▼
품질(inspections)
 └─ lot_no 문자열 매칭만 → ❌ work_order_id FK 없음

1.4 핵심 데이터 흐름 (TO-BE)

견적(quotes)
 └─ calculation_inputs JSON → items[].productCode
 └─ product_code 컬럼 → ✅ 대표 제품코드 저장
    │
    ▼
수주(orders)
 └─ order_nodes.options → ✅ product_code, product_name
    │
    ▼
작업지시(work_orders)
 └─ work_order_items.options → ✅ product_code, product_name (전 경로에서 복사)
 └─ work_results → ✅ work_order_item FK로 역추적 가능
    │
    ▼
출하(shipments)
 └─ shipment_items → ✅ product_code 포함 or work_order_item 참조
    │
    ▼
품질(inspections)
 └─ ✅ work_order_id FK 추가 → 직접 연결

1.5 기준 원칙

┌─────────────────────────────────────────────────────────────────┐
│  🎯 핵심 원칙                                                    │
├─────────────────────────────────────────────────────────────────┤
│  1. 컬럼 추가 정책: FK/조인키만 컬럼, 나머지는 options JSON       │
│  2. 기존 데이터 보존: 파괴적 변경 없이 점진적 개선                 │
│  3. 역추적 가능: 어떤 단계에서든 원래 제품코드로 돌아갈 수 있어야 함│
│  4. 최소 변경: 현재 동작하는 로직에 영향을 주지 않는 범위에서 진행  │
│  5. 네이밍 통일: Backend JSON=snake_case, Frontend=camelCase      │
└─────────────────────────────────────────────────────────────────┘

1.6 변경 승인 정책

분류 예시 승인
즉시 가능 options JSON에 필드 추가, 프론트 표시 변경 불필요
⚠️ 컨펌 필요 서비스 로직 변경, 쿼리 변경, 마이그레이션 필수
🔴 금지 기존 테이블 컬럼 삭제, 기존 기능 제거 별도 협의

2. 문제 목록 (우선순위별)

🔴 P0 - 즉시 수정 필요

# 문제 위치 영향
P0-1 product_codework_order_items.options에 복사되지 않음 OrderService::createProductionOrder (L1410) 생산 현장에서 제품코드 확인 불가
P0-2 product_name도 동일하게 누락 위와 동일 제품명 확인 불가
P0-3 WorkOrderService::store 수주복사 경로도 동일 누락 WorkOrderService::store (L287-296) 수주 기반 직접 생성 시에도 누락
P0-4 WorkOrderService::store 직접 입력 경로에 product_code 전달 방법 없음 WorkOrderService::store (L311-317) 수동 생성 작업지시는 product_code 전달 자체가 불가
P0-5 WorkOrderService::update 품목 추가/수정 시 options 미전달 WorkOrderService::update (L416-438) 수정 시 product_code 소실 가능

🟡 P1 - 단기 개선

# 문제 위치 영향
P1-1 quotes.product_code 컬럼이 비어있음 QuoteService (견적 저장 로직) 견적 → 수주 변환 시 제품코드 전달 부정확
P1-2 orders.item_id NULL OrderService::createFromQuote 수주에서 대표 품목 참조 불가 (⚠️ Phase 4 FG 마스터 등록에 의존)
P1-3 inspections.work_order_id FK 없음 마이그레이션 필요 품질검사 ↔ 작업지시 직접 연결 불가, lot_no 문자열 매칭에 의존

🟢 P2 - 중장기 과제

# 문제 위치 영향
P2-1 완제품 마스터(FG-KQTS01) 미등록 items 테이블 품목 기준 통합 관리 불가
P2-2 LOT 번호 연결 일관성 부족 inspections ↔ stock_lots FK 대신 문자열 매칭
P2-3 출하 시 제품코드 미포함 shipment_items 출하 현황에서 모델별 추적 어려움
P2-4 work_order_items.product_code 장기적 컬럼 승격 필요 work_order_items 테이블 통계/GROUP BY 시 JSON 쿼리 성능 병목

3. 대상 범위

3.0 Phase 0: 사전 데이터 조사 (Phase 1 선행 필수)

목표: 마이그레이션 영향 범위 파악 및 보정 가능 건수 확인

# 작업 항목 상태 비고
0.1 order_nodes.optionsproduct_code 보유율 조사 SQL 쿼리
0.2 work_order_items에서 source_order_item_id NULL 비율 조사 보정 불가 건수 파악
0.3 soft deleted된 order_items/order_nodes 건수 조사 withTrashed 필요 여부
0.4 stock_lots.lot_no 중복 건수 조사 Phase 3 역추적 신뢰성

조사 쿼리:

-- 0.1: order_nodes의 product_code 보유율
SELECT COUNT(*) as total,
       SUM(CASE WHEN JSON_EXTRACT(options, '$.product_code') IS NOT NULL THEN 1 ELSE 0 END) as has_code,
       ROUND(SUM(CASE WHEN JSON_EXTRACT(options, '$.product_code') IS NOT NULL THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 1) as pct
FROM order_nodes WHERE deleted_at IS NULL;

-- 0.2: work_order_items의 source_order_item_id NULL 비율
SELECT COUNT(*) as total,
       SUM(CASE WHEN source_order_item_id IS NULL THEN 1 ELSE 0 END) as no_source,
       ROUND(SUM(CASE WHEN source_order_item_id IS NULL THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 1) as pct
FROM work_order_items WHERE deleted_at IS NULL;

-- 0.3: soft deleted된 원본 데이터
SELECT 'order_items' as tbl, COUNT(*) as deleted_count FROM order_items WHERE deleted_at IS NOT NULL
UNION ALL
SELECT 'order_nodes', COUNT(*) FROM order_nodes WHERE deleted_at IS NOT NULL;

-- 0.4: lot_no 중복 확인
SELECT lot_no, COUNT(*) as cnt FROM stock_lots
WHERE deleted_at IS NULL GROUP BY lot_no HAVING COUNT(*) > 1;

3.1 Phase 1: product_code 전달 수정 (P0)

목표: 모든 work_order_items 생성/수정 경로에서 product_code, product_name 전달

# 작업 항목 상태 비고
1.1 OrderService::createProductionOrder options 복사에 product_code/product_name 추가 L1410-1419
1.2 WorkOrderService::store 수주복사 로직에도 동일 추가 L287-296
1.3 WorkOrderService::store 직접 입력 경로 — 프론트에서 options.product_code 전달 L311-317 (범위 확인 필요)
1.4 WorkOrderService::update 품목 수정 시 기존 options 보존 확인 L416-438
1.5 기존 work_order_items 데이터 보정 (마이그레이션) 스냅샷 백업 후 실행
1.6 프론트엔드 WorkerScreen에 제품코드 표시 actions.ts + index.tsx
1.7 프론트엔드 ProductionDashboard에 제품코드 표시 actions.ts

배포 순서: 백엔드 배포 → 마이그레이션 실행 → 프론트엔드 배포

3.2 Phase 2: 견적 → 수주 데이터 정합성 (P1-1)

목표: quotes.product_code 컬럼 활용

⚠️ 의존성 주의: orders.item_id 설정은 items 테이블에 FG 품목이 등록되어야 가능하므로 Phase 4에 의존함. Phase 2에서는 item_id 설정을 보류하고 order_nodes.options.product_code를 통한 추적에 집중.

# 작업 항목 상태 비고
2.1 견적 저장 시 quotes.product_code 컬럼에 대표 제품코드 저장 다중 개소: 첫 번째 개소 코드 저장
2.2 견적→수주 변환 시 productCode(camelCase) → product_code(snake_case) 변환 확인 OrderService::createFromQuote
2.3 기존 데이터 보정 스크립트 calculation_inputs에서 추출

다중 개소 정책: quotes.product_code에는 첫 번째 개소의 코드를 대표값으로 저장. 전체 목록은 calculation_inputs.items[].productCode를 참조.

3.3 Phase 3: 품질검사 연결 강화 (P1-3)

목표: inspections ↔ work_orders 직접 FK 연결

Phase 2와 병렬 실행 가능 — 서로 독립적인 경로 (견적-수주 vs 생산-품질)

# 작업 항목 상태 비고
3.1 inspections 테이블에 work_order_id FK 마이그레이션 추가 nullable
3.2 Inspection 모델에 workOrder() 관계 메서드 추가 N+1 쿼리 방지
3.3 품질검사 생성 시 work_order_id 설정 로직 추가 InspectionService
3.4 기존 inspections 데이터에 work_order_id 보정 lot_no 기반 역추적 (중복 lot_no 사전 확인 필수)

3.4 Phase 4: 완제품 마스터 및 출하 연결 (P2)

목표: 완제품 코드 등록, 출하 시 제품코드 포함, orders.item_id 설정

# 작업 항목 상태 비고
4.1 완제품(FG) 품목 자동 등록 방안 설계 견적 확정 시 or 수주 확정 시
4.2 orders.item_id 설정 로직 추가 (Phase 2에서 보류한 것) FG 품목 등록 후 가능
4.3 shipment_items에 product_code 포함 방안 부분 출하 시 개소별 매핑 고려
4.4 work_order_items.product_code 컬럼 승격 검토 통계 쿼리 성능용 (JSON → 컬럼)
4.5 출하 → 품질 → 재고 전체 제품코드 추적 검증 E2E 테스트

4. 상세 작업 내용

4.1 Phase 1 상세: product_code 전달 수정

4.1.1 백엔드 수정 — 전체 5개 경로

경로 1: OrderService::createProductionOrder (L1410-1419)

현재 코드:

$woItemOptions = array_filter([
    'floor'        => $orderItem->floor_code,
    'code'         => $orderItem->symbol_code,
    'width'        => $nodeOptions['width'] ?? $nodeOptions['open_width'] ?? null,
    'height'       => $nodeOptions['height'] ?? $nodeOptions['open_height'] ?? null,
    'cutting_info' => $nodeOptions['cutting_info'] ?? null,
    'slat_info'    => $slatInfo,
    'bending_info' => $nodeOptions['bending_info'] ?? null,
    'wip_info'     => $nodeOptions['wip_info'] ?? null,
], fn ($v) => $v !== null);

수정 후:

$woItemOptions = array_filter([
    'floor'        => $orderItem->floor_code,
    'code'         => $orderItem->symbol_code,
    'product_code' => !empty($nodeOptions['product_code']) ? $nodeOptions['product_code'] : null,
    'product_name' => !empty($nodeOptions['product_name']) ? $nodeOptions['product_name'] : null,
    'width'        => $nodeOptions['width'] ?? $nodeOptions['open_width'] ?? null,
    'height'       => $nodeOptions['height'] ?? $nodeOptions['open_height'] ?? null,
    'cutting_info' => $nodeOptions['cutting_info'] ?? null,
    'slat_info'    => $slatInfo,
    'bending_info' => $nodeOptions['bending_info'] ?? null,
    'wip_info'     => $nodeOptions['wip_info'] ?? null,
], fn ($v) => $v !== null);

참고: !empty() 사용으로 빈 문자열("")도 필터링. ?? null 대신 사용.

경로 2: WorkOrderService::store 수주복사 (L287-296)

동일하게 product_code, product_name 추가.

⚠️ 주의: 이 경로는 OrderService와 달리 slat_info 자동계산 로직이 없음 (단순 nodeOptions 복사). 이 차이는 별도 이슈로 추적.

경로 3: WorkOrderService::store 직접 입력 (L311-317)

프론트에서 items[].options에 product_code를 포함시켜 전달해야 함. 수동 생성이므로 수주 연결 없이 product_code가 없는 것을 허용 (nullable). 프론트 UI에 product_code 입력 필드 추가는 Phase 1 범위 밖.

경로 4: WorkOrderService::update 품목 수정 (L416-438)

현재 update 시 options 필드가 itemData에 미포함. 기존 options가 덮어씌워지지 않는지 확인 필요.

  • update(['item_name' => ...]) 식으로 특정 필드만 업데이트하면 options 보존됨 (OK)
  • items()->updateOrCreate(...) 패턴이면 options 소실 위험 → 점검 필요

경로 5: WorkOrderService::update 품목 신규 추가 (L435)

경로 3과 동일 — 프론트 전달 의존. 수동 추가이므로 product_code nullable 허용.

4.1.2 기존 데이터 보정 마이그레이션

// ⚠️ 보정 전 스냅샷 백업
DB::statement('CREATE TABLE IF NOT EXISTS work_order_items_backup_product_code
    AS SELECT id, options FROM work_order_items');

// ⚠️ BelongsToTenant 글로벌 스코프 우회 + SoftDeletes 포함
WorkOrderItem::withoutGlobalScopes()
    ->whereNull(DB::raw("JSON_EXTRACT(options, '$.product_code')"))
    ->whereNotNull('source_order_item_id')
    ->chunk(100, function ($items) {
        // bulk 조회로 N+1 방지
        $orderItemIds = $items->pluck('source_order_item_id')->filter()->unique();
        $orderItems = OrderItem::withTrashed()
            ->with(['orderNode' => fn($q) => $q->withTrashed()])
            ->whereIn('id', $orderItemIds)
            ->get()
            ->keyBy('id');

        foreach ($items as $item) {
            $orderItem = $orderItems->get($item->source_order_item_id);
            if ($orderItem?->orderNode) {
                $nodeOptions = $orderItem->orderNode->options ?? [];
                $productCode = !empty($nodeOptions['product_code']) ? $nodeOptions['product_code'] : null;
                $productName = !empty($nodeOptions['product_name']) ? $nodeOptions['product_name'] : null;
                if ($productCode) {
                    $options = $item->options ?? [];
                    $options['product_code'] = $productCode;
                    if ($productName) $options['product_name'] = $productName;
                    $item->updateQuietly(['options' => $options]); // 이벤트 미발생
                }
            }
        }
    });

// 보정 결과 로그
$total = WorkOrderItem::withoutGlobalScopes()->whereNull('deleted_at')->count();
$withCode = WorkOrderItem::withoutGlobalScopes()
    ->whereNull('deleted_at')
    ->whereNotNull(DB::raw("JSON_EXTRACT(options, '$.product_code')"))
    ->count();
Log::info("product_code 보정 완료: {$withCode}/{$total} ({pct}%)");

source_order_item_id가 NULL인 건: 수동 생성 작업지시로 보정 불가. Phase 0 조사에서 건수 파악 후 감수 범위로 문서화.

4.1.3 프론트엔드 수정

WorkerScreen/actions.ts - API 응답에서 productCode 매핑:

// work_order_items의 options에서 product_code 추출
const productCode = api.items?.[0]?.options?.product_code || '-';
const productName = api.items?.[0]?.options?.product_name || api.items?.[0]?.item_name || '-';

다중 개소 표시: items[0]만 가져오므로 다중 개소 작업지시 시 첫 번째만 표시됨. 향후 UI 개선 시 items 전체 순회 필요.

WorkerScreen/index.tsx - 작업 카드에 제품코드 표시:

itemName: productCode !== '-' ? `${productCode} - ${productName}` : productName,

ProductionDashboard/actions.ts - 대시보드에도 동일 적용.


5. DB 테이블 관계도 (현재)

quotes ─────────────────────────────────────── items (product_id FK, 미활용)
  │ calculation_inputs.items[].productCode (camelCase)
  │
  ▼ (createFromQuote)
orders ─────────────────────────────────────── items (item_id FK, NULL)
  │
  ├── order_nodes ──── options: {product_code, product_name, width, height, ...}
  │     │
  │     └── order_items ── item_id → items (원자재)
  │           │
  ▼           ▼ (createProductionOrder)
work_orders
  │
  ├── work_order_items ── options: {floor, code, width, height, slat_info, ...}
  │     │                         ❌ product_code 없음 (TO-BE: 추가)
  │     │
  │     ├── source_order_item_id → order_items (역추적 가능)
  │     ├── work_order_material_inputs ── work_order_item_id FK (자재 투입)
  │     └── work_order_step_progress ── work_order_item_id FK (공정 진행)
  │
  ├── work_results ── work_order_id FK (작업 실적, product_name만 있음)
  │
  ▼
  stock_lots ── work_order_id FK ✅
        │
        ▼
  stocks ── item_id → items
        │
        ▼
  shipment_items ── stock_lot_id FK ✅, item_code (문자열, 원자재코드)
        │
        ▼
  inspections ── lot_no (문자열 매칭), ❌ work_order_id 없음

6. 롤백 전략

Phase 위험도 롤백 방법
Phase 1 (options 필드 추가) 낮음 options에서 product_code, product_name 키 제거 스크립트
Phase 1 (데이터 보정) 중간 work_order_items_backup_product_code 테이블에서 복원
Phase 3 (inspections FK) 중간 work_order_id 컬럼 drop 마이그레이션 (down 메서드)
Phase 4 (FG 품목 등록) 높음 자동 등록 FG 품목에 auto_generated 플래그 → 식별 후 삭제

필수 규칙:

  1. 모든 데이터 보정 마이그레이션에 down() 메서드 구현
  2. 보정 전 반드시 스냅샷 백업 테이블 생성
  3. 각 Phase 완료 후 검증 통과 확인 → 다음 Phase 진행

7. 컨펌 대기 목록

# 항목 변경 내용 영향 범위 상태
1 Phase 0 사전 조사 실행 4개 SQL 쿼리 실행 읽기 전용 ⚠️ 대기
2 Phase 1 실행 승인 5개 경로 options 복사에 product_code 추가 작업지시 생성/수정 ⚠️ 대기
3 데이터 보정 마이그레이션 기존 work_order_items에 product_code 역추적 보정 기존 데이터 ⚠️ 대기
4 inspections.work_order_id FK 추가 마이그레이션 + 품질검사 로직 수정 inspections 테이블 ⚠️ 대기
5 완제품 마스터 자동 등록 items 테이블에 FG 유형 품목 자동 생성 items, 견적/수주 로직 ⚠️ 대기

8. 작업 절차 요약

Phase 0 (사전 조사) ─── 읽기 전용, 위험 없음
  ├── SQL 4개 실행 → 영향 범위 파악
  └── 결과에 따라 Phase 1 보정 전략 조정

Phase 1 (P0 수정) ─── 즉시 실행 가능, 영향 범위 최소
  ├── Step 1: OrderService.php 경로 1 (createProductionOrder)
  ├── Step 2: WorkOrderService.php 경로 2-5 (store 수주복사, store 직접, update x2)
  ├── Step 3: 스냅샷 백업 → 데이터 보정 마이그레이션
  ├── Step 4: 프론트 WorkerScreen에 제품코드 표시
  └── Step 5: 프론트 ProductionDashboard에 제품코드 표시
  ※ 배포 순서: 백엔드 → 마이그레이션 → 프론트

Phase 2 (P1 개선) ─── 견적/수주 데이터 정합성 ──┐ 병렬 실행 가능
  ├── Step 1: QuoteService에서 quotes.product_code 저장│
  ├── Step 2: 다중 개소 대표 코드 정책 적용           │
  └── Step 3: 기존 데이터 보정                        │
                                                      │
Phase 3 (P1 개선) ─── 품질검사 연결 ────────────────┘
  ├── Step 1: inspections.work_order_id 마이그레이션
  ├── Step 2: Inspection 모델 관계 + InspectionService 수정
  └── Step 3: lot_no 기반 기존 데이터 보정

Phase 4 (P2 중장기) ─── 완제품 마스터 + 출하 + item_id
  ├── Step 1: FG 품목 자동 등록 설계
  ├── Step 2: orders.item_id 설정 (FG 등록 후)
  ├── Step 3: 출하 product_code 포함 (부분 출하 고려)
  ├── Step 4: product_code 컬럼 승격 검토
  └── Step 5: E2E 추적 검증

9. 성공 기준

기준 측정 방법 수치 목표
WorkerScreen에서 제품코드 표시 다중 개소 수주 작업지시 5건에서 확인 100% 표시
신규 작업지시 생성 시 product_code 포함 SELECT JSON_EXTRACT(options, '$.product_code') FROM work_order_items WHERE ... NOT NULL
기존 데이터 보정율 source_order_item_id 있는 건 중 보정 비율 90% 이상
보정 데이터 정확도 원본 order_nodes와 대조 검증 MATCH 100%
기존 기능 회귀 없음 작업지시 목록/상세 API 정상 응답 에러 0건
API 성능 영향 없음 options 필드 추가로 인한 응답 시간 변화 5% 미만
Phase 3: inspections FK 보정 정확도 lot_no 기반 역추적 MATCH 비율 95% 이상
Phase 4: E2E 추적 견적→수주→작업지시→완료→출하→품질 전 과정 product_code 일관성 100%

10. 참고 파일

백엔드

파일 역할 주요 메서드/라인
api/app/Services/OrderService.php 수주 → 작업지시 변환 createProductionOrder (L1177), options 복사 (L1410-1419)
api/app/Services/WorkOrderService.php 작업지시 서비스 store 수주복사 (L287-296), store 직접입력 (L311-317), update (L416-438), copyWorkOrderItemsToShipment (L716-773)
api/app/Services/Quote/QuoteService.php 견적 서비스 product_code 저장 (L324), 개소별 노드 생성 (L645-674)
api/app/Services/InspectionService.php 품질검사 서비스 검사 생성 로직
api/app/Services/WorkResultService.php 작업실적 서비스 실적 기록 (product_code 미포함)
api/app/Models/Production/WorkOrderItem.php 작업지시 품목 모델 options 캐스트, setResult (L155), completeWithResult (L164)
api/app/Models/Production/WorkResult.php 작업실적 모델 product_name만 있음, product_code 없음
api/app/Models/OrderNode.php 수주 노드 모델 options 캐스트

프론트엔드

파일 역할
react/src/components/production/WorkerScreen/actions.ts 작업자 화면 서버 액션
react/src/components/production/WorkerScreen/index.tsx 작업자 화면 메인 컴포넌트
react/src/components/production/ProductionDashboard/actions.ts 대시보드 서버 액션
react/src/components/production/ProductionDashboard/types.ts 공통 타입 정의

DB 테이블

테이블 핵심 컬럼/필드
quotes product_code, product_name, calculation_inputs (JSON)
orders item_id, quote_id
order_nodes options (JSON: product_code, product_name, width, height, ...)
order_items order_node_id, item_id, floor_code, symbol_code
work_orders sales_order_id
work_order_items source_order_item_id, options (JSON: product_code 누락)
work_results work_order_id, product_name (product_code 없음)
work_order_material_inputs work_order_item_id (자재 투입 이력)
work_order_step_progress work_order_item_id (공정 단계 진행)
inspections lot_no ( work_order_id 없음)
stock_lots work_order_id, lot_no
shipment_items stock_lot_id, item_code

11. 변경 이력

날짜 항목 변경 내용 파일 승인
2026-02-25 문서 초안 전체 분석 결과 기반 계획 문서 작성 - -
2026-02-25 v2 리뷰 반영 SuperClaude 3개 페르소나 리뷰 결과 반영 (아래 상세) - -

v2 리뷰 반영 상세

# 반영 항목 출처
1 Phase 0 (사전 데이터 조사) 추가 Backend Architect, Quality Engineer
2 work_order_items 생성 경로 3곳 추가 (P0-4, P0-5 + Phase 1 작업 1.3, 1.4) System Architect
3 용어 정의 섹션 (1.2) 추가 — product_code vs item_code, 네이밍 규칙 System Architect
4 롤백 전략 섹션 (6) 추가 — 스냅샷 백업, down() 마이그레이션 Quality Engineer
5 마이그레이션 코드 보강 — withTrashed, withoutGlobalScopes, bulk 조회, empty 체크 Backend Architect
6 DB 관계도에 work_results, work_order_material_inputs, work_order_step_progress 추가 System Architect
7 Phase 2↔4 의존성 해결 — item_id 설정을 Phase 4로 이동 Quality Engineer
8 Phase 2/3 병렬 실행 가능 명시 System Architect
9 성공 기준 수치화 — 보정율 90%+, MATCH 100%, 성능 5% 미만 등 Quality Engineer
10 다중 개소 대표 코드 정책 정의 (첫 번째 개소) Quality Engineer
11 배포 순서 명시 (백엔드 → 마이그레이션 → 프론트) Backend Architect
12 P2-4 추가 — product_code 컬럼 승격 로드맵 (장기 통계 성능용) System Architect
13 검증 섹션 전 Phase 테스트 케이스 확장 Quality Engineer

리뷰에서 별도 이슈로 추적할 항목 (이 계획 범위 밖)

항목 설명 우선순위
WorkOrderService slat_info 로직 차이 OrderService에는 자동계산 있고 WorkOrderService에는 없음 Medium
견적 수정 시 하위 데이터 동기화 수주 후 견적 수정 시 product_code 불일치 가능 Low
부분 출하 시 개소별 N:M 매핑 shipment_items ↔ work_order_items 매핑 설계 Phase 4에서
options 조합 로직 리팩토링 OrderService와 WorkOrderService 중복 코드 통합 Low

12. 세션 및 메모리 관리 정책

12.1 세션 시작 시

1. 이 문서(product-code-traceability-plan.md) 읽기
2. 진행 상태 테이블 확인 → 마지막 완료 작업 파악
3. 다음 작업 시작

12.2 작업 중 관리

  • Phase 완료 시 이 문서의 상태 테이블 업데이트
  • 컨펌 필요 사항 발생 시 컨펌 대기 목록에 추가

12.3 세션 종료 시

  • 변경 이력 섹션에 최종 업데이트 기록

13. 검증 결과

작업 완료 후 이 섹션에 검증 결과 추가

13.1 Phase 0 사전 조사 결과

조사 항목 결과 판단
order_nodes product_code 보유율
work_order_items source_order_item_id NULL 비율
soft deleted 원본 데이터 건수
lot_no 중복 건수

13.2 Phase 1 검증

테스트 예상 결과 실제 결과 상태
신규 작업지시 생성 (OrderService 경로) options에 product_code 포함
신규 작업지시 생성 (WorkOrderService 수주복사) options에 product_code 포함
product_code가 NULL인 order_nodes에서 생성 오류 없이 product_code NULL 저장
product_code가 빈 문자열인 경우 empty 체크로 필터링, options에 미포함
기존 데이터 보정 (source_order_item_id 있는 건) product_code 채워짐
기존 데이터 보정 (source_order_item_id NULL) skip, 오류 없음
기존 데이터 보정 (soft deleted 원본) withTrashed로 정상 조회
WorkerScreen 표시 "FG-KQTS01-측면형-SUS - 슬랫 방화"
WorkerScreen - product_code 없는 건 기존과 동일 표시 (productName만)
기존 API 회귀 테스트 작업지시 목록/상세 정상 응답

데이터 검증 쿼리:

-- 보정 후 성공률
SELECT COUNT(*) as total,
       COUNT(CASE WHEN JSON_EXTRACT(options, '$.product_code') IS NOT NULL THEN 1 END) as with_code,
       ROUND(COUNT(CASE WHEN JSON_EXTRACT(options, '$.product_code') IS NOT NULL THEN 1 END) * 100.0 / COUNT(*), 1) as pct
FROM work_order_items WHERE deleted_at IS NULL;

-- 보정 데이터 정확도 (원본 대조)
SELECT woi.id,
       JSON_EXTRACT(woi.options, '$.product_code') as wo_code,
       JSON_EXTRACT(onode.options, '$.product_code') as node_code,
       CASE WHEN JSON_EXTRACT(woi.options, '$.product_code') = JSON_EXTRACT(onode.options, '$.product_code')
            THEN 'MATCH' ELSE 'MISMATCH' END as status
FROM work_order_items woi
JOIN order_items oi ON woi.source_order_item_id = oi.id
JOIN order_nodes onode ON oi.order_node_id = onode.id
WHERE woi.deleted_at IS NULL
  AND JSON_EXTRACT(woi.options, '$.product_code') IS NOT NULL;

13.3 Phase 2 검증

테스트 예상 결과 실제 결과 상태
견적 저장 시 quotes.product_code 저장 첫 번째 개소 코드 저장
다중 개소 견적의 대표 코드 첫 번째 개소 코드가 quotes.product_code에
견적→수주 변환 시 productCode→product_code 변환 snake_case로 저장

13.4 Phase 3 검증

테스트 예상 결과 실제 결과 상태
inspections.work_order_id FK 추가 마이그레이션 성공, nullable
기존 inspection 조회 정상 동작 회귀 없음
lot_no 기반 역추적 보정 정확도 MATCH 95% 이상

13.5 Phase 4 검증

테스트 예상 결과 실제 결과 상태
FG 품목 자동 등록 items에 FG-KQTS01 등록
E2E: 견적→출하→품질 전 구간 product_code 일관성 100% 추적 가능

14. 자기완결성 점검 결과

14.1 체크리스트 검증

# 검증 항목 상태 비고
1 작업 목적이 명확한가? 전 단계 제품코드 추적성 확보
2 성공 기준이 정의되어 있는가? 섹션 9 - 수치 목표 포함
3 작업 범위가 구체적인가? Phase 0-4, 전체 5개 코드 경로 명시
4 의존성이 명시되어 있는가? Phase 2↔4 의존, Phase 2/3 병렬 가능
5 참고 파일 경로가 정확한가? 메서드명 + 라인 번호 병행 참조
6 단계별 절차가 실행 가능한가? 코드 변경 예시 + 사전 조사 쿼리 포함
7 검증 방법이 명시되어 있는가? 전 Phase 테스트 케이스 + SQL 검증 쿼리
8 모호한 표현이 없는가? 용어 정의, 네이밍 규칙, 다중 개소 정책 명시
9 롤백 전략이 있는가? 섹션 6 - Phase별 롤백 방법
10 범위 밖 항목이 명시되어 있는가? 별도 이슈 추적 테이블

14.2 새 세션 시뮬레이션 테스트

질문 답변 가능 참조 섹션
Q1. 이 작업의 목적은 무엇인가? 1.1 배경
Q2. 어디서부터 시작해야 하는가? 3.0 Phase 0 사전 조사
Q3. 어떤 파일을 수정해야 하는가? 10. 참고 파일 (메서드명+라인)
Q4. 작업 완료 확인 방법은? 9. 성공 기준 + 13. 검증 결과 (SQL 포함)
Q5. 막혔을 때 참고 문서는? 10. 참고 파일 + 5. DB 관계도
Q6. 실패 시 어떻게 복원하는가? 6. 롤백 전략
Q7. 이 계획 범위 밖인 것은? 11. 별도 이슈 추적 테이블

이 문서는 /plan 스킬로 생성되었습니다. v2: SuperClaude 페르소나 리뷰 반영.