Skip to content
This repository has been archived by the owner on Nov 5, 2024. It is now read-only.

Commit

Permalink
Fix issue 2740 (#2928)
Browse files Browse the repository at this point in the history
* Initial changes commit

* Feature Implementation
- LoanDetailsScreen displays list of all records involving records that increase and decrease total loan amount
- LoanDetailsScreenState contains calculated `loanTotalAmount`
- LoanRecordModal provides a selection if loan record decrease or increase loan amount. Provided loan record increase total amount, `Mark as interest` checkbox disappears.

* Feature Implementation
- DisplayLoan includes loanTotalAmount
- LoanViewModel calculates total amount paid as well as loan total amount.
- LoanScreen displays correct information about each loan.

* CI pipeline fix

- Tests were failing due to the missing field in backup test. Now by default `loanRecordType` in `LoanRecordEntity` has DECRESE value. This fixes the issue with the backup, and is the simplest fix. This makes sense because before this pull request all loan records were implicitly of type DECREASE.

* Fix detekt errors.

* Suppress detekt.

Suppressed detekt errors that were forcing this pull request to make changes unrelated to scope of this issue or keeping this pull request inconsistent with the rest of the codebase.
- Suppressed LongMethod for LoanInfoCard
- Suppressed DataClassDefaultValues for LoanRecordEntity, since there is a few default values already, and `LoanRecordEntity.loanRecordType` default value is the easiest fix for the backup problem.
- Suppressed MagicNumber and ClassNaming for Migration class to keep it consistent with the rest of migration classes.

* Fix Lint issue.

* Resolved part of the review requests.

* Fixed detekt.

* DB migration test

* Add check for LoanRecordType

* Fix broken test.
  • Loading branch information
michalguspiel authored Feb 8, 2024
1 parent b5450a3 commit f5012fb
Show file tree
Hide file tree
Showing 19 changed files with 1,226 additions and 45 deletions.
42 changes: 26 additions & 16 deletions screen/loans/src/main/java/com/ivy/loans/loan/LoanViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.viewModelScope
import com.ivy.base.legacy.SharedPrefs
import com.ivy.base.model.processByType
import com.ivy.data.db.dao.read.LoanRecordDao
import com.ivy.data.db.dao.read.SettingsDao
import com.ivy.data.db.dao.write.WriteLoanDao
Expand Down Expand Up @@ -164,22 +165,26 @@ class LoanViewModel @Inject constructor(
allLoans = ioThread {
loansAct(Unit)
.map { loan ->
val amountPaid = calculateAmountPaid(loan)
val loanAmount = loan.amount
val percentPaid = amountPaid / loanAmount
val (amountPaid, loanTotalAmount) = calculateAmountPaidAndTotalAmount(loan)
val percentPaid = if (loanTotalAmount != 0.0) {
amountPaid / loanTotalAmount
} else {
0.0
}
var currCode = findCurrencyCode(accounts.value, loan.accountId)

when (loan.type) {
LoanType.BORROW -> totalOweAmount += (loanAmount - amountPaid)
LoanType.LEND -> totalOwedAmount += (loanAmount - amountPaid)
LoanType.BORROW -> totalOweAmount += (loanTotalAmount - amountPaid)
LoanType.LEND -> totalOwedAmount += (loanTotalAmount - amountPaid)
}

DisplayLoan(
loan = loan,
loanTotalAmount = loanTotalAmount,
amountPaid = amountPaid,
currencyCode = currCode,
formattedDisplayText = "${amountPaid.format(currCode)} $currCode / ${
loanAmount.format(
loanTotalAmount.format(
currCode
)
} $currCode (${
Expand Down Expand Up @@ -300,18 +305,23 @@ class LoanViewModel @Inject constructor(
} ?: defaultCurrencyCode
}

private suspend fun calculateAmountPaid(loan: Loan): Double {
/**
* Calculates the total amount paid and the total loan amount including any changes made to the loan.
* @return A Pair containing the total amount paid and the total loan amount.
*/
private suspend fun calculateAmountPaidAndTotalAmount(loan: Loan): Pair<Double, Double> {
val loanRecords = ioThread { loanRecordDao.findAllByLoanId(loanId = loan.id) }
var amount = 0.0

loanRecords.forEach { loanRecord ->
if (!loanRecord.interest) {
val convertedAmount = loanRecord.convertedAmount ?: loanRecord.amount
amount += convertedAmount
}
val (amountPaid, loanTotalAmount) = loanRecords.fold(0.0 to loan.amount) { value, loanRecord ->
val (currentAmountPaid, currentLoanTotalAmount) = value
if (loanRecord.interest) return@fold value
val convertedAmount = loanRecord.convertedAmount ?: loanRecord.amount

loanRecord.loanRecordType.processByType(
decreaseAction = { currentAmountPaid + convertedAmount to currentLoanTotalAmount },
increaseAction = { currentAmountPaid to currentLoanTotalAmount + convertedAmount }
)
}

return amount
return amountPaid to loanTotalAmount
}

private fun updatePaidOffLoanVisibility() {
Expand Down
5 changes: 4 additions & 1 deletion screen/loans/src/main/java/com/ivy/loans/loan/LoansScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ private fun LoanHeader(

Spacer(Modifier.height(4.dp))

val leftToPay = loan.amount - displayLoan.amountPaid
val leftToPay = displayLoan.loanTotalAmount - displayLoan.amountPaid
BalanceRow(
modifier = Modifier
.align(Alignment.CenterHorizontally),
Expand Down Expand Up @@ -413,6 +413,7 @@ private fun Preview() {
type = LoanType.BORROW,
dateTime = LocalDateTime.now()
),
loanTotalAmount = 5500.0,
amountPaid = 0.0,
percentPaid = 0.4
),
Expand All @@ -425,6 +426,7 @@ private fun Preview() {
type = LoanType.BORROW,
dateTime = LocalDateTime.now()
),
loanTotalAmount = 252.36,
amountPaid = 124.23,
percentPaid = 0.2
),
Expand All @@ -437,6 +439,7 @@ private fun Preview() {
type = LoanType.LEND,
dateTime = LocalDateTime.now()
),
loanTotalAmount = 7000.0,
amountPaid = 8000.0,
percentPaid = 0.8
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.ivy.wallet.domain.data.Reorderable

data class DisplayLoan(
val loan: Loan,
val loanTotalAmount: Double,
val amountPaid: Double,
val currencyCode: String? = getDefaultFIATCurrency().currencyCode,
val formattedDisplayText: String = "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.ivy.base.model.LoanRecordType
import com.ivy.base.model.TransactionType
import com.ivy.base.model.processByType
import com.ivy.data.model.LoanType
import com.ivy.design.l0_system.UI
import com.ivy.design.l0_system.style
Expand Down Expand Up @@ -125,6 +127,7 @@ private fun BoxWithConstraintsScope.UI(
Header(
loan = state.loan,
baseCurrency = state.baseCurrency,
loanTotalAmount = state.loanTotalAmount,
amountPaid = state.amountPaid,
loanAmountPaid = state.loanAmountPaid,
itemColor = itemColor,
Expand Down Expand Up @@ -172,6 +175,13 @@ private fun BoxWithConstraintsScope.UI(
)
}
)
item {
InitialRecordItem(
loan = state.loan,
amount = state.loan.amount,
baseCurrency = state.baseCurrency,
)
}
}

if (state.displayLoanRecords.isEmpty()) {
Expand Down Expand Up @@ -234,14 +244,14 @@ private fun BoxWithConstraintsScope.UI(
private fun Header(
loan: Loan,
baseCurrency: String,
loanTotalAmount: Double,
amountPaid: Double,
loanAmountPaid: Double = 0.0,
itemColor: Color,
selectedLoanAccount: Account? = null,

onAmountClick: () -> Unit,
onEditLoan: () -> Unit,
onDeleteLoan: () -> Unit,
loanAmountPaid: Double = 0.0,
selectedLoanAccount: Account? = null,
onAddRecord: () -> Unit
) {
val contrastColor = findContrastTextColor(itemColor)
Expand Down Expand Up @@ -278,7 +288,7 @@ private fun Header(
},
textColor = contrastColor,
currency = baseCurrency,
balance = loan.amount,
balance = loanTotalAmount,
)

Spacer(Modifier.height(20.dp))
Expand All @@ -288,6 +298,7 @@ private fun Header(
baseCurrency = baseCurrency,
amountPaid = amountPaid,
loanAmountPaid = loanAmountPaid,
loanTotalAmount = loanTotalAmount,
selectedLoanAccount = selectedLoanAccount,
onAddRecord = onAddRecord
)
Expand Down Expand Up @@ -356,10 +367,12 @@ private fun LoanItem(
}
}

@Suppress("LongMethod")
@Composable
private fun LoanInfoCard(
loan: Loan,
baseCurrency: String,
loanTotalAmount: Double,
amountPaid: Double,
loanAmountPaid: Double = 0.0,
selectedLoanAccount: Account? = null,
Expand All @@ -373,8 +386,8 @@ private fun LoanInfoCard(
}

val contrastColor = findContrastTextColor(backgroundColor)
val percentPaid = amountPaid / loan.amount
val loanPercentPaid = loanAmountPaid / loan.amount
val percentPaid = amountPaid / loanTotalAmount
val loanPercentPaid = loanAmountPaid / loanTotalAmount
val nav = navigation()

Column(
Expand Down Expand Up @@ -437,7 +450,7 @@ private fun LoanInfoCard(
modifier = Modifier
.padding(horizontal = 24.dp)
.testTag("amount_paid"),
text = "${amountPaid.format(baseCurrency)} / ${loan.amount.format(baseCurrency)}",
text = "${amountPaid.format(baseCurrency)} / ${loanTotalAmount.format(baseCurrency)}",
style = UI.typo.nB1.style(
color = contrastColor,
fontWeight = FontWeight.ExtraBold
Expand Down Expand Up @@ -707,9 +720,23 @@ private fun LoanRecordItem(
if (loanRecord.note.isNullOrEmpty()) {
Spacer(Modifier.height(16.dp))
}
val transactionType = when (loan.type) {
LoanType.LEND -> {
loanRecord.loanRecordType.processByType(
increaseAction = { TransactionType.EXPENSE },
decreaseAction = { TransactionType.INCOME }
)
}

LoanType.BORROW -> {
loanRecord.loanRecordType.processByType(
increaseAction = { TransactionType.INCOME },
decreaseAction = { TransactionType.EXPENSE }
)
}
}
TypeAmountCurrency(
transactionType = if (loan.type == LoanType.LEND) TransactionType.INCOME else TransactionType.EXPENSE,
transactionType = transactionType,
dueDate = null,
currency = baseCurrency,
amount = loanRecord.amount
Expand All @@ -730,6 +757,61 @@ private fun LoanRecordItem(
}
}

@Composable
private fun InitialRecordItem(
loan: Loan,
amount: Double,
baseCurrency: String,
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.clip(UI.shapes.r4)
.background(UI.colors.medium, UI.shapes.r4)
.testTag("loan_record_item")
) {
IvyButton(
modifier = Modifier.padding(16.dp),
backgroundGradient = Gradient.solid(UI.colors.pure),
text = stringResource(id = R.string.initial_loan_record),
iconTint = UI.colors.pureInverse,
iconStart = getCustomIconIdS(
iconName = loan.icon,
defaultIcon = R.drawable.ic_custom_loan_s
),
textStyle = UI.typo.c.style(
color = UI.colors.pureInverse,
fontWeight = FontWeight.ExtraBold
),
padding = 8.dp,
) {}

loan.dateTime?.formatNicelyWithTime(
noWeekDay = false
)?.let { nicelyFormattedDate ->
Text(
modifier = Modifier.padding(horizontal = 24.dp),
text = nicelyFormattedDate.uppercase(),
style = UI.typo.nC.style(
color = Gray,
fontWeight = FontWeight.Bold
)
)
}

Spacer(modifier = Modifier.height(16.dp))

TypeAmountCurrency(
transactionType = if (loan.type == LoanType.LEND) TransactionType.EXPENSE else TransactionType.INCOME,
dueDate = null,
currency = baseCurrency,
amount = amount
)
Spacer(Modifier.height(16.dp))
}
}

@Composable
private fun NoLoanRecordsEmptyState() {
Column(
Expand Down Expand Up @@ -783,6 +865,7 @@ private fun Preview_Empty() {
),
displayLoanRecords = persistentListOf(),
amountPaid = 3821.00,
loanTotalAmount = 4023.54,
loanAmountPaid = 100.0,
accounts = persistentListOf(),
selectedLoanAccount = null,
Expand Down Expand Up @@ -816,25 +899,29 @@ private fun Preview_Records() {
amount = 123.45,
dateTime = timeNowUTC().minusDays(1),
note = "Cash",
loanId = UUID.randomUUID()
loanId = UUID.randomUUID(),
loanRecordType = LoanRecordType.INCREASE
)
),
DisplayLoanRecord(
LoanRecord(
amount = 0.50,
dateTime = timeNowUTC().minusYears(1),
loanId = UUID.randomUUID()
loanId = UUID.randomUUID(),
loanRecordType = LoanRecordType.DECREASE
)
),
DisplayLoanRecord(
LoanRecord(
amount = 1000.00,
dateTime = timeNowUTC().minusMonths(1),
note = "Revolut",
loanId = UUID.randomUUID()
loanId = UUID.randomUUID(),
loanRecordType = LoanRecordType.INCREASE
)
),
),
loanTotalAmount = 4023.54,
amountPaid = 3821.00,
loanAmountPaid = 100.0,
accounts = persistentListOf(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ data class LoanDetailsScreenState(
val baseCurrency: String,
val loan: Loan?,
val displayLoanRecords: ImmutableList<DisplayLoanRecord>,
val loanTotalAmount: Double,
val amountPaid: Double,
val loanAmountPaid: Double,
val accounts: ImmutableList<Account>,
Expand Down
Loading

0 comments on commit f5012fb

Please sign in to comment.