diff options
Diffstat (limited to 'components/common/vendor/vendor-service.ts')
| -rw-r--r-- | components/common/vendor/vendor-service.ts | 263 |
1 files changed, 263 insertions, 0 deletions
diff --git a/components/common/vendor/vendor-service.ts b/components/common/vendor/vendor-service.ts new file mode 100644 index 00000000..83a63cae --- /dev/null +++ b/components/common/vendor/vendor-service.ts @@ -0,0 +1,263 @@ +"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 + status: string + displayText: string // vendorName + vendorCode로 구성된 표시용 텍스트 +} + +// 벤더 검색 옵션 +export interface VendorSearchOptions { + searchTerm?: string + statusFilter?: string // 특정 상태로 필터링 + limit?: number + offset?: number + sortBy?: 'vendorName' | 'vendorCode' | 'status' + sortOrder?: 'asc' | 'desc' +} + +// 페이지네이션 정보 +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<VendorSearchOptions, 'searchTerm' | 'limit' | 'offset'> = {} +): Promise<{ + success: boolean + data: VendorSearchItem[] + pagination: VendorPagination + error?: string +}> { + try { + const { statusFilter, sortBy = 'vendorName', sortOrder = 'asc' } = 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 + + // 데이터 조회 + let dataQuery = db + .select({ + id: vendors.id, + vendorName: vendors.vendorName, + vendorCode: vendors.vendorCode, + status: vendors.status, + }) + .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 + })) + + // 페이지네이션 정보 계산 + 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, + 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: '벤더 목록을 조회하는 중 오류가 발생했습니다.' + } + } +} + +/** + * 특정 벤더 조회 (ID로) + */ +export async function getVendorById(vendorId: number): Promise<VendorSearchItem | null> { + if (!vendorId) { + return null + } + + try { + const result = await db + .select({ + id: vendors.id, + vendorName: vendors.vendorName, + vendorCode: vendors.vendorCode, + 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: '벤더 상태 목록을 조회하는 중 오류가 발생했습니다.' + } + } +} |
