Skip to content

Commit

Permalink
feat: robust implementation with validation of split bill function cu…
Browse files Browse the repository at this point in the history
…stom and equal
  • Loading branch information
abhiraj-ku committed Nov 11, 2024
1 parent 4799dcb commit 3d605ed
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 9 deletions.
65 changes: 62 additions & 3 deletions src/controllers/splitLogicController.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const validateBillSplit = require('../helpers/validateBillsplitInputs');

module.exports.splitBill = async (req, res) => {
// Step 1. Get the bare split equal done
const {
description,
amount,
Expand All @@ -11,7 +10,8 @@ module.exports.splitBill = async (req, res) => {
customAmounts,
isPaidByIncluded,
} = req.body;
if (!description || !amount || !paidBy || !splitType || !selectedMemeber) {

if (!description || !amount || !paidBy || !splitType || !selectedMembers) {
return res.status(400).json({ message: 'All fields are required' });
}

Expand Down Expand Up @@ -81,11 +81,70 @@ const splitEqual = (amount, paidBy, selectedMembers, isPaidByIncluded) => {
// participating members can have different payments
// amount to be paid.

const custom = (amount, paidBy, selectedMembers, customAmounts, isPaidByIncluded) => {
const splitCustom = (amount, paidBy, selectedMembers, customAmounts, isPaidByIncluded) => {
let membersIndex = [...selectedMembers];

// If paidBy should be included and isn't already in the array
if (isPaidByIncluded && !membersIndex.includes(paidBy)) {
membersIndex.push(paidBy);
}

try {
// validate the amount with customAmounts so that they
// match with amount (should not exceed total amount or be less than total amount)

const totalCustomAmount = Object.values(customAmounts).reduce(
(sum, value) => sum + parseInt(value),
0
);

if (totalCustomAmount < amount) {
const amountShort = amount - totalCustomAmount;
throw new Error(
`Custom amounts do not fully match the total amount. Shortfall of ${amountShort} remains`
);
} else if (Math.abs(totalCustomAmount > amount)) {
throw new Error(
`The sum of custom amounts (${totalCustomAmount}) does not match the total amount (${amount}).`
);
}

// Get each members money they owe from customAmounts object
const payments = membersIndex.map((member) => {
const thismemeberOwes = customAmounts[member] ? parseFloat(customAmounts[member]) : 0;

// if member == paidBy & paidBy is also part of transaction reduce his spendings and
// that is his whole reimbursement he is supposed to Get
// example:
/*
Assuming amount = 100, paidBy = '1', customAmounts = { '1': 10,'2': 40, '3': 50 },
and isPaidByIncluded = true:
so paidBy total reimbursement = amount - thismemeberOwes (paidBy in this case)
100 -10 =90 he is supposed to get
*/
if (member === paidBy) {
return {
member,
owes: thismemeberOwes.toFixed(2),
description: `Paid Rs. ${amount} and will get ${amount - memberOwes} back`,
};
} else {
return {
member,
owes: thismemeberOwes.toFixed(2),
description: `Owes Rs. ${thismemeberOwes} to ${paidBy}`,
};
}
});
return {
splitType: 'custom',
totalAmount: amount,
paidBy,
selectedMembers: membersIndex,
payments,
};
} catch (error) {
throw new Error(`Failed to process custom bill split : ${error.message}`);
}
};
27 changes: 21 additions & 6 deletions src/helpers/validateBillsplitInputs.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const validateBarterPayInput = (data) => {
}

if (typeof data.amount !== 'number' || data.amount <= 0) {
errors.push('Amount must be a greater than 0');
errors.push('Amount must be a positive number greater than 0');
}

if (typeof data.paidBy !== 'string' || !data.paidBy.trim()) {
Expand All @@ -17,15 +17,30 @@ const validateBarterPayInput = (data) => {
typeof data.splitType !== 'string' ||
!['EQUAL', 'CUSTOM'].includes(data.splitType.toUpperCase())
) {
errors.push('Invalid splitType');
errors.push('Invalid splitType; must be either "EQUAL" or "CUSTOM"');
}

if (!Array.isArray(data.selectedMemeber) || data.selectedMemeber.length === 0) {
errors.push('selectedMemeber must be a non-empty array');
} else if (new Set(data.selectedMembers).size !== data.selectedMembers.length) {
errors.push('selectedMembers must contain unique values');
} else if (!data.selectedMembers.every((member) => typeof member === 'string' && member.trim())) {
errors.push('All selectedMembers entries must be non-empty strings');
}

return {
isValid: errors.length === 0,
errors,
};
// check customAmounts Objects
if (data.splitType.toUpperCase() === 'CUSTOM') {
if (typeof data.customAmounts !== 'object' || Array.isArray(data.customAmounts)) {
errors.push('customAmounts must be an object');
} else {
for (const [member, amount] of Object.entries(data.customAmounts)) {
if (!data.selectedMembers.includes(member)) {
errors.push(`customAmounts contains member (${member}) not in selectedMembers`);
}
if (typeof amount !== 'number' || amount < 0) {
errors.push(`Amount for ${member} in customAmounts must be a non-negative number`);
}
}
}
}
};

0 comments on commit 3d605ed

Please sign in to comment.