Files
sam-docs/plans/integrated-phase-2.md

19 KiB
Raw Blame History

Phase 2: 절곡 검사 분석/설계 + 견적/품질 개선

통합 계획: integrated-master-plan.md 원본:


1. Phase 2A: 절곡 검사 분석/설계

목표: 절곡 구성품(검사 항목) 정보를 API에서 제공하는 구조 설계 Phase 1과 병렬 가능 (분석 전용, 코드 변경 없음)

1.1 작업 항목

# 작업 항목 상태 비고
2A.1 절곡 제품코드별 구성품(BOM) 데이터 구조 분석 7개 모델 18종 FG, 150+ BD 구성품, dynamic_bom 발견
2A.2 마감유형(S1/S2/S3)별 차이 분석 5130에서 S1/S2/S3별 갭 포인트 수·값 차이 확인
2A.3 inspection-config 범용 API 설계 dynamic_bom 활용으로 설계 단순화. 상세 1.6절 참조
2A.4 DEFAULT_GAP_PROFILES 기준치 5130 대조 Template 3값 오류, 측면형 전면 불일치. 상세 1.7절 참조

1.2 구성품 데이터 소스 분석 결과

분석 대상 → 결과:
1. items 테이블 — FG 18종, BD 150+ 종 등록 확인 ✅
2. bom_templates — 1건만 존재 (MATERIAL 참조). 제품별 BOM은 미등록
3. order_nodes.options.bending_info — 데이터 없음 (0건)
4. work_order_items.options.dynamic_bom — ✅ 핵심 발견! BOM 기반 구성품이 이미 저장됨
5. 5130/output/viewMidInspectBending.php — 갭 기준치 원본 (S1/S2/S3별)
6. TemplateInspectionContent DEFAULT_GAP_PROFILES — 일부 오류 확인 (1.7절)

1.2.1 핵심 발견: dynamic_bom

work_order_items.options에 BOM 기반 구성품이 카테고리별로 이미 저장되어 있음:

{
  "dynamic_bom": [
    { "category": "guideRail", "part_type": "마감재", "child_item_code": "BD-SS-35", "length_mm": 3500, "qty": 6 },
    { "category": "guideRail", "part_type": "본체", "child_item_code": "BD-SM-35", "length_mm": 3500, "qty": 6 },
    { "category": "bottomBar", "part_type": "메인", "child_item_code": "BD-BS-40", "length_mm": 4000, "qty": 3 },
    { "category": "bottomBar", "part_type": "L-Bar", "child_item_code": "BD-LA-40", "length_mm": 4000, "qty": 3 },
    { "category": "shutterBox", "part_type": "전면부", "child_item_code": "BD-XX-35", "length_mm": 3500, "qty": 3 },
    { "category": "smokeBarrier", "part_type": "연기차단재(W50)", "child_item_code": "BD-GI-54", "length_mm": 4000, "qty": 6 },
    { "category": "smokeBarrier", "part_type": "연기차단재(W80)", "child_item_code": "BD-GI-83", "length_mm": 3000, "qty": 9 }
  ]
}

카테고리 매핑:

dynamic_bom category 검사 대상 구성품 part_type 예시
guideRail 가이드레일 마감재, 본체, C형, D형, 하부BASE
bottomBar 하단마감재 메인, L-Bar, 보강평철
shutterBox 케이스 전면부, 린텔부, 점검구, 후면코너부, 상부덮개, 마구리
smokeBarrier 연기차단재 W50, W80

1.2.2 완제품(FG) 품목 현황

모델 설치형 마감 FG 코드 절곡 구성품 규격
KWE01 벽면/측면 SUS, EGI FG-KWE01-* 가이드레일 120×120/70, 하단마감재 64×43/60×40, L-BAR 17×60
KSS01 벽면/측면 SUS FG-KSS01-* 가이드레일 120×120/70, 하단마감재 60×40, L-BAR 17×60
KSS02 벽면/측면 SUS FG-KSS02-* 가이드레일 120×120/70, 하단마감재 60×40, L-BAR 17×60
KQTS01 벽면/측면 SUS FG-KQTS01-* 가이드레일 130×125/75, 하단마감재 60×30
KTE01 벽면/측면 SUS, EGI FG-KTE01-* 가이드레일 130×125/75, 하단마감재 64×34/60×30
KSE01 벽면/측면 SUS, EGI FG-KSE01-* 가이드레일 120×120/70, 하단마감재 64×43/60×40, L-BAR 17×60
KDSS01 SUS 가이드레일 150×150/212, 하단마감재 140×78, L-BAR 17×100

1.3 구성품 결정 로직 (설계안)

입력: work_order_id
  ↓
1차: 작업지시 → 공정 자동 판별 (inspection-config API)
  ↓
2차: product_code → BOM 테이블에서 하위 구성품 조회
  ↓ (BOM 미등록 시)
3차: DEFAULT_GAP_PROFILES 기본값 사용
  ↓ (템플릿 미설정 시 = 레거시)
4차: INITIAL_PRODUCTS fallback (BendingInspectionContent, KWE01 하위호환)

각 단계의 역할은 다음과 같다.

  • 1차 (공정 판별): work_order_id로부터 작업지시의 공정 타입(bending, screen, slat)을 자동 판별한다. 비절곡 공정이면 빈 items 배열을 반환하고 종료한다.
  • 2차 (BOM 조회): 해당 제품코드의 BOM에 등록된 구성품 목록을 조회한다. BOM 데이터가 있으면 이를 기준으로 검사 항목을 구성한다.
  • 3차 (기본 프로파일): BOM에 구성품이 등록되지 않은 경우, DEFAULT_GAP_PROFILES에 정의된 기본 갭 기준치를 사용한다.
  • 4차 (레거시 fallback): 템플릿 설정도 없는 경우, 기존 BendingInspectionContentINITIAL_PRODUCTS 7개 항목을 KWE01 전용 하위호환으로 사용한다.

1.4 inspection-config API 설계안 (I5 정책 결정)

GET /api/v1/work-orders/{id}/inspection-config

※ BelongsToTenant 스코프 필수 (M1)
※ 공정 타입 자동 판별

절곡 Response:

{
  "data": {
    "work_order_id": 123,
    "process_type": "bending",
    "product_code": "FG-KQTS01",
    "finish_type": "S1",
    "template_id": 60,
    "items": [
      {
        "id": "guide-rail-wall",
        "category": "KWE01",
        "product_name": "가이드레일",
        "product_type": "벽면형",
        "length_design": "3000",
        "width_design": "N/A",
        "gap_points": [
          { "point": "1", "design_value": "30" },
          { "point": "2", "design_value": "78" }
        ]
      }
    ]
  }
}

비절곡 Response (스크린/슬랫):

{
  "data": {
    "work_order_id": 456,
    "process_type": "screen",
    "product_code": "FG-KQTS01",
    "template_id": 12,
    "items": []
  }
}

응답 필드 설명:

필드 타입 설명
work_order_id integer 작업지시 ID
process_type string 공정 타입 (bending, screen, slat)
product_code string 제품코드
finish_type string|null 마감유형 (절곡 전용: S1, S2, S3)
template_id integer|null 검사 양식 ID
items array 검사 대상 구성품 목록 (비절곡 시 빈 배열)
items[].id string 구성품 식별자 (kebab-case)
items[].category string 제품 카테고리 코드
items[].product_name string 구성품 명칭
items[].product_type string 구성품 유형/규격
items[].length_design string 설계 길이
items[].width_design string 설계 폭 (N/A 가능)
items[].gap_points array 갭 측정 포인트 목록

1.6 inspection-config API 설계 (수정안)

dynamic_bom 발견으로 기존 설계안 대비 단순화됨:

입력: work_order_id
  ↓
1차: work_order → process 자동 판별 (bending/screen/slat)
  ↓ (비절곡이면 빈 items 반환)
2차: work_order_items.options.dynamic_bom → 카테고리별 구성품 추출
  ↓
3차: 카테고리 + 마감유형(S타입) → 갭 기준치 매핑 (GAP_PROFILES 테이블 or 상수)
  ↓ (dynamic_bom 없으면)
4차: DEFAULT_GAP_PROFILES fallback
  ↓ (템플릿 미설정 = 레거시)
5차: INITIAL_PRODUCTS fallback (BendingInspectionContent)

기존 설계 대비 변경점:

  • 2차에서 BOM 테이블 조회 → dynamic_bom JSON 직접 사용 (DB 조회 불필요)
  • 케이스 갭 포인트: 고정값 → dynamic_bomlength_mm 기반 동적 계산

1.7 DEFAULT_GAP_PROFILES 5130 대조 결과

원본 소스: 5130/output/viewMidInspectBending.php (L170-786)

가이드레일 벽면형

포인트 5130 S1 5130 S2 5130 S3 Template (신규) Bending (레거시) 판정
(1) 30 30 30 30 30
(2) 80 80 80 78 80 Template
(3) 45 45 45 25 45 Template
(4) 40 40 45 40 Template
(5) 34 34 S3 전용

Bending(레거시)이 5130과 일치, Template에 3개 값 오류

가이드레일 측면형

포인트 5130 S1 Template Bending 판정
(1) 30 28 28 둘 다
(2) 70 75 75 둘 다
(3) 45 42 42 둘 다
(4) 35 38 38 둘 다
(5) 95 32 32 둘 다
(6) 90 누락

Template과 Bending 모두 5130 S1과 불일치 — 별도 근거 확인 필요 (다른 S타입 or 버전)

케이스

포인트 5130 Template Bending 판정
(1) boxheight (동적) 550 380 5130=동적계산
(2) frontbottom/50 (동적) 50 50
(3) 계산식 (동적) 385 240 5130=동적계산
(4) frontbottom/boxheight 50 50
(5) boxheight-140 410 5130=동적계산

5130은 주문 정보(boxwidth/boxheight)에서 동적 계산 — 두 컴포넌트 모두 특정 사이즈 고정값

하단마감재

포인트 5130 S1/S2 5130 S3 Template Bending 판정
(1) 60 60 60 60
(2) 64 64 S3 전용

→ Bending이 S3까지 커버, Template은 S1/S2만

하단 L-BAR / 연기차단재

항목 5130 Template Bending 판정
L-BAR (1) 17 17 (Template에 없음)
연기차단재 W50 50, 12 50, 12 50, 12 3자 일치
연기차단재 W80 80, 12 80, 12 80, 12 3자 일치

5130 마감유형별 갭 포인트 수 정리

구성품 S1 S2 (KSS02형) S3 (별도마감형)
가이드레일 벽면 4점 3점 5점
가이드레일 측면 6점 5점 7점
하단마감재 1점 1점 2점
L-BAR 1점 1점 1점
케이스 동적 (점검구 방향별) 동적 동적
연기차단재 2점 2점 2점

Phase 3 대응 방향 (I1: Single Source of Truth)

  1. DEFAULT_GAP_PROFILES 수정 필요: 벽면형 3값 오류 → 5130 기준으로 보정
  2. 측면형 재검토: Template/Bending 모두 5130 S1과 다름 → 사용 중인 실제 버전 확인 필요
  3. 케이스 동적 계산: dynamic_bom의 치수 정보 활용하여 동적 계산 구현
  4. 마감유형 분기: S1/S2/S3별 갭 포인트 수 차이 → inspection-config API에서 처리

1.8 현재 하드코딩 현황 (레거시 동결 — C3)

BendingInspectionContent.tsxINITIAL_PRODUCTS (7개, KWE01 전용):

# 항목 ID productName productType gapPoints 수
1 guide-rail-wall 가이드레일 벽면형 5
2 guide-rail-side 가이드레일 측면형 5
3 case 케이스 500X380 4
4 bottom-finish 하단마감재 60X40 2
5 bottom-l-bar 하단L-BAR 17X60 1
6 smoke-w50 연기차단재 W50 가이드레일용 2
7 smoke-w80 연기차단재 W80 케이스용 2

Phase 2A 분석 완료 후, 이 하드코딩 항목들은 inspection-config API 응답으로 대체될 예정이다. 현재는 C3(레거시 동결) 정책에 따라 수정하지 않는다.


2. Phase 2B: 견적/수주 정합성 + 품질검사 FK

목표: quotes.product_code 활용 + inspectionswork_orders FK 연결 선행 조건: Phase 1 완료 내부 병렬성: 2B-견적과 2B-품질은 독립 경로

Phase 2B 내부 구조:

  Phase 1 완료
       |
  +----+----+
  |         |
2B-견적   2B-품질
(2B.1~3)  (2B.4~7)
  |         |
  +----+----+
       |
    Phase 2B 완료

2.1 견적 데이터 정합성 (원본 Phase 2)

# 작업 항목 상태 비고
2B.1 견적 저장 시 quotes.product_code 저장 extractProductCodeFromInputs() 자동 추출 추가
2B.2 견적→수주 변환 시 camelCasesnake_case 변환 확인 L673 이미 정상 동작 확인
2B.3 기존 데이터 보정 스크립트 25/46건 보정 완료 (21건 초기 데이터 productCode 없음)

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

의존성 주의: orders.item_id 설정은 items 테이블에 FG 품목 등록이 필요하므로 Phase 5에서 처리한다.

데이터 보정 스크립트 상세:

// 2B.3 보정 로직 개요
// quotes 테이블에서 product_code가 null인 레코드 대상
// calculation_inputs JSON에서 items[0].productCode 추출하여 저장

$quotes = Quote::whereNull('product_code')
    ->whereNotNull('calculation_inputs')
    ->get();

foreach ($quotes as $quote) {
    $inputs = json_decode($quote->calculation_inputs, true);
    $productCode = $inputs['items'][0]['productCode'] ?? null;
    if ($productCode) {
        $quote->update(['product_code' => $productCode]);
    }
}

2.2 품질검사 연결 강화 (원본 Phase 3)

# 작업 항목 상태 비고
2B.4 inspections 테이블에 work_order_id FK 마이그레이션 마이그레이션 실행 완료, nullable + nullOnDelete
2B.5 Inspection 모델에 workOrder() 관계 메서드 추가 양방향: Inspection→workOrder, WorkOrder→inspections
2B.6 품질검사 생성 시 work_order_id 설정 로직 store/show/index + transformToFrontend 업데이트
2B.7 기존 inspections 데이터에 work_order_id 보정 대상 0건 — 보정 불필요

마이그레이션 설계 (2B.4):

Schema::table('inspections', function (Blueprint $table) {
    $table->unsignedBigInteger('work_order_id')->nullable()->after('id');
    $table->foreign('work_order_id')
          ->references('id')
          ->on('work_orders')
          ->nullOnDelete();
    $table->index('work_order_id');
});

모델 관계 (2B.5):

// Inspection.php
public function workOrder(): BelongsTo
{
    return $this->belongsTo(WorkOrder::class);
}

// WorkOrder.php
public function inspections(): HasMany
{
    return $this->hasMany(Inspection::class);
}

역추적 보정 로직 (2B.7):

inspections.lot_no → work_order_items.lot_no → work_orders.id
  |
중복 검사: 동일 lot_no에 다수 work_order 매칭 시 경고 로그
  |
MATCH: work_order_id 설정
NO_MATCH: null 유지 (수동 보정 대상)

3. 검증 결과

3.1 Phase 2A 검증

조사 항목 결과 판단
KWE01 구성품 가이드레일(SUS/EGI 120×120/70), 하단마감재(SUS 64×43, EGI 60×40), L-BAR 17×60 items 등록 확인
KSS01 구성품 가이드레일(SUS 120×120/70), 하단마감재(SUS 60×40), L-BAR 17×60 items 등록 확인
KSS02 구성품 가이드레일(SUS 120×120/70), 하단마감재(SUS 60×40), L-BAR 17×60 items 등록 확인
KQTS01 구성품 가이드레일(SUS 130×125/75), 하단마감재(SUS 60×30) L-BAR 없음
dynamic_bom 존재 work_order_items.options에 카테고리별 BOM 저장됨 (1건 확인) 핵심 발견
마감유형(S1/S2/S3)별 차이 갭 포인트 수·값 차이. 벽면 S1=4점, S2=3점, S3=5점 상세 1.7절
DEFAULT_GAP_PROFILES 5130 대조 벽면형 3값 오류, 측면형 전면 불일치, 케이스 동적계산 상세 1.7절
inspections 테이블 0건 (데이터 없음), work_order_id 컬럼 미존재 2B.4 FK 추가 필요
quotes.product_code 컬럼 존재, 49건 중 0건 채워짐 2B.1 저장 로직 + 2B.3 보정 필요

3.2 Phase 2B 검증

테스트 예상 결과 실제 결과 상태
견적 저장 시 quotes.product_code 첫 번째 개소 코드 T5.1: extractProductCodeFromInputs('FG-TEST-001')FG-TEST-001
다중 개소 대표 코드 첫 번째 개소 T5.4: 2개소 [FG-FIRST, FG-SECOND]FG-FIRST 반환
CI 없는 경우 null 반환 null T5.2: {}null, T5.3: {items:[]}null
견적→수주 변환 camelCasesnake_case 정상 변환 T4: order_nodes 5건 확인 — $.product_code 존재, $.productCode NULL
inspections.work_order_id FK 마이그레이션 성공, nullable T2: bigint unsigned, MUL 인덱스, FK→work_orders.id 확인
Inspection 모델/서비스 회귀 정상 T3: fillable YES, workOrder() YES, inspections() YES, index() total=0 OK
기존 데이터 보정 (quotes) 보정 완료 T1: 25/49건 보정 (21건 CI에 productCode 없음, 3건 CI 자체 없음)

4. 참고 파일

4.1 Phase 2A 관련

파일 역할
react/.../documents/TemplateInspectionContent.tsx DEFAULT_GAP_PROFILES (L184-214), buildBendingProducts (L217-282)
react/.../documents/BendingInspectionContent.tsx INITIAL_PRODUCTS (L71-135, 레거시 동결)
5130/output/viewMidInspectBending.php 절곡 중간검사 성적서 원본 (L170-786, 갭 기준치)
5130/estimate/common/common_addrowJS.php 레거시 구성품 정의

4.2 Phase 2B 관련

파일 역할
api/app/Services/Quote/QuoteService.php 견적 서비스 (product_code L324)
api/app/Services/InspectionService.php 품질검사 서비스
api/app/Models/Quality/Inspection.php 검사 모델

5. 변경 이력

날짜 항목 변경 내용
2026-02-27 문서 작성 통합 계획 Phase 2 상세 문서 작성
2026-02-27 2A 분석 완료 BOM 구조 분석(dynamic_bom 발견), 마감유형 S1/S2/S3 차이 분석, inspection-config API 재설계, DEFAULT_GAP_PROFILES 5130 대조 완료. 1.2~1.7절 추가
2026-02-27 2B 구현 완료 견적 product_code 자동추출(2B.1), camelCase 확인(2B.2), 25건 보정(2B.3), inspections.work_order_id FK(2B.4), 양방향 관계(2B.5), 서비스 업데이트(2B.6), 0건 보정(2B.7)
2026-02-27 2B 테스트 완료 SQL 4건(T1T4) + 단위테스트 4건(T5.1T5.4) 전항 PASS. 검증 결과 3.2절 업데이트