Contrato de API — Sync de productos compuestos (PC Manager → Integrador)¶
Para: equipo PC Manager (Python)
Endpoint: POST /api/supplier-products/sync (Sanctum) — alias POST /api/supplier-products (token estático)
Referencia de diseño: docs/rfc/004-soporte-productos-compuestos.md (en el repositorio del Integrador)
Fecha: 2026-07-14
1. Qué cambia¶
El payload de cada producto acepta dos campos nuevos, opcionales:
| Campo | Tipo | Reglas |
|---|---|---|
type |
string | "simple" o "composite". Opcional; default "simple". |
components |
array | Requerido si type = "composite", mínimo 1 elemento. Cada elemento: {"sku": string, "quantity": int >= 1}. |
El sku de cada componente es el sku de product_items del Integrador — el mismo que PC Manager lee de GET /api/products (campo sku). No usar sku_sap ni IDs.
Los productos simples no necesitan ningún cambio: el payload actual sigue siendo 100% válido.
2. Ejemplo — alta/actualización de un combo¶
{
"supplier_id": 1,
"supplier_code": "CL",
"products": [
{
"sku": "pcm-00123",
"name": "PC Gamer X",
"short_description": "",
"long_description": "",
"regular_price": 5500000.0,
"special_price": null,
"special_from_date": null,
"special_to_date": null,
"stock": 5,
"category": "PC ARMADAS",
"brand": "Compulandia",
"active": true,
"images": [],
"type": "composite",
"components": [
{ "sku": "MEM-KING-16", "quantity": 2 },
{ "sku": "SSD-WD-1TB", "quantity": 1 }
]
}
]
}
3. Semántica de actualización (importante)¶
La composición se trata con semántica PATCH: solo se toca si viene en el payload.
| Payload recibido | Comportamiento en el Integrador |
|---|---|
Sin type ni components |
La receta guardada no se toca. Se actualizan solo los campos enviados (precio, stock, etc.). Un combo puede recibir updates de precio/stock sin re-enviar su receta. |
type: "composite" + components |
La receta recibida reemplaza completa a la anterior (snapshot, sin merge). Es la forma de crear el combo y también de comunicar una sustitución de componente: se envía la lista final vigente. |
type: "composite" sin components |
HTTP 422 — rechazado por validación. Si se declara compuesto, la receta debe venir completa. |
components sin type |
Se asume composite (la presencia de receta implica compuesto). |
type: "simple" en un producto que era compuesto |
La receta se elimina y la variante vuelve a simple. Es la forma de "des-armar" un combo. |
Recomendación para PC Manager: enviar type + components completos en cada push del combo (alta, cambio de receta, sustitución), y omitir ambos campos en pushes que solo actualizan precio/stock. Nunca enviar components: [] ni components: null para "no cambiar" — simplemente omitir la clave.
4. Respuesta y warnings¶
La respuesta incluye, por producto, el resultado y los problemas de resolución de componentes:
{
"status": "success",
"message": "Productos procesados correctamente.",
"processed": [
{
"sku": "pcm-00123",
"status": "updated",
"warnings": [
"Componente 'SSD-NUEVO-XYZ': no existe como ProductItem"
]
}
]
}
warningsvacío = receta aceptada (y materializada, si el producto ya está confirmado en el Integrador).- El push nunca falla por un componente no resoluble: el JSON crudo se guarda siempre. Pero atención a la semántica:
- Producto pendiente (aún no confirmado por un operador): los warnings son informativos; la receta se validará al confirmar.
- Producto confirmado: si algún componente no resuelve, la receta nueva NO se aplica — se conserva la anterior hasta recibir un push cuya receta resuelva completa. El warning es la señal para corregir y reenviar.
Motivos posibles de warning por componente: no existe como ProductItem, el ProductItem está inactivo, no tiene sku_sap (sin inventario en SAP).
5. Efectos aguas abajo (contexto)¶
Cuando la receta se materializa en un combo confirmado, el Integrador reindexa el documento del combo en Algolia (product_items_view_index) en el mismo request, agregando:
{ "product_type": "composite",
"components": [
{ "product_item_id": 501, "sku": "MEM-KING-16", "sku_sap": "A00123", "name": "RAM 16GB DDR5", "quantity": 2 }
] }
Por eso importa que PC Manager pushee inmediatamente al sustituir un componente (comportamiento ya confirmado): la ventana en que el OMS puede ver una receta vieja depende solo de ese push.
6. Notas de implementación — PC Manager¶
Decisiones adoptadas del lado PC Manager (2026-07-15):
- La receta se envía siempre: todo push de un producto compuesto incluye
type: "composite"+componentscompletos (snapshot de los componentes asignados vigentes), incluso en actualizaciones que solo tocan precio/stock. La semántica de reemplazo completo hace que el envío repetido sea idempotente. - Los componentes de categoría SERVICIO no se envían en
components: son ítems locales de PC Manager que solo participan del cálculo de precio del combo. No existen comoproduct_itemsen el Integrador. - Los regalos (
is_gift) sí se incluyen en la receta, con su cantidad. - Caso borde: si un combo queda sin componentes asignados, se omiten
typeycomponentsen ese push (enviarcomponentsvacío produce HTTP 422) y se registra un warning en el logproduct_sync. El push de precio/stock sigue saliendo; la receta guardada en el Integrador no se toca. - Los
warningsdeprocessed[]en la respuesta se registran en el logproduct_sync. En un producto confirmado, un warning significa que la receta nueva no quedó vigente en el Integrador: corregir y reenviar. - Puntos de push de receta: la señal
pre_savedel producto (cambios de precio, stock, disponibilidad, textos) y, adicionalmente, un push explícito cuando la reselección de componentes modifica el conjunto asignado (sustitución), tanto desde el admin como desde la tarea periódica de sincronización de componentes. - Un solo POST por guardado: los distintos disparadores de un mismo guardado se deduplican (
queue_product_sync+transaction.on_commit) y el push único sale después del commit, con el estado final persistido. Esto evita que un guardado dispare múltiples workflows consecutivos en el Integrador. - Stock real del combo (desde 2026-07-17): el campo
stockde un compuesto ya no es el binario 1/0 sino la cantidad real de combos armables:min(floor(stock_componente / cantidad_requerida))sobre los componentes asignados (SERVICIO no participa del cálculo; los regalos sí).stock = 0cuando el combo está inactivo, incompleto o algún componente no alcanza la cantidad requerida — y en ese caso ademásis_availablepasa a false. - Advertencia sobre stocks de combos: los stocks reportados no son exclusivos entre combos. Dos combos que comparten un componente cuentan las mismas unidades físicas; vender uno no descuenta el stock reportado del otro hasta que el ciclo de sync propague el nuevo stock del componente (ver contrato de recepción
contrato-recepcion-productos-pcm.md, que reduce esa ventana al round-trip). - Precio manual (desde 2026-07-17): un combo puede tener
auto_pricedesactivado; en ese casoregular_price(y el precio especial) los fija el operador y no se recalculan desde los componentes. Para el Integrador es transparente.