Files
sam-docs/plans/bending-material-input-mapping-plan.md
권혁성 3fff99095e docs: 절곡/품목 관련 신규 계획 문서 추가
- 절곡 정보 자동 생성 계획 (bending-info-auto-generation)
- 절곡 자재투입 매핑 GAP 분석 (bending-material-input-mapping)
- FG 코드 통합 계획 (fg-code-consolidation)
- 품목 재고 관리 계획 (item-inventory-management)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 10:02:47 +09:00

31 KiB
Raw Blame History

절곡 세부품목 → 자재투입 → LOT 매핑 통합 개발 계획

작성일: 2026-02-21 목적: 절곡 작업일지의 4대 제품 카테고리(가이드레일/하단마감재/셔터박스/연기차단재) 세부품목을 items 테이블과 연동하고, BOM 기반 자재투입 → LOT 추적 파이프라인 구축 기준 문서: 5130/output/viewBendingWork_UA.php, api/app/Services/Production/BendingInfoBuilder.php, docs/plans/bending-preproduction-stock-plan.md 상태: 📋 분석 완료, 개발 계획 수립 중


📍 현재 진행 상태

항목 내용
마지막 완료 작업 LOT 추적 데이터 누락 분석 (7개 GAP 발견, 조치 계획 수립)
다음 작업 GAP 1 즉시 수정 (registerMaterialInput 통일) → 방안 B 구현
진행률 분석 완료, GAP 해결 및 개발 착수 전
마지막 업데이트 2026-02-22

1. 개요

1.1 배경

절곡 작업일지(WorkerScreen)에는 4대 제품 카테고리가 표시되며, 각 카테고리별 세부품목에 LOT 번호를 입력하여 자재를 투입해야 한다.

작업일지 (절곡 WO202602210027)
├── 1. 가이드레일 (세부: 마감재, 본체, C형, D형, 하부BASE)
├── 2. 하단마감재 (세부: 하단마감재, 보강엘바, 보강평철, 별도마감)
├── 3. 셔터박스 (세부: 전면부, 린텔부, 점검구, 후면부, 상부덮개, 마구리)
└── 4. 연기차단재 (세부: 레일용 W50, 케이스용 W80)

현재 상태:

  • 구현 완료: BendingInfoBuilder(bending_info 자동생성), Items Master(BD-XX-XX 품목 등록), getMaterials API, 자재투입/LOT 연동 API
  • 미구현(핵심 Gap): 세부품목이 items 테이블의 BOM으로 연결되지 않아 자재투입 시 세부품목별 LOT 매핑 불가

1.2 핵심 문제

현재 흐름 (불완전):
  견적 → bom_result에 부모 품목 저장 (BD-가이드레일-KSS01-SUS-120*70, qty=8.5m)
       → 작업지시 → BendingInfoBuilder가 길이 버킷팅 (4300mm×1, 4000mm×1)
       → work_order_items에 부모 품목 등록
       → getMaterials() 호출 시 item.bom이 null
       → fallback: 부모 품목 자체를 자재로 표시 (1건)
       → 세부품목(BD-RS-43, BD-RM-40 등) LOT 매핑 불가

목표 흐름 (방안 B 채택):
  견적 → bom_result에 부모 품목 저장 (기존 그대로, 수정 불필요)
       → 작업지시 생성 시 BendingInfoBuilder 확장:
         길이 버킷팅 결과로 BD-XX-NN 세부품목 조회 → 동적 BOM 생성
       → work_order_items.options.dynamic_bom에 세부품목 저장
       → getMaterials()에서 dynamic_bom 우선 사용
       → 각 세부품목별 StockLot 조회 → LOT 입력 → 자재투입 완료

1.3 성공 기준

기준 측정 방법
작업일지의 4대 카테고리 세부품목이 items와 1:1 매핑 각 세부품목의 item_id 존재 확인
자재투입 화면에서 세부품목별 LOT 입력 가능 getMaterials API가 세부품목 리스트 반환
LOT 번호 입력 시 재고 차감 정상 동작 stock_transactions 기록 확인
레거시 5130과 동일한 LOT prefix 체계 유지 LOT prefix 코드 일치 검증

2. 레거시 5130 절곡품 체계 분석

2.1 제품코드 시스템

참고: 제품코드는 작업일지 4대 카테고리(가이드레일/하단마감재/셔터박스/연기차단재)와 별개 개념. 제품코드는 스크린/철재 × SUS/EGI 조합에 의한 제품 모델 구분이며, 각 모델별로 전개치수가 다르다.

제품코드 마감재질 설명
KSS01 SUS 1.2T (기본) 스크린 SUS
KSS02 SUS 1.2T 스크린 SUS (변형)
KSE01 EGI 1.55T (기본) + 옵션 SUS 스크린 EGI (표준)
KWE01 EGI 1.55T (기본) + 옵션 SUS 스크린 EGI (광폭)
KTE01 EGI/SUS 철재
KDSS01 SUS 디딤형 SUS
KQTS01 SUS 특수형

마감재질 결정 로직 (5130/output/viewBendingWork_UA.php:317-355):

KSS01/KSS02 → GuidrailFinish = SUS 1.2T, bodyMaterial = EGI 1.55T
KSE01/KWE01 + SUS마감 → GuidrailFinish = EGI 1.55T, GuidrailExtraFinish = SUS 1.2T
KSE01/KWE01 + EGI마감 → GuidrailFinish = EGI 1.55T, GuidrailExtraFinish = EGI 1.55T

2.2 LOT Prefix 전체 맵

2.2.1 가이드레일 (Guide Rail)

벽면형 (Wall type, 412*350)

세부품목 KSS01 prefix KSE01/KWE01 EGI prefix KSE01/KWE01 SUS prefix
①마감재 RS RE RE
②본체 RM RM RM
③C형 RC RC RC
④D형 RD RD RD
⑤별도마감 - - YY
하부BASE XX XX XX

측면형 (Side type, 120*120)

세부품목 KSS01 prefix KSE01/KWE01 EGI prefix KSE01/KWE01 SUS prefix
①②마감재 SS SE SE
③본체 SM SM SM
④본체디딤 SC SC SC
⑤C형 SD SD SD
⑥D형 SM SM SM
⑦⑧별도마감 - - YY
하부BASE XX XX XX

2.2.2 하단마감재 (Bottom Bar)

세부품목 EGI prefix SUS prefix 재질 전개치수
①하단마감재 BE BS EGI 1.55T / SUS 1.2T (60*40)
②보강엘바 LA LA EGI 1.55T (60*17)
③보강평철 HH HH EGI 1.15T -
④별도마감재 YY - SUS 1.2T (SUS마감 시만) -

하단마감재 prefix 결정 로직 (5130:718-721):

if ($GuidrailFinish == 'EGI 1.55T')  $BTmat = 'BE';
else  $BTmat = 'BS';

2.2.3 셔터박스 (Shutter Box)

표준 사이즈 (500*380)

세부품목 prefix 치수 계산
①전면부 CF boxheight + 122
②린텔부 CL boxwidth - 330
③점검구 CP boxwidth - 200
④후면코너부/후면부 CB 170 또는 boxheight + 170
⑥상부덮개 XX -
⑦마구리(측면부) XX -

비표준 사이즈: 모든 세부품목에 XX prefix 사용

2.2.4 연기차단재 (Smoke Barrier)

세부품목 prefix 재질
레일용 W50 GI EGI 0.8T + 화이바 글라스 코팅직물
케이스용 W80 GI EGI 0.8T + 화이바 글라스 코팅직물

2.3 길이 코드 매핑 (getSLengthCode)

길이(mm) 코드 카테고리
1219 12 기타
2438 24 기타
3000 30 기타
3500 35 기타
4000 40 기타
4150 41 기타
4200 42 기타
4300 43 기타
3000 53 연기차단재50
4000 54 연기차단재50
3000 83 연기차단재80
4000 84 연기차단재80

2.4 동적 품목코드 생성 규칙

5130에서 LOT 입력 시 사용되는 data-itemname 속성:

[PREFIX]-[LENGTH_CODE]

예시:
  RS-40  = 가이드레일 벽면형 SUS 마감재 4000mm
  RM-35  = 가이드레일 본체 3500mm
  BE-30  = 하단마감재 EGI 3000mm
  CF-24  = 셔터박스 전면부 2438mm
  GI-53  = 연기차단재 W50 3000mm

핵심: 품목코드가 길이에 따라 동적으로 결정됨. 같은 "마감재"라도 3000mm면 RS-30, 4000mm면 RS-40이 된다.


3. SAM 현재 구현 현황

3.1 구현 완료

기능 위치 설명
BendingInfoBuilder api/app/Services/Production/BendingInfoBuilder.php 수주→작업지시 시 bending_info JSON 자동생성
categorizeBomItem() 위 파일 :96-130 BOM 아이템을 8개 카테고리로 분류
Items Master (BD-*) items 테이블 (로컬+dev) 절곡 품목 148개 (제품 마스터형 58 + LOT prefix형 90)
getMaterials API WorkOrderService.php:1183 work_order_items 순회 → item.bom 확인 → StockLot 조회
getMaterialsForItem API WorkOrderService.php:2678 개별 품목 자재 조회
registerMaterialInput react/.../WorkerScreen/actions.ts:288 자재투입 등록 POST API
increaseFromProduction api/app/Services/StockService.php 생산완료 → 재고입고
선생산 재고 흐름 docs/plans/bending-preproduction-stock-plan.md Phase 1-3 완료

3.2 BD-* 품목 현황 (로컬 DB 확인 완료)

총 148개 BD-* 품목 (2026-02-21 확인):

A. 제품 마스터형 (58개) — 부모 품목 (제품코드+재질+전개치수)

BD-가이드레일-KSS01-SUS-120*70   (20개: KSS01/KSS02/KSE01/KWE01/KTE01/KDSS01/KQTS01별)
BD-하단마감재-KSE01-EGI-60*40    (10개)
BD-케이스-500*380                 (10개: 사이즈별)
BD-마구리-505*355                 (10개: 사이즈별)
BD-L-BAR-KSS01-17*60             (5개)
BD-보강평철-50                    (1개)
BD-가이드레일용 연기차단재         (1개)
BD-케이스용 연기차단재             (1개)

B. LOT prefix형 (90개) — 자재투입 대상 세부품목 (길이별)

prefix 개수 설명
BD-RS 5 가이드레일(벽면) SUS 마감재
BD-RM 6 가이드레일(벽면) 본체
BD-RC 6 가이드레일(벽면) C형
BD-RD 6 가이드레일(벽면) D형
BD-RT 2 가이드레일(벽면) 본체(철재)
BD-SS 4 가이드레일(측면) SUS 마감재
BD-SM 5 가이드레일(측면) 본체/D형
BD-SC 5 가이드레일(측면) C형
BD-SD 5 가이드레일(측면) D형
BD-ST 1 가이드레일(측면) 본체(철재)
BD-SU 4 가이드레일(측면) SUS2 (별도마감)
BD-BE 2 하단마감재(스크린) EGI
BD-BS 5 하단마감재(스크린) SUS
BD-TS 1 하단마감재(철재) SUS
BD-LA 2 L-Bar 스크린용
BD-CF 6 케이스 전면부
BD-CL 6 케이스 린텔부
BD-CP 6 케이스 점검구
BD-CB 6 케이스 후면코너부
BD-GI 7 연기차단재 화이바원단

XX(하부BASE), YY(별도SUS마감), HH(보강평철)은 미등록 → 방안 B 구현 전 BD-XX-NN, BD-YY-NN, BD-HH-NN 형태로 등록 예정

3.3 미구현 Gap → 해결 방향

방안 B 확정(섹션 4) 및 LOT GAP 분석(섹션 7)으로 모두 해결 방향 확정됨.

Gap 해결 방향 참조
items.bom 연결 (bom = null) dynamic_bom으로 대체 (items.bom 수정 불필요) 섹션 4.4, 4.5
가변 세부품목 배정 BendingInfoBuilder 확장으로 길이별 동적 품목 결정 섹션 4.3
order_items 세부품목 bom_result 기반으로 BendingInfoBuilder가 직접 생성, order_items 수정 불필요 섹션 4.3
LOT prefix 매핑 dynamic_bom JSON에 lot_prefix 필드 포함 섹션 4.4
XX/YY/HH 미등록 품목 BD-XX-NN, BD-YY-NN, BD-HH-NN 형태로 items에 등록 예정 섹션 3.2

4. 아키텍처 설계 (방안 B 확정)

4.1 방안 선택 근거

방안 B (작업지시 시 동적 BOM 생성) 채택.

근거 설명
견적 금액과 무관 견적은 "부모 품목 × 총길이(m) × 단가"로 계산. 세부품목은 금액에 영향 없음
길이 버킷팅 이미 구현됨 BendingInfoBuilder에 heightLengthData(), bottomBarDistribution(), shutterBoxDistribution() 존재
수정 범위 최소 BendingInfoBuilder에 BD-XX-NN 조회 로직만 추가. 견적 로직 수정 불필요
bom_result 일관성 유지 견적 결과(bom_result)를 변경하지 않고, 그 위에 세부 매핑만 추가

참고: 견적과 작업지시는 동일한 BOM 산출 결과(order_nodes.options.bom_result)를 공유한다. 견적 계산과 자재투입은 같은 기준을 사용해야 일관성 유지.

4.2 bom_result 실제 데이터 구조 (DB 확인 완료)

견적 시 order_nodes.options.bom_result.items에 저장되는 절곡 관련 부모 품목:

BD-가이드레일-KSS01-SUS-120*70  qty=8.5m   ← 부모 품목 (전개치수 기준)
BD-케이스-500*380               qty=3.22m
BD-마구리-505*385               qty=1
00035 (하장바)                   qty=3
BD-L-BAR-KSS01-17*60           qty=3.22m
BD-보강평철-50                   qty=3.22m
EST-SMOKE-레일용                 qty=8.5
EST-SMOKE-케이스용               qty=3.22

이 부모 품목들은 길이별 세부품목(BD-RS-40 등)으로 분해되어야 자재투입이 가능.

4.3 동적 BOM 생성 흐름

[견적] (기존 그대로, 수정 불필요)
  QuoteCalculationService.calculateBom()
    → bom_result: { BD-가이드레일-KSS01-SUS-120*70, qty=8.5m, ... }
    → order_nodes.options.bom_result에 저장
      ↓
[수주 확정 → 작업지시 생성]
  BendingInfoBuilder.build()  ← 확장 대상
    ① bom_result에서 부모 품목 읽기 (기존)
    ② 치수별 길이 버킷팅 (기존: heightLengthData 등)
       예: 8.5m → 4300mm×1개 + 4000mm×1개
    ③ [신규] 길이코드 + LOT prefix → BD-XX-NN 품목 조회
       예: 4300mm → 코드43, 마감재 RS → BD-RS-43 (item_id 조회)
    ④ [신규] dynamic_bom 생성 → work_order_items.options에 저장
      ↓
[자재투입]
  getMaterials(workOrderId)  ← 소폭 수정
    → work_order_items 순회
    → [수정] options.dynamic_bom이 있으면 우선 사용
    → 없으면 기존 item.bom fallback
    → 각 세부품목(BD-RS-43 등)의 StockLot 조회
      ↓
[자재투입 등록]
  registerMaterialInput()  (기존 그대로)
    → stock_transactions 기록
    → stock_lots 차감

4.4 dynamic_bom JSON 구조 (work_order_items.options)

{
  "dynamic_bom": [
    {
      "child_item_id": 15812,
      "child_item_code": "BD-RS-43",
      "lot_prefix": "RS",
      "part_type": "마감재",
      "category": "guideRail",
      "material_type": "SUS",
      "length_mm": 4300,
      "qty": 1
    },
    {
      "child_item_id": 15809,
      "child_item_code": "BD-RS-40",
      "lot_prefix": "RS",
      "part_type": "마감재",
      "category": "guideRail",
      "material_type": "SUS",
      "length_mm": 4000,
      "qty": 1
    },
    {
      "child_item_id": 15826,
      "child_item_code": "BD-RM-43",
      "lot_prefix": "RM",
      "part_type": "본체",
      "category": "guideRail",
      "material_type": "EGI",
      "length_mm": 4300,
      "qty": 1
    }
  ]
}

4.5 getMaterials() 수정 범위

WorkOrderService.php:1198-1238에서 기존 item.bom 체크 앞에 dynamic_bom 체크 추가:

foreach (work_order_items as woItem):
  // [신규] dynamic_bom 우선 체크
  dynamicBom = woItem.options.dynamic_bom ?? null
  if (dynamicBom is not empty):
    foreach (dynamicBom as bomItem):
      childItem = Item::find(bomItem.child_item_id)
      materialItems[] = {item: childItem, bom_qty: bomItem.qty, ...}

  // [기존] items.bom fallback
  elseif (item.bom is not empty):
    ... 기존 로직 ...

  // [기존] 최종 fallback: 품목 자체를 자재로
  else:
    ...

5. LOT Prefix → BD 코드 대응 관계 (실제 DB 확인)

LOT Prefix 5130 세부품목 SAM 품목코드 패턴 등록 수 카테고리
RS 벽면형 SUS 마감재 BD-RS-[길이코드] 5 가이드레일
RM 벽면형 본체 BD-RM-[길이코드] 6 가이드레일
RC 벽면형 C형 BD-RC-[길이코드] 6 가이드레일
RD 벽면형 D형 BD-RD-[길이코드] 6 가이드레일
RT 벽면형 본체(철재) BD-RT-[길이코드] 2 가이드레일
SS 측면형 SUS 마감재 BD-SS-[길이코드] 4 가이드레일
SM 측면형 본체/D형 BD-SM-[길이코드] 5 가이드레일
SC 측면형 C형 BD-SC-[길이코드] 5 가이드레일
SD 측면형 D형 BD-SD-[길이코드] 5 가이드레일
ST 측면형 본체(철재) BD-ST-[길이코드] 1 가이드레일
SU 측면형 SUS2 (별도마감) BD-SU-[길이코드] 4 가이드레일
BE 하단마감재(스크린) EGI BD-BE-[길이코드] 2 하단마감재
BS 하단마감재(스크린) SUS BD-BS-[길이코드] 5 하단마감재
TS 하단마감재(철재) SUS BD-TS-[길이코드] 1 하단마감재
LA L-Bar 스크린용 BD-LA-[길이코드] 2 하단마감재
CF 케이스 전면부 BD-CF-[길이코드] 6 셔터박스
CL 케이스 린텔부 BD-CL-[길이코드] 6 셔터박스
CP 케이스 점검구 BD-CP-[길이코드] 6 셔터박스
CB 케이스 후면코너부 BD-CB-[길이코드] 6 셔터박스
GI 연기차단재 화이바원단 BD-GI-[길이코드] 7 연기차단재

6. 프론트엔드 매핑 검토 결과

6.1 작업일지 세부품명 → BD-* 매핑: 가능

각 세부품목에 lotPrefix 필드가 이미 정의되어 있다.

섹션 LOT Prefix (utils.ts 하드코딩) BD-* 매핑 예시
가이드레일(벽면) RS, RT, RC, RD, XX(하부BASE) BD-RS-40, BD-RT-43
가이드레일(측면) SS, ST, SC, SD, XX(하부BASE) BD-SS-40, BD-ST-43
하단바 BE, BS, LA BD-BE-40, BD-BS-35
셔터박스 CF, CL, CP, CB BD-CF-40, BD-CL-35
방연 GI BD-GI-53, BD-GI-83

매핑 공식: lotPrefix + getSLengthCode(길이mm)BD-{prefix}-{lengthCode} → items 테이블 code 컬럼 현재 한계: LOT NO 컬럼이 "-"으로 하드코딩 → dynamic_bom 연동 후 실제 LOT 번호 표시 가능 프론트 수정 범위: 소규모

6.2 자재투입 모달 세부품목 선택: 현재 불가 → 수정 필요

항목 현재 상태 방안 B 적용 후
자재 그룹핑 부모 품목 단위 세부품목(BD-RS-40 등) 단위
LOT 선택 부모 품목의 StockLot만 표시 세부품목의 StockLot 표시
FIFO 배분 품목 단위 세부품목 단위

핵심: 백엔드 getMaterials() 수정(섹션 4.5)이 완료되면 응답에 세부품목이 포함되므로, 프론트 모달은 기존 렌더링 로직 그대로 세부품목을 표시할 수 있다. 프론트 수정 범위: 중규모 — 그룹 헤더에 세부품목명 표시, 선택적 UX 개선

6.3 종합 연결 흐름

작업일지 세부품명 ──── lotPrefix + lengthCode ────→ BD-XX-NN (items 테이블)
        │                                                │
        ▼                                                ▼
   LOT NO 표시 ◄──── dynamic_bom ────────────────── getMaterials()
        │                                                │
        ▼                                                ▼
자재투입 모달 ◄──── 세부품목 단위 LOT 선택 ────── FIFO 배분

구현 순서: BendingInfoBuilder 확장(dynamic_bom 생성) → getMaterials() 수정 → 프론트 모달 수정 → 작업일지 LOT NO 표시


7. LOT 추적 데이터 누락 분석 (2026-02-22)

7.1 현재 LOT 추적 인프라

수주(orders) ──FK──→ 작업지시(work_orders) ──FK──→ 산출물 LOT(stock_lots)
     │                      │                          │
     │ source_order_item_id │ work_order_material_inputs│ work_order_id
     ▼                      ▼                          ▼
order_items ←── work_order_items ──→ 투입 LOT(stock_lots) ──→ stock_transactions
연결 FK/테이블 상태
수주 → 작업지시 work_orders.sales_order_id
수주품목 → 작업지시품목 work_order_items.source_order_item_id
생산완료 → 산출물 LOT stock_lots.work_order_id
구매입고 → 원자재 LOT stock_lots.receiving_id
자재투입 이력 work_order_material_inputs
거래 이력 stock_transactions

7.2 발견된 GAP

🔴 GAP 1: registerMaterialInput()에서 투입 이력 레코드 미생성

위치: WorkOrderService.php L1330-1390

registerMaterialInput() (L1330)     ← 작업지시 전체 단위
  → 재고 차감 ✅, 감사 로그 ✅, WorkOrderMaterialInput 레코드 ❌

registerMaterialInputForItem() (L2821) ← 개소(품목) 단위
  → 재고 차감 ✅, 감사 로그 ✅, WorkOrderMaterialInput 레코드 ✅

해결: registerMaterialInputForItem()으로 API 통일 우선순위: 🔴 즉시 (방안 B와 독립적으로 수정 가능)

🔴 GAP 2: dynamic_bom 미구현 → 절곡 세부품목 LOT 추적 불가

현재 items.bom만 체크 → 절곡 부모 품목의 bom이 null → 세부품목이 자재 목록에 미포함. 해결: 방안 B 구현 (섹션 4.5) 우선순위: 🔴 방안 B와 동시

🔴 GAP 5: bending_info ↔ dynamic_bom 정합성 보장 메커니즘 없음

별도 생성 시 작업일지 표시 ≠ 자재투입 대상 불일치 위험. 해결: BendingInfoBuilder에서 동시에 생성하여 같은 길이 버킷팅 결과 공유 우선순위: 🔴 방안 B와 동시 (설계 시 반영 필수)

🔴 GAP 4: 수주 연결 작업지시 산출물이 stock_lots 안 거침

위치: WorkOrderService.php L576-583 (updateStatus())

if ($workOrder->sales_order_id) {
    $this->createShipmentFromWorkOrder(...);  // 출하 직행, stock_lots 미거침
} else {
    $this->stockInFromProduction($workOrder);  // 재고 입고 → LOT 생성
}

원인: 출하 시스템이 아직 러프하게 구성된 상태 (의도된 설계 아님) 해결 (권장): "생산완료 → 항상 재고 입고(stock_lots)" 통일

항목 현재 권장 변경
선생산 완료 stockInFromProduction() → stock_lots 변경 없음
수주 연결 완료 createShipmentFromWorkOrder() → 출하 직행 stockInFromProduction() → stock_lots 생성 → 출하는 별도 프로세스

우선순위: 🔴 출하 시스템 설계 시 함께 해결

🟡 GAP 3: 투입 LOT → 산출 LOT 직접 연결 없음

간접 추적 가능 (산출 LOT → work_order_id → material_inputs → 투입 LOT). 직접 연결 테이블(lot_genealogy)은 향후 고도화.

🟢 GAP 6, 7

  • GAP 6: 불량 LOT 별도 관리 없음 → 품질 관리 고도화 시
  • GAP 7: 공정 간 반제품 LOT 연결 → 기존 registerMaterialInputForItem() 구조로 충분

7.3 우선순위별 조치 계획

우선순위 GAP 조치 시점
🔴 #1 registerMaterialInput 이력 미기록 registerMaterialInputForItem()으로 API 통일 즉시
🔴 #2 dynamic_bom 미구현 getMaterials()에 dynamic_bom 우선 체크 방안 B 동시
🔴 #5 bending_info ↔ dynamic_bom 정합성 BendingInfoBuilder에서 동시 생성 방안 B 동시
🔴 #4 수주 연결 산출물 LOT 미생성 생산완료 → 항상 stock_lots 입고 통일 출하 시스템 설계 시
🟡 #3 투입↔산출 LOT 직접 연결 lot_genealogy 테이블 고려 향후 고도화

7.4 방안 B 적용 후 목표 LOT 추적 체인

[수주] orders
  └─ order_nodes.options.bom_result (부모 품목 + 총길이)
       │
       ▼ source_order_item_id
[작업지시] work_orders + work_order_items
  ├─ options.bending_info (작업일지 표시) ─┐
  └─ options.dynamic_bom (세부품목 매핑)  ─┤ 같은 BendingInfoBuilder에서 동시 생성
       │                                   └─ 정합성 자동 보장
       ▼ getMaterials() → dynamic_bom 우선 체크
[자재투입] work_order_material_inputs
  ├─ work_order_item_id (부모 품목 개소)
  ├─ item_id = BD-RS-43 (세부품목)
  └─ stock_lot_id = LOT-XXXX (투입 LOT)
       │
       ▼ 재고 차감 (stock_transactions: OUT, work_order_input)
[생산완료] stock_lots (work_order_id = 작업지시 ID)
  ├─ 선생산: stock_lots 생성 ✅ (현재 동작)
  └─ 수주 연결: stock_lots 생성 ✅ (GAP 4 해결 후)
       │
       ▼ 역추적
산출물 LOT → work_order → material_inputs → 투입 LOT → receiving → 공급업체

8. 개발 영향 분석 및 위험 평가 (2026-02-22)

8.1 과제별 효과 및 위험

과제 1: registerMaterialInput() API 통일 (GAP #1)

효과: 자재투입 이력이 work_order_material_inputs에 빠짐없이 기록 → 역추적 체인 완성

위험:

  • 기존 registerMaterialInput()work_order_item_id 파라미터 미수신 → 프론트에서 해당 값 전달하도록 수정 필요
  • L2860-2861 StockLot::find()$lot->stock->item_id 역추적 시 Eager Loading 없으면 N+1 쿼리

과제 2: BendingInfoBuilder 확장 — dynamic_bom 생성 (GAP #2, #5)

효과: 견적 로직 수정 없이 세부품목별 LOT 추적 가능. bending_info와 동시 생성으로 정합성 보장.

위험:

위험 상세 대응
items 미매칭 bucketToStandardLength()가 표준 길이 초과 시 원본 반환(L862-864) → BD-RS-4500 같은 비표준 코드 생성 아이템 미발견 시 fallback + 경고 로그
prefix 결정 복잡성 KSS01→RS, KSE01→RE. SUS마감 여부로 YY 포함. 벽면/측면 prefix 세트 상이 PrefixResolver 클래스 분리 (하드코딩 지양)
혼합형 가이드레일 buildGuideRail()에서 wall+side 동시 생성 시 prefix 분기 복잡 벽면/측면 각각 독립 dynamic_bom 생성
생성 이후 수정 치수/품목 변경 시 bending_info + dynamic_bom 동시 재생성 필요 업데이트 메커니즘 설계
JSON 검증 부재 dynamic_bom은 JSON → DB 레벨 제약 없음 Application 레벨 DTO/Validator

과제 3: getMaterials() 수정 — dynamic_bom 우선 체크

효과: 프론트 MaterialInputModal이 세부품목 단위로 LOT 선택 가능

위험:

  • N+1 쿼리 누적: 현재 getMaterials() 자체가 N+1 다수. dynamic_bom 추가 시 세부품목 15-25개만큼 쿼리 추가(총 30-50회). Item::whereIn() 배치 조회로 개선 필수
  • uniqueMaterials 합산 시 정보 소실: L1240-1248에서 같은 item_id면 required_qty 합산 → 어느 work_order_item에 속하는지 소실. registerMaterialInputForItem() 호출 시 work_order_item_id 지정 어려움 → 합산 단위를 (item_id, work_order_item_id) 쌍으로 변경 권장

과제 4: 수주 연결 산출물 LOT 생성 (GAP #4)

효과: 모든 생산 완료 건에 stock_lots 기록 → 완전한 LOT 추적 체인

위험:

  • 출하 시스템 의존성: createShipmentFromWorkOrder() 단순 제거 시 현재 출하 흐름 깨짐 → 출하 재설계와 병행 필수
  • 재고 이중 계상: stock_lots 입고~출하 시간 차 동안 재고로 잡힘 → 다른 주문에 배정될 위험

8.2 Race Condition 분석

시나리오 리스크 대응
자재투입 동시 요청 두 작업자가 같은 LOT 동시 차감 → 초과 차감 lockForUpdate() 비관적 잠금
getMaterials→투입 시간 차 조회 후 다른 작업지시에서 같은 LOT 소진 투입 시 available_qty 재검증 (decreaseFromLot에서 수행), 부족 시 명확한 오류

8.3 마이그레이션/롤백 평가

항목 평가
DB 스키마 변경 없음 — 기존 options JSON 컬럼 활용
코드 롤백 Git 롤백으로 복원 가능
데이터 롤백 dynamic_bom이 있는 건도 코드 롤백 시 기존 fallback 동작 → 하위 호환성 확보
items 마스터 롤백 dynamic_bom의 child_item_id가 참조 가능 → 주의

8.4 개선 권장사항

영역 제안 시점
쿼리 최적화 getMaterials() 내 whereIn() 배치 조회 + Eager Loading 방안 B 구현 시
Prefix 매핑 BendingInfoBuilder 하드코딩 대신 PrefixResolver 클래스 분리 방안 B 구현 시
검증 레이어 dynamic_bom JSON DTO/Validator 클래스 방안 B 구현 시
마스터 데이터 검증 prefix × lengthCode 전체 조합 items 존재 확인 스크립트 방안 B 구현 전
아이템 미발견 처리 로그 경고 + 관리자 알림 + graceful fallback 방안 B 구현 시
dynamic_bom 메타정보 생성 시각/빌더 버전을 options에 포함 → 디버깅 용이 방안 B 구현 시
테스트 productCode × guideType 전 조합 단위 테스트 + getMaterials→투입 통합 테스트 방안 B 구현 후

8.5 종합 평가

방안 B는 기술적으로 타당. 견적 로직 미변경, 기존 JSON options 패턴 활용, 하위 호환성 유지.

핵심 리스크 2가지:

  1. items 마스터 데이터 완전성 — 19종 prefix × 7-12개 길이코드 조합이 items에 정확히 존재해야 함
  2. LOT prefix 결정 로직의 복잡성 — 제품코드/마감재질/가이드타입에 따른 분기 다수 → 하드코딩 시 유지보수 어려움

마스터 데이터 검증 스크립트PrefixResolver 분리를 개발 초기에 확보할 것


9. 참고 문서

문서 경로
선생산 재고 계획 docs/plans/bending-preproduction-stock-plan.md
BendingInfoBuilder api/app/Services/Production/BendingInfoBuilder.php
QuoteCalculationService api/app/Services/Quote/QuoteCalculationService.php
FormulaEvaluatorService api/app/Services/Quote/FormulaEvaluatorService.php
EstimatePriceService api/app/Services/Quote/EstimatePriceService.php
WorkOrderService api/app/Services/WorkOrderService.php
StockService api/app/Services/StockService.php
WorkOrderMaterialInput 모델 api/app/Models/Production/WorkOrderMaterialInput.php
자재투입 마이그레이션 api/database/migrations/2026_02_12_100000_create_work_order_material_inputs_table.php
stock_lots work_order_id FK api/database/migrations/2026_02_21_100000_add_work_order_id_to_stock_lots_table.php
MaterialInputModal react/src/components/production/WorkerScreen/MaterialInputModal.tsx
5130 작업일지 5130/output/viewBendingWork_UA.php
Bending types/utils react/src/components/production/WorkOrders/documents/bending/

10. 변경 이력

날짜 변경 내용
2026-02-21 문서 초안 작성 (현황 분석, 5130 체계 정리)
2026-02-21 로컬 DB BD-* 148개 확인, 제품코드 7종 추가, 추가 prefix(RT/ST/SU/TS) 발견
2026-02-21 방안 B 확정: 작업지시 시 BendingInfoBuilder 확장으로 동적 BOM 생성
2026-02-21 프론트엔드 매핑 검토 추가 (lotPrefix→BD-* 매핑 가능, 자재투입 모달 수정 필요)
2026-02-22 LOT 추적 데이터 누락 분석: 7개 GAP 발견, 우선순위별 조치 계획 수립
2026-02-22 문서 정리: 중복/해소 항목 제거, dynamic_bom에 category/material_type 추가
2026-02-22 섹션 8 추가: 개발 영향 분석 및 위험 평가 (과제별 효과/위험, race condition, 롤백, 개선 권장)