summaryrefslogtreecommitdiff
path: root/components/common/vendor/vendor-service.ts
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-09-22 18:59:13 +0900
committerjoonhoekim <26rote@gmail.com>2025-09-22 18:59:13 +0900
commitba35e67845f935c8ce0151c9ef1fefa0b0510faf (patch)
treed05eb27fab2acc54a839b2590c89e860d58fb747 /components/common/vendor/vendor-service.ts
parente4bd037d158513e45373ad9e1ef13f71af12162a (diff)
(김준회) AVL 피드백 반영 (이진용 프로 건)
Diffstat (limited to 'components/common/vendor/vendor-service.ts')
-rw-r--r--components/common/vendor/vendor-service.ts263
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: '벤더 상태 목록을 조회하는 중 오류가 발생했습니다.'
+ }
+ }
+}