diff --git a/backend/vkt/db/4_init.sql b/backend/vkt/db/4_init.sql index 447092b29..18769a20c 100644 --- a/backend/vkt/db/4_init.sql +++ b/backend/vkt/db/4_init.sql @@ -485,6 +485,26 @@ VALUES (2, null, 51400, '78b29334-a283-11ef-88a1-bf672bd574b1', '7676156682', 'https://pay.paytrail.com/pay/78b29334-a283-11ef-88a1-bf672bd574b1', 'OK', null, 3); +INSERT INTO enrollment_appointment(person_id, examiner_id, examiner_exam_event_id, + skill_oral, skill_textual, skill_understanding, + partial_exam_speaking, partial_exam_speech_comprehension, partial_exam_writing, partial_exam_reading_comprehension, + status, digital_certificate_consent, email, phone_number, street, postal_code, town, country, first_name, last_name, + auth_hash, auth_hash_expires, auth_hash_sent, created_at) +VALUES (2, 1, 1, + false, true, false, + false, false, false, true, + 'COMPLETED', false, + 'test@test.invalid', '0401234504', null, null, null, null, + 'Anneli', 'Annikkinen', + '22223089-83a8-4163-8180-d8b675ff5337', NOW() - INTERVAL '13 days', NOW() - INTERVAL '16 days', + NOW() - INTERVAL '17 days'); + +INSERT INTO payment(version, enrollment_id, amount, transaction_id, reference, payment_url, + payment_status, refunded_at, enrollment_appointment_id) +VALUES (3, null, 51400, '12345634-a283-11ef-88a1-bf672bd574b1', '9676156682', + 'https://pay.paytrail.com/pay/12345634-a283-11ef-88a1-bf672bd574b1', 'OK', + null, 3); + -- Insert enrollment appointment INSERT INTO enrollment_appointment(person_id, examiner_id, examiner_exam_event_id, skill_oral, skill_textual, skill_understanding, diff --git a/backend/vkt/src/main/java/fi/oph/vkt/service/ExaminerEnrollmentService.java b/backend/vkt/src/main/java/fi/oph/vkt/service/ExaminerEnrollmentService.java index 36511a6a4..20bc21bf5 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/service/ExaminerEnrollmentService.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/service/ExaminerEnrollmentService.java @@ -16,6 +16,7 @@ import fi.oph.vkt.repository.EnrollmentGradesRepository; import fi.oph.vkt.repository.ExaminerExamEventRepository; import fi.oph.vkt.util.ClerkEnrollmentUtil; +import fi.oph.vkt.util.ExaminerUtil; import fi.oph.vkt.util.UUIDSource; import fi.oph.vkt.util.exception.APIException; import fi.oph.vkt.util.exception.APIExceptionType; @@ -119,7 +120,7 @@ public ExaminerEnrollmentGradesDTO getAppointmentGrades(final String oid, final enrollmentAppointment ); - return enrollmentGradeOptional.map(this::createGradesDTO).orElse(null); + return enrollmentGradeOptional.map(ExaminerUtil::createGradesDTO).orElse(null); } @Transactional(readOnly = true) @@ -193,36 +194,7 @@ public ExaminerEnrollmentGradesDTO upsertAppointmentGrades( enrollmentAppointment.setGrade(enrollmentGrade); enrollmentAppointmentRepository.saveAndFlush(enrollmentAppointment); - return createGradesDTO(enrollmentGrade); - } - - private ExaminerEnrollmentGradesDTO createGradesDTO(final EnrollmentGrade enrollmentGrade) { - return ExaminerEnrollmentGradesDTO - .builder() - .version(enrollmentGrade.getVersion()) - .writingPartialExam( - createGradeDTO(enrollmentGrade.getWritingPartialExamGrade(), enrollmentGrade.getWritingPartialExamComment()) - ) - .readingComprehensionPartialExam( - createGradeDTO( - enrollmentGrade.getReadingComprehensionPartialExamGrade(), - enrollmentGrade.getReadingComprehensionPartialExamComment() - ) - ) - .speakingPartialExam( - createGradeDTO(enrollmentGrade.getSpeakingPartialExamGrade(), enrollmentGrade.getSpeakingPartialExamComment()) - ) - .speechComprehensionPartialExam( - createGradeDTO( - enrollmentGrade.getSpeechComprehensionPartialExamGrade(), - enrollmentGrade.getSpeechComprehensionPartialExamComment() - ) - ) - .build(); - } - - private EnrollmentGradeDTO createGradeDTO(final EnrollmentGradeType grade, final String comment) { - return grade == null ? null : EnrollmentGradeDTO.builder().grade(grade).comment(comment).build(); + return ExaminerUtil.createGradesDTO(enrollmentGrade); } @Transactional @@ -295,7 +267,8 @@ public List getEnrollmentAppointmentHis return enrollmentAppointments .stream() - .map(e -> ClerkEnrollmentUtil.createClerkEnrollmentAppointmentHistoryDTO(enrollmentAppointment)) + .filter(e -> e.getId() != enrollmentAppointmentId) + .map(ClerkEnrollmentUtil::createClerkEnrollmentAppointmentHistoryDTO) .toList(); } } diff --git a/backend/vkt/src/main/java/fi/oph/vkt/util/ClerkEnrollmentUtil.java b/backend/vkt/src/main/java/fi/oph/vkt/util/ClerkEnrollmentUtil.java index dff161cce..6ee8dc874 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/util/ClerkEnrollmentUtil.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/util/ClerkEnrollmentUtil.java @@ -17,12 +17,14 @@ import fi.oph.vkt.audit.dto.ClerkEnrollmentAuditDTO; import fi.oph.vkt.model.Enrollment; import fi.oph.vkt.model.EnrollmentAppointment; +import fi.oph.vkt.model.EnrollmentGrade; import fi.oph.vkt.model.Examiner; import fi.oph.vkt.model.FreeEnrollment; import fi.oph.vkt.model.KoskiEducations; import fi.oph.vkt.model.Person; import java.util.Comparator; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; public class ClerkEnrollmentUtil { @@ -243,6 +245,7 @@ public static ExaminerEnrollmentAppointmentHistoryDTO createClerkEnrollmentAppoi final ExaminerExamEventDTO examinerExamEventDTO = enrollmentAppointment.getExaminerExamEvent() != null ? ExaminerUtil.toExaminerExamEventWithoutEnrollmentsDTO(enrollmentAppointment.getExaminerExamEvent()) : null; + final EnrollmentGrade grade = enrollmentAppointment.getGrade(); return ExaminerEnrollmentAppointmentHistoryDTO .builder() @@ -256,6 +259,7 @@ public static ExaminerEnrollmentAppointmentHistoryDTO createClerkEnrollmentAppoi .readingComprehensionPartialExam(enrollmentAppointment.isReadingComprehensionPartialExam()) .examEvent(examinerExamEventDTO) .examinerName(examiner.getNickname() + " " + examiner.getLastName()) + .grades(grade != null ? ExaminerUtil.createGradesDTO(grade) : null) .build(); } } diff --git a/backend/vkt/src/main/java/fi/oph/vkt/util/ExaminerUtil.java b/backend/vkt/src/main/java/fi/oph/vkt/util/ExaminerUtil.java index dd7a0a60f..70869ab29 100644 --- a/backend/vkt/src/main/java/fi/oph/vkt/util/ExaminerUtil.java +++ b/backend/vkt/src/main/java/fi/oph/vkt/util/ExaminerUtil.java @@ -1,13 +1,18 @@ package fi.oph.vkt.util; +import fi.oph.vkt.api.dto.EnrollmentGradeDTO; import fi.oph.vkt.api.dto.MunicipalityDTO; import fi.oph.vkt.api.dto.examiner.ExaminerContactRequestDTO; import fi.oph.vkt.api.dto.examiner.ExaminerDetailsDTO; +import fi.oph.vkt.api.dto.examiner.ExaminerEnrollmentGradesDTO; import fi.oph.vkt.api.dto.examiner.ExaminerExamEventDTO; import fi.oph.vkt.model.EnrollmentAppointment; +import fi.oph.vkt.model.EnrollmentGrade; import fi.oph.vkt.model.Examiner; import fi.oph.vkt.model.ExaminerExamEvent; import fi.oph.vkt.model.Municipality; +import fi.oph.vkt.model.type.EnrollmentGradeType; + import java.util.List; public class ExaminerUtil { @@ -92,4 +97,34 @@ public static ExaminerDetailsDTO toExaminerDetailsDTO( .contactRequests(enrollmentAppointments.stream().map(ExaminerUtil::toContactRequestDTO).toList()) .build(); } + + private static EnrollmentGradeDTO createGradeDTO(final EnrollmentGradeType grade, final String comment) { + return grade == null ? null : EnrollmentGradeDTO.builder().grade(grade).comment(comment).build(); + } + + public static ExaminerEnrollmentGradesDTO createGradesDTO(final EnrollmentGrade enrollmentGrade) { + return ExaminerEnrollmentGradesDTO + .builder() + .version(enrollmentGrade.getVersion()) + .writingPartialExam( + createGradeDTO(enrollmentGrade.getWritingPartialExamGrade(), enrollmentGrade.getWritingPartialExamComment()) + ) + .readingComprehensionPartialExam( + createGradeDTO( + enrollmentGrade.getReadingComprehensionPartialExamGrade(), + enrollmentGrade.getReadingComprehensionPartialExamComment() + ) + ) + .speakingPartialExam( + createGradeDTO(enrollmentGrade.getSpeakingPartialExamGrade(), enrollmentGrade.getSpeakingPartialExamComment()) + ) + .speechComprehensionPartialExam( + createGradeDTO( + enrollmentGrade.getSpeechComprehensionPartialExamGrade(), + enrollmentGrade.getSpeechComprehensionPartialExamComment() + ) + ) + .build(); + } + } diff --git a/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetailsFields.tsx b/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetailsFields.tsx index 579742c2f..3707c09e7 100644 --- a/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetailsFields.tsx +++ b/frontend/packages/vkt/src/components/clerkEnrollment/appointment/ClerkEnrollmentAppointmentDetailsFields.tsx @@ -35,7 +35,7 @@ import { useCommonTranslation, useKoodistoMunicipalitiesTranslation, } from 'configs/i18n'; -import { useAppDispatch } from 'configs/redux'; +import { useAppDispatch, useAppSelector } from 'configs/redux'; import { EnrollmentAppointmentStatus } from 'enums/app'; import { ClerkEnrollmentTextFieldEnum } from 'enums/clerkEnrollment'; import { @@ -46,6 +46,7 @@ import { ClerkEnrollmentTextFieldProps } from 'interfaces/clerkEnrollmentTextFie import { PartialExamsAndSkills } from 'interfaces/common/enrollment'; import { ExaminerExamEvent } from 'interfaces/examinerExamEvent'; import { sendClerkEnrollmentAppointmentAuthLink } from 'redux/reducers/clerkEnrollmentAppointment'; +import { clerkEnrollmentAppointmentSelector } from 'redux/selectors/clerkEnrollmentAppointment'; import { DateTimeUtils } from 'utils/dateTime'; const CheckboxField = ({ @@ -365,6 +366,7 @@ export const ClerkEnrollmentAppointmentDetailsFields = ({ const [enrollmentHistoryModalOpen, setEnrollmentHistoryModalOpen] = useState(false); const [gradeModalOpen, setGradeModalOpen] = useState(false); + const { grades } = useAppSelector(clerkEnrollmentAppointmentSelector); const initialFieldErrors = Object.values( ClerkEnrollmentAppointmentDetailsFields, @@ -544,7 +546,7 @@ export const ClerkEnrollmentAppointmentDetailsFields = ({ )} {isViewMode ? ( - + ) : ( {enrollmentHistory && enrollmentHistory.map( - (enrollment: ClerkEnrollmentAppointmentHistory) => ( - - - {ExamEventUtils.languageAndLevelText( - enrollment.examEvent.language, - ExamLevel.GOOD_AND_SATISFACTORY, - translateCommon, - )} - {', '} - {DateTimeUtils.renderDate(enrollment.examEvent.date)} - + ( + enrollment: ClerkEnrollmentAppointmentHistory, + idx: number, + ) => ( + + }> + + {ExamEventUtils.languageAndLevelText( + enrollment.examEvent.language, + ExamLevel.GOOD_AND_SATISFACTORY, + translateCommon, + )} + {', '} + {DateTimeUtils.renderDate(enrollment.examEvent.date)} + +
- -
+ + + + ), )} diff --git a/frontend/packages/vkt/src/components/clerkEnrollment/appointment/EnrollmentSkillsListTable.tsx b/frontend/packages/vkt/src/components/clerkEnrollment/appointment/EnrollmentSkillsListTable.tsx index f1948eca5..503417c04 100644 --- a/frontend/packages/vkt/src/components/clerkEnrollment/appointment/EnrollmentSkillsListTable.tsx +++ b/frontend/packages/vkt/src/components/clerkEnrollment/appointment/EnrollmentSkillsListTable.tsx @@ -2,14 +2,20 @@ import { Fragment } from 'react'; import { H3, Text } from 'shared/components'; import { useClerkTranslation, useCommonTranslation } from 'configs/i18n'; -import { useAppSelector } from 'configs/redux'; -import { PartialExamsAndSkills } from 'interfaces/common/enrollment'; -import { clerkEnrollmentAppointmentSelector } from 'redux/selectors/clerkEnrollmentAppointment'; +import { ClerkEnrollmentAppointmentGrades } from 'interfaces/clerkEnrollment'; +import { + PartialExams, + PartialExamsAndSkills, +} from 'interfaces/common/enrollment'; + +interface GradedPartialExams extends Omit {} export const EnrollmentSkillsListTable = ({ enrollment, + grades, }: { enrollment: PartialExamsAndSkills; + grades: ClerkEnrollmentAppointmentGrades; }) => { const translateCommon = useCommonTranslation(); const { t } = useClerkTranslation({ @@ -19,25 +25,23 @@ export const EnrollmentSkillsListTable = ({ const partialTextualExams = [ 'writingPartialExam', 'readingComprehensionPartialExam', - ].filter( - (exam) => !!enrollment[exam as keyof PartialExamsAndSkills], - ) as Array; + ].filter((exam) => !!enrollment[exam as keyof GradedPartialExams]) as Array< + keyof GradedPartialExams + >; const partialOralExams = [ 'speakingPartialExam', 'speechComprehensionPartialExam', - ].filter( - (exam) => !!enrollment[exam as keyof PartialExamsAndSkills], - ) as Array; - - const { grades } = useAppSelector(clerkEnrollmentAppointmentSelector); + ].filter((exam) => !!enrollment[exam as keyof GradedPartialExams]) as Array< + keyof GradedPartialExams + >; const renderGrade = (grade: string) => grade && grade !== '' ? translateCommon(`enrollment.grades.${grade}`) : '-'; const renderComment = (comment: string) => comment && comment !== '' ? comment : '-'; - const partialExamsRow = (exams: Array) => { + const partialExamsRow = (exams: Array) => { return exams.map((exam, idx) => ( {idx > 0 &&
} @@ -46,8 +50,8 @@ export const EnrollmentSkillsListTable = ({ {translateCommon(`enrollment.partialExamsAndSkills.${exam}`)}
- {renderGrade(grades[exam]?.grade)} - {renderComment(grades[exam]?.comment)} + {renderGrade(grades && grades[exam]?.grade)} + {renderComment(grades && grades[exam]?.comment)}
)); }; diff --git a/frontend/packages/vkt/src/interfaces/clerkEnrollment.ts b/frontend/packages/vkt/src/interfaces/clerkEnrollment.ts index 2157778d0..508e677ce 100644 --- a/frontend/packages/vkt/src/interfaces/clerkEnrollment.ts +++ b/frontend/packages/vkt/src/interfaces/clerkEnrollment.ts @@ -134,6 +134,7 @@ export interface ClerkEnrollmentAppointmentHistory enrollmentTime: Dayjs; examEvent: ExaminerExamEvent; examinerName: string; + grades: ClerkEnrollmentAppointmentGrades; } export interface ClerkEnrollmentAppointmentHistoryResponse