Document
Customer
Items
Rates are editable. Confirm current GST / TGST rates with MIRA before issuing.
Notes, terms, payment
My business
Layout
Hosting copy (save everything into the file)

Fill in My business, Layout and Google Sheets first. Then download a copy with your URL, logo, signature, bank details, terms, saved items and customers built in. Upload that file to your free host. Do this again whenever you change those settings.

Google Sheets setup
  1. Create a Google Sheet.
  2. Extensions › Apps Script, paste the code below.
  3. Deploy › New deployment › Web app. Execute as: Me. Access: Anyone.
  4. Copy the Web App URL above.
  5. Open this file from your device. If you used the earlier script, paste this one and choose Deploy › Manage deployments › New version.
function sh_(n,hd){var s=SpreadsheetApp.getActive(),h=s.getSheetByName(n);if(!h){h=s.insertSheet(n);h.appendRow(hd)}return h}
function up_(h,k,r){var v=h.getDataRange().getValues();for(var i=1;i<v.length;i++)if(v[i][0]==k){h.getRange(i+1,1,1,r.length).setValues([r]);return}h.appendRow(r)}
var KEY=''; // optional secret word; type the same word in the page's Access key box
var DH=['No','Type','Date','Customer','Currency','Total','Status','JSON'],CH=['Name','TIN','Address','Phone'];
function doPost(e){var b=JSON.parse(e.postData.contents);if(KEY&&b.key!=KEY)return ContentService.createTextOutput('denied');var d=b.doc;
up_(sh_('Documents',DH),d.no,[d.no,d.type,d.date,d.cust.name,d.cur,d.total,d.status,JSON.stringify(d)]);
var I=sh_('Items',['DocNo','Description','Unit','Qty','Price','Tax']),v=I.getDataRange().getValues();
for(var i=v.length-1;i>0;i--)if(v[i][0]==d.no)I.deleteRow(i+1);
d.items.forEach(function(x){I.appendRow([d.no,x.d,x.u,x.q,x.p,x.t])});
if(d.cust.name)up_(sh_('Customers',CH),d.cust.name,[d.cust.name,d.cust.tin,d.cust.addr,d.cust.ph]);
return ContentService.createTextOutput('ok')}
function doGet(e){if(KEY&&e.parameter.key!=KEY)return ContentService.createTextOutput('[]');var v=sh_('Documents',DH).getDataRange().getValues().slice(1).map(function(x){return JSON.parse(x[7])});
var c=sh_('Customers',CH).getDataRange().getValues().slice(1).map(function(x){return{name:x[0],tin:x[1],addr:x[2],ph:x[3]}});
return ContentService.createTextOutput(JSON.stringify({docs:v,customers:c})).setMimeType(ContentService.MimeType.JSON)}