Build an Invoice System in WordPress | Crocoblock Guide

Here is how to build an invoicing system in WordPress. The aim of this tutorial is to show you how you can use JetEngine and JetFormBuilder to build something like this, it does not have to be this exact use case.

Tobi Salami
Updated 1 month ago
Transcript of Video

Hey smart people. Tobi here. In this video, I’m going to show you how you can create an invoice system inside of WordPress. The essence of showing you how to do this, as usual, is not just to show you how to build an invoice system inside of WordPress. I want to show you just how you can make use of the tools.

I’m gonna show you two major plugins: JetEngine and JetForm Builder, to build out something that is absolutely spectacular.

Something you could come up with on your own and absolutely implement. Every single step I show you here will be absolutely important for you to understand just how I piece these tools together to create this particular scenario.

There are a plethora of these scenarios. You only have to think about it, see how I implemented some ideas here, and apply them to your own unique scenarios. Okay, so without further ado, let me show you what I’ve built so far, and then I’m gonna go through every single aspect of it.

Right here, I am on my “Add Invoice” page, and as you can see, you have an automatically generating invoice number. I’m gonna show you just how to do that. You have the ability to select your invoice currency. You can change that from USD to Euro if you want. And as you can see down here, it’s also adjusting right here.

If you change this back to USD right here, you could come down here. You can see it’s also changed to USD. Okay. Now, you can select your clients. Here is the dynamically generated list of all the clients you already have on your WordPress website, so you can definitely do that.

I’m gonna show you how, inside of “Invoice Date,” you can select the date. Let’s say I want to select the 13th of June, 2025. That’s okay. Inside of the “Work Type,” I have either hourly or service-based. If I say it’s hourly, then you see the invoice details here show me: description, hours, unit price, and total.

If I change this from hourly to service-based, you see here, when I add new items, it says instead: description, quantity, unit price, and total. Instead of “hours,” it says “Quantity.” Let’s come to “Invoice Name.” We can give this an invoice name of, let’s say, “Web Dev Invoice.”

Something like this is absolutely fine. Now I can begin to add each of the line items inside of my invoice. So let me do that. Let’s say web development is the first line item for maybe the month of May.

And I could say here that the number of hours that was spent was, let’s say, 20 hours, right? If the unit price, which is equivalent to the cost per hour of my work, is let’s say $50, right? Let’s say 50. We say 50 multiplied by 20, and it totally gives us a thousand dollars, right?

That’s okay. We can add a new item. Let’s say this one is web development for the month of June. I can say the number of hours spent in the month of June so far, let’s say is five hours, and at the price of 50.

Okay, so that’s five times 50 — 250. You can see it’s automatically calculating these values for us. Down here, you can have cost reduction. So far, you can see the invoice subtotal is 1,250, which is 1,000 plus 250. It makes sense. That’s 1,250. You also have the invoice total, which is the same — 1,250. And why is that?

Because of the fact that there are no cost reductions just yet. If we want to add some cost reduction, like, let’s say, we give the clients a discount of, let’s say, 500 USD, this removes 500 from 1,250, and the total invoice value is 750. Now, this is where it gets very interesting. The moment I click on submit, here is what happens:

It automatically refreshes the page by generating a new invoice PDF, a downloadable PDF for us. Here is exactly what we filled in. We have the invoice number, which is right here. We have the date, which we filled right there. I’m gonna show you how to display that date. It’s not as easy as it appears.

You have here my address, which is somewhere in the world. Hello. That’s where I am. You have my email address, which is [email protected]. You have down here the website name and the phone number.

All of these details are fetched dynamically. Right here at the top, you also have the invoice title. Let’s say the name of the company, which is Tobi Salami. In this case, I’m an individual. My name is Tobi Salami. And if you’re new here —

Hello, my name is Tobi Salami, and I release dynamic WordPress development videos every single week.

If you enjoy a dynamic WordPress development video such as this, you want to be sure to subscribe to this YouTube channel so that you get notified the next time I release a video such as this.

If you also appreciate all of the time that I spent trying to make this video available to you, please be sure to hit that like button below to say thank you, and leave a comment down below.

Okay. Let me come back to showing you what was generated here. If you see inside of the invoice, you have the description, the hours, and as you can see, this table automatically understands that we’re dealing with hours here and not unit costs for a service-based invoice.

Right. So we had unit price, and we also have the currency that was selected inside of that form. You have all of the items. You have the total item returned here as a subtotal.

You have the deductible, which was the discount, which is returned here as 500. 1,250 minus 500 gives a total of 750.

And just like this, we’re able to generate a very nice invoice. I want to show you every single bit of this — it’s a bit complicated.

We’re going to go through some hoops.

You’re gonna approach areas that feel a little bit too complicated, but don’t worry — I’m gonna make everything as simple as possible. Okay?

Let’s come back to the main website right here. Let’s just load this up. And let’s come back inside of “Add Invoice” because this is where things get very important.

I first want to show you how I set things up in the backend, so let’s go there.

Let’s go inside the plugins, and we can take a look at all of the plugins I have used here. Automatic CSS is not necessary for this tutorial. It’s just a plugin I made use of to style the Bricks Builder, which is my page builder of choice right here. But I’m almost not going to go inside of the page builder.

Whether you’re using Bricks Builder or you’re using Elementor or you’re using Gutenberg, it really doesn’t matter. As long as your page builder supports JetEngine and JetForm Builder, you’re good to go.

Those are the two major plugins that are important in ensuring that this tutorial works out very smoothly. You also have the JetForm Builder PDF Attachment, which is just an add-on for JetForm Builder. You can find it inside of your JetForm Builder account, and you can use this very easily to generate those invoices.

I have Command UI, which I’m just gonna be using to navigate around my website. It is not a necessity as it relates to this tutorial, but it does help to speed up your workflow. You’re gonna see as I go ahead inside of this tutorial.

And these Fluent Snippets — I use that to add just a simple PHP snippet, which I’m gonna show you how to make use of as we go ahead inside of this tutorial.

I also added a link to that snippet in the description below. Okay. So let me start from the beginning. I came inside of “Custom Content Types” by JetEngine, which you can create and absolutely make use of.

Of course, I assume you should know how to add a custom content type inside of JetEngine. Okay, so this is the content type I first created, which is called “Invoice System.” Invoice System is what you find right here, and inside of it, I just added a number of fields. For example, I added the field for invoice dates, the invoice description, the invoice number — just everything as you see right here in the front end.

You also have the repeater for “Invoice Details Hourly,” which you can find right here. If you come down here to invoice details, you can see hourly down here. You also have service-based right here. So if it’s hourly, you have these four fields: description, hours, unit price, total. If it is service-based, you have: description, quantity, unit price, total.

Now, if I just edit any of the items inside of the Invoice System — let’s open Invoice System. That’s Command UI, by the way, which I’m making use of to just speedily go from place to place without having to find where it is in the menu. All I need to do is double tap Shift, and then type in the title of whatever I’m searching for, and it just gives me a link to that place, and I go there very quickly.

It’s pretty simple. It’s a way to speed up your workflow.

And by the way, if you need the Command UI license in order to speed up your workflow, you can use the link in the description below. You’re gonna get 10% off, and I’m also gonna get a little bit of a commission. So thank you, and you are welcome.

Back here inside of our Invoice System — these are the CCT items that get created every single time we submit the form in the front end, right? And this just makes things very easy so that you can — if you want to come inside of the invoice — you can see all of the items here.

You can see the date, you can see the name and everything else. You can see the invoice details. If it’s service-based, you also have — if it’s hourly — you can see the items there. But in this case, it’s service-based, so that’s absolutely why it’s selected. You also have all of the items inside of here, and you can modify the invoice or you can even change the invoice status right here.

This way, it’s very easy for you to query this in the front end, show this in the table, and then have someone in the front end very easily view the invoice using the generated PDF right here in the front end if you want to. The generated PDF — once it’s generated — it’s also gonna add that link to that generated PDF right inside of this CCT down here.

You also take note that this is connected to “Clients,” and as you can see, Clients is a different CCT, which I just created, and I have the client’s details specified there. For example, if you edit this one client right here — this is a fictional client, by the way. Okay? If you edit this one client right here, you can take note that although this is a CCT, it’s also connected to invoices, right?

So you have the client, and you have the client’s email address, the phone number of the client, and you can see the currency in which the client pays, whether it’s USD or Euro, and you can see all of the invoices associated with this particular client. Okay? Now, if you’re wondering, why do we have a list of empty invoices inside of here?

It’s all because of the fact that I have deleted quite a number of invoice items inside of the Invoice System. So all of these are empty. I can go ahead and disconnect all of this right now if I absolutely want to, but that’s beside the point. Okay, so how did I connect this together? Very easily. I came inside of JetEngine, and I came to “Relations,” which you can very easily open up right there.

Now, the relation was between the Invoice System, which is a CCT — you find it right here. You can come inside of the relation right here, and you find “Relate Clients to Invoices.” I said that the client details, which you find inside of the CCT for client details — I said that should be related to the Invoice System.

So one client can have multiple invoices, which I specified with “one to many,” right? And then I registered control for the parent object and also for the child object. That way you see it here, and you also see it inside of here, right? That makes sense? Great. Now that’s it with the relations. Nothing too complicated on that front.

We can close this out, and this is absolutely fine. Now, this is pretty much about how I set up this CCT in the backend. If you need to just download the file and upload it to your own site based on these particular templates, which I have taken the time to set up inside of here, you can. If you want to download it, there’s a link to that in the description below too.

Okay. So we have all of this set up.

We need to come back first into the basic invoicing details, which is just a simple options page that I created. The only reason for creating this options page right here is because I really needed to have a place to store the latest invoice number.

This way, once we create a new invoice item, it fetches this one — the current one. It adds the number one to it and updates this value right here. So I always have a running value — the latest invoice number — which I can show inside of the form.

Does that make sense? Great. This is very simple. You can come inside of JetEngine and create a new options page just like this. What you have inside of the basic invoice details right here is my address, which I’m gonna show inside of the PDF, just as I showed you at the beginning of this video.

Remember that you have my address, you have my email, you have my website, and you have my phone number. If I want to add my invoice logo, I can also add it right here, but I manually added that inside of the invoice. You can make it dynamic too if you so desire. Okay, so this is it. This is basically it. What we have inside of “Pay To” profiles — I’m not willing to delve into that right now. I did not even implement that inside of the PDF, so it really doesn’t matter. Okay, this is what you need: the latest invoice number and these basic details right here. Okay?

Now we’re done with everything as it relates to what we set up in the backend right here. We can refer to this from time to time as I explain to you the build-out of the form.

But this is basically how things are set up right here. So let’s go inside of the JetForm Builder forms. Let’s just enter inside of “Forms” right here. Our aim now is to show just how we build this out. How did we set up the PDF so that it works this way? First, I want to show you how I set up this form. So let’s come inside the forms and edit that form.

We have this “Add New Invoice System Item.” I’m also gonna add that inside of the zip file that you find in the description below. If you want to pay for it, you can absolutely pay for it. But if you want to get it for free, it will absolutely be for free.

I’m gonna add a link to all of these files. I’m gonna export them and put them inside of a zip file that you can very easily use as a starting point. If you want to get it for free, just add the value zero there, and you’ll be able to download it for free.

Okay? So let me show you how I built out this form. It looks a bit complicated just looking at it right now, but I assure you it’s super simple. You’re gonna wonder why it looks so complicated. It’s very, very simple. The first field I have at the top right here is a simple hidden field, and what does it do?

It gets the current invoice number — and from where? From the same options page where we have that running value. Okay? Why is the manual input value here equal to nothing? As you can see, there is no value inside of here. It’s because of the fact that inside of “Value” on the right, you see “Default Value” on the right.

I have here set up the source from the options page to get the latest invoice number. That’s it. It retrieves the latest invoice number from the options page, and that’s it. The second field inside of here is a calculated field.

You can see right here that what it does is just fetch the current invoice number, which you get from this previous field. It fetches that current invoice number and adds the number one to it. So that way we’re able to increment the value of the invoice as we generate it. So we generate one invoice, we increment the value right here, and we update it with the incremented value.

The second aspect here is the invoice currency. This I got from a glossary — because why do I need a glossary? Because of course — in fact, I’m just gonna show you right now. Let’s open up JetEngine.

What it contains is inside of here you have the selected glossary of invoice currency. So let’s see that glossary. You come inside of glossaries right inside of JetEngine. You have invoice currency. Right here, you see you have two items.

You have USD, which the value is the value of USD. You have Euro, which is the value in Euro. And that way we’re able to return those values inside of the form right here, and also use it right here in the front end, wherever these values are needed, right? So very easily we can get the invoice currency there through the glossary — and specifically the invoice currency glossary item.

Then I have the client, which is very easily selectable. Here, you can see this is a dynamic list of all of my clients, and that is fetched from the CCT. And how did I do that? It’s very simple. Down here you can see you have “Field Options From,” and I selected “Generate Dynamically.” So this is a select field — a simple select field — and I’m filling the options by generating them dynamically.

Dynamically from where? From the list of a JetEngine query. And from what JetEngine query? The JetEngine query with the value — the ID of 6. You know what? Let’s go to the query builder. We just want to enter the query builder right here.

Let’s just open that inside of a new tab. I’m gonna show you this query, which has the ID of 6. You can see it right inside of here. You see, this is the ID of 6 right inside of here. All this query does is just fetch a list of the custom content type query of client details. So it fetches all of the list of the clients.

And if you toggle on “Preview Results” at the top here, you see that all of the clients are fetched — each of the clients. Here, you see the ID of the client, which I need as the value. So I added that here as ID. You can see all of it separated with a pipe character. Okay, so I added the ID, then I added another pipe character and added the display name.

What do I need to show in the front end right here? Do I need to show the client name? Then I need to enter inside of here “Client Name.” And where do I get that value from? Inside of Query Builder — right here you can see “Client Name.” So this returns the equivalent value inside of here. That make sense? Great.

So whenever I add a new client, this invoice form just simply populates the values of all of the clients right inside of here. Okay, great. That’s all done.

And as you can see, that’s pretty much all of what is inside of there. That’s the select field done. Now you have two other fields here. You have the invoice date, which is a simple date field. You can see it’s right inside of here — date field. Great. Now you see down here that I selected that this field should be populated as a timestamp.

Why is that? Because back here, if we come back inside of here, let’s take a look at the CCT for invoice dates. You can see also here this is saved as a timestamp. And why? In the front end, I can then manipulate the list of my CCT items based on these date fields.

I can say, okay, show me all of the invoices between this date and that date. And I’ll be able to very easily do that because of the fact that I stored this as a timestamp. I could also do some manipulations such as, “Hey, only show me invoices that are unpaid and are between one date and another.”

So this is the reason why I have “Timestamp” toggled on here, and I also have inside of the form that this is timestamped so that both fields can match. Alright?

Now because of that, it means that whatever value is taken from this date field is not going to be a regular date value. It’s gonna be a string of numbers that is not understandable to a user. A user can look at the string of numbers and think, what is that? So in order to make sure that this shows as a regular date, I have another hidden field just below it — right here.

It has a name: “Date as Regular Date.” It’s a simple hidden field, and I have here the manual input value as nothing. But if you look at the right-hand side inside of here, you see the default value is “Invoice Date.” Why? Because I’m getting that from inside of here. You can see “Invoice Date” right inside of here.

I’m getting that into this one. So inside of here, “Invoice Date,” you can see there are some quotes at the beginning and the end. And what does that do? It makes sure that whatever a person selects inside of this date — before it is converted to a timestamp as at the time of submitting this form — I need to be able to catch that date as a regular date field.

And that way I’m able to use whatever is inside of that date as a regular date inside of my invoice PDF — because that’s what’s gonna be returned to me right inside of here. So because I don’t want to return any timestamp value inside of there, I need a hidden field that will translate that to a regular date.

Does that make sense? Great.

Now that’s done. Inside of the work type, I can select whether it’s hourly or whether it is service-based. It’s a simple select field. You can come to “Manage Items.” I added them as manual items because we have just two items.

No need to create a glossary for this, right? It’s a simple two-item field. Also, it’s important because I’m gonna tell you about how I made use of this calculated field just in a bit. But just so you know, we have two items: hourly and service-based inside of here, and that’s totally fine. We can click on “Update” and close that out.

I’m gonna come back to that field just in a bit. Okay? Now down here, you also have the invoice name. It’s a simple text field. It’s as simple as that. Nothing too fancy there. The invoice status — it’s a simple select field, just a list of items you have inside of here: unpaid, you also have inside of here partly paid, and so on.

You can add all of the different statuses just as you see in the front end here inside of invoice status.

You can add all of them right there. Now, the important part. Okay, great. Now you have two fields right here. You have one big field. You can see inside of it, this is a simple conditional block. Inside of that conditional block, you have a repeater field, and you also have two other fields here, which are two calculated fields.

You have exactly the same thing inside of another conditional block right here, which has inside of it a repeater field and two other calculated fields. Now, the difference between these two conditional blocks is the fact that one is for hourly, which you can see here, and one is for service-based, which you can see here. Which means that this one just has quantity and unit price.

And down here, this one has hours and unit price. It’s the same reason why, when you select service-based right here, you have invoice details for service-based. If you select hourly, you have invoice details for hourly. Right? Now, come back here. Let me take a look at something else inside of the first one.

Let’s just talk about the first conditional block inside of here. See the conditional block, first of all, has the condition that the field “Work Type,” which is above — remember — that should be equal to service-based. So we’re saying, “If it’s equal to service-based, you need to show us this.”

Then we have another one that says, “If it is not equal to service-based,” which means if it’s equal to hourly, then you need to show this other group of fields. Okay? Now let me come back to this other conditional block right here. You can see you have — very simple — let’s open the structure here so you can take a look at it and understand it very well.

You have, inside of this condition block, a repeater field. The repeater field, which you see right here, has inside of it a calculation formula. You would, by default, not see a calculation formula because this will, by default, be set to “Default” here. So you wouldn’t see this. But you need to come inside of the block for that particular repeater field.

Come down here to where you have “Calculate Repeater Row Value” and select “Custom.” And this will basically do one thing and one thing alone — it just needs to understand what is the total value for each of the items inside of this repeater. We know that this is a repeater, so you can add as many items as you so desire, right?

It’s a repeater. That’s why it’s a repeater — because you can add an unlimited number of items. It is perfect for such invoices because you can add unlimited number of line items to such invoices. That’s correct. Great. Because we know this, we also know that we need a repeater right here, but we also need a way to be able to specify that each repeater item has a total value gotten as a result of something.

In this case, you have quantity multiplied by unit price, which will give us the total value right here, right? The total value right here, as you can see inside of here when you try to add items, is not really a regular field. It’s a calculated field — and a calculated field that cannot be edited. It only shows up as a result of “Hours multiplied by Unit Price,” for example.

Okay, so let’s come back here. We’re looking at quantity versus unit price right here, which means that we’re not looking at hourly — we’re looking at service-based. So if you come inside of here, you see quantity, you see unit price — same thing, same principle right there. Now, if you come inside of here, you take note that the quantity, which is the quantity field right here, will be multiplied by the unit price, which is inside of here, to give us what the total for this particular line of that repeater item should be.

So that’s why we have the calculation formula here saying “quantity multiplied by unit price.” We’re saying for every single line of this repeater item, this is how you get the total price for that item. So the total quantity will be quantity multiplied by unit price. It’ll then say — it knows — that when you add up two items, it’s gonna add the first item with the second.

If you have a third item, it’s gonna add the first to the second to the third. Based on this same formula, it’s gonna multiply this and then add it based on the number of items inside of that repeater. Does that make sense? Great. Because we know this, we’re pretty much done. Inside of the total, here is a simple calculated field that just says “quantity multiplied by unit price,” just as we have inside of here.

So we’re multiplying both together to get the total, which is essentially what the subtotal is. It is a total of all totals inside of this repeater. And down here — this is a macro for JetForm Builder that helps you return the invoice details (regular).

And where do you get that from? You get that from inside of here — “Invoice Details Regular.” You get that right inside of here. So what this does is basically one thing — it says, “Okay, once every single one of these repeater items has a value, we want to add all of those values together by virtue of this calculation formula.”

And then this one down here, which is just another calculated field — you can see it here — it’s a calculated field. It just says, “Okay, just get whatever is the total from that repeater field by virtue of this.” And you have inside of here, specifying what the name of that field is.

You can see that inside of here — that field key — you have it inside of here. Now, inside of the invoice total amount, it’s a bit different because this does not just take into consideration the total here. It has to remove what the total cost reduction is. For example, if you come to the front end here, you could have a deductible of, let’s say, 100.

Let’s say you have a discount here of 100, right? Inside of here you have maybe web development — let’s say quantity of five at a unit price of 500. Let’s say that’s 2,500. The subtotal inside of here is just everything — all of what we have inside of the invoice details right here, which is just one item right now.

So 2,500 is gonna then remove what the total discount is — 100 — to say, “Okay, now what we have as the actual total is 2,400,” because this is a reduction, right? So it means, therefore, that we need to calculate the total of all of the reductions that we have inside of here, and then remove that from this.

And it’s very simple. What we did here was just say, “Okay, whatever we have inside of here, which is the invoice subtotal amount…” I’m just gonna copy that, put that inside of the URL here so that you see the total field key — that’s it — “Invoice Subtotal Amount Service.” That’s what we have — exactly what we have inside of here.

We should subtract cost reduction. And where’s cost reduction? It’s right down here. This is cost reduction — right inside of here. Cost reduction is just all of the discounts or whatever that’s gonna be added to that invoice.

It’s a simple repeater field again that just has the calculated formula to say, “We just need the value of reduction” as the calculated formula. So each item inside of this repeater is just simply what the total value of reduction is — right here. Okay. So every single one of these items is just gonna add every one of them and give you the total right here based on this formula.

Now if you come back up here, based on this particular repeater field, which is cost reduction, I’m saying up here that the total amount we have inside of this subtotal right here — we want to subtract from that the cost reduction, which we have below. And that’s exactly what’s going on inside of here.

This is the reason why the subtotal has to remove from it whatever the discount is, or whatever other deductibles there are, and have a total invoice amount. I hope that explanation is not too complicated because that’s very essential to building out something like this.

Okay. If we come back up here, we take note that apart from this particular conditional block, we have another conditional block. Let’s close this out. We have another conditional block right here, and this one is pretty much a duplicate of the previous one. The only difference is that inside of here, we have “Hours” and we have “Unit Price.”

Again, we say hours multiplied by unit price will give us the total for each line item, right? Then we have the subtotal amount, which is just going to be the total of what we have inside of here, which is “Invoice Details Hourly.” We have that right inside of here as a subtotal. Then we have our subtotal, subtracting from it what the same cost reduction is.

This cost reduction is used by both of the same situations, because whether it’s service-based or whether it’s hourly, they’re both using the same cost reduction. It could be maybe a discount or whatever that’s added. Here, we have to add the name of the deduction, which is a text field, and the value of that deduction, which is just a simple number field right here.

Okay, so that’s it. That’s basically how we generated this inside of here. And we have just a few more fields to cover inside of here — not too complicated. We have a unique hash. The entire reason for doing this — I’m just gonna tell you — is that I needed a situation where when I generate an invoice, the invoice itself (the PDF file) does not actually have a discernible name. The name should be a string of indiscernible characters. And this is the reason why I have this unique hash right here.

So I have a simple hidden field that has a random string. As you can see, the random string is selected, and I can select the string length. In this case, I said it’s 21 characters, and it should all be numbers. And that’s what basically is going to be filled inside of this unique hash field right here in the backend, which is a simple text field.

It’s also going to be the title of that PDF. You can see it right here inside of here for the PDF. You see this is the unique hash right here, so that basically is what it is. Nothing too fancy. If you want it to be a discernible name, you can absolutely do that, but I have no particular reason for doing this. I just thought to do something nice.

Okay, so let’s come back inside of the form right here and talk about these last two or three fields.

We’re almost done with how this form was set up. Inside of here, we have a formula that looks complicated. It’s absolutely not complicated. Don’t be scared by what it looks like right there.

What you have here is simply a field that says: If you see the “Work Type” field, that is equal to the value of zero, then you need to return what you have inside of the total amount for the hourly invoice.

If it is not equal to zero, then you need to return the amount instead for service, if the value of work type is not equal to zero.

Let’s scroll back up and let me show you what I talked about at the beginning of demonstrating this form to you. Inside of the work type right here, you can see inside of “Manage Items” that you have “Hourly” set to the calculated value of zero. You have “Service-based” set to the calculated value of one. So meaning that once you select hourly, this form knows that the calculated value of work type must be equal to zero.

And that means we need to return the subtotal amount, which is hourly, which you find right inside of here. So hourly subtotal — right here. So you have this right inside of here. Let’s just copy — you have it right inside of here: subtotal hourly, right inside of here. This is where you have it, right inside of that.

So it says: If this is equal to zero, then return the hourly subtotal. If it is not equal to zero — which is what this question mark does — it says it’s either this or, by saying “or,” this is what the colon is right there. And it says this. So if it’s not equal to zero, return the service subtotal instead.

That’s what it is, right? It’s just a conditional calculation. Okay?

You can understand more about this if you just click on this in-depth guide. You can open this in a new tab and take a look at all of the different examples that are shown. It’s nothing too fancy. Nothing too complicated. Again, I’m going to include a link to this file, so you can take a look at it, download it, bring it inside of your own installation, and then understand how each of these things work.

I’m going to give you the template. It’s entirely for free — or for a fee, if you so decide. Okay? The same thing is done inside of invoice total. You have here “Work Type.” When it’s equal to zero, I’m saying, “Get me the total hourly invoice,” which you find inside of here. The total amount hourly — right here.

This is the service total, and this one is the hourly total. So I’m saying: Get this invoice total hourly if the work type is equal to hourly, which is zero, right? So it’s going to give us that. If it’s not equal to that, then give me the invoice amount, which is the service invoice amount.

You can find that inside of — right here — invoice total amount. You can see this is called invoice amount. So instead I’m saying: If that is not equal to zero, then give me invoice amount. Pretty much, that’s it. Then I have one more field inside of here.

This one, if you come inside of the block to the right, you see this one is hidden. And what it does is just give me a total of the cost reductions. Cost reductions — where do I get that from? This is a simple calculated field. You scroll up, you’re going to see that this repeater field is called “Cost Reductions.”

So what I’m doing is just returning that as a hidden field — which is a calculated field, but it’s hidden inside of here — to get the cost reductions. And that’s simply it. If you want to return any form field inside of a calculated field, you can very easily use this right here and just return any of the form fields.

But the point is that right now I am able to return my cost reduction as a hidden field and be able to use this right inside my invoice. If you come inside of the invoice, that’s what I have inside of here. That’s exactly where I fetched it to.

Now I’m going to show you how I built out the PDF itself — the template for the PDF — so that every single one of these fields are mapped correctly the way they’re supposed to be mapped.

Let me show you that. It’s pretty simple. I’m going to come inside of “Templates” inside of JetForm Builder. You come inside of “Templates.”

If you’re wondering where “Templates” comes from — just in case you don’t have that inside of your own JetForm Builder setup — it’s pretty simple. That comes as part of the add-on for PDF Generation, which you find inside of the plugins that I showed you at the beginning of this video.

So inside of “Templates” here, you create a new template. We can add a new template very easily. Let me edit the template I have already added. Now, when you add a new template, you can select the form on the right-hand side, right here. Don’t be scared by what you have showing inside of here. Don’t worry — I’m going to break every single aspect of this down for you.

It’s pretty, pretty, pretty simple. Okay, so inside of “Select Form” right here, you have this “Add New Invoice System,” which is the JetForm Builder form associated with this particular PDF. You don’t have to select it here, but the advantage of selecting that form inside of here is that you now have the option — this option — whenever you click on any field block, any block inside of here, you have the option to select the JetForm Builder macros.

And that is gotten only as a result of having to select the equivalent form. So it just returns a list of all of the form fields, and you can begin to return each of them inside of here. Now let me open the structure panel, show you how I built this thing out. It’s pretty simple. First, at the top here, I have three columns.

A simple column block. You can add, of course, a column block by just simply typing in “Column” right here. When you type in “Column,” you can add a simple three-column block right there.

So I have a three-column block. Inside of the first column here, I just have a simple heading, which says “Invoice.” I have inside of here a simple image, which is a simple logo of my business right here.

Then down here, I have invoice number and invoice date. Now inside of invoice number — how did I get this? It’s pretty simple. I’m getting that from my form. If you come inside of my form right here, you see I have invoice number right here called “Invoice Number” — right inside of here.

I can also put invoice number right inside of here. The only difference is that I have a percentage symbol at the beginning and at the end of it. Each field that you need, just select that field, grab the field key right here, and just put a percentage at the front and at the back of that field name.

And that’s it. Just add a percentage symbol at the beginning and at the end of that field key, and voila — you have the placeholder that’s going to fetch that particular value from the form. The same thing applies to the invoice date. Because I don’t want to get this particular invoice date right here — this one, which is going to save as timestamp —

I don’t want to show a random string. Instead, I have this “Date as Regular Date,” which I explained previously, and I’m going to use that instead. So “Date as Regular Date” is what I have brought inside of here. You can, as I explained before, just select here and just come inside of here. Select “Date as Regular Date,” or, as is the case for the previous one, which is “Invoice Number.”

I can very easily select “Invoice Number” here. And as you can see, it just brings in that macro right there.

I left the middle column empty. Nothing should be there. And then on the right-hand side — the right column right here — I added a simple text, which just shows my name. If you want to add your business name, this is where you add it. And then you have a bunch of items right inside of here. Each of these items are dynamic values — and you can tell by this dynamic content value icon inside of here.

If you click on it, you can see where the content is fetched from. You can see this one is a custom value, gotten from the option value of “My Address.” Do you remember when I showed you the options page? I got my address right there from that options page — right inside of there. If you select the second one, you see that inside of there is “Content.”

And this gets my email. Down inside of this one — this one gets, inside of here, the same custom data, which is from the options page of my website. And finally, I have the last one here, which gets from the options value of my phone number. And just like that, you have all of the four items that are supposed to show right at the top of your invoice.

What do I have inside of here as “hr”? It’s just something to signify this line which you see inside of here in HTML. Once you add “hr,” which you find inside of here, it translates it — when you are creating the PDF — into a simple line.

A line that spans from end to end. Okay? So I added an HTML block — a custom HTML block — and just added “hr” just like that. Okay? And that solves it. Now, these parts right here — these two parts right here — may look pretty complicated because you may be wondering, how do you come up with all of this code?

Well, I didn’t come up with all of this code. It’s something that you can generate yourself, and I’m going to show you just how to do that, okay? You need to pay very, very rapt attention because you need to understand how these things work in order to be able to replicate it in your own situation.

Okay? So for now, let’s just copy what you have right here: repeater content, invoice details hourly. Let’s just copy that. Let’s also copy inside of here: repeater content. Don’t worry right now about what they mean — I’m going to tell you about that in a bit. Okay, so first of all, what this is is just what generates the simple table you find inside of here.

You have it in two places here because of the fact that you could either have service-based, which will have inside of it things like quantity — which you can find inside of here — or you can have things like hours, which you have inside of this other table — hours, which will have hours and unit price for such hours.

I couldn’t manage to do both of these scenarios inside of one single table, so I instead created two different tables. So you can find inside of here — you can see this is WP Block Table — which means that it is a native tool that you can create yourself right inside of here.

You don’t need to understand how to code HTML to do this. I’m going to show you how to do it. But you notice that you have, containing each one, a conditional block. You have a conditional block here and a conditional block here. So I’m pretty much saying that for the first conditional block right here, you should only show this inside of your PDF if the work type is equal to “Hourly,” which you can find right inside of here.

So I’m saying: If the work type is equal to “Hourly,” then show this one — which is going to contain the hours — which you can find right inside of here. You don’t have to understand the code. Again, just look at what I’m showing you. This is “Hours.” This is “Hours” right inside of here.

You know that this table is for generating things as it relates to hours. When it’s hours, then we are making use of this one table. That’s why the conditional block right here explicitly states that it should be the work type of “Hourly.”

Right? The second conditional block says that it should be if the work type is equal to “Service-based.” Okay? So we’re saying: Show that if the work type is equal to service-based. And that is very easily discernible from here — you have either hourly or service-based. Great. Now you can come back inside of here.

Let’s close this out, and let me show you how I generated this. We’re going to come down to this one in a bit, but first, let me show you how I generated this part. You can see the same “hr” here, which gave us the line at the top, is the same “hr” here, which gives us the line below — which you can see right here and right here.

Right now, all that’s left is to talk about the table inside of here and also this table right inside of here. So let’s talk about both of these situations. First, you need to understand that because of the fact that we have a repeater field inside of our form — let’s come back to our form — because of the fact that we have a repeater field right here, we cannot directly access all of these values.

We cannot do that. And why can we not directly access all of these values? Because when you create a repeater, how many items should be inside the repeater? What do you think? How many items should be inside? How many should be the maximum number of items?

How many should be the minimum? We simply have no answers to these questions. And why is that? Because that’s how a repeater was meant to work. A repeater was meant to help you add an unlimited number of items inside of it. So you just keep on clicking on “Add Item” and “Add Item” and “Add Item.”

And if I want to click this 500 times, I’m going to have 500 repeater items inside of here. It means, therefore, that this table right inside of our PDF — right here — needs to also be dynamic. We cannot map things directly as we map them when we had these items right here. We cannot access and begin to map each of the repeater items.

You cannot even find it inside of the list — inside of here. And why? Because of the fact that each of those repeater items constitutes what is known as an array. An array is just a list of items, right? An unlimited list — a list that you cannot specifically quantify right now. Because of that fact — because we know that we have an unlimited number of items — we need a way to break out that repeater item’s array.

We need a way to really break it out and then look inside of the list of items it returns and access those values one by one. And how do we do that? We need to do something that returns those repeater items as though they were listing items inside of the front end — or inside of our PDF, as is the case right here.

So let me show you how we can do that. We first need to make use of some code that I’m going to add a link for in the description. Don’t worry — you’re going to find the code. It’s created by the official Crocoblock team. So you don’t have to be bothered about how legit the code is. It is. It’s proven. It works.

Okay. Let’s just come to where we have the snippets. Let’s open that up.

I’m not going to explain to you how every single part of this code handles the repeater. It really doesn’t matter. You can just copy and paste and make use of it. But the point is, once you’ve copied, you’ve pasted, and you’ve published the snippet — and the snippet is very much activated by toggling this on right here —

You can ensure that it’s published. You’re good to go. Now, the part that’s important is making use of that inside of your PDF — or in the front end. You can also work in the front end if you so desire. But inside of your PDF, let me show you how you can make use of it. Let’s just add inside of here a simple, you know, new block. Let’s add a new block right inside of here. The essence of adding a new block here is just to show you very simply how this code right inside of here was generated. It’s pretty simple.

So let’s add a new block. We’re going to add a table. Let’s just drop that table inside of there. How many columns and how many rows should this table have?

We know that it should have four columns. And why? Because it’s going to have things like the description, it’s going to have hours, it’s going to have unit price, and it’s going to have the total. So we know it’s four items. Let’s leave the row count as two and create the table.

And we can — if we want to — toggle on the header section so we can add all of the four items right inside of here. We can add: Description, Hours, Unit Price, Total.

Let’s just add that right there. This is the same table that we generated earlier. It’s the same one you find here and the same one you find here. And now we need to convert this into an HTML table that we can manipulate.

So what do we do? We just click on the options for this block — right here — and we select “Edit as HTML.” Once you select “Edit as HTML,” you have this block returned to you as HTML. Now what you want to do is grab everything from the beginning to the end. So grab the whole thing — copy — and then I’m going to paste it inside of an editor.

Let me open up Sublime Text and paste it there. Great. So we have that right here. I’m going to do some formatting to clean it up so that we can very easily read this. Now let’s clean this up just a little bit. The idea is we want to add inside of here some code that will make this table loop through all of the repeater items.

And this is how you do it. Just look at this table. First, we want to duplicate the second row. We want to duplicate this second row because this is going to be the one that loops. This is the one that repeats. The first row — we don’t want to loop this. The first row should remain constant. It should just be there like a header. But the second one — we want that to loop.

So I’m going to just copy the second one, duplicate it inside of here, and now we want to wrap that in some code. And this is the code: {% set rows = jf.repeater('invoice_details_hourly') %} {% for row in rows %}.

That’s how you start it. Then at the end of that row, you end it by saying {% endfor %}. That’s all. So we’re saying: For every single row that we find inside of “invoice_details_hourly,” which is the name of the repeater, we want to loop this table row. Now we want to populate the values dynamically. For example, instead of saying “Hours” or instead of writing “Web Development” here or whatever, we want to return the dynamic value that corresponds to that field.

So inside of the first column — which is the description — we’re going to add {{ row.description }}. That is going to return the value for description inside of the repeater. For hours, we write {{ row.hours }}. For unit price, we write {{ row.unit_price }}. And for total, we write {{ row.total }}.

That’s it. That’s all you have to do. Now once this is done, you copy this entire block, come back into your PDF template, and paste it inside of a custom HTML block. That’s what I did. You can see this is a custom HTML block.

If I click inside of it, everything we just wrote is exactly what’s here. This is the opening of the loop. This is the duplicated table row. And inside of each td, you have a dynamic field from the row: description, hours, unit price, total. And this is the endfor at the end to close the loop.

That’s it. Once you save your template and regenerate the PDF, it’s going to automatically loop through every repeater item and show each row inside the PDF table. And you can do the same thing for the other repeater, which is for service-based.

Just change the repeater name in this part of the code — instead of 'invoice_details_hourly', you’ll use 'invoice_details_regular', and update the field keys accordingly: description, quantity, unit_price, and total. Everything else remains the same.

And that’s how I was able to create both dynamic tables — one for hourly, one for service-based — and show them conditionally based on the work type selected.

If all of this looks like a lot, don’t worry. I’ve added this exact code inside of the files that are linked below this video. Just download the zip file, open the HTML, and copy it into your own custom HTML block.

Then all you need to do is change the field keys if your own repeater has a different name. You can also rename the table headers, but the structure stays the same. This setup works reliably, and it’s how I’ve been generating my dynamic invoice tables ever since.

Okay, so let’s now talk about this part of the invoice — the totals section. You’ll notice this is a table that has just two rows.

The first row is for subtotal. The second row is for total. That’s it. Very simple. So what did I do?

Same thing — I created a regular table block inside the editor. I added two rows and two columns. First column has the label — “Subtotal” and “Total.” Second column has the dynamic values. These dynamic values are just the form fields we already set up.

For the subtotal, I return this: %invoice_subtotal_amount_hourly%. This is the subtotal value for hourly work. But remember, we also have a service-based version. That one is %invoice_subtotal_amount_service%.

So what do we do? We use conditional logic again. If the work type is hourly, show the hourly subtotal. If it’s service-based, show the service subtotal.

That’s why this whole section is inside a conditional block. So we have two versions of this totals section — one that’s shown if the work type is hourly, and one that’s shown if it’s service-based.

The same applies to the total row. We’re returning either %invoice_total_hourly% or %invoice_amount%, depending on the work type. That’s why we have two blocks, and each one is wrapped in a conditional logic block.

You can use the JetForm macros to insert the values directly. Or you can hard-code the placeholders using %field_key% if you know what you’re doing.

Also, make sure to add the appropriate formatting. If you want these to be shown as currency, you can either apply styles inside the template, or you can format them before saving the form data. In my case, I let the form store raw values and then format them in the template.

Finally, let’s talk about this section — the cost reductions. Remember, we saved all deductions (like discounts) inside a repeater field. I also showed you earlier how we looped through a repeater field in the PDF.

This one is no different. We use the same method. Get the repeater field. Loop through it. Show each deduction name and its value.

This one also uses a table. Two columns: one for name, one for amount. I created the structure just like before — added a table block, converted it to HTML, duplicated the row, and wrapped the repeating part in a {% for %} loop.

So we loop through each row in the cost reductions repeater and output:

  • {{ row.deduction_name }}
  • {{ row.value }}

This displays every discount item on its own row.

Same method. Same approach. Nothing different.

Once this was all set up, I saved the template. And now every time I fill out the invoice form and submit it, the PDF is automatically generated with all the correct dynamic content — the client details, invoice items, totals, discounts, and everything in between.

That’s it. That’s how I built the entire invoice system.

Let me know if this was helpful. And if you’d like the export files, check the link in the description — you can download the zip, open the templates, and make them your own.

I’ll see you in the next one.

Code snippet for using repeater fields in PDF templates.

PHP
/*
for example:
%repeater_content(repeater1|br)%
{item_count}: %text1%
%repeater_content%
or
%repeater_content(repeater1|nl)%
or
%repeater_content(repeater1|, )%
etc.
for the first line
you can also use it in Hidden fields (turn Render in HTML off), putting that in Manual input with the prefix mailmacros::
*/

class JFB_Readable_Repeater {
	
	public function __construct() {
		add_filter( 'jet-form-builder/rich-content', array( $this, 'convert' ), 0 );
		add_action( 'jet-form-builder/form-handler/before-send', array( $this, 'do_macros_in_fields' ) );
	}
	
	public function is_new() {
		return function_exists( 'jet_fb_context' );
	}
	
	public function get_form_data() {
		
		if ( $this->is_new() ) {
			$form_data = jet_fb_context()->resolve_request();	
		} else {
			$form_data = jet_fb_action_handler()->request_data;
		}
		
		return $form_data;
		
	}
	
	public function do_macros_in_fields( $form_handler ) {
		
		$form_data = $this->get_form_data();

		foreach ( $form_data as $key => $value ) {
			
			if ( ! is_scalar( $value ) || false === strpos( $value, 'mailmacros::' ) ) {
				continue;
			}
			
			$value = str_replace( 'mailmacros::', '', $value );
		
			if ( $this->is_new() ) {
				jet_fb_context()->update_request( $this->convert( $value ), $key );
			} else {
				$action_handler->request_data[ $key ] = $this->convert( $value );
			}
			
		}

	}
	
	public function convert( $content ) {
	
		$pattern = '/%repeater_content\((.+?)\)%(.+?)%repeater_content%/ms';

		$form_data = $this->get_form_data();
		
		if ( class_exists( '\Jet_Form_Builder\Classes\Macros_Parser' ) ) {
			$parser = new \Jet_Form_Builder\Classes\Macros_Parser();	
		} else {
			$parser = new \JFB_Modules\Rich_Content\Macros_Parser();
		}
		
		$content = preg_replace_callback( $pattern, function( $matches ) use ( $form_data, $parser ) {

			$params = explode( '|', $matches[1] );

			$repeater_key = $params[0];
			$delimiter    = $params[1] ?? ', ';

			$delimiter = str_replace( "br", "<br>", $delimiter );
			$delimiter = str_replace( "nl", "\n", $delimiter );
            $delimiter = str_replace( "tr", "</tr><tr>", $delimiter );

			$repeater_value = $form_data[ $repeater_key ];

			if ( empty( $repeater_value ) || ! is_array( $repeater_value ) ) {
				return '';
			}

			$template = $matches[2];

			$repeater_content = array();

			$count = 1;

			foreach ( $repeater_value as $item ) {
				$item_content = str_replace( '{item_count}', $count, $template );
				$repeater_content[] = trim( $parser->parse_macros( $item_content, $item ), "\n\r" );
				$count++;
			}

			return implode( $delimiter, $repeater_content );

		}, $content );

		return $content;

	}
	
}

new JFB_Readable_Repeater();
From Tobi Salami, with Love

Written by Tobi Salami

Web Developer

Tobi Salami is a web developer with a passion for dynamic web applications built on WordPress.

Message Me