Repository navigation
RAMP: August-September features - #39
Conversation
…st to enrolment list
…in tracker programs
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unresolved correctness and compatibility issues can omit form fields, alter existing Simprints module IDs, and hide enrollment information.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 1
Open (5)
What changed in this PR
Adds RAMP-specific biometric credential handling, configurable search/form behavior, history visualization, scheduling, filters, and enrollment details.
Changes:
- Captures and stores Simprints QR external credentials.
- Adds RAMP-configurable charts, visit scheduling, search, and enrollment presentation.
- Adds overdue/sync-error quick filters and extensive tests.
| File | Description |
|---|---|
form/src/test/java/org/dhis2/form/ui/FormViewModelTest.kt |
Tests credential-save sequencing and failures. |
form/src/test/java/org/dhis2/form/simprints/ramp/ui/IntegerAwareValueFormatterTest.kt |
Tests categorical chart labels. |
form/src/test/java/org/dhis2/form/simprints/ramp/data/FormHistoryChartRepositoryTest.kt |
Tests categorical and inverted charts. |
form/src/test/java/org/dhis2/form/data/FormValueStoreTest.kt |
Tests enrollment UID resolution. |
form/src/test/java/org/dhis2/form/data/FormRepositoryImplTest.kt |
Tests capture-only and credential behavior. |
form/src/test/java/org/dhis2/form/data/EnrollmentRepositoryTest.kt |
Tests capture-only biometrics fields. |
form/src/main/java/org/dhis2/form/ui/intent/FormIntent.kt |
Carries external credentials in intents. |
form/src/main/java/org/dhis2/form/ui/FormViewModel.kt |
Saves credentials after biometric GUIDs. |
form/src/main/java/org/dhis2/form/ui/FormView.kt |
Extracts credentials from callout results. |
form/src/main/java/org/dhis2/form/simprints/ramp/ui/FormHistoryChartView.kt |
Renders categorical and inverted axes. |
form/src/main/java/org/dhis2/form/simprints/ramp/model/FormHistoryChart.kt |
Models chart categories and mapping. |
form/src/main/java/org/dhis2/form/simprints/ramp/data/FormHistoryChartRepository.kt |
Loads option-set chart categories. |
form/src/main/java/org/dhis2/form/di/Injector.kt |
Wires RAMP form configuration. |
form/src/main/java/org/dhis2/form/data/FormValueStore.kt |
Exposes enrollment UID. |
form/src/main/java/org/dhis2/form/data/FormRepositoryImpl.kt |
Implements credential and visibility behavior. |
form/src/main/java/org/dhis2/form/data/FormRepository.kt |
Adds credential-save API. |
form/src/main/java/org/dhis2/form/data/EnrollmentRepository.kt |
Relocates capture-only biometrics fields. |
commonskmm/src/androidMain/kotlin/org/dhis2/mobile/commons/customintents/CustomIntentRepositoryImpl.kt |
Configures Simprints module IDs. |
commons/src/test/kotlin/org/dhis2/commons/simprints/utils/SimprintsExternalCredentialUtilsTest.kt |
Tests credential parsing. |
commons/src/test/kotlin/org/dhis2/commons/simprints/usecases/SimprintsResolveConfirmIdentityCalloutUseCaseTest.kt |
Tests biometric attribute propagation. |
commons/src/test/kotlin/org/dhis2/commons/simprints/repository/SimprintsD2RepositoryTest.kt |
Tests credential persistence validation. |
commons/src/test/kotlin/org/dhis2/commons/simprints/ramp/repository/RampDatastoreRepositoryTest.kt |
Tests new RAMP settings. |
commons/src/main/res/values/strings.xml |
Adds sync-error filter label. |
commons/src/main/res/layout/item_filter_sync_error.xml |
Adds sync-error quick-filter UI. |
commons/src/main/res/layout/item_filter_overdue.xml |
Adds overdue quick-filter UI. |
commons/src/main/java/org/dhis2/commons/ui/model/ListCardUiModel.kt |
Removes custom styled values. |
commons/src/main/java/org/dhis2/commons/ui/ListCardProvider.kt |
Simplifies emphasized rows. |
commons/src/main/java/org/dhis2/commons/simprints/utils/SimprintsIntentUtils.kt |
Tracks biometric attribute IDs. |
commons/src/main/java/org/dhis2/commons/simprints/utils/SimprintsExternalCredentialUtils.kt |
Parses QR credentials. |
commons/src/main/java/org/dhis2/commons/simprints/usecases/SimprintsResolveConfirmIdentityCalloutUseCase.kt |
Propagates biometric field IDs. |
commons/src/main/java/org/dhis2/commons/simprints/repository/SimprintsD2Repository.kt |
Persists validated external credentials. |
commons/src/main/java/org/dhis2/commons/simprints/ramp/repository/RampDatastoreRepository.kt |
Exposes new RAMP options. |
commons/src/main/java/org/dhis2/commons/simprints/ramp/model/RampDatastoreConfig.kt |
Models new RAMP configuration. |
commons/src/main/java/org/dhis2/commons/filters/Filters.kt |
Registers quick-filter types. |
commons/src/main/java/org/dhis2/commons/filters/FilterResources.kt |
Supplies quick-filter labels. |
commons/src/main/java/org/dhis2/commons/filters/FilterManager.java |
Tracks quick-filter state. |
commons/src/main/java/org/dhis2/commons/filters/FilterItem.kt |
Implements quick-filter models. |
commons/src/main/java/org/dhis2/commons/filters/data/FilterRepository.kt |
Inserts available quick filters. |
app/src/test/java/org/dhis2/usescases/teiDashboard/teiProgramList/TeiProgramListRepositoryImplTest.kt |
Tests enrollment detail formatting. |
app/src/test/java/org/dhis2/usescases/teiDashboard/dialogs/scheduling/SchedulingViewModelTest.kt |
Updates scheduling dependencies. |
app/src/test/java/org/dhis2/usescases/searchTrackEntity/ui/mapper/TEICardMapperTest.kt |
Removes obsolete detail-card tests. |
app/src/test/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModelTest.kt |
Tests direct search and hidden lists. |
app/src/test/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryTest.kt |
Tests unfiltered-list configuration. |
app/src/test/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/domain/ConfigureEventReportDateTest.kt |
Tests anchored scheduling. |
app/src/test/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/data/EventDetailsRepositoryTest.kt |
Tests anchored visit context. |
app/src/test/java/org/dhis2/simprints/SimprintsEnrollmentViewModelTest.kt |
Tests enrollment credential sequencing. |
app/src/test/java/org/dhis2/simprints/ramp/data/EventHistoryTableRepositoryTest.kt |
Tests visit ranges and labels. |
app/src/test/java/org/dhis2/simprints/ramp/data/DetailedEnrollmentRepositoryTest.kt |
Tests enrollment detail mapping. |
app/src/test/java/org/dhis2/data/filter/FilterRepositoryTest.kt |
Tests quick-filter availability. |
app/src/main/res/values/strings.xml |
Adds search and detail labels. |
app/src/main/res/layout/item_tei_programs_enrollment.xml |
Displays active enrollment details. |
app/src/main/res/layout/item_tei_programs_enrollment_inactive.xml |
Displays inactive enrollment details. |
app/src/main/res/layout-land/activity_search.xml |
Adds direct-search landscape styling. |
app/src/main/res/drawable/bg_landscape_direct_search_simprints_ramp.xml |
Defines direct-search background. |
app/src/main/java/org/dhis2/usescases/teiDashboard/teiProgramList/TeiProgramListRepositoryImpl.java |
Maps detailed enrollment rows. |
app/src/main/java/org/dhis2/usescases/teiDashboard/teiProgramList/TeiProgramListModule.java |
Wires detail repositories. |
app/src/main/java/org/dhis2/usescases/teiDashboard/teiProgramList/TeiProgramListActivity.java |
Receives current program ID. |
app/src/main/java/org/dhis2/usescases/teiDashboard/teiProgramList/EnrollmentViewModel.kt |
Adds formatted enrollment details. |
app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardMobileActivity.kt |
Passes program ID to enrollment list. |
app/src/main/java/org/dhis2/usescases/teiDashboard/dialogs/scheduling/SchedulingViewModelFactory.kt |
Injects RAMP scheduling settings. |
app/src/main/java/org/dhis2/usescases/teiDashboard/dialogs/scheduling/SchedulingViewModel.kt |
Configures anchored scheduling. |
app/src/main/java/org/dhis2/usescases/teiDashboard/dialogs/scheduling/SchedulingModule.kt |
Provides RAMP repository. |
app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/teidata/TeiDataRepositoryImpl.kt |
Sorts and expands visit lists. |
app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchTEUi.kt |
Supports full-width search buttons. |
app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/SearchScreenConfigurator.kt |
Configures direct biometric search UI. |
app/src/main/java/org/dhis2/usescases/searchTrackEntity/ui/mapper/TEICardMapper.kt |
Removes detailed enrollment card rendering. |
app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEModule.java |
Wires module-ID and credential dependencies. |
app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEIViewModel.kt |
Adds direct search and list controls. |
app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTeiModel.java |
Removes detailed enrollment state. |
app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.kt |
Handles credential results and UI state. |
app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryImplKt.kt |
Removes search-card enrollment details. |
app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryImpl.java |
Delegates unfiltered-list setting. |
app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepository.java |
Adds unfiltered-list API. |
app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchJavaToCompose.kt |
Presents direct-search button. |
app/src/main/java/org/dhis2/usescases/searchTrackEntity/listView/SearchTEList.kt |
Hides empty unfiltered results. |
app/src/main/java/org/dhis2/usescases/searchTrackEntity/di/SearchTEKoinModule.kt |
Injects credential repository. |
app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/models/AnchoredScheduleContext.kt |
Models anchored visit context. |
app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/injection/EventDetailsModule.kt |
Wires anchored scheduling. |
app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/domain/ConfigureEventReportDate.kt |
Calculates anchored dates. |
app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventDetails/data/EventDetailsRepository.kt |
Resolves initial and skipped visits. |
app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentModule.kt |
Configures enrollment callout module IDs. |
app/src/main/java/org/dhis2/simprints/SimprintsSearchViewModel.kt |
Saves credentials after confirmation. |
app/src/main/java/org/dhis2/simprints/SimprintsEnrollmentViewModel.kt |
Saves enrollment credentials. |
app/src/main/java/org/dhis2/simprints/ramp/ui/EventHistoryTableScreen.kt |
Displays configured visit labels. |
app/src/main/java/org/dhis2/simprints/ramp/model/EventHistoryTableModels.kt |
Adds visit-label state. |
app/src/main/java/org/dhis2/simprints/ramp/model/DetailedEnrollment.kt |
Simplifies detail model. |
app/src/main/java/org/dhis2/simprints/ramp/data/EventHistoryTableRepository.kt |
Applies configured visit ranges. |
app/src/main/java/org/dhis2/simprints/ramp/data/DetailedEnrollmentRepository.kt |
Maps SDK enrollments to details. |
app/src/main/java/org/dhis2/simprints/di/SimprintsSearchViewModelFactory.kt |
Injects credential persistence. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| resultCode: Int, | ||
| data: Intent? = null, | ||
| ): PendingDashboardNavigation? { | ||
| val navigation = pendingDashboardNavigation.exchange(null) ?: return null |
There was a problem hiding this comment.
The pending navigation is cleared (exchange(null)) before saveExternalCredential runs. If the save throws, SearchTEIViewModel.onConfirmIdentityResult catches it and shows custom_intent_error, but the navigation is already gone. The user confirmed their identity in SID and still ends up stuck on the search screen.
The credential is optional, so I'd wrap the save in its own try/catch (log it and carry on) and always return navigation when resultCode == RESULT_OK. Also, the Timber.e(...) in the SearchTEIViewModel catch doesn't pass the exception, so the cause is lost. Timber.e(e, "...") would keep it.
| result.valueStoreResult in | ||
| listOf(ValueStoreResult.VALUE_CHANGED, ValueStoreResult.VALUE_HAS_NOT_CHANGED) | ||
| ) { | ||
| val credentialResult = repository.saveSimprintsExternalCredential(it.uid, it.simprintsExternalCredential) |
There was a problem hiding this comment.
When the credential save fails, saveSimprintsExternalCredential returns ERROR_UPDATING_VALUE for the biometric field's uid, and that result replaces the biometric save's own result. So even though the GUID saved fine, the form shows update_field_error, never posts _savedValue, and runs processCalculatedItems(true), which skips program rules.
Could we keep result as the returned value and handle a credential failure separately (log, or a non-blocking message)? It might still be worth using the credential result when it's VALUE_CHANGED and the biometric result was VALUE_HAS_NOT_CHANGED, so the list refreshes.
| data?.extras, | ||
| ) | ||
| if (externalCredentialValue != null && enrollmentUid != null) { | ||
| simprintsD2Repository.saveEnrollmentExternalCredential( |
There was a problem hiding this comment.
Same pattern here. By this point the GUID is saved and SID has registered the person, but if saveEnrollmentExternalCredential throws, the outer catch clears the pending enrollment and rethrows. sessionRepository.clear() never runs and CONTINUE_FINISH is never returned. I'd wrap the credential save in its own try/catch so it can't turn a successful registration into an error.
| .trackedEntityAttributeValues() | ||
| .value(attributeUid, teiUid) | ||
| if (attributeValue.blockingGet()?.value() != externalCredentialValue) { | ||
| attributeValue.blockingSet(externalCredentialValue) |
There was a problem hiding this comment.
This writes straight through trackedEntityAttributeValues().value(...).blockingSet(), which skips the unique-attribute check that FormValueStore normally does (UniqueAttributeController). A QR credential attribute is very likely configured as unique. If the same code gets scanned for two people, both writes succeed locally and one TEI then fails to sync with a non-unique error. The field is read-only in the form, so the user can't fix it.
Could we check attribute.unique() and look for an existing value on another TEI before writing? Skipping the write, or telling the user, would be better than storing a duplicate.
There was a problem hiding this comment.
The QR code is not configured as unique, it is just a "fallback" info field to assist with secondary, non-biometric search. In the whole system, SID is the part where the uniqueness is enforced, it will be so for most of the normal use. I'd say we should avoid repeating the QR uniqueness duty as extra fork changes. At worst, there would be just more than 1 search results per code. If that becomes an issue as a case of duplicates, we can then clean those up via API or WebUI and revisit the fork change.
| if (sourceField.isLoadingData || !sourceField.value.isNullOrEmpty()) { | ||
| return filterNot { it.uid == attributeId } | ||
| } | ||
| if (any { it.uid == attributeId }) return this |
There was a problem hiding this comment.
As far as I can tell, the only way the capture-only field is in itemList but missing from the list here is that applyRuleEffects removed it (HIDEFIELD or HIDESECTION). If so, this branch puts the field back in and overrides the program rule, so a hidden biometrics button still shows and can still be used.
If that's the only case, I think the insert branch can go: EnrollmentRepository already places the field, and this function only needs the filterNot for loading or has-value. If there's another case it's meant for, could we exclude fields the rule effects hid?


External credential (QR code) capture from Simprints ID responses, + RAMP-specific non-biometric features