HomeThe Old OnesZimbabwe PAYE Calculator The First Time

Mistakes Were Made (and Fixed-ish)

Published:

Last time, I got the Zimbabwe Tax Calculator Live. Unfortunatley, it didn’t work properly. The biggest problem I noticed was that it was wrong. Very wrong. As in, you wont get taxed if you’re at a high tax bracket. In fact, it’s wrong as in the tax authority will actually give you money.

Two forms of the wrong computation. On the left shows a zero result and on the right shows a zero result and on the right shows a negative liability.

At this point, I realized the importance of modular, testable code. So I spent a lot of time setting up tests using Jest, making most of my code modular and restructuring the project so it’s easier to work with.

From that version, I fixed the data table and made the functions which would help with calculating the tax liability. Also set up a few tests for them. I also shifted from using prose to display the answers into using a table which is much easier to read. I even made a nice VS Code snippet to help me make all the boilerplate.

I then modularize some code. To minimize the work I had to do between the development and deployment of the scripts, I added some checks to ensure the code would run in only node or the browser. If require isn’t undefined, it’s on node and I can import the libaries into it. If document isn’t undefined, it’s in the browser and I can put the browser specific code in it. Hey! It’s nasty, sure, but it works 🤷🏾‍♂️ (for now). That way, instead of copying the models generated by make-rates.js, I would print it into a file.

After that, I looked into getting a linter, ESLint to be exact. Remember how I said “but hey, it works! (for now)”? At this point I had a lot of errors to fix. More sytle recommendations than errors, but VS Code makes them look like errors so I can’t ignore them. I could but then what’s the point of linting to begin with?

At this point, I looked into trying to make the code responsible for generating the rates files neater. I tried to put it all in an async function, but I discovered that async functions are for promises, not callbacks[^1]. So I decided to make the function responsible for reading the csv file into a callback so other applications can interact with it. That turned out to be a wise move even if I didn’t know it at the time.

That’s because it helped me make the build system. Not that smart of a build system, but it works. Instead of having if (typeof document !== "undefined"), the build system would get the rates from the make-rates.js extract all the functions in the utilities.js file and write both of them into a file with the index.js file with the code responsible for making the website work. It’s very scruffy, but it’s cleaned the project directory.

//build.js
const fs = require('fs')
const path = require('path')
const util = require('util')
const Terser = require('terser')
const HTMLMinifier = require('html-minifier')
const getRates = require('./make-rates').main
const utilities = require('./utilities')
const buildDir = path.join(process.cwd(), './docs')

fs.mkdir(buildDir, () => {
  getRates((Rates) => {
    let ratesStr = `const Rates = ${util.inspect(Rates, { depth: Infinity })}\n`
    for (const func of Object.keys(utilities)) {
      ratesStr += `${utilities[func].toString()}\n`
    }

    fs.readFile('index.js', 'utf8', (e, data) => {
      ratesStr += `${data}`
      ratesStr = Terser.minify(ratesStr).code
      fs.writeFile(path.join(buildDir, 'index.js'), ratesStr, () => {
        fs.readFile('./index.html', 'utf8', (e, data) => {
          const minifiedHTML = HTMLMinifier.minify(data, {
            collapseWhitespace: true,
            minifyCSS: true
          })
          fs.writeFile(path.join(buildDir, 'index.html'), minifiedHTML, () => { process.exit(1) })
        })
      })
    })
  })
})

What’s next?

So after all that work, I got something which is marginally better to code with, actually works and doesn’t look any better. With that, there’s more work I need to do.

One thing I need to do is to test properly. While there’s some sort of testing, I don’t think I’m doing it right. I want to go through and see if there’s anything else I can unit test along with adding integration tests and acceptance testing.

To do that though, I still need to structure the project in a way that it’s easy to test locally and deploy properly. To that end, I want to learn how to intergrate a CI/CD pipeline. There are so many options out there so I’ve decided to just stick with GitLab CI. They already host my website and while it might not be the best option. I’m hoping to get familliar with how it works since I’m sure I can transfer those skills into any other CI/CD solution out there.

Another big thing I have to do is to make the website pretty. I haven’t touched the HTML since the last post and it’s still ugly. Sure it’s fast and responsive but it doesn’t hurt to make it look better.

There are also a bunch of little things I wanna do, like adding different languages and moving to a templating engine (Hugo maybe?).

You can check out the Zimbabwe Tax Calculator online and check out it’s source code on GitHub.

[1]: You can use utils.promisify but I didn’t know that then.