UNPKG

softseti-sale-calculator-library

Version:

Sales calculation engine by Softseti

125 lines (104 loc) 4.14 kB
// src/helpers.js function findProductInSale(sale, productId) { return (sale.return_items || []).find(item => item.product_id == productId); } function calculatePreviousRefundsTotal(sale, options, currentMemoId) { let total = 0; // 1. Get all credit memos from options const creditMemos = []; // Handle array format if (Array.isArray(options.credit_memo)) { creditMemos.push(...options.credit_memo); } // Handle single object format else if (options.credit_memo && typeof options.credit_memo === 'object') { creditMemos.push(options.credit_memo); } // 2. Sum total_refund from all memos except current for (const memo of creditMemos) { if (currentMemoId && memo.id === currentMemoId) continue; total += parseFloat(memo.total_refund || 0); } return total; } function validateRefund(sale, returnItems, options, currentMemoId) { const previousRefundsTotal = calculatePreviousRefundsTotal(sale, options, currentMemoId); // Check if sale is fully refunded if (previousRefundsTotal >= sale.total) { return 'The sale has already been fully refunded'; } const maxRefundable = sale.total - previousRefundsTotal; // Validate based on input type if (options.input_type === 'amount_only') { if (options.requested_refund_amount === undefined) { return 'Requested refund amount is required for amount-only refunds'; } if (options.requested_refund_amount > maxRefundable) { return 'Requested amount exceeds maximum refundable amount'; } } else { // Precompute a map of productId -> total refunded quantity const refundedQuantityMap = {}; // Process all credit memos from options const creditMemos = []; if (Array.isArray(options.credit_memo)) { creditMemos.push(...options.credit_memo); } else if (options.credit_memo && typeof options.credit_memo === 'object') { creditMemos.push(options.credit_memo); } for (const memo of creditMemos) { // Process return items in memo let memoItems = []; if (Array.isArray(memo.return_items)) { memoItems = memo.return_items; } else if (memo.return_items && typeof memo.return_items === 'object') { memoItems = [memo.return_items]; } for (const item of memoItems) { if (item.product_id) { const productId = item.product_id; refundedQuantityMap[productId] = (refundedQuantityMap[productId] || 0) + (parseFloat(item.quantity) || 0); } } } // Validate each return item for (const returnItem of returnItems) { const saleItem = findProductInSale(sale, returnItem.product_id); if (!saleItem) { return `Product not found: ${returnItem.product_id}`; } // Calculate available quantity const totalRefunded = refundedQuantityMap[returnItem.product_id] || 0; const availableQuantity = (saleItem.sale_product_quantity || 0) - totalRefunded; // Handle different input types let requestedQuantity = 0; if (options.input_type === 'items_full_refund') { requestedQuantity = parseFloat(returnItem.quantity) || 0; } else if (options.input_type === 'items_with_custom_amount') { // Convert refund amount to equivalent quantity const unitPrice = saleItem.price > 0 ? saleItem.price : 1; requestedQuantity = returnItem.refund_amount ? parseFloat((returnItem.refund_amount / unitPrice).toFixed(2)) : 0; } if (requestedQuantity > availableQuantity) { return `Refund exceeds available quantity for product: ${saleItem.name || saleItem.product_id}. ` + `Available: ${availableQuantity}, Requested: ${requestedQuantity}, ` + `Previously refunded: ${totalRefunded}`; } } } return null; } function calculateMaxRefundableAmount(sale, options, currentMemoId) { return sale.total - calculatePreviousRefundsTotal(sale, options, currentMemoId); } module.exports = { findProductInSale, calculatePreviousRefundsTotal, validateRefund, calculateMaxRefundableAmount };