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; } 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 { const res = await apiClient.get("/development/ebom/list", { params: filter }); return res.data?.data as BomReportListResponse; }, async detail(objid: string): Promise { 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 { const res = await apiClient.get("/development/ebom-tree/ascending", { params: filter }); return res.data?.data as BomTreeResponse; }, async descending(filter: BomTreeFilter): Promise { 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 { 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 { 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 { const res = await apiClient.get("/development/ebom/excel-check-duplicate", { params: { partNo, exclude }, }); return !!res.data?.data?.isDuplicate; }, async excelCopySource(productCd?: string): Promise { 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 { const res = await apiClient.get(`/development/ebom/excel-copy/${objid}`); return ((res.data?.data?.rows as BomCsvRow[]) ?? []); }, async excelSave(input: BomExcelSaveInput): Promise { 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"; }