discount-calculator-js
Version:
A versatile utility for calculating various types of discounts
287 lines (249 loc) • 8.78 kB
JavaScript
/**
* Discount Calculator
* A versatile utility for calculating various types of discounts
*/
/**
* Calculate percentage discount
* @param {number} amount - The original amount
* @param {number} percentage - The discount percentage
* @returns {Object} Object containing discount amount and final price
*/
function calculatePercentageDiscount(amount, percentage) {
if (typeof amount !== 'number' || typeof percentage !== 'number') {
throw new Error('Amount and percentage must be numbers');
}
if (amount < 0) {
throw new Error('Amount must be a positive number');
}
if (percentage < 0 || percentage > 100) {
throw new Error('Percentage must be between 0 and 100');
}
const discountAmount = (amount * percentage) / 100;
const finalPrice = amount - discountAmount;
return {
originalAmount: amount,
discountType: 'percentage',
discountValue: percentage,
discountAmount: parseFloat(discountAmount.toFixed(2)),
finalPrice: parseFloat(finalPrice.toFixed(2)),
savings: parseFloat(discountAmount.toFixed(2)),
savingsPercentage: parseFloat(percentage.toFixed(2))
};
}
/**
* Calculate fixed amount discount
* @param {number} amount - The original amount
* @param {number} discountAmount - The fixed discount amount
* @returns {Object} Object containing discount amount and final price
*/
function calculateFixedDiscount(amount, discountAmount) {
if (typeof amount !== 'number' || typeof discountAmount !== 'number') {
throw new Error('Amount and discount amount must be numbers');
}
if (amount < 0) {
throw new Error('Amount must be a positive number');
}
if (discountAmount < 0) {
throw new Error('Discount amount must be a positive number');
}
if (discountAmount > amount) {
throw new Error('Discount amount cannot be greater than the original amount');
}
const finalPrice = amount - discountAmount;
const savingsPercentage = (discountAmount / amount) * 100;
return {
originalAmount: amount,
discountType: 'fixed',
discountValue: discountAmount,
discountAmount: parseFloat(discountAmount.toFixed(2)),
finalPrice: parseFloat(finalPrice.toFixed(2)),
savings: parseFloat(discountAmount.toFixed(2)),
savingsPercentage: parseFloat(savingsPercentage.toFixed(2))
};
}
/**
* Calculate buy X get Y discount (e.g., buy one get one free)
* @param {number} amount - The original amount per item
* @param {number} quantity - Total number of items
* @param {number} buyQuantity - Number of items to buy
* @param {number} getQuantity - Number of items free
* @returns {Object} Object containing discount amount and final price
*/
function calculateBuyXGetYDiscount(amount, quantity, buyQuantity, getQuantity) {
if (typeof amount !== 'number' || typeof quantity !== 'number' ||
typeof buyQuantity !== 'number' || typeof getQuantity !== 'number') {
throw new Error('All parameters must be numbers');
}
if (amount < 0 || quantity <= 0 || buyQuantity <= 0 || getQuantity <= 0) {
throw new Error('All parameters must be positive numbers');
}
const totalSets = Math.floor(quantity / (buyQuantity + getQuantity));
const remainingItems = quantity % (buyQuantity + getQuantity);
let itemsToPay = (totalSets * buyQuantity) + Math.min(remainingItems, buyQuantity);
let itemsFree = (totalSets * getQuantity) + Math.max(0, remainingItems - buyQuantity);
const originalTotal = amount * quantity;
const finalPrice = amount * itemsToPay;
const discountAmount = originalTotal - finalPrice;
const savingsPercentage = (discountAmount / originalTotal) * 100;
return {
originalAmount: originalTotal,
discountType: 'buyXgetY',
discountValue: `Buy ${buyQuantity} Get ${getQuantity}`,
itemsPaid: itemsToPay,
itemsFree: itemsFree,
discountAmount: parseFloat(discountAmount.toFixed(2)),
finalPrice: parseFloat(finalPrice.toFixed(2)),
savings: parseFloat(discountAmount.toFixed(2)),
savingsPercentage: parseFloat(savingsPercentage.toFixed(2))
};
}
/**
* Calculate tiered discount based on amount thresholds
* @param {number} amount - The original amount
* @param {Array} tiers - Array of tier objects [{threshold: number, discount: number, type: 'percentage'|'fixed'}]
* @returns {Object} Object containing discount amount and final price
*/
function calculateTieredDiscount(amount, tiers) {
if (typeof amount !== 'number') {
throw new Error('Amount must be a number');
}
if (!Array.isArray(tiers)) {
throw new Error('Tiers must be an array');
}
if (amount < 0) {
throw new Error('Amount must be a positive number');
}
// Sort tiers by threshold in descending order
const sortedTiers = [...tiers].sort((a, b) => b.threshold - a.threshold);
// Find the applicable tier
const applicableTier = sortedTiers.find(tier => amount >= tier.threshold);
if (!applicableTier) {
return {
originalAmount: amount,
discountType: 'none',
discountValue: 0,
discountAmount: 0,
finalPrice: amount,
savings: 0,
savingsPercentage: 0
};
}
if (applicableTier.type === 'percentage') {
return calculatePercentageDiscount(amount, applicableTier.discount);
} else if (applicableTier.type === 'fixed') {
return calculateFixedDiscount(amount, applicableTier.discount);
} else {
throw new Error('Invalid tier discount type. Must be "percentage" or "fixed"');
}
}
/**
* Format the result as a string
* @param {Object} result - The calculation result
* @returns {string} Formatted string with the calculation result
*/
function formatResult(result) {
if (!result) {
return 'Invalid calculation result';
}
let output = `
Original Amount: $${result.originalAmount.toFixed(2)}
Discount Type: ${result.discountType}
`;
if (result.discountType === 'percentage') {
output += `Discount: ${result.discountValue}%
`;
} else if (result.discountType === 'fixed') {
output += `Discount: $${result.discountValue.toFixed(2)}
`;
} else if (result.discountType === 'buyXgetY') {
output += `Discount: ${result.discountValue}
Items Paid: ${result.itemsPaid}
Items Free: ${result.itemsFree}
`;
} else if (result.discountType === 'tiered') {
output += `Discount: Tiered discount applied
`;
}
output += `
Discount Amount: $${result.discountAmount.toFixed(2)}
Final Price: $${result.finalPrice.toFixed(2)}
You Save: $${result.savings.toFixed(2)} (${result.savingsPercentage.toFixed(2)}%)
`;
return output;
}
/**
* Calculate discount based on the specified type
* @param {number} amount - The original amount
* @param {Object} options - Options for the calculation
* @returns {Object} The calculation result
*/
function calculateDiscount(amount, options = {}) {
const { type = 'percentage', value, formatOutput = false, ...additionalParams } = options;
if (typeof amount !== 'number') {
throw new Error('Amount must be a number');
}
let result;
switch (type.toLowerCase()) {
case 'percentage':
result = calculatePercentageDiscount(amount, value);
break;
case 'fixed':
result = calculateFixedDiscount(amount, value);
break;
case 'buyxgety':
const { quantity, buyQuantity, getQuantity } = additionalParams;
result = calculateBuyXGetYDiscount(amount, quantity, buyQuantity, getQuantity);
break;
case 'tiered':
const { tiers } = additionalParams;
result = calculateTieredDiscount(amount, tiers);
break;
default:
throw new Error('Invalid discount type. Must be one of: percentage, fixed, buyXgetY, tiered');
}
if (formatOutput) {
return {
...result,
formattedOutput: formatResult(result)
};
}
return result;
}
/**
* Get available discount calculation types
* @returns {Array} Array of available discount types
*/
function getAvailableDiscountTypes() {
return [
{
id: 'percentage',
name: 'Percentage Discount',
description: 'Calculate discount based on a percentage of the original price'
},
{
id: 'fixed',
name: 'Fixed Amount Discount',
description: 'Apply a fixed amount discount to the original price'
},
{
id: 'buyxgety',
name: 'Buy X Get Y Free',
description: 'Calculate discount for "buy X, get Y free" promotions'
},
{
id: 'tiered',
name: 'Tiered Discount',
description: 'Apply different discount rates based on purchase amount thresholds'
}
];
}
// Export functions
module.exports = {
calculateDiscount,
calculatePercentageDiscount,
calculateFixedDiscount,
calculateBuyXGetYDiscount,
calculateTieredDiscount,
formatResult,
getAvailableDiscountTypes
};