Adding new fields to the AX 2012 R3 retail distribution sub-jobs:

RetailCDXSeedData_AX63:


Adding new fields to the AX 2012 R3 retail distribution sub-jobs:
I finally located the class RetailCDXSeedData and its child class RetailCDXSeedData_AX63.


I spent some time looking at what the two classes do so that I could understand how to add in the fields that I need to replicate. From what I can see, the parent class hunts down the child class looking for methods whose names start either “C_” or “A_” which it then executes in turn to define the tables and fields that will be synchronised.
So, what’s the difference between methods starting “A_” and those starting “C_”? Well, currently there are no examples of “A_” methods in AX 2012 R3 so its not entirely clear. They are used differently but as it stands it is not immediately obvious why. What I can say is that immediately after the “C_” or “A_” method is called, the following code is run…
1
2
3
4
5
6
7
8
        if (subStr(subjobMethod, 1, 2) == 'C_')
        {
            seedDataClass.createSubJob();
        }
        else if (subStr(subjobMethod, 1, 2) == 'A_')
        {
            seedDataClass.appendFieldMapping();
        }

This suggests to me that the “A_” methods are meant to be used to append new fields to an existing sub-job and, indeed, the appendFieldMapping() method does throw an error if the sub-job in question does not already exist. That said, I see that as a problem because there does not appear to be a way to ensure that “C_” methods run before “A_” methods and yet, if the Retail Initialise function was run on a new install it would run both types of method and could hit this problem.
On that basis I have ignored the “A_” method approach and customised the “C_” methods to add my fields. I should say at this point that I have successfully re-run the Retail Initialise function on an existing AX instance from the Retail Parameters form and it added all of my new fields without complaint so this is definitely a viable approach.

What’s in a C_ method?

Each of the “C_” methods simply initialise some class instance variables so they’re all really simple. Each method will generate the setup required for one sub-job and attach that sub-job to one or more jobs. The variables that you must set depend on whether its a sub-job to send data to the POS or a sub-job to pull data back.
For a send style sub-job, you need to initialise the following variables
Variable namePurpose
JobIDContainerA container of strings with each element defining the distribution job Id that the sub-job should be linked to. Typically this would be a single element container but there is no reason why you cannot link a single sub-job to multiple jobs using this approach.
subjobIDA string defining the id of the sub-job to be created/amended.
axTableNameThe name of the table that will be synchronised by this sub-job.
axFieldNamesA container defining the list of fields that the sub-job will synchronise.
Here’s an example of a typical “C_” method of this type.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private void C_RetailBarcodeMaskTable()
{
    jobIDContainer = ['1040'];
    subjobID = 'RetailBarcodeMaskTable';
 
    axTableName = tableStr(RetailBarcodeMaskTable);
 
    axFieldNames = [
        fieldStr(RetailBarcodeMaskTable, Description),
        fieldStr(RetailBarcodeMaskTable, Mask),
        fieldStr(RetailBarcodeMaskTable, MaskId),
        fieldStr(RetailBarcodeMaskTable, Prefix),
        fieldStr(RetailBarcodeMaskTable, RecId),
        fieldStr(RetailBarcodeMaskTable, Symbology),
        fieldStr(RetailBarcodeMaskTable, Type)
    ];
}

For a sub-job designed to pull data back from the store database we need to define the same variables as a send sub-job plus the following additional variables.
Variable namePurpose
isUploadA boolean variable that should be set to true.
ReplicationCounterFieldNameA string that defines the replication counter field name (typically “REPLICATIONCOUNTERFROMORIGIN”) which should also be included in the axFieldNames container.
tempDBTableNameThe name of the TempDB temporary table used during synchronisation.
Here’s an example of a typical “C_” method for a pull sub-job.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void C_RetailTransactionAttributeTrans()
{
    jobIDContainer = ['0001'];
    subjobID = 'RetailTransactionAttributeTrans';
 
    axTableName = tableStr(RetailTransactionAttributeTrans);
 
    axFieldNames = [
        fieldStr(RetailTransactionAttributeTrans, Channel),
        fieldStr(RetailTransactionAttributeTrans, Name),
        fieldStr(RetailTransactionAttributeTrans, ReplicationCounterFromOrigin),
        fieldStr(RetailTransactionAttributeTrans, store),
        fieldStr(RetailTransactionAttributeTrans, terminal),
        fieldStr(RetailTransactionAttributeTrans, TextValue),
        fieldStr(RetailTransactionAttributeTrans, transactionId)
    ];
 
    isUpload = true;
    replicationCounterFieldName = 'REPLICATIONCOUNTERFROMORIGIN';
    tempDBTableName = 'RetailTransactionAttributeTransT';
}

When it came to customising these methods to add new fields I decided that I wanted to keep my changes reasonably isolated to help minimise the code upgrade impact. I  considered using a post method event handler but decided against this because I felt that a simpler approach would provide better clarity for other developers when they are trying to understand what will be synchronised. The solution that I came up with is still pretty easy to upgrade so I don’t think I have lost much using this approach.
Let’s assume that I want to add a field to the RetailBarcodeMaskTable table and have that synchronise to the store database. For this I would need to modify the C_RetailBarcodeMaskTable() method as follows.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private void C_RetailBarcodeMaskTable()
{
    container    myCustomFields;
 
    jobIDContainer = ['1040'];
    subjobID = 'RetailBarcodeMaskTable';
 
    axTableName = tableStr(RetailBarcodeMaskTable);
 
    axFieldNames = [
        fieldStr(RetailBarcodeMaskTable, Description),
        fieldStr(RetailBarcodeMaskTable, Mask),
        fieldStr(RetailBarcodeMaskTable, MaskId),
        fieldStr(RetailBarcodeMaskTable, Prefix),
        fieldStr(RetailBarcodeMaskTable, RecId),
        fieldStr(RetailBarcodeMaskTable, Symbology),
        fieldStr(RetailBarcodeMaskTable, Type)
    ];
 
    myCustomFields = [
        fieldStr(RetailBarcodeMaskTable, MyCustomField1),
        fieldStr(RetailBarcodeMaskTable, MyCustomField2)
    ];
    axFieldNames += myCustomFields;
}

As you can see, rather than directly modify the code that sets the axFieldNames variable I have created my own container variable local to the method and added my fields to that. The local variable is then simply appended to the axFieldNames container so that it contains both the standard and custom fields.  This means that, although we will still have to merge code during an upgrade if the standard field list has been changed, it will always be a simple operation to merge in whatever new changes are in the new Microsoft code.

Adding a new table

It should be reasonably obvious from the text above that adding a new table into the synchronisation setup should be a simple matter of creating a new “C_” method a following either the send or pull method pattern appropriately to determine which variables should be set. The framework will automatically see the new method and will execute it along with all of the existing standard methods, creating your new sub-job and attaching it to whichever job you want to attach it to. If you need to define an entirely new job then you should modify the RetailCDXSeedData_AX63.createJob() method, adding a new line of code to create your job.


Reference:  http://www.k3technical.com/adding-new-fields-to-the-ax-2012-r3-retail-distribution-sub-jobs/

Comments