Add computed shares per expense to fix #127 (#269)

* Added computed expenses per balance to fix #127

* add missing import that got lost during merge

* if we are in percentage mode or amount mode, the shares have to be multiplied by 100
This commit is contained in:
Daniel Thiem
2025-04-19 21:16:37 +02:00
committed by GitHub
parent 9fec8f9eaa
commit 728e072376
2 changed files with 83 additions and 38 deletions

View File

@@ -23,48 +23,56 @@ export function getTotalActiveUserPaidFor(
)
}
type Expense = NonNullable<Awaited<ReturnType<typeof getGroupExpenses>>>[number]
export function calculateShare(
participantId: string | null,
expense: Pick<
Expense,
'amount' | 'paidFor' | 'splitMode' | 'isReimbursement'
>,
): number {
if (expense.isReimbursement) return 0
const paidFors = expense.paidFor
const userPaidFor = paidFors.find(
(paidFor) => paidFor.participant.id === participantId,
)
if (!userPaidFor) return 0
const shares = Number(userPaidFor.shares)
switch (expense.splitMode) {
case 'EVENLY':
// Divide the total expense evenly among all participants
return expense.amount / paidFors.length
case 'BY_AMOUNT':
// Directly add the user's share if the split mode is BY_AMOUNT
return shares
case 'BY_PERCENTAGE':
// Calculate the user's share based on their percentage of the total expense
return (expense.amount * shares) / 10000 // Assuming shares are out of 10000 for percentage
case 'BY_SHARES':
// Calculate the user's share based on their shares relative to the total shares
const totalShares = paidFors.reduce(
(sum, paidFor) => sum + Number(paidFor.shares),
0,
)
return (expense.amount * shares) / totalShares
default:
return 0
}
}
export function getTotalActiveUserShare(
activeUserId: string | null,
expenses: NonNullable<Awaited<ReturnType<typeof getGroupExpenses>>>,
): number {
let total = 0
expenses.forEach((expense) => {
if (expense.isReimbursement) return
const paidFors = expense.paidFor
const userPaidFor = paidFors.find(
(paidFor) => paidFor.participant.id === activeUserId,
)
if (!userPaidFor) {
// If the active user is not involved in the expense, skip it
return
}
switch (expense.splitMode) {
case 'EVENLY':
// Divide the total expense evenly among all participants
total += expense.amount / paidFors.length
break
case 'BY_AMOUNT':
// Directly add the user's share if the split mode is BY_AMOUNT
total += userPaidFor.shares
break
case 'BY_PERCENTAGE':
// Calculate the user's share based on their percentage of the total expense
total += (expense.amount * userPaidFor.shares) / 10000 // Assuming shares are out of 10000 for percentage
break
case 'BY_SHARES':
// Calculate the user's share based on their shares relative to the total shares
const totalShares = paidFors.reduce(
(sum, paidFor) => sum + paidFor.shares,
0,
)
total += (expense.amount * userPaidFor.shares) / totalShares
break
}
})
const total = expenses.reduce(
(sum, expense) => sum + calculateShare(activeUserId, expense),
0,
)
return parseFloat(total.toFixed(2))
}