torsdag den 6. oktober 2016

Toggling visibility of fields in a Grid in then preview Pane of a ListPage form using code

Tasked with hiding a specific field on the salesLine table in the sales order forms depending on the value of a field on SalesTable I searched for methods and came across:

I agree with the method of finding the field on the datasource of the formpart
however a better method of making the field invisible is to reference


instead of actually having to reference the form control.

This will make the field visible/invisble anywhere in the form that the field of the datasource is used.

In my case it was done like this:

private void toggleintraCodeReturn()
    PartList    pl;
    int pCount,partdsCount;
    FormRun part;
    boolean shown;
    FormDataSource pfds;

    FormDataSource  fds,partfds;
    shown = this.currentSalesTable().CustMaterial == NoYes::Yes;
    if (this.currentSalesTable().isFormDataSource())
        fds = this.currentSalesTable().dataSource();
        pl = new PartList(fds.formRun());
        if (pl)
            // Getting parts for form
                part = pl.getPartById(pCount);
                if ( == identifierStr('SalesTableListPagePreviewPane'))
                    // Getting datasources for part form
                        fds = part.dataSource(partdsCount);
                        // Here we got hold of sales lines data source
                        if (fds.table() == tableNum(SalesLine))

fredag den 16. september 2016

Dynamics AX and project quotations in in .docx format

Just finished an assignment making a tool for Dynamics AX 2012R2 for creating project quotations in word, using AX data.
The customer is an ETO company manufacturing production lines for slaugtherhouses.

The tool should create a quotation file word document (.docx) using a word template (.dotx) prepared in a certain language. This would allow the customer to design the layout of the document in a easy and familiar way.

The spec required the quotation to include

Budgets with detailed information for each sub project of a main project.
Budgets summarized for each sub project of a main project.
Scope of supply, which contains short technical descriptions of the included units on the production lines (sub projects)
Technical descriptions - a section of the document containing listing (only once for each unit/sub project) complete descriptions with pictures of each included unit

The algorithm in short is this:

Now the challenge was that for most itemids in the item forecast of the projects, there exists a word document (product sheet) containing detailed product facts. Each product sheet contains four sections

a title
a short description
a middle long description (containing a picture of the unit)
a long description (containing a picture of the unit)

Depending on the user input in the dialog when running the tool, the tool should extract one or more of the sections of the product sheet (mentioned above), and include the extracted sections in the quotation word document.

I have used a 3rd party component called Aspose. This component allows you to work with several document formats, reading, editing and creating documents, without having to have the software normally used to work with the document format installed. That is, Dynamics AX can actually work with the documents SERVER SIDE without having to have Office installed on the AOS.

So I made a .dll in c# that the x++ code uses for the work to be done with the word files, and the x++ code is made so that we can replace the query with other queries if necessary.

The template setup contains features for

searching the word file for bookmarks for insertion of values
searching the word file for fields for insertion of values

The values that AX can insert are categorized as so:

Ordinary table.field values
Ordinary table.method values
A table of values (that can be Axtable fields og Axtable table methods)
A word document section (an extracted section of a separate word document)
A fixed text

The template can be setup by the user combining the bookmarks/fields to search for in the word document and the type of value to be inserted when found.

When a new template is created, the tool can examine the word template, and extract all bookmarks/fields (discover) so that the user has a basic setup he can use to map the ax fields/methods etc up against which bookmarks/fields should be substituted.
Again Aspose is used to do this.

We have used Aspose in other Dynamics AX projects:

Axapta version 3.0 - where we used Aspose to ensure that print out of .pdf-files was always the same page orientation (potrait) in connection with a AX report output )

Dynamics AX 2012 R3  creating .pdf-files for customer visits with user editable forms to send out to sales personnel - ensuring they can open the file and gather input regardless of hardware platform and being online or not.

Aspose is a good tool when you have to make applications that create or edit .pdf-.files, excel-files, word-files. It can even do imaging and OCR, though I haven't worked with those areas.

torsdag den 9. april 2015

Dynamics AX 2012 AOT add-in copying field list to clip board

Before Dynamics AX 2012 and the new editor which makes things a bit more Visual Studio like, we had some features in the AOT, that are gone now but I miss.

I *might* have rambled and raved about this before ( ;-).

E.g. I miss the ability to mark all fields on a table in the AOT and simply copy them to code.
In the olds days this could be done by simply marking the fields in AOT and dragging them to the editor window.

I needed this and got fed up with having to type the field names my self so I made a little class with the main method:

public static void main(Args _args)

    DictTable   dt;
    DictField   df;
    int f,start,end;
    Set s;
    SetEnumerator se;
    int tableno;
    TextBuffer txtb = new TextBuffer();
    str fieldNameList;
    str tableName;
    TreeNode treeNode;
    str searchFor = 'Path: '+#TablesPath+#AOTDelimiter;

    if (SysContextMenu::startedFrom(_args))   // started from SysContextMenu
        treeNode = _args.parmObject().first();
    else    // started with a button (or from a menu!)
    if (_args.dataset() == tablenum(UtilElements))
        treeNode = xUtilElements::getNodeInTree(_args.record());
    if (strScan(treeNode.toString(),#TablesPath,1,1024))
        start = strScan(treeNode.toString(),searchFor,1,1024)+strLen(searchFor);
        if (strScan(treeNode.toString(),'\\Fields',1,1024))
            end = strScan(treeNode.toString(),#FIELDS,1,1024);
            end = strScan(treeNode.toString(),' Layer: ',1,1024);
        tablename = subStr(treeNode.toString(),start,end-start);
        s = new Set(Types::String);

        if (tableName)
            tableno = tableName2id(tableName);
            dt = new DictTable(tableno);
            if (dt)
                    df = new DictField(tableno,dt.fieldCnt2Id(f));
                    if (!df.isSystem())
                se = s.getEnumerator();
                while (se.moveNext())
                    fieldNameList += (fieldNameList ? "\n" : "") + se.current();

                info("Field names copied to clipboard.");

And dragged the class to the Menu Items \ Action to create the action menu item:

I then added this menu item to the SysContextMenu:

Then I can go to a table in the AOT and right click and choose add-ins / Copy field names to clipboard.

and afterwards just paste into word or excel:

I think its useful for making documentation.

You can download a private project from:!80242&authkey=!AH8DxBaNZUc1LTg&ithint=file%2cxpo

onsdag den 25. marts 2015

Calculating easter sunday in different ways

Once a long time ago I found an interesting document on the inter web, describing different calendar systems.

From that I made a php-class for my website that can calculate easter sunday in the gregorian calendar. Then it is easy to calculate the rest of the danish holidays as they are offset according to easter sunday (except of course those that have a fixed date).

I translated that class in to x++.

You can find it here:!39710&authkey=!ABv0yYPULV3ReIE&ithint=file%2cxpo

Today I got talking with a colleague about calculating easter sunday so he mentioned that he had also made a version of the calculation:

So of course - being a bit nerdy - we just *had* to check if the routines arrived at the same result. So we came up with:

static void EasterTest(Args _args)
    Yr x;
    date easter(Yr yr) // Påskedag / påske søndag
        int g,c,h,i,j,l;
        int easterday, eastermonth;
        g = yr mod 19;
        // Gregorian Calendar
        c = real2int(rounddown(yr / 100,1));
        h = (c - roundDown(c/4,1)-rounddown(((8*c)+13)/25,1)+(19*g)+15) mod 30;
        i = h - rounddown(h/28,1)*(1- rounddown(29 / (h+1),1) * rounddown((21-g) / 11,1));
        j = (yr + (rounddown(yr/4,1))+i+2 - c + (rounddown(c/4,1))) mod 7;
        l = i - j;
        easterday = 3+rounddown((l+40)/44,1);
        return mkdate(l+28-31*rounddown(easterday / 4,1), easterday, yr);
    date dateOfEaster(Yr y)
        int a = y mod 19;
        int b = y div 100;
        int c = y mod 100;
        int d = b div 4;
        int e = b mod 4;
        int f = (b+8) div 25;
        int g = (b-f+1) div 3;
        int h = (19*a+b-d-g+15) mod 30;
        int i = c div 4;
        int k = c mod 4;
        int l = (32+2*e+2*i-h-k) mod 7;
        int m = (a+11*h+22*l) div 451;
        int n = (h+l-7*m+114) div 31;
        int p = (h+l-7*m+114) mod 31;
        return mkdate(p+1,n,y);
    for (x = 1900; x <= 2154; x++)
        if (dateOfEaster(x) != easter(x))
            info(strFmt('E1=%1 E2=%2', dateOfEaster(x), easter(x)));

And the routines calculated the same dates for easter sunday.

We stopped ourselves when discussing if we should implement a tick-couting-measurement to see if my colleagues routine was faster than mine. :-)

But I think mine is slower as it uses several function calls and not pure arithmetic.

torsdag den 5. marts 2015

Forcing the Name field of a salesline to be synchronized to the purchline when using Drop shipment

Using non-stock items in the daily business can be handled in Dynamics AX 2012 by using Direct delivery.

You can use the Button "Direct delivery" from a sales order you have created, to create a matching purchase order.

However if you use the Name field on the salesline to describe the specifications of the item you want to the vendor, the standard functionality does create the matching purchase order lines so that the name of the originating sales line is also used on the sales lines.

Direct deliveries are handled so that the inventory transactions of the salesline are marked against the purchline, so I wrote this small script to be able to get the hang of how to find the direct delivery purchaselines from the saleslines records of a sales order.

static void Job235(Args _args)
    SalesLine   salesLine;
    PurchLine   purchLine;
    InventTransOriginSalesLine itosl;
    InventTransOriginPurchLine itopl;
    InventTrans it,it1;
    InventTable iTbl;
    InventHandlingGroup ihg;
    while select salesLine
        where salesLine.SalesId == "1001135"
        join itosl
        where itosl.SalesLineDataAreaId == salesLine.dataAreaId
           && itosl.SalesLineInventTransId == salesLine.InventTransId
        join it
            where it.InventTransOrigin == itosl.InventTransOrigin
        join it1
            where it1.InventTransOrigin == it.MarkingRefInventTransOrigin
        join itopl
            where itopl.PurchLineDataAreaId == it1.dataAreaId
               && itopl.InventTransOrigin == it1.InventTransOrigin
        join forupdate purchLine
            where purchLine.dataAreaId == itopl.PurchLineDataAreaId
               && purchLine.InventTransId == itopl.PurchLineInventTransId
               && purchLine.Name !=
        join itbl
            where itbl.itemid == salesLine.ItemId
        info("***** FØR ********");
        info(strFmt("Salg: %1 %2 %3 %4",salesLine.SalesId,salesLine.ItemId,salesLine.LineNumber,;
        info(strFmt("Indkøb: %1 %2 %3 %4",purchLine.purchid,purchLine.ItemId,purchLine.LineNumber,;
        purchLine.Name = salesLine.Name;
        info("***** EFTER ********");
        info(strFmt("Salg: %1 %2 %3 %4",salesLine.SalesId,salesLine.ItemId,salesLine.LineNumber,;
        info(strFmt("Indkøb: %1 %2 %3 %4",purchLine.purchid,purchLine.ItemId,purchLine.LineNumber,;

With this code at hand I could continue to customize the PurchAutoCreate_Sales class to sync the names of the salesLines to the purchLines.

tirsdag den 7. oktober 2014

Returning a weeknumber from the standard calendar lookup form

A customer had a requirement for setting a week number for approximate delivery of purchased goods on shipment as an indication for the sales department of when the goods will be arriving.

An integer field had been introduced on the table in question, and the customer wanted to be able to do a lookup in the standard calendar, but wanted a week number to be returned in stead of a date.

I solved it like this:

For the field that was to contain ETAWeek there was an Extended Data Type called ETAWeek.
On the EDT I put SysDateLookup in the FormHelp property

In the Form SysDateLookup I added a boolean variable in Classdeclaration.

boolean calledFromIntegerField;

3. In the Form SysDateLookup - method init I added check to see if the SysDateLookup form was called from an integer form control.

if (formRun.selectedControl() is FormIntControl) 
    calledFromIntegerField = true;

4.In the Form SysDateLookup - method closeSelect I added an extra else if block in the bottom to handle returning the week number.

else if (calledFromIntegerField)

Now the SysDateLookup works for the ETAWeek field.
UI design wise you can set the "ButtonImage" property on the Extended Data Type ETAWeek mentioned above to the value Calendar to get an calendar icon beside the field.

Dynamics AX 2012 R2 - Export to Excel command button

Today I had some trouble getting a normally simple thing to work in an AX form.

The form consists of to synchronized grids, and the user wanted an "Export to Excel" command button, so the active grid can be exported to Excel.

Normally this is very simple as you just need to add a command button to the form in the ActionPane somewhere and you're home free.

Not this time. It didn't work.

I googled and found this:

and put a breakpoint in the performPushAndFormatting method.
I didn't reach my breakpoint and Excel didn't even start.

Then I started to investigate the form.
Maybe the tables of the datasources of the form had some obscure property that needed tweaking, but no.

After pondering this for a while I found that the designer of the form had dropped a field group containing all the fields in the grid. Could that be the problem ?
Clearing the "DataGroup" property of the field group, allowed for dragging all the fields from the field group to the Grid node in the design, and presto, the Export button now worked like a charm.

Lesson learned. If you have a grid in your form, and you want to be able to use an "Export to Excel" command button, using field groups in the grid is a bad idea.

I experienced this on a Dynamics AX 2012 R2.