diff options
Diffstat (limited to 'lib/rfq-last/vendor-response/service.ts')
| -rw-r--r-- | lib/rfq-last/vendor-response/service.ts | 175 |
1 files changed, 22 insertions, 153 deletions
diff --git a/lib/rfq-last/vendor-response/service.ts b/lib/rfq-last/vendor-response/service.ts index 7de3ae58..04cc5234 100644 --- a/lib/rfq-last/vendor-response/service.ts +++ b/lib/rfq-last/vendor-response/service.ts @@ -7,7 +7,7 @@ import { and, or, eq, desc, asc, count, ilike, inArray } from "drizzle-orm"; import { rfqsLastView, rfqLastDetails, - rfqLastVendorResponses, + rfqLastVendorResponses,vendorQuotationView, type RfqsLastView } from "@/db/schema"; import { filterColumns } from "@/lib/filter-columns"; @@ -26,25 +26,6 @@ export type VendorQuotationStatus = | "최종확정" // 최종 확정됨 | "취소" // 취소됨 -// 벤더 견적 뷰 타입 확장 -export interface VendorQuotationView extends RfqsLastView { - // 벤더 응답 정보 - responseStatus?: VendorQuotationStatus; - displayStatus?:string; - responseVersion?: number; - submittedAt?: Date; - totalAmount?: number; - vendorCurrency?: string; - - // 벤더별 조건 - vendorPaymentTerms?: string; - vendorIncoterms?: string; - vendorDeliveryDate?: Date; - - participationStatus: "미응답" | "참여" | "불참" | null - participationRepliedAt: Date | null - nonParticipationReason: string | null -} /** * 벤더별 RFQ 목록 조회 @@ -66,28 +47,9 @@ export async function getVendorQuotationsLast( const perPage = input.perPage || 10; const offset = (page - 1) * perPage; - // 1. 먼저 벤더가 포함된 RFQ ID들 조회 - const vendorRfqIds = await db - .select({ rfqsLastId: rfqLastDetails.rfqsLastId }) - .from(rfqLastDetails) - .where( - and( - eq(rfqLastDetails.vendorsId, numericVendorId), - eq(rfqLastDetails.isLatest, true) - ) - ); - - - const rfqIds = vendorRfqIds.map(r => r.rfqsLastId).filter(id => id !== null); - - if (rfqIds.length === 0) { - return { data: [], pageCount: 0 }; - } - - // 2. 필터링 설정 - // advancedTable 모드로 where 절 구성 + // 필터링 설정 const advancedWhere = filterColumns({ - table: rfqsLastView, + table: vendorQuotationView, filters: input.filters, joinOperator: input.joinOperator, }); @@ -97,148 +59,55 @@ export async function getVendorQuotationsLast( if (input.search) { const s = `%${input.search}%`; globalWhere = or( - ilike(rfqsLastView.rfqCode, s), - ilike(rfqsLastView.rfqTitle, s), - ilike(rfqsLastView.itemName, s), - ilike(rfqsLastView.projectName, s), - ilike(rfqsLastView.packageName, s), - ilike(rfqsLastView.status, s) + ilike(vendorQuotationView.rfqCode, s), + ilike(vendorQuotationView.rfqTitle, s), + ilike(vendorQuotationView.itemName, s), + ilike(vendorQuotationView.projectName, s), + ilike(vendorQuotationView.packageName, s), + ilike(vendorQuotationView.status, s), + ilike(vendorQuotationView.displayStatus, s) ); } - // RFQ ID 조건 (벤더가 포함된 RFQ만) - const rfqIdWhere = inArray(rfqsLastView.id, rfqIds); + // 벤더 ID 조건 (필수) + const vendorIdWhere = eq(vendorQuotationView.vendorId, numericVendorId); // 모든 조건 결합 - let whereConditions = [rfqIdWhere]; // 필수 조건 + let whereConditions = [vendorIdWhere]; if (advancedWhere) whereConditions.push(advancedWhere); if (globalWhere) whereConditions.push(globalWhere); - // 최종 조건 const finalWhere = and(...whereConditions); - // 3. 정렬 설정 + // 정렬 설정 const orderBy = input.sort && input.sort.length > 0 ? input.sort.map((item) => { - // @ts-ignore - 동적 속성 접근 - return item.desc ? desc(rfqsLastView[item.id]) : asc(rfqsLastView[item.id]); + // @ts-ignore + return item.desc ? desc(vendorQuotationView[item.id]) : asc(vendorQuotationView[item.id]); }) - : [desc(rfqsLastView.updatedAt)]; + : [desc(vendorQuotationView.updatedAt)]; - // 4. 메인 쿼리 실행 + // 메인 쿼리 실행 - 이제 한 번의 쿼리로 모든 데이터를 가져옴 const quotations = await db .select() - .from(rfqsLastView) + .from(vendorQuotationView) .where(finalWhere) .orderBy(...orderBy) .limit(perPage) .offset(offset); - // 5. 각 RFQ에 대한 벤더 응답 정보 조회 - const quotationsWithResponse = await Promise.all( - quotations.map(async (rfq) => { - // 벤더 응답 정보 조회 - const response = await db.query.rfqLastVendorResponses.findFirst({ - where: and( - eq(rfqLastVendorResponses.rfqsLastId, rfq.id), - eq(rfqLastVendorResponses.vendorId, numericVendorId), - eq(rfqLastVendorResponses.isLatest, true) - ), - columns: { - status: true, - responseVersion: true, - submittedAt: true, - totalAmount: true, - vendorCurrency: true, - vendorPaymentTermsCode: true, - vendorIncotermsCode: true, - vendorDeliveryDate: true, - participationStatus: true, - participationRepliedAt: true, - nonParticipationReason: true, - } - }); - - // 벤더 상세 정보 조회 - const detail = await db.query.rfqLastDetails.findFirst({ - where: and( - eq(rfqLastDetails.rfqsLastId, rfq.id), - eq(rfqLastDetails.vendorsId, numericVendorId), - eq(rfqLastDetails.isLatest, true) - ), - columns: { - id: true, // rfqLastDetailsId 필요 - emailSentAt: true, - emailStatus: true, - shortList: true, - } - }); - - // 표시할 상태 결정 (새로운 로직) - let displayStatus: string | null = null; - - if (response) { - // 응답 레코드가 있는 경우 - if (response.participationStatus === "불참") { - displayStatus = "불참"; - } else if (response.participationStatus === "참여") { - // 참여한 경우 실제 작업 상태 표시 - displayStatus = response.status || "작성중"; - } else { - // participationStatus가 없거나 "미응답"인 경우 - displayStatus = "미응답"; - } - } else { - // 응답 레코드가 없는 경우 - if (detail?.emailSentAt) { - displayStatus = "미응답"; // 초대는 받았지만 응답 안함 - } else { - displayStatus = null; // 아직 초대도 안됨 - } - } - - return { - ...rfq, - // 새로운 상태 체계 - displayStatus, // UI에서 표시할 통합 상태 - - // 참여 관련 정보 - participationStatus: response?.participationStatus || "미응답", - participationRepliedAt: response?.participationRepliedAt, - nonParticipationReason: response?.nonParticipationReason, - - // 견적 작업 상태 (참여한 경우에만 의미 있음) - responseStatus: response?.status, - responseVersion: response?.responseVersion, - submittedAt: response?.submittedAt, - totalAmount: response?.totalAmount, - vendorCurrency: response?.vendorCurrency, - vendorPaymentTerms: response?.vendorPaymentTermsCode, - vendorIncoterms: response?.vendorIncotermsCode, - vendorDeliveryDate: response?.vendorDeliveryDate, - - // 초대 관련 정보 - rfqLastDetailsId: detail?.id, // 참여 결정 시 필요 - emailSentAt: detail?.emailSentAt, - emailStatus: detail?.emailStatus, - shortList: detail?.shortList, - } as VendorQuotationView; - }) - ); - - // 6. 전체 개수 조회 + // 전체 개수 조회 const { totalCount } = await db .select({ totalCount: count() }) - .from(rfqsLastView) + .from(vendorQuotationView) .where(finalWhere) .then(rows => rows[0]); // 페이지 수 계산 const pageCount = Math.ceil(Number(totalCount) / perPage); - return { - data: quotationsWithResponse, + data: quotations, pageCount }; } catch (err) { |
