diff options
Diffstat (limited to 'lib/rfqs/service.ts')
| -rw-r--r-- | lib/rfqs/service.ts | 122 |
1 files changed, 95 insertions, 27 deletions
diff --git a/lib/rfqs/service.ts b/lib/rfqs/service.ts index 6b8b4738..b56349e2 100644 --- a/lib/rfqs/service.ts +++ b/lib/rfqs/service.ts @@ -208,6 +208,7 @@ export async function modifyRfq(input: UpdateRfqSchema & { id: number }) { rfqCode: input.rfqCode, projectId: input.projectId || null, dueDate: input.dueDate, + rfqType: input.rfqType, status: input.status as "DRAFT" | "PUBLISHED" | "EVALUATION" | "AWARDED", createdBy: input.createdBy, }); @@ -1246,6 +1247,11 @@ export async function getTBE(input: GetTBESchema, rfqId: number) { } export async function getTBEforVendor(input: GetTBESchema, vendorId: number) { + + if (isNaN(vendorId) || vendorId === null || vendorId === undefined) { + throw new Error("유효하지 않은 vendorId: 숫자 값이 필요합니다"); + } + return unstable_cache( async () => { // 1) 페이징 @@ -1801,13 +1807,6 @@ export interface BudgetaryRfq { projectName: string | null; } -interface GetBudgetaryRfqsParams { - search?: string; - projectId?: number; - limit?: number; - offset?: number; -} - type GetBudgetaryRfqsResponse = | { rfqs: BudgetaryRfq[]; totalCount: number; error?: never } | { error: string; rfqs?: never; totalCount: number } @@ -1816,16 +1815,40 @@ type GetBudgetaryRfqsResponse = * Purchase RFQ 생성 시 부모 RFQ로 선택할 수 있도록 함 * 페이징 및 필터링 기능 포함 */ +export interface GetBudgetaryRfqsParams { + search?: string; + projectId?: number; + rfqId?: number; // 특정 ID로 단일 RFQ 검색 + rfqTypes?: RfqType[]; // 특정 RFQ 타입들로 필터링 + limit?: number; + offset?: number; +} + export async function getBudgetaryRfqs(params: GetBudgetaryRfqsParams = {}): Promise<GetBudgetaryRfqsResponse> { - const { search, projectId, limit = 50, offset = 0 } = params; - const cacheKey = `budgetary-rfqs-${JSON.stringify(params)}`; + const { search, projectId, rfqId, rfqTypes, limit = 50, offset = 0 } = params; + const cacheKey = `rfqs-query-${JSON.stringify(params)}`; + return unstable_cache( async () => { try { - - const baseCondition = eq(rfqs.rfqType, RfqType.BUDGETARY); - - let where1 + // 기본 검색 조건 구성 + let baseCondition; + + // 특정 RFQ 타입들로 필터링 (rfqTypes 배열이 주어진 경우) + if (rfqTypes && rfqTypes.length > 0) { + // 여러 타입으로 필터링 (OR 조건) + baseCondition = inArray(rfqs.rfqType, rfqTypes); + } else { + // 기본적으로 BUDGETARY 타입만 검색 (이전 동작 유지) + baseCondition = eq(rfqs.rfqType, RfqType.BUDGETARY); + } + + // 특정 ID로 검색하는 경우 + if (rfqId) { + baseCondition = and(baseCondition, eq(rfqs.id, rfqId)); + } + + let where1; // 검색어 조건 추가 (있을 경우) if (search && search.trim()) { const searchTerm = `%${search.trim()}%`; @@ -1835,30 +1858,31 @@ export async function getBudgetaryRfqs(params: GetBudgetaryRfqsParams = {}): Pro ilike(projects.code, searchTerm), ilike(projects.name, searchTerm) ); - where1 = searchCondition + where1 = searchCondition; } - - let where2 + + let where2; // 프로젝트 ID 조건 추가 (있을 경우) if (projectId) { where2 = eq(rfqs.projectId, projectId); } - - const finalWhere = and(where1, where2, baseCondition) - + + const finalWhere = and(baseCondition, where1, where2); + // 총 개수 조회 const [countResult] = await db .select({ count: count() }) .from(rfqs) .leftJoin(projects, eq(rfqs.projectId, projects.id)) .where(finalWhere); - + // 실제 데이터 조회 - const budgetaryRfqs = await db + const resultRfqs = await db .select({ id: rfqs.id, rfqCode: rfqs.rfqCode, description: rfqs.description, + rfqType: rfqs.rfqType, // RFQ 타입 필드 추가 projectId: rfqs.projectId, projectCode: projects.code, projectName: projects.name, @@ -1869,15 +1893,15 @@ export async function getBudgetaryRfqs(params: GetBudgetaryRfqsParams = {}): Pro .orderBy(desc(rfqs.createdAt)) .limit(limit) .offset(offset); - + return { - rfqs: budgetaryRfqs as BudgetaryRfq[], // 타입 단언으로 호환성 보장 + rfqs: resultRfqs, totalCount: Number(countResult?.count) || 0 }; } catch (error) { - console.error("Error fetching budgetary RFQs:", error); + console.error("Error fetching RFQs:", error); return { - error: "Failed to fetch budgetary RFQs", + error: "Failed to fetch RFQs", totalCount: 0 }; } @@ -1885,11 +1909,10 @@ export async function getBudgetaryRfqs(params: GetBudgetaryRfqsParams = {}): Pro [cacheKey], { revalidate: 60, // 1분 캐시 - tags: ["rfqs-budgetary"], + tags: ["rfqs-query"], } )(); } - export async function getAllVendors() { // Adjust the query as needed (add WHERE, ORDER, etc.) const allVendors = await db.select().from(vendors) @@ -2812,4 +2835,49 @@ export async function getCBE(input: GetCBESchema, rfqId: number) { tags: ["cbe-vendors"], } )(); +} + + +export async function generateNextRfqCode(rfqType: RfqType): Promise<{ code: string; error?: string }> { + try { + if (!rfqType) { + return { code: "", error: 'RFQ 타입이 필요합니다' }; + } + + // 현재 연도 가져오기 + const currentYear = new Date().getFullYear(); + + // 현재 연도와 타입에 맞는 최신 RFQ 코드 찾기 + const latestRfqs = await db.select({ rfqCode: rfqs.rfqCode }) + .from(rfqs) + .where(and( + sql`SUBSTRING(${rfqs.rfqCode}, 5, 4) = ${currentYear.toString()}`, + eq(rfqs.rfqType, rfqType) + )) + .orderBy(desc(rfqs.rfqCode)) + .limit(1); + + let sequenceNumber = 1; + + if (latestRfqs.length > 0 && latestRfqs[0].rfqCode) { + // null 체크 추가 - TypeScript 오류 해결 + const latestCode = latestRfqs[0].rfqCode; + const matches = latestCode.match(/[A-Z]+-\d{4}-(\d{3})/); + + if (matches && matches[1]) { + sequenceNumber = parseInt(matches[1], 10) + 1; + } + } + + // 새로운 RFQ 코드 포맷팅 + const typePrefix = rfqType === RfqType.BUDGETARY ? 'BUD' : + rfqType === RfqType.PURCHASE_BUDGETARY ? 'PBU' : 'RFQ'; + + const newCode = `${typePrefix}-${currentYear}-${String(sequenceNumber).padStart(3, '0')}`; + + return { code: newCode }; + } catch (error) { + console.error('Error generating next RFQ code:', error); + return { code: "", error: '코드 생성에 실패했습니다' }; + } }
\ No newline at end of file |
