Taxation Rules
The seven endpoints for tax rates, country rules and the invoice document settings.
Overview
Tax is built in three layers. At the bottom sit the basic settings. They say whether tax is on, what the default rate is, and where the tax sits.
Above them stand the country and state rules. When a rule matches a client's address the rate comes from there. A state rule comes before a country rule, and a country rule before the default.
A separate group covers the invoice document itself. It shapes the numbering, opens or closes formalising, names the tax fields a company client is asked for, and handles posting a printed bill.
Reference
Reading the Tax Settings
Returns whether tax is on, its rate and how it is worked out.
curl 'https://panel.example.com/api/v1/admin/financial/taxation' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/financial/taxation', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/financial/taxation');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The rate here is the DEFAULT; a rule set for the client's country comes first.
$t = Api::Financial()->GetTaxation()['data'];Writing the Tax Settings
Switches tax on and writes its rate and how it is worked out.
curl -X PUT 'https://panel.example.com/api/v1/admin/financial/taxation' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"enabled":true,"rate":20,"taxation_type":"exclusive"}'const res = await fetch('https://panel.example.com/api/v1/admin/financial/taxation', {
method: 'PUT',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
enabled: true,
rate: 20,
taxation_type: 'exclusive',
}),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/financial/taxation');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['enabled' => true, 'rate' => 20]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Changing how tax is worked out turns the SAME price into a different total; think again
// about what the prices you advertise are meant to say.
Api::Financial()->UpdateTaxation(['taxation_type' => 'inclusive']);Reading the Advanced Settings
Returns the settings for invoice numbering and the company tax fields.
curl 'https://panel.example.com/api/v1/admin/financial/taxation/advanced' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/financial/taxation/advanced', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/financial/taxation/advanced');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// There are two numbering series: one for invoices and one for PAID invoices.
$a = Api::Financial()->GetTaxationAdvanced()['data'];Writing the Advanced Settings
Changes the advanced tax and invoice settings you send.
curl -X PUT 'https://panel.example.com/api/v1/admin/financial/taxation/advanced' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"invoice_number_format":"FTR-{NUMBER}","invoice_number_format_status":true}'const res = await fetch('https://panel.example.com/api/v1/admin/financial/taxation/advanced', {
method: 'PUT',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
invoice_number_format: 'INV-{NUMBER}',
invoice_number_format_status: true,
}),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/financial/taxation/advanced');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'invoice_number_format' => 'INV-{NUMBER}',
'invoice_number_format_status' => true,
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// Lowering the numbering START can hand the same number out twice; do not drop below
// the highest number already given.
Api::Financial()->UpdateTaxationAdvanced(['invoice_increment' => 1000]);Listing the Tax Rules
Returns the tax rates set by country and state.
curl 'https://panel.example.com/api/v1/admin/financial/tax-rules' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/financial/tax-rules', {
headers: { Authorization: `Bearer ${apiKey}` },
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/financial/tax-rules');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// A state rule comes before the COUNTRY rule; with neither, the default rate applies.
$rules = Api::Financial()->GetTaxRules()['data'];Writing a Tax Rule
Writes the tax rate for a country or a state.
curl -X PUT 'https://panel.example.com/api/v1/admin/financial/tax-rules' \
-H "Authorization: Bearer $API_KEY" \
-H 'Content-Type: application/json' \
-d '{"country_id":792,"state_id":0,"rates":[{"name":"KDV","value":20}]}'const res = await fetch('https://panel.example.com/api/v1/admin/financial/tax-rules', {
method: 'PUT',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
country_id: 840,
state_id: 0,
rates: [{ name: 'VAT', value: 20 }],
}),
});
const body = await res.json();$ch = curl_init('https://panel.example.com/api/v1/admin/financial/tax-rules');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PUT',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'country_id' => 840,
'rates' => [['name' => 'VAT', 'value' => 20]],
]),
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// The parts REPLACE what was there: your list clears the old one rather than adding.
Api::Financial()->UpdateTaxRule([
'country_id' => 840,
'rates' => [['name' => 'VAT', 'value' => 20]],
]);Defining the European Rates
Defines the value-added tax rates of the European Union countries at once.
curl -X POST 'https://panel.example.com/api/v1/admin/financial/tax-rules/define-all' \
-H "Authorization: Bearer $API_KEY"const res = await fetch('https://panel.example.com/api/v1/admin/financial/tax-rules/define-all', {
method: 'POST',
headers: { Authorization: `Bearer ${apiKey}` },
});
const { data } = await res.json();
console.log(data.defined);$ch = curl_init('https://panel.example.com/api/v1/admin/financial/tax-rules/define-all');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $apiKey],
]);
$body = json_decode(curl_exec($ch), true);
curl_close($ch);// If the installation's own country is on the list the DEFAULT rate moves; keep the old
// rules first.
$before = Api::Financial()->GetTaxRules()['data'];
Api::Financial()->DefineAllTaxRates();Pitfalls
The rate on an invoice comes first from the client's state rule. Failing that it comes from the country rule, and only then from the default. Changing the default moves nothing for clients in countries that carry a rule. That is usually behind a "the rate update did nothing" report.
When writing a tax rule the list of parts you send replaces what was there rather than adding to it. To add a second part to a country, read the current list and send both together. Sending one part quietly drops the others.
Defining the European rates in bulk writes over the rules you entered by hand for those countries. When the installation's own country is among them, the default rate moves as well. Read and keep the current rules before running it, because there is no other way back.
Dropping the invoice numbering start below the highest number already given hands the same number out twice. Two documents sharing a number is a serious matter in accounting and hard to put right afterwards. Move the start upward alone.
Whether tax sits inside the amount or on top of it changes what your advertised prices say. Inside, an item marked a hundred sells for a hundred with the tax taken out of it. On top, the client pays a hundred and twenty. Changing this once an installation is live means going back over the whole price list.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.