Buscar en este blog

jueves, 25 de septiembre de 2014

Crear reportes SSRS usando RDP (Report Data Provider) con clase controladora en Dynamics Ax 2012

La clase controller mejor conocida en el mundo latino como clase controladora implementa el patrón estándar de diseño MVC (Model View Controller). Lo que significa que con este patrón podemos separar la lógica de negocio de la interfaz de usuario, en este caso, podemos separar la lógica de negocio de la vista de nuestro reporte. 
Model: Es el responsable de recuperar los datos y la lógica de negocio, esto incluye: queries, datos de métodos o clases diseñadas para traer datos.
View: Esta es la interfaz de usuario, nuestro diseño del reporte.
Controller: Orquesta el flujo entre el Model y el View.

En general usamos este tipo de reportes para proveer grouping y validaciones para los parámetros de un reporte SSRS. Esta clase también es necesaria cuando necesitamos personalizar el dialogo que se muestra cuando abrimos un reporte, por ejemplo, cuando ciertos campos no son parámetros del reporte.

Para crearnos un reporte en Ax 2012, necesitamos crearnos algunos elementos, el orden sería el siguiente:
1. Un query que contenga todos los campos que vamos  necesitar en nuestro reporte
2. Una tabla de tipo temporal
3. Clase "contrato", en la declaración de la clase  usar el atributo [DataContractAttribute] y por cada parámetro necesario para el reporte usar un método parm con el atributo [DataMemberAttribute]
4. Clase controladora que extienda de SrsReportRunController, en esta clase crear el método main para definir nombre de reporte y diseño (este es nuestro punto de inicio del reporte). Sobreescribir los métodos necesarios para el arranque de nuestro reporte.
5. Clase DataProvider que extienda de SrsReportDataProviderPreProcess, en la declaración usar los atributos SRSReportQueryAttribute para indicar el query con que el trabaja en conjunto y el atributo SRSReportParameterAttribute  para indicar la clase contrato que va a proveer los parámetros del reporte. Crear el método getNombreTabla para devolver los registros de la tabla temporal con el atributo SRSReportDataSetAttribute.

Clase contrato
Esta clase es usada para crear metodos parm para el reporte. Si le vamos a pasar parametros al reporte necesitamos crear tantos métodos parm como parámetros necesitemos como "datamembers" de nuestra clase. Por ejemplo:

[DataMemberAttribute('UseQuantity')]
public ProdBOMJournalQty parmUseQuantity(ProdBOMJournalQty _useQuantity = useQuantity)
{
    useQuantity = _useQuantity;
    return useQuantity;
}

Crearemos un método de estos por cada parámetro que el usuario nos pida o que necesitemos.

Es posible definir las etiquetas y los textos de ayuda de los parámetros que definamos en la clase controladora, por ejemplo en cada uno de ellos pondríamos los atributos como sigue:

[
    DataMemberAttribute("DifferencesOnly"),   //Esto es para el nombre del parametro
    SysOperationGroupMemberAttribute("DifferencesOnlyGroup"),  //Para indicar si pertenece a un grupo de parametros
    SysOperationLabelAttribute( literalstr("@SYS57830" )),    //Indica la etiqueta del paramtero que se va a mostrar al usuario
    SysOperationHelpTextAttribute( literalstr("@SYS328295" )),  //Puedes poner un texto de ayuda que aparecerá en la barra inferior cuando posicionen el cursor en el parámetro
    SysOperationGroupMemberAttribute("1"),  //El orden en que aparecerá el parámetro si pertenece a un grupo
]
public boolean parmDifferencesOnly(boolean _differencesOnly = differencesOnly)
{
    differencesOnly = _differencesOnly;
    return differencesOnly;
}



Clase controladora
La clase controladora debe extender de SrsReportRunController. Esta es la clase que lanza el reporte y de esta clase es la cual hacemos el menu item para ponerlo en nuestros menus o en algun botón. Por ejemplo:

public class ProdPackListController extends SrsReportRunController
{
}

En el método "main" se debe definir el nombre de reporte y el diseño. El método parmReportName es usado para especificar el nombre del reporte que correrá con esta clase, en caso de tener dos diseños para un reporte, aquí es donde deberá especificarse.

El método startOperation() es usado para correr el reporte.

El método parmShowDialog(false) sirve para lanzar solo el reporte sin presentar el dialog antes. Esto se pone antes del starOperation() en el main.

Por ejemplo:
public static void main(Args _args)
{
    SrsReportRunController    controller = new  ProdPackListController  ();
    controller.parmReportName("ProdPackList.Report");
    controller.parmArgs(_args);
    controller.startOperation();
}

Cancelación de métodos:
PreRunModifyContract: Para modificar el query antes que el reporte se corra. Por ejemplo:
protected void preRunModifyContract()
{
    GNProdPacklistContract    contract;
    contract = this.parmReportContract().parmRdpContract() as GNProdPacklistContract;
}

prePromptModifyContract: Para modificar el query antes de que el dialog del reporte se lance.

preRunValidate: Aqui es posible manejar los warnings con este método.

La clase controladora debe implementar la interfase SySOperationValidatable. Esto solo es necesario en caso de que se necesite validación de los parámetros del reporte. Después se necesitara sobreescribir el método "validate" de la clase controladora.


Clase DataProvider
La clase DataProvider ya la vimos en el post pasado, pero aqui hay otro ejemplo, debe extender de SrsReportDataProviderPreProcess. Por ejemplo:
[   SRSReportQueryAttribute(queryStr(ProdPackList)),
    SRSReportParameterAttribute(classStr(ProdPacklistContract))]
class ProdPacklistDP extends SrsReportDataProviderPreProcess
{

    boolean                     showQuery;
 
    boolean                     firstPage;
    ProdTable                   prodTable;
    ProdId                      prodId;

    CompanyInfo                 companyInfo;

    ProdBOM                     prodBOM;
    InventDim                   inventDim;

   ProdPackingSlipDetailsTmp prodPackingSlipDetailsTmp;
    BarcodeSetup                barCodeSetup;
    BarcodeSetupId  barcodeSetupId;
    Barcode         barcode;
}

Debemos crear un método con el atributo SRSReportDataSetAttribute que devuelva todos los registros de la tabla temporal. Por ejemplo:
[    SRSReportDataSetAttribute(tableStr('ProdPackingSlipDetailsTmp'))]
public ProdPackingSlipDetailsTmp getProdPacklistDetailsTmp()
{
    select prodPackingSlipDetailsTmp;
    return prodPackingSlipDetailsTmp;
}

Y sobreescribimos el método ProcessReport, que es donde vamos a escribir la lógica del negocio y a llenar nuestra tabla temporal. Por ejemplo:
[SysEntryPointAttribute(false)]
public void processReport()
{
    QueryRun                queryRun;
     ProdPacklistContract contract       = this.parmDataContract() as  ProdPacklistContract ;
    // Set the userconnection to use on table.
    // This is required to ensure that createdTransactionId of inserted record is different than default 
           transaction.
    prodPackingSlipDetailsTmp.setConnection(this.parmUserConnection());
    this.init();
    this.setupBarcode();
    queryRun                        = new QueryRun(this.parmQuery());
    while (queryRun.next())
    {
        prodTable       = queryRun.get(tableNum(ProdTable));
        inventDim       = queryRun.get(tableNum(InventDim));
        prodId          = prodTable.ProdId;
        if(prodTable.InventRefType==InventRefType::Sales)
        {
           this.insertHeader();
           this.insertDetails(prodId,inventDim.inventSerialId);
        }
    }
}

Aquí les dejo otro processReport un poco mas complejo, por que si hacen reportes complejos, siempre se necesitan filtros personalizados.
public void processReport()
{
    QueryRun        qr;
    QueryBuildDataSource    qbDS;
    QueryBuildRange rangeOnlyOpen;
    QueryBuildRange rangeOnlyInvoices;
    QueryBuildRange rangeTransDate;
    QueryBuildRange rangeDueDate;
    ;
    contract            = this.parmDataContract() as GRWCustAccStatementContract;
    primaryCurrencyCode = CompanyInfo::standardCurrency();

    qr = new QueryRun(this.parmQuery());

    if(contract.parmOnlyOpen())
    {
        rangeOnlyOpen = qr.query().dataSourceNo( 2).addRange(fieldNum (CustTrans, Closed));
        rangeOnlyOpen.value(queryRangeConcat(queryRange(contract.parmToDate() + 1, maxdate ()), dateNull()));
    }

    if(contract.parmOnlyInvoices())
    {
        rangeOnlyInvoices = qr.query().dataSourceNo( 2).addRange(fieldNum (CustTrans, AmountCur));
        rangeOnlyInvoices.value(SysQuery::value( ">0"));
    }

    if(SysQuery::value(contract.parmFromDate()) != "" && SysQuery::value(contract.parmToDate()) != "")
    {
        rangeTransDate = qr.query().dataSourceNo( 2).addRange(fieldNum (CustTrans, DueDate));
        rangeTransDate.value( strFmt("(DueDate>=%1) && (DueDate<=%2)" , date2StrXpp(contract.parmFromDate()), date2StrXpp(contract.parmToDate())));
    }

    While (qr.next())
    {
        if(qr.changed(tableNum(custTable)))
        {
            custTable = qr.get( tableNum(custTable));
        }
        if(qr.changed(tableNum(CustTrans)))
        {
            custTrans = qr.get( tableNum(custTrans));
            custTrans.transactionPerDate(contract.parmToDate());
            if(contract.parmOnlyOpen() && !custTrans.remainAmountCur())
                continue;

            this.proccessRecord();
        }
    }
}




Por último, te invito a que te unas a la página de facebook recién creada para estar al día con las actualizaciones del blog y que podamos tener más comunicación. La meta? es hacer la comunidad de habla hispana mas grande sobre Dynamics Ax en cuestiones de desarrollo.



Y por cierto, acuerdate de darle click a algún anuncio si el post te sirvio de algo.



miércoles, 24 de septiembre de 2014

Escribir en un puerto serial RS232 mediante Dynamics Ax 2012

Hace poco necesite escribir en un puerto serial para enviar información a un dispositivo que a su vez enviaba información a dispositivos inalambricos. Así que aquí esta el método para enviar lo que necesiten por RS232 o mejor conocido como puerto serial.

public void sendSerial(int _numeroLocalizador)
{
    str linea1, linea2, linea3, completo;
    boolean banderaPuerto;
    System.IO.Ports.SerialPort puerto;
    ;
    try
    {
        linea1 = int2str(_numeroLocalizador) + num2char(13) + "Movimiento de Unidad *Empresa1*" + num2char(13 );
        linea2 = num2char(127 ) + num2char( 127) + num2char (127);
        linea3 = num2char(127 ) + num2char( 127) + num2char (127);

        puerto = new System.IO.Ports.SerialPort();
        banderaPuerto = puerto.get_IsOpen();
        puerto.set_PortName( "COM1");
        puerto.set_BaudRate( 9600);
        puerto.set_Parity(System.IO.Ports.Parity::None);
        puerto.set_DataBits( 8);
        puerto.set_StopBits(System.IO.Ports.StopBits::One);
        puerto.Open();
        puerto.set_ReceivedBytesThreshold( 1);

        banderaPuerto = puerto.get_IsOpen();
        if(banderaPuerto)
        {
            puerto.Write(linea1);
            puerto.Write(linea2);
            puerto.Write(linea3);

            puerto.Dispose();
            puerto.Close();
            banderaPuerto = puerto.get_IsOpen();
            info( strFmt("Alerta enviada a localizador: %1" , _numeroLocalizador));
        }
    }
    catch (Exception::CLRError)
    {
        puerto.Dispose();
        puerto.Close();
        info(CLRInterop::getLastException());
    }
}



Por último, te invito a que te unas a la página de facebook recién creada para estar al día con las actualizaciones del blog y que podamos tener más comunicación. La meta? es hacer la comunidad de habla hispana mas grande sobre Dynamics Ax en cuestiones de desarrollo.



Por qué y para qué pongo el botón de PayPal en el blog


Sin rodeos, porque necesito dinero, esa es la razón principal.

La segunda razón, es porque no soy hija de Carlos Slim y parece que ni remotamente me quiere adoptar.

Y la tercera, es porque un par de comentarios me han dicho que les ha sido de ayuda mi blog y que si pudieran me invitarían una hamburguesa, así que como generalmente no vivimos en el mismo país, se me ocurrió que si ustedes quieren y pueden donar unos centavos o un dólar o unos dólares enteros para mi, se los agradecería, no estoy cobrando lo que pongo aquí porque lo hago con harto gusto, es para compartir algo de lo que aprendo, y si ustedes cooperan para esta noble causa (que soy yo) pues todos ganamos, yo les ayudo a facilitarles algún código y ahorrarles algunas horas de búsqueda y ustedes me ayudan a juntar para un micrófono decente porque quiero hacer un video tutorial de desarrollo en Ax, por supuesto en español, porque en ingles ya hay.

No espero hacerme millonaria con el botón de donaciones, pero ojala algún día me alcance para comprarme un café y una dona para acompañarme cuando pongo tips en el blog.

Así que si alguien quiere ponerse guapa y guapo con las donaciones, gracias adelantadas. Y si alguien necesita resolver algún tema en Ax pues coméntenmelo, igual lo resolvemos en conjunto.

También quiero hacerles la atenta invitación de que si a alguien le da flojera poner ese código valiosísimo que les costo horas y horas en un blog, pues mándenmelo y yo lo publico aquí, eso si en español y con su nombre, así nos vamos ayudando unos a otros.

Fin.

martes, 23 de septiembre de 2014

Poner campos referenciados en un grid en AX 2012


Esto sirve para cuando queremos que con la opción "Ver detalles..." del menu contextual en algún campo del grid, ax abra el formulario que tiene la información fuente referente a ese campo.

En este ejemplo, el campo referenciado en el grid, abre el formulario de orden de compra.
Cancelar campo jumpRef del campo en el formulario

public void jumpRef()
{
    //super();
    Args args= new Args();
    PurchTable purchTable;
    ;
    args.caller(this);
    purchTable = PurchTable::find(OrdenCompra.valueStr());
    Args.record(purchTable);

    new MenuFunction( menuitemDisplayStr(VendPackingSlipJournal), MenuItemType::Display).run(args);
}

Es todo.


Por último, te invito a que te unas a la página de facebook recién creada para estar al día con las actualizaciones del blog y que podamos tener más comunicación. La meta? es hacer la comunidad de habla hispana mas grande sobre Dynamics Ax en cuestiones de desarrollo.

domingo, 21 de septiembre de 2014

Conservar los filtros de usuario en un grid


Aquí un pedazo de código que me costó tiempo, es por si alguna vez necesitan conservar los filtros del usuario en un grid y al mismo tiempo aplicar y/o quitar un filtro por código.

En mi caso, necesitaba que dependiendo del valor de un checkbox se mostraran u ocultaran los registros en un grid dependiendo del valor de un campo especifico, pero además que se conservaran los filtros que el usuario hubiera puesto.

En el executequery del datasource del form:

public void executeQuery()
{
    //Instancia para el queryRun del Datasource
    //(es la que va a traer los filtros del usuario)
    Query                   queryRunForProjRevenueTrans;
    QueryBuildDataSource    qbdsForProjRevenueTrans;
    QueryBuildRange         qbrForProjRevenueTrans;

    //Instancia para el query del Datasource
    //(es la que trae el query original)
    Query                   origQueryForProjRevenueTrans;
    QueryBuildDataSource    origQbdsForProjRevenueTrans;
    QueryBuildRange         origQbrForProjRevenueTrans;
  
    boolean updateOrigQuery;
 
    //Revisa si el query con filtros de usuario existe,
    //sino existe, toma el query original
    if(ProjRevenueTrans_DS !=null && ProjRevenueTrans_DS.queryRun() != null && ProjRevenueTrans_DS.queryRun().query() != null)
    {
        queryRunForProjRevenueTrans = ProjRevenueTrans_DS.queryRun().query();
        updateOrigQuery = true;
    }
    else
        queryRunForProjRevenueTrans = ProjRevenueTrans_DS.query();
       
    //Limpiando el range personalizado que se necesita por código
    qbdsForProjRevenueTrans = queryRunForProjRevenueTrans.dataSourceName("ProjRevenueTrans");
    qbdsForProjRevenueTrans.clearRange(fieldnum(ProjRevenueTrans, GRWReferencia));
    qbrForProjRevenueTrans = qbdsForProjRevenueTrans.addRange(fieldnum(ProjRevenueTrans, GRWReferencia));
 
    //Actualizando los querys en ambos casos (query o queryRun)
    if(MostrarDetalles.value() == 1//Este es mi control (checkbox) condicionante para poner un range por código
    {
        qbrForProjRevenueTrans.value(SysQuery::valueUnlimited());
        if(updateOrigQuery)
        {
            origQbdsForProjRevenueTrans =  ProjRevenueTrans_DS.query().dataSourceName("ProjRevenueTrans");
            origQbdsForProjRevenueTrans.clearRange(fieldnum(ProjRevenueTrans, GRWReferencia));
            origQbrForProjRevenueTrans = origQbdsForProjRevenueTrans.addRange(fieldnum(ProjRevenueTrans,GRWReferencia));
            origQbrForProjRevenueTrans.value(SysQuery::valueUnlimited());
        }
 }
    else
    {
        qbrForProjRevenueTrans.value(SysQuery::valueEmptyString());
        if(updateOrigQuery)
        {
            origQbdsForProjRevenueTrans =  ProjRevenueTrans_DS.query().dataSourceName("ProjRevenueTrans");
            origQbdsForProjRevenueTrans.clearRange(fieldnum(ProjRevenueTrans, GRWReferencia));
            origQbrForProjRevenueTrans = origQbdsForProjRevenueTrans.addRange(fieldnum(ProjRevenueTrans,GRWReferencia));
            origQbrForProjRevenueTrans.value(SysQuery::valueEmptyString());
        }
    }
 
    super();
}


Y en el modified de mi checkbox solo llamé un método que hace el research del datasource

ProjRevenueTrans_ds.research(true);



Por último, te invito a que te unas a la página de facebook recién creada para estar al día con las actualizaciones del blog y que podamos tener más comunicación. La meta? es hacer la comunidad de habla hispana mas grande sobre Dynamics Ax en cuestiones de desarrollo.


jueves, 31 de julio de 2014

Crear servicio personalizado AIF – Custom Services, Microsoft Dynamics AX 2012


Aquí voy a presentar cómo se crea un servicio AIF en 4 pasos para consultar el grupo de clientes y exponerlo en un servicio WCF para que sea consumido por cualquier herramienta, por ejemplo .NET. Este ejemplo por supuesto funciona para cualquier escenario donde se necesiten consultar datos de Ax.

Los conceptos básico de AIF no los explico aquí porque supongo que de algo tienen idea si es que están leyendo este post. Y sino tienen toda la teoría referente a este tema, les recomiendo el libro que recientemente (Marzo, 2014) saco Packt Publishing, se llama “Microsoft Dynamics AX 2012 R2 Services” es un libro bastante completo y bien explicado.

Portada de libro

1. Crear los métodos que va a exponer el servicio. Para esto lo primero es crearnos una clase, esta va a servir para exponer las operaciones (métodos), en este caso, consultar el grupo de clientes, le vamos a llamar GRW_InterfazAxCDI_GrupoClientes. Ponemos su propiedad de RunOn en Server para que siempre corra del lado del servidor y consulte ahí siempre los grupos de clientes.

image

Al método que nos consulte los grupos de clientes, le ponemos el atributo SysEntryPointAttribute en true, esto le indica a Ax que es un método del tipo Service Operation.
[SysEntryPointAttribute( true ), AifCollectionTypeAttribute('return' , Types::String)]
//regresa una lista de strings
public List getCustomerGroupList()
{
    CustGroup       custGroup;
    List list = new List(Types::String);  
  
    //seleccionamos todos los grupos de clientes
    while select from custGroup      
    {
        //agregamos los grupos a la lista
        list.addEnd(custGroup.CustGroup);
    }
  
    return list;
}
2. Crear el servicio. Vamos a crear un servicio y agregarle el Service Operation que creamos. Esto lo hacemos en AOT, Servicios –> Nuevo servicio, le ponemos el nombre que queramos que en este caso yo elegí GRW_interfazAxCDI_GrupoClientesServicio y en la propiedad Class seleccionamos nuestra clase anterior llamada GRW_InterfazAxCDI_GrupoClientes.

imageimage

Expandemos nuestro servicio y click derecho sobre “Operations”, seleccionamos “Agregar Operation”, con esto, nos va a mostrar todos los métodos que estén expuestos en nuestra clase, en este caso solo tenemos un método llamado getCustomerGroupList, lo seleccionamos para que se agregue a nuestro servicio.
image    image

3. Crear un Service Group, para exponer nuestro servicio. Vamos a Service Group –> Nuevo Service Group, le ponemos el nombre que queramos y en la propiedad “AutoDeploy” lo marcamos en “Yes”, esto le indica a Ax que este servicio se debe iniciar cuando el AOS se inicie y en la propiedad “Descripción” el nombre con el que se va a exponer nuestro servicio.

image     image

Ahora, sobre el mismo grupo creado vamos a crear un Node Reference y en la propiedad “Service” seleccionamos nuestro servicio que creamos en el paso 2.

image   image

4. Implementar servicio. Sobre el grupo creado, click derecho e Implementar servicio. Esto se tarda un poco.
image

Listo! con esto ya tenemos nuestro servicio creado. Ahora vamos a verlo en Ax.

Consultar nuestro servicio implementado.

Para esto vamos a Administración de sistema –> Configuración –> Servicios y aplicaciones –> Puertos de entrada, ahí buscamos el nombre de nuestro grupo de servicios y podremos ver la URL donde se expone el WSDL.

En esta pantalla debemos verificar que el servicio se encuentra activo, sino esta activo, presionar el botón de “Activar”.
image image
image

Consumir servicio desde .Net

Para probar que el servicio este devolviendo los datos que requerimos, vamos usar el WCF Test Client de Visual Studio, para abrirlo vamos a All Programs -> Microsoft Visual Studio 2010 -> Visual Studio Tools -> Visual Studio Command Prompt y escribimos: wcftestclient
Ya que se abre el WCF test client de .Net, agregamos nuestro servicio copiando la URL del WSDL de nuestro servicio expuesto.

En el WCF Test Client vamos a ver nuestro método expuesto que nos consulta los grupos de clientes, en este caso, solo tenemos que indicar la compañia de la cuál vamos a consultar los datos, ya que siempre que vamos a invocar un servicio, el parámetro por defaul, es el context, le damos doble click y después click en el botón de “Invocar”, con esto veremos el resultado.

Y la otra forma, es crearnos un proyecto de visual studio, agregar la referencia de nuestro servicio y consumirlo, sencillo, no?

Con la referencia me refiero a la URL donde se publico el WSDL del servicio. Y cuando se haya agregado, y esto es solo como tip y al gusto de cualquiera, le dan click derecho al servicio –> Configurar y en la opción “Collection type” elegir System.Collections.Generic.List esto nos facilita la vida en .Net ya que podemos trabajar nuestro resultados con un simple List<string> por ejemplo, es decir, usando listas genéricas fuertemente tipadas. En caso de no hacer eso, tendremos que usar arrays, que a mi en lo personal no me gustan nada nada.

image

Aquí un pedazo de código de ejemplo de cómo leer los datos que estoy recibiendo de ax.

private void button1_Click( object sender, EventArgs e)
{
     //Instanciamos el servicio
     using( ServiceReference1.QueryCustGroupAXClient servicio = new ServiceReference1.QueryCustGroupAXClient())
     {
          List< string> gruposDeClientes = new List< string>();
          StringBuilder cadenaGrupos = new StringBuilder ();

          //Instanciamos el CallContext para enviar la compañia de la que se quiere consultar
          //los grupos de clientes
          ServiceReference1.CallContext paramsCompania = new ServiceReference1.CallContext();
          paramsCompania.Company = "companiaEjemplo" ;

          //Invocamos el servicio enviandole el CallContext como parámetro
          //y lo asignamos a una lista de strings
          gruposDeClientes = servicio.getCustomerGroupList(paramsCompania);

          foreach(String grupoCliente in gruposDeClientes)
          {
               cadenaGrupos.Append(grupoCliente);
               cadenaGrupos.Append( ", " );
          }

          textBox1.Text = cadenaGrupos.ToString();
     }
}

Resultado:
image

Todo lo anterior se hizo desde el mismo segmento de red donde se encuentra el servidor de ax y con usuario de dominio en la misma red.

Qué pasa si por ejemplo nos conectamos por vpn a donde se encuentra el servicio expuesto de ax?
Qué pasa si nuestra máquina no esta dentro del dominio?

Para ambos escenarios debemos enviar las credenciales, eso lo veo en el siguiente post.



Y por cierto, acuerdate de darle click a algún anuncio si el post te sirvio de algo.


No olvides que te puedes unir a la página en Facebook Aprendiendo Dynamics Ax donde únicamente se tratan temas de desarrollo y se busca crear una comunidad de desarrollador@s de Ax en nuestro idioma. 


Consumir servicio personalizado AIF con credenciales   ----->>>>>





miércoles, 9 de julio de 2014

Consultar direcciones de compañia actual por tipo.


Para consultar las distintas direcciones de la compañia una por una indicando el tipo se hace se la siguiente forma:
static void getAddressBasedOnPurposeForLegalEntity(Args _args)
{
    LogisticsAddressing  address;
    ;
    address = DirParty::findPostalAddressByRole(CompanyInfo::current(),LogisticsLocationRoleType::Delivery).Address;
    info(address);  
}


Por último, te invito a que te unas a la página de facebook recién creada para estar al día con las actualizaciones del blog y que podamos tener más comunicación. La meta? es hacer la comunidad de habla hispana mas grande sobre Dynamics Ax en cuestiones de desarrollo.




jueves, 9 de enero de 2014

Enterprise Portal Dynamics Ax 2012 - Crear control de usuario (User Control) en Visual Studio para Ax

 
1. Abrir el Visual Studio con permisos de administrador
2. Nuevo proyecto > Microsoft Dynamics AX > EP Web Application
Image(58)
3. Lo recomendable es agregar el proyecto al AOT para que el control de guardado quede del lado de AX
Image(59)
Este proyecto va a poder ser abierto desde el AOT en Visual Studio Projects > Web Application Projects.
Agregar un nuevo item
Image(60)
Image(61)
Se agrega el data set en el control AxDataSource
Image(62)
Ligamos el datasource con el control de usuario:
Image(63)
Agregamos los campos a los grupos que vayamos necesitando.
Ahora para implementar esta página de detalles, creamos un nuevo menu item del tipo URLs, lo nombramos como queramos y haciendo clic derecho sobre el seleccionamos Importar página
Image(64)
Seleccionar la nueva URL que acabamos de crear (algunas veces esto marca error, asi que también se puede hacer en las propiedades del URL y poner a mano la dirección de la página que creamos). Además de esto tambien en la propiedad WindowMode seleccionamos Modal para que nuestra página de detalles se ve tipo modal.
Image(65)
Ahora debemos asociar los botones del list page para que cuando se quieran ver los detalles de un registro se abra esta ventana modal. Lo primero es crear un Menuitem del tipo Display que vamos a asociar a un botón, este MenuItem debe tener en su propiedad WebMenuItem el nombre del URL que creamos para nuestro DetailPage y opcional en la propiedad Object el nombre de un form si queremos que este mismo MenuItem funcione en el cliente de AX llamando a un formulario en particular.
Image(66)
Para asociar este MenuItem que creamos a nuestro botón View del ListPage lo que debemos hacer es, en la propiedad MenuItemIname del botón seleccionar el nombre de nuestro MenuItem del tipo display que acabamos de crear.
Listo!!!!!! con esto ya tenemos nuestro botón de ver detalles enlazado a nuestra página de detalles y se abre en modo modal. Pero ahora, esto se va a ver mejor si enlazamos el número de orden de compra para que se abra en automático la ventana modal de detalles. Para hacer esto en el campo dentro del grid, en la propiedad HyperLinkMenuItem seleccionar nuestro MenuItem tipo display que creamos.
Image(67)
Image(68)
Image(69)



Y por cierto, acuérdate de darle click a algún anuncio si el post te sirvió de algo.


No olvides que te puedes unir a la página en Facebook Aprendiendo Dynamics Ax donde únicamente se tratan temas de desarrollo y se busca crear una comunidad de desarrollador@s de Ax en nuestro idioma. 





<<    Crear DataSet                                                                     Modificar un control de usuario  >>>>


Post relacionados:
Crear DataSet
Crear ListPage con PartPreview Pane en EP   
Implementar List Page en EP 
Crear Cues en Enterprise Portal   

Enterprise Portal Dynamics Ax 2012 – Crear Data Set


Para crear un data set puede ser con un grupo de tablas o mediante un query igual que cuando se crea un form y al igual que el form se le puede poner código para el tratamiento de datos.

1. Crear un nuevo DataSet en el nodo DataSets del AOT
2. En el nodo de DataSource de nuestro DataSet nuevo arrastrar la(s) tablas que vayamos a necesitar para el detalle de la página de detalles.
3. Si se usan varias tablas, usar las propiedades JoinSource and LinkType en cada datasource como se necesite, igual que en los forms.
Las propiedades en el datasource son muy similares al datasource que se agrega a un form normalito del AOT.

Image(57)




<< Implementar List Page                                                Crear control de usuario >>