# MES 구조 및 PC-POP 연동 가이드

> 작성일: 2026-03-20
> 대상: PC 화면 개발자 / POP 연동 담당자
> 목적: PC에서 작업지시를 등록할 때, POP(생산실적관리)에 공정이 자동 연동되는 전체 구조를 설명

---

## 1. 전체 구조 개요

### 1.1 시스템 구성

```
[PC 영역 - 브라우저]                    [POP 영역 - 태블릿]
  작업지시 등록 화면                       생산실적관리 화면 (4480)
  (screen 4155, 4493)                     카드 리스트 + 상세 모달
       |                                         |
       |  (1) work_instruction INSERT             |  (3) 카드 리스트 조회
       |  (2) create-work-processes 호출           |  (4) 접수/실적/확정
       v                                         v
=================================================================
              [백엔드 - Express + PostgreSQL]
              /api/data/work_instruction (범용 CRUD)
              /api/pop/production/* (MES 전용 API 10개)
=================================================================
```

### 1.2 데이터 흐름 요약

```
PC 등록                         서버 자동 처리                    POP 표시
---------                       ----------------                  ----------
1. 품목 선택                    3. 라우팅 공정 조회               5. 카드 목록
2. 작업지시 INSERT     --->     4. work_order_process             6. 접수
   + create-work-processes         N건 일괄 INSERT     --->       7. 실적 입력
     API 호출                      + process_work_result          8. 완료 확정
                                     체크리스트 복사
```

---

## 2. DB 테이블 상세 구조

### 2.1 테이블 관계도

```
[마스터 데이터 - PC에서 사전 등록]

  item_info          process_mng          defect_standard_mng
  (품목 마스터)       (공정 마스터)         (불량 유형 마스터)
      |                   |
      v                   v
  item_routing_version -----> item_routing_detail
  (품목별 라우팅 버전)         (공정 순서 정의)
                                   |
                                   v
                           process_work_item -----> process_work_item_detail
                           (공정별 작업항목)          (체크리스트 상세)


[트랜잭션 데이터 - PC 등록 + POP 실행]

  work_instruction ─── 1:N ──> work_order_process ─── 1:N ──> process_work_result
  (작업지시 마스터)              (공정별 작업 단위)               (체크리스트 결과)
                                      |
                                 parent_process_id (자기참조)
                                      |
                                work_order_process (분할 행)
                                (접수/재작업 분할 카드)
                                      |
                                work_order_process_log
                                (변경 이력 - 트리거 자동)
```

### 2.2 work_instruction (작업지시 마스터) - 19컬럼

> PC에서 등록하는 핵심 테이블. POP에서는 읽기 전용으로 참조.

| 컬럼 | 타입 | 기본값 | 역할 | 비고 |
|------|------|--------|------|------|
| `id` | varchar | gen_random_uuid() | PK | UUID 자동 생성 |
| `work_instruction_no` | varchar | - | 작업지시번호 | 사용자 채번 (예: WI-20260320-001) |
| `item_id` | varchar | - | 품목 FK | -> item_info.id |
| `status` | varchar | - | 작업지시 상태 | waiting / in_progress / completed / cancelled |
| `progress_status` | varchar | - | 진행상태 | POP에서 완료 시 'completed'로 자동 갱신 |
| `qty` | varchar | - | 지시수량 | 핵심. POP 접수 상한의 기준 |
| `completed_qty` | varchar | '0' | 완성수량 | 마지막 공정 양품 합계로 자동 갱신 |
| `routing` | varchar | - | 라우팅 참조 | 현재 미사용 (비어있음) |
| `worker` | varchar | - | 작업자 | |
| `work_team` | varchar | - | 작업팀 | |
| `equipment_id` | varchar | - | 설비 FK | |
| `start_date` | varchar | - | 시작일 | |
| `end_date` | varchar | - | 종료일(납기) | |
| `reason` | varchar | - | 사유 | |
| `remark` | text | - | 비고 | |
| `company_code` | varchar | - | 멀티테넌시 | 필수 |
| `created_date` | timestamp | now() | 생성일 | |
| `updated_date` | timestamp | now() | 수정일 | |
| `writer` | varchar | - | 작성자 | |

### 2.3 work_order_process (공정별 작업 단위) - 37컬럼

> create-work-processes API 호출 시 자동 생성. POP에서 접수/실적/완료를 처리하는 핵심 테이블.

| 컬럼 그룹 | 컬럼 | 타입 | 기본값 | 역할 |
|-----------|------|------|--------|------|
| **연결** | `wo_id` | varchar | - | -> work_instruction.id (작업지시 FK) |
| | `seq_no` | varchar | - | 공정 순서 (1, 2, 3...) |
| | `routing_detail_id` | varchar | - | -> item_routing_detail.id (라우팅 스냅샷) |
| | `parent_process_id` | varchar | NULL | 분할 시 마스터 행 참조 (NULL = 마스터) |
| **공정정보** | `process_code` | varchar | - | 공정코드 (예: P002) |
| | `process_name` | varchar | - | 공정명 (예: 가공) |
| | `is_required` | varchar | - | 필수 여부 |
| | `is_fixed_order` | varchar | - | 순서 고정 여부 |
| | `standard_time` | varchar | - | 표준시간 |
| | `equipment_code` | varchar | - | 사용 설비 |
| **수량** | `plan_qty` | varchar | - | 계획수량 |
| | `input_qty` | varchar | - | 접수량 (접수 시 설정) |
| | `good_qty` | varchar | - | 양품수량 (누적) |
| | `defect_qty` | varchar | - | 불량수량 (누적) |
| | `total_production_qty` | varchar | - | 총 생산수량 (누적) |
| | `concession_qty` | varchar | '0' | 특채수량 (양품에 합산 + 별도 추적) |
| **상태** | `status` | varchar | - | waiting / acceptable / in_progress / completed |
| | `result_status` | varchar | 'draft' | draft / confirmed |
| **타이머** | `started_at` | varchar | - | 작업 시작 시각 |
| | `paused_at` | varchar | - | 일시정지 시각 |
| | `total_paused_time` | varchar | 0 | 누적 일시정지 시간(초) |
| | `completed_at` | varchar | - | 완료 시각 |
| | `actual_work_time` | varchar | NULL | 실 작업시간(초) |
| **작업자** | `accepted_by` | varchar | - | 접수자 |
| | `accepted_at` | varchar | - | 접수 시각 |
| | `completed_by` | varchar | NULL | 완료 처리자 |
| **실적** | `defect_detail` | varchar | - | 불량 상세 JSON (코드/수량/처분) |
| | `result_note` | varchar | - | 실적 메모 |
| | `attachments` | varchar | - | 첨부파일 |
| **재작업** | `is_rework` | varchar | 'N' | 재작업 카드 여부 (Y/N) |
| | `rework_source_id` | varchar | NULL | 재작업 원본 행 참조 |
| **표준** | `company_code`, `created_date`, `updated_date`, `writer`, `remark` | - | - | 표준 컬럼 |

### 2.4 process_work_result (체크리스트/검사 결과) - 35컬럼

> 공정별 체크리스트. create-work-processes 시 마스터 템플릿에서 복사.

| 컬럼 그룹 | 컬럼 | 역할 |
|-----------|------|------|
| **연결** | `work_order_process_id` | -> work_order_process.id |
| | `source_work_item_id` | -> process_work_item.id (템플릿 원본) |
| | `source_detail_id` | -> process_work_item_detail.id (템플릿 상세) |
| **작업항목** | `work_phase` | 작업 단계 (PRE/IN/POST) |
| | `item_title` | 작업항목 제목 |
| | `item_sort_order` | 항목 정렬 순서 |
| **검사 상세** | `detail_content` | 검사 내용 |
| | `detail_type` | 상세 유형 |
| | `detail_sort_order` | 상세 정렬 순서 |
| | `is_required` | 필수 여부 |
| **검사 기준** | `inspection_code` | 검사 코드 |
| | `inspection_method` | 검사 방법 |
| | `unit` | 단위 |
| | `lower_limit` / `upper_limit` | 하한/상한 |
| **입력** | `input_type` | 입력 유형 |
| | `lookup_target` | 조회 대상 |
| | `display_fields` | 표시 필드 |
| | `duration_minutes` | 소요시간(분) |
| **결과** | `result_value` | 입력 결과값 |
| | `is_passed` | 합격 여부 |
| | `status` | pending / completed |
| | `recorded_by` | 기록자 |
| | `recorded_at` | 기록 시각 |
| **그룹 타이머** | `group_started_at`, `group_paused_at` | 그룹 시작/정지 |
| | `group_total_paused_time`, `group_completed_at` | 누적 정지/완료 |

### 2.5 마스터 데이터 테이블

#### item_info (품목 마스터)

| 주요 컬럼 | 역할 |
|-----------|------|
| `id` | PK (UUID) |
| `item_number` | 품목코드 (라우팅 연결 키) |
| `item_name` | 품목명 |
| `type` | 품목 유형 |
| `division` | 구분 |

#### process_mng (공정 마스터)

| 주요 컬럼 | 역할 |
|-----------|------|
| `process_code` | 공정코드 (PK 역할) |
| `process_name` | 공정명 (예: 가공, 검사, 포장) |
| `process_type` | 공정 유형 |
| `use_yn` | 사용 여부 |

#### item_routing_version (품목별 라우팅 버전)

| 주요 컬럼 | 역할 |
|-----------|------|
| `id` | PK (UUID) |
| `item_code` | -> item_info.item_number |
| `version_name` | 버전명 (예: 기본 라우팅, v1) |
| `is_default` | 기본 버전 여부 (boolean) |

#### item_routing_detail (공정 순서 정의)

| 주요 컬럼 | 역할 |
|-----------|------|
| `id` | PK (UUID) |
| `routing_version_id` | -> item_routing_version.id |
| `seq_no` | 공정 순서 (1, 2, 3...) |
| `process_code` | -> process_mng.process_code |
| `is_required` | 필수 여부 |
| `is_fixed_order` | 순서 고정 여부 |
| `standard_time` | 표준시간 |

---

## 3. PC에서 작업지시 등록 -> POP 연동 흐름

### 3.1 전체 시퀀스 다이어그램

```
[PC 사용자]         [프론트엔드]           [백엔드]                 [DB]
    |                   |                    |                       |
    | 1. 품목 선택       |                    |                       |
    |------------------>|                    |                       |
    |                   | 2. 라우팅 버전 조회  |                       |
    |                   |--- GET /api/data/  |                       |
    |                   |    item_routing_    |--- SELECT             |
    |                   |    version          |    item_routing_      |
    |                   |    ?item_code=XXX ->|    version         -->|
    |                   |<-------------------|<----------------------|
    |                   |                    |                       |
    | 3. 정보 입력       |                    |                       |
    |  (수량/납기/etc)   |                    |                       |
    |------------------>|                    |                       |
    |                   |                    |                       |
    | 4. "등록" 클릭     |                    |                       |
    |------------------>|                    |                       |
    |                   | 5. 작업지시 INSERT   |                       |
    |                   |--- POST /api/data/ |                       |
    |                   |    work_instruction |--- INSERT             |
    |                   |                --->|    work_instruction -->|
    |                   |<-------------------|<-- RETURNING id ------|
    |                   |                    |                       |
    |                   | 6. 공정 일괄 생성   |                       |
    |                   |--- POST /api/pop/  |                       |
    |                   |    production/      |                       |
    |                   |    create-work-     |                       |
    |                   |    processes     -->|                       |
    |                   |                    |-- SELECT item_routing  |
    |                   |                    |   _detail + process_mng|
    |                   |                    |<----------------------|
    |                   |                    |                       |
    |                   |                    |-- FOR EACH 공정:      |
    |                   |                    |   INSERT work_order_   |
    |                   |                    |   process           -->|
    |                   |                    |   INSERT process_work_ |
    |                   |                    |   result (체크리스트)->|
    |                   |                    |                       |
    |                   |<-- 성공 응답 ------|                       |
    |<-- 등록 완료 -----|                    |                       |
    |                   |                    |                       |
    |                   |  [이 시점부터 POP에서 조회 가능]              |
```

### 3.2 Step 1: 작업지시 INSERT (필수)

**API**: `POST /api/data/work_instruction`

**필수 데이터**:

```json
{
  "item_id": "품목 UUID (item_info.id)",
  "qty": "지시수량 (예: 500)",
  "status": "waiting",
  "work_instruction_no": "작업지시번호 (예: WI-20260320-001)"
}
```

**선택 데이터**:

```json
{
  "worker": "작업자",
  "work_team": "작업팀",
  "equipment_id": "설비 UUID",
  "start_date": "시작일",
  "end_date": "종료일(납기)",
  "remark": "비고"
}
```

**응답 예시**:

```json
{
  "success": true,
  "data": {
    "id": "a1b2c3d4-...",
    "work_instruction_no": "WI-20260320-001",
    "status": "waiting"
  }
}
```

> 이 시점에서는 work_instruction 행만 생성되고, POP에서 공정 카드가 표시되지 않는다.

### 3.3 Step 2: 공정 일괄 생성 (핵심 - 반드시 호출해야 POP 연동됨)

**API**: `POST /api/pop/production/create-work-processes`

**인증**: JWT 토큰 필수 (Authorization 헤더)

**필수 파라미터**:

| 파라미터 | 타입 | 설명 |
|---------|------|------|
| `work_instruction_id` | string | Step 1에서 받은 작업지시 ID |
| `routing_version_id` | string | 선택한 라우팅 버전 UUID |

**선택 파라미터**:

| 파라미터 | 타입 | 설명 |
|---------|------|------|
| `item_code` | string | 품목코드 (참고용) |
| `plan_qty` | string | 계획수량 (work_order_process.plan_qty에 저장) |

**요청 예시**:

```json
{
  "work_instruction_id": "a1b2c3d4-...작업지시ID",
  "routing_version_id": "e5f6g7h8-...라우팅버전ID",
  "plan_qty": "500"
}
```

**서버 내부 동작**:

1. **중복 방지**: 해당 work_instruction_id로 이미 공정이 있으면 409 에러
2. **라우팅 조회**: `item_routing_detail` + `process_mng` JOIN으로 공정 목록 취득
3. **공정별 INSERT**: seq_no 순서대로 work_order_process 행 생성
   - 1공정: `status = 'acceptable'` (POP에서 즉시 접수 가능)
   - 2~N공정: `status = 'waiting'` (앞공정 완료 대기)
4. **체크리스트 복사**: 각 공정의 `routing_detail_id`에 연결된 `process_work_item` + `process_work_item_detail`을 `process_work_result`로 복사

**응답 예시**:

```json
{
  "success": true,
  "data": {
    "processes": [
      { "id": "uuid-1", "seq_no": "1", "process_name": "가공", "checklist_count": 3 },
      { "id": "uuid-2", "seq_no": "2", "process_name": "검사", "checklist_count": 5 },
      { "id": "uuid-3", "seq_no": "3", "process_name": "포장", "checklist_count": 2 }
    ],
    "total_processes": 3,
    "total_checklists": 10
  }
}
```

**에러 케이스**:

| HTTP | 상황 | 메시지 |
|------|------|--------|
| 400 | 필수 파라미터 누락 | "work_instruction_id와 routing_version_id는 필수입니다." |
| 409 | 이미 공정이 존재 | "이미 공정이 생성된 작업지시입니다." |
| 404 | 라우팅에 공정 없음 | "라우팅 버전에 등록된 공정이 없습니다." |

### 3.4 Step 1 + Step 2를 하나의 트랜잭션으로 묶는 방법

> PC 화면에서 "등록" 버튼 1회 클릭으로 두 API를 순차 호출해야 한다.

```javascript
// PC 프론트엔드 예시 코드
async function registerWorkInstruction(formData) {
  // Step 1: 작업지시 INSERT
  const wiResponse = await apiClient.post("/api/data/work_instruction", {
    item_id: formData.itemId,
    qty: formData.qty,
    status: "waiting",
    work_instruction_no: formData.wiNo,
    worker: formData.worker,
    start_date: formData.startDate,
    end_date: formData.endDate,
  });

  if (!wiResponse.data.success) {
    throw new Error("작업지시 등록 실패: " + wiResponse.data.message);
  }

  const workInstructionId = wiResponse.data.data.id;

  // Step 2: 공정 일괄 생성 (이것이 POP 연동의 핵심)
  const processResponse = await apiClient.post(
    "/api/pop/production/create-work-processes",
    {
      work_instruction_id: workInstructionId,
      routing_version_id: formData.routingVersionId,
      plan_qty: formData.qty,
    }
  );

  if (!processResponse.data.success) {
    // 실패 시 작업지시도 삭제 또는 상태 변경 필요
    throw new Error("공정 생성 실패: " + processResponse.data.message);
  }

  return {
    workInstruction: wiResponse.data.data,
    processes: processResponse.data.data.processes,
  };
}
```

### 3.5 PC 화면에서 필요한 사전 데이터 조회

#### 3.5.1 품목 목록 조회

```
GET /api/data/item_info
```

#### 3.5.2 선택한 품목의 라우팅 버전 목록 조회

```
GET /api/data/item_routing_version?item_code={선택한 품목의 item_number}
```

> `is_default = true`인 버전을 자동 선택하면 UX가 좋다.

#### 3.5.3 라우팅 버전의 공정 목록 미리보기 (선택사항)

```
GET /api/data/item_routing_detail?routing_version_id={선택한 버전 ID}
```

> 등록 전에 사용자에게 "이 라우팅에 포함된 공정 목록"을 보여줄 수 있다.

---

## 4. POP 워크플로우 상세

### 4.1 공정 상태 전이 다이어그램

```
[PC 등록]
    |
    v
 waiting ---------> acceptable ---------> in_progress ---------> completed
 (대기중)            (접수가능)             (진행중)                (완료)
                       |                     |   |
                       |  (접수 취소)          |   | (실적 저장 -> 자동완료)
                       |<--------------------+   |
                       |                         | (수동 확정)
                       |                         +----> completed + confirmed
                       |
                       | (앞공정에서 양품 발생 시 자동 전환)
                       |
                    waiting ---> acceptable
```

**상태별 의미**:

| 상태 | 의미 | POP 탭 | 전환 조건 |
|------|------|--------|----------|
| `waiting` | 앞공정 미완료, 접수 불가 | 대기 탭 | 앞공정 양품 발생 시 -> acceptable |
| `acceptable` | 접수 가능, 작업자 대기 | 접수가능 탭 | 작업자가 접수 시 -> in_progress (분할 행) |
| `in_progress` | 작업 진행 중 | 진행 탭 | 실적 전량 생산 시 -> completed (자동) |
| `completed` | 작업 완료 | 완료 탭 | 수동 확정 또는 자동 완료 |

### 4.2 수량 계산 공식

```
접수가능량 = 앞공정.양품합계 - 내공정.접수합계
           = (앞공정 SUM(good_qty + concession_qty)) - (내공정 분할행 SUM(input_qty))
           (1공정은 앞공정.양품합계 = work_instruction.qty)

양품 = 생산수량 - 불량수량
     = production_qty - defect_qty
     (서버에서 계산, 클라이언트 값은 참고만)

특채(concession) = 불량 중 disposition='accept'인 항목 합계
                   양품에는 포함되지 않으나, 다음 공정 전달량에 합산

자동완료 조건:
  - 분할 행: total_production_qty >= input_qty
  - 마스터 행: 모든 분할 행 completed + 잔여 접수가능 <= 0
  - 작업지시: 마지막 seq_no의 모든 행 completed
```

### 4.3 접수(Accept) 상세

```
[접수가능 카드 클릭] -> [수량 입력 모달] -> [접수 확인]
                                              |
                                              v
                               POST /api/pop/production/accept-process
                               body: { work_order_process_id: 마스터행ID,
                                        accept_qty: 접수수량 }
                                              |
                                              v
                               [분할 행 INSERT]
                               - parent_process_id = 마스터행ID
                               - input_qty = 접수수량
                               - status = 'in_progress'
                               - accepted_by = 로그인 사용자
                               - 체크리스트 복사
                                              |
                                              v
                               [POP 카드 갱신]
                               - 새 분할 카드가 "진행중" 탭에 표시
                               - 잔여 접수가능량이 남으면 마스터 카드도 "접수가능"에 유지
```

### 4.4 실적 저장(Save Result) 상세

```
[진행중 카드 클릭] -> [상세 모달 열림] -> [실적 입력]
                                          |
                    생산수량 + 불량상세(코드/수량/처분) 입력
                                          |
                                          v
                          POST /api/pop/production/save-result
                          body: { work_order_process_id: 분할행ID,
                                  production_qty: 생산수량,
                                  defect_detail: [
                                    { defect_code, defect_name, qty, disposition }
                                  ] }
                                          |
                                          v
                          [서버 처리]
                          1. 양품/불량/특채 서버 계산
                          2. 기존 수량에 누적 (total_production_qty += production_qty)
                          3. 불량 상세 JSON 병합
                          4. disposition='rework' -> 재작업 카드 자동 생성
                          5. 양품 발생 -> 다음 공정 마스터 acceptable로 전환
                          6. 접수분 전량 생산 -> 분할 행 자동 completed
                          7. 모든 분할 행 완료 + 잔여 0 -> 마스터 자동 completed
                          8. 마지막 공정 전부 완료 -> work_instruction 완료
```

**불량 처분(disposition) 3종**:

| 처분 | 의미 | 수량 영향 |
|------|------|----------|
| `scrap` (폐기) | 폐기 처리 | 불량 집계에 포함, 양품에서 차감 |
| `rework` (재작업) | 같은 공정에서 재작업 | 재작업 카드 자동 생성, 양품에서 차감 |
| `accept` (특채) | 조건부 합격 | concession_qty에 기록, 다음 공정 전달량에 합산 |

### 4.5 확정(Confirm Result) 상세

```
수동 확정 = 접수분 전량 미생산이지만 강제로 완료 처리
           (예: 생산 중단, 일부만 완료 등)

POST /api/pop/production/confirm-result
body: { work_order_process_id: 분할행ID }

결과:
- status = 'completed'
- result_status = 'confirmed'
- 양품 있으면 다음 공정 활성화
- 마스터/작업지시 캐스케이드 완료 판정
```

---

## 5. MES 전용 API 엔드포인트 정리

**베이스 URL**: `/api/pop/production`
**인증**: 모든 엔드포인트에 JWT 토큰 필수

| 순서 | 메서드 | URL | 용도 | 호출 주체 |
|------|--------|-----|------|----------|
| 1 | POST | `/create-work-processes` | 작업지시에 공정 일괄 생성 | PC |
| 2 | POST | `/accept-process` | 공정 접수 (분할 행 생성) | POP |
| 3 | POST | `/cancel-accept` | 접수 취소 | POP |
| 4 | POST | `/save-result` | 실적 저장 (누적) | POP |
| 5 | POST | `/confirm-result` | 실적 확정 (수동 완료) | POP |
| 6 | GET | `/available-qty` | 접수가능량 조회 | POP |
| 7 | GET | `/result-history` | 차수별 실적 이력 | POP |
| 8 | GET | `/defect-types` | 불량 유형 목록 | POP |
| 9 | POST | `/timer` | 공정 타이머 (시작/정지/완료) | POP |
| 10 | POST | `/group-timer` | 그룹 타이머 (체크리스트별) | POP |

---

## 6. PC 화면 현황 (COMPANY_7 탑씰)

### 6.1 등록된 화면 목록

| 화면 ID | 이름 | 메인 테이블 | 용도 |
|---------|------|------------|------|
| 4155 | 작업지시 목록 | work_instruction | 목록 조회 |
| 4493 | 작업지시 등록화면 | work_instruction | 신규 등록 |
| 4156 | 수주 선택 | sales_order_detail | 모달 (수주 참조) |
| 4157 | 적용 확인 | work_instruction | 모달 (등록 확인) |

### 6.2 메뉴 구조

```
생산관리 (COMPANY_7)
  ├── 생산옵션설정 (/screens/1606)
  ├── 생산계획 (/screens/3985)
  ├── 작업지시 (/production/work-instruction)  <-- React 페이지 미구현
  ├── 공정정보관리 (/production/process-info)
  ├── 생산실적 (하위 없음)
  └── 생산리포트 (/admin/report/production)
```

### 6.3 현재 미구현 사항 (PC 개발 필요)

1. **라우팅 버전 선택 UI**: 품목 선택 시 해당 품목의 라우팅 버전 목록을 표시하고 선택하는 기능
2. **create-work-processes 호출 연동**: 작업지시 등록 시 자동으로 공정 생성 API를 호출하는 로직
3. **work_instruction.routing 컬럼 활용**: 현재 비어있음. routing_version_id를 저장하면 추적 가능
4. **작업지시 상태 관리**: 등록/수정/취소 워크플로우

---

## 7. 마스터 데이터 사전 등록 요건

> 작업지시 등록 전에 다음 마스터 데이터가 반드시 등록되어 있어야 한다.

### 7.1 필수 사전 등록 순서

```
1. process_mng (공정 마스터) 등록
   예: P002=가공, P003=검사, P009=포장

2. item_info (품목 마스터) 등록
   예: item_number=R_FREE3_002, item_name=원제_AK1000

3. item_routing_version (라우팅 버전) 등록
   item_code = item_info.item_number
   is_default = true (기본 버전)

4. item_routing_detail (공정 순서) 등록
   routing_version_id = 위에서 만든 버전 ID
   seq_no = 1, process_code = P002  (1공정: 가공)
   seq_no = 2, process_code = P003  (2공정: 검사)
   seq_no = 3, process_code = P009  (3공정: 포장)

5. (선택) process_work_item + detail (체크리스트 템플릿)
   routing_detail_id = 위의 item_routing_detail.id
```

### 7.2 현재 COMPANY_7 데이터 현황

**등록된 공정 (15개)**:

| 공정코드 | 공정명 | 유형 |
|---------|--------|------|
| P002 | 가공 | PT001 |
| P003 | 검사 | PT003 |
| P005 | 치수검사 | PT004 |
| P006 | 테스트 | PT001 |
| P007 | 인쇄 | PT006 |
| P008 | 조립 | PT002 |
| P009 | 포장 | PT004 |
| PRC-001 ~ PRC-006 | 검수/가공/조립/검사/포장 | 다양 |
| PROC-001 | 확인 | 세척 |

**등록된 라우팅 버전 (24건)**: 다양한 품목에 대해 1~3개 공정 조합

---

## 8. 데이터 예시: 전체 흐름 시뮬레이션

### 시나리오: 품목 R_FREE3_002를 500개 생산

#### Step 1: 작업지시 등록 (PC)

```sql
-- 자동 생성되는 행
INSERT INTO work_instruction (work_instruction_no, item_id, qty, status)
VALUES ('WI-20260320-001', 'a4e492a0-...', '500', 'waiting');
-- id = 'wi-new-001' (UUID 자동생성)
```

#### Step 2: 공정 생성 API 호출 (PC -> 서버)

```
POST /api/pop/production/create-work-processes
{ work_instruction_id: 'wi-new-001',
  routing_version_id: '5cff0c1e-...' }
```

```sql
-- 서버가 자동 생성하는 행 (item_routing_detail 기반)
INSERT INTO work_order_process (wo_id, seq_no, process_code, process_name, status, plan_qty)
VALUES
  ('wi-new-001', '2', 'P002', '가공', 'acceptable', '500'),  -- 1공정: 접수가능
  ('wi-new-001', '30', 'P006', '테스트', 'waiting', '500');    -- 2공정: 대기
```

#### Step 3: POP에서 작업자가 1공정 접수 (300개)

```sql
-- accept-process가 생성하는 분할 행
INSERT INTO work_order_process (wo_id, seq_no, process_code, status, input_qty,
                                 parent_process_id, accepted_by)
VALUES ('wi-new-001', '2', 'P002', 'in_progress', '300',
        '마스터행ID', '작업자A');
```

#### Step 4: POP에서 실적 저장 (생산 300개, 불량 10개)

```sql
-- save-result가 UPDATE하는 행
UPDATE work_order_process
SET total_production_qty = '300',
    good_qty = '290',        -- 서버 계산: 300 - 10
    defect_qty = '10',
    status = 'completed'     -- 자동완료: 300 >= 300(input_qty)
WHERE id = '분할행ID';

-- 다음 공정 자동 활성화
UPDATE work_order_process
SET status = 'acceptable'    -- waiting -> acceptable
WHERE wo_id = 'wi-new-001' AND seq_no = '30'
  AND parent_process_id IS NULL;
```

#### Step 5: 2공정도 접수 -> 실적 -> 완료하면

```sql
-- 작업지시 자동 완료
UPDATE work_instruction
SET status = 'completed',
    progress_status = 'completed',
    completed_qty = '280'    -- 마지막 공정 양품 합계
WHERE id = 'wi-new-001';
```

---

## 9. 주의사항 및 제약

### 9.1 필수 규칙

1. **create-work-processes는 1회만 호출 가능**: 같은 작업지시에 대해 2번 호출하면 409 에러
2. **routing_version_id는 필수**: 라우팅 없이는 공정을 생성할 수 없음
3. **1공정만 즉시 접수가능**: 나머지는 앞공정 양품 발생 후 자동 전환
4. **수량은 모두 VARCHAR**: 정수 변환 시 parseInt 필수
5. **멀티테넌시**: 모든 쿼리에 company_code 필터 필수
6. **분할 행 구조**: 접수 시 마스터 행에서 분할 행을 INSERT하는 방식. 마스터 행에는 직접 실적 등록 불가

### 9.2 현재 미활용 컬럼

| 컬럼 | 테이블 | 상태 |
|------|--------|------|
| `routing` | work_instruction | 비어있음 (routing_version_id 저장 권장) |
| `equipment_id` | work_instruction | 등록 가능하나 POP 연동 미구현 |
| `item_id` | work_instruction | 일부 테스트 데이터에서 비어있음 |

### 9.3 자동 완료 판정 주의

- 재작업 카드가 있으면 해당 카드가 완료될 때까지 마스터 행이 완료되지 않음
- 특채(concession_qty)는 양품에 포함되지 않으나 다음 공정 전달량에는 합산됨
- 초과 생산은 경고만 하고 차단하지 않음 (현장 유연성)
