Agregación en entidad OData mediante anotación en CDS

Hace unos días un colega me comentó sobre un problema que tenía en un informe con Fiori Elements, donde necesitaba mostrar los datos de surtidos de distintos artículos en diferentes centros. Hasta aquí todo normal, el asunto es que el cliente le pedía además, poder filtrar por unidades de medida; pero no quería que, si un mismo artículo estaba listado con más de una unidad de medida en una misma tienda, apareciera por duplicado en el listado.

Mi respuesta directa fue: “crea una CDS analítica y no muestres en la App Fiori el campo de unidad de medida, de esa forma se agregarán todos los registros con el resto de campos idénticos en una misma fila”. El tema es que no me expliqué muy bien o no usé los términos adecuados y a mi amigo no le quedó muy claro lo que le quería decir; por eso he hecho este post en especial para ti Borja 😉

Decidí montar un ejemplo con lo que estaba intentando explicarle, y así de paso estrenar el servidor recién instalado en este artículo y montar allí el ejemplo.

Lo primero que hice fue crear un par de CDS con un caso de uso similar a lo que necesita mi amigo, pero en mi caso, lo que voy a mostrar son todos los pedidos de ventas en la tabla SNWD_SOH (EPM) con algunos campos de cabecera, y permitir filtrar por la unidad de medida del material en las M posiciones de los N pedidos.

Para ello en mi CDS de consumo, voy a exponer varios datos del pedido así como los productos y las unidades de medida de sus respectivas posiciones; además, añadiré una anotación de agregación para permitir que cuando se excluyan columnas en el informe CDS, los registros similares se agreguen y no muestre filas duplicadas. Pero bueno, corto un poco el rollo y como diría Goyo Giménez, “no lo cuento, lo hago”.

Aquí os dejo el código de ambas CDS, y explicaré las anotaciones principales de la CDS de consumo, que es la que publicaré como OData:

@AbapCatalog.sqlViewName: 'ZDEMOORDER'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Demo Purchase Orders - Material'
define view ZDEMO_I_PURCHASE_ORDER as select from SEPM_I_SalesOrder as SalesOrder    
{
   SalesOrder.SalesOrder,
   SalesOrder.CreatedByUser,
   SalesOrder.CreationDateTime,
   SalesOrder.GrossAmountInTransacCurrency,
   SalesOrder.NetAmountInTransactionCurrency,
   SalesOrder.TransactionCurrency,
   SalesOrder._Customer.CompanyName,
   SalesOrder._Item.SalesOrderItem,
   SalesOrder._Item._Product.Product,
   SalesOrder._Item._Product.ProductBaseUnit
}
@AbapCatalog.sqlViewName: 'ZDEMOCORDER'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Demo Purchase Orders - Material'
@OData.publish: true
define view ZDEMO_C_PURCHASE_ORDER
  as select from ZDEMO_I_PURCHASE_ORDER as SalesOrder
{
  @UI: {
    lineItem: [ { position: 10, importance: #HIGH } ]
  }
  SalesOrder,
  @UI: {
    lineItem: [ { position: 20, importance: #HIGH } ]
  }
  CreatedByUser,
  @UI: {
    lineItem: [ { position: 30, importance: #HIGH } ]
  }
  CreationDateTime,
  @UI: {
    lineItem: [ { position: 40, importance: #HIGH } ]
  }
  @Semantics.amount.currencyCode: 'TransactionCurrency'
  GrossAmountInTransacCurrency,
  @UI: {
    lineItem: [ { position: 50, importance: #HIGH } ]
  }
  @Semantics.amount.currencyCode: 'TransactionCurrency'
  NetAmountInTransactionCurrency,
  @UI: {
    lineItem: [ { position: 60, importance: #HIGH } ]
  }
  @Semantics.currencyCode: true
  TransactionCurrency,
  @UI: {
    lineItem: [ { position: 70, importance: #HIGH } ]
  }
  CompanyName,
  SalesOrderItem,
  @UI: {
    selectionField.position: 10
  }
  @Consumption.valueHelpDefinition: [
       { entity:  { name:    'SEPM_I_Product',
                    element: 'Product' }
       }]
  Product,
  @UI: {
    selectionField.position: 20
  }
  @Consumption.valueHelpDefinition: [
       { entity:  { name:    'SEPM_I_UnitOfMeasure',
                    element: 'UnitOfMeasure' }
       }]
  ProductBaseUnit,
  
  @Aggregation.referenceElement: ['SalesOrder']
  @Aggregation.default: #COUNT_DISTINCT
  @EndUserText.label: 'Number of items'
  cast(1 as abap.int4) as CountSalesOrderItems
}

En este caso, las anotaciones que nos interesan son:

...  
@Consumption.valueHelpDefinition: [
       { entity:  { name:    'SEPM_I_UnitOfMeasure',
                    element: 'UnitOfMeasure' }
       }]
ProductBaseUnit
...

Con esta anotación defino una ayuda de búsqueda para el campo ProductBaseUnit (unidad de medida)

...  
@Aggregation.referenceElement: ['SalesOrder']
@Aggregation.default: #COUNT_DISTINCT
@EndUserText.label: 'Number of items'
cast(1 as abap.int4) as CountSalesOrderItems
...

Aquí creo un campo de agregación, y le defino una etiqueta.

Una vez publicada la CDS, y registrado el servicio OData en la transacción /IWFND/MAINT_SERVICE:

Voy a WebIDE y creo una aplicación Fiori Elements – List Report. Como ya he añadido las anotaciones de columnas,  filtros, etc. a mi CDS de consumo, no necesito definir ninguna anotación local dentro de la App Fiori, y al ejecutar la misma, ya obtengo los resultados que quiero, eso si, sin haberle aplicado ningún filtro aún.

Para comprobar que la App se está comportando como debería, añado las columnas relacionadas a las posiciones del pedido, y vemos que ahora si salen los datos duplicados:

Por otra parte, si filtramos por la unidad de medida EA, sin ocultar las columnas de posición de pedido, podemos ver que siguen apareciendo registros duplicados:

Pero si ocultamos las columnas de posición, vemos como los registros duplicados desaparecen:

Y con esto, espero que ahora si le haya quedado claro lo que le conté en 3 segundos por teléfono mientras al mismo tiempo respondía a un correo electrónico XD.

Deja un comentario

Por favor sea cortés. Rellene los campos obligatorios, su dirección de correo electrónico no se publicará.

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.