I Made A Tax Calculator

Published: | Updated:

The other day, I got bored and decided to make a tax calculator from the Zimbabwean PAYE Tax Tables. Right now, the Zimbabwe PAYE tax tables are only availible as a set of PDFs on the the tax authority’s (Zimra) website. Much like the better election results project I’m working on, I hope to improve how the data is shown.

What I Did (and Didn’t) Include

Zimbabwe has been through a lot in 2019 affecting tax. Biggest change is that there are separate tax rates for salaries paid in local currency (ZWL$/RTGS$) and US$. That came after another change stating that taxes in local currency are paid based on the US$ tax rates according to an exchange rate.

To that end, I decided just to use the ZWL$ Tax Tables for 2019, saving the US$ table for another update. There are also a range of exemptions and deductions which complicate things further. I’ll also save that for another update.

Transforming the PDF Data Into Code

Unlike my Better Election Results project, there was way less data to copy into a CSV. I initially copied all the data as it is before I realized that it would be better to represent the tier amounts in dollars instead of cents. In doing so, I hope to limit errors that come through due to floating point numbers. It was a late change so I had to change everything to cents somehow.

Two adjacent tables with tax liability at different tax brackets and periods

The table on the right showing the dollar amounts and percentages as they compared to the table on the left where all the values are in their base value (i.e. cents)

The next think to do is to find a way to represent the this data in JavaScript. Along with this project, I’ve been working on another project where I’ve had a similar problem. Since browsers don’t support JS imports, I had to find a way to tie the models to the logic1.

The only way of doing this (I think) is to programmatically generate the JavaScript models from the CSV file and output the data to another file. Getting the CSV file into JavaScript wasn’t that hard thanks to the csv-parser library (though I miss how Python has it built in). The hard part was outputting it into another file. I tried using JSON.stringify, but that didn’t work. I tried detecting each occurrence of Infinity (which came as a string) and turning it into the number but that didn’t help. I also found out that you can’t parseInt(Infinity)–you’ll get NaN.

Before I decided to manually print out all the data, I found the util.inspect function which prints out an object as it’s defined. Some tinkering later, I got it to print data to a file. I just copied the resulting data into another file so I can work on it and I hope to set things up properly later.

As for how to manage multiple version of the tax tables, I decided on the format Rates.<currency>.<table_date>.<period> where period contains another object with keys representing the lower tax bracket and the objects containing the tax rate at that level along with the applicable deduction. Hard to explain so I’ll show it instead.

 * ZWL is the <currency>
 * '2019_12_02' is the <table_date>
 * daily is the period
const Rates = {
    ZWL: {
        '2019_12_02': {
            daily: { '0': { rate: 0, deduct: 0 },
           '1152': { rate: 20, deduct: 230 },
           '4933': { rate: 25, deduct: 477 },
           '16439': { rate: 30, deduct: 1299 },
           '32878': { rate: 35, deduct: 2942 },
           '49316': { rate: 40, deduct: 5408 },
           '65754': { rate: 45, deduct: 8696 } },

I did this is so that I will be easier to compute since I can start from the highest tax bracket’s minimum amount and loop down to the lowest. Again, another diagram which explains this better than I have.

A diagram which starts with the input amount and period with arrows pointing down through various tax brackets until the last once pointinh to the final calculation

The daily income flows down to the various objects until it reaches a point where it’s bigger, then it calculates the tax liability.

Bunch o’ Magic Code

Now I’ve got the data in the client JavaScript, I coded up the form and made the code to calculate the tax rate. There are just 2 interesting parts to this. The first one is the hidden output I have which will display the results when someone submits the form. The relevant details are in strong tags which I update as the user calculates their taxes. I hide it when the page loads and show it once the user submits the form.

<output id="result" class="vanish">
    <p>Over <span id="period"></span> period, you are in the <strong id="marginal_rate"></strong> with an effective tax rate of <strong id="effective_rate"></strong> which includes the <strong id="aids_levy"></strong> AIDS Levy.</p>
    <p>You are liable for <strong id="liability"></strong> in taxes, leaving you with a take-home pay of <strong id="take_home_pay"></strong>.</p>

The second interesting part is the the code to calculate the tax liability itself. I was in a rush so it was a big mess, but it works, I think?

Why It Sucks

While I was able to get the page live, it sucks.

An unstyled webpage with a lot of text and some form controls

The calculator in an unstyles webpage

Well, I don’t think it sucks, it just needs some work. But “Why It Sucks” sounds more dramatic so lets go with that.

For one, I haven’t actualy tested the code. I know it’s a guideline, but I did say I tried to make it accurate so I might as well do that.

Also, the code is a mess. Given the way the project is structured, it’s a nightmare to put together so I hope to make some build scripts to ease the process. As for the code itself, it’s so tightly coupled I could make spaghetti bolognaise out of it. The big thing though is that I haven’t formatted the numbers properly.

Looking right at the project itself the webpage is ugly. At least it’s responsive!

Also, I’m hoping to set the project up so that other people can contribute to it. Not just in terms of code, but with verifying information and translating it into other languages.

Thanks for reading! Try it out or check out the project on GitHub!.

  1. Just as I was proof reading this I discovered that modern browsers do support ES 6 imports. It might not be that useful and I’ll explain why another time. ↩︎