Saturday, May 30, 2020

Using the product configurator in integration scenarios

Product configuration in D365FO must happen through the user interface, as the product configuration form is the only piece of code that communicates with the solver, wrapped in a dll outside D365FO's X++ codebase. The communication happens through the JavaScript of a designated form control. This is not great if you want to create a configuration in integration scenarios.

In AX 2012 R3 CU8 a class was introduced to allow you to pass a set of attributes and values for these, without using UI parts, and then get the solver to return the XML needed for processing in the backend engine. The class is named PCRuntimeSynchronousConfigurator and you can read a bit about it here.
Even though the solver dll basically is the same between AX 2012 and D365FO, this class didn't make it to D365FO. Well until now, where the class is included in 10.0.12.

So here is an example of how to generate configuration XML for the backend engine:
public static void main(Args _args)
    PCProductConfigurationModel productConfigurationModel = 
        PCProductConfigurationModel::findByName('High End Speaker (D0004)');

    str modelXML = productConfigurationModel.getXML();

    System.String values;
    values = '<Assignments>' +  
             '<Assignment xPath="CabinetFinish" value="Black"' +
             '<Assignment xPath="SpeakerHeight" value="12"' +
             '<Assignment xPath="FrontGrill" value="Metal"' +
             '<Assignment xPath="CornerProtection" value="true"' +

    PCRuntimeSynchronousConfigurator configurator = PCRuntimeSynchronousConfigurator::construct();
    Microsoft.Dynamics.Ax.Frameworks.Controls.ProductConfiguration.IsConfigurationComplete isConfigurationComplete;
    isConfigurationComplete = configurator.configure(modelXML, values);

    if (isConfigurationComplete == 
        str configuredProductXML = configurator.getAllAssignedValues();

Inside the class, the attributes are loaded this way:

Of course you could also previously do what the "new" class does, but it would have been an unsupported and undocumented call to the solver dll and Microsoft could change the dll without any prior notice.

There are a couple of other ways to pass the attributes and values to the solver dll, but they are not supported as no standard X++ code uses these:
    configuratordll.AssignAttributeValueByXPath('CabinetFinish', 'Black'); 
    configuratordll.AssignAttributeValueByXPath('SpeakerHeight', '12');
    configuratordll.AssignAttributeValueByXPath('FrontGrill', 'Metal'); 
    configuratordll.AssignAttributeValueByXPath('CornerProtection', 'true');
    configuratordll.AssignAttributeValueById(2, 'Black');  // Cabinet finish
    configuratordll.AssignAttributeValueById(5, '14');     // Speaker height
    configuratordll.AssignAttributeValueById(3, 'Metal');  // Front grill
    configuratordll.AssignAttributeValueById(4, 'true');   // Corner protection

If you need to implement something like this, I encourage you to check out this flow diagram on docs:
It would be nice if more things were documented with flow diagrams like this.