Logo Get a Quote

Quote Builder

Build and Share Customer Quotes

Generate, print, download, and email branded quotations quickly from one responsive page for Greater Norwich homeowners.

Project Inputs

Quotation Details

Line Items

Scope of Works Items

\n `})()),e.document.close()):alert("Please allow pop-ups for printing on mobile.")};l.addEventListener("click",()=>{w({qty:1})}),m.addEventListener("input",()=>{}),a.addEventListener("click",()=>{alert("Add one or more line items using Description of Service, Labour, Materials, Item Price and Qty. Each line total, the sub-total, VAT and grand total are calculated automatically. The generated quote groups items by Labour & Materials, Labour Only, Materials Only and Other Items.")}),n.addEventListener("submit",i=>{if(i.preventDefault(),!n.reportValidity())return;const{items:a,incompleteRows:l}=(()=>{const e=[],n=[];return h().forEach((t,r)=>{const o=t.querySelector(".item-description").value.trim(),i=t.querySelector(".item-labour").checked,a=t.querySelector(".item-materials").checked,l=b(t.querySelector(".item-price").value),d=b(t.querySelector(".item-qty").value),s=+(l*d).toFixed(2);(o||i||a||l>0||d>0)&&(!o||d<=0?n.push(r+1):e.push({description:o,labour:i,materials:a,price:l,qty:d,total:s}))}),{items:e,incompleteRows:n}})();if(l.length)return void alert(`Please complete Description of Service and Qty for line item row(s): ${l.join(", ")}.`);if(!a.length)return void alert("Please add at least one complete line item before generating the quote.");const d=v(),s=new FormData(n),c=String(s.get("client_name")||"").trim(),u=String(s.get("project_address")||"").trim(),m=String(s.get("client_phone")||"").trim(),y=String(s.get("client_email")||"").trim(),q=String(s.get("scope_notes")||"").trim(),x=new Date,S=new Date(x);S.setDate(S.getDate()+30);const w=[m,y].filter(Boolean),L=w.length?w.join(" / "):"Not provided";e.querySelector("#outQuoteNumber").textContent=(()=>{const e=new Date;return`MHI-${[e.getFullYear(),String(e.getMonth()+1).padStart(2,"0"),String(e.getDate()).padStart(2,"0")].join("")}-${String(Math.floor(900*Math.random())+100)}`})(),e.querySelector("#outDate").textContent=g(x),e.querySelector("#outValidUntil").textContent=g(S),e.querySelector("#outClientName").textContent=c,e.querySelector("#outSiteAddress").textContent=u||"To be confirmed",e.querySelector("#outContact").textContent=L,e.querySelector("#outScopeText").textContent=q||"Detailed description of the project based on our site visit.",e.querySelector("#outLineItemGroups").innerHTML=(e=>{const n={labourMaterials:[],labourOnly:[],materialsOnly:[],other:[]};return e.forEach(e=>{e.labour&&e.materials?n.labourMaterials.push(e):e.labour?n.labourOnly.push(e):e.materials?n.materialsOnly.push(e):n.other.push(e)}),[C("Labour & Materials",n.labourMaterials),C("Labour Only",n.labourOnly),C("Materials Only",n.materialsOnly),C("Other Items",n.other)].filter(Boolean).join("")})(a),e.querySelector("#outSubTotal").textContent=p(d.subtotal),e.querySelector("#outVatTotal").textContent=p(d.vat),e.querySelector("#outGrandTotal").textContent=p(d.grandTotal),e.querySelector("#outVatNote").textContent=d.vat>0?"VAT calculated at 20% and included in the grand total.":"No VAT applied.",t.classList.remove("hidden"),f(r),f(o),t.scrollIntoView({behavior:"smooth",block:"start"})}),i.addEventListener("click",()=>{n.reset(),L(),s.value="",c.value="",u.value="",e.querySelector("#outQuoteNumber").textContent="",e.querySelector("#outDate").textContent="",e.querySelector("#outValidUntil").textContent="",e.querySelector("#outClientName").textContent="",e.querySelector("#outSiteAddress").textContent="",e.querySelector("#outContact").textContent="",e.querySelector("#outScopeText").textContent="Detailed description of the project based on our site visit.",e.querySelector("#outLineItemGroups").innerHTML="",e.querySelector("#outSubTotal").textContent="£0.00",e.querySelector("#outVatTotal").textContent="£0.00",e.querySelector("#outGrandTotal").textContent="£0.00",e.querySelector("#outVatNote").textContent="VAT calculated at 20% and included in the grand total.",k()}),o.addEventListener("click",()=>{k()}),window.addEventListener("afterprint",T),r.addEventListener("click",()=>{t.classList.contains("hidden")||(window.matchMedia("(max-width: 767px)").matches||/Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent)?E():((()=>{let e=document.getElementById("quotePrintRoot");e||(e=document.createElement("div"),e.id="quotePrintRoot",document.body.appendChild(e));const n=t.cloneNode(!0);n.classList.remove("hidden"),n.classList.add("block"),e.innerHTML="",e.appendChild(n)})(),document.body.classList.add("quote-print-mode"),requestAnimationFrame(()=>{requestAnimationFrame(()=>{window.print(),setTimeout(()=>{T()},800)})})))}),L(),v()})()