34 KiB
34 KiB
생산계획관리 화면 구현 설계서
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
목적: 수주 데이터를 품목별로 그룹핑하여 반환. 그룹 헤더에 집계값(총수주량, 출고량, 잔량, 현재고, 안전재고, 기생산계획량 등) 포함.
응답 구조:
{
"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 로직 (핵심):
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
응답 구조:
{
"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 로직:
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:
{
"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": "완제품"
}
}
비즈니스 로직:
- 각 품목의 필요생산계획량, 납기일, 일일생산능력을 기반으로 생산일수 계산
생산일수 = ceil(필요생산계획량 / 일일생산능력)시작일 = 납기일 - 생산일수 - 안전리드타임- 시작일이 오늘 이전이면 오늘로 조정
recalculate_unstarted = true면 기존 진행중/작업지시/완료 스케줄은 유지, 미진행(planned)만 제거 후 재계산- 결과를
production_plan_mng에 INSERT - 변경사항 요약(신규/유지/삭제 건수) 반환
응답 구조:
{
"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:
{
"schedule_ids": [101, 102, 103],
"product_type": "완제품"
}
비즈니스 로직:
- 선택된 스케줄이 모두 동일 품목인지 검증
- 완제품/반제품이 섞여있지 않은지 검증
- 수량 합산, 가장 빠른 시작일/납기일, 가장 늦은 종료일 적용
- 원본 스케줄 DELETE, 병합된 스케줄 INSERT
- 수주 정보(order_no)는 병합 (중복 제거)
4.5 반제품 계획 자동 생성 API
POST /api/production/generate-semi-schedule
요청 body:
{
"plan_ids": [101, 102],
"options": {
"consider_stock": true,
"keep_in_progress": false,
"exclude_used": true
}
}
비즈니스 로직:
- 선택된 완제품 계획의 품목코드로 BOM 조회
bom테이블에서 해당 품목의item_id→bom_detail에서 하위 반제품(child_item_id) 조회- 각 반제품의 필요 수량 =
완제품 계획수량 x BOM 소요량(quantity) consider_stock = true면 현재고/안전재고 감안하여 순 필요량 계산exclude_used = true면 이미 투입된 반제품 수량 차감- 모품목 생산 시작일 고려하여 반제품 납기일 설정 (시작일 - 반제품 리드타임)
production_plan_mng에product_type = '반제품',parent_plan_id설정하여 INSERT
4.6 스케줄 상세 저장/수정 API
PUT /api/production/plan/:id
요청 body:
{
"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:
{
"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" }
]
}
비즈니스 로직:
- 분할 수량 합산이 원본 수량과 일치하는지 검증
- 원본 스케줄 DELETE
- 분할된 각 조각을 신규 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에 적용
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 (빨간색) |
지연 |