Skip to content

Commit

Permalink
Managed transaction with expense in group home simply
Browse files Browse the repository at this point in the history
  • Loading branch information
cp-nirali-s committed Jun 21, 2024
1 parent 84d5324 commit 7a15d6a
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ public class UserProfileViewModel: BaseViewModel, ObservableObject {
guard let user = preference.user else { return }

var newUser = user
newUser.firstName = firstName.capitalized
newUser.lastName = lastName.capitalized
newUser.emailId = email
newUser.firstName = firstName.trimming(spaces: .leadingAndTrailing).capitalized
newUser.lastName = lastName.trimming(spaces: .leadingAndTrailing).capitalized
newUser.emailId = email.trimming(spaces: .leadingAndTrailing)
newUser.phoneNumber = phoneNumber

let resizedImage = profileImage?.aspectFittedToHeight(200)
Expand Down
4 changes: 2 additions & 2 deletions Splito/UI/Home/Expense/AddExpenseViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -209,15 +209,15 @@ extension AddExpenseViewModel {

if let expense {
var newExpense = expense
newExpense.name = expenseName.capitalized
newExpense.name = expenseName.trimming(spaces: .leadingAndTrailing).capitalized
newExpense.amount = expenseAmount
newExpense.date = Timestamp(date: expenseDate)
newExpense.paidBy = selectedPayer.id
newExpense.splitTo = selectedMembers

updateExpense(expense: newExpense)
} else {
let expense = Expense(name: expenseName.capitalized, amount: expenseAmount,
let expense = Expense(name: expenseName.trimming(spaces: .leadingAndTrailing).capitalized, amount: expenseAmount,
date: Timestamp(date: expenseDate), paidBy: selectedPayer.id,
addedBy: user.id, splitTo: selectedMembers, groupId: groupId)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class CreateGroupViewModel: BaseViewModel, ObservableObject {
private func createGroup() {
currentState = .loading
let userId = preference.user?.id ?? ""
let group = Groups(name: groupName.capitalized, createdBy: userId, members: [userId], imageUrl: nil, createdAt: Timestamp())
let group = Groups(name: groupName.trimming(spaces: .leadingAndTrailing).capitalized, createdBy: userId, members: [userId], imageUrl: nil, createdAt: Timestamp())

let resizedImage = profileImage?.aspectFittedToHeight(200)
let imageData = resizedImage?.jpegData(compressionQuality: 0.2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ class GroupSettleUpViewModel: BaseViewModel, ObservableObject {
}
} receiveValue: { [weak self] transactions in
guard let self else { return }
print("transactions data: \(transactions)")
self.transactions = transactions
}.store(in: &cancelable)
}
Expand All @@ -86,7 +85,6 @@ class GroupSettleUpViewModel: BaseViewModel, ObservableObject {
} receiveValue: { [weak self] expenses in
guard let self, let group else { return }
self.expenses = expenses
print("expenses data: \(expenses)")
if group.isDebtSimplified {
self.calculateExpensesSimply()
} else {
Expand Down
88 changes: 51 additions & 37 deletions Splito/UI/Home/Groups/Group/GroupHomeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class GroupHomeViewModel: BaseViewModel, ObservableObject {
@Inject private var preference: SplitoPreference
@Inject private var groupRepository: GroupRepository
@Inject private var expenseRepository: ExpenseRepository
@Inject private var transactionRepository: TransactionRepository

@Published private var expenses: [Expense] = []
@Published var expensesWithUser: [ExpenseWithUser] = []
Expand Down Expand Up @@ -48,6 +49,7 @@ class GroupHomeViewModel: BaseViewModel, ObservableObject {
private let groupId: String
private let router: Router<AppRoute>
private var groupUserData: [AppUser] = []
private var transactions: [Transactions] = []
private let onGroupSelected: ((String?) -> Void)?

init(router: Router<AppRoute>, groupId: String, onGroupSelected: ((String?) -> Void)?) {
Expand Down Expand Up @@ -76,6 +78,7 @@ class GroupHomeViewModel: BaseViewModel, ObservableObject {
self.groupUserData.append(memberData)
}
}
self.fetchTransactions()
self.fetchExpenses()
}.store(in: &cancelable)
}
Expand All @@ -98,6 +101,19 @@ class GroupHomeViewModel: BaseViewModel, ObservableObject {
}.store(in: &cancelable)
}

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

private func fetchExpenses() {
expenseRepository.fetchExpensesBy(groupId: groupId)
.sink { [weak self] completion in
Expand All @@ -108,6 +124,7 @@ class GroupHomeViewModel: BaseViewModel, ObservableObject {
} receiveValue: { [weak self] expenses in
guard let self, let group else { return }
self.expenses = expenses
print("xxx expenses: \(expenses)")
if group.isDebtSimplified {
self.calculateExpensesSimply()
} else {
Expand Down Expand Up @@ -164,7 +181,7 @@ class GroupHomeViewModel: BaseViewModel, ObservableObject {
}

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

let queue = DispatchGroup()
var ownAmounts: [String: Double] = [:]
Expand All @@ -182,7 +199,6 @@ class GroupHomeViewModel: BaseViewModel, ObservableObject {
for member in expense.splitTo {
ownAmounts[member, default: 0.0] -= splitAmount
}

self.fetchUserData(for: expense.paidBy) { user in
combinedData.append(ExpenseWithUser(expense: expense, user: user))
queue.leave()
Expand All @@ -193,52 +209,50 @@ class GroupHomeViewModel: BaseViewModel, ObservableObject {
let debts = self.settleDebts(users: ownAmounts)

for debt in debts where debt.0 == userId || debt.1 == userId {
self.overallOwingAmount += debt.1 == userId ? debt.2 : -debt.2
self.memberOwingAmount[debt.1 == userId ? debt.0 : debt.1] = debt.1 == userId ? debt.2 : -debt.2
}
self.memberOwingAmount = self.memberOwingAmount.filter { $0.value != 0 }

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
}
}

self.memberOwingAmount = self.memberOwingAmount.filter { $0.value != 0 }
self.overallOwingAmount = self.memberOwingAmount.values.reduce(0, +)
self.expensesWithUser = combinedData
self.setGroupViewState()
}
}

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
}

private func fetchUserData(for userId: String, completion: @escaping (AppUser) -> Void) {
Expand Down

0 comments on commit 7a15d6a

Please sign in to comment.