Thumbnail image

Adopta la mentalidad de extensiones con Dynamics 365 for Finance and Operations

!
Atención: Este artículo se publicó hace más de 365 días. La información podría estar desactualizada.

Índice

Advertencia
Este artículo ha sido traducido automáticamente con Copilot y todavía está pendiente de revisión manual. Ante cualquier error por favor revise el original el inglés cambiando el idioma en la parte superior-derecha

Hace un par de semanas, lanzamos la última platform update 10 (agosto 2017) para Dynamics 365 for Finance and Operations, Enterprise Edition y, como en casi cada versión, hubo cambios respecto a los Planes de Extensibilidad de Aplicaciones.

Se está invirtiendo mucho esfuerzo en el camino hacia una forma no intrusiva de extender la aplicación, y esto debe entenderse como un cambio crítico en la forma en que pensamos sobre las personalizaciones. Puedes encontrar más información sobre los planes futuros en el Dynamics Roadmap (Filtro - Application: Dynamics 365 for Finance and Operations; Área: Extensibility).

Pero a veces es difícil cambiar nuestra mentalidad solo mirando estas notas de actualización fuera de contexto, así que quiero mostrar cómo podemos aprovechar algunas de las últimas mejoras de la plataforma X++ viendo algunos ejemplos reales.

Ten en cuenta el principio básico: queremos evitar cambios intrusivos.

Escenario 1: Crear una secuencia numérica en un módulo estándar

Este es un escenario común y la buena noticia es que apenas ha cambiado desde Dynamics AX 2012. En nuestro ejemplo, añadiremos una nueva secuencia numérica al módulo de Cuentas a Cobrar (Clientes) para proporcionar nuevos valores para el EDT personalizado MSVRMInsuranceProviderId. Para lograrlo, necesitamos hacer algunos pequeños cambios (hay muchos ejemplos de código de AX 2012 disponibles por ahí):

  • Añadir un nuevo bloque de código a loadModule en la clase NumberSeqModuleXXX de nuestro módulo. Es decir: NumberSeqModuleCust.
  • Añadir un nuevo método estático a la tabla de Parámetros del módulo para obtener la referencia de la secuencia numérica, CustParameters en mi ejemplo. Esto no es obligatorio, aunque es una buena práctica.

Una de mis nuevas características favoritas en X++ es el patrón Chain of Command (o method wrapping). Se añadió en la Platform Update 9 (julio 2017) y es la clave para evitar el over-layering en muchos escenarios comunes, como el nuestro. Se explicó muy bien en un vídeo publicado por Michael F. Pontoppidan en su blog. Te animo a que le eches un vistazo, merece la pena los 10 minutos que dura.

Vamos a crear una nueva clase en un modelo de extensión con el siguiente código:

[ExtensionOf(classStr(NumberSeqModuleCustomer))]
final class NumberSeqModuleCustMSVRM_Extension
{
    protected void loadModule()
    {
        NumberSeqDatatype datatype = NumberSeqDatatype::construct();

        next loadModule(); // Ejecuta el método original aumentado de la clase

        /* Insurance Provider Id */
        datatype.parmDatatypeId(extendedTypeNum(MSVRMInsuranceProviderId));
        datatype.parmReferenceHelp(literalStr('Provider ID'));
        datatype.parmWizardIsContinuous(false);
        datatype.parmWizardIsManual(NoYes::No);
        datatype.parmWizardIsChangeDownAllowed(NoYes::No);
        datatype.parmWizardIsChangeUpAllowed(NoYes::No);
        datatype.parmSortField(1);
        
        datatype.addParameterType(NumberSeqParameterType::DataArea, true, false);
        this.create(datatype);
    }
}

Esta es una clase de extensión regular como las que venimos usando desde los inicios de Dynamics 365. Las partes más importantes del código están marcadas en negrita:

  • Sufijo Extension en el nombre de la clase. Esto es obligatorio según los estándares de .NET.
  • El atributo ExtensionOf le dice al compilador qué clase estamos extendiendo.
  • La llamada a next. Esto es lo nuevo que quería mostrar. Nos permite llamar al mismo método en la clase que estamos extendiendo. Puede confundirse con una llamada a super(), pero no es lo mismo ya que aquí no hay herencia ni sabemos cuántas extensiones puede tener esta misma clase implementadas en diferentes módulos. Esto último es importante ya que -igual que con los event handlers- no podemos contar con el orden de ejecución aquí.

De esta forma podemos añadir (o “encadenar”, según la terminología chain-of-command) código al método existente y esto es suficiente para satisfacer nuestros requisitos para este escenario. Con solo esta pequeña clase de extensión, tenemos una nueva secuencia numérica en el formulario de Parámetros de cuentas a cobrar sin ningún over-layering:

El siguiente paso es añadir el método estático que devolverá la referencia de la secuencia numérica. Es una especie de buena práctica (BP) añadir este método a la tabla de parámetros del módulo (CustParameters en mi ejemplo). En Dynamics AX 2012 había una excepción aceptada para esta BP donde a veces estos métodos se creaban en una tabla personalizada (como la tabla de parámetros de nuestro propio módulo) para evitar hacer over-layering a una tabla estándar solo por este pequeño cambio. Ya no es un problema.

Podemos crear una clase de extensión de tabla para añadir el método estático a la tabla estándar sin over-layering:

[ExtensionOf(tableStr(CustParameters))]
final class CustParametersMSVRM_Extension
{
    client server static NumberSequenceReference numRefMSVRMInsuranceProviderId()
    {
        return NumberSeqReference::findReference(extendedTypeNum(MSVRMInsuranceProviderId));
    }
}

Por supuesto, como las extensiones son aumentos fuertemente tipados de las clases, tenemos IntelliSense sobre los nuevos métodos, ya que de hecho forman parte de la versión final de la clase (llamada: effective class):

Escenario 2: Crear una secuencia numérica en un módulo nuevo

Ahora imagina que quieres crear la misma secuencia numérica que en el escenario anterior pero creando tu propio módulo. Los pasos requeridos son bien conocidos:

  • Añadir un nuevo elemento al enum base NumberSeqModule.
  • Crear una clase NumberSeqModule para nuestro módulo.
  • Hacer que el sistema sepa que nuestro módulo existe.

Para añadir un nuevo elemento al enum base NumberSeqModule podemos usar una extensión (en la última versión se hicieron extensibles un gran número de enums base):

NOTA: Usa los filtros “e:” o “c:” en el diseñador de objetos para ver solo los elementos e:xtendidos o c:ustomizados, como se muestra en la captura.

Ahora necesitamos crear una nueva clase (una clase completamente nueva, no extensiones aquí ya que es un módulo nuevo, no una extensión de uno existente) con el siguiente código:

// Detailed description of how to setup references for number sequences can
// be found in method loadModule() on the superclass: NumberSeqApplicationModule.
class NumberSeqModuleMSVRM extends NumberSeqApplicationModule
{
    protected void loadModule()
    {
        NumberSeqDatatype datatype = NumberSeqDatatype::construct();

        /* Insurance Provider Id */
        datatype.parmDatatypeId(extendedTypeNum(MSVRMInsuranceProviderId));
        datatype.parmReferenceHelp(literalStr('Provider ID'));
        datatype.parmWizardIsContinuous(false);
        datatype.parmWizardIsManual(NoYes::No);
        datatype.parmWizardIsChangeDownAllowed(NoYes::No);
        datatype.parmWizardIsChangeUpAllowed(NoYes::No);
        datatype.parmSortField(1);
        
        datatype.addParameterType(NumberSeqParameterType::DataArea, true, false);
        this.create(datatype);
    }

    public NumberSeqModule numberSeqModule()
    {
        return NumberSeqModule::MSVRM;
    }

    [SubscribesTo(classstr(NumberSeqGlobal), delegatestr(NumberSeqGlobal, buildModulesMapDelegate))]
    static void buildModulesMapSubsciber(Map numberSeqModuleNamesMap)
    {
        NumberSeqGlobal::addModuleToMap(classnum(NumberSeqModuleMSVRM), numberSeqModuleNamesMap);
    }
}

Una suscripción al delegado buildModuleMapDelegate nos permite añadir nuestro nuevo módulo al Map estándar de secuencias numéricas sin ningún over-layering. Se han añadido muchos nuevos delegados a los objetos estándar últimamente para permitir más puntos donde se puede hacer una suscripción para cambiar el comportamiento del código estándar. Búscalos, ya que esta es la forma en que los desarrolladores indican a otros dónde poner sus modificaciones.

Si eres un ISV, añade delegados a tu propio código para permitir que otros extiendan tus módulos sin over-layering. Esto facilitará la vida a todas las partes, ya que actualizar el código del ISV será posible sin ningún conflicto de código.

Si crees que tienes un escenario de negocio que es imposible de lograr usando solo extensiones, por favor registra una solicitud de extensión explicando tu caso.

Última actualización — Ago/2017

  • Corregido el segundo ejemplo de código.

Publicado originalmente en “Dynamics AX in the Field”, el blog del equipo de Premier Field Engineering de Microsoft, y en LinkedIn, ¡déjame un comentario allí para conocer tu opinión!

Publicaciones en esta serie

Publicaciones similares