Home Free Tools GST Invoice Generator — Create and Print GST Tax Invoices Free
Utility Tool — GST Invoice Generator

GST Invoice Generator — Create and Print GST Tax Invoices Free

Generate GST-compliant tax invoices with all mandatory fields — GSTIN, HSN, CGST/SGST breakdown, and taxable value. Print or save as PDF instantly.

Generate & Download Invoice

Formula & How It Works

Taxable Value = Qty × Rate
CGST = Taxable Value × (GST Rate ÷ 2) ÷ 100
SGST = Taxable Value × (GST Rate ÷ 2) ÷ 100
Invoice Total = Taxable Value + CGST + SGST

Worked Example

Seller: ABC Traders | Buyer: XYZ Pvt Ltd | Product: Office Chair × 5 @ ₹8,000 | GST 18%

Taxable = ₹40,000 | CGST ₹3,600 | SGST ₹3,600 | Total = ₹47,200

When to Use This Calculator

  • Create quick invoices for one-off sales
  • Generate sample invoice format for new business
  • Create pro-forma invoice for customer approval
  • Print backup invoice when billing software is down
  • Use as reference for setting up ERP billing

Frequently Asked Questions

Supplier name, GSTIN, address; Recipient name, GSTIN (for B2B); Invoice number, date; HSN/SAC code; Taxable value; GST rate and amount (CGST+SGST or IGST); Total value.
For goods: on or before date of removal. For services: within 30 days of supply. For banking/insurance: within 45 days.
Tax invoice is for taxable supplies (you charge GST). Bill of supply is for exempt/nil-rated supplies or composition dealers (no GST charged).
Yes. Digital invoices are valid under GST as long as they contain all mandatory fields and are issued in a tamper-proof format.

Recent Invoices (Local)

No invoices yet. Generated invoices will appear here.
Take the next step

Need a complete ERP for your business?

Use VasyERP to automate billing, inventory, GST, distribution, manufacturing, and business growth in one intelligent platform.

Generate Another Invoice Explore More Free Tools Book Free ERP Demo
'); w.document.close(); w.focus(); setTimeout(function () { w.print(); }, 400); } function resetCalc() { ['igSellerName', 'igSellerGST', 'igSellerPAN', 'igSellerContact', 'igSellerAddr', 'igBuyerName', 'igBuyerGST', 'igBuyerMobile', 'igBuyerEmail', 'igBuyerAddr', 'igShipAddr', 'igInvNo', 'igDueDate', 'igPO', 'igEWay', 'igPayTerms', 'igUPI', 'igBank', 'igVehicle', 'igNotes', 'igTerms' ].forEach(function (id) { var el = document.getElementById(id); if (el) el.value = ''; }); document.getElementById('igItemsWrapper').innerHTML = ''; itemCount = 0; addItem(); document.getElementById('resultBox').classList.remove('show'); } // ── Global state ──────────────────────────────────────────────────────────── var CSYM = { INR: '\u20b9', USD: '$', AED: 'AED\u00a0', GBP: '\u00a3', EUR: '\u20ac', SAR: 'SAR\u00a0', ZAR: 'R\u00a0', AUD: 'A$' }; var gCurr = '\u20b9'; // current currency symbol — shared across ALL functions var gTmpl = 'classic'; // current template function setCurrency() { var sel = document.getElementById('igCurrency'); gCurr = sel ? (CSYM[sel.value] || '\u20b9') : '\u20b9'; // Update live row displays document.querySelectorAll('[id^="item-"]').forEach(function (el) { var id = el.id.split('-')[1]; calcRow(id); }); } function setTmpl(name, btn) { gTmpl = name; document.querySelectorAll('.tmpl-btn').forEach(function (b) { b.classList.remove('active'); }); if (btn) btn.classList.add('active'); } // ── Override calcRow to use gCurr ─────────────────────────────────────────── function calcRow(id) { var qty = parseFloat(document.getElementById('qty-' + id).value) || 0; var rate = parseFloat(document.getElementById('rate-' + id).value) || 0; var disc = parseFloat(document.getElementById('disc-' + id).value) || 0; var gstR = parseFloat(document.getElementById('gst-' + id).value) || 0; var incl = document.getElementById('incl-' + id).value; var gross = qty * rate; var dAmt = gross * disc / 100; var after = gross - dAmt; var taxable, taxAmt; if (incl === 'incl') { taxable = after / (1 + gstR / 100); taxAmt = after - taxable; } else { taxable = after; taxAmt = taxable * gstR / 100; } var total = taxable + taxAmt; document.getElementById('taxable-' + id).textContent = gCurr + fmt(taxable); document.getElementById('taxamt-' + id).textContent = gCurr + fmt(taxAmt); document.getElementById('discamt-' + id).textContent = gCurr + fmt(dAmt); document.getElementById('total-' + id).textContent = gCurr + fmt(total); updateGrandTotal(); } // ── Upload helpers ─────────────────────────────────────────────────────────── function prevImg(input, imgId, previewId, zcId) { var file = input.files[0]; if (!file) return; var r = new FileReader(); r.onload = function (e) { document.getElementById(imgId).src = e.target.result; document.getElementById(previewId).style.display = 'flex'; var zc = document.getElementById(zcId); if (zc) zc.style.display = 'none'; }; r.readAsDataURL(file); } function rmImg(imgId, previewId, zcId) { document.getElementById(imgId).src = ''; document.getElementById(previewId).style.display = 'none'; var zc = document.getElementById(zcId); if (zc) zc.style.display = 'flex'; } function uzDrag(e, zoneId) { e.preventDefault(); document.getElementById(zoneId).classList.add('dragover'); } function uzLeave(zoneId) { document.getElementById(zoneId).classList.remove('dragover'); } function uzDrop(e, imgId, previewId, zcId) { e.preventDefault(); var zoneId = (imgId === 'logoImg') ? 'logoZone' : 'sigZone'; document.getElementById(zoneId).classList.remove('dragover'); var file = e.dataTransfer.files[0]; if (!file || !file.type.startsWith('image/')) return; var r = new FileReader(); r.onload = function (ev) { document.getElementById(imgId).src = ev.target.result; document.getElementById(previewId).style.display = 'flex'; var zc = document.getElementById(zcId); if (zc) zc.style.display = 'none'; }; r.readAsDataURL(file); } // ── UPI QR preview ─────────────────────────────────────────────────────────── function previewUpiQr() { var upi = document.getElementById('igUPI').value.trim(); var wrap = document.getElementById('upiQRWrap'); var div = document.getElementById('upiQRDiv'); if (!upi) { wrap.classList.remove('show'); return; } wrap.classList.add('show'); div.innerHTML = ''; if (typeof QRCode !== 'undefined') { try { new QRCode(div, { text: 'upi://pay?pa=' + encodeURIComponent(upi) + '&pn=VasyERP', width: 80, height: 80, colorDark: '#303335', colorLight: '#ffffff', correctLevel: QRCode.CorrectLevel.M }); } catch (e) { } } } // ── Copy billing ───────────────────────────────────────────────────────────── function copyBilling() { document.getElementById('igShipAddr').value = document.getElementById('igBuyerAddr').value; } // ── Duplicate row ──────────────────────────────────────────────────────────── function dupRow(srcId) { addItem(); var newId = itemCount; ['name', 'sku', 'sdesc', 'hsn', 'qty', 'unit', 'rate', 'disc', 'gst', 'incl'].forEach(function (f) { var s = document.getElementById(f + '-' + srcId); var d = document.getElementById(f + '-' + newId); if (s && d) d.value = s.value; }); calcRow(newId); } // ── Amount in words ─────────────────────────────────────────────────────────── function numWords(n) { n = Math.round(n); if (n === 0) return 'Zero'; var a = ['', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten', 'Eleven', 'Twelve', 'Thirteen', 'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen']; var b = ['', '', 'Twenty', 'Thirty', 'Forty', 'Fifty', 'Sixty', 'Seventy', 'Eighty', 'Ninety']; function w(x) { if (x < 20) return a[x]; if (x < 100) return b[Math.floor(x / 10)] + (x % 10 ? ' ' + a[x % 10] : ''); if (x < 1000) return a[Math.floor(x / 100)] + ' Hundred' + (x % 100 ? ' ' + w(x % 100) : ''); if (x < 100000) return w(Math.floor(x / 1000)) + ' Thousand' + (x % 1000 ? ' ' + w(x % 1000) : ''); if (x < 10000000) return w(Math.floor(x / 100000)) + ' Lakh' + (x % 100000 ? ' ' + w(x % 100000) : ''); return w(Math.floor(x / 10000000)) + ' Crore' + (x % 10000000 ? ' ' + w(x % 10000000) : ''); } return w(n) + ' Only'; } // ── Invoice History ─────────────────────────────────────────────────────────── function saveHist(invNo, customer, total, data) { var h = JSON.parse(localStorage.getItem('vasy_inv') || '[]'); h.unshift({ id: Date.now(), invNo: invNo, customer: customer, total: total, date: new Date().toLocaleDateString('en-IN'), curr: gCurr, data: data }); if (h.length > 20) h = h.slice(0, 20); localStorage.setItem('vasy_inv', JSON.stringify(h)); renderHist(); } function renderHist() { var h = JSON.parse(localStorage.getItem('vasy_inv') || '[]'); var body = document.getElementById('histBody'); if (!body) return; if (!h.length) { body.innerHTML = '
No invoices yet.
'; return; } body.innerHTML = h.map(function (e) { var cs = e.curr || '\u20b9'; return '
' + '
' + e.customer + '
' + '
' + e.invNo + ' · ' + e.date + '
' + '
' + cs + parseFloat(e.total).toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + '
' + '
' + '' + '' + '' + '
'; }).join(''); } function clearHist() { if (!confirm('Clear all saved invoices?')) return; localStorage.removeItem('vasy_inv'); renderHist(); } function delHist(id) { var h = JSON.parse(localStorage.getItem('vasy_inv') || '[]'); localStorage.setItem('vasy_inv', JSON.stringify(h.filter(function (e) { return e.id !== id; }))); renderHist(); } function reopenHist(id) { var h = JSON.parse(localStorage.getItem('vasy_inv') || '[]'); var entry = h.find(function (e) { return e.id === id; }); if (!entry || !entry.data) return; loadForm(entry.data); window.scrollTo({ top: document.getElementById('calculator').offsetTop - 80, behavior: 'smooth' }); } function dupHist(id) { reopenHist(id); setTimeout(function () { var el = document.getElementById('igInvNo'); if (el && el.value) { var p = el.value.split('/'); p[p.length - 1] = String((parseInt(p[p.length - 1]) || 0) + 1).padStart(4, '0'); el.value = p.join('/'); } }, 120); } function captureForm() { var data = {}; ['igSellerName', 'igSellerGST', 'igSellerPAN', 'igSellerContact', 'igSellerAddr', 'igBuyerName', 'igBuyerGST', 'igBuyerMobile', 'igBuyerEmail', 'igBuyerAddr', 'igShipAddr', 'igInvNo', 'igDate', 'igDueDate', 'igPO', 'igEWay', 'igGSTType', 'igRC', 'igPS', 'igPayTerms', 'igUPI', 'igBank', 'igVehicle', 'igNotes', 'igTerms', 'igCurrency'].forEach(function (id) { var el = document.getElementById(id); if (el) data[id] = el.value; }); data.items = []; document.querySelectorAll('[id^="item-"]').forEach(function (el) { var id = el.id.split('-')[1]; var item = {}; ['name', 'sku', 'sdesc', 'hsn', 'qty', 'unit', 'rate', 'disc', 'gst', 'incl'].forEach(function (f) { var fe = document.getElementById(f + '-' + id); if (fe) item[f] = fe.value; }); data.items.push(item); }); return data; } function loadForm(data) { resetCalc(); ['igSellerName', 'igSellerGST', 'igSellerPAN', 'igSellerContact', 'igSellerAddr', 'igBuyerName', 'igBuyerGST', 'igBuyerMobile', 'igBuyerEmail', 'igBuyerAddr', 'igShipAddr', 'igInvNo', 'igDate', 'igDueDate', 'igPO', 'igEWay', 'igGSTType', 'igRC', 'igPS', 'igPayTerms', 'igUPI', 'igBank', 'igVehicle', 'igNotes', 'igTerms', 'igCurrency'].forEach(function (id) { var el = document.getElementById(id); if (el && data[id] !== undefined) el.value = data[id]; }); if (data.igCurrency) setCurrency(); document.getElementById('igItemsWrapper').innerHTML = ''; itemCount = 0; if (data.items && data.items.length) { data.items.forEach(function (item) { addItem(); var id = itemCount; ['name', 'sku', 'sdesc', 'hsn', 'qty', 'unit', 'rate', 'disc', 'gst', 'incl'].forEach(function (f) { var fe = document.getElementById(f + '-' + id); if (fe && item[f] !== undefined) fe.value = item[f]; }); calcRow(id); }); } else { addItem(); } } // ── Share ───────────────────────────────────────────────────────────────────── // Build the print-ready HTML string (same CSS used in openPrintWindow) function buildInvoiceDocStr() { var previewEl = document.getElementById('igPreview'); if (!previewEl || !previewEl.innerHTML.trim()) return null; var content = previewEl.innerHTML; return ( '' + 'Invoice - VasyERP' + '' + content + ' ' ); } function shaWA() { var previewEl = document.getElementById('igPreview'); if (!previewEl || !previewEl.innerHTML.trim()) { toast('Please generate an invoice first'); return; } var seller = document.getElementById('igSellerName').value || 'VasyERP'; var buyer = document.getElementById('igBuyerName').value || ''; var invNo = document.getElementById('igInvNo').value || ''; var buyerMobile = document.getElementById('igBuyerMobile').value || ''; var waMsg = (buyer ? 'Dear ' + buyer + ',' : 'Hi,') + '\n\nPlease find your invoice' + (invNo ? ' #' + invNo : '') + ' from ' + seller + ' attached.' + '\n\nGenerated using VasyERP Free Invoice Generator.' + '\nhttps://vasyerp.com'; // ── Try Web Share API with PDF file (works on Android & iOS Chrome/Safari) ── if (navigator.share && navigator.canShare) { var docStr = buildInvoiceDocStr(); if (docStr) { // Convert HTML to a Blob, then use html2canvas isn't available — // so we share as an HTML file that renders as invoice // Best available: share the HTML as a file, which WhatsApp shows as attachment var blob = new Blob([docStr], { type: 'text/html;charset=utf-8' }); var file = new File([blob], 'Invoice-' + (invNo || 'VasyERP') + '.html', { type: 'text/html' }); // Check if sharing files is supported if (navigator.canShare({ files: [file] })) { navigator.share({ title: 'Invoice ' + (invNo ? '#' + invNo + ' ' : '') + 'from ' + seller, text: waMsg, files: [file] }).then(function () { toast('Invoice shared successfully'); }).catch(function (err) { if (err.name !== 'AbortError') { // User didn't cancel — fallback to text share shaWATextFallback(waMsg, buyerMobile); } }); return; } } // Web Share API available but no file support — share text + link navigator.share({ title: 'Invoice from ' + seller, text: waMsg }).then(function () { toast('Shared successfully'); }).catch(function (err) { if (err.name !== 'AbortError') shaWATextFallback(waMsg, buyerMobile); }); return; } // ── Desktop fallback: download PDF + open WhatsApp with message ── shaWADesktopFallback(waMsg, buyerMobile, seller, invNo); } function shaWATextFallback(msg, mobile) { // Open WhatsApp with pre-filled text (no attachment) var url = mobile ? 'https://wa.me/91' + mobile.replace(/\D/g, '') + '?text=' + encodeURIComponent(msg) : 'https://wa.me/?text=' + encodeURIComponent(msg); window.open(url, '_blank'); } function shaWADesktopFallback(msg, mobile, seller, invNo) { // Step 1: auto-download the invoice as HTML file var docStr = buildInvoiceDocStr(); if (docStr) { try { var blob = new Blob([docStr], { type: 'text/html;charset=utf-8' }); var a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = 'Invoice-' + (invNo || seller || 'VasyERP') + '.html'; document.body.appendChild(a); a.click(); document.body.removeChild(a); setTimeout(function () { URL.revokeObjectURL(a.href); }, 5000); } catch (e) { } } // Step 2: open WhatsApp with the text message // User can then manually attach the downloaded file var url = mobile ? 'https://wa.me/91' + mobile.replace(/\D/g, '') + '?text=' + encodeURIComponent(msg) : 'https://wa.me/?text=' + encodeURIComponent(msg); toast('Invoice downloaded — attach it in WhatsApp'); setTimeout(function () { window.open(url, '_blank'); }, 800); } function shaEmail() { var seller = document.getElementById('igSellerName').value || 'VasyERP'; var buyer = document.getElementById('igBuyerName').value || 'Customer'; var invNo = document.getElementById('igInvNo').value || ''; var email = document.getElementById('igBuyerEmail').value || ''; var subject = 'Invoice' + (invNo ? ' ' + invNo : '') + ' from ' + seller; var body = 'Dear ' + buyer + ',\n\nPlease find your invoice' + (invNo ? ' #' + invNo : '') + ' enclosed.\n\nGenerated using VasyERP Free Invoice Generator.\nhttps://vasyerp.com\n\nRegards,\n' + seller; window.location.href = 'mailto:' + email + '?subject=' + encodeURIComponent(subject) + '&body=' + encodeURIComponent(body); } function shaLink() { var url = window.location.href.split('?')[0] + '?utm_source=share&utm_medium=tool_share&utm_campaign=gst-invoice-generator'; navigator.clipboard.writeText(url).then(function () { toast('Link copied!'); }).catch(function () { toast('URL: ' + url); }); } function dlPDF() { printInvoice(); toast('Print dialog open \u2014 select Save as PDF'); } function toast(msg) { var t = document.getElementById('p27toast'); if (!t) return; t.textContent = msg; t.classList.add('show'); setTimeout(function () { t.classList.remove('show'); }, 2600); } // ── MAIN CALCULATE ──────────────────────────────────────────────────────────── function calculate() { setCurrency(); // ensure gCurr is fresh var items = getItems(); if (!items.length) { alert('Add at least one line item.'); return; } var seller = document.getElementById('igSellerName').value || 'Your Business'; var sellerG = document.getElementById('igSellerGST').value || ''; var sellerP = document.getElementById('igSellerPAN').value || ''; var sellerC = document.getElementById('igSellerContact').value || ''; var sellerA = document.getElementById('igSellerAddr').value || ''; var buyer = document.getElementById('igBuyerName').value || 'Customer'; var buyerG = document.getElementById('igBuyerGST').value || ''; var buyerM = document.getElementById('igBuyerMobile').value || ''; var buyerE = document.getElementById('igBuyerEmail').value || ''; var buyerA = document.getElementById('igBuyerAddr').value || ''; var shipA = document.getElementById('igShipAddr').value || buyerA; var invNo = document.getElementById('igInvNo').value || 'INV/25-26/0001'; var date = document.getElementById('igDate').value || new Date().toISOString().split('T')[0]; var dueD = document.getElementById('igDueDate').value || ''; var po = document.getElementById('igPO').value || ''; var eway = document.getElementById('igEWay').value || ''; var gstType = document.getElementById('igGSTType').value; var rcEl = document.getElementById('igRC'); var rc = rcEl ? rcEl.value : 'no'; var psEl = document.getElementById('igPS'); var ps = psEl ? psEl.value : ''; var payT = document.getElementById('igPayTerms').value || ''; var upi = document.getElementById('igUPI').value || ''; var bank = document.getElementById('igBank').value || ''; var veh = document.getElementById('igVehicle').value || ''; var notes = document.getElementById('igNotes').value || ''; var terms = document.getElementById('igTerms').value || 'Goods once sold will not be taken back.'; var logoSrc = document.getElementById('logoImg').src; var sigSrc = document.getElementById('sigImg').src; var cs = gCurr; // use global currency symbol var subTotal = 0, totDisc = 0, totTax = 0, grand = 0; items.forEach(function (it) { subTotal += it.taxable; totDisc += it.discAmt; totTax += it.taxAmt; grand += it.total; }); var roundOff = Math.round(grand) - grand; var final = grand + roundOff; // ── Table styles based on template ── var td = 'padding:5px 6px;border:1px solid #dde4ea;font-size:10.5px;word-break:break-word;overflow-wrap:break-word;'; var thBg = '#f0f8ff', thColor = '#303335'; if (gTmpl === 'modern') { thBg = '#3BACDE'; thColor = '#fff'; } else if (gTmpl === 'corporate') { thBg = '#303335'; thColor = '#fff'; } else if (gTmpl === 'retail') { thBg = '#E4F1F6'; thColor = '#303335'; } else if (gTmpl === 'manufacturing') { thBg = '#f0f0f0'; thColor = '#303335'; } var th = 'padding:5px 6px;border:1px solid #dde4ea;font-size:10.5px;font-weight:700;word-break:break-word;background:' + thBg + ';color:' + thColor + ';'; // ── Header style based on template ── var hStyle, tStyle, sStyle, infoColor = '#666'; if (gTmpl === 'modern') { hStyle = 'background:linear-gradient(135deg,#303335 0%,#1a1d1f 100%);color:#fff;padding:22px;border-radius:10px 10px 0 0;margin:-24px -24px 18px;display:flex;justify-content:space-between;align-items:flex-start'; tStyle = 'font-size:22px;font-weight:800;letter-spacing:1px;color:#3BACDE;margin-bottom:4px'; sStyle = 'font-size:15px;font-weight:700;color:#fff'; infoColor = 'rgba(255,255,255,.75)'; } else if (gTmpl === 'corporate') { hStyle = 'border-left:5px solid #303335;padding-left:18px;margin-bottom:16px;display:flex;justify-content:space-between;align-items:flex-start'; tStyle = 'font-size:18px;font-weight:800;color:#303335;text-transform:uppercase;letter-spacing:2px;margin-bottom:4px'; sStyle = 'font-size:15px;font-weight:700;color:#303335'; } else if (gTmpl === 'retail') { hStyle = 'background:#E4F1F6;padding:16px;border-radius:10px;margin-bottom:14px;display:flex;justify-content:space-between;align-items:flex-start'; tStyle = 'font-size:16px;font-weight:800;color:#3BACDE;text-transform:uppercase;letter-spacing:1px;margin-bottom:4px'; sStyle = 'font-size:15px;font-weight:700;color:#303335'; } else if (gTmpl === 'manufacturing') { hStyle = 'border:2px solid #303335;padding:14px;border-radius:8px;margin-bottom:14px;display:flex;justify-content:space-between;align-items:flex-start'; tStyle = 'font-size:20px;font-weight:800;color:#303335;margin-bottom:4px'; sStyle = 'font-size:15px;font-weight:700;color:#303335'; } else { // classic hStyle = 'display:flex;justify-content:space-between;align-items:flex-start;border-bottom:2.5px solid #3BACDE;padding-bottom:14px;margin-bottom:14px'; tStyle = 'font-size:20px;font-weight:800;color:#303335;letter-spacing:-.5px'; sStyle = 'font-size:16px;font-weight:700;color:#3BACDE;margin-top:3px'; } var logoHtml = (logoSrc && logoSrc.length > 10) ? 'Logo' : ''; var sigHtml = (sigSrc && sigSrc.length > 10) ? 'Sig' : ''; // ── Build rows ── var rows = ''; items.forEach(function (it, i) { var cgst = gstType === 'cgst_sgst' ? it.taxAmt / 2 : 0; var sgst = gstType === 'cgst_sgst' ? it.taxAmt / 2 : 0; var igst = gstType === 'igst' ? it.taxAmt : 0; rows += '' + '' + (i + 1) + '' + '' + it.name + '' + '' + (it.sku || '—') + '' + '' + (it.sdesc || '—') + '' + '' + it.hsn + '' + '' + it.qty + '' + '' + it.unit + '' + '' + cs + fmt(it.rate) + '' + '' + (it.disc > 0 ? it.disc + '%' : '—') + '' + '' + cs + fmt(it.taxable) + '' + '' + it.gstRate + '%' + (gstType === 'cgst_sgst' ? '' + cs + fmt(cgst) + '' + cs + fmt(sgst) + '' : '' + cs + fmt(igst) + '') + '' + cs + fmt(it.total) + '' + ''; }); var taxCols = gstType === 'cgst_sgst' ? 'CGSTSGST' : 'IGST'; // ── UPI QR placeholder in invoice ── var upiQR = (upi && typeof QRCode !== 'undefined') ? '
Scan to pay: ' + upi + '
' : ''; var rcBanner = rc === 'yes' ? '
Reverse Charge: Tax payable by recipient under RCM
' : ''; var html2 = '
' + '
' + '
' + (logoHtml ? '
' + logoHtml + '
' : '') + '
TAX INVOICE
' + '
' + seller + '
' + (sellerG ? '
GSTIN: ' + sellerG + '
' : '') + (sellerP ? '
PAN: ' + sellerP + '
' : '') + (sellerC ? '
' + sellerC + '
' : '') + '
' + sellerA + '
' + '
' + '
' + '
Invoice No: ' + invNo + '
' + '
Date: ' + date + '
' + (dueD ? '
Due Date: ' + dueD + '
' : '') + (ps ? '
Place of Supply: ' + ps + '
' : '') + (po ? '
PO Ref: ' + po + '
' : '') + (eway ? '
E-Way Bill: ' + eway + '
' : '') + (veh ? '
Vehicle No: ' + veh + '
' : '') + '
' + '
' + rcBanner + '
' + '
' + '
Bill To
' + '
' + buyer + '
' + (buyerG ? '
GSTIN: ' + buyerG + '
' : '') + (buyerM ? '
Mob: ' + buyerM + '
' : '') + (buyerE ? '
' + buyerE + '
' : '') + '
' + buyerA + '
' + '
' + '
' + '
Ship To
' + '
' + buyer + '
' + '
' + shipA + '
' + '
' + '
' + '
' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + taxCols + '' + '' + '' + rows + '' + '
Sr.ProductSKUDescriptionHSN/SACQtyUnitRateDisc%TaxableGST%Total
' + '
' + '
' + '
Sub-Total (Taxable)' + cs + fmt(subTotal) + '
' + (totDisc > 0 ? '
Total Discount−' + cs + fmt(totDisc) + '
' : '') + (gstType === 'cgst_sgst' ? '
CGST' + cs + fmt(totTax / 2) + '
' + '
SGST' + cs + fmt(totTax / 2) + '
' : gstType === 'igst' ? '
IGST' + cs + fmt(totTax) + '
' : '') + (Math.abs(roundOff) > 0.001 ? '
Round Off' + (roundOff >= 0 ? '+' : '') + fmt(roundOff) + '
' : '') + '
Grand Total' + cs + fmt(final) + '
' + '
Amount in words: ' + numWords(Math.round(final)) + '
' + '
' + (upi || bank || payT ? '
' + '
Payment Information
' + (payT ? '
Terms: ' + payT + '
' : '') + (upi ? '
UPI: ' + upi + '
' : '') + upiQR + (bank ? '
Bank: ' + bank + '
' : '') + '
' : '') + (notes || terms ? '
' + (notes ? '
Notes: ' + notes + '
' : '') + (terms ? '
Terms & Conditions:
    ' + terms.split('\n').filter(function (l) { return l.trim(); }).map(function (l) { return '
  • ' + l.trim() + '
  • '; }).join('') + '
' : '') + '
' : '') + '
' + '
' + sigHtml + '
Authorised Signatory
' + seller + '
' + '
' + '
' + '
Computer-generated invoice. Powered by VasyERP Free Tools — vasyerp.com
' + '
'; document.getElementById('igPreview').innerHTML = html2; // Render UPI QR inside invoice if (upi && typeof QRCode !== 'undefined') { var qrDiv = document.getElementById('invUpiQR'); if (qrDiv) { qrDiv.innerHTML = ''; try { new QRCode(qrDiv, { text: 'upi://pay?pa=' + encodeURIComponent(upi) + '&pn=' + encodeURIComponent(seller) + '&am=' + final.toFixed(2) + '&tn=' + encodeURIComponent('Inv ' + invNo), width: 80, height: 80, colorDark: '#303335', colorLight: '#ffffff', correctLevel: QRCode.CorrectLevel.M }); } catch (e) { } } } document.getElementById('noteArea').textContent = 'Invoice ready \u00b7 ' + items.length + ' item(s) \u00b7 Total: ' + cs + fmt(final) + ' \u00b7 Download or Print below.'; document.getElementById('resultBox').classList.add('show'); document.getElementById('calcBtn').innerHTML = 'Generate Another '; saveHist(invNo, buyer, final, captureForm()); setTimeout(function () { document.getElementById('resultBox').scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }, 100); updateStickyBar(true); } // ── printInvoice ───────────────────────────────────────────────────────────── function printInvoice() { openPrintWindow(); } // ── resetCalc ───────────────────────────────────────────────────────────────── function resetCalc() { ['igSellerName', 'igSellerGST', 'igSellerPAN', 'igSellerContact', 'igSellerAddr', 'igBuyerName', 'igBuyerGST', 'igBuyerMobile', 'igBuyerEmail', 'igBuyerAddr', 'igShipAddr', 'igInvNo', 'igDueDate', 'igPO', 'igEWay', 'igPayTerms', 'igUPI', 'igBank', 'igVehicle', 'igNotes', 'igTerms', 'igRC', 'igPS' ].forEach(function (id) { var el = document.getElementById(id); if (el) el.value = ''; }); document.getElementById('igItemsWrapper').innerHTML = ''; itemCount = 0; addItem(); document.getElementById('resultBox').classList.remove('show'); var btn = document.getElementById('calcBtn'); if (btn) btn.innerHTML = 'Generate Invoice '; var wrap = document.getElementById('upiQRWrap'); if (wrap) wrap.classList.remove('show'); var div = document.getElementById('upiQRDiv'); if (div) div.innerHTML = ''; updateStickyBar(false); localStorage.removeItem(AS_KEY); } // ── p27 compat ──────────────────────────────────────────────────────────────── function p27Copy() { shaLink(); } function p27WA() { shaWA(); } function p27LI() { window.open('https://www.linkedin.com/sharing/share-offsite/?url=' + encodeURIComponent(window.location.href), '_blank'); } // ── Init ────────────────────────────────────────────────────────────────────── document.addEventListener('DOMContentLoaded', function () { addItem(); renderHist(); var d = document.getElementById('igDate'); if (d && !d.value) d.value = new Date().toISOString().split('T')[0]; }); // ══════════════════════════════════════════════════════ // MOBILE-FIRST JS // ══════════════════════════════════════════════════════ // ── Auto-save draft ────────────────────────────────── var AS_KEY = 'vasy_inv_draft'; var autoSaveTimer = null; var autoSaveBusy = false; function autoSave() { clearTimeout(autoSaveTimer); autoSaveTimer = setTimeout(function () { try { var data = captureForm(); data._saved = new Date().toISOString(); localStorage.setItem(AS_KEY, JSON.stringify(data)); var badge = document.getElementById('autosaveBadge'); if (badge) { badge.classList.add('visible'); clearTimeout(badge._t); badge._t = setTimeout(function () { badge.classList.remove('visible'); }, 2500); } } catch (e) { } }, 900); } function restoreDraft() { try { var raw = localStorage.getItem(AS_KEY); if (!raw) return; var data = JSON.parse(raw); if (!data || !data._saved) return; // Check there's actual content worth restoring if (!data.igSellerName && !data.igBuyerName && !(data.items && data.items.length)) return; loadForm(data); var banner = document.getElementById('draftBanner'); var sub = document.getElementById('draftBannerSub'); if (banner) { var d = new Date(data._saved); if (sub) sub.textContent = 'Last saved: ' + d.toLocaleString('en-IN', { dateStyle: 'short', timeStyle: 'short' }); banner.classList.add('visible'); } } catch (e) { } } function dismissDraftBanner() { var b = document.getElementById('draftBanner'); if (b) b.classList.remove('visible'); } function clearDraftAndDismiss() { localStorage.removeItem(AS_KEY); dismissDraftBanner(); resetCalc(); } function attachAutoSave() { // Listen for any change/input on the form area var body = document.getElementById('calculator'); if (!body) return; ['input', 'change'].forEach(function (evt) { body.addEventListener(evt, function () { autoSave(); }, true); }); } // ── Sticky action bar state ────────────────────────── function updateStickyBar(generated) { var gen = document.getElementById('sabGenerate'); var dl = document.getElementById('sabDownload'); var sh = document.getElementById('sabShare'); var rst = document.getElementById('sabReset'); if (!gen) return; if (generated) { gen.style.display = 'none'; dl.style.display = ''; sh.style.display = ''; rst.style.display = ''; } else { gen.style.display = ''; dl.style.display = 'none'; sh.style.display = 'none'; rst.style.display = 'none'; } } // ── Inline validation ──────────────────────────────── function showFieldError(inputEl, msg) { inputEl.classList.add('invalid'); var wrapper = inputEl.closest('.field') || inputEl.parentElement; var errEl = wrapper.querySelector('.field-error'); if (!errEl) { errEl = document.createElement('div'); errEl.className = 'field-error'; wrapper.appendChild(errEl); } errEl.textContent = msg; errEl.classList.add('visible'); } function clearFieldError(inputEl) { inputEl.classList.remove('invalid'); var wrapper = inputEl.closest('.field') || inputEl.parentElement; var errEl = wrapper.querySelector('.field-error'); if (errEl) errEl.classList.remove('visible'); } function validateGSTIN(val) { // Basic GSTIN format: 15 chars, digits 1-2 are state code, format: 99XXXXX9999X9X9 return !val || /^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}$/.test(val.toUpperCase()); } function validateEmail(val) { return !val || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val); } function validateMobile(val) { return !val || /^[6-9]\d{9}$/.test(val.replace(/\s/g, '')); } function attachValidation() { // GSTIN fields ['igSellerGST', 'igBuyerGST'].forEach(function (id) { var el = document.getElementById(id); if (!el) return; el.addEventListener('blur', function () { var v = el.value.trim().toUpperCase(); if (v) el.value = v; if (!validateGSTIN(v)) showFieldError(el, 'Invalid GSTIN format (e.g. 27AABCU9603R1ZM)'); else clearFieldError(el); }); el.addEventListener('input', function () { if (el.classList.contains('invalid')) clearFieldError(el); }); }); // Email var eEl = document.getElementById('igBuyerEmail'); if (eEl) { eEl.addEventListener('blur', function () { if (!validateEmail(eEl.value.trim())) showFieldError(eEl, 'Please enter a valid email address'); else clearFieldError(eEl); }); eEl.addEventListener('input', function () { if (eEl.classList.contains('invalid')) clearFieldError(eEl); }); } // Mobile var mEl = document.getElementById('igBuyerMobile'); if (mEl) { mEl.addEventListener('blur', function () { var v = mEl.value.trim(); if (v && !validateMobile(v)) showFieldError(mEl, 'Enter a valid 10-digit Indian mobile number'); else clearFieldError(mEl); }); mEl.addEventListener('input', function () { if (mEl.classList.contains('invalid')) clearFieldError(mEl); }); } } // ── iOS PDF fix: use blob + object URL approach ────── function iosSafePDF() { openPrintWindow(); toast('Opening print dialog \u2014 select Save as PDF'); } function dlPDF() { openPrintWindow(); toast('Opening print dialog \u2014 select Save as PDF'); } // ── Single null-safe print/PDF entry point ──────────── function openPrintWindow() { var previewEl = document.getElementById('igPreview'); if (!previewEl || !previewEl.innerHTML.trim()) { toast('Please generate an invoice first'); return; } var content = previewEl.innerHTML; var docStr = '' + 'Invoice - VasyERP' + '' + content + ' '; // Try window.open — may return null if popups are blocked var w = window.open('', '_blank'); if (w && w.document) { try { w.document.open(); w.document.write(docStr); w.document.close(); w.focus(); setTimeout(function () { w.print(); }, 600); } catch (e) { w.close(); blobFallback(docStr); } return; } // Popup blocked — try Blob URL (works on most Android/Desktop browsers) blobFallback(docStr); } function blobFallback(docStr) { try { var blob = new Blob([docStr], { type: 'text/html;charset=utf-8' }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.target = '_blank'; a.rel = 'noopener'; document.body.appendChild(a); a.click(); document.body.removeChild(a); setTimeout(function () { URL.revokeObjectURL(url); }, 10000); } catch (e) { toast('Enable pop-ups for this site to print / save PDF'); } } // resetCalc sticky bar update: handled inline in main resetCalc() above // calculate sticky bar update: handled inline in main calculate() above // ── Apply inputmode after addItem (called directly) ── function applyInputmodeToItem() { // Apply inputmode to the newly created item's number fields var id = itemCount; var numFields = ['qty', 'rate', 'disc']; numFields.forEach(function (f) { var el = document.getElementById(f + '-' + id); if (el) { el.setAttribute('inputmode', 'decimal'); el.setAttribute('autocorrect', 'off'); el.setAttribute('autocomplete', 'off'); } }); var nameEl = document.getElementById('name-' + id); if (nameEl) { nameEl.setAttribute('inputmode', 'text'); nameEl.setAttribute('autocapitalize', 'words'); nameEl.setAttribute('autocorrect', 'off'); nameEl.setAttribute('autocomplete', 'off'); } var skuEl = document.getElementById('sku-' + id); if (skuEl) { skuEl.setAttribute('inputmode', 'text'); skuEl.setAttribute('autocorrect', 'off'); skuEl.setAttribute('autocomplete', 'off'); } var hsnEl = document.getElementById('hsn-' + id); if (hsnEl) { hsnEl.setAttribute('inputmode', 'numeric'); hsnEl.setAttribute('autocorrect', 'off'); hsnEl.setAttribute('autocomplete', 'off'); } var sdescEl = document.getElementById('sdesc-' + id); if (sdescEl) { sdescEl.setAttribute('inputmode', 'text'); sdescEl.setAttribute('autocapitalize', 'sentences'); sdescEl.setAttribute('autocorrect', 'on'); } autoSave(); } // ── DOMContentLoaded ───────────────────────────────── document.addEventListener('DOMContentLoaded', function () { // Add addItem + renderHist + date (from main init) // These are already done by the main init block above, // so just attach extra mobile behaviors here: attachAutoSave(); attachValidation(); // Draft restore (slight delay so form is initialized) setTimeout(restoreDraft, 150); // Show sticky bar on mobile if (window.innerWidth <= 900) { var bar = document.getElementById('stickyBar'); if (bar) bar.classList.add('visible'); } // Prevent double-tap zoom on buttons document.querySelectorAll('button, .btn-primary, .btn-outline').forEach(function (el) { el.addEventListener('touchend', function (e) { e.preventDefault(); el.click(); }, { passive: false }); }); }); window.addEventListener('resize', function () { var bar = document.getElementById('stickyBar'); if (!bar) return; if (window.innerWidth <= 900) bar.classList.add('visible'); else bar.classList.remove('visible'); });