Thursday, February 21, 2019

¿Cómo quitar el mensaje.. "More Than One Form Was Opened at Once for the Lookup Control"? - D365FO

Hola.

Cuando por alguna situación requerimos escribir código para el evento  "onLookup" de un control en un formulario de Dynamics 365 FO, nos encontramos que al ejecutarlo se muestra el mensaje "More Than One Form Was Opened at Once for the Lookup Control"

Captura de ejemplo:


Para solucionarlo, sigue los siguientes pasos:

1. Declara una nueva variable del tipo "FormControlCancelableSuperEventArgs"
2. Inicializa la variable con el valor FormControlEventArgs del parámetro "e"
3. Usa la variable para llamar al método CancelSuperCall()

Ejemplo:




Enjoy daXing


Wednesday, December 19, 2018

Extension de una Tabla en Dynamics 365 Finance and Operations

Una de las novedades que más impacto puede traer a la hora de querer agregar funcionalidad a objetos nativos de Ax es que se recomienda NO tocar los Originales (Overlaying) y en vez de esto, crear un extensión del mismo y ahí hacer las modificaciones pertinentes.

Esto tiene una ventaja enorme, significa mantener los objetos intactos y cuando Microsoft haga alguna actualización pues no tocará nada de lo que hayamos desarrollado afectando únicamente al estándar de Ax.

Vamos a la práctica.

Supongamos que tenemos una tabla con cierto número de campos y queremos agregar algún otro campo que haga falta sin afectar a la tabla original.

Vamos a crear una nueva Tabla (en la vida real, esta sería una tabla nativa de Ax)

Clic derecho sobre el proyecto, Add y finalmente New ítem..
En la sección Data Model, tenemos la opción Table. Le damos un nombre y agregamos

Le agregamos los campos base:
Los campos se agregan de manera tradicional; click derecho, New, tipo de dato deseado...

Ahora vamos a crear una extensión de la tabla (Buscamos nuestra tabla desde el AOT, si no se encuentra, debemos sincronizar primero el proyecto con la base de datos)
Sobre el AOT, se puede hacer una búsqueda para encontrar nuestro objeto al cual le crearemos un "Extensión"
Esto agrega un nuevo objeto a nuestro proyecto con el Nombre del objeto original y agregando .Extensión
Extensión del Objeto 

Lo abrimos y le agregamos un nuevo campo:
Abrir el objeto Extension
Se agrega el nuevo campo 


Sincronizamos nuevamente y ahora vamos a escribir algo de código. La idea es instanciar un objeto de la tabla original (MyTable) que a su vez, al tener una extensión, debería mostrarnos tres campos (dos originales y uno de la extensión)
La instancia del Objeto MyTable muestra el campo creado en la Extension

El job, agrega un nuevo registro. Para comprobar la funcionalidad, visualizamos la tabla en el explorador:
Para visualizar los datos de la tabla en un Web Browser

Vista previa de la tabla con los tres campos y sus datos respectivamente.

Como podemos ver, la tabla tiene tres campos, y no dos como originalmente estaba diseñada.

Espero haberles ayudado.

Saludos!

Tuesday, December 18, 2018

Ejecutando un JOB en Dynamics 365 Finance and Operations

En Dynamics 365 todo es diferente.

Veamos como ejecutar un Job con un poco de código sencillo

Primero creamos el Proyecto:

Configuramos el Model y la opción para que se sincronice automáticamente al hacer "Build"

Agregamos una "Runnable class"



Agregamos algo de código

Establecemos la clase como objeto de inicio (Set as StartUp Object)
Finalmente ejecutamos el proyecto (F5)


Y ahí tenemos el resultado!

:)

Saludos

Wednesday, November 29, 2017

Max number of sessions in WHS Dynamcs Ax 2012 R3

When we implemented a WHS module in Dynamics Ax 2012 R3, we experimented some issues but the most stressfull issue was the WHS web service crashed with five or six concurrent users.

I tought it was a configuration parameter about limit of conecctions, but i didnt know wich parameter, maybe an IIS parameter or AIF parámeter, was the second.

First of all, we have to select the follow menú: System Administration/ Setup / Services and Application Integration Framework/ EndPoints










In the endpoints form we are going to filter the whs* as the follow image show



We are going to desactivate the endpoint and then we clic the setup button and then, if we don’t have the right software tool to edit the file, the notepad Will open the file.

The netTCPBinding node has a maxConnections parameter but it was useless in this scenario.
But ServiceThrotting parameter had the solution, we had 200 in values of maxCurrentCalls, maxCurrentInstances, maxCurrentSessions



So, each users made several petitions to the service, even a user who refreshed the webpage repeatly



Just we increased the value of each parameter and we notice the website was supported more users tan before.


It really works for us!

See you!

Tuesday, November 28, 2017

Saving a Dynamics Ax Report to PDF

Hi guys!

I am gonna show you how to save a Dynamics Ax Report to PDF file.

We are going to use de getTempPath() method from WinApi class for getting the tmp directory.

static void saveProdId()
{
    ReportRun report;
    Args args;
    ProdBOM prodBOM;
    FilePath  path;
   ProdId  prodId;
    ;

    path = WinAPI::getTempPath();
    prodId= 'ProdIdNumber';

    select prodBOM where prodBOM.ProdId == prodId
            && prodBOM.ProdLineType == BomType::Vendor;


    args =  new Args();
    args.name(reportStr(SomeReport));
    args.record(prodBOM);
    report = ClassFactory.reportRunClass(args);
    report.printJobSettings().setTarget(PrintMedium::File);
    report.printJobSettings().preferredTarget(PrintMedium::File);
    report.printJobSettings().format(PrintFormat::PDF);
    report.printJobSettings().fileName(path + prodId  + '.pdf');
    report.query().interactive(false);
    report.run();


}

See you!

Monday, November 13, 2017

Using Infolog Ax 2012 R3

In this post you can learn how to use InfoLog class for sending a beautiful and professional messages in Dynamics Ax, however final user could not notice your effort.

static void testJCAinfo(Args _args)
{

    SetPrefix("Grandpa");
    SetPrefix("Dad");
    SetPrefix("Me");
    Infolog.add(Exception::Warning,"Puppy");
}

And the result is:



Now you have the power to agroup the info messages in Ax, but you can give more functionally if you want. check the code below


static void Test(Args _args)
{
      SysInfoAction_FormRun    infoAction =       SysInfoAction_FormRun::newFormName(formStr(EcoResProductDetailsExtended)); //Which form?

    
    infoAction.parmCallerBuffer(InventTable::find('TEST5')); // which record??
    
    Infolog.add(Exception::Warning,"El artículo no está propiamente configurado",'',infoAction);

}


First, we got the itemid with errors (TEST5)

In the message infolog we select the message


Notice a new button is appear automatically, and if you click on it, Ax shows you the EcoResProductDetailsExtended with the TEST5 Item.





Wednesday, November 1, 2017

Using RoundUp and RoundDown

Sometimes we would need to round a number, in this case you can use global method roundUp or rounDown.


static void roundExample()
{

    info(num2expstr(roundUp(6.5 ,1)));
  
}


The result is 7, 

This morning I was in trouble using this function... the result was 6 instead 7 such as if I would using roundDown,  some minutes later I notice I was tryng rounding a negative number LOL.



Thursday, October 19, 2017

Creating a purchPackingSlip from Code X++

Hi there!


In this code i'm showing you how to create a purch packing slip from code...


private void createPackingSlip(PurchId _purchId,packingSlipId _packingSlipId, Qty _qtyToReceive)
{
    PurchFormLetter             purchFormLetter;
    PurchFormletterParmData     purchFormLetterParmData;
    PurchParmUpdate             purchParmUpdate;
    PurchParmTable              purchParmTable;
    PurchParmLine               purchParmLine;
    PurchTable                  purchTable;
    PurchLine                   purchLine;
    PurchId                     purchId;
    Num                         packingSlipId;
   Qty                            qtyToReceive;


    purchId       = _purchId;
    packingSlipId = _packingSlipId;
    purchTable    = PurchTable::find(purchId);
   qtyToReceive = _qtyToReceive;

    ttsBegin;
    // Create PurchParamUpdate table
    purchFormLetterParmData = PurchFormletterParmData::newData(
        DocumentStatus::PackingSlip,
        VersioningUpdateType::Initial);

    purchFormLetterParmData.parmOnlyCreateParmUpdate(true);
    purchFormLetterParmData.createData(false);
    purchParmUpdate = purchFormLetterParmData.parmParmUpdate();

    // Set PurchParmTable table
    purchParmTable.clear();
    purchParmTable.TransDate                = SystemDateGet();
    purchParmTable.Ordering                 = DocumentStatus::PackingSlip;
    purchParmTable.ParmJobStatus            = ParmJobStatus::Waiting;
    purchParmTable.Num                      = packingSlipId;
    purchParmTable.PurchId                  = purchTable.PurchId;
    purchParmTable.PurchName                = purchTable.PurchName;
    purchParmTable.DeliveryName             = purchTable.DeliveryName;
    purchParmTable.DeliveryPostalAddress    = purchTable.DeliveryPostalAddress;
    purchParmTable.OrderAccount             = purchTable.OrderAccount;
    purchParmTable.CurrencyCode             = purchTable.CurrencyCode;
    purchParmTable.InvoiceAccount           = purchTable.InvoiceAccount;
    purchParmTable.ParmId                   = purchParmUpdate.ParmId;
    purchParmTable.insert();

    // Set PurchParmLine table
  
    select purchLine
        where purchLine.PurchId == _purchId;

    purchParmLine.InitFromPurchLine(purchLine);

    purchParmLine.ReceiveNow    = _qtyToReceive;
    purchParmLine.ParmId        = purchParmTable.ParmId;
    purchParmLine.TableRefId    = purchParmTable.TableRefId;
    purchParmLine.setQty(DocumentStatus::PackingSlip, false, true);
    purchParmLine.setLineAmount();
    purchParmLine.insert();


    purchFormLetter = PurchFormLetter::construct(DocumentStatus::PackingSlip);
    purchFormLetter.transDate(systemDateGet());
    purchFormLetter.proforma(false);
    purchFormLetter.specQty(PurchUpdate::All);
    purchFormLetter.purchTable(purchTable);

    
    purchFormLetter.parmParmTableNum(purchParmTable.ParmId);
    purchFormLetter.parmId(purchParmTable.ParmId);
    purchFormLetter.purchParmUpdate(purchFormLetterParmData.parmParmUpdate());
    purchFormLetter.run();
    ttsCommit;

}


I hope this will be useful.

Thursday, January 7, 2016

Validating user domain password in Ax 2012 (AxaptaUserManager)

Hi there!

I think it is time to let you know how you can validate the user domain password in Ax 2012 R3, also it works on Ax 2009.

We need to create an instance of AxaptaUserManager... let's codify

01 static void JC(Args _args)
02 {
03
04      AxaptaUserManager axUserManager;
05      UserInfo userInfo;
06      ;
07
08      userInfo = xUserInfo::find(false, curuserId());
09
10      axUserManager = new AxaptaUserManager();
11
12     if (axUserManager.validatePassword(userInfo.networkAlias , userInfo.NetworkDomain, "4V3rYC0mpl3xPa$$w0rd!!"))
13      {
14        info ("Welcome to the jungle!!!");
15      }
16      else
17      {
18        info ("Intruder");
19      }

20
21}


In the line number 12, the last parameter of the code is a very complex password of the user of the first parameter in the domain specified in the second parameter, return true if the password is correct!


I do not want to insult your intelligence, so I do not profundize on the topic, LOL



Tuesday, November 24, 2015

Creating a CSV File from Ax

Hi!

Now I am about to show you how create a CSV file in Ax 2012 R3.

First of all, we need to create an instance of the class CommaTextIo, a Container, and the reference to the #File macro.

 
01 static void CreateCSV(Args _args)
02 {
03     CommaTextIo file;
04     Container line;
05     InventTable inventTable;
06    #File
07
08    file = new CommaTextIo("c:\\users\\jarizmendi\\aCSVFile.csv", #io_write);
09    if (!file || file.status() != IO_Status::Ok)
10    {
11        throw error("File cannot be opened.");
12    }
13
14    while select * from
inventTable
15    {
16        line = [
inventTable.ItemId, inventTable.ItemName];
17        file.writeExp(line);
18    }
19    info("It works!!.");
20 }


Then, we need to create the CSV file in a specified directory, and finally, we need to write the data as lines 14 to 18 show.

I hope it helps!!

Friday, November 20, 2015

Updating Inventory Dimensions Group in Ax 2012

Hi!

If you need to change the Inventory Dimension Groups In Ax (StorageDimensionGroup and TrackingDimensionGroup), you could use this.

1  InventTable                     inventTable;
2  EcoResStorageDimensionGroup     ecoResStorageDimensionGroup;
3  EcoResTrackingDimensionGroup    ecoResTrackingDimensionGroup;

4 ;
5   inventTable                                      =   InventTable::find("anArticle");
6   ecoResStorageDimensionGroup     =   ecoResStorageDimensionGroup::findByDimensionGroupName("anotherGroup");

7   ecoResTrackingDimensionGroup   =   ecoResTrackingDimensionGroup::findByDimensionGroupName("anotherGroup");
8

9   InventTableInventoryDimensionGroups::updateDimensionGroupsForItem(
10            curext()

11           , inventTable.ItemId
12            , ecoResStorageDimensionGroup.RecId
13            , ecoResTrackingDimensionGroup.RecId
14            , inventTable.Product);
15 }

  

Line 1 to 4 declarations, line 5 to  7 finding the ItemId named "AnArticle" and the Storage Dimension Group and the Tracking Dimension Group, they both named "anotherGroup".

Line 9 performs the updateDimensionGroupsForItem of the class InventTableInventoryDimensionGroups.

Important!. There must not be physical movementsor financial movements pending for this item.


Monday, July 27, 2015

Using SysLastValue in Ax 2012 R3



We can save some prompt values in forms without using a table, in this post I will show you how you can use SystemLastValue.

1. Design a form (jcSysLastValue) with an editable field, in this case I used am ImventLocationId field.


2. We have to edit the classDeclaration as follows:

public class FormRun extends ObjectRun
{

    InventLocationId   inventLocationId;
    InventLocationId   inventLocationId2;

    #define.CurrentVersion(1)
    #define.version1(1)
    #localmacro.CurrentList
        inventLocationId,
        inventLocationId2
    #endmacro
}


3. Write the method initParmDefault() to set a default Value

public void initParmDefault()
{
;
    inventLocationId = "001";
}



4.  Define the methods pack and unPack()

public container pack()
{
    return [#CurrentVersion,#CurrentList];
}



public boolean unpack(container packedClass)
{
    int version     = RunBase::getVersion(packedClass);

    switch (version)
    {
        case #CurrentVersion:
            [version,#CurrentList] = packedClass;
            return true;
        default :
            return false;
    }

    return false;
}


5.  Define the methods lastValue....

public dataAreaId lastValueDataAreaId()
{
    return curext();
}



public identifiername lastValueDesignName()
{
    return '';



public identifiername lastValueElementName()
{
    return formStr(jcSysLastValue);
}



public UtilElementType lastValueType()
{
    return UtilElementType::Form;


public userId lastValueUserId()
{
    return curuserid();
}


6. Getting saved values and setting them to the field.

public void run()
{
    super();
    xSysLastValue::getLast(this);
    inventLocationId_text.text(inventLocationId);
}  


7. Saving values to somewhere  

public void close()
{
    super();
    inventLocationId = inventLocationId_text.text();
    xSysLastValue::saveLast(this);
}



If you want to clear the values just delete de follow record and that's it.