From 0a8e56f80033ce889b38b01f2287cd6fbbe36dd6 Mon Sep 17 00:00:00 2001 From: Sebastien Castiel Date: Mon, 8 Jan 2024 18:11:11 +0100 Subject: Add splitmode and shares to expenses (#11) * Add splitmode and shares to expenses * Update balances based on shares * Change field size * Form validation * Redesign expense form * Split unevenly by amount --- src/lib/balances.ts | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) (limited to 'src/lib/balances.ts') diff --git a/src/lib/balances.ts b/src/lib/balances.ts index 77f4622..0bcd915 100644 --- a/src/lib/balances.ts +++ b/src/lib/balances.ts @@ -1,5 +1,6 @@ import { getGroupExpenses } from '@/lib/api' import { Participant } from '@prisma/client' +import { match } from 'ts-pattern' export type Balances = Record< Participant['id'], @@ -19,34 +20,42 @@ export function getBalances( for (const expense of expenses) { const paidBy = expense.paidById - const paidFors = expense.paidFor.map((p) => p.participantId) + const paidFors = expense.paidFor if (!balances[paidBy]) balances[paidBy] = { paid: 0, paidFor: 0, total: 0 } balances[paidBy].paid += expense.amount balances[paidBy].total += expense.amount + + const totalPaidForShares = paidFors.reduce( + (sum, paidFor) => sum + paidFor.shares, + 0, + ) + let remaining = expense.amount paidFors.forEach((paidFor, index) => { - if (!balances[paidFor]) - balances[paidFor] = { paid: 0, paidFor: 0, total: 0 } + if (!balances[paidFor.participantId]) + balances[paidFor.participantId] = { paid: 0, paidFor: 0, total: 0 } + + const isLast = index === paidFors.length - 1 + + const [shares, totalShares] = match(expense.splitMode) + .with('EVENLY', () => [1, paidFors.length]) + .with('BY_SHARES', () => [paidFor.shares, totalPaidForShares]) + .with('BY_PERCENTAGE', () => [paidFor.shares, totalPaidForShares]) + .with('BY_AMOUNT', () => [paidFor.shares, totalPaidForShares]) + .exhaustive() - const dividedAmount = divide( - expense.amount, - paidFors.length, - index === paidFors.length - 1, - ) - balances[paidFor].paidFor += dividedAmount - balances[paidFor].total -= dividedAmount + const dividedAmount = isLast + ? remaining + : Math.floor((expense.amount * shares) / totalShares) + remaining -= dividedAmount + balances[paidFor.participantId].paidFor += dividedAmount + balances[paidFor.participantId].total -= dividedAmount }) } return balances } -function divide(total: number, count: number, isLast: boolean): number { - if (!isLast) return Math.floor(total / count) - - return total - divide(total, count, false) * (count - 1) -} - export function getSuggestedReimbursements( balances: Balances, ): Reimbursement[] { -- cgit v1.3