Tax Rates
Tax rates define the tax percentages applied to transactions. Moon ERP supports both VAT (Value Added Tax) and withholding tax types. Each tax rate can be linked to a GL account for automatic posting and can be marked as inclusive (tax included in the price) or exclusive (tax added on top).
Purpose
- Define VAT and withholding tax rates with bilingual names
- Link each tax rate to a GL account for automatic journal entry posting
- Support inclusive and exclusive tax calculation methods
- Set a default tax rate per company for streamlined data entry
- Enable tax reporting and compliance
Entity Attributes
Tax Rate
| Field | Type | Description |
|---|---|---|
id | bigint | Primary key |
company_id | bigint | FK to companies |
name | string | English name (e.g., "VAT 5%") |
name_ar | string | Arabic name (e.g., "ضريبة القيمة المضافة 5%") |
code | string(50) | Unique code (e.g., VAT5, WHT10) |
rate | decimal(8,4) | Percentage rate (e.g., 5.0000 for 5%) |
tax_type | enum | vat or withholding |
is_inclusive | boolean | Whether tax is included in the price (default: false) |
is_default | boolean | Whether this is the default tax rate (default: false) |
account_id | bigint? | FK to accounts (GL account for tax liability/asset) |
is_active | boolean | Whether the tax rate is available for use |
created_at | timestamp | Creation timestamp |
updated_at | timestamp | Last update timestamp |
deleted_at | timestamp? | Soft-delete timestamp |
Indexes:
UNIQUE (company_id, code)
Relationships
Tax Types
| Type | Value | Description |
|---|---|---|
| VAT | vat | Value Added Tax -- charged on sales and purchases |
| Withholding | withholding | Withholding tax -- deducted at source |
API Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/accounting/tax-rates | List tax rates (paginated) |
POST | /api/accounting/tax-rates | Create a new tax rate |
GET | /api/accounting/tax-rates/{id} | Get a single tax rate |
PUT | /api/accounting/tax-rates/{id} | Update a tax rate |
DELETE | /api/accounting/tax-rates/{id} | Soft-delete a tax rate |
Request/Response Examples
Create Tax Rate
Request POST /api/accounting/tax-rates
curl -X POST https://moon-erp.test/api/accounting/tax-rates \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"name": "VAT 5%",
"name_ar": "ضريبة القيمة المضافة 5%",
"code": "VAT5",
"rate": "5.0000",
"tax_type": "vat",
"is_inclusive": false,
"is_default": true,
"account_id": 19,
"is_active": true
}'final response = await http.post(
Uri.parse('https://moon-erp.test/api/accounting/tax-rates'),
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: jsonEncode({
'name': 'VAT 5%',
'name_ar': 'ضريبة القيمة المضافة 5%',
'code': 'VAT5',
'rate': '5.0000',
'tax_type': 'vat',
'is_inclusive': false,
'is_default': true,
'account_id': 19,
'is_active': true,
}),
);Response 201 Created
{
"data": {
"id": 1,
"company_id": 1,
"name": "VAT 5%",
"name_en": "VAT 5%",
"name_ar": "ضريبة القيمة المضافة 5%",
"code": "VAT5",
"rate": "5.0000",
"tax_type": "vat",
"tax_type_label": "VAT",
"is_inclusive": false,
"is_default": true,
"account_id": 19,
"account": {
"id": 19,
"code": "2105",
"name": "Taxes Payable",
"name_ar": "ضرائب مستحقة"
},
"is_active": true,
"created_at": "2026-02-16T12:00:00.000000Z",
"updated_at": "2026-02-16T12:00:00.000000Z"
}
}List Tax Rates
Request GET /api/accounting/tax-rates
Response 200 OK
{
"data": [
{
"id": 1,
"name": "ضريبة القيمة المضافة 5%",
"code": "VAT5",
"rate": "5.0000",
"tax_type": "vat",
"is_inclusive": false,
"is_default": true,
"is_active": true
},
{
"id": 2,
"name": "ضريبة الخصم والإضافة 1%",
"code": "WHT1",
"rate": "1.0000",
"tax_type": "withholding",
"is_inclusive": false,
"is_default": false,
"is_active": true
}
],
"links": { "first": "...", "last": "...", "prev": null, "next": null },
"meta": { "current_page": 1, "last_page": 1, "per_page": 25, "total": 2 }
}Business Rules
- Code uniqueness -- Tax rate codes must be unique per company.
- Rate as percentage -- The
ratefield stores a percentage value. For example,5.0000represents 5%. It usesdecimal(8,4)for precision. - One default -- Only one tax rate can be marked as
is_default = trueper company. Setting a new default should unset the previous one. - GL account link -- The
account_idlinks to the GL account where tax amounts are posted (typically a "Taxes Payable" liability account for VAT, or a "Tax Receivable" asset account for withholding). - Cannot delete used rates -- Tax rates referenced in transactions cannot be deleted.
- Active status -- Only active tax rates can be applied to new transactions.
Tax Calculation Methods
Exclusive Tax (Default)
Tax is added on top of the base amount:
Base amount: 1,000.000 KWD
Tax rate: 5%
Tax amount: 1,000.000 * 0.05 = 50.000 KWD
Total: 1,050.000 KWDInclusive Tax
Tax is already included in the total amount:
Total amount: 1,050.000 KWD
Tax rate: 5%
Base amount: 1,050.000 / 1.05 = 1,000.000 KWD
Tax amount: 1,050.000 - 1,000.000 = 50.000 KWDCompound Tax (tax on tax)
When several rates apply in sequence, each rate is charged on the running total — the base plus the tax accumulated so far:
Base amount: 100.000
Tax A (10%): 100.000 * 10% = 10.000 → running 110.000
Tax B (5%): 110.000 * 5% = 5.500 → running 115.500
Total tax: 15.500 Total: 115.500Multi-line Tax
A document with several lines computes tax per line — each line's tax is rounded to 3 decimals, then the rounded line values are summed so the printed total always foots.
Single source of truth
All tax math runs through TaxService (calculateTax, calculateCompound, calculateMultiLine). Controllers never compute amount * rate inline — that path silently ignored inclusive pricing.
Common Tax Configurations
| Region | Tax | Code | Rate | Type |
|---|---|---|---|---|
| Kuwait | VAT | VAT5 | 5.0000 | vat |
| Egypt | VAT | VAT14 | 14.0000 | vat |
| Egypt | Withholding | WHT1 | 1.0000 | withholding |
| Egypt | Withholding | WHT3 | 3.0000 | withholding |
| Saudi Arabia | VAT | VAT15 | 15.0000 | vat |