Skip to main content

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:

  1. Location Rules: Define what the tax rate is (e.g., 10%).
  2. Tax Type: Defines how tax is calculated (Inclusive vs Exclusive).
  3. 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

FieldDatabase ColumnDescription
Service Tax Rateservice_taxPercentage tax applied to Services, Gift Cards, & Memberships
Product Tax Rateproduct_taxPercentage tax applied to Retail Products
Tax Calculationtax_tyoe0 = Inclusive (Price includes tax)
1 = Exclusive (Price + Tax)

[!WARNING] Database Field Note: The tax type column is named tax_tyoe (typo in schema) in the locations table.

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_tax rate.

Products

  • Flag: charge_tax (boolean)
  • Logic: Uses Location's product_tax rate.
  • Configuration: Found in the "Add/Edit Product" form.

Gift Cards

  • Flag: charge_service_tax
  • Logic: Uses Location's service_tax rate.
  • 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_tax rate.

[!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_tax rate.

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 NameTax Flag ColumnRate Source
servicesservice_taxlocations.service_tax
productscharge_taxlocations.product_tax
giftcardscharge_service_taxlocations.service_tax
membershipscharce_service_taxlocations.service_tax
packagestaxlocations.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.