Taxes - the rates charged on invoice and quote lines, and how new * lines get one. * * A rate is a single percent, or parts (state and county, GST and PST) each * charged on the line or on top of the parts before it. Changing the rate of * one already billed makes a new rate in its place (functions/tax.php: * taxChangeRate()), so each shows what uses it and, once archived, what * replaced it. A rate nothing has used can be deleted. */ // Default Column Sortby Filter $sort = "tax_name"; $order = "ASC"; require_once "includes/inc_all_admin.php"; // ?archived=1 is how older links asked for the archived ones $show_archived = ($_GET['show'] ?? '') === 'archived' || !empty($_GET['archived']); // Only these sort - an unknown column would take the page down. Used on sorts by the invoices with a line at it $sortable_columns = [ 'tax_name' => "tax_name $order, tax_percent ASC", 'tax_percent' => "tax_percent $order, tax_name ASC", 'invoice_count' => "invoice_count $order, tax_name ASC", ]; if (!isset($sortable_columns[$sort])) { $sort = 'tax_name'; } $search = "(tax_name LIKE '%$q%' OR tax_description LIKE '%$q%' OR tax_registration_number LIKE '%$q%')"; $sql = mysqli_query($mysqli, "SELECT SQL_CALC_FOUND_ROWS tax_id, COALESCE(invoices.invoice_count, 0) AS invoice_count FROM taxes LEFT JOIN (SELECT item_tax_id, COUNT(DISTINCT item_invoice_id) AS invoice_count FROM invoice_items WHERE item_tax_id > 0 GROUP BY item_tax_id) invoices ON invoices.item_tax_id = tax_id WHERE $search AND tax_archived_at IS " . ($show_archived ? 'NOT NULL' : 'NULL') . " ORDER BY {$sortable_columns[$sort]}, tax_id ASC LIMIT $record_from, $record_to"); $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()")); $rates = []; while ($row = mysqli_fetch_row($sql)) { $rates[] = taxRow($row[0]); } $counts = mysqli_fetch_row(mysqli_query($mysqli, "SELECT COALESCE(SUM(tax_archived_at IS NULL), 0), COALESCE(SUM(tax_archived_at IS NOT NULL), 0) FROM taxes WHERE $search")); $usage = taxUsageAll(); $tax_settings = taxSettings(); $active_taxes = billingTaxes(); // "4 invoices, 1 quote" - what has a line at a rate, and what picks it for new ones $used_text = function (array $u) { $words = ['invoices' => ['invoice', 'invoices'], 'quotes' => ['quote', 'quotes'], 'recurring' => ['recurring invoice', 'recurring invoices'], 'products' => ['product', 'products'], 'clients' => ['client', 'clients']]; $parts = []; foreach ($words as $key => [$one, $many]) { if ($u[$key]) { $parts[] = number_format($u[$key]) . ' ' . ($u[$key] === 1 ? $one : $many); } } return $parts; }; $sort_link = function ($column, $label) use ($url_query_strings_sort, $sort, $disp, $order_icon) { return '' . $label . ($sort === $column ? " $order_icon" : '') . ''; }; ?>
| = $sort_link('tax_name', 'Tax') ?> | = $sort_link('tax_percent', 'Rate') ?> | = $sort_link('invoice_count', 'Used on') ?> | |
|---|---|---|---|
|
= escapeHtml($rate['tax_name']) ?>
Default
= escapeHtml($rate['tax_description']) ?>
= escapeHtml($rate['tax_registration_number']) ?>
= $replaced_by ? 'Replaced by ' . escapeHtml(taxLabel($replaced_by)) . ' on ' : 'Archived ' ?>= date('M j, Y', strtotime($rate['tax_archived_at'])) ?>
= $used ? escapeHtml(implode(', ', $used)) : 'Not used yet' ?>
|
= taxPercentText($rate['tax_percent']) ?>%
$part) { ?>
= $i ? '+' : '' ?>>= escapeHtml($part['name']) ?> = taxPercentText($part['percent']) ?>%= $part['compound'] ? ' on top' : '' ?>= $part['registration'] !== '' ? '' : '' ?>
|
= $used ? escapeHtml(implode(', ', $used)) : 'Not used yet' ?> |
|
Changing a rate. Once a rate is on an invoice or quote, saving a new percent or parts makes a new rate with the same name. What was already billed keeps the old rate; products, clients, the default and recurring invoices move to the new one.
Exempt clients. Mark a client tax exempt, with its certificate, on the client's Billing tab. Its new lines get no tax, and its invoices say why.
Filing. The Tax Collected report adds up each part by month or quarter, invoiced or paid, with exempt sales and the tax paid on expenses.