Saltar al contenido principal

Entity Parameters

Overview

The Entity Parameters module provides a flexible key-value configuration and feature-flag system. It allows business-level and entity-level (e.g., location) parameter overrides by linking entries from a Parameter Catalog to specific entities.

Architecture

entity-parameters/
├── entity-parameters.module.ts # NestJS module (DI wiring)
├── domain/
│ └── entity-parameters-repository.domain.ts # Port interface + DI token + parseBooleanParameterValue
├── application/
│ └── entity-parameters.service.ts # Use cases (CRUD + getBooleanForBusiness)
├── infrastructure/
│ ├── entity-parameters.repository.ts # Kysely DB adapter
│ └── feature-parameter.guard.ts # LoyaltyEnabledGuard, StoreCreditEnabledGuard
└── interfaces/
├── entity-parameters.controller.ts # REST endpoints (/entity-parameters)
├── dtos/
│ ├── create-entity-parameter.dto.ts
│ └── update-entity-parameter.dto.ts
└── query/
└── paginate-entity-parameters.query.ts

Dependency flow: Controller -> Service -> Repository (via domain port)

Domain Concepts

Parameter Catalog

A catalog of available parameters (e.g., LOYALTY_ENABLED, BARCODE_SCAN_INPUT_ENABLED). Each entry defines:

  • code — unique parameter key
  • dataType — expected type (boolean, string, number)
  • entityType — scope level (business, location)
  • defaultValue / allowedValues — constraints

Entity Parameter

A concrete assignment of a catalog parameter to a business entity:

  • parameterCatalogId — links to the catalog entry
  • parameterValue — JSON object, convention: { "value": <actual_value> }
  • businessId — owning business
  • entityType + entityId — target entity (null entityId = business-wide default)

Priority Resolution

When resolving a parameter value for a business:

  1. Specific entity match (entityId = businessId) takes priority
  2. Business-wide default (entityId = null) is the fallback
  3. null if no parameter is set

Feature Guards

The module exports two NestJS guards for feature-gating:

GuardParameter CodePurpose
LoyaltyEnabledGuardLOYALTY_ENABLEDGates loyalty program endpoints
StoreCreditEnabledGuardSTORE_CREDIT_ENABLEDGates store credit endpoints

Both guards resolve businessId from: Firebase claims -> request body -> query params -> locationId DB lookup.

Available Parameter Codes

CodeTypeEntity TypeDescription
SERVICE_BOOKING_ENABLEDbooleanbusinessEnable service booking
SERVICE_BOOKING_REQUIREDbooleanbusinessRequire service booking in documents
ASSIGN_CUSTOMER_FROM_BOOKING_PLATFORMbooleanbusinessAuto-assign booking customer
CASH_REGISTER_DISCREPANCY_APPROVAL_THRESHOLDnumberbusinessApproval threshold amount
CASH_REGISTER_CASH_OUT_APPROVAL_THRESHOLDnumberbusinessCash-out threshold amount
REQUIRE_CASH_REGISTER_SESSION_FOR_ADD_DOCUMENTbooleanbusinessRequire cash session for docs
LOYALTY_ENABLEDbooleanbusinessEnable loyalty program
STORE_CREDIT_ENABLEDbooleanbusinessEnable store credit wallet
BARCODE_SCAN_INPUT_ENABLEDbooleanlocationEnable barcode scan input
DISCOUNT_ROUNDING_MODEenumbusinessHow stacked discount totals are rounded (half_up | up | down). Default: half_up. See Discount rounding policy.
DISCOUNT_ROUNDING_PRECISIONenumbusinessDecimal precision for rounded discount totals (cents | whole). Default: cents (2 decimal places). whole rounds to the nearest whole currency unit (0 decimal places). See Discount rounding policy.

Frontend i18n (PWA)

The Entity Parameter form shows human-readable labels in the parameter dropdown (#parameterCatalogId). Labels resolve in this order:

  1. system.parameterCatalog.codes.<CODE> in apps/frontend-pwa/src/i18n/locales/en.json and es.json
  2. The catalog row name from the API (custom parameters or missing translations)
  3. The raw code as a last resort

When you add a new parameter_catalog row via migration, add matching entries under system.parameterCatalog.codes in both locale files. Parameters created in the Parameter Catalog UI only need i18n keys if you want translated labels beyond the name field stored in the database.

API Endpoints

All endpoints require Bearer token authentication.

MethodPathDescription
POST/entity-parametersCreate entity parameter
GET/entity-parametersList with pagination/search
GET/entity-parameters/:idGet by ID
PATCH/entity-parameters/:idUpdate (partial)
DELETE/entity-parameters/:idDelete

Create Entity Parameter

POST /entity-parameters
{
"parameterCatalogId": "<uuid>",
"parameterValue": { "value": true },
"businessId": "<uuid>",
"isActive": true,
"createdBy": "<uuid>",
"entityType": "business",
"entityId": "<uuid or omit for business-wide>"
}

List Entity Parameters

GET /entity-parameters?businessId=<uuid>&page=1&size=20&search=LOYALTY

Update Entity Parameter

PATCH /entity-parameters/:id
{
"parameterValue": { "value": false },
"updatedBy": "<uuid>"
}

Design Decisions

  1. Parameter values are JSON objects ({ "value": ... }) rather than raw primitives — this allows future extension with metadata (e.g., { "value": true, "overriddenAt": "..." }).

  2. parseBooleanParameterValue lives in the domain layer — it encodes business rules about how to interpret parameter values and is framework-agnostic.

  3. Repository is injected via DI token (ENTITY_PARAMETERS_REPOSITORY Symbol) — the service depends on the domain port interface, not the concrete Kysely implementation.

  4. Feature guards use shared resolveBusinessId helper — centralizes the multi-source businessId resolution logic.