Skip to content

Commit f320736

Browse files
committed
fix: account balance not updated when deleting a transaction
1 parent 374c873 commit f320736

2 files changed

Lines changed: 154 additions & 1 deletion

File tree

‎app/src/main/java/com/ritesh/cashiro/data/repository/TransactionRepository.kt‎

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.ritesh.cashiro.data.repository
22

33
import com.ritesh.cashiro.data.database.dao.TransactionDao
4+
import com.ritesh.cashiro.data.database.entity.AccountBalanceEntity
45
import com.ritesh.cashiro.data.database.entity.TransactionEntity
56
import com.ritesh.cashiro.data.database.entity.TransactionType
67
import java.math.BigDecimal
@@ -13,7 +14,10 @@ import kotlinx.coroutines.flow.Flow
1314
import kotlinx.coroutines.flow.map
1415

1516
@Singleton
16-
class TransactionRepository @Inject constructor(private val transactionDao: TransactionDao) {
17+
class TransactionRepository @Inject constructor(
18+
private val transactionDao: TransactionDao,
19+
private val accountBalanceRepository: AccountBalanceRepository
20+
) {
1721
fun getAllTransactions(): Flow<List<TransactionEntity>> = transactionDao.getAllTransactions()
1822

1923
fun getTransactionCount(): Flow<Int> = transactionDao.getTransactionCount()
@@ -124,14 +128,17 @@ class TransactionRepository @Inject constructor(private val transactionDao: Tran
124128
} else {
125129
transactionDao.softDeleteTransaction(transaction.id)
126130
}
131+
reverseBalanceForDeletion(transaction)
127132
}
128133

129134
suspend fun deleteTransactionById(id: Long, hardDelete: Boolean = false) {
135+
val transaction = transactionDao.getTransactionById(id) ?: return
130136
if (hardDelete) {
131137
transactionDao.deleteTransactionById(id)
132138
} else {
133139
transactionDao.softDeleteTransaction(id)
134140
}
141+
reverseBalanceForDeletion(transaction)
135142
}
136143

137144
suspend fun deleteAllTransactions() = transactionDao.deleteAllTransactions()
@@ -144,6 +151,7 @@ class TransactionRepository @Inject constructor(private val transactionDao: Tran
144151

145152
suspend fun undoDeleteTransaction(transaction: TransactionEntity) {
146153
transactionDao.updateTransaction(transaction.copy(isDeleted = false))
154+
applyBalanceForTransaction(transaction)
147155
}
148156

149157
suspend fun deleteTransactions(transactions: List<TransactionEntity>, hardDelete: Boolean = false) {
@@ -153,14 +161,100 @@ class TransactionRepository @Inject constructor(private val transactionDao: Tran
153161
} else {
154162
transactionDao.softDeleteTransactions(transactionIds)
155163
}
164+
transactions.forEach { reverseBalanceForDeletion(it) }
156165
}
157166

158167
suspend fun undoDeleteTransactions(transactions: List<TransactionEntity>) {
159168
transactions.forEach { transaction ->
160169
transactionDao.updateTransaction(transaction.copy(isDeleted = false))
170+
applyBalanceForTransaction(transaction)
161171
}
162172
}
163173

174+
private suspend fun reverseBalanceForDeletion(transaction: TransactionEntity) {
175+
val bankName = transaction.bankName ?: return
176+
val accountLast4 = transaction.accountNumber ?: return
177+
val latestBalance = accountBalanceRepository.getLatestBalance(bankName, accountLast4) ?: return
178+
val currentBalance = latestBalance.balance
179+
val isCreditCard = latestBalance.isCreditCard
180+
181+
val reversedBalance = when {
182+
isCreditCard -> {
183+
when (transaction.transactionType) {
184+
TransactionType.EXPENSE, TransactionType.INVESTMENT -> currentBalance - transaction.amount
185+
TransactionType.INCOME -> currentBalance + transaction.amount
186+
else -> currentBalance
187+
}
188+
}
189+
else -> {
190+
when (transaction.transactionType) {
191+
TransactionType.EXPENSE, TransactionType.INVESTMENT -> currentBalance + transaction.amount
192+
TransactionType.INCOME -> currentBalance - transaction.amount
193+
else -> currentBalance
194+
}
195+
}
196+
}.max(BigDecimal.ZERO)
197+
198+
val balanceEntity = AccountBalanceEntity(
199+
bankName = bankName,
200+
accountLast4 = accountLast4,
201+
balance = reversedBalance,
202+
timestamp = LocalDateTime.now(),
203+
transactionId = null,
204+
creditLimit = latestBalance.creditLimit,
205+
isCreditCard = isCreditCard,
206+
iconResId = latestBalance.iconResId,
207+
iconName = latestBalance.iconName,
208+
isWallet = latestBalance.isWallet,
209+
color = latestBalance.color,
210+
currency = transaction.currency,
211+
sourceType = "DELETE_REVERSAL"
212+
)
213+
accountBalanceRepository.insertBalance(balanceEntity)
214+
}
215+
216+
private suspend fun applyBalanceForTransaction(transaction: TransactionEntity) {
217+
val bankName = transaction.bankName ?: return
218+
val accountLast4 = transaction.accountNumber ?: return
219+
val latestBalance = accountBalanceRepository.getLatestBalance(bankName, accountLast4) ?: return
220+
val currentBalance = latestBalance.balance
221+
val isCreditCard = latestBalance.isCreditCard
222+
223+
val newBalance = when {
224+
isCreditCard -> {
225+
when (transaction.transactionType) {
226+
TransactionType.EXPENSE, TransactionType.INVESTMENT -> currentBalance + transaction.amount
227+
TransactionType.INCOME -> (currentBalance - transaction.amount).max(BigDecimal.ZERO)
228+
else -> currentBalance
229+
}
230+
}
231+
else -> {
232+
when (transaction.transactionType) {
233+
TransactionType.EXPENSE, TransactionType.INVESTMENT -> (currentBalance - transaction.amount).max(BigDecimal.ZERO)
234+
TransactionType.INCOME -> currentBalance + transaction.amount
235+
else -> currentBalance
236+
}
237+
}
238+
}
239+
240+
val balanceEntity = AccountBalanceEntity(
241+
bankName = bankName,
242+
accountLast4 = accountLast4,
243+
balance = newBalance,
244+
timestamp = LocalDateTime.now(),
245+
transactionId = null,
246+
creditLimit = latestBalance.creditLimit,
247+
isCreditCard = isCreditCard,
248+
iconResId = latestBalance.iconResId,
249+
iconName = latestBalance.iconName,
250+
isWallet = latestBalance.isWallet,
251+
color = latestBalance.color,
252+
currency = transaction.currency,
253+
sourceType = "UNDO_REVERSAL"
254+
)
255+
accountBalanceRepository.insertBalance(balanceEntity)
256+
}
257+
164258
suspend fun updateCategoryForMerchant(merchantName: String, newCategory: String) {
165259
transactionDao.updateCategoryForMerchant(merchantName, newCategory)
166260
}

‎app/src/main/java/com/ritesh/cashiro/receiver/NotificationActionReceiver.kt‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import android.content.Context
66
import android.content.Intent
77
import android.util.Log
88
import com.ritesh.cashiro.data.database.CashiroDatabase
9+
import com.ritesh.cashiro.data.database.entity.AccountBalanceEntity
10+
import com.ritesh.cashiro.data.database.entity.TransactionType
11+
import java.math.BigDecimal
12+
import java.time.LocalDateTime
913
import kotlinx.coroutines.CoroutineScope
1014
import kotlinx.coroutines.Dispatchers
1115
import kotlinx.coroutines.SupervisorJob
@@ -66,10 +70,65 @@ class NotificationActionReceiver : BroadcastReceiver() {
6670
val database = CashiroDatabase.getInstance(context)
6771
val transactionDao = database.transactionDao()
6872

73+
// Fetch transaction before deleting to get account info for balance reversal
74+
val transaction = transactionDao.getTransactionById(transactionId)
75+
if (transaction == null) {
76+
Log.e(TAG, "Transaction not found: $transactionId")
77+
dismissNotification(context, notificationId)
78+
return@launch
79+
}
80+
6981
// Soft delete the transaction
7082
transactionDao.softDeleteTransaction(transactionId)
7183
Log.d(TAG, "Deleted transaction: $transactionId")
7284

85+
// Reverse account balance if transaction has account info
86+
val bankName = transaction.bankName
87+
val accountLast4 = transaction.accountNumber
88+
if (bankName != null && accountLast4 != null) {
89+
val balanceDao = database.accountBalanceDao()
90+
val latestBalance = balanceDao.getLatestBalance(bankName, accountLast4)
91+
if (latestBalance != null) {
92+
val currentBalance = latestBalance.balance
93+
val isCreditCard = latestBalance.isCreditCard
94+
95+
val reversedBalance = when {
96+
isCreditCard -> {
97+
when (transaction.transactionType) {
98+
TransactionType.EXPENSE, TransactionType.INVESTMENT -> currentBalance - transaction.amount
99+
TransactionType.INCOME -> currentBalance + transaction.amount
100+
else -> currentBalance
101+
}
102+
}
103+
else -> {
104+
when (transaction.transactionType) {
105+
TransactionType.EXPENSE, TransactionType.INVESTMENT -> currentBalance + transaction.amount
106+
TransactionType.INCOME -> currentBalance - transaction.amount
107+
else -> currentBalance
108+
}
109+
}
110+
}.max(BigDecimal.ZERO)
111+
112+
val balanceEntity = AccountBalanceEntity(
113+
bankName = bankName,
114+
accountLast4 = accountLast4,
115+
balance = reversedBalance,
116+
timestamp = LocalDateTime.now(),
117+
transactionId = null,
118+
creditLimit = latestBalance.creditLimit,
119+
isCreditCard = isCreditCard,
120+
iconResId = latestBalance.iconResId,
121+
iconName = latestBalance.iconName,
122+
isWallet = latestBalance.isWallet,
123+
color = latestBalance.color,
124+
currency = transaction.currency,
125+
sourceType = "DELETE_REVERSAL"
126+
)
127+
balanceDao.insertBalance(balanceEntity)
128+
Log.d(TAG, "Reversed balance for deleted transaction: $transactionId")
129+
}
130+
}
131+
73132
// Dismiss the notification
74133
dismissNotification(context, notificationId)
75134
} catch (e: Exception) {

0 commit comments

Comments
 (0)