Freeside 4 refactors the code for tax calculation into "tax engines". This document describes the interface to use a tax engine for tax calculation, or to implement a new tax calculation method.
A tax engine is a method for calculating tax on invoices. Before version 4, Freeside supported two methods:
- "Internal" tax, using the cust_main_county table to define tax rates manually. Tax elements on each line item are stored in cust_bill_pkg_tax_location and linked to both cust_main_county and cust_location.
- "External" tax using tax tables downloaded from CCH. The name is misleading, as Freeside performs the tax calculations internally using tables stored in the Freeside database.
Starting with version 4, "External" is renamed to "CCH", and the following new methods are available:
The enable_taxproducts config sets the tax engine to use on the system. FS::TaxEngine->new will return an object of that type.
The "normal" workflow is:
- As each line item is created, call add_sale, passing that line item. Not all tax engines use this.
- Insert the invoice and its line items.
- Call calculate_taxes, passing the FS::cust_bill object.
- This calls make_taxlines to create a list of tax elements.
- It then calls consolidate_taxlines to group the elements by tax name and create a list of tax line items (FS::cust_bill_pkg objects).
- calculate_taxes will return that list, with each line item linked to an array of tax elements.
- Insert the tax line items.
- Add the total tax to the invoice's "charged" field, and set the "pending" field to null.
FS::TaxEngine objects also implement a few peripheral methods:
- info: Returns some metadata about the tax engine, such as which tables it uses for tax rates and elements, whether it requires batch submission of invoices, allows manual location selection, etc.
- cust_tax_locations: Takes an FS::cust_location object, returns a list of tax jurisdictions that it could possibly be in. Only used if manual location selection is allowed.
- add_taxproduct: A "taxproduct" (FS::part_pkg_taxproduct) is a category of taxable products. Some tax systems predefine these and provide a large list. Others expect the user to define them; in that case, this method is called when the user tries to manually add a taxproduct to the system.
Finally, each tax engine can provide a UI component (under browse/part_pkg_taxproduct/) to select the taxproduct when editing a package definition. This is necessary because the part_pkg_taxproduct table is used in different ways by different engines. See examples in that directory.
You must implement either make_taxlines or calculate_taxes. Everything else is optional.
If your calculation method produces pre-consolidated line items (that is, one line for each distinct tax name), it should be implemented as calculate_taxes. This takes an FS::cust_bill object and returns a list of FS::cust_bill_pkg objects for the line items. Each of these should have tax elements (either FS::cust_bill_pkg_tax_location or FS::cust_bill_pkg_tax_rate_location) that reference the "billpkgnum" fields of the line items tax was charged on.
If your calculation method just returns a list of taxes for each taxed line item, it should be implemented as make_taxlines. This also takes an FS::cust_bill object, and returns a list of tax elements. The tax line items will be created from those. In this case, the info hash will need to include link_table, the name of the table where you store tax elements (e.g. cust_bill_pkg_tax_location).