Skip to content

Commit

Permalink
fix: [ANDROAPP-6660] Incorrect list of available periods
Browse files Browse the repository at this point in the history
  • Loading branch information
Balcan committed Dec 5, 2024
1 parent 680abfd commit d8cb13b
Show file tree
Hide file tree
Showing 26 changed files with 894 additions and 262 deletions.
5 changes: 5 additions & 0 deletions app/src/main/java/org/dhis2/data/server/ServerModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import org.dhis2.R
import org.dhis2.bindings.app
import org.dhis2.commons.di.dagger.PerServer
import org.dhis2.commons.filters.data.GetFiltersApplyingWebAppConfig
import org.dhis2.commons.periods.PeriodUseCase
import org.dhis2.commons.prefs.PreferenceProvider
import org.dhis2.commons.reporting.CrashReportController
import org.dhis2.commons.resources.ColorUtils
Expand Down Expand Up @@ -172,6 +173,10 @@ class ServerModule {
return ResourceManager(contextWrapper, colorUtils)
}

@Provides
@PerServer
fun providePeriodUseCase(d2: D2) = PeriodUseCase(d2)

companion object {
@JvmStatic
fun getD2Configuration(context: Context): D2Configuration {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.databinding.DataBindingUtil
import androidx.paging.compose.collectAsLazyPagingItems
import org.dhis2.App
import org.dhis2.R
import org.dhis2.commons.date.DateUtils
import org.dhis2.commons.dialogs.PeriodDialog
import org.dhis2.commons.dialogs.AlertBottomDialog
import org.dhis2.commons.periods.PeriodSelectorContent
import org.dhis2.databinding.ActivityEventScheduledBinding
import org.dhis2.form.model.EventMode
import org.dhis2.ui.dialogs.bottomsheet.BottomSheetDialog
import org.dhis2.ui.dialogs.bottomsheet.BottomSheetDialogUiModel
import org.dhis2.usescases.eventsWithoutRegistration.eventCapture.EventCaptureActivity
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.models.EventDate
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.models.EventInputDateUiModel
Expand Down Expand Up @@ -145,7 +149,12 @@ class ScheduledEventActivity : ActivityGlobalAbstract(), ScheduledEventContract.
uiModel = EventInputDateUiModel(
eventDate = eventDate,
detailsEnabled = true,
onDateClick = { showEventDatePeriodDialog(programStage.periodType()) },
onDateClick = {
showPeriodDialog(
periodType = programStage.periodType(),
scheduling = false,
)
},
onDateSelected = {},
onClear = { },
required = true,
Expand All @@ -160,7 +169,12 @@ class ScheduledEventActivity : ActivityGlobalAbstract(), ScheduledEventContract.
uiModel = EventInputDateUiModel(
eventDate = dueDate,
detailsEnabled = true,
onDateClick = { showDueDatePeriodDialog(programStage.periodType()) },
onDateClick = {
showPeriodDialog(
periodType = programStage.periodType(),
scheduling = true,
)
},
onDateSelected = {},
onClear = { },
required = true,
Expand All @@ -181,58 +195,28 @@ class ScheduledEventActivity : ActivityGlobalAbstract(), ScheduledEventContract.
binding.name = program.displayName()
}

private fun showEventDatePeriodDialog(periodType: PeriodType?) {
if (periodType != null) {
var minDate =
DateUtils.getInstance().expDate(null, program.expiryDays()!!, periodType)
val lastPeriodDate =
DateUtils.getInstance().getNextPeriod(periodType, minDate, -1, true)

if (lastPeriodDate.after(
DateUtils.getInstance().getNextPeriod(
program.expiryPeriodType(),
minDate,
0,
),
)
) {
minDate = DateUtils.getInstance().getNextPeriod(periodType, lastPeriodDate, 0)
}

PeriodDialog()
.setPeriod(periodType)
.setMinDate(minDate)
.setMaxDate(DateUtils.getInstance().today)
.setPossitiveListener { selectedDate -> presenter.setEventDate(selectedDate) }
.show(supportFragmentManager, PeriodDialog::class.java.simpleName)
}
}

private fun showDueDatePeriodDialog(periodType: PeriodType?) {
if (periodType != null) {
var minDate =
DateUtils.getInstance().expDate(null, program.expiryDays()!!, periodType)
val lastPeriodDate =
DateUtils.getInstance().getNextPeriod(periodType, minDate, -1, true)

if (lastPeriodDate.after(
DateUtils.getInstance().getNextPeriod(
program.expiryPeriodType(),
minDate,
0,
),
)
) {
minDate = DateUtils.getInstance().getNextPeriod(periodType, lastPeriodDate, 0)
}

PeriodDialog()
.setPeriod(periodType)
.setMinDate(minDate)
.setMaxDate(DateUtils.getInstance().today)
.setPossitiveListener { selectedDate -> presenter.setDueDate(selectedDate) }
.show(supportFragmentManager, PeriodDialog::class.java.simpleName)
}
private fun showPeriodDialog(periodType: PeriodType?, scheduling: Boolean) {
BottomSheetDialog(
bottomSheetDialogUiModel = BottomSheetDialogUiModel(
title = "PeriodType title", // TODO:6660
iconResource = -1,
),
onSecondaryButtonClicked = {
},
onMainButtonClicked = { bottomSheetDialog ->
},
showDivider = true,
content = { bottomSheetDialog, scrollState ->
val periods = presenter.fetchPeriods(scheduling).collectAsLazyPagingItems()
PeriodSelectorContent(
periods = periods,
scrollState = scrollState,
) { selectedPeriod ->
presenter.setDueDate(selectedPeriod)
bottomSheetDialog.dismiss()
}
},
).show(supportFragmentManager, AlertBottomDialog::class.java.simpleName)
}

override fun openFormActivity() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.dhis2.usescases.events

import androidx.paging.PagingData
import kotlinx.coroutines.flow.Flow
import org.dhis2.commons.periods.Period
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.providers.InputDateValues
import org.dhis2.usescases.general.AbstractActivityContracts
import org.hisp.dhis.android.core.category.CategoryOption
Expand Down Expand Up @@ -32,5 +35,6 @@ class ScheduledEventContract {
fun getEventTei(): String
fun getEnrollment(): Enrollment?
fun getSelectableDates(program: Program, isDueDate: Boolean): SelectableDates?
fun fetchPeriods(scheduling: Boolean): Flow<PagingData<Period>>
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package org.dhis2.usescases.events

import androidx.paging.PagingData
import androidx.paging.map
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.map
import org.dhis2.commons.bindings.event
import org.dhis2.commons.bindings.programStage
import org.dhis2.commons.date.DateUtils
import org.dhis2.commons.periods.Period
import org.dhis2.commons.periods.PeriodUseCase
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.providers.DEFAULT_MAX_DATE
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.providers.DEFAULT_MIN_DATE
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.providers.InputDateValues
Expand All @@ -12,6 +21,7 @@ import org.hisp.dhis.android.core.arch.helpers.UidsHelper
import org.hisp.dhis.android.core.category.CategoryOption
import org.hisp.dhis.android.core.enrollment.Enrollment
import org.hisp.dhis.android.core.event.EventStatus
import org.hisp.dhis.android.core.period.PeriodType
import org.hisp.dhis.android.core.program.Program
import org.hisp.dhis.mobile.ui.designsystem.component.SelectableDates
import timber.log.Timber
Expand All @@ -27,6 +37,7 @@ class ScheduledEventPresenterImpl(
) : ScheduledEventContract.Presenter {

private lateinit var disposable: CompositeDisposable
private val periodUseCase = PeriodUseCase(d2)

override fun init() {
disposable = CompositeDisposable()
Expand Down Expand Up @@ -106,11 +117,53 @@ class ScheduledEventPresenterImpl(
} else {
null
}
val minDateString = if (minDate == null) null else SimpleDateFormat("ddMMyyyy", Locale.US).format(minDate)
val maxDateString = if (isDueDate) DEFAULT_MAX_DATE else SimpleDateFormat("ddMMyyyy", Locale.US).format(Date(System.currentTimeMillis() - 1000))
val minDateString =
if (minDate == null) null else SimpleDateFormat("ddMMyyyy", Locale.US).format(minDate)
val maxDateString = if (isDueDate) {
DEFAULT_MAX_DATE
} else {
SimpleDateFormat(
"ddMMyyyy",
Locale.US,
).format(Date(System.currentTimeMillis() - 1000))
}
return SelectableDates(minDateString ?: DEFAULT_MIN_DATE, maxDateString)
}

override fun fetchPeriods(scheduling: Boolean): Flow<PagingData<Period>> {
return with(periodUseCase) {
val event = d2.event(eventUid) ?: return emptyFlow()
val stage = event.programStage()?.let { d2.programStage(it) } ?: return emptyFlow()
val unavailableDates = getEventUnavailableDates(
programStageUid = stage.uid(),
enrollmentUid = event.enrollment(),
currentEventUid = eventUid,
)
fetchPeriods(
periodType = stage.periodType() ?: PeriodType.Daily,
selectedDate = if (scheduling) {
event.dueDate()
} else {
event.eventDate()
},
initialDate = getEventPeriodMinDate(
programStage = stage,
isScheduling = scheduling,
eventEnrollmentUid = event.enrollment(),
),
maxDate = getEventPeriodMaxDate(
programStage = stage,
isScheduling = scheduling,
eventEnrollmentUid = event.enrollment(),
),
).map { paging ->
paging.map { period ->
period.copy(enabled = unavailableDates.contains(period.startDate).not())
}
}
}
}

override fun setDueDate(date: Date) {
d2.eventModule().events().uid(eventUid).setDueDate(date)
d2.eventModule().events().uid(eventUid).setStatus(EventStatus.SCHEDULE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ class EventDetailsRepository(
else -> OrganisationUnit.Scope.SCOPE_DATA_CAPTURE
}

fun isScheduling(): Boolean = eventCreationType == EventCreationType.SCHEDULE

fun getOrganisationUnit(orgUnitUid: String): OrganisationUnit? {
return d2.organisationUnitModule().organisationUnits()
.byUid()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.dhis2.usescases.eventsWithoutRegistration.eventDetails.domain

import androidx.paging.PagingData
import androidx.paging.map
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.map
import org.dhis2.commons.periods.Period
import org.dhis2.commons.periods.PeriodUseCase
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.data.EventDetailsRepository
import org.hisp.dhis.android.core.period.PeriodType

class ConfigurePeriodSelector(
private val eventDetailRepository: EventDetailsRepository,
private val periodUseCase: PeriodUseCase,
) {
operator fun invoke(): Flow<PagingData<Period>> {
val programStage = eventDetailRepository.getProgramStage() ?: return emptyFlow()
val event = eventDetailRepository.getEvent()
val periodType = programStage.periodType() ?: PeriodType.Daily
return with(periodUseCase) {
val unavailableDate = getEventUnavailableDates(
programStageUid = programStage.uid(),
enrollmentUid = event?.enrollment(),
currentEventUid = event?.uid(),
)
fetchPeriods(
periodType = periodType,
selectedDate = if (eventDetailRepository.isScheduling()) {
event?.dueDate()
} else {
event?.eventDate()
},
initialDate = getEventPeriodMinDate(
programStage = programStage,
isScheduling = eventDetailRepository.isScheduling(),
eventEnrollmentUid = event?.enrollment(),
),
maxDate = getEventPeriodMaxDate(
programStage = programStage,
isScheduling = eventDetailRepository.isScheduling(),
eventEnrollmentUid = event?.enrollment(),
),
).map { paging ->
paging.map { period ->
period.copy(enabled = unavailableDate.contains(period.startDate).not())
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import dagger.Provides
import org.dhis2.commons.data.EventCreationType
import org.dhis2.commons.di.dagger.PerFragment
import org.dhis2.commons.locationprovider.LocationProvider
import org.dhis2.commons.periods.PeriodUseCase
import org.dhis2.commons.prefs.PreferenceProvider
import org.dhis2.commons.prefs.PreferenceProviderImpl
import org.dhis2.commons.resources.DhisPeriodUtils
Expand All @@ -30,6 +31,7 @@ import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.domain.Configu
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.domain.ConfigureEventDetails
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.domain.ConfigureEventReportDate
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.domain.ConfigureOrgUnit
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.domain.ConfigurePeriodSelector
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.domain.CreateOrUpdateEventDetails
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.providers.EventDetailResourcesProvider
import org.dhis2.usescases.eventsWithoutRegistration.eventDetails.ui.EventDetailsViewModelFactory
Expand Down Expand Up @@ -101,6 +103,18 @@ class EventDetailsModule(
)
}

@Provides
@PerFragment
fun provideConfigurePeriodSelector(
eventDetailsRepository: EventDetailsRepository,
periodUseCase: PeriodUseCase,
): ConfigurePeriodSelector {
return ConfigurePeriodSelector(
eventDetailRepository = eventDetailsRepository,
periodUseCase = periodUseCase,
)
}

@Provides
@PerFragment
fun eventDetailsViewModelFactory(
Expand All @@ -112,6 +126,7 @@ class EventDetailsModule(
locationProvider: LocationProvider,
eventDetailResourcesProvider: EventDetailResourcesProvider,
metadataIconProvider: MetadataIconProvider,
configurePeriodSelector: ConfigurePeriodSelector,
): EventDetailsViewModelFactory {
return EventDetailsViewModelFactory(
ConfigureEventDetails(
Expand Down Expand Up @@ -152,6 +167,7 @@ class EventDetailsModule(
resourcesProvider = resourcesProvider,
),
eventDetailResourcesProvider = eventDetailResourcesProvider,
configurePeriodSelector = configurePeriodSelector,
)
}
}
Loading

0 comments on commit d8cb13b

Please sign in to comment.