Files
vexplor_dev/docs/POP_작업진행_설계서.md
kjs 3df9a39ebe feat: implement registered items management in process work standard
- Added new endpoints for managing registered items, including retrieval, registration, and batch registration.
- Enhanced the existing processWorkStandardController to support filtering and additional columns in item queries.
- Updated the processWorkStandardRoutes to include routes for registered items management.
- Introduced a new documentation file detailing the design and structure of the POP 작업진행 관리 system.

These changes aim to improve the management of registered items within the process work standard, enhancing usability and functionality.

Made-with: Cursor
2026-03-13 11:26:59 +09:00

23 KiB

POP 작업진행 관리 설계서

작성일: 2026-03-13 목적: POP 시스템에서 작업지시 기반으로 라우팅/작업기준정보를 조회하고, 공정별 작업 진행 상태를 관리하는 구조 설계


1. 핵심 설계 원칙

작업지시에 라우팅ID, 작업기준정보ID 등을 별도 컬럼으로 넣지 않는다.

  • 작업지시(work_instruction)에는 item_id(품목 ID)만 있으면 충분
  • 품목 → 라우팅 → 작업기준정보는 마스터 데이터 체인으로 조회
  • 작업 진행 상태만 별도 테이블에서 관리

2. 기존 테이블 구조 (마스터 데이터)

2-1. ER 다이어그램

GitHub / VSCode Mermaid 플러그인에서 렌더링됩니다.

erDiagram
    %% ========== 마스터 데이터 (변경 없음) ==========

    item_info {
        varchar id PK "UUID"
        varchar item_number "품번"
        varchar item_name "품명"
        varchar company_code "회사코드"
    }

    item_routing_version {
        varchar id PK "UUID"
        varchar item_code "품번 (= item_info.item_number)"
        varchar version_name "버전명"
        boolean is_default "기본버전 여부"
        varchar company_code "회사코드"
    }

    item_routing_detail {
        varchar id PK "UUID"
        varchar routing_version_id FK "→ item_routing_version.id"
        varchar seq_no "공정순서 10,20,30..."
        varchar process_code FK "→ process_mng.process_code"
        varchar is_required "필수/선택"
        varchar is_fixed_order "고정/선택"
        varchar standard_time "표준시간(분)"
        varchar company_code "회사코드"
    }

    process_mng {
        varchar id PK "UUID"
        varchar process_code "공정코드"
        varchar process_name "공정명"
        varchar process_type "공정유형"
        varchar company_code "회사코드"
    }

    process_work_item {
        varchar id PK "UUID"
        varchar routing_detail_id FK "→ item_routing_detail.id"
        varchar work_phase "PRE / IN / POST"
        varchar title "작업항목명"
        varchar is_required "Y/N"
        int sort_order "정렬순서"
        varchar company_code "회사코드"
    }

    process_work_item_detail {
        varchar id PK "UUID"
        varchar work_item_id FK "→ process_work_item.id"
        varchar detail_type "check/inspect/input/procedure/info"
        varchar content "내용"
        varchar input_type "입력타입"
        varchar inspection_code "검사코드"
        varchar unit "단위"
        varchar lower_limit "하한값"
        varchar upper_limit "상한값"
        varchar company_code "회사코드"
    }

    %% ========== 트랜잭션 데이터 ==========

    work_instruction {
        varchar id PK "UUID"
        varchar work_instruction_no "작업지시번호"
        varchar item_id FK "→ item_info.id ★핵심★"
        varchar status "waiting/in_progress/completed/cancelled"
        varchar qty "지시수량"
        varchar completed_qty "완성수량"
        varchar worker "작업자"
        varchar company_code "회사코드"
    }

    work_order_process {
        varchar id PK "UUID"
        varchar wo_id FK "→ work_instruction.id"
        varchar routing_detail_id FK "→ item_routing_detail.id ★추가★"
        varchar seq_no "공정순서"
        varchar process_code "공정코드"
        varchar process_name "공정명"
        varchar status "waiting/in_progress/completed/skipped"
        varchar plan_qty "계획수량"
        varchar good_qty "양품수량"
        varchar defect_qty "불량수량"
        timestamp started_at "시작시간"
        timestamp completed_at "완료시간"
        varchar company_code "회사코드"
    }

    work_order_work_item {
        varchar id PK "UUID ★신규★"
        varchar company_code "회사코드"
        varchar work_order_process_id FK "→ work_order_process.id"
        varchar work_item_id FK "→ process_work_item.id"
        varchar work_phase "PRE/IN/POST"
        varchar status "pending/completed/skipped/failed"
        varchar completed_by "완료자"
        timestamp completed_at "완료시간"
    }

    work_order_work_item_result {
        varchar id PK "UUID ★신규★"
        varchar company_code "회사코드"
        varchar work_order_work_item_id FK "→ work_order_work_item.id"
        varchar work_item_detail_id FK "→ process_work_item_detail.id"
        varchar detail_type "check/inspect/input/procedure"
        varchar result_value "결과값"
        varchar is_passed "Y/N/null"
        varchar recorded_by "기록자"
        timestamp recorded_at "기록시간"
    }

    %% ========== 관계 ==========

    %% 마스터 체인: 품목 → 라우팅 → 작업기준정보
    item_info ||--o{ item_routing_version : "item_number = item_code"
    item_routing_version ||--o{ item_routing_detail : "id = routing_version_id"
    item_routing_detail }o--|| process_mng : "process_code"
    item_routing_detail ||--o{ process_work_item : "id = routing_detail_id"
    process_work_item ||--o{ process_work_item_detail : "id = work_item_id"

    %% 트랜잭션: 작업지시 → 공정진행 → 작업기준정보 진행
    work_instruction }o--|| item_info : "item_id = id"
    work_instruction ||--o{ work_order_process : "id = wo_id"
    work_order_process }o--|| item_routing_detail : "routing_detail_id = id"
    work_order_process ||--o{ work_order_work_item : "id = work_order_process_id"
    work_order_work_item }o--|| process_work_item : "work_item_id = id"
    work_order_work_item ||--o{ work_order_work_item_result : "id = work_order_work_item_id"
    work_order_work_item_result }o--|| process_work_item_detail : "work_item_detail_id = id"

2-1-1. 관계 요약 (텍스트)

[마스터 데이터 체인 - 조회용, 변경 없음]

  item_info ─── 1:N ───→ item_routing_version ─── 1:N ───→ item_routing_detail
  (품목)      item_number    (라우팅 버전)     routing_      (공정별 상세)
              = item_code                      version_id
                                                    │
                                    process_mng ◄───┘ process_code (공정 마스터)
                                                    │
                                                    ├── 1:N ───→ process_work_item ─── 1:N ───→ process_work_item_detail
                                                    │             (작업기준정보)                   (작업기준정보 상세)
                                                    │             routing_detail_id                 work_item_id
                                                    │
[트랜잭션 데이터 - 상태 관리]                        │
                                                    │
  work_instruction ─── 1:N ───→ work_order_process ─┘ routing_detail_id (★추가★)
  (작업지시)         wo_id       (공정별 진행)
  item_id → item_info                │
                                     ├── 1:N ───→ work_order_work_item ─── 1:N ───→ work_order_work_item_result
                                     │             (작업기준정보 진행)                  (상세 결과값)
                                     │             work_order_process_id               work_order_work_item_id
                                     │             work_item_id → process_work_item    work_item_detail_id → process_work_item_detail
                                     │             ★신규 테이블★                        ★신규 테이블★

2-2. 마스터 테이블 상세

item_info (품목 마스터)

컬럼 설명 비고
id PK (UUID)
item_number 품번 item_routing_version.item_code와 매칭
item_name 품명
company_code 회사코드 멀티테넌시

item_routing_version (라우팅 버전)

컬럼 설명 비고
id PK (UUID)
item_code 품번 item_info.item_number와 매칭
version_name 버전명 예: "기본 라우팅", "버전2"
is_default 기본 버전 여부 true/false, 기본 버전을 사용
company_code 회사코드

item_routing_detail (라우팅 상세 - 공정별)

컬럼 설명 비고
id PK (UUID)
routing_version_id FK → item_routing_version.id
seq_no 공정 순서 10, 20, 30...
process_code 공정코드 FK → process_mng.process_code
is_required 필수/선택 "필수" / "선택"
is_fixed_order 순서고정 여부 "고정" / "선택"
work_type 작업유형
standard_time 표준시간(분)
outsource_supplier 외주업체
company_code 회사코드

process_work_item (작업기준정보)

컬럼 설명 비고
id PK (UUID)
routing_detail_id FK → item_routing_detail.id
work_phase 작업단계 PRE(작업전) / IN(작업중) / POST(작업후)
title 작업항목명 예: "장비 체크", "소재 준비"
is_required 필수여부 Y/N
sort_order 정렬순서
description 설명
company_code 회사코드

process_work_item_detail (작업기준정보 상세)

컬럼 설명 비고
id PK (UUID)
work_item_id FK → process_work_item.id
detail_type 상세유형 check(체크) / inspect(검사) / input(입력) / procedure(절차) / info(정보)
content 내용 예: "소음검사", "치수검사"
input_type 입력타입 select, text 등
inspection_code 검사코드
inspection_method 검사방법
unit 단위
lower_limit 하한값
upper_limit 상한값
is_required 필수여부 Y/N
sort_order 정렬순서
company_code 회사코드

3. 작업 진행 테이블 (트랜잭션 데이터)

3-1. work_instruction (작업지시) - 기존 테이블

컬럼 설명 비고
id PK (UUID)
work_instruction_no 작업지시번호 예: WO-2026-001
item_id FK → item_info.id 이것만으로 라우팅/작업기준정보 전부 조회 가능
status 작업지시 상태 waiting / in_progress / completed / cancelled
qty 지시수량
completed_qty 완성수량
work_team 작업팀
worker 작업자
equipment_id 설비
start_date 시작일
end_date 종료일
remark 비고
company_code 회사코드

routing 컬럼: 현재 존재하지만 사용하지 않음 (null). 라우팅 버전을 지정하고 싶으면 이 컬럼에 item_routing_version.id를 넣어 특정 버전을 지정할 수 있음. 없으면 is_default=true 버전 자동 사용.

3-2. work_order_process (공정별 진행) - 기존 테이블, 변경 필요

작업지시가 생성될 때, 해당 품목의 라우팅 공정을 복사해서 이 테이블에 INSERT.

컬럼 설명 비고
id PK (UUID)
wo_id FK → work_instruction.id 작업지시 참조
routing_detail_id FK → item_routing_detail.id 추가 필요 - 라우팅 상세 참조
seq_no 공정 순서 라우팅에서 복사
process_code 공정코드 라우팅에서 복사
process_name 공정명 라우팅에서 복사 (비정규화, 조회 편의)
is_required 필수여부 라우팅에서 복사
is_fixed_order 순서고정 라우팅에서 복사
standard_time 표준시간 라우팅에서 복사
status 공정 상태 waiting / in_progress / completed / skipped
plan_qty 계획수량
input_qty 투입수량
good_qty 양품수량
defect_qty 불량수량
equipment_code 사용설비
accepted_by 접수자
accepted_at 접수시간
started_at 시작시간
completed_at 완료시간
remark 비고
company_code 회사코드

3-3. work_order_work_item (작업기준정보별 진행) - 신규 테이블

POP에서 작업자가 각 작업기준정보 항목을 체크/입력할 때 사용.

컬럼 설명 비고
id PK (UUID) gen_random_uuid()
company_code 회사코드 멀티테넌시
work_order_process_id FK → work_order_process.id 어떤 작업지시의 어떤 공정인지
work_item_id FK → process_work_item.id 어떤 작업기준정보인지
work_phase 작업단계 PRE / IN / POST (마스터에서 복사)
status 완료상태 pending / completed / skipped / failed
completed_by 완료자 작업자 ID
completed_at 완료시간
created_date 생성일
updated_date 수정일
writer 작성자

3-4. work_order_work_item_result (작업기준정보 상세 결과) - 신규 테이블

작업기준정보의 상세 항목(체크, 검사, 입력 등)에 대한 실제 결과값 저장.

컬럼 설명 비고
id PK (UUID) gen_random_uuid()
company_code 회사코드 멀티테넌시
work_order_work_item_id FK → work_order_work_item.id
work_item_detail_id FK → process_work_item_detail.id 어떤 상세항목인지
detail_type 상세유형 check / inspect / input / procedure (마스터에서 복사)
result_value 결과값 체크: "Y"/"N", 검사: 측정값, 입력: 입력값
is_passed 합격여부 Y / N / null(해당없음)
remark 비고 불합격 사유 등
recorded_by 기록자
recorded_at 기록시간
created_date 생성일
updated_date 수정일
writer 작성자

4. POP 데이터 플로우

4-1. 작업지시 등록 시 (ERP 측)

[작업지시 생성]
  │
  ├── 1. work_instruction INSERT (item_id, qty, status='waiting' 등)
  │
  ├── 2. item_id → item_info.item_number 조회
  │
  ├── 3. item_number → item_routing_version 조회 (is_default=true 또는 지정 버전)
  │
  ├── 4. routing_version_id → item_routing_detail 조회 (공정 목록)
  │
  └── 5. 각 공정별로 work_order_process INSERT
         ├── wo_id = work_instruction.id
         ├── routing_detail_id = item_routing_detail.id  ← 핵심!
         ├── seq_no, process_code, process_name 복사
         ├── status = 'waiting'
         └── plan_qty = work_instruction.qty

4-2. POP 작업 조회 시

[POP 화면: 작업지시 선택]
  │
  ├── 1. work_instruction 목록 조회 (status = 'waiting' or 'in_progress')
  │
  ├── 2. 선택한 작업지시의 공정 목록 조회
  │      SELECT wop.*, pm.process_name
  │      FROM work_order_process wop
  │      LEFT JOIN process_mng pm ON wop.process_code = pm.process_code
  │      WHERE wop.wo_id = {작업지시ID}
  │      ORDER BY CAST(wop.seq_no AS int)
  │
  └── 3. 선택한 공정의 작업기준정보 조회 (마스터 데이터 참조)
         SELECT pwi.*, pwid.*
         FROM process_work_item pwi
         LEFT JOIN process_work_item_detail pwid ON pwi.id = pwid.work_item_id
         WHERE pwi.routing_detail_id = {work_order_process.routing_detail_id}
         ORDER BY pwi.work_phase, pwi.sort_order, pwid.sort_order

4-3. POP 작업 실행 시

[작업자가 공정 시작]
  │
  ├── 1. work_order_process UPDATE
  │      SET status = 'in_progress', started_at = NOW(), accepted_by = {작업자}
  │
  ├── 2. work_instruction UPDATE (첫 공정 시작 시)
  │      SET status = 'in_progress'
  │
  ├── 3. 작업기준정보 항목별 체크/입력 시
  │      ├── work_order_work_item UPSERT (항목별 상태)
  │      └── work_order_work_item_result UPSERT (상세 결과값)
  │
  └── 4. 공정 완료 시
         ├── work_order_process UPDATE
         │    SET status = 'completed', completed_at = NOW(),
         │        good_qty = {양품}, defect_qty = {불량}
         │
         └── (모든 공정 완료 시)
              work_instruction UPDATE
              SET status = 'completed', completed_qty = {최종양품}

5. 핵심 조회 쿼리

5-1. 작업지시 → 전체 공정 + 작업기준정보 한방 조회

-- 작업지시의 공정별 진행 현황 + 작업기준정보
SELECT
  wi.work_instruction_no,
  wi.qty,
  wi.status as wi_status,
  ii.item_number,
  ii.item_name,
  wop.id as process_id,
  wop.seq_no,
  wop.process_code,
  wop.process_name,
  wop.status as process_status,
  wop.plan_qty,
  wop.good_qty,
  wop.defect_qty,
  wop.started_at,
  wop.completed_at,
  wop.routing_detail_id,
  -- 작업기준정보는 routing_detail_id로 마스터 조회
  pwi.id as work_item_id,
  pwi.work_phase,
  pwi.title as work_item_title,
  pwi.is_required as work_item_required
FROM work_instruction wi
JOIN item_info ii ON wi.item_id = ii.id
JOIN work_order_process wop ON wi.id = wop.wo_id
LEFT JOIN process_work_item pwi ON wop.routing_detail_id = pwi.routing_detail_id
WHERE wi.id = $1
  AND wi.company_code = $2
ORDER BY CAST(wop.seq_no AS int), pwi.work_phase, pwi.sort_order;

5-2. 특정 공정의 작업기준정보 + 진행 상태 조회

-- POP에서 특정 공정 선택 시: 마스터 + 진행 상태 조인
SELECT
  pwi.id as work_item_id,
  pwi.work_phase,
  pwi.title,
  pwi.is_required,
  pwid.id as detail_id,
  pwid.detail_type,
  pwid.content,
  pwid.input_type,
  pwid.inspection_code,
  pwid.inspection_method,
  pwid.unit,
  pwid.lower_limit,
  pwid.upper_limit,
  -- 진행 상태
  wowi.status as item_status,
  wowi.completed_by,
  wowi.completed_at,
  -- 결과값
  wowir.result_value,
  wowir.is_passed,
  wowir.remark as result_remark
FROM process_work_item pwi
LEFT JOIN process_work_item_detail pwid
  ON pwi.id = pwid.work_item_id
LEFT JOIN work_order_work_item wowi
  ON wowi.work_item_id = pwi.id
  AND wowi.work_order_process_id = $1  -- work_order_process.id
LEFT JOIN work_order_work_item_result wowir
  ON wowir.work_order_work_item_id = wowi.id
  AND wowir.work_item_detail_id = pwid.id
WHERE pwi.routing_detail_id = $2  -- work_order_process.routing_detail_id
ORDER BY
  CASE pwi.work_phase WHEN 'PRE' THEN 1 WHEN 'IN' THEN 2 WHEN 'POST' THEN 3 END,
  pwi.sort_order,
  pwid.sort_order;

6. 변경사항 요약

6-1. 기존 테이블 변경

테이블 변경내용
work_order_process routing_detail_id VARCHAR(500) 컬럼 추가

6-2. 신규 테이블

테이블 용도
work_order_work_item 작업지시 공정별 작업기준정보 진행 상태
work_order_work_item_result 작업기준정보 상세 항목의 실제 결과값

6-3. 건드리지 않는 것

테이블 이유
work_instruction item_id만 있으면 충분. 라우팅/작업기준정보 ID 추가 불필요
item_routing_version 마스터 데이터, 변경 없음
item_routing_detail 마스터 데이터, 변경 없음
process_work_item 마스터 데이터, 변경 없음
process_work_item_detail 마스터 데이터, 변경 없음

7. DDL (마이그레이션 SQL)

-- 1. work_order_process에 routing_detail_id 추가
ALTER TABLE work_order_process
ADD COLUMN IF NOT EXISTS routing_detail_id VARCHAR(500);

CREATE INDEX IF NOT EXISTS idx_wop_routing_detail_id
ON work_order_process(routing_detail_id);

-- 2. 작업기준정보별 진행 상태 테이블
CREATE TABLE IF NOT EXISTS work_order_work_item (
  id VARCHAR(500) PRIMARY KEY DEFAULT gen_random_uuid()::text,
  company_code VARCHAR(500) NOT NULL,
  work_order_process_id VARCHAR(500) NOT NULL,
  work_item_id VARCHAR(500) NOT NULL,
  work_phase VARCHAR(500),
  status VARCHAR(500) DEFAULT 'pending',
  completed_by VARCHAR(500),
  completed_at TIMESTAMP,
  created_date TIMESTAMP DEFAULT NOW(),
  updated_date TIMESTAMP DEFAULT NOW(),
  writer VARCHAR(500)
);

CREATE INDEX idx_wowi_process_id ON work_order_work_item(work_order_process_id);
CREATE INDEX idx_wowi_work_item_id ON work_order_work_item(work_item_id);
CREATE INDEX idx_wowi_company_code ON work_order_work_item(company_code);

-- 3. 작업기준정보 상세 결과 테이블
CREATE TABLE IF NOT EXISTS work_order_work_item_result (
  id VARCHAR(500) PRIMARY KEY DEFAULT gen_random_uuid()::text,
  company_code VARCHAR(500) NOT NULL,
  work_order_work_item_id VARCHAR(500) NOT NULL,
  work_item_detail_id VARCHAR(500) NOT NULL,
  detail_type VARCHAR(500),
  result_value VARCHAR(500),
  is_passed VARCHAR(500),
  remark TEXT,
  recorded_by VARCHAR(500),
  recorded_at TIMESTAMP DEFAULT NOW(),
  created_date TIMESTAMP DEFAULT NOW(),
  updated_date TIMESTAMP DEFAULT NOW(),
  writer VARCHAR(500)
);

CREATE INDEX idx_wowir_work_order_work_item_id ON work_order_work_item_result(work_order_work_item_id);
CREATE INDEX idx_wowir_detail_id ON work_order_work_item_result(work_item_detail_id);
CREATE INDEX idx_wowir_company_code ON work_order_work_item_result(company_code);

8. 상태값 정의

work_instruction.status (작업지시 상태)

의미
waiting 대기
in_progress 진행중
completed 완료
cancelled 취소

work_order_process.status (공정 상태)

의미
waiting 대기 (아직 시작 안 함)
in_progress 진행중 (작업자가 시작)
completed 완료
skipped 건너뜀 (선택 공정인 경우)

work_order_work_item.status (작업기준정보 항목 상태)

의미
pending 미완료
completed 완료
skipped 건너뜀
failed 실패 (검사 불합격 등)

work_order_work_item_result.is_passed (검사 합격여부)

의미
Y 합격
N 불합격
null 해당없음 (체크/입력 항목)

9. 설계 의도 요약

  1. 마스터와 트랜잭션 분리: 라우팅/작업기준정보는 마스터(템플릿), 실제 진행은 트랜잭션 테이블에서 관리
  2. 조회 경로: work_instruction.item_iditem_info.item_numberitem_routing_versionitem_routing_detailprocess_work_itemprocess_work_item_detail
  3. 진행 경로: work_order_process.routing_detail_id로 마스터 작업기준정보를 참조하되, 실제 진행/결과는 work_order_work_item + work_order_work_item_result에 저장
  4. 중복 저장 최소화: 작업지시에 공정/작업기준정보 ID를 넣지 않음. 품목만 있으면 전부 파생 조회 가능
  5. work_order_process: 작업지시 생성 시 라우팅 공정을 복사하는 이유는 진행 중 수량/상태/시간 등 트랜잭션 데이터를 기록해야 하기 때문 (마스터가 변경되어도 이미 발행된 작업지시의 공정은 유지)

10. 주의사항

  • work_order_process에 공정 정보를 복사(스냅샷)하는 이유: 마스터 라우팅이 나중에 변경되어도 이미 진행 중인 작업지시의 공정 구성은 영향받지 않아야 함
  • routing_detail_id는 "이 공정이 어떤 마스터 라우팅에서 왔는지" 추적용. 작업기준정보 조회 키로 사용
  • POP에서 작업기준정보를 표시할 때는 항상 마스터(process_work_item)를 조회하고, 결과만 트랜잭션 테이블에 저장
  • 모든 테이블에 company_code 필수 (멀티테넌시)