diff options
Diffstat (limited to 'lib/evaluation-target-list/service.ts')
| -rw-r--r-- | lib/evaluation-target-list/service.ts | 267 |
1 files changed, 174 insertions, 93 deletions
diff --git a/lib/evaluation-target-list/service.ts b/lib/evaluation-target-list/service.ts index 1c133e3a..8d890604 100644 --- a/lib/evaluation-target-list/service.ts +++ b/lib/evaluation-target-list/service.ts @@ -73,79 +73,63 @@ export async function countEvaluationTargetsFromView( // ============= 메인 서버 액션도 함께 수정 ============= export async function getEvaluationTargets(input: GetEvaluationTargetsSchema) { try { + console.log("=== 서버 액션 호출 ==="); + console.log("필터 수:", input.filters?.length || 0); + console.log("조인 연산자:", input.joinOperator); + const offset = (input.page - 1) * input.perPage; - // ✅ getRFQ 방식과 동일한 필터링 처리 - // 1) 고급 필터 조건 + // ✅ 단순화된 필터 처리 let advancedWhere: SQL<unknown> | undefined = undefined; - if (input.filters && input.filters.length > 0) { - advancedWhere = filterColumns({ - table: evaluationTargetsWithDepartments, - filters: input.filters, - joinOperator: input.joinOperator || 'and', - }); - } - - // 2) 기본 필터 조건 - let basicWhere: SQL<unknown> | undefined = undefined; - if (input.basicFilters && input.basicFilters.length > 0) { - basicWhere = filterColumns({ - table: evaluationTargetsWithDepartments, - filters: input.basicFilters, - joinOperator: input.basicJoinOperator || 'and', - }); + + if (input.filters && Array.isArray(input.filters) && input.filters.length > 0) { + console.log("필터 적용:", input.filters.map(f => `${f.id} ${f.operator} ${f.value}`)); + + try { + advancedWhere = filterColumns({ + table: evaluationTargetsWithDepartments, + filters: input.filters, + joinOperator: input.joinOperator || 'and', + }); + + console.log("필터 조건 생성 완료"); + } catch (error) { + console.error("필터 조건 생성 오류:", error); + // 필터 오류 시에도 전체 데이터 반환 + advancedWhere = undefined; + } } - // 3) 글로벌 검색 조건 + // 2) 글로벌 검색 조건 let globalWhere: SQL<unknown> | undefined = undefined; if (input.search) { const s = `%${input.search}%`; - const validSearchConditions: SQL<unknown>[] = []; - - const vendorCodeCondition = ilike(evaluationTargetsWithDepartments.vendorCode, s); - if (vendorCodeCondition) validSearchConditions.push(vendorCodeCondition); - - const vendorNameCondition = ilike(evaluationTargetsWithDepartments.vendorName, s); - if (vendorNameCondition) validSearchConditions.push(vendorNameCondition); - - const adminCommentCondition = ilike(evaluationTargetsWithDepartments.adminComment, s); - if (adminCommentCondition) validSearchConditions.push(adminCommentCondition); - - const consolidatedCommentCondition = ilike(evaluationTargetsWithDepartments.consolidatedComment, s); - if (consolidatedCommentCondition) validSearchConditions.push(consolidatedCommentCondition); - - // 담당자 이름으로도 검색 - const orderReviewerCondition = ilike(evaluationTargetsWithDepartments.orderReviewerName, s); - if (orderReviewerCondition) validSearchConditions.push(orderReviewerCondition); - - const procurementReviewerCondition = ilike(evaluationTargetsWithDepartments.procurementReviewerName, s); - if (procurementReviewerCondition) validSearchConditions.push(procurementReviewerCondition); - - const qualityReviewerCondition = ilike(evaluationTargetsWithDepartments.qualityReviewerName, s); - if (qualityReviewerCondition) validSearchConditions.push(qualityReviewerCondition); - - const designReviewerCondition = ilike(evaluationTargetsWithDepartments.designReviewerName, s); - if (designReviewerCondition) validSearchConditions.push(designReviewerCondition); - - const csReviewerCondition = ilike(evaluationTargetsWithDepartments.csReviewerName, s); - if (csReviewerCondition) validSearchConditions.push(csReviewerCondition); - - if (validSearchConditions.length > 0) { - globalWhere = or(...validSearchConditions); + const searchConditions: SQL<unknown>[] = [ + ilike(evaluationTargetsWithDepartments.vendorCode, s), + ilike(evaluationTargetsWithDepartments.vendorName, s), + ilike(evaluationTargetsWithDepartments.adminComment, s), + ilike(evaluationTargetsWithDepartments.consolidatedComment, s), + ilike(evaluationTargetsWithDepartments.orderReviewerName, s), + ilike(evaluationTargetsWithDepartments.procurementReviewerName, s), + ilike(evaluationTargetsWithDepartments.qualityReviewerName, s), + ilike(evaluationTargetsWithDepartments.designReviewerName, s), + ilike(evaluationTargetsWithDepartments.csReviewerName, s), + ].filter(Boolean); + + if (searchConditions.length > 0) { + globalWhere = or(...searchConditions); } } - // ✅ getRFQ 방식과 동일한 WHERE 조건 생성 + // 3) 최종 WHERE 조건 결합 const whereConditions: SQL<unknown>[] = []; - if (advancedWhere) whereConditions.push(advancedWhere); - if (basicWhere) whereConditions.push(basicWhere); if (globalWhere) whereConditions.push(globalWhere); const finalWhere = whereConditions.length > 0 ? and(...whereConditions) : undefined; - // ✅ getRFQ 방식과 동일한 전체 데이터 수 조회 (Transaction 제거) + // 4) 전체 데이터 수 조회 const totalResult = await db .select({ count: count() }) .from(evaluationTargetsWithDepartments) @@ -157,12 +141,14 @@ export async function getEvaluationTargets(input: GetEvaluationTargetsSchema) { return { data: [], pageCount: 0, total: 0 }; } - console.log("Total evaluation targets:", total); + console.log("총 데이터 수:", total); - // ✅ getRFQ 방식과 동일한 정렬 및 페이징 처리된 데이터 조회 + // 5) 정렬 및 페이징 처리 const orderByColumns = input.sort.map((sort) => { const column = sort.id as keyof typeof evaluationTargetsWithDepartments.$inferSelect; - return sort.desc ? desc(evaluationTargetsWithDepartments[column]) : asc(evaluationTargetsWithDepartments[column]); + return sort.desc + ? desc(evaluationTargetsWithDepartments[column]) + : asc(evaluationTargetsWithDepartments[column]); }); if (orderByColumns.length === 0) { @@ -179,10 +165,11 @@ export async function getEvaluationTargets(input: GetEvaluationTargetsSchema) { const pageCount = Math.ceil(total / input.perPage); + console.log("반환 데이터 수:", evaluationData.length); + return { data: evaluationData, pageCount, total }; } catch (err) { - console.error("Error in getEvaluationTargets:", err); - // ✅ getRFQ 방식과 동일한 에러 반환 (total 포함) + console.error("getEvaluationTargets 오류:", err); return { data: [], pageCount: 0, total: 0 }; } } @@ -440,8 +427,6 @@ export interface UpdateEvaluationTargetInput { } export async function updateEvaluationTarget(input: UpdateEvaluationTargetInput) { - console.log(input, "update input") - try { const session = await getServerSession(authOptions) @@ -542,8 +527,8 @@ export async function updateEvaluationTarget(input: UpdateEvaluationTargetInput) { departmentCode: EVALUATION_DEPARTMENT_CODES.ORDER_EVAL, isApproved: input.orderIsApproved }, { departmentCode: EVALUATION_DEPARTMENT_CODES.PROCUREMENT_EVAL, isApproved: input.procurementIsApproved }, { departmentCode: EVALUATION_DEPARTMENT_CODES.QUALITY_EVAL, isApproved: input.qualityIsApproved }, - { departmentCode: EVALUATION_DEPARTMENT_CODES.DESIGN_EVAL, isApproved: input.designIsApproved }, - { departmentCode: EVALUATION_DEPARTMENT_CODES.CS_EVAL, isApproved: input.csIsApproved }, + // { departmentCode: EVALUATION_DEPARTMENT_CODES.DESIGN_EVAL, isApproved: input.designIsApproved }, + // { departmentCode: EVALUATION_DEPARTMENT_CODES.CS_EVAL, isApproved: input.csIsApproved }, ] for (const review of reviewUpdates) { @@ -589,46 +574,43 @@ export async function updateEvaluationTarget(input: UpdateEvaluationTargetInput) } } - // 4. 의견 일치 상태 및 전체 상태 자동 계산 + const requiredDepartments = [ + EVALUATION_DEPARTMENT_CODES.ORDER_EVAL, + EVALUATION_DEPARTMENT_CODES.PROCUREMENT_EVAL, + EVALUATION_DEPARTMENT_CODES.QUALITY_EVAL + ] + const currentReviews = await tx .select({ isApproved: evaluationTargetReviews.isApproved, departmentCode: evaluationTargetReviews.departmentCode, }) .from(evaluationTargetReviews) - .where(eq(evaluationTargetReviews.evaluationTargetId, input.id)) - - - const evaluationTargetForConcensus = await tx - .select({ - materialType: evaluationTargets.materialType, - }) - .from(evaluationTargets) - .where(eq(evaluationTargets.id, input.id)) - .limit(1) - - if (evaluationTargetForConcensus.length === 0) { - throw new Error("평가 대상을 찾을 수 없습니다.") - } - - const { materialType } = evaluationTargetForConcensus[0] - const minimumReviewsRequired = materialType === "BULK" ? 3 : 5 + .where( + and( + eq(evaluationTargetReviews.evaluationTargetId, input.id), + inArray(evaluationTargetReviews.departmentCode, requiredDepartments) + ) + ) + // 3개 필수 부서의 리뷰가 모두 완료된 경우에만 의견 일치 상태 계산 + const reviewedDepartments = currentReviews.map(r => r.departmentCode) + const allRequiredDepartmentsReviewed = requiredDepartments.every(dept => + reviewedDepartments.includes(dept) + ) - // 최소 3개 부서에서 평가가 완료된 경우 의견 일치 상태 계산 - if (currentReviews.length >= minimumReviewsRequired) { + if (allRequiredDepartmentsReviewed) { const approvals = currentReviews.map(r => r.isApproved) const allApproved = approvals.every(approval => approval === true) const allRejected = approvals.every(approval => approval === false) const hasConsensus = allApproved || allRejected - // let newStatus: "PENDING" | "CONFIRMED" | "EXCLUDED" = "PENDING" - // if (hasConsensus) { - // newStatus = allApproved ? "CONFIRMED" : "EXCLUDED" - // } - - // console.log("Auto-updating status:", { hasConsensus, newStatus, approvals }) - console.log("Auto-updating status:", { hasConsensus, approvals }) + console.log("Auto-updating consensus status:", { + hasConsensus, + approvals, + reviewedDepartments, + allRequiredDepartmentsReviewed + }) await tx .update(evaluationTargets) @@ -954,7 +936,7 @@ export async function confirmEvaluationTargets( return { success: true, - message: `${result.totalConfirmed}개 평가 대상이 확정되었습니다. ${result.createdEvaluationsCount}개의 정기평가와 ${result.createdSubmissionsCount}개의 제출 요청이 생성되었습니다.`, + message: `${result.totalConfirmed}개 평가 대상이 확정되었습니다. ${result.createdEvaluationsCount}개의 정기평가가 생성되었습니다.`, confirmedCount: result.totalConfirmed, createdEvaluationsCount: result.createdEvaluationsCount, // createdSubmissionsCount: result.createdSubmissionsCount @@ -1377,4 +1359,103 @@ export async function autoGenerateEvaluationTargets( message: "평가 대상 자동 생성에 실패했습니다." } } -}
\ No newline at end of file +} + + +export async function deleteEvaluationTargets(targetIds: number[]) { + console.log(targetIds, "targetIds to delete"); + + try { + const session = await getServerSession(authOptions); + + if (!session?.user?.id) { + throw new Error("로그인이 필요합니다."); + } + + // 권한 체크 (필요한 경우) + // const hasPermission = await checkUserPermission(session.user.id, 'manage_evaluations'); + // if (!hasPermission) { + // throw new Error("평가 관리 권한이 없습니다."); + // } + + return await db.transaction(async (tx) => { + // 1. 삭제하려는 타겟들이 존재하고 PENDING 상태인지 확인 + const targetsToDelete = await tx + .select({ + id: evaluationTargets.id, + status: evaluationTargets.status, + vendorName: evaluationTargets.vendorName, + evaluationYear: evaluationTargets.evaluationYear, + division: evaluationTargets.division, + }) + .from(evaluationTargets) + .where(inArray(evaluationTargets.id, targetIds)); + + if (targetsToDelete.length === 0) { + throw new Error("삭제할 평가 대상을 찾을 수 없습니다."); + } + + // PENDING 상태가 아닌 타겟들 확인 + const nonPendingTargets = targetsToDelete.filter(target => target.status !== 'PENDING'); + if (nonPendingTargets.length > 0) { + const nonPendingNames = nonPendingTargets + .map(t => `${t.vendorName} (${t.evaluationYear}년)`) + .join(', '); + throw new Error(`다음 평가 대상은 PENDING 상태가 아니어서 삭제할 수 없습니다: ${nonPendingNames}`); + } + + // 실제로 삭제할 수 있는 타겟 ID들 + const validTargetIds = targetsToDelete.map(t => t.id); + + console.log(`Deleting ${validTargetIds.length} evaluation targets:`, + targetsToDelete.map(t => `${t.vendorName} (${t.evaluationYear}년, ${t.division})`)); + + // 2. 관련된 자식 테이블들 먼저 삭제 + // evaluationTargetReviewers 테이블 삭제 + const deletedReviewers = await tx + .delete(evaluationTargetReviewers) + .where(inArray(evaluationTargetReviewers.evaluationTargetId, validTargetIds)) + .returning({ id: evaluationTargetReviewers.id }); + + console.log(`Deleted ${deletedReviewers.length} reviewer assignments`); + + // 3. 기타 관련 테이블들 삭제 (필요한 경우 추가) + // 예: evaluationTargetDepartments, evaluationComments 등 + // const deletedDepartments = await tx + // .delete(evaluationTargetDepartments) + // .where(inArray(evaluationTargetDepartments.evaluationTargetId, validTargetIds)) + // .returning({ id: evaluationTargetDepartments.id }); + + // 4. 메인 테이블 삭제 + const deletedTargets = await tx + .delete(evaluationTargets) + .where(inArray(evaluationTargets.id, validTargetIds)) + .returning({ + id: evaluationTargets.id, + vendorName: evaluationTargets.vendorName, + evaluationYear: evaluationTargets.evaluationYear + }); + + console.log(`Successfully deleted ${deletedTargets.length} evaluation targets`); + + return { + success: true, + deletedCount: deletedTargets.length, + deletedTargets: deletedTargets.map(t => ({ + id: t.id, + vendorName: t.vendorName, + evaluationYear: t.evaluationYear + })), + message: `${deletedTargets.length}개의 평가 대상이 성공적으로 삭제되었습니다.`, + }; + }); + + } catch (error) { + console.error("Error deleting evaluation targets:", error); + return { + success: false, + error: error instanceof Error ? error.message : "평가 대상 삭제 중 오류가 발생했습니다.", + }; + } +} + |
