Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions cmp-android/dependencies/prodReleaseRuntimeClasspath.tree.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3012,6 +3012,38 @@
| | +--- org.jetbrains.compose.material3:material3:1.8.2 (*)
| | +--- org.jetbrains.compose.components:components-resources:1.8.2 (*)
| | \--- org.jetbrains.compose.components:components-ui-tooling-preview:1.8.2 (*)
| +--- project :feature:autopay
| | +--- androidx.lifecycle:lifecycle-runtime-compose:2.9.2 (*)
| | +--- androidx.lifecycle:lifecycle-viewmodel-compose:2.9.2 (*)
| | +--- androidx.tracing:tracing-ktx:1.3.0 (*)
| | +--- io.insert-koin:koin-bom:4.1.0 (*)
| | +--- io.insert-koin:koin-android:4.1.0 (*)
| | +--- io.insert-koin:koin-androidx-compose:4.1.0 (*)
| | +--- io.insert-koin:koin-androidx-navigation:4.1.0 (*)
| | +--- io.insert-koin:koin-core-viewmodel:4.1.0 (*)
| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.1.20 -> 2.1.21 (*)
| | +--- io.insert-koin:koin-core:4.1.0 (*)
| | +--- io.insert-koin:koin-annotations:2.1.0 (*)
| | +--- project :core:ui (*)
| | +--- project :core:designsystem (*)
| | +--- project :core:data (*)
| | +--- io.insert-koin:koin-compose:4.1.0 (*)
| | +--- io.insert-koin:koin-compose-viewmodel:4.1.0 (*)
| | +--- org.jetbrains.compose.runtime:runtime:1.8.2 (*)
| | +--- org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose:2.9.1 (*)
| | +--- org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.9.1 (*)
| | +--- org.jetbrains.androidx.lifecycle:lifecycle-viewmodel:2.9.1 (*)
| | +--- org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.1 (*)
| | +--- org.jetbrains.androidx.savedstate:savedstate:1.3.1 (*)
| | +--- org.jetbrains.androidx.core:core-bundle:1.0.1 (*)
| | +--- org.jetbrains.androidx.navigation:navigation-compose:2.9.0-beta03 (*)
| | +--- org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.8 (*)
| | +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3 -> 1.8.0 (*)
| | +--- org.jetbrains.compose.ui:ui:1.8.2 (*)
| | +--- org.jetbrains.compose.foundation:foundation:1.8.2 (*)
| | +--- org.jetbrains.compose.material3:material3:1.8.2 (*)
| | +--- org.jetbrains.compose.components:components-resources:1.8.2 (*)
| | \--- org.jetbrains.compose.components:components-ui-tooling-preview:1.8.2 (*)
| \--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.1.20 (*)
+--- project :core:data (*)
+--- project :core:ui (*)
Expand Down
1 change: 1 addition & 0 deletions cmp-android/dependencies/prodReleaseRuntimeClasspath.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
:core:ui
:feature:accounts
:feature:auth
:feature:autopay
:feature:editpassword
:feature:faq
:feature:finance
Expand Down
2 changes: 1 addition & 1 deletion cmp-android/prodRelease-badging.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package: name='org.mifospay' versionCode='1' versionName='2025.8.2-beta.0.3' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
package: name='org.mifospay' versionCode='1' versionName='2025.8.4-beta.0.9' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
minSdkVersion:'26'
targetSdkVersion:'34'
uses-permission: name='android.permission.INTERNET'
Expand Down
1 change: 1 addition & 0 deletions cmp-shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ kotlin {
implementation(projects.feature.qr)
implementation(projects.feature.merchants)
implementation(projects.feature.upiSetup)
implementation(projects.feature.autopay)
}

desktopMain.dependencies {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import org.mifospay.core.network.di.LocalModule
import org.mifospay.core.network.di.NetworkModule
import org.mifospay.feature.accounts.di.AccountsModule
import org.mifospay.feature.auth.di.AuthModule
import org.mifospay.feature.autopay.di.AutoPayModule
import org.mifospay.feature.editpassword.di.EditPasswordModule
import org.mifospay.feature.faq.di.FaqModule
import org.mifospay.feature.history.di.HistoryModule
Expand Down Expand Up @@ -88,6 +89,7 @@ object KoinModules {
QrModule,
MerchantsModule,
UpiSetupModule,
AutoPayModule,
)
}
private val LibraryModule = module {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ import org.mifospay.feature.accounts.savingsaccount.addEditSavingAccountScreen
import org.mifospay.feature.accounts.savingsaccount.details.navigateToSavingAccountDetails
import org.mifospay.feature.accounts.savingsaccount.details.savingAccountDetailRoute
import org.mifospay.feature.accounts.savingsaccount.navigateToSavingAccountAddEdit
import org.mifospay.feature.autopay.AutoPayScreen
import org.mifospay.feature.autopay.autoPayGraph
import org.mifospay.feature.autopay.navigateToAutoPay
import org.mifospay.feature.autopay.navigateToAutoPayHistory
import org.mifospay.feature.autopay.navigateToAutoPayPreferences
import org.mifospay.feature.autopay.navigateToAutoPayRules
import org.mifospay.feature.autopay.navigateToAutoPayScheduleDetails
import org.mifospay.feature.autopay.navigateToAutoPaySetup
import org.mifospay.feature.editpassword.navigation.editPasswordScreen
import org.mifospay.feature.editpassword.navigation.navigateToEditPassword
import org.mifospay.feature.faq.navigation.faqScreen
Expand Down Expand Up @@ -72,7 +80,12 @@ import org.mifospay.feature.savedcards.details.cardDetailRoute
import org.mifospay.feature.savedcards.details.navigateToCardDetails
import org.mifospay.feature.send.money.SendMoneyScreen
import org.mifospay.feature.send.money.navigation.SEND_MONEY_BASE_ROUTE
import org.mifospay.feature.send.money.navigation.SEND_MONEY_OPTIONS_ROUTE
import org.mifospay.feature.send.money.navigation.navigateToPayeeDetailsScreen
import org.mifospay.feature.send.money.navigation.navigateToSendMoneyOptionsScreen
import org.mifospay.feature.send.money.navigation.navigateToSendMoneyScreen
import org.mifospay.feature.send.money.navigation.payeeDetailsScreen
import org.mifospay.feature.send.money.navigation.sendMoneyOptionsScreen
import org.mifospay.feature.send.money.navigation.sendMoneyScreen
import org.mifospay.feature.settings.navigation.settingsScreen
import org.mifospay.feature.standing.instruction.StandingInstructionsScreen
Expand All @@ -97,6 +110,7 @@ internal fun MifosNavHost(
onBackClick = navController::navigateUp,
navigateToTransferScreen = navController::navigateToTransferScreen,
navigateToScanQrScreen = navController::navigateToScanQr,
navigateToPayeeDetails = navController::navigateToPayeeDetailsScreen,
showTopBar = false,
)
},
Expand All @@ -121,6 +135,26 @@ internal fun MifosNavHost(
navigateToInvoiceDetailScreen = navController::navigateToInvoiceDetail,
)
},
TabContent(PaymentsScreenContents.AUTOPAY.name) {
AutoPayScreen(
onNavigateToSetup = {
navController.navigateToAutoPaySetup()
},
onNavigateToRules = {
navController.navigateToAutoPayRules()
},
onNavigateToPreferences = {
navController.navigateToAutoPayPreferences()
},
onNavigateToHistory = {
navController.navigateToAutoPayHistory()
},
onNavigateToScheduleDetails = { scheduleId ->
navController.navigateToAutoPayScheduleDetails(scheduleId)
},
showTopBar = false,
)
},
)

val tabContents = listOf(
Expand Down Expand Up @@ -160,7 +194,10 @@ internal fun MifosNavHost(
onRequest = {
navController.navigateToShowQrScreen()
},
onPay = navController::navigateToSendMoneyScreen,
onPay = navController::navigateToSendMoneyOptionsScreen,
onAutoPay = {
navController.navigateToAutoPay()
},
navigateToTransactionDetail = navController::navigateToSpecificTransaction,
navigateToAccountDetail = navController::navigateToSavingAccountDetails,
)
Expand Down Expand Up @@ -279,12 +316,55 @@ internal fun MifosNavHost(
navigateBack = navController::navigateUp,
)

sendMoneyOptionsScreen(
onBackClick = navController::popBackStack,
onScanQrClick = {
// This is now handled by the ViewModel using ML Kit scanner
},
onPayAnyoneClick = {
// TODO: Navigate to Pay Anyone screen
},
onBankTransferClick = {
// TODO: Navigate to Bank Transfer screen
},
onFineractPaymentsClick = {
navController.navigateToSendMoneyScreen()
},
onAutoPayClick = {
navController.navigateToAutoPay()
},
onQrCodeScanned = { qrData ->
navController.navigateToSendMoneyScreen(
requestData = qrData,
navOptions = navOptions {
popUpTo(SEND_MONEY_OPTIONS_ROUTE) {
inclusive = true
}
},
)
},
onNavigateToPayeeDetails = { qrCodeData ->
navController.navigateToPayeeDetailsScreen(qrCodeData)
},
)

sendMoneyScreen(
onBackClick = navController::popBackStack,
navigateToTransferScreen = navController::navigateToTransferScreen,
navigateToPayeeDetailsScreen = navController::navigateToPayeeDetailsScreen,
navigateToScanQrScreen = navController::navigateToScanQr,
)

payeeDetailsScreen(
onBackClick = navController::popBackStack,
onNavigateToUpiPayment = { state ->
// TODO: Handle UPI payment navigation
},
onNavigateToFineractPayment = { state ->
// TODO: Handle Fineract payment navigation
},
)

transferScreen(
navigateBack = navController::popBackStack,
onTransferSuccess = {
Expand Down Expand Up @@ -322,6 +402,16 @@ internal fun MifosNavHost(
},
)
},
navigateToPayeeDetailsScreen = {
navController.navigateToPayeeDetailsScreen(
qrCodeData = it,
navOptions = navOptions {
popUpTo(SCAN_QR_ROUTE) {
inclusive = true
}
},
)
},
)

merchantTransferScreen(
Expand All @@ -332,5 +422,10 @@ internal fun MifosNavHost(
setupUpiPinScreen(
navigateBack = navController::navigateUp,
)

autoPayGraph(
navController = navController,
onNavigateBack = navController::navigateUp,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import org.mifospay.core.common.MifosDispatchers
import org.mifospay.core.data.repository.AccountRepository
import org.mifospay.core.data.repository.AssetRepository
import org.mifospay.core.data.repository.AuthenticationRepository
import org.mifospay.core.data.repository.AutoPayRepository
import org.mifospay.core.data.repository.BeneficiaryRepository
import org.mifospay.core.data.repository.ClientRepository
import org.mifospay.core.data.repository.DocumentRepository
Expand All @@ -36,6 +37,7 @@ import org.mifospay.core.data.repository.UserRepository
import org.mifospay.core.data.repositoryImpl.AccountRepositoryImpl
import org.mifospay.core.data.repositoryImpl.AssetRepositoryImpl
import org.mifospay.core.data.repositoryImpl.AuthenticationRepositoryImpl
import org.mifospay.core.data.repositoryImpl.AutoPayRepositoryImpl
import org.mifospay.core.data.repositoryImpl.BeneficiaryRepositoryImpl
import org.mifospay.core.data.repositoryImpl.ClientRepositoryImpl
import org.mifospay.core.data.repositoryImpl.DocumentRepositoryImpl
Expand Down Expand Up @@ -93,6 +95,7 @@ val RepositoryModule = module {
}
single<TwoFactorAuthRepository> { TwoFactorAuthRepositoryImpl(get(), get(ioDispatcher)) }
single<UserRepository> { UserRepositoryImpl(get(), get(ioDispatcher)) }
single<AutoPayRepository> { AutoPayRepositoryImpl(get(), get(ioDispatcher)) }

includes(platformModule)
single<PlatformDependentDataModule> { getPlatformDataModule }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright 2024 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/mobile-wallet/blob/master/LICENSE.md
*/
package org.mifospay.core.data.repository

import kotlinx.coroutines.flow.Flow
import org.mifospay.core.common.DataState
import org.mifospay.core.model.autopay.AutoPay
import org.mifospay.core.model.autopay.AutoPayHistory
import org.mifospay.core.model.autopay.AutoPayPayload
import org.mifospay.core.model.autopay.AutoPayTemplate
import org.mifospay.core.model.autopay.AutoPayUpdatePayload
import org.mifospay.core.model.autopay.UpcomingPayment
import org.mifospay.core.network.model.entity.Page

interface AutoPayRepository {
/**
* Get AutoPay template for creating new AutoPay schedules
*/
fun getAutoPayTemplate(
clientId: Long,
sourceAccountId: Long,
): Flow<DataState<AutoPayTemplate>>

/**
* Get all AutoPay schedules for a client
*/
fun getAllAutoPaySchedules(
clientId: Long,
): Flow<DataState<List<AutoPay>>>

/**
* Get AutoPay schedule by ID
*/
fun getAutoPaySchedule(autoPayId: Long): Flow<DataState<AutoPay>>

/**
* Create a new AutoPay schedule
*/
suspend fun createAutoPaySchedule(
payload: AutoPayPayload,
): DataState<String>

/**
* Update an existing AutoPay schedule
*/
suspend fun updateAutoPaySchedule(
autoPayId: Long,
payload: AutoPayUpdatePayload,
): DataState<String>

/**
* Delete an AutoPay schedule
*/
suspend fun deleteAutoPaySchedule(autoPayId: Long): DataState<String>

/**
* Pause an AutoPay schedule
*/
suspend fun pauseAutoPaySchedule(autoPayId: Long): DataState<String>

/**
* Resume a paused AutoPay schedule
*/
suspend fun resumeAutoPaySchedule(autoPayId: Long): DataState<String>

/**
* Get AutoPay payment history
*/
fun getAutoPayHistory(
autoPayId: Long,
limit: Int = 20,
): Flow<DataState<Page<AutoPayHistory>>>

/**
* Get upcoming payments for all AutoPay schedules
*/
fun getUpcomingPayments(
clientId: Long,
limit: Int = 10,
): Flow<DataState<List<UpcomingPayment>>>

/**
* Get AutoPay statistics for dashboard
*/
fun getAutoPayStatistics(
clientId: Long,
): Flow<DataState<AutoPayStatistics>>

/**
* Validate AutoPay payload before submission
*/
suspend fun validateAutoPayPayload(payload: AutoPayPayload): DataState<Boolean>
}

data class AutoPayStatistics(
val totalActiveSchedules: Int = 0,
val totalPausedSchedules: Int = 0,
val totalCompletedSchedules: Int = 0,
val totalUpcomingPayments: Int = 0,
val totalAmountThisMonth: Double = 0.0,
val currency: String = "USD",
)
Loading
Loading