수주관리 (Sales Order Management)
Screen ID: /screens/156
메뉴 경로: 영업관리 > 수주관리
테이블: sales_order_mng
⚠️ 문서 사용 안내
이 문서는 "수주관리" 화면의 구현 예시입니다.
📌 중요: JSON 데이터는 참고용입니다!
이 문서에 포함된 JSON 설정(레이아웃, 컴포넌트 구성 등)은 수주관리 화면에 특화된 예시입니다.
다른 화면을 구현할 때:
- 이 JSON을 그대로 복사해서 사용하지 마세요
- 해당 화면의 테이블 구조를 먼저 분석하세요
- 화면의 요구사항과 기능을 파악하세요
- 분석 결과에 맞는 새로운 JSON 구조를 작성하세요
1. 테이블 선택 및 화면 구조
1.1 사용 테이블
| 테이블명 |
용도 |
비고 |
sales_order_mng |
수주 마스터 데이터 |
주 테이블 |
customer_mng |
거래처 정보 |
FK: partner_id |
item_info |
품목 정보 |
FK: part_code |
1.2 테이블 컬럼 정의 (실제 DB 기준)
| 컬럼명 |
표시명 |
타입 |
필수 |
설명 |
id |
ID |
integer |
PK |
자동 생성 (시퀀스) |
company_code |
회사코드 |
varchar |
✅ |
멀티테넌시 |
order_no |
수주번호 |
varchar |
✅ |
수주 고유 코드 |
order_date |
수주일 |
date |
|
수주 등록일 |
due_date |
납기일 |
date |
|
납품 예정일 |
partner_id |
거래처ID |
varchar |
|
거래처 코드 (FK) |
delivery_partner_id |
납품처ID |
varchar |
|
납품처 코드 |
delivery_address |
납품장소 |
text |
|
납품 주소 |
shipping_method |
배송방법 |
varchar |
|
택배, 화물, 직송 등 |
part_code |
품목코드 |
varchar |
|
품목 코드 (FK) |
part_name |
품명 |
varchar |
|
품목명 |
spec |
규격 |
varchar |
|
규격 정보 |
material |
재질 |
varchar |
|
재질 정보 |
order_qty |
수주수량 |
numeric |
|
기본값 0 |
ship_qty |
출하수량 |
numeric |
|
기본값 0 |
balance_qty |
잔량 |
numeric |
|
기본값 0 (수주수량 - 출하수량) |
inventory_qty |
현재고 |
numeric |
|
기본값 0 |
plan_ship_qty |
출하계획량 |
numeric |
|
기본값 0 |
unit_price |
단가 |
numeric |
|
기본값 0 |
total_amount |
금액 |
numeric |
|
기본값 0 (수주수량 × 단가) |
status |
상태 |
varchar |
|
수주, 진행중, 완료, 취소 (기본값: 수주) |
manager_id |
담당자ID |
varchar |
|
담당자 ID |
manager_name |
담당자명 |
varchar |
|
담당자 이름 |
memo |
메모 |
text |
|
비고 |
sales_type |
영업유형 |
varchar |
|
내수, 수출 등 |
part_name_eng |
품명(영문) |
varchar |
|
영문 품목명 |
item_due_date |
품목납기일 |
varchar |
|
품목별 납기일 |
incoterms |
인코텀즈 |
varchar |
|
무역조건 (수출용) |
payment_term |
결제조건 |
varchar |
|
결제 조건 |
port_of_loading |
선적항 |
varchar |
|
선적 항구 (수출용) |
port_of_discharge |
도착항 |
varchar |
|
도착 항구 (수출용) |
hs_code |
HS코드 |
varchar |
|
관세 코드 (수출용) |
currency |
통화 |
varchar |
|
통화 코드 |
created_date |
등록일 |
timestamp |
|
자동 생성 |
created_by |
등록자 |
varchar |
|
등록자 ID |
updated_date |
수정일 |
timestamp |
|
자동 갱신 |
updated_by |
수정자 |
varchar |
|
수정자 ID |
writer |
작성자 |
varchar |
|
작성자 ID |
1.3 화면 구조 개요
- 화면 유형: 목록형 (단일 테이블 CRUD)
- 주요 기능:
- 수주 조회/검색/필터링
- 수주 등록/수정/삭제
- 그룹핑 (Group By)
- 출하계획 연동
- 엑셀 업로드/다운로드
- 통계 표시 (총 금액, 총 수량)
2. 컴포넌트 배치도
2.1 전체 레이아웃
┌─────────────────────────────────────────────────────────────────────────────┐
│ [검색 영역] │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ v2-table-search-widget │ │
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │
│ │ │ 수주번호 │ │ 거래처 │ │ 품목명 │ │ 상태 │ │ 수주일 │ │ │
│ │ │ (text) │ │ (select) │ │ (text) │ │ (select) │ │ (date) │ │ │
│ │ └───────────┘ └───────────┘ └───────────┘ └───────────┘ └───────────┘ │ │
│ │ ┌─────────┐ ┌──────────┐ ┌──────┐ │ │
│ │ │ 사용자 │ │ 엑셀 │ │엑셀 │ │ │
│ │ │ 옵션 │ │ 업로드 │ │다운 │ │ │
│ │ └─────────┘ └──────────┘ └──────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────────┤
│ [테이블 헤더 + 액션 버튼 + 통계] │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 📋 수주 목록 (10) 총 금액: 1,234,000원 총 수량: 5,000개 [Group by ▼]│ │
│ │ [수주등록][수정][삭제][출하계획] │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────────────────┤
│ [데이터 테이블] │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ v2-table-list │ │
│ │ ┌──┬────────┬────────┬────────┬────────┬──────┬──────┬──────┬────────┐ │ │
│ │ │☐ │수주번호│거래처 │품목코드│품명 │규격 │재질 │단위 │수주수량│ │ │
│ │ ├──┼────────┼────────┼────────┼────────┼──────┼──────┼──────┼────────┤ │ │
│ │ │☐ │ORD-001 │삼성전자│ITEM001 │볼트 M8 │M8x20 │SUS304│EA │1,000 │ │ │
│ │ │☐ │ORD-002 │LG전자 │ITEM002 │너트 M8 │M8 │SUS304│EA │2,000 │ │ │
│ │ └──┴────────┴────────┴────────┴────────┴──────┴──────┴──────┴────────┘ │ │
│ │ (수평 스크롤: 출하수량, 잔량, 현재고, 출하계획량, 단가, 금액, 납품처, │ │
│ │ 납품장소, 배송방법, 납기일, 수주일, 상태, 담당자, 메모) │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
2.2 사용 가능한 V2 컴포넌트 목록
📌 V2 컴포넌트 전체 목록 - 화면 구성 시 사용 가능한 컴포넌트
| 컴포넌트 ID |
설명 |
카테고리 |
v2-input |
텍스트, 숫자, 비밀번호, 이메일 등 입력 |
입력 |
v2-select |
드롭다운, 콤보박스, 라디오, 체크박스 |
입력 |
v2-date |
날짜/시간 입력 |
입력 |
v2-button-primary |
버튼 |
액션 |
v2-table-list |
테이블 리스트 (CRUD) |
테이블 |
v2-table-search-widget |
테이블 검색/필터 위젯 |
유틸리티 |
v2-aggregation-widget |
집계 위젯 |
위젯 |
v2-text-display |
텍스트 표시 (읽기 전용) |
표시 |
2.3 이 화면에서 사용하는 컴포넌트
| 컴포넌트 타입 |
역할 |
v2-table-search-widget |
검색 필터 (수주번호, 거래처, 품목명, 상태, 수주일) |
v2-table-list |
수주 데이터 테이블 |
v2-button-primary |
사용자옵션 |
v2-button-primary |
엑셀 업로드 |
v2-button-primary |
엑셀 다운로드 |
v2-button-primary |
수주등록 (모달 열기) |
v2-button-primary |
수정 (모달 열기) |
v2-button-primary |
삭제 |
v2-button-primary |
출하계획 |
v2-aggregation-widget |
통계 표시 (총 금액, 총 수량) |
v2-input |
모달 - 텍스트 입력 필드 |
v2-select |
모달 - 선택 필드 |
v2-date |
모달 - 날짜 입력 필드 |
3. 화면 디자이너 설정 가이드
3.1 v2-table-search-widget (검색 필터) 설정
- 좌측 컴포넌트 패널에서
v2-table-search-widget 드래그하여 화면 상단에 배치
- 대상 테이블로 아래에 배치할 테이블 리스트 선택
💡 참고: 검색 필터는 사용자가 런타임에서 원하는 필드를 직접 추가/삭제하여 사용할 수 있습니다.
3.2 v2-table-list (수주 테이블) 설정
Step 1: 컴포넌트 추가
- 좌측 컴포넌트 패널에서
v2-table-list 드래그하여 검색 필터 아래에 배치
Step 2: 데이터 소스 설정
| 설정 항목 |
설정 값 |
| 테이블 선택 |
sales_order_mng |
| 자동 컬럼 생성 |
✅ 체크 (테이블 컬럼 자동 로드) |
Step 3: 컬럼 설정
[컬럼 설정] 패널에서 표시할 컬럼 선택 및 순서 조정:
| 순서 |
컬럼 |
표시명 |
너비 |
정렬 |
표시 |
특수 설정 |
| 1 |
order_no |
수주번호 |
130 |
좌측 |
✅ |
굵게 표시 |
| 2 |
partner_id |
거래처 |
150 |
좌측 |
✅ |
조인: customer_mng.name |
| 3 |
part_code |
품목코드 |
130 |
좌측 |
✅ |
|
| 4 |
part_name |
품명 |
180 |
좌측 |
✅ |
|
| 5 |
spec |
규격 |
120 |
좌측 |
✅ |
|
| 6 |
material |
재질 |
100 |
좌측 |
✅ |
|
| 7 |
unit |
단위 |
80 |
중앙 |
✅ |
기본값: EA |
| 8 |
order_qty |
수주수량 |
100 |
우측 |
✅ |
숫자 포맷 |
| 9 |
ship_qty |
출하수량 |
100 |
우측 |
✅ |
숫자 포맷 |
| 10 |
balance_qty |
잔량 |
100 |
우측 |
✅ |
숫자 포맷, 굵게 |
| 11 |
inventory_qty |
현재고 |
100 |
우측 |
✅ |
숫자 포맷 |
| 12 |
plan_ship_qty |
출하계획량 |
100 |
우측 |
✅ |
숫자 포맷 |
| 13 |
unit_price |
단가 |
120 |
우측 |
✅ |
숫자 포맷 |
| 14 |
total_amount |
금액 |
140 |
우측 |
✅ |
숫자 포맷, 굵게 |
| 15 |
delivery_partner_id |
납품처 |
150 |
좌측 |
✅ |
|
| 16 |
delivery_address |
납품장소 |
150 |
좌측 |
✅ |
|
| 17 |
shipping_method |
배송방법 |
120 |
중앙 |
✅ |
|
| 18 |
due_date |
납기일 |
120 |
중앙 |
✅ |
날짜 포맷 |
| 19 |
order_date |
수주일 |
120 |
중앙 |
✅ |
날짜 포맷 |
| 20 |
status |
상태 |
100 |
중앙 |
✅ |
뱃지 스타일 |
| 21 |
manager_name |
담당자 |
100 |
좌측 |
✅ |
|
| 22 |
memo |
메모 |
200 |
좌측 |
✅ |
|
Step 4: 기능 설정
| 설정 항목 |
설정 값 |
설명 |
| 체크박스 |
✅ 사용 |
다중 선택 활성화 |
| 페이지네이션 |
✅ 사용 |
|
| 페이지 크기 |
20 |
기본 표시 행 수 |
| 정렬 |
✅ 사용 |
컬럼 헤더 클릭 정렬 |
| 컬럼 리사이즈 |
✅ 사용 |
컬럼 너비 조정 |
| 그룹핑 |
✅ 사용 |
Group By 기능 |
| 수평 스크롤 |
✅ 사용 |
컬럼 수가 많으므로 필수 |
Step 5: 그룹핑 옵션 설정
Group By 드롭다운에 표시할 컬럼 선택:
- ✅
partner_id (거래처)
- ✅
status (상태)
- ✅
part_name (품목명)
- ✅
material (재질)
3.3 버튼 설정
검색 영역 우측 버튼
사용자옵션 버튼
| 설정 항목 |
설정 값 |
| 라벨 |
사용자옵션 |
| 아이콘 |
⚙️ |
| 액션 타입 |
custom |
| 스타일 |
secondary |
| 동작 |
사용자 옵션 모달 열기 |
엑셀 업로드 버튼
| 설정 항목 |
설정 값 |
| 라벨 |
엑셀 업로드 |
| 아이콘 |
📥 |
| 액션 타입 |
excel_upload |
| 스타일 |
secondary |
| 대상 테이블 |
sales_order_mng |
엑셀 다운로드 버튼
| 설정 항목 |
설정 값 |
| 라벨 |
엑셀 다운로드 |
| 아이콘 |
📤 |
| 액션 타입 |
excel_download |
| 스타일 |
secondary |
| 대상 |
현재 테이블 리스트 |
테이블 헤더 우측 버튼
수주등록 버튼
| 설정 항목 |
설정 값 |
| 라벨 |
수주 등록 |
| 아이콘 |
➕ |
| 액션 타입 |
modal |
| 스타일 |
success |
| 연결 화면 |
수주 등록/수정 화면 (아래 3.4 참조) |
| 모달 제목 |
수주 등록 |
| 모달 사이즈 |
lg |
수정 버튼
| 설정 항목 |
설정 값 |
| 라벨 |
수정 |
| 아이콘 |
✏️ |
| 액션 타입 |
edit |
| 스타일 |
secondary |
| 선택 필수 |
✅ 체크 (1개만) |
| 연결 화면 |
수주 등록/수정 화면 (아래 3.4 참조) |
삭제 버튼
| 설정 항목 |
설정 값 |
| 라벨 |
삭제 |
| 아이콘 |
🗑️ |
| 액션 타입 |
delete |
| 스타일 |
secondary |
| 선택 필수 |
✅ 체크 (복수 선택 가능) |
| 확인 메시지 |
선택한 수주를 삭제하시겠습니까? |
출하계획 버튼
| 설정 항목 |
설정 값 |
| 라벨 |
출하계획 |
| 아이콘 |
🚚 |
| 액션 타입 |
custom |
| 스타일 |
secondary |
| 선택 필수 |
✅ 체크 (복수 선택 가능) |
| 동작 |
출하계획 슬라이드 패널 열기 |
3.4 수주 등록/수정 화면 (모달용 화면)
📌 별도 화면 생성 필요: 수주등록/수정 버튼에 연결할 모달 화면을 새로 생성합니다.
Step 1: 새 화면 생성
- 화면 관리에서 [+ 새 화면] 클릭
- 화면 정보 입력:
- 화면명:
수주 등록/수정
- 테이블:
sales_order_mng
- 화면 유형:
모달
Step 2: 폼 필드 배치
모달 레이아웃 배치도:
┌─────────────────────────────────────────────────────────────────┐
│ 수주 등록/수정 [✕] │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ 수주번호 * │ │ 수주일 * │ │
│ │ [____________________] │ │ [____________________] │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ 거래처 * │ │ 품목코드 * │ │
│ │ [삼성전자 ▼] │ │ [____________________] │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ 품명 │ │ 규격 │ │
│ │ [____________________] │ │ [____________________] │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ 재질 │ │ 단위 │ │
│ │ [____________________] │ │ [EA ▼] │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ 수주수량 * │ │ 단가 * │ │
│ │ [____________________] │ │ [____________________] │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ 납기일 │ │ 상태 * │ │
│ │ [____________________] │ │ [수주 ▼] │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ 납품처 │ │ 납품장소 │ │
│ │ [____________________] │ │ [____________________] │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
│ │ 배송방법 │ │ 담당자 │ │
│ │ [택배 ▼] │ │ [____________________] │ │
│ └─────────────────────────┘ └─────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 메모 │ │
│ │ [______________________________________________________]│ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────┤
│ [취소] [💾 저장] │
└─────────────────────────────────────────────────────────────────┘
필드 목록:
| 순서 |
필드 (컬럼명) |
라벨 |
입력 타입 |
필수 |
비고 |
| 1 |
order_no |
수주번호 |
text |
✅ |
자동채번 또는 수동입력 |
| 2 |
order_date |
수주일 |
date |
✅ |
기본값: 오늘 |
| 3 |
partner_id |
거래처 |
select |
✅ |
거래처 목록에서 선택 |
| 4 |
part_code |
품목코드 |
text |
✅ |
품목 검색 |
| 5 |
part_name |
품명 |
text |
|
품목코드 선택 시 자동 입력 |
| 6 |
spec |
규격 |
text |
|
품목코드 선택 시 자동 입력 |
| 7 |
material |
재질 |
text |
|
|
| 8 |
unit |
단위 |
select |
|
옵션: EA, kg, L, Box 등 |
| 9 |
order_qty |
수주수량 |
number |
✅ |
|
| 10 |
unit_price |
단가 |
number |
✅ |
|
| 11 |
due_date |
납기일 |
date |
|
|
| 12 |
status |
상태 |
select |
✅ |
옵션: 수주, 진행중, 완료, 취소 |
| 13 |
delivery_partner_id |
납품처 |
text |
|
|
| 14 |
delivery_address |
납품장소 |
text |
|
|
| 15 |
shipping_method |
배송방법 |
select |
|
옵션: 택배, 화물, 직송, 퀵서비스 등 |
| 16 |
manager_name |
담당자 |
text |
|
|
| 17 |
memo |
메모 |
textarea |
|
|
Step 3: 버튼 배치
| 버튼 |
액션 타입 |
스타일 |
설정 |
| 저장 |
저장 |
primary |
저장 후 모달 닫기, 부모 화면 테이블 새로고침 |
| 취소 |
모달 닫기 |
secondary |
|
4. 컴포넌트 연동 설정
4.1 이벤트 흐름
[검색 입력]
│
▼
v2-table-search-widget
│ onFilterChange
▼
v2-table-list (자동 재조회)
│
▼
[데이터 표시]
│
▼
v2-aggregation-widget (통계 업데이트)
[수주등록/수정 버튼 클릭]
│
▼
[모달 열기] → [폼 입력] → [저장]
│ │
│ ▼
│ refreshTable 이벤트
│ │
└────────────────────────┘
│
▼
v2-table-list (재조회)
│
▼
v2-aggregation-widget (통계 업데이트)
4.2 연동 설정
| 소스 컴포넌트 |
이벤트/액션 |
대상 컴포넌트 |
동작 |
| 검색 위젯 |
onFilterChange |
테이블 리스트 |
필터 적용, 재조회 |
| 테이블 리스트 |
onDataChange |
집계 위젯 |
통계 업데이트 |
| 수주등록 버튼 |
click |
모달 |
빈 폼으로 모달 열기 |
| 수정 버튼 |
click |
모달 |
선택 데이터가 채워진 폼 열기 (수정) |
| 삭제 버튼 |
click |
테이블 리스트 |
선택 항목 삭제 |
| 출하계획 버튼 |
click |
슬라이드 패널 |
선택 항목 기반 출하계획 생성 |
| 모달 저장 |
afterSave |
테이블 리스트 |
refreshTable |
5. 사용자 사용 예시 시나리오
시나리오 1: 수주 조회
| 단계 |
사용자 동작 |
기대 결과 |
| 1 |
화면 진입 |
전체 수주 목록 표시, 통계(총 금액, 총 수량) 표시 |
| 2 |
거래처 필터를 "삼성전자"로 선택 |
자동 필터링, 통계 업데이트 |
| 3 |
상태를 "진행중"으로 선택 |
추가 필터링 |
| 4 |
Group by에서 "거래처" 선택 |
거래처별 그룹핑 표시 |
시나리오 2: 수주 등록
| 단계 |
사용자 동작 |
기대 결과 |
| 1 |
[수주 등록] 버튼 클릭 |
빈 폼 모달 표시 |
| 2 |
거래처 선택, 품목코드 입력 |
품명, 규격 자동 입력 |
| 3 |
수주수량, 단가 입력 |
금액 자동 계산 |
| 4 |
[저장] 버튼 클릭 |
저장 완료, 모달 닫힘, 목록 갱신, 통계 업데이트 |
시나리오 3: 수주 수정
| 단계 |
사용자 동작 |
기대 결과 |
| 1 |
테이블에서 행 체크박스 선택 |
행 선택 표시 |
| 2 |
[수정] 버튼 클릭 |
수정 모달 표시 (기존 데이터 로드) |
| 3 |
데이터 수정 |
필드 값 변경 |
| 4 |
[저장] 버튼 클릭 |
저장 완료, 목록 갱신 |
시나리오 4: 수주 삭제
| 단계 |
사용자 동작 |
기대 결과 |
| 1 |
삭제할 행 체크박스 선택 (다중 가능) |
행 선택 표시 |
| 2 |
[삭제] 버튼 클릭 |
삭제 확인 다이얼로그 표시 |
| 3 |
확인 |
삭제 완료, 목록 갱신, 통계 업데이트 |
시나리오 5: 출하계획 생성
| 단계 |
사용자 동작 |
기대 결과 |
| 1 |
출하할 수주 행 체크박스 선택 (다중) |
행 선택 표시 |
| 2 |
[출하계획] 버튼 클릭 |
출하계획 슬라이드 패널 열림 |
| 3 |
출하 수량 입력, 출하일 선택 |
출하계획 데이터 설정 |
| 4 |
[적용] 버튼 클릭 |
출하계획 저장, 수주 데이터 업데이트 |
6. 검증 체크리스트
기본 기능
테이블 기능
검색 위젯 연동
그룹핑 기능
통계 기능
출하계획 연동
7. 참고 사항
관련 테이블
customer_mng - 거래처 정보 (partner_id 참조)
item_info - 품목 정보 (part_code 참조)
sales_order_detail - 수주 상세 (다중 품목 관리 시)
shipment_mng - 출하 정보 (출하계획 연동)
특이 사항
partner_id는 거래처 테이블의 ID를 참조 (조인 필요)
balance_qty = order_qty - ship_qty (잔량 자동 계산)
total_amount = order_qty × unit_price (금액 자동 계산)
- 상태별 뱃지 색상:
- 수주: 파란색 (#dbeafe, #1e40af)
- 진행중: 노란색 (#fef3c7, #92400e)
- 완료: 초록색 (#d1fae5, #065f46)
- 취소: 빨간색 (#fee2e2, #991b1b)
- 수출용 필드: incoterms, payment_term, port_of_loading, port_of_discharge, hs_code, currency
8. DB INSERT용 JSON 설정 (screen_layouts_v2 방식)
📌 실제 화면 저장은 screen_definitions + screen_layouts_v2 테이블을 사용합니다.
⚠️ 주의: 아래 JSON은 "수주관리" 화면 전용 예시입니다!
8.1 화면 정의 (screen_definitions)
필수 입력 필드:
{
"screenName": "수주관리",
"tableName": "sales_order_mng",
"companyCode": "COMPANY_7",
"description": "수주 관리 화면"
}
8.2 레이아웃 데이터 (screen_layouts_v2.layout_data)
{
"version": "2.0",
"components": [
{
"id": "comp_search",
"url": "@/lib/registry/components/v2-table-search-widget",
"size": { "width": 1920, "height": 80 },
"position": { "x": 0, "y": 20, "z": 1 },
"overrides": {
"type": "v2-table-search-widget",
"label": "검색 필터",
"webTypeConfig": {}
},
"displayOrder": 0
},
{
"id": "comp_table",
"url": "@/lib/registry/components/v2-table-list",
"size": { "width": 1920, "height": 800 },
"position": { "x": 0, "y": 150, "z": 1 },
"overrides": {
"type": "v2-table-list",
"label": "수주 목록",
"filter": { "enabled": true, "filters": [] },
"height": "auto",
"actions": { "actions": [], "bulkActions": false, "showActions": false },
"columns": [
{ "align": "left", "order": 0, "format": "text", "visible": true, "sortable": true, "columnName": "order_no", "searchable": true, "displayName": "수주번호" },
{ "align": "left", "order": 1, "format": "text", "visible": true, "sortable": true, "columnName": "partner_id", "searchable": true, "displayName": "거래처" },
{ "align": "left", "order": 2, "format": "text", "visible": true, "sortable": true, "columnName": "part_code", "searchable": true, "displayName": "품목코드" },
{ "align": "left", "order": 3, "format": "text", "visible": true, "sortable": true, "columnName": "part_name", "searchable": true, "displayName": "품명" },
{ "align": "left", "order": 4, "format": "text", "visible": true, "sortable": true, "columnName": "spec", "searchable": true, "displayName": "규격" },
{ "align": "left", "order": 5, "format": "text", "visible": true, "sortable": true, "columnName": "material", "searchable": true, "displayName": "재질" },
{ "align": "right", "order": 6, "format": "number", "visible": true, "sortable": true, "columnName": "order_qty", "searchable": false, "displayName": "수주수량" },
{ "align": "right", "order": 7, "format": "number", "visible": true, "sortable": true, "columnName": "ship_qty", "searchable": false, "displayName": "출하수량" },
{ "align": "right", "order": 8, "format": "number", "visible": true, "sortable": true, "columnName": "balance_qty", "searchable": false, "displayName": "잔량" },
{ "align": "right", "order": 9, "format": "number", "visible": true, "sortable": true, "columnName": "inventory_qty", "searchable": false, "displayName": "현재고" },
{ "align": "right", "order": 10, "format": "number", "visible": true, "sortable": true, "columnName": "plan_ship_qty", "searchable": false, "displayName": "출하계획량" },
{ "align": "right", "order": 11, "format": "number", "visible": true, "sortable": true, "columnName": "unit_price", "searchable": false, "displayName": "단가" },
{ "align": "right", "order": 12, "format": "number", "visible": true, "sortable": true, "columnName": "total_amount", "searchable": false, "displayName": "금액" },
{ "align": "left", "order": 13, "format": "text", "visible": true, "sortable": true, "columnName": "delivery_partner_id", "searchable": true, "displayName": "납품처" },
{ "align": "left", "order": 14, "format": "text", "visible": true, "sortable": true, "columnName": "delivery_address", "searchable": true, "displayName": "납품장소" },
{ "align": "center", "order": 15, "format": "text", "visible": true, "sortable": true, "columnName": "shipping_method", "searchable": true, "displayName": "배송방법" },
{ "align": "center", "order": 16, "format": "date", "visible": true, "sortable": true, "columnName": "due_date", "searchable": false, "displayName": "납기일" },
{ "align": "center", "order": 17, "format": "date", "visible": true, "sortable": true, "columnName": "order_date", "searchable": false, "displayName": "수주일" },
{ "align": "center", "order": 18, "format": "text", "visible": true, "sortable": true, "columnName": "status", "searchable": true, "displayName": "상태" },
{ "align": "left", "order": 19, "format": "text", "visible": true, "sortable": true, "columnName": "manager_name", "searchable": true, "displayName": "담당자" },
{ "align": "left", "order": 20, "format": "text", "visible": true, "sortable": true, "columnName": "memo", "searchable": true, "displayName": "메모" }
],
"autoLoad": true,
"checkbox": { "enabled": true, "multiple": true, "position": "left", "selectAll": true },
"pagination": { "enabled": true, "pageSize": 20, "showPageInfo": true, "pageSizeOptions": [10, 20, 50, 100], "showSizeSelector": true },
"showFooter": true,
"showHeader": true,
"tableStyle": { "theme": "default", "rowHeight": "normal", "borderStyle": "light", "headerStyle": "default", "hoverEffect": true, "alternateRows": true },
"displayMode": "table",
"stickyHeader": false,
"selectedTable": "sales_order_mng",
"webTypeConfig": {},
"horizontalScroll": { "enabled": true, "maxColumnWidth": 300, "minColumnWidth": 80, "maxVisibleColumns": 10 }
},
"displayOrder": 1
},
{
"id": "comp_btn_user_options",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 100, "height": 40 },
"position": { "x": 1500, "y": 30, "z": 1 },
"overrides": {
"text": "사용자옵션",
"type": "v2-button-primary",
"label": "사용자옵션 버튼",
"action": { "type": "custom" },
"variant": "secondary",
"actionType": "button",
"webTypeConfig": { "variant": "secondary", "actionType": "custom" }
},
"displayOrder": 2
},
{
"id": "comp_btn_upload",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 100, "height": 40 },
"position": { "x": 1610, "y": 30, "z": 1 },
"overrides": {
"text": "엑셀 업로드",
"type": "v2-button-primary",
"label": "엑셀 업로드 버튼",
"action": { "type": "excel_upload" },
"variant": "secondary",
"actionType": "button",
"webTypeConfig": { "variant": "secondary", "actionType": "custom" }
},
"displayOrder": 3
},
{
"id": "comp_btn_download",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 100, "height": 40 },
"position": { "x": 1720, "y": 30, "z": 1 },
"overrides": {
"text": "엑셀 다운로드",
"type": "v2-button-primary",
"label": "엑셀 다운로드 버튼",
"action": { "type": "excel_download" },
"variant": "secondary",
"actionType": "button",
"webTypeConfig": { "variant": "secondary", "actionType": "custom" }
},
"displayOrder": 4
},
{
"id": "comp_btn_register",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 100, "height": 40 },
"position": { "x": 1500, "y": 100, "z": 1 },
"overrides": {
"text": "수주 등록",
"type": "v2-button-primary",
"label": "수주 등록 버튼",
"action": {
"type": "modal",
"modalSize": "lg",
"modalTitle": "수주 등록",
"targetScreenId": "{{modal_screen_id}}",
"successMessage": "저장되었습니다.",
"errorMessage": "저장 중 오류가 발생했습니다."
},
"variant": "success",
"actionType": "button",
"webTypeConfig": { "variant": "default", "actionType": "custom" }
},
"displayOrder": 5
},
{
"id": "comp_btn_edit",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 80, "height": 40 },
"position": { "x": 1610, "y": 100, "z": 1 },
"overrides": {
"text": "수정",
"type": "v2-button-primary",
"label": "수정 버튼",
"action": {
"type": "edit",
"modalSize": "lg",
"modalTitle": "수주 수정",
"targetScreenId": "{{modal_screen_id}}",
"successMessage": "수정되었습니다.",
"errorMessage": "수정 중 오류가 발생했습니다."
},
"variant": "secondary",
"actionType": "button",
"webTypeConfig": { "variant": "secondary", "actionType": "custom" }
},
"displayOrder": 6
},
{
"id": "comp_btn_delete",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 80, "height": 40 },
"position": { "x": 1700, "y": 100, "z": 1 },
"overrides": {
"text": "삭제",
"type": "v2-button-primary",
"label": "삭제 버튼",
"action": {
"type": "delete",
"successMessage": "삭제되었습니다.",
"errorMessage": "삭제 중 오류가 발생했습니다."
},
"variant": "secondary",
"actionType": "button",
"webTypeConfig": { "variant": "secondary", "actionType": "custom" }
},
"displayOrder": 7
},
{
"id": "comp_btn_shipment",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 100, "height": 40 },
"position": { "x": 1790, "y": 100, "z": 1 },
"overrides": {
"text": "출하계획",
"type": "v2-button-primary",
"label": "출하계획 버튼",
"action": { "type": "custom" },
"variant": "secondary",
"actionType": "button",
"webTypeConfig": { "variant": "secondary", "actionType": "custom" }
},
"displayOrder": 8
}
]
}
8.3 모달 화면 (수주 등록/수정)
화면 정의 (필수 입력)
{
"screenName": "수주 등록/수정",
"tableName": "sales_order_mng",
"companyCode": "COMPANY_7",
"description": "수주 등록/수정 폼 화면"
}
레이아웃 데이터 (screen_layouts_v2.layout_data)
{
"version": "2.0",
"components": [
{
"id": "comp_order_no",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 20, "y": 20, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "수주번호",
"fieldName": "order_no",
"placeholder": "수주번호를 입력하세요",
"required": true
},
"displayOrder": 0
},
{
"id": "comp_order_date",
"url": "@/lib/registry/components/v2-date",
"size": { "width": 300, "height": 60 },
"position": { "x": 340, "y": 20, "z": 1 },
"overrides": {
"type": "v2-date",
"label": "수주일",
"fieldName": "order_date",
"required": true
},
"displayOrder": 1
},
{
"id": "comp_partner_id",
"url": "@/lib/registry/components/v2-select",
"size": { "width": 300, "height": 60 },
"position": { "x": 20, "y": 100, "z": 1 },
"overrides": {
"type": "v2-select",
"label": "거래처",
"fieldName": "partner_id",
"required": true,
"config": {
"mode": "dropdown",
"source": "table",
"sourceTable": "customer_mng",
"valueField": "id",
"labelField": "name"
}
},
"displayOrder": 2
},
{
"id": "comp_part_code",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 340, "y": 100, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "품목코드",
"fieldName": "part_code",
"placeholder": "품목코드를 입력하세요",
"required": true
},
"displayOrder": 3
},
{
"id": "comp_part_name",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 20, "y": 180, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "품명",
"fieldName": "part_name",
"placeholder": "품명을 입력하세요"
},
"displayOrder": 4
},
{
"id": "comp_spec",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 340, "y": 180, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "규격",
"fieldName": "spec",
"placeholder": "규격을 입력하세요"
},
"displayOrder": 5
},
{
"id": "comp_material",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 20, "y": 260, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "재질",
"fieldName": "material",
"placeholder": "재질을 입력하세요"
},
"displayOrder": 6
},
{
"id": "comp_order_qty",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 340, "y": 260, "z": 1 },
"overrides": {
"type": "v2-input",
"inputType": "number",
"label": "수주수량",
"fieldName": "order_qty",
"placeholder": "수주수량을 입력하세요",
"required": true
},
"displayOrder": 7
},
{
"id": "comp_unit_price",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 20, "y": 340, "z": 1 },
"overrides": {
"type": "v2-input",
"inputType": "number",
"label": "단가",
"fieldName": "unit_price",
"placeholder": "단가를 입력하세요",
"required": true
},
"displayOrder": 8
},
{
"id": "comp_due_date",
"url": "@/lib/registry/components/v2-date",
"size": { "width": 300, "height": 60 },
"position": { "x": 340, "y": 340, "z": 1 },
"overrides": {
"type": "v2-date",
"label": "납기일",
"fieldName": "due_date"
},
"displayOrder": 9
},
{
"id": "comp_status",
"url": "@/lib/registry/components/v2-select",
"size": { "width": 300, "height": 60 },
"position": { "x": 20, "y": 420, "z": 1 },
"overrides": {
"type": "v2-select",
"label": "상태",
"fieldName": "status",
"required": true,
"config": {
"mode": "dropdown",
"source": "static",
"options": [
{ "value": "수주", "label": "수주" },
{ "value": "진행중", "label": "진행중" },
{ "value": "완료", "label": "완료" },
{ "value": "취소", "label": "취소" }
]
}
},
"displayOrder": 10
},
{
"id": "comp_shipping_method",
"url": "@/lib/registry/components/v2-select",
"size": { "width": 300, "height": 60 },
"position": { "x": 340, "y": 420, "z": 1 },
"overrides": {
"type": "v2-select",
"label": "배송방법",
"fieldName": "shipping_method",
"config": {
"mode": "dropdown",
"source": "static",
"options": [
{ "value": "택배", "label": "택배" },
{ "value": "화물", "label": "화물" },
{ "value": "직송", "label": "직송" },
{ "value": "퀵서비스", "label": "퀵서비스" },
{ "value": "해상운송", "label": "해상운송" }
]
}
},
"displayOrder": 11
},
{
"id": "comp_delivery_address",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 620, "height": 60 },
"position": { "x": 20, "y": 500, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "납품장소",
"fieldName": "delivery_address",
"placeholder": "납품장소를 입력하세요"
},
"displayOrder": 12
},
{
"id": "comp_manager_name",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 300, "height": 60 },
"position": { "x": 20, "y": 580, "z": 1 },
"overrides": {
"type": "v2-input",
"label": "담당자",
"fieldName": "manager_name",
"placeholder": "담당자를 입력하세요"
},
"displayOrder": 13
},
{
"id": "comp_memo",
"url": "@/lib/registry/components/v2-input",
"size": { "width": 620, "height": 80 },
"position": { "x": 20, "y": 660, "z": 1 },
"overrides": {
"type": "v2-input",
"inputType": "textarea",
"label": "메모",
"fieldName": "memo",
"placeholder": "메모를 입력하세요"
},
"displayOrder": 14
},
{
"id": "comp_btn_save",
"url": "@/lib/registry/components/v2-button-primary",
"size": { "width": 100, "height": 40 },
"position": { "x": 540, "y": 760, "z": 1 },
"overrides": {
"text": "저장",
"type": "v2-button-primary",
"label": "저장 버튼",
"action": {
"type": "save",
"closeModalAfterSave": true,
"refreshParentTable": true,
"successMessage": "저장되었습니다.",
"errorMessage": "저장 중 오류가 발생했습니다."
},
"variant": "primary",
"actionType": "button"
},
"displayOrder": 15
}
]
}
8.4 화면 생성 순서 (중요!)
1. 모달 화면 생성 (screen_definitions INSERT)
│
▼
2. 모달 레이아웃 저장 (screen_layouts_v2 INSERT)
│
▼
3. 메인 화면 생성 (screen_definitions INSERT)
│
▼
4. 메인 레이아웃 저장 (screen_layouts_v2 INSERT)
└── targetScreenId에 모달 screen_id 사용!
│
▼
5. (선택) 메뉴에 화면 연결
9. 화면 구현 체크리스트
📋 새로운 화면을 구현할 때 아래 체크리스트를 순서대로 확인하세요.
9.1 분석 단계
| 체크 |
항목 |
설명 |
| ☐ |
테이블 구조 분석 |
sales_order_mng 테이블 스키마 확인 완료 |
| ☐ |
화면 기능 파악 |
조회/등록/수정/삭제, 검색, 필터, 그룹핑, 출하계획 연동 |
| ☐ |
컴포넌트 매핑 |
필요 기능 → V2 컴포넌트 매핑 완료 |
| ☐ |
구현 불가 항목 확인 |
현재 V2 컴포넌트로 구현 가능 |
9.2 INSERT 후 검증
| 체크 |
항목 |
설명 |
| ☐ |
화면 접속 테스트 |
/screens/{screen_id} URL로 접속 |
| ☐ |
컴포넌트 렌더링 확인 |
모든 컴포넌트가 정상 표시되는지 확인 |
| ☐ |
검색 기능 테스트 |
검색 위젯 동작 확인 |
| ☐ |
테이블 데이터 로드 |
테이블에 데이터 표시되는지 확인 |
| ☐ |
버튼 동작 테스트 |
등록/수정/삭제/출하계획 버튼 동작 확인 |
| ☐ |
모달 폼 테스트 |
모달 열림, 입력 필드 표시, 저장 동작 확인 |
| ☐ |
통계 업데이트 |
총 금액, 총 수량이 정확히 표시되는지 확인 |
10. 메뉴에 화면 연결하기
📋 화면 생성 후, 특정 메뉴에 연결하여 사용자가 접근할 수 있도록 설정합니다.
10.1 메뉴 연결 절차
1. 대상 메뉴 찾기 (menu_info 테이블에서 objid 확인)
│
▼
2. screen_menu_assignments 테이블에 할당 레코드 INSERT
│
▼
3. menu_info 테이블의 menu_url, screen_code 업데이트
│
▼
4. 연결 결과 확인
10.2 메뉴 찾기
-- 메뉴 이름으로 검색
SELECT objid, menu_name_kor, menu_url, screen_code, company_code
FROM menu_info
WHERE menu_name_kor = '55566' -- 메뉴 이름
AND company_code = 'COMPANY_19'; -- 회사 코드
-- 결과 예시:
-- objid: 1769415229091
10.3 기존 할당 확인 및 제거 (중복 방지)
⚠️ 중요: 새 화면을 할당하기 전에 해당 메뉴에 이미 할당된 화면이 있는지 확인해야 합니다. 중복 할당 시 화면이 정상적으로 표시되지 않을 수 있습니다.
-- 1. 해당 메뉴에 이미 할당된 화면 확인
SELECT
sma.assignment_id,
sma.screen_id,
sd.screen_name,
sd.screen_code
FROM screen_menu_assignments sma
JOIN screen_definitions sd ON sma.screen_id = sd.screen_id
WHERE sma.menu_objid = '1769415229091'; -- 대상 메뉴 objid
-- 2. 기존 할당이 있다면 삭제
DELETE FROM screen_menu_assignments
WHERE menu_objid = '1769415229091'; -- 모든 기존 할당 삭제
-- 또는 특정 화면만 남기고 삭제
DELETE FROM screen_menu_assignments
WHERE menu_objid = '1769415229091'
AND screen_id != 3733; -- 3733(수주관리)만 남기고 삭제
10.4 화면-메뉴 할당 INSERT
-- screen_menu_assignments에 할당 레코드 추가
INSERT INTO screen_menu_assignments (
screen_id,
menu_objid,
company_code,
display_order,
is_active,
created_date
) VALUES (
3733, -- 메인 화면의 screen_id
'1769415229091', -- menu_info의 objid (문자열로 저장)
'COMPANY_19', -- 회사 코드
1, -- 표시 순서
'Y', -- 활성화 여부
NOW()
) RETURNING assignment_id;
10.6 메뉴 URL 및 screen_code 업데이트 (필수!)
⚠️ 중요: screen_menu_assignments에 레코드를 추가해도 menu_info의 menu_url과 screen_code를 업데이트하지 않으면 메뉴 클릭 시 화면이 표시되지 않습니다.
-- menu_info 테이블의 menu_url, screen_code 업데이트
UPDATE menu_info
SET menu_url = '/screens/3733', -- 화면 URL
screen_code = 'COMPANY_19_SO_MAIN' -- 화면 코드
WHERE objid = 1769415229091;
10.7 연결 확인
-- 메뉴-화면 연결 상태 확인
SELECT
mi.objid,
mi.menu_name_kor,
mi.menu_url,
mi.screen_code,
sd.screen_id,
sd.screen_name
FROM menu_info mi
JOIN screen_definitions sd ON mi.screen_code = sd.screen_code
WHERE mi.objid = 1769415229091;
-- 예상 결과:
-- objid: 1769415229091
-- menu_name_kor: 55566
-- menu_url: /screens/3733
-- screen_code: COMPANY_19_SO_MAIN
-- screen_id: 3733
-- screen_name: 수주관리
10.8 전체 SQL 예시 (수주관리 화면 → 55566 메뉴)
-- 1. 메뉴 찾기
SELECT objid, menu_name_kor FROM menu_info
WHERE menu_name_kor = '55566' AND company_code = 'COMPANY_19';
-- 결과: objid = 1769415229091
-- 2. 기존 할당 확인 및 삭제 (중복 방지!)
SELECT sma.assignment_id, sma.screen_id, sd.screen_name
FROM screen_menu_assignments sma
JOIN screen_definitions sd ON sma.screen_id = sd.screen_id
WHERE sma.menu_objid = '1769415229091';
-- 기존 할당이 있다면 삭제
DELETE FROM screen_menu_assignments
WHERE menu_objid = '1769415229091';
-- 3. 새 화면 할당
INSERT INTO screen_menu_assignments (screen_id, menu_objid, company_code, display_order, is_active, created_date)
VALUES (3733, '1769415229091', 'COMPANY_19', 1, 'Y', NOW());
-- 4. 메뉴 URL 업데이트 (필수!)
UPDATE menu_info
SET menu_url = '/screens/3733',
screen_code = 'COMPANY_19_SO_MAIN'
WHERE objid = 1769415229091;
10.9 메뉴 연결 체크리스트
| 체크 |
항목 |
설명 |
| ☐ |
대상 메뉴 확인 |
menu_info에서 메뉴 objid 확인 |
| ☐ |
기존 할당 확인 |
screen_menu_assignments에서 중복 할당 여부 확인 |
| ☐ |
기존 할당 삭제 |
중복 할당이 있다면 기존 레코드 DELETE |
| ☐ |
새 화면 할당 INSERT |
screen_menu_assignments 테이블에 새 레코드 추가 |
| ☐ |
menu_url 업데이트 |
/screens/{screen_id} 형식으로 업데이트 |
| ☐ |
screen_code 업데이트 |
화면의 screen_code로 업데이트 |
| ☐ |
메뉴 클릭 테스트 |
해당 회사로 로그인하여 메뉴 클릭 시 화면 표시 확인 |