# 생산계획관리 화면 구현 설계서

> **Screen Code**: `TOPSEAL_PP_MAIN` (screen_id: 3985)
> **메뉴 경로**: 생산관리 > 생산계획관리
> **HTML 예시**: `00_화면개발_html/Cursor 폴더/화면개발/PC브라우저/생산/생산계획관리.html`
> **작성일**: 2026-03-13

---

## 1. 화면 전체 구조

```
+---------------------------------------------------------------------+
|                        검색 섹션 (상단)                                |
|  [품목코드] [품명] [계획기간(daterange)] [상태]                         |
|                       [사용자옵션] [엑셀업로드] [엑셀다운로드]           |
+----------------------------------+--+-------------------------------+
|     좌측 패널 (50%, 리사이즈)     |  |      우측 패널 (50%)           |
| +------------------------------+ |리| +---------------------------+ |
| | [수주데이터] [안전재고 부족분] | |사| | [완제품] [반제품]          | |
| +------------------------------+ |이| +---------------------------+ |
| | 수주 목록 헤더                 | |즈| | 완제품 생산 타임라인 헤더   | |
| |  [계획에없는품목만] [불러오기]  | |핸| |  [새로고침] [자동스케줄]    | |
| | +---------------------------+| |들| |  [병합] [반제품계획] [저장] | |
| | | 품목 그룹 테이블            || |  | | +------------------------+| |
| | | - 품목별 그룹 행 (13컬럼)  || |  | | | 옵션 패널               || |
| | |   -> 수주 상세 행 (7컬럼)  || |  | | | [리드타임] [기간] [재계산]|| |
| | | - 접기/펼치기 토글          || |  | | +------------------------+| |
| | | - 체크박스 (그룹/개별)      || |  | | | 범례                    || |
| | +---------------------------+| |  | | +------------------------+| |
| +------------------------------+ |  | | | 타임라인 스케줄러        || |
|                                  |  | | | (간트차트 형태)          || |
| -- 안전재고 부족분 탭 --          |  | | +------------------------+| |
| | 부족 품목 테이블 (8컬럼)       | |  | +---------------------------+ |
| | - 체크박스, 품목코드, 품명     | |  |                               |
| | - 현재고, 안전재고, 부족수량   | |  | -- 반제품 탭 --                |
| | - 권장생산량, 최종입고일       | |  | | 옵션 + 안내 패널            | |
| +------------------------------+ |  | | 반제품 타임라인 스케줄러     | |
+----------------------------------+--+-------------------------------+
```

---

## 2. 사용 테이블 및 컬럼 매핑

### 2.1 메인 테이블

| 테이블명 | 용도 | PK |
|----------|------|-----|
| `production_plan_mng` | 생산계획 마스터 | `id` (serial) |
| `sales_order_mng` | 수주 데이터 (좌측 패널 조회용) | `id` (serial) |
| `item_info` | 품목 마스터 (참조) | `id` (uuid text) |
| `inventory_stock` | 재고 현황 (안전재고 부족분 탭) | `id` (uuid text) |
| `equipment_info` | 설비 정보 (타임라인 리소스) | `id` (serial) |
| `bom` / `bom_detail` | BOM 정보 (반제품 계획 생성) | `id` (uuid text) |
| `work_instruction` | 작업지시 (타임라인 연동) | 별도 확인 필요 |

### 2.2 핵심 컬럼 매핑 - production_plan_mng

| 컬럼명 | 타입 | 용도 | HTML 매핑 |
|--------|------|------|-----------|
| `id` | serial PK | 고유 ID | `schedule.id` |
| `company_code` | varchar | 멀티테넌시 | - |
| `plan_no` | varchar NOT NULL | 계획번호 | `SCH-{timestamp}` |
| `plan_date` | date | 계획 등록일 | 자동 |
| `item_code` | varchar NOT NULL | 품목코드 | `schedule.itemCode` |
| `item_name` | varchar | 품목명 | `schedule.itemName` |
| `product_type` | varchar | 완제품/반제품 | `'완제품'` or `'반제품'` |
| `plan_qty` | numeric NOT NULL | 계획 수량 | `schedule.quantity` |
| `completed_qty` | numeric | 완료 수량 | `schedule.completedQty` |
| `progress_rate` | numeric | 진행률(%) | `schedule.progressRate` |
| `start_date` | date NOT NULL | 시작일 | `schedule.startDate` |
| `end_date` | date NOT NULL | 종료일 | `schedule.endDate` |
| `due_date` | date | 납기일 | `schedule.dueDate` |
| `equipment_id` | integer | 설비 ID | `schedule.equipmentId` |
| `equipment_code` | varchar | 설비 코드 | - |
| `equipment_name` | varchar | 설비명 | `schedule.productionLine` |
| `status` | varchar | 상태 | `planned/in_progress/completed/work-order` |
| `priority` | varchar | 우선순위 | `normal/high/urgent` |
| `hourly_capacity` | numeric | 시간당 생산능력 | `schedule.hourlyCapacity` |
| `daily_capacity` | numeric | 일일 생산능력 | `schedule.dailyCapacity` |
| `lead_time` | integer | 리드타임(일) | `schedule.leadTime` |
| `work_shift` | varchar | 작업조 | `DAY/NIGHT/BOTH` |
| `work_order_no` | varchar | 작업지시번호 | `schedule.workOrderNo` |
| `manager_name` | varchar | 담당자 | `schedule.manager` |
| `order_no` | varchar | 연관 수주번호 | `schedule.orderInfo[].orderNo` |
| `parent_plan_id` | integer | 모 계획 ID (반제품용) | `schedule.parentPlanId` |
| `remarks` | text | 비고 | `schedule.remarks` |

### 2.3 수주 데이터 조회용 - sales_order_mng

| 컬럼명 | 용도 | 좌측 테이블 컬럼 매핑 |
|--------|------|----------------------|
| `order_no` | 수주번호 | 수주 상세 행 - 수주번호 |
| `part_code` | 품목코드 | 그룹 행 - 품목코드 (그룹 기준) |
| `part_name` | 품명 | 그룹 행 - 품목명 |
| `order_qty` | 수주량 | 총수주량 (SUM) |
| `ship_qty` | 출고량 | 출고량 (SUM) |
| `balance_qty` | 잔량 | 잔량 (SUM) |
| `due_date` | 납기일 | 수주 상세 행 - 납기일 |
| `partner_id` | 거래처 | 수주 상세 행 - 거래처 |
| `status` | 상태 | 상태 배지 (일반/긴급) |

### 2.4 안전재고 부족분 조회용 - inventory_stock + item_info

| 컬럼명 | 출처 | 좌측 테이블 컬럼 매핑 |
|--------|------|----------------------|
| `item_code` | inventory_stock | 품목코드 |
| `item_name` | item_info (JOIN) | 품목명 |
| `current_qty` | inventory_stock | 현재고 |
| `safety_qty` | inventory_stock | 안전재고 |
| `부족수량` | 계산값 (`safety_qty - current_qty`) | 부족수량 (음수면 부족) |
| `권장생산량` | 계산값 (`safety_qty * 2 - current_qty`) | 권장생산량 |
| `last_in_date` | inventory_stock | 최종입고일 |

---

## 3. V2 컴포넌트 구현 가능/불가능 분석

### 3.1 구현 가능 (기존 V2 컴포넌트)

| 기능 | V2 컴포넌트 | 현재 상태 |
|------|-------------|-----------|
| 좌우 분할 레이아웃 | `v2-split-panel-layout` (`displayMode: "custom"`) | layout_data에 이미 존재 |
| 검색 필터 | `v2-table-search-widget` | layout_data에 이미 존재 |
| 좌측/우측 탭 전환 | `v2-tabs-widget` | layout_data에 이미 존재 |
| 체크박스 선택 | `v2-table-grouped` (`showCheckbox: true`) | layout_data에 이미 존재 |
| 단순 그룹핑 테이블 | `v2-table-grouped` (`groupByColumn`) | layout_data에 이미 존재 |
| 타임라인 스케줄러 | `v2-timeline-scheduler` | layout_data에 이미 존재 |
| 버튼 액션 | `v2-button-primary` | layout_data에 이미 존재 |
| 안전재고 부족분 테이블 | `v2-table-list` 또는 `v2-table-grouped` | 미구성 (탭2에 컴포넌트 없음) |

### 3.2 부분 구현 가능 (개선/확장 필요)

| 기능 | 문제점 | 필요 작업 |
|------|--------|-----------|
| 수주 그룹 테이블 (2레벨) | `v2-table-grouped`는 **동일 컬럼 기준 그룹핑**만 지원. HTML은 그룹 행(13컬럼)과 상세 행(7컬럼)이 완전히 다른 구조 | 컴포넌트 확장 or 백엔드에서 집계 데이터를 별도 API로 제공 |
| 스케줄러 옵션 패널 | HTML의 안전리드타임/표시기간/재계산 옵션을 위한 전용 UI 없음 | `v2-input` + `v2-select` 조합으로 구성 가능 |
| 범례 UI | `v2-timeline-scheduler`에 statusColors 설정은 있지만 범례 UI 자체는 없음 | `v2-text-display` 또는 커스텀 구성 |
| 부족수량 빨간색 강조 | 조건부 서식(conditional formatting) 미지원 | 컴포넌트 확장 필요 |
| "계획에 없는 품목만" 필터 | 단순 테이블 필터가 아닌 교차 테이블 비교 필터 | 백엔드 API 필요 |

### 3.3 신규 개발 필요 (현재 V2 컴포넌트로 불가능)

| 기능 | 설명 | 구현 방안 |
|------|------|-----------|
| **자동 스케줄 생성 API** | 선택 품목의 필요생산계획량, 납기일, 설비 생산능력 기반으로 타임라인 자동 배치 | 백엔드 전용 API |
| **선택 계획 병합 API** | 동일 품목 복수 스케줄을 하나로 합산 | 백엔드 전용 API |
| **반제품 계획 자동 생성 API** | BOM 기반으로 완제품 계획에서 필요 반제품 소요량 계산 | 백엔드 전용 API (BOM + 재고 연계) |
| **수주 잔량/현재고 연산 조회 API** | 여러 테이블 JOIN + 집계 연산으로 좌측 패널 데이터 제공 | 백엔드 전용 API |
| **스케줄 상세 모달** | 기본정보, 근거정보, 생산정보, 계획기간, 계획분할, 설비할당 | 모달 화면 (`TOPSEAL_PP_MODAL` screen_id: 3986) 보강 |
| **설비 선택 모달** | 설비별 수량 할당 및 일정 등록 | 신규 모달 화면 필요 |
| **변경사항 확인 모달** | 자동 스케줄 생성 전후 비교 (신규/유지/삭제 건수 요약) | 신규 모달 또는 확인 다이얼로그 |

---

## 4. 백엔드 API 설계

### 4.1 수주 데이터 조회 API (좌측 패널 - 수주데이터 탭)

```
GET /api/production/order-summary
```

**목적**: 수주 데이터를 **품목별로 그룹핑**하여 반환. 그룹 헤더에 집계값(총수주량, 출고량, 잔량, 현재고, 안전재고, 기생산계획량 등) 포함.

**응답 구조**:
```json
{
  "success": true,
  "data": [
    {
      "item_code": "ITEM-001",
      "item_name": "탑씰 Type A",
      "hourly_capacity": 100,
      "daily_capacity": 800,
      "lead_time": 1,
      "total_order_qty": 1000,
      "total_ship_qty": 300,
      "total_balance_qty": 700,
      "current_stock": 100,
      "safety_stock": 150,
      "plan_ship_qty": 0,
      "existing_plan_qty": 0,
      "in_progress_qty": 0,
      "required_plan_qty": 750,
      "orders": [
        {
          "order_no": "SO-2025-101",
          "partner_name": "ABC 상사",
          "order_qty": 500,
          "ship_qty": 200,
          "balance_qty": 300,
          "due_date": "2025-11-05",
          "is_urgent": false
        },
        {
          "order_no": "SO-2025-102",
          "partner_name": "XYZ 무역",
          "order_qty": 500,
          "ship_qty": 100,
          "balance_qty": 400,
          "due_date": "2025-11-10",
          "is_urgent": false
        }
      ]
    }
  ]
}
```

**SQL 로직 (핵심)**:
```sql
WITH order_summary AS (
  SELECT
    so.part_code AS item_code,
    so.part_name AS item_name,
    SUM(COALESCE(so.order_qty, 0)) AS total_order_qty,
    SUM(COALESCE(so.ship_qty, 0)) AS total_ship_qty,
    SUM(COALESCE(so.balance_qty, 0)) AS total_balance_qty
  FROM sales_order_mng so
  WHERE so.company_code = $1
    AND so.status NOT IN ('cancelled', 'completed')
    AND so.balance_qty > 0
  GROUP BY so.part_code, so.part_name
),
stock_info AS (
  SELECT
    item_code,
    SUM(COALESCE(current_qty::numeric, 0)) AS current_stock,
    MAX(COALESCE(safety_qty::numeric, 0)) AS safety_stock
  FROM inventory_stock
  WHERE company_code = $1
  GROUP BY item_code
),
plan_info AS (
  SELECT
    item_code,
    SUM(CASE WHEN status = 'planned' THEN plan_qty ELSE 0 END) AS existing_plan_qty,
    SUM(CASE WHEN status = 'in_progress' THEN plan_qty ELSE 0 END) AS in_progress_qty
  FROM production_plan_mng
  WHERE company_code = $1
    AND product_type = '완제품'
    AND status NOT IN ('completed', 'cancelled')
  GROUP BY item_code
)
SELECT
  os.*,
  COALESCE(si.current_stock, 0) AS current_stock,
  COALESCE(si.safety_stock, 0) AS safety_stock,
  COALESCE(pi.existing_plan_qty, 0) AS existing_plan_qty,
  COALESCE(pi.in_progress_qty, 0) AS in_progress_qty,
  GREATEST(
    os.total_balance_qty + COALESCE(si.safety_stock, 0) - COALESCE(si.current_stock, 0)
    - COALESCE(pi.existing_plan_qty, 0) - COALESCE(pi.in_progress_qty, 0),
    0
  ) AS required_plan_qty
FROM order_summary os
LEFT JOIN stock_info si ON os.item_code = si.item_code
LEFT JOIN plan_info pi ON os.item_code = pi.item_code
ORDER BY os.item_code;
```

**파라미터**:
- `company_code`: req.user.companyCode (자동)
- `exclude_planned` (optional): `true`이면 기존 계획이 있는 품목 제외

---

### 4.2 안전재고 부족분 조회 API (좌측 패널 - 안전재고 탭)

```
GET /api/production/stock-shortage
```

**응답 구조**:
```json
{
  "success": true,
  "data": [
    {
      "item_code": "ITEM-001",
      "item_name": "탑씰 Type A",
      "current_qty": 50,
      "safety_qty": 200,
      "shortage_qty": -150,
      "recommended_qty": 300,
      "last_in_date": "2025-10-15"
    }
  ]
}
```

**SQL 로직**:
```sql
SELECT
  ist.item_code,
  ii.item_name,
  COALESCE(ist.current_qty::numeric, 0) AS current_qty,
  COALESCE(ist.safety_qty::numeric, 0) AS safety_qty,
  (COALESCE(ist.current_qty::numeric, 0) - COALESCE(ist.safety_qty::numeric, 0)) AS shortage_qty,
  GREATEST(COALESCE(ist.safety_qty::numeric, 0) * 2 - COALESCE(ist.current_qty::numeric, 0), 0) AS recommended_qty,
  ist.last_in_date
FROM inventory_stock ist
JOIN item_info ii ON ist.item_code = ii.id AND ist.company_code = ii.company_code
WHERE ist.company_code = $1
  AND COALESCE(ist.current_qty::numeric, 0) < COALESCE(ist.safety_qty::numeric, 0)
ORDER BY shortage_qty ASC;
```

---

### 4.3 자동 스케줄 생성 API

```
POST /api/production/generate-schedule
```

**요청 body**:
```json
{
  "items": [
    {
      "item_code": "ITEM-001",
      "item_name": "탑씰 Type A",
      "required_qty": 750,
      "earliest_due_date": "2025-11-05",
      "hourly_capacity": 100,
      "daily_capacity": 800,
      "lead_time": 1,
      "orders": [
        { "order_no": "SO-2025-101", "balance_qty": 300, "due_date": "2025-11-05" },
        { "order_no": "SO-2025-102", "balance_qty": 400, "due_date": "2025-11-10" }
      ]
    }
  ],
  "options": {
    "safety_lead_time": 1,
    "recalculate_unstarted": true,
    "product_type": "완제품"
  }
}
```

**비즈니스 로직**:
1. 각 품목의 필요생산계획량, 납기일, 일일생산능력을 기반으로 생산일수 계산
2. `생산일수 = ceil(필요생산계획량 / 일일생산능력)`
3. `시작일 = 납기일 - 생산일수 - 안전리드타임`
4. 시작일이 오늘 이전이면 오늘로 조정
5. `recalculate_unstarted = true`면 기존 진행중/작업지시/완료 스케줄은 유지, 미진행(planned)만 제거 후 재계산
6. 결과를 `production_plan_mng`에 INSERT
7. 변경사항 요약(신규/유지/삭제 건수) 반환

**응답 구조**:
```json
{
  "success": true,
  "data": {
    "summary": {
      "total": 3,
      "new_count": 2,
      "kept_count": 1,
      "deleted_count": 1
    },
    "schedules": [
      {
        "id": 101,
        "plan_no": "PP-2025-0001",
        "item_code": "ITEM-001",
        "item_name": "탑씰 Type A",
        "plan_qty": 750,
        "start_date": "2025-10-30",
        "end_date": "2025-11-03",
        "due_date": "2025-11-05",
        "status": "planned"
      }
    ]
  }
}
```

---

### 4.4 스케줄 병합 API

```
POST /api/production/merge-schedules
```

**요청 body**:
```json
{
  "schedule_ids": [101, 102, 103],
  "product_type": "완제품"
}
```

**비즈니스 로직**:
1. 선택된 스케줄이 모두 동일 품목인지 검증
2. 완제품/반제품이 섞여있지 않은지 검증
3. 수량 합산, 가장 빠른 시작일/납기일, 가장 늦은 종료일 적용
4. 원본 스케줄 DELETE, 병합된 스케줄 INSERT
5. 수주 정보(order_no)는 병합 (중복 제거)

---

### 4.5 반제품 계획 자동 생성 API

```
POST /api/production/generate-semi-schedule
```

**요청 body**:
```json
{
  "plan_ids": [101, 102],
  "options": {
    "consider_stock": true,
    "keep_in_progress": false,
    "exclude_used": true
  }
}
```

**비즈니스 로직**:
1. 선택된 완제품 계획의 품목코드로 BOM 조회
2. `bom` 테이블에서 해당 품목의 `item_id` → `bom_detail`에서 하위 반제품(`child_item_id`) 조회
3. 각 반제품의 필요 수량 = `완제품 계획수량 x BOM 소요량(quantity)`
4. `consider_stock = true`면 현재고/안전재고 감안하여 순 필요량 계산
5. `exclude_used = true`면 이미 투입된 반제품 수량 차감
6. 모품목 생산 시작일 고려하여 반제품 납기일 설정 (시작일 - 반제품 리드타임)
7. `production_plan_mng`에 `product_type = '반제품'`, `parent_plan_id` 설정하여 INSERT

---

### 4.6 스케줄 상세 저장/수정 API

```
PUT /api/production/plan/:id
```

**요청 body**:
```json
{
  "plan_qty": 750,
  "start_date": "2025-10-30",
  "end_date": "2025-11-03",
  "equipment_id": 1,
  "equipment_code": "LINE-01",
  "equipment_name": "1호기",
  "manager_name": "홍길동",
  "work_shift": "DAY",
  "priority": "high",
  "remarks": "긴급 생산"
}
```

---

### 4.7 스케줄 분할 API

```
POST /api/production/split-schedule
```

**요청 body**:
```json
{
  "plan_id": 101,
  "splits": [
    { "qty": 500, "start_date": "2025-10-30", "end_date": "2025-11-01" },
    { "qty": 250, "start_date": "2025-11-02", "end_date": "2025-11-03" }
  ]
}
```

**비즈니스 로직**:
1. 분할 수량 합산이 원본 수량과 일치하는지 검증
2. 원본 스케줄 DELETE
3. 분할된 각 조각을 신규 INSERT (동일 `order_no`, `item_code` 유지)

---

## 5. 모달 화면 설계

### 5.1 스케줄 상세 모달 (screen_id: 3986 보강)

**섹션 구성**:

| 섹션 | 필드 | 타입 | 비고 |
|------|------|------|------|
| **기본 정보** | 품목코드, 품목명 | text (readonly) | 자동 채움 |
| **근거 정보** | 수주번호/거래처/납기일 목록 | text (readonly) | 연관 수주 정보 표시 |
| **생산 정보** | 총 생산수량 | number | 수정 가능 |
| | 납기일 (수주 기준) | date (readonly) | 가장 빠른 납기일 |
| **계획 기간** | 계획 시작일, 종료일 | date | 수정 가능 |
| | 생산 기간 | text (readonly) | 자동 계산 표시 |
| **계획 분할** | 분할 개수, 분할 수량 입력 | select, number | 분할하기 기능 |
| **설비 할당** | 설비 선택 버튼 | button → 모달 | 설비 선택 모달 오픈 |
| **생산 상태** | 상태 | select (disabled) | `planned/work-order/in_progress/completed` |
| **추가 정보** | 담당자, 작업지시번호, 비고 | text | 수정 가능 |
| **하단 버튼** | 삭제, 취소, 저장 | buttons | - |

### 5.2 수주 불러오기 모달

**구성**:
- 선택된 품목 목록 표시
- 주의사항 안내
- 라디오 버튼: "기존 계획에 추가" / "별도 계획으로 생성"
- 취소/불러오기 버튼

### 5.3 안전재고 불러오기 모달

**구성**: 수주 불러오기 모달과 동일한 패턴

### 5.4 설비 선택 모달

**구성**:
- 총 수량 / 할당 수량 / 미할당 수량 요약
- 설비 카드 그리드 (설비명, 생산능력, 할당 수량 입력, 시작일/종료일)
- 취소/저장 버튼

### 5.5 변경사항 확인 모달

**구성**:
- 경고 메시지
- 변경사항 요약 카드 (총 계획, 신규 생성, 유지됨, 삭제됨)
- 변경사항 상세 목록 (품목별 변경 전/후 비교)
- 취소/확인 및 적용 버튼

---

## 6. 현재 layout_data 수정 필요 사항

### 6.1 현재 layout_data 구조 (screen_id: 3985, layout_id: 9192)

```
comp_search (v2-table-search-widget) - 검색 필터
comp_split_panel (v2-split-panel-layout)
  ├── leftPanel (custom mode)
  │   ├── left_tabs (v2-tabs-widget) - [수주데이터, 안전재고 부족분]
  │   ├── order_table (v2-table-grouped) - 수주 테이블
  │   └── btn_import (v2-button-primary) - 선택 품목 불러오기
  ├── rightPanel (custom mode)
  │   ├── right_tabs (v2-tabs-widget) - [완제품, 반제품]
  │   │   └── finished_tab.components
  │   │       ├── v2-timeline-scheduler - 타임라인
  │   │       └── v2-button-primary - 스케줄 생성
  │   ├── btn_save (v2-button-primary) - 자동 스케줄 생성
  │   └── btn_clear (v2-button-primary) - 초기화
comp_q0iqzkpx (v2-button-primary) - 하단 저장 버튼 (무의미)
```

### 6.2 수정 필요 사항

| 항목 | 현재 상태 | 필요 상태 |
|------|-----------|-----------|
| **좌측 - 안전재고 탭** | 컴포넌트 없음 (`"컴포넌트가 없습니다"` 표시) | `v2-table-list` 또는 별도 조회 API 연결된 테이블 추가 |
| **좌측 - order_table** | `selectedTable: "sales_order_mng"` (범용 API) | 전용 API (`/api/production/order-summary`)로 변경 필요 |
| **좌측 - 체크박스 필터** | 없음 | "계획에 없는 품목만" 체크박스 UI 추가 |
| **우측 - 반제품 탭** | 컴포넌트 없음 | 반제품 타임라인 + 옵션 패널 추가 |
| **우측 - 타임라인** | `selectedTable: "work_instruction"` | `selectedTable: "production_plan_mng"` + 필터 `product_type='완제품'` |
| **우측 - 옵션 패널** | 없음 | 안전리드타임, 표시기간, 재계산 체크박스 → `v2-input` 조합 |
| **우측 - 범례** | 없음 | `v2-text-display` 또는 커스텀 범례 컴포넌트 |
| **우측 - 버튼들** | 일부만 존재 | 병합, 반제품계획, 저장, 초기화 추가 |
| **하단 저장 버튼** | 존재 (무의미) | 제거 |
| **우측 패널 렌더링 버그** | 타임라인 미렌더링 | SplitPanelLayout custom 모드 디버깅 필요 |

---

## 7. 구현 단계별 계획

### Phase 1: 기존 버그 수정 + 기본 구조 안정화

**목표**: 현재 layout_data로 화면이 최소한 정상 렌더링되게 만들기

| 작업 | 상세 | 예상 난이도 |
|------|------|-------------|
| 1-1. 좌측 z-index 겹침 수정 | SplitPanelLayout의 custom 모드에서 내부 컴포넌트가 비대화형 div에 가려지는 이슈 | 중 |
| 1-2. 우측 타임라인 렌더링 수정 | tabs-widget 내부 timeline-scheduler가 렌더링되지 않는 이슈 | 중 |
| 1-3. 하단 저장 버튼 제거 | layout_data에서 `comp_q0iqzkpx` 제거 | 하 |
| 1-4. 타임라인 데이터 소스 수정 | `work_instruction` → `production_plan_mng`으로 변경 | 하 |

### Phase 2: 백엔드 API 개발

**목표**: 화면에 필요한 데이터를 제공하는 전용 API 구축

| 작업 | 상세 | 예상 난이도 |
|------|------|-------------|
| 2-1. 수주 데이터 조회 API | `GET /api/production/order-summary` (4.1 참조) | 중 |
| 2-2. 안전재고 부족분 API | `GET /api/production/stock-shortage` (4.2 참조) | 하 |
| 2-3. 자동 스케줄 생성 API | `POST /api/production/generate-schedule` (4.3 참조) | 상 |
| 2-4. 스케줄 CRUD API | `PUT/DELETE /api/production/plan/:id` (4.6 참조) | 중 |
| 2-5. 스케줄 병합 API | `POST /api/production/merge-schedules` (4.4 참조) | 중 |
| 2-6. 반제품 계획 자동 생성 API | `POST /api/production/generate-semi-schedule` (4.5 참조) | 상 |
| 2-7. 스케줄 분할 API | `POST /api/production/split-schedule` (4.7 참조) | 중 |

### Phase 3: layout_data 보강 + 모달 화면

**목표**: 안전재고 탭, 반제품 탭, 모달들 구성

| 작업 | 상세 | 예상 난이도 |
|------|------|-------------|
| 3-1. 안전재고 부족분 탭 구성 | `stock_tab`에 테이블 컴포넌트 + "선택 품목 불러오기" 버튼 추가 | 중 |
| 3-2. 반제품 탭 구성 | `semi_tab`에 타임라인 + 옵션 + 버튼 추가 | 중 |
| 3-3. 옵션 패널 구성 | v2-input 조합으로 안전리드타임, 표시기간, 체크박스 | 중 |
| 3-4. 버튼 액션 연결 | 자동 스케줄, 병합, 반제품계획, 저장, 초기화 → API 연결 | 중 |
| 3-5. 스케줄 상세 모달 보강 | screen_id: 3986 layout_data 수정 | 중 |
| 3-6. 수주/안전재고 불러오기 모달 | 신규 모달 screen 생성 | 중 |
| 3-7. 설비 선택 모달 | 신규 모달 screen 생성 | 중 |

### Phase 4: v2-table-grouped 확장 (2레벨 트리 지원)

**목표**: HTML 예시의 "품목 그룹 → 수주 상세" 2레벨 트리 테이블 구현

| 작업 | 상세 | 예상 난이도 |
|------|------|-------------|
| 4-1. 컴포넌트 확장 설계 | 그룹 행과 상세 행이 다른 컬럼 구조를 가질 수 있도록 설계 | 상 |
| 4-2. expandedRowRenderer 구현 | 그룹 행 펼침 시 별도 컬럼/데이터로 하위 행 렌더링 | 상 |
| 4-3. 그룹 행 집계 컬럼 설정 | 그룹 헤더에 SUM, 계산 필드 표시 (현재고, 안전재고, 필요생산계획 등) | 중 |
| 4-4. 조건부 서식 지원 | 부족수량 빨간색, 양수 초록색 등 | 중 |

**대안**: Phase 4가 너무 복잡하면, 좌측 수주데이터를 2개 연동 테이블로 분리 (상단: 품목별 집계 테이블, 하단: 선택 품목의 수주 상세 테이블) 하는 방식도 검토 가능

---

## 8. 파일 생성/수정 목록

### 8.1 백엔드

| 파일 | 작업 | 비고 |
|------|------|------|
| `backend-node/src/routes/productionRoutes.ts` | 라우터 등록 | 신규 or 기존 확장 |
| `backend-node/src/controllers/productionController.ts` | API 핸들러 | 신규 or 기존 확장 |
| `backend-node/src/services/productionPlanService.ts` | 비즈니스 로직 서비스 | 신규 |

### 8.2 DB (layout_data 수정)

| 대상 | 작업 |
|------|------|
| `screen_layouts_v2` (screen_id: 3985) | layout_data JSON 수정 |
| `screen_layouts_v2` (screen_id: 3986) | 모달 layout_data 보강 |
| `screen_definitions` + `screen_layouts_v2` | 설비 선택 모달 신규 등록 |
| `screen_definitions` + `screen_layouts_v2` | 불러오기 모달 신규 등록 |

### 8.3 프론트엔드 (API 클라이언트)

| 파일 | 작업 |
|------|------|
| `frontend/lib/api/production.ts` | 생산계획 전용 API 클라이언트 함수 추가 |

### 8.4 프론트엔드 (V2 컴포넌트 확장, Phase 4)

| 파일 | 작업 |
|------|------|
| `frontend/lib/registry/components/v2-table-grouped/` | 2레벨 트리 지원 확장 |
| `frontend/lib/registry/components/v2-timeline-scheduler/` | 옵션 패널/범례 확장 (필요시) |

---

## 9. 이벤트 흐름 (주요 시나리오)

### 9.1 자동 스케줄 생성 흐름

```
1. 사용자가 좌측 수주데이터에서 품목 체크박스 선택
2. 우측 "자동 스케줄 생성" 버튼 클릭
3. (옵션 확인) 안전리드타임, 재계산 모드 체크
4. POST /api/production/generate-schedule 호출
5. (응답) 변경사항 확인 모달 표시 (신규/유지/삭제 건수)
6. 사용자 "확인 및 적용" 클릭
7. 타임라인 스케줄러 새로고침
8. 좌측 수주 목록의 "기생산계획량" 컬럼 갱신
```

### 9.2 수주 불러오기 흐름

```
1. 사용자가 좌측 수주데이터에서 품목 체크박스 선택
2. "선택 품목 불러오기" 버튼 클릭
3. 불러오기 모달 표시 (선택 품목 목록 + 추가방식 선택)
4. "기존 계획에 추가" or "별도 계획으로 생성" 선택
5. "불러오기" 버튼 클릭
6. POST /api/production/generate-schedule 호출 (단건)
7. 타임라인 새로고침
```

### 9.3 타임라인 스케줄 클릭 → 상세 모달

```
1. 사용자가 타임라인의 스케줄 바 클릭
2. 스케줄 상세 모달 오픈 (TOPSEAL_PP_MODAL)
3. 기본정보(readonly), 근거정보(readonly), 생산정보(수정가능) 표시
4. 계획기간 수정, 설비할당, 분할 등 작업
5. "저장" → PUT /api/production/plan/:id
6. "삭제" → DELETE /api/production/plan/:id
7. 모달 닫기 → 타임라인 새로고침
```

### 9.4 반제품 계획 생성 흐름

```
1. 우측 완제품 탭에서 스케줄 체크박스 선택
2. "선택 품목 → 반제품 계획" 버튼 클릭
3. POST /api/production/generate-semi-schedule 호출
   - BOM 조회 → 필요 반제품 목록 + 소요량 계산
   - 재고 감안 → 순 필요량 계산
   - 반제품 계획 INSERT (product_type='반제품', parent_plan_id 설정)
4. 반제품 탭으로 자동 전환
5. 반제품 타임라인 새로고침
```

---

## 10. 검색 필드 설정

| 필드명 | 타입 | 라벨 | 대상 컬럼 |
|--------|------|------|-----------|
| `item_code` | text | 품목코드 | `part_code` (수주) / `item_code` (계획) |
| `item_name` | text | 품명 | `part_name` / `item_name` |
| `plan_date` | daterange | 계획기간 | `start_date` ~ `end_date` |
| `status` | select | 상태 | 전체 / 계획 / 진행 / 완료 |

---

## 11. 권한 및 멀티테넌시

### 11.1 모든 API에 적용

```typescript
const companyCode = req.user!.companyCode;

if (companyCode === '*') {
  // 최고관리자: 모든 회사 데이터 조회 가능
} else {
  // 일반 회사: WHERE company_code = $1 필수
}
```

### 11.2 데이터 격리

- `production_plan_mng.company_code` 필터 필수
- `sales_order_mng.company_code` 필터 필수
- `inventory_stock.company_code` 필터 필수
- JOIN 시 양쪽 테이블 모두 `company_code` 조건 포함

---

## 12. 우선순위 정리

| 우선순위 | 작업 | 이유 |
|----------|------|------|
| **1 (긴급)** | Phase 1: 기존 렌더링 버그 수정 | 현재 화면 자체가 정상 동작하지 않음 |
| **2 (높음)** | Phase 2-1, 2-2: 수주/재고 조회 API | 좌측 패널의 핵심 데이터 |
| **3 (높음)** | Phase 2-3: 자동 스케줄 생성 API | 우측 패널의 핵심 기능 |
| **4 (중간)** | Phase 3: layout_data 보강 | 안전재고 탭, 반제품 탭, 모달 |
| **5 (중간)** | Phase 2-4~2-7: 나머지 API | 병합, 분할, 반제품 계획 |
| **6 (낮음)** | Phase 4: 2레벨 트리 테이블 확장 | 현재 단순 그룹핑으로도 기본 동작 |

---

## 부록 A: HTML 예시의 모달 목록

| 모달명 | HTML ID | 용도 |
|--------|---------|------|
| 스케줄 상세 모달 | `scheduleModal` | 스케줄 기본정보/근거정보/생산정보/계획기간/분할/설비할당/상태/추가정보 |
| 수주 불러오기 모달 | `orderImportModal` | 선택 품목 목록 + 추가방식 선택 (기존추가/별도생성) |
| 안전재고 불러오기 모달 | `stockImportModal` | 부족 품목 목록 + 추가방식 선택 |
| 설비 선택 모달 | `equipmentSelectModal` | 설비 카드 + 수량할당 + 일정등록 |
| 변경사항 확인 모달 | `changeConfirmModal` | 자동스케줄 생성 결과 요약 + 상세 비교 |

## 부록 B: HTML 예시의 JS 핵심 함수 목록

| 함수명 | 기능 | 매핑 API |
|--------|------|----------|
| `generateSchedule()` | 자동 스케줄 생성 (품목별 합산) | POST /api/production/generate-schedule |
| `saveSchedule()` | 스케줄 저장 (localStorage → DB) | POST /api/production/plan (bulk) |
| `mergeSelectedSchedules()` | 선택 계획 병합 | POST /api/production/merge-schedules |
| `generateSemiFromSelected()` | 반제품 계획 자동 생성 | POST /api/production/generate-semi-schedule |
| `saveScheduleFromModal()` | 모달에서 스케줄 저장 | PUT /api/production/plan/:id |
| `deleteScheduleFromModal()` | 모달에서 스케줄 삭제 | DELETE /api/production/plan/:id |
| `openOrderImportModal()` | 수주 불러오기 모달 열기 | - (프론트엔드 UI) |
| `importOrderItems()` | 수주 품목 불러오기 실행 | POST /api/production/generate-schedule |
| `openStockImportModal()` | 안전재고 불러오기 모달 열기 | - (프론트엔드 UI) |
| `importStockItems()` | 안전재고 품목 불러오기 실행 | POST /api/production/generate-schedule |
| `refreshOrderList()` | 수주 목록 새로고침 | GET /api/production/order-summary |
| `refreshStockList()` | 재고 부족 목록 새로고침 | GET /api/production/stock-shortage |
| `switchTab(tabName)` | 좌측 탭 전환 | - (프론트엔드 UI) |
| `switchTimelineTab(tabName)` | 우측 탭 전환 | - (프론트엔드 UI) |
| `toggleOrderDetails(itemGroup)` | 품목 그룹 펼치기/접기 | - (프론트엔드 UI) |
| `renderTimeline()` | 완제품 타임라인 렌더링 | - (프론트엔드 UI) |
| `renderSemiTimeline()` | 반제품 타임라인 렌더링 | - (프론트엔드 UI) |
| `executeSplit()` | 계획 분할 실행 | POST /api/production/split-schedule |
| `openEquipmentSelectModal()` | 설비 선택 모달 열기 | GET /api/equipment (기존) |
| `saveEquipmentSelection()` | 설비 할당 저장 | PUT /api/production/plan/:id |
| `applyScheduleChanges()` | 변경사항 확인 후 적용 | - (프론트엔드 상태 관리) |

## 부록 C: 수주 데이터 테이블 컬럼 상세

### 그룹 행 (품목별 집계)

| # | 컬럼 | 데이터 소스 | 정렬 |
|---|------|-------------|------|
| 1 | 체크박스 | - | center |
| 2 | 토글 (펼치기/접기) | - | center |
| 3 | 품목코드 | `sales_order_mng.part_code` (GROUP BY) | left |
| 4 | 품목명 | `sales_order_mng.part_name` | left |
| 5 | 총수주량 | `SUM(order_qty)` | right |
| 6 | 출고량 | `SUM(ship_qty)` | right |
| 7 | 잔량 | `SUM(balance_qty)` | right |
| 8 | 현재고 | `inventory_stock.current_qty` (JOIN) | right |
| 9 | 안전재고 | `inventory_stock.safety_qty` (JOIN) | right |
| 10 | 출하계획량 | `SUM(plan_ship_qty)` | right |
| 11 | 기생산계획량 | `production_plan_mng` 조회 (JOIN) | right |
| 12 | 생산진행 | `production_plan_mng` (status='in_progress') 조회 | right |
| 13 | 필요생산계획 | 계산값 (잔량+안전재고-현재고-기생산계획량-생산진행) | right, 빨간색 강조 |

### 상세 행 (개별 수주)

| # | 컬럼 | 데이터 소스 |
|---|------|-------------|
| 1 | (빈 칸) | - |
| 2 | (빈 칸) | - |
| 3-4 | 수주번호, 거래처, 상태배지 | `order_no`, `partner_id` → partner_name, `status` |
| 5 | 수주량 | `order_qty` |
| 6 | 출고량 | `ship_qty` |
| 7 | 잔량 | `balance_qty` |
| 8-13 | 납기일 (colspan) | `due_date` |

## 부록 D: 타임라인 스케줄러 필드 매핑

### 완제품 타임라인

| 타임라인 필드 | production_plan_mng 컬럼 | 비고 |
|--------------|--------------------------|------|
| `id` | `id` | PK |
| `resourceId` | `item_code` | 품목 기준 리소스 (설비 기준이 아님) |
| `title` | `item_name` + `plan_qty` | 표시 텍스트 |
| `startDate` | `start_date` | 시작일 |
| `endDate` | `end_date` | 종료일 |
| `status` | `status` | planned/in_progress/completed/work-order |
| `progress` | `progress_rate` | 진행률(%) |

### 반제품 타임라인

동일 구조, 단 `product_type = '반제품'` 필터 적용

### statusColors 매핑

| 상태 | 색상 | 의미 |
|------|------|------|
| `planned` | `#3b82f6` (파란색) | 계획됨 |
| `work-order` | `#f59e0b` (노란색) | 작업지시 |
| `in_progress` | `#10b981` (초록색) | 진행중 |
| `completed` | `#6b7280` (회색, 반투명) | 완료 |
| `delayed` | `#ef4444` (빨간색) | 지연 |
