Tax Calculator for CiviCRM
Calculates the taxes based on the place of supply for the purposes of tax calculation.
The main use-case is, for example, memberships or events in Canada, which must be taxed differently depending on the location of the organisation, the event and the member.
Formerly biz.jmaconsulting.cdntaxcalculator
Requirements
- PHP 7.2 or later
- CiviCRM 5.latest
Configuration
Navigate to: Administration > CiviContribute > Tax Calculator.
See notes below about configuring tax rates (formerly civicrm_constants.php).
Installation
Install like any CiviCRM extension.
Upgrading from cdntaxcalculator
You may have settings that need to be converted from cdntaxcalculator to taxcalculator.
The simplest option is to enable both extensions, compare the settings and copy-paste from one screen to the other. Alternatively, you could use the API to copy the settings over programmatically or use the following SQL query:
update civicrm_setting set name = replace(name, 'cdntaxcalculator', 'taxcalculator') where name like 'cdn%';
Converting civicrm_constants.php to database settings
Currently there is no interface for handling these settings. Here is an example for Canada:
-- GST for non-HST provinces/territories
insert into civicrm_taxcalculator (name, label, rate, country_id, state_province_id) VALUES
('gst_al', 'GST', 5, 1039, 1100),
('gst_bc', 'GST', 5, 1039, 1101),
('gst_mb', 'GST', 5, 1039, 1102),
('gst_nt', 'GST', 5, 1039, 1105),
('gst_nu', 'GST', 5, 1039, 1107),
('gst_qc', 'GST', 5, 1039, 1110),
('gst_sk', 'GST', 5, 1039, 1111),
('gst_ty', 'GST', 5, 1039, 1112);
-- HST provinces
insert into civicrm_taxcalculator (name, label, rate, country_id, state_province_id) VALUES
('hst_nb', 'HST-NB', 15, 1039, 1103),
('hst_nl', 'HST-NFL', 15, 1039, 1104),
('hst_ns', 'HST-NS', 15, 1039, 1106),
('hst_on', 'HST-ON', 13, 1039, 1108),
('hst_pe', 'HST-PEI', 15, 1039, 1109);
-- PST provinces
-- Typically an organization only collects PST for the province where they are registered
-- i.e. only one of the taxes below, not all.
-- Examples below are posted as separate queries for easier copy-pasting.
insert into civicrm_taxcalculator (name, label, rate, country_id, state_province_id) VALUES
('pst_bc', 'PST-BC', 7, 1039, 1101);
insert into civicrm_taxcalculator (name, label, rate, country_id, state_province_id) VALUES
('pst_mb', 'PST-MB', 7, 1039, 1102);
insert into civicrm_taxcalculator (name, label, rate, country_id, state_province_id) VALUES
('pst_qc', 'PST-QC', 9.975, 1039, 1110);
insert into civicrm_taxcalculator (name, label, rate, country_id, state_province_id) VALUES
('pst_sk', 'PST-SK', 6, 1039, 1111);
Displaying taxes in Contribution Invoices
Edit the invoice message-template with the following code:
{* towards the top *}
{taxcalculatorTaxTotals contribution_id=$id}
{* After the line items *}
{* TOTAL TAXES *}
{if $taxcalculator_tax_totals}
<tr>
<td colspan="3"></td>
<td colspan="2" class="division"><hr style="border: 1px solid #000" /></td>
</tr>
{foreach from=$taxcalculator_tax_totals item=tax}
<tr>
<th colspan="4" align="right"><nobr style="font-size: small;">{$tax.label}</nobr></th>
<td align="right" class="money" style="padding-left: 10px;"><nobr>{$tax.amount|crmMoney:$currency}</nobr></td>
</tr>
{/foreach}
{/if}
Displaying taxes in Contribution Email Receipts
Edit the contribution message-template (online and offline) with the following code:
{* towards the top - note the contribution_id variable is not the same as the PDF invoice *}
{taxcalculatorTaxTotals contribution_id=$contributionID}
{* Replace the 'if $totalTaxAmount' block - here to different, td instead of th *}
{* TOTAL TAXES *}
{if $taxcalculator_tax_totals}
{foreach from=$taxcalculator_tax_totals item=tax}
<tr>
<td {$labelStyle}>{$tax.label}</td>
<td align="right" {$valueStyle}>{$tax.amount|crmMoney:$currency}</td>
</tr>
{/foreach}
{/if}
You may also want to adjust the alignment on the Sub-Total and some other fields.
Displaying taxes in Event Email Receipts
Edit the event message-template (online and offline) with the following code:
{* towards the top - note the contribution_id variable is not the same as the PDF invoice *}
{taxcalculatorTaxTotals participant_id=$participantID}
{* Replace the 'if $totalTaxAmount' block - here to different, td instead of th *}
{* TOTAL TAXES *}
{if $taxcalculator_tax_totals}
{foreach from=$taxcalculator_tax_totals item=tax}
<tr>
<td {$labelStyle}>{$tax.label}</th>
<td align="right" {$valueStyle}>{$tax.amount|crmMoney:$currency}</td>
</tr>
{/foreach}
{/if}
You may also want to add align="right"
to the other rows in this area, so that they
all align in the same direction (or you can remove the align="right"
from the tax
amounts, but it looks odd).
Special tax rules
Taxcalculator also supports "override rules" per Financial Type (in the admin interface, edit a Financial Type), to alter the behaviour of tax calculations (for situations such as webinars).
The only tax rule which is implemented out of the box is the rule for "Online Events", which will apply the tax of the state/province of the contact, rather than the tax of the location of the event.
Other tax rules are not implemented out of the box. They are there mostly to help with customizations (see example below).
Altering tax rates with a hook
Example:
/**
* Implements hook_taxcalculator_alter_lineitems().
*/
function mycrm_taxcalculator_alter_lineitems(&$line_items) {
foreach ($line_items as &$item) {
// Apply the federal tax only
$rule = Civi::settings()->get('taxcalculator_rule_' . $item['financial_type_id']);
if ($rule == 'federalonly' && !empty($item['tax_rate'])) {
$session = CRM_Core_Session::singleton();
$province_id = $session->get('cdntax_province_id');
if (!$province_id) {
$contact_id = CRM_Core_Session::getLoggedInContactID();
$province_id = CRM_Taxcalculator_BAO_CDNTaxes::getStateProvince($contact_id);
}
$item['tax_rate'] = CRM_Core_DAO::singleValueQuery("SELECT rate FROM civicrm_taxcalculator WHERE name like 'gst_%' and state_province_id = %1", [
1 => [$province_id, 'Positive'],
]);
$item['tax_amount'] = round($item['amount'] * $item['tax_rate']/100, 2);
}
}
}
Of course this is a just an example (for an organization based in Quebec). Double-check with your accountant.
Running the tests
The phpunit tests can be run on top of a buildkit site:
cd /path/to/files/civicrm/ext/taxcalculator
env CIVICRM_UF=UnitTests phpunit5
The tests will enable the CiviCRM debug mode, which will make taxcalculator keep a trace of all tax calculations in the ConfigAndLog. This can be useful to debug failing tests.
The Puppeteer tests are only a proof of concept and depend on a (not very maintained) test site.
Support
Please post bug reports in the issue tracker of this project:
https://lab.civicrm.org/extensions/taxcalculator/issues
Commercial support is available from:
- JMA Consulting: https://jmaconsulting.biz/
- Coop SymbioTIC: https://www.symbiotic.coop/en