summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-11-05 09:38:31 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-11-05 09:38:31 +0000
commit35e373fe29a4145d0692ee35ff9e6b0c887df0eb (patch)
tree29e8217d614afaaaa7ef6ca6a7e547fbd9717bd7
parent0a0a4feabdc587125e58ce9c810719e208a481ed (diff)
(임수민) 정기평가 요청사항 수정 (김태석프로)
-rw-r--r--lib/vendor-evaluation-submit/service.ts130
-rw-r--r--lib/vendor-evaluation-submit/table/evaluation-submit-dialog.tsx15
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">
{/* 전체 완성도 카드 */}