Wednesday, October 5, 2016

Duplication SSRS for AX 2012 R3

Here is a light post to duplicate SSRS report on Microsoft AX Dynamics.
This is not a standard feature of AX Dynamics (with a good wizard for example). So your need to do that manually, adapting all components of your new report and business logic.


SSRS report is composed by many class and objets, so to duplicate an existing report, you need to duplicate certain or all composants. I listed main components and steps just below :

- Duplicate Controller, Contract, and Data Provider classes on AOT (MorphX) (& Tmp tables "attached" on Data provider if you need to use them)
- Duplicate the query associated on the Data Provider Class
- If controller is not overhead because your report template use super classes (srsReportRunController to start operation), you could create it duplicating other one. Check Type Hiearachy Browser on SrsReportRunController class to show all extended controller class. Adapt main class and modify/create class to custom it (prePrompt, preRunModifyContract for example)
- Duplicate SSRS report with Visual Studio AOT solution explorer (this option is available only in VS)
- Duplicate also the Business Logic class (C# project) if your template report contains business methods. You need to do that, export C# Projetct in XPO and modify name of class, Origin GUID and name of .cs. (See link below for that)
- Create a new project on Visual with the new name of report and add the duplicated report
You need after set the report and parameters properties with new objects :
* Data provider to fill DataSets (this is the query which select data from data provider, a propertie in the DataSets of your report.
* Check parameters (after duplication, parameters is sometime duplicate also)
* Change the business logic of report (properties of the report --> Data Method Library which contains methods of the report.

Build the report (without any error) and publish it with Microsoft AX Dynamics Management Shell program with the command :
Publish-AXReport -ReportName myReport

Don't forget to debug your report to :
- Activate breakpoint in Server configuration
- Run as administrator the debugger
- Put a breakpoint keyword in your code (process report for example)

Thank you to this post : http://axvuongbao.blogspot.fr/2013/08/how-to-duplicate-ssrs-report-in-ax2012.html for Business Logic duplication.

There is a tool to duplicate report on AX 2012 (provide by Sreenath) but if you could to this manually, it's a good training to keep the logic of SSRS feature in Dynamics AX.


Wednesday, September 14, 2016

DIXF : Language and Entities

Today, I'm writing a new post about DIXF on Dynamics R3, specially on entities name.
After a new installation of AX Dynamics on a french operating system (things we never should implement), I checked entities of the new DIXF system and many errors on those.
On few entities, edition et mapping generation didn't work with strange message :
"Column is empty" on edition of an entities for example or also The ' ' character, hexadecimal value 0x27, cannot be included in a name (DMFXMLWizard execution)...

After debug, the problem was localized on entity name which was in french character.
DMFEntityName table initializes default entities with the current language of current user when AX Dynamics is installed.

I had to reinitialize default entities name by standard table method and regenerate mapping on DIXF.

I join my job to do that.


  // Job to regenerate default entity on the right language   
  // Login with EN-US language and run the job.   
  static void regenerateDefaultEntity(Args _args)   
  {   
   DMFEntity     dmfEntityOLD, dmfEntityUpd;   
   void deleteDMFTargetMapping(DMFEntityName _oldEntityName)   
   {   
    DMFTargetXMLToEntityMap  dmfTargetXMLToEntityMap;   
    // delete specified target Mapping   
    ttsBegin;   
     delete_from dmfTargetXMLToEntityMap where dmfTargetXMLToEntityMap.Entity == _oldEntityName;   
    ttsCommit;   
    info(strFmt("Entity %1 deleted",_oldEntityName));   
   }   
   void regenerateDMFTargetMapping(DMFEntityName _newEntityName)   
   {   
    DMFTargetXMLToEntityMap  dmfTargetXMLToEntityMap;   
    DMFEntity     dmfEntity;   
    // Regenerate Target Mapping for specified Entity   
    select dmfEntity where dmfEntity.EntityName == _newEntityName;   
    {   
     DMFTargetXMLToEntityMap::generateMapping(dmfEntity);   
    }   
    info(strFmt("Entity %1 generated",_newEntityName));   
   }   
   ;   
   if(Box::yesNo("Do you want to regenerate DMF entities?", DialogButton::Yes))   
   {   
    while select forUpdate dmfEntityOLD   
     //where dmfEntityOLD.EntityName == "Codes Taxe" // for test   
    {   
     if (dmfEntityOLD.EntityType == DMFEntityTypes::Entity)   
     {   
      ttsBegin;   
      dmfEntityUpd = dmfEntity::find(dmfEntityOLD.EntityName,true);   
      dmfEntityUpd.defaultModule(dmfEntityOLD.EntityTypeName);   
      dmfEntityUpd.update();   
      ttsCommit;   
      deleteDMFTargetMapping(dmfEntityOLD.EntityName);   
      regenerateDMFTargetMapping(dmfEntityUpd.EntityName);   
     }   
    }   
    info("Process over");   
   }   
   else   
   {   
    info("Process cancelled");   
   }   
  }   


Hope it helps someone with the same problem.
Thank you for this post which resolve for me regeneration !


Thursday, February 13, 2014

Backup du versioning MorphX VCS

Voici un post intéressant sur AX 2009 qui est compatible pour AX Dynamics 2012, pour sauvegarder et porter les données liées au versioning d'objet MorphX.

En effet, en cas de rechargement de base de données sur un environnement de développement, les objets développés sont portables en AX Model (selon la couche de développemen) voirvia  un modelstore (toutes les couches compilées).

Pour éviter la perte des versions antérieurs stockés via l'outil VCS intégré à Dynamics, il est nécessaire de sauvegarder les tables SQL "SYSVERSION*" de la base d'origine :

- SYSVERSIONCONTROLMORPHXITEMTABLE --> contient les enregistrements d'objets avec leur chemin
- SYSVERSIONCONTROLMORPHXLOCKTABLE --> contient les objets en cours d'utilisation
- SYSVERSIONCONTROLMORPHXREVISIONTABLE --> contient les données de révision
- SYSVERSIONCONTROLPARAMETERS --> paramétrage de versioning (VCS, TFS etc..)
- SYSVERSIONCONTROLSYNCHRONIZELOG --> Utiliser pour TFS donc vide à priori en cas d'utilisation de VCS.

Un modèle simple mais qui nécessite une fois exporté/importé dans la nouvelle base, une correction du compteur de recID afin de repartir d'un nombre correct pour les prochains enregistrements de versioning.

Lien vers l'article

Wednesday, January 22, 2014

Récupérer les filtres et enregistrements d'un formulaire

Pour récupérer les valeurs de filtrages, entrées dans un client AX Dynamics, il m'a fallu tatonner jusqu'à trouver la solution suivante :

public void process(Args args, FormDataSource _formDataSource)
{
    QueryRun queryRun;
    QueryFilter     qf;
    QueryBuildRange range;
    Query           q;
    int j;
    int k = 1;
    int i = 1;
    int cnt,filtercnt;
    DictTable dictTable;
    DictField dictField;
    str test;
    ;

    q =  _formDataSource.queryRun().query();
    queryRun = new QueryRun(q);

    if(q)
    {

        // Récupération de l'appelant et des lignes cochées
        info (strFmt("Formulaire appelant %1, Company : %2. Nombre de ligne Selectionné = %3",

               args.name(),
               curExt(),  
               _formDataSource.recordsMarked().lastIndex()));

        for(i = 1; i <= q.dataSourceCount(); i++)
        {
            cnt = q.dataSourceNo(i).rangeCount();
            filtercnt = 0;
            if(!q.dataSourceNo(i).embedded())
            {
                filtercnt = q.queryFilterCount(q.dataSourceNo(k));
            }
            dictTable = new DictTable(q.dataSourceNo(k).table());

            for (k=1; k<= filtercnt; k++)
            {
                qf = q.queryFilter(k, q.dataSourceNo(i));
                if(qf)
                {

                    // Récupération du filtre
                    info (strFmt("%1, %2. Range = %3", dictTable.label(), qf.field(), qf.value()));
                }
            }
            ttsCommit;

            for (j=1; j<=cnt; j++)
            {
                range = queryRun.query().dataSourceNo(i).range(j);
                if(range.value())
                {

                    // Récupération du filtre
                    info(strfmt("%1, %2. Range = %3",dictTable.label(), range.AOTname(), range.value()));
                }
            }
        }
    }
}


Wednesday, November 20, 2013

Purger les données d'utilisations

En cas de migration, il convient parfois de devoir supprimer certaines données d'utilisations des utilisateurs afin que des ensembles variables soit correctement utiliser par le système.

Les reports par exemple nécessitent en cas de modification de version d'AX sur leur classe de contrôle, une purge des données des utilisateurs de l'ancienne version.

Voici un exemple de job permettant de faire l'opération. L'exemple créé un formulaire permettant de choisir un filtrage sur une colonne de la SysLastValue DesignName ou RecorType pour choisir les entrées à supprimer.



static void deleteUsageData(Args _args)
{
    Dialog                  dialog;
    DialogField             dlgRange1,dlgRange2;
    DialogField             dlgbox1;
    SysLastValue            sysLastValue;
    int i = 0;
    #OCCRetryCount
    ;
    dialog                      = new Dialog("Usage Data Deletion");
    dlgRange1                   = dialog.addField(extendedTypeStr(Range),"DesignName");
    dlgRange2                   = dialog.addField(extendedTypeStr(Range),"RecordType");
 // Need to declare TypeofSysLastValueRange Enum with two values : designName & RecordType
    dlgbox1                     = dialog.addField(enumStr("TypeofSysLastValueRange"),"Colonne de filtrage");

    dlgRange1.value("*Report*");
    // ou
    dlgRange2.value("!userSetupQuery");

    dlgbox1.value(TypeofSysLastValueRange::designName);
    if (dialog.run())
    {
        try
        {
            ttsbegin;
            if(dlgbox1.value() == TypeofSysLastValueRange::designName)
            {
                while select forUpdate SysLastValue
                where sysLastValue.designName like dlgRange1.value()
                {
                info(strFmt("Delete information : company : %1, designName : %2, element Name : %3, User : %4",
                                    sysLastValue.company,
                                    sysLastValue.designName,
                                    sysLastValue.elementName,
                                    sysLastValue.userId));
                SysLastValue.delete();
                i++;
                }
            }
            else
            {
                while select forUpdate SysLastValue
                where sysLastValue.recordType like dlgRange2.value()
                {
                info(strFmt("Delete information : company : %1 , DesignName : %2, Element Name : %3, User : %4",
                                    sysLastValue.company,
                                    sysLastValue.designName,
                                    sysLastValue.elementName,
                                    sysLastValue.userId));
                SysLastValue.delete();
                i++;
                }
            }


        ttsCommit;
        }
        catch (Exception::Deadlock)
        {
            retry;
        }
        catch (Exception::UpdateConflict)
        {
            if (appl.ttsLevel() == 0)
            {
                if (xSession::currentRetryCount() >= #RetryNum)
                {
                    throw Exception::UpdateConflictNotRecovered;
                }
                else
                {
                    retry;
                }
            }
            else
            {
                throw Exception::UpdateConflict;
            }
        }
        catch (Exception::Error)
        {
            ttsabort;
            throw error("@SYS93835");
        }

        info(strFmt("Total deleted lines : %1",i));
    }
}

Tuesday, July 2, 2013

CU6 AX RTM/FP = CU2 AX R2 !



Fin juin 2013, le customer/Partner Source propose une CU6 pour Ax Dynamics 2012 RTM, Feature Pack, et pour AX Dynamics R2 :-/.
Les dénominations sont parfois trompeuses chez Microsoft : La CU6 nécessite la mise à jour R2 et la CU5 sera donc la dernière cumulative update des versions RTM et Feature Pack - a priori (afin d'inciter les clients à migrer ?).

Néanmoins, le CU6 n'est pas cumulative sur la R2, donc nécessite d'appliquer les CU1 à 5 pour bénéficier des correctifs de AX RTM/FP... de quoi s'y perdre!
Si j'ai bien tout compris, l'idéal serait un AX2012 FP CU 1 à 5 puis une migration en R2 + CU6 (ouf !)

Lien vers la CU6 (Customer/Partner Source)

De quoi nous donner un peu de travail.






Tuesday, June 18, 2013

Utiliser les services AIF d'AX dynamics

La couche AIF de Microsoft AX Dynamics sert générallement à l'échange de données entre des systèmes externes et l'ERP AX Dynamics. Microsoft a simplifié l'implémentation des services personnalisés sous 2012.

Cette couche permet d'accéder en entrée ou en sortie à des classes dédiées aux services de votre AOS.

Voici un exemple de l'implémentation et de l'utilisation d'un service sous DAX2012 : lien

Cet exemple simpliste est très bien fait : Il montre la facilité d'interaction entre l'extérieur et AX.
Vous pouvez imaginer toute sorte d'application des services AIF pour votre système d'information :
- Echange de données entre les applications externes et AX (données référentes par exemple)
- Déclenchement de traitement à distance via une application C# et piloté par un ordonnanceur
- Mise à jour d'AX après un traitement externe.
- Permettre des accès via de l'informatique mobile (PDA, système industriel etc...)

Un ERP n'étant jamais seul dans son environnement urbanisé, la couche AIF devient une solution SOA très intéressante et facile à mettre en oeuvre par rapport à des communications asynchrones.

Reste à étudier l'impact de l'utilisation massive des services AIF sur les performances de l'AOS.