diff options
| author | joonhoekim <26rote@gmail.com> | 2025-09-09 10:40:11 +0000 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-09-09 10:40:11 +0000 |
| commit | caa01b321311de3983fb8bcf65bb20a6c047cf57 (patch) | |
| tree | a01fb9078aaa044f57d8cdf59a06bf18a6e3e7df /lib/material-groups/sync-service.ts | |
| parent | 86b1fd1cc801f45642f84d24c0b5c84368454ff0 (diff) | |
(김준회) 자재그룹코드 및 자재그룹명에 대해 별도 테이블 생성, 동기화 로직 작성(일회성 사용이며 수신시점에는 자동저장하므로 추후 사용 불필요), 자재그룹 선택기를 변경사항에 맞춰 업데이트, 자재그룹명은 MAKTX 로 김학의 프로 답변에 따라 변경
Diffstat (limited to 'lib/material-groups/sync-service.ts')
| -rw-r--r-- | lib/material-groups/sync-service.ts | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/lib/material-groups/sync-service.ts b/lib/material-groups/sync-service.ts new file mode 100644 index 00000000..eda5dba5 --- /dev/null +++ b/lib/material-groups/sync-service.ts @@ -0,0 +1,200 @@ +"use server"; + +import { sql } from "drizzle-orm"; +import db from "@/db/db"; +import { + MATERIAL_MASTER_PART_MATL, + MATERIAL_MASTER_PART_MATL_DESC, + MATERIAL_GROUP_MASTER +} from "@/db/schema/MDG/mdg"; + +export interface SyncResult { + success: boolean; + message: string; + data?: { + totalProcessed: number; + newRecords: number; + updatedRecords: number; + skippedRecords: number; + }; +} + +/** + * 기존 MDG 데이터로부터 MATERIAL_GROUP_MASTER 테이블을 동기화하는 함수 + * + * 로직: + * 1. MATERIAL_MASTER_PART_MATL에서 MATKL(자재그룹코드)가 있는 레코드 조회 + * 2. 각 MATKL에 대해 MATERIAL_MASTER_PART_MATL_DESC에서 SPRAS='E'인 MAKTX 찾기 + * 3. MATERIAL_GROUP_MASTER에 UPSERT (MATKL 기준으로 중복 제거) + */ +export async function syncMaterialGroupMaster(): Promise<SyncResult> { + try { + console.log("📊 자재그룹 마스터 동기화 시작..."); + + const result = await db.transaction(async (tx) => { + // 1단계: 기존 MDG 테이블에서 MATKL별 영어 설명 조회 + const materialGroupData = await tx + .select({ + MATKL: MATERIAL_MASTER_PART_MATL.MATKL, + MAKTX: sql<string>` + ( + SELECT ${MATERIAL_MASTER_PART_MATL_DESC.MAKTX} + FROM ${MATERIAL_MASTER_PART_MATL_DESC} + WHERE ${MATERIAL_MASTER_PART_MATL_DESC.MATNR} = ${MATERIAL_MASTER_PART_MATL.MATNR} + AND ${MATERIAL_MASTER_PART_MATL_DESC.SPRAS} = 'E' + AND ${MATERIAL_MASTER_PART_MATL_DESC.MAKTX} IS NOT NULL + AND ${MATERIAL_MASTER_PART_MATL_DESC.MAKTX} != '' + LIMIT 1 + ) + `.as("MAKTX") + }) + .from(MATERIAL_MASTER_PART_MATL) + .where(sql` + ${MATERIAL_MASTER_PART_MATL.MATKL} IS NOT NULL + AND ${MATERIAL_MASTER_PART_MATL.MATKL} != '' + `) + .groupBy(MATERIAL_MASTER_PART_MATL.MATKL); // MATKL별 중복 제거 + + console.log(`🔍 발견된 고유 자재그룹코드: ${materialGroupData.length}개`); + + // 2단계: MAKTX가 있는 레코드만 필터링 + const validData = materialGroupData.filter(item => + item.MAKTX && item.MAKTX.trim() !== '' + ); + + console.log(`✅ 유효한 자재그룹 데이터: ${validData.length}개`); + + if (validData.length === 0) { + return { + totalProcessed: 0, + newRecords: 0, + updatedRecords: 0, + skippedRecords: materialGroupData.length + }; + } + + // 3단계: 기존 MATERIAL_GROUP_MASTER의 모든 MATKL 조회 + const existingRecords = await tx + .select({ MATKL: MATERIAL_GROUP_MASTER.MATKL }) + .from(MATERIAL_GROUP_MASTER); + + const existingMatkls = new Set(existingRecords.map(r => r.MATKL)); + + // 4단계: 신규/업데이트 데이터 분류 + const newRecords = validData.filter(item => !existingMatkls.has(item.MATKL)); + const updateRecords = validData.filter(item => existingMatkls.has(item.MATKL)); + + let insertedCount = 0; + let updatedCount = 0; + + // 5단계: 신규 레코드 삽입 (배치 처리) + if (newRecords.length > 0) { + await tx + .insert(MATERIAL_GROUP_MASTER) + .values( + newRecords.map(item => ({ + MATKL: item.MATKL, + MAKTX: item.MAKTX + })) + ); + insertedCount = newRecords.length; + console.log(`➕ 신규 삽입: ${insertedCount}개`); + } + + // 6단계: 기존 레코드 업데이트 (배치 처리) + if (updateRecords.length > 0) { + // PostgreSQL의 UPDATE ... FROM 구문 사용 + await tx.execute(sql` + UPDATE ${MATERIAL_GROUP_MASTER} + SET + ${MATERIAL_GROUP_MASTER.MAKTX} = temp_data.maktx, + ${MATERIAL_GROUP_MASTER.updatedAt} = NOW() + FROM ( + VALUES ${sql.join( + updateRecords.map(item => + sql`(${item.MATKL}, ${item.MAKTX})` + ), + sql`, ` + )} + ) AS temp_data(matkl, maktx) + WHERE ${MATERIAL_GROUP_MASTER.MATKL} = temp_data.matkl + `); + updatedCount = updateRecords.length; + console.log(`🔄 업데이트: ${updatedCount}개`); + } + + return { + totalProcessed: validData.length, + newRecords: insertedCount, + updatedRecords: updatedCount, + skippedRecords: materialGroupData.length - validData.length + }; + }); + + console.log("✅ 자재그룹 마스터 동기화 완료"); + + return { + success: true, + message: `동기화 완료: 신규 ${result.newRecords}개, 업데이트 ${result.updatedRecords}개, 건너뜀 ${result.skippedRecords}개`, + data: result + }; + + } catch (error) { + console.error("❌ 자재그룹 마스터 동기화 오류:", error); + + return { + success: false, + message: `동기화 실패: ${error instanceof Error ? error.message : '알 수 없는 오류'}` + }; + } +} + +/** + * 동기화 상태 확인 함수 + */ +export async function getMaterialGroupSyncStatus(): Promise<{ + success: boolean; + data?: { + totalMDGRecords: number; + totalMasterRecords: number; + lastSyncDate?: string; + }; +}> { + try { + const [mdgCount, masterCount] = await Promise.all([ + // MDG에서 유효한 MATKL 개수 + db + .select({ count: sql<number>`count(distinct ${MATERIAL_MASTER_PART_MATL.MATKL})` }) + .from(MATERIAL_MASTER_PART_MATL) + .where(sql` + ${MATERIAL_MASTER_PART_MATL.MATKL} IS NOT NULL + AND ${MATERIAL_MASTER_PART_MATL.MATKL} != '' + `), + + // MATERIAL_GROUP_MASTER 개수 + db + .select({ count: sql<number>`count(*)` }) + .from(MATERIAL_GROUP_MASTER) + ]); + + // 최근 업데이트 시간 조회 + const lastUpdated = await db + .select({ + lastUpdate: sql<string>`max(${MATERIAL_GROUP_MASTER.updatedAt})` + }) + .from(MATERIAL_GROUP_MASTER); + + return { + success: true, + data: { + totalMDGRecords: mdgCount[0]?.count || 0, + totalMasterRecords: masterCount[0]?.count || 0, + lastSyncDate: lastUpdated[0]?.lastUpdate || undefined + } + }; + + } catch (error) { + console.error("동기화 상태 확인 오류:", error); + return { success: false }; + } +} |
