0c791d21d6
운영판 wace 재확인 결과 BOM 등록은 XLSX가 아니라 CSV 가 진짜 입력 포맷이었음.
근거:
· openBomReportExcelImportPopUp.jsp : 제목 "PART 및 구조등록 CSV upload",
Drop Zone "Drag & Drop CSV 템플릿", fnc_setFileDropZone(..., "csv") 로 CSV 만 허용
· /partMng/parsingExcelFile.do 가 .csv 분기에서 별도 함수 parsingCsvFile() 호출
· CSV 11컬럼 : 수준 / 품번 / 품명 / 수량 / 항목수량 / 재료 / 열처리경도 / 열처리방법 /
표면처리 / 공급업체(MAKER) / 범주이름(PART_TYPE)
· CSV 는 PARENT_PART_NO 컬럼이 없고 "수준" 으로 부모-자식 자동 결정
· 숫자("1","2","3"): 깊이 D 의 부모 = D-1 의 최신 품번
· 점 표기법("1","1.1","1.4.1"): 마지막 점 이전 수준이 부모
backend (devBomExcelImportService.ts):
· XLSX(xlsx 라이브러리) 파싱 → CSV(iconv-lite) 파싱으로 완전 재작성
· 인코딩 자동 감지 1:1 — CP949 → UTF-8 → EUC-KR → MS949 순서, � 카운트로 베스트 선택,
UTF-8 BOM() 자동 제거
· CSV 영문 범주명 자동 변환 — Assy/ASSY→조립품, Buy/BUY→구매품, Make/MAKE→부품
· 범주별 ACCTFG/ODRFG 자동 설정 — 조립품(0001813)·부품(0001812) → ACCTFG=4·ODRFG=1,
구매품(0000063) → ACCTFG=7·ODRFG=0
· 기본값 일괄 — UNIT_DC=0001400(EA) · UNITMANG_DC=0001400 · UNITCHNG_NB=1 ·
LOT_FG=1 · USE_YN=1 · QC_FG=0 · SETITEM_FG=0 · REQ_FG=0
· 검증 1:1 — rowIndex > 2 에서만 모품번 자품번 목록 존재 검사, NOTE 누적
· 저장 로직(savePartBomMaster)은 동일 유지 — 자식 PART updatePartInfoFromCsv UPDATE /
insertpartInfo INSERT, 부모 PART 존재 시 lookup·없으면 "" (INSERT 안 함),
bom_part_qty INSERT 시 ACCTFG/ODRFG/UNIT_DC/UNITCHNG_NB 등 모든 컬럼 동기
frontend:
· BomExcelRow → BomCsvRow (LEVEL 컬럼 + ACCTFG/ODRFG/UNIT_DC/UNITCHNG_NB/LOT_FG/USE_YN/
QC_FG/SETITEM_FG/REQ_FG 자동 채움 필드 추가). BomExcelRow 는 호환 type alias 로 보존
· BomReportExcelImportDialog : 제목 "PART 및 구조등록 CSV upload", accept=".csv", Drop Zone
안내 텍스트 변경, 그리드 컬럼 — 결과/수준/모품번(자동)/품번/품명/수량/항목수량/재료/
열처리경도/열처리방법/표면처리/공급업체/범주/계정구분(자동)/조달구분(자동)
· 파싱 결과의 encoding 정보 표시 (CP949/UTF-8 등)
정적 자산:
· 운영 BOM_REPORT_EXCEL_IMPORT_TEMPLATE.xlsx 제거 (운영판도 실제 사용 안 함)
· 신규 BOM_REPORT_CSV_IMPORT_TEMPLATE.csv 추가 (11컬럼 헤더 + 4행 샘플)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
246 lines
6.7 KiB
TypeScript
246 lines
6.7 KiB
TypeScript
import { apiClient } from "./client";
|
|
|
|
// ============================================================
|
|
// 개발관리 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
export interface BomTreeResponse {
|
|
rows: BomTreeRow[];
|
|
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;
|
|
},
|
|
|
|
// 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";
|
|
}
|