# 생산계획관리 화면 구현 설계서 > **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` (빨간색) | 지연 |