// helper: convert number to Indian-notation words (Crore, Lakh, Thousand, Hundred) function numberToWords(num) { const small = ['', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen']; const tens = ['', '', 'Twenty', 'Thirty', 'Forty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninety']; function twoDigit(n) { if (n < 20) return small[n]; return tens[Math.floor(n / 10)] + (n % 10 ? ' ' + small[n % 10] : ''); } let words = ''; const crore = Math.floor(num / 10000000); num %= 10000000; if (crore) words += twoDigit(crore) + ' Crore '; const lakh = Math.floor(num / 100000); num %= 100000; if (lakh) words += twoDigit(lakh) + ' Lakh '; const thousand = Math.floor(num / 1000); num %= 1000; if (thousand) words += twoDigit(thousand) + ' Thousand '; const hundred = Math.floor(num / 100); num %= 100; if (hundred) words += small[hundred] + ' Hundred '; if (num) words += (words ? 'and ' : '') + twoDigit(num) + ' '; return words.trim() || 'Zero'; } if (typeof document !== 'undefined') { document.addEventListener('DOMContentLoaded', function () { const addItemBtn = document.getElementById('add-item'); const itemsTableBody = document.querySelector('#items-table tbody'); const form = document.getElementById('quotation-form'); const output = document.getElementById('quotation-output'); function updateSerialNumbers() { itemsTableBody.querySelectorAll('tr').forEach((row, i) => { row.querySelector('.item-slno').textContent = i + 1; }); } function addItemRow() { const row = document.createElement('tr'); row.innerHTML = ` 0.00 `; itemsTableBody.appendChild(row); updateSerialNumbers(); const inputs = row.querySelectorAll('input'); inputs.forEach(input => input.addEventListener('input', updateItemAmount)); row.querySelector('.remove-item').addEventListener('click', () => { row.remove(); updateSerialNumbers(); }); } function updateItemAmount(e) { const row = e.target.closest('tr'); const qty = parseFloat(row.querySelector('.item-qty').value) || 0; const price = parseFloat(row.querySelector('.item-price').value) || 0; const discountRate = parseFloat(row.querySelector('.item-discount').value) || 0; const discountAmount = qty * price * discountRate / 100; const amount = qty * price - discountAmount; row.querySelector('.item-amount').textContent = amount.toFixed(2); } addItemBtn.addEventListener('click', addItemRow); form.addEventListener('submit', function (e) { e.preventDefault(); const data = new FormData(form); const company = { name: data.get('company-name'), address: data.get('company-address'), phone: data.get('company-phone'), email: data.get('company-email'), gstin: data.get('company-gstin') }; const customer = { name: data.get('customer-name'), address: data.get('customer-address'), phone: data.get('customer-phone'), email: data.get('customer-email'), gstin: data.get('customer-gstin') }; const quotationNumber = data.get('quotation-number'); const quotationDate = data.get('quotation-date'); const igstRate = parseFloat(data.get('igst-rate')) || 0; const freightCharges = parseFloat(data.get('freight-charges')) || 0; const bank = { name: data.get('bank-name'), account: data.get('bank-account'), ifsc: data.get('bank-ifsc'), branch: data.get('bank-branch') }; const items = []; itemsTableBody.querySelectorAll('tr').forEach(row => { items.push({ description: row.querySelector('.item-desc').value, hsn: row.querySelector('.item-hsn').value, qty: parseFloat(row.querySelector('.item-qty').value) || 0, price: parseFloat(row.querySelector('.item-price').value) || 0, discount: parseFloat(row.querySelector('.item-discount').value) || 0, amount: parseFloat(row.querySelector('.item-amount').textContent) || 0 }); }); const { subtotal, igstAmount, _total, rOff, finalTotal } = calculateQuotation(items, igstRate, freightCharges); // convert total to words (Rupees and Paise) const rupeePart = Math.floor(finalTotal); const paisePart = Math.round((finalTotal - rupeePart) * 100); const rupeeWords = numberToWords(rupeePart); const paiseWords = paisePart > 0 ? numberToWords(paisePart) : ''; let html = `
${company.name}
{{address}}
GST NO. : ${company.gstin || ''}
CONTACT NO : ${company.phone} ${company.email}

QUOTATION

QUO. NO ${quotationNumber}
DATE ${quotationDate}
CUSTOMER INFO
${customer.name}
{{cutomer_address}}
GST NO. : ${customer.gstin || ''}
CONTACT NO : ${customer.phone} ${customer.email}
`; items.forEach((item, idx) => { html += ` `; }); // Add empty rows to fill the page for (let i = items.length; i < 10; i++) { html += ''; } html += `
SL NO DESCRIPTION HSN CODE QTY UNIT PRICE DISCOUNT AMOUNT
${idx + 1} ${item.description} ${item.hsn} ${item.qty} ${item.price.toFixed(2)} ${item.discount.toFixed(2)}% ${item.amount.toFixed(2)}
 
`; html = html.replace('{{address}}', company.address.replace(/\n/g, '
')); html = html.replace('{{cutomer_address}}', company.address.replace(/\n/g, '
')); output.innerHTML = html; output.style.display = 'block'; document.getElementById('form-container').style.display = 'none'; addItemRow(); }); }); } function calculateQuotation(items, igstRate, freightCharges) { const subtotal = items.reduce((sum, i) => sum + i.amount, 0); const igstAmount = (subtotal * igstRate) / 100; const total = subtotal + igstAmount + freightCharges; const totalBeforeRoundOff = total; const finalTotal = Math.round(totalBeforeRoundOff); const rOff = totalBeforeRoundOff - finalTotal; return { subtotal, igstAmount, total, rOff, finalTotal }; } // Export for testing (Node.js) if (typeof module !== 'undefined' && module.exports) { module.exports = { numberToWords, calculateQuotation }; }