Skip to content

Commit e96f915

Browse files
feat : New Loan Account – Charges Step Implementation (#2497)
1 parent 586174c commit e96f915

File tree

10 files changed

+913
-25
lines changed

10 files changed

+913
-25
lines changed

core/database/src/commonMain/kotlin/com/mifos/room/entities/templates/loans/LoanTemplate.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import com.mifos.core.model.objects.template.loan.TransactionProcessingStrategyO
3838
import com.mifos.core.model.utils.IgnoredOnParcel
3939
import com.mifos.core.model.utils.Parcelable
4040
import com.mifos.core.model.utils.Parcelize
41+
import com.mifos.room.entities.client.ChargesEntity
4142
import com.mifos.room.entities.noncore.DataTableEntity
4243
import kotlinx.serialization.Serializable
4344

@@ -190,4 +191,6 @@ data class LoanTemplate(
190191

191192
val loanScheduleType: TermFrequencyTypeOptions? = null,
192193

194+
val overdueCharges: List<ChargesEntity> = emptyList(),
195+
193196
) : Parcelable

core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/icon/MifosIcons.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,9 @@ import androidx.compose.material.icons.rounded.Task
113113
import androidx.compose.material.icons.rounded.Translate
114114
import androidx.compose.ui.graphics.vector.ImageVector
115115
import fluent.ui.system.icons.FluentIcons
116+
import fluent.ui.system.icons.filled.ChevronDown
116117
import fluent.ui.system.icons.filled.ChevronRight
118+
import fluent.ui.system.icons.filled.ChevronUp
117119
import fluent.ui.system.icons.regular.ChevronLeft
118120

119121
object MifosIcons {
@@ -217,6 +219,8 @@ object MifosIcons {
217219

218220
val ChevronLeft = FluentIcons.Regular.ChevronLeft
219221
val ChevronRight = FluentIcons.Filled.ChevronRight
222+
val ChevronUp = FluentIcons.Filled.ChevronUp
223+
val ChevronDown = FluentIcons.Filled.ChevronDown
220224

221225
val PiggyBank = Icons.Outlined.Savings
222226

core/ui/src/commonMain/composeResources/values/strings.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@
9898
<string name="error_digits_only">This field should contain digits only</string>
9999
<string name="error_number_zero">Number should not be zero</string>
100100

101+
<string name="ok">Ok</string>
102+
<string name="cancel">Cancel</string>
103+
<string name="name">Name</string>
104+
<string name="date">Date</string>
105+
<string name="amount">Amount</string>
106+
<string name="type">Type</string>
107+
<string name="collected_on">Collected On</string>
108+
101109
<string name="client_share_accounts_share_product">Share Product</string>
102110
<string name="client_share_accounts_pending_for_approval_shares">Pending For Approval Shares</string>
103111
<string name="client_share_accounts_approved_shares">Approved Shares</string>
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
/*
2+
* Copyright 2025 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
7+
*
8+
* See https://github.com/openMF/android-client/blob/master/LICENSE.md
9+
*/
10+
package com.mifos.core.ui.components
11+
12+
import androidclient.core.ui.generated.resources.Res
13+
import androidclient.core.ui.generated.resources.amount
14+
import androidclient.core.ui.generated.resources.cancel
15+
import androidclient.core.ui.generated.resources.collected_on
16+
import androidclient.core.ui.generated.resources.date
17+
import androidclient.core.ui.generated.resources.name
18+
import androidclient.core.ui.generated.resources.ok
19+
import androidclient.core.ui.generated.resources.type
20+
import androidx.compose.foundation.layout.Column
21+
import androidx.compose.foundation.layout.Spacer
22+
import androidx.compose.foundation.layout.height
23+
import androidx.compose.foundation.layout.padding
24+
import androidx.compose.foundation.text.KeyboardOptions
25+
import androidx.compose.material3.DatePicker
26+
import androidx.compose.material3.DatePickerDialog
27+
import androidx.compose.material3.ExperimentalMaterial3Api
28+
import androidx.compose.material3.Text
29+
import androidx.compose.material3.TextButton
30+
import androidx.compose.material3.rememberDatePickerState
31+
import androidx.compose.runtime.Composable
32+
import androidx.compose.ui.Modifier
33+
import androidx.compose.ui.text.input.KeyboardType
34+
import com.mifos.core.designsystem.component.MifosBottomSheet
35+
import com.mifos.core.designsystem.component.MifosDatePickerTextField
36+
import com.mifos.core.designsystem.component.MifosOutlinedTextField
37+
import com.mifos.core.designsystem.component.MifosTextFieldConfig
38+
import com.mifos.core.designsystem.component.MifosTextFieldDropdown
39+
import com.mifos.core.designsystem.theme.DesignToken
40+
import com.mifos.core.designsystem.theme.MifosTheme
41+
import com.mifos.core.designsystem.theme.MifosTypography
42+
import kotlinx.datetime.Clock
43+
import org.jetbrains.compose.resources.stringResource
44+
import org.jetbrains.compose.ui.tooling.preview.Preview
45+
46+
@OptIn(ExperimentalMaterial3Api::class)
47+
@Composable
48+
fun AddChargeBottomSheet(
49+
title: String,
50+
confirmText: String,
51+
dismissText: String,
52+
showDatePicker: Boolean,
53+
selectedChargeName: String,
54+
selectedDate: String,
55+
chargeAmount: String,
56+
chargeType: String,
57+
chargeCollectedOn: String,
58+
chargeOptions: List<String>,
59+
onConfirm: () -> Unit,
60+
onDismiss: () -> Unit,
61+
onChargeSelected: (Int, String) -> Unit,
62+
onDatePick: (Boolean) -> Unit,
63+
onDateChange: (Long) -> Unit,
64+
onAmountChange: (String) -> Unit,
65+
) {
66+
MifosBottomSheet(
67+
onDismiss = onDismiss,
68+
content = {
69+
val datePickerState = rememberDatePickerState(
70+
initialSelectedDateMillis = Clock.System.now().toEpochMilliseconds(),
71+
)
72+
73+
Column(
74+
Modifier.padding(DesignToken.padding.large),
75+
) {
76+
Text(text = title, style = MifosTypography.titleMediumEmphasized)
77+
78+
Spacer(Modifier.height(DesignToken.padding.large))
79+
if (showDatePicker) {
80+
DatePickerDialog(
81+
onDismissRequest = { onDatePick(false) },
82+
confirmButton = {
83+
TextButton(
84+
onClick = {
85+
onDatePick(false)
86+
datePickerState.selectedDateMillis?.let {
87+
onDateChange(it)
88+
}
89+
},
90+
) { Text(stringResource(Res.string.ok)) }
91+
},
92+
dismissButton = {
93+
TextButton(
94+
onClick = { onDatePick(false) },
95+
) { Text(stringResource(Res.string.cancel)) }
96+
},
97+
) {
98+
DatePicker(state = datePickerState)
99+
}
100+
}
101+
102+
MifosTextFieldDropdown(
103+
value = selectedChargeName,
104+
onValueChanged = {},
105+
onOptionSelected = onChargeSelected,
106+
options = chargeOptions,
107+
label = stringResource(Res.string.name),
108+
)
109+
110+
MifosDatePickerTextField(
111+
value = selectedDate,
112+
label = stringResource(Res.string.date),
113+
openDatePicker = { onDatePick(true) },
114+
)
115+
116+
Spacer(Modifier.height(DesignToken.padding.large))
117+
118+
MifosOutlinedTextField(
119+
value = chargeAmount,
120+
onValueChange = onAmountChange,
121+
label = stringResource(Res.string.amount),
122+
config = MifosTextFieldConfig(
123+
keyboardOptions = KeyboardOptions(
124+
keyboardType = KeyboardType.Decimal,
125+
),
126+
),
127+
)
128+
129+
Spacer(Modifier.height(DesignToken.padding.large))
130+
131+
MifosOutlinedTextField(
132+
value = chargeType,
133+
onValueChange = {},
134+
label = stringResource(Res.string.type),
135+
config = MifosTextFieldConfig(
136+
readOnly = true,
137+
enabled = false,
138+
),
139+
)
140+
141+
Spacer(Modifier.height(DesignToken.padding.large))
142+
143+
MifosOutlinedTextField(
144+
value = chargeCollectedOn,
145+
onValueChange = {},
146+
label = stringResource(Res.string.collected_on),
147+
config = MifosTextFieldConfig(
148+
readOnly = true,
149+
enabled = false,
150+
),
151+
)
152+
153+
Spacer(Modifier.height(DesignToken.padding.large))
154+
155+
MifosTwoButtonRow(
156+
firstBtnText = dismissText,
157+
secondBtnText = confirmText,
158+
onFirstBtnClick = onDismiss,
159+
onSecondBtnClick = onConfirm,
160+
isSecondButtonEnabled = chargeAmount.isNotEmpty() && chargeType.isNotEmpty(),
161+
)
162+
}
163+
},
164+
)
165+
}
166+
167+
@Preview
168+
@Composable
169+
private fun AddChargeBottomSheetPreview() {
170+
val sampleChargeOptions = listOf("Bank Fee", "Overdue Fee", "Processing Fee")
171+
172+
MifosTheme {
173+
AddChargeBottomSheet(
174+
title = "Add Charge",
175+
confirmText = "Confirm",
176+
dismissText = "Cancel",
177+
showDatePicker = false,
178+
selectedChargeName = sampleChargeOptions[0],
179+
selectedDate = "2025-09-06",
180+
chargeAmount = "1500",
181+
chargeType = "Flat",
182+
chargeCollectedOn = "2025-09-06",
183+
chargeOptions = sampleChargeOptions,
184+
onConfirm = { },
185+
onDismiss = { },
186+
onChargeSelected = { _, _ -> },
187+
onDatePick = { },
188+
onDateChange = { },
189+
onAmountChange = {},
190+
)
191+
}
192+
}

0 commit comments

Comments
 (0)