diff options
Diffstat (limited to 'lib/b-rfq/service.ts')
| -rw-r--r-- | lib/b-rfq/service.ts | 218 |
1 files changed, 216 insertions, 2 deletions
diff --git a/lib/b-rfq/service.ts b/lib/b-rfq/service.ts index e60e446d..0dc61832 100644 --- a/lib/b-rfq/service.ts +++ b/lib/b-rfq/service.ts @@ -4,15 +4,16 @@ import { revalidateTag, unstable_cache } from "next/cache" import { count, desc, asc, and, or, gte, lte, ilike, eq, inArray, sql } from "drizzle-orm" import { filterColumns } from "@/lib/filter-columns" import db from "@/db/db" -import { RfqDashboardView, bRfqAttachmentRevisions, bRfqs, bRfqsAttachments, projects, users, vendorAttachmentResponses, vendors } from "@/db/schema" // 실제 스키마 import 경로에 맞게 수정 +import { Incoterm, RfqDashboardView, Vendor, bRfqAttachmentRevisions, bRfqs, bRfqsAttachments, incoterms, initialRfq, initialRfqDetailView, projects, users, vendorAttachmentResponses, vendors } from "@/db/schema" // 실제 스키마 import 경로에 맞게 수정 import { rfqDashboardView } from "@/db/schema" // 뷰 import import type { SQL } from "drizzle-orm" -import { AttachmentRecord, CreateRfqInput, DeleteAttachmentsInput, GetRFQDashboardSchema, GetRfqAttachmentsSchema, attachmentRecordSchema, createRfqServerSchema, deleteAttachmentsSchema } from "./validations" +import { AttachmentRecord, CreateRfqInput, DeleteAttachmentsInput, GetInitialRfqDetailSchema, GetRFQDashboardSchema, GetRfqAttachmentsSchema, attachmentRecordSchema, createRfqServerSchema, deleteAttachmentsSchema } from "./validations" import { getServerSession } from "next-auth/next" import { authOptions } from "@/app/api/auth/[...nextauth]/route" import { unlink } from "fs/promises" const tag = { + initialRfqDetail:"initial-rfq", rfqDashboard: 'rfq-dashboard', rfq: (id: number) => `rfq-${id}`, rfqAttachments: (rfqId: number) => `rfq-attachments-${rfqId}`, @@ -1017,4 +1018,217 @@ export async function deleteRfqAttachments(input: DeleteAttachmentsInput) { message: error instanceof Error ? error.message : "첨부파일 삭제 중 오류가 발생했습니다.", } } +} + + + +//Initial RFQ + +export async function getInitialRfqDetail(input: GetInitialRfqDetailSchema, rfqId?: number) { + return unstable_cache( + async () => { + try { + const offset = (input.page - 1) * input.perPage; + + // 1) 고급 필터 조건 + let advancedWhere: SQL<unknown> | undefined = undefined; + if (input.filters && input.filters.length > 0) { + advancedWhere = filterColumns({ + table: initialRfqDetailView, + filters: input.filters, + joinOperator: input.joinOperator || 'and', + }); + } + + // 2) 기본 필터 조건 + let basicWhere: SQL<unknown> | undefined = undefined; + if (input.basicFilters && input.basicFilters.length > 0) { + basicWhere = filterColumns({ + table: initialRfqDetailView, + filters: input.basicFilters, + joinOperator: input.basicJoinOperator || 'and', + }); + } + + let rfqIdWhere: SQL<unknown> | undefined = undefined; + if (rfqId) { + rfqIdWhere = eq(initialRfqDetailView.rfqId, rfqId); + } + + + // 3) 글로벌 검색 조건 + let globalWhere: SQL<unknown> | undefined = undefined; + if (input.search) { + const s = `%${input.search}%`; + + const validSearchConditions: SQL<unknown>[] = []; + + const rfqCodeCondition = ilike(initialRfqDetailView.rfqCode, s); + if (rfqCodeCondition) validSearchConditions.push(rfqCodeCondition); + + const vendorNameCondition = ilike(initialRfqDetailView.vendorName, s); + if (vendorNameCondition) validSearchConditions.push(vendorNameCondition); + + const vendorCodeCondition = ilike(initialRfqDetailView.vendorCode, s); + if (vendorCodeCondition) validSearchConditions.push(vendorCodeCondition); + + const vendorCountryCondition = ilike(initialRfqDetailView.vendorCountry, s); + if (vendorCountryCondition) validSearchConditions.push(vendorCountryCondition); + + const incotermsDescriptionCondition = ilike(initialRfqDetailView.incotermsDescription, s); + if (incotermsDescriptionCondition) validSearchConditions.push(incotermsDescriptionCondition); + + const classificationCondition = ilike(initialRfqDetailView.classification, s); + if (classificationCondition) validSearchConditions.push(classificationCondition); + + const sparepartCondition = ilike(initialRfqDetailView.sparepart, s); + if (sparepartCondition) validSearchConditions.push(sparepartCondition); + + if (validSearchConditions.length > 0) { + globalWhere = or(...validSearchConditions); + } + } + + + // 5) 최종 WHERE 조건 생성 + const whereConditions: SQL<unknown>[] = []; + + if (advancedWhere) whereConditions.push(advancedWhere); + if (basicWhere) whereConditions.push(basicWhere); + if (globalWhere) whereConditions.push(globalWhere); + if (rfqIdWhere) whereConditions.push(rfqIdWhere); + + const finalWhere = whereConditions.length > 0 ? and(...whereConditions) : undefined; + + // 6) 전체 데이터 수 조회 + const totalResult = await db + .select({ count: count() }) + .from(initialRfqDetailView) + .where(finalWhere); + + const total = totalResult[0]?.count || 0; + + if (total === 0) { + return { data: [], pageCount: 0, total: 0 }; + } + + console.log(total); + + // 7) 정렬 및 페이징 처리된 데이터 조회 + const orderByColumns = input.sort.map((sort) => { + const column = sort.id as keyof typeof initialRfqDetailView.$inferSelect; + return sort.desc ? desc(initialRfqDetailView[column]) : asc(initialRfqDetailView[column]); + }); + + if (orderByColumns.length === 0) { + orderByColumns.push(desc(initialRfqDetailView.createdAt)); + } + + const initialRfqData = await db + .select() + .from(initialRfqDetailView) + .where(finalWhere) + .orderBy(...orderByColumns) + .limit(input.perPage) + .offset(offset); + + const pageCount = Math.ceil(total / input.perPage); + + return { data: initialRfqData, pageCount, total }; + } catch (err) { + console.error("Error in getInitialRfqDetail:", err); + return { data: [], pageCount: 0, total: 0 }; + } + }, + [JSON.stringify(input)], + { revalidate: 3600, tags: [tag.initialRfqDetail] }, + )(); +} + +export async function getVendorsForSelection() { + try { + const vendorsData = await db + .select({ + id: vendors.id, + vendorName: vendors.vendorName, + vendorCode: vendors.vendorCode, + country: vendors.country, + status: vendors.status, + }) + .from(vendors) + // .where( + // and( + // ne(vendors.status, "BLACKLISTED"), + // ne(vendors.status, "REJECTED") + // ) + // ) + .orderBy(vendors.vendorName) + + return vendorsData.map(vendor => ({ + id: vendor.id, + vendorName: vendor.vendorName || "", + vendorCode: vendor.vendorCode || "", + country: vendor.country || "", + status: vendor.status, + })) + } catch (error) { + console.error("Error fetching vendors:", error) + throw new Error("Failed to fetch vendors") + } +} + +export async function addInitialRfqRecord(data: AddInitialRfqFormData & { rfqId: number }) { + try { + const [newRecord] = await db + .insert(initialRfq) + .values({ + rfqId: data.rfqId, + vendorId: data.vendorId, + initialRfqStatus: data.initialRfqStatus, + dueDate: data.dueDate, + validDate: data.validDate, + incotermsCode: data.incotermsCode, + gtc: data.gtc, + gtcValidDate: data.gtcValidDate, + classification: data.classification, + sparepart: data.sparepart, + shortList: data.shortList, + returnYn: data.returnYn, + cpRequestYn: data.cpRequestYn, + prjectGtcYn: data.prjectGtcYn, + returnRevision: data.returnRevision, + }) + .returning() + + return { + success: true, + message: "초기 RFQ가 성공적으로 추가되었습니다.", + data: newRecord, + } + } catch (error) { + console.error("Error adding initial RFQ:", error) + return { + success: false, + message: "초기 RFQ 추가에 실패했습니다.", + error, + } + } +} + +export async function getIncotermsForSelection() { + try { + const incotermData = await db + .select({ + code: incoterms.code, + description: incoterms.description, + }) + .from(incoterms) + .orderBy(incoterms.code) + + return incotermData + + } catch (error) { + console.error("Error fetching incoterms:", error) + throw new Error("Failed to fetch incoterms") + } }
\ No newline at end of file |
