Skip to main content

화면 구성 — 고급

기본 화면 문법은 화면 구성 (기본)을 참조하세요.

전표/문서 화면 — screenType: "document"

적합 대상: 견적서, 수주서, 발주서, 출하지시서 등 헤더 + 행 항목 + 합계가 필요한 비즈니스 문서

screenType: "document"는 두 가지 렌더링 모드를 지원합니다:

renderMode설명적합 대상
"split" (기본)좌측 목록 + 우측 헤더/라인아이템 분할 화면간단한 전표, 빠른 문서 탐색
"full-page"목록 → 전체 화면 폼 → 목록 전환 (SAP Fiori 패턴)견적서, 수주서, 발주서 등 복잡한 전표
renderMode 선택 가이드
  • 헤더 필드 5개 이하 + 라인아이템 단순"split" 적합
  • 헤더 필드 6개 이상 + 라인아이템 + 합계 + 상태 워크플로우"full-page" 권장
  • 미지정 시 "split" 으로 폴백 → 모든 기존 프리셋이 수정 없이 동작

기본 구조 — split 모드 (기본)

{
"refId": "screen_quotations",
"label": "견적서 관리",
"screenType": "document",
"menuIcon": "SolutionOutlined",
"menuOrder": 1,
"boundTableRef": "quotations",
"parentMenuRef": "menu_quotation",

"listConfig": { "...": "좌측 문서 목록 컬럼/필터/정렬 설정" },
"formConfig": { "...": "우측 헤더 폼 섹션/필드 설정" },

"documentConfig": {
"lineItems": [
{
"title": "견적항목",
"boundTableRef": "quotation_items",
"foreignKeyColumn": "qt_id",
"sortColumn": "line_no",
"columns": [
{ "columnName": "line_no", "label": "순번", "width": 60 },
{ "columnName": "item_id", "label": "품목", "width": 150 },
{ "columnName": "quantity", "label": "수량", "width": 100, "render": "number" },
{ "columnName": "unit_price", "label": "단가", "width": 120, "render": "currency" },
{ "columnName": "discount_rate", "label": "할인율(%)", "width": 100 },
{ "columnName": "amount", "label": "금액", "width": 120, "render": "currency" }
],
"addable": true,
"deletable": true,
"itemLookupRef": "items",
"itemFkColumn": "item_id",
"itemAutoFillMappings": [
{ "sourceColumn": "unit_price", "targetColumn": "unit_price" }
]
}
],

"footerSummary": {
"summaryFields": [
{ "label": "합계금액", "sourceColumn": "amount", "aggregation": "SUM", "targetHeaderColumn": "total_amount", "format": "currency" },
{ "label": "부가세액", "targetHeaderColumn": "vat_amount", "format": "currency" },
{ "label": "총합계", "targetHeaderColumn": "grand_total", "format": "currency" }
]
},

"documentNumbering": {
"prefix": "QT",
"dateFormat": "YYYYMM",
"sequenceDigits": 4,
"resetPeriod": "yearly",
"targetColumn": "qt_number"
},

"statusFlow": {
"statusColumn": "status",
"initialStatus": "작성",
"finalStatuses": ["수주전환", "실주", "만료"],
"transitions": [
{ "from": "작성", "to": "제출", "label": "제출" },
{ "from": "제출", "to": "협상", "label": "협상 시작" },
{ "from": "협상", "to": "수주전환", "label": "수주 전환" }
]
},

"allowCopy": true,

"crossCalculations": [
{ "targetColumn": "total_amount", "expression": "SUM(quotation_items.amount)", "dependencies": ["quotation_items"], "isAggregation": true },
{ "targetColumn": "vat_amount", "expression": "total_amount * 0.1", "dependencies": ["total_amount"] },
{ "targetColumn": "grand_total", "expression": "total_amount + vat_amount", "dependencies": ["total_amount", "vat_amount"] }
],

"layout": "horizontal",
"defaultSplitRatio": 0.35
}
}

documentConfig 필드 명세

필드타입필수설명
lineItemsarray필수행 항목 정의 (1개 이상). 서브테이블 확장
footerSummaryobject선택문서 하단 합계 영역 (합계, 세금 등)
documentNumberingobject선택전표번호 자동 채번 설정
statusFlowobject선택문서 상태 워크플로우 (상태 전이 버튼)
allowCopyboolean선택문서 복사 허용 여부 (기본: false)
crossCalculationsarray선택교차 계산 — 라인 합계 → 헤더 필드 실시간 재계산
renderModestring선택"split" (기본) 또는 "full-page" — 아래 renderMode 상세 참조
listViewConfigobject선택full-page 모드: 목록 화면 설정 (pageSize, displayColumns 등)
formLayoutConfigobject선택full-page 모드: 폼 레이아웃 설정 (headerSections, headerColumns 등)
layoutstring선택"horizontal" (기본) 또는 "vertical" — split 모드 전용
defaultSplitRationumber선택좌측 목록 영역 비율 (기본 0.35) — split 모드 전용

lineItems 필드 명세

lineItemsformConfig.subtables의 확장으로, 전표 전용 추가 속성을 포함합니다.

필드타입필수설명
titlestring필수서브테이블 제목 (한글)
boundTableRefstring필수행 항목 테이블 refId
foreignKeyColumnstring필수부모(헤더)를 참조하는 FK 컬럼
sortColumnstring선택행 정렬 컬럼 (예: "line_no")
columnsarray필수표시할 컬럼 목록
addableboolean선택행 추가 허용 (기본: true)
deletableboolean선택행 삭제 허용 (기본: true)
itemLookupRefstring선택품목/자재 참조 테이블 refId (FK lookup)
itemFkColumnstring선택품목 FK 컬럼명 (예: "item_id")
itemAutoFillMappingsarray선택품목 선택 시 자동 복사 매핑

footerSummary 필드 명세

필드타입필수설명
summaryFieldsarray필수합계 항목 배열
summaryFields[].labelstring필수표시 라벨 (한글)
summaryFields[].sourceColumnstring선택라인 항목에서 집계할 컬럼
summaryFields[].aggregationstring선택집계 함수: "SUM", "AVG", "COUNT"
summaryFields[].targetHeaderColumnstring선택결과를 저장할 헤더 컬럼
summaryFields[].formatstring선택표시 형식: "currency", "number", "percent"

statusFlow 필드 명세

필드타입필수설명
statusColumnstring필수상태 ENUM 컬럼명
initialStatusstring필수최초 상태 (예: "작성")
finalStatusesstring[]필수종료 상태 배열
transitionsarray필수상태 전이 규칙 배열
transitions[].fromstring필수시작 상태
transitions[].tostring필수도착 상태
transitions[].labelstring필수전이 버튼 라벨 (한글)

documentConfig 규칙

  1. screenType: "document" 일 때만 documentConfig 유효
  2. lineItemsboundTableRef는 같은 JSON 내 테이블 refId와 일치해야 함
  3. lineItemsforeignKeyColumn은 행 항목 테이블에 실제 존재하는 FK 컬럼
  4. footerSummary.summaryFields[].targetHeaderColumn은 헤더 테이블(boundTableRef)의 실제 컬럼
  5. statusFlow.transitionsfrom/to는 헤더 테이블의 상태 ENUM 값과 일치
  6. crossCalculationstargetColumn은 헤더 테이블의 실제 컬럼
  7. listConfigformConfig를 함께 정의하면 각각 좌측 목록과 우측 헤더 폼으로 활용됨

화면 레이아웃

┌─────────────┬────────────────────────────┐
│ 문서 목록 │ 헤더 폼 │
│ (listConfig) │ (formConfig) │
│ │ │
│ QT-001 ✅ │ 기본정보: 견적번호, 거래처 │
│ QT-002 🔄 │ 금액: 할인율, 합계, 부가세 │
│ QT-003 📝 │ │
│ │ ───────────────────── │
│ [+ 신규] │ 품목 테이블 (lineItems) │
│ │ ┌──┬────┬──┬────┬────┐ │
│ │ │No│품목│수량│단가│금액│ │
│ │ ├──┼────┼──┼────┼────┤ │
│ │ │1 │볼트│100│500│50K │ │
│ │ │ │[+행 추가] │ │
│ │ └──┴────┴──┴────┴────┘ │
│ │ │
│ │ 합계 영역 (footerSummary) │
│ │ 합계금액: 110,000 │
│ │ 부가세: 11,000 │
│ │ 총합계: 121,000 │
│ │ │
│ │ [저장] [제출] [수주전환] │
│ │ (statusFlow transitions) │
└─────────────┴────────────────────────────┘

renderMode: "full-page" (전체 화면 전환 모드)

v2.3.0 신규 — SAP Fiori Object Page, Oracle Fusion, Odoo 스타일의 목록 → 전체 화면 폼 → 목록 복귀 패턴

renderMode: "full-page"를 설정하면 좌우 분할 대신, 목록 화면과 폼 화면이 전체 페이지 단위로 전환됩니다.

full-page 기본 구조

{
"refId": "screen_quotations",
"label": "견적서 관리",
"screenType": "document",
"menuIcon": "SolutionOutlined",
"menuOrder": 1,
"boundTableRef": "quotations",
"parentMenuRef": "menu_quotation",

"listConfig": {
"columns": [
{ "columnName": "qt_number", "width": 150, "sortable": true, "searchable": true },
{ "columnName": "partner_id", "width": 200, "filterable": true },
{ "columnName": "qt_date", "width": 120, "sortable": true },
{ "columnName": "status", "width": 100, "filterable": true },
{ "columnName": "grand_total", "width": 150, "render": "currency" }
],
"filters": [
{ "columnName": "status", "filterType": "select", "label": "상태" }
],
"defaultSort": { "columnName": "qt_date", "order": "descend" },
"pageSize": 20
},

"documentConfig": {
"renderMode": "full-page",

"lineItems": [
{
"title": "견적항목",
"boundTableRef": "quotation_items",
"foreignKeyColumn": "qt_id",
"sortColumn": "line_no",
"columns": [
{ "columnName": "line_no", "label": "순번", "width": 60 },
{ "columnName": "item_id", "label": "품목", "width": 150 },
{ "columnName": "quantity", "label": "수량", "width": 100, "render": "number" },
{ "columnName": "unit_price", "label": "단가", "width": 120, "render": "currency" },
{ "columnName": "discount_rate", "label": "할인율(%)", "width": 100 },
{ "columnName": "amount", "label": "금액", "width": 120, "render": "currency" }
],
"addable": true,
"deletable": true,
"itemLookupRef": "items",
"itemFkColumn": "item_id",
"itemAutoFillMappings": [
{ "sourceColumn": "unit_price", "targetColumn": "unit_price" }
]
}
],

"footerSummary": {
"summaryFields": [
{ "label": "합계금액", "sourceColumn": "amount", "aggregation": "SUM", "targetHeaderColumn": "total_amount", "format": "currency" },
{ "label": "부가세액", "targetHeaderColumn": "vat_amount", "format": "currency" },
{ "label": "총합계", "targetHeaderColumn": "grand_total", "format": "currency" }
]
},

"documentNumbering": {
"prefix": "QT",
"dateFormat": "YYYYMM",
"sequenceDigits": 4,
"resetPeriod": "yearly",
"targetColumn": "qt_number"
},

"statusFlow": {
"statusColumn": "status",
"initialStatus": "작성",
"finalStatuses": ["수주전환", "실주", "만료"],
"transitions": [
{ "from": "작성", "to": "제출", "label": "제출" },
{ "from": "제출", "to": "협상", "label": "협상 시작" },
{ "from": "협상", "to": "수주전환", "label": "수주 전환" }
]
},

"allowCopy": true,

"crossCalculations": [
{ "targetColumn": "total_amount", "expression": "SUM(quotation_items.amount)", "dependencies": ["quotation_items"], "isAggregation": true },
{ "targetColumn": "vat_amount", "expression": "total_amount * 0.1", "dependencies": ["total_amount"] },
{ "targetColumn": "grand_total", "expression": "total_amount + vat_amount", "dependencies": ["total_amount", "vat_amount"] }
],

"listViewConfig": {
"pageSize": 20,
"displayColumns": ["qt_number", "partner_id", "qt_date", "status", "grand_total"],
"searchableColumns": ["qt_number", "partner_id"],
"showStatusFilter": true,
"defaultSort": { "columnName": "qt_date", "order": "descend" }
},

"formLayoutConfig": {
"headerColumns": 3,
"lineItemDisplay": "inline",
"stickyHeader": true
}
}
}

full-page 화면 흐름

[메뉴: 견적서] 클릭


┌─────────────────────────────────────────────────────────────┐
│ 견적서 관리 [+ 신규작성] [🔄] │
├─────────────────────────────────────────────────────────────┤
│ 번호 거래처 견적일 상태 합계금액 │
│ QT2603-0001 (주)현대 2026-03-10 ● 수주전환 72,500K │
│ QT2603-0002 (주)삼성 2026-03-11 ● 제출 8,500K │ ← 목록 화면
│ QT2603-0003 (주)LG 2026-03-11 ● 작성 21,000K │ (전체 페이지)
│ ... │
└─────────────────────────────────────────────────────────────┘

│ [+ 신규작성] 또는 행 클릭

┌─────────────────────────────────────────────────────────────┐
│ ← 견적서 관리 / QT2603-0003 [저장] [수주전환] [인쇄]│ ← 브레드크럼+액션
├─────────────────────────────────────────────────────────────┤
│ ┌─ 기본정보 ──────────────────────────────────────────────┐ │
│ │ 견적번호 거래처 * 견적일 * │ │ ← 헤더 폼
│ │ QT2603-0003 [(주)LG ▾] [2026-03-11] │ │
│ │ 유효기한 * 상태 통화 │ │
│ │ [2026-04-11] [작성 ▾] [KRW ▾] │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─ 견적항목 ──────────────────────────────── [+ 행 추가] ─┐ │
│ │ 순번 품목 수량 단가 할인% 금액 │ │ ← 라인아이템
│ │ 1 브레이크캘리퍼 500 ₩145,000 3% ₩70,325,000 │ │
│ │ 2 브레이크패드 200 ₩22,000 0% ₩4,400,000 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─ 합계 ─────────────────────────────────────────────────┐ │
│ │ 공급가액: ₩74,725,000 │ │ ← 푸터 합계
│ │ 부가세액: ₩7,472,500 │ │
│ │ 총 합계: ₩82,197,500 │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

full-page 전용 설정

listViewConfig — 목록 화면 세부 설정

필드타입설명
pageSizenumber페이지당 행 수 (기본 20)
displayColumnsstring[]목록에 표시할 컬럼 refId 배열
searchableColumnsstring[]검색 대상 컬럼 배열
showStatusFilterboolean상태 필터 표시 여부
defaultSortobject기본 정렬: { columnName, order }

formLayoutConfig — 폼 화면 레이아웃 설정

필드타입설명
headerSectionsarray헤더 폼 섹션 정의 (SolutionFormSectionDef[])
headerColumnsnumber헤더 그리드 열 수: 2, 3 (기본), 4
lineItemDisplaystring라인아이템 표시: "inline" (기본), "tabs", "collapsible"
stickyHeaderboolean헤더 고정 여부 (기본 true)

full-page 모드 동작 규칙

  1. renderMode: "full-page" 일 때 layoutdefaultSplitRatio무시됨 (split 전용)
  2. listConfig가 있으면 목록 화면에서 사용, 없으면 listViewConfig로 폴백
  3. 브레드크럼 클릭으로 목록 복귀, 미저장 변경이 있으면 확인 모달 표시
  4. statusFlow.transitions 버튼은 폼 화면 상단 액션바에 표시
  5. 브라우저 뒤로가기/탭 닫기 시에도 미저장 변경 이탈 방지 동작

split → full-page 마이그레이션

기존 split 모드 documentConfig에 renderMode: "full-page"만 추가하면 됩니다:

{
"documentConfig": {
"renderMode": "full-page",
"lineItems": ["... 기존과 동일 ..."],
"footerSummary": {"... 기존과 동일 ..."},
"statusFlow": {"... 기존과 동일 ..."}
}
}
  • lineItems, footerSummary, statusFlow, crossCalculations, documentNumbering, allowCopy모든 기존 설정이 그대로 호환
  • listViewConfig, formLayoutConfig는 선택 — 없으면 시스템이 자동 구성
  • layout, defaultSplitRatio는 full-page에서 무시되므로 남겨두어도 무방

고급 화면 설정 — sourceDocumentConfig (소스 문서 끌고오기)

출하, 생산 등에서 원본 문서(수주, 재고)를 끌고와서 하위 레코드를 자동 생성하는 패턴.

{
"refId": "scr_shipments",
"screenType": "form",
"boundTableRef": "shipments",
"parentMenuRef": "mg_sales",
"formConfig": { "...": "..." },
"sourceDocumentConfig": {
"sources": [
{
"key": "sales_order",
"label": "주문기준",
"icon": "ShoppingCartOutlined",
"sourceTable": "sales_order_items",
"targetTable": "shipment_items",
"filters": [
{ "field": "status", "operator": "=", "value": "확정" },
{ "field": "remaining_ship_qty", "operator": ">", "value": 0 }
],
"displayColumns": [
{
"field": "order_number",
"label": "수주번호",
"source": "parent",
"parentTable": "sales_orders",
"parentFk": "sales_order_id"
},
{
"field": "item_name",
"label": "품목명",
"source": "fk",
"fkTable": "items",
"fkColumn": "item_id"
},
{ "field": "quantity", "label": "주문수량" },
{ "field": "remaining_ship_qty", "label": "미출하수량" }
],
"fieldMapping": [
{ "sourceField": "item_id", "targetField": "item_id" },
{ "sourceField": "remaining_ship_qty", "targetField": "ship_qty" },
{ "sourceField": "id", "targetField": "sales_order_item_id" }
],
"autoFillParent": [
{
"sourceField": "partner_id",
"targetField": "partner_id",
"via": "sales_orders",
"fk": "sales_order_id"
}
]
},
{
"key": "inventory",
"label": "재고기준",
"icon": "DatabaseOutlined",
"sourceTable": "inventory",
"targetTable": "shipment_items",
"filters": [{ "field": "available_qty", "operator": ">", "value": 0 }],
"displayColumns": [
{
"field": "item_name",
"label": "품목명",
"source": "fk",
"fkTable": "items",
"fkColumn": "item_id"
},
{
"field": "warehouse_name",
"label": "창고",
"source": "fk",
"fkTable": "warehouses",
"fkColumn": "warehouse_id"
},
{ "field": "available_qty", "label": "가용재고" }
],
"fieldMapping": [
{ "sourceField": "item_id", "targetField": "item_id" },
{ "sourceField": "available_qty", "targetField": "ship_qty" },
{ "sourceField": "warehouse_id", "targetField": "warehouse_id" }
]
}
]
}
}

sourceDocumentConfig 필드 명세

필드타입설명
sourcesarray소스 문서 유형 목록
sources[].keystring소스 고유 키
sources[].labelstring탭 라벨 (한글)
sources[].iconstring탭 아이콘 (Ant Design)
sources[].sourceTablestring소스 테이블 refId
sources[].targetTablestring생성할 대상(하위) 테이블 refId
sources[].filtersarray소스 행 필터 조건
sources[].displayColumnsarray패널에 표시할 컬럼 목록
sources[].fieldMappingarray소스→대상 필드 매핑
sources[].autoFillParentarray선택 — 소스에서 부모 폼 필드 자동 채움

displayColumns.source 타입

source 값설명
(없음)소스 테이블의 직접 컬럼
"fk"FK를 통해 참조 테이블에서 조회 (fkTable, fkColumn 필요)
"parent"소스의 부모 테이블에서 조회 (parentTable, parentFk 필요)

sourceDocumentConfig 규칙

  1. sourceTable, targetTable은 같은 JSON 내 테이블 refId와 일치해야 함
  2. fieldMappingsourceField는 소스 테이블 컬럼, targetField는 대상 테이블 컬럼
  3. 패널에서 선택한 행들이 fieldMapping 규칙에 따라 대상 서브테이블에 자동 삽입됨
  4. autoFillParent는 첫 번째 선택 행에서 부모 폼 필드(헤더)를 자동 채움

고급 화면 설정 — formulas (집계 수식)

폼 화면에서 서브테이블 행 변경 시 부모 필드를 실시간 재계산하는 수식 정의.

{
"refId": "scr_sales_orders",
"screenType": "form",
"boundTableRef": "sales_orders",
"formConfig": {
"sections": ["..."],
"subtables": [
{
"tableRef": "sales_order_items",
"label": "수주항목",
"fkColumn": "sales_order_id",
"columns": ["item_id", "quantity", "unit_price", "amount"],
"editable": true
}
],
"formulas": [
{
"targetField": "total_amount",
"type": "subtableAggregate",
"subtableRef": "sales_order_items",
"sourceField": "amount",
"function": "SUM"
},
{
"targetField": "total_qty",
"type": "subtableAggregate",
"subtableRef": "sales_order_items",
"sourceField": "quantity",
"function": "SUM"
}
]
}
}

formulas 필드 명세

필드타입설명
targetFieldstring계산 결과가 들어갈 부모 테이블 컬럼명
typestring"subtableAggregate" — 서브테이블 집계
subtableRefstring집계 대상 서브테이블 refId
sourceFieldstring집계 대상 컬럼명
functionstring집계 함수: "SUM", "AVG", "COUNT", "MAX", "MIN"

formulas 규칙

  1. targetField는 부모 테이블(boundTableRef)의 실제 컬럼이어야 함
  2. subtableRefformConfig.subtables[].tableRef와 일치해야 함
  3. sourceField는 서브테이블의 실제 컬럼이어야 함
  4. 서브테이블 행 추가/수정/삭제 시 실시간 재계산 → onRowsModified 콜백 트리거
  5. 서버에서도 동일 집계를 수행 (CascadeUpdateService 또는 computedConfig aggregation)

POP 화면 설정 — popConfig

중요: POP 화면은 반드시 screenType: "pop"으로 정의하고, 별도의 popMenuGroups 전용 노드에 매핑해야 합니다. → POP 전용 노드 참조

{
"refId": "pop_hcm_result_input",
"label": "공정실적입력",
"screenType": "pop",
"menuIcon": "MobileOutlined",
"menuOrder": 1,
"boundTableRef": "hcm_production_results",
"parentMenuRef": "menu_hcm_pop",
"popConfig": {
"layout": "input-form",
"fontScale": 1.3,
"theme": "light",
"refreshInterval": 10,

"headerFields": [
{ "columnName": "wo_id", "label": "작업지시", "order": 1, "size": "large" },
{ "columnName": "process_id", "label": "공정", "order": 2, "size": "medium" }
],

"scanConfig": {
"enabled": true,
"targetField": "lot_barcode",
"autoSubmit": false,
"scanMode": "single",
"multiScanFields": []
},

"referenceInfo": [
{
"sourceTableRef": "hcm_work_orders",
"linkColumn": "wo_id",
"title": "작업지시 정보",
"displayFields": [
{ "columnName": "wo_number", "label": "작업지시번호" },
{ "columnName": "item_id", "label": "품목" },
{ "columnName": "order_qty", "label": "지시수량" }
]
}
],

"actionFields": [
{
"columnName": "good_qty",
"label": "양품수량",
"fieldType": "number-pad",
"required": true,
"order": 1
},
{
"columnName": "defect_qty",
"label": "불량수량",
"fieldType": "number-pad",
"required": true,
"order": 2
},
{
"columnName": "defect_type",
"label": "불량유형",
"fieldType": "select-grid",
"required": false,
"order": 3,
"options": [
{ "value": "치수불량", "label": "치수불량", "color": "#e74c3c" },
{ "value": "외관불량", "label": "외관불량", "color": "#f39c12" }
]
}
],

"statusDisplay": {
"cards": [
{ "columnName": "order_qty", "label": "목표수량", "format": "number" },
{ "columnName": "good_qty", "label": "양품수량", "format": "number" },
{
"columnName": "status",
"label": "상태",
"format": "status",
"colorMap": { "대기": "#8c8c8c", "진행중": "#1890ff", "완료": "#52c41a" }
}
],
"progressBar": { "targetField": "order_qty", "actualField": "good_qty", "label": "달성률" }
},

"quickActions": [
{ "label": "실적등록", "action": "submit", "color": "#52c41a", "size": "large", "order": 1 },
{ "label": "불량상세", "action": "defect", "color": "#faad14", "size": "medium", "order": 2 },
{ "label": "일시중단", "action": "pause", "color": "#8c8c8c", "size": "small", "order": 3 }
],

"offlineMode": { "enabled": true, "syncOnReconnect": true },
"touchOptimized": { "minButtonSize": "60px", "fontSize": "18px" }
}
}

popConfig 필드 상세

layout (POP 레이아웃 타입)

레이아웃설명용도
status-board상태 현황판작업현황, 생산라인 모니터링
input-form입력 폼실적입력, 검사결과 입력
scan-action바코드 스캔 + 액션LOT 스캔, 입출고 스캔
monitor실시간 모니터링설비 상태, 환경 감시
list-action리스트 + 액션작업목록 선택 후 처리
process-execution공정 실행순차 공정 진행

actionFields.fieldType (POP 입력 타입)

필드타입설명용도
number-pad숫자 키패드 (큰 버튼)수량, 무게 입력
select-grid그리드형 선택 (큰 타일)불량유형, 상태 선택
barcode바코드 입력LOT번호, 시리얼
toggle토글 스위치ON/OFF, 합격/불합격
text텍스트 입력비고, 메모
counter증감 카운터수량 카운팅
system-ref시스템 참조 선택작업자, 부서 선택

quickActions.action (POP 액션 타입)

액션설명
submit데이터 저장/제출
scan바코드 스캔 시작
defect불량 등록
pause일시 중단
andon안돈 호출
login출근
logout퇴근
custom커스텀 액션

POP 전용 노드 (popMenuGroups) — 필수 사용!

핵심 규칙: POP 화면이 있는 경우 반드시 popMenuGroups 전용 노드를 사용해야 합니다. 일반 menuGroups에 POP 메뉴를 넣는 것만으로는 불충분합니다.

{
"popMenuGroups": [
{
"refId": "pop_menu_hcm",
"label": "현장POP",
"icon": "MobileOutlined",
"menuOrder": 100,
"enableFullscreen": true,
"theme": "light",
"fontScale": 1.3,
"touchOptimized": true,
"categories": [
{
"id": "cat_production",
"label": "생산",
"icon": "ExperimentOutlined",
"order": 1,
"color": "#1890ff"
},
{
"id": "cat_quality",
"label": "품질",
"icon": "SafetyCertificateOutlined",
"order": 2,
"color": "#faad14"
},
{
"id": "cat_equipment",
"label": "설비",
"icon": "ToolOutlined",
"order": 3,
"color": "#722ed1"
},
{
"id": "cat_logistics",
"label": "물류",
"icon": "SendOutlined",
"order": 4,
"color": "#52c41a"
}
],
"screenMappings": [
{ "screenRef": "pop_hcm_result_input", "categoryId": "cat_production" },
{ "screenRef": "pop_hcm_quality_check", "categoryId": "cat_quality" },
{ "screenRef": "pop_hcm_equipment_monitor", "categoryId": "cat_equipment" },
{ "screenRef": "pop_hcm_lot_scan", "categoryId": "cat_logistics" }
]
}
]
}

SolutionPopMenuGroupDef 필드 명세

필드타입필수설명
refIdstring필수POP 메뉴 참조 ID: pop_menu_{접두사}
labelstring필수한글 표시명
iconstring필수아이콘 (보통 MobileOutlined)
menuOrdernumber필수정렬 순서 (보통 100 — 일반 메뉴 뒤에 배치)
enableFullscreenboolean필수전체화면 모드 활성화
themestring필수"light" 또는 "dark"
fontScalenumber필수글꼴 배율 (1.0~2.0, 보통 1.3)
touchOptimizedboolean필수터치 최적화 활성화
categoriesarray필수POP 카테고리 분류
screenMappingsarray필수화면-카테고리 매핑

categories 필드

{
"id": "cat_production",
"label": "생산",
"icon": "ExperimentOutlined",
"order": 1,
"color": "#1890ff"
}
필드타입설명
idstring카테고리 고유 ID (cat_으로 시작)
labelstring한글 카테고리명
iconstringAnt Design 아이콘명
ordernumber표시 순서
colorstring카테고리 색상 (hex)

screenMappings 필드

{ "screenRef": "pop_hcm_result_input", "categoryId": "cat_production" }
필드타입설명
screenRefstringscreens 배열 내 POP 화면의 refId
categoryIdstringcategories 배열 내 카테고리의 id

POP 전용 노드 규칙

  1. POP 화면이 있으면 반드시 popMenuGroups 정의 — 일반 menuGroups만으로는 POP 기능 불완전
  2. screenMappingsscreenRefscreens 배열의 POP 화면 refId정확히 일치해야 함
  3. screenMappingscategoryIdcategories 배열의 id정확히 일치해야 함
  4. POP 화면은 동시에 일반 menuGroups에도 포함 가능 (일반 메뉴에서도 접근 허용)
  5. 하나의 JSON에 popMenuGroups는 보통 1개 (여러 개 가능하지만 대부분 1개로 충분)

전체 POP 구성 요약 (3개 레이어)

Layer 1: menuGroups — 일반 메뉴에 POP 그룹 추가 (menu_hcm_pop)
Layer 2: screens — screenType: "pop" + popConfig 정의
Layer 3: popMenuGroups — POP 전용 터치 메뉴 노드 (카테고리 + 매핑)

3개 모두 정의해야 완전한 POP 화면 구성이 됩니다.


ActionButtons (액션 버튼 정의)

화면 단위로 비즈니스 액션 버튼을 정의합니다. 상태 전환, 승인/반려, 외부 프로세스 트리거 등에 사용됩니다.

SolutionScreenActionButtonDef 인터페이스

interface SolutionScreenActionButtonDef {
label: string; // "생산의뢰", "승인", "반려"
action: string; // 'approve' | 'reject' | 'complete' | 'custom'
style?: 'primary' | 'default' | 'danger';
confirmMessage?: string; // "생산의뢰 하시겠습니까?"
requireComment?: boolean;
order?: number;
visibleWhen?: Record<string, string[]>; // { status: ['확정'] }
}

필드 명세

필드타입필수설명
labelstring필수버튼 표시 텍스트 (한글)
actionstring필수실행할 액션 타입
stylestring필수버튼 스타일: primary, default, danger
confirmMessagestring선택실행 전 확인 메시지 (없으면 바로 실행)
requireCommentboolean선택true → 코멘트 입력 필수
ordernumber선택버튼 표시 순서
visibleWhenRecord<string, string[]>선택조건부 표시 — 특정 필드값일 때만 버튼 노출

조건부 표시 (visibleWhen)

visibleWhen은 현재 레코드의 필드값에 따라 버튼을 조건부로 표시합니다.

{
"visibleWhen": { "status": ["확정"] }
}
  • 위 설정은 status 필드가 "확정"일 때만 버튼을 표시합니다.
  • 여러 값 지정 가능: { "status": ["확정", "부분출하"] } → 둘 중 하나일 때 표시
  • 여러 필드 지정 시 AND 조건: 모든 필드 조건을 만족해야 표시

화면 정의에 actionButtons 추가

{
"refId": "scr_sales_orders",
"screenType": "form",
"boundTableRef": "sales_orders",
"parentMenuRef": "mg_sales",
"formConfig": { "...": "..." },
"actionButtons": [
{
"label": "생산의뢰",
"action": "custom",
"style": "primary",
"confirmMessage": "선택한 수주를 생산의뢰 하시겠습니까?",
"requireComment": false,
"order": 1,
"visibleWhen": { "status": ["확정"] }
},
{
"label": "주문취소",
"action": "custom",
"style": "danger",
"confirmMessage": "수주를 취소하시겠습니까? 이 작업은 되돌릴 수 없습니다.",
"requireComment": true,
"order": 2,
"visibleWhen": { "status": ["확정", "대기"] }
}
]
}

actionButtons 규칙

  1. 동일 화면 내 label중복 불가
  2. action은 기정의된 타입(approve, reject, complete, submit) 또는 custom 사용
  3. visibleWhen의 필드값은 해당 테이블의 ENUM enumValues에 포함되어야 함
  4. order 값이 동일하면 배열 순서대로 표시

actionButtons와 문서 전환 API 연동

action: "custom" 버튼은 Document Conversion Rules와 연동하여 문서 간 전환을 트리거합니다.

{
"refId": "screen_quotations",
"actionButtons": [
{
"label": "수주전환",
"action": "custom",
"style": "primary",
"confirmMessage": "선택한 견적서를 수주로 전환하시겠습니까?",
"order": 1,
"visibleWhen": { "status": ["확정"] }
}
]
}
화면버튼 라벨연결 APIvisibleWhen
screen_quotations수주전환/_convert-quotationstatus: ["확정"]
screen_sales_orders생산의뢰/_create-production-ordersstatus: ["확정"]
screen_production_orders작업지시 발행/_create-work-ordersstatus: ["확정", "작업중"]

FieldMapping Type (필드 매핑)

sourceDocumentConfig에서 소스 문서의 데이터를 대상 테이블로 매핑할 때 사용하는 확장 인터페이스입니다.

FieldMapping 인터페이스

interface FieldMapping {
sourceColumn: string; // '_parent.id', '_id', 'item_id', '_remaining'
targetColumn: string; // 대상 테이블의 컬럼명
type: 'direct' | 'computed' | 'constant';
expression?: string; // type='computed' 전용: 'quantity - received_qty'
value?: string; // type='constant' 전용: 고정값
}

필드 명세

필드타입필수설명
sourceColumnstring필수소스 컬럼명 또는 특수 접두사 사용
targetColumnstring필수대상 테이블의 실제 컬럼명
typestring필수direct (직접), computed (계산), constant (상수)
expressionstring선택computed 타입 전용 — JavaScript 수식
valuestring선택constant 타입 전용 — 고정값

특수 접두사 (sourceColumn)

접두사설명예시
_parent.소스 행의 부모 테이블 필드 참조_parent.id → 수주 헤더의 ID
_id소스 행 자체의 PK (id 컬럼)_id → 선택된 수주항목의 ID
_remaining잔량 계산 — computed 타입과 함께 사용_remainingquantity - shipped_qty

type별 사용법

direct (직접 매핑)

소스 컬럼 값을 대상 컬럼에 그대로 복사합니다.

{ "sourceColumn": "item_id", "targetColumn": "item_id", "type": "direct" }

computed (계산 매핑)

소스 행의 여러 컬럼을 사용한 수식 결과를 매핑합니다.

{
"sourceColumn": "_remaining",
"targetColumn": "ship_qty",
"type": "computed",
"expression": "quantity - received_qty"
}

constant (상수 매핑)

고정값을 대상 컬럼에 설정합니다.

{ "sourceColumn": "", "targetColumn": "status", "type": "constant", "value": "대기" }

headerMapping vs itemMapping

sourceDocumentConfig에서 매핑은 두 레벨로 나뉩니다.

구분용도예시
headerMapping소스 헤더 → 대상 헤더 (부모 폼) 매핑수주.partner_id → 출하.partner_id
itemMapping소스 항목 → 대상 항목 (서브테이블) 매핑수주항목.item_id → 출하항목.item_id
{
"sourceDocumentConfig": {
"sources": [{
"key": "sales_order",
"headerMapping": [
{ "sourceColumn": "_parent.partner_id", "targetColumn": "partner_id", "type": "direct" },
{ "sourceColumn": "_parent.id", "targetColumn": "sales_order_id", "type": "direct" }
],
"itemMapping": [
{ "sourceColumn": "item_id", "targetColumn": "item_id", "type": "direct" },
{ "sourceColumn": "_id", "targetColumn": "sales_order_item_id", "type": "direct" },
{
"sourceColumn": "_remaining",
"targetColumn": "ship_qty",
"type": "computed",
"expression": "quantity - shipped_qty"
}
]
}]
}
}

documentConfig 자동 생성 (캔버스 Edge 연동)

documentConfig.lineItems는 두 가지 방식으로 정의할 수 있습니다. 프리셋 JSON에 직접 작성하는 기존 방법과, 캔버스 Edge에서 자동 생성하는 신규 방법을 모두 지원합니다.

방법 A: 프리셋 JSON에 직접 정의 (기존)

기존 방식 그대로 lineItems 배열을 프리셋 JSON 내에 직접 작성합니다.

{
"documentConfig": {
"lineItems": [
{
"title": "견적항목",
"boundTableRef": "quotation_items",
"foreignKeyColumn": "qt_id",
"columns": ["..."]
}
]
}
}

방법 B: 캔버스 Edge에서 자동 생성 (신규)

캔버스에서 1:N 관계 Edge를 생성하고 subtableConfig를 설정하면, 배포 시 해당 설정이 자동으로 documentConfig.lineItems로 변환됩니다.

  • 1:N Edge에서 subtableConfig 설정 → 배포 시 자동 변환
  • 프리셋 JSON에 lineItems를 명시하지 않아도 됨
  • Edge 설정이 우선, 프리셋 정의는 fallback으로 동작
Canvas Edge (1:N)
└─ subtableConfig 설정
├─ title: "견적항목"
├─ foreignKeyColumn: "qt_id"
├─ columns: [...]
└─ addable: true

병합 규칙

Edge 기반 lineItems와 프리셋 JSON 기반 lineItems가 동시에 존재할 경우, 다음 규칙에 따라 병합됩니다.

우선순위조건동작
1Edge와 프리셋 모두 동일 boundTableRef 존재Edge 설정이 우선 (프리셋 무시)
2프리셋에만 존재하는 boundTableRef프리셋 lineItems 유지 (Edge가 없는 서브테이블)
3Edge에만 존재하는 boundTableRefEdge lineItems 추가
  • boundTableRef를 키로 Edge lineItems와 프리셋 lineItems를 매칭
  • 동일 키가 있으면 Edge 설정이 우선 적용
  • 프리셋에만 있는 lineItems는 유지됨 (Edge가 없는 서브테이블일 수 있음)
// 병합 로직 개념 (pseudo-code)
function mergeLineItems(
edgeLineItems: LineItem[],
presetLineItems: LineItem[]
): LineItem[] {
const merged = new Map<string, LineItem>();

// 1. 프리셋 lineItems를 먼저 등록 (fallback)
for (const item of presetLineItems) {
merged.set(item.boundTableRef, item);
}

// 2. Edge lineItems로 덮어쓰기 (우선)
for (const item of edgeLineItems) {
merged.set(item.boundTableRef, item);
}

return Array.from(merged.values());
}

Phase 3 확장: Screen 노드 직접 설정

screenType: "document" 타입의 Screen 노드에서 다음 속성을 추가로 설정할 수 있습니다. 이 설정들은 배포 시 documentConfig에 자동 병합됩니다.

설정설명주요 속성
footerSummary합계 영역 정의SUM, AVG, COUNT 집계 및 formula 수식
documentNumbering자동채번 설정prefix + dateFormat + sequenceDigits
statusFlow상태 흐름 정의전이 규칙, 역방향 전이, 권한 기반 제어
{
"footerSummary": {
"summaryFields": [
{ "label": "합계금액", "sourceColumn": "amount", "aggregation": "SUM", "format": "currency" }
]
},
"documentNumbering": {
"prefix": "QT",
"dateFormat": "YYYYMM",
"sequenceDigits": 4,
"resetPeriod": "yearly",
"targetColumn": "qt_number"
},
"statusFlow": {
"statusColumn": "status",
"initialStatus": "작성",
"finalStatuses": ["완료", "취소"],
"transitions": [
{ "from": "작성", "to": "승인요청", "label": "승인요청" },
{ "from": "승인요청", "to": "승인", "label": "승인", "requiredRole": "manager" },
{ "from": "승인", "to": "작성", "label": "반려", "reverse": true }
]
}
}

데이터 흐름

전체 데이터 흐름은 다음과 같습니다. 캔버스 Edge의 subtableConfig와 Screen 노드의 설정이 배포 파이프라인에서 병합되어 최종 documentConfig를 구성합니다.

Canvas Edge subtableConfig
→ enrichScreenNodesFromEdges()
→ documentConfig.lineItems

Screen node footerSummary / documentNumbering / statusFlow
→ documentConfig에 병합

→ canvas_deployments.deployed_screens에 저장
→ Runtime에서 읽어 렌더링

단계별 상세:

  1. 캔버스 설계 시: 사용자가 1:N Edge에 subtableConfig를 설정하거나, Screen 노드에서 footerSummary, documentNumbering, statusFlow를 설정
  2. 배포 시 (enrichScreenNodesFromEdges()): Edge의 subtableConfigdocumentConfig.lineItems로 변환되고, 프리셋 JSON의 lineItems와 병합 규칙에 따라 최종 lineItems 생성
  3. Screen 노드 병합: footerSummary, documentNumbering, statusFlow 설정이 documentConfig에 추가 병합
  4. 저장: 최종 documentConfigcanvas_deployments.deployed_screens에 저장
  5. 런타임: 저장된 deployed_screens에서 documentConfig를 읽어 전표 화면을 렌더링

이전: 화면 구성 (기본) | 다음: 시각화 화면 구성