diff options
| -rw-r--r-- | app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/eng/[formCode]/page.tsx | 2 | ||||
| -rw-r--r-- | components/vendor-data-plant/project-swicher.tsx | 2 | ||||
| -rw-r--r-- | components/vendor-data-plant/sidebar.tsx | 1 | ||||
| -rw-r--r-- | components/vendor-data-plant/vendor-data-container.tsx | 3 | ||||
| -rw-r--r-- | lib/forms-plant/services.ts | 23 | ||||
| -rw-r--r-- | lib/forms-plant/stat.ts | 2 | ||||
| -rw-r--r-- | lib/sedp/get-tags-plant.ts | 24 | ||||
| -rw-r--r-- | lib/sedp/sync-form.ts | 120 | ||||
| -rw-r--r-- | lib/tags-plant/service.ts | 17 | ||||
| -rw-r--r-- | lib/tags-plant/table/add-tag-dialog.tsx | 2 |
10 files changed, 140 insertions, 56 deletions
diff --git a/app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/eng/[formCode]/page.tsx b/app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/eng/[formCode]/page.tsx index 351fbca3..620a407f 100644 --- a/app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/eng/[formCode]/page.tsx +++ b/app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/eng/[formCode]/page.tsx @@ -46,6 +46,8 @@ export default async function EngineeringFormPage({ const { formId } = await getFormId(projectCode, packageCode, formCode, mode); const projectId = await getProjectIdByCode(projectCode) + + console.log(data,"data-eng") // 6) 예외 처리 if (!columns) { diff --git a/components/vendor-data-plant/project-swicher.tsx b/components/vendor-data-plant/project-swicher.tsx index 9b8f9bea..75c0752b 100644 --- a/components/vendor-data-plant/project-swicher.tsx +++ b/components/vendor-data-plant/project-swicher.tsx @@ -55,8 +55,6 @@ export function ProjectSwitcher({ ) - console.log(projects,"projects") - const displayText = selectedPackage ? `${selectedProject?.projectCode} - ${selectedPackage.packageCode}` : selectedProject?.projectCode || "Select Package" diff --git a/components/vendor-data-plant/sidebar.tsx b/components/vendor-data-plant/sidebar.tsx index b746e69d..ca88abb0 100644 --- a/components/vendor-data-plant/sidebar.tsx +++ b/components/vendor-data-plant/sidebar.tsx @@ -46,6 +46,7 @@ export function Sidebar({ const [isLoadingEngineering, setIsLoadingEngineering] = React.useState(false) const [isLoadingIM, setIsLoadingIM] = React.useState(false) + // Engineering 폼 로드 React.useEffect(() => { if (!selectedPackageCode || !projectCode) { diff --git a/components/vendor-data-plant/vendor-data-container.tsx b/components/vendor-data-plant/vendor-data-container.tsx index 7ce831df..bf4c30f1 100644 --- a/components/vendor-data-plant/vendor-data-container.tsx +++ b/components/vendor-data-plant/vendor-data-container.tsx @@ -77,6 +77,7 @@ export function VendorDataContainer({ // 상태 관리 const [selectedProjectId, setSelectedProjectId] = React.useState(projects[0]?.projectId || 0) + const [selectedProjectCode, setSelectedProjectCode] = React.useState(projects[0]?.projectCode || "") const [isCollapsed, setIsCollapsed] = React.useState(defaultCollapsed) const [selectedPackageCode, setSelectedPackageCode] = React.useState<string | null>(null) const [selectedFormCode, setSelectedFormCode] = React.useState<string | null>(null) @@ -94,6 +95,7 @@ export function VendorDataContainer({ const project = projects.find(p => p.projectCode === projectCode) if (project) { setSelectedProjectId(project.projectId) + setSelectedProjectCode(project.projectCode) setSelectedPackageCode(packageCode) } } @@ -207,6 +209,7 @@ export function VendorDataContainer({ <Sidebar isCollapsed={isCollapsed} selectedPackageCode={selectedPackageCode} + projectCode={selectedProjectCode} selectedFormCode={selectedFormCode} currentMode={currentMode} onMasterTagListClick={handleMasterTagListClick} diff --git a/lib/forms-plant/services.ts b/lib/forms-plant/services.ts index 7e1976e6..3f50bd47 100644 --- a/lib/forms-plant/services.ts +++ b/lib/forms-plant/services.ts @@ -173,8 +173,8 @@ export async function getEditableFieldsByTag( // 1. 해당 contractItemId의 모든 태그 조회 const tagList = await db .select({ - tagNo: tags.tagNo, - tagClass: tags.class + tagNo: tagsPlant.tagNo, + tagClass: tagsPlant.class }) .from(tagsPlant) .where( @@ -236,6 +236,8 @@ export async function getEditableFieldsByTag( export async function getFormData(formCode: string, projectCode: string, packageCode:string) { try { + console.log(formCode,projectCode, packageCode) + const project = await db.query.projects.findFirst({ where: eq(projects.code, projectCode), columns: { @@ -544,7 +546,7 @@ export async function syncMissingTags( // Build a dynamic OR clause to match (tagType, class) pairs from the mappings. const orConditions = formMappings.map((m) => - and(eq(tags.tagType, m.tagTypeLabel), eq(tags.class, m.classLabel)) + and(eq(tagsPlant.tagType, m.tagTypeLabel), eq(tagsPlant.class, m.classLabel)) ); // (3) Fetch all matching `tags` for the contractItemId + any of the (tagType, class) pairs. @@ -1357,17 +1359,18 @@ async function transformDataToSEDPFormat( let tagClassCode = ""; // Get tagClass code if TAG_NO exists - if (row.TAG_NO && contractItemId) { - const cacheKey = `${contractItemId}-${row.TAG_NO}`; + if (row.TAG_NO && projectCode) { + const cacheKey = `${projectCode}-${row.TAG_NO}`; if (tagClassCodeCache.has(cacheKey)) { tagClassCode = tagClassCodeCache.get(cacheKey)!; } else { try { - const tagResult = await db.query.tags.findFirst({ + const tagResult = await db.query.tagsPlant.findFirst({ where: and( - eq(tags.contractItemId, contractItemId), - eq(tags.tagNo, row.TAG_NO) + eq(tagsPlant.projectCode, projectCode), + eq(tagsPlant.packageCode, packageCode), + eq(tagsPlant.tagNo, row.TAG_NO) ) }); @@ -1997,9 +2000,9 @@ export async function deleteFormDataByTags({ } // 4. 캐시 무효화 - const cacheKey = `form-data-${formCode}-${contractItemId}` + const cacheKey = `form-data-${formCode}-${projectCode}` revalidateTag(cacheKey) - revalidateTag(`tags-${contractItemId}`) + revalidateTag(`tags-${projectCode}`) // 페이지 재검증 (필요한 경우) diff --git a/lib/forms-plant/stat.ts b/lib/forms-plant/stat.ts index f734e782..ed497211 100644 --- a/lib/forms-plant/stat.ts +++ b/lib/forms-plant/stat.ts @@ -371,7 +371,7 @@ export async function getFormStatusByVendor(projectId: number, projectCode: stri return vendorStatusList } catch (error) { - console.error('Error getting vendor form status:', error) + console.log('Error getting vendor form status:', error) throw new Error('벤더별 Form 입력 현황 조회 중 오류가 발생했습니다.') } }
\ No newline at end of file diff --git a/lib/sedp/get-tags-plant.ts b/lib/sedp/get-tags-plant.ts index d1957db4..be0e398b 100644 --- a/lib/sedp/get-tags-plant.ts +++ b/lib/sedp/get-tags-plant.ts @@ -210,8 +210,8 @@ export async function importTagsFromSEDP( const form = await db.query.formsPlant.findFirst({ where: and( eq(formsPlant.projectCode, projectCode), - eq(formsPlant.packageCode, packageCode), - eq(formsPlant.formCode, formCode) + eq(formsPlant.formCode, formCode), + eq(formsPlant.packageCode, packageCode) ) }); @@ -239,8 +239,20 @@ export async function importTagsFromSEDP( } } - const insertResult = await db.insert(formsPlant).values(insertValues).returning({ id: formsPlant.id }); - + const insertResult = await db.insert(formsPlant) + .values(insertValues) + .onConflictDoUpdate({ + target: [formsPlant.projectCode, formsPlant.formCode], + set: { + packageCode: insertValues.packageCode, + formName: insertValues.formName, + eng: insertValues.eng ?? false, + im: insertValues.im ?? false, + updatedAt: new Date() + } + }) + .returning({ id: formsPlant.id }); + if (insertResult.length === 0) { allErrors.push(`Failed to create form record for formCode ${formCode}`); continue; // 다음 매핑으로 진행 @@ -318,6 +330,8 @@ export async function importTagsFromSEDP( status: string; [key: string]: any; }> = []; + const SEDP_API_BASE_URL = process.env.SEDP_API_BASE_URL || 'http://sedpwebapi.ship.samsung.co.kr/api'; + const apiKey = await getSEDPToken(); const registerResponse = await fetch( `${SEDP_API_BASE_URL}/Register/GetByID`, @@ -560,7 +574,7 @@ export async function importTagsFromSEDP( } // 캐시 무효화 - revalidateTag(`form-data-${formCode}-${packageId}`); + // 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}`); diff --git a/lib/sedp/sync-form.ts b/lib/sedp/sync-form.ts index a6d473ad..4a3c847e 100644 --- a/lib/sedp/sync-form.ts +++ b/lib/sedp/sync-form.ts @@ -1,6 +1,6 @@ // src/lib/cron/syncTagFormMappings.ts import db from "@/db/db"; -import { projects, tagTypes, tagClasses, tagTypeClassFormMappings, formMetas, forms, contractItems, items, contracts, templateItems } from '@/db/schema'; +import { projects, tagTypes, tagClasses, tagTypeClassFormMappings, formMetas, forms, formsPlant, contractItems, items, contracts, templateItems } from '@/db/schema'; import { eq, and, inArray, ilike } from 'drizzle-orm'; import { getSEDPToken } from "./sedp-token"; @@ -38,6 +38,18 @@ interface FormRecord { updatedAt: Date; } +// FormPlantRecord 인터페이스 추가 (plant용) +interface FormPlantRecord { + projectCode: string; + packageCode: string; + formCode: string; + formName: string; + eng: boolean; + im: boolean; + createdAt: Date; + updatedAt: Date; +} + interface TemplateItem { TMPL_ID: string; NAME: string; @@ -920,6 +932,7 @@ async function getContractItemsByItemCodes(itemCodes: string[], projectId: numbe export async function saveFormMappingsAndMetas( projectId: number, projectCode: string, + projectType: string | null, // 프로젝트 타입 파라미터 추가 registers: Register[], newRegisters: newRegister[] ): Promise<number> { @@ -936,30 +949,35 @@ export async function saveFormMappingsAndMetas( const registerMap = new Map(registers.map(r => [r.TYPE_ID, r])); const attributeMap = await getAttributes(projectCode); - // getCodeLists 호출 제거 - // const codeListMap = await getCodeLists(projectCode); const uomMap = await getUOMs(projectCode); const defaultAttributes = await getDefaulTAttributes(); - // 성능 향상을 위한 코드 리스트 캐시 추가 (선택사항) const codeListCache = new Map<string, CodeList | null>(); /* ------------------------------------------------------------------ */ - /* 2. Contract‑item look‑up (SCOPES) */ + /* 2. Contract‑item look‑up (SCOPES) - ship 타입일 때만 필요 */ /* ------------------------------------------------------------------ */ - const uniqueItemCodes = [...new Set( - newRegisters - .filter(nr => nr.SCOPES && nr.SCOPES.length > 0) - .flatMap(nr => nr.SCOPES as string[]) - )]; - const itemCodeToContractItemIds = await getContractItemsByItemCodes(uniqueItemCodes, projectId); + const isShipType = projectType === 'ship'; + const isPlantType = projectType === 'plant'; + + let itemCodeToContractItemIds = new Map<string, number[]>(); + + if (isShipType) { + const uniqueItemCodes = [...new Set( + newRegisters + .filter(nr => nr.SCOPES && nr.SCOPES.length > 0) + .flatMap(nr => nr.SCOPES as string[]) + )]; + itemCodeToContractItemIds = await getContractItemsByItemCodes(uniqueItemCodes, projectId); + } /* ------------------------------------------------------------------ */ /* 3. Buffers for bulk insert */ /* ------------------------------------------------------------------ */ const mappingsToSave: TagTypeClassFormMapping[] = []; const formMetasToSave: FormMeta[] = []; - const formsToSave: FormRecord[] = []; + const formsToSave: FormRecord[] = []; // ship용 + const formsPlantToSave: FormPlantRecord[] = []; // plant용 const contractItemIdsWithForms = new Set<number>(); const templateDataByFormCode: Map<string, TemplateItem[]> = new Map(); @@ -1012,20 +1030,17 @@ export async function saveFormMappingsAndMetas( ...(uomSymbol ? { uom: uomSymbol, uomId } : {}) }; - // 수정된 부분: getCodeListById 사용 if (!defaultAttributes.includes(attId) && (attribute.VAL_TYPE === "LIST" || attribute.VAL_TYPE === "DYNAMICLIST") && attribute.CL_ID) { - // 캐시 확인 let cl = codeListCache.get(attribute.CL_ID); - // 캐시에 없으면 API 호출 if (!codeListCache.has(attribute.CL_ID)) { try { cl = await getCodeListById(projectCode, attribute.CL_ID); - codeListCache.set(attribute.CL_ID, cl); // 캐시에 저장 + codeListCache.set(attribute.CL_ID, cl); } catch (error) { console.warn(`코드 리스트 ${attribute.CL_ID} 가져오기 실패:`, error); cl = null; - codeListCache.set(attribute.CL_ID, null); // 실패도 캐시에 저장 + codeListCache.set(attribute.CL_ID, null); } } @@ -1066,24 +1081,42 @@ export async function saveFormMappingsAndMetas( }); }); - /* ---------- 4‑d. contractItem ↔ form -------------------------- */ + /* ---------- 4‑d. contractItem ↔ form (프로젝트 타입별 분기) ------ */ if (newReg.SCOPES && newReg.SCOPES.length > 0) { - for (const itemCode of newReg.SCOPES) { - const contractItemIds = itemCodeToContractItemIds.get(itemCode); - if (contractItemIds && contractItemIds.length > 0) { - contractItemIds.forEach(cId => { - contractItemIdsWithForms.add(cId); - formsToSave.push({ - contractItemId: cId, - formCode, - formName: legacy?.DESC || formCode, - eng: true, - createdAt: new Date(), - updatedAt: new Date() + for (const packageCode of newReg.SCOPES) { + if (isShipType) { + // Ship 타입: forms 테이블에 저장 (contractItemId 기반) + const contractItemIds = itemCodeToContractItemIds.get(packageCode); + if (contractItemIds && contractItemIds.length > 0) { + contractItemIds.forEach(cId => { + contractItemIdsWithForms.add(cId); + formsToSave.push({ + contractItemId: cId, + formCode, + formName: legacy?.DESC || formCode, + eng: true, + im: false, + createdAt: new Date(), + updatedAt: new Date() + }); }); + } else { + console.warn(`[Ship] packageCode ${packageCode} 의 contractItemId 없음`); + } + } else if (isPlantType) { + // Plant 타입: formsPlant 테이블에 저장 (projectCode, packageCode 기반) + formsPlantToSave.push({ + projectCode, + packageCode, + formCode, + formName: legacy?.DESC || formCode, + eng: true, + im: false, + createdAt: new Date(), + updatedAt: new Date() }); } else { - console.warn(`itemCode ${itemCode} 의 contractItemId 없음`); + console.warn(`알 수 없는 프로젝트 타입: ${projectType}`); } } } @@ -1098,7 +1131,14 @@ export async function saveFormMappingsAndMetas( if (old.length) await tx.delete(templateItems).where(inArray(templateItems.formMappingId, old.map(o => o.id))); await tx.delete(tagTypeClassFormMappings).where(eq(tagTypeClassFormMappings.projectId, projectId)); await tx.delete(formMetas).where(eq(formMetas.projectId, projectId)); - if (contractItemIdsWithForms.size) await tx.delete(forms).where(inArray(forms.contractItemId, [...contractItemIdsWithForms])); + + // 프로젝트 타입별로 기존 데이터 삭제 및 새 데이터 저장 + if (isShipType && contractItemIdsWithForms.size) { + await tx.delete(forms).where(inArray(forms.contractItemId, [...contractItemIdsWithForms])); + } + if (isPlantType) { + await tx.delete(formsPlant).where(eq(formsPlant.projectCode, projectCode)); + } const savedMappings = mappingsToSave.length ? await tx.insert(tagTypeClassFormMappings).values(mappingsToSave).onConflictDoNothing().returning({ id: tagTypeClassFormMappings.id, formCode: tagTypeClassFormMappings.formCode }) : []; totalSaved += mappingsToSave.length; @@ -1110,7 +1150,18 @@ export async function saveFormMappingsAndMetas( } if (formMetasToSave.length) { await tx.insert(formMetas).values(formMetasToSave).onConflictDoNothing(); totalSaved += formMetasToSave.length; } - if (formsToSave.length) { await tx.insert(forms).values(formsToSave).onConflictDoNothing(); totalSaved += formsToSave.length; } + + // Ship 타입: forms 테이블에 저장 + if (isShipType && formsToSave.length) { + await tx.insert(forms).values(formsToSave).onConflictDoNothing(); + totalSaved += formsToSave.length; + } + + // Plant 타입: formsPlant 테이블에 저장 + if (isPlantType && formsPlantToSave.length) { + await tx.insert(formsPlant).values(formsPlantToSave).onConflictDoNothing(); + totalSaved += formsPlantToSave.length; + } }); return totalSaved; @@ -1119,7 +1170,6 @@ export async function saveFormMappingsAndMetas( throw err; } } - // 메인 동기화 함수 export async function syncTagFormMappings() { try { @@ -1137,7 +1187,7 @@ export async function syncTagFormMappings() { const newRegisters = await getNewRegisters(project.code); // 데이터베이스에 저장 - const count = await saveFormMappingsAndMetas(project.id, project.code, registers, newRegisters); + const count = await saveFormMappingsAndMetas(project.id, project.code, project.type || null, registers, newRegisters); return { project: project.code, success: true, diff --git a/lib/tags-plant/service.ts b/lib/tags-plant/service.ts index 02bd33be..9e9d9ebf 100644 --- a/lib/tags-plant/service.ts +++ b/lib/tags-plant/service.ts @@ -15,6 +15,7 @@ import { contractItems, contracts } from "@/db/schema/contract"; import { getCodeListsByID } from "../sedp/sync-object-class"; import { projects, vendors } from "@/db/schema"; import { randomBytes } from 'crypto'; +import { getSEDPToken } from "../sedp/sedp-token"; // 폼 결과를 위한 인터페이스 정의 interface CreatedOrExistingForm { @@ -105,12 +106,15 @@ export async function createTag( projectCode: string, packageCode: string, ) { + + if (!projectCode) { return { error: "No selectedPackageId provided" } } // Validate formData const validated = createTagSchema.safeParse(formData) + if (!validated.success) { return { error: validated.error.flatten().formErrors.join(", ") } } @@ -121,6 +125,7 @@ export async function createTag( try { // 하나의 트랜잭션에서 모든 작업 수행 return await db.transaction(async (tx) => { + const project = await db.query.projects.findFirst({ where: eq(projects.code, projectCode), columns: { @@ -130,6 +135,7 @@ export async function createTag( const projectId = project.id + // 2) 해당 계약 내에서 같은 tagNo를 가진 태그가 있는지 확인 const duplicateCheck = await tx .select({ count: sql<number>`count(*)` }) @@ -153,10 +159,12 @@ export async function createTag( projectId, // projectId 전달 validated.data.class ) + // ep가 "IMEP"인 것만 필터링 const formMappings = allFormMappings?.filter(mapping => mapping.ep === "IMEP") || [] + console.log(formMappings,"formMappings") // 폼 매핑이 없으면 로그만 남기고 진행 if (!formMappings || formMappings.length === 0) { @@ -244,7 +252,6 @@ export async function createTag( .values(insertValues) .returning({ id: formsPlant.id, formCode: formsPlant.formCode, formName: formsPlant.formName }) - console.log("insertResult:", insertResult) formId = insertResult[0].id createdOrExistingForms.push({ id: formId, @@ -282,7 +289,7 @@ export async function createTag( for (const form of createdOrExistingForms) { try { // 기존 formEntry 가져오기 - const existingEntry = await tx.query.formEntries.findFirst({ + const existingEntry = await tx.query.formEntriesPlant.findFirst({ where: and( eq(formEntriesPlant.formCode, form.formCode), eq(formEntriesPlant.projectCode, projectCode), @@ -348,6 +355,7 @@ export async function createTag( console.log(`[CREATE TAG] Created new formEntry with tag ${validated.data.tagNo} and tagIdx ${generatedTagIdx} for form ${form.formCode}`); } } catch (formEntryError) { + console.log(formEntryError) console.error(`[CREATE TAG] Error updating formEntry for form ${form.formCode}:`, formEntryError); // 개별 formEntry 에러는 로그만 남기고 전체 트랜잭션은 계속 진행 } @@ -1612,6 +1620,9 @@ export async function getEngineeringForms( projectCode: string, packageCode: string ): Promise<FormInfo[]> { + console.log("eng 폼 조회 시작") + console.log(projectCode,packageCode) + try { // 1. DB에서 eng=true인 폼 조회 const existingForms = await db @@ -1628,6 +1639,8 @@ export async function getEngineeringForms( ) ) + console.log("existingForms",existingForms) + // DB에 데이터가 있으면 반환 if (existingForms.length > 0) { return existingForms diff --git a/lib/tags-plant/table/add-tag-dialog.tsx b/lib/tags-plant/table/add-tag-dialog.tsx index 41731f63..de5d2bf8 100644 --- a/lib/tags-plant/table/add-tag-dialog.tsx +++ b/lib/tags-plant/table/add-tag-dialog.tsx @@ -345,7 +345,7 @@ export function AddTagDialog({ projectCode, packageCode }: AddTagDialogProps) { try { const res = await createTag(tagData, projectCode, packageCode); if ("error" in res) { - console.log(res.error) + console.log(res) failedTags.push({ tag: row.tagNo, error: res.error }); } else { successfulTags.push(row.tagNo); |
