Saltar al contenido principal

Tax Definitions

Overview

The tax-definitions module manages a global catalog of tax definitions (IVA, ISR, withholding taxes, etc.) scoped by country. These are reference records that businesses link to via the business-tax-config module.

Tax definitions are not business-specific — they are shared across all tenants. A tax definition describes a tax rate, type, and country, which businesses can then adopt into their own tax configuration.


Domain Concepts

ConceptDescription
TaxDefinitionA globally-defined tax record (e.g. "IVA Guatemala 12%"). Contains name, code, rate, type, and country.
isInclusiveWhether the tax is already included in prices (tax-inclusive pricing) or added on top.
assignByDefaultToEntity type this tax is automatically assigned to (e.g. product, service).
countryIdISO country code (e.g. gt, mx, us) used to scope taxes by country.
businessTaxConfigA separate module that links businesses to tax definitions. This module provides query endpoints that join with businessTaxConfig for convenience.

Architecture

This module follows Hexagonal Architecture with a repository injection token:

tax-definitions/
tax-definitions.module.ts <- NestJS module (DI config)
domain/
tax-definitions-repository.domain.ts <- ITaxDefinitionsRepository port + TAX_DEFINITIONS_REPOSITORY token
application/
tax-definitions.service.ts <- Use cases (depends on port interface)
infrastructure/
tax-definitions.repository.ts <- Kysely adapter implementing the port
interfaces/
tax-definitions.controller.ts <- HTTP routes with Swagger documentation
dtos/
create-tax-definition.dto.ts <- POST request body
update-tax-definition.dto.ts <- PATCH request body (partial)
tax-definition-response.dto.ts <- Swagger response schemas
query/
paginate-tax-definitions.query.ts <- GET query parameters

Dependency direction: Controller -> Service -> Repository interface <- Repository implementation

The service is injected with the TAX_DEFINITIONS_REPOSITORY symbol token, depending only on the ITaxDefinitionsRepository interface — never on the concrete Kysely implementation.


API Endpoints

All endpoints require Bearer token authentication (global AuthGuard).

MethodPathDescription
POST/tax-definitionsCreate a new tax definition
GET/tax-definitionsList tax definitions (paginated, filterable)
GET/tax-definitions/:idGet a tax definition by ID
PATCH/tax-definitions/:idPartially update a tax definition
DELETE/tax-definitions/:idDelete a tax definition (204 No Content)
GET/tax-definitions/with-relation/:businessIdGet tax definitions linked to a business
GET/tax-definitions/unlinked-or-global/:businessIdGet all taxes for a country with link status

GET /tax-definitions

Query parameters:

ParameterTypeRequiredDescription
countryIdstringNoFilter by country code (e.g. gt)
searchstringNoSearch across name, code, type, and countryId
pagenumberNoPage number (default 1)
sizenumberNoPage size (default 10; 0 = no limit)
orderBystringNoSort field: name, code, or type
orderstringNoSort direction: asc or desc

POST /tax-definitions

{
"name": "IVA Guatemala",
"code": "IVA-GT",
"rate": 12,
"type": "percentage",
"isInclusive": true,
"countryId": "gt"
}

PATCH /tax-definitions/:id

All fields are optional:

{
"rate": 7
}

GET /tax-definitions/with-relation/:businessId

Returns only tax definitions that are actively linked to the given business (INNER JOIN on businessTaxConfig where isActive = true). Includes businessTaxConfigId in the response.

GET /tax-definitions/unlinked-or-global/:businessId?countryId=gt

Returns all active tax definitions for the given country, with a businessTaxConfigId field indicating whether each is linked to the business (btcOrder: 1) or unlinked (btcOrder: 2, businessTaxConfigId: null).


Bruno API Collection

Located at api-client/flowpos/collections/tax-definitions/. Uses environment variables:

VariableDescription
{{BASE_URL}}API base URL (e.g. http://localhost:4000)
{{ID_TOKEN}}Firebase Bearer token
{{businessId}}Target business UUID
{{taxDefinitionId}}Auto-set after POST by the after-response script

Design Decisions

  • No RBAC guards — Tax definitions are global reference data, not business-scoped. The global AuthGuard ensures only authenticated users can access them, but no role or CASL checks are applied. Business-level access control is handled by the business-tax-config module.
  • Business relation queries in this module — The with-relation and unlinked-or-global endpoints join with businessTaxConfig for convenience, avoiding a separate API call + client-side merge. This is a read-only concern and doesn't violate module boundaries.
  • btcOrder sort field — Returned in business relation queries to allow the UI to display linked taxes before unlinked ones without client-side sorting.
  • No transactions for reads — Read queries don't use transactions to avoid tying up connection pool resources unnecessarily.
  • PartialType from @nestjs/swagger — The update DTO uses Swagger's PartialType (not @nestjs/mapped-types) so all optional fields are discoverable in Swagger UI.