diff --git a/CHANGELOG.md b/CHANGELOG.md index 703633881bb..e35bfc88924 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ ### Internal +- Bump AGP to v9.4.0 +- Bump Lint to v32.4.0 + +## 2026.09.03 + +### Internal + - Bump AndroidX Benchmark to v1.5.0-rc02 - Bump Sentry to v8.54.0 - Bump Sentry Android to v6.20.0 @@ -23,12 +30,16 @@ - Update GH Actions `setup-jdk` to v6 - Replaced deprecated Gradle Kotlin DSL by project property delegates with explicit project.property(...) access to improve Gradle 10 compatibility. - ### Changes - Enabled CDSS nudges for all countries, previously limited to Sri Lanka and Ethiopia. - Applied the lower blood pressure threshold of 130/80 for diabetic patients in Sri Lanka. +### Fixes + +- Fixed a crash when loading statin information for a patient. +- Fixed a crash when loading the patient's assigned facility. + ## 2026.08.31 ### Internal diff --git a/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffect.kt b/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffect.kt index b3379b7ddfa..e4561d353eb 100644 --- a/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffect.kt +++ b/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffect.kt @@ -83,7 +83,9 @@ data class UpdateCVDRisk( val newRiskRange: CVDRiskRange ) : PatientSummaryEffect() -data class LoadStatinInfo(val patientUuid: UUID) : PatientSummaryEffect() +data class LoadStatinInfo( + val patient: Patient +) : PatientSummaryEffect() data class LoadCVDRiskInfo(val patientUuid: UUID) : PatientSummaryEffect() diff --git a/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffectHandler.kt b/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffectHandler.kt index 0b6ce697229..f25d7ca35f2 100644 --- a/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffectHandler.kt +++ b/app/src/main/java/org/simple/clinic/summary/PatientSummaryEffectHandler.kt @@ -307,14 +307,13 @@ class PatientSummaryEffectHandler @AssistedInject constructor( effects .observeOn(schedulersProvider.io()) .map { effect -> - val patientUuid = effect.patientUuid - val patient = patientRepository.patientImmediate(patientUuid) + val patient = effect.patient val medicalHistory = medicalHistoryRepository.historyForPatientOrDefaultImmediate( defaultHistoryUuid = uuidGenerator.v4(), - patientUuid = patientUuid + patientUuid = patient.uuid ) - val patientAttribute = patientAttributeRepository.getPatientAttributeImmediate(patientUuid) - val riskRange = cvdRiskRepository.getCVDRiskImmediate(patientUuid)?.riskScore + val patientAttribute = patientAttributeRepository.getPatientAttributeImmediate(patient.uuid) + val riskRange = cvdRiskRepository.getCVDRiskImmediate(patient.uuid)?.riskScore val canPrescribeStatin = if (country.isoCountryCode == Country.SRI_LANKA) { riskRange?.canPrescribeStatinInSriLanka ?: false } else { @@ -322,7 +321,7 @@ class PatientSummaryEffectHandler @AssistedInject constructor( } StatinInfoLoaded( - age = patient!!.ageDetails.estimateAge(userClock), + age = patient.ageDetails.estimateAge(userClock), medicalHistory = medicalHistory, canPrescribeStatin = canPrescribeStatin, riskRange = riskRange, @@ -523,12 +522,6 @@ class PatientSummaryEffectHandler @AssistedInject constructor( .flatMap(Function { facilityRepository.facility(it) }) } - private fun getAssignedFacility(assignedFacilityId: UUID?): Optional { - return Optional - .ofNullable(assignedFacilityId) - .flatMap { facilityRepository.facility(it) } - } - private fun mapPatientProfileToSummaryProfile( patientProfile: PatientProfile, facility: Optional @@ -564,7 +557,7 @@ class PatientSummaryEffectHandler @AssistedInject constructor( missingPhoneReminderRepository .markReminderAsShownFor(effect.patientUuid) .subscribeOn(scheduler) - .andThen(Observable.empty()) + .andThen(Observable.empty()) } } } diff --git a/app/src/main/java/org/simple/clinic/summary/PatientSummaryUpdate.kt b/app/src/main/java/org/simple/clinic/summary/PatientSummaryUpdate.kt index 3e63d1d16b6..f914fa417cc 100644 --- a/app/src/main/java/org/simple/clinic/summary/PatientSummaryUpdate.kt +++ b/app/src/main/java/org/simple/clinic/summary/PatientSummaryUpdate.kt @@ -106,7 +106,7 @@ class PatientSummaryUpdate( is HypertensionNotNowClicked -> hypertensionNotNowClicked(event.continueToDiabetesDiagnosisWarning) is StatinPrescriptionCheckInfoLoaded -> statinPrescriptionCheckInfoLoaded(event, model) is CVDRiskCalculated -> saveOrUpdateCVDRisk(event, model) - is CVDRiskUpdated -> dispatch(LoadStatinInfo(model.patientUuid)) + is CVDRiskUpdated -> dispatch(LoadStatinInfo(model.patientSummaryProfile!!.patient)) is StatinInfoLoaded -> statinInfoLoaded(event, model) is AddTobaccoUseClicked -> dispatch(ShowTobaccoStatusDialog) is TobaccoUseAnswered -> dispatch(UpdateTobaccoUse(model.patientUuid, event.isSmoker, event.isUsingSmokelessTobacco)) @@ -237,7 +237,7 @@ class PatientSummaryUpdate( } isEligibleForLabBasedCvdRisk -> { - dispatch(LoadStatinInfo(model.patientUuid)) + dispatch(LoadStatinInfo(model.patientSummaryProfile!!.patient)) } else -> { @@ -294,7 +294,7 @@ class PatientSummaryUpdate( } isEligibleForNonLabBasedCvdRisk -> { - dispatch(LoadStatinInfo(model.patientUuid)) + dispatch(LoadStatinInfo(model.patientSummaryProfile!!.patient)) } else -> { @@ -314,7 +314,7 @@ class PatientSummaryUpdate( model: PatientSummaryModel ): Next { return when { - event.newRiskRange == null -> dispatch(LoadStatinInfo(model.patientUuid)) + event.newRiskRange == null -> dispatch(LoadStatinInfo(model.patientSummaryProfile!!.patient)) event.oldRisk != null -> dispatch(UpdateCVDRisk(event.oldRisk, event.newRiskRange)) else -> dispatch(SaveCVDRisk(model.patientUuid, event.newRiskRange)) } diff --git a/app/src/main/java/org/simple/clinic/summary/assignedfacility/AssignedFacilityEffectHandler.kt b/app/src/main/java/org/simple/clinic/summary/assignedfacility/AssignedFacilityEffectHandler.kt index d4d65abdf63..fbaad861bcf 100644 --- a/app/src/main/java/org/simple/clinic/summary/assignedfacility/AssignedFacilityEffectHandler.kt +++ b/app/src/main/java/org/simple/clinic/summary/assignedfacility/AssignedFacilityEffectHandler.kt @@ -36,7 +36,7 @@ class AssignedFacilityEffectHandler @AssistedInject constructor( return ObservableTransformer { effects -> effects .observeOn(schedulersProvider.io()) - .map { (patientUuid, assignedFacilityId) -> + .doOnNext { (patientUuid, assignedFacilityId) -> patientRepository.updateAssignedFacilityId(patientUuid, assignedFacilityId) } .map { AssignedFacilityChanged } @@ -47,8 +47,11 @@ class AssignedFacilityEffectHandler @AssistedInject constructor( return ObservableTransformer { effects -> effects .observeOn(schedulersProvider.io()) - .map { patientRepository.patientImmediate(it.patientUuid) } - .map(::getAssignedFacility) + .map { effect -> + patientRepository.patientImmediate(effect.patientUuid) + ?.let(::getAssignedFacility) + ?: Optional.empty() + } .map(::AssignedFacilityLoaded) } } diff --git a/app/src/test/java/org/simple/clinic/summary/PatientSummaryEffectHandlerTest.kt b/app/src/test/java/org/simple/clinic/summary/PatientSummaryEffectHandlerTest.kt index 9c7803e7ad4..cd50454aaec 100644 --- a/app/src/test/java/org/simple/clinic/summary/PatientSummaryEffectHandlerTest.kt +++ b/app/src/test/java/org/simple/clinic/summary/PatientSummaryEffectHandlerTest.kt @@ -988,9 +988,7 @@ class PatientSummaryEffectHandlerTest { @Test fun `when load statin info effect is received, then load statin info`() { //given - val bmiReading = BMIReading(height = 177f, weight = 53f) - - whenever(patientRepository.patientImmediate(patientUuid)) doReturn TestData.patient( + val patient = TestData.patient( uuid = patientUuid, patientAgeDetails = PatientAgeDetails( ageValue = 55, @@ -998,6 +996,9 @@ class PatientSummaryEffectHandlerTest { dateOfBirth = null, ) ) + val bmiReading = BMIReading(height = 177f, weight = 53f) + + whenever(patientRepository.patientImmediate(patientUuid)) doReturn patient val medicalHistory = TestData.medicalHistory(isSmoking = Yes) whenever(medicalHistoryRepository.historyForPatientOrDefaultImmediate( @@ -1012,7 +1013,7 @@ class PatientSummaryEffectHandlerTest { TestData.cvdRisk(riskScore = CVDRiskRange(27, 27)) //when - testCase.dispatch(LoadStatinInfo(patientUuid)) + testCase.dispatch(LoadStatinInfo(patient)) //then testCase.assertOutgoingEvents(StatinInfoLoaded( diff --git a/app/src/test/java/org/simple/clinic/summary/PatientSummaryUpdateTest.kt b/app/src/test/java/org/simple/clinic/summary/PatientSummaryUpdateTest.kt index 6e3a7c7d748..ea34beab6ae 100644 --- a/app/src/test/java/org/simple/clinic/summary/PatientSummaryUpdateTest.kt +++ b/app/src/test/java/org/simple/clinic/summary/PatientSummaryUpdateTest.kt @@ -2463,7 +2463,7 @@ class PatientSummaryUpdateTest { )) .then(assertThatNext( hasNoModel(), - hasEffects(LoadStatinInfo(patientUuid)) + hasEffects(LoadStatinInfo(model.patientSummaryProfile!!.patient)) )) } @@ -2496,14 +2496,15 @@ class PatientSummaryUpdateTest { @Test fun `when cvd risk score is calculated and both range and old cvd risk are null, then load statin info`() { + val model = defaultModel.patientSummaryProfileLoaded(patientSummaryProfile) updateSpec - .given(defaultModel) + .given(model) .whenEvent(CVDRiskCalculated( oldRisk = null, newRiskRange = null )) .then(assertThatNext( - hasEffects(LoadStatinInfo(defaultModel.patientUuid)) + hasEffects(LoadStatinInfo(model.patientSummaryProfile!!.patient)) )) } @@ -2995,7 +2996,7 @@ class PatientSummaryUpdateTest { )) .then(assertThatNext( hasNoModel(), - hasEffects(LoadStatinInfo(patientUuid)) + hasEffects(LoadStatinInfo(model.patientSummaryProfile!!.patient)) )) } diff --git a/app/src/test/java/org/simple/clinic/summary/assignedfacility/AssignedFacilityEffectHandlerTest.kt b/app/src/test/java/org/simple/clinic/summary/assignedfacility/AssignedFacilityEffectHandlerTest.kt index 273c17c4d8f..da96f6468b4 100644 --- a/app/src/test/java/org/simple/clinic/summary/assignedfacility/AssignedFacilityEffectHandlerTest.kt +++ b/app/src/test/java/org/simple/clinic/summary/assignedfacility/AssignedFacilityEffectHandlerTest.kt @@ -90,4 +90,22 @@ class AssignedFacilityEffectHandlerTest { verify(uiActions).notifyAssignedFacilityChanged() verifyNoMoreInteractions(uiActions) } + + @Test + fun `when load assigned facility effect is received and patient is not found, then assigned facility is empty`() { + // given + val patientUuid = UUID.fromString("17bb9690-9a17-4d90-a45f-8bfdcc4153e4") + + whenever(patientRepository.patientImmediate(patientUuid)) doReturn null + + // when + effectHandlerTestCase.dispatch(LoadAssignedFacility(patientUuid)) + + // then + effectHandlerTestCase.assertOutgoingEvents( + AssignedFacilityLoaded(Optional.empty()) + ) + + verifyNoInteractions(uiActions) + } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b665d057efe..2cbd5af4fcf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -agp = "9.3.2" +agp = "9.4.0" androidx-cameraView = "1.6.2" androidx-camera = "1.6.2" @@ -21,7 +21,7 @@ ksp = "2.3.11" ktlint = "0.36.0" -lint = "32.3.2" +lint = "32.4.0" mobius = "2.1.2"