UNPKG

discount-calculator-js

Version:

A versatile utility for calculating various types of discounts

287 lines (249 loc) 8.78 kB
/** * 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 };