Skip to content

Commit

Permalink
Managed transaction with expense in settle up screen
Browse files Browse the repository at this point in the history
  • Loading branch information
cp-nirali-s committed Jun 21, 2024
1 parent e4804b5 commit a6705ad
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//

import SwiftUI
import Data
import BaseStyle

struct GroupBalancesView: View {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,20 @@ import SwiftUI

class GroupSettleUpViewModel: BaseViewModel, ObservableObject {

@Inject var preference: SplitoPreference
@Inject var groupRepository: GroupRepository
@Inject var expenseRepository: ExpenseRepository
@Inject private var preference: SplitoPreference
@Inject private var groupRepository: GroupRepository
@Inject private var expenseRepository: ExpenseRepository
@Inject private var transactionRepository: TransactionRepository

@Published var group: Groups?
@Published var members: [AppUser] = []
@Published var viewState: ViewState = .initial

@Published private var expenses: [Expense] = []
@Published var memberOwingAmount: [String: Double] = [:]
@Published private(set) var viewState: ViewState = .initial
@Published private(set) var memberOwingAmount: [String: Double] = [:]

private let groupId: String
private var group: Groups?
private var members: [AppUser] = []
private var expenses: [Expense] = []
private var groupMemberData: [AppUser] = []
private var transactions: [Transactions] = []
private let router: Router<AppRoute>?

init(router: Router<AppRoute>? = nil, groupId: String) {
Expand Down Expand Up @@ -59,10 +60,22 @@ class GroupSettleUpViewModel: BaseViewModel, ObservableObject {
guard let self else { return }
self.members = members
self.members.removeAll(where: { $0.id == user.id })
self.fetchTransactions()
self.fetchExpenses()
}.store(in: &cancelable)
}

func fetchTransactions() {
transactionRepository.fetchTransactionsBy(groupId: groupId).sink { [weak self] completion in
if case .failure(let error) = completion {
self?.handleServiceError(error)
}
} receiveValue: { [weak self] transactions in
guard let self else { return }
self.transactions = transactions
}.store(in: &cancelable)
}

private func fetchExpenses() {
expenseRepository.fetchExpensesBy(groupId: groupId)
.sink { [weak self] completion in
Expand All @@ -81,7 +94,10 @@ class GroupSettleUpViewModel: BaseViewModel, ObservableObject {
}

private func calculateExpenses() {
guard let userId = self.preference.user?.id else { return }
guard let userId = preference.user?.id else { return }

print("xxx expenses \(expenses)")
print("xxx transactions \(transactions)")

var owesToUser: [String: Double] = [:]
var owedByUser: [String: Double] = [:]
Expand Down Expand Up @@ -111,7 +127,7 @@ class GroupSettleUpViewModel: BaseViewModel, ObservableObject {
}

private func calculateExpensesSimply() {
guard let userId = self.preference.user?.id else { return }
guard let userId = preference.user?.id else { return }

var ownAmounts: [String: Double] = [:]

Expand All @@ -126,57 +142,58 @@ class GroupSettleUpViewModel: BaseViewModel, ObservableObject {
}
}

DispatchQueue.main.async {
let debts = self.settleDebts(users: ownAmounts)
for debt in debts where debt.0 == userId || debt.1 == userId {
self.memberOwingAmount[debt.1 == userId ? debt.0 : debt.1] = debt.1 == userId ? debt.2 : -debt.2
let debts = self.settleDebts(users: ownAmounts)
for debt in debts where debt.0 == userId || debt.1 == userId {
self.memberOwingAmount[debt.1 == userId ? debt.0 : debt.1] = debt.1 == userId ? debt.2 : -debt.2
}
self.memberOwingAmount = self.memberOwingAmount.filter { $0.value != 0 }

// Adjust memberOwingAmount based on transactions
for transaction in self.transactions {
let payer = transaction.payerId
let receiver = transaction.receiverId
let amount = transaction.amount

if payer == userId, let currentAmount = self.memberOwingAmount[receiver] {
self.memberOwingAmount[receiver] = currentAmount + amount
} else if receiver == userId, let currentAmount = self.memberOwingAmount[payer] {
self.memberOwingAmount[payer] = currentAmount - amount
}
}

// Remove zero or settled debts
self.memberOwingAmount = self.memberOwingAmount.filter { $0.value != 0 }
}

private func settleDebts(users: [String: Double]) -> [(String, String, Double)] {
var creditors: [(String, Double)] = []
var debtors: [(String, Double)] = []

// Separate users into creditors and debtors
for (user, balance) in users {
if balance > 0 {
creditors.append((user, balance))
} else if balance < 0 {
debtors.append((user, -balance)) // Store as positive for ease of calculation
var mutableUsers = users
var debts: [(String, String, Double)] = []
let positiveAmounts = mutableUsers.filter { $0.value > 0 }
let negativeAmounts = mutableUsers.filter { $0.value < 0 }

for (creditor, creditAmount) in positiveAmounts {
var remainingCredit = creditAmount

for (debtor, debtAmount) in negativeAmounts {
if remainingCredit == 0 { break }
let amountToSettle = min(remainingCredit, -debtAmount)
if amountToSettle > 0 {
debts.append((debtor, creditor, amountToSettle))
remainingCredit -= amountToSettle
mutableUsers[debtor]! += amountToSettle
mutableUsers[creditor]! -= amountToSettle
}
}
}

// Sort creditors and debtors by the amount they owe or are owed
creditors.sort { $0.1 < $1.1 }
debtors.sort { $0.1 < $1.1 }

var transactions: [(String, String, Double)] = [] // (debtor, creditor, amount)
var cIdx = 0
var dIdx = 0

while cIdx < creditors.count && dIdx < debtors.count { // Process all debts
let (creditor, credAmt) = creditors[cIdx]
let (debtor, debtAmt) = debtors[dIdx]
let minAmt = min(credAmt, debtAmt)

transactions.append((debtor, creditor, minAmt)) // Record the transaction

// Update the amounts
creditors[cIdx] = (creditor, credAmt - minAmt)
debtors[dIdx] = (debtor, debtAmt - minAmt)

// Move the index forward if someone's balance is settled
if creditors[cIdx].1 == 0 { cIdx += 1 }
if debtors[dIdx].1 == 0 { dIdx += 1 }
}
return transactions
return debts
}

func getMemberDataBy(id: String) -> AppUser? {
return members.first(where: { $0.id == id })
}

// MARK: - User Actions
func handleMoreButtonTap() {
router?.push(.GroupWhoIsPayingView(groupId: groupId))
}
Expand Down
2 changes: 1 addition & 1 deletion Splito/UI/Login/LoginViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class LoginViewModel: BaseViewModel, ObservableObject {

@Inject private var preference: SplitoPreference
@Inject private var userRepository: UserRepository

@Published private(set) var showGoogleLoading = false
@Published private(set) var showAppleLoading = false

Expand Down

0 comments on commit a6705ad

Please sign in to comment.