Aller directement à la fin des métadonnées
Aller au début des métadonnées

Which Province to Use?

Revenue Canada calls the place used to calculate sales taxes the 'place of supply'. I've tried to distill down what I understand the rules to mean for us below. (The long version: and the shorter version


The place of supply for events is normally the venue of the event. If the event is an online event, then the place of supply is the primary address of the purchaser, or if it is known, e.g. in case of multiple tickets being purchased, the primary address of each attendee.

Contributions and Memberships

'Contributions' are sometimes used for goods to be delivered or downloads that need sales tax applied. In all cases, the primary address of the purchaser will be the place of supply.

Which Rate(s) to Use?

Calculate one or two sales taxes as appropriate for membership sales based on the place of supply and the following table:


We'll use the core implementation (Sales Taxes and Invoicing - Phase 1) as much as possible to keep things easier. In a couple of places if its simple implementation does not support the needs of the extension then we will enhance core to meet the needs of our extension. We will presume that our extension's approach will be used for city and state taxes in the US, and possibly for sales taxes in Australia.

One assumption of core that we retain is that all things that appear on a 'line item' have the same tax rate. This means that if multiple participants having tickets purchased at the same time provide primary addresses in different provinces for an online event then their tickets need to be put on different lines if that is not the case right now.

The legal requirements for what needs to be presented to buyers is on pp. 13-14 of One important point is that we can either display the HST/GST amount and PST amount separately from the total amount, or if it is included then we need to display the rate used to calculate them. This is somewhat similar to the two styles of presenting tax information currently implemented for VAT.

As it was only tested for a single tax per line item, we should test applying two taxes to a single item to make sure that it works in terms of layouts on screen and receipts, etc. We also need to test things like the weight of tax accounts for ordering of taxes, and that if either one of the two tax types is not needed on a invoice or receipt that it doesn't appear. These are all core features that should be tested before 4.6 is released.

I propose we modify core to support the sort of display we need as follows, rather than put it in an extension. Alternatively, we should ensure there are appropriate hooks, and we can specify alternative .tpl's in some appropriate way.

Financial Accounts

As PST has to be remitted to each province separately, it would be good to make it easy to have a separate account for each PST province on export. Manual changes of these default account numbers could allow organizations to collapse all of the funds into a single account if they want. As GST and HST are both remitted to the federal government, it would be good if they defaulted to the same account number.

If we decide to go with just a single column label, GST/HST, then it will be a good idea to create a single tax account labelled GST/HST. As both are remitted to the same place there is no accounting need to separate them. 

Determining Applicable Taxes Based on Place of Supply

There is a hook that is called to allow the taxes calculated for a transaction to be modified. I think it would be simplest if we aimed to have taxable Financial Types set up with two 'Sales Tax Account is' relationships by default: one with a GST/HST reserved account and one with a generic PST reserved account. When the hook is called, the place of supply would be used to create one or two civicrm_line_item records as appropriate for each taxable line item in the contribution.

Phase 1: Implement a Simple Extension to Calculate Tax on Membership

This phase involves providing minimal support for a very simple use case: taxes on a single membership. It hard-codes things for a single client that will need to be supported with Admin interfaces later. Intended contributions to fuller functionality:

  • Testing of sales tax support in 4.6 that has been backported to 4.5.4
  • Creating an initial implementation of tax calculation
  • Sorting out simple base case for adding Canadian taxes to database

Configuring Tax Rates for Province

Hard code : (10-15 mins) 
        Create an array as Global variable in extension setting/module file with Key as State/Province ID And value as array('HST_GST' => 5, 'PST' =>7). (eg for British Columbia). Use Global variable to get the tax values for calculating taxes. 

    Configure from UI: (6-8 hours) JM: Ignore for phase 1 which will just use hardcoding. 
        Create a Setting Page where admin is allowed to add or change the values for HST/GST, PST for Province and fetch from DB the tax values for calculating taxes. 
            Create a Menu Link 'Sales Tax Rate' in Administer -> CiviContribute -> Sales Tax Rate 
            Create a Admin form 'Sales Tax Rate' with CiviCRM Administer Access permission. 
            Add fields like in attached Sales_Tax_Rate.png 
            Form rule to restrict creation tax rate for same Province. 
            On Submission save data in civicrm_settings tables using either group_name = 'Canadian PST Rates' or 'Canadian GST/HST Rates', name=province id, value = rate stored in appropriate non-float format for calculation, eg 12.75. 
            Create a static variable and function which will retrieve these values and assign them to a static variable in the form of array with Key as State/Province ID And value as array('HST_GST' => 5.0 'PST' =>7.0) 

Configuring 'Sales Account is' for GST/HST and PST (4-5 hours) Let's hardcode as defined below. 
    Add a checkbox on Add/Update Financial Account form as 'Is PST?'. 
    Rename 'Is Tax' to 'Is GST/HST?' 
    Best place to save the value of #1 for financial account in table? civicrm_settings or civicrm_option_value? 
    On form provide a way to add account relationship for 'Is PHT?' financial Account. (Do we need to add new account relationship as 'Sales account is (PST)' or use the existing Account relationship). 
Hmm. Reasonable design. Probably can avoid it for one-off this week. Worried it doesn't work when this extension may eventuall need to coexist with tax extensions for other countries. We should consult others and let them help design this as not needed for this phase. 
How about we insert a GST/HST financial account, and one PST for each relevant province (ie 4 or so, eg Manitoba PST). And we hard code these financial accounts into an array with province id as its key for Phase 1. Phase 2 we create Settings group 'Canadian Sales Tax Accounts', and use names for accounts and values that hold their account machine name or just ID if that is all there is in relevant table. 

Calculating Taxes on Online Membership Page(8-10 hours) 
This needs refinement to avoid showing tax fields until membership type and province are known. It also needs to work for anonymous and authenticated users. I've spec'ed it for jQuery to calculate the taxes and show/hide the fields. 

    Use hook_civicrm_buildform() or hook_civicrm_buildAmount() for $formName = 'CRM_Contribute_Form_Contribution_Main' and Membership new or renewal and Contribution Page ID and Price set id. Create two invisible and frozen (ie user can't edit but jQuery can) tax fields. 
    Logged in user will have their primary address populated from db. As that is the only address on this form and it is the correct one, we'll be using it as the place of supply. Either logged in or anonymous user may have no province defined at load time. By end of form load the taxes should be calculated and displayed if country=Canada and province <> null and there is a default membership type e.g. on renewal, and unused tax field should be hidden. Can we send jQuery that would do all of the work using the two arrays defined above? This would allow field exit event for membership type and province to trigger recalculate function. Let Province field exit proceed if field is null, but do form validation on server to validate province is non-null and valid if country= Canada. (Phase 2: add in browser form validation on submit for this rule.) 
    On server, validate that the tax calculations done in browser are correct in order to prevent spoofing attacks. 

Algorithm for both browser jQuery and server php: 
Ensure that recalculating the taxes does not lead to them being duplicated, I think by removing the the code equivalent of the schema's financial_line_items for taxes that are linked to the line_item before the recalculation, and recalculating total as just the membership cost. 
        If no province, then exit 
        Get Tax rates for the Province. 
        Foreach Line item(for membership_type_id IS NOT NULL) 
            if Financial type is configured for GST/HST 
                calculate GST/HST for item 
                total += GST/HST amount 
                label = province's GST/HST label 
            if financial type is configured for PST 
                calculate PST for the item 
                total += PST amount 
                label = province's PST label 
    Make Sure the Tax is recalculated when a form is submitted using hook_civicrm_postProcess() and assigned to $form->_lineItems(Calculate tax using submitted province or user saved province). 


     For Anonymous User will the Tax be calculated? Since we cant predict the Province until the user feeds in. Or do we need to Add JS to calculate Tax on selection of Province? 
Use JS. 
     For Authenticated user, 
        If the user has Province set to Ontario and if he changes the billing address and set to different Province, then do we need to recalculate the tax as per changed Province? 
Yes. recalc on province field exit 
        Cons: If the user wants to save the tax then he can update the Province or use different province for billing and then purchase a membership. :-( 
(The tax system depends on honesty.) 

Showing Taxes on Confirm, Thank You Page and Email Receipt(6-8 hours) 

    Use hook_civicrm_buildform() and assign $form->_lineItems to smarty. 
    Create custom template file Confirm.extra.tpl and ThankYou.extra.tpl and add JS/JQuery to replace the Line item section with new Line item included with tax column (as attached in line_items.png). 
    Change the message template code to include tax columns. 

Entries in Database(using postProcess, post and pre hooks) (5 Hours) 
Scenario: If a User purchase a Membership of amount $100 with GST = 5% and PST = 10% 
     Total Amount = 100 + 5 + 10 = 115 
Entries in : 

a. 1 entry in civicrm_contribution(no changes required) 
     total_amount = 115.00 
     tax_amount = 15.00 

b. 1 entry in civicrm_financ
ial_trxn (no changes required) 

   total_amount = 115.00 

c. 1 entry for each item selected in civicrm_line_item (need to check if tax_amount is set properly) 
  line_total = 115.00 
  tax_amount = 15.00 
(follow what VAT did) 

d. 3 entries in civicrm_financial_item for #c 
    1. amount = 100.00 
         entity_table = civicrm_line_item 
         entity_id = 
         financial_account_id = income account of #c.financial_type_id 
     2. amount = 5.00 
         entity_table = civicrm_line_item 
         entity_id = 
         financial_account_id = sales account of #c.financial_type_id(GST/HST) 

     3. amount = 10.00 
         entity_table = civicrm_line_item 
         entity_id = 
         financial_account_id = sales account of #c.financial_type_id(PST) 
NB: make sure it is the correct PST, since there are different PSTs for each relevant province. 

e. 4 entries in civicrm_entity_financial_trxn 
    1. amount = 115.00 
         entity_table = civicrm_contribution 
         entity_id = 
     financial_trxn_id = ( 

    2. amount = 100.00 
         entity_table = civicrm_financial_item 
         entity_id = 
     financial_trxn_id = ( 

    3. amount = 5.00 
         entity_table = civicrm_financial_item 
         entity_id = 
     financial_trxn_id = ( 

   4. amount = 10.00 
         entity_table = civicrm_financial_item 
         entity_id = 
     financial_trxn_id = ( 

Note: The above scenario for db entry, line item display are for non-quick config price set. The display would differ for quick config price set. 
Just support non-quick config for Phase 1. 

Joe to confirm: 
1. Handle quick config priceset or non-quick config price set or both. 
non-quick config 
2. Create new extension or use already created extension. 
We need a new extension that takes advantage of new VAT-type functionality in core. 
3. Handling for online Membership page or Backoffice (Add or/and Edit) membership or both. 
online and backoffice. 
Both. Start with front office. 
5. Handle tax for only Membership price filelds only? 

 Yes, just for the one membership page of our client for now. Phase 2 will generalize.

  • Aucun

Creative Commons License
Except where otherwise noted, content on this site is licensed under a Creative Commons Attribution-Share Alike 3.0 United States Licence.