"use server" import db from '@/db/db' import { vendors } from '@/db/schema/vendors' import { eq, or, ilike, and, asc, desc } from 'drizzle-orm' // 벤더 타입 정의 export interface VendorSearchItem { id: number vendorName: string vendorCode: string | null taxId: string | null // 사업자번호 status: string displayText: string // vendorName + vendorCode로 구성된 표시용 텍스트 country?: string | null // 국가 정보 (선택적) } // 벤더 검색 옵션 export interface VendorSearchOptions { searchTerm?: string statusFilter?: string // 특정 상태로 필터링 limit?: number offset?: number sortBy?: 'vendorName' | 'vendorCode' | 'status' sortOrder?: 'asc' | 'desc' includeCountry?: boolean // 국가 정보 포함 여부 } // 페이지네이션 정보 export interface VendorPagination { page: number perPage: number total: number pageCount: number hasNextPage: boolean hasPrevPage: boolean } /** * 벤더 검색 (페이지네이션 지원) * 벤더명, 벤더코드로 검색 가능 */ export async function searchVendorsForSelector( searchTerm: string = "", page: number = 1, perPage: number = 10, options: Omit = {} ): Promise<{ success: boolean data: VendorSearchItem[] pagination: VendorPagination error?: string }> { try { const { statusFilter, sortBy = 'vendorName', sortOrder = 'asc', includeCountry = false } = options const offset = (page - 1) * perPage // WHERE 조건 구성 let whereClause // 검색어 조건 const searchCondition = searchTerm && searchTerm.trim() ? or( ilike(vendors.vendorName, `%${searchTerm.trim()}%`), ilike(vendors.vendorCode, `%${searchTerm.trim()}%`) ) : undefined // 상태 필터 조건 - 타입 안전하게 처리 const statusCondition = statusFilter ? eq(vendors.status, statusFilter as string) : undefined // 조건들을 결합 if (searchCondition && statusCondition) { whereClause = and(searchCondition, statusCondition) } else if (searchCondition) { whereClause = searchCondition } else if (statusCondition) { whereClause = statusCondition } // 정렬 옵션 const orderBy = sortOrder === 'desc' ? desc(vendors[sortBy]) : asc(vendors[sortBy]) // 전체 개수 조회 let totalCountQuery = db .select({ count: vendors.id }) .from(vendors) if (whereClause) { totalCountQuery = totalCountQuery.where(whereClause) } const totalCountResult = await totalCountQuery const total = totalCountResult.length // 데이터 조회 - includeCountry에 따라 필드 선택 const selectFields = includeCountry ? { id: vendors.id, vendorName: vendors.vendorName, vendorCode: vendors.vendorCode, taxId: vendors.taxId, status: vendors.status, country: vendors.country, } : { id: vendors.id, vendorName: vendors.vendorName, vendorCode: vendors.vendorCode, taxId: vendors.taxId, status: vendors.status, } let dataQuery = db .select(selectFields) .from(vendors) if (whereClause) { dataQuery = dataQuery.where(whereClause) } const result = await dataQuery .orderBy(orderBy) .limit(perPage) .offset(offset) // displayText 추가 const vendorItems: VendorSearchItem[] = result.map(vendor => ({ ...vendor, displayText: vendor.vendorCode ? `${vendor.vendorName} (${vendor.vendorCode})` : vendor.vendorName, ...(includeCountry && { country: (vendor as any).country }) })) // 페이지네이션 정보 계산 const pageCount = Math.ceil(total / perPage) const pagination: VendorPagination = { page, perPage, total, pageCount, hasNextPage: page < pageCount, hasPrevPage: page > 1, } return { success: true, data: vendorItems, pagination } } catch (error) { console.error('Error searching vendors:', error) return { success: false, data: [], pagination: { page: 1, perPage: 10, total: 0, pageCount: 0, hasNextPage: false, hasPrevPage: false, }, error: '벤더 검색 중 오류가 발생했습니다.' } } } /** * 모든 벤더 조회 (필터링 없음) */ export async function getAllVendors(): Promise<{ success: boolean data: VendorSearchItem[] error?: string }> { try { const result = await db .select({ id: vendors.id, vendorName: vendors.vendorName, vendorCode: vendors.vendorCode, taxId: vendors.taxId, status: vendors.status, }) .from(vendors) .orderBy(asc(vendors.vendorName)) const vendorItems: VendorSearchItem[] = result.map(vendor => ({ ...vendor, displayText: vendor.vendorCode ? `${vendor.vendorName} (${vendor.vendorCode})` : vendor.vendorName })) return { success: true, data: vendorItems } } catch (error) { console.error('Error fetching all vendors:', error) return { success: false, data: [], error: '벤더 목록을 조회하는 중 오류가 발생했습니다.' } } } /** * 벤더 코드로 조회 */ export async function getVendorByCode(vendorCode: string): Promise { if (!vendorCode.trim()) { return null } try { const result = await db .select({ id: vendors.id, vendorName: vendors.vendorName, vendorCode: vendors.vendorCode, taxId: vendors.taxId, status: vendors.status, }) .from(vendors) .where(eq(vendors.vendorCode, vendorCode.trim())) .limit(1) if (result.length === 0) { return null } const vendor = result[0] return { ...vendor, displayText: vendor.vendorCode ? `${vendor.vendorName} (${vendor.vendorCode})` : vendor.vendorName } } catch (error) { console.error('Error fetching vendor by code:', error) return null } } /** * 특정 벤더 조회 (ID로) */ export async function getVendorById(vendorId: number): Promise { if (!vendorId) { return null } try { const result = await db .select({ id: vendors.id, vendorName: vendors.vendorName, vendorCode: vendors.vendorCode, taxId: vendors.taxId, status: vendors.status, }) .from(vendors) .where(eq(vendors.id, vendorId)) .limit(1) if (result.length === 0) { return null } const vendor = result[0] return { ...vendor, displayText: vendor.vendorCode ? `${vendor.vendorName} (${vendor.vendorCode})` : vendor.vendorName } } catch (error) { console.error('Error fetching vendor by ID:', error) return null } } /** * 벤더 상태 목록 조회 */ export async function getVendorStatuses(): Promise<{ success: boolean data: string[] error?: string }> { try { const result = await db .selectDistinct({ status: vendors.status }) .from(vendors) .orderBy(asc(vendors.status)) const statuses = result.map(row => row.status).filter(Boolean) return { success: true, data: statuses } } catch (error) { console.error('Error fetching vendor statuses:', error) return { success: false, data: [], error: '벤더 상태 목록을 조회하는 중 오류가 발생했습니다.' } } }