Tax System Configuration
The SalonNZ platform implements a flexible Location-Based Tax System with Item-Level Applicability. This architecture allows different branches to operate under different tax jurisdictions while giving granular control over which items are taxable.
Architectural Overview
The tax engine determines the final price based on three layers of configuration:
- Location Rules: Define what the tax rate is (e.g., 10%).
- Tax Type: Defines how tax is calculated (Inclusive vs Exclusive).
- Item Flags: Define if tax applies to a specific item (Service/Product/Card).
1. Location Configuration
Tax rates are set globally per location. This ensures that all sales originating from a specific branch adhere to its local tax laws.
Navigation: Admin Panel → Settings → Location → Edit Location
| Field | Database Column | Description |
|---|---|---|
| Service Tax Rate | service_tax | Percentage tax applied to Services, Gift Cards, & Memberships |
| Product Tax Rate | product_tax | Percentage tax applied to Retail Products |
| Tax Calculation | tax_tyoe | 0 = Inclusive (Price includes tax) 1 = Exclusive (Price + Tax) |
[!WARNING] Database Field Note: The tax type column is named
tax_tyoe(typo in schema) in thelocationstable.
Inclusive vs Exclusive
-
Tax Exclusive (
1):- Item Price: $100
- Tax Rate: 10%
- Customer Pays: $110 ($100 + $10 tax)
-
Tax Inclusive (
0):- Item Price: $110
- Tax Rate: 10%
- Customer Pays: $110 ($100 base + $10 tax embedded)
2. Item-Level Applicability
Each "sellable" entity in the system has a dedicated flag to enable or disable tax. This allows for complex scenarios like "Tax-free Gift Cards" vs "Taxable Products".
Services
- Flag:
service_tax(boolean) - Logic: Uses Location's
service_taxrate.
Products
- Flag:
charge_tax(boolean) - Logic: Uses Location's
product_taxrate. - Configuration: Found in the "Add/Edit Product" form.
Gift Cards
- Flag:
charge_service_tax - Logic: Uses Location's
service_taxrate. - Note: Often set to No (0) if tax is collected upon redemption, or Yes (1) if collected at purchase.
Memberships
- Flag:
charce_service_tax - Logic: Uses Location's
service_taxrate.
[!CAUTION] Typo Alert: The membership table uses
charce_service_tax(with a 'c' instead of 'g'). Ensure your API payloads match this exact spelling.
Packages
- Flag:
tax - Logic: Uses Location's
service_taxrate.
3. Database Schema
Locations Table (locations)
CREATE TABLE locations (
id BIGINT PRIMARY KEY,
name VARCHAR(255),
tax_tyoe ENUM('0','1') DEFAULT '0', -- 0=Inclusive, 1=Exclusive
service_tax FLOAT DEFAULT 0,
product_tax FLOAT DEFAULT 0,
...
);
Affected Item Tables
| Table Name | Tax Flag Column | Rate Source |
|---|---|---|
services | service_tax | locations.service_tax |
products | charge_tax | locations.product_tax |
giftcards | charge_service_tax | locations.service_tax |
memberships | charce_service_tax | locations.service_tax |
packages | tax | locations.service_tax |
4. Integration Guide
Calculating Tax in Frontend
When building custom checkout flows, use this logic to calculate totals:
const calculateTotal = (item, location) => {
let taxAmount = 0;
let subtotal = parseFloat(item.price);
// 1. Check if item is taxable
const isTaxable = (
item.charge_service_tax == 1 ||
item.charce_service_tax == 1 ||
item.charge_tax == 1
);
// 2. Select correct rate (Service vs Product)
const taxRate = item.type === 'product'
? parseFloat(location.product_tax)
: parseFloat(location.service_tax);
if (isTaxable && taxRate > 0) {
if (location.tax_tyoe == '1') {
// Exclusive: Add tax on top
taxAmount = (subtotal * taxRate) / 100;
} else {
// Inclusive: Extract tax from price
// Tax = Price - (Price / (1 + Rate/100))
taxAmount = subtotal - (subtotal / (1 + taxRate / 100));
subtotal = subtotal - taxAmount; // Adjust base price
}
}
return {
subtotal: subtotal.toFixed(2),
tax: taxAmount.toFixed(2),
total: (subtotal + taxAmount).toFixed(2)
};
};
5. Troubleshooting & FAQ
Q: I updated the location tax rate, but existing orders didn't change. A: Correct. Tax rates are snapshotted at the time of sale. Changing the global rate only affects future orders.
Q: Why is my membership tax calculation failing?
A: Check the spelling of the flag. Memberships specifically use charce_service_tax instead of charge_service_tax.
Q: Can I have different tax rates for different services?
A: No. The service_tax rate is global for the Location. You can only toggle it ON or OFF per service. To achieve different effective rates, you would need to adjust the base price.