summaryrefslogtreecommitdiff
path: root/lib/sedp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sedp')
-rw-r--r--lib/sedp/get-form-tags.ts282
-rw-r--r--lib/sedp/sync-form.ts4
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 ?? "",