Files
sam-docs/dev/dev_plans/qms-document-template-integration-plan.md
권혁성 bac20a093e docs: [QMS] 문서 양식 연동 상세 계획 수립
- 수주서/출고증/납품확인서 Mock→실데이터 전환 계획
- Phase 1-2 (실 데이터 매핑) 상세화, Phase 3-4 보류
- 검증 분석 반영 (필드명 수정, 구조 보강, 래퍼 컴포넌트 경유)
2026-03-12 23:54:07 +09:00

34 KiB

QMS 문서 양식 연동 계획

작성일: 2026-03-12 목적: 수주서/출고증/납품확인서의 Mock 데이터를 실제 수주/출하 데이터로 교체하고 QMS 로트 추적 심사에 연동 기준 문서: docs/features/quality-management/README.md, docs/system/database/documents.md 상태: 🔄 진행중 (Phase 1-2 범위)


📍 현재 진행 상태

항목 내용
마지막 완료 작업 계획 수립 + 검증 분석 반영
다음 작업 Phase 1.1 — 사전 조사 (DB 조회) → getOrderDetail() 보강
진행률 0/12 (0%)
마지막 업데이트 2026-03-12

1. 개요

1.1 배경

QMS 로트 추적 심사에서 8종 서류를 확인할 때, 수주서/출고증/납품확인서 3종이 Mock 데이터(하드코딩)로 렌더링되고 있다. 수입검사 성적서와 제품검사 성적서는 이미 document_templates 기반 실 데이터 연동이 완료되었다.

1.2 현재 상태

문서 QMS 연동 데이터 양식 기반
수입검사 성적서 완료 실 데이터 document_templates
제품검사 성적서 완료 inspection_data + FQC template 기반
수주서 ⚠️ Mock MOCK_* 하드코딩 (12개 상수) 하드코딩
출고증 ⚠️ Mock MOCK_* 하드코딩 (12개 상수, 중복) 하드코딩
납품확인서 ⚠️ Mock MOCK_* 하드코딩 (출고증과 동일) 하드코딩

1.3 목표

┌─────────────────────────────────────────────────────────────────┐
│  🎯 핵심 목표 (Phase 1-2)                                       │
├─────────────────────────────────────────────────────────────────┤
│  1. Mock 데이터 제거 → 실제 수주/출하 데이터로 렌더링            │
│  2. QMS 로트 추적 심사에서 실 데이터 기반 서류 확인 가능          │
│  3. 수주 페이지 + 출하 페이지 문서 모달도 동시 실 데이터 전환     │
├─────────────────────────────────────────────────────────────────┤
│  ⏸️ 보류 (Phase 3-4 — Phase 1-2 완료 후 재검토)                 │
│  - mng.sam.kr/document-templates에서 양식 관리                  │
│  - 양식 기반 동적 렌더링 전환                                    │
│  - 사유: document_templates 스키마가 검사 성적서 전용 설계이므로  │
│    수주서/출고증에 적합한 양식 구조 재설계 필요                   │
└─────────────────────────────────────────────────────────────────┘

1.4 성공 기준

기준 설명 상태
SC-1 수주 57(스크린), 59(철재)에서 수주서 실 데이터 렌더링
SC-2 출하 13에서 출고증/납품확인서 실 데이터 렌더링
SC-3 QMS /quality/qms에서 수주서/출고증/납품확인서 모달 정상 표시
SC-3b 수주 페이지 OrderDocumentModal에서도 수주서 실 데이터 정상 표시
SC-4 mng에서 수주서/출고증/납품확인서 양식 생성/편집 가능 ⏸️ 보류
SC-5 양식 기반 동적 렌더링으로 전환 완료 ⏸️ 보류

1.5 변경 승인 정책

분류 예시 승인
즉시 가능 Mock 데이터 제거, API 응답 필드 추가, 프론트 데이터 매핑 불필요
⚠️ 컨펌 필요 API 엔드포인트 변경, 새 양식 카테고리 추가, DB 스키마 변경 필수
🔴 금지 document_templates 테이블 구조 변경, 기존 수입검사 양식 로직 변경 별도 협의

1.6 준수 규칙

  • docs/dev/standards/api-rules.md — Service-First, FormRequest
  • docs/dev/standards/quality-checklist.md — 코드 품질
  • docs/dev/standards/git-conventions.md — 커밋 컨벤션
  • docs/dev/standards/options-column-policy.md — JSON options 정책

2. 대상 범위

2.1 테스트 데이터

ID 유형 로트번호 URL
수주 57 스크린 KD-SS-260211-05 /sales/order-management-sales/57
수주 59 철재 (확인필요) /sales/order-management-sales/59
출하 13 출고/납품 - /outbound/shipments/13
입고 108 수입검사(참고) - /material/receiving-management/108

2.2 Phase 목록

Phase 작업 상태 의존성
1 수주서 실 데이터 매핑 없음
2 출고증/납품확인서 실 데이터 매핑 없음 (Phase 1과 병렬 가능)
3 mng 양식 템플릿 생성 ⏸️ 보류 Phase 1-2 완료 후 재검토
4 양식 기반 동적 렌더링 전환 ⏸️ 보류 Phase 3 완료 후
5 통합 검증 Phase 1-2 완료 후

Phase 3-4 보류 사유: document_templates 스키마는 검사 성적서용(고정 행 + EAV)으로 설계됨. 수주서/출고증은 동적 행 + 제품유형별 다른 컬럼 구조이므로 현재 스키마로는 부적합. Phase 1-2로 실 데이터 렌더링을 먼저 완료한 후, 양식 구조를 별도 설계하는 것이 합리적.


3. 작업 절차

3.1 전체 흐름

Phase 1: 수주서 실 데이터            Phase 2: 출고증/납품확인서 실 데이터
├── 1.1 사전 조사 (DB 조회)          ├── 2.1 사전 조사 (Shipment 관계 확인)
├── 1.2 API getOrderDetail 보강      ├── 2.2 API getShipmentDetail 보강
├── 1.3 SalesOrderDocument 수정      ├── 2.3 ShipmentOrderDocument 수정
├── 1.4 OrderDocumentModal 연동      ├── 2.4 QMS InspectionModal 연동
└── 1.5 QMS InspectionModal 연동     │   ├── case 'confirmation' → DeliveryConfirmation 래퍼
         ↓                           │   └── case 'shipping' → ShippingSlip 래퍼
         ↓                           └── 2.5 actions.ts transform 함수 추가
         ↓                                    ↓
    Phase 5: 통합 검증 (Phase 1-2 완료 후)
    ├── 5.1 수주 57/59 수주서 검증
    ├── 5.2 수주 페이지 OrderDocumentModal 검증
    ├── 5.3 출하 13 출고증/납품확인서 검증
    └── 5.4 QMS 로트 추적 전체 시나리오

    ─── ⏸️ Phase 3-4는 별도 계획으로 분리 ───

4. 상세 작업 내용

Phase 1: 수주서 실 데이터 매핑

1.1 사전 조사 (DB 조회)

Phase 1 첫 단계로 실행. 결과에 따라 1.2의 데이터 추출 로직이 달라짐.

# 조사 항목 SQL/방법 목적
C1 bom_result 변수키 SELECT id, JSON_EXTRACT(options, '$.bom_result.variables') FROM order_nodes WHERE order_id IN (57,59) AND parent_id IS NULL LIMIT 5 products 매핑 키 확정
C2 모터/절곡/부자재 데이터 출처 C1 결과에서 motor/guide_rail/case 관련 키 존재 여부 확인 + SELECT * FROM order_items WHERE order_id IN (57,59) 데이터 추출 경로 결정

OrderNode vs OrderItem 차이 (참고):

  • OrderNode = 설계/위치 뷰 (개소별 사이즈, BOM 계산 결과 in options.bom_result.variables)
  • OrderItem = 자재/구매 뷰 (품목명, 규격, 수량, 단가 — 발주/출하에 사용)
  • 두 테이블은 관점이 다르지 저장이 중복되는 것이 아님

1.2 백엔드 — getOrderDetail() 보강

파일: api/app/Services/QmsLotAuditService.php (L416-431)

현재 응답 (불충분):

return [
    'type' => 'order',
    'data' => [
        'id', 'order_no', 'status', 'received_at', 'site_name',
        'nodes_count'  // ← 노드 개수만 반환
    ],
];

보강 후 응답:

return [
    'type' => 'order',
    'data' => [
        // 기본 정보
        'id', 'order_no', 'status', 'received_at', 'site_name',
        'category_code',  // 제품 유형 (screen/steel)

        // 거래처 정보 (order.client 관계)
        'client_name',        // client.name
        'client_contact',     // client.contact_person 또는 options에서

        // 배송 정보
        'delivery_date',              // order.delivery_date
        'delivery_method_code',       // ⚠️ 실제 컬럼명 확인 필요 (delivery_method가 아닐 수 있음)
        'delivery_address',           // order.options.delivery_address

        // 담당자
        'manager_name',       // order.manager 관계 또는 options

        // 금액
        'total_quantity',     // rootNodes count
        'supply_amount',      // order.supply_amount
        'total_amount',       // order.total_amount

        // 메모
        'remarks',            // order.remarks

        // 개소별 제품 상세 (rootNodes → 변환)
        'products' => [
            [
                'no' => 1,
                'floor' => '10F',
                'code' => 'FA123',
                'product_name' => '스크린',       // ⚠️ SalesOrderDocument에서 사용됨 (L145)
                'open_width' => 4300,             // node.options.open_width
                'open_height' => 4300,            // node.options.open_height
                'made_width' => 4300,             // node.options.width (= 제작가로)
                'made_height' => 3000,            // node.options.height (= 제작세로)
                'guide_rail' => '백면형',          // bom_result.variables.{키 C1에서 확인}
                'shaft' => 5,
                'joint_bar' => null,              // 스크린은 null, 철재만
                'case_inch' => 5,
                'bracket' => '500X300',
                'capacity' => 300,
                'finish' => 'SUS마감',
            ],
        ],

        // 모터 정보 — ⚠️ 좌/우 2열 레이아웃 필요
        'motors' => [
            'left' => [
                ['item' => '모터', 'type' => '380V 단상', 'spec' => 'KD-150K', 'qty' => 6],
                ['item' => '브라켓트', 'type' => '-', 'spec' => '380X180', 'qty' => 6],
            ],
            'right' => [
                ['item' => '앵글', 'type' => '-', 'spec' => '50X50', 'qty' => 6],
                ['item' => '전동개폐기', 'type' => '-', 'spec' => 'KD-200', 'qty' => 6],
            ],
        ],

        // 절곡물 — ⚠️ 서브그룹 구조 필요 (가이드레일/케이스/하단마감 + 연기차단재)
        'bending_parts' => [
            [
                'group' => '가이드레일',
                'items' => [
                    ['name' => '가이드레일(백면형)', 'spec' => 'L: 3000', 'qty' => 22],
                ],
            ],
            [
                'group' => '케이스',
                'items' => [
                    ['name' => '케이스(5인치)', 'spec' => 'L: 4300', 'qty' => 10],
                ],
            ],
            [
                'group' => '하단마감',
                'items' => [
                    ['name' => '하단마감(알루미늄)', 'spec' => 'L: 4300', 'qty' => 5],
                ],
            ],
            [
                'group' => '연기차단재',  // ⚠️ MOCK_GUIDE_SMOKE, MOCK_CASE_SMOKE 대응
                'items' => [
                    ['name' => '가이드레일(연기차단재)', 'spec' => 'L: 3000', 'qty' => 4],
                    ['name' => '케이스(연기차단재)', 'spec' => 'L: 4300', 'qty' => 2],
                ],
            ],
        ],

        // 부자재 (BOM에서 집계)
        'subsidiary_parts' => [
            ['name' => '감기사프트', 'spec' => 'L: 4000', 'qty' => 22],
            ['name' => '조인트바', 'spec' => '-', 'qty' => 12],
        ],
    ],
];

데이터 추출 로직:

Order
├─ 기본: order.*, order.client.name, order.options.delivery_address 등
├─ products: rootNodes.map(node => {
│   node.options.floor / symbol / open_width / open_height
│   node.options.width (제작가로) / node.options.height (제작세로)
│   node.options.bom_result.variables.{C1에서 확인한 키} (guide_rail, shaft 등)
│ })
├─ motors: rootNodes.flatMap → bom_result.variables에서 집계 (좌/우 분리)
│   ⚠️ 좌/우 분리 규칙은 C1 조사 후 Mock 데이터(MOCK_MOTOR_LEFT/RIGHT) 참고하여 결정
├─ bending_parts: rootNodes.flatMap → bom_result.variables에서 그룹별 집계
│   ⚠️ 연기차단재(MOCK_GUIDE_SMOKE, MOCK_CASE_SMOKE) 포함 필수
└─ subsidiary_parts: order.items에서 부자재 카테고리 필터

주의사항:

  • 누락 데이터는 null 반환 (프론트에서 '-' 표시)
  • bom_result.variables의 실제 키 이름은 BOM 공식(QuoteFormula)이 동적 생성하므로 코드만으로 확정 불가 → C1 조사 필수
  • Order 모델에 없는 필드: client_phone, address, recipient_name, recipient_contact, manager_contact, fee → 모델/관계 확인 후 존재하는 필드만 매핑

1.3 프론트엔드 — SalesOrderDocument.tsx 수정

파일: react/src/components/orders/documents/SalesOrderDocument.tsx

⚠️ 중요: 이 컴포넌트의 props 구조

SalesOrderDocument는 18개의 개별 props를 받음 (단일 data 객체가 아님):

interface SalesOrderDocumentProps {
  orderNumber?: string;
  orderDate?: string;
  client?: string;
  siteName?: string;
  // ... 18개 개별 props
  products?: ProductInfo[];  // ⚠️ L145에서 productName 표시에 사용됨 (미사용이 아님!)
  items?: OrderItem[];
}

Mock 상수 목록 (12개 + 연기차단재 2개 = 14개):

# 상수명 대응 데이터
1 MOCK_SCREEN_ROWS products (스크린)
2 MOCK_STEEL_ROWS products (철재)
3 MOCK_MOTOR_LEFT motors.left
4 MOCK_MOTOR_RIGHT motors.right
5 MOCK_GUIDE_RAIL bending_parts[가이드레일]
6 MOCK_CASE bending_parts[케이스]
7 MOCK_BOTTOM_FINISH bending_parts[하단마감]
8 MOCK_GUIDE_SMOKE bending_parts[연기차단재-가이드]
9 MOCK_CASE_SMOKE bending_parts[연기차단재-케이스]
10 MOCK_SUBSIDIARY subsidiary_parts
11 MOCK_SUMMARY_LEFT 합계 (좌)
12 MOCK_SUMMARY_RIGHT 합계 (우)

수정 내용:

# 작업 상태
1.3.1 MOCK_* 상수 전체 제거 (14개)
1.3.2 API 응답 → 18개 개별 props 매핑 로직 작성
1.3.3 제품 유형별 분기 (스크린 vs 철재)
1.3.4 모터 좌/우 2열 레이아웃 실 데이터 렌더링
1.3.5 절곡물 서브그룹별 (가이드/케이스/하단/연기차단재) 렌더링
1.3.6 부자재 섹션 실 데이터 렌더링
1.3.7 누락 데이터 '-' 표시 (에러 방지)

데이터 매핑 (API → 18개 개별 props):

// API 응답을 SalesOrderDocument의 개별 props로 매핑
function mapOrderToProps(data: OrderDetailResponse): SalesOrderDocumentProps {
  return {
    orderNumber: data.order_no,
    client: data.client_name,
    siteName: data.site_name,
    orderDate: data.received_at,
    deliveryRequestDate: data.delivery_date,
    deliveryMethod: data.delivery_method_code,
    manager: data.manager_name,
    // products → products prop (productName 표시에 사용됨!)
    products: data.products?.map(p => ({
      productName: p.product_name,
      // ... 기타 ProductInfo 필드
    })),
    // ... 나머지 props
  };
}

1.4 수주 페이지 연동 — OrderDocumentModal.tsx

파일: react/src/components/orders/documents/OrderDocumentModal.tsx (L167-190)

⚠️ SalesOrderDocument는 이중 사용 (dual-use):

  • QMS InspectionModal에서 수주서 렌더링
  • 수주 페이지 OrderDocumentModal에서도 수주서 렌더링

둘 다 동일한 실 데이터 매핑이 적용되어야 함.

수정: OrderDocumentModal에서도 API 실 데이터로 SalesOrderDocument 렌더링


1.5 QMS 연동 — InspectionModal.tsx 수주서 케이스

파일: react/src/app/[locale]/(protected)/quality/qms/components/InspectionModal.tsx

현재: case 'order'에서 QMS_MOCK_* 데이터로 SalesOrderDocument 렌더링

수정:

case 'order':
  // 기존: QMS_MOCK_* 데이터 사용
  // 변경: getDocumentDetail('order', id) 호출 → 실 데이터 전달
  const orderData = await getDocumentDetail('order', item.id);
  const mappedProps = mapOrderToProps(orderData.data);
  return <SalesOrderDocument {...mappedProps} />;

Phase 2: 출고증/납품확인서 실 데이터 매핑

2.1 사전 조사

# 조사 항목 방법 목적
C3 Shipment 관계 구조 Shipment::with(['vehicleDispatches', 'items'])->find(13) 관계 로드 확인
C3b ShipmentItem → item 관계 ShipmentItem 모델에 item() belongsTo 관계 존재 여부 확인 eager loading 가능 여부

2.2 백엔드 — getShipmentDetail() 보강

파일: api/app/Services/QmsLotAuditService.php (L450-470)

현재 응답 (불충분):

return [
    'type' => 'shipping',
    'data' => [
        'id', 'shipment_no', 'status', 'scheduled_date',
        'customer_name', 'site_name', 'delivery_address',
        'delivery_method', 'vehicle_no', 'driver_name', 'remarks'
    ],
];

보강 후 응답:

return [
    'type' => 'shipping',
    'data' => [
        // 기본 정보
        'id', 'shipment_no', 'lot_no', 'status',
        'scheduled_date',
        // ⚠️ shipment_date 컬럼 존재 여부 확인 필요 (없을 수 있음)
        'customer_name', 'customer_grade',
        'site_name',
        // ⚠️ registrant, orderer 컬럼 존재 여부 확인 필요

        // 배송 정보
        'delivery_method',
        'shipping_cost',      // ⚠️ freight_cost가 아님! 실제 컬럼명 = shipping_cost
        'receiver', 'receiver_contact',
        'delivery_address',
        // ⚠️ zip_code, address_detail은 별도 컬럼이 아닐 수 있음 (options 확인)

        // 배차정보 (출고증 전용)
        'vehicle_dispatches' => [
            [
                'logistics_company',
                'arrival_datetime',   // ⚠️ arrival_date가 아님! 실제 = arrival_datetime
                'tonnage',
                'vehicle_no',
                'driver_contact',
                'remarks',
            ],
        ],

        // 제품 그룹
        'product_groups' => [
            [
                'product_name' => '스크린',
                'specification' => '...',
                'part_count' => 5,
                'parts' => [
                    ['item_name', 'specification', 'quantity', 'unit'],
                ],
            ],
        ],
        'other_parts' => [...],
    ],
];

주의: Shipment 모델의 관계 로드 필요:

$shipment = Shipment::with([
    'vehicleDispatches',
    'items.item',           // ⚠️ ShipmentItem에 item() 관계가 없으면 추가 필요
    'order.rootNodes',      // 수주 개소 정보
])->findOrFail($id);

⚠️ Shipment 모델 필드 확인 필요 목록:

계획상 필드 확인 사항
shipment_date 컬럼 존재 여부 (scheduled_date만 있을 수 있음)
registrant 컬럼 존재 여부
orderer 컬럼 존재 여부
shipping_cost 실제 컬럼명 (freight_cost 아님)
zip_code 별도 컬럼 vs options JSON
address_detail 별도 컬럼 vs options JSON

2.3 프론트엔드 — ShipmentOrderDocument.tsx 수정

파일: react/src/components/outbound/ShipmentManagement/documents/ShipmentOrderDocument.tsx

현재: MOCK_* 상수 12개 사용 (SalesOrderDocument와 중복된 MOCK 상수들)

⚠️ 중요: props 구조

  • data: ShipmentDetail 단일 props — 헤더 정보에만 사용됨
  • 제품 테이블은 data 내의 productGroups/parts에서 매핑
  • showDispatchInfo / showLotColumn boolean 토글
# 작업 상태
2.3.1 MOCK_* 상수 제거 (12개)
2.3.2 data.productGroups → 제품 테이블 매핑
2.3.3 data.vehicleDispatches → 배차정보 매핑
2.3.4 showDispatchInfo/showLotColumn 조건부 렌더링 유지

출고증 vs 납품확인서 차이:

  • 출고증: showDispatchInfo=true, showLotColumn=true
  • 납품확인서: showDispatchInfo=false, showLotColumn=false

2.4 QMS 연동 — InspectionModal.tsx 출고증/납품확인서 케이스

⚠️ InspectionModal은 래퍼 컴포넌트를 사용함 (ShipmentOrderDocument를 직접 렌더링하지 않음)

현재 구조:

case 'confirmation':
  return <DeliveryConfirmation ... />;  // ← 래퍼 컴포넌트
case 'shipping':
  return <ShippingSlip ... />;          // ← 래퍼 컴포넌트

수정: 래퍼 컴포넌트(DeliveryConfirmation, ShippingSlip)가 내부적으로 ShipmentOrderDocument에 실 데이터를 전달하도록 수정

case 'confirmation':
  const confData = await getDocumentDetail('confirmation', item.id);
  return <DeliveryConfirmation data={confData.data} />;

case 'shipping':
  const shipData = await getDocumentDetail('shipping', item.id);
  return <ShippingSlip data={shipData.data} />;

DeliveryConfirmation → <ShipmentOrderDocument showDispatchInfo={false} showLotColumn={false} /> ShippingSlip → <ShipmentOrderDocument showDispatchInfo={true} showLotColumn={true} />


2.5 actions.ts transform 함수 추가

파일: react/src/app/[locale]/(protected)/quality/qms/actions.ts

현재: getDocumentDetail이 API 응답을 그대로 반환 (snake_case raw response, transform 없음)

추가 필요:

// snake_case → camelCase 변환 + 타입 매핑
function transformOrderDetail(raw: any): OrderDetailForDocument { ... }
function transformShipmentDetail(raw: any): ShipmentDetailForDocument { ... }

Phase 3: ⏸️ 보류 — mng 양식 템플릿 생성

보류 사유: document_templates 스키마는 검사 성적서용 설계 (고정 행 + EAV 패턴). 수주서/출고증은 동적 행 수 + 제품유형별 다른 컬럼 + 서브그룹 구조를 가지므로 현재 스키마로는 부적합.

Phase 1-2 완료 후, 양식 구조를 별도 설계하거나 document_templates 스키마를 확장하는 방안을 재검토.

재검토 시 확인 사항:

  • C4: mng 양식 편집기가 다중 섹션/조건부 섹션을 지원하는지
  • C5: 양식 카테고리 명명 패턴

Phase 4: ⏸️ 보류 — 양식 기반 렌더링 전환

Phase 3과 동일한 사유로 보류.


Phase 5: 통합 검증

5.1 검증 시나리오

# 시나리오 입력 기대 결과
T1 수주 57 수주서 스크린 제품 개소별 사이즈, 모터(좌/우), 절곡(서브그룹), 부자재 실 데이터
T2 수주 59 수주서 철재 제품 조인트바 컬럼 추가, 철재 전용 데이터
T3 수주 57 OrderDocumentModal 수주 페이지 T1과 동일한 실 데이터 렌더링
T4 출하 13 출고증 배차정보 포함 배차정보 + LOT 컬럼 표시
T5 출하 13 납품확인서 배차정보 미포함 배차/LOT 컬럼 숨김
T6 QMS 수주서 모달 로트 추적 심사 실 데이터 기반 수주서 렌더링
T7 QMS 출고증 모달 로트 추적 심사 실 데이터 기반 출고증 렌더링
T8 QMS 납품확인서 모달 로트 추적 심사 실 데이터 기반 납품확인서 렌더링

5.2 Phase 1-2 완료 후 서류 상태

# 문서 Phase 1-2 후 비고
1 수입검사 성적서 완료 기존 유지
2 수주서 실 데이터 Phase 1
3 작업일지 🔄 공정별 별도 계획
4 중간검사 성적서 🔄 공정별 별도 계획
5 납품확인서 실 데이터 Phase 2
6 출고증 실 데이터 Phase 2
7 제품검사 성적서 완료 기존 유지
8 품질관리서 PDF 업로드 PDF 유지

5. 사전 조사 목록

C1C3b는 각 Phase 시작 시 DB 조회로 직접 확인. C4C5는 Phase 3 재검토 시 확인.

# 항목 조사 방법 시점 상태
C1 bom_result 변수키 확인 SELECT id, JSON_EXTRACT(options, '$.bom_result.variables') FROM order_nodes WHERE order_id IN (57,59) AND parent_id IS NULL LIMIT 5 Phase 1.1 시작 시
C2 모터/절곡/부자재 데이터 출처 C1 결과에서 motor/guide_rail/case 키 확인 + SELECT * FROM order_items WHERE order_id IN (57,59) Phase 1.1 시작 시
C3 Shipment 관계 구조 Shipment::with(['vehicleDispatches', 'items'])->find(13) 관계 확인 Phase 2.1 시작 시
C3b ShipmentItem.item() 관계 ShipmentItem 모델에 item() belongsTo 관계 존재 여부 확인 Phase 2.1 시작 시
C4 mng 양식 편집기 확장 범위 mng Blade 코드 분석 — 다중 섹션/조건부 섹션 지원 여부 Phase 3 재검토 시 ⏸️
C5 양식 카테고리 명명 기존 document_templates.category 값 패턴 확인 후 결정 Phase 3 재검토 시 ⏸️

6. 검증 분석 결과 (계획 검증)

2026-03-12 검증 수행 결과. 계획 정확도 향상을 위해 반영됨.

6.1 CRITICAL 이슈 (계획에 반영 완료)

# 이슈 내용 조치
C-1 Order 필드명 불일치 client_phone, address, recipient_name, recipient_contact, manager_contact, fee — Order 모델에 없는 필드 제거/조건부로 변경
C-2 Shipment 필드명 불일치 shipment_date, registrant, orderer, zip_code, address_detail 미확인, freight_costshipping_cost 필드명 수정 + 확인필요 표시
C-3 SalesOrderDocument props 단일 data 객체가 아닌 18개 개별 props props 매핑 방식 수정
C-4 InspectionModal 래퍼 case 'confirmation' → DeliveryConfirmation, case 'shipping' → ShippingSlip 래퍼 사용 래퍼 컴포넌트 경유로 수정
C-5 arrival_datetime arrival_date가 아닌 arrival_datetime이 실제 컬럼명 수정 완료

6.2 HIGH 이슈 (계획에 반영 완료)

# 이슈 내용 조치
H-1 연기차단재 Mock 누락 MOCK_GUIDE_SMOKE, MOCK_CASE_SMOKE 미반영 bending_parts에 연기차단재 그룹 추가
H-2 products prop 사용됨 "미사용"이 아닌 L145에서 productName 표시에 활용 product_name 필드 매핑 추가
H-3 OrderDocumentModal 이중사용 SalesOrderDocument가 QMS + 수주 페이지 양쪽에서 사용 Phase 1.4 작업 항목 추가
H-4 bending_parts 서브그룹 단순 flat 배열로는 가이드/케이스/하단/연기차단재 구분 불가 group 필드 추가한 중첩 구조로 변경
H-5 motors 좌/우 분리 단일 배열로는 2열 레이아웃 불가 left/right 분리 구조로 변경
H-6 ShipmentItem.item() ShipmentItem에 item() belongsTo 관계 미확인 C3b 사전 조사 추가
H-7 actions.ts transform getDocumentDetail에 transform 함수 없음 (raw snake_case 반환) Phase 2.5 작업 추가

6.3 아키텍처 이슈 (Phase 3-4 보류 근거)

document_templates 스키마 부적합:

  • 현재 스키마: 고정 행 수 + EAV(Entity-Attribute-Value) 패턴 → 검사 성적서에 최적화
  • 수주서/출고증: 동적 행 수 + 제품유형별 다른 컬럼 + 서브그룹(모터 좌/우, 절곡 그룹) 구조
  • 결론: Phase 1-2 (실 데이터 매핑)를 먼저 완료하고, Phase 3-4는 양식 구조를 재설계한 후 진행

7. 변경 이력

날짜 항목 변경 내용 파일 승인
2026-03-12 러프 계획 초안 작성 (5 Phase 구조) 이 문서 -
2026-03-12 상세 계획 Phase 1-5 상세 작업 내용 추가 이 문서 -
2026-03-12 계획 확정 컨펌→사전조사 전환, bom_result 설명 보강 이 문서
2026-03-12 검증 반영 C1-5, H1-7 이슈 반영, Phase 3-4 보류, 필드명 수정, 구조 보강 이 문서

8. 참고 문서

문서 역할
docs/INDEX.md 문서 인덱스
docs/features/quality-management/README.md 품질관리 시스템 전체 구조
docs/system/database/documents.md 문서 템플릿 DB 스키마
docs/system/database/sales.md 수주/견적 DB 스키마
docs/dev/standards/api-rules.md API 개발 규칙
docs/dev/standards/quality-checklist.md 코드 품질 체크리스트

9. 참고 파일 (코드)

파일 역할 비고
api/app/Services/QmsLotAuditService.php QMS 서류 API 수정 대상 (L416-431, L450-470)
react/.../orders/documents/SalesOrderDocument.tsx 수주서 컴포넌트 수정 대상 (18개 개별 props)
react/.../orders/documents/OrderDocumentModal.tsx 수주 페이지 문서 모달 수정 대상 (dual-use, L167-190)
react/.../outbound/.../ShipmentOrderDocument.tsx 출고증/납품확인서 수정 대상 (data prop)
react/.../quality/qms/components/InspectionModal.tsx QMS 문서 뷰어 수정 대상 (래퍼 컴포넌트 경유)
react/.../quality/qms/actions.ts QMS 서버 액션 수정 대상 (transform 추가)
react/.../quality/qms/components/documents/ImportInspectionDocument.tsx 수입검사 참고 패턴
react/.../quality/InspectionManagement/documents/InspectionReportDocument.tsx 제품검사 참고 패턴
api/app/Models/Documents/DocumentTemplate.php 양식 모델 참고
api/app/Models/Orders/Order.php 수주 모델 필드 확인 필요
api/app/Models/Orders/OrderNode.php 수주 노드 모델 bom_result 구조
api/app/Models/Tenants/Shipment.php 출하 모델 필드 확인 필요
api/app/Models/Tenants/ShipmentItem.php 출하 품목 item() 관계 확인
api/app/Models/Tenants/ShipmentVehicleDispatch.php 배차 모델 arrival_datetime
api/app/Services/OrderService.php 수주 서비스 데이터 로드 패턴 참고
api/app/Services/ShipmentService.php 출하 서비스 데이터 로드 패턴 참고

10. 세션 및 메모리 관리 정책 (Serena Optimized)

10.1 세션 시작 시 (Load Strategy)

read_memory("qms-doc-template-state")          // 1. 상태 파악
read_memory("qms-doc-template-snapshot")        // 2. 사고 흐름 복구
read_memory("qms-doc-template-active-symbols")  // 3. 작업 대상 파악

10.2 작업 중 관리 (Context Defense)

컨텍스트 잔량 Action 내용
30% 이하 Snapshot write_memory("qms-doc-template-snapshot", "코드변경+논의요약")
20% 이하 Context Purge write_memory("qms-doc-template-active-symbols", "주요 수정 파일/함수")
10% 이하 Stop & Save 최종 상태 저장 후 세션 교체 권고

10.3 Serena 메모리 구조

  • qms-doc-template-state: { phase, progress, next_step, last_decision }
  • qms-doc-template-snapshot: 현재까지의 논의 및 코드 변경점 요약
  • qms-doc-template-rules: 해당 작업에서 결정된 불변의 규칙들
  • qms-doc-template-active-symbols: 현재 수정 중인 파일/심볼 리스트

11. 검증 결과

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

11.1 테스트 케이스

입력값 예상 결과 실제 결과 상태
수주 57 (스크린) 수주서 개소별 실 데이터 + 모터 좌/우 + 절곡 서브그룹
수주 59 (철재) 수주서 조인트바 포함 실 데이터
수주 57 OrderDocumentModal 수주 페이지에서 동일 렌더링
출하 13 출고증 배차정보 + 제품 실 데이터
출하 13 납품확인서 제품 실 데이터 (배차 제외)
QMS 전체 시나리오 8종 서류 실 데이터 확인

12. 자기완결성 점검 결과

12.1 체크리스트 검증

# 검증 항목 상태 비고
1 작업 목적이 명확한가? Mock 제거 → 실 데이터 (Phase 1-2)
2 성공 기준이 정의되어 있는가? SC-13b (Phase 1-2), SC-45 보류
3 작업 범위가 구체적인가? Phase 1-2, 12개 세부 작업
4 의존성이 명시되어 있는가? Phase 간 의존성 + 사전 조사 의존
5 참고 파일 경로가 정확한가? 섹션 8, 9에 전체 목록 + 라인 번호
6 단계별 절차가 실행 가능한가? 코드 수준 상세 + 검증 이슈 반영
7 검증 방법이 명시되어 있는가? Phase 5 검증 시나리오 T1-T8
8 모호한 표현이 없는가? ⚠️ C1-C3b 사전 조사 필요 (의도적)

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

질문 답변 가능 참조 섹션
Q1. 이 작업의 목적은 무엇인가? 1.1 배경, 1.3 목표
Q2. 어디서부터 시작해야 하는가? 3.1 전체 흐름, Phase 1.1 사전 조사
Q3. 어떤 파일을 수정해야 하는가? 9. 참고 파일
Q4. 작업 완료 확인 방법은? 1.4 성공 기준, 11. 검증 결과
Q5. 막혔을 때 참고 문서는? 8. 참고 문서
Q6. 어떤 필드명이 잘못될 수 있는가? 6. 검증 분석 결과

이 문서는 /plan 스킬로 생성되었습니다.