diff options
Diffstat (limited to 'lib/forms/services.ts')
| -rw-r--r-- | lib/forms/services.ts | 390 |
1 files changed, 195 insertions, 195 deletions
diff --git a/lib/forms/services.ts b/lib/forms/services.ts index 269fb4c6..cff23806 100644 --- a/lib/forms/services.ts +++ b/lib/forms/services.ts @@ -20,7 +20,7 @@ import { vendorDataReportTemps, VendorDataReportTemps, } from "@/db/schema/vendorData"; -import { eq, and, desc, sql, DrizzleError, inArray, or,type SQL ,type InferSelectModel } from "drizzle-orm"; +import { eq, and, desc, sql, DrizzleError, inArray, or, type SQL, type InferSelectModel } from "drizzle-orm"; import { unstable_cache } from "next/cache"; import { revalidateTag } from "next/cache"; import { getErrorMessage } from "../handle-error"; @@ -53,41 +53,41 @@ export async function getFormsByContractItemId( // `[Forms Service] Fetching forms for contractItemId: ${contractItemId}, mode: ${mode}` // ); - try { - // 쿼리 생성 - 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; + try { + // 쿼리 생성 + let query = db.select().from(forms).where(eq(forms.contractItemId, contractItemId)); - console.log( - `[Forms Service] Found ${formRecords.length} forms for contractItemId: ${contractItemId}, mode: ${mode}` - ); + // 모드에 따른 추가 필터 + 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) + ) + ); + } - return { forms: formRecords }; - } catch (error) { - getErrorMessage( - `Database error for contractItemId ${contractItemId}, mode: ${mode}: ${error}` - ); - throw error; // 캐시 함수에서 에러를 던져 캐싱이 발생하지 않도록 함 - } + // 쿼리 실행 + const formRecords = await query; + + console.log( + `[Forms Service] Found ${formRecords.length} forms for contractItemId: ${contractItemId}, mode: ${mode}` + ); + + return { forms: formRecords }; + } catch (error) { + getErrorMessage( + `Database error for contractItemId ${contractItemId}, mode: ${mode}: ${error}` + ); + throw error; // 캐시 함수에서 에러를 던져 캐싱이 발생하지 않도록 함 + } // }, // [cacheKey], // { @@ -109,7 +109,7 @@ export async function getFormsByContractItemId( // 쿼리 생성 let query = db.select().from(forms).where(eq(forms.contractItemId, contractItemId)); - + // 모드에 따른 추가 필터 if (mode === "ENG") { query = db.select().from(forms).where( @@ -126,7 +126,7 @@ export async function getFormsByContractItemId( ) ); } - + // 쿼리 실행 const formRecords = await query; @@ -164,7 +164,7 @@ export interface EditableFieldsInfo { // TAG별 편집 가능 필드 조회 함수 async function getEditableFieldsByTag( - contractItemId: number, + contractItemId: number, projectId: number ): Promise<Map<string, string[]>> { try { @@ -232,104 +232,104 @@ export async function getFormData(formCode: string, contractItemId: number) { try { - // 기존 로직으로 projectId, columns, data 가져오기 - const contractItemResult = await db - .select({ - projectId: projects.id - }) - .from(contractItems) - .innerJoin(contracts, eq(contractItems.contractId, contracts.id)) - .innerJoin(projects, eq(contracts.projectId, projects.id)) - .where(eq(contractItems.id, contractItemId)) - .limit(1); - - if (contractItemResult.length === 0) { - console.warn(`[getFormData] No contract item found with ID: ${contractItemId}`); - return { columns: null, data: [], editableFieldsMap: new Map() }; - } + // 기존 로직으로 projectId, columns, data 가져오기 + const contractItemResult = await db + .select({ + projectId: projects.id + }) + .from(contractItems) + .innerJoin(contracts, eq(contractItems.contractId, contracts.id)) + .innerJoin(projects, eq(contracts.projectId, projects.id)) + .where(eq(contractItems.id, contractItemId)) + .limit(1); - const projectId = contractItemResult[0].projectId; - - const metaRows = await db - .select() - .from(formMetas) - .where( - and( - eq(formMetas.formCode, formCode), - eq(formMetas.projectId, projectId) - ) - ) - .orderBy(desc(formMetas.updatedAt)) - .limit(1); + if (contractItemResult.length === 0) { + console.warn(`[getFormData] No contract item found with ID: ${contractItemId}`); + return { columns: null, data: [], editableFieldsMap: new Map() }; + } - const meta = metaRows[0] ?? null; - if (!meta) { - console.warn(`[getFormData] No form meta found for formCode: ${formCode} and projectId: ${projectId}`); - return { columns: null, data: [], editableFieldsMap: new Map() }; - } - - const entryRows = await db - .select() - .from(formEntries) - .where( - and( - eq(formEntries.formCode, formCode), - eq(formEntries.contractItemId, contractItemId) - ) - ) - .orderBy(desc(formEntries.updatedAt)) - .limit(1); + const projectId = contractItemResult[0].projectId; - const entry = entryRows[0] ?? null; + const metaRows = await db + .select() + .from(formMetas) + .where( + and( + eq(formMetas.formCode, formCode), + eq(formMetas.projectId, projectId) + ) + ) + .orderBy(desc(formMetas.updatedAt)) + .limit(1); - let columns = meta.columns as DataTableColumnJSON[]; - const excludeKeys = ['BF_TAG_NO', 'TAG_TYPE_ID', 'PIC_NO']; - columns = columns.filter(col => !excludeKeys.includes(col.key)); + const meta = metaRows[0] ?? null; + if (!meta) { + console.warn(`[getFormData] No form meta found for formCode: ${formCode} and projectId: ${projectId}`); + return { columns: null, data: [], editableFieldsMap: new Map() }; + } - + const entryRows = await db + .select() + .from(formEntries) + .where( + and( + eq(formEntries.formCode, formCode), + eq(formEntries.contractItemId, contractItemId) + ) + ) + .orderBy(desc(formEntries.updatedAt)) + .limit(1); - columns.forEach((col) => { - if (!col.displayLabel) { - if (col.uom) { - col.displayLabel = `${col.label} (${col.uom})`; - } else { - col.displayLabel = col.label; - } - } - }); + const entry = entryRows[0] ?? null; - columns.push({ - key:"status", - label:"status", - displayLabel:"Status", - type:"STRING" - }) + let columns = meta.columns as DataTableColumnJSON[]; + const excludeKeys = ['BF_TAG_NO', 'TAG_TYPE_ID', 'PIC_NO']; + columns = columns.filter(col => !excludeKeys.includes(col.key)); - let data: Array<Record<string, any>> = []; - if (entry) { - if (Array.isArray(entry.data)) { - data = entry.data; - data.sort((a,b) => { - const statusA = a.status || ''; - const statusB = b.status || ''; - return statusB.localeCompare(statusA) - }) - } else { - console.warn("formEntries data was not an array. Using empty array."); - } + columns.forEach((col) => { + if (!col.displayLabel) { + if (col.uom) { + col.displayLabel = `${col.label} (${col.uom})`; + } else { + col.displayLabel = col.label; } + } + }); - // *** 새로 추가: 편집 가능 필드 정보 계산 *** - const editableFieldsMap = await getEditableFieldsByTag(contractItemId, projectId); + columns.push({ + key: "status", + label: "status", + displayLabel: "Status", + type: "STRING" + }) + + let data: Array<Record<string, any>> = []; + if (entry) { + if (Array.isArray(entry.data)) { + data = entry.data; + + data.sort((a, b) => { + const statusA = a.status || ''; + const statusB = b.status || ''; + return statusB.localeCompare(statusA) + }) + + } else { + console.warn("formEntries data was not an array. Using empty array."); + } + } + + // *** 새로 추가: 편집 가능 필드 정보 계산 *** + const editableFieldsMap = await getEditableFieldsByTag(contractItemId, projectId); + + return { columns, data, editableFieldsMap }; - return { columns, data, editableFieldsMap }; - } catch (cacheError) { console.error(`[getFormData] Cache operation failed:`, cacheError); - + // Fallback logic (기존과 동일하게 editableFieldsMap 추가) try { console.log(`[getFormData] Fallback DB query for (${formCode}, ${contractItemId})`); @@ -384,7 +384,7 @@ export async function getFormData(formCode: string, contractItemId: number) { const entry = entryRows[0] ?? null; let columns = meta.columns as DataTableColumnJSON[]; - const excludeKeys = [ 'BF_TAG_NO', 'TAG_TYPE_ID', 'PIC_NO']; + const excludeKeys = ['BF_TAG_NO', 'TAG_TYPE_ID', 'PIC_NO']; columns = columns.filter(col => !excludeKeys.includes(col.key)); columns.forEach((col) => { @@ -426,7 +426,7 @@ export async function getFormData(formCode: string, contractItemId: number) { export async function findContractItemId(contractId: number, formCode: string): Promise<number | null> { try { console.log(`[findContractItemId] 계약 ID ${contractId}와 formCode ${formCode}에 대한 contractItem 조회 시작`); - + // 1. forms 테이블에서 formCode에 해당하는 모든 레코드 조회 const formsResult = await db .select({ @@ -434,16 +434,16 @@ export async function findContractItemId(contractId: number, formCode: string): }) .from(forms) .where(eq(forms.formCode, formCode)); - + if (formsResult.length === 0) { console.warn(`[findContractItemId] formCode ${formCode}에 해당하는 form을 찾을 수 없습니다.`); return null; } - + // 모든 contractItemId 추출 const contractItemIds = formsResult.map(form => form.contractItemId); console.log(`[findContractItemId] formCode ${formCode}에 해당하는 ${contractItemIds.length}개의 contractItemId 발견`); - + // 2. contractItems 테이블에서 추출한 contractItemId 중에서 // contractId가 일치하는 항목 찾기 const contractItemResult = await db @@ -458,15 +458,15 @@ export async function findContractItemId(contractId: number, formCode: string): ) ) .limit(1); - + if (contractItemResult.length === 0) { console.warn(`[findContractItemId] 계약 ID ${contractId}와 일치하는 contractItemId를 찾을 수 없습니다.`); return null; } - + const contractItemId = contractItemResult[0].id; console.log(`[findContractItemId] 계약 아이템 ID ${contractItemId} 발견`); - + return contractItemId; } catch (error) { console.error(`[findContractItemId] contractItem 조회 중 오류 발생:`, error); @@ -788,7 +788,7 @@ export async function fetchFormMetadata( const rows = await db .select() .from(formMetas) - .where(and(eq(formMetas.formCode, formCode),eq(formMetas.projectId, projectId))) + .where(and(eq(formMetas.formCode, formCode), eq(formMetas.projectId, projectId))) .limit(1); // rows는 배열 @@ -883,8 +883,8 @@ export async function uploadReportTemp( ); } if (file && file.size > 0) { - - const saveResult = await saveFile({file, directory:"vendorFormData",originalName:customFileName}); + + const saveResult = await saveFile({ file, directory: "vendorFormData", originalName: customFileName }); if (!saveResult.success) { return { success: false, error: saveResult.error }; } @@ -897,7 +897,7 @@ export async function uploadReportTemp( contractItemId: packageId, formId: formId, fileName: customFileName, - filePath:saveResult.publicPath!, + filePath: saveResult.publicPath!, }) .returning(); }); @@ -962,7 +962,7 @@ export async function getFormTagTypeMappings(formCode: string, projectId: number eq(tagTypeClassFormMappings.projectId, projectId) ) }); - + return mappings; } catch (error) { console.error("Error fetching form tag type mappings:", error); @@ -984,7 +984,7 @@ export async function getTagTypeByDescription(description: string, projectId: nu eq(tagTypes.projectId, projectId) ) }); - + return tagType; } catch (error) { console.error("Error fetching tag type by description:", error); @@ -1007,7 +1007,7 @@ export async function getSubfieldsByTagTypeForForm(tagTypeCode: string, projectI ), orderBy: tagSubfields.sortOrder }); - + const subfieldsWithOptions = await Promise.all( subfields.map(async (subfield) => { const options = await db.query.tagSubfieldOptions.findMany({ @@ -1016,7 +1016,7 @@ export async function getSubfieldsByTagTypeForForm(tagTypeCode: string, projectI eq(tagSubfieldOptions.projectId, projectId) ) }); - + return { name: subfield.attributesId, label: subfield.attributesDescription, @@ -1027,7 +1027,7 @@ export async function getSubfieldsByTagTypeForForm(tagTypeCode: string, projectI }; }) ); - + return { subFields: subfieldsWithOptions }; } catch (error) { console.error("Error fetching subfields for form:", error); @@ -1043,13 +1043,13 @@ interface SEDPAttribute { NAME: string; VALUE: any; UOM: string; - UOM_ID?: string; + UOM_ID?: string; } interface SEDPDataItem { TAG_NO: string; TAG_DESC: string; - CLS_ID:string; + CLS_ID: string; ATTRIBUTES: SEDPAttribute[]; SCOPE: string; TOOLID: string; @@ -1081,37 +1081,37 @@ async function transformDataToSEDPFormat( 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>(); - + // Cache for packageCode to avoid duplicate DB queries for same tag const packageCodeCache = new Map<string, string>(); - + // Cache for tagClass code to avoid duplicate DB queries for same tag const tagClassCodeCache = new Map<string, string>(); - + // Transform each row const transformedItems = []; - + for (const row of tableData) { // Get packageCode for this specific tag let packageCode = formCode; // fallback to formCode let tagClassCode = ""; // for CLS_ID - + if (row.TAG_NO && contractItemId) { // Check cache first const cacheKey = `${contractItemId}-${row.TAG_NO}`; - + if (packageCodeCache.has(cacheKey)) { packageCode = packageCodeCache.get(cacheKey)!; } else { @@ -1123,7 +1123,7 @@ async function transformDataToSEDPFormat( eq(tags.tagNo, row.TAG_NO) ) }); - + if (tagResult) { // Get tagClass code if tagClassId exists if (tagResult.tagClassId) { @@ -1134,30 +1134,30 @@ async function transformDataToSEDPFormat( const tagClassResult = await db.query.tagClasses.findFirst({ where: eq(tagClasses.id, tagResult.tagClassId) }); - + if (tagClassResult) { tagClassCode = tagClassResult.code; console.log(`Found tagClass code for tag ${row.TAG_NO}: ${tagClassCode}`); } else { console.warn(`No tagClass found for tagClassId: ${tagResult.tagClassId}`); } - + // Cache the tagClass code result tagClassCodeCache.set(cacheKey, tagClassCode); } } - + // Get the contract item const contractItemResult = await db.query.contractItems.findFirst({ where: eq(contractItems.id, tagResult.contractItemId) }); - + if (contractItemResult) { // Get the first item with this itemId const itemResult = await db.query.items.findFirst({ where: eq(items.id, contractItemResult.itemId) }); - + if (itemResult && itemResult.packageCode) { packageCode = itemResult.packageCode; console.log(`Found packageCode for tag ${row.TAG_NO}: ${packageCode}`); @@ -1170,7 +1170,7 @@ async function transformDataToSEDPFormat( } else { console.warn(`No tag found for contractItemId: ${contractItemId}, tagNo: ${row.TAG_NO}, using fallback`); } - + // Cache the result (even if it's the fallback value) packageCodeCache.set(cacheKey, packageCode); } catch (error) { @@ -1179,7 +1179,7 @@ async function transformDataToSEDPFormat( packageCodeCache.set(cacheKey, packageCode); } } - + // Get tagClass code if not already retrieved above if (!tagClassCode && tagClassCodeCache.has(cacheKey)) { tagClassCode = tagClassCodeCache.get(cacheKey)!; @@ -1191,18 +1191,18 @@ async function transformDataToSEDPFormat( eq(tags.tagNo, row.TAG_NO) ) }); - + if (tagResult && tagResult.tagClassId) { const tagClassResult = await db.query.tagClasses.findFirst({ where: eq(tagClasses.id, tagResult.tagClassId) }); - + if (tagClassResult) { tagClassCode = tagClassResult.code; console.log(`Found tagClass code for tag ${row.TAG_NO}: ${tagClassCode}`); } } - + // Cache the tagClass code result tagClassCodeCache.set(cacheKey, tagClassCode); } catch (error) { @@ -1212,7 +1212,7 @@ async function transformDataToSEDPFormat( } } } - + // Create base SEDP item with required fields const sedpItem: SEDPDataItem = { TAG_NO: row.TAG_NO || "", @@ -1235,20 +1235,20 @@ async function transformDataToSEDPFormat( 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 { @@ -1269,7 +1269,7 @@ async function transformDataToSEDPFormat( }) } ); - + if (response.ok) { const uomData = await response.json(); if (uomData && uomData.FACTOR !== undefined && uomData.FACTOR !== null) { @@ -1284,33 +1284,33 @@ async function transformDataToSEDPFormat( 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 || "", - CLS_ID:tagClassCode || "", + CLS_ID: tagClassCode || "", }; - + // 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; } @@ -1343,11 +1343,11 @@ export async function getProjectCodeById(projectId: number): Promise<string> { .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; } @@ -1361,12 +1361,12 @@ export async function sendDataToSEDP( 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`, @@ -1381,12 +1381,12 @@ export async function sendDataToSEDP( 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) { @@ -1408,7 +1408,7 @@ export async function sendFormDataToSEDP( try { // 1. Get project code const projectCode = await getProjectCodeById(projectId); - + // 2. Get class mapping const mappingsResult = await db.query.tagTypeClassFormMappings.findFirst({ where: and( @@ -1416,13 +1416,13 @@ export async function sendFormDataToSEDP( 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( @@ -1430,10 +1430,10 @@ export async function sendFormDataToSEDP( 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 { @@ -1442,7 +1442,7 @@ export async function sendFormDataToSEDP( } else { console.warn(`No mapping found for formCode ${formCode} in project ${projectId}, using default object code`); } - + // 3. Transform data to SEDP format const sedpData = await transformFormDataToSEDP( formData, @@ -1452,10 +1452,10 @@ export async function sendFormDataToSEDP( projectCode, contractItemId // Add contractItemId parameter ); - + // 4. Send to SEDP API const result = await sendDataToSEDP(projectCode, sedpData); - + // 5. SEDP 전송 성공 후 formEntries에 status 업데이트 try { // Get the current formEntries data @@ -1473,7 +1473,7 @@ export async function sendFormDataToSEDP( if (entries && entries.length > 0) { const entry = entries[0]; const dataArray = entry.data as Array<Record<string, any>>; - + if (Array.isArray(dataArray)) { // Extract TAG_NO list from formData const sentTagNumbers = new Set( @@ -1481,7 +1481,7 @@ export async function sendFormDataToSEDP( .map(item => item.TAG_NO) .filter(tagNo => tagNo) // Remove null/undefined values ); - + // Update status for sent tags const updatedDataArray = dataArray.map(item => { if (item.TAG_NO && sentTagNumbers.has(item.TAG_NO)) { @@ -1492,7 +1492,7 @@ export async function sendFormDataToSEDP( } return item; }); - + // Update the database await db .update(formEntries) @@ -1501,7 +1501,7 @@ export async function sendFormDataToSEDP( updatedAt: new Date() }) .where(eq(formEntries.id, entry.id)); - + console.log(`Updated status for ${sentTagNumbers.size} tags to "Sent to S-EDP"`); } } else { @@ -1511,7 +1511,7 @@ export async function sendFormDataToSEDP( // Status 업데이트 실패는 경고로만 처리 (SEDP 전송은 성공했으므로) console.warn("Failed to update status after SEDP send:", statusUpdateError); } - + return { success: true, message: "Data successfully sent to SEDP", @@ -1535,7 +1535,7 @@ export async function deleteFormDataByTags({ formCode: string contractItemId: number tagNos: string[] -}): Promise<{ +}): Promise<{ error?: string success?: boolean deletedCount?: number @@ -1576,7 +1576,7 @@ export async function deleteFormDataByTags({ console.log(`[DELETE ACTION] Current data count: ${currentData.length}`) // 2. 삭제할 항목들 필터링 (formEntries에서) - const updatedData = currentData.filter((item: any) => + const updatedData = currentData.filter((item: any) => !tagNos.includes(item.TAG_NO) ) @@ -1630,7 +1630,7 @@ export async function deleteFormDataByTags({ const cacheKey = `form-data-${formCode}-${contractItemId}` revalidateTag(cacheKey) revalidateTag(`tags-${contractItemId}`) - + // 페이지 재검증 (필요한 경우) console.log(`[DELETE ACTION] Transaction completed successfully`) |
