Files
wace_rps/docs/manual/RPS_시스템_사용자_매뉴얼.html
T
chpark 68b68694e2
Build and Push Images / build-and-push (push) Has been cancelled
docs(manual): 실 캡처 10장 임베딩 + 자재관리·구매관리 신규 기능 반영
캡처 이미지 (Playwright 자동 캡처, docs/manual/screenshots/):
 - 01 로그인 → 02 메인 진입
 - 03 견적관리 그리드 → 04 행 선택 + 결재상신 활성화
 - 05 외부 커넥션 관리 (DB 연결 탭)
 - 08 화면 관리 목록 → 09 화면 상세 (디자이너 패널)
 - 10 사용자 대시보드
 - 11 반제품검사 → 12 공정검사
 + _capture.mjs / _capture_retry.mjs (재실행 스크립트, 1600x900 viewport)

매뉴얼 본문 업데이트:
 - §1 시스템 개요: 로그인 절차 + 그림 1·2 추가
 - §3.1 영업·수주: 견적관리/행 선택 그림 3·4
 - §3.3 구매·입고: 발주서 양식(일반/외주/영문) + 메일·PDF + 직인,
   입고관리 3뷰 + 매입마감 신규 항목 보강
 - §3.4 자재관리 신규 섹션 (자재리스트 + 불출의뢰서,
   StockRegister/MaterialMove/InventoryHistory/IssueDispatch 다이얼로그)
 - §4.1 외부 커넥션 그림 5, §5 대시보드 그림 6,
   §6 화면관리 그림 7·8, §6.3·6.4 품질관리 그림 9·10
 - §2 카드 자재관리(NEW)·구매관리(메일·PDF·3양식) 보강
 - 좌측 목차에 자재관리 항목 추가, 3.5→3.5 생산 / 3.6 품질 / 3.7 매출·CS 재번호

figure.shot CSS 추가 (1100px 폭, 부드러운 그림자).
2026-05-20 10:56:46 +09:00

1104 lines
69 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>RPS PLM 시스템 사용자 매뉴얼</title>
<style>
:root {
--bg: #f7f9fc;
--card: #ffffff;
--line: #e2e8f0;
--text: #0f172a;
--muted: #64748b;
--primary: #2563eb;
--primary-soft: #dbeafe;
--success: #059669;
--success-soft: #d1fae5;
--warn: #d97706;
--warn-soft: #fef3c7;
--danger: #dc2626;
--danger-soft: #fee2e2;
--code-bg: #0f172a;
--code-fg: #e2e8f0;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
background: var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Apple SD Gothic Neo", "Noto Sans KR", sans-serif;
font-size: 15px;
line-height: 1.65;
}
.layout { display: grid; grid-template-columns: 260px 1fr; min-height: 100vh; }
nav.toc {
position: sticky; top: 0; align-self: start;
height: 100vh; overflow-y: auto;
background: #fff; border-right: 1px solid var(--line); padding: 24px 16px;
}
nav.toc h2 { font-size: 14px; margin: 0 0 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
nav.toc ol { list-style: none; padding: 0; margin: 0; counter-reset: c; }
nav.toc li { counter-increment: c; margin-bottom: 4px; }
nav.toc li::before { content: counter(c) ". "; color: var(--muted); margin-right: 4px; font-variant-numeric: tabular-nums; }
nav.toc a { color: var(--text); text-decoration: none; font-size: 14px; }
nav.toc a:hover { color: var(--primary); }
nav.toc ul { list-style: none; padding: 6px 0 0 16px; margin: 0; border-left: 2px solid var(--line); }
nav.toc ul li { counter-increment: none; margin-bottom: 2px; }
nav.toc ul li::before { content: ""; }
nav.toc ul a { font-size: 13px; color: var(--muted); }
main { padding: 40px 56px 80px; max-width: 1100px; }
header.cover {
background: linear-gradient(135deg, #1e293b 0%, #2563eb 100%);
color: white; border-radius: 12px; padding: 36px 40px; margin-bottom: 36px;
}
header.cover h1 { margin: 0 0 8px; font-size: 32px; }
header.cover p { margin: 0; opacity: .85; }
header.cover .meta { margin-top: 16px; display: flex; gap: 16px; font-size: 13px; opacity: .8; }
h2.section { font-size: 24px; margin: 48px 0 16px; padding-bottom: 8px; border-bottom: 2px solid var(--primary); }
h3 { font-size: 18px; margin: 28px 0 10px; }
h4 { font-size: 15px; margin: 18px 0 8px; color: var(--muted); text-transform: uppercase; letter-spacing: .03em; }
p, li { line-height: 1.75; }
.card { background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 20px 24px; margin: 16px 0; }
.card.compact { padding: 12px 16px; }
.badge { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 12px; font-weight: 500; vertical-align: middle; }
.badge.primary { background: var(--primary-soft); color: var(--primary); }
.badge.success { background: var(--success-soft); color: var(--success); }
.badge.warn { background: var(--warn-soft); color: var(--warn); }
.badge.danger { background: var(--danger-soft); color: var(--danger); }
.grid { display: grid; gap: 16px; }
.grid.cols-2 { grid-template-columns: repeat(2, 1fr); }
.grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
table { width: 100%; border-collapse: collapse; margin: 12px 0; font-size: 14px; }
th, td { border: 1px solid var(--line); padding: 8px 12px; text-align: left; vertical-align: top; }
th { background: #f1f5f9; font-weight: 600; }
td code { background: #f1f5f9; padding: 1px 5px; border-radius: 3px; font-size: 13px; }
code, pre { font-family: "SF Mono", Menlo, Consolas, monospace; }
pre { background: var(--code-bg); color: var(--code-fg); padding: 16px 20px; border-radius: 8px; overflow-x: auto; font-size: 13px; line-height: 1.55; }
.callout { border-left: 4px solid var(--primary); background: #eff6ff; padding: 12px 16px; margin: 16px 0; border-radius: 6px; }
.callout.warn { border-color: var(--warn); background: #fefce8; }
.callout.success { border-color: var(--success); background: #ecfdf5; }
.callout.danger { border-color: var(--danger); background: #fef2f2; }
.callout strong { display: block; margin-bottom: 4px; }
.flow { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; padding: 16px; background: #f8fafc; border-radius: 8px; border: 1px solid var(--line); }
.flow .step { background: white; border: 1px solid var(--primary); color: var(--primary); padding: 6px 12px; border-radius: 6px; font-size: 13px; font-weight: 500; }
.flow .arrow { color: var(--muted); }
.kv { display: grid; grid-template-columns: 140px 1fr; gap: 6px 12px; font-size: 14px; }
.kv dt { color: var(--muted); }
.kv dd { margin: 0; }
.stat-card { background: #fff; border: 1px solid var(--line); border-radius: 8px; padding: 16px; text-align: center; }
.stat-card .n { font-size: 28px; font-weight: 700; color: var(--primary); }
.stat-card .label { font-size: 12px; color: var(--muted); margin-top: 4px; }
details { background: #fff; border: 1px solid var(--line); border-radius: 8px; padding: 12px 16px; margin: 8px 0; }
details summary { cursor: pointer; font-weight: 500; }
details[open] summary { margin-bottom: 10px; }
figure.shot { margin: 16px 0; padding: 0; }
figure.shot img { width: 100%; max-width: 1100px; height: auto; display: block; border: 1px solid var(--line); border-radius: 8px; box-shadow: 0 4px 12px rgba(15, 23, 42, 0.08); }
figure.shot figcaption { margin-top: 8px; font-size: 13px; color: var(--muted); text-align: center; }
figure.shot figcaption strong { color: var(--text); }
@media print {
.layout { grid-template-columns: 1fr; }
nav.toc { display: none; }
main { padding: 0; max-width: 100%; }
header.cover { break-after: page; }
h2.section { break-after: avoid; }
}
</style>
</head>
<body>
<div class="layout">
<nav class="toc">
<h2>목차</h2>
<ol>
<li><a href="#overview">시스템 개요</a></li>
<li><a href="#menu">전체 메뉴 구조</a></li>
<li><a href="#business">업무 프로세스 흐름</a>
<ul>
<li><a href="#proc-sales">영업·수주</a></li>
<li><a href="#proc-dev">개발·설계</a></li>
<li><a href="#proc-purchase">구매·입고</a></li>
<li><a href="#proc-material">자재관리</a></li>
<li><a href="#proc-production">생산</a></li>
<li><a href="#proc-quality">품질</a></li>
<li><a href="#proc-revenue">매출·CS</a></li>
</ul>
</li>
<li><a href="#amaranth">Amaranth(ERP) 연계</a>
<ul>
<li><a href="#amaranth-conn">외부 커넥션 관리</a></li>
<li><a href="#amaranth-restapi">REST API 신규 등록 가이드</a></li>
<li><a href="#amaranth-approval">결재상신 워크플로</a></li>
<li><a href="#amaranth-troubleshoot">문제 해결</a></li>
</ul>
</li>
<li><a href="#dashboard">대시보드 관리</a></li>
<li><a href="#screen-designer">화면 관리 — 사용자 정의 화면</a>
<ul>
<li><a href="#sd-overview">화면 디자이너 개요</a></li>
<li><a href="#sd-create">신규 화면 만들기</a></li>
<li><a href="#sd-table">테이블 매핑</a></li>
<li><a href="#sd-button">버튼·플로우 추가</a></li>
<li><a href="#sd-dataflow">데이터 흐름 설정</a></li>
</ul>
</li>
<li><a href="#quality">품질관리 메뉴 가이드</a></li>
<li><a href="#common">공통 UI 사용법</a></li>
<li><a href="#faq">자주 묻는 질문</a></li>
</ol>
</nav>
<main>
<header class="cover">
<h1>RPS PLM 시스템 사용자 매뉴얼</h1>
<p>영업·개발·구매·생산·품질·CS·ECR 전 업무 + Amaranth(ERP) 결재 연계</p>
<div class="meta">
<span>버전: v1.0</span>
<span>대상: 일반 사용자 / 관리자</span>
<span>최종 갱신: 2026-05-15</span>
</div>
</header>
<!-- ======================================================== -->
<h2 class="section" id="overview">1. 시스템 개요</h2>
<p>
RPS PLM 은 영업(견적·수주)부터 개발(E-BOM), 구매(발주·입고), 생산(M-BOM·실적),
품질(수입·공정·반제품·CS·ECR), 매출까지 전사 업무를 단일 시스템으로 처리합니다.
회계·결재·인사 등 ERP 영역은 Wehago/Amaranth 와 REST API 로 연계됩니다.
</p>
<div class="grid cols-4">
<div class="stat-card"><div class="n">11</div><div class="label">주요 모듈</div></div>
<div class="stat-card"><div class="n">40+</div><div class="label">화면 메뉴</div></div>
<div class="stat-card"><div class="n">7</div><div class="label">Amaranth 커넥션</div></div>
<div class="stat-card"><div class="n">3</div><div class="label">모니터링 보드</div></div>
</div>
<h3>시스템 구성</h3>
<table>
<tr><th width="180">레이어</th><th>기술</th><th>역할</th></tr>
<tr><td>프론트엔드</td><td>Next.js 15 + React + Tailwind</td><td>탭 기반 SPA, DataGrid, 그리드 차트/엑셀</td></tr>
<tr><td>백엔드</td><td>Node.js + Express + TypeScript</td><td>REST API, JWT 인증, 외부 ERP 프록시</td></tr>
<tr><td>데이터베이스</td><td>PostgreSQL 15</td><td>업무 데이터 + 메뉴/권한 메타</td></tr>
<tr><td>외부 ERP</td><td>Wehago / Amaranth</td><td>전자결재, 인사, 거래처, 계정과목 마스터</td></tr>
<tr><td>인증</td><td>JWT + Refresh Token</td><td>로그인 세션, 401 자동 갱신</td></tr>
</table>
<h3>로그인</h3>
<p>브라우저로 접속하면 가장 먼저 로그인 화면이 표시됩니다.</p>
<figure class="shot">
<img src="screenshots/01_login.png" alt="로그인 화면">
<figcaption><strong>그림 1.</strong> 로그인 화면 — 사용자 ID 와 비밀번호 입력.</figcaption>
</figure>
<ol>
<li>사용자 ID 와 비밀번호를 입력하고 [로그인] 클릭.</li>
<li>인증 성공 시 사용자에게 할당된 첫 메뉴(예: 영업관리 &gt; 견적관리)로 자동 이동합니다.</li>
<li>잘못된 자격증명이면 "패스워드가 일치하지 않습니다." 메시지가 표시됩니다.</li>
</ol>
<figure class="shot">
<img src="screenshots/02_main.png" alt="로그인 직후 메인 화면">
<figcaption><strong>그림 2.</strong> 로그인 직후 메인 화면 — 좌측 모듈 메뉴 + 자동 진입된 첫 화면.</figcaption>
</figure>
<!-- ======================================================== -->
<h2 class="section" id="menu">2. 전체 메뉴 구조</h2>
<div class="grid cols-2">
<div class="card compact">
<h4>📋 영업관리</h4>
<ul>
<li>견적관리 — 견적 작성·승인·메일발송</li>
<li>주문서관리 — 수주 등록·확정</li>
<li>판매관리 — 출하 처리</li>
<li>매출관리 — 세금계산서 발행</li>
</ul>
</div>
<div class="card compact">
<h4>📁 프로젝트관리</h4>
<ul>
<li>제품구분_WBS관리 — 제품별 WBS 템플릿</li>
<li>진행관리 — 프로젝트 단계별 진척률</li>
</ul>
</div>
<div class="card compact">
<h4>🛠 개발관리</h4>
<ul>
<li>PART 등록·조회</li>
<li>E-BOM 등록·조회</li>
<li>설계변경 리스트</li>
</ul>
</div>
<div class="card compact">
<h4>🛒 구매관리</h4>
<ul>
<li>M-BOM 관리</li>
<li>구매리스트 / 견적요청서 / 품의서</li>
<li>발주서관리 <span class="badge success">메일·PDF·3양식</span></li>
<li>입고관리 (3가지 뷰 + 매입마감)</li>
<li>프로젝트별 발주/입고 현황</li>
</ul>
</div>
<div class="card compact">
<h4>📦 자재관리 <span class="badge success">NEW</span></h4>
<ul>
<li>자재리스트 — 재고등록/이동/이력 다이얼로그</li>
<li>불출의뢰서 — 작성·불출처리</li>
</ul>
</div>
<div class="card compact">
<h4>🏭 생산관리</h4>
<ul>
<li>M-BOM 관리</li>
<li>생산계획&실적 (일반/장비)</li>
<li>반제품·원자재 소요량</li>
</ul>
</div>
<div class="card compact">
<h4>🔬 품질관리</h4>
<ul>
<li>수입검사 요청·관리</li>
<li>공정검사 관리</li>
<li>반제품검사 관리</li>
</ul>
</div>
<div class="card compact">
<h4>💬 고객CS관리</h4>
<ul>
<li>고객CS관리 — 민원 접수·조치</li>
</ul>
</div>
<div class="card compact">
<h4>📝 ECR관리</h4>
<ul>
<li>ECR관리 — 설계변경요청서</li>
</ul>
</div>
<div class="card compact">
<h4>📊 모니터링</h4>
<ul>
<li>품질 모니터링</li>
<li>생산 모니터링</li>
<li>설비 모니터링</li>
</ul>
</div>
</div>
<!-- ======================================================== -->
<h2 class="section" id="business">3. 업무 프로세스 흐름</h2>
<p>
각 모듈의 작업 결과는 다음 모듈의 입력이 되어 자동으로 흘러갑니다. 한 화면에서 다른 화면으로
<strong>물려 있는</strong> 점이 RPS PLM의 핵심입니다.
</p>
<div class="flow" style="font-size: 13px;">
<div class="step">견적 작성</div>
<span class="arrow"></span>
<div class="step">결재상신 (Amaranth)</div>
<span class="arrow"></span>
<div class="step">수주 확정</div>
<span class="arrow"></span>
<div class="step">M-BOM 생성</div>
<span class="arrow"></span>
<div class="step">발주서</div>
<span class="arrow"></span>
<div class="step">입고·수입검사</div>
<span class="arrow"></span>
<div class="step">생산실적</div>
<span class="arrow"></span>
<div class="step">공정·반제품 검사</div>
<span class="arrow"></span>
<div class="step">출하</div>
<span class="arrow"></span>
<div class="step">매출·세금계산서 (Amaranth)</div>
</div>
<!-- 3.1 영업·수주 -->
<h3 id="proc-sales">3.1 영업·수주</h3>
<div class="card">
<h4>핵심 시나리오</h4>
<ol>
<li><strong>견적요청 등록</strong> — 영업관리 &gt; 견적관리 [+ 견적요청등록]. 주문유형/고객사/유무상/접수일/품목 입력.</li>
<li><strong>견적작성</strong> — 행 선택 후 [견적작성]. 일반/장비 템플릿 중 선택 → 신규 차수 생성.</li>
<li><strong>결재상신</strong> — 견적서가 작성된 행에서 [결재상신] → Amaranth 팝업 → 결재완료까지 대기.</li>
<li><strong>수주 확정</strong> — 결재완료 시 자동으로 contract_result 가 변경되어 주문서관리에 노출됨.</li>
</ol>
<p class="callout warn">
<strong>⚠ 가드 조건</strong>
① 견적서 미작성 → 결재상신 불가 ② 결재중·결재완료 건은 재상신 차단
③ 수주확정(0000964/0000968) 행은 품목 추가/삭제 불가
</p>
</div>
<figure class="shot">
<img src="screenshots/03_estimate_list.png" alt="견적관리 그리드">
<figcaption><strong>그림 3.</strong> 견적관리 그리드 — 12 컬럼 (영업번호 / 주문유형 / 접수일 / 고객사 / 품명 / 견적수량 / 유무상 / 공급가액 등).
상단 액션: [삭제] [+ 견적요청등록] [✎ 견적작성] [✈ 결재상신] [📧 메일발송] [초기화] [검색].</figcaption>
</figure>
<figure class="shot">
<img src="screenshots/04_estimate_selected.png" alt="견적 행 선택 시 상태">
<figcaption><strong>그림 4.</strong> 견적 행을 체크하면 [✈ 결재상신] 버튼이 활성화됩니다. 행 더블클릭 시 수정 모달이 열립니다.</figcaption>
</figure>
<!-- 3.2 개발·설계 -->
<h3 id="proc-dev">3.2 개발·설계</h3>
<div class="card">
<p>
제품 양산 전 단계의 부품(PART)과 설계 BOM(E-BOM)을 관리합니다.
설계변경(ECR)이 발생하면 E-BOM 의 차수가 올라가고, 변경 이력은 [설계변경 리스트]에서 추적됩니다.
</p>
<div class="flow">
<div class="step">PART 등록</div>
<span class="arrow"></span>
<div class="step">E-BOM 트리 구성</div>
<span class="arrow"></span>
<div class="step">ECR 발의</div>
<span class="arrow"></span>
<div class="step">E-BOM 차수 업데이트</div>
</div>
</div>
<!-- 3.3 구매·입고 -->
<h3 id="proc-purchase">3.3 구매·입고</h3>
<div class="card">
<p>
수주 → M-BOM 분해 → 구매리스트 → 견적요청서 → 품의서(승인) → 발주서 → 입고 → 수입검사 순서로 진행됩니다.
Amaranth 와는 거래처/계정과목 마스터를 실시간 동기화합니다.
</p>
<table>
<tr><th>단계</th><th>메뉴</th><th>핵심 동작</th></tr>
<tr><td>1. 구매대상 추출</td><td>구매리스트관리</td><td>수주 M-BOM 에서 외주/구매 품목만 필터링</td></tr>
<tr><td>2. 견적요청</td><td>견적요청서관리</td><td>거래처별 RFQ 발송 (Amaranth 거래처 조회 연동)</td></tr>
<tr><td>3. 품의 → 결재</td><td>품의서관리</td><td>구매 품의서 작성 후 Amaranth 결재 (formId=품의서)</td></tr>
<tr><td>4. 발주</td><td>발주서관리</td><td>결재완료된 품의서를 발주로 전환</td></tr>
<tr><td>5. 입고</td><td>입고관리 (3가지 뷰)</td><td>전체/품목별/입고일별 입고 확정</td></tr>
<tr><td>6. 수입검사</td><td>품질관리 &gt; 수입검사</td><td>입고된 자재의 검사 진행 + 합/불 판정</td></tr>
</table>
</div>
<h4>발주서관리 — 양식 선택 / 메일 발송 / PDF (신규)</h4>
<div class="card">
<p>발주서관리에서 행을 선택하면 다음 액션이 가능합니다:</p>
<ul>
<li><strong>양식 선택</strong> — [발주서 작성] 클릭 시 양식 선택 모달이 뜹니다.
<ul>
<li><span class="badge primary">일반 양식</span> — 국내 표준 한글 양식</li>
<li><span class="badge primary">외주 양식</span> — 외주 가공 협력사용 양식</li>
<li><span class="badge primary">영문 양식</span> — 해외 거래처용 영문 발주서</li>
</ul>
</li>
<li><strong>로고 / 직인</strong> — 폼 상단에 RPS 로고, 하단에 자동으로 회사 직인 이미지가 합쳐집니다 (<code>frontend/public/images/rps-stamp-seal.png</code>).</li>
<li><strong>📄 PDF 다운로드</strong> — 양식 미리보기에서 [PDF 다운로드] 클릭 → 발주번호로 파일 저장.</li>
<li><strong>📧 메일 발송</strong> — [메일발송] 버튼 → 거래처 메일 자동 첨부 + PDF 동봉. SMTP_SEND_SWITCH 활성 시 실 발송.</li>
<li><strong>읽기전용 모드</strong> — 결재 완료된 발주서는 자동으로 읽기전용으로 잠금.</li>
</ul>
</div>
<h4>입고관리 — 3가지 뷰 + 입고등록 / 매입마감 (신규)</h4>
<div class="card">
<ul>
<li><code>/purchase/inbound</code><strong>전체 입고 리스트</strong>. 상단 [+ 입고등록] 으로 발주서 행 선택 → 수량 / 입고일 / 검사여부 입력.</li>
<li><code>/purchase/inbound-by-item</code><strong>품목별 집계</strong>. 같은 part_no 의 분할 입고를 한 행에 합산.</li>
<li><code>/purchase/inbound-by-date</code><strong>입고일별 마감</strong>. 일자 단위 [마감정보입력] → 매입 마감 처리 + Amaranth 계정과목 연계.</li>
</ul>
</div>
<!-- 3.4 자재관리 (신규) -->
<h3 id="proc-material">3.4 자재관리 (신규)</h3>
<div class="card">
<p>입고 후 창고로 들어온 자재를 관리합니다. 두 메뉴 모두 풀-CRUD + 인라인 액션을 지원합니다.</p>
<table>
<tr><th width="180">메뉴</th><th>경로</th><th>핵심 다이얼로그</th></tr>
<tr>
<td>자재리스트</td>
<td><code>/material/list</code></td>
<td>
<span class="badge primary">재고등록</span> StockRegisterDialog ·
<span class="badge primary">자재이동</span> MaterialMoveDialog ·
<span class="badge primary">재고이력</span> InventoryHistoryDialog
</td>
</tr>
<tr>
<td>불출의뢰서</td>
<td><code>/material/issue-request</code></td>
<td>
<span class="badge primary">의뢰서 작성</span> IssueRequestCreateDialog ·
<span class="badge success">불출 처리</span> IssueDispatchDialog
</td>
</tr>
</table>
<p class="callout success">
<strong>✓ 흐름</strong>
① 입고 확정 → 자재리스트 자동 추가
② 생산팀 [불출의뢰서] 작성 → 부서/창고/품목/수량 지정
③ 자재팀 [불출 처리] → 실제 출고 + 재고 차감 + 이력 기록
</p>
</div>
<!-- 3.5 생산 -->
<h3 id="proc-production">3.5 생산</h3>
<div class="card">
<p>
M-BOM 으로 생산계획을 수립하고 작업지시번호 단위로 실적을 입력합니다.
장비 생산은 별도 메뉴(생산계획&실적관리(장비))에서 처리합니다.
</p>
<ul>
<li><strong>M-BOM 관리</strong> — 구조 트리 + 본 편집(품목 추가/삭제) + 변경이력</li>
<li><strong>반제품/원자재 소요량</strong> — 계획 대비 필요 자재 자동 계산</li>
<li><strong>생산실적</strong> — 작업지시 단위 양품/불량 등록 (반제품검사와 inspection_group_id 로 연계)</li>
</ul>
</div>
<!-- 3.6 품질 -->
<h3 id="proc-quality">3.6 품질</h3>
<div class="card">
<p>품질관리는 4단계로 흐릅니다:</p>
<div class="flow">
<div class="step">수입검사 요청 (발주서 단위)</div>
<span class="arrow"></span>
<div class="step">수입검사 진행 (검사자 판정)</div>
<span class="arrow"></span>
<div class="step">공정검사 (작업 중 SPC)</div>
<span class="arrow"></span>
<div class="step">반제품검사 (양품/불량 분류)</div>
</div>
<p style="margin-top: 12px;">불량 발생 → 책임부서로 통보 → 수정완료 시 재생수량 가산 → 최종양품수량 자동 산정. 자세한 내용은 <a href="#quality">6. 품질관리 메뉴 가이드</a>.</p>
</div>
<!-- 3.7 매출·CS -->
<h3 id="proc-revenue">3.7 매출·CS</h3>
<div class="card">
<p>출하 완료 → 매출 인식 → Amaranth 로 세금계산서 발행. 고객 클레임은 CS 메뉴로 접수되어 분석/조치 후 종결됩니다.</p>
<div class="flow">
<div class="step">판매관리 (출하)</div>
<span class="arrow"></span>
<div class="step">매출관리</div>
<span class="arrow"></span>
<div class="step">세금계산서 발행 (Amaranth)</div>
<span class="arrow"></span>
<div class="step">CS 접수 (필요 시)</div>
<span class="arrow"></span>
<div class="step">ECR (설계 반영 필요 시)</div>
</div>
</div>
<!-- ======================================================== -->
<h2 class="section" id="amaranth">4. Amaranth(ERP) 연계</h2>
<p>
RPS PLM 은 결재·인사·거래처 등 ERP 영역을 Wehago/Amaranth 시스템과
REST API 로 통합합니다. 모든 외부 호출 정보는 [관리자 &gt; 자동화 관리 &gt; 외부 커넥션 관리] 에 등록되어 있고,
자격 증명(accessToken/hashKey)은 AES-256-GCM 으로 암호화 저장됩니다.
</p>
<!-- 4.1 외부 커넥션 관리 -->
<h3 id="amaranth-conn">4.1 외부 커넥션 관리 — 한눈에 보기</h3>
<div class="card">
<p>위치: <code>관리자 &gt; 자동화 관리 &gt; 외부 커넥션 관리</code></p>
<p>외부 시스템(ERP / 마스터 / 공공 API) 과의 모든 연결을 한 곳에서 관리합니다. 두 종류의 탭이 있습니다:</p>
<figure class="shot">
<img src="screenshots/05_excon_db_tab.png" alt="외부 커넥션 관리 - 데이터베이스 연결 탭">
<figcaption><strong>그림 5.</strong> 외부 커넥션 관리 — [데이터베이스 연결] / [REST API 연결] 탭 전환. 각 행의 [테스트] 버튼으로 즉시 검증.</figcaption>
</figure>
<pre style="white-space: pre; line-height: 1.4;">┌────────────────────────────────────────────────────────────────────────────┐
│ 외부 커넥션 관리 외부 데이터베이스 및 REST API 연결 정보를 관리합니다. │
├────────────────────────────────────────────────────────────────────────────┤
│ [🗄 데이터베이스 연결] [🌐 REST API 연결] ← 탭 전환 │
│ │
│ 🔍 [연결명 또는 URL로 검색...] [전체 ▾] [전체 ▾] 총 7건 [+ 새 연결] │
├──────────────────────┬─────────────┬──────────────────────┬────────────────┤
│ 연결명 │ 회사 │ 기본 URL │ 상태 테스트 │
├──────────────────────┼─────────────┼──────────────────────┼────────────────┤
│ Amaranth - 계정과목 │ COMPANY_16 │ https://erp.rps... │ 활성 [테스트] │
│ Amaranth - 부서 │ COMPANY_16 │ https://erp.rps... │ 활성 [테스트] │
│ Amaranth - 거래처 │ COMPANY_16 │ https://erp.rps... │ 활성 [테스트] │
│ Amaranth - 결재 │ COMPANY_16 │ https://erp.rps... │ 활성 [테스트] │
└──────────────────────┴─────────────┴──────────────────────┴────────────────┘</pre>
<p>각 행의 [테스트] 버튼을 누르면 실제 호출이 발생하고 <span class="badge success">성공</span> / <span class="badge danger">실패</span> 로 즉시 결과가 표시됩니다.</p>
</div>
<h4>등록된 Amaranth 커넥션 (운영 기준 7종)</h4>
<table>
<tr><th width="200">연결명</th><th>엔드포인트 path</th><th>용도</th></tr>
<tr><td>Amaranth - 결재</td><td><code>(SSO/결재 호출 시 path 지정)</code></td><td>전자결재 SSO URL 발급, 결재함 조회</td></tr>
<tr><td>Amaranth - Wehago 사용자</td><td><code>/apiproxy/api99u01A11</code></td><td>사용자 인증 토큰 발급</td></tr>
<tr><td>Amaranth - 거래처</td><td><code>/apiproxy/api16S11</code></td><td>거래처 마스터 동기화</td></tr>
<tr><td>Amaranth - 부서</td><td><code>/apiproxy/api16S10</code></td><td>부서 마스터 동기화</td></tr>
<tr><td>Amaranth - 사원</td><td><code>/apiproxy/api16S05</code></td><td>사원 마스터 동기화</td></tr>
<tr><td>Amaranth - 창고</td><td><code>/apiproxy/api20A00S00801</code></td><td>창고 마스터 동기화</td></tr>
<tr><td>Amaranth - 계정과목</td><td><code>/apiproxy/api11A02</code></td><td>계정과목 마스터 동기화</td></tr>
</table>
<!-- 4.2 REST API 신규 등록 가이드 -->
<h3 id="amaranth-restapi">4.2 REST API 신규 등록 가이드 (URL + 키만 입력하면 끝)</h3>
<p>
외부 시스템과 새로 연동할 때 코드 수정 없이 <strong>UI에서 URL/인증 키만 입력</strong>하면 RPS PLM 어디서든 호출할 수 있습니다.
Amaranth 외에 ITS 교통정보, 기상특보, 환율, 자체 백오피스 API 등도 동일하게 등록 가능합니다.
</p>
<h4>STEP 1 · [+ 새 연결] 버튼 클릭 → 등록 모달 오픈</h4>
<pre style="white-space: pre; line-height: 1.4;">┌──────────────────────────────────────────────────────┐
│ REST API 연결 등록 [X] │
├──────────────────────────────────────────────────────┤
│ ▶ 기본 정보 │
│ 연결명 * [Amaranth - 사원_____________] │
│ 설명 [api16S05 — 사원 마스터________] │
│ 회사 코드 [COMPANY_16 ▾] │
│ │
│ ▶ 엔드포인트 │
│ 기본 URL * [https://erp.rps-korea.com___] │
│ 엔드포인트 path [/apiproxy/api16S05_________] │
│ HTTP 메서드 [POST ▾] ⏱ Timeout [5000] ms │
│ │
│ ▶ 인증 정보 │
│ 인증 타입 [Wehago / Amaranth ▾] │
│ callerName [API_gcmsAmaranth40578________] │
│ accessToken [••••••••••••••••••••••••••••]🔒 │
│ hashKey [••••••••••••••••••••••••••••]🔒 │
│ groupSeq [gcmsAmaranth40578____________] │
│ │
│ ▶ 기본 헤더 / 기본 요청 본문 (JSON, 선택) │
│ │
│ [테스트] [취소] [저장] │
└──────────────────────────────────────────────────────┘</pre>
<h4>STEP 2 · 필드 작성 가이드</h4>
<table>
<tr><th width="160">필드</th><th>설명</th><th>예시</th></tr>
<tr><td>연결명 *</td><td>유일 식별자. 백엔드가 이 이름으로 조회 (<code>amaranthApprovalClient</code>).</td><td><code>Amaranth - 결재</code></td></tr>
<tr><td>설명</td><td>운영자 참고용. 자유 입력.</td><td>"전자결재 통합 — 인증토큰/SSO/결재함"</td></tr>
<tr><td>기본 URL *</td><td>스킴+호스트까지만. path는 별도. 끝의 / 는 자동 제거.</td><td><code>https://erp.rps-korea.com</code></td></tr>
<tr><td>엔드포인트 path</td><td>고정 path. 호출 시 함수에서 override 가능.</td><td><code>/apiproxy/api11A02</code></td></tr>
<tr><td>HTTP 메서드</td><td>GET / POST / PUT / DELETE.</td><td><code>POST</code></td></tr>
<tr><td>인증 타입</td><td>API_KEY / BEARER / BASIC / OAUTH2 / WEHAGO_AMARANTH 중 선택.</td><td><code>Wehago/Amaranth</code></td></tr>
<tr><td>callerName</td><td>Wehago 발급 API 이름. <strong>평문 저장</strong>.</td><td><code>API_gcmsAmaranth40578</code></td></tr>
<tr><td>accessToken</td><td>Wehago 발급 토큰. <strong>저장 시 AES-256-GCM 자동 암호화</strong>.</td><td><code>MN5Kz...</code> (저장 후 마스킹)</td></tr>
<tr><td>hashKey</td><td>HMAC 서명용 키. <strong>자동 암호화</strong>.</td><td><code>22519103...</code></td></tr>
<tr><td>groupSeq</td><td>Wehago 그룹 시퀀스. 평문.</td><td><code>gcmsAmaranth40578</code></td></tr>
</table>
<p class="callout">
<strong>🔐 자동 암호화</strong>
<code>accessToken</code>·<code>hashKey</code>·<code>password</code>·<code>clientSecret</code> 같은 민감 필드는 저장 직전에
<code>iv:authTag:cipher</code> 3-part hex 형식으로 변환됩니다.
복호화 키는 환경변수 <code>DB_PASSWORD_SECRET</code> 이며, 코드(<code>amaranthApprovalClient.loadApprovalConnection</code>)에서
자동 복호화됩니다. 직접 평문 입력해도 자동 처리되므로 사용자가 별도로 암호화할 필요가 없습니다.
</p>
<h4>STEP 3 · [테스트] 버튼으로 즉시 검증</h4>
<p>저장 전에 [테스트] 를 누르면 실제 외부 서버로 호출을 보내 응답을 표시합니다:</p>
<pre style="white-space: pre; line-height: 1.4;">┌────────────────────────────────────────────────┐
│ ✅ 연결 성공 │
│ HTTP 200 · 응답시간 234ms │
│ 마지막 테스트: 2026-05-13 09:42 │
└────────────────────────────────────────────────┘</pre>
<p>실패 시 응답 본문이 그대로 표시되어 토큰 오타 / IP 화이트리스트 / 시그니처 불일치를 즉시 디버그할 수 있습니다.</p>
<h4>STEP 4 · 저장 후 백엔드 호출에서 활용</h4>
<p>등록된 커넥션은 즉시 코드 어디서나 이름으로 조회 가능합니다:</p>
<pre>// backend-node/src/services/amaranthApprovalClient.ts
const conn = await loadApprovalConnection(); // connection_name='Amaranth - 결재'
// conn.baseUrl, conn.callerName, conn.accessToken(복호화됨), ...
const url = conn.baseUrl + "/apiproxy/api99u02A01";</pre>
<p>업무 화면이 호출하는 모든 ERP API는 이 헬퍼를 거치므로, 키가 만료되면 코드 수정 없이 외부 커넥션 관리에서 토큰만 갱신해주면 됩니다.</p>
<h4>auth_config 저장 구조 (참고)</h4>
<pre>{
"callerName": "API_gcmsAmaranth40578", // 평문
"groupSeq": "gcmsAmaranth40578", // 평문
"accessToken": "fa9c..:b3e8..:7d2a..", // iv:authTag:cipher
"hashKey": "3a91..:c2f4..:8e6b..", // iv:authTag:cipher
"aesKey": "8441e27489d402cd" // 결재 SSO empSeq AES-128-CBC 키
}</pre>
<!-- 4.3 결재상신 워크플로 -->
<h3 id="amaranth-approval">4.3 결재상신 워크플로 (사용자 시점)</h3>
<p>견적관리·주문서관리·품의서관리·ECR관리·CS관리 등 결재 대상 화면에서 동일한 흐름으로 작동합니다.</p>
<h4>사용자 화면 흐름</h4>
<pre style="white-space: pre; line-height: 1.4;">┌─ 견적관리 ──────────────────────────────────────────────────────────────┐
│ [삭제] [+ 견적요청등록] [✎ 견적작성] [✈ 결재상신] [📧 메일발송] [초기화] │
├─────────────────────────────────────────────────────────────────────────┤
│ ☑ 영업번호 주문유형 접수일 고객사 품명 견적현황 │
│ ☑ 26C-0801 수리 2026-05-06 라온기술 10인치... 📁 │
│ ☐ 26C-0800 수리 2026-04-28 에스에프에이.. 12인치... 📁 │
└─────────────────────────────────────────────────────────────────────────┘
↓ ① 견적행 1건 체크
↓ ② [✈ 결재상신] 버튼 클릭
┌─ Sweet Alert 확인 ─────────────────────────────────────┐
│ 결재상신 │
│ 결재상신 하시겠습니까? │
│ * 결재완료 후 메일발송이 가능합니다. │
│ [취소] [결재상신 →] │
└────────────────────────────────────────────────────────┘
↓ ③ 백엔드가 Amaranth SSO URL 발급
↓ ④ 새 창 자동 오픈 (1200x900 popup)
┌─ Amaranth 결재 작성 화면 (외부 시스템) ────────────────┐
│ 견적서 결재 - 26C-0801 │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 결재선: [부서장] → [임원] → [대표] │ │
│ │ 첨부: 견적서 PDF (자동 합본) │ │
│ │ 본문: 기안자 코멘트 작성... │ │
│ │ [임시저장] [상신] │ │
│ └──────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘
↓ ⑤ 사용자가 [상신] 클릭 시 Amaranth 가 PLM 으로 webhook
↓ ⑥ PLM 의 amaranth_approval.status 가 inProcess 로 변경
↓ ⑦ 결재완료 시 status=complete + 견적관리 그리드 "결재상태" 컬럼 갱신</pre>
<h4>가드 조건 (버튼 비활성 / 토스트)</h4>
<table>
<tr><th width="280">상황</th><th>처리</th></tr>
<tr><td>행 미선택</td><td>"결재상신할 행을 선택해주십시오." 토스트</td></tr>
<tr><td>견적서 미작성 (est_objid 없음)</td><td>"견적서를 먼저 작성해주세요." 토스트 → [견적작성] 유도</td></tr>
<tr><td>이미 inProcess 상태</td><td>"결재 진행중인 건은 상신할 수 없습니다." 토스트</td></tr>
<tr><td>complete 상태</td><td>"결재 완료된 건은 상신할 수 없습니다." 토스트</td></tr>
<tr><td>approval_required='N' (결재불필요로 처리됨)</td><td>"결재불필요로 처리된 건입니다." 토스트</td></tr>
<tr><td>reject / delete / create</td><td>재상신 허용 (새 approKey 발급)</td></tr>
</table>
<h4>백엔드 내부 처리 11 단계</h4>
<div class="card">
<ol>
<li>프론트 → <code>POST /api/sales/estimate/:id/amaranth-approval</code></li>
<li>JWT 에서 userId 추출 → <code>user_info.emp_seq</code> 조회 (없으면 401)</li>
<li>최신 차수 <code>estimate_template.objid</code> 추출 → <code>target_objid</code></li>
<li><code>amaranth_approval</code> 기존 매핑 조회 (target_type + target_objid)
<ul>
<li>없음 → 신규 <code>approKey</code> = <code>UB_</code> + UUID</li>
<li>status ∈ {reject, delete, create} → 재상신: 새 approKey 발급</li>
<li>그 외 → 기존 approKey 재사용</li>
</ul>
</li>
<li>[외부 커넥션 관리]에서 <strong>Amaranth - 결재</strong> 레코드 조회 + 자동 복호화</li>
<li>wehago-sign 헤더 생성 (HMAC-SHA256, transaction-id, timestamp)</li>
<li>empSeq AES-128-CBC 암호화 (KST yyyyMMddHHmmss▦empSeq)</li>
<li>SSO URL 발급 API 호출 → <code>fullUrl</code> 수신</li>
<li><code>amaranth_approval</code> INSERT 또는 UPDATE</li>
<li>프론트로 <code>{fullUrl, approKey, status}</code> 반환</li>
<li>프론트가 <code>window.open(fullUrl, "amaranthApproval", "width=1200,height=900")</code></li>
</ol>
<div class="callout success">
<strong>✓ 주요 파라미터</strong>
<div class="kv" style="margin-top: 8px;">
<dt>targetType</dt><dd><code>CONTRACT_ESTIMATE</code> (견적) / <code>CONTRACT_ORDER</code> (수주) / 등</dd>
<dt>formId</dt><dd>1162 (견적) / 1161 (수주) — Amaranth 양식 ID</dd>
<dt>compSeq</dt><dd>1000</dd>
<dt>approvalTitle</dt><dd>"견적서 결재 - 26C-0801" 같은 결재함 제목</dd>
</div>
</div>
</div>
<h4>결재 상태 코드</h4>
<table>
<tr><th>코드</th><th>의미</th><th>가능 액션</th></tr>
<tr><td><span class="badge primary">create</span></td><td>임시 저장 (작성중)</td><td>재상신 가능</td></tr>
<tr><td><span class="badge warn">inProcess</span></td><td>결재 진행 중</td><td>상신 불가 (가드)</td></tr>
<tr><td><span class="badge success">complete</span></td><td>결재 완료</td><td>후속 처리 가능 (수주확정 등)</td></tr>
<tr><td><span class="badge danger">reject</span></td><td>반려</td><td>수정 후 재상신</td></tr>
<tr><td><span class="badge danger">delete</span></td><td>회수/삭제</td><td>재상신 가능</td></tr>
<tr><td></td><td>notRequired / N</td><td>결재불필요 처리</td></tr>
</table>
<!-- 4.3 문제 해결 -->
<h3 id="amaranth-troubleshoot">4.3 문제 해결</h3>
<details>
<summary>Q. [결재상신] 클릭 시 "결재 시스템 연동 중 오류" 토스트가 표시됩니다.</summary>
<ol>
<li>[외부 커넥션 관리] 에서 <strong>Amaranth - 결재</strong> 의 [테스트] 버튼 실행</li>
<li>실패 시 auth_config 의 accessToken/hashKey 가 만료됐을 가능성 → 운영팀 또는 Wehago 관리자에게 키 재발급 요청</li>
<li>성공인데 결재상신이 안 되면 백엔드 로그 <code>docker logs rps_backend</code> 에서 <code>SSO API 응답</code> 확인</li>
</ol>
</details>
<details>
<summary>Q. "empSeq 정보가 없습니다" 오류가 납니다.</summary>
<p><code>user_info.emp_seq</code> 가 비어 있는 사용자입니다. 관리자 메뉴 [유저관리] 에서 해당 사용자의 emp_seq 를 Amaranth 사원 마스터의 값으로 채워야 합니다.</p>
</details>
<details>
<summary>Q. 결재 후 PLM 상태가 자동 업데이트되지 않습니다.</summary>
<p>Amaranth 측 webhook (콜백 URL) 설정이 RPS PLM 의 <code>/api/amaranth-approval/callback</code> 으로 등록돼 있어야 합니다. Wehago 관리 콘솔에서 확인 가능.</p>
</details>
<!-- ======================================================== -->
<h2 class="section" id="dashboard">5. 대시보드 관리</h2>
<h3>5.1 사용자 대시보드</h3>
<div class="card">
<p>로그인 후 메인 화면(<code>/dashboard</code>)에서 보여지는 위젯형 대시보드입니다. 회사별로 위젯 구성이 다릅니다.</p>
<ul>
<li><strong>매출 추이</strong> — 월별 매출 합계 라인 차트</li>
<li><strong>수주 잔량</strong> — 미출하 수주 막대 차트</li>
<li><strong>리스크 / 알림</strong> — 기상특보 / 교통사고 / 환율 (외부 API 자동 갱신, 10분 간격)</li>
<li><strong>To-Do</strong> — 결재 대기, 마감 임박 견적, 미입고 발주</li>
</ul>
</div>
<figure class="shot">
<img src="screenshots/10_dashboard.png" alt="사용자 대시보드">
<figcaption><strong>그림 6.</strong> 사용자 대시보드 — 매출/수주 / 리스크 알림 / To-Do 위젯 통합.</figcaption>
</figure>
<h3>5.2 모니터링 보드</h3>
<table>
<tr><th>보드</th><th>경로</th><th>주요 위젯</th></tr>
<tr>
<td>품질 모니터링</td>
<td><code>/COMPANY_16/monitoring/quality</code></td>
<td>일별 불량률 추이, 검사자 별 처리 건, 불량 유형 Top 10</td>
</tr>
<tr>
<td>생산 모니터링</td>
<td><code>/COMPANY_16/monitoring/production</code></td>
<td>실시간 작업지시 현황, 라인별 가동률, 계획 대비 실적</td>
</tr>
<tr>
<td>설비 모니터링</td>
<td><code>/COMPANY_16/monitoring/equipment</code></td>
<td>설비별 상태(가동/정지/점검), 알람 이력</td>
</tr>
</table>
<h3>5.3 대시보드 관리자 기능</h3>
<div class="card">
<p>위치: <code>관리자 &gt; 화면 관리 &gt; 대시보드 리스트</code></p>
<ul>
<li>회사별 대시보드 신규 생성 / 위젯 배치 / 권한 설정</li>
<li>위젯 종류: 카드 / 라인 차트 / 막대 차트 / 파이 차트 / 그리드 / 외부 URL embed</li>
<li>저장된 대시보드는 <code>/dashboard/{dashboardId}</code> 경로로 접근</li>
</ul>
</div>
<p class="callout">
<strong>💡 그리드 → 차트 즉시 변환</strong>
모든 업무 그리드(견적관리, 매출관리, 품질관리 등)는 우상단 <span class="badge primary">📊</span> 토글로
Top-N 데이터를 막대/파이 차트로 즉시 시각화할 수 있습니다.
</p>
<!-- ======================================================== -->
<h2 class="section" id="screen-designer">6. 화면 관리 — 사용자 정의 화면 만들기 (코딩 없이)</h2>
<p>
RPS PLM 의 가장 강력한 기능 중 하나는 <strong>관리자가 코드 수정 없이 새 업무 화면을 직접 만들 수 있다</strong>는 점입니다.
테이블 선택 → 컬럼 매핑 → 버튼 / 데이터 흐름 정의 → 저장만으로 즉시 메뉴에 노출됩니다.
</p>
<h3 id="sd-overview">6.1 화면 디자이너 개요</h3>
<figure class="shot">
<img src="screenshots/08_screen_mng_list.png" alt="화면 관리 목록">
<figcaption><strong>그림 7.</strong> 화면 관리 — 좌측 그룹 트리 + 우측 화면 카드 목록. 각 화면 카드의 회색 영역을 누르면 상세가 열립니다.</figcaption>
</figure>
<figure class="shot">
<img src="screenshots/09_screen_detail.png" alt="화면 상세 — 디자이너 패널">
<figcaption><strong>그림 8.</strong> 화면 상세 — 메인 테이블 / 버튼 / 데이터 흐름 / 우측 미리보기가 한 화면에 통합. 우하단 [화면 디자이너에서 편집] 으로 풀스크린 편집 진입.</figcaption>
</figure>
<div class="card">
<p>위치: <code>관리자 &gt; 화면 관리 &gt; 화면관리</code></p>
<pre style="white-space: pre; line-height: 1.4;">┌─────────────────────────────────────────────────────────────────────────┐
│ ● 하이큐마그 영업 수주관리 화면 #6456 [↻] │
├─────────────────────────────────────────────────────────────────────────┤
│ sales_order_detail │
│ │
│ 메인 테이블: sales_order_detail │
│ 컴포넌트: 버튼 4 │
├──────────────────────────────────┬──────────────────────────────────────┤
│ 🗄 테이블 1 │ ▼ 미리보기 영역 │
│ ┌────────────────────────────┐ │ ● 하이큐마그 영업 수주관리 화면 │
│ │ sales_order_detail │ │ │
│ │ 메인 · 0 컬럼 사용중 [설정]│ │ ⌛ 화면 로딩 중... │
│ └────────────────────────────┘ │ │
│ │ │
│ 🔘 버튼 4 │ │
│ [수주 등록 · MODAL · 플로우 1] │ │
│ [수정 · EDIT · 플로우 1] │ │
│ [삭제 · DELETE ] │ │
│ [출하계획 · OPENMODALWITHDATA] │ │
│ │ │
│ → 데이터 흐름 0 │ │
│ 데이터 흐름이 없어요 │ │
│ │ │
│ [↗ 화면 디자이너에서 편집] │
└──────────────────────────────────┴──────────────────────────────────────┘</pre>
<p>화면 카드 한 장에 <strong>① 메인 테이블 / ② 버튼 N 개 / ③ 데이터 흐름 N 개 / ④ 미리보기</strong> 가 통합되어 있어
설정 변경 즉시 결과가 우측에 표시됩니다.</p>
</div>
<h3 id="sd-create">6.2 신규 화면 만들기 — 4단계</h3>
<h4>STEP 1 · [+ 새 화면] 클릭 → 기본 정보 입력</h4>
<pre style="white-space: pre; line-height: 1.4;">┌──────────────────────────────────────────────────┐
│ 새 화면 만들기 [X] │
├──────────────────────────────────────────────────┤
│ 화면명 * [영업 수주관리 화면_________] │
│ 설명 [수주 마스터/디테일 관리_____] │
│ 화면 코드 [SALES_ORDER_MGMT_______] │
│ 그룹 [영업관리 ▾] │
│ 화면 종류 ○ 일반 화면 ● POP 화면 │
│ 메뉴 노출 ☑ 좌측 메뉴 / 권한 그룹 자동 매핑 │
│ │
│ [취소] [생성] │
└──────────────────────────────────────────────────┘</pre>
<h4>STEP 2 · 메인 테이블 매핑</h4>
<p>[테이블] 섹션의 [설정] 버튼을 누르면 컬럼 매핑 모달이 열립니다:</p>
<pre style="white-space: pre; line-height: 1.4;">┌─ 테이블 컬럼 매핑 ─────────────────────────────────────────┐
│ 메인 테이블: sales_order_detail │
│ │
│ ☑ 사용 컬럼명 데이터타입 그리드 라벨 필터 │
│ ──────────────────────────────────────────────────────── │
│ ☑ objid BIGINT PK — │
│ ☑ order_no VARCHAR(50) 주문번호 text │
│ ☑ customer_objid BIGINT 고객사 FK→client_mng │
│ ☑ product_objid BIGINT 제품 FK→part_mng │
│ ☑ quantity NUMERIC 수량 number │
│ ☐ writer VARCHAR(50) 작성자(숨김) — │
│ ☑ reg_date TIMESTAMP 등록일 date │
│ │
│ [취소] [저장] │
└────────────────────────────────────────────────────────────┘</pre>
<p>체크박스만 토글하면 즉시 미리보기 그리드에 컬럼이 추가/제거됩니다. FK 매핑이 잡힌 컬럼은 자동으로 select 드롭다운으로 렌더링됩니다.</p>
<h4>STEP 3 · 버튼 추가 (등록 / 수정 / 삭제 / 모달 등)</h4>
<table>
<tr><th width="160">버튼 타입</th><th>동작</th><th>예시</th></tr>
<tr><td><span class="badge primary">MODAL</span></td><td>등록/수정 폼 모달 오픈</td><td>"+ 수주 등록"</td></tr>
<tr><td><span class="badge primary">EDIT</span></td><td>선택 행 인라인 수정 모드</td><td>"✎ 수정"</td></tr>
<tr><td><span class="badge danger">DELETE</span></td><td>선택 행 삭제 (확인 다이얼로그)</td><td>"🗑 삭제"</td></tr>
<tr><td><span class="badge primary">OPENMODALWITHDATA</span></td><td>현재 행 데이터를 다른 화면 모달에 전달</td><td>"출하계획" → 출하 화면 자동 채움</td></tr>
<tr><td><span class="badge success">FLOW</span></td><td>데이터 흐름(워크플로) 실행</td><td>견적→수주 자동 전환</td></tr>
<tr><td><span class="badge warn">EXTERNAL</span></td><td>외부 커넥션 호출 + 결과 매핑</td><td>"거래처 동기화" → Amaranth API</td></tr>
</table>
<h4>STEP 4 · 저장 → 즉시 메뉴에 노출</h4>
<p>화면 디자이너 우상단 [저장] 클릭 시:</p>
<ol>
<li><code>screen_definitions</code> 테이블에 화면 정의 INSERT</li>
<li>좌측 [메뉴 관리]에서 자동으로 신규 메뉴 후보로 표시 → 활성화하면 즉시 사이드바 등장</li>
<li>권한 그룹 자동 매핑 (관리자 그룹 전체 허용으로 기본 등록)</li>
<li>다른 사용자는 새로고침(F5) 후 메뉴에서 클릭 → 새 화면 사용 시작</li>
</ol>
<h3 id="sd-table">6.3 테이블 매핑 — 조인 / 다중 테이블</h3>
<div class="card">
<p>한 화면에 메인 1개 + 조인 테이블 N개를 함께 매핑할 수 있습니다.</p>
<pre style="white-space: pre; line-height: 1.4;">┌─ 조인 추가 ────────────────────────────────────────────┐
│ 메인: sales_order_detail │
│ └── ON customer_objid = ? │
│ 조인 1: client_mng (LEFT JOIN) │
│ └── client.objid = sales.customer_objid │
│ └── 노출 컬럼: client_nm │
│ 조인 2: part_mng (LEFT JOIN) │
│ └── part.objid = sales.product_objid │
│ └── 노출 컬럼: part_no, part_name │
└─────────────────────────────────────────────────────────┘</pre>
<p>조인된 컬럼은 자동으로 <code>partner_name</code> 같은 별칭으로 그리드에 합쳐 보입니다.</p>
</div>
<h3 id="sd-button">6.4 버튼·플로우 추가 — 클릭 시 동작 정의</h3>
<div class="card">
<p>버튼 카드 → [편집] → "플로우 연결" 화면에서 시각적 노드 편집기로 동작을 구성합니다:</p>
<pre style="white-space: pre; line-height: 1.4;">┌─ 플로우 편집기 (DataFlowDesigner) ─────────────────────┐
│ │
│ [버튼 클릭] │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ 입력 검증 │ ← 필수 컬럼 null 체크 등 │
│ └──────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ SQL 실행 │ → INSERT INTO sales_order... │
│ └──────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ 외부 커넥션 호출 │ → Amaranth 결재상신 │
│ └──────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ 토스트 + 새로고침 │ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────────┘</pre>
</div>
<h3 id="sd-dataflow">6.5 데이터 흐름 — 화면 간 자동 연계</h3>
<p>한 화면에서 다른 화면으로 데이터를 전달할 때 사용합니다. 예시:</p>
<ul>
<li><strong>견적 → 수주 전환</strong>: 견적 행 [수주확정] 클릭 → 견적 데이터를 수주 등록 모달에 자동 채움</li>
<li><strong>수주 → 출하계획</strong>: 수주 행 [출하계획] 버튼 → 출하 화면을 새 탭으로 열면서 contract_objid / part / qty 자동 전달</li>
<li><strong>입고 → 수입검사 요청</strong>: 입고 확정 시 자동으로 incoming_inspection_detail INSERT</li>
</ul>
<p class="callout success">
<strong>✓ 화면 디자이너의 장점</strong><br>
<strong>코드 수정 0</strong> — 신규 메뉴 추가 시 Next.js / Node 빌드 불필요<br>
<strong>즉시 반영</strong> — 저장 즉시 모든 사용자에게 새로고침으로 노출<br>
<strong>일관된 UI</strong> — 자동으로 PageHeader + CompactFilterBar + DataGrid 패턴 적용<br>
<strong>버전 관리</strong><code>#6456</code> 처럼 화면별 ID + 변경 이력 보관
</p>
<!-- ======================================================== -->
<h2 class="section" id="quality">7. 품질관리 메뉴 가이드</h2>
<h3>6.1 수입검사 요청</h3>
<div class="card">
<p><strong>경로</strong>: <code>/COMPANY_16/quality/incoming-request</code></p>
<p>발주서 단위로 입고/검사 요청 현황을 한 줄씩 보여줍니다. 검사 디테일이 아직 없으면 "미요청", 있으면 "요청중"/"요청완료".</p>
<h4>필터 (12종)</h4>
<p>품의서 No · 발주서 No · 프로젝트번호 · 품번 · 품명 · 공급업체 · 입고결과 · 제품구분 · 검사여부 · 요청현황 · 요청자 · 요청일 범위</p>
<h4>그리드 컬럼 (12개)</h4>
<p>품의서 No · 발주서 No · 프로젝트번호 · 제품구분 · 품번 · 품명 · 공급업체 · 입고결과 · 요청일 · 요청자 · 검사여부 · 요청현황</p>
</div>
<h3>6.2 수입검사 진행</h3>
<div class="card">
<p><strong>경로</strong>: <code>/COMPANY_16/quality/incoming-mgmt</code></p>
<p>검사자가 실제 검사를 진행하여 합/불 판정과 불량률(검사수량 대비)을 입력하는 화면입니다.</p>
<h4>주요 컬럼 (19개)</h4>
<p>검사일 · 검사자 · 품의서 · 발주서 · 프로젝트번호 · 제품구분 · 품명(모델명) · 부품품번 · 부품명 · 공급업체 · 입고일 · 입고수량 · 입고결과 · 검사수량 · 불량수량 · 불량률 · 검사현황 · 검사성적서(첨부파일)</p>
<p class="callout">불량률 = SUM(defect_qty 서브쿼리) / inspection_qty × 100, 소수점 2자리.</p>
</div>
<h3>6.3 공정검사 관리</h3>
<figure class="shot">
<img src="screenshots/12_quality_process.png" alt="공정검사 관리">
<figcaption><strong>그림 9.</strong> 공정검사 관리 — 검사일/검사자(자동산정)/프로젝트번호/품번/품명/검사수량 합계 등 9컬럼.</figcaption>
</figure>
<div class="card">
<p><strong>경로</strong>: <code>/COMPANY_16/quality/process-inspection</code></p>
<p>작업 중 SPC 검사. 마스터 1건당 디테일 N건(검사항목)을 SUM 으로 집계하여 한 줄로 표시.</p>
<h4>필터 (10종, 2행)</h4>
<ul>
<li>1행: 프로젝트번호 · 제품구분 · 품번 · 품명 · 작업환경상태 · 측정기</li>
<li>2행: 검사일 범위 · 검사자 · 검사결과 · 진행공정</li>
</ul>
<h4>그리드 컬럼 (9개)</h4>
<p>검사일 · 검사자 · 프로젝트번호 · 제품구분 · 품번 · 품명 · 검사수량 합계 · 검사결과(OK/NG) · 첨부파일</p>
</div>
<h3>6.4 반제품검사 관리</h3>
<figure class="shot">
<img src="screenshots/11_quality_semi.png" alt="반제품검사 관리">
<figcaption><strong>그림 10.</strong> 반제품검사 관리 — 14컬럼. 입고/양품/불량/재생/최종양품 SUM 자동 산정 + 불량률(%) 계산.</figcaption>
</figure>
<div class="card">
<p><strong>경로</strong>: <code>/COMPANY_16/quality/semi-product-inspection</code></p>
<p>반제품 입고에 대한 양품/불량 판정. 단일 테이블(<code>pms_quality_semi_product_inspection</code>)에 양품 마스터(<code>DATA_TYPE='GOOD'</code>)와 불량 행(<code>'DEFECT'</code>)을 동일 <code>INSPECTION_GROUP_ID</code> 로 묶어 저장합니다.</p>
<h4>자동 산정 컬럼</h4>
<table>
<tr><th>컬럼</th><th>계산식</th></tr>
<tr><td>불량수량 합계</td><td>같은 group_id 의 DEFECT 행 defect_qty 합</td></tr>
<tr><td>불량률(%)</td><td>round(불량수량 / 입고수량 × 100, 2)</td></tr>
<tr><td>재생수량 합계</td><td>DEFECT 행 중 disposition_type='수정완료' 의 defect_qty 합</td></tr>
<tr><td>최종양품수량</td><td>good_qty + 재생수량</td></tr>
</table>
</div>
<!-- ======================================================== -->
<h2 class="section" id="common">8. 공통 UI 사용법</h2>
<h3>7.1 데이터 그리드</h3>
<div class="card">
<p>모든 업무 화면의 표는 동일한 <code>DataGrid</code> 컴포넌트로 통일됩니다.</p>
<ul>
<li><strong>컬럼 드래그</strong> — 헤더 ⋮⋮ 영역을 드래그해 컬럼 순서 변경</li>
<li><strong>정렬</strong> — 헤더 클릭 (asc/desc 토글)</li>
<li><strong>필터</strong> — 헤더 옆 깔때기 아이콘 → 고유값 체크박스 다중 선택</li>
<li><strong>인라인 편집</strong> — 셀 더블클릭 (편집 가능 컬럼만)</li>
<li><strong>🔃 새로고침 / 📊 차트 / ⬇ 엑셀 다운로드 / ⚙ 컬럼 표시 설정</strong> — 그리드 우상단 툴바</li>
<li><strong>페이지 사이즈</strong> — 우하단 드롭다운 (10/15/20/50/100)</li>
</ul>
</div>
<h3>7.2 필터바 (CompactFilterBar)</h3>
<p>화면 상단의 검색 영역. <strong>요청일</strong>·<strong>검사일</strong> 같은 날짜 범위는 캘린더 아이콘 클릭으로 달력 popover가 뜹니다(YYYY-MM-DD 직접 입력도 가능).</p>
<h3>7.3 페이지 헤더</h3>
<p>모든 화면 상단의 [검색] / [초기화] / 업무 액션(등록·삭제·결재상신 등)은 <code>PageHeader</code> 한 곳에 모입니다.</p>
<!-- ======================================================== -->
<h2 class="section" id="faq">9. 자주 묻는 질문</h2>
<details>
<summary>Q. 로그인 후 좌측 메뉴가 안 보입니다.</summary>
<p>해당 계정이 권한 그룹에 속해있지 않습니다. <code>관리자 &gt; 권한 그룹 관리</code>에서 사용자에게 메뉴를 부여하세요.</p>
</details>
<details>
<summary>Q. 그리드의 엑셀 다운로드가 "내보낼 데이터가 없습니다" 토스트만 나옵니다.</summary>
<p>현재 필터로 조회된 결과가 0건이라 그렇습니다. 검색 조건을 변경한 후 다시 [⬇ 엑셀 다운로드] 를 누르세요.</p>
</details>
<details>
<summary>Q. 결재상신 버튼이 비활성화 되어 있습니다.</summary>
<ul>
<li>행을 선택했는지 확인 (체크박스 클릭 또는 셀 클릭)</li>
<li>견적관리의 경우 [견적작성] 으로 견적서(템플릿)가 먼저 생성되어 있어야 함</li>
<li>이미 결재중/결재완료 인 건은 재상신 불가</li>
</ul>
</details>
<details>
<summary>Q. Amaranth 결재 팝업이 안 뜨고 에러만 납니다.</summary>
<p><a href="#amaranth-troubleshoot">4.3 문제 해결</a> 참조.</p>
</details>
<details>
<summary>Q. 화면이 "Loading chunk app/layout failed" 로 깨집니다.</summary>
<p>프론트엔드 dev 서버가 초기 컴파일 중이거나 OOM 으로 재시작 중입니다. 1~2분 기다린 후 하드 새로고침(Cmd+Shift+R / Ctrl+F5).</p>
</details>
<details>
<summary>Q. 외부 REST API 의 [테스트] 가 "실패" 로 나옵니다.</summary>
<ol>
<li>기본 URL 끝에 슬래시가 들어가 있는지 확인 (자동 제거되지만 path 와 중복은 회피)</li>
<li>accessToken/hashKey 가 만료됐는지 — Wehago 관리 콘솔에서 키 재발급</li>
<li>외부 서버의 IP 화이트리스트에 RPS 서버 IP 가 등록돼 있는지</li>
<li>HTTP 메서드(GET/POST) 가 외부 API 명세와 일치하는지</li>
<li>실패 메시지에 표시된 HTTP 상태 코드 / 응답 본문을 운영팀에 공유</li>
</ol>
</details>
<details>
<summary>Q. 화면 디자이너로 새 화면을 만들었는데 좌측 메뉴에 안 나옵니다.</summary>
<ol>
<li>[관리자 &gt; 시스템 관리 &gt; 메뉴 관리] 에서 신규 화면이 메뉴 트리에 등록됐는지 확인</li>
<li>등록은 됐는데 안 보이면 → 권한 그룹 매핑 누락. [관리자 &gt; 권한 그룹 관리] 에서 해당 그룹에 메뉴 권한 부여</li>
<li>본인 계정으로 로그아웃 → 재로그인 (메뉴 캐시 갱신)</li>
</ol>
</details>
<details>
<summary>Q. 사용자 정의 화면에서 데이터를 등록했는데 저장이 안 됩니다.</summary>
<ol>
<li>버튼 카드의 동작 타입이 <code>MODAL</code> / <code>EDIT</code> 인지 확인</li>
<li>버튼의 데이터 흐름 (DataFlowDesigner) 에 "SQL 실행" 노드가 포함돼 있는지</li>
<li>SQL 노드의 대상 컬럼이 메인 테이블의 NOT NULL 컬럼을 모두 채우는지</li>
<li>저장 후 미리보기 영역에서 즉시 새로고침되는지 (안 되면 콘솔 로그 확인)</li>
</ol>
</details>
<footer style="margin-top: 80px; padding-top: 24px; border-top: 1px solid var(--line); color: var(--muted); font-size: 13px;">
© RPS Korea PLM Team · 본 문서는 시스템 변경 시 함께 업데이트해야 합니다 (docs/manual/).
</footer>
</main>
</div>
</body>
</html>