diff --git a/departments/department-programs-config.json b/departments/department-programs-config.json index 63cdd985c..587f35739 100755 --- a/departments/department-programs-config.json +++ b/departments/department-programs-config.json @@ -212,5 +212,14 @@ "name": "RETENTION PROGRAM" } ] + }, + "uud7": { + "name": "HTS", + "programs": [ + { + "uuid": "a0f8382f-df8a-4f1d-8959-9fb6eef90353", + "name": "HTS PROGRAM" + } + ] } } diff --git a/programs/patient-program-config.json b/programs/patient-program-config.json index ee1369d51..8ac541850 100755 --- a/programs/patient-program-config.json +++ b/programs/patient-program-config.json @@ -5699,5 +5699,81 @@ ] } ] + }, + "a0f8382f-df8a-4f1d-8959-9fb6eef90353": { + "name": "HTS PROGRAM", + "dataDependencies": ["patient", "enrollment", "patientEncounters"], + "incompatibleWith": [], + "visitTypes": [ + { + "uuid": "f92e8456-4de6-4776-82c3-c15b81eb8f5d", + "name": "HTS New/Retest Visit", + "message": "HTS retest should have been done.", + "allowedIf": "", + "encounterTypes": [ + { + "uuid": "82749926-63b1-467b-9d41-453c7542678a", + "display": "HTSSCREENING", + "allowedIf": "showOnlyHTSScreening" + }, + { + "uuid": "ae9693ff-d341-4997-8166-fa46ac4d38f4", + "display": "HTSINITIAL", + "allowedIf": "showOnlyHTSINITIAL" + }, + { + "uuid": "16bac581-5a17-43f2-9315-3e12a1f3189a", + "display": "HTSRETEST", + "allowedIf": "showOnlyHTSRetest" + }, + { + "uuid": "fbb106cf-d24f-4917-b905-42db7549a788", + "display": "HTSLINKAGE", + "allowedIf": "showOthersHTSEncounters" + }, + { + "uuid": "55c10a7a-2732-4063-be25-68d5e1bce1fc", + "display": "HTSREFERRAL", + "allowedIf": "showOthersHTSEncounters" + } + ] + }, + { + "uuid": "7d2b0fb1-f1d7-4173-81aa-c8b832407656", + "name": "HTS Retest To Confirm Positive Visit", + "message": "HTS retest should have been done.", + "allowedIf": "", + "encounterTypes": [ + { + "uuid": "16bac581-5a17-43f2-9315-3e12a1f3189a", + "display": "HTSRETEST" + }, + { + "uuid": "55c10a7a-2732-4063-be25-68d5e1bce1fc", + "display": "HTSREFERRAL" + }, + { + "uuid": "fbb106cf-d24f-4917-b905-42db7549a788", + "display": "HTSLINKAGE" + } + ] + }, + { + "uuid": "81978aea-61c1-426e-82e6-ade88f4e25ef", + "name": "HTS Linkage Visit", + "message": "HTS retest should have been done.", + "allowedIf": "", + "encounterTypes": [ + { + "uuid": "fbb106cf-d24f-4917-b905-42db7549a788", + "display": "HTSLINKAGE" + }, + { + "uuid": "55c10a7a-2732-4063-be25-68d5e1bce1fc", + "display": "HTSREFERRAL" + } + ] + } + ] } } diff --git a/programs/scope-builder.service.js b/programs/scope-builder.service.js index 0fa02288d..4dc43988b 100755 --- a/programs/scope-builder.service.js +++ b/programs/scope-builder.service.js @@ -16,7 +16,12 @@ function buildScope(dataDictionary) { lastCovidScreeningDate: '', retroSpective: false, screenedForCovidToday: false, - isViremicHighVL: false + isViremicHighVL: false, + hasHTSEncounters: false, + showOnlyHTSScreening: false, + showOnlyHTSINITIAL: false, + showOnlyHTSRetest: false, + showOthersHTSEncounters: false }; let isStandardDcVisit = false; @@ -272,7 +277,6 @@ function buildScope(dataDictionary) { } else { const today = Moment().format('YYYY-MM-DD'); const visitDate = Moment(dataDictionary.visitDate).format('YYYY-MM-DD'); - if (dataDictionary.latestCovidAssessment) { scope['lastCovidScreeningDate'] = dataDictionary.latestCovidAssessment; const screeningDate = Moment(dataDictionary.latestCovidAssessment).format( @@ -305,6 +309,33 @@ function buildScope(dataDictionary) { ) { scope.isViremicHighVL = true; } + + if (dataDictionary.patientEncounters) { + scope.patientEncounters = dataDictionary.patientEncounters; + scope.programUuid = dataDictionary.programUuid; + + // Add HTS scope building alongside other program builders + if (dataDictionary.programUuid === 'a0f8382f-df8a-4f1d-8959-9fb6eef90353') { + buildHTSScopeMembers(scope, dataDictionary.patientEncounters); + } + + buildHivScopeMembers( + scope, + dataDictionary.patientEncounters, + dataDictionary?.intendedVisitLocationUuid + ); + buildOncologyScopeMembers( + scope, + dataDictionary.patientEncounters, + dataDictionary.programUuid + ); + buildMNCHScopeMembers( + scope, + dataDictionary.patientEncounters, + dataDictionary.patientEnrollment + ); + } + // add other methods to build the scope objects return scope; } @@ -546,6 +577,81 @@ function isInitialOncologyVisit(encounters, programUuid) { return initialOncologyEncounters.length === 0; } +function buildHTSScopeMembers(scope, patientEncounters) { + const HTS_ENCOUNTER_TYPES = { + SCREENING: '82749926-63b1-467b-9d41-453c7542678a', + INITIAL: 'ae9693ff-d341-4997-8166-fa46ac4d38f4', + RETEST: '16bac581-5a17-43f2-9315-3e12a1f3189a', + LINKAGE: 'fbb106cf-d24f-4917-b905-42db7549a788', + REFERRAL: '55c10a7a-2732-4063-be25-68d5e1bce1fc' + }; + + const today = Moment().startOf('day'); + + const isFromPreviousDate = (encounter) => { + const encounterDate = + encounter?.encounterDatetime && + Moment(encounter.encounterDatetime).startOf('day'); + return encounterDate ? encounterDate.isBefore(today) : false; + }; + + const getLatestEncounterOfType = (encounters, encounterTypeUuid) => + encounters + ?.filter(({ encounterType }) => encounterType?.uuid === encounterTypeUuid) + .sort( + (a, b) => + Moment(b.encounterDatetime).valueOf() - + Moment(a.encounterDatetime).valueOf() + )[0]; + + scope.hasHTSEncounters = + patientEncounters?.some(({ encounterType }) => + Object.values(HTS_ENCOUNTER_TYPES).includes(encounterType?.uuid) + ) || false; + + if (!scope.hasHTSEncounters) { + scope.showOnlyHTSScreening = true; + return scope; + } + + const latestScreening = getLatestEncounterOfType( + patientEncounters, + HTS_ENCOUNTER_TYPES.SCREENING + ); + const latestInitial = getLatestEncounterOfType( + patientEncounters, + HTS_ENCOUNTER_TYPES.INITIAL + ); + const latestRetest = getLatestEncounterOfType( + patientEncounters, + HTS_ENCOUNTER_TYPES.RETEST + ); + + const hasOldScreening = + latestScreening && isFromPreviousDate(latestScreening); + const hasOldInitial = latestInitial && isFromPreviousDate(latestInitial); + const hasScreeningToday = + latestScreening && !isFromPreviousDate(latestScreening); + const hasInitialToday = latestInitial && !isFromPreviousDate(latestInitial); + const hasRetestToday = latestRetest && !isFromPreviousDate(latestRetest); + + if (!hasScreeningToday) { + scope.showOnlyHTSScreening = true; + scope.isFirstHTSNewRetestVisit = true; + } else if (hasScreeningToday && !hasInitialToday) { + scope.showOnlyHTSINITIAL = true; + scope.isFirstHTSNewRetestVisit = false; + } else if (hasInitialToday && !hasRetestToday) { + scope.showOnlyHTSRetest = true; + } else if (hasRetestToday) { + scope.showOthersHTSEncounters = true; + } else { + scope.showOnlyHTSScreening = true; + } + + return scope; +} + function buildProgramScopeMembers(scope, programEnrollment) { if ( programEnrollment &&