개발관리>PART 등록·조회 메뉴 신설 (PR-A) — wace partMng 1:1 이식

backend (M1+M2):
- devPartService: listTemp/listRelease/getByObjid/create/update/deploy/removeMany
- partMngBaseSimple SELECT + 추가 15컬럼(acctfg/odrfg/unit_dc/unitmang_dc/lot_fg 등) 라벨/CASE
- deploy 트랜잭션 3단계 (isLastInit → part_mng_history INSERT → partMngDeploy + EO_NO 채번)
- EO_NO 분기: is_longd='1'→EOB{yy}-{seq} / else EO{yy}-{seq}
- objidUtil: wace CommonUtils.createObjId() 1:1 (bigint objid 채번)
- DDL: 9 신규 테이블 + part_mng 15컬럼 ALTER (운영판 1:1 추출)

frontend (M1+M2):
- part-regist (M1) / part-search (M2): 23셀 그리드 + 검색폼 + 액션
- PartFormDialog: 등록/수정 통합 (mode prop, 4 섹션)
- PartDetailDialog: 읽기 전용 + "수정" dispatch
- AdminPageRenderer dynamic 임포트 2건 + menu_info URL spec 정렬

본 PR 제외 (별 PR): 도면 다중 업로드, ERP 업로드, Excel Import, BOM_PART_QTY R/W

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hjjeong
2026-05-12 16:14:10 +09:00
parent d8d9ad3bcc
commit ea6606da0c
16 changed files with 3155 additions and 0 deletions
+197
View File
@@ -0,0 +1,197 @@
# 개발관리 이식 GAP 분석 (원본 wace_plm 대비)
> 작성: 2026-05-12 / 작성자: hjjeong
> 대상 메뉴 5종 (1 도메인 `development/`):
> - PART 등록 / PART 조회 / E-BOM 등록 / E-BOM 조회 / 설계변경 리스트
> 원본 위치: `wace_plm/WebContent/WEB-INF/view/partMng/` (단일 디렉토리) + `mapper/partMng.xml` 단일 매퍼.
---
## 0. 한 문장 요약
5개 메뉴 모두 wace `partMng/` 단일 디렉토리 + `partMng.xml` 매퍼에 1:1 매핑됨. 의존 테이블 15개 중 **6개 보유(`part_mng`/`comm_code`/`pms_wbs_task`/`project_mgmt`/`user_info`/`product_mgmt`)** · **9개 신규 추가 완료(`300_part_bom.sql`)** · **`part_mng`에 누락 15컬럼 ALTER 완료(`301_alter_part_mng.sql`)**. 5개 메뉴 모두 P1에서 실데이터 표시 가능.
## 0.1 이식 원칙
- JSP/매퍼XML 안의 `/* */`, `<!-- -->`, `//` 주석 블록은 비활성. 활성 코드만 이식.
- `company_code` 멀티테넌시 분기는 vexplor_rps 측에 만들지 않음 (COMPANY_16 단독).
- `CODE_NAME()`은 영업/프로젝트와 동일하게 `LEFT JOIN comm_code CC_X ON CC_X.code_id=...` 패턴 통일.
- `client_mng`/`supply_mng` → vexplor는 `customer_mng`로 통합되어 있으나, 개발관리 5개 메뉴는 `customer_mng`를 직접 참조하지 않음(`project_mgmt.customer_objid` 경유). 분기 변환 불필요.
- 금액 1,234.00 / 수량 1,234 / 모든 숫자 right-align (memory `feedback_number_format.md`).
- wace JSP 컬럼 정의 끝의 주석 블록은 비활성 항목 — grep만으로 카운트하지 말 것 (memory `feedback_wace_jsp_columns.md`).
---
## 1. 메뉴 ↔ JSP ↔ 매퍼 1:1 매핑
| # | 메뉴 | wace JSP | 매퍼 쿼리 (partMng.xml) | LOC |
|---|---|---|---|---:|
| M1 | **PART 등록** | `partMngTempList.jsp` | `partMngTempGridList` (S), `partMngDeploy` (U), `partMngDelete` (D) | 649 |
| M2 | **PART 조회** | `partMngList.jsp` | `partMngGridList` (S), `partMngDelete` (D), `partMngFormPopUp` (S) | 834 |
| M3 | **E-BOM 등록** | `structureList.jsp` | `getBOMStandardStructureGridList` (S), `deleteStructure` (D), `structureStatusChange` (U) | 782 |
| M4 | **E-BOM 조회** | `structureAscendingList.jsp` | `structureAscendingList`/`structureAscendingListExcel`/`structureDescendingExcelList` (S) | 1,064 |
| M5 | **설계변경 리스트** | `partMngHisList.jsp` | `partMngHistList` (S, read-only) | 198 |
vexplor_rps 측 라우트(예정):
```
GET /api/development/part-temp/list (M1 그리드)
POST /api/development/part-temp/deploy (M1 확정)
DEL /api/development/part-temp (M1·M2 삭제 공용)
GET /api/development/part/list (M2 그리드)
GET /api/development/part/:objid (M2 상세 팝업)
GET /api/development/ebom/list (M3 그리드)
PUT /api/development/ebom/status (M3 상태변경)
DEL /api/development/ebom/:objid (M3 삭제)
GET /api/development/ebom/ascending (M4 정전개)
GET /api/development/ebom/descending (M4 역전개)
GET /api/development/eo/history/list (M5 그리드)
```
---
## 2. 메뉴별 검색 필드 & 그리드 컬럼 (활성만)
### M1 PART 등록 (`partMngTempList.jsp`)
**검색**: SEARCH_PART_NO, SEARCH_PART_NAME (둘 다 autocomplete)
**그리드 23셀**: PART_NO · PART_NAME · CU01_CNT(3D) · CU02_CNT(2D) · CU03_CNT(PDF) · MATERIAL · HEAT_TREATMENT_HARDNESS · HEAT_TREATMENT_METHOD · SURFACE_TREATMENT · MAKER · PART_TYPE_TITLE · SPEC · ACCTFG_NM · ODRFG_NM · UNIT_DC_NM · UNITMANG_DC_NM · UNITCHNG_NB · LOT_FG_NM · USE_YN_NM · QC_FG_NM · SETITEM_FG_NM · REQ_FG_NM · UNIT_LENGTH/QTY
**액션**: 확정(Deploy) · 삭제 · 등록 · Excel Upload · 도면다중업로드 · 조회
**팝업**: partMngFormPopUp(신규) · partMngDetailPopUp(편집) · openPartExcelImportPopUp
**핵심 의존 테이블**: `part_mng` (메인) · `order_spec_mng` · `admin_supply_mng` · `bom_part_qty`
### M2 PART 조회 (`partMngList.jsp`)
**검색**: 없음(메인 조회 화면). 그리드 컬럼 동일하게 23셀(M1과 동일).
**액션**: 등록 · 삭제 · 도면연동 · ERP업로드(전체/단일/모두) · Excel Upload · 조회
**팝업**: partMngFormPopUp · partMngDetailPopUp · FileRegistPopup · openPartExcelImportPopUp
**핵심 의존 테이블**: `part_mng` · `bom_part_qty`
### M3 E-BOM 등록 (`structureList.jsp`)
**검색 9 필드**: customer_cd · project_name · unit_code · SEARCH_UNIT_NAME · SEARCH_WRITER · product_cd · SEARCH_PART_NO · SEARCH_PART_NAME · search_fromDate~toDate · status
**그리드 9셀**: PRODUCT_NAME · PART_NO · PART_NAME · BOM_CNT · DEPT_USER_NAME · REG_DATE · DEPLOY_DATE · REVISION · STATUS
**액션**: 조회 · 삭제 · E-BOM등록 · 상태변경
**팝업**: setStructureStandardFormPopup · setBomCopyFormPopup · setStructurePopupMainFS · changeDesignNotePopUp · structureStatusChangePopup · openBomReportExcelImportPopUp
**핵심 의존 테이블**: `part_bom_report` · `supply_mng` · `project_mgmt` · `pms_wbs_task` · `user_info` · `bom_part_qty` · `part_mng` · `comm_code`
### M4 E-BOM 조회 (`structureAscendingList.jsp`)
**검색 4 필드**: project_name · unit_code · search_partNo · search_partName
**그리드**: 동적 — MAX_LEVEL 레벨 컬럼 + 품번 · 품명 · 3D/2D/PDF · 수량 · 변경일 · 변경항목 · 규격 · 재질 · 중량 · 비고
**액션**: 정전개조회 · 역전개조회 · 엑셀다운로드(정/역전개)
**팝업**: partMngDetailPopUp(클릭) · FileRegistPopup(도면)
**핵심 의존 테이블**: `bom_part_qty` · `sales_bom_report` · `part_bom_report` · `product_mgmt_upg_detail`/`_master` · `part_mng` · `project_mgmt` · `pms_wbs_task` · `user_info` · `product_mgmt`
### M5 설계변경 리스트 (`partMngHisList.jsp`)
**검색 10 필드**: Year · contract_objid · unit_code · part_no · part_name · change_option · eo_start_date~end_date · change_type · part_type · writer_id
**그리드 16셀**: EO_NO · PROJECT_NO · PROJECT_NAME · UNIT_NAME · PARENT_PART_INFO · PART_NO · PART_NAME · QTY · QTY_TEMP · CHANGE_TYPE_NAME · CHANGE_OPTION_NAME · REVISION · EO_DATE · PART_TYPE_NAME · WRITER_NAME · HIS_REG_DATE_TITLE
**액션**: 조회만(Read-Only)
**팝업**: partMngHisDetailPopUp(행 클릭)
**핵심 의존 테이블**: `part_mng_history` · `project_mgmt` · `part_bom_report` · `pms_wbs_task` · `user_info` · `comm_code`
---
## 3. RPS DB 보유 매트릭스 (적용 완료)
| 테이블 | M1 | M2 | M3 | M4 | M5 | 종류 | RPS 상태 |
|---|:-:|:-:|:-:|:-:|:-:|---|---|
| `part_mng` | R/W | R/W | R | R | R | 메인 | ✅ +15컬럼 ALTER(`301_alter_part_mng.sql`) |
| `bom_part_qty` | R | R | R/W | R | R | BOM 수량 | ✅ 신규(`300`) |
| `part_bom_report` | R | R | R/W | R | R | BOM 리포트 헤더 | ✅ 신규(`300`) |
| `part_mng_history` | | – | – | – | R | 변경이력 | ✅ 신규(`300`) |
| `order_spec_mng` | R | – | – | – | – | 발주 스펙 | ✅ 신규(`300`) |
| `admin_supply_mng` | R | | – | – | – | 공급사(관리자) | ✅ 신규(`300`) |
| `supply_mng` | | – | R | – | – | 공급사 | ✅ 신규(`300`) |
| `sales_bom_report` | | | | R | – | 영업 BOM 단가 | ✅ 신규(`300`) |
| `product_mgmt_upg_master` | | – | – | R | – | 제품 업그레이드 마스터 | ✅ 신규(`300`) |
| `product_mgmt_upg_detail` | | – | – | R | – | 제품 업그레이드 디테일 | ✅ 신규(`300`) |
| `project_mgmt` | | | R | R | R | 프로젝트 | ✅ 기존 |
| `pms_wbs_task` | | | R | R | R | 작업/유닛 | ✅ 기존 |
| `user_info` | | | R | R | R | 사용자 | ✅ 기존(컬럼명 매핑 필요) |
| `comm_code` | | | R | R | R | 공통코드 | ✅ 기존 |
| `product_mgmt` | | | R | R | | 제품 | ✅ 기존 |
**→ 5개 메뉴 모두 P1에서 실데이터 표시 가능.**
---
## 4. GAP 매트릭스
| # | 우선 | 항목 | 권장 작업 |
|---|---|---|---|
| **DEV-1** | 🔴 | 개발관리 메뉴 자체 부재 → 5개 메뉴 운영판 1:1 이식 | **본 PR 시리즈 (3 묶음)** |
| **DEV-2** | 🔴 | `part_mng` 15컬럼 누락 (열처리/표면처리/단위/Y-N flag) | ✅ **완료**`301_alter_part_mng.sql` |
| **DEV-3** | 🔴 | 9개 테이블 부재 | ✅ **완료**`300_part_bom.sql` (BEGIN/COMMIT 트랜잭션, IDEMPOTENT) |
| **DEV-4** | 🟠 | `user_info` 컬럼명 매핑 (wace `empseq`/`rank` ↔ vexplor `emp_seq`/`rank_code`+`rank_name`) | 코드 측 alias로 처리 |
| **DEV-5** | 🟠 | M3 상태값(작성중/적용완료 등) — wace는 `comm_code` 0000099 자식 사용 | comm_code 그대로 사용. RPS DB에 이미 존재 여부 확인 후 부재 시 INSERT |
| **DEV-6** | 🟠 | M1·M2 팝업(등록/상세) 다이얼로그 — wace `partMngFormPopUp.jsp` 별도 LOC 큼 | M1·M2 묶음 PR에 포함 (한 번에 가는 게 효율) |
| **DEV-7** | 🟡 | M1 도면 다중 업로드 / M2 ERP 업로드 | 본 PR 시리즈 제외 (별 PR) |
| **DEV-8** | 🟡 | M3 BOM Excel Import / M4 엑셀 다운로드 | 본 PR 시리즈 제외 (별 PR) |
| **DEV-9** | 🟢 | M4 동적 MAX_LEVEL 컬럼 — BOM 트리 깊이에 따라 컬럼 추가 | DataGrid 동적 컬럼 모드. 본 PR(E-BOM 묶음)에 포함 |
| **DEV-10** | 🟢 | `admin_supply_mng.employee_email` 운영 타입 버그(`xid`) | ✅ **완료**`300` 추출 시 `character varying`으로 정정 |
---
## 5. PR 묶음 스코프 (3 PR 시리즈)
### 5.1 PR-A : PART 등록·조회 묶음 (M1+M2)
**범위**:
- backend: `routes/devPartRoutes.ts` + `services/devPartService.ts` + `controllers/devPartController.ts`
- 엔드포인트: `/api/development/part-temp/list`·`/deploy`, `/api/development/part/list`·`/:objid`, `/api/development/part` (DELETE)
- frontend:
- `app/(main)/COMPANY_16/development/part-regist/page.tsx` (M1)
- `app/(main)/COMPANY_16/development/part-search/page.tsx` (M2)
- `components/development/PartFormDialog.tsx` (등록/수정 공용)
- `components/development/PartDetailDialog.tsx` (상세)
- `lib/api/devPart.ts`
- 매퍼 1:1: `partMngTempGridList` · `partMngGridList` · `partMngFormPopUp` · `partMngDeploy` · `partMngDelete`
**제외**: 도면 다중 업로드 · ERP 업로드 · Excel Import → 별 PR
### 5.2 PR-B : E-BOM 등록·조회 묶음 (M3+M4)
**범위**:
- backend: `routes/devBomRoutes.ts` + `services/devBomService.ts` + `controllers/devBomController.ts`
- 엔드포인트: `/api/development/ebom/list`·`/status`·`/:objid` (DELETE), `/api/development/ebom/ascending`·`/descending`
- frontend:
- `app/(main)/COMPANY_16/development/ebom-regist/page.tsx` (M3)
- `app/(main)/COMPANY_16/development/ebom-search/page.tsx` (M4)
- `components/development/BomStandardFormDialog.tsx` (M3 등록)
- `components/development/BomStatusChangeDialog.tsx` (M3 상태변경)
- `lib/api/devBom.ts`
- M4 동적 MAX_LEVEL 컬럼 처리 (DataGrid 동적 컬럼)
**제외**: BOM Excel Import · 정/역전개 엑셀 다운로드 → 별 PR
### 5.3 PR-C : 설계변경 리스트 (M5)
**범위**:
- backend: `routes/devEoHistoryRoutes.ts` + `services/devEoHistoryService.ts` + `controllers/devEoHistoryController.ts`
- 엔드포인트: `/api/development/eo/history/list` (read-only)
- frontend:
- `app/(main)/COMPANY_16/development/change-list/page.tsx`
- `components/development/PartHisDetailDialog.tsx` (행 클릭 상세)
- `lib/api/devEoHistory.ts`
- read-only — INSERT/UPDATE/DELETE 없음
---
## 6. 사용자 결정 사항 (2026-05-12)
| # | 항목 | 결정 |
|---|---|---|
| 1 | 도메인 폴더 | 단일 `development/` |
| 2 | 메뉴 진행 순서 | PART 묶음(M1+M2) → E-BOM 묶음(M3+M4) → 설계변경(M5) |
| 3 | 문서 구조 | 단일 00-gap.md (본 문서) + 묶음별 *.md (총 3개) |
| 4 | DDL 적용 | 운영DB → vexplor_rps 직접 적용 완료 (9 신규 + 1 ALTER) |
---
## 7. 다음 단계
1. **PR-A** : `01-part.md` 작성 → backend route → frontend page 2개 → verify
2. **PR-B** : `02-ebom.md` 작성 → backend route → frontend page 2개 → verify
3. **PR-C** : `03-eo-history.md` 작성 → backend route → frontend page → verify
+313
View File
@@ -0,0 +1,313 @@
# PR-A : PART 등록·조회 묶음 구현 명세
> 작성: 2026-05-12 / 범위: 개발관리 M1(PART 등록) + M2(PART 조회) — 같은 `part_mng` 테이블 R/W, 매퍼 공유.
---
## 1. 매퍼 쿼리 1:1 매핑
원본 `wace_plm/src/com/pms/mapper/partMng.xml`:
| Query id | Line | 본 PR 매핑 | 용도 |
|---|---:|---|---|
| `partMngBaseSimple` (sql) | 78 | (서비스 측 공통 SELECT fragment) | PART_MNG 메인 88+ 컬럼 SELECT |
| `partMngTempGridList` | 2,354 | `GET /api/development/part-temp/list` | M1 그리드 (status != 'release') + ORDER_SPEC_MNG·ADMIN_SUPPLY_MNG JOIN |
| `partMngGridList` | 1,903 | `GET /api/development/part/list` | M2 그리드 (status = 'release' 고정) |
| `partMngInfo` | 2,699 | `GET /api/development/part/:objid` | 상세 단건 (편집 팝업 진입) |
| `insertpartInfo` | 7,625 | `POST /api/development/part` | 신규 등록 (38 컬럼 INSERT) |
| `updatePartDetail` | 2,711 | `PUT /api/development/part/:objid` | 상세 수정 (21 컬럼 UPDATE) |
| `partMngDeploy` | 4,190 | `POST /api/development/part-temp/deploy` | 확정 (M1→M2) STATUS='release', EO_NO 채번 |
| `partMngIsLastInit` | 4,230 | (deploy 트랜잭션 내부) | 동일 PART_NO 이전 IS_LAST='0' |
| `insertPartMngHistory` | 4,244 | (deploy 트랜잭션 내부) | PART_MNG_HISTORY 이력 INSERT |
| `partMngDelete` | 4,486 | `DELETE /api/development/part` (body: `objids: string[]`) | 다중 삭제 |
`partMngBaseSimple` SELECT 핵심: `PART_MNG P` + `COMM_CODE CC_UNIT`(UNIT) + `COMM_CODE CC_PART`(PART_TYPE) + `admin_supply_mng SUP`(SUPPLY_CODE) + LATERAL `BOM_PART_QTY`(LAST_PART_OBJID·status='deploy'·최신 1행) + LATERAL `COMM_CODE`(CHANGE_OPTION 다중 라벨) + `ATTACH_FILE_INFO`(3D/2D/PDF 파일 카운트). 23개 그리드 컬럼 + CODE_NAME 라벨 + Y/N flag CASE 변환 자체 처리.
---
## 2. API 엔드포인트 명세
### 2.1 M1 그리드 — `GET /api/development/part-temp/list`
**Query**:
```
search_part_no?: string
search_part_name?: string
search_material?: string
search_spec?: string
search_part_type?: string (PART_TYPE_CODE comm_code id)
writer?: string
status?: string // 단일: 'create'/'changing'/'editing'
status_arr?: string[] // 다중 (둘 중 하나만 사용)
product_code?: string
upg_no?: string
page?: number // 기본 1
page_size?: number // 기본 20
```
**SQL** (요약):
```sql
SELECT T.*, SORT (REVISION), O.PARTNER_TITLE, Q.OBJID/CHILD_OBJID/QTY/QTY_TEMP, Q_QTY (CASE),
(SELECT PART_NO FROM PART_MNG SP WHERE SP.OBJID = Q.PARENT_PART_NO) PARENT_PART_INFO
FROM <partMngBaseSimple> T
LEFT JOIN (ORDER_SPEC_MNG OSM JOIN ADMIN_SUPPLY_MNG SUP) O ON T.OBJID::VARCHAR = O.PART_OBJID::VARCHAR
LEFT JOIN BOM_PART_QTY Q ON (
T.OBJID IN (SELECT DISTINCT PM1.OBJID FROM PART_MNG PM1, PART_MNG PM2
WHERE PM1.STATUS='changing' AND PM2.STATUS!='changing'
AND PM2.OBJID = Q.PART_NO AND PM1.PART_NO = PM2.PART_NO)
AND Q.STATUS = 'beforeEdit'
)
WHERE 1=1 + (SEARCH_PART_NO/NAME/MATERIAL/SPEC/PART_TYPE, WRITER, STATUS, STATUS_ARR)
ORDER BY PARENT_PART_INFO, T.PART_NO
```
**Response**:
```ts
{
rows: PartTempRow[]; // 그리드 23셀 + 추가 필드
total: number;
page: number;
pageSize: number;
}
```
### 2.2 M2 그리드 — `GET /api/development/part/list`
**Query**: 위 + `search_year?` `search_hardness?` `search_method?` `search_surface?` `customer_objid?` `customer_cd?` `project_name?` `unit_code?` `search_design_date_from?` `search_design_date_to?` `is_last?` `eo?`
**SQL** (요약):
```sql
SELECT NUM (ROW_NUMBER), T.*,
DECODE(PART_TYPE, '0000063', '1',
(SELECT SUM(...) FROM BOM_PART_QTY Q WHERE Q.LAST_PART_OBJID=T.OBJID)::CHARACTER) BOM_QTY
FROM <partMngBaseSimple> T
WHERE 1=1 AND T.status='release' -- M1 vs M2 핵심 차이
+ (M1 + 5)
```
**Response**: `{ rows: PartRow[]; total, page, pageSize }`
### 2.3 단건 상세 — `GET /api/development/part/:objid`
```sql
SELECT T.* FROM <partMngBaseSimple> T WHERE T.OBJID = #{OBJID}
```
`PartRow` 단일 반환. 404 시 `{ error: 'not_found' }`.
### 2.4 신규 등록 — `POST /api/development/part`
**Body** (38 컬럼, 핵심):
```ts
{
part_objid: string; // numeric, 클라이언트 채번 (nanoid-based) 또는 서버 채번
part_no: string;
part_name: string;
unit?: string; // comm_code
qty?: string;
spec?: string;
material?: string;
thickness?: string; width?: string; height?: string;
out_diameter?: string; in_diameter?: string; length?: string;
remark?: string;
part_type: string; // comm_code (PART_TYPE_CODE)
product_mgmt_objid?: string;
supply_code?: string;
maker?: string;
contract_objid?: string;
post_processing?: string;
heat_treatment_hardness?: string;
heat_treatment_method?: string;
surface_treatment?: string;
acctfg?: string; // comm_code 계정구분
odrfg?: string; // 0=구매/1=생산/8=Phantom
unit_dc?: string; unitmang_dc?: string;
unitchng_nb?: string;
lot_fg?: '0'|'1';
use_yn?: '0'|'1';
qc_fg?: '0'|'1';
setitem_fg?: '0'|'1';
req_fg?: '0'|'1';
unit_length?: string;
unit_qty?: string;
}
```
**SQL**: `insertpartInfo` (7,625) 그대로. `STATUS='create'`, `REG_DATE=now()`, `IS_LAST='1'`, `WRITER=#{CONNECTUSERID}` (서버에서 `req.user.user_id` 주입).
**채번 정책**: `part_mng.objid`**`bigint`** 타입(다른 영업관리 테이블 `contract_mgmt.objid` 등은 varchar — `genObjid("CM")` 패턴 사용). bigint 컬럼은 prefix-string 못 쓰므로 **wace `CommonUtils.createObjId()` 1:1 구현** 사용:
```typescript
// backend-node/src/utils/objidUtil.ts (신규)
import { randomUUID } from 'crypto';
function javaStringHashCode(s: string): number {
let h = 0;
for (let i = 0; i < s.length; i++) h = (Math.imul(31, h) + s.charCodeAt(i)) | 0;
return h;
}
/** wace CommonUtils.createObjId() 1:1 — UUID v4 → 하이픈 제거(32 hex) → Java String.hashCode (int32) → String. 결과: -2,147,483,648 ~ 2,147,483,647. */
export function createObjId(): string {
return String(javaStringHashCode(randomUUID().replaceAll('-', '')));
}
```
INSERT 시 `body.part_objid` 가 비어 있으면 서버에서 `createObjId()` 호출(클라이언트 채번도 허용하되 권장 X).
### 2.5 상세 수정 — `PUT /api/development/part/:objid`
**Body** (21 컬럼, `updatePartDetail` 1:1):
`part_name, material, heat_treatment_hardness, heat_treatment_method, surface_treatment, maker, part_type, acctfg, odrfg, spec, unit_dc, unitmang_dc, unitchng_nb, lot_fg, use_yn, qc_fg, setitem_fg, req_fg, unit_length, unit_qty, remark`
`EDIT_DATE = NOW()` 자동.
### 2.6 확정 — `POST /api/development/part-temp/deploy`
**Body**: `{ objids: string[] }` — 다중 선택 확정.
**트랜잭션 (각 objid에 대해 순차 처리)**:
1. `partMngIsLastInit`: 같은 PART_NO 모든 행 `IS_LAST='0'`
2. `insertPartMngHistory`: 현재 행을 `PART_MNG_HISTORY`로 복사 (이력 보존)
3. `partMngDeploy`: 본 행 `IS_LAST='1'`, `STATUS='release'`, `DEPLOY_DATE=NOW()`, `REVISION=COALESCE(REVISION,'RE')`, `EO_DATE=...`, `EO_NO=` 채번 (IS_LONGD에 따라 `EOB{yy}-{seq}` or `EO{yy}-{seq}`)
**EO_NO 채번 SQL** (wace 운영판 그대로):
```sql
CASE WHEN P.IS_LONGD = '1' THEN
'EOB' || TO_CHAR(NOW(),'yy') || '-' || LPAD(
(SELECT COALESCE(SUBSTR(MAX(EO_NO),7,8)::INTEGER+1, 1)
FROM PART_MNG SP
WHERE SP.EO_NO LIKE 'EOB' || TO_CHAR(NOW(),'yy') || '-%'
AND SP.PART_NO != P.PART_NO
AND SP.REVISION != P.REVISION
)||'', 4, '0')
ELSE
'EO' || TO_CHAR(NOW(),'yy') || '-' || LPAD(... 'EO{yy}-{seq}' ...)
END
```
**Response**: `{ deployed: number, eo_nos: Record<objid, eo_no> }`
### 2.7 다중 삭제 — `DELETE /api/development/part`
**Body**: `{ objids: string[] }`
**SQL** (wace 그대로 POSITION 트릭):
```sql
DELETE FROM PART_MNG WHERE POSITION(OBJID||',' IN #{checkArr}||',') > 0
```
→ backend-node에서는 PostgreSQL 표준인 `WHERE OBJID = ANY($1::numeric[])` 로 정리(동일 효과 + 인덱스 활용 가능).
---
## 3. Backend 파일 구조
```
backend-node/src/
routes/
devPartRoutes.ts // Express Router — 7 endpoint
controllers/
devPartController.ts // req/res 처리, validation
services/
devPartService.ts // SQL 실행 (pg 트랜잭션 처리 포함)
devPartSqlFragments.ts // partMngBaseSimple SELECT fragment 재사용
```
`app.ts``app.use('/api/development', devPartRoutes)` 추가 (또는 메뉴 묶음 라우터 도입 시 그쪽).
---
## 4. Frontend 파일 구조
```
frontend/
app/(main)/COMPANY_16/development/
part-regist/
page.tsx // M1 그리드 + 상단 액션 + 페이징
part-search/
page.tsx // M2 그리드 + 상단 액션 + 페이징
components/development/
PartFormDialog.tsx // 신규/수정 통합 (mode prop)
PartDetailDialog.tsx // 읽기 전용 상세
lib/api/
devPart.ts // 7 endpoint 호출 함수 + 타입
```
### 4.1 그리드 23셀 (M1·M2 공통)
| key | 라벨 | 정렬 | 너비 |
|---|---|---|---:|
| part_no | 품번 | left | 140 |
| part_name | 품명 | left | 220 |
| cu01_cnt | 3D | right | 60 |
| cu02_cnt | 2D | right | 60 |
| cu03_cnt | PDF | right | 60 |
| material | 재료 | left | 100 |
| heat_treatment_hardness | 열처리경도 | left | 110 |
| heat_treatment_method | 열처리방법 | left | 110 |
| surface_treatment | 표면처리 | left | 100 |
| maker | 메이커 | left | 100 |
| part_type_title | 범주이름 | left | 100 |
| spec | 규격 | left | 140 |
| acctfg_nm | 계정구분 | center | 80 |
| odrfg_nm | 조달구분 | center | 80 |
| unit_dc_nm | 재고단위 | center | 80 |
| unitmang_dc_nm | 관리단위 | center | 80 |
| unitchng_nb | 환산수량 | right | 90 |
| lot_fg_nm | LOT구분 | center | 80 |
| use_yn_nm | 사용여부 | center | 80 |
| qc_fg_nm | 검사여부 | center | 80 |
| setitem_fg_nm | SET품여부 | center | 90 |
| req_fg_nm | 의뢰여부 | center | 80 |
| unit_length / unit_qty | 개당길이/수량 | right | 100 |
추가 (M1만): `partner_title`, `q_qty`, `parent_part_info`
추가 (M2만): `bom_qty`
### 4.2 검색 폼
**M1 (PART 등록)** — 2 필드: SEARCH_PART_NO · SEARCH_PART_NAME (둘 다 PartSelect autocomplete)
**M2 (PART 조회)** — 메인 조회 화면 (별도 검색 폼 없음, 그리드 헤더 inline 필터로 처리하거나 상단 간소화 검색바 1줄로 통합 — 본 PR 우선 `<Input>` 2개로 시작, 추후 보강)
### 4.3 액션 버튼 (각 page 상단)
**M1**: 등록 · 수정 · 삭제 · 확정 · 조회
**M2**: 등록 · 수정 · 삭제 · 조회 (도면연동/ERP업로드/Excel은 본 PR 제외)
### 4.4 PartFormDialog (신규/수정 통합)
- mode: `'create' | 'edit'`
- 38 필드 — `<Input>` + `<CommCodeSelect>` 조합
- 검증: part_no/part_name 필수, comm_code 필드는 SmartSelect
- 신규: POST → 신규 행 추가
- 수정: PUT → 21 필드만 전송 (insertpartInfo는 38, updatePartDetail는 21 — wace 그대로)
### 4.5 PartDetailDialog (읽기 전용)
행 더블클릭 시 진입. 모든 필드 disabled. "수정" 버튼 → PartFormDialog(mode='edit') 전환.
---
## 5. 본 PR 제외 항목
| 항목 | 사유 / 후속 |
|---|---|
| 도면 다중 업로드 (M1) | `ATTACH_FILE_INFO` 다파일 업로드 — 별 PR |
| ERP 업로드 (M2) | wace 외부 시스템 연동 — 별 PR |
| Excel Upload (M1·M2) | `openPartExcelImportPopUp.jsp` 별도 — 별 PR |
| BOM_PART_QTY R/W (M3 영역) | PR-B 에서 다룸 |
| EO_NO 채번 분기 일부 (`IS_LONGD` flag) | 본 PR 포함 — 운영판 그대로 |
---
## 6. 검증 시나리오 (verify.md 기준)
1. M1 페이지 진입 → 그리드 표시(status != 'release') 확인
2. "등록" → PartFormDialog 신규 → POST → M1 그리드에 새 행
3. M1 행 선택 → "확정" → POST deploy → STATUS='release', EO_NO 채번 확인
4. M2 페이지 진입 → deploy된 행이 M2 그리드에 표시
5. M2 행 선택 → "수정" → PartFormDialog 수정 → PUT
6. M2 행 다중 선택 → "삭제" → DELETE → 그리드에서 제거
7. 검색 (SEARCH_PART_NO/NAME) → 필터 적용 확인
8. 운영DB 11133/waceplm 의 동일 SQL 결과와 vexplor_rps 결과 행 수 비교 (sanity)
@@ -0,0 +1,428 @@
-- ============================================================
-- 개발관리(PART/E-BOM/설계변경) 운영 DDL — wace_plm 운영DB(211.115.91.141:11133/waceplm) 추출
-- 추출일: 2026-05-12
-- 추출 방법: information_schema + pg_indexes + pg_description 쿼리
-- (pg_dump 14.19 ↔ PG 16.8 mismatch로 pg_dump 사용 불가)
--
-- 대상 테이블 9개 (운영 카운트):
-- admin_supply_mng 28 cols / 7건 ← 공급업체 마스터(관리자)
-- bom_part_qty 19 cols / 835건 ← BOM 수량 트리
-- order_spec_mng 12 cols / 12,522건 ← 발주 스펙 이력
-- part_bom_report 23 cols / 40건 ← BOM 리포트 헤더
-- part_mng_history 59 cols / 263건 ← 파트 이력(설계변경)
-- product_mgmt_upg_detail 7 cols / 87건 ← 제품 업그레이드 디테일
-- product_mgmt_upg_master 5 cols / 6건 ← 제품 업그레이드 마스터
-- sales_bom_report 16 cols / 1,529건 ← 영업 BOM 단가
-- supply_mng 29 cols / 1건 ← 공급업체(고객)
--
-- 비고:
-- · 운영 스키마 1:1 보존 — 컬럼 순서/타입/길이/default 모두 운영과 동일.
-- · objid 컬럼은 wace Java 측에서 UUID/임의 채번(연관 시퀀스 없음 — 확인 완료).
-- · admin_supply_mng.objid·supply_mng.objid는 numeric, default 0(운영 그대로).
-- · admin_supply_mng.employee_email 운영 타입이 'xid'(PostgreSQL 시스템 타입, transaction id)
-- → 데이터 의미(이메일)와 무관한 운영 측 추정 실수. character varying 으로 정정 적용.
-- · part_mng_history.objid는 numeric NOT NULL (PK)이지만 default 미지정.
-- · product_mgmt_upg_master PK는 (objid, target_objid) 복합키.
-- · sales_bom_report 의 supply_objid*/price* 컬럼 length 가 들쭉날쭉한 부분은 운영 그대로.
-- · sales_bom_report_parent_objid_idx 는 UNIQUE 인덱스(운영 정의 그대로 — 1 BOM당 1 단가).
-- · company_code 분기 없음(vexplor_rps는 COMPANY_16 단독).
-- ============================================================
BEGIN;
-- ------------------------------------------------------------
-- 1) admin_supply_mng (공급업체 마스터 — 관리자 측 입력)
-- ------------------------------------------------------------
DROP TABLE IF EXISTS admin_supply_mng CASCADE;
CREATE TABLE admin_supply_mng (
objid numeric NOT NULL DEFAULT '0'::numeric,
supply_code character varying(100) DEFAULT 'NULL::character varying'::character varying,
supply_name character varying(100) DEFAULT 'NULL::character varying'::character varying,
reg_no character varying(100) DEFAULT 'NULL::character varying'::character varying,
supply_address character varying(500) DEFAULT 'NULL::character varying'::character varying,
supply_busname character varying(100) DEFAULT 'NULL::character varying'::character varying,
supply_stockname character varying(100) DEFAULT 'NULL::character varying'::character varying,
supply_tel_no character varying DEFAULT 'NULL::character varying'::character varying,
supply_fax_no character varying DEFAULT 'NULL::character varying'::character varying,
charge_user_name character varying DEFAULT 'NULL::character varying'::character varying,
payment_method character varying,
reg_id character varying DEFAULT 'NULL::character varying'::character varying,
reg_date timestamp,
status character varying DEFAULT 'NULL::character varying'::character varying,
area_cd character varying DEFAULT 'NULL::character varying'::character varying,
bus_reg_no character varying DEFAULT 'NULL::character varying'::character varying,
office_no character varying DEFAULT 'NULL::character varying'::character varying,
email character varying DEFAULT 'NULL::character varying'::character varying,
account_code character varying,
remark character varying,
account_bank character varying,
account_number character varying,
account_user_name character varying,
employee_name character varying,
employee_position character varying,
employee_number character varying,
-- 운영은 'xid' 시스템 타입으로 잘못 정의됨. 의미상 이메일 → character varying 으로 정정.
employee_email character varying,
david character varying(50),
CONSTRAINT admin_supply_mng_pkey PRIMARY KEY (objid)
);
-- ------------------------------------------------------------
-- 2) bom_part_qty (BOM 수량 트리 — 835건)
-- ------------------------------------------------------------
DROP TABLE IF EXISTS bom_part_qty CASCADE;
CREATE TABLE bom_part_qty (
bom_report_objid character varying(64) NOT NULL,
objid character varying(64) NOT NULL,
parent_objid character varying(64) DEFAULT 'NULL::character varying'::character varying,
child_objid character varying(64) DEFAULT 'NULL::character varying'::character varying,
parent_part_no character varying(64) DEFAULT 'NULL::character varying'::character varying,
part_no character varying(64) DEFAULT 'NULL::character varying'::character varying,
qty character varying,
regdate timestamp,
seq integer,
status character varying,
deploy_date character varying,
deploy_user_id character varying,
edit_date character varying,
writer character varying,
qty_temp character varying,
last_part_objid character varying,
editer character varying,
item_qty character varying,
supplier character varying,
CONSTRAINT bom_part_qty_pkey PRIMARY KEY (objid)
);
CREATE INDEX bom_part_qty_bom_report_objid2_idx ON bom_part_qty USING btree (bom_report_objid, last_part_objid, part_no);
CREATE INDEX bom_part_qty_bom_report_objid_idx ON bom_part_qty USING btree (bom_report_objid);
CREATE INDEX bom_part_qty_last_part_objid_idx ON bom_part_qty USING btree (last_part_objid);
CREATE INDEX bom_part_qty_parent_objid_idx ON bom_part_qty USING btree (parent_objid);
CREATE INDEX idx_bom_part_qty_part_no ON bom_part_qty USING btree (part_no) WHERE ((part_no IS NOT NULL) AND ((part_no)::text <> ''::text));
COMMENT ON COLUMN bom_part_qty.status IS '상태';
COMMENT ON COLUMN bom_part_qty.deploy_date IS '배포일';
COMMENT ON COLUMN bom_part_qty.deploy_user_id IS '배포자';
COMMENT ON COLUMN bom_part_qty.edit_date IS '수정일';
COMMENT ON COLUMN bom_part_qty.writer IS '등록자';
COMMENT ON COLUMN bom_part_qty.qty_temp IS '수량(설변중)';
COMMENT ON COLUMN bom_part_qty.last_part_objid IS '마지막 품번키';
COMMENT ON COLUMN bom_part_qty.editer IS '수정자';
COMMENT ON COLUMN bom_part_qty.item_qty IS '항목수량';
COMMENT ON COLUMN bom_part_qty.supplier IS '공급업체';
-- ------------------------------------------------------------
-- 3) order_spec_mng (발주 스펙 이력 — 12,522건, 운영 최다)
-- ------------------------------------------------------------
DROP TABLE IF EXISTS order_spec_mng CASCADE;
CREATE TABLE order_spec_mng (
objid character varying NOT NULL,
seq character varying NOT NULL,
part_objid character varying NOT NULL,
partner_rank character varying,
partner_objid character varying,
partner_price character varying,
partner_qty character varying,
apply_date character varying,
remark character varying,
regdate timestamp,
is_last character varying,
writer character varying,
CONSTRAINT order_spec_mng_pkey PRIMARY KEY (objid)
);
-- ------------------------------------------------------------
-- 4) part_bom_report (BOM 리포트 헤더 — 40건)
-- ------------------------------------------------------------
DROP TABLE IF EXISTS part_bom_report CASCADE;
CREATE TABLE part_bom_report (
objid character varying NOT NULL DEFAULT ''::character varying,
customer_objid character varying,
contract_objid character varying,
unit_code character varying,
revision character varying,
writer character varying(64),
regdate timestamp,
status character varying(64),
deploy_date character varying(64),
eo_no character varying(100),
eo_date character varying(100),
note character varying(2000),
edit_date timestamp,
editer character varying,
unit_code_old character varying,
multi_break_yn character varying,
multi_yn character varying,
multi_master_yn character varying,
multi_master_objid character varying,
product_cd character varying,
part_no character varying,
part_name character varying,
version character varying,
CONSTRAINT part_bom_report_pkey PRIMARY KEY (objid)
);
CREATE INDEX idx_part_bom_report_customer ON part_bom_report USING btree (customer_objid) WHERE ((customer_objid IS NOT NULL) AND ((customer_objid)::text <> ''::text));
CREATE INDEX idx_part_bom_report_regdate ON part_bom_report USING btree (regdate DESC);
CREATE INDEX idx_part_bom_report_writer ON part_bom_report USING btree (writer);
CREATE INDEX part_bom_report_contract_objid_idx ON part_bom_report USING btree (contract_objid);
CREATE INDEX part_bom_report_unit_code_idx ON part_bom_report USING btree (unit_code, contract_objid);
COMMENT ON COLUMN part_bom_report.objid IS 'OBJECT ID';
COMMENT ON COLUMN part_bom_report.customer_objid IS '고객사 OBJID';
COMMENT ON COLUMN part_bom_report.contract_objid IS '계약objid';
COMMENT ON COLUMN part_bom_report.unit_code IS 'unit';
COMMENT ON COLUMN part_bom_report.revision IS 'rev';
COMMENT ON COLUMN part_bom_report.writer IS '작성자';
COMMENT ON COLUMN part_bom_report.regdate IS '등록일';
COMMENT ON COLUMN part_bom_report.status IS '상태';
COMMENT ON COLUMN part_bom_report.deploy_date IS '배포일';
COMMENT ON COLUMN part_bom_report.edit_date IS '수정일';
COMMENT ON COLUMN part_bom_report.editer IS '수정자';
COMMENT ON COLUMN part_bom_report.unit_code_old IS 'UNIT_CODE';
COMMENT ON COLUMN part_bom_report.multi_break_yn IS '동시적용프로젝트 깨짐 여부';
COMMENT ON COLUMN part_bom_report.multi_yn IS '동시적용프로젝트 여부';
COMMENT ON COLUMN part_bom_report.multi_master_yn IS '동시적용프로젝트 마스터 여부';
COMMENT ON COLUMN part_bom_report.multi_master_objid IS '동시적용프로젝트 마스터 키';
-- ------------------------------------------------------------
-- 5) part_mng_history (파트 이력 — 설계변경 핵심, 59 cols / 263건)
-- ------------------------------------------------------------
DROP TABLE IF EXISTS part_mng_history CASCADE;
CREATE TABLE part_mng_history (
objid numeric NOT NULL,
product_mgmt_objid character varying(100) DEFAULT NULL::character varying,
upg_no character varying(100) DEFAULT NULL::character varying,
part_no character varying(100) DEFAULT NULL::character varying,
part_name character varying(100) DEFAULT NULL::character varying,
unit character varying(50) DEFAULT NULL::character varying,
qty character varying(50) DEFAULT NULL::character varying,
spec character varying(100) DEFAULT 'NULL::character varying'::character varying,
material character varying(100) DEFAULT NULL::character varying,
weight character varying(50) DEFAULT NULL::character varying,
part_type character varying(100) DEFAULT NULL::character varying,
remark character varying(1000) DEFAULT NULL::character varying,
es_spec character varying(100) DEFAULT NULL::character varying,
ms_spec character varying(100) DEFAULT NULL::character varying,
change_option character varying(50) DEFAULT NULL::character varying,
design_apply_point character varying(50) DEFAULT NULL::character varying,
management_flag character varying(50) DEFAULT NULL::character varying,
revision character varying(50) DEFAULT NULL::character varying,
status character varying(30) DEFAULT NULL::character varying,
reg_date timestamp,
edit_date timestamp,
writer character varying(30) DEFAULT NULL::character varying,
is_last character varying(5) DEFAULT NULL::character varying,
eo_no character varying,
eo_temp character varying,
excel_upload_seq character varying,
sourcing_code character varying,
sub_material character varying(100) DEFAULT 'NULL::character varying'::character varying,
parent_part_no character varying,
design_date character varying,
eo_date character varying,
deploy_date timestamp,
thickness character varying,
width character varying,
height character varying,
out_diameter character varying,
in_diameter character varying,
length character varying,
supply_code character varying,
change_type character varying,
contract_objid character varying,
maker character varying,
qty_temp character varying,
bom_report_objid character varying,
parent_part_objid character varying,
parent_qty_child_objid character varying,
bom_qty_status character varying,
his_reg_date timestamp,
his_writer character varying,
his_status character varying,
qty_child_objid character varying,
bom_status character varying,
bom_deploy_date timestamp,
chg_part_objid character varying,
chg_part_no character varying,
chg_part_rev character varying,
heat_treatment_hardness character varying,
heat_treatment_method character varying,
surface_treatment character varying,
CONSTRAINT part_mng_history_pkey PRIMARY KEY (objid)
);
COMMENT ON COLUMN part_mng_history.qty_temp IS '수량(설변중)';
COMMENT ON COLUMN part_mng_history.bom_report_objid IS 'BOM 키';
COMMENT ON COLUMN part_mng_history.parent_part_objid IS '부모 파트 키';
COMMENT ON COLUMN part_mng_history.parent_qty_child_objid IS '부모 구조 키';
COMMENT ON COLUMN part_mng_history.bom_qty_status IS 'BOM 상태';
COMMENT ON COLUMN part_mng_history.his_reg_date IS '등록일';
COMMENT ON COLUMN part_mng_history.his_writer IS '등록자';
COMMENT ON COLUMN part_mng_history.his_status IS '상태';
COMMENT ON COLUMN part_mng_history.qty_child_objid IS '구조 키';
COMMENT ON COLUMN part_mng_history.bom_status IS 'BOM 상태';
COMMENT ON COLUMN part_mng_history.bom_deploy_date IS '배포일';
-- ------------------------------------------------------------
-- 6) product_mgmt_upg_detail (제품 업그레이드 디테일 — 87건)
-- ------------------------------------------------------------
DROP TABLE IF EXISTS product_mgmt_upg_detail CASCADE;
CREATE TABLE product_mgmt_upg_detail (
objid integer NOT NULL,
target_objid integer,
upg_name character varying(100),
upg_code character varying(100),
vc character varying(100),
note character varying(1000),
product_objid integer,
CONSTRAINT product_mgmt_upg_detail_pkey PRIMARY KEY (objid)
);
COMMENT ON COLUMN product_mgmt_upg_detail.objid IS 'objid';
COMMENT ON COLUMN product_mgmt_upg_detail.target_objid IS 'upg_masterobjid';
COMMENT ON COLUMN product_mgmt_upg_detail.upg_name IS 'upg명';
COMMENT ON COLUMN product_mgmt_upg_detail.upg_code IS 'upg코드';
COMMENT ON COLUMN product_mgmt_upg_detail.vc IS 'vc';
COMMENT ON COLUMN product_mgmt_upg_detail.note IS '비고';
-- ------------------------------------------------------------
-- 7) product_mgmt_upg_master (제품 업그레이드 마스터 — 6건, 복합 PK)
-- ------------------------------------------------------------
DROP TABLE IF EXISTS product_mgmt_upg_master CASCADE;
CREATE TABLE product_mgmt_upg_master (
objid integer NOT NULL,
target_objid integer NOT NULL,
spec_name character varying NOT NULL,
writer character varying,
regdate timestamp,
CONSTRAINT product_mgmt_upg_master_pkey PRIMARY KEY (objid, target_objid)
);
COMMENT ON COLUMN product_mgmt_upg_master.objid IS '제품사양마스터코드';
COMMENT ON COLUMN product_mgmt_upg_master.target_objid IS '양산마스터코드';
COMMENT ON COLUMN product_mgmt_upg_master.spec_name IS '사양명';
COMMENT ON COLUMN product_mgmt_upg_master.writer IS '작성자';
COMMENT ON COLUMN product_mgmt_upg_master.regdate IS '동록일';
-- ------------------------------------------------------------
-- 8) sales_bom_report (영업 BOM 단가 — 1,529건)
-- ------------------------------------------------------------
DROP TABLE IF EXISTS sales_bom_report CASCADE;
CREATE TABLE sales_bom_report (
objid character varying NOT NULL DEFAULT ''::character varying,
parent_objid character varying,
supply_objid character varying,
price character varying,
supply_objid1 character varying,
price1 character varying(64),
supply_objid2 character varying(100),
price2 character varying(64),
supply_objid3 character varying(64),
price3 character varying(100),
supply_objid4 character varying(100),
price4 character varying(2000),
writer character varying,
regdate timestamp,
update_date timestamp,
modifier character varying,
CONSTRAINT sales_bom_report_pkey PRIMARY KEY (objid)
);
-- 운영 인덱스는 UNIQUE — 1 BOM(parent_objid) 당 1 단가행
CREATE UNIQUE INDEX sales_bom_report_parent_objid_idx ON sales_bom_report USING btree (parent_objid);
COMMENT ON COLUMN sales_bom_report.objid IS '';
COMMENT ON COLUMN sales_bom_report.parent_objid IS 'bom_report_objid';
COMMENT ON COLUMN sales_bom_report.supply_objid IS '공급업체key';
COMMENT ON COLUMN sales_bom_report.price IS '단가';
COMMENT ON COLUMN sales_bom_report.supply_objid1 IS '레이져업체명';
COMMENT ON COLUMN sales_bom_report.price1 IS '단가';
COMMENT ON COLUMN sales_bom_report.supply_objid2 IS '용접업체명';
COMMENT ON COLUMN sales_bom_report.price2 IS '단가';
COMMENT ON COLUMN sales_bom_report.supply_objid3 IS '가공업체명';
COMMENT ON COLUMN sales_bom_report.price3 IS '단가';
COMMENT ON COLUMN sales_bom_report.supply_objid4 IS '후처리';
COMMENT ON COLUMN sales_bom_report.price4 IS '단가';
COMMENT ON COLUMN sales_bom_report.writer IS '담당자';
COMMENT ON COLUMN sales_bom_report.regdate IS '작성일';
COMMENT ON COLUMN sales_bom_report.update_date IS '수정일';
COMMENT ON COLUMN sales_bom_report.modifier IS '수정자';
-- ------------------------------------------------------------
-- 9) supply_mng (공급업체/고객 — 1건)
-- ------------------------------------------------------------
DROP TABLE IF EXISTS supply_mng CASCADE;
CREATE TABLE supply_mng (
objid numeric NOT NULL DEFAULT 0,
supply_code character varying(100) DEFAULT NULL::character varying,
supply_name character varying(100) DEFAULT NULL::character varying,
reg_no character varying(100) DEFAULT NULL::character varying,
supply_address character varying(500) DEFAULT NULL::character varying,
supply_busname character varying(100) DEFAULT NULL::character varying,
supply_stockname character varying(100) DEFAULT NULL::character varying,
supply_tel_no character varying(30) DEFAULT NULL::character varying,
supply_fax_no character varying(30) DEFAULT NULL::character varying,
charge_user_name character varying(100) DEFAULT NULL::character varying,
payment_method character varying(100),
reg_id character varying(100) DEFAULT NULL::character varying,
reg_date timestamp,
status character varying(32) DEFAULT NULL::character varying,
area_cd character varying(32) DEFAULT NULL::character varying,
bus_reg_no character varying(100) DEFAULT NULL::character varying,
office_no character varying(32) DEFAULT 'NULL::character varying'::character varying,
email character varying(32) DEFAULT 'NULL::character varying'::character varying,
cus_no character varying,
manager1_name character varying(100),
manager1_email character varying(100),
manager2_name character varying(100),
manager2_email character varying(100),
manager3_name character varying(100),
manager3_email character varying(100),
manager4_name character varying(100),
manager4_email character varying(100),
manager5_name character varying(100),
manager5_email character varying(100),
CONSTRAINT supply_mng_pkey PRIMARY KEY (objid)
);
COMMENT ON COLUMN supply_mng.supply_code IS '구분';
COMMENT ON COLUMN supply_mng.supply_name IS '고객명';
COMMENT ON COLUMN supply_mng.reg_no IS '법인/주민번호';
COMMENT ON COLUMN supply_mng.supply_address IS '주소';
COMMENT ON COLUMN supply_mng.supply_busname IS '업태';
COMMENT ON COLUMN supply_mng.supply_stockname IS '업종';
COMMENT ON COLUMN supply_mng.supply_tel_no IS '핸드폰';
COMMENT ON COLUMN supply_mng.supply_fax_no IS '팩스번호';
COMMENT ON COLUMN supply_mng.charge_user_name IS '대표자명';
COMMENT ON COLUMN supply_mng.reg_id IS '실사용자명';
COMMENT ON COLUMN supply_mng.reg_date IS '등록일';
COMMENT ON COLUMN supply_mng.status IS '상태';
COMMENT ON COLUMN supply_mng.area_cd IS '지역';
COMMENT ON COLUMN supply_mng.bus_reg_no IS '사업자등록번호';
COMMENT ON COLUMN supply_mng.office_no IS '오피스no';
COMMENT ON COLUMN supply_mng.email IS '이메일';
COMMENT ON COLUMN supply_mng.cus_no IS '고객번호';
COMMENT ON COLUMN supply_mng.manager1_name IS '담당자1 이름';
COMMENT ON COLUMN supply_mng.manager1_email IS '담당자1 이메일';
COMMENT ON COLUMN supply_mng.manager2_name IS '담당자2 이름';
COMMENT ON COLUMN supply_mng.manager2_email IS '담당자2 이메일';
COMMENT ON COLUMN supply_mng.manager3_name IS '담당자3 이름';
COMMENT ON COLUMN supply_mng.manager3_email IS '담당자3 이메일';
COMMENT ON COLUMN supply_mng.manager4_name IS '담당자4 이름';
COMMENT ON COLUMN supply_mng.manager4_email IS '담당자4 이메일';
COMMENT ON COLUMN supply_mng.manager5_name IS '담당자5 이름';
COMMENT ON COLUMN supply_mng.manager5_email IS '담당자5 이메일';
COMMIT;
-- ============================================================
-- vexplor_rps 적용 방법 (메인 agent 검토 후 직접 실행):
-- PGPASSWORD='vexplor0909!!' psql -h 211.115.91.141 -p 11134 -U postgres -d vexplor_rps \
-- -f /Users/jhj/vexplor_rps/docs/migration/development/ddl-extracted/300_part_bom.sql
--
-- 적용 후 검증:
-- PGPASSWORD='vexplor0909!!' psql -h 211.115.91.141 -p 11134 -U postgres -d vexplor_rps -c "
-- SELECT relname, (SELECT count(*) FROM pg_attribute WHERE attrelid = c.oid AND attnum > 0 AND NOT attisdropped) AS cols
-- FROM pg_class c WHERE relkind='r' AND relnamespace=(SELECT oid FROM pg_namespace WHERE nspname='public')
-- AND relname IN ('admin_supply_mng','bom_part_qty','order_spec_mng','part_bom_report','part_mng_history','product_mgmt_upg_detail','product_mgmt_upg_master','sales_bom_report','supply_mng')
-- ORDER BY relname;"
-- 기대값: 28 / 19 / 12 / 23 / 59 / 7 / 5 / 16 / 29
-- ============================================================
@@ -0,0 +1,45 @@
-- ============================================================
-- part_mng ALTER — 개발관리 메뉴(PART 등록/조회) 누락 컬럼 추가
-- 추출일: 2026-05-12
-- 출처: 211.115.91.141:11133/waceplm (PG 16.8)
-- 대상: 211.115.91.141:11134/vexplor_rps
--
-- 사유: wace PART 등록/조회 그리드 23개 컬럼 중 15개가 vexplor part_mng 에 부재.
-- 전부 ADD COLUMN IF NOT EXISTS 로 안전하게 추가 (IDEMPOTENT).
-- ============================================================
BEGIN;
ALTER TABLE part_mng ADD COLUMN IF NOT EXISTS heat_treatment_hardness character varying;
ALTER TABLE part_mng ADD COLUMN IF NOT EXISTS heat_treatment_method character varying;
ALTER TABLE part_mng ADD COLUMN IF NOT EXISTS surface_treatment character varying;
ALTER TABLE part_mng ADD COLUMN IF NOT EXISTS acctfg character varying;
ALTER TABLE part_mng ADD COLUMN IF NOT EXISTS odrfg character varying;
ALTER TABLE part_mng ADD COLUMN IF NOT EXISTS unit_dc character varying(20);
ALTER TABLE part_mng ADD COLUMN IF NOT EXISTS unitmang_dc character varying(20);
ALTER TABLE part_mng ADD COLUMN IF NOT EXISTS unitchng_nb numeric(11,6);
ALTER TABLE part_mng ADD COLUMN IF NOT EXISTS lot_fg character(1);
ALTER TABLE part_mng ADD COLUMN IF NOT EXISTS use_yn character(1);
ALTER TABLE part_mng ADD COLUMN IF NOT EXISTS qc_fg character(1);
ALTER TABLE part_mng ADD COLUMN IF NOT EXISTS setitem_fg character(1);
ALTER TABLE part_mng ADD COLUMN IF NOT EXISTS req_fg character(1);
ALTER TABLE part_mng ADD COLUMN IF NOT EXISTS unit_length character varying(20);
ALTER TABLE part_mng ADD COLUMN IF NOT EXISTS unit_qty character varying(20);
COMMENT ON COLUMN part_mng.heat_treatment_hardness IS '열처리경도';
COMMENT ON COLUMN part_mng.heat_treatment_method IS '열처리방법';
COMMENT ON COLUMN part_mng.surface_treatment IS '표면처리';
COMMENT ON COLUMN part_mng.acctfg IS '계정구분 (comm_code)';
COMMENT ON COLUMN part_mng.odrfg IS '조달구분 (comm_code)';
COMMENT ON COLUMN part_mng.unit_dc IS '재고단위 (comm_code)';
COMMENT ON COLUMN part_mng.unitmang_dc IS '관리단위 (comm_code)';
COMMENT ON COLUMN part_mng.unitchng_nb IS '환산수량';
COMMENT ON COLUMN part_mng.lot_fg IS 'LOT구분 (Y/N)';
COMMENT ON COLUMN part_mng.use_yn IS '사용여부 (Y/N)';
COMMENT ON COLUMN part_mng.qc_fg IS '검사여부 (Y/N)';
COMMENT ON COLUMN part_mng.setitem_fg IS 'SET품여부 (Y/N)';
COMMENT ON COLUMN part_mng.req_fg IS '의뢰여부 (Y/N)';
COMMENT ON COLUMN part_mng.unit_length IS '개당길이';
COMMENT ON COLUMN part_mng.unit_qty IS '개당수량';
COMMIT;