diff options
Diffstat (limited to 'lib/sedp')
| -rw-r--r-- | lib/sedp/get-form-tags-plant.ts | 933 | ||||
| -rw-r--r-- | lib/sedp/get-tags-plant.ts | 639 | ||||
| -rw-r--r-- | lib/sedp/sync-form.ts | 9 |
3 files changed, 1580 insertions, 1 deletions
diff --git a/lib/sedp/get-form-tags-plant.ts b/lib/sedp/get-form-tags-plant.ts new file mode 100644 index 00000000..176f1b3f --- /dev/null +++ b/lib/sedp/get-form-tags-plant.ts @@ -0,0 +1,933 @@ +import db from "@/db/db"; +import { + contractItems, + tagsPlant, + formsPlant,formEntriesPlant, + items, + tagTypeClassFormMappings, + projects, + tagTypes, + tagClasses, + formMetas, +} from "@/db/schema"; +import { eq, and, like, inArray } from "drizzle-orm"; +import { getSEDPToken } from "./sedp-token"; +import { getFormMappingsByTagTypebyProeject } from "../tags/form-mapping-service"; + + +interface Attribute { + ATT_ID: string; + VALUE: any; + VALUE_DBL: number; + UOM_ID: string | null; +} + +interface TagEntry { + TAG_IDX: string; + TAG_NO: string; + BF_TAG_NO: string; + TAG_DESC: string; + EP_ID: string; + TAG_TYPE_ID: string; + CLS_ID: string; + ATTRIBUTES: Attribute[]; + [key: string]: any; +} + +interface Column { + key: string; + label: string; + type: string; + shi?: string | null; +} + +interface newRegister { + PROJ_NO: string; + MAP_ID: string; + EP_ID: string; + CATEGORY: string; + BYPASS: boolean; + REG_TYPE_ID: string; + TOOL_ID: string; + TOOL_TYPE: string; + SCOPES: string[]; + MAP_CLS: { + TOOL_ATT_NAME: string; + ITEMS: ClassItmes[]; + }; + MAP_ATT: MapAttribute[]; + MAP_TMPLS: string[]; + CRTER_NO: string; + CRTE_DTM: string; + CHGER_NO: string; + _id: string; +} + +interface ClassItmes { + SEDP_OBJ_CLS_ID: string; + TOOL_VALS: string; + ISDEFALUT: boolean; +} + +interface MapAttribute { + SEDP_ATT_ID: string; + TOOL_ATT_NAME: string; + KEY_YN: boolean; + DUE_DATE: string; //"YYYY-MM-DDTHH:mm:ssZ" + INOUT: string | null; +} + + + +async function getNewRegisters(projectCode: string): Promise<newRegister[]> { + try { + // 토큰(API 키) 가져오기 + const apiKey = await getSEDPToken(); + + const SEDP_API_BASE_URL = process.env.SEDP_API_BASE_URL || 'http://sedpwebapi.ship.samsung.co.kr/api'; + + const response = await fetch( + `${SEDP_API_BASE_URL}/AdapterDataMapping/GetByToolID`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'accept': '*/*', + 'ApiKey': apiKey, + 'ProjectNo': projectCode + }, + body: JSON.stringify({ + ProjectNo: projectCode, + "TOOL_ID": "eVCP" + }) + } + ); + + if (!response.ok) { + throw new Error(`새 레지스터 요청 실패: ${response.status} ${response.statusText}`); + } + + // 안전하게 JSON 파싱 + let data; + try { + data = await response.json(); + } catch (parseError) { + console.error(`프로젝트 ${projectCode}의 새 레지스터 응답 파싱 실패:`, parseError); + // 응답 내용 로깅 + const text = await response.clone().text(); + console.log(`응답 내용: ${text.substring(0, 200)}${text.length > 200 ? '...' : ''}`); + throw new Error(`새 레지스터 응답 파싱 실패: ${parseError instanceof Error ? parseError.message : String(parseError)}`); + } + + // 결과를 배열로 변환 (단일 객체인 경우 배열로 래핑) + let registers: newRegister[] = Array.isArray(data) ? data : [data]; + + console.log(`프로젝트 ${projectCode}에서 ${registers.length}개의 새 레지스터를 가져왔습니다.`); + return registers; + } catch (error) { + console.error(`프로젝트 ${projectCode}의 새 레지스터 가져오기 실패:`, error); + throw error; + } +} + + +/** + * 태그 가져오기 서비스 함수 + * formEntries와 tags 테이블 모두에 데이터를 저장 + */ +export async function importTagsFromSEDP( + formCode: string, + projectCode: string, + packageCode: string, + progressCallback?: (progress: number) => void +): Promise<{ + processedCount: number; + excludedCount: number; + totalEntries: number; + formCreated?: boolean; + errors?: string[]; +}> { + try { + // 진행 상황 보고 + if (progressCallback) progressCallback(5); + + // 에러 수집 배열 + const errors: string[] = []; + + // SEDP API에서 태그 데이터 가져오기 + const tagData = await fetchTagDataFromSEDP(projectCode, formCode); + const newRegisters = await getNewRegisters(projectCode); + + const registerMatched = newRegisters.find(v => v.REG_TYPE_ID === formCode).MAP_ATT + + + // 트랜잭션으로 모든 DB 작업 처리 + return await db.transaction(async (tx) => { + // 프로젝트 정보 가져오기 (type 포함) + const projectRecord = await tx.select({ id: projects.id, type: projects.type }) + .from(projects) + .where(eq(projects.code, projectCode)) + .limit(1); + + if (!projectRecord || projectRecord.length === 0) { + throw new Error(`Project not found for code: ${projectCode}`); + } + + const projectId = projectRecord[0].id; + const projectType = projectRecord[0].type; + + // 프로젝트 타입에 따라 packageCode를 찾을 ATT_ID 결정 + const packageCodeAttId = projectType === "ship" ? "CM3003" : "ME5074"; + + + + + const targetPackageCode = packageCode; + + // 데이터 형식 처리 - tagData의 첫 번째 키 사용 + const tableName = Object.keys(tagData)[0]; + + if (!tableName || !tagData[tableName]) { + throw new Error("Invalid tag data format from SEDP API"); + } + + const allTagEntries: TagEntry[] = tagData[tableName]; + + if (!Array.isArray(allTagEntries) || allTagEntries.length === 0) { + return { + processedCount: 0, + excludedCount: 0, + totalEntries: 0, + errors: ["No tag entries found in API response"] + }; + } + + // packageCode로 필터링 - ATTRIBUTES에서 지정된 ATT_ID의 VALUE와 packageCode 비교 + const tagEntries = allTagEntries.filter(entry => { + if (Array.isArray(entry.ATTRIBUTES)) { + const packageCodeAttr = entry.ATTRIBUTES.find(attr => attr.ATT_ID === packageCodeAttId); + if (packageCodeAttr && packageCodeAttr.VALUE === targetPackageCode) { + return true; + } + } + return false; + }); + + if (tagEntries.length === 0) { + return { + processedCount: 0, + excludedCount: 0, + totalEntries: allTagEntries.length, + errors: [`No tag entries found with ${packageCodeAttId} attribute value matching packageCode: ${targetPackageCode}`] + }; + } + + // 진행 상황 보고 + if (progressCallback) progressCallback(20); + + // 나머지 코드는 기존과 동일... + // form ID 가져오기 - 없으면 생성 + let formRecord = await tx.select({ id: formsPlant.id }) + .from(formsPlant) + .where(and( + eq(formsPlant.formCode, formCode), + eq(formsPlant.projectCode, projectCode), + eq(formsPlant.packageCode, packageCode) + )) + .limit(1); + + let formCreated = false; + + // form이 없으면 생성 + if (!formRecord || formRecord.length === 0) { + console.log(`[IMPORT TAGS] Form ${formCode} not found, attempting to create...`); + + // 첫 번째 태그의 정보를 사용해서 form mapping을 찾습니다 + // 모든 태그가 같은 formCode를 사용한다고 가정 + if (tagEntries.length > 0) { + const firstTag = tagEntries[0]; + + // tagType 조회 (TAG_TYPE_ID -> description) + let tagTypeDescription = firstTag.TAG_TYPE_ID; // 기본값 + if (firstTag.TAG_TYPE_ID) { + const tagTypeRecord = await tx.select({ description: tagTypes.description }) + .from(tagTypes) + .where(and( + eq(tagTypes.code, firstTag.TAG_TYPE_ID), + eq(tagTypes.projectId, projectId) + )) + .limit(1); + + if (tagTypeRecord && tagTypeRecord.length > 0) { + tagTypeDescription = tagTypeRecord[0].description; + } + } + + // tagClass 조회 (CLS_ID -> label) + let tagClassLabel = firstTag.CLS_ID; // 기본값 + if (firstTag.CLS_ID) { + const tagClassRecord = await tx.select({ id: tagClasses.id, label: tagClasses.label }) + .from(tagClasses) + .where(and( + eq(tagClasses.code, firstTag.CLS_ID), + eq(tagClasses.projectId, projectId) + )) + .limit(1); + + if (tagClassRecord && tagClassRecord.length > 0) { + tagClassLabel = tagClassRecord[0].label; + } + } + + // 태그 타입에 따른 폼 정보 가져오기 + const allFormMappings = await getFormMappingsByTagTypebyProeject( + projectId, + ); + + // 현재 formCode와 일치하는 매핑 찾기 + const targetFormMapping = allFormMappings.find(mapping => mapping.formCode === formCode); + + if (targetFormMapping) { + console.log(`[IMPORT TAGS] Found form mapping for ${formCode}, creating form...`); + + // form 생성 + const insertResult = await tx + .insert(formsPlant) + .values({ + projectCode, + packageCode, + formCode: targetFormMapping.formCode, + formName: targetFormMapping.formName, + eng: true, // ENG 모드에서 가져오는 것이므로 eng: true + im: targetFormMapping.ep === "IMEP" ? true : false + }) + .returning({ id: formsPlant.id }); + + formRecord = insertResult; + formCreated = true; + + console.log(`[IMPORT TAGS] Successfully created form:`, insertResult[0]); + } else { + console.log(`[IMPORT TAGS] No form mapping found for formCode: ${formCode}`); + console.log(`[IMPORT TAGS] Available mappings:`, allFormMappings.map(m => m.formCode)); + throw new Error(`Form ${formCode} not found and no mapping available for tag type ${tagTypeDescription}`); + } + } else { + throw new Error(`Form not found for formCode: ${formCode} and, and no tags to derive form mapping`); + } + } else { + console.log(`[IMPORT TAGS] Found existing form:`, formRecord[0].id); + + // 기존 form이 있는 경우 eng와 im 필드를 체크하고 업데이트 + const existingForm = await tx.select({ + eng: formsPlant.eng, + im: formsPlant.im + }) + .from(formsPlant) + .where(eq(formsPlant.id, formRecord[0].id)) + .limit(1); + + if (existingForm.length > 0) { + // form mapping 정보 가져오기 (im 필드 업데이트를 위해) + let shouldUpdateIm = false; + let targetImValue = false; + + // 첫 번째 태그의 정보를 사용해서 form mapping을 확인 + if (tagEntries.length > 0) { + const firstTag = tagEntries[0]; + + // tagType 조회 + let tagTypeDescription = firstTag.TAG_TYPE_ID; + if (firstTag.TAG_TYPE_ID) { + const tagTypeRecord = await tx.select({ description: tagTypes.description }) + .from(tagTypes) + .where(and( + eq(tagTypes.code, firstTag.TAG_TYPE_ID), + eq(tagTypes.projectId, projectId) + )) + .limit(1); + + if (tagTypeRecord && tagTypeRecord.length > 0) { + tagTypeDescription = tagTypeRecord[0].description; + } + } + + // tagClass 조회 + let tagClassLabel = firstTag.CLS_ID; + if (firstTag.CLS_ID) { + const tagClassRecord = await tx.select({ id: tagClasses.id, label: tagClasses.label }) + .from(tagClasses) + .where(and( + eq(tagClasses.code, firstTag.CLS_ID), + eq(tagClasses.projectId, projectId) + )) + .limit(1); + + if (tagClassRecord && tagClassRecord.length > 0) { + tagClassLabel = tagClassRecord[0].label; + } + } + + // form mapping 정보 가져오기 + const allFormMappings = await getFormMappingsByTagTypebyProeject( + projectId, + ); + + // 현재 formCode와 일치하는 매핑 찾기 + const targetFormMapping = allFormMappings.find(mapping => mapping.formCode === formCode); + + if (targetFormMapping) { + targetImValue = targetFormMapping.ep === "IMEP"; + shouldUpdateIm = existingForm[0].im !== targetImValue; + } + } + + // 업데이트할 필드들 준비 + const updates: any = {}; + let hasUpdates = false; + + // eng 필드 체크 + if (existingForm[0].eng !== true) { + updates.eng = true; + hasUpdates = true; + } + + // im 필드 체크 + if (shouldUpdateIm) { + updates.im = targetImValue; + hasUpdates = true; + } + + // 업데이트 실행 + if (hasUpdates) { + await tx + .update(formsPlant) + .set(updates) + .where(eq(formsPlant.id, formRecord[0].id)); + + console.log(`[IMPORT TAGS] Form ${formRecord[0].id} updated with:`, updates); + } + } + } + + const formId = formRecord[0].id; + + // 나머지 처리 로직은 기존과 동일... + // (양식 메타데이터 가져오기, 태그 처리 등) + + // 양식 메타데이터 가져오기 + const formMetaRecord = await tx.select({ columns: formMetas.columns }) + .from(formMetas) + .where(and( + eq(formMetas.projectId, projectId), + eq(formMetas.formCode, formCode) + )) + .limit(1); + + if (!formMetaRecord || formMetaRecord.length === 0) { + throw new Error(`Form metadata not found for formCode: ${formCode} and projectId: ${projectId}`); + } + + // 진행 상황 보고 + if (progressCallback) progressCallback(30); + + // 컬럼 정보 파싱 + const columnsJSON: Column[] = (formMetaRecord[0].columns); + + // 현재 formEntries 데이터 가져오기 + const existingEntries = await tx.select({ id: formEntriesPlant.id, data: formEntriesPlant.data }) + .from(formEntriesPlant) + .where(and( + eq(formEntriesPlant.formCode, formCode), + eq(formEntriesPlant.projectCode, projectCode), + eq(formEntriesPlant.packageCode, packageCode) + )); + + // 기존 tags 데이터 가져오기 + const existingTags = await tx.select() + .from(tagsPlant) + .where(and( + eq(tagsPlant.projectCode, projectCode), + eq(tagsPlant.packageCode, packageCode), + ) + ); + + // 진행 상황 보고 + if (progressCallback) progressCallback(50); + + // 기존 데이터를 맵으로 변환 + const existingTagMap = new Map(); + const existingTagsMap = new Map(); + + existingEntries.forEach(entry => { + const data = entry.data as any[]; + data.forEach(item => { + if (item.TAG_IDX) { + existingTagMap.set(item.TAG_IDX, { + entryId: entry.id, + data: item + }); + } + }); + }); + + existingTags.forEach(tag => { + existingTagsMap.set(tag.tagIdx, tag); + }); + + // 진행 상황 보고 + if (progressCallback) progressCallback(60); + + // 처리 결과 카운터 + let processedCount = 0; + let excludedCount = 0; + + // 새로운 태그 데이터와 업데이트할 데이터 준비 + const newTagData: any[] = []; + const upsertTagRecords: any[] = []; // 새로 추가되거나 업데이트될 태그들 + const updateData: { entryId: number, tagNo: string, updates: any }[] = []; + + // SEDP 태그 데이터 처리 + for (const tagEntry of tagEntries) { + try { + if (!tagEntry.TAG_IDX) { + excludedCount++; + errors.push(`Missing TAG_NO in tag entry`); + continue; + } + + // tagType 조회 (TAG_TYPE_ID -> description) + let tagTypeDescription = tagEntry.TAG_TYPE_ID; // 기본값 + if (tagEntry.TAG_TYPE_ID) { + const tagTypeRecord = await tx.select({ description: tagTypes.description }) + .from(tagTypes) + .where(and( + eq(tagTypes.code, tagEntry.TAG_TYPE_ID), + eq(tagTypes.projectId, projectId) + )) + .limit(1); + + if (tagTypeRecord && tagTypeRecord.length > 0) { + tagTypeDescription = tagTypeRecord[0].description; + } + } + + // tagClass 조회 (CLS_ID -> label) + let tagClassLabel = tagEntry.CLS_ID; // 기본값 + let tagClassId = null; // 기본값 + if (tagEntry.CLS_ID) { + const tagClassRecord = await tx.select({ id: tagClasses.id, label: tagClasses.label }) + .from(tagClasses) + .where(and( + eq(tagClasses.code, tagEntry.CLS_ID), + eq(tagClasses.projectId, projectId) + )) + .limit(1); + + if (tagClassRecord && tagClassRecord.length > 0) { + tagClassLabel = tagClassRecord[0].label; + tagClassId = tagClassRecord[0].id; + } + } + + const packageCode = projectType === "ship" ? tagEntry.ATTRIBUTES.find(v => v.ATT_ID === "CM3003")?.VALUE : tagEntry.ATTRIBUTES.find(v => v.ATT_ID === "ME5074")?.VALUE + + // 기본 태그 데이터 객체 생성 (formEntries용) + const tagObject: any = { + TAG_IDX: tagEntry.TAG_IDX, // SEDP 고유 식별자 + TAG_NO: tagEntry.TAG_NO, + TAG_DESC: tagEntry.TAG_DESC || "", + CLS_ID: tagEntry.CLS_ID || "", + VNDRCD: vendorRecord[0].vendorCode, + VNDRNM_1: vendorRecord[0].vendorName, + status: "From S-EDP", // SEDP에서 가져온 데이터임을 표시 + source: "S-EDP", // 태그 출처 (불변) - S-EDP에서 가져옴 + ...(projectType === "ship" ? { CM3003: packageCode } : { ME5074: packageCode }) + } + + let latestDueDate: Date | null = null; + + // tags 테이블용 데이터 (UPSERT용) + const tagRecord = { + projectCode, + packageCode, + formId: formId, + tagIdx: tagEntry.TAG_IDX, // SEDP 고유 식별자 + tagNo: tagEntry.TAG_NO, + tagType: tagTypeDescription || "", + class: tagClassLabel, + tagClassId: tagClassId, + description: tagEntry.TAG_DESC || null, + createdAt: new Date(), + updatedAt: new Date() + }; + + // ATTRIBUTES 필드에서 shi=true인 컬럼의 값 추출 + if (Array.isArray(tagEntry.ATTRIBUTES)) { + for (const attr of tagEntry.ATTRIBUTES) { + const columnInfo = columnsJSON.find(col => col.key === attr.ATT_ID); + if (columnInfo && (columnInfo.shi === "BOTH" || columnInfo.shi === "OUT" || columnInfo.shi === null)) { + if (columnInfo.type === "NUMBER") { + if (attr.VALUE !== undefined && attr.VALUE !== null) { + if (typeof attr.VALUE === 'string') { + const numberMatch = attr.VALUE.match(/(-?\d+(\.\d+)?)/); + if (numberMatch) { + tagObject[attr.ATT_ID] = parseFloat(numberMatch[0]); + } else { + const parsed = parseFloat(attr.VALUE); + if (!isNaN(parsed)) { + tagObject[attr.ATT_ID] = parsed; + } + } + } else if (typeof attr.VALUE === 'number') { + tagObject[attr.ATT_ID] = attr.VALUE; + } + } + } else if (attr.VALUE !== null && attr.VALUE !== undefined) { + tagObject[attr.ATT_ID] = attr.VALUE; + } + } + + // registerMatched에서 해당 SEDP_ATT_ID의 DUE_DATE 찾기 + if (registerMatched && Array.isArray(registerMatched)) { + const matchedAttribute = registerMatched.find( + regAttr => regAttr.SEDP_ATT_ID === attr.ATT_ID + ); + + if (matchedAttribute && matchedAttribute.DUE_DATE) { + try { + const dueDate = new Date(matchedAttribute.DUE_DATE); + + // 유효한 날짜인지 확인 + if (!isNaN(dueDate.getTime())) { + // 첫 번째 DUE_DATE이거나 현재까지 찾은 것보다 더 늦은 날짜인 경우 업데이트 + if (!latestDueDate || dueDate > latestDueDate) { + latestDueDate = dueDate; + } + } + } catch (dateError) { + console.warn(`Invalid DUE_DATE format for ${attr.ATT_ID}: ${matchedAttribute.DUE_DATE}`); + } + } + } + + } + } + + if (latestDueDate) { + // ISO 형식의 문자열로 저장 (또는 원하는 형식으로 변경 가능) + tagObject.DUE_DATE = latestDueDate.toISOString(); + + // 또는 YYYY-MM-DD 형식을 원한다면: + // tagObject.DUE_DATE = latestDueDate.toISOString().split('T')[0]; + + // 또는 원본 형식 그대로 유지하려면: + // tagObject.DUE_DATE = latestDueDate.toISOString().replace('Z', ''); + } + + + + // 기존 태그가 있는지 확인하고 처리 + const existingTag = existingTagMap.get(tagEntry.TAG_IDX); + + if (existingTag) { + // 기존 태그가 있으면 formEntries 업데이트 데이터 준비 + const updates: any = {}; + let hasUpdates = false; + + for (const key of Object.keys(tagObject)) { + if (key === "TAG_IDX") continue; + + if (key === "TAG_NO" && tagObject[key] !== existingTag.data[key]) { + updates[key] = tagObject[key]; + hasUpdates = true; + continue; + } + + + if (key === "TAG_DESC" && tagObject[key] !== existingTag.data[key]) { + updates[key] = tagObject[key]; + hasUpdates = true; + continue; + } + + if (key === "status" && tagObject[key] !== existingTag.data[key]) { + updates[key] = tagObject[key]; + hasUpdates = true; + continue; + } + if (key === "CLS_ID" && tagObject[key] !== existingTag.data[key]) { + updates[key] = tagObject[key]; + hasUpdates = true; + continue; + } + + if (key === "DUE_DATE" && tagObject[key] !== existingTag.data[key]) { + updates[key] = tagObject[key]; + hasUpdates = true; + continue; + } + + const columnInfo = columnsJSON.find(col => col.key === key); + if (columnInfo && (columnInfo.shi === "BOTH" || columnInfo.shi === "OUT" || columnInfo.shi === null)) { + if (existingTag.data[key] !== tagObject[key]) { + updates[key] = tagObject[key]; + hasUpdates = true; + } + } + } + + if (hasUpdates) { + updateData.push({ + entryId: existingTag.entryId, + tagIdx: tagEntry.TAG_IDX, // TAG_IDX로 변경 + updates + }); + } + } else { + // 기존 태그가 없으면 새로 추가 + newTagData.push(tagObject); + } + + // tags 테이블에는 항상 upsert (새로 추가되거나 업데이트) + upsertTagRecords.push(tagRecord); + + processedCount++; + } catch (error) { + excludedCount++; + errors.push(`Error processing tag ${tagEntry.TAG_IDX || 'unknown'}: ${error}`); + } + } + + // 진행 상황 보고 + if (progressCallback) progressCallback(80); + + // formEntries 업데이트 실행 + // entryId별로 업데이트를 그룹화 + const updatesByEntryId = new Map(); + + for (const update of updateData) { + if (!updatesByEntryId.has(update.entryId)) { + updatesByEntryId.set(update.entryId, []); + } + updatesByEntryId.get(update.entryId).push(update); + } + + // 그룹화된 업데이트를 처리 + for (const [entryId, updates] of updatesByEntryId) { + try { + const entry = existingEntries.find(e => e.id === entryId); + if (!entry) continue; + + const data = entry.data as any[]; + + // 해당 entryId의 모든 업데이트를 한 번에 적용 + const updatedData = data.map(item => { + let updatedItem = { ...item }; + + // 현재 item에 적용할 모든 업데이트를 찾아서 적용 + for (const update of updates) { + if (item.TAG_IDX === update.tagIdx) { + updatedItem = { ...updatedItem, ...update.updates }; + } + } + + return updatedItem; + }); + + // entryId별로 한 번만 DB 업데이트 + await tx.update(formEntriesPlant) + .set({ + data: updatedData, + updatedAt: new Date() + }) + .where(eq(formEntriesPlant.id, entryId)); + + } catch (error) { + const tagNos = updates.map(u => u.tagNo || u.tagIdx).join(', '); + errors.push(`Error updating formEntry ${entryId} for tags ${tagNos}: ${error}`); + } + } + + // 새 태그 추가 (formEntriesPlant) + if (newTagData.length > 0) { + if (existingEntries.length > 0) { + const firstEntry = existingEntries[0]; + const existingData = firstEntry.data as any[]; + const updatedData = [...existingData, ...newTagData]; + + await tx.update(formEntriesPlant) + .set({ + data: updatedData, + updatedAt: new Date() + }) + .where(eq(formEntriesPlant.id, firstEntry.id)); + } else { + await tx.insert(formEntriesPlant) + .values({ + formCode, + projectCode, + packageCode, + data: newTagData, + createdAt: new Date(), + updatedAt: new Date() + }); + } + } + + // tags 테이블 처리 (INSERT + UPDATE 분리) + if (upsertTagRecords.length > 0) { + const newTagRecords: any[] = []; + const updateTagRecords: { tagId: number, updates: any }[] = []; + + // 각 태그를 확인하여 신규/업데이트 분류 + for (const tagRecord of upsertTagRecords) { + const existingTagRecord = existingTagsMap.get(tagRecord.tagIdx); + + if (existingTagRecord) { + // 기존 태그가 있으면 업데이트 준비 + const tagUpdates: any = {}; + let hasTagUpdates = false; + + // tagNo도 업데이트 가능 (편집된 경우) + if (existingTagRecord.tagNo !== tagRecord.tagNo) { + tagUpdates.tagNo = tagRecord.tagNo; + hasTagUpdates = true; + } + + if (existingTagRecord.tagType !== tagRecord.tagType) { + tagUpdates.tagType = tagRecord.tagType; + hasTagUpdates = true; + } + if (existingTagRecord.class !== tagRecord.class) { + tagUpdates.class = tagRecord.class; + hasTagUpdates = true; + } + if (existingTagRecord.tagClassId !== tagRecord.tagClassId) { + tagUpdates.tagClassId = tagRecord.tagClassId; + hasTagUpdates = true; + } + + if (existingTagRecord.description !== tagRecord.description) { + tagUpdates.description = tagRecord.description; + hasTagUpdates = true; + } + if (existingTagRecord.formId !== tagRecord.formId) { + tagUpdates.formId = tagRecord.formId; + hasTagUpdates = true; + } + + if (hasTagUpdates) { + updateTagRecords.push({ + tagId: existingTagRecord.id, + updates: { ...tagUpdates, updatedAt: new Date() } + }); + } + } else { + // 새로운 태그 + newTagRecords.push(tagRecord); + } + } + + // 새 태그 삽입 + if (newTagRecords.length > 0) { + try { + await tx.insert(tagsPlant) + .values(newTagRecords) + .onConflictDoNothing({ + target: [tagsPlant.projectCode,tagsPlant.packageCode, tagsPlant.tagIdx] + }); + } catch (error) { + // 개별 삽입으로 재시도 + for (const tagRecord of newTagRecords) { + try { + await tx.insert(tagsPlant) + .values(tagRecord) + .onConflictDoNothing({ + target: [tagsPlant.projectCode,tagsPlant.packageCode, tagsPlant.tagIdx] + }); + } catch (individualError) { + errors.push(`Error inserting tag ${tagRecord.tagIdx}: ${individualError}`); + } + } + } + } + + // 기존 태그 업데이트 + for (const update of updateTagRecords) { + try { + await tx.update(tagsPlant) + .set(update.updates) + .where(eq(tagsPlant.id, update.tagId)); + } catch (error) { + errors.push(`Error updating tag record ${update.tagId}: ${error}`); + } + } + } + + // 진행 상황 보고 + if (progressCallback) progressCallback(100); + + // 최종 결과 반환 + return { + processedCount, + excludedCount, + totalEntries: tagEntries.length, + formCreated, + errors: errors.length > 0 ? errors : undefined + }; + }); + + } catch (error: any) { + console.error("Tag import error:", error); + throw error; + } +} +/** + * SEDP API에서 태그 데이터 가져오기 + * + * @param projectCode 프로젝트 코드 + * @param formCode 양식 코드 + * @returns API 응답 데이터 + */ +async function fetchTagDataFromSEDP(projectCode: string, formCode: string): Promise<any> { + try { + // Get the token + const apiKey = await getSEDPToken(); + + // Define the API base URL + const SEDP_API_BASE_URL = process.env.SEDP_API_BASE_URL || 'http://sedpwebapi.ship.samsung.co.kr/api'; + + // Make the API call + const response = await fetch( + `${SEDP_API_BASE_URL}/Data/GetPubData`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'accept': '*/*', + 'ApiKey': apiKey, + 'ProjectNo': projectCode + }, + body: JSON.stringify({ + ProjectNo: projectCode, + REG_TYPE_ID: formCode, + // TODO: 이창국 프로 요청으로, ContainDeleted: true로 변경예정, EDP에서 삭제된 데이터도 가져올 수 있어야 한다고 함. + // 삭제된 게 들어오면 eVCP내에서 지우거나, 비활성화 하는 등의 처리를 해야 할 걸로 보임 + ContainDeleted: false + }) + } + ); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`SEDP API request failed: ${response.status} ${response.statusText} - ${errorText}`); + } + + const data = await response.json(); + return data; + } catch (error: any) { + console.error('Error calling SEDP API:', error); + throw new Error(`Failed to fetch data from SEDP API: ${error.message || 'Unknown error'}`); + } +}
\ No newline at end of file diff --git a/lib/sedp/get-tags-plant.ts b/lib/sedp/get-tags-plant.ts new file mode 100644 index 00000000..d1957db4 --- /dev/null +++ b/lib/sedp/get-tags-plant.ts @@ -0,0 +1,639 @@ +import db from "@/db/db"; +import { + tagsPlant, + formsPlant, + formEntriesPlant, + items, + tagTypeClassFormMappings, + projects, + tagTypes, + tagClasses, +} from "@/db/schema"; +import { eq, and, like, inArray } from "drizzle-orm"; +import { revalidateTag } from "next/cache"; // 추가 +import { getSEDPToken } from "./sedp-token"; + +/** + * 태그 가져오기 서비스 함수 + * contractItemId(packageId)를 기반으로 외부 시스템에서 태그 데이터를 가져와 DB에 저장 + * TAG_IDX를 기준으로 태그를 식별합니다. + * + * @param projectCode 계약 아이템 ID (contractItemId) + * @param packageCode 계약 아이템 ID (contractItemId) + * @param progressCallback 진행 상황을 보고하기 위한 콜백 함수 + * @returns 처리 결과 정보 (처리된 태그 수, 오류 목록 등) + */ +export async function importTagsFromSEDP( + projectCode: string, + packageCode: string, + progressCallback?: (progress: number) => void, + mode?: string +): Promise<{ + processedCount: number; + excludedCount: number; + totalEntries: number; + errors?: string[]; +}> { + try { + // 진행 상황 보고 + if (progressCallback) progressCallback(5); + + const project = await db.query.projects.findFirst({ + where: eq(projects.code, projectCode), + columns: { + id: true + } + }); + + + // 프로젝트 ID 획득 + const projectId = project?.id; + + // Step 1-2: Get the item using itemId from contractItem + const item = await db.query.items.findFirst({ + where: and(eq(items.ProjectNo, projectCode), eq(items.packageCode, packageCode)) + }); + + if (!item) { + throw new Error(`Item with ID ${item?.id} not found`); + } + + const itemCode = item.itemCode; + + // 진행 상황 보고 + if (progressCallback) progressCallback(10); + + // 기본 매핑 검색 - 모든 모드에서 사용 + const baseMappings = await db.query.tagTypeClassFormMappings.findMany({ + where: and( + like(tagTypeClassFormMappings.remark, `%${itemCode}%`), + eq(tagTypeClassFormMappings.projectId, projectId) + ) + }); + + if (baseMappings.length === 0) { + throw new Error(`No mapping found for item code ${itemCode}`); + } + + // Step 2: Find the mapping entries - 모드에 따라 다른 조건 적용 + let mappings = []; + + if (mode === 'IM') { + // IM 모드일 때는 먼저 SEDP에서 태그 데이터를 가져와 TAG_TYPE_ID 리스트 확보 + + // 프로젝트 코드 가져오기 + const project = await db.query.projects.findFirst({ + where: eq(projects.id, projectId) + }); + + if (!project) { + throw new Error(`Project with ID ${projectId} not found`); + } + + // 각 매핑의 formCode에 대해 태그 데이터 조회 + const tagTypeIds = new Set<string>(); + + for (const mapping of baseMappings) { + try { + // SEDP에서 태그 데이터 가져오기 + const tagData = await fetchTagDataFromSEDP(project.code, mapping.formCode); + + // 첫 번째 키를 테이블 이름으로 사용 + const tableName = Object.keys(tagData)[0]; + const tagEntries = tagData[tableName]; + + if (Array.isArray(tagEntries)) { + // 모든 태그에서 TAG_TYPE_ID 수집 + for (const entry of tagEntries) { + if (entry.TAG_TYPE_ID && entry.TAG_TYPE_ID !== "") { + tagTypeIds.add(entry.TAG_TYPE_ID); + } + } + } + } catch (error) { + console.error(`Error fetching tag data for formCode ${mapping.formCode}:`, error); + } + } + + if (tagTypeIds.size === 0) { + throw new Error('No valid TAG_TYPE_ID found in SEDP tag data'); + } + + // 수집된 TAG_TYPE_ID로 tagTypes에서 정보 조회 + const tagTypeInfo = await db.query.tagTypes.findMany({ + where: and( + inArray(tagTypes.code, Array.from(tagTypeIds)), + eq(tagTypes.projectId, projectId) + ) + }); + + if (tagTypeInfo.length === 0) { + throw new Error('No matching tag types found for the collected TAG_TYPE_IDs'); + } + + // 태그 타입 설명 수집 + const tagLabels = tagTypeInfo.map(tt => tt.description); + + // IM 모드에 맞는 매핑 조회 - ep가 "IMEP"인 항목만 + mappings = await db.query.tagTypeClassFormMappings.findMany({ + where: and( + inArray(tagTypeClassFormMappings.tagTypeLabel, tagLabels), + eq(tagTypeClassFormMappings.projectId, projectId), + eq(tagTypeClassFormMappings.ep, "IMEP") + ) + }); + + } else { + // ENG 모드 또는 기본 모드일 때 - 기본 매핑 사용 + mappings = [...baseMappings]; + + // ENG 모드에서는 ep 필드가 "IMEP"가 아닌 매핑만 필터링 + if (mode === 'ENG') { + mappings = mappings.filter(mapping => mapping.ep !== "IMEP"); + } + } + + // 매핑이 없는 경우 모드에 따라 다른 오류 메시지 사용 + if (mappings.length === 0) { + if (mode === 'IM') { + throw new Error('No suitable mappings found for IM mode'); + } else { + throw new Error(`No mapping found for item code ${itemCode}`); + } + } + + // 진행 상황 보고 + if (progressCallback) progressCallback(15); + + // 결과 누적을 위한 변수들 초기화 + let totalProcessedCount = 0; + let totalExcludedCount = 0; + let totalEntriesCount = 0; + const allErrors: string[] = []; + + // 각 매핑에 대해 처리 + for (let mappingIndex = 0; mappingIndex < mappings.length; mappingIndex++) { + const mapping = mappings[mappingIndex]; + + // Step 3: Get the project code + const project = await db.query.projects.findFirst({ + where: eq(projects.id, mapping.projectId) + }); + + if (!project) { + allErrors.push(`Project with ID ${mapping.projectId} not found`); + continue; // 다음 매핑으로 진행 + } + + // IM 모드에서는 baseMappings에서 같은 formCode를 가진 매핑을 찾음 + let formCode = mapping.formCode; + if (mode === 'IM') { + // baseMapping에서 동일한 formCode를 가진 매핑 찾기 + const originalMapping = baseMappings.find( + baseMapping => baseMapping.formCode === mapping.formCode + ); + + // 찾았으면 해당 formCode 사용, 못 찾았으면 현재 매핑의 formCode 유지 + if (originalMapping) { + formCode = originalMapping.formCode; + } + } + + // 진행 상황 보고 - 매핑별 진행률 조정 + if (progressCallback) { + const baseProgress = 15; + const mappingProgress = Math.floor(15 * (mappingIndex + 1) / mappings.length); + progressCallback(baseProgress + mappingProgress); + } + + // Step 4: Find the form ID + const form = await db.query.formsPlant.findFirst({ + where: and( + eq(formsPlant.projectCode, projectCode), + eq(formsPlant.packageCode, packageCode), + eq(formsPlant.formCode, formCode) + ) + }); + + let formId; + + // If form doesn't exist, create it + if (!form) { + // 폼이 없는 경우 새로 생성 - 모드에 따른 필드 설정 + const insertValues: any = { + projectCode, + packageCode, + formCode: formCode, + formName: mapping.formName + }; + + // 모드 정보가 있으면 해당 필드 설정 + if (mode) { + if (mode === "ENG") { + insertValues.eng = true; + } else if (mode === "IM") { + insertValues.im = true; + if (mapping.remark && mapping.remark.includes("VD_")) { + insertValues.eng = true; + } + } + } + + const insertResult = await db.insert(formsPlant).values(insertValues).returning({ id: formsPlant.id }); + + if (insertResult.length === 0) { + allErrors.push(`Failed to create form record for formCode ${formCode}`); + continue; // 다음 매핑으로 진행 + } + + formId = insertResult[0].id; + } else { + // 폼이 이미 존재하는 경우 - 필요시 모드 필드 업데이트 + formId = form.id; + + if (mode) { + let shouldUpdate = false; + const updateValues: any = {}; + + if (mode === "ENG" && form.eng !== true) { + updateValues.eng = true; + shouldUpdate = true; + } else if (mode === "IM" && form.im !== true) { + updateValues.im = true; + shouldUpdate = true; + } + + if (shouldUpdate) { + await db.update(formsPlant) + .set({ + ...updateValues, + updatedAt: new Date() + }) + .where(eq(formsPlant.id, formId)); + + console.log(`Updated form ${formId} with ${mode} mode enabled`); + } + } + } + + // 진행 상황 보고 - 매핑별 진행률 조정 + if (progressCallback) { + const baseProgress = 30; + const mappingProgress = Math.floor(20 * (mappingIndex + 1) / mappings.length); + progressCallback(baseProgress + mappingProgress); + } + + try { + // Step 5: Call the external API to get tag data + const tagData = await fetchTagDataFromSEDP(projectCode, baseMappings[0].formCode); + + // 진행 상황 보고 + if (progressCallback) { + const baseProgress = 50; + const mappingProgress = Math.floor(10 * (mappingIndex + 1) / mappings.length); + progressCallback(baseProgress + mappingProgress); + } + + // Step 6: Process the data and insert into the tags table + let processedCount = 0; + let excludedCount = 0; + + // Get the first key from the response as the table name + const tableName = Object.keys(tagData)[0]; + const tagEntries = tagData[tableName]; + + if (!Array.isArray(tagEntries) || tagEntries.length === 0) { + allErrors.push(`No tag data found in the API response for formCode ${baseMappings[0].formCode}`); + continue; // 다음 매핑으로 진행 + } + + const entriesCount = tagEntries.length; + totalEntriesCount += entriesCount; + + // formEntries를 위한 데이터 수집 + const newTagsForFormEntry: Array<{ + TAG_IDX: string; // 변경: TAG_NO → TAG_IDX + TAG_NO?: string; // TAG_NO도 함께 저장 (편집 가능한 필드) + TAG_DESC: string | null; + status: string; + [key: string]: any; + }> = []; + + const registerResponse = await fetch( + `${SEDP_API_BASE_URL}/Register/GetByID`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'accept': '*/*', + 'ApiKey': apiKey, + 'ProjectNo': projectCode + }, + body: JSON.stringify({ + ProjectNo: projectCode, + TYPE_ID: baseMappings[0].formCode, // 또는 mapping.formCode + ContainDeleted: false + }) + } + ) + + if (!registerResponse.ok) { + allErrors.push(`Failed to fetch register details for ${baseMappings[0].formCode}`) + continue + } + + const registerDetail: Register = await registerResponse.json() + + // ✅ MAP_ATT에서 허용된 ATT_ID 목록 추출 + const allowedAttIds = new Set<string>() + if (Array.isArray(registerDetail.MAP_ATT)) { + for (const mapAttr of registerDetail.MAP_ATT) { + if (mapAttr.ATT_ID) { + allowedAttIds.add(mapAttr.ATT_ID) + } + } + } + + + // Process each tag entry + for (let i = 0; i < tagEntries.length; i++) { + try { + const entry = tagEntries[i]; + + // TAG_IDX가 없는 경우 제외 (변경: TAG_NO → TAG_IDX 체크) + if (!entry.TAG_IDX) { + excludedCount++; + totalExcludedCount++; + + // 주기적으로 진행 상황 보고 (건너뛰어도 진행률은 업데이트) + if (progressCallback && (i % 10 === 0 || i === tagEntries.length - 1)) { + const baseProgress = 60; + const entryProgress = Math.floor(30 * ((mappingIndex * entriesCount + i) / (mappings.length * entriesCount))); + progressCallback(baseProgress + entryProgress); + } + + continue; // 이 항목은 건너뜀 + } + + const attributes: Record<string, string> = {} + if (Array.isArray(entry.ATTRIBUTES)) { + for (const attr of entry.ATTRIBUTES) { + // MAP_ATT에 정의된 ATT_ID만 포함 + if (attr.ATT_ID && allowedAttIds.has(attr.ATT_ID)) { + if (attr.VALUE !== null && attr.VALUE !== undefined) { + attributes[attr.ATT_ID] = String(attr.VALUE) + } + } + } + } + + + // TAG_TYPE_ID가 null이거나 빈 문자열인 경우 제외 + if (entry.TAG_TYPE_ID === null || entry.TAG_TYPE_ID === "") { + excludedCount++; + totalExcludedCount++; + + // 주기적으로 진행 상황 보고 (건너뛰어도 진행률은 업데이트) + if (progressCallback && (i % 10 === 0 || i === tagEntries.length - 1)) { + const baseProgress = 60; + const entryProgress = Math.floor(30 * ((mappingIndex * entriesCount + i) / (mappings.length * entriesCount))); + progressCallback(baseProgress + entryProgress); + } + + continue; // 이 항목은 건너뜀 + } + + // Get tag type description + const tagType = await db.query.tagTypes.findFirst({ + where: and( + eq(tagTypes.code, entry.TAG_TYPE_ID), + eq(tagTypes.projectId, mapping.projectId) + ) + }); + + // Get tag class label + const tagClass = await db.query.tagClasses.findFirst({ + where: and( + eq(tagClasses.code, entry.CLS_ID), + eq(tagClasses.projectId, mapping.projectId) + ) + }); + + // Insert or update the tag - tagIdx 필드 추가 + await db.insert(tagsPlant).values({ + projectCode, + packageCode, + formId: formId, + tagIdx: entry.TAG_IDX, + tagNo: entry.TAG_NO || entry.TAG_IDX, + tagType: tagType?.description || entry.TAG_TYPE_ID, + tagClassId: tagClass?.id, + class: tagClass?.label || entry.CLS_ID, + description: entry.TAG_DESC, + attributes: attributes, // JSONB로 저장 + }).onConflictDoUpdate({ + target: [tagsPlant.projectCode, tagsPlant.packageCode, tagsPlant.tagIdx], + set: { + formId: formId, + tagNo: entry.TAG_NO || entry.TAG_IDX, + tagType: tagType?.description || entry.TAG_TYPE_ID, + class: tagClass?.label || entry.CLS_ID, + description: entry.TAG_DESC, + attributes: attributes, // JSONB 업데이트 + updatedAt: new Date() + } + }) + // formEntries용 데이터 수집 + const tagDataForFormEntry = { + TAG_IDX: entry.TAG_IDX, // 변경: TAG_NO → TAG_IDX + TAG_NO: entry.TAG_NO || entry.TAG_IDX, // TAG_NO도 함께 저장 + TAG_DESC: entry.TAG_DESC || null, + status: "From S-EDP", // SEDP에서 가져온 데이터임을 표시 + source: "S-EDP" // 태그 출처 (불변) - S-EDP에서 가져옴 + }; + + // ATTRIBUTES가 있으면 추가 (SHI 필드들) + if (Array.isArray(entry.ATTRIBUTES)) { + for (const attr of entry.ATTRIBUTES) { + if (attr.ATT_ID && attr.VALUE !== null && attr.VALUE !== undefined) { + tagDataForFormEntry[attr.ATT_ID] = attr.VALUE; + } + } + } + + newTagsForFormEntry.push(tagDataForFormEntry); + + processedCount++; + totalProcessedCount++; + + // 주기적으로 진행 상황 보고 + if (progressCallback && (i % 10 === 0 || i === tagEntries.length - 1)) { + const baseProgress = 60; + const entryProgress = Math.floor(30 * ((mappingIndex * entriesCount + i) / (mappings.length * entriesCount))); + progressCallback(baseProgress + entryProgress); + } + } catch (error: any) { + console.error(`Error processing tag entry:`, error); + allErrors.push(error.message || 'Unknown error'); + } + } + + // Step 7: formEntries 업데이트 - TAG_IDX 기준으로 변경 + if (newTagsForFormEntry.length > 0) { + try { + // 기존 formEntry 가져오기 + const existingEntry = await db.query.formEntriesPlant.findFirst({ + where: and( + eq(formEntriesPlant.formCode, formCode), + eq(formEntriesPlant.projectCode, projectCode), + eq(formEntriesPlant.packageCode, packageCode) + ) + }); + + if (existingEntry && existingEntry.id) { + // 기존 formEntry가 있는 경우 + let existingData: Array<{ + TAG_IDX?: string; // 추가: TAG_IDX 필드 + TAG_NO?: string; + TAG_DESC?: string; + status?: string; + [key: string]: any; + }> = []; + + if (Array.isArray(existingEntry.data)) { + existingData = existingEntry.data; + } + + // 기존 TAG_IDX들 추출 (변경: TAG_NO → TAG_IDX) + const existingTagIdxs = new Set( + existingData + .map(item => item.TAG_IDX) + .filter(tagIdx => tagIdx !== undefined && tagIdx !== null) + ); + + // 중복되지 않은 새 태그들만 필터링 (변경: TAG_NO → TAG_IDX) + const newUniqueTagsData = newTagsForFormEntry.filter( + tagData => !existingTagIdxs.has(tagData.TAG_IDX) + ); + + // 기존 태그들의 status와 ATTRIBUTES 업데이트 (변경: TAG_NO → TAG_IDX) + const updatedExistingData = existingData.map(existingItem => { + const newTagData = newTagsForFormEntry.find( + newItem => newItem.TAG_IDX === existingItem.TAG_IDX + ); + + if (newTagData) { + // 기존 태그가 있으면 SEDP 데이터로 업데이트 + return { + ...existingItem, + ...newTagData, + TAG_IDX: existingItem.TAG_IDX // TAG_IDX는 유지 + }; + } + + return existingItem; + }); + + const finalData = [...updatedExistingData, ...newUniqueTagsData]; + + await db + .update(formEntriesPlant) + .set({ + data: finalData, + updatedAt: new Date() + }) + .where(eq(formEntriesPlant.id, existingEntry.id)); + + console.log(`[IMPORT SEDP] Updated formEntry with ${newUniqueTagsData.length} new tags, updated ${updatedExistingData.length - newUniqueTagsData.length} existing tags for form ${formCode}`); + } else { + // formEntry가 없는 경우 새로 생성 + await db.insert(formEntriesPlant).values({ + formCode: formCode, + projectCode, + packageCode, + data: newTagsForFormEntry, + createdAt: new Date(), + updatedAt: new Date(), + }); + + console.log(`[IMPORT SEDP] Created new formEntry with ${newTagsForFormEntry.length} tags for form ${formCode}`); + } + + // 캐시 무효화 + revalidateTag(`form-data-${formCode}-${packageId}`); + } catch (formEntryError) { + console.error(`[IMPORT SEDP] Error updating formEntry for form ${formCode}:`, formEntryError); + allErrors.push(`Error updating formEntry for form ${formCode}: ${formEntryError}`); + } + } + + } catch (error: any) { + console.error(`Error processing mapping for formCode ${formCode}:`, error); + allErrors.push(`Error with formCode ${formCode}: ${error.message || 'Unknown error'}`); + } + } + + // 모든 매핑 처리 완료 - 진행률 100% + if (progressCallback) { + progressCallback(100); + } + + // 최종 결과 반환 + return { + processedCount: totalProcessedCount, + excludedCount: totalExcludedCount, + totalEntries: totalEntriesCount, + errors: allErrors.length > 0 ? allErrors : undefined + }; + } catch (error: any) { + console.error("Tag import error:", error); + throw error; + } +} + +/** + * SEDP API에서 태그 데이터 가져오기 + * + * @param projectCode 프로젝트 코드 + * @param formCode 양식 코드 + * @returns API 응답 데이터 + */ +async function fetchTagDataFromSEDP(projectCode: string, formCode: string): Promise<any> { + try { + // Get the token + const apiKey = await getSEDPToken(); + + // Define the API base URL + const SEDP_API_BASE_URL = process.env.SEDP_API_BASE_URL || 'http://sedpwebapi.ship.samsung.co.kr/api'; + + // Make the API call + const response = await fetch( + `${SEDP_API_BASE_URL}/Data/GetPubData`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'accept': '*/*', + 'ApiKey': apiKey, + 'ProjectNo': projectCode + }, + body: JSON.stringify({ + ProjectNo: projectCode, + REG_TYPE_ID: formCode, + ContainDeleted: false + }) + } + ); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`SEDP API request failed: ${response.status} ${response.statusText} - ${errorText}`); + } + + const data = await response.json(); + return data; + } catch (error: any) { + console.error('Error calling SEDP API:', error); + throw new Error(`Failed to fetch data from SEDP API: ${error.message || 'Unknown error'}`); + } +}
\ No newline at end of file diff --git a/lib/sedp/sync-form.ts b/lib/sedp/sync-form.ts index 904d27ba..a6d473ad 100644 --- a/lib/sedp/sync-form.ts +++ b/lib/sedp/sync-form.ts @@ -94,7 +94,7 @@ interface Register { SEQ: number; CMPLX_YN: boolean; CMPL_SETT: any | null; - MAP_ATT: any[]; + MAP_ATT: MapAttribute2[]; MAP_CLS_ID: string[]; MAP_OPER: any | null; LNK_ATT: LinkAttribute[]; @@ -157,6 +157,13 @@ interface MapAttribute { INOUT: string | null; } +interface MapAttribute2 { + ATT_ID: string; + VALUE: string; + IS_PARA: boolean; + OPER: string | null; +} + interface Attribute { PROJ_NO: string; ATT_ID: string; |
