Saltar a contenido

Fix backend — Alta de ítems simples sin almacén (dropship) falla el pedido

Fecha: 2026-07-20. Audiencia: agente de backend (NestJS). Tipo: bug fix puntual. Estado: diagnosticado, listo para implementar.


Síntoma

Al guardar un pedido con un producto simple que aún no existe en SAP (dropship), el backend falla: el ítem no queda usable y el POST del pedido intenta agregar un ítem que "no existe" en el depósito de la línea. (Los combos no tienen este problema; su flujo crea con almacén explícito.)

Causa raíz

OrdersService.processOrderLines (src/modules/orders/orders.service.ts), paso 3 — creación del ítem faltante (líneas ~128-171):

const createdItem = await this.itemsService.create(itemToCreate);   // ← SIN opciones

Se llama a create sin warehouseCodes, pero el ítem se crea con InventoryItem: 'tYES' (default de STATIC_ITEM_DATA_FOR_CREATION, items.service.ts:29-39).

El mapper solo agrega ItemWarehouseInfoCollection si recibe warehouseCodes (src/modules/items/mappers/item.mapper.ts:167). Su propio comentario lo advierte:

"Almacenes a asignar explícitamente. Sin esto, el ítem queda sin filas de almacén cuando la parametrización de empresa no asigna todos los almacenes (dev), y la explosión del BOM en el pedido falla con Internal error (-10)."

Resultado: el ítem simple se crea sin filas de almacén; la línea del pedido lo referencia en WarehouseCode (p. ej. CEN), un depósito donde el ítem no tiene fila → SAP lo rechaza.

Es la misma trampa que ya documentó 03 §2 y que el flujo de combos evita pasando almacenes explícitos. El flujo legacy de simples nunca lo hizo, por eso solo se ve con ítems realmente nuevos (los que resuelven por SupplierCatalogNo a un ítem existente no pasan por la creación).

Fix

Pasar el depósito de la línea al crear. itemsService.create ya acepta la opción (items.service.ts:48-62; la usa el flujo de combos):

const createdItem = await this.itemsService.create(itemToCreate, {
  warehouseCodes: [line.WarehouseCode ?? 'CEN'],
});
  • line.WarehouseCode está disponible en CreateOrderLineDto.
  • No requiere cambios de frontend: el front ya envía WarehouseCode, ItemName y SupplierCatalogNo en la línea.

Corrección posterior (2026-07-20) — asignar TODOS los almacenes activos

El fix inicial pasaba solo el almacén de la línea. Eso desbloqueó el primer pedido pero dejó un segundo bug: al pedir el mismo ítem desde otra sucursal, el ítem ya existía (no pasa por la creación) y le faltaba la fila de ese depósito → Enter a valid value in "Whse" field - MUL - 06581 [Message 173-62].

Solución: al crear se asignan todos los almacenes, replicando la parametrización de empresa que producción ya tiene ("añadir todos los almacenes a artículos nuevos") y que dev no tiene. itemsService.create sin warehouseCodes ahora resuelve la lista sola (getAllWarehouseCodes(), cacheada 1h).

⚠ Hay que excluir los almacenes inactivos. SAP rechaza asignarlos y voltea el alta entera con 1980000017 - Warehouse "01" is not active. En dev el almacén 01 (nombre literal "INACTIVO") tiene Inactive: tYES; la query filtra por Inactive eq 'tNO' (10 activos de 11).

Nota verificada: el PATCH de ItemWarehouseInfoCollection agrega filas sin borrar las existentes (no hace falta el header de replace) — sirve para reparar ítems ya creados.

Qué NO hacer

  • No expandir /items/composite/sync para simples. Ese endpoint es específico de combos (padre + componentes + ProductTree/BOM, comparando la receta contra Algolia). Un simple solo necesita "crear si falta"; meterlo ahí sobrecarga el endpoint con una responsabilidad ajena. La creación silenciosa en el POST es lo más simple y atómico (ítem + pedido juntos, server-side).

Verificación

  1. Dev ZZ_COMPU_3. Armar un pedido con un ítem simple cuyo SKU no exista en SAP (ni por ItemCode ni por SupplierCatalogNo) y con ItemName presente.
  2. Antes del fix: el POST del pedido falla (-10 / ítem no disponible en el depósito).
  3. Después del fix: el ítem se crea con ItemWarehouseInfoCollection en el depósito de la línea, y el pedido se crea OK. Confirmar en SAP que el ítem nuevo tiene fila de almacén en ese depósito (GET Items('<code>')?$select=ItemCode,ItemWarehouseInfoCollection).

Nota — otro pendiente de backend (aparte)

Está también el contrato de 10-endpoint-enriquecimiento-lineas.md: extender serials-with-price/batch para que devuelva también existencia + stock + flags (ItemWarehouseInfoCollection, ManageSerialNumbers, InventoryItem, found), unificando la consulta y arreglando el bug de resolución SKU→ItemCode del single de stock. Son dos tareas independientes; esta (alta de simples) es la que desbloquea el guardado ahora.