- 수주서/출고증/납품확인서 Mock→실데이터 전환 계획 - Phase 1-2 (실 데이터 매핑) 상세화, Phase 3-4 보류 - 검증 분석 반영 (필드명 수정, 구조 보강, 래퍼 컴포넌트 경유)
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 | ⏸️ 보류 | |
| SC-5 | ⏸️ 보류 |
1.5 변경 승인 정책
| 분류 | 예시 | 승인 |
|---|---|---|
| ✅ 즉시 가능 | Mock 데이터 제거, API 응답 필드 추가, 프론트 데이터 매핑 | 불필요 |
| ⚠️ 컨펌 필요 | API 엔드포인트 변경, 새 양식 카테고리 추가, DB 스키마 변경 | 필수 |
| 🔴 금지 | document_templates 테이블 구조 변경, 기존 수입검사 양식 로직 변경 | 별도 협의 |
1.6 준수 규칙
docs/dev/standards/api-rules.md— Service-First, FormRequestdocs/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 | ⏸️ 보류 | 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/showLotColumnboolean 토글
| # | 작업 | 상태 |
|---|---|---|
| 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. 사전 조사 목록
C1
C3b는 각 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_cost → shipping_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-1 |
| 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 스킬로 생성되었습니다.