summaryrefslogtreecommitdiff
path: root/lib/forms/services.ts
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-04-28 02:13:30 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-04-28 02:13:30 +0000
commitef4c533ebacc2cdc97e518f30e9a9350004fcdfb (patch)
tree345251a3ed0f4429716fa5edaa31024d8f4cb560 /lib/forms/services.ts
parent9ceed79cf32c896f8a998399bf1b296506b2cd4a (diff)
~20250428 작업사항
Diffstat (limited to 'lib/forms/services.ts')
-rw-r--r--lib/forms/services.ts637
1 files changed, 488 insertions, 149 deletions
diff --git a/lib/forms/services.ts b/lib/forms/services.ts
index d77f91d3..bd6e4bbc 100644
--- a/lib/forms/services.ts
+++ b/lib/forms/services.ts
@@ -10,74 +10,78 @@ import {
formEntries,
formMetas,
forms,
+ tagClasses,
tags,
+ tagSubfieldOptions,
+ tagSubfields,
tagTypeClassFormMappings,
+ tagTypes,
vendorDataReportTemps,
VendorDataReportTemps,
} from "@/db/schema/vendorData";
-import { eq, and, desc, sql, DrizzleError, or } from "drizzle-orm";
+import { eq, and, desc, sql, DrizzleError, or,type SQL ,type InferSelectModel } from "drizzle-orm";
import { unstable_cache } from "next/cache";
import { revalidateTag } from "next/cache";
import { getErrorMessage } from "../handle-error";
import { DataTableColumnJSON } from "@/components/form-data/form-data-table-columns";
import { contractItems, contracts, projects } from "@/db/schema";
+import { getSEDPToken } from "../sedp/sedp-token";
-export interface FormInfo {
- id: number;
- formCode: string;
- formName: string;
- // tagType: string
-}
-export async function getFormsByContractItemId(contractItemId: number | null) {
+export type FormInfo = InferSelectModel<typeof forms>;
+export async function getFormsByContractItemId(
+ contractItemId: number | null,
+ mode: "ENG" | "IM" | "ALL" = "ALL"
+): Promise<{ forms: FormInfo[] }> {
// 유효성 검사
if (!contractItemId || contractItemId <= 0) {
console.warn(`Invalid contractItemId: ${contractItemId}`);
return { forms: [] };
}
- // 고유 캐시 키
- const cacheKey = `forms-${contractItemId}`;
+ // 고유 캐시 키 (모드 포함)
+ const cacheKey = `forms-${contractItemId}-${mode}`;
try {
return unstable_cache(
-
async () => {
- console.log(contractItemId,"contractItemId")
-
console.log(
- `[Forms Service] Fetching forms for contractItemId: ${contractItemId}`
+ `[Forms Service] Fetching forms for contractItemId: ${contractItemId}, mode: ${mode}`
);
try {
- // 데이터베이스에서 폼 조회
- const formRecords = await db
- .select({
- id: forms.id,
- formCode: forms.formCode,
- formName: forms.formName,
- // tagType: forms.tagType,
- })
- .from(forms)
- .where(eq(forms.contractItemId, contractItemId));
+ // 쿼리 생성
+ let query = db.select().from(forms).where(eq(forms.contractItemId, contractItemId));
+
+ // 모드에 따른 추가 필터
+ if (mode === "ENG") {
+ query = db.select().from(forms).where(
+ and(
+ eq(forms.contractItemId, contractItemId),
+ eq(forms.eng, true)
+ )
+ );
+ } else if (mode === "IM") {
+ query = db.select().from(forms).where(
+ and(
+ eq(forms.contractItemId, contractItemId),
+ eq(forms.im, true)
+ )
+ );
+ }
+
+ // 쿼리 실행
+ const formRecords = await query;
console.log(
- `[Forms Service] Found ${formRecords.length} forms for contractItemId: ${contractItemId}`
+ `[Forms Service] Found ${formRecords.length} forms for contractItemId: ${contractItemId}, mode: ${mode}`
);
- // 결과가 배열인지 확인
- if (!Array.isArray(formRecords)) {
- getErrorMessage(
- `Unexpected result format for contractItemId ${contractItemId} ${formRecords}`
- );
- return { forms: [] };
- }
-
return { forms: formRecords };
} catch (error) {
getErrorMessage(
- `Database error for contractItemId ${contractItemId}: ${error}`
+ `Database error for contractItemId ${contractItemId}, mode: ${mode}: ${error}`
);
throw error; // 캐시 함수에서 에러를 던져 캐싱이 발생하지 않도록 함
}
@@ -91,29 +95,42 @@ export async function getFormsByContractItemId(contractItemId: number | null) {
)();
} catch (error) {
getErrorMessage(
- `Cache operation failed for contractItemId ${contractItemId}: ${error}`
+ `Cache operation failed for contractItemId ${contractItemId}, mode: ${mode}: ${error}`
);
// 캐시 문제 시 직접 쿼리 시도
try {
console.log(
- `[Forms Service] Fallback: Direct query for contractItemId: ${contractItemId}`
+ `[Forms Service] Fallback: Direct query for contractItemId: ${contractItemId}, mode: ${mode}`
);
- const formRecords = await db
- .select({
- id: forms.id,
- formCode: forms.formCode,
- formName: forms.formName,
- // tagType: forms.tagType,
- })
- .from(forms)
- .where(eq(forms.contractItemId, contractItemId));
+ // 쿼리 생성
+ let query = db.select().from(forms).where(eq(forms.contractItemId, contractItemId));
+
+ // 모드에 따른 추가 필터
+ if (mode === "ENG") {
+ query = db.select().from(forms).where(
+ and(
+ eq(forms.contractItemId, contractItemId),
+ eq(forms.eng, true)
+ )
+ );
+ } else if (mode === "IM") {
+ query = db.select().from(forms).where(
+ and(
+ eq(forms.contractItemId, contractItemId),
+ eq(forms.im, true)
+ )
+ );
+ }
+
+ // 쿼리 실행
+ const formRecords = await query;
return { forms: formRecords };
} catch (dbError) {
getErrorMessage(
- `Fallback query failed for contractItemId ${contractItemId}:${dbError}`
+ `Fallback query failed for contractItemId ${contractItemId}, mode: ${mode}: ${dbError}`
);
return { forms: [] };
}
@@ -145,6 +162,7 @@ export async function revalidateForms(contractItemId: number) {
export async function getFormData(formCode: string, contractItemId: number) {
// 고유 캐시 키 (formCode + contractItemId)
const cacheKey = `form-data-${formCode}-${contractItemId}`;
+ console.log(cacheKey, "getFormData")
try {
// 1) unstable_cache로 전체 로직을 감싼다
@@ -338,88 +356,6 @@ export async function getFormData(formCode: string, contractItemId: number) {
}
}
-// export async function syncMissingTags(contractItemId: number, formCode: string) {
-
-// // (1) forms 테이블에서 (contractItemId, formCode) 찾기
-// const [formRow] = await db
-// .select()
-// .from(forms)
-// .where(and(eq(forms.contractItemId, contractItemId), eq(forms.formCode, formCode)))
-// .limit(1)
-
-// if (!formRow) {
-// throw new Error(`Form not found for contractItemId=${contractItemId}, formCode=${formCode}`)
-// }
-
-// const { tagType, class: className } = formRow
-
-// // (2) tags 테이블에서 (contractItemId, tagType, class)인 태그 찾기
-// const tagRows = await db
-// .select()
-// .from(tags)
-// .where(
-// and(
-// eq(tags.contractItemId, contractItemId),
-// eq(tags.tagType, tagType),
-// eq(tags.class, className),
-// )
-// )
-
-// if (tagRows.length === 0) {
-// console.log("No matching tags found.")
-// return { createdCount: 0 }
-// }
-
-// // (3) formEntries에서 (contractItemId, formCode)인 row 1개 조회
-// let [entry] = await db
-// .select()
-// .from(formEntries)
-// .where(
-// and(
-// eq(formEntries.contractItemId, contractItemId),
-// eq(formEntries.formCode, formCode)
-// )
-// )
-// .limit(1)
-
-// // (4) 만약 없다면 새로 insert: data = []
-// if (!entry) {
-// const [inserted] = await db.insert(formEntries).values({
-// contractItemId,
-// formCode,
-// data: [], // 초기 상태는 빈 배열
-// }).returning()
-// entry = inserted
-// }
-
-// // entry.data는 배열이라고 가정
-// // Drizzle에서 jsonb는 JS object로 파싱되어 들어오므로, 타입 캐스팅
-// const existingData = entry.data as Array<{ tagNumber: string }>
-// let createdCount = 0
-
-// // (5) tagRows 각각에 대해, 이미 배열에 존재하는지 확인 후 없으면 push
-// const updatedArray = [...existingData]
-// for (const tagRow of tagRows) {
-// const tagNo = tagRow.tagNo
-// const found = updatedArray.some(item => item.tagNumber === tagNo)
-// if (!found) {
-// updatedArray.push({ tagNumber: tagNo })
-// createdCount++
-// }
-// }
-
-// // (6) 변경이 있으면 UPDATE
-// if (createdCount > 0) {
-// await db
-// .update(formEntries)
-// .set({ data: updatedArray })
-// .where(eq(formEntries.id, entry.id))
-// }
-
-// revalidateTag(`form-data-${formCode}-${contractItemId}`);
-
-// return { createdCount }
-// }
export async function syncMissingTags(
contractItemId: number,
@@ -490,10 +426,10 @@ export async function syncMissingTags(
entry = inserted;
}
- // entry.data는 [{ tagNumber: string, tagDescription?: string }, ...] 형태라고 가정
+ // entry.data는 [{ TAG_NO: string, TAG_DESC?: string }, ...] 형태라고 가정
const existingData = entry.data as Array<{
- tagNumber: string;
- tagDescription?: string;
+ TAG_NO: string;
+ TAG_DESC?: string;
}>;
// Create a Set of valid tagNumbers from tagRows for efficient lookup
@@ -501,8 +437,8 @@ export async function syncMissingTags(
// Copy existing data to work with
let updatedData: Array<{
- tagNumber: string;
- tagDescription?: string;
+ TAG_NO: string;
+ TAG_DESC?: string;
}> = [];
let createdCount = 0;
@@ -511,7 +447,7 @@ export async function syncMissingTags(
// First, filter out items that should be deleted (not in validTagNumbers)
for (const item of existingData) {
- if (validTagNumbers.has(item.tagNumber)) {
+ if (validTagNumbers.has(item.TAG_NO)) {
updatedData.push(item);
} else {
deletedCount++;
@@ -523,25 +459,25 @@ export async function syncMissingTags(
for (const tagRow of tagRows) {
const { tagNo, description } = tagRow;
- // 5-1. 기존 데이터에서 tagNumber 매칭
+ // 5-1. 기존 데이터에서 TAG_NO 매칭
const existingIndex = updatedData.findIndex(
- (item) => item.tagNumber === tagNo
+ (item) => item.TAG_NO === tagNo
);
// 5-2. 없다면 새로 추가
if (existingIndex === -1) {
updatedData.push({
- tagNumber: tagNo,
- tagDescription: description ?? "",
+ TAG_NO: tagNo,
+ TAG_DESC: description ?? "",
});
createdCount++;
} else {
// 5-3. 이미 있으면, description이 다를 때만 업데이트(선택 사항)
const existingItem = updatedData[existingIndex];
- if (existingItem.tagDescription !== description) {
+ if (existingItem.TAG_DESC !== description) {
updatedData[existingIndex] = {
...existingItem,
- tagDescription: description ?? "",
+ TAG_DESC: description ?? "",
};
updatedCount++;
}
@@ -565,7 +501,7 @@ export async function syncMissingTags(
/**
* updateFormDataInDB:
* (formCode, contractItemId)에 해당하는 "단 하나의" formEntries row를 가져와,
- * data: [{ tagNumber, ...}, ...] 배열에서 tagNumber 매칭되는 항목을 업데이트
+ * data: [{ TAG_NO, ...}, ...] 배열에서 TAG_NO 매칭되는 항목을 업데이트
* 업데이트 후, revalidateTag()로 캐시 무효화.
*/
type UpdateResponse = {
@@ -581,8 +517,8 @@ export async function updateFormDataInDB(
): Promise<UpdateResponse> {
try {
// 1) tagNumber로 식별
- const tagNumber = newData.tagNumber;
- if (!tagNumber) {
+ const TAG_NO = newData.TAG_NO;
+ if (!TAG_NO) {
return {
success: false,
message: "tagNumber는 필수 항목입니다.",
@@ -626,12 +562,12 @@ export async function updateFormDataInDB(
};
}
- // 4) tagNumber = newData.tagNumber 항목 찾기
- const idx = dataArray.findIndex((item) => item.tagNumber === tagNumber);
+ // 4) TAG_NO = newData.TAG_NO 항목 찾기
+ const idx = dataArray.findIndex((item) => item.TAG_NO === TAG_NO);
if (idx < 0) {
return {
success: false,
- message: `태그 번호 "${tagNumber}"를 가진 항목을 찾을 수 없습니다.`,
+ message: `태그 번호 "${TAG_NO}"를 가진 항목을 찾을 수 없습니다.`,
};
}
@@ -640,7 +576,7 @@ export async function updateFormDataInDB(
const updatedItem = {
...oldItem,
...newData,
- tagNumber: oldItem.tagNumber, // tagNumber 변경 불가 시 유지
+ TAG_NO: oldItem.TAG_NO, // TAG_NO 변경 불가 시 유지
};
const updatedArray = [...dataArray];
@@ -675,6 +611,7 @@ export async function updateFormDataInDB(
try {
// 캐시 태그를 form-data-${formCode}-${contractItemId} 형태로 가정
const cacheTag = `form-data-${formCode}-${contractItemId}`;
+ console.log(cacheTag, "update")
revalidateTag(cacheTag);
} catch (cacheError) {
console.warn("Cache revalidation warning:", cacheError);
@@ -685,9 +622,9 @@ export async function updateFormDataInDB(
success: true,
message: "데이터가 성공적으로 업데이트되었습니다.",
data: {
- tagNumber,
+ TAG_NO,
updatedFields: Object.keys(newData).filter(
- (key) => key !== "tagNumber"
+ (key) => key !== "TAG_NO"
),
},
};
@@ -922,3 +859,405 @@ export const deleteReportTempFile: deleteReportTempFile = async (id) => {
return { result: false, error: (err as Error).message };
}
};
+
+
+/**
+ * Get tag type mappings specific to a form
+ * @param formCode The form code to filter mappings
+ * @param projectId The project ID
+ * @returns Array of tag type-class mappings for the form
+ */
+export async function getFormTagTypeMappings(formCode: string, projectId: number) {
+
+ try {
+ const mappings = await db.query.tagTypeClassFormMappings.findMany({
+ where: and(
+ eq(tagTypeClassFormMappings.formCode, formCode),
+ eq(tagTypeClassFormMappings.projectId, projectId)
+ )
+ });
+
+ return mappings;
+ } catch (error) {
+ console.error("Error fetching form tag type mappings:", error);
+ throw new Error("Failed to load form tag type mappings");
+ }
+}
+
+/**
+ * Get tag type by its description
+ * @param description The tag type description (used as tagTypeLabel in mappings)
+ * @param projectId The project ID
+ * @returns The tag type object
+ */
+export async function getTagTypeByDescription(description: string, projectId: number) {
+ try {
+ const tagType = await db.query.tagTypes.findFirst({
+ where: and(
+ eq(tagTypes.description, description),
+ eq(tagTypes.projectId, projectId)
+ )
+ });
+
+ return tagType;
+ } catch (error) {
+ console.error("Error fetching tag type by description:", error);
+ throw new Error("Failed to load tag type");
+ }
+}
+
+/**
+ * Get subfields for a specific tag type
+ * @param tagTypeCode The tag type code
+ * @param projectId The project ID
+ * @returns Object containing subfields with their options
+ */
+export async function getSubfieldsByTagTypeForForm(tagTypeCode: string, projectId: number) {
+ try {
+ const subfields = await db.query.tagSubfields.findMany({
+ where: and(
+ eq(tagSubfields.tagTypeCode, tagTypeCode),
+ eq(tagSubfields.projectId, projectId)
+ ),
+ orderBy: tagSubfields.sortOrder
+ });
+
+ const subfieldsWithOptions = await Promise.all(
+ subfields.map(async (subfield) => {
+ const options = await db.query.tagSubfieldOptions.findMany({
+ where: and(
+ eq(tagSubfieldOptions.attributesId, subfield.attributesId),
+ eq(tagSubfieldOptions.projectId, projectId)
+ )
+ });
+
+ return {
+ name: subfield.attributesId,
+ label: subfield.attributesDescription,
+ type: options.length > 0 ? "select" : "text",
+ options: options.map(opt => ({ value: opt.code, label: opt.label })),
+ expression: subfield.expression || undefined,
+ delimiter: subfield.delimiter || undefined
+ };
+ })
+ );
+
+ return { subFields: subfieldsWithOptions };
+ } catch (error) {
+ console.error("Error fetching subfields for form:", error);
+ throw new Error("Failed to load subfields");
+ }
+}
+
+interface GenericData {
+ [key: string]: any;
+}
+
+interface SEDPAttribute {
+ NAME: string;
+ VALUE: any;
+ UOM: string;
+ UOM_ID?: string;
+}
+
+interface SEDPDataItem {
+ TAG_NO: string;
+ TAG_DESC: string;
+ ATTRIBUTES: SEDPAttribute[];
+ SCOPE: string;
+ TOOLID: string;
+ ITM_NO: string;
+ OP_DELETE: boolean;
+ MAIN_YN: boolean;
+ LAST_REV_YN: boolean;
+ CRTER_NO: string;
+ CHGER_NO: string;
+ TYPE: string;
+ PROJ_NO: string;
+ REV_NO: string;
+ CRTE_DTM?: string;
+ CHGE_DTM?: string;
+ _id?: string;
+}
+
+async function transformDataToSEDPFormat(
+ tableData: GenericData[],
+ columnsJSON: DataTableColumnJSON[],
+ formCode: string,
+ objectCode: string,
+ projectNo: string,
+ designerNo: string = "253213"
+): Promise<SEDPDataItem[]> {
+ // Create a map for quick column lookup
+ const columnsMap = new Map<string, DataTableColumnJSON>();
+ columnsJSON.forEach(col => {
+ columnsMap.set(col.key, col);
+ });
+
+ // Current timestamp for CRTE_DTM and CHGE_DTM
+ const currentTimestamp = new Date().toISOString();
+
+ // Define the API base URL
+ const SEDP_API_BASE_URL = process.env.SEDP_API_BASE_URL || 'http://sedpwebapi.ship.samsung.co.kr/api';
+
+ // Get the token
+ const apiKey = await getSEDPToken();
+
+ // Cache for UOM factors to avoid duplicate API calls
+ const uomFactorCache = new Map<string, number>();
+
+ // Transform each row
+ const transformedItems = [];
+
+ for (const row of tableData) {
+ // Create base SEDP item with required fields
+ const sedpItem: SEDPDataItem = {
+ TAG_NO: row.TAG_NO || "",
+ TAG_DESC: row.TAG_DESC || "",
+ ATTRIBUTES: [],
+ SCOPE: objectCode,
+ TOOLID: "eVCP", // Changed from VDCS
+ ITM_NO: row.TAG_NO || "",
+ OP_DELETE: false,
+ MAIN_YN: true,
+ LAST_REV_YN: true,
+ CRTER_NO: designerNo,
+ CHGER_NO: designerNo,
+ TYPE: formCode,
+ PROJ_NO: projectNo,
+ REV_NO: "00",
+ CRTE_DTM: currentTimestamp,
+ CHGE_DTM: currentTimestamp,
+ _id: ""
+ };
+
+ // Convert all other fields (except TAG_NO and TAG_DESC) to ATTRIBUTES
+ for (const key in row) {
+ if (key !== "TAG_NO" && key !== "TAG_DESC") {
+ const column = columnsMap.get(key);
+ let value = row[key];
+
+ // Only process non-empty values
+ if (value !== undefined && value !== null && value !== "") {
+ // Check if we need to apply UOM conversion
+ if (column?.uomId) {
+ // First check cache to avoid duplicate API calls
+ let factor = uomFactorCache.get(column.uomId);
+
+ // If not in cache, make API call to get the factor
+ if (factor === undefined) {
+ try {
+ const response = await fetch(
+ `${SEDP_API_BASE_URL}/UOM/GetByID`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'accept': '*/*',
+ 'ApiKey': apiKey,
+ 'ProjectNo': projectNo
+ },
+ body: JSON.stringify({
+ 'ProjectNo': projectNo,
+ 'UOMID': column.uomId,
+ 'ContainDeleted': false
+ })
+ }
+ );
+
+ if (response.ok) {
+ const uomData = await response.json();
+ if (uomData && uomData.FACTOR !== undefined && uomData.FACTOR !== null) {
+ factor = Number(uomData.FACTOR);
+ // Store in cache for future use (type assertion to ensure it's a number)
+ uomFactorCache.set(column.uomId, factor);
+ }
+ } else {
+ console.warn(`Failed to get UOM data for ${column.uomId}: ${response.statusText}`);
+ }
+ } catch (error) {
+ console.error(`Error fetching UOM data for ${column.uomId}:`, error);
+ }
+ }
+
+ // Apply the factor if we got one
+ if (factor !== undefined && typeof value === 'number') {
+ value = value * factor;
+ }
+ }
+
+ const attribute: SEDPAttribute = {
+ NAME: key,
+ VALUE: String(value), // 모든 값을 문자열로 변환
+ UOM: column?.uom || ""
+ };
+
+ // Add UOM_ID if present in column definition
+ if (column?.uomId) {
+ attribute.UOM_ID = column.uomId;
+ }
+
+ sedpItem.ATTRIBUTES.push(attribute);
+ }
+ }
+ }
+
+ transformedItems.push(sedpItem);
+ }
+
+ return transformedItems;
+}
+
+// Server Action wrapper (async)
+export async function transformFormDataToSEDP(
+ tableData: GenericData[],
+ columnsJSON: DataTableColumnJSON[],
+ formCode: string,
+ objectCode: string,
+ projectNo: string,
+ designerNo: string = "253213"
+): Promise<SEDPDataItem[]> {
+ // Use the utility function within the async Server Action
+ return transformDataToSEDPFormat(
+ tableData,
+ columnsJSON,
+ formCode,
+ objectCode,
+ projectNo,
+ designerNo
+ );
+}
+
+/**
+ * Get project code by project ID
+ */
+export async function getProjectCodeById(projectId: number): Promise<string> {
+ const projectRecord = await db
+ .select({ code: projects.code })
+ .from(projects)
+ .where(eq(projects.id, projectId))
+ .limit(1);
+
+ if (!projectRecord || projectRecord.length === 0) {
+ throw new Error(`Project not found with ID: ${projectId}`);
+ }
+
+ return projectRecord[0].code;
+}
+
+/**
+ * Send data to SEDP
+ */
+export async function sendDataToSEDP(
+ projectCode: string,
+ sedpData: SEDPDataItem[]
+): 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';
+
+ console.log("Sending data to SEDP:", JSON.stringify(sedpData, null, 2));
+
+ // Make the API call
+ const response = await fetch(
+ `${SEDP_API_BASE_URL}/AdapterData/Create`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'accept': '*/*',
+ 'ApiKey': apiKey,
+ 'ProjectNo': projectCode
+ },
+ body: JSON.stringify(sedpData)
+ }
+ );
+
+ 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 send data to SEDP API: ${error.message || 'Unknown error'}`);
+ }
+}
+
+/**
+ * Server action to send form data to SEDP
+ */
+export async function sendFormDataToSEDP(
+ formCode: string,
+ projectId: number,
+ formData: GenericData[],
+ columns: DataTableColumnJSON[]
+): Promise<{ success: boolean; message: string; data?: any }> {
+ try {
+ // 1. Get project code
+ const projectCode = await getProjectCodeById(projectId);
+
+ // 2. Get class mapping
+ const mappingsResult = await db.query.tagTypeClassFormMappings.findFirst({
+ where: and(
+ eq(tagTypeClassFormMappings.formCode, formCode),
+ eq(tagTypeClassFormMappings.projectId, projectId)
+ )
+ });
+
+ // Check if mappings is an array or a single object and handle accordingly
+ const mappings = Array.isArray(mappingsResult) ? mappingsResult[0] : mappingsResult;
+
+ // Default object code to fallback value if we can't find it
+ let objectCode = ""; // Default fallback
+
+ if (mappings && mappings.classLabel) {
+ const objectCodeResult = await db.query.tagClasses.findFirst({
+ where: and(
+ eq(tagClasses.label, mappings.classLabel),
+ eq(tagClasses.projectId, projectId)
+ )
+ });
+
+ // Check if result is an array or a single object
+ const objectCodeRecord = Array.isArray(objectCodeResult) ? objectCodeResult[0] : objectCodeResult;
+
+ if (objectCodeRecord && objectCodeRecord.code) {
+ objectCode = objectCodeRecord.code;
+ } else {
+ console.warn(`No tag class found for label ${mappings.classLabel} in project ${projectId}, using default`);
+ }
+ } else {
+ console.warn(`No mapping found for formCode ${formCode} in project ${projectId}, using default object code`);
+ }
+
+ // 4. Transform data to SEDP format
+ const sedpData = await transformFormDataToSEDP(
+ formData,
+ columns,
+ formCode,
+ objectCode,
+ projectCode
+ );
+
+ // 5. Send to SEDP API
+ const result = await sendDataToSEDP(projectCode, sedpData);
+
+ return {
+ success: true,
+ message: "Data successfully sent to SEDP",
+ data: result
+ };
+ } catch (error: any) {
+ console.error("Error sending data to SEDP:", error);
+ return {
+ success: false,
+ message: error.message || "Failed to send data to SEDP"
+ };
+ }
+} \ No newline at end of file