Taxi Driver - A Flexible International Tax Engine Microservice
Built with Micro & Lowdb โก๏ธ
Slides: https://slides.com/benoror/taxi-driver
- ๐ฒ๐ฝMรฉxico
- ๐ฆ๐ทArgentina
- ๐ธ๐ฆSaudi Arabia
*Full tests & specification for each country can be found at tests/integration
Tax Rules are defined in a LowDB located at db.json#L18
These rules are matched and applied based on the provided Query.
A range of Datetime
values for specifying the validity of a rule
validFrom
&validUntil
: International Standard Date (ISO) (see moment.js documentation)
Taxi Driver use Currency.js for all Currency calculations, thus getting rid of decimals rounding issues.
For currency amounts the default number of cents is 2
For the rate factors the precision is set to 8 using the FACTOR_PRECISION
constant.
A query includes the following (case insensitive) fields:
Initially, these required parameters are used to filter out the tax rules by:
country
(String): Country codetaxes
(Array(String)): Name(s) to identify the taxes
Optional match params
(defined at db.json#L3) are used to "best match" a single rule while querying the app via the API:
region
: Region (ex. border tax, state, county, municipality)bpType
: Business Partner typetxType
: Transaction typedocType
: Document typecategory
: Category name (ex.: for products)area
: Area (ex.: business area, service station)
vars
object is used both in a query and rules, to store variable dependant values (such as sub-totals), and other formulas to be evaluated by the FormulaParser
.
*Commonly used for: subTotal
, unitPrice
, ...
An array of String
's to be matched against rule(s) taxName
*Both vars
, rate
& amount
can also be String
's, as they can be evaluated using handsontable/formula-parser
{
country: "sa",
txType: "sales",
docType: "invoice",
category: 'DRUG',
bpTaxType: 'TAXYES',
area: undefined,
vars: {
unitPrice: 5000
},
taxes: ['VAT'],
}
The query above will match to the following rule(s):
[
{
"countryCode": "sa",
"txType": "sales",
"docType": "invoice",
"taxName": "VAT",
"category": "DRUG",
"bpTaxType": "TAXYES",
"rate": "IF(unitPrice > 2000, 0.02, 0.05)"
}
]
Postman collection: https://web.postman.co/collections/27932-1280fe65-8858-4d0f-bde4-4c3b79d6b5b3?workspace=61c65267-c247-4243-8558-65eaee551abe
Request:
{
country: "sa",
// ... match params
vars: {
// ...
},
taxes: [ /* ... */ ]
}
Response:
{
taxes: {
[taxName]: {
rate: {error: /*...*/, result: /*...*/},
// ...
},
[taxName]: {
rate: {error: /*...*/, result: /*...*/},
// ...
}
}
}
Rules can have a meta
object, containing any informative fields that
need to be passed to the consumer. For example, an exempt
flag for
Mexico's particular exempt taxes.
If a vars.subTotal
is provided, the engine will automatically
calculate and return:
- totals (
taxTotal
&grandTotal
) in the response header. amount
&factor
per tax
Rules can have a dep
string with the name of a dependant taxName that
has to be used to calculate the factor
https://github.com/ecaresoft/taxi-driver-ui
Easy development using zeit/micro-dev:
yarn run dev
Jest is used for both unit & integration tests. Run with:
yarn test
- https://github.com/commerceguys/tax
- https://github.com/valeriansaliou/node-sales-tax
- https://gist.github.com/luissifu/b368f308b37d6e98031e737d9d04d2d1
- https://discourse.ecaresoft.com/t/rfd-tech-spec-tax-engine-mvp/1213/25
- https://docs.google.com/document/d/1NHwMpuQZTLa8VinhX3xmyXNg-0OGlkKZFnyRscprhkc/edit#heading=h.krjmur7zqqnt
- https://docs.google.com/spreadsheets/d/1Qu_NgDyhRS3DAEdrxe-fNzUuG67E2XZxHsejAKCrPjI/edit#gid=0
- https://docs.google.com/presentation/d/1QYAy7qPoEwJPGlu7Ss-alNH1dBmeBFFFa2ph8hy5WsI/edit#slide=id.g45bef5faee_0_122