How We Build Our Own Quotes With ExpressionEngine

by Jonathan Longnecker

For a small web shop, creating estimates and quotes can be a joy killer. I just want to make stuff, man - not write out endless spec documents! It's true. Being old school print designers let me tell you how this process went for years:

  1. Open up our quote InDesign document
  2. Save it as a new file
  3. Pour over every legal aspect to make sure it was right
  4. Do the whole spec part of the job in the middle
  5. Make sure all the styles were right so it would look awesome
  6. Do all the math (arghhh)
  7. Save as a PDF
  8. Email it

And. We. Hated. It.

When we rebuilt and re-designed the site last year we knew some things had to change. First of all, we didn't want to go through that huge document and re-do all the styles. Bleh. Second of all, it had become a huge bottleneck and we wanted to fix that.

Separating Scope and Legal

Our first hint came from one of Less Everything's Less Money workshops a couple of years ago. They shared a legal contract that they use for all their clients and then add a scope of work separately. “Hey that makes sense.” Why have all that legal stuff that barely changes anyway be on the estimate? It's ripe for mistakes and a barrier to entry for the client.

So we created a Google Doc and link all of our clients to it as part of our estimate. Again, it's just the legal side of stuff, not anything specific to their project. That's where we got creative.

Building our own Quoting Engine

Another piece of the puzzle was that we moved to hourly pricing last year as well. So this was going to change how we created estimates anyway. It was time to do something different. Looking at our bottleneck, we spent all our time on styling the text and doing the math. The actual “we're doing this and it will cost this much” part was easy.

After doing all the work rebranding our website I started to think, “What if our quotes were just built in to the site - that way we inherit all the styles automatically and if we do rebrand again it comes along for the ride?” And then I thought, “Our site is built in ExpressionEngine which is crazy powerful - surely we could create a channel that lets us input the content, easily style it with a WYSIWYG and do all the math for us!” And then I got excited. 'Cause that was a good idea.

Thanks to a few Add-On's it wasn't that hard, either. Here's a picture of how we enter the data:

Entering data for the quote
Matrix saves the day

Basically we just used Wygwam and Matrix to allow us to repeat major sections. So Design would get it's own row, HTML/CSS would be on another row, etc… This lets us build custom quotes really fast while keeping styles in tact. We also threw in an expenses field since it was a dollar amount and not an hourly amount.

Image of Test Quote
That took like 10 minutes. No joke.

On the template side we used MX Calculator and Price Format as well as the built in sum functions in Matrix to add everything up. That was probably the trickiest part.

And just like that, it got stupid easy to build a quote for someone. We have links hardcoded to the legal document and a billing policy document so we just password protect it, send them a link with login information and they can literally print, sign and email back to us from right there. Since our site is responsive, even printing the page works, too.

We've been using this for at least a year and it's taken such a load off when creating estimates. So I thought you might find it useful, too. Here's how we made it:

What You'll Need

Then create a channel called Quote - shortname {quote} and create these fields:

  1. Client - {quote_client} - text field
  2. Hourly Rate - {quote_hourly_rate} - text field
  3. Discovery/Deposit - {quote_discovery} - text field
  4. Quote Table - {quote_table} - Matrix with two columns - Wygwam field called {desc} and a text field called {hour_low}
  5. Expenses Table - {quote_expenses_table} - Also a Matrix with two columns - Wygwam field called {desc} and a text field called {amount}

The hourly rate field lets you adjust per quote if you decide to raise your rates or give a non-profit a discount. The deposit fields lets you add an additional fee to the quote if you need to. We also use the expiration date filed built into EE so they know how long the price is good for. And we've already talked about how the quote table and expenses table work.

Finally, you'll need two templates. One for the main file and one to do your number formatting. I've stripped down the main one as much as I can, but it's still pretty long:

<!DOCTYPE html>

  {exp:channel:entries channel="quote" limit="1" disable="pagination|categories|member_data|trackbacks" url_title="{segment_2}"}
    <meta name="description" content="{summary}" />
    <title>{title} | Estimate | {site_name}</title>
    <!-- Stop search engines from indexing your estimates :)-->
    <meta name="robots" content="noindex,nofollow">

  <body class="estimate">

    <div id="content">
    {if logged_in}
    {exp:channel:entries channel="quote" limit="1" disable="member_data|trackbacks" url_title="{segment_2}"}

    <h1>Exhibit A: Estimate for {quote_client}</h1>
    <p class="date">{entry_date format='%F %d, %Y'} - price good until {expiration_date format='%F %d, %Y'}</p>
    <table cellspacing="0" cellpadding="0">
          <th scope="col" class="desc">Description</th>
          <th scope="col" class="hour_low">Hours</th>
          <td class="desc">{desc}</td>
          <td class="hour_low">{hour_low} <span>hrs</span></td>
          <td class="desc">{desc}</td>
          <td class="hour_low">${amount}</td>
        <td class="desc"><h3>Total</h3></td>
        <td class="hour_low total">{quote_table:sum col="hour_low"} <span>hrs</span> <br />
          <span class="multiply">x &nbsp;&nbsp;&nbsp;</span> ${quote_hourly_rate}<span>/hr</span><br /><hr />
          {exp:mx_calc expression="{quote_table:sum col="hour_low"}*{quote_hourly_rate}"}
            ${embed="quotes/calc" result="{calc_result}"}
          {/exp:mx_calc}<br />
          <span class="multiply">+ expenses &nbsp; &nbsp; &nbsp; </span> ${embed="quotes/calc" result="{quote_expenses_table}{amount}{/quote_expenses_table}"} <br />
          {if quote_discovery != "0"} <span class="multiply">+ discovery/deposit &nbsp; </span> ${embed="quotes/calc" result="{quote_discovery}"} <br />{/if}
          <hr />
          <h2>{exp:mx_calc expression="({quote_table:sum col="hour_low"}*{quote_hourly_rate})+{quote_discovery}+{quote_expenses_table}{amount}{/quote_expenses_table}"}
             ${embed="quotes/calc" result="{calc_result}"}
    <p class="disclaimer"><em>Hours are billed in 30 minute increments. See contract and billing document for additional information.</em></p>
      <div class="signature"><hr />{quote_client} Representative</div>
      <div class="signature last"><hr />{site_name} Representative</div>
      <div class="signature"><hr />Date</div>
      <div class="signature last"><hr />Date</div>

    {if logged_out}
      <h1>Client Quote Login</h1>
      <p>Hey there! If you're a potential client just login with the info we gave you to see your quote.</p>

  </div><!--End Content-->

    <div id="sidebar">
    {if logged_out}
      {exp:member:login_form return="/quotes/{segment_2}"}
        <div class="loginbox clearfix">
          <input class="text required" type="text" name="username" value="" maxlength="32" size="25">
          <input class="text required" type="password" name="password" value="" maxlength="32" size="25">
          {if auto_login}
            <p><input type="checkbox" name="auto_login" value="1"> Auto-login on future visits</p>
          <span class="button"><input name="submit" type="submit" value="Submit" /></span>
    {if logged_in}
      <div class="contact-section clearfix">
        <h2>Sign These</h2>
          <li class="print"><a href="#" onclick="javascript:window.print();">Print/Sign This Page</a></li>
          <li class="download"><a href="#">Quote Agreement</a></li>
        <h2>Read This</h2>
          <li class="download"><a href="#">Billing Policy</a></li>
      <p class="logout"><em><a href="{path='logout'}">Log Out</a></em></p>

And the second template - {calc} is just this:

{exp:price_format price="{embed:result}" decimals="0"}

How it works now

So after a little bit of work we have a really great system in place for creating estimates. Our process now is just this:

  1. Log into ExpressionEngine
  2. Publish a new quote entry (or use MX Cloner to re-use as much as possible from an old one)
  3. Enter content and hours
  4. Have it automagically styled and added up
  5. Email it

You can obviously adjust as needed, but I thought having the math already put together would be helpful. If copying and pasting isn't your thing, I've made text files for you to download, too. So, if your quoting process is driving you crazy and your site is already built with ExpressionEngine give this a shot! Shoudn't take too long to setup and you'll save tons of time creating estimates in the long run.


August 01, 2013

Business, ExpressionEngine


  1. Great stuff! How does the signing work?

  2. @Mike Lohrman That’s up to the client. They can either print, sign and mail or print to PDF and digitally sign.

  3. Thanks guys. It’s great to get some insight on how others handle this frustrating process.

  4. Thanks a lot for this, very useful and i’m going to create one for my site.

  5. Thanks for this article - really great stuff.

    We have been trying some of the “proposal websites” like BidSketch and QuoteRoller and have thought of doing something similar in EE also.

    In the few proposals we have tested using BidSketch we have found clients seem to sign off on the proposal really quickly as they can digitally sign the document/proposal online just by clicking an “accept” button.

    Just wondering if you thought of using something like this service to allow digital signatures.

  6. @Lincoln Gbenga Olagbaju Awesome, let us know how it goes!

    @Jules We already use Less Accounting and Harvest, both of which provide some sort of “proposal” and we didn’t really want to signup for another service. And again, we wanted the branding to be easily changed as well as having our own record of quotes in EE. 

    As far as digital signing - I’m not sure if it’s worth the hassle to setup and/or hassle to the clients. Sound easier in theory, but going from “Print this and sign it with my hand” to “here’s a bunch of options for digitally signing, and I’m not sure if I’m comfortable with that anyway” doesn’t seem like it’s worth it. Maybe for $5k or less jobs, but anything bigger I think the client would prefer a real signature.

  7. This is fantastic! We had been doing something similar through EE for our ad clients, but your method is much simpler (I always complicate things).

    For the digital signatures, since it is password protected - I would think you could use Safecracker and have them input (sign) their name plus ask for the last 4 numbers of a business TIN/EIN and that would make it a legal signature. Would be good to put that in the contract as well.

    Thanks for sharing this again - very insightful.

  8. Great write up guys - thanks for sharing.

    I’m curious as to why you built a quote engine yourself when you use Harvest which produces pretty good quotes already?

    I’m part way through building a CRM/Prospect Tracking site for ourselves and adding a quote engine could be an option. Like you guys, we also use Harvest but would only see it worthwhile taking it that far if it meant we didn’t need Harvest at all. For me that means creating a ‘job’ list in EE that hours can be recorded against (we don’t use Harvests invoicing features) to be able to replace it (although its a great app).

  9. @John Derrick Yep, you could definitely do that. That’s why I like working with EE smile

    @Andrew Armitage We love Harvest for hourly billing and invoicing, but we need to do full blown quotes with contract lingo. Harvest’s “accept project” doesn’t really do much as far as I can tell in the way of getting real deal signatures and giving you an easy way to give them a full, binding contract.

    Ours does all that at the same time and it happens all on our site which adds to the trust and brand factor.

    If you want to build your own versions of Harvest and a CRM together that’s up to you wink

  10. Thanks for sharing and nice idea. Two questions for you all if I may.

    1) Have you received any pushback from prospective or existing clients with having to use a username/password to login to receive your quotes?

    2) What are your thoughts for versioning moving forward. For example, are you at all concerned that if you revise your proposal format and/or legalease at any point, that your change may be made retroactive?  Would you just create a new channel/template and somehow distinguish between old and new?

  11. @Seth Thanks, Seth!

    1) Nope, none at all
    2) Depends on how much we revise it. For example if we went back to project pricing then yeah probably a new channel. Otherwise probably not as whatever estimates we gave in the past would have been printed and signed so they stay in that form. Clients don’t generally come back to the online quote once they’ve signed.

  12. Hey guys!

    Awesome post, as usual. I’m in the process of going out on my own and setting up a one man studio.

    My question for you guys is, when it comes to clients that you don’t have the chance to meet face to face, how does the contract signing process go? If they print it out, will they then have to mail it to you so you can sign it?  Do clients ever have any issues with that as I imagine you could get them to sign it then not sign it yourselves or do you have to copies of it?

    Would love to know more about your contract signing process specifically? 

    Thanks a lot guys,

    Keep kicking *happy word*,


  13. @Ashley Thanks so much! Yeah we don’t meet most of our clients face to face so they just print and sign. Some email, some physically mail it. Either way we sign it after they do and send them a copy back along with a w9 for their accounting records.

    We try to encourage digital signing, even if they print it, sign it, scan it and email it so we have digital records, but no hard and fast rule here. Different clients are comfortable with different methods. Hope that helps!

  14. Greetings and thanks for this really informative post! I’m in the process of trying to do a better job with my quotes, and this is very thought provoking.

    A question or two: I’ve been going back and forth about detailing the expenses for EE and the plugins, etc. Originally, I did that, but then I moved to making just a set price for the project to a certain point, and going hourly after that. But lately I’m thinking about separating out the expenses again. So I was wondering: how much detail to you go into with expenses? Do you list every plugin and the EE license with the costs for each item? I don’t think most of my clients would have any idea what I’m talking about any way!

    Also, would you have any tips on a source for good legal boilerplate for web projects? This is something else I haven’t really looked into enough….

    Thanks again for sharing all this! Very generous!

  15. @edward You are welcome! I do list out the EE license and individual add-ons, but set a lump sum for expenses rather than give totals for each. I also pad it for things we may not know we need yet like icons sets, fonts, photography, etc…

    I’ve only had one client ask for prices for each add-on. I feel like more than anything it shows that you looked at their project and carefully considered exactly what they need. We did the “roll it into the cost” thing as well, but as jobs have gotten larger often a client will ask for additional functionality and it’s good to have things line item’d so you can say - well we’ll need to get this other add-on to make that work. Because they saw what we needed to do the original spec it makes more sense than everything just working magically for one price smile

    I’d check out Docracy or some of the resources here: - our contract is based off stuff from the Less Everything guys.

    Good luck!

  16. Thanks for the reply! Might I ask a follow-up? I’m trying to get my business practices figured out better….

    So is this quote an estimate then? Or is it considered the final price? I notice at the bottom you mention that hours are billed in 30 minute increments. And I was also curious about the “discovery/deposit”: is this the fee you are charging to do a wireframe or detailed layout of the site?

    I really appreciate you posting this; it’s so helpful!

  17. @edward We currently bill on an hourly basis, so yes this is just an estimate. We do bill in 30min increments, though you could start with 15.

    Our deposit ties in with the main contract as a non-refundable deposit. We also use it to let people secure a spot in our schedule. This is totally optional and something we’re still playing around with.

    BTW, we run a small business training site that will help answer a lot of these questions (it’s free!)

    Good luck man!

  18. Good morning,

    I came across this post while researching MX Calculator and it has been really valuable in helping me get a handle on how I can use the add-on. With this in mind, I was hoping you may be able to help me answer a question (or point me toward a resource that may) ...

    This is the first project I’ve tried to use ExpressionEngine to perform calculations on numerical channel data and I think I understand the basics, but I would like to take it a step further and I’m not sure how.

    Let’s say I have a channel called {survey} that contains the answers to survey questions about where respondents attended school, what their academic major was, and what their starting salary is after graduation. The channel fields would include the following:


    I would like to create a page with a listing of each possible academic major, linked to dynamic pages that use MX Calculator to calculate and display the average salary of all records where the {major} field = “Engineering” or “Art,” etc.

    I’m not sure if this should be built with channel fields like those I listed above, or maybe with a single, matrix field with columns for “salary,” “school,” “major,” etc. and rows for each response/entry in the survey

    What do you suggest? Is one way better that another to try to produce this, and, if, so, could you please provide or point me toward a resource that will provide an example of what the code would look like to perform such a calculation?

    Thanks for making this post available and for your help,

    Darin Parker

  19. @Darin Well, kind of depends on a number of factors. The biggest being - where is that survey data coming from? Are people inputting it through a form on the site so that these calculations are changing on a regular basis? Or are you just taking a spreadsheet and inputting data?

    If it’s the former you’re going to be dealing with a new channel entry for each person so you’ll have to do it that way. If the other you could do Matrix rows and keep it more contained (and way less overhead query-wise).

    Otherwise, it should just be simple math with the plugin, I think. I’ve only done addition and multiplication with it, but a quick look at the docs says it does to averaging so you should be good to go.

  20. @Jonathan Longnecker
    Thanks. The survey data is gathered through another method and I was planning to use the Solspace “Importer” add-on to bring it into ExpressionEngine.

    In my current test, I have a channel named {outcomes} that contains a matrix field named {outcomes_data}.

    The {outcomes_data} field contains the following columns:

    {school} - the college attended within the university, such as journalism, arts and sciences, etc.
    {status} - this is a “yes” or “no” field related to whether or not this individual responded to the survey

    I have a little experience with basic SQL statements but not with perform calculations in EE or PHP and the documentation I found with the MX Calculator add-on focused on examples using actual numbers instead of data pulled from EE channels. Since that is the case, your blog entry has been very helpful in applying to my project.

    At this point, with few examples available, I’m having difficulty determining how to perform and display something like, “average salary for history majors.” Would this need to start with a new {exp:channel:entries} tag pair that specifies the parameters, would I use the EE query module, or is there a way to get that specific (pulling columns with certain values or columns that do not contain null values) within the “exp:mx_calc expression” tags?

    I know these are broad questions and I really appreciate your time and help.

  21. @Darin I’d recommend reading up on how Matrix works for sure, but the basic idea would be this:

    {exp:channel:entries channel=“outcomes”}
    {exp:mx_calc expression=“average({outcomes_data backspace=“2”}{salary}, {/outcomes_data})”}

    You use the Matrix as a opening/closing tag pair and then just call in the individual cell row. The tricky part is MX Calculator wants commas between them so you have to use the backspace parameter to remove that comma and space from the last one.

    This is just the rough idea, hopefully you can dig through the docs and get more specific with syntax and stuff since I can’t really test it at the moment smile

  22. @Darin If the matrix field won’t parse inside MX Calc you might have to do that in one template and pass the result though as a variable in an embedded template. Kind of like we did for our number formatting.

  23. tutorialnya lengkap sekali,ilmu yang bermanfaa bagi saya..

  24. It provide the best website service in design the various kind of website according to user requirement.view for more detail :-

Behold our Amazing Portfolio

Check it Out