If you have ever worked with calculations in the financial sector you know that decimals and roundings are both incredibly important and incredibly annoying. When you round, and to what decimal, means a lot in a sector where accountability is key and numbers have to match to the n’th decimal. Therefore the more information you have at your disposal the better. Especially when you do not control all variables.
This could for instance be when designing an API. All you have available to troubleshoot, is the values of the properties coming into your application. You cannot un-sum something that has been summed unless you know the individual parts of the summation. And the same goes for the consumer of the output of your API.
When it comes to VAT it is both simple and very complicated. It is simple in the sense that it is a percentage added, but it can be complicated in regards to when it should be added. And of course then how to handle amounts that in some cases have VAT and in some cases do not.
This case with occasional VAT can be complicated enough to handle in in-house code/applications, but it only gets more complicated and complex across API’s to external partners. Because things that seems obvious and common knowledge in-house, rarely travels well across API’s – even though it may be in the documentation. So how do we handle this ambiguity?
One solution that may seem overkill but have served me well in the past is having all amount represented by 3 properties. If this is MonthlyPrice then in our API we would have:
Having this granularity may seem overkill as you could argue that you can always calculate the third from only two of the others. This should also be a correct assumption. But there are arguments against doing that. For starters if you calculate it then you are responsible. You touched the calculation so now you took over control. In financial applications that might be dangerous or at least it needs to be done only with full intent.
Another is to do with decimals and thereby roundings. If two amounts are added before being rounded and you then afterwards round to fewer decimals, then you can actually end up with “simple” VAT amounts that do not add up. This does not look pretty and can lead to a lot of time spent figuring out why your summation does not align with the summation of your consumer/partner.
If the consumer owns the calculation then you should not put yourself in a position where you have to re-calculate amounts on your side, that could have been supplied through the API. That is only asking for problems. Having fine-grained API’s solves this problem.
Another problem is to do with amounts that are sometimes including VAT and sometimes excluding VAT. What is the best way to communicate that across an interface?
We have defined our structure of amounts above and if we buy into that, then how do we handle the sometimes including VAT sometimes excluding VAT.
There are three options, if we stick to having the structure described above and not adding additional properties to indicated whether an amount is including our excluding VAT.
First option, keeping to the example, is having MonthlyPriceInVat and MonthlyPriceVat set to zero, when amount is to be excluding Vat. This is not a good solution as it opens up all sorts of issues with totals adding up to very strange amounts as InVat-amounts will be zero. It also puts the responsibility on the consumer. They have to know or decide.
Second option is always returning InVat and Vat filled even though the current amount should be ExVat. This is perhaps a little better than above. But again it sets arbitrary values and opens up to summation errors. And again the knowledge about whether a certain amount in the certain context should be handled with or without Vat is up to the consumer.
The third option, which is what I would recommend, is to set both ExVat and InVat to the same amount and only set Vat-amount to zero, when an amount is excluding VAT. Then you avoid having summation errors on InVat. It is also very easy to see that this amount should be excluding VAT. The consumer can have all their normal logic to handle amounts and ExVat, InVat and Vat will all add up to the right amount. Even mixing amounts that are both including and excluding VAT will give correct totals.
Level up every day