summaryrefslogtreecommitdiff
path: root/lib/rfq-last/vendor-response/service.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rfq-last/vendor-response/service.ts')
-rw-r--r--lib/rfq-last/vendor-response/service.ts175
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) {