ccb0c8df4c
- Created a new .env.example file to provide a template for environment variables, including database connection details, JWT settings, encryption keys, and external API keys. - Updated .gitignore to include additional test output directories and archive files, ensuring that unnecessary files are not tracked by Git. - Removed outdated approval test reports and scripts that are no longer needed, streamlining the project structure. These changes improve the clarity of environment configuration and maintain a cleaner repository.
604 lines
23 KiB
Markdown
604 lines
23 KiB
Markdown
# POP 화면 배포서버 마이그레이션 가이드
|
|
|
|
> **작성일**: 2026-03-23
|
|
> **목적**: 로컬(탑씰 COMPANY_7) POP 화면 5종을 배포서버 COMPANY_21(테스트회사)로 복사
|
|
> **대상 화면**: 4173, 4479, 4480, 4576, 4577
|
|
> **주의**: DB 작업 전 반드시 백업 후 진행
|
|
|
|
---
|
|
|
|
## 0. 개념 정리 (먼저 읽기)
|
|
|
|
### PopDeployModal이란?
|
|
|
|
POP 관리 화면 내 내장된 **화면 배포 도구**입니다.
|
|
|
|
- **접근**: POP 디자이너 > 카테고리 트리 > 그룹 우클릭 또는 배포 버튼
|
|
- **하는 일**:
|
|
1. 선택한 화면들을 다른 회사 계정으로 복사 (`screen_definitions` 새 행 생성)
|
|
2. POP 레이아웃 JSON 복사 (`screen_layouts_pop`)
|
|
3. **layout_json 내 화면 ID 참조 자동 리매핑** (screenId, cartScreenId, sourceScreenId, targetScreenId)
|
|
4. 카테고리 그룹 구조 생성 (`screen_groups`, `screen_group_screens`)
|
|
5. numberingRuleId 자동 제거 (회사별 고유값이므로)
|
|
- **제약**: 같은 서버 안에서만 동작 (로컬 → 배포 서버 간 복사 불가)
|
|
- **권장 사용 시점**: 배포 DB COMPANY_7에 화면이 먼저 세팅된 후, 같은 배포 서버 내 테스트 계정으로 복사할 때
|
|
|
|
### 마이그레이션 전체 흐름
|
|
|
|
```
|
|
[로컬 DB / COMPANY_7] ──── SQL 직접 복사 ────→ [배포 DB / COMPANY_7]
|
|
│
|
|
PopDeployModal
|
|
(배포서버 내)
|
|
│
|
|
↓
|
|
[배포 DB / COMPANY_21]
|
|
(테스트 환경)
|
|
```
|
|
|
|
---
|
|
|
|
## 1. 현황 요약
|
|
|
|
### 1-1. 환경 정보
|
|
|
|
| 구분 | 로컬 DB | 배포 DB |
|
|
|------|---------|---------|
|
|
| Host | 39.117.244.52:11132 | 211.115.91.141:11134 |
|
|
| Database | plm | plm |
|
|
| 소스 회사 | COMPANY_7 (탑씰) | - |
|
|
| 1차 타겟 | - | COMPANY_7 (탑씰, SQL 직접 삽입) |
|
|
| 2차 타겟 | - | COMPANY_21 (테스트회사, PopDeployModal) |
|
|
|
|
### 1-2. 복사 대상 화면
|
|
|
|
| 화면 ID | screen_code | screen_name | 역할 |
|
|
|---------|-------------|-------------|------|
|
|
| 4479 | COMPANY_7_179 | 홈 | POP 메인 홈 화면 |
|
|
| 4576 | COMPANY_7_194 | 입고메뉴 | 입고 카테고리 메뉴 |
|
|
| 4173 | COMPANY_7_169 | 구매입고 담기 | 구매입고 항목 선택/담기 |
|
|
| 4577 | COMPANY_7_195 | 구매입고 장바구니 | 장바구니 확인/입고 확정 |
|
|
| 4480 | COMPANY_7_180 | MES공정 | MES 생산실적 관리 |
|
|
|
|
### 1-3. POP 카테고리 구조 (screen_groups)
|
|
|
|
```
|
|
탑씰 (id:3134, code:TOPSSEAL)
|
|
├── 홈 #4479
|
|
├── 입고관리 (id:3216, code:INBOUND_MENU)
|
|
│ ├── 입고메뉴 #4576
|
|
│ └── 구매입고 (id:3221, code:PURCHASE RECEIPT)
|
|
│ ├── 구매입고 담기 #4173
|
|
│ └── 구매입고 장바구니 #4577
|
|
└── 생산실적 (id:3220, code:PRODUCTION RESULTS)
|
|
└── MES공정 #4480
|
|
```
|
|
|
|
### 1-4. 화면 간 상호참조 (layout_json 내부)
|
|
|
|
| 출발 화면 | 참조 방식 | 대상 화면 | JSON 키 |
|
|
|----------|----------|----------|---------|
|
|
| 4479 홈 | navigate | 4480 MES공정 | `screenId: "4480"` |
|
|
| 4479 홈 | navigate | 4576 입고메뉴 | `screenId: "4576"` |
|
|
| 4576 입고메뉴 | navigate | 4173 구매입고 | `screenId: "4173"` |
|
|
| 4173 구매입고 | cart-save | 4577 장바구니 | `cartScreenId: "4577"` |
|
|
| 4577 장바구니 | source | 4173 구매입고 | `sourceScreenId: 4173` (숫자) |
|
|
| 4577 장바구니 | navigate | 4173 구매입고 | `targetScreenId: "4173"` |
|
|
|
|
> PopDeployModal을 사용하면 이 참조들이 모두 자동 리매핑됩니다.
|
|
|
|
---
|
|
|
|
## 2. 배포 DB 현황 점검 결과
|
|
|
|
### 2-1. 테이블 누락 상태
|
|
|
|
| 테이블 | 로컬 | 배포 | MES/기능 의존도 |
|
|
|--------|------|------|----------------|
|
|
| work_order_process | O (37컬럼) | **없음** | MES공정 화면 전체 동작 불가 |
|
|
| process_work_result | O (35컬럼) | **없음** | MES 체크리스트 기능 불가 |
|
|
| work_order_process_log | O (12컬럼+트리거) | **없음** | 공정 변경 이력 로깅 불가 |
|
|
| cart_items | O (16컬럼) | **없음** | 구매입고 장바구니 전체 불가 |
|
|
|
|
### 2-2. 컬럼 누락 상태
|
|
|
|
| 테이블 | 누락 컬럼 | 영향 |
|
|
|--------|----------|------|
|
|
| work_instruction | `reason`, `completed_qty` | MES 완료수량 업데이트 실패 |
|
|
|
|
### 2-3. COMPANY_21 (테스트회사) 현황
|
|
|
|
| 항목 | 상태 |
|
|
|------|------|
|
|
| 회사 존재 | O (active) |
|
|
| 기존 POP 레이아웃 | 없음 (깨끗한 상태) |
|
|
| 로그인 가능 계정 | **0개 (계정 없음!)** |
|
|
| 기존 화면 수 | 23개 (일반 ERP 화면들) |
|
|
|
|
> **중요**: COMPANY_21에는 현재 등록된 사용자가 없습니다.
|
|
> 테스트 전에 사용자 계정을 먼저 생성해야 로그인 및 POP 테스트가 가능합니다.
|
|
|
|
### 2-4. COMPANY_7 (탑씰) 배포 서버 현황
|
|
|
|
- 기존 POP 레이아웃: `screen_id 4114` (테스트용 1개만 존재)
|
|
- 로그인 계정: `topseal_admin`, `topseal_admin2`, `topseal_user`, `test1`, `test2`
|
|
- 5개 화면(4173, 4479, 4480, 4576, 4577) 모두 배포 DB에 없음 → 안전하게 삽입 가능
|
|
|
|
---
|
|
|
|
## 3. 누락 테이블/컬럼 추가 방법
|
|
|
|
### 3-1. Vexplor DDL 시스템으로 가능한 작업
|
|
|
|
Vexplor에는 **관리자 DDL 실행 시스템**이 내장되어 있습니다.
|
|
|
|
- **접근**: 관리자 > 시스템관리 > 테이블관리 (`/admin/systemMng/tableMngList`)
|
|
- **가능한 작업**:
|
|
- 테이블 생성 (`POST /api/ddl/tables`)
|
|
- 컬럼 추가 (`POST /api/ddl/tables/:tableName/columns`)
|
|
- 생성 시 `table_type_columns` 메타데이터 자동 등록
|
|
- **권한**: 슈퍼 어드민 계정 (`company_code = '*'`)만 사용 가능
|
|
- **불가능한 것**: 트리거 함수 생성 (이건 psql 직접 실행 필요)
|
|
|
|
### 3-2. 작업 분류
|
|
|
|
| 작업 | 방법 | 비고 |
|
|
|------|------|------|
|
|
| work_order_process 생성 | **psql 직접 실행** | 컬럼 37개 + 인덱스 7개, UI 입력보다 SQL이 효율적 |
|
|
| process_work_result 생성 | **psql 직접 실행** | 컬럼 35개 |
|
|
| cart_items 생성 | **psql 직접 실행** | 컬럼 16개 + 인덱스 5개 |
|
|
| work_order_process_log 생성 | **psql 직접 실행** | 트리거 함수 포함 필수 |
|
|
| work_instruction 컬럼 추가 | DDL UI 또는 psql | 컬럼 2개, 어느 방법이든 가능 |
|
|
| table_type_columns 메타데이터 | psql COPY 명령 | 로컬에서 추출 후 배포에 삽입 |
|
|
|
|
> **결론**: DDL UI는 컬럼 추가(`work_instruction`) 정도에 활용하고,
|
|
> 테이블 생성은 모두 psql SQL 직접 실행이 현실적입니다.
|
|
|
|
---
|
|
|
|
## 4. 실행 절차 (단계별)
|
|
|
|
### STEP 0: 배포 DB 백업
|
|
|
|
```sql
|
|
-- 배포 DB에서 실행
|
|
CREATE TABLE backup_20260323_screen_definitions AS
|
|
SELECT * FROM screen_definitions WHERE company_code = 'COMPANY_7';
|
|
CREATE TABLE backup_20260323_screen_layouts_pop AS
|
|
SELECT * FROM screen_layouts_pop WHERE company_code = 'COMPANY_7';
|
|
CREATE TABLE backup_20260323_screen_groups AS
|
|
SELECT * FROM screen_groups WHERE company_code = 'COMPANY_7';
|
|
CREATE TABLE backup_20260323_screen_group_screens AS
|
|
SELECT * FROM screen_group_screens WHERE company_code = 'COMPANY_7';
|
|
```
|
|
|
|
---
|
|
|
|
### STEP 1: 누락 테이블 생성 (배포 DB에서 psql 실행)
|
|
|
|
```bash
|
|
PGPASSWORD='$DB_PASSWORD' psql -h 211.115.91.141 -p 11134 -U postgres -d plm
|
|
```
|
|
|
|
#### 1-1. work_order_process
|
|
|
|
```sql
|
|
CREATE TABLE work_order_process (
|
|
id character varying(500) NOT NULL DEFAULT (gen_random_uuid())::text,
|
|
created_date timestamp without time zone DEFAULT now(),
|
|
updated_date timestamp without time zone DEFAULT now(),
|
|
writer character varying(255),
|
|
company_code character varying(255),
|
|
wo_id character varying(500),
|
|
seq_no character varying(255),
|
|
process_code character varying(255),
|
|
process_name character varying(255),
|
|
is_required character varying(255),
|
|
is_fixed_order character varying(255),
|
|
standard_time character varying(255),
|
|
status character varying(255),
|
|
accepted_by character varying(255),
|
|
accepted_at character varying(255),
|
|
started_at character varying(255),
|
|
completed_at character varying(255),
|
|
plan_qty character varying(255),
|
|
input_qty character varying(255),
|
|
good_qty character varying(255),
|
|
defect_qty character varying(255),
|
|
equipment_code character varying(255),
|
|
remark character varying(500),
|
|
paused_at character varying(500),
|
|
total_paused_time character varying(500) DEFAULT '0',
|
|
routing_detail_id character varying(500),
|
|
actual_work_time character varying(500) DEFAULT NULL::character varying,
|
|
completed_by character varying(500) DEFAULT NULL::character varying,
|
|
total_production_qty character varying(500),
|
|
defect_detail character varying(500),
|
|
result_note character varying(500),
|
|
result_status character varying(500) DEFAULT 'draft'::character varying,
|
|
attachments character varying(500),
|
|
parent_process_id character varying(500) DEFAULT NULL::character varying,
|
|
concession_qty character varying(500) DEFAULT '0'::character varying,
|
|
is_rework character varying(500) DEFAULT 'N'::character varying,
|
|
rework_source_id character varying(500) DEFAULT NULL::character varying,
|
|
CONSTRAINT work_order_process_pkey PRIMARY KEY (id)
|
|
);
|
|
|
|
CREATE INDEX idx_wop_company ON work_order_process (company_code);
|
|
CREATE INDEX idx_wop_wo_id ON work_order_process (wo_id);
|
|
CREATE INDEX idx_wop_wo_id_seq_no ON work_order_process (wo_id, seq_no);
|
|
CREATE INDEX idx_wop_process ON work_order_process (company_code, process_code);
|
|
CREATE INDEX idx_wop_status ON work_order_process (company_code, process_code, status);
|
|
CREATE INDEX idx_wop_parent_process_id ON work_order_process (parent_process_id);
|
|
```
|
|
|
|
#### 1-2. process_work_result
|
|
|
|
```sql
|
|
CREATE TABLE process_work_result (
|
|
id character varying(500) NOT NULL DEFAULT (gen_random_uuid())::text,
|
|
created_date timestamp without time zone DEFAULT now(),
|
|
updated_date timestamp without time zone DEFAULT now(),
|
|
writer character varying(500) DEFAULT NULL::character varying,
|
|
company_code character varying(500),
|
|
work_order_process_id character varying(500),
|
|
source_work_item_id character varying(500),
|
|
source_detail_id character varying(500),
|
|
work_phase character varying(500),
|
|
item_title character varying(500),
|
|
item_sort_order character varying(500),
|
|
detail_content character varying(500),
|
|
detail_type character varying(500),
|
|
detail_sort_order character varying(500),
|
|
is_required character varying(500),
|
|
inspection_code character varying(500),
|
|
inspection_method character varying(500),
|
|
unit character varying(500),
|
|
lower_limit character varying(500),
|
|
upper_limit character varying(500),
|
|
input_type character varying(500),
|
|
lookup_target character varying(500),
|
|
display_fields character varying(500),
|
|
duration_minutes character varying(500),
|
|
status character varying(500),
|
|
result_value character varying(500),
|
|
is_passed character varying(500),
|
|
remark character varying(500),
|
|
recorded_by character varying(500),
|
|
recorded_at character varying(500),
|
|
started_at character varying(500) DEFAULT NULL::character varying,
|
|
group_started_at character varying(500) DEFAULT NULL::character varying,
|
|
group_paused_at character varying(500) DEFAULT NULL::character varying,
|
|
group_total_paused_time character varying(500) DEFAULT NULL::character varying,
|
|
group_completed_at character varying(500) DEFAULT NULL::character varying,
|
|
CONSTRAINT process_work_result_pkey PRIMARY KEY (id)
|
|
);
|
|
```
|
|
|
|
#### 1-3. work_order_process_log + 트리거
|
|
|
|
```sql
|
|
-- 로그 테이블
|
|
CREATE TABLE work_order_process_log (
|
|
log_id SERIAL PRIMARY KEY,
|
|
operation_type character varying(10) NOT NULL,
|
|
original_id character varying(100),
|
|
changed_column character varying(100),
|
|
old_value text,
|
|
new_value text,
|
|
changed_by character varying(50),
|
|
changed_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP,
|
|
ip_address character varying(50),
|
|
user_agent text,
|
|
full_row_before jsonb,
|
|
full_row_after jsonb
|
|
);
|
|
|
|
-- 트리거 함수 (로컬 DB에서 정확한 정의 먼저 추출)
|
|
-- 로컬에서: SELECT pg_get_functiondef(oid) FROM pg_proc WHERE proname = 'work_order_process_log_trigger_func';
|
|
-- 추출한 함수 정의를 아래에 붙여넣기
|
|
|
|
-- 트리거 등록 (함수 생성 후 실행)
|
|
CREATE TRIGGER work_order_process_audit_trigger
|
|
AFTER INSERT OR UPDATE OR DELETE ON work_order_process
|
|
FOR EACH ROW EXECUTE FUNCTION work_order_process_log_trigger_func();
|
|
```
|
|
|
|
#### 1-4. cart_items
|
|
|
|
```sql
|
|
CREATE TABLE cart_items (
|
|
id character varying(255) NOT NULL DEFAULT (gen_random_uuid())::text,
|
|
created_date timestamp without time zone DEFAULT now(),
|
|
updated_date timestamp without time zone DEFAULT now(),
|
|
company_code character varying(20),
|
|
cart_type character varying(255),
|
|
screen_id character varying(255),
|
|
user_id character varying(255),
|
|
source_table character varying(255),
|
|
row_key text,
|
|
row_data text,
|
|
quantity character varying(255),
|
|
unit character varying(255),
|
|
package_unit character varying(255),
|
|
package_entries text,
|
|
status character varying(255),
|
|
memo text,
|
|
CONSTRAINT cart_items_pkey PRIMARY KEY (id)
|
|
);
|
|
|
|
CREATE INDEX idx_cart_items_company ON cart_items (company_code);
|
|
CREATE INDEX idx_cart_items_screen_user ON cart_items (screen_id, user_id);
|
|
CREATE INDEX idx_cart_items_type ON cart_items (cart_type);
|
|
CREATE INDEX idx_cart_items_status ON cart_items (status);
|
|
```
|
|
|
|
---
|
|
|
|
### STEP 2: 기존 테이블 컬럼 추가
|
|
|
|
#### 방법 A: DDL UI (관리자 화면)
|
|
|
|
1. 슈퍼 어드민 계정으로 배포서버 접속
|
|
2. 관리자 > 시스템관리 > 테이블관리
|
|
3. `work_instruction` 테이블 선택
|
|
4. 컬럼 추가 버튼 → `reason` (varchar 500) 추가
|
|
5. 컬럼 추가 버튼 → `completed_qty` (varchar 500, 기본값: '0') 추가
|
|
|
|
#### 방법 B: SQL 직접
|
|
|
|
```sql
|
|
ALTER TABLE work_instruction
|
|
ADD COLUMN IF NOT EXISTS reason character varying(500),
|
|
ADD COLUMN IF NOT EXISTS completed_qty character varying(500) DEFAULT '0'::character varying;
|
|
```
|
|
|
|
---
|
|
|
|
### STEP 3: table_type_columns 메타데이터 복사 (로컬 → 배포)
|
|
|
|
```bash
|
|
# 로컬 DB에서 추출
|
|
PGPASSWORD='ph0909!!' psql -h 39.117.244.52 -p 11132 -U postgres -d plm -c "
|
|
COPY (
|
|
SELECT * FROM table_type_columns
|
|
WHERE table_name IN ('work_order_process', 'cart_items', 'process_work_result')
|
|
) TO STDOUT WITH CSV HEADER" > /tmp/ttc_export.csv
|
|
|
|
# 배포 DB에 삽입 (충돌 시 무시)
|
|
PGPASSWORD='$DB_PASSWORD' psql -h 211.115.91.141 -p 11134 -U postgres -d plm -c "
|
|
COPY table_type_columns FROM STDIN WITH CSV HEADER
|
|
ON CONFLICT DO NOTHING" < /tmp/ttc_export.csv
|
|
```
|
|
|
|
---
|
|
|
|
### STEP 4: 화면 5종 → 배포 COMPANY_7 복사 (SQL)
|
|
|
|
> 화면 ID 4173~4577은 배포 DB에 없으므로 동일 ID로 안전하게 삽입 가능
|
|
|
|
```bash
|
|
# 로컬에서 screen_definitions 추출
|
|
PGPASSWORD='ph0909!!' psql -h 39.117.244.52 -p 11132 -U postgres -d plm -c "
|
|
COPY (
|
|
SELECT * FROM screen_definitions
|
|
WHERE screen_id IN (4173, 4479, 4480, 4576, 4577)
|
|
) TO STDOUT WITH CSV HEADER" > /tmp/screen_def.csv
|
|
|
|
# 배포에 삽입
|
|
PGPASSWORD='$DB_PASSWORD' psql -h 211.115.91.141 -p 11134 -U postgres -d plm -c "
|
|
COPY screen_definitions FROM STDIN WITH CSV HEADER
|
|
ON CONFLICT DO NOTHING" < /tmp/screen_def.csv
|
|
|
|
|
|
# screen_layouts_pop 추출 (layout_id 제외)
|
|
PGPASSWORD='ph0909!!' psql -h 39.117.244.52 -p 11132 -U postgres -d plm -c "
|
|
COPY (
|
|
SELECT screen_id, company_code, layout_data, created_at, updated_at, created_by, updated_by
|
|
FROM screen_layouts_pop
|
|
WHERE screen_id IN (4173, 4479, 4480, 4576, 4577)
|
|
) TO STDOUT WITH CSV HEADER" > /tmp/screen_layouts.csv
|
|
|
|
# 배포에 삽입
|
|
PGPASSWORD='$DB_PASSWORD' psql -h 211.115.91.141 -p 11134 -U postgres -d plm -c "
|
|
COPY screen_layouts_pop (screen_id, company_code, layout_data, created_at, updated_at, created_by, updated_by)
|
|
FROM STDIN WITH CSV HEADER
|
|
ON CONFLICT (screen_id, company_code) DO NOTHING" < /tmp/screen_layouts.csv
|
|
|
|
|
|
# screen_groups 추출 (그룹 4개: 3134, 3216, 3220, 3221)
|
|
PGPASSWORD='ph0909!!' psql -h 39.117.244.52 -p 11132 -U postgres -d plm -c "
|
|
COPY (
|
|
SELECT * FROM screen_groups
|
|
WHERE id IN (3134, 3216, 3220, 3221)
|
|
) TO STDOUT WITH CSV HEADER" > /tmp/screen_groups.csv
|
|
|
|
# 배포에 삽입
|
|
PGPASSWORD='$DB_PASSWORD' psql -h 211.115.91.141 -p 11134 -U postgres -d plm -c "
|
|
COPY screen_groups FROM STDIN WITH CSV HEADER
|
|
ON CONFLICT DO NOTHING" < /tmp/screen_groups.csv
|
|
|
|
|
|
# screen_group_screens 추출
|
|
PGPASSWORD='ph0909!!' psql -h 39.117.244.52 -p 11132 -U postgres -d plm -c "
|
|
COPY (
|
|
SELECT * FROM screen_group_screens
|
|
WHERE screen_id IN (4173, 4479, 4480, 4576, 4577)
|
|
) TO STDOUT WITH CSV HEADER" > /tmp/screen_group_screens.csv
|
|
|
|
# 배포에 삽입
|
|
PGPASSWORD='$DB_PASSWORD' psql -h 211.115.91.141 -p 11134 -U postgres -d plm -c "
|
|
COPY screen_group_screens FROM STDIN WITH CSV HEADER
|
|
ON CONFLICT DO NOTHING" < /tmp/screen_group_screens.csv
|
|
|
|
|
|
# 시퀀스 동기화 (배포 DB에서 실행)
|
|
SELECT setval('screen_definitions_screen_id_seq',
|
|
GREATEST((SELECT MAX(screen_id) FROM screen_definitions),
|
|
(SELECT last_value FROM screen_definitions_screen_id_seq)));
|
|
|
|
SELECT setval('screen_layouts_pop_layout_id_seq',
|
|
GREATEST((SELECT MAX(layout_id) FROM screen_layouts_pop),
|
|
(SELECT last_value FROM screen_layouts_pop_layout_id_seq)));
|
|
|
|
SELECT setval('screen_groups_id_seq',
|
|
GREATEST((SELECT MAX(id) FROM screen_groups),
|
|
(SELECT last_value FROM screen_groups_id_seq)));
|
|
|
|
SELECT setval('screen_group_screens_id_seq',
|
|
GREATEST((SELECT MAX(id) FROM screen_group_screens),
|
|
(SELECT last_value FROM screen_group_screens_id_seq)));
|
|
```
|
|
|
|
---
|
|
|
|
### STEP 5: COMPANY_21 테스트 계정 사용자 생성
|
|
|
|
> COMPANY_21에 현재 등록된 사용자가 없습니다. 로그인하려면 계정이 필요합니다.
|
|
|
|
배포서버 관리자 화면(슈퍼 어드민)에서 COMPANY_21 소속 사용자를 추가합니다:
|
|
- 관리자 > 회사관리 > COMPANY_21 > 사용자 추가
|
|
- 또는 SQL:
|
|
|
|
```sql
|
|
-- user_info 테이블에 테스트 사용자 추가 (기존 패턴 참고)
|
|
-- 실제 password 해시는 기존 계정 방식과 동일하게 처리 필요
|
|
INSERT INTO user_info (user_id, user_name, company_code, user_type, status, password)
|
|
VALUES ('test21', '테스트계정', 'COMPANY_21', 'COMPANY_ADMIN', 'active', '-- 해시된 비밀번호 --');
|
|
```
|
|
|
|
---
|
|
|
|
### STEP 6: PopDeployModal로 COMPANY_7 → COMPANY_21 복사
|
|
|
|
1. 배포서버에서 `topseal_admin` 계정으로 로그인
|
|
2. POP 디자이너 > POP 관리 화면 > 카테고리 트리 접속
|
|
3. "탑씰" 그룹에서 배포 버튼 클릭
|
|
4. 대상 회사: **COMPANY_21 (테스트회사)** 선택
|
|
5. 5개 화면 포함 여부 확인 후 배포 실행
|
|
6. PopDeployModal이 COMPANY_21 전용 새 화면 ID 자동 부여 + 참조 자동 리매핑
|
|
|
|
---
|
|
|
|
### STEP 7: 검증
|
|
|
|
```sql
|
|
-- 배포 DB에서 실행 --
|
|
|
|
-- 7-1. 누락 테이블 생성 확인
|
|
SELECT table_name FROM information_schema.tables
|
|
WHERE table_schema = 'public'
|
|
AND table_name IN ('work_order_process', 'cart_items', 'process_work_result', 'work_order_process_log');
|
|
-- 예상: 4건
|
|
|
|
-- 7-2. work_instruction 컬럼 확인
|
|
SELECT column_name FROM information_schema.columns
|
|
WHERE table_name = 'work_instruction' AND column_name IN ('reason', 'completed_qty');
|
|
-- 예상: 2건
|
|
|
|
-- 7-3. COMPANY_7 화면 삽입 확인
|
|
SELECT screen_id, screen_name, company_code FROM screen_definitions
|
|
WHERE screen_id IN (4173, 4479, 4480, 4576, 4577);
|
|
-- 예상: 5건 (COMPANY_7)
|
|
|
|
-- 7-4. COMPANY_21 화면 복사 확인 (PopDeployModal 후)
|
|
SELECT sd.screen_id, sd.screen_name, sd.company_code,
|
|
CASE WHEN slp.layout_id IS NOT NULL THEN 'Y' ELSE 'N' END as has_layout
|
|
FROM screen_definitions sd
|
|
LEFT JOIN screen_layouts_pop slp ON sd.screen_id = slp.screen_id
|
|
WHERE sd.company_code = 'COMPANY_21'
|
|
AND sd.screen_name IN ('홈', 'MES공정', '입고메뉴', '구매입고 담기', '구매입고 장바구니');
|
|
-- 예상: 5건 + has_layout = Y
|
|
|
|
-- 7-5. 화면 간 참조 무결성 (COMPANY_21 기준 새 ID로 리매핑됐는지 확인)
|
|
SELECT slp.screen_id,
|
|
layout_data::text LIKE '%screenId%' as has_nav_ref,
|
|
layout_data::text LIKE '%cartScreenId%' as has_cart_ref
|
|
FROM screen_layouts_pop slp
|
|
JOIN screen_definitions sd ON slp.screen_id = sd.screen_id
|
|
WHERE sd.company_code = 'COMPANY_21'
|
|
AND sd.screen_name IN ('홈', '입고메뉴', '구매입고 담기', '구매입고 장바구니');
|
|
|
|
-- 7-6. 시퀀스 정합성
|
|
SELECT 'screen_definitions' as tbl,
|
|
(SELECT MAX(screen_id) FROM screen_definitions) as max_id,
|
|
(SELECT last_value FROM screen_definitions_screen_id_seq) as seq_val,
|
|
CASE WHEN (SELECT last_value FROM screen_definitions_screen_id_seq)
|
|
>= (SELECT MAX(screen_id) FROM screen_definitions)
|
|
THEN 'OK' ELSE 'MISMATCH' END as status;
|
|
```
|
|
|
|
---
|
|
|
|
## 5. COMPANY_21 테스트 환경 수정 가능 여부
|
|
|
|
| 항목 | 수정 가능? | 방법 |
|
|
|------|----------|------|
|
|
| 회사명/정보 | O | 관리자 > 회사관리 |
|
|
| 사용자 추가/수정 | O | 관리자 > 사용자관리 |
|
|
| POP 화면 수정 | O | POP 디자이너에서 직접 편집 |
|
|
| 화면 삭제 후 재배포 | O | PopDeployModal 재실행 |
|
|
| 기존 ERP 화면 영향 없음 | O | POP 레이아웃 별도 테이블 관리 |
|
|
|
|
> COMPANY_21은 테스트 전용 계정이므로 자유롭게 수정/삭제 가능합니다.
|
|
> 기존 23개 ERP 화면(구매관리, 영업관리 등)은 POP과 무관하므로 건드리지 않아도 됩니다.
|
|
|
|
---
|
|
|
|
## 6. 요약: 실행 순서
|
|
|
|
| 순서 | 작업 | 방법 | 담당 |
|
|
|------|------|------|------|
|
|
| STEP 0 | 배포 DB 백업 | psql | DB 담당자 |
|
|
| STEP 1 | 누락 테이블 4개 생성 | psql SQL | DB 담당자 |
|
|
| STEP 2 | work_instruction 컬럼 추가 | DDL UI 또는 psql | DB 담당자 |
|
|
| STEP 3 | table_type_columns 메타데이터 복사 | psql COPY | DB 담당자 |
|
|
| STEP 4 | 화면 5종 COMPANY_7에 삽입 | psql COPY | DB 담당자 |
|
|
| STEP 5 | COMPANY_21 테스트 사용자 생성 | 관리자 UI 또는 SQL | 어드민 계정 |
|
|
| STEP 6 | PopDeployModal로 COMPANY_21 복사 | 배포서버 UI | 어드민 계정 |
|
|
| STEP 7 | 검증 쿼리 실행 | psql | DB 담당자 |
|
|
| STEP 8 | 브라우저 POP 화면 테스트 | 브라우저 | 테스터 |
|
|
|
|
---
|
|
|
|
## 7. 롤백 방법
|
|
|
|
```sql
|
|
-- COMPANY_21 POP 화면 삭제 (PopDeployModal로 생성된 것)
|
|
DELETE FROM screen_group_screens
|
|
WHERE screen_id IN (
|
|
SELECT screen_id FROM screen_definitions WHERE company_code = 'COMPANY_21'
|
|
AND screen_name IN ('홈', 'MES공정', '입고메뉴', '구매입고 담기', '구매입고 장바구니')
|
|
);
|
|
DELETE FROM screen_layouts_pop
|
|
WHERE screen_id IN (
|
|
SELECT screen_id FROM screen_definitions WHERE company_code = 'COMPANY_21'
|
|
AND screen_name IN ('홈', 'MES공정', '입고메뉴', '구매입고 담기', '구매입고 장바구니')
|
|
);
|
|
DELETE FROM screen_definitions
|
|
WHERE company_code = 'COMPANY_21'
|
|
AND screen_name IN ('홈', 'MES공정', '입고메뉴', '구매입고 담기', '구매입고 장바구니');
|
|
|
|
-- COMPANY_7 화면 삭제 (SQL로 삽입한 것)
|
|
DELETE FROM screen_group_screens WHERE screen_id IN (4173, 4479, 4480, 4576, 4577);
|
|
DELETE FROM screen_layouts_pop WHERE screen_id IN (4173, 4479, 4480, 4576, 4577);
|
|
DELETE FROM screen_definitions WHERE screen_id IN (4173, 4479, 4480, 4576, 4577);
|
|
DELETE FROM screen_groups WHERE id IN (3134, 3216, 3220, 3221)
|
|
AND id NOT IN (SELECT id FROM backup_20260323_screen_groups);
|
|
|
|
-- 생성한 테이블 제거
|
|
DROP TABLE IF EXISTS work_order_process_log;
|
|
DROP TABLE IF EXISTS process_work_result;
|
|
DROP TABLE IF EXISTS work_order_process;
|
|
DROP TABLE IF EXISTS cart_items;
|
|
|
|
-- 추가 컬럼 제거
|
|
ALTER TABLE work_instruction DROP COLUMN IF EXISTS reason;
|
|
ALTER TABLE work_instruction DROP COLUMN IF EXISTS completed_qty;
|
|
```
|
|
|
|
---
|
|
|
|
*이 문서는 로컬 DB와 배포 DB를 읽기 전용으로 점검한 결과를 바탕으로 작성되었습니다.*
|
|
*실제 실행 전 반드시 배포 DB 백업을 완료하세요.*
|