Pages

Saturday, March 4, 2017

Custom AIF operation in Microsoft Dynamics AX 2009

Today let's try to create custom operation on generated by AX AIF Wizard service code.

1. Create new method and write code. 




2. Find you service object in AOT and Add service operation. Select checkbox and press OK.


3. In Visual Studio  add reference to your service.


4. Now can call your custom AX method  in C# code.

private int GetEmplVacationDays(string _emplID, DateTime _from, DateTime _to)
{
    var emplServiceClient = new goEmplTableServiceClient();
    int ret;
    
    try
    {
        ret = emplServiceClient.calcDays(_emplID, _from, _to);
    }
    catch (Exception xException)
    {
        ret = 0;
    }

    return ret;
}

Exposing display methods by AIF in Microsoft Dynamics AX 2009

Sometimes you need to send by AIF data display methods. In my case I have created AIF for EmplTable and additionally I would like to send full name.

1. Find in generated by AIF Wizard classes, class starts with Ax*. In this case it is AxEmplTable.
Write additional parm method returning full name.


2. Save class, Refresh Services, press Generate and do iisreset on IIS Server from command prompt.


3. In Visual Studio Update Service References.



4. Now I can see FullName on my fields list.



Sunday, February 19, 2017

Class extensions in X++

Suggested way to develop Dynamics 365 is not touching base code. Avoid overlayering is good practice especially in Azure environment. If you would like to add code for example to CustTable you can use extension methods. Below code is showing how to do it.

Note to ExtensionOf attribute, final keyword and _Extension suffix to the name of class.

[ExtensionOf(tableStr(CustTable))]
final class CustTable_Extension
{
    public str formatedName()
    {
        return strFmt('[%1] %2', this.AccountNum, this.name());
    }
}

And here example how to call extension class.

public static void main(Args _args)
{   
    CustTable       custTable;
    select firstonly custTable;
    info(custTable.formatedName());
}

And here example how to call extension class. The same we can do to class. Just change ExtensionOf attribute. Here we are extending MainClass.

[ExtensionOf(classStr(MainClass))]
final class MainClass_Extension
{
    public void sayHello()
    {
        info("Say hello from extension class");
    }
}

Here you can read more details.

Sunday, January 1, 2017

Batch jobs in Dynamics 365

My intention was to test how batch jobs are working in new Dynamics 365.

At first configuration. Configure batch group. Please check mentioned blog post and see if your configuration is correct.

Additional check if Microsoft Dynamics AX Batch Management Service is Running.






Basic code for batch job.

class goBatchTest extends RunBaseBatch
{
    #define.CurrentVersion(1)
    #define.Version1(1)

    public boolean canGoBatchJournal()
    {
        return true;
    }

    public boolean init()
    {
        return true;
    }

    protected void new()
    {
        super();
    }

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

    public void Update()
    {
        CustTable custTable;

        while select firstonly10 custTable
        {
    
            info(strFmt("[%1] %2", custTable.AccountNum, custTable.name()));
        }
    }

    public void run()
    {
        #OCCRetryCount
        if (! this.validate())
            throw error("");

        try
        {
            ttsbegin;

            this.Update();

            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;
            }
        }

    }

    public boolean runsImpersonated()
    {
        return true;
    }

    public boolean unpack(container packedClass)
    {
        Version version = RunBase::getVersion(packedClass);
    ;
        switch (version)
        {
            case #CurrentVersion:
                [version] = packedClass;
                break;
            default:
                return false;
        }

        return true;
    }

    public boolean validate(Object _calledFrom = null)
    {
        if (false)
            return checkFailed("");

        return true;
    }

    server static goBatchTest construct()
    {
        return new goBatchTest();
    }

    static ClassDescription description()
    {
        return "Test batch job";
} static void main(Args args) { goBatchTest goBatchTest; goBatchTest = goBatchTest::construct(); if (goBatchTest.prompt()) goBatchTest.runOperation(); } protected boolean canRunInNewSession() { return false; } }

When you are coding and extending from classes, below screen shows how and what methods you can override.




Next thing is right click on batch class object and select Set as Startup Object. After that Ctrl+F5.

You should see this windows. Configure batch job as usually in older versions of Dynamics AX.


Check results in System Administration. Working as expected.



As I see in this very basic actions, I haven't found no changes to previous versions of Dynamics AX batch jobs. Everything seems to be pretty much the same, at least till that moment.

Wednesday, December 28, 2016

Form patterns report

Form pattern report can give you a lot of useful informations about how forms in Dynamics 365.

Create report from Visual Studio menu. It will iterate through all forms in AOT.

It will create CSV file type report. You can open it in Excel.

Let's describe columns:

Model - Model name where form belongs
Form - Form name
Style - Form style. You can find more here
Pattern - Top level pattern applied to that form
Controls - Indication how big form is
Covered controls - Number of covered controls by non custom pattern
Percent covered controls - Percent of covered controls by non custom pattern
Pattern Version - Pattern version
Unspecified count - Indicate how many controls doesn't have pattern applied
Custom count - How many controls do not fit to existing pattern

Based with this report you can track progress of work with your forms.

Wednesday, December 7, 2016

Deploying packaged in AX7

Today's plan is to move development between two enviroments. From development to test enviroments for example. We have two options. Using power shell and using LCS. Let's do this with power shell.

It's diffrent to we are used for. There are no XPO files. Now we have models and deployable packages.

To make things faster I have create new model dev7, create new class in that model.

Next thing is to create package.

 Select model from which package will created.




After Visual Studio will create package go to folder c:\Pckg in my case, where it was placed.
Extract zip file, run PowerShell as Administrator and start typing:

Get-ChildItem C:\Pckg\AXDeployablePackage_20161111_14_22_30 -recurse | Unblock-File -Confirm

This command will unblodck files. Now go to C:\Pckg\AXDeployablePackage_20161111_14_22_30  folder and perform steps.

Generation runbook. Following MSDN:
The runbook provides the sequence of steps that must be run to update the environment. The following illustration shows an example of a runbook file. Each step in a runbook is associated with an ID, a machine name, and step execution details.

.\AXUpdateInstaller.exe generate -runbookid="dev7_RB" -topologyfile="DefaultTopologyData.xml" -servicemodelfile="DefaultServiceModelData.xml" -runbookfile="dev7_RB.xml"

Import runbook into AX
.\AXUpdateInstaller.exe import -runbookfile="dev7_RB.xml" 

By this command you can check what runbooks you have imported and are ready to install
.\AXUpdateInstaller.exe list

Start importing to target enviroment
.\AXUpdateInstaller.exe execute -runbookid="dev7_RB"

Check runbook
.\AXUpdateInstaller.exe export -runbookid="dev7_RB" -runbookfile="dev7_RB.xml"

Below you can find more references about AX7 architecture.

References:
Design-Compile-Run Part1: 2012 Paradigms

Design-Compile-Run Part2: Design, Compile, Run in AX 2012

Design-Compile-Run Part3: Design, Compile, Run in AX7

Design-Compile-Run Part4: Paradigms in AX7

Install a deployable package



Tuesday, November 1, 2016

Create sales order, sales line and confirmation from X++ code in AX7

I have installed few days ago Virtual Machine with AX7. I have described it on my blog post.
Today I would like to write some code which was often used in older versions of AX system. Let's try to create sales order and sales line from X++ and after all confirm it. At the end I will compare to what we have in 2012/2009.

First thing what you have to do is model. Create model.

Name it. In this case it is Development7.

Select existing package and Next.

You should finish with following.

As you may see AOT is diffrent to what we are used to in previous versions. In my opinion moving development to Visual Studio gets better for Dynamics. And now I would like name X++ rather to X# because as I see C# and X++ are getting closer from year to year which is fine because both have their pros and cons.

Let's add standad job. You can do this by pressing right click on project name and select Add and naxt New Item...


Select Job.

Right click on more time on project name and this time select Properties. As you may see on screen below, what I'm doing is selecting company on which I would like to have orders and lines created. In this case it is BRMF company.




Below code. Code is almost the same as I'm using on AX 2012. We may assume at this moment that code in AX7 is the same as in AX2012. We will see in future how it is, when I will investigate new AX7.

public static void main(Args _args)
{
    SalesTable       salesTable;
    SalesLine        salesLine;
    SalesFormLetter  salesFormLetter;
    InventDim        inventDim;
    NumberSeq        numberSeq;
    
    numberSeq = NumberSeq::newGetNum(SalesParameters::numRefSalesId());
    numberSeq.used();
    
    // Sales table
    salesTable.SalesId = numberSeq.num();
    
    salesTable.CustAccount = "BRMF-000002";
    
    salesTable.ShippingDateRequested = today();
    
    salesTable.SalesType = SalesType::Sales;
    
    salesTable.initFromCustTable();
    
    if (!salesTable.validateWrite())
    
        throw Exception::Error;
    
    salesTable.insert();
    
    
    // Sales lines
    salesLine.SalesId = salesTable.SalesId;
    
    salesLine.ItemId = "BRMF020";
    
    salesLine.SalesQty = 2;
    
    inventDim.InventSiteId = "SAL-01";
    
    salesLine.InventDimId = InventDim::findOrCreate(inventDim).inventDimId;
    
    salesLine.createLine(true, true, true ,true ,true, true);
    
    
    // Confirmation
    salesFormLetter = SalesFormLetter::construct(DocumentStatus::Confirmation);
    
    salesFormLetter.update(salesTable);
    
    info(strFmt("Sales %1 created and confirmed.", salesTable.SalesId));
}


Right click on job and Set as Startup Object.


Run job by pressing F5. Sales order created.












Go to accounts receivables and all sales orders and find previously created. Select tab SELL. Find GENERATE group and click on Confirmation.



Sales order is confirmed.

Also we can see how line is looking,

It seems that a lot of code is reused from older AX versions in new AX7 which is fine for us. However as much as I'm reading I see there are a lot of new fantastic technologies which we will investigate in next posts.