'use server' import { and, asc, desc, ilike, or, sql, eq } from 'drizzle-orm'; import db from '@/db/db'; import { filterColumns } from "@/lib/filter-columns"; import { MATERIAL_MASTER_PART_MATL, MATERIAL_MASTER_PART_MATL_CHARASGN, MATERIAL_MASTER_PART_MATL_CLASSASGN, MATERIAL_MASTER_PART_MATL_DESC, MATERIAL_MASTER_PART_MATL_UNIT } from "@/db/schema/MDG/mdg"; // 자재마스터 테이블의 컬럼 타입 정의 type MaterialColumn = keyof typeof MATERIAL_MASTER_PART_MATL.$inferSelect; export interface GetMaterialsInput { page: number; perPage: number; search?: string; sort: Array<{ id: MaterialColumn; desc: boolean; }>; filters?: any[]; joinOperator: 'and' | 'or'; } /** * 자재마스터 목록을 조회합니다. * 필수 컬럼: MATKL(자재그룹), MATNR(자재코드), ZZNAME(자재명), ZZPJT(프로젝트), createdAt, updatedAt */ export async function getMaterials(input: GetMaterialsInput) { const safePerPage = Math.min(input.perPage, 100); try { const offset = (input.page - 1) * safePerPage; // 고급 필터링 const advancedWhere = filterColumns({ table: MATERIAL_MASTER_PART_MATL, filters: (input.filters || []) as any, joinOperator: input.joinOperator, }); // 전역 검색 - 주요 컬럼들에 대해 검색 let globalWhere; if (input.search) { const s = `%${input.search}%`; globalWhere = or( ilike(MATERIAL_MASTER_PART_MATL.MATKL, s), // 자재그룹코드 ilike(MATERIAL_MASTER_PART_MATL.MATNR, s), // 자재코드 ilike(MATERIAL_MASTER_PART_MATL.ZZNAME, s), // 자재명 ilike(MATERIAL_MASTER_PART_MATL.ZZPJT, s), // 프로젝트 ); } const finalWhere = and(advancedWhere, globalWhere); // 정렬 처리 - 타입 안전하게 처리 const orderBy = input.sort.length > 0 ? input.sort.map((item) => { const column = MATERIAL_MASTER_PART_MATL[item.id]; return item.desc ? desc(column) : asc(column); }) : [desc(MATERIAL_MASTER_PART_MATL.createdAt)]; // 데이터 조회 - 필요한 컬럼만 선택 const { data, total } = await db.transaction(async (tx) => { const data = await tx .select({ id: MATERIAL_MASTER_PART_MATL.id, MATKL: MATERIAL_MASTER_PART_MATL.MATKL, // 자재그룹(=자재그룹코드) MATNR: MATERIAL_MASTER_PART_MATL.MATNR, // 자재코드(=자재번호) ZZNAME: MATERIAL_MASTER_PART_MATL.ZZNAME, // 자재명(=자재그룹명) ZZPJT: MATERIAL_MASTER_PART_MATL.ZZPJT, // 프로젝트 createdAt: MATERIAL_MASTER_PART_MATL.createdAt, updatedAt: MATERIAL_MASTER_PART_MATL.updatedAt, }) .from(MATERIAL_MASTER_PART_MATL) .where(finalWhere) .orderBy(...orderBy) .offset(offset) .limit(safePerPage); const totalResult = await tx .select({ count: sql`count(*)` }) .from(MATERIAL_MASTER_PART_MATL) .where(finalWhere); const total = Number(totalResult[0]?.count) || 0; return { data, total }; }); const pageCount = Math.ceil(total / safePerPage); return { data, pageCount }; } catch (err) { console.error('Error in getMaterials:', err); return { data: [], pageCount: 0 }; } } /** * 무한 스크롤을 위한 자재마스터 조회 (페이지네이션 없음) */ export interface GetMaterialsInfiniteInput extends Omit { limit?: number; // 무한 스크롤용 추가 옵션 } export async function getMaterialsInfinite(input: GetMaterialsInfiniteInput) { try { // 고급 필터링 const advancedWhere = filterColumns({ table: MATERIAL_MASTER_PART_MATL, filters: (input.filters || []) as any, joinOperator: input.joinOperator || "and", }); // 전역 검색 let globalWhere; if (input.search) { const s = `%${input.search}%`; globalWhere = or( ilike(MATERIAL_MASTER_PART_MATL.MATKL, s), ilike(MATERIAL_MASTER_PART_MATL.MATNR, s), ilike(MATERIAL_MASTER_PART_MATL.ZZNAME, s), ilike(MATERIAL_MASTER_PART_MATL.ZZPJT, s), ); } const finalWhere = and(advancedWhere, globalWhere); // 정렬 처리 - 타입 안전하게 처리 const orderBy = input.sort.length > 0 ? input.sort.map((item) => { const column = MATERIAL_MASTER_PART_MATL[item.id]; return item.desc ? desc(column) : asc(column); }) : [desc(MATERIAL_MASTER_PART_MATL.createdAt)]; // 전체 데이터 조회 (클라이언트에서 가상화 처리) const data = await db .select({ id: MATERIAL_MASTER_PART_MATL.id, MATKL: MATERIAL_MASTER_PART_MATL.MATKL, MATNR: MATERIAL_MASTER_PART_MATL.MATNR, ZZNAME: MATERIAL_MASTER_PART_MATL.ZZNAME, ZZPJT: MATERIAL_MASTER_PART_MATL.ZZPJT, createdAt: MATERIAL_MASTER_PART_MATL.createdAt, updatedAt: MATERIAL_MASTER_PART_MATL.updatedAt, }) .from(MATERIAL_MASTER_PART_MATL) .where(finalWhere) .orderBy(...orderBy); return { data }; } catch (err) { console.error('Error in getMaterialsInfinite:', err); return { data: [] }; } } /** * 자재마스터 상세 정보를 조회합니다 (모든 관련 테이블 포함) */ export async function getMaterialDetail(matnr: string) { try { // 메인 자재 정보 조회 const material = await db .select() .from(MATERIAL_MASTER_PART_MATL) .where(eq(MATERIAL_MASTER_PART_MATL.MATNR, matnr)) .limit(1); if (material.length === 0) { return null; } // 관련 테이블들 조회 const [ characteristics, classifications, descriptions, units ] = await Promise.all([ // CHARASGN - 특성 할당 db .select() .from(MATERIAL_MASTER_PART_MATL_CHARASGN) .where(eq(MATERIAL_MASTER_PART_MATL_CHARASGN.MATNR, matnr)), // CLASSASGN - 클래스 할당 db .select() .from(MATERIAL_MASTER_PART_MATL_CLASSASGN) .where(eq(MATERIAL_MASTER_PART_MATL_CLASSASGN.MATNR, matnr)), // DESC - 설명 db .select() .from(MATERIAL_MASTER_PART_MATL_DESC) .where(eq(MATERIAL_MASTER_PART_MATL_DESC.MATNR, matnr)), // UNIT - 단위 db .select() .from(MATERIAL_MASTER_PART_MATL_UNIT) .where(eq(MATERIAL_MASTER_PART_MATL_UNIT.MATNR, matnr)) ]); return { material: material[0], characteristics, classifications, descriptions, units }; } catch (err) { console.error('Error in getMaterialDetail:', err); return null; } }