Hace un tiempo, un cliente solicitó sacar en el grid de las transacciones de cliente los valores de las dimensiones financieras en diferentes campos, con el fin de filtrar por dichos valores. Esto, que a priori puede parecer algo sencillo, tiene cierta complejidad. En este artículo vamos a ver una posible solución a este problema que no implica crear campos en ninguna tabla.
Consideración inicial:
Tratamos aquí las dimensiones financieras, alojadas en la DimensionAttributeValueSet, pero todo lo tratado aquí es extensible a las dimensiones contables, de la DimensionAttributeValueCombination.
Problema:
Como ya sabréis, el tema de las dimensiones en D365FO es un poco menos intuitivo que en versiones anteriores de AX. El problema radica en que no tenemos una tabla en la que estén alojados el RecId de las diferentes combinaciones de dimensiones (DefaultDimension) y los diferentes valores de las dimensiones como campos de dicha tabla… ¿o sí?
La respuesta es span SÍ, en la tabla DimensionAttributeValueSet, cuyo RecId es el famosoDefaultDimension que se usa en todas las tablas con dimensiones financieras.
En esta tabla tenemos ni más ni menos que todo lo que necesitamos, un campo con el valor de la dimensión para cada una de ellas. Entonces, ¿cuál es el problema?
Dichos campos no forman parte de los xmls a los que nosotros tenemos acceso en VS, sino que son generados directamente en SQL durante la sincronización.
Con lo cual, aunque nosotros tratemos de acceder a dichos campos, usando el nombre, por ejemplo, dimensionAttributeValueSet.DepartamentoValue, dará error de compilación, puesto que el compilador no encontrará en la tabla DimensionAttributeValueSet dicho campo. Esto además lleva a que esos campos no aparezcan cuando tenemos la tabla como data source en formularios, vistas etc. Sin embargo, existe una entidad estándar, la DimensionSetEntity (DimensionCombinationEntity para el LedgerDimension) en la que tenemos ni más ni menos que todas las dimensiones como campos en una entidad.
(que no deja de ser una “vista”).
¿Cómo hace la entidad estándar para sacar en campos un conjunto de dimensiones? Y la respuesta es, computed columns.
Solución:
La dimensionSetEntity rellena mediante computed columns, que suponen directamente modificaciones en la consulta de sql, los diferentes valores a partir de la DimensionAttributeValueSet. Esto es posible porque es en SQL donde sí existen los campos de dicha tabla, con lo cual mediante estas computed columns, consigues tener acceso desde una vista o entidad a los campos creados durante la sincronización, y el nombre de dichos campos, los tenemos en la tabla DimensionAttribute. Por todo esto nos propusimos reproducir el mismo proceso que hace la dimensionSetEntity en una nueva vista. Y quizá os preguntaréis, si ya está creada, ¿por qué no usar directamente la entidad estándar? Pues efectivamente siempre intentamos usar los objetos y procesos estándar pero sencillamente no puedes añadir una entidad en un formulario como data source, y tampoco puedes añadir a una vista los campos de una entidad. Por eso la solución pasa por crear una nueva vista (AXZDimensionSetView).
El resultado final es tan sencillo como:
1 Crear la vista AXZDimensionSetView. Con data source DimensionAttributeValueSet, y sacar el campo RecId (que, recordemos, es el DefaultDimension).
2 Crear los métodos que usaremos para obtener los valores de las diferentes dimensiones. Crearemos, tantos métodos como dimensiones haya que enseñar, a diferencia de como el estándar hace en la DimensionSetEntity. De esta forma nos evitamos tener que hacer el método genérico, puesto que es una complicación completamente innecesaria y que no aporta ningún valor en este caso, ya que, si algún día se añadieran nuevas dimensiones, aún con el código genérico en el método, habría que desarrollar igualmente para añadir el campo. En el caso estándar tiene sentido porque los campos se añaden con un add-in de forma que los crea de forma automática, y cada cliente tendrá dimensiones diferentes, en nuestro caso nos podemos ahorrar esta generalización.
Simplemente lo que hacemos con este código es, a la consulta de la vista, añadirle el nombre del campo de dicha dimensión que queda alojado en la tabla DimesionAttribute. De esta forma tras sincronizar la base de datos vemos que:
Efectivamente, en la consulta de la vista, se han añadido los campos ClienteValue, ProveedorValue… de la tabla DimensionAttributeValueSet. Con lo cual conseguimos lo que queríamos, tener acceso desde desarrollo a dichos campos, mediante una vista, ¡así de fácil!
3 Creamos computed columns de tipo string, y les asociamos en la propiedad View Method el método correspondiente
4 Añadimos una relación (de tipo normal) de la tabla custTrans a nuestra vista.
5 ¡Ya está! Añadimos la vista como data source al formulario CustTrans, relacionado con el data source CustTrans en modo outer join, y podemos sacar los valores de las dimensiones filtrables en el propio grid.
Espero que esto sirva de ayuda a algún desarrollador en apuros en el futuro, a mí me habría ahorrado bastante trabajo haberlo sabido antes.