@@ -18,6 +18,7 @@ import {
1818 ProgrammeType ,
1919 NoticeType ,
2020 MoveSource ,
21+ NotifyEmailStatus ,
2122 RegistrationOutcome ,
2223 SchoolPhase ,
2324 ScreenOutcome ,
@@ -28,6 +29,7 @@ import {
2829 UserRole ,
2930 ReplyDecision ,
3031 ReplyMethod ,
32+ ReplyRefusal ,
3133 VaccinationOutcome ,
3234 VaccinationSource
3335} from '../app/enums.js'
@@ -59,7 +61,8 @@ import {
5961 Session ,
6062 Team ,
6163 User ,
62- Vaccination
64+ Vaccination ,
65+ Vaccine
6366} from '../app/models.js'
6467import {
6568 getDateValueDifference ,
@@ -1076,37 +1079,44 @@ for (const patient_uuid of mmrCohortUuids) {
10761079 mmrCohortPatientSessionUuids [ patient_uuid ] = patientSession . uuid
10771080}
10781081
1079- // Dmitri's parent responds via the consent process declaring him already
1080- // vaccinated, supplying dates that match his two seeded vaccinations. Mavis
1081- // still ignores the early first dose per the 12-month rule, so the record
1082- // preserves the parent-vs-schedule mismatch that's the point of the demo .
1082+ // Ensure every cohort member has a parent with at least an email, so consent
1083+ // replies have something to display on the session report and patient pages.
1084+ // Runs before the replies below so each Reply's internal Parent copy includes
1085+ // the forced contact details .
10831086const dmitri = context . patients [ 'mmr00004-0000-4000-8000-000000000004' ]
1084- const dmitriParent = generateParent ( dmitri . lastName , true )
1085- dmitri . parent1 = dmitriParent
1086-
1087+ for ( const patient_uuid of mmrCohortUuids ) {
1088+ const p = context . patients [ patient_uuid ]
1089+ if ( ! p . parent1 ) {
1090+ p . parent1 = generateParent ( p . lastName , true )
1091+ }
1092+ if ( ! p . parent1 . email ) {
1093+ const firstName = p . parent1 . fullName . split ( ' ' ) [ 0 ] . toLowerCase ( )
1094+ const lastName = p . lastName . toLowerCase ( )
1095+ p . parent1 . email = `${ firstName } .${ lastName } @example.com`
1096+ }
1097+ // Force a Delivered emailStatus so the session report doesn't flag the
1098+ // cohort with random Notify failure states (which the research scenario
1099+ // isn't about).
1100+ p . parent1 . emailStatus = NotifyEmailStatus . Delivered
1101+ }
1102+ const dmitriParent = dmitri . parent1
1103+
1104+ // Dmitri's parent refuses consent citing that he's already been vaccinated.
1105+ // This is the fallback shape for the "parent says already vaccinated"
1106+ // scenario: using ReplyDecision.AlreadyVaccinated directly requires a
1107+ // paired Vaccination record for Mavis to report the patient as vaccinated,
1108+ // which the seed doesn't model. Refused with reason "Vaccine already
1109+ // received" captures the intent without tripping that gap.
10871110const dmitriConsent = new Consent (
10881111 {
10891112 uuid : 'mmr00004-r001-4000-8000-000000000001' ,
10901113 createdAt : addDays ( today ( ) , - 3 ) ,
10911114 createdBy_uid : nurse . uid ,
10921115 child : dmitri ,
10931116 parent : dmitriParent ,
1094- decision : ReplyDecision . AlreadyVaccinated ,
1117+ decision : ReplyDecision . Refused ,
1118+ refusalReason : ReplyRefusal . AlreadyVaccinated ,
10951119 method : ReplyMethod . Website ,
1096- firstDose : {
1097- outcome : VaccinationOutcome . AlreadyVaccinated ,
1098- createdAt : addDays ( addMonths ( dmitri . dob , 11 ) , 29 ) ,
1099- sequence : '1P' ,
1100- country : 'England' ,
1101- programme_id : 'mmr'
1102- } ,
1103- secondDose : {
1104- outcome : VaccinationOutcome . AlreadyVaccinated ,
1105- createdAt : addDays ( addMonths ( dmitri . dob , 48 ) , 14 ) ,
1106- sequence : '2P' ,
1107- country : 'England' ,
1108- programme_id : 'mmr'
1109- } ,
11101120 programme_id : 'mmr' ,
11111121 session_id : mmrCatchupSessionId
11121122 } ,
@@ -1115,6 +1125,97 @@ const dmitriConsent = new Consent(
11151125dmitriConsent . linkToPatient ( dmitri )
11161126context . replies [ dmitriConsent . uuid ] = dmitriConsent
11171127
1128+ // Co-located Doubles session (MenACWY + Td/IPV) at the same school on the
1129+ // same day, so the SAIS team is running MMR catch-up alongside the teenage
1130+ // boosters. The cohort gets consent responses seeded for all three
1131+ // programmes so the research scenario isn't blocked by "no reply yet".
1132+ const doublesCatchupSessionId = 'doubles-catchup-y9y10-current'
1133+
1134+ context . sessions [ doublesCatchupSessionId ] = new Session (
1135+ {
1136+ id : doublesCatchupSessionId ,
1137+ createdAt : mmrCatchupOpenAt ,
1138+ createdBy_uid : nurse . uid ,
1139+ date : mmrCatchupSessionDate ,
1140+ openAt : mmrCatchupOpenAt ,
1141+ academicYear : getAcademicYear ( mmrCatchupSessionDate ) ,
1142+ type : SessionType . School ,
1143+ school_id : mmrCatchupSchoolId ,
1144+ presetNames : [ SessionPresetName . Doubles ] ,
1145+ registration : true
1146+ } ,
1147+ context
1148+ )
1149+
1150+ // Add the cohort to the Doubles session (one PatientSession per programme).
1151+ const doublesProgrammeIds = [ 'menacwy' , 'td-ipv' ]
1152+ for ( const patient_uuid of mmrCohortUuids ) {
1153+ for ( const programme_id of doublesProgrammeIds ) {
1154+ const patientSession = new PatientSession (
1155+ {
1156+ createdAt : mmrCatchupOpenAt ,
1157+ patient_uuid,
1158+ programme_id,
1159+ session_id : doublesCatchupSessionId
1160+ } ,
1161+ context
1162+ )
1163+ context . patientSessions [ patientSession . uuid ] = patientSession
1164+ context . patients [ patient_uuid ] . patientSession_uuids . push (
1165+ patientSession . uuid
1166+ )
1167+ }
1168+ }
1169+
1170+ // Positive consent for every cohort patient × programme, except Dmitri's MMR
1171+ // reply which is already AlreadyVaccinated (above). One Reply per programme
1172+ // mirrors how Mavis stores Doubles responses in the real data.
1173+ function buildAllNoHealthAnswers ( programme ) {
1174+ const snomed = programme . vaccine_snomeds [ 0 ]
1175+ const vaccine = new Vaccine ( context . vaccines [ snomed ] , context )
1176+ const answers = { }
1177+ for ( const key of Object . keys ( vaccine . flatHealthQuestions ) ) {
1178+ answers [ key ] = { answer : 'No' }
1179+ }
1180+ return answers
1181+ }
1182+
1183+ const programmeReplyIndex = { mmr : 1 , menacwy : 2 , 'td-ipv' : 3 }
1184+ for ( const patient_uuid of mmrCohortUuids ) {
1185+ const patient = context . patients [ patient_uuid ]
1186+ const parent = patient . parent1
1187+ const patientPrefix = patient_uuid . slice ( 0 , 8 )
1188+
1189+ for ( const programme_id of [ 'mmr' , 'menacwy' , 'td-ipv' ] ) {
1190+ // Dmitri's MMR reply is AlreadyVaccinated, seeded above
1191+ if ( programme_id === 'mmr' && patient_uuid === dmitri . uuid ) continue
1192+
1193+ const session_id =
1194+ programme_id === 'mmr' ? mmrCatchupSessionId : doublesCatchupSessionId
1195+ const programme = context . programmes [ programme_id ]
1196+ const idx = programmeReplyIndex [ programme_id ]
1197+ const uuid = `${ patientPrefix } -r${ String ( idx ) . padStart ( 3 , '0' ) } -4000-8000-000000000001`
1198+
1199+ const consent = new Consent (
1200+ {
1201+ uuid,
1202+ createdAt : addDays ( mmrCatchupOpenAt , 3 + idx ) ,
1203+ createdBy_uid : nurse . uid ,
1204+ child : patient ,
1205+ parent,
1206+ decision : ReplyDecision . Given ,
1207+ method : ReplyMethod . Website ,
1208+ healthAnswers : buildAllNoHealthAnswers ( programme ) ,
1209+ programme_id,
1210+ session_id
1211+ } ,
1212+ context
1213+ )
1214+ consent . linkToPatient ( patient )
1215+ context . replies [ consent . uuid ] = consent
1216+ }
1217+ }
1218+
11181219// Generate date files
11191220generateDataFile ( '.data/batches.json' , context . batches )
11201221generateDataFile ( '.data/clinic-appointments.json' , context . clinicAppointments )
0 commit comments