diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-05 09:38:31 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-05 09:38:31 +0000 |
| commit | 35e373fe29a4145d0692ee35ff9e6b0c887df0eb (patch) | |
| tree | 29e8217d614afaaaa7ef6ca6a7e547fbd9717bd7 | |
| parent | 0a0a4feabdc587125e58ce9c810719e208a481ed (diff) | |
(임수민) 정기평가 요청사항 수정 (김태석프로)
| -rw-r--r-- | lib/vendor-evaluation-submit/service.ts | 130 | ||||
| -rw-r--r-- | lib/vendor-evaluation-submit/table/evaluation-submit-dialog.tsx | 15 |
2 files changed, 130 insertions, 15 deletions
diff --git a/lib/vendor-evaluation-submit/service.ts b/lib/vendor-evaluation-submit/service.ts index c7fe7122..d105ad5c 100644 --- a/lib/vendor-evaluation-submit/service.ts +++ b/lib/vendor-evaluation-submit/service.ts @@ -18,9 +18,10 @@ import { esgEvaluations, esgAnswerOptions, esgEvaluationItems, - periodicEvaluations + periodicEvaluations, + evaluationTargets } from "@/db/schema"; -import { and, asc, desc, eq, ilike, or, SQL, count , sql, avg} from "drizzle-orm"; +import { and, asc, desc, eq, ilike, or, SQL, count , sql, avg, inArray} from "drizzle-orm"; import { filterColumns } from "@/lib/filter-columns"; import { GetEvaluationsSubmitSchema } from "./validation"; @@ -38,6 +39,14 @@ export type EvaluationSubmissionWithVendor = EvaluationSubmission & { esgResponses: number; attachments: number; }; + // 연결된 평가 정보 (조선/해양 구분 표시용) + linkedEvaluations: Array<{ + id: number; + division: string; + evaluationPeriod: string; + }>; + // 같은 그룹의 모든 submission IDs (제출 처리용) + groupSubmissionIds?: number[]; }; /** @@ -111,6 +120,7 @@ export async function getEvaluationSubmissions(input: GetEvaluationsSubmitSchema .select({ id: evaluationSubmissions.id, submissionId: evaluationSubmissions.submissionId, + periodicEvaluationId: evaluationSubmissions.periodicEvaluationId, companyId: evaluationSubmissions.companyId, evaluationYear: evaluationSubmissions.evaluationYear, evaluationRound: evaluationSubmissions.evaluationRound, @@ -145,8 +155,8 @@ export async function getEvaluationSubmissions(input: GetEvaluationsSubmitSchema // 각 제출에 대한 응답/첨부파일 수 조회 const dataWithCounts = await Promise.all( - data.map(async (submission) => { - const [generalCount, esgCount, attachmentCount] = await Promise.all([ + data.map(async (submission) => { + const [generalCount, esgCount, attachmentCount, linkedEvaluationsResult] = await Promise.all([ tx .select({ count: count() }) .from(generalEvaluationResponses) @@ -179,8 +189,25 @@ export async function getEvaluationSubmissions(input: GetEvaluationsSubmitSchema ) ) .then(result => result[0]?.count || 0), + + // 해당 submission의 periodicEvaluation 정보만 조회 + submission.periodicEvaluationId + ? tx + .select({ + id: periodicEvaluations.id, + division: evaluationTargets.division, + evaluationPeriod: periodicEvaluations.evaluationPeriod, + }) + .from(periodicEvaluations) + .innerJoin(evaluationTargets, eq(periodicEvaluations.evaluationTargetId, evaluationTargets.id)) + .where(eq(periodicEvaluations.id, submission.periodicEvaluationId)) + .limit(1) + .catch(() => []) + : Promise.resolve([]), ]); + const linkedEvaluations = linkedEvaluationsResult || []; + return { ...submission, totalGeneralItems: totalGeneralItemsCount , @@ -190,20 +217,58 @@ export async function getEvaluationSubmissions(input: GetEvaluationsSubmitSchema esgResponses: esgCount, attachments: attachmentCount, }, + linkedEvaluations: linkedEvaluations, }; }) ); - // 총 개수 조회 - const totalResult = await tx - .select({ count: count() }) + // 같은 그룹의 evaluationSubmission들을 묶어서 하나의 그룹으로 반환 + const groupedData = dataWithCounts.reduce((groups, submission) => { + const key = `${submission.companyId}_${submission.evaluationYear}_${submission.evaluationRound}`; + if (!groups[key]) { + groups[key] = []; + } + groups[key].push(submission); + return groups; + }, {} as Record<string, typeof dataWithCounts>); + + // 각 그룹의 첫 번째 submission을 대표로 해서 반환 + const finalData = Object.values(groupedData).map(group => { + const representative = group[0]; + const allLinkedEvaluations = group.flatMap(sub => sub.linkedEvaluations); + + return { + ...representative, + linkedEvaluations: allLinkedEvaluations, + // 그룹 내 모든 submission의 통계 합산 + _count: { + generalResponses: group.reduce((sum, sub) => sum + sub._count.generalResponses, 0), + esgResponses: group.reduce((sum, sub) => sum + sub._count.esgResponses, 0), + attachments: group.reduce((sum, sub) => sum + sub._count.attachments, 0), + }, + // 그룹 내 submission IDs 저장 (제출 처리용) + groupSubmissionIds: group.map(sub => sub.id), + }; + }); + + // 총 개수 조회 - grouping 키를 기준으로 고유한 그룹 수를 계산 + // 먼저 모든 레코드를 조회하여 grouping 키를 추출 + const allSubmissions = await tx + .select({ + companyId: evaluationSubmissions.companyId, + evaluationYear: evaluationSubmissions.evaluationYear, + evaluationRound: evaluationSubmissions.evaluationRound, + }) .from(evaluationSubmissions) .innerJoin(vendors, eq(evaluationSubmissions.companyId, vendors.id)) .where(finalWhere); - const total = totalResult[0]?.count || 0; - - return { data: dataWithCounts, total }; + // 고유한 그룹 키 계산 + const uniqueGroupKeys = new Set( + allSubmissions.map(sub => `${sub.companyId}_${sub.evaluationYear}_${sub.evaluationRound}`) + ); + const total = uniqueGroupKeys.size; + return { data: finalData, total }; }); const pageCount = Math.ceil(total / input.perPage); @@ -446,15 +511,50 @@ export async function updateEvaluationSubmissionStatus( // newStatus === 'submitted'일 때 periodicEvaluations 테이블도 업데이트 if (newStatus === 'submitted' && updatedSubmission) { + // 그룹 내 모든 submission IDs를 DB 기준으로 계산 (companyId/evaluationYear/evaluationRound) + const siblingSubmissions = await tx + .select({ id: evaluationSubmissions.id }) + .from(evaluationSubmissions) + .where( + and( + eq(evaluationSubmissions.companyId, updatedSubmission.companyId), + eq(evaluationSubmissions.evaluationYear, updatedSubmission.evaluationYear), + eq(evaluationSubmissions.evaluationRound, updatedSubmission.evaluationRound), + eq(evaluationSubmissions.isActive, true), + ) + ); + + const submissionIdsToUpdate = siblingSubmissions.map(s => s.id); + + // 그룹 내 모든 submission들의 상태를 submitted로 업데이트 await tx - .update(periodicEvaluations) + .update(evaluationSubmissions) .set({ - documentsSubmitted: true, - submissionDate: new Date(), - status: 'SUBMITTED', + submissionStatus: 'submitted', + submittedAt: new Date(), updatedAt: new Date(), }) - .where(eq(periodicEvaluations.id, updatedSubmission.periodicEvaluationId)); + .where(inArray(evaluationSubmissions.id, submissionIdsToUpdate)); + + // 그룹 내 모든 periodicEvaluation들도 함께 업데이트 + const periodicEvaluationIds = await tx + .select({ periodicEvaluationId: evaluationSubmissions.periodicEvaluationId }) + .from(evaluationSubmissions) + .where(inArray(evaluationSubmissions.id, submissionIdsToUpdate)); + + await Promise.all( + periodicEvaluationIds.map(({ periodicEvaluationId }) => + tx + .update(periodicEvaluations) + .set({ + documentsSubmitted: true, + status: 'SUBMITTED', + submissionDate: new Date(), + updatedAt: new Date(), + }) + .where(eq(periodicEvaluations.id, periodicEvaluationId)) + ) + ); } return updatedSubmission; diff --git a/lib/vendor-evaluation-submit/table/evaluation-submit-dialog.tsx b/lib/vendor-evaluation-submit/table/evaluation-submit-dialog.tsx index 20ed5f30..a6f62f82 100644 --- a/lib/vendor-evaluation-submit/table/evaluation-submit-dialog.tsx +++ b/lib/vendor-evaluation-submit/table/evaluation-submit-dialog.tsx @@ -133,6 +133,11 @@ export function EvaluationSubmissionDialog({ const isKorean = submission?.vendor.countryCode === 'KR' + // 조선/해양 동시 제출 안내 표시 조건: linkedEvaluations에 서로 다른 division이 2개 이상 존재할 때 + const hasBothDivisions = + Array.isArray(submission?.linkedEvaluations) && + new Set((submission?.linkedEvaluations || []).map((e: any) => e.division)).size > 1 + if (isLoading) { return ( <Dialog open={open} onOpenChange={onOpenChange}> @@ -161,6 +166,16 @@ export function EvaluationSubmissionDialog({ </DialogDescription> </DialogHeader> + {/* 안내 알림: 조선/해양 동시 제출 (조건부 표시) */} + {hasBothDivisions && ( + <Alert className="mb-4 border-red-300 bg-red-50 text-red-800"> + <AlertTitle className="text-red-800">안내</AlertTitle> + <AlertDescription className="text-red-700"> + 제출 버튼을 누르면 동일 연도/라운드의 조선·해양 평가가 함께 제출 처리됩니다. + </AlertDescription> + </Alert> + )} + {completeness && ( <div className="space-y-6"> {/* 전체 완성도 카드 */} |
