Skip to main content

Product name generation

This page describes the catalog name formula, the fields it reads, when the server regenerates the name, and the errors and UI messages around it.

Formula​

{presentation} con {content} {unit} de {categoryName} {model} {color} {size} {flavor}
RuleDetail
Literalscon and de are always emitted.
Content and unitEmitted only when both are present (content not null/undefined and unit non-empty).
Empty partsSkipped.
SeparatorParts are joined by a single space.
CategoryThe category name (Tipo de Producto). Category names are stored in lowercase (modules/inventory/src/categories/application/use-cases/Create.ts).

Examples​

InputOutput
presentation Caja, content 5, unit Kg, category Arroz, model PremiumCaja con 5 Kg de Arroz Premium
presentation Botella, content 750, unit ML, category Perfume, model X1, color Rojo, size M, flavor FresaBotella con 750 ML de Perfume X1 Rojo M Fresa
presentation Caja, content 5, unit null, category Arroz, model PremiumCaja con de Arroz Premium

The full parity table is in modules/inventory/tests/products/domain/services/NameGenerator.test.ts and apps/web/utils/products/__tests__/product-name.spec.ts.

Implementations​

SideFunctionFile
Server (source of truth)generateProductNamemodules/inventory/src/products/domain/services/NameGenerator.ts
Client mirrorbuildCatalogProductNameapps/web/utils/products/product-name.ts

The client mirror reads form keys (presentacion, contenido, unidad, tipo, modelo, color, talla, sabor) and treats contenido === '' as absent.

Fields​

Server keyForm keyRequired in catalogLabel in errors
presentationpresentacionyesPresentación
contentcontenidoyes (≥ 1)Contenido
unitunidadyesUnidad
categoryIdtipoyesTipo de producto
modelmodeloyesModelo
colorcolorno—
sizetallano—
flavorsaborno—

Defined in modules/inventory/src/products/domain/services/CatalogNameFields.ts.

When the name is generated​

Catalog mode is resolved per request by ProductController._resolveCatalogMode (modules/inventory/src/products/infrastructure/controllers/Product.ts), with partner inheritance for VENDOR organizations.

STOCK products​

Entry pointCatalog OFFCatalog ON
Web create (POST /api/products → CreateProductUseCase)Name as sentRequired fields validated; name always generated; client name ignored
Web bulk create (POST /api/products/bulk) and gateway POST /products (CreateProductsManyUseCase)Name as sentRequired fields validated on every STOCK row; name always generated
Web update (PATCH /api/products/:id → UpdateProductUseCase)Name as sentRegenerated only when a name field changed; otherwise the stored name is kept and the client name ignored
Gateway update (PUT /products/:id → updateApi)Name as sentName as sent (catalogMode: false is always passed)

A product with no type is treated as STOCK on create (isStockType in modules/inventory/src/products/domain/enums/Type.ts). On update, the check is product.type === 'STOCK'.

Name-field change detection (update)​

nameFieldsChanged(current, effective) in CatalogNameFields.ts:

RuleDetail
Compared fieldsServer NAME_FIELDS
Equivalent values'', null, undefined
StringsTrimmed
content, categoryIdCompared as numbers

Category name resolution (update)​

When the name is regenerated, the category name comes from the first non-empty value of:

  1. The name of the categoryId sent in the body.
  2. The stored category name, if the category did not change.
  3. resolveCategoryName(categoryId), an RLS-scoped lookup (findCategoryNames).

KIT products​

Entry pointCatalog ON
Create, bulk createName generated only when the sent name is empty
Web updateName generated only when the body has no name
Required fieldsNot validated

The web edit page always sends the name for KIT products.

Errors​

MissingCatalogFieldsError​

File: modules/inventory/src/products/domain/errors/MissingCatalogFields.ts. HTTP status 422. Response body: { "error": "<message>" }.

CaseMessage format
Single productCampos obligatorios con catálogo activo: <labels>
BulkCampos obligatorios con catálogo activo: SKU <sku>: <labels>; Fila <n>: <labels>

Labels follow declaration order and are joined by , . In bulk messages a row is identified by SKU <sku> when it has a SKU, otherwise by Fila <n> (1-based).

Example: Campos obligatorios con catálogo activo: Unidad, Modelo.

UI​

Create page (apps/web/pages/[client]/products/new.vue)​

ElementBehavior
Name inputReplaced by a read-only preview (catalog ON + STOCK)
Helper textGenerado automáticamente según los datos del producto
Submit without a generated nameError Los campos de catálogo son requeridos para generar el nombre

Edit page (apps/web/pages/[client]/products/[id].vue)​

ElementBehavior
Name inputRead-only; shows the stored name until the save succeeds and the product is refetched
Confirmation triggerCatalog ON + STOCK + org:products:edit + at least one name field changed
Confirmation dialogTitle ¿Estás seguro?, confirm button Actualizar

Dialog message:

Esta acción actualizará el nombre del producto.

Nombre actual: <stored name>
Nuevo nombre: <buildCatalogProductName(form)>