Factuarea APIDevelopers

Catálogo de productos

Modela productos y servicios con unidades UNECE, presentaciones, variantes, ofertas de proveedor y snapshots documentales inmutables.

El catálogo modela qué vendes y, por separado, cómo se presenta, identifica, almacena y compra. Empieza con una clave fact_test_ y concede solo products:read y/o products:write según necesites.

Producto o servicio

Cada ítem declara un item_kind explícito:

  • product puede gestionar stock decimal y tener variantes con stock propio.
  • service siempre tiene manage_stock: false, stock 0.0000 y umbral de stock bajo nulo. Un servicio no admite variantes con stock propio.

La unidad base pertenece al catálogo UNECE cerrado: C62, KGM, GRM, LTR, MLT, MTR, MTK, HUR o DAY. Cantidades y stock admiten cuatro decimales; los factores de conversión, seis. La unidad base no se puede cambiar cuando reinterpretaría stock, presentaciones, variantes, ofertas o histórico existente.

{
  "name": "Aceite de oliva virgen extra",
  "sku": "AOVE-GRANEL",
  "price": "8.75",
  "item_kind": "product",
  "base_unit": "LTR",
  "manage_stock": true,
  "stock": "125.5000",
  "low_stock_threshold": "20.0000",
  "specifications": { "origen": "Jaén", "cosecha": 2026 }
}

Semántica de creación y actualización

POST /v1/products exige name y price. Si external_id ya identifica un producto de la misma empresa, la creación actúa como upsert idempotente en vez de duplicarlo. sku también es único por empresa, pero una colisión con otro producto es un error, no un upsert.

PUT /v1/products/{product} tiene semántica parcial: cada campo omitido conserva su valor actual. Dos campos aplican sustitución explícita:

  • specifications sustituye el objeto clave-valor completo; null lo vacía. Las listas JSON no vacías se rechazan, mientras que {} y [] siguen siendo representaciones vacías compatibles.
  • stock es una cantidad absoluta, no un delta. null lo pone a cero. Una diferencia real registra un movimiento manual; enviar el valor actual no registra nada. Usa el endpoint dedicado de stock con increase o decrease para deltas explícitos.

stock y low_stock_threshold admiten hasta cuatro decimales. metadata de Producto conserva su frontera retrocompatible: hasta 50 claves, los valores escalares se convierten a strings y las listas JSON se aceptan y persisten con claves numéricas. Esto es deliberadamente distinto de specifications.

Presentaciones

Una presentación es una forma comercial de vender el producto; nunca tiene stock propio.

ModoSignificado
fixedcantidad base = cantidad comercial × conversion_factor. Una caja de 12 usa 12.000000.
variable_measureLa medida nominal es informativa. Cada línea debe confirmar su cantidad base real.
POST /v1/products/{product}/presentations
{
  "name": "Caja de 12",
  "mode": "fixed",
  "unit": "C62",
  "conversion_factor": "12.000000",
  "active": true
}

Usa GET/POST sobre la colección y PUT/DELETE sobre cada ítem. Una presentación inactiva no se puede seleccionar en líneas nuevas, pero los documentos antiguos siguen renderizando desde su snapshot.

Variantes

Una variante conserva identidad comercial propia: nombre, SKU/código de barras opcionales, overrides de precio y coste y, opcionalmente, stock propio.

{
  "name": "Azul / M",
  "sku": "CAM-AZU-M",
  "price_override": "24.5000",
  "manage_stock": true,
  "stock": "8.5000",
  "active": true
}

Con manage_stock: true, los movimientos afectan solo a la variante; en caso contrario delegan en el producto base sin perder la identidad de variante en la línea ni en el ledger. Una presentación puede combinarse con una variante: la variante elige el destino de stock y la presentación convierte la cantidad.

Opciones configurables y combinaciones vendibles

Los grupos de opciones modelan elecciones del comprador —acabado, sabor o nivel de servicio—, no specifications informativas. Cada grupo contiene valores de selección única; un valor puede añadir un price_adjustment por unidad comercial. Los grupos y valores inactivos siguen siendo legibles para conservar snapshots históricos, pero no se pueden elegir en una línea nueva.

Las configuraciones comerciales combinan una variante opcional, una presentación opcional y un conjunto canónico de valores de opción. Cada configuración tiene una signature estable, puede declarar un precio final por unidad comercial y puede estar activa o inactiva.

catalog_availability_mode controla qué se puede vender:

ModoDisponibilidad
openEl producto cartesiano de los ejes activos es vendible. Una configuración solo restringe la disponibilidad con restricts_availability: true: limita su variante nombrada y trata presentación/opciones omitidas como comodines.
closedLas configuraciones activas son la lista exacta permitida. Sin ninguna configuración activa, no se puede vender nada.

La creación usa open por defecto; omitir el campo al actualizar conserva el modo actual. Una fila con restricts_availability: false todavía puede aportar identidad o un precio exacto sin estrechar un catálogo abierto.

Usa GET /v1/products/{product}/options y GET /v1/products/{product}/configurations para inspeccionar el catálogo. Ambos usan products:read y paginación por cursor. El detalle del producto puede incluirlos con include=configurable_catalog.

POST /v1/products/{product}/resolve-selection también es una lectura pese a ser POST: acepta una selección parcial anidada y devuelve resolved, incomplete, ambiguous o incompatible, además de los ejes compatibles y grupos pendientes. Dos valores del mismo grupo forman una petición inválida.

Antes de una escritura que reduzca la disponibilidad, llama a POST /v1/products/{product}/configurations/impact-preview con products:write. No persiste nada y devuelve las combinaciones afectadas, los precios negociados que se retirarían y un impact_token que identifica ese estado exacto del catálogo. Devuelve el token en la actualización destructiva; si el catálogo o los precios afectados cambian, solicita otra previsualización.

Ledger de stock

GET /v1/products/{product}/stock-movements expone el libro mayor inmutable de stock de más reciente a más antiguo. Cada fila lleva el delta firmado en unidades base, el saldo stock_after calculado sobre el libro completo, el motivo, el documento origen, el usuario y la variante gestora. La paginación usa limit y starting_after; direction=in|out filtra las filas visibles sin cambiar los valores históricos de stock_after.

Ofertas de proveedor

Una oferta representa condiciones de compra sin cambiar el precio de venta. Apunta al producto o a una variante y registra proveedor, unidad de compra, factor, disponibilidad, coste y plazo.

{
  "supplier_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a10",
  "variant_id": null,
  "supplier_sku": "PROV-AOVE-20L",
  "purchase_unit": "LTR",
  "conversion_factor": "1.000000",
  "unit_cost": "5.1250",
  "availability": "available",
  "minimum_quantity": "20.0000",
  "lead_time_days": 3,
  "preferred": true,
  "active": true
}

available exige unit_cost no negativo. unavailable, unknown, seasonal y store_dependent exigen unit_cost: null. Solo puede haber una oferta activa preferida por destino; la acción preferred la cambia atómicamente.

Cuando una línea de factura de compra compra contra una oferta, purchase_measurement registra los bultos recibidos y la unidad en la que se expresa el coste (offer_unit, base_unit o presentation_unit); el servidor deriva la cantidad facturada y congela el coste unitario. unit_price omitido en una línea de catálogo significa «coste no confirmado»: la API aplica el coste de la oferta o rechaza la línea si el proveedor no tiene oferta activa. Consulta Líneas de compra medidas.

Los documentos guardan snapshots

Las líneas de catálogo devuelven product_id, variant_id, presentation_id, configuración/firma comercial y valores de opción, item_kind, unidad comercial y base, factor, cantidad base, origen y semántica del precio, ajustes de opción, identidad de tarifa y unidad de precio. Son snapshots: cambiar o retirar el catálogo vivo no reescribe documentos emitidos ni históricos.

Para una presentación variable_measure, envía confirmed_base_quantity al crear la línea. Deja que la API derive totales y cantidades de movimiento.

Permisos, borrado y MCP

  • Las lecturas usan products:read; altas, ediciones y borrados de los nuevos subrecursos usan products:write. El producto base se borra con products:delete.
  • Los borrados de presentación, variante y oferta llevan x-irreversible: true en OpenAPI.
  • Un producto referenciado por un ítem de tarifa u oferta vigente no se puede borrar físicamente. Retira antes la referencia; el histórico queda en snapshots.
  • El dominio Product de MCP tiene ahora 38 tools. Además de las 13 tools de presentaciones, variantes y ofertas, cuatro lecturas nuevas exponen opciones, configuraciones, movimientos de stock y resolución de selecciones.

Consulta Tarifas, Scopes e irreversibilidad y los esquemas exactos en la Referencia de la API.

Descripciones y consultas de documentos

Las líneas admiten additional_description nullable de hasta 5000 caracteres junto a description. Está disponible en facturas de venta y rectificativas, presupuestos, proformas, albaranes, facturas de compra y plantillas recurrentes. Mantén ese detalle separado de cantidades, precios, campos fiscales e identificadores del catálogo; añadir descripción no cambia el cálculo del importe. Consulta GET /v1/products/low-stock-report para la colección y total_count, y GET /v1/products/{product}/sales-analytics para las métricas de ventas; estas respuestas no son recursos de producto individuales. Sus ejemplos completos están en la referencia de la API.

En esta página

¿Te echamos una mano?Contactar con soporte