Saltar al contenido principal

Categories Module

Overview

The categories module manages product and service categories within FlowPOS. Categories are used to organize the catalog and support filtering, reporting, and merchandising.

Categories are business-scoped: every category belongs to a specific business and is isolated from other businesses.


Architecture

The module follows Hexagonal Architecture with strict layer boundaries.

categories/
├── categories.module.ts # NestJS module wiring
├── domain/
│ └── categories-repository.domain.ts # ICategoriesRepository port
├── application/
│ └── categories.service.ts # Use cases (CRUD + delete-with-replacement)
├── infrastructure/
│ └── categories.repository.ts # Kysely DB adapter (implements port)
└── interfaces/
├── categories.controller.ts # HTTP adapter
├── dtos/
│ ├── create-category.dto.ts
│ ├── update-category.dto.ts
│ └── delete-category.dto.ts
└── query/
└── paginate-categories.query.ts

Layer responsibilities

LayerResponsibility
domain/Repository port (ICategoriesRepository).
application/CategoriesService — orchestrates CRUD and the delete-with-replacement use case (transactional reassignment of products/services).
infrastructure/CategoriesRepository — Kysely queries against the category table. Implements ICategoriesRepository.
interfaces/CategoriesController — maps HTTP requests to service calls. Thin layer with no business logic.

Dependency rule

interfaces → application → domain ← infrastructure

Domain Concepts

Category

A named grouping used to classify products and services.

FieldTypeDescription
idUUIDPrimary key (auto-generated)
namestringDisplay name (required)
businessIdUUIDFK to business
categoryCodestringOptional external code for integrations/imports
isActivebooleanDefaults to true
createdByUUIDFK to user
updatedByUUIDFK to user (nullable)
createdAttimestamptzAuto-set on creation
updatedAttimestamptzSet on update

Delete with Replacement

When deleting a category that has products or services assigned to it, you can provide a replaceCategoryId. This triggers a transactional flow:

  1. Validate the replacement category exists
  2. Reassign all products with the old category to the replacement
  3. Reassign all services with the old category to the replacement
  4. Delete the original category

All steps execute within a single database transaction.


API Endpoints

All endpoints require Bearer token authentication and are scoped to PolicyResource.Category for RBAC.

POST /categories

Create a new category.

Body:

{
"name": "Beverages",
"businessId": "<uuid>",
"isActive": true,
"createdBy": "<uuid>"
}

GET /categories

List categories with pagination, search, and sorting.

Query parameters:

ParamRequiredDescription
businessIdYesUUID of the business
pageNoPage number (default: 1)
sizeNoPage size (default: 10)
searchNoCase-insensitive name search
orderByNoSort field (name)
orderNoasc or desc

GET /categories/:id

Get a single category by UUID.

Query parameters: businessId (required)

PATCH /categories/:id

Partially update a category.

Body:

{
"name": "Updated Name",
"businessId": "<uuid>",
"updatedBy": "<uuid>"
}

DELETE /categories/:id

Delete a category. Optionally reassign products/services to another category.

Query parameters: businessId (required)

Body (optional):

{
"replaceCategoryId": "<uuid>"
}

Data Import

Categories can be bulk-imported via the data-import module. The CategoryImportHandler supports:

  • Required field: name
  • Optional field: code (maps to categoryCode)
  • Upsert behavior: matches by name within the business; updates categoryCode if the category exists, creates a new one otherwise.

  • Products — products reference categoryId as a FK
  • Services — services reference categoryId as a FK
  • MarkdownMarkdownScopeType.Category enables category-level price markdowns
  • Collections — merchandising collections are a separate grouping mechanism