Files
wace_rps/frontend/lib/api/devBom.ts
T
hjjeong 68d2dcb32e 개발관리>E-BOM 조회 — 운영판 1:1 그리드 + 토글 + 품번 상세 + 검색 anchor 정정
(1) 정전개 트리 화면 운영판 wace structureAscendingList.jsp 1:1 정정:
   - L1..LMaxLevel 컬럼 — row.lev 와 일치하는 컬럼에만 "*" 표시 (이전엔 품번 표시)
   - 별도 품번 컬럼 1개 (모든 행 part_no)
   - 3D/2D/PDF — renderType: "folder" (wace fnc_getFolderIcon 1:1)
   - 컬럼 운영판 1:1 : 품번/품명/수량/항목수량/3D/2D/PDF/재료/열처리경도/열처리방법/표면처리/메이커/범주이름/비고
   - 제거 : 변경일/REV/규격/중량 (운영판 미사용)

(2) 토글 -/+ 버튼 추가 (wace 트리 1:1):
   - 첫 컬럼 __toggle — 자식 있는 행만 − / + 표시, 클릭 시 자식 숨김/표시
   - collapsedChildIds Set<string> 상태로 접힘 관리
   - ancestor 체인: parent_objid → 부모 행 child_objid 추적 (cycle guard)
   - 가시 행 필터: ancestor 중 하나라도 collapsed Set 에 있으면 hide → 자손 전체 숨김
   - 새 조회 시 collapsed Set 초기화 (모두 펼침)

(3) 품번 셀 클릭 → PartDetailDialog (wace partMngDetailPopUp 1:1):
   - row.part_no = part_mng.objid::varchar 이므로 그대로 detail dialog 의 objid 로 전달
   - ebom-search 페이지에 PartDetailDialog 임포트 + state

(4) 검색 필터 anchor 정정 (사용자 검증: 1행만 나오고 자식 안 풀림):
   - 이전: search_part_no/search_part_name 을 결과 단계 WHERE PM.part_no LIKE ... 로 적용
            → 매칭 행 1개만 살아남고 자식 잘림
   - 정정: anchor 단계에서 매칭된 PART 가 들어있는 bom_report_objid 전체를 startWhere 로
            → 재귀 CTE 가 자식 모두 풀어냄 (운영판 1:1)
   - search_level (1~5) 은 결과 단계 유지 (트리 깊이 제한)
   - ascending / ascendingForExcel 양쪽 동일 패턴

(5) ascending SELECT 풀 컬럼 보강:
   - 추가 : item_qty(p_qty), heat_treatment_hardness/method, surface_treatment,
            maker, part_type, part_type_title (comm_code.code_name)
   - TREE CTE 컬럼에 item_qty 추가
   - BomTreeRow 타입 동기 (lib/api/devBom.ts)

(6) 상태변경 시 확정일(DEPLOY_DATE) 처리 — 사용자 요청:
   - status = 'Y' 변경 시 DEPLOY_DATE = TO_CHAR(NOW(), 'YYYY-MM-DD') 채움 (varchar)
   - 'N' 변경 시 기존 DEPLOY_DATE 보존
   - $5 prepared statement 타입 추론 충돌 (varchar vs unknown) → $5::varchar 명시 캐스팅
   - STATUS_TITLE 매핑은 운영판 1:1 — CREATE/CHANGEDESIGN/DEPLOY 만 라벨, Y/N 등은 raw 표시

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:11:08 +09:00

313 lines
9.3 KiB
TypeScript

import { apiClient } from "./client";
// Content-Disposition 의 filename / filename* 파싱 (UTF-8 인코딩 우선)
function extractFileName(cd: string | undefined): string | null {
if (!cd) return null;
const utf8 = /filename\*=UTF-8''([^;]+)/i.exec(cd);
if (utf8 && utf8[1]) {
try { return decodeURIComponent(utf8[1]); } catch { /* fallthrough */ }
}
const plain = /filename="?([^";]+)"?/i.exec(cd);
if (plain && plain[1]) {
try { return decodeURIComponent(plain[1]); } catch { return plain[1]; }
}
return null;
}
// ============================================================
// 개발관리 E-BOM (M3 등록 / M4 조회) — wace partMng.xml 1:1
// 라우트: /api/development/ebom/*, /api/development/ebom-tree/*
// ============================================================
export interface BomReportListFilter {
customer_cd?: string;
project_name?: string;
unit_code?: string;
search_unit_name?: string;
search_writer?: string;
product_cd?: string;
search_part_no?: string;
search_part_name?: string;
search_from_date?: string;
search_to_date?: string;
status?: string;
page?: number;
page_size?: number;
}
export interface BomReportRow {
num: number | string;
objid: string;
customer_objid: string | null;
customer_name: string | null;
contract_objid: string | null;
customer_project_name: string | null;
project_no: string | null;
unit_code: string | null;
unit_name: string | null;
status: string | null;
status_title: string | null;
writer: string | null;
dept_name: string | null;
user_name: string | null;
dept_user_name: string | null;
regdate: string | null;
reg_date: string | null;
deploy_date: string | null;
revision: string | null;
eo_no: string | null;
eo_date: string | null;
note: string | null;
multi_yn: string | null;
multi_master_yn: string | null;
multi_break_yn: string | null;
multi_master_objid: string | null;
bom_cnt: number | string | null;
product_cd: string | null;
product_name: string | null;
part_no: string | null;
part_name: string | null;
}
export interface BomReportListResponse {
rows: BomReportRow[];
total: number;
page: number;
pageSize: number;
}
export interface BomReportStatusBody {
product_cd?: string;
part_no?: string;
part_name?: string;
version?: string;
status: string;
}
export interface BomTreeFilter {
bom_report_objid?: string;
project_name?: string;
unit_code?: string;
search_part_no?: string;
search_part_name?: string;
search_level?: string | number; // wace 1:1 — 1~5 표시 레벨
}
export interface BomTreeRow {
bom_report_objid: string | null;
objid: string;
parent_objid: string | null;
child_objid: string | null;
part_no: string | null; // bom_part_qty.part_no (= part_mng.objid)
qty: string | null;
seq: number | string | null;
status: string | null;
lev: number;
path: string[] | null;
// part_mng JOIN
pm_part_no: string | null;
pm_part_name: string | null;
spec: string | null;
material: string | null;
weight: string | null;
remark: string | null;
edit_date: string | null;
eo_no: string | null;
revision: string | null;
cu01_cnt: number | string | null;
cu02_cnt: number | string | null;
cu03_cnt: number | string | null;
max_level: number | string | null;
// 풀 컬럼 (운영판 1:1)
p_qty: string | number | null;
heat_treatment_hardness: string | null;
heat_treatment_method: string | null;
surface_treatment: string | null;
maker: string | null;
part_type: string | null;
part_type_title: string | null;
}
export interface BomTreeResponse {
rows: BomTreeRow[];
max_level: number;
}
// 트리 풀 컬럼 (ascendingForExcel 1:1) — BomReportTreeDialog 용
export interface BomTreeFullRow {
lev: number | string;
pm_part_no: string | null;
pm_part_name: string | null;
qty: string | number | null;
p_qty: string | number | null;
material: string | null;
remark: string | null;
heat_treatment_hardness: string | null;
heat_treatment_method: string | null;
surface_treatment: string | null;
maker: string | null;
part_type: string | null;
part_type_title: string | null;
cu01_cnt: number | string | null;
cu02_cnt: number | string | null;
cu03_cnt: number | string | null;
}
export interface BomTreeFullResponse {
rows: BomTreeFullRow[];
max_level: number;
}
// ─── API ─────────────────────────────────────────────────
export const devBomApi = {
// M3 그리드
async list(filter: BomReportListFilter = {}): Promise<BomReportListResponse> {
const res = await apiClient.get("/development/ebom/list", { params: filter });
return res.data?.data as BomReportListResponse;
},
async detail(objid: string): Promise<BomReportRow | null> {
const res = await apiClient.get(`/development/ebom/${objid}`);
return res.data?.data ?? null;
},
async updateStatus(objid: string, body: BomReportStatusBody) {
return (await apiClient.put(`/development/ebom/${objid}/status`, body)).data;
},
async remove(objids: string[]) {
const res = await apiClient.delete("/development/ebom", { data: { objids } });
return res.data;
},
// M4
async ascending(filter: BomTreeFilter): Promise<BomTreeResponse> {
const res = await apiClient.get("/development/ebom-tree/ascending", { params: filter });
return res.data?.data as BomTreeResponse;
},
async descending(filter: BomTreeFilter): Promise<BomTreeResponse> {
const res = await apiClient.get("/development/ebom-tree/descending", { params: filter });
return res.data?.data as BomTreeResponse;
},
// E-BOM 트리 (풀 컬럼) — M3 그리드 행 클릭 → BomReportTreeDialog
async treeFull(filter: BomTreeFilter): Promise<BomTreeFullResponse> {
const res = await apiClient.get("/development/ebom-tree/full", { params: filter });
return res.data?.data as BomTreeFullResponse;
},
// M4 엑셀 다운로드 (정/역전개) — wace 1:1
async excelAscending(filter: BomTreeFilter): Promise<{ blob: Blob; fileName: string }> {
const res = await apiClient.get("/development/ebom-tree/ascending/excel", {
params: filter, responseType: "blob",
});
return { blob: res.data as Blob, fileName: extractFileName(res.headers?.["content-disposition"]) ?? "BOM_ascending.xlsx" };
},
async excelDescending(filter: BomTreeFilter): Promise<{ blob: Blob; fileName: string }> {
const res = await apiClient.get("/development/ebom-tree/descending/excel", {
params: filter, responseType: "blob",
});
return { blob: res.data as Blob, fileName: extractFileName(res.headers?.["content-disposition"]) ?? "BOM_descending.xlsx" };
},
// Excel Import
async excelParse(file: File): Promise<BomExcelParseResponse> {
const fd = new FormData();
fd.append("file", file);
const res = await apiClient.post("/development/ebom/excel-parse", fd, {
headers: { "Content-Type": "multipart/form-data" },
});
return res.data?.data as BomExcelParseResponse;
},
async excelCheckDuplicate(partNo: string, exclude?: string): Promise<boolean> {
const res = await apiClient.get("/development/ebom/excel-check-duplicate", {
params: { partNo, exclude },
});
return !!res.data?.data?.isDuplicate;
},
async excelCopySource(productCd?: string): Promise<BomCopySourceRow[]> {
const res = await apiClient.get("/development/ebom/excel-copy-source", {
params: productCd ? { productCd } : undefined,
});
return (res.data?.data as BomCopySourceRow[]) ?? [];
},
async excelCopy(objid: string): Promise<BomCsvRow[]> {
const res = await apiClient.get(`/development/ebom/excel-copy/${objid}`);
return ((res.data?.data?.rows as BomCsvRow[]) ?? []);
},
async excelSave(input: BomExcelSaveInput): Promise<BomExcelSaveResult> {
const res = await apiClient.post("/development/ebom/excel-save", input);
return res.data?.data as BomExcelSaveResult;
},
};
// ─── CSV Import 타입 (wace parsingCsvFile 1:1) ─────────────
export interface BomCsvRow {
NOTE: string;
LEVEL: string;
PARENT_PART_NO: string;
PART_NO: string;
PART_NAME: string;
QTY: string;
ITEM_QTY: string;
MATERIAL: string;
HEAT_TREATMENT_HARDNESS: string;
HEAT_TREATMENT_METHOD: string;
SURFACE_TREATMENT: string;
MAKER: string;
PART_TYPE: string;
PART_TYPE_NAME: string;
ACCTFG: string;
ODRFG: string;
UNIT_DC: string;
UNITMANG_DC: string;
UNITCHNG_NB: string;
LOT_FG: string;
USE_YN: string;
QC_FG: string;
SETITEM_FG: string;
REQ_FG: string;
}
// 기존 코드 호환용 별칭 (필요 시 마이그레이션)
export type BomExcelRow = BomCsvRow;
export interface BomExcelParseResponse {
rows: BomCsvRow[];
hasError: boolean;
firstLevel: { part_no: string; part_name: string } | null;
encoding: string;
}
export interface BomCopySourceRow {
objid: string;
part_no: string;
part_name: string;
revision: string | null;
product_cd: string | null;
regdate: string | null;
}
export interface BomExcelSaveInput {
bomReportObjid?: string;
productCd: string;
partNo: string;
partName: string;
version?: string;
rows: BomCsvRow[];
}
export interface BomExcelSaveResult {
bomReportObjid: string;
insertedParts: number;
updatedParts: number;
bomRows: number;
mode: "create" | "update";
}