🧮 D365 SCM ya calcula descuentos multilínea por API

Microsoft ha ampliado el alcance documentado de la pricing calculation API de Unified Pricing Management: desde el 2 de octubre de 2026, los descuentos multilínea o de nivel carrito aparecen expresamente como soportados. Hasta el día anterior figuraban en la columna de capacidades no admitidas.

El cambio importa para portales B2B, configuradores, herramientas de oferta e integraciones de partners. Una aplicación externa puede enviar varias líneas como una sola transacción, hacer que Supply Chain Management evalúe el contexto común y recuperar precios y descuentos sin crear un pedido de venta.

No convierte esta API en un motor de checkout de Commerce. Microsoft mantiene fuera de alcance la basket pricing con concurrencia de promociones, los escaparates de comercio electrónico y las llamadas de alta frecuencia o volumen.

Arquitectura de cálculo de descuentos multilínea mediante Unified Pricing Management.

Diagrama original. Las líneas viajan juntas dentro de priceLookupContext; SCM devuelve el desglose por producto sin persistir un pedido.

Qué ha cambiado exactamente

La tabla de alcance oficial ahora diferencia estas capacidades:

SoportadoSigue sin estar soportado
Cálculo de precio por productoBasket pricing y concurrencia de promociones de Commerce
Descuentos simplesLlamadas de alta frecuencia o volumen
Descuentos multilínea o de nivel carritoComercio electrónico o escenarios de escaparate
Reglas y atributos de Unified Pricing ManagementSustituir las API de Commerce Scale Unit
Precios por cantidad y rangos de variantes—

La distinción entre «descuento de nivel carrito» y «basket pricing» es importante. La primera expresión describe reglas que necesitan el conjunto de líneas de una transacción; la segunda pertenece a escenarios de Commerce con concurrencia, rendimiento y semántica de cesta específicos. La API de SCM puede evaluar las líneas juntas, pero no adquiere por ello el contrato operativo de un POS o una tienda web.

Microsoft no marca la API como preview. Los requisitos publicados son:

  • Dynamics 365 Supply Chain Management 10.0.47 o posterior;
  • Unified Pricing Management habilitado;
  • una aplicación registrada en Microsoft Entra ID;
  • la misma aplicación registrada en SCM y asociada a un usuario con permisos adecuados.

La documentación no anuncia un SKU ni un medidor específico para la nueva capacidad. Se mantienen los requisitos de licencia y seguridad del entorno de Supply Chain Management y del usuario de servicio utilizado por la integración.

Arquitectura y contrato

La aplicación externa no crea SalesTable ni SalesLine. Invoca un servicio JSON síncrono:

POST https://<entorno>/api/services/GUPPricingServiceGroup/GUPPricingService/getActivePrices
Authorization: Bearer <token>
Content-Type: application/json

El flujo es:

  1. La aplicación obtiene un token OAuth 2.0 de Microsoft Entra ID con el ámbito https://<entorno>/.default.
  2. SCM resuelve el Client ID contra System administration > Setup > Microsoft Entra ID applications y ejecuta con el usuario asociado.
  3. GUPPricingService convierte HeaderContext y LineContexts en un contexto transaccional para Unified Pricing Management.
  4. El motor evalúa precios, ajustes y descuentos aplicables a la fecha indicada.
  5. La respuesta contiene una Transaction y un array ItemResults con el precio y el desglose de cada producto.

El servicio calcula; no documenta persistencia de un pedido ni reserva de inventario. Si la integración necesita que el precio quede fijado en un documento, debe crear o actualizar ese documento mediante un flujo separado y decidir cómo controla la vigencia entre consulta y confirmación.

Dos modos de petición

El contrato ofrece dos rutas mutuamente excluyentes:

EntradaUso
productIdsConsulta sencilla de productos, con cantidad predeterminada 1
priceLookupContext + LineContextsVarias líneas, cantidades, unidades, almacenes, modos de entrega y atributos

No envíes productIds y LineContexts en la misma petición. Para descuentos multilínea necesitas LineContexts, porque cada línea forma parte del contexto transaccional.

Los parámetros que gobiernan el cálculo son:

  • dataAreaId: empresa, obligatorio si no se proporciona ChannelId;
  • activeDate: fecha y hora efectiva; si se omite utiliza la fecha y hora del sistema;
  • calculateSimpleDiscountOnly: true limita el cálculo a descuentos simples; false trata la entrada como una transacción para el cálculo completo;
  • includeVariantPriceRange: devuelve mínimo y máximo de variantes cuando vale true.

Petición multilínea completa

El siguiente ejemplo envía dos productos con cantidades distintas y solicita el cálculo completo:

{
  "request": {
    "dataAreaId": "usrt",
    "activeDate": "2026-10-03T08:00:00+02:00",
    "priceLookupContext": {
      "HeaderContext": {
        "CustomerAccount": "004009",
        "InventorySiteId": "CENTRAL",
        "InventoryLocationId": "DC-CENTRAL",
        "SalesOrderProperties": [
          {
            "Name": "order type",
            "Value": "3"
          }
        ]
      },
      "LineContexts": [
        {
          "ProductRecordId": 22565421974,
          "UnitOfMeasureSymbol": "ea",
          "Quantity": 3,
          "SalesLineProperties": [
            {
              "Name": "material",
              "Value": "brass"
            }
          ]
        },
        {
          "ProductRecordId": 22565421975,
          "UnitOfMeasureSymbol": "ea",
          "Quantity": 2
        }
      ]
    },
    "calculateSimpleDiscountOnly": false,
    "includeVariantPriceRange": false
  }
}

Los nombres son sensibles a mayúsculas y minúsculas. ProductRecordId es el identificador de registro del producto, no el ItemId. Para una línea, Quantity vale 1 si se omite. Sitio y almacén heredan primero del canal y, si no hay canal, del cliente; utiliza "" cuando necesites anular el valor heredado y dejarlo vacío.

Contexto de cabecera

HeaderContext permite enviar:

  • CustomerAccount;
  • ChannelId en lugar de dataAreaId cuando el cálculo se basa en un canal;
  • sitio y almacén comunes;
  • afiliaciones y niveles de fidelización;
  • SalesOrderProperties para atributos de cabecera.

Las afiliaciones ya vinculadas al cliente se aplican automáticamente. Solo hay que enviarlas para añadir un contexto distinto o adicional.

Contexto de línea

Cada elemento de LineContexts puede aportar:

  • producto, unidad y cantidad;
  • sitio y almacén específicos;
  • modo de entrega;
  • SalesLineProperties con atributos utilizados por las reglas de precio.

SalesOrderProperties y SalesLineProperties no admiten dos valores para el mismo atributo dentro de una petición. Si necesitas comparar alternativas, realiza peticiones distintas.

Interpretar la respuesta

ItemResults devuelve una entrada por producto calculado. Entre los campos documentados están:

CampoSignificado
BasePricePrecio base del producto
TradeAgreementPricePrecio procedente de acuerdos comerciales
AdjustedPricePrecio después de ajustes
CustomerContextualPricePrecio final contextual para el cliente
DiscountAmountDescuento total aplicado
CurrencyCode, UnitOfMeasure, QuantityContexto del resultado
AttainablePriceLinesTrazabilidad de métodos y orígenes de precio
DiscountLinesOfertas, porcentajes e importes efectivos

Para una integración multilínea no basta con leer CustomerContextualPrice. Conserva también DiscountLines, OfferId, SaleLineNumber y los importes efectivos para diagnosticar por qué una promoción se aplicó o no. No asumas que el orden del array sustituye a una clave estable: correlaciona por producto y los identificadores de línea que exponga el contrato.

Implementación paso a paso

1. Preparar Unified Pricing Management

Habilita Unified Pricing Management en un sandbox y configura reglas, grupos, prioridades, concurrencia y atributos. Valida primero el resultado dentro de SCM. La API consume el motor configurado; no compensa reglas incompletas ni una prioridad incorrecta.

2. Registrar la identidad

  1. Crea o reutiliza una aplicación en Microsoft Entra ID.
  2. Genera el secreto indicado por la guía oficial y protégelo en un gestor de secretos.
  3. Solicita el token para https://<entorno>/.default.
  4. En SCM, registra el Client ID y asígnalo a un usuario dedicado.
  5. Concede al usuario únicamente los privilegios necesarios para el servicio y los datos de pricing.

No utilices una cuenta humana ni un administrador global como usuario de integración. Rota el secreto antes de su vencimiento y evita escribir token, secreto, cliente o condiciones comerciales completas en logs.

3. Construir la petición como una transacción

Agrupa en la misma llamada todas las líneas que deban participar en la regla multilínea. Fija calculateSimpleDiscountOnly a false, envía una fecha efectiva explícita y conserva juntos empresa, cliente, canal, divisas, cantidades, unidades y atributos que condicionan el resultado.

Si divides las líneas en llamadas independientes, el motor no dispone del conjunto que necesita una regla transaccional. Si, por el contrario, mezclas líneas que pertenecen a cestas diferentes, puedes aplicar un descuento que no corresponde.

4. Tratar el resultado y la vigencia

Define qué valor utilizará el sistema consumidor y qué trazabilidad almacenará. Guarda como mínimo la fecha efectiva, empresa, cliente o canal, productos, cantidades, moneda, precio final y ofertas aplicadas. Antes de confirmar un documento mucho después de la consulta, decide si recalculas o si existe un compromiso contractual que fija el precio anterior.

5. Diseñar errores y reintentos

Usa un identificador de correlación propio y reintentos limitados para fallos transitorios. No reintentes indefinidamente errores funcionales de producto, unidad, regla o permisos. La operación es de cálculo, pero una tormenta de reintentos puede superar el perfil de frecuencia bajo o moderado que Microsoft admite.

Extensibilidad

Microsoft documenta extensiones de GUPPricingServiceClass para añadir parámetros o resultados:

  • initHeaderPricingObjectHash para contexto de cabecera;
  • initLinePricingObjectHash para contexto de línea;
  • setProductPrice para ampliar la salida.

Una extensión de clase debe usar [ExtensionOf(classStr(GUPPricingServiceClass))] y llamar a next. Los atributos personalizados viajan en SalesOrderProperties o SalesLineProperties; valida su nombre, tipo y valor antes de aplicarlos. No cambies el contrato estándar ni reutilices un atributo para dos semánticas distintas entre canales.

Límites que siguen vigentes

  • Requiere SCM 10.0.47 o posterior y Unified Pricing Management.
  • Está orientada a integraciones sistema a sistema de frecuencia baja o moderada.
  • No sustituye a Commerce Scale Unit para PDP, POS, e-commerce, picos de concurrencia o navegación de catálogo.
  • La documentación sigue excluyendo basket pricing y concurrencia de promociones de Commerce.
  • No crea un pedido, reserva inventario ni promete mantener el resultado hasta la confirmación.
  • activeDate y los valores predeterminados pueden cambiar el resultado; no dependas implícitamente del reloj del servidor.
  • El contrato usa IDs de producto y varios nombres sensibles a mayúsculas.
  • El rendimiento debe probarse con el número real de líneas y reglas, sin extrapolar desde una petición unitaria.

Matriz de pruebas

CasoValidación
Una línea, calculateSimpleDiscountOnly: trueMantiene el comportamiento simple existente
Varias líneas, falseAplica la regla multilínea esperada y devuelve desglose por línea
Umbral justo por debajo y por encimaEl descuento aparece solo cuando corresponde
Dos unidades de medidaCantidad y conversión no alteran indebidamente el umbral
Cliente y canalSe aplican contexto y afiliaciones correctos
Sitio/almacén omitido, vacío y explícitoSe valida la herencia documentada
Fecha actual y fecha futuraVigencias de reglas y acuerdos son correctas
Atributos de cabecera y líneaPrioridades y condiciones coinciden con SCM
Dos promociones concurrentesSe comprueba el límite frente a Commerce, no se presupone paridad
Carga baja, media y ráfagaLatencia, errores y reintentos permanecen dentro del diseño

Compara el resultado con una transacción equivalente dentro de SCM. Verifica no solo el precio final, sino también DiscountLines, origen, moneda, unidad, cantidad y regla efectiva.

Recomendación práctica

La ampliación elimina una barrera importante para cotizadores y portales B2B internos: ya no necesitan calcular cada línea aislada cuando una regla depende del conjunto. Diseña la llamada como una transacción inmutable, utiliza calculateSimpleDiscountOnly: false y conserva el desglose devuelto.

Mantén, sin embargo, una frontera clara. Si el caso es un escaparate público, un POS o una cesta con concurrencia compleja de promociones y gran volumen, la API correcta sigue siendo Commerce Scale Unit. El nuevo soporte aporta cálculo multilínea en SCM; no convierte SCM en el plano de pricing de una tienda de alta escala.

Referencias