Laura10101 / contractor-tax-calculator

0 stars 2 forks source link

Tax Calculator

The aim of this project was to create a tax calculator for IT contractors to compare take home pay across different jurisdictions.

Tax law is complex and time consuming to research. This project aims to democratise tax knowledge so that the average IT contractor can see with a quick calculation how they will be affected by taxes in different jurisdictions.

This calculator is of use to any IT contractor considering moving countries. It is also useful to those in the UK, since certain tax matters are devolved which means that tax rates can be different in England, Wales, Scotland and Northern Ireland. As a result, for some residents who live near the borders, a simple move to the next village can result in a drastically different tax bill.

The goal of this calculator is to allow them to see an indicative calculation of how a move would affect their take home pay, without them having to spend weeks investigating complex legal issues or spending thousands on tax advisers. In the event that they are seriously considering a move to another jurisdiction after seeing the indicative calculation, then they will be advised to seek personalised, specialist advice.

The tax calculator could be expanded in the future to be of use to a wider demographic than just IT contractors. The software architecture of this project has been designed with scalability/xxxx? in mind, to ensure that additional jurisdictions and demographics can be added easily.

Am I responsive?

Table of Contents

Notes to Assessors

The admin user site has been designed for a sophisticated professional tax adviser who has commissioned the project. As a result, and to keep the user experience streamlined, it does not explain tax concepts, rules, or basic instructions as the user would have in-depth knowledge of these.

Users

The users of this site will be any IT contractor working outside IR35 and considering moving to another tax jurisdiction. The site may also be suitable for any other type of contractor working through a limited company in the same way.

Finding accurate demographics for this group was not easy, and as this is a new application I have no existing business data to draw on. The most accurate data I found related to the IT contractor market in the USA, so it was not ideal, but it does give a clear view of the typical demographic:

Since most IT contractors work through limited companies and submit tax returns each year I would expect the users to be reasonably sophisticated, possibly with multiple sources of income, and a reasonable understanding of tax language. I therefore did not define terms like ‘corporation tax’ as the average user of this site would more likely than not already be familiar with them. I do not have any data to test this assumption, as this is a new site. As it is used, data will be collected and if my assumptions are incorrect the user experience can be modified. For example the terms could be clarified in some additional notes. For the sake of a clean user experience those notes have not been included at this stage.

Additionally, there will also be one admin user who will be able to log in, add new jurisdictions and update tax rates for existing jurisdictions. It is critical to note that the admin user is a sophisticated professional tax adviser who has commissioned the project. As a result, and to keep the user experience streamlined, the admin user section does not explain tax concepts, rules, or basic instructions as the user would have in-depth knowledge of these.

User stories

All user stories were documented, and progress towards delivering them, was tracked in Github.

As an IT contractor I want to…..

  1. Select the jurisdictions to compare tax calculations for, so that I only see jurisdictions I am interested in
  2. Easily enter my income details, so that my tax can be calculated for each jurisdiction I have selected
  3. See how much progress I am making while using the application, so that I can easily see where I am in the process
  4. See a clear tax calculation for each jurisdiction I have selected that is easy to understand, so that I can easily decide which jurisdiction is most favourable
  5. Have my data be protected by login and appropriate security measures, so that I have confidence only I can access the data
  6. Purchase subscriptions for the tax calculator, so that I can begin comparing my tax calculations right away

As the Contractor Tax Calculator team, I want...

  1. Non-admin users to be prevented from creating tax calculations without an active subscription, so that I can generate an income

As an admin user I want to….

  1. Add new jurisdictions, so that I can continuously improve the product
  2. Delete jurisdictions, so that I can continuously improve the product
  3. Define the questions that IT contractors should be asked for a given jurisdiction - and the format of the corresponding answer fields - so that the tax calculator will display only relevant questions to users
  4. Edit the questions and answer formats associated with a given jurisdiction at any time, so that I can keep the tax calculator up to date with changes in tax regimes
  5. Questions and answer formats that are associated with a jurisdiction to be deleted if the jurisdiction itself is deleted, so that I do not store redundant data in the database
  6. Create the tax rates for a jurisdiction, so that I can add new jurisdictions to improve the product
  7. Update tax rates for a jurisdiction, so that I can keep the product up to date with changing tax regimes
  8. Tax rates for a jurisdiction to be deleted if the jurisdiction is deleted, so that I do not store unnecessary data in the database
  9. View subscriptions, so that I can assist users with any subscription-related queries
  10. View payments, so that I can assist users with any payment-related queries
  11. Admin functionality to be protected by an admin user account, so that only authorised users can modify jurisdiction, form, and tax rate data
  12. View field names for question and rule fields in the config app, so that I can easily understand which data elements relate to which fields

Challenges Faced

Tax law is highly complex. Different systems have different types of rules and caluclations. In order to limit the scope of the project, personal allowances have not been included yet. The project is dependent upon the calculations being accurate. Therefore research was a critical aspect. The sophisticated admin user will need to keep the information up to date for the calculator to continue to be accurate over time.

Tax ranges and limits are specified in the currency of the jurisdiction. This site is currently aimed at UK residents so will only take income in GBP for the time being.

The architecture of the project was critical to ensuring that the project is scalable and can be amended and kept up to date.

UX

Colour Scheme

The aim of this site was to appear professional, accurate and trustworthy with good levels of contrast to satisfy optimal UX design.

As this is an entirely new site, I do not have any demographic data about the expected users. Once the site is in use data will be collected and demographic assumptions will be revised. The colour scheme can then be changed if it is felt necessary.

Typography

Since the site is conveing complex informaiton, a simple, clear text was desired.

Imagery

The site is free from images to ensure a clean, simple interface.

Font awesome was used to provide simple, clear graphics.

Wireframes

IT Contractor Wireframes

As an IT contractor, I am first asked to choose whether I need to register or login:

The contractor index wireframe

If I choose to register, I am presented with a registration form:

The registration form wireframe

If I choose to login, I am presented with a login form to access the Tax Calculator using my existing username and password:

The login form wireframe

When I login, I can see a summary of my past tax calculations, and my subscription status:

The contractor dashboard wireframe

If my subscription has expired, or I have not subscribed yet, then I can choose to extend my subscription:

The subscription option page wireframe

Once I have chosen my new subscription plan, I see a checkout page for my subscription:

The checkout form wireframe

When I have entered my payment details, payment is taken and I am shown a page confirming the status of the payment:

The payment status wireframe

Once my payment has been completed, and my subscription updated, then I can start creating a tax calculation. The first step is to choose the countries that I want to compare:

The select jurisdictions page wireframe

Next, I enter my financial information following the on-screen instructions. Firstly, I select my income sources:

The income sources wireframe

Then I enter my income details:

The income details wireframe

Finally, once I have provided all required data, my tax calculation is displayed:

The calculation results wireframe

Admin Wireframes

As an admin, I can see a list of jurisdictions. I can choose to delete a jurisdiction, or add a new one:

The admin jurisdictions page wireframe

If I choose to add a jurisdiction, then I am shown a form to enter jurisdiction details:

The admin add jurisdictions page wireframe

As an admin, I can see a list of tax categories. I can choose to add a new tax category, or delete one:

The admin tax categories page wireframe

If I choose to add a tax category, then I am shown a form to enter tax category details:

The admin add tax category page wireframe

As an admin, I can see a list of questions for a selected jurisdiction. I can choose to edit or delete existing questions, or add a new one:

The admin questions list wireframe

When I choose to add or edit a question, I am shown a form to enter the details for the question. If I am editing an existing question, then the details of the current question are displayed:

The admin edit question modal wireframe

As an admin, I can see a list of tax rates for a selected jurisdiction. I can choose to edit or delete existing tax rates, or add a new one:

The admin tax rates list wireframe

When I choose to add or edit a tax rate, I am shown a form to enter the details for the tax rate. If I am editing an existing tax rate, then the details of the current tax rate are displayed:

The admin edit tax rate modal wireframe

Features

Existing Features

Features Left to Implement

For this project I had to be very careful to keep the scope as tight as possible since there was a large amount of legal research, algorithms and architecture to carry out. With the limited time available, I had to prioritise. I architected the project in an agile way, to ensure that I could come back to it at a later date and add functionality as easily as possible. With more time, I would consider adding the following functionality:

Technical Design

The Technical Challenges

The fundamental problem for the Tax Calculator is to gather financial information from the user and then apply a series of calculations to work out, based on that info, how the user will be taxed. The problem is that the structure of the calculations, and the calculations themselves, and the info on which they are based, varies from country to country.

The forms will be different for each country - different questions for each country, and the calculations are going to be different for each country. Given this, I needed to come up with a piece of software that can gather the right info from the user for all of the countries, and then apply the right calculation for all of the countries.

The problem is this: how do I get info from users when the info needed is dependent on the particular country and the calculation needed is also dependent on the particular country?

I wanted to architect this in a way that means other countries can be added in the future without needing to change the software itself. This will make the project more extensible and future-proof, and is in line with the principles of Uncle Bob’s ‘Clean Code’ - reducing the time and expense needed to update the project in the future.

There were three options:

  1. To use lots of if statement, with hard coded calculations for each country, and then the algorithm selects the calculations based on the country. But with this design, software engineers would have to change it every time a new country is added, requiring a release of code every time. This is because all of the logic is hard coded. Additionally, Uncle Bob doesn’t like if statements!

  2. Enable additional countries to be added by extension. Create a software module for each country and each will have hard coded questions and answers. A plug-in would effectively be created for each country. This doesn’t require a redeployment of the whole application code if changes are made, but you need a software engineer to create a new plugin and maintain the logic for each country.

  3. Enable administrators to configure each country. This approach would come up with a generic algorithm that is data driven and store all of the knowledge in a database. The algorithm uses the knowledge in the database to work out what questions to ask and what calculations to apply. This would enable an admin user to update the database with a new country, and no software engineering is needed. This makes the project much more accessible, usable and updateable.

I selected the third option to make it as easy as possible for tax experts to rapidly expand the range of countries supported by the Tax Calculator. The issue with this option is that if a new country has totally different tax rules than those in the existing database, then a software engineer would need to update the system to provide new types of tax rate but this is much less common than having to change the tax rates themselves. This option can lead a developer down a rabbit hole, trying to anticipate every single tax rule set that might possibly come up. But all tax systems I have studied so far have had similar rules, so this still remains the best option. In the event that a new country was added with drastically different rules, software engineering would be required to ensure the overall logic still worked.

This project is about striking a balance between ensuring the application is useful to the end user (IT contractors) whilst also being easily updatable by an admin user as far as that is reasonably practicable. There may be outlying cases where a software engineer would be required to add a new country, but I am limiting the scope where possible to minimise this risk.

Monolith versus Microservices

Given the user stories and analysis of the Tax Calculator problem above, it is clear that the backend of the Tax Calculator will be very complicated with a need to support several different areas of functionality:

Given these different types of functionality that the backend of the Tax Calculator will need to support, I had to decide how best to architect the solution. One option would be to adopt a monolithic style similar to that used on the Boutique ADO project. In this approach, the code for the user interface (templates and views) lives in the same module (in our case, Django app) as the models and data access logic which that user interface relates to. This works well for small scale applications. However, as the application grows it becomes harder to maintain clean separation of concerns between modules.

The Tax Calculator has already reached this point. For example, there will be need to be two different applications at least - one for admins and one for IT contractors. Both applications, however, will need access to both the question and rules data. The admin will need to be able to view lists of questions and rules so they can edit that data. The IT contractor will need to access the same data to generate the financial information form and their tax calculation. Similarly, administrators need to be able to view subscriptions and create subscription options. Users need to choose subscription options and update subscriptions.

This raises the problem as to which Django app should own each piece of data. Using the conventional monolithic approach, there are many scenarios where one app would need to access the data provided by another app. Furthermore, this is likely to lead to duplicated code in many places.

To avoid this problem, an alternative architectural style is the microservices architecture. Using this style involves hiding data and functionality microservices which are accessed via RESTful APIs. Each API manages the data and provides the functionality for a specific, closely-related set of data models. This allows the functions that process data to be decoupled from the user interfaces which allow users to view and input data. Because the functionality provided by each microservice is accessed via a RESTful API, this means that many Django apps could access the same functionality and data without replicating too much code. This also allows for consistent validation of data where multiple apps can edit the same data.

I therefore decided to opt for the microservices style.

Domain-Driven Design

Having decided to adopt a microservice approach, the next question I had to answer is what are the microservices that I need?

Microservices are closely related to the concept of domain-driven design. Each microservice should manage the data for exactly one domain. Domain-driven design involves designing data models in the software that closely resembles the terminology used by real users of the application. An important aspect of domain design is:

How to organize large domains into a network of Bounded Contexts.

This involves separating complex data models into separate bounded contexts. The guidance is that the context changes when the language changes, and that different contexts have unrelated concepts - but also some overlapping concepts.

Using this approach I was able to identify five bounded contexts. To do this, I worked out from the list of all of the data that the Tax Calculator would need, which bits of data belonged together.

The five contexts are:

  1. The Jurisdictions context which holds and manages the list of tax jurisdictions.
  2. The Forms context which holds and manages all of the data needed to generate the forms required to capture the correct financial information from IT contractors.
  3. The Rules context which holds and manages all of the data needed to generate the tax calculations for a user based on the provided financial information.
  4. The Subscription context which holds and manages data relating to user subscriptions and options.
  5. The Payments context which takes payments from users and stores a record of these.

Originally, I had intended to separate out a sixth context - the Calculations context - from the Rules context. However, it became clear that this was not sensible or feasible since the Calculations context would need all of the data contained within the Rules context to do its job. The calculation algorithms are in fact just the functionality associated with the Rules context.

I also considered holding Forms and Rules data as part of the Jurisdiction context. However, Forms and Rules data are not very closely related and this would have felt like too much data and functionality in a single microservice.

High-Level Architecture

Having identified the bounded contexts for the Tax Calculator, I was able to design my high-level architecture as shown below:

High-level architecture

In this diagram, there is an API for each bounded context identified above. Each API is intended to fully own and manage the data and functionality associated with that bounded context.

Additionally, there are a number of Django applications. Each application is intended to contain a complete set of end-to-end user journeys as follows:

API Code Design

An API needs to do a number of things:

Following the single-responsibility principle, and loosely based on the Model-View-Controller pattern, I decided to separate out my API code into several layers as shown in the diagram below:

API architecture

The layers are:

High Level System Flow

The following diagram provides an early high-level design for how these different components will collaborate to provide the end-to-end calculation journey:

The calculation process

Data Model

The Jurisdictions Domain The data model for the Jurisdiction domain is shown below:

The jurisdiction data model

Arguably, this data model is too small to be considered a context in its own right. However, as noted above this data model is referenced by both the Forms and Rules context. Given that the Jurisdiction data model is shared in this way, it was not logical to place the Jurisdiction data model in either the Forms or Rules.

The alternative would have been to place Jurisdiction, Forms and Rules data in a single bounded context but I felt the resulting domain would have been too large with lots of unrelated functionality living behind a single API. This would have broken the single responsibility principle. I therefore felt splitting the Jurisdictions data out was the best option.

The Forms Domain The data model for the Forms domain is shown below:

The forms data model

In this data model, Questions for a single jurisdiction are grouped together into a Form. Given that the Form lives in a separate API, it holds the integer ID of the corresponding Jurisdiction rather than a Django foreign key. Holding the Foreign Key would have broken the microservices principle by allowing a model in one service to access the data inside another without going via the API.

The model allows the admin to configure three types of question:

The Rules Domain The data model for the Rules domain is shown below:

The rules data model

The Rules data model follows a similar pattern to the Questions data model. This is an implementation of the Strategy Pattern which allows multiple implementations of the same function to be used interchangeably. In this case, the functions are the Validate and Calculate functions. This allows multiple rules of varying types to be defined for a jurisdiction and processed without complex conditional logic. Uncle Bob would like this!

TaxCategory objects correspond to different types of tax (e.g., income, corporation, VAT, dividend, inheritance).

RuleSets are used to group all of the tax Rules for a specific Jurisdiction and TaxCategory combination. For example, one RuleSet instance would be defined to hold Corporation Tax rules for France. Another would hold Corporation Tax rules for Germany, and so on.

Three types of rule are supported:

The Rules context also contains three models that hold the results of a tax calculation. Rather than just displaying the final amount of tax payable for each jurisdiction, I wanted to be able to display each step of the calculation to the IT Contractors so they can understand how the calculation was reached if they wish to. This data model allows the steps to be grouped and ordered.

It's worth noting that these models do not hold foreign keys to any of the RuleSet or Rule data models but instead hold integer IDs referencing the RuleSet or Rule models, as well as the summary data for the associated rules and rulesets. The reason for this is to minimise the complexity of SQL queries when retrieving TaxCalculationResults. Instead of having to join the TaxRuleTierResults to the associated Rules in order to get the details of the step, all of the data needed to explain the step to the user is held within the TaxRuleTierResults.

The Subscriptions Domain The data model for the Subscriptions domain is shown below:

The subscriptions data model

The Payments Domain The data model for the Payments domain is shown below:

The payments data model

One important factor to note is that the Payment model holds the payment ID that is returned by Stripe for the payment. This is required to properly handle webhooks that are returned by Stripe. By storing the stripe_pid on the Payment model, the API can correctly identify the Payment entity to which a given webhook coming in from Stripe corresponds. This is needed since Stripe cannot hold the API's Payment.id value and a common identifier is needed between the two systems to allow the matching to take place.

It is debatable whether the Payments context should really have been separated out from the Subscriptions context. Initially this decision was taken since the Payment functionality is very different to, and separate from, the functionality required to update Subscriptions. However, an alternative point of view is that the Pamyent process is simply part of the subscription user journey.

One consequence of separating out the Payment and Subscription contexts was that it became harder to update a Subscription once Payment was completed. In a future version of the Tax Calculator, therefore, I would merge these two contexts together.

Testing

For all testing, please refer to the TESTING.md file.

Deployment

The live deployed application can be found at Tax Calculator.

Heroku

This project uses Heroku, a platform as a service (PaaS) that enables developers to build, run, and operate applications entirely in the cloud.

Deployment steps are as follows, after account setup:

Heroku needs two additional files in order to deploy properly.

You can install this project's requirements (where applicable) using: pip3 install -r requirements.txt. If you have your own packages that have been installed, then the requirements file needs updated using: pip3 freeze --local > requirements.txt

The Procfile can be created with the following command: echo -e web: python app.py\nworker: celery -A worker.celery worker > Procfile

For Heroku deployment, follow these steps to connect your GitHub repository to the newly created app:

Either:

Or:

The frontend terminal should now be connected and deployed to Heroku.

ElephantSQL DB

This project uses ElephantSQL DB as the relational database for the application's data warehouse.

Deployment steps to create the database in ElephantSQL, after account setup, are as follows:

Stripe

This project uses Stripe as the payment provider to receive payments for subscriptions from the user.

Deployment steps to set up Stripe integration, after account setup, are as follows:

Local Deployment

Gitpod IDE was used to write the code for this project.

To make a local copy of this repository, you can clone the project by typing the follow into your IDE terminal:

You can install this project's requirements (where applicable) using: pip3 install -r requirements.txt.

Create an env.py file, and add the following environment variables:

import os

os.environ['SECRET_KEY'] = "11)7m!ubta%^*^e68=^03k)ht^yyh@724#&%eyn6ihng9i+uim"
os.environ['STRIPE_PUBLIC_KEY'] = 'pk_test_51NDoGHFkVBiDxSnkTY8frrULeHhmIUMQUtoAJPyqnRCV3xM7kgd1PNX4AkWOx7lWDuRdzXTXQCcvIqMvpGLJvUZR00gMGZRSEG'
os.environ['STRIPE_SECRET_KEY'] = 'sk_test_51NDoGHFkVBiDxSnkgbUnGaJO53YUKEj8snA7eqrRYmgRzmzjV0JyOUv1dHF9VUAmgg9lfGOoORrnYT126SpgFk7m00HsjjFcmm'
os.environ['STRIPE_WH_SECRET'] = 'whsec_PBvl6xDiaj1yrqG8dQ3mQLLyslnheCj0'
os.environ['DATABASE_URL'] = 'postgres://mqrlkcwy:aAuiwNbWU-9KjIL1Vsrrxn8ju8ZZuSXl@tyke.db.elephantsql.com/mqrlkcwy'
os.environ['DEVELOPMENT'] = 'True'
os.environ['USE_TEST_DB'] = 'False'

Alternatively, if using Gitpod, you can click below to create your own workspace using this repository.

Open in Gitpod

Credits

Thanks

I would like to thank the Code Institute for all of the support through all four of my projects. Special thanks goes to Tim Nelson who was my personal tutor. He is a remarkable software professional and has a natural ability to teach and inspire.

Educational Resources