diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/TransactionsList.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/TransactionsList.kt index 2467f6d..4908367 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/TransactionsList.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/composables/TransactionsList.kt @@ -16,15 +16,17 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import kotlinx.datetime.LocalDate import net.codinux.banking.client.model.Amount -import net.codinux.banking.ui.extensions.toBigDecimal -import net.codinux.banking.ui.forms.RoundedCornersCard import net.codinux.banking.ui.config.Colors import net.codinux.banking.ui.config.DI +import net.codinux.banking.ui.forms.RoundedCornersCard +import net.codinux.banking.ui.service.CalculatorService import net.codinux.banking.ui.state.UiState import org.jetbrains.compose.ui.tooling.preview.Preview private val filterService = DI.accountTransactionsFilterService +private val calculator = CalculatorService() + private val formatUtil = DI.formatUtil @Composable @@ -46,49 +48,56 @@ fun TransactionsList(uiState: UiState) { derivedStateOf { transactionsToDisplay.groupBy { LocalDate(it.valueDate.year, it.valueDate.monthNumber, 1) } } } - LazyColumn( - modifier = Modifier.padding(horizontal = 8.dp).widthIn(max = 800.dp) - ) { - items(groupedByMonth.keys.sortedDescending()) { month -> - Column(Modifier.fillMaxWidth()) { - Text( - text = DI.formatUtil.formatMonth(month), - fontSize = 16.sp, - fontWeight = FontWeight.SemiBold, - modifier = Modifier.padding(top = 8.dp, bottom = 2.dp), - ) - Spacer(Modifier.height(4.dp)) + Column(Modifier.fillMaxHeight().padding(horizontal = 8.dp)) { + Row(Modifier.fillMaxWidth().height(32.dp).background(Colors.Zinc200), verticalAlignment = Alignment.CenterVertically) { + Text("${transactionsToDisplay.size} Umsätze") + Spacer(Modifier.weight(1f)) - val monthTransactions = groupedByMonth[month].orEmpty().sortedByDescending { it.valueDate } + val balance = calculator.calculateBalanceOfDisplayedTransactions(transactionsToDisplay, userAccounts, transactionsFilter) + Text(formatUtil.formatAmount(balance, "EUR"), color = formatUtil.getColorForAmount(balance)) + } - RoundedCornersCard { - Column(Modifier.background(Color.White)) { // LazyColumn inside LazyColumn is not allowed - monthTransactions.forEachIndexed { index, transaction -> - val backgroundColor = if (index % 2 == 1) Colors.Zinc100_50 else Color.Transparent - TransactionListItem(userAccountsId[transaction.userAccountId], transaction, backgroundColor) + LazyColumn(Modifier.fillMaxHeight()) { + items(groupedByMonth.keys.sortedDescending()) { month -> + Column(Modifier.fillMaxWidth()) { + Text( + text = DI.formatUtil.formatMonth(month), + fontSize = 16.sp, + fontWeight = FontWeight.SemiBold, + modifier = Modifier.padding(top = 8.dp, bottom = 2.dp), + ) - if (index < monthTransactions.size - 1) { - Divider(color = Colors.Zinc200, thickness = 1.dp) + Spacer(Modifier.height(4.dp)) + + val monthTransactions = groupedByMonth[month].orEmpty().sortedByDescending { it.valueDate } + + RoundedCornersCard { + Column(Modifier.background(Color.White)) { // LazyColumn inside LazyColumn is not allowed + monthTransactions.forEachIndexed { index, transaction -> + val backgroundColor = if (index % 2 == 1) Colors.Zinc100_50 else Color.Transparent + TransactionListItem(userAccountsId[transaction.userAccountId], transaction, backgroundColor) + + if (index < monthTransactions.size - 1) { + Divider(color = Colors.Zinc200, thickness = 1.dp) + } } } } - } - Column(Modifier.fillMaxWidth().padding(top = 10.dp), horizontalAlignment = Alignment.End) { - Text( - // TODO: find a better solution - text = formatUtil.formatAmount(Amount(monthTransactions.map { it.amount.toBigDecimal() }.filter { it > 0 }.sum().toString()), "EUR"), - color = formatUtil.getColorForAmount(Amount.Zero) - ) - } + Column(Modifier.fillMaxWidth().padding(top = 10.dp), horizontalAlignment = Alignment.End) { + Text( + text = formatUtil.formatAmount(calculator.sumIncome(monthTransactions), calculator.getTransactionsCurrency(monthTransactions)), + color = formatUtil.getColorForAmount(Amount.Zero) + ) + } - Column(Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 16.dp), horizontalAlignment = Alignment.End) { - Text( - // TODO: find a better solution - text = formatUtil.formatAmount(Amount(monthTransactions.map { it.amount.toBigDecimal() }.filter { it < 0 }.sum().toString()), "EUR"), - color = formatUtil.getColorForAmount(Amount("-1")) - ) + Column(Modifier.fillMaxWidth().padding(top = 2.dp, bottom = 16.dp), horizontalAlignment = Alignment.End) { + Text( + text = formatUtil.formatAmount(calculator.sumExpenses(monthTransactions), calculator.getTransactionsCurrency(monthTransactions)), + color = formatUtil.getColorForAmount(Amount("-1")) + ) + } } } } diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/AccountTransactionsFilter.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/AccountTransactionsFilter.kt index b1145bf..0e3e7f6 100644 --- a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/AccountTransactionsFilter.kt +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/model/AccountTransactionsFilter.kt @@ -6,6 +6,10 @@ import net.codinux.banking.dataaccess.entities.UserAccountEntity class AccountTransactionsFilter { + val noFiltersApplied: Boolean + get() = showAllAccounts && noSearchTermApplied + + var selectedAccounts = mutableStateOf>(emptyList()) private set @@ -27,6 +31,9 @@ class AccountTransactionsFilter { var searchTerm by mutableStateOf("") private set + val noSearchTermApplied: Boolean + get() = searchTerm.isBlank() + fun updateSearchTerm(searchTerm: String) { this.searchTerm = searchTerm } diff --git a/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/service/CalculatorService.kt b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/service/CalculatorService.kt new file mode 100644 index 0000000..a516961 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/net/codinux/banking/ui/service/CalculatorService.kt @@ -0,0 +1,49 @@ +package net.codinux.banking.ui.service + +import net.codinux.banking.client.model.Amount +import net.codinux.banking.dataaccess.entities.UserAccountEntity +import net.codinux.banking.ui.extensions.toBigDecimal +import net.codinux.banking.ui.model.AccountTransactionViewModel +import net.codinux.banking.ui.model.AccountTransactionsFilter + +class CalculatorService { + + fun sumTransactions(transactions: Collection): Amount = + // TODO: find a better solution + Amount(transactions.sumOf { it.amount.toBigDecimal() }.toString()) + + fun sumAmounts(amounts: Collection): Amount = + // TODO: find a better solution + Amount(amounts.sumOf { it.toBigDecimal() }.toString()) + + fun sumIncome(transactions: Collection): Amount = + // TODO: find a better solution + Amount(transactions.map { it.amount.toBigDecimal() }.filter { it > 0 }.sum().toString()) + + fun sumExpenses(transactions: Collection): Amount = + // TODO: find a better solution + Amount(transactions.map { it.amount.toBigDecimal() }.filter { it < 0 }.sum().toString()) + + fun calculateBalanceOfDisplayedTransactions(transactions: Collection, userAccounts: Collection, filter: AccountTransactionsFilter): Amount { + if (filter.noFiltersApplied) { + return sumAmounts(userAccounts.flatMap { it.accounts.map { it.balance } }) + } + + val selectedAccount = filter.selectedAccount + + return if (selectedAccount != null) { + if (selectedAccount.bankAccount != null) { + selectedAccount.bankAccount.balance + } else { + sumAmounts(selectedAccount.userAccount.accounts.map { it.balance }) + } + } else { + sumTransactions(transactions) + } + } + + fun getTransactionsCurrency(transactions: Collection): String = + // TODO: really get transactions' currency + "EUR" + +} \ No newline at end of file