diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-13 11:05:09 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-13 11:05:09 +0000 |
| commit | 33be47506f0aa62b969d82521580a29e95080268 (patch) | |
| tree | 6b7e232f2d78ef8775944ea085a36b3ccbce7d95 /lib/sedp | |
| parent | 2ac95090157c355ea1bd0b8eb1e1e5e2bd56faf4 (diff) | |
(대표님) 입찰, 법무검토, EDP 변경사항 대응, dolce 개선, form-data 개선, 정규업체 등록관리 추가
(최겸) pq 미사용 컴포넌트 및 페이지 제거, 파일 라우트에 pq 적용
Diffstat (limited to 'lib/sedp')
| -rw-r--r-- | lib/sedp/get-form-tags.ts | 282 | ||||
| -rw-r--r-- | lib/sedp/sync-form.ts | 4 |
2 files changed, 155 insertions, 131 deletions
diff --git a/lib/sedp/get-form-tags.ts b/lib/sedp/get-form-tags.ts index 821fa372..efa4a9c0 100644 --- a/lib/sedp/get-form-tags.ts +++ b/lib/sedp/get-form-tags.ts @@ -1,5 +1,5 @@ import db from "@/db/db"; -import { +import { contractItems, tags, forms, @@ -64,13 +64,13 @@ export async function importTagsFromSEDP( try { // 진행 상황 보고 if (progressCallback) progressCallback(5); - + // 에러 수집 배열 const errors: string[] = []; - + // SEDP API에서 태그 데이터 가져오기 const tagData = await fetchTagDataFromSEDP(projectCode, formCode); - + // 트랜잭션으로 모든 DB 작업 처리 return await db.transaction(async (tx) => { // 프로젝트 정보 가져오기 (type 포함) @@ -78,57 +78,57 @@ export async function importTagsFromSEDP( .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"; - + // packageId로 contractItem과 item 정보 가져오기 - const contractItemRecord = await tx.select({ itemId: contractItems.itemId, contractId:contractItems.contractId }) + const contractItemRecord = await tx.select({ itemId: contractItems.itemId, contractId: contractItems.contractId }) .from(contractItems) .where(eq(contractItems.id, packageId)) .limit(1); - + if (!contractItemRecord || contractItemRecord.length === 0) { throw new Error(`Contract item not found for packageId: ${packageId}`); } const contractRecord = await tx.select({ vendorId: contracts.vendorId }) - .from(contracts) - .where(eq(contracts.id, contractItemRecord[0].contractId)) - .limit(1); + .from(contracts) + .where(eq(contracts.id, contractItemRecord[0].contractId)) + .limit(1); const vendorRecord = await tx.select({ vendorCode: vendors.vendorCode, vendorName: vendors.vendorName }) - .from(vendors) - .where(eq(vendors.id, contractRecord[0].vendorId)) - .limit(1); - + .from(vendors) + .where(eq(vendors.id, contractRecord[0].vendorId)) + .limit(1); + const itemRecord = await tx.select({ packageCode: items.packageCode }) .from(items) .where(eq(items.id, contractItemRecord[0].itemId)) .limit(1); - + if (!itemRecord || itemRecord.length === 0) { throw new Error(`Item not found for itemId: ${contractItemRecord[0].itemId}`); } - + const targetPackageCode = itemRecord[0].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, @@ -137,7 +137,7 @@ export async function importTagsFromSEDP( errors: ["No tag entries found in API response"] }; } - + // packageCode로 필터링 - ATTRIBUTES에서 지정된 ATT_ID의 VALUE와 packageCode 비교 const tagEntries = allTagEntries.filter(entry => { if (Array.isArray(entry.ATTRIBUTES)) { @@ -157,10 +157,10 @@ export async function importTagsFromSEDP( errors: [`No tag entries found with ${packageCodeAttId} attribute value matching packageCode: ${targetPackageCode}`] }; } - + // 진행 상황 보고 if (progressCallback) progressCallback(20); - + // 나머지 코드는 기존과 동일... // form ID 가져오기 - 없으면 생성 let formRecord = await tx.select({ id: forms.id }) @@ -170,18 +170,18 @@ export async function importTagsFromSEDP( eq(forms.contractItemId, packageId) )) .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) { @@ -192,16 +192,16 @@ export async function importTagsFromSEDP( 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 }) + const tagClassRecord = await tx.select({ id: tagClasses.id, label: tagClasses.label }) .from(tagClasses) .where(and( eq(tagClasses.code, firstTag.CLS_ID), @@ -213,18 +213,18 @@ export async function importTagsFromSEDP( 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(forms) @@ -236,10 +236,10 @@ export async function importTagsFromSEDP( im: targetFormMapping.ep === "IMEP" ? true : false }) .returning({ id: forms.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}`); @@ -251,25 +251,25 @@ export async function importTagsFromSEDP( } } else { console.log(`[IMPORT TAGS] Found existing form:`, formRecord[0].id); - + // 기존 form이 있는 경우 eng와 im 필드를 체크하고 업데이트 - const existingForm = await tx.select({ + const existingForm = await tx.select({ eng: forms.eng, - im: forms.im + im: forms.im }) .from(forms) .where(eq(forms.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) { @@ -280,12 +280,12 @@ export async function importTagsFromSEDP( 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) { @@ -296,59 +296,59 @@ export async function importTagsFromSEDP( 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(forms) .set(updates) .where(eq(forms.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) @@ -357,17 +357,17 @@ export async function importTagsFromSEDP( 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: formEntries.id, data: formEntries.data }) .from(formEntries) @@ -375,19 +375,19 @@ export async function importTagsFromSEDP( eq(formEntries.formCode, formCode), eq(formEntries.contractItemId, packageId) )); - + // 기존 tags 데이터 가져오기 const existingTags = await tx.select() .from(tags) .where(eq(tags.contractItemId, packageId)); - + // 진행 상황 보고 if (progressCallback) progressCallback(50); - + // 기존 데이터를 맵으로 변환 const existingTagMap = new Map(); const existingTagsMap = new Map(); - + existingEntries.forEach(entry => { const data = entry.data as any[]; data.forEach(item => { @@ -399,23 +399,23 @@ export async function importTagsFromSEDP( } }); }); - + 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}[] = []; - + const updateData: { entryId: number, tagNo: string, updates: any }[] = []; + // SEDP 태그 데이터 처리 for (const tagEntry of tagEntries) { try { @@ -424,7 +424,7 @@ export async function importTagsFromSEDP( 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) { @@ -435,40 +435,42 @@ export async function importTagsFromSEDP( 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 }) + 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; } } - + // 기본 태그 데이터 객체 생성 (formEntries용) const tagObject: any = { TAG_IDX: tagEntry.TAG_IDX, // SEDP 고유 식별자 TAG_NO: tagEntry.TAG_NO, TAG_DESC: tagEntry.TAG_DESC || "", - VNDRCD:vendorRecord[0].vendorCode, - VNDRNM_1:vendorRecord[0].vendorName, - status: "From S-EDP" // SEDP에서 가져온 데이터임을 표시 - }; - + CLS_ID: tagEntry.CLS_ID || "", + VNDRCD: vendorRecord[0].vendorCode, + VNDRNM_1: vendorRecord[0].vendorName, + status: "From S-EDP", // SEDP에서 가져온 데이터임을 표시 + ...(projectType === "ship" ? { CM3003: tagEntry.CM3003 } : { ME5074: tagEntry.ME5074 }) + } + // tags 테이블용 데이터 (UPSERT용) const tagRecord = { contractItemId: packageId, @@ -482,7 +484,7 @@ export async function importTagsFromSEDP( createdAt: new Date(), updatedAt: new Date() }; - + // ATTRIBUTES 필드에서 shi=true인 컬럼의 값 추출 if (Array.isArray(tagEntry.ATTRIBUTES)) { for (const attr of tagEntry.ATTRIBUTES) { @@ -510,15 +512,15 @@ export async function importTagsFromSEDP( } } } - + // 기존 태그가 있는지 확인하고 처리 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; @@ -527,20 +529,20 @@ export async function importTagsFromSEDP( 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; } - + const columnInfo = columnsJSON.find(col => col.key === key); if (columnInfo && columnInfo.shi === true) { if (existingTag.data[key] !== tagObject[key]) { @@ -549,7 +551,7 @@ export async function importTagsFromSEDP( } } } - + if (hasUpdates) { updateData.push({ entryId: existingTag.entryId, @@ -561,54 +563,76 @@ export async function importTagsFromSEDP( // 기존 태그가 없으면 새로 추가 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 === update.entryId); + const entry = existingEntries.find(e => e.id === entryId); if (!entry) continue; - + const data = entry.data as any[]; + + // 해당 entryId의 모든 업데이트를 한 번에 적용 const updatedData = data.map(item => { - if (item.TAG_IDX === update.tagIdx) { - return { ...item, ...update.updates }; + let updatedItem = { ...item }; + + // 현재 item에 적용할 모든 업데이트를 찾아서 적용 + for (const update of updates) { + if (item.TAG_IDX === update.tagIdx) { + updatedItem = { ...updatedItem, ...update.updates }; + } } - return item; + + return updatedItem; }); - + + // entryId별로 한 번만 DB 업데이트 await tx.update(formEntries) - .set({ + .set({ data: updatedData, updatedAt: new Date() }) - .where(eq(formEntries.id, update.entryId)); + .where(eq(formEntries.id, entryId)); + } catch (error) { - errors.push(`Error updating formEntry for tag ${update.tagNo}: ${error}`); + const tagNos = updates.map(u => u.tagNo || u.tagIdx).join(', '); + errors.push(`Error updating formEntry ${entryId} for tags ${tagNos}: ${error}`); } } - + // 새 태그 추가 (formEntries) 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(formEntries) - .set({ + .set({ data: updatedData, updatedAt: new Date() }) @@ -624,27 +648,27 @@ export async function importTagsFromSEDP( }); } } - + // tags 테이블 처리 (INSERT + UPDATE 분리) if (upsertTagRecords.length > 0) { const newTagRecords: any[] = []; - const updateTagRecords: {tagId: number, updates: any}[] = []; - + const updateTagRecords: { tagId: number, updates: any }[] = []; + // 각 태그를 확인하여 신규/업데이트 분류 for (const tagRecord of upsertTagRecords) { const existingTagRecord = existingTagsMap.get(tagRecord.tagNo); - + if (existingTagRecord) { // 기존 태그가 있으면 업데이트 준비 const tagUpdates: any = {}; let hasTagUpdates = false; - // tagNo도 업데이트 가능 (편집된 경우) - if (existingTagRecord.tagNo !== tagRecord.tagNo) { - tagUpdates.tagNo = tagRecord.tagNo; - hasTagUpdates = true; - } - + // tagNo도 업데이트 가능 (편집된 경우) + if (existingTagRecord.tagNo !== tagRecord.tagNo) { + tagUpdates.tagNo = tagRecord.tagNo; + hasTagUpdates = true; + } + if (existingTagRecord.tagType !== tagRecord.tagType) { tagUpdates.tagType = tagRecord.tagType; hasTagUpdates = true; @@ -661,7 +685,7 @@ export async function importTagsFromSEDP( tagUpdates.formId = tagRecord.formId; hasTagUpdates = true; } - + if (hasTagUpdates) { updateTagRecords.push({ tagId: existingTagRecord.id, @@ -673,7 +697,7 @@ export async function importTagsFromSEDP( newTagRecords.push(tagRecord); } } - + // 새 태그 삽입 if (newTagRecords.length > 0) { try { @@ -697,7 +721,7 @@ export async function importTagsFromSEDP( } } } - + // 기존 태그 업데이트 for (const update of updateTagRecords) { try { @@ -709,10 +733,10 @@ export async function importTagsFromSEDP( } } } - + // 진행 상황 보고 if (progressCallback) progressCallback(100); - + // 최종 결과 반환 return { processedCount, @@ -722,7 +746,7 @@ export async function importTagsFromSEDP( errors: errors.length > 0 ? errors : undefined }; }); - + } catch (error: any) { console.error("Tag import error:", error); throw error; @@ -739,10 +763,10 @@ async function fetchTagDataFromSEDP(projectCode: string, formCode: string): Prom 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`, @@ -761,12 +785,12 @@ async function fetchTagDataFromSEDP(projectCode: string, formCode: string): Prom }) } ); - + 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) { diff --git a/lib/sedp/sync-form.ts b/lib/sedp/sync-form.ts index 6ae2e675..1f903c78 100644 --- a/lib/sedp/sync-form.ts +++ b/lib/sedp/sync-form.ts @@ -983,7 +983,7 @@ export async function saveFormMappingsAndMetas( if (!attribute) continue; const tmplMeta = templateAttrMap.get(attId); - const isShi = mapAtt.INOUT === "IN"; + const isShi = mapAtt.INOUT === null || mapAtt.INOUT === "OUT"; let uomSymbol: string | undefined; let uomId: string | undefined; if (legacy?.LNK_ATT) { @@ -995,7 +995,7 @@ export async function saveFormMappingsAndMetas( key: attId, label: attribute.DESC as string, type: (attribute.VAL_TYPE === "LIST" || attribute.VAL_TYPE === "DYNAMICLIST") ? "LIST" : (attribute.VAL_TYPE || "STRING"), - shi: !isShi, + shi: isShi, hidden: tmplMeta?.hidden ?? false, seq: tmplMeta?.seq ?? 0, head: tmplMeta?.head ?? "", |
