diff --git a/app/src/androidTest/java/org/dhis2/usescases/event/EventRegistrationRobot.kt b/app/src/androidTest/java/org/dhis2/usescases/event/EventRegistrationRobot.kt index c13f41c502..1bd0c7ccdb 100644 --- a/app/src/androidTest/java/org/dhis2/usescases/event/EventRegistrationRobot.kt +++ b/app/src/androidTest/java/org/dhis2/usescases/event/EventRegistrationRobot.kt @@ -40,7 +40,7 @@ class EventRegistrationRobot : BaseRobot() { } private fun clickOnNextQR() { - onView(withId(R.id.next)).perform(click()) + waitForView(withId(R.id.next)).perform(click()) } fun clickOnAllQR(listQR: Int) { diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCapturePresenterImpl.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCapturePresenterImpl.kt index cc317af7eb..fe94d41867 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCapturePresenterImpl.kt +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCapturePresenterImpl.kt @@ -113,29 +113,38 @@ class EventCapturePresenterImpl( warningFields: List, eventMode: EventMode?, ) { - val eventStatus = eventStatus - if (eventStatus != EventStatus.ACTIVE) { - setUpActionByStatus(eventStatus) - } else { - val canSkipErrorFix = canSkipErrorFix( - hasErrorFields = errorFields.isNotEmpty(), - hasEmptyMandatoryFields = emptyMandatoryFields.isNotEmpty(), - hasEmptyEventCreationMandatoryFields = with(emptyMandatoryFields) { - containsValue(EventRepository.EVENT_DETAILS_SECTION_UID) || - containsValue(EventRepository.EVENT_CATEGORY_COMBO_SECTION_UID) - }, - eventMode = eventMode, - validationStrategy = eventCaptureRepository.validationStrategy(), - ) - val eventCompletionDialog = configureEventCompletionDialog.invoke( - errorFields, - emptyMandatoryFields, - warningFields, - canComplete, - onCompleteMessage, - canSkipErrorFix, - ) - view.showCompleteActions(eventCompletionDialog) + when (eventStatus) { + EventStatus.ACTIVE, EventStatus.COMPLETED -> { + var canSkipErrorFix = canSkipErrorFix( + hasErrorFields = errorFields.isNotEmpty(), + hasEmptyMandatoryFields = emptyMandatoryFields.isNotEmpty(), + hasEmptyEventCreationMandatoryFields = with(emptyMandatoryFields) { + containsValue(EventRepository.EVENT_DETAILS_SECTION_UID) || + containsValue(EventRepository.EVENT_CATEGORY_COMBO_SECTION_UID) + }, + eventMode = eventMode, + validationStrategy = eventCaptureRepository.validationStrategy(), + ) + if (eventStatus == EventStatus.COMPLETED) canSkipErrorFix = false + val eventCompletionDialog = configureEventCompletionDialog.invoke( + errorFields, + emptyMandatoryFields, + warningFields, + canComplete, + onCompleteMessage, + canSkipErrorFix, + eventStatus, + ) + + if (eventStatus == EventStatus.COMPLETED && eventCompletionDialog.fieldsWithIssues.isEmpty()) { + finishCompletedEvent() + } else { + view.showCompleteActions(eventCompletionDialog) + } + } + else -> { + setUpActionByStatus(eventStatus) + } } view.showNavigationBar() } @@ -158,13 +167,6 @@ class EventCapturePresenterImpl( private fun setUpActionByStatus(eventStatus: EventStatus) { when (eventStatus) { - EventStatus.COMPLETED -> - if (!hasExpired && !eventCaptureRepository.isEnrollmentCancelled) { - view.saveAndFinish() - } else { - view.finishDataEntry() - } - EventStatus.OVERDUE -> view.attemptToSkip() EventStatus.SKIPPED -> view.attemptToReschedule() else -> { @@ -173,6 +175,14 @@ class EventCapturePresenterImpl( } } + private fun finishCompletedEvent() { + if (!hasExpired && !eventCaptureRepository.isEnrollmentCancelled) { + view.saveAndFinish() + } else { + view.finishDataEntry() + } + } + override fun isEnrollmentOpen(): Boolean { return eventCaptureRepository.isEnrollmentOpen } diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/domain/ConfigureEventCompletionDialog.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/domain/ConfigureEventCompletionDialog.kt index 3543b4641b..7e6a53f935 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/domain/ConfigureEventCompletionDialog.kt +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/domain/ConfigureEventCompletionDialog.kt @@ -15,6 +15,7 @@ import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.model.EventCom import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.model.EventCompletionDialog import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.provider.EventCaptureResourcesProvider import org.dhis2.utils.customviews.FormBottomDialog +import org.hisp.dhis.android.core.event.EventStatus class ConfigureEventCompletionDialog( val provider: EventCaptureResourcesProvider, @@ -27,6 +28,7 @@ class ConfigureEventCompletionDialog( canComplete: Boolean, onCompleteMessage: String?, canSkipErrorFix: Boolean, + eventState: EventStatus, ): EventCompletionDialog { val dialogType = getDialogType( errorFields, @@ -34,10 +36,10 @@ class ConfigureEventCompletionDialog( warningFields, !canComplete && onCompleteMessage != null, ) - val mainButton = getMainButton(dialogType) - val secondaryButton = if (canSkipErrorFix) { + val mainButton = getMainButton(dialogType, eventState) + val secondaryButton = if (canSkipErrorFix || eventState == EventStatus.COMPLETED) { EventCompletionButtons( - SecondaryButton(provider.provideNotNow()), + SecondaryButton(if (eventState == EventStatus.COMPLETED) provider.provideSaveAnyway() else provider.provideNotNow()), FormBottomDialog.ActionType.FINISH, ) } else { @@ -45,7 +47,7 @@ class ConfigureEventCompletionDialog( } val bottomSheetDialogUiModel = BottomSheetDialogUiModel( title = getTitle(dialogType), - message = getSubtitle(dialogType), + message = getSubtitle(dialogType, eventState), iconResource = getIcon(dialogType), mainButton = mainButton.buttonStyle, secondaryButton = secondaryButton?.buttonStyle, @@ -69,10 +71,10 @@ class ConfigureEventCompletionDialog( else -> provider.provideSavedText() } - private fun getSubtitle(type: DialogType) = when (type) { + private fun getSubtitle(type: DialogType, eventState: EventStatus) = when (type) { ERROR -> provider.provideErrorInfo() MANDATORY -> provider.provideMandatoryInfo() - WARNING -> provider.provideWarningInfo() + WARNING -> if (eventState == EventStatus.COMPLETED) provider.provideWarningInfoCompletedEvent() else provider.provideWarningInfo() SUCCESSFUL -> provider.provideCompleteInfo() COMPLETE_ERROR -> provider.provideOnCompleteErrorInfo() } @@ -84,7 +86,7 @@ class ConfigureEventCompletionDialog( SUCCESSFUL -> provider.provideSavedIcon() } - private fun getMainButton(type: DialogType) = when (type) { + private fun getMainButton(type: DialogType, eventState: EventStatus) = when (type) { ERROR, MANDATORY, COMPLETE_ERROR, @@ -93,7 +95,17 @@ class ConfigureEventCompletionDialog( FormBottomDialog.ActionType.CHECK_FIELDS, ) - WARNING, + WARNING -> if (eventState == EventStatus.COMPLETED) { + EventCompletionButtons( + MainButton(provider.provideReview()), + FormBottomDialog.ActionType.CHECK_FIELDS, + ) + } else { + EventCompletionButtons( + CompleteButton, + FormBottomDialog.ActionType.COMPLETE, + ) + } SUCCESSFUL, -> EventCompletionButtons( CompleteButton, diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/provider/EventCaptureResourcesProvider.kt b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/provider/EventCaptureResourcesProvider.kt index 439a3c8ee6..34fa0910c7 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/provider/EventCaptureResourcesProvider.kt +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/provider/EventCaptureResourcesProvider.kt @@ -25,10 +25,14 @@ class EventCaptureResourcesProvider( fun provideWarningInfo() = resourceManager.getString(R.string.missing_warning_fields_events) + fun provideWarningInfoCompletedEvent() = resourceManager.getString(R.string.missing_warning_fields_completed_events) + fun provideReview() = R.string.review fun provideNotNow() = R.string.not_now + fun provideSaveAnyway() = R.string.save_anyway + fun provideCompleteInfo() = resourceManager.getString(R.string.event_can_be_completed) fun provideOnCompleteErrorInfo() = resourceManager.getString(R.string.event_error_on_complete) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 99bb27844c..6321e1aa7c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -863,6 +863,7 @@ Ok Start a search to find any %s You can search or create a new %s + You have some warning messages. You have some warning messages.\nDo you want to mark this form as complete? Some fields have errors and they are not saved. \nDo you want to review the form? Do you want to mark this form as complete? diff --git a/app/src/test/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCapturePresenterTest.kt b/app/src/test/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCapturePresenterTest.kt index 51e53df2f2..b32e1a1534 100644 --- a/app/src/test/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCapturePresenterTest.kt +++ b/app/src/test/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/EventCapturePresenterTest.kt @@ -6,6 +6,7 @@ import io.reactivex.Observable import io.reactivex.Single import org.dhis2.commons.prefs.PreferenceProvider import org.dhis2.data.schedulers.TrampolineSchedulerProvider +import org.dhis2.ui.dialogs.bottomsheet.FieldWithIssue import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.domain.ConfigureEventCompletionDialog import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.model.EventCompletionDialog import org.hisp.dhis.android.core.common.ValidationStrategy @@ -252,7 +253,16 @@ class EventCapturePresenterTest { whenever(eventRepository.eventIntegrityCheck()) doReturn Flowable.just(false) whenever(eventRepository.eventStatus()) doReturn Flowable.just(EventStatus.COMPLETED) whenever(eventRepository.isEventEditable("eventUid")) doReturn true - + whenever( + eventRepository.validationStrategy(), + ) doReturn ValidationStrategy.ON_UPDATE_AND_INSERT + val eventCompletionDialog: EventCompletionDialog = mock() + whenever( + configureEventCompletionDialog.invoke( + emptyList(), emptyMap(), emptyList(), true, null, false, + EventStatus.COMPLETED, + ), + ) doReturn eventCompletionDialog whenever(eventRepository.isCompletedEventExpired(any())) doReturn Observable.just(true) whenever(eventRepository.isEventEditable(any())) doReturn true @@ -269,7 +279,13 @@ class EventCapturePresenterTest { whenever(eventRepository.isEventEditable("eventUid")) doReturn true presenter.init() - + whenever( + eventRepository.validationStrategy(), + ) doReturn ValidationStrategy.ON_UPDATE_AND_INSERT + val eventCompletionDialog: EventCompletionDialog = mock() + whenever( + configureEventCompletionDialog.invoke(emptyList(), emptyMap(), emptyList(), true, null, false, EventStatus.COMPLETED), + ) doReturn eventCompletionDialog whenever(eventRepository.isCompletedEventExpired(any())) doReturn Observable.just(false) whenever(eventRepository.isEventEditable(any())) doReturn true whenever(eventRepository.isEnrollmentCancelled) doReturn true @@ -325,7 +341,7 @@ class EventCapturePresenterTest { ) doReturn ValidationStrategy.ON_UPDATE_AND_INSERT val eventCompletionDialog: EventCompletionDialog = mock() whenever( - configureEventCompletionDialog.invoke(any(), any(), any(), any(), any(), any()), + configureEventCompletionDialog.invoke(any(), any(), any(), any(), any(), any(), any()), ) doReturn eventCompletionDialog whenever( eventRepository.isEnrollmentOpen, @@ -334,7 +350,37 @@ class EventCapturePresenterTest { presenter.attemptFinish( canComplete = true, onCompleteMessage = "Complete", - errorFields = emptyList(), + errorFields = listOf(mock()), + emptyMandatoryFields = emptyMap(), + warningFields = emptyList(), + ) + + verify(view).showCompleteActions(any()) + verify(view).showNavigationBar() + } + + @Test + fun `Should show completion dialog and not navigate back when event is completed and there are error fields`() { + initializeMocks() + whenever(eventRepository.eventIntegrityCheck()) doReturn Flowable.just(false) + whenever(eventRepository.eventStatus()) doReturn Flowable.just(EventStatus.COMPLETED) + whenever(eventRepository.isEventEditable("eventUid")) doReturn true + + whenever( + eventRepository.validationStrategy(), + ) doReturn ValidationStrategy.ON_UPDATE_AND_INSERT + val eventCompletionDialog = EventCompletionDialog(mock(), mock(), null, listOf(FieldWithIssue("uid", "fieldName", mock(), "message"))) + whenever( + configureEventCompletionDialog.invoke(any(), any(), any(), any(), any(), any(), any()), + ) doReturn eventCompletionDialog + whenever( + eventRepository.isEnrollmentOpen, + ) doReturn true + + presenter.attemptFinish( + canComplete = true, + onCompleteMessage = "Complete", + errorFields = listOf(FieldWithIssue("uid", "fieldName", mock(), "message")), emptyMandatoryFields = emptyMap(), warningFields = emptyList(), ) @@ -343,6 +389,35 @@ class EventCapturePresenterTest { verify(view).showNavigationBar() } + @Test + fun `Should save and finish if event is is completed and has no errors`() { + initializeMocks() + whenever(eventRepository.eventIntegrityCheck()) doReturn Flowable.just(false) + whenever(eventRepository.eventStatus()) doReturn Flowable.just(EventStatus.COMPLETED) + whenever(eventRepository.isEventEditable("eventUid")) doReturn true + + whenever( + eventRepository.validationStrategy(), + ) doReturn ValidationStrategy.ON_UPDATE_AND_INSERT + val eventCompletionDialog: EventCompletionDialog = mock() + whenever( + configureEventCompletionDialog.invoke(any(), any(), any(), any(), any(), any(), any()), + ) doReturn eventCompletionDialog + whenever( + eventRepository.isEnrollmentOpen, + ) doReturn true + + presenter.attemptFinish( + canComplete = true, + onCompleteMessage = "Complete", + errorFields = emptyList(), + emptyMandatoryFields = emptyMap(), + warningFields = emptyList(), + ) + + verify(view).saveAndFinish() + } + @Test fun `Should init note counter`() { whenever(eventRepository.noteCount) doReturnConsecutively listOf( diff --git a/app/src/test/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/domain/ConfigureEventCompletionDialogTest.kt b/app/src/test/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/domain/ConfigureEventCompletionDialogTest.kt index 3c151bb86f..a7b65bd709 100644 --- a/app/src/test/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/domain/ConfigureEventCompletionDialogTest.kt +++ b/app/src/test/java/org/dhis2/usescases/eventsWithoutRegistration/eventCapture/domain/ConfigureEventCompletionDialogTest.kt @@ -3,6 +3,7 @@ package org.dhis2.usescases.eventsWithoutRegistration.eventCapture.domain import org.dhis2.ui.dialogs.bottomsheet.FieldWithIssue import org.dhis2.ui.dialogs.bottomsheet.IssueType import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.provider.EventCaptureResourcesProvider +import org.hisp.dhis.android.core.event.EventStatus import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test @@ -56,6 +57,7 @@ class ConfigureEventCompletionDialogTest { canComplete = true, onCompleteMessage = null, canSkipErrorFix = true, + EventStatus.ACTIVE, ) // Then Dialog should has Error info @@ -79,6 +81,7 @@ class ConfigureEventCompletionDialogTest { canComplete = true, onCompleteMessage = null, canSkipErrorFix = true, + EventStatus.ACTIVE, ) // Then Dialog should has Error info @@ -101,6 +104,7 @@ class ConfigureEventCompletionDialogTest { canComplete = true, onCompleteMessage = null, canSkipErrorFix = true, + EventStatus.ACTIVE, ) // Then Dialog should has Error info @@ -121,6 +125,7 @@ class ConfigureEventCompletionDialogTest { canComplete = true, onCompleteMessage = null, canSkipErrorFix = true, + EventStatus.ACTIVE, ) // Then Dialog should has Error info @@ -141,6 +146,7 @@ class ConfigureEventCompletionDialogTest { canComplete = true, onCompleteMessage = WARNING_MESSAGE, canSkipErrorFix = true, + EventStatus.ACTIVE, ) // Then Dialog should has Error info @@ -161,6 +167,7 @@ class ConfigureEventCompletionDialogTest { canComplete = false, onCompleteMessage = ERROR_INFO, canSkipErrorFix = true, + EventStatus.ACTIVE, ) // Then Dialog should has Error info diff --git a/form/src/main/res/values/strings.xml b/form/src/main/res/values/strings.xml index 88ca0e3b24..75eada1571 100644 --- a/form/src/main/res/values/strings.xml +++ b/form/src/main/res/values/strings.xml @@ -58,6 +58,7 @@ Saved! Some fields need your attention.\nDo you want to review the form? Not now + Save anyway Keep editing If you exit now all the information in the form will be discarded. Some fields have errors and they are not saved. \nIf you exit now the changes will be discarded.