// 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 = `
CUSTOMER INFO
${customer.name}
{{cutomer_address}}
GST NO. : ${customer.gstin || ''}
CONTACT NO : ${customer.phone} ${customer.email}
SL NO |
DESCRIPTION |
HSN CODE |
QTY |
UNIT PRICE |
DISCOUNT |
AMOUNT |
`;
items.forEach((item, idx) => {
html += `
${idx + 1} |
${item.description} |
${item.hsn} |
${item.qty} |
${item.price.toFixed(2)} |
${item.discount.toFixed(2)}% |
${item.amount.toFixed(2)} |
`;
});
// Add empty rows to fill the page
for (let i = items.length; i < 10; i++) {
html += ' | | | | | | |
';
}
html += `
`;
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 };
}