Tax (VAT)
Hoikka uses a gross pricing strategy where all prices include VAT. This follows Finnish e-commerce conventions.
Key Concepts
- Gross prices: All stored prices include VAT
- Back-calculation: Net prices are calculated from gross at checkout
- Group-based exemption: Customers in a tax-exempt group pay net prices
Tax Rates
Finnish VAT rates are supported:
| Code | Rate | Description |
|---|---|---|
standard | 25.5% | Default (most products) |
food | 13.5% | Food items |
books | 13.5% | Books, newspapers |
zero | 0% | Exports, exempt |
Unknown tax codes fall back to the standard rate (0.255). Rates are stored in the database as integer basis points with 4 decimals (0.255 → 2550); business logic works with decimals.
Product Tax Code
Tax codes live on categories. A product’s tax code is resolved from its first assigned category at cart/checkout time; products without a category use standard:
import { categoryService } from "$lib/server/services/categories.js";
const taxCode = await categoryService.getProductTaxCode(productId); // e.g. "food"Tax Calculation
Tax is back-calculated from the gross price:
import { taxService } from "$lib/server/services/tax.js";
// Calculate tax breakdown
const result = taxService.calculateTax(2510, 0.255);
// { gross: 2510, net: 2000, tax: 510 }
// Formula:
// net = gross / (1 + taxRate)
// tax = gross - netOrder Tax Fields
Orders track tax information:
order = {
subtotal: 5020, // Gross subtotal (VAT-inclusive)
taxTotal: 1020, // Total VAT amount
totalNet: 4000, // Subtotal minus VAT
isTaxExempt: false, // Group-based exemption flag
total: 6020 // Final total
};Order Line Tax Fields
Each order line stores tax details:
orderLine = {
unitPrice: 2510, // Gross unit price
unitPriceNet: 2000, // Net unit price
lineTotal: 5020, // Gross line total
lineTotalNet: 4000, // Net line total
taxCode: "standard",
taxRate: 2550, // basis points, 4 decimals (0.255)
taxAmount: 1020
};B2B Tax Exemption
Tax exemption is group-based. A customer group with isTaxExempt = true makes all its members tax-exempt:
// Check exemption — looks up customer's groups for isTaxExempt flag
const isExempt = await taxService.isCustomerTaxExempt(customerId);
// For exempt customers:
// - Prices shown without VAT
// - taxTotal = 0
// - order.isTaxExempt = trueTo make customers tax-exempt: create a customer group with isTaxExempt enabled in Admin > Customers > Groups, then add the customer to that group.
Admin: Setting Tax Code
Tax codes are set on categories:
- Admin > Categories, edit a category
- Select the “Tax Rate” dropdown
- Choose: Standard VAT (25.5%), Food VAT (13.5%), Books VAT (13.5%), or Zero VAT (0%)
Checkout Display
The checkout shows tax breakdown:
Subtotal: 50.20 EUR
VAT (included): 10.20 EUR
Shipping: 5.00 EUR
─────────────────────────
Total: 55.20 EURFor tax-exempt customers:
Subtotal: 40.00 EUR
VAT: Tax exempt (B2B)
Shipping: 5.00 EUR
─────────────────────────
Total: 45.00 EUR
Net amount: 45.00 EUR (VAT 0%)Tax Service API
import { taxService } from "$lib/server/services/tax.js";
// Get rate for a tax code
const rate = await taxService.getTaxRate("standard"); // 0.255
// Get all available rates
const rates = await taxService.getAllTaxRates();
// Calculate tax from gross price
const { gross, net, tax } = taxService.calculateTax(2510, 0.255);
// Calculate line item tax
const lineTax = taxService.calculateLineTax(grossUnitPrice, quantity, taxRate, isTaxExempt);
// Check customer exemption (group-based)
const exempt = await taxService.isCustomerTaxExempt(customerId);
// Seed default rates (run once)
await taxService.seedDefaultRates();