Tracking Bills and Receipts with WordPress

Tracking Bills and Receipts with WordPress

I like to use WordPress to create little spaces to organize whatever it is I’m doing in my life at the moment, not just to create websites for clients.

Earlier in the year I looked at how to customize WordPress create a workout diary and recently walked you through creating a travel blog. Today, I’ll want to show you how I’ve started to organize my important documents, like receipts, warranties, insurance papers and other important documents.

Let’s get started.

The Goal

Documents are a bit more difficult to handle, especially if they contain important information because they exist outside your server. In many cases, the real document will be more important than the scanned one.

In my case, I devised a four-part system:

  1. I used WordPress to make my documents easily searchable, creating a diary-like solution
  2. I used Google Drive (as well) to store the raw documents, using a different organizational structure
  3. I used simple folders to store the original documents
  4. I used Wunderlist to keep track of any tasks I needed to do in relation to documents

Getting Started

To get started, you’ll need a WordPress installation, naturally. I recommend using a localhost environment rather than a live site. Take a look at how to set up WordPress locally if you don’t already have one running.

You can also use a WordPress installation running on a remote server, but I recommend using a test site or a private space, just in case.

I used two plugins to make my life easier: Advanced Custom Fields and WP Offload S3. The later is not required, I just tend to store my files in my cloud so I can easily move my projects from a local installation to a remote one.

If you need more information about using Amazon S3 with your WordPress website take a look at our moving WordPress media to the cloud article. By the end of the process all your newly uploaded files will automatically be transferred to your S3 servers.

Finally, we’ll be creating a plugin to modify our theme to display our custom data. To get this done, create a folder in your plugins folder and name it document-diary. Within that folder create a file named document-diary.php and paste the following inside:

You can now go to the plugins in WordPress and activate it. We’ll add a little bit of code to this file further down.

Planning Ahead

For all these projects, the majority of the work is done in the planning phase. How do you want to organize the data? What do you want to show? Are there any potential issues? Have you covered all of the angles?

Documents come in all shapes and sizes and types so I tried making sense of what I had. I ended up with few thoughts:

  • I have a few types of documents like, including:
    • Receipts
    • Warranties
    • Personal Documents
    • Extra-important things like car insurance documents and such
  • I want to keep track of where I got these documents from and any bits and pieces of information that would help me remember what it the importance of the documents about a year or two from now
  • The document itself can range from a multi-page PDF to a single JPG image
  • Each document has three important bits of information I want to see at a glance:
    • Document ID
    • Date issued
    • Validity date
  • I want to use my WordPress installation as reference, not necessarily the way to access the documents themselves

Those were some of the ideas I headed into all this with. One of my main goals was also to make this as painless as possible. This was one of the reasons why I wanted the option of an image input. I don’t want to start scanning things and in most cases I’ll just snap a picture with my phone and be done with it.

Why Google Docs and WordPress

At this stage, you may ask: why do we need both Google Drive and WordPress for this? I arrived at this method through personal experience. It may be overkill for some, but it’s the system that works for me. My reasoning has two parts: long-term storage and how my need to reference these documents arises.

The two systems serve very different purposes. Google Drive allows to separate my documents into logical groupings. For example, I created a folder for my car-related documents, which includes some sub-folders: core documents, gas receipts, permits, service documents, other. This is great for finding all my parking tickets or all compulsory service invoices.

What it doesn’t provide is context. I have a terrible memory and I don’t know why I have so many documents in the first place, I can’t recall the events that generated these documents. For example, I’m about to take my new guitar to be fixed under warranty because some of the frets have moved about a tiny bit. A year from now I’ll have the invoice and the warranty card, but no idea of the work that was done.

If you think about something like a car, which can be re-sold for a nice little sum, this becomes a lot more important. By documenting everything that has happened and being able to provide a paper trail, you can ask for a higher price. This diary-like structure also helps you recall important details in warranty or insurance disputes.

Above all, it gives me peace of mind. Instead of having to remember things I can just write down what happened quickly and forget about it until I need it.

Data Storage Needs

Once I know what I want to do (roughly), I try to map out how it all translates to the custom data we’ll need to save. I figure out if any custom posts and taxonomies are needed, set them up, then add options to them using ACF. In this case, we can use regular posts to hold our documents (each post being one document) and regular categories and tags for categorization.

In the end, I added three field groups. One field group is a simple map that allows me to specify where I got hold of a specific document, it is attached to posts. Another field group contains the options for the document ID, issue data, and validity, it is also attached to posts. The third field group is attached to media items and contains two options: page and Google Drive link.

The idea each document (post) will have attachments – the actual document image. The document will also be uploaded to Google Drive and I’ll provide the link in the attachment options. The page field is needed to separate different pages of a document. If I’m lucky enough to have a multi-page PDF I’ll upload that but if I take separate photos of each page I’ll need this field.

Creating Options

You can add options in two ways. You can either create them in the admin or add them programmatically within your PHP code.

Let’s include the options in our plugin since it will make the admin cleaner. In the document-diary.php file we created earlier add the following:

Then go ahead and create that acf-options.php file and paste the following into it. This is a direct export of the options I created initially in my local installation.

Once done you should see additional options in the post screen and media form. Take a look at the screenshots below.

Post options for our documents.
Post options for our documents.
Attachment options for our documents
Attachment options for our documents


When I want to add a document, I first upload all related files to the logical place in Google Drive. I then create a post in WordPress and name it something like “Guitar Warranty.” This document has two parts: a receipt and the actual warranty document, which I uploaded to Google Drive already.

I then upload the images to WordPress as well. Remember, it is mirrored to my Amazon S3 server automatically. This allows me to move my site online and provides a backup at the same time. I always upload the images from the Add Media screen in the post, which will attach the uploads to the post in question.

I then fill out the page and Google Drive link custom field for each uploaded file. I also fill out the fields for the document itself (ID, created date and validity date).

I save the post and figure out if I’ll need to do something with it in the future. An example of this is my local parking permit, which allows me to park in the neighborhood for free. It expires in January, so I go into Wunderlist and add a to do for myself.

Renewal task in Wunderlist
Renewal task in Wunderlist

Displaying Information

I’ll use the the_content() hook to append any additional data to the content of the post. This allows us to use the content area to jot down notes and see all the important info below it. The code below displays the attachments for a post and the basic data we’ve entered.

We start by pulling in all attachments for a post using get_posts(). If there are attachments we show them as a simple list that displays the page number and a link to the local file and the Google Drive version.

Next I pull in our three fields into an array and filter out any empty elements. After making sure we’re not left with an empty array I create the heading and the table that displays the data by looping through the array. Finally, we prepend all that to the original content of the post and return it.

To display the map, we’ll need to do a bit more work, starting with the inclusion of the Google Maps API using the code below.

Don’t forget to go to the Google Developer Console to create an API key for yourself and enable the Maps Javascript API.

Once done you can display the map by adding a bit of code within the diary_documents() function from above. I added the following code just before we return the content.

All this code does is create an element to hold the map (using a unique ID) and some Javascript which we pass the coordinates to. The Javascript API takes care of the rest.

At the end of the day you should end up with something like the screenshot below:

Final view of our document
Final view of our document

Advanced Functionality

Once you’ve pinned down the basics you can play pimp my app and do all sorts of cool customizations. Want to track how much fuel you’re buying to try and cut back on your usage? No problem!

Options recording our gas usage
Options recording our gas usage

I’ve assigned these options to only posts that have the tag “Gas.” You can use the following code to add the options for yourself, just make sure to create the tag “Gas,” or modify the code to use the tag of your choice.

What I’d love to do is create a page template, but this seems difficult from a template. Fear not, Tom McFarlin has the solution – as he always does! – a class that adds templates from plugins.

To get started with it, download the class-page-template-example.php from the link above and place it in your plugin. Rename the file to Page_Template_Plugin.class.php. Now, go into document-diary.php and include and initialize the class using the following code:

Now create a templates directory and add a file named gas-usage.php. Copy the following code into it, this is basically a template file containing the code of a single page from Twenty Fifteen.

I started by adding the header and footer bit, then copy-pasting the code from page.php within the Twenty Fifteen theme. The file contained a call to the content-page.php file, I replaced that with the actual code from the file, arriving at the final structure. I deleted the post pagination links and arrived at the final structure, we’ll add our custom code right after the the_content() function.

First, we pull in all posts that have the tag “Gas” and then loop through these posts and fetch the custom field data we saved and display it in a table. I’ve kept a running total throughout so at the end I’ve added a row that displays the totals.

Gas Usage Table
Gas Usage Table

If you spend a bit more time you can use a jQuery library to graph all that, calculate how much more fuel you’ve spent from one month to the other and do all sorts of other great things.

Wrapping Up

We’ve created a nice little customizable document tracking solution for ourselves and learned all about ACF, plugins and creating templates from plugins along the way. I hope you can take this example and make it your own to create something useful!

If you have any suggestions on how this example could be more useful or what you would add to customize it to your own needs let me know in the comments below.