From 0c2386ca0a9dd9d21f01a35e6db18205debe9327 Mon Sep 17 00:00:00 2001 From: Xavier Molloy Date: Fri, 15 Nov 2024 16:38:15 +0100 Subject: [PATCH] fix: [ANDROAPP-64659] Implement no periods available functionality (#3884) * fix: [ANDROAPP-64659] Implement no periods available functionality, correct app version * fix: [ANDROAPP-6659] take into account last stage event date for showing next period --- .../dhislogic/EnrollmentEventGenerator.kt | 10 +- .../enrollment/EnrollmentActivity.kt | 7 +- .../enrollment/EnrollmentPresenterImpl.kt | 21 +++++ .../org/dhis2/form/data/EventRepository.kt | 16 +--- .../inputfield/PeriodSelectorProvider.kt | 92 ++++++++++++++----- form/src/main/res/values/strings.xml | 2 + gradle/libs.versions.toml | 2 +- .../events/CreateEventUseCaseRepository.kt | 8 +- 8 files changed, 111 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/org/dhis2/data/dhislogic/EnrollmentEventGenerator.kt b/app/src/main/java/org/dhis2/data/dhislogic/EnrollmentEventGenerator.kt index 02fcb566ff..0152df3c40 100644 --- a/app/src/main/java/org/dhis2/data/dhislogic/EnrollmentEventGenerator.kt +++ b/app/src/main/java/org/dhis2/data/dhislogic/EnrollmentEventGenerator.kt @@ -1,12 +1,13 @@ package org.dhis2.data.dhislogic import org.dhis2.commons.Constants -import org.dhis2.utils.DateUtils +import org.dhis2.commons.date.DateUtils import org.hisp.dhis.android.core.enrollment.Enrollment import org.hisp.dhis.android.core.maintenance.D2Error import org.hisp.dhis.android.core.program.ProgramStage import timber.log.Timber import java.util.Calendar +import java.util.Date class EnrollmentEventGenerator( private val generatorRepository: EnrollmentEventGeneratorRepository, @@ -121,10 +122,11 @@ class EnrollmentEventGenerator( calendar.set(Calendar.SECOND, 0) calendar.set(Calendar.MILLISECOND, 0) var eventDate = calendar.time - + val currentDate = DateUtils.getInstance().getStartOfDay(Date()) periodType?.let { eventDate = generatorRepository.periodStartingDate(it, eventDate) } - - generatorRepository.setEventDate(eventUid, eventDate) + if (eventDate.before(currentDate) || eventDate == currentDate) { + generatorRepository.setEventDate(eventUid, eventDate) + } } catch (d2Error: D2Error) { Timber.e(d2Error) } diff --git a/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentActivity.kt b/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentActivity.kt index fd5062a1be..5e35d7c5c9 100644 --- a/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentActivity.kt +++ b/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentActivity.kt @@ -172,10 +172,11 @@ class EnrollmentActivity : ActivityGlobalAbstract(), EnrollmentView { } override fun openEvent(eventUid: String) { - if (presenter.isEventScheduleOrSkipped(eventUid)) { + val suggestedEventDateIsNotFutureDate = presenter.suggestedReportDateIsNotFutureDate(eventUid) + if (presenter.isEventScheduleOrSkipped(eventUid) && suggestedEventDateIsNotFutureDate) { val scheduleEventIntent = ScheduledEventActivity.getIntent(this, eventUid) openEventForResult.launch(scheduleEventIntent) - } else { + } else if (suggestedEventDateIsNotFutureDate) { val eventCreationIntent = Intent(abstracContext, EventCaptureActivity::class.java) eventCreationIntent.putExtras( EventCaptureActivity.getActivityBundle( @@ -185,6 +186,8 @@ class EnrollmentActivity : ActivityGlobalAbstract(), EnrollmentView { ), ) startActivityForResult(eventCreationIntent, RQ_EVENT) + } else { + openDashboard(presenter.getEnrollment()?.uid()!!) } } diff --git a/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentPresenterImpl.kt b/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentPresenterImpl.kt index 0d510b5e49..2908e511fa 100644 --- a/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentPresenterImpl.kt +++ b/app/src/main/java/org/dhis2/usescases/enrollment/EnrollmentPresenterImpl.kt @@ -6,6 +6,7 @@ import io.reactivex.processors.PublishProcessor import org.dhis2.bindings.profilePicturePath import org.dhis2.commons.bindings.trackedEntityTypeForTei import org.dhis2.commons.data.TeiAttributesInfo +import org.dhis2.commons.date.DateUtils import org.dhis2.commons.matomo.Actions.Companion.CREATE_TEI import org.dhis2.commons.matomo.Categories.Companion.TRACKER_LIST import org.dhis2.commons.matomo.Labels.Companion.CLICK @@ -31,6 +32,8 @@ import org.hisp.dhis.android.core.program.Program import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValue import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceObjectRepository import timber.log.Timber +import java.util.Calendar.DAY_OF_YEAR +import java.util.Date private const val TAG = "EnrollmentPresenter" @@ -238,4 +241,22 @@ class EnrollmentPresenterImpl( event?.status() == EventStatus.SKIPPED || event?.status() == EventStatus.OVERDUE } + + fun suggestedReportDateIsNotFutureDate(eventUid: String): Boolean { + return try { + val event = eventCollectionRepository.uid(eventUid).blockingGet() + val programStage = d2.programModule().programStages().uid(event?.programStage()).blockingGet() + val enrollment = enrollmentObjectRepository.blockingGet() + val generatedByEnrollment = programStage?.generatedByEnrollmentDate() ?: false + val startDate = if (generatedByEnrollment) enrollment?.enrollmentDate() else enrollment?.incidentDate() + val calendar = DateUtils.getInstance().getCalendarByDate(startDate) + calendar.add(DAY_OF_YEAR, programStage?.minDaysFromStart() ?: 0) + val minStartReportEventDate = calendar.time + val currentDate = DateUtils.getInstance().getStartOfDay(Date()) + return minStartReportEventDate.before(currentDate) || minStartReportEventDate == currentDate + } catch (e: Exception) { + Timber.d(e.message) + true + } + } } diff --git a/form/src/main/java/org/dhis2/form/data/EventRepository.kt b/form/src/main/java/org/dhis2/form/data/EventRepository.kt index 19c13a9c5a..05bee4fb1a 100644 --- a/form/src/main/java/org/dhis2/form/data/EventRepository.kt +++ b/form/src/main/java/org/dhis2/form/data/EventRepository.kt @@ -43,7 +43,6 @@ import org.hisp.dhis.android.core.program.ProgramStageDataElement import org.hisp.dhis.android.core.program.ProgramStageSection import org.hisp.dhis.android.core.program.SectionRenderingType import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor -import java.util.Calendar.DAY_OF_YEAR import java.util.Date class EventRepository( @@ -477,13 +476,8 @@ class EventRepository( ?: getEnrollmentDate(enrollmentUid) } val calendar = DateUtils.getInstance().getCalendarByDate(minEventDate) - if (stageLastDate == null) { - val minDaysFromStart = getMinDaysFromStartByProgramStage(programStage) - calendar.add(DAY_OF_YEAR, minDaysFromStart) - } else { - calendar.add(DAY_OF_YEAR, programStage?.standardInterval() ?: 0) - } - return dateUtils.getNextPeriod(programStage?.periodType(), calendar.time ?: event?.eventDate(), 1) + + return dateUtils.getNextPeriod(programStage?.periodType(), calendar.time ?: event?.eventDate(), if (stageLastDate == null) 0 else 1) } private fun getStageLastDate(): Date? { @@ -499,7 +493,7 @@ class EventRepository( d2.eventModule().events().byEnrollmentUid().eq(enrollmentUid).byProgramStageUid() .eq(programStageUid) .byDeleted().isFalse - .orderByDueDate(RepositoryScope.OrderByDirection.DESC).blockingGet() + .orderByDueDate(RepositoryScope.OrderByDirection.DESC).blockingGet().filter { it.uid() != eventUid } var activeDate: Date? = null var scheduleDate: Date? = null @@ -516,10 +510,6 @@ class EventRepository( } } - private fun getMinDaysFromStartByProgramStage(programStage: ProgramStage?): Int { - return programStage?.minDaysFromStart() ?: 0 - } - private fun getEnrollmentDate(uid: String?): Date? { val enrollment = d2.enrollmentModule().enrollments().byUid().eq(uid).blockingGet().first() return enrollment.enrollmentDate() diff --git a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/PeriodSelectorProvider.kt b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/PeriodSelectorProvider.kt index 4c86ca392e..860ae6fbd7 100644 --- a/form/src/main/java/org/dhis2/form/ui/provider/inputfield/PeriodSelectorProvider.kt +++ b/form/src/main/java/org/dhis2/form/ui/provider/inputfield/PeriodSelectorProvider.kt @@ -7,6 +7,9 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.res.stringResource +import org.dhis2.commons.date.DateUtils +import org.dhis2.form.R import org.dhis2.form.extensions.inputState import org.dhis2.form.extensions.legend import org.dhis2.form.extensions.supportingText @@ -14,7 +17,10 @@ import org.dhis2.form.model.FieldUiModel import org.dhis2.form.ui.event.RecyclerViewUiEvents import org.hisp.dhis.mobile.ui.designsystem.component.DropdownInputField import org.hisp.dhis.mobile.ui.designsystem.component.DropdownItem +import org.hisp.dhis.mobile.ui.designsystem.component.InputDropDown +import org.hisp.dhis.mobile.ui.designsystem.component.InputShellState import org.hisp.dhis.mobile.ui.designsystem.component.InputStyle +import java.util.Date @Composable fun ProvidePeriodSelector( @@ -24,38 +30,74 @@ fun ProvidePeriodSelector( focusRequester: FocusRequester, uiEventHandler: (RecyclerViewUiEvents) -> Unit, ) { - var selectedItem by remember(fieldUiModel.displayName) { - mutableStateOf( - fieldUiModel.displayName, + val currentDate = DateUtils.getInstance().getStartOfDay(Date()) + if ((fieldUiModel.periodSelector?.minDate?.after(currentDate) == true)) { + ProvideEmptyPeriodSelector( + modifier = modifier, + name = fieldUiModel.label, + inputStyle = inputStyle, ) + } else { + var selectedItem by remember(fieldUiModel.displayName) { + mutableStateOf( + fieldUiModel.displayName, + ) + } + + DropdownInputField( + modifier = modifier, + title = fieldUiModel.label, + state = fieldUiModel.inputState(), + inputStyle = inputStyle, + legendData = fieldUiModel.legend(), + supportingTextData = fieldUiModel.supportingText(), + isRequiredField = fieldUiModel.mandatory, + selectedItem = DropdownItem(selectedItem ?: ""), + onResetButtonClicked = { + selectedItem = null + fieldUiModel.onClear() + }, + onDropdownIconClick = { + uiEventHandler( + RecyclerViewUiEvents.SelectPeriod( + uid = fieldUiModel.uid, + title = fieldUiModel.label, + periodType = fieldUiModel.periodSelector!!.type, + minDate = fieldUiModel.periodSelector!!.minDate, + maxDate = fieldUiModel.periodSelector!!.maxDate, + ), + ) + }, + onFocusChanged = {}, + focusRequester = focusRequester, + expanded = false, + ) + } +} + +@Composable +fun ProvideEmptyPeriodSelector( + modifier: Modifier = Modifier, + name: String, + inputStyle: InputStyle, +) { + var selectedItem by remember { + mutableStateOf("") } - DropdownInputField( + InputDropDown( modifier = modifier, - title = fieldUiModel.label, - state = fieldUiModel.inputState(), + title = name, + state = InputShellState.UNFOCUSED, inputStyle = inputStyle, - legendData = fieldUiModel.legend(), - supportingTextData = fieldUiModel.supportingText(), - isRequiredField = fieldUiModel.mandatory, - selectedItem = DropdownItem(selectedItem ?: ""), + selectedItem = DropdownItem(selectedItem), onResetButtonClicked = { - selectedItem = null - fieldUiModel.onClear() + selectedItem = "" }, - onDropdownIconClick = { - uiEventHandler( - RecyclerViewUiEvents.SelectPeriod( - uid = fieldUiModel.uid, - title = fieldUiModel.label, - periodType = fieldUiModel.periodSelector!!.type, - minDate = fieldUiModel.periodSelector!!.minDate, - maxDate = fieldUiModel.periodSelector!!.maxDate, - ), - ) + onItemSelected = { newSelectedDropdownItem -> + selectedItem = newSelectedDropdownItem.label }, - onFocusChanged = {}, - focusRequester = focusRequester, - expanded = false, + dropdownItems = listOf(DropdownItem(stringResource(id = R.string.no_periods))), + isRequiredField = false, ) } diff --git a/form/src/main/res/values/strings.xml b/form/src/main/res/values/strings.xml index 5e31225758..4291c0a4df 100644 --- a/form/src/main/res/values/strings.xml +++ b/form/src/main/res/values/strings.xml @@ -118,4 +118,6 @@ No options available Cat combo Storage permission is not granted.\nYou need to enable it to use this feature. + No periods available + \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c0f3e88d4f..1464a48a35 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ sdk = "34" minSdk = "21" vCode = "136" -vName = "3.1.1-DEV" +vName = "3.1.0" gradle = "8.6.1" kotlin = '2.0.20' hilt = '2.47' diff --git a/tracker/src/main/kotlin/org/dhis2/tracker/events/CreateEventUseCaseRepository.kt b/tracker/src/main/kotlin/org/dhis2/tracker/events/CreateEventUseCaseRepository.kt index b1da4fff11..3738635d6c 100644 --- a/tracker/src/main/kotlin/org/dhis2/tracker/events/CreateEventUseCaseRepository.kt +++ b/tracker/src/main/kotlin/org/dhis2/tracker/events/CreateEventUseCaseRepository.kt @@ -45,8 +45,12 @@ class CreateEventUseCaseRepository( } else { currentDate } - val eventRepository = d2.eventModule().events().uid(eventUid) - eventRepository.setEventDate(eventDate) + + if(eventDate.before(currentDate) || eventDate == currentDate) { + val eventRepository = d2.eventModule().events().uid(eventUid) + eventRepository.setEventDate(eventDate) + } + } private fun getStageLastDate(enrollmentUid: String?, programStageUid: String?): Date? {