ERP-node/docs/POP_작업진행_설계서.md

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 필수 (멀티테넌시)