Friday, 16 December 2016

Cannot alter 'PURCHLINEVIEW_IN' because it is not a table

We hit this issue in DEV environment, not sure how it happenned in the first place, but this table "PurchLineView_IN" has been created as a View in SQL.

Due to it shouldn't appear as View in the first place, so I deleted it straight away from SQL, then rerun the sync from AX. It then created the "PurchLineView_IN" as table in SQL instead of as a View. The sync issue is resolved.

Wednesday, 28 September 2016

Unable to edit AX7 element properties (dimmed/grayed out)

Quick tips: In AX2012 and the version before, when you click on an element in the AOT, (assuming the properties window is opened) they're editable right away. But in AX7, if you click on an element in your project, the properties is not editable (grayed out, read only). You'll need to double click to open the element in the editor window, then the properties will be enabled for editing.

Tuesday, 20 September 2016

Workflow error - The condition could not be evaluated. No data has been returned

Stopped (error): X++ Exception: The condition could not be evaluated. No data has been returned.
 at SysWorkflowEvaluationProvider-evaluateCondition

During one of the workflow development, the requirement is to have a batch job to auto-submit all records that meet certain condition. Let's call this TableA for now, auto-submit all records where FieldA == 12. Then workflow configuration is set to auto-approve records if certain conditions are met.

The development started with creating a query object.
Then use this query in the Workflow wizard and created all the related classes, menu item, etc.
Then a batch job created to auto-submit all TableA records which FieldA == 12.
The batch job uses the same query object created earlier.
And the mistake is the FieldA is added into the query object as a range field with fixed enum value 12.

This is due to during the workflow approval process, this FieldA will be updated into other status.
When it is updated to other status than 12, this causes the record doesn't meet the criteria (Range) in the query object, hence, the error "The condition could not be evaluated. No data has been returned.".

The query object (used in workflow document class) is used throughout the approval process, not just the initial submission. So the query shouldn't have any conditions added to it, especially those field which the value will change throughout the process. To fix this, the range (FieldA = 12) has been removed from the query object. Then added during runtime of the batch job. Eg. SysQuery::findOrCreateRange(qbds, fieldNum(TableA,FieldA)).value(...).

For my scenario, the error can be replicated from: (Module) > Setup > **** workflows > (Select the related workflow> Edit > Properties > Automatic actions > Test.

Wednesday, 29 June 2016

SysQueryRangeUtil - Custom query function

In one of old post (Dynamics AX batch job relative day parameter), I've mention regarding using function in query range. There're a set of standard functions available in the SysQueryRangeUtil class. But if you happen to need a custom function (especially with more complex logic), you can develop it in this class and use it just like you'll do with the standard methods.


You can use this function in query object, under the "Range" node, or anywhere that provides filtering functionality.

Tuesday, 21 June 2016

Replenishment error - Location xxxxxx failed due to the strategy

Just a quick post for non-developer who hit this error and looking for solution.

Path to functionality:

  • Warehouse management > Periodic > Replenishments
  • Warehouse management > Inquiries > History > Work creation history log

The error message "Location xxxxxx failed due to the strategy" in "Work creation history log" is due to the "Total available" is less than "Physical stock".

Before replenishment run:
- License plate 90, Physical stock = 106, Total available = 96
- License plate 91, Physical stock = 100, Total available = 100

When the replenishment run, it check whether it can locate a full license plate, if it can’t, then it give the “Location xxxxxx failed due to the strategy.” error.

The way it finds the full license plate is by checking the Total available against the Physical stock (this is a simplified explanation of the logic, it does much more than these two checking).

In this instance, LP 01C02E is not fully available (Physical stock is 106, but Total available is only 96), hence, error.
Then it check next LP, 01C03E, this is fully available (Physical stock is 100, Total available is 100), hence, successfully use it.

The code which checks and produce the error is location at this path: \Classes\WHSLocationDirective\performStrategyLPLocate

Wednesday, 15 June 2016

"The endpoint does not exist or the action is not allowed" on Payment journal voucher send

Just a quick post for non-developer who hit this error and looking for solution.

If you're setting up AIF Outbound port for Payment journal, you'll need to add the "LedgerVendorPaymentService.find" operation to the port.

Without this port, when you click on the "Send..." button on the Payment journal voucher, you'll first get the error "No endPoints are configured for the action".

But the "Send document..." dialog will still shows up, and if you continue to select the Port name and click "OK", you'll get the error "The endpoint does not exist or the action is not allowed".

To fix this issue:

  1. Go to: System administration > Setup > Services and Application Integration Framework > Outbound ports
  2. Select the respective port name and click “Deactivate”
  3. Click on “Service operations” button under the “Service contract customizations” FastTab
  4. Double click on the “LedgerVendorPaymentService.find” service operation under the “Remaining service operations” list to add it into the “Selected service operations”
  5. Close the form
  6. Click “Activate” button to activate the Outbound port again

Friday, 20 May 2016

Find AIF service class name by Axd document class name

Just a quick snippet to find AIF service class name by Axd document class name.

static void GetAxdClassNameByServiceName(Args _args)
    str                     mySearchAxdName = "AxdCountingJournal"; //Change this to your Axd class name
    List                    list;
    ListEnumerator          listEnumerator;
    SysDictClass            sysDictClass, serviceDictClass;
    AxdWizardParameters     axdWizardParameters;
    int                     progressTotal, progressCount;
    SysOperationProgress    progressBar;
    sysDictClass = new SysDictClass(classnum(AifDocumentService));
    list = sysDictClass.extendedBy();
    progressTotal = list.elements();
    progressBar   = SysOperationProgress::newGeneral(#aviUpdate, 'Axd/service class search', progressTotal);
    listEnumerator = list.getEnumerator();
    while (listEnumerator.moveNext())
        serviceDictClass = new SysDictClass(listEnumerator.current());
        progressBar.setText(strfmt("Searching: %1 of %2 (%3)", progressCount, progressTotal,;       
        if ( != classNum(AifGenericDocumentService))
            axdWizardParameters = AifServiceClassGenerator::getServiceParameters(;
            if(axdWizardParameters && axdWizardParameters.parmName() == mySearchAxdName)
                info(strFmt("Aif service class name: %1", axdWizardParameters.parmAifServiceClassName()));
                info(strFmt("Document name: %1", axdWizardParameters.parmAifEntityClassName()));
                info(strFmt("Axd name: %1", axdWizardParameters.parmName()));
                info(strFmt("Query name: %1", axdWizardParameters.parmQueryName()));
    //Use this if you know the Document class name and wanted to find the Axd class name
    str                     documentClassName = "LedgerGeneralJournal";
    SysDictClass            documentDictClass;
    AifDocument             documentInstance;
    AxdBase                 axdBase;
    documentDictClass = new SysDictClass(className2Id(documentClassName));
    documentInstance = documentDictClass.makeObject();
    axdBase = documentInstance.getAxdInstance();
    info(strFmt("Document name: %1", documentClassName));
    info(strFmt("Axd name: %1", classId2Name(classIdGet(axdBase))));
    info(strFmt("Query name: %1", axdBase.getQueryName()));

Tuesday, 26 April 2016

UTCDateTime field in AX & daylight saving

This is an addition to an old post "UTCDateTime field in AX & filtering" (

When using the DateTimeUtil::newDateTime() method to create a datetime value, if you're using daylight saving, then you'll probably need to take extra care with the optional parameter in this method - Timezone.

Let's walk-through with an example.
Today is 26/04/2016, and this year the daylight saving ends on 30 Oct for UK.
The job below generate datetime value from 25 Oct to 5 Nov, with the time a minute before midnight (11:59:59pm). One with the Timezone parameter and the other one without.

Below is the data if you view it in AX.
You'll notice the field "DateTimeTest" for records between 26/10 - 30/10 isn't showing 11:59:59pm, instead, it is showing 12:59:59am, Then looking at the field "DateTimeTestWithTimezone", it correctly display all the time value at 11:59:59pm.

And if you look at the backend data in SQL, it was the other way round, the "DateTimeTest" field saved in all the record has the time value of 23:59:59.000, which is different from how it is shown in AX. If you look at the "DateTimeTestWithTimezone", for records between 25/10 - 29/10, it is saved with 22:59:59.000 and records after 29/10 is saved with 23:59:59.000.

AX will show time value based on your computer settings.
If you use daylight saving, it will automatically convert the time accordingly.
In the above example, the field "DateTimeTest" is assigned with a datetime value generated without the timezone parameter, so it is saved as 23:59:59.000 in SQL, when this value is shown in AX at a machine with daylight saving, it automatically fast forward an hour for the dates before the daylight saving ends, hence, 23:59:59 (11:59:59pm) becomes the next day 00:59:59 (12:59:59am).

It works the same way if user enter a datetime value. Says, user enter 01/05/2016, 5:00:00pm with a machine with daylight saving on, this value will be saved as 01/05/2016, 4:00:00pm in SQL.

Enjoy playing around with UTCDateTime!!!