The Infolog

A blog of Dynamics AX development tips and tricks

Skip to: Content | Sidebar | Footer

Creating a lookup to a field ID

5 December, 2016 (21:45) | Dynamics AX | By: Howard Webb

Sometimes we might want to allow the end user to select a field from a table within a form, for example if we are parameterizing the mapping values. To do with we will need the following items:

 

  • A field to hold the field ID (EDT FieldID)
  • A edit method on the form datasource
  • A string control on our form (EDT FieldLabel)
  • A lookup method on the form control

 

Firstly we add the field ID to the table (there is no need to make that visible) . That should be easy enough to do

 

 

On to our lookup. We will be using the class SysTableFieldLookup and just like a normal lookup we will create a query, however we only need the datasource in this instance, which should be the table the fields are on. We will need to return our lookup class after the lookup is performed as we will need to access it later to get the selected field. Once done it should look as follows

 

fieldlookupeditmethod

We will need a new global variable on the form of type SysTableFieldLookup

Next we will create the edit method.

 

fieldlookupeditmethod2

 

You can see that if the control is set we pull the record from the lookup class and get the field ID for a table field from it.  Now its time to create our form control and point it at the edit method. With that done the last step is to override the lookup method to call ours and populate the global variable:

 

fieldlookupeditmethod3

 

With that done you should now get a lookup on your form with the field label and help text

 

fieldlookupeditmethod4

Filter options for a date effective table

5 December, 2016 (21:19) | Dynamics AX | By: Howard Webb

As standard if we have a form that uses a datasource that is linked to a date effective table we will only see the current record. In AX we can give the user a new button in the action pane which allow them to change that and show the records they need or want.

To do this we have a class that does all the heavy lifting for us: DateEffectivenessPaneController. To use this class and functionality we need to implement IDateEffectivenessPaneCaller in our form, declare an instance of it as a global variable and create a new instance of it in the form init passing in the form and the datasource along with a few other switches:

Filter options for a date effective table 1

 

We also need a public method to make the instance of our class accessible:

Filter options for a date effective table 2

 

With that we have the filter button added to our form.

Filter options for a date effective table 3

 

Using the MediaViewerControl in Dynamics AX 7

8 September, 2016 (11:09) | Dynamics AX | By: Howard Webb

In AX 7 we have a whole new UI that is web based and around that we also have a few new controls. With things being new I have tried to get one of the newer controls working. I struggled for documentation but in the end got the MediaViewerControl to display a video in a new AX form.

To do this I added a control to my test form:

Then in the init method I set the URL and the content type of the window via code:

When we run the form we will get a video with the controls needed to play the video:

Dynamics AX 7: Form extensions and event handling

26 August, 2016 (11:13) | Dynamics AX | By: Howard Webb

As I’ve just got my hands on AX 7 I wanted to do a little exercise that allowed me to work with form extensions and also the new event handlers. The task I attempted was to add a small street view thumbnail to the address grid that will show the view of the address. Unfortunately, Bing Streetside was not available in my area so I have used Google’s StreetView as the method to produce the images. To do this you’ll need to have an API key from Google. If you have a quick read of their documentation, you’ll see all we need to do is build a URL and then pass it to our image control.

To develop this mod we will need to make changes to the standard form LogisticsPostalAddressGrid. With AX 7 we now have the option of creating an extension rather than changing the form on our layer and requiring a merge. This allows for easier deployment and while cannot be used in every instance it should be your default option for customisations. To add a new extension right click on the object and select ‘Create extension’:

This will then create and add the extension to the standard object to your project. You can then edit the extension.

We can now add the new control to the form extension and set the properties we need:

With the image created we can now look to see what event we can use. I would like to change the image every time we change the record, in previous versions of AX we would override or edit the active method. Looking at the new events on the form data source we can see that there is a event called “OnActivated”

This will fire after the super of “active” so we will use that. To use these new methods we need to create a new class and an event handler method to write our code within. Once we have our new class created we can just right click on the method and select ‘Copy event handler method’ and this will put in to our clipboard the bits we need to create our new method

With that done we are pretty much back to how things used to be. We can write the code to build the URL and then set the URL for the image:

We can then rebuild and test:

Not the prettiest, but enough to show the principle.

Creating a Load and shipment from a sales line

26 January, 2016 (15:00) | Dynamics AX | By: Howard Webb

I’ve recently been asked to develop a mod to create a load, load line and a shipment for a SalesLine record from code. I had a quick look and could not see anything on the internet so below is the code I used. Sadly it seems that the shipment table does not have any init methods so the values are assigned individually:


static void Job1(Args _args)
{
    SalesLine                   salesLine;
    SalesTable                  salesTable;
    WHSLoadTable                loadTable;
    WHSLoadLine                 loadLine;
    InventDim                   inventDim;
    InventSite                  inventSite;
    WHSShipmentTable            whsShipmentTable;
    WHSShipmentTable            consolidateShipmentTable;
    LogisticsPostalAddress      dlvAddress;
    boolean                     consolidate;
    TMSSalesTable               tmsSalesTable;
    ;
    
    select firstOnly salesLine
        where salesLine.InventTransId == "LOT006059";
    
    salesTable  = salesLine.salesTable();
    InventDim   = salesLine.inventDim();
    inventSite  = inventDim.inventSite();

    
    ttsBegin;
    
    loadTable.initValue();
    loadTable.initFromItem(salesLine.ItemId);
    loadTable.LoadId        = "MyCustomID4";
    loadTable.LoadDirection = WHSLoadDirection::Outbound;
    loadTable.initFromLoadTemplateId(inventSite.DefaultLoadTemplateId);
    loadTable.LoadPaysFreight           = NoYes::No;
    loadTable.insert();
    
    dlvAddress = salesLine.deliveryAddress();
    
    loadLine.initValue();
    loadLine.LoadId = loadTable.LoadId;
    loadLine.initFromSalesLine(salesLine);
    if(loadLine.validateWrite())
        loadLine.insert();
    
    
    
    if(!dlvAddress.whsAddressFormatValidation())
        throw error("error");
    
    whsShipmentTable.initValue();
    
    consolidate = InventLocation::find(inventDim.InventLocationId).ConsolidateShipAtRTW;
    
    select firstonly forupdate consolidateShipmentTable
        where   consolidateShipmentTable.AccountNum                 == salesLine.CustAccount
            &&  consolidateShipmentTable.DeliveryName               == salesLine.DeliveryName
            &&  consolidateShipmentTable.DeliveryPostalAddress      == dlvAddress.RecId
            &&  consolidateShipmentTable.InventLocationId           == inventDim.InventLocationId
            &&  consolidateShipmentTable.LoadId                     == loadLine.LoadId
            &&  consolidateShipmentTable.ShipmentStatus             < WHSShipmentStatus::Shipped
            &&  (consolidate
            ||  consolidateShipmentTable.OrderNum                   == salesLine.SalesId);
    
    if(consolidateShipmentTable)
    {
        consolidateShipmentTable.OrderNum       = consolidateShipmentTable.OrderNum         != salesLine.SalesId                ? '' : salesLine.SalesId;
        consolidateShipmentTable.CustomerRef    = consolidateShipmentTable.CustomerRef      != salesTable.CustomerRef           ? '' : salesTable.CustomerRef;
        consolidateShipmentTable.CustomerReq    = consolidateShipmentTable.CustomerReq      != salesTable.PurchOrderFormNum     ? '' : salesTable.PurchOrderFormNum;
        consolidateShipmentTable.DlvTermId      = consolidateShipmentTable.DlvTermId        != salesTable.DlvTerm               ? '' : salesTable.DlvTerm;
        consolidateShipmentTable.update();
    }
    else
    {
     
        whsShipmentTable.clear();
        whsShipmentTable.ShipmentId                 = "CustomID4";
        whsShipmentTable.LoadId                     = loadLine.LoadId;
        whsShipmentTable.WorkTransType              = WHSWorkTransType::Sales;
        whsShipmentTable.OrderNum                   = salesLine.SalesId;
        whsShipmentTable.AccountNum                 = salesLine.CustAccount;
        whsShipmentTable.DeliveryName               = salesLine.DeliveryName;
        whsShipmentTable.DeliveryPostalAddress      = dlvAddress.RecId;   
        
        whsShipmentTable.CountryRegionISOCode       = LogisticsAddressCountryRegion::find(dlvAddress.CountryRegionId).isOcode;
        
        whsShipmentTable.Address                    = LogisticsPostalAddress::formatAddress(    dlvAddress.Street,
                                                                                                dlvAddress.ZipCode,
                                                                                                dlvAddress.City,
                                                                                                dlvAddress.CountryRegionId,
                                                                                                dlvAddress.State,
                                                                                                dlvAddress.County);
        whsShipmentTable.DlvTermId                  = salesTable.DlvTerm;
        whsShipmentTable.InventSiteId               = inventDim.InventSiteId;
        whsShipmentTable.InventLocationId           = inventDim.InventLocationId;
        whsShipmentTable.CarrierCode                = tmsSalesTable.CarrierCode;
        whsShipmentTable.CarrierServiceCode         = tmsSalesTable.CarrierServiceCode;
        whsShipmentTable.BrokerCode                 = tmsSalesTable.BrokerCode;
        whsShipmentTable.RouteCode                  = tmsSalesTable.RouteConfigCode;
        whsShipmentTable.ModeCode                   = tmsSalesTable.ModeCode;
        whsShipmentTable.CarrierGroupCode           = tmsSalesTable.CarrierGroupCode;
        whsShipmentTable.LoadDirection              = WHSLoadDirection::Outbound;
        whsShipmentTable.CustomerRef                = salesTable.CustomerRef;
        whsShipmentTable.CustomerReq                = salesTable.PurchOrderFormNum;
        if(whsShipmentTable.validateWrite())
        {
            whsShipmentTable.insert();
            whsShipmentTable.createShipmentNotes(salesTable);
            
            loadLine.ShipmentId = whsShipmentTable.ShipmentId ? whsShipmentTable.ShipmentId : consolidateShipmentTable.ShipmentId;
            if(loadLine.validateWrite())
                loadLine.update();
        }

    }
    
    ttsCommit;
}

Using the SysOperationFramework

11 January, 2016 (11:41) | Uncategorized | By: Howard Webb

As we all know that moving forward we should really be making better use of the SysOperationFramework. Having a little more time over xmas I decided to use the framework in a mod I was doing. The mod called for a new export to excel without using BIS.

There is a Microsoft white paper about using it but after reading it I found that the Art of creation have a blog post which is much easier to follow.

We develop using the framework in a similar way to the way we would develop an SSRS report (SSRS uses the framework in the background).

Firstly we need to create a contract class. This will store all of the user input and will be used by the framework to produce the dialog fields. Just like with a report we need to add the DataContractAttribute and then also all of the global variables that we will later create parm methods for it. A word of warning about this, if you add, remove or change any of these methods you’ll need to run a CIL or possibly even restart the AOS

 

 

Then for each of the variables we will need to create a parm method making sure to add DataMemberAttribute. Please note if you get your return data type correct it will handle all of the help text etc. for you but you can override them with additional attributes.

 

If we would like a query to be shown we need a few more methods in our class, first off a parm method for our query. Note the extra attribute AifQueryTypeAttribute in which we pass the packed query and the name of our query.

 

We also have set and get methods for our query:

 

 

 

With our contract class finished we can now move on to our data service class. We don’t have to do anything special with this class other than making sure that it runs on server and that the mothod you will run has the SysEntryPointAttribute and accepts in your contract class:

 

Add your business logic so your class does what you want and then we can create your menu item. The menu item needs to point to a controller class (in this instance I’ve used the system one but you could create a custom class and extend from the standard class. It also needs to accept the execution mode and finally in to the parameters you put the method you need to run.

 

 

With that done you are ready to test. Kick off a CIL and then you can open your menu item. You should get your dialog up, along with the parameters and the query ready to run, your user selections will be automatically packed and unpacked and you will have a batch tab:

 

 

 

 

 

Validate if a string is a hex value

4 December, 2015 (20:16) | Dynamics AX | By: Howard Webb

I’ve recently needed to validate if a string was a colour hex value. There was not a standard method I could find to check (although I am convinced there must be a .net class I can use) but as speed was of the essence I wrote my own:

 

 

Converting hex colour to RGB

3 December, 2015 (16:25) | Uncategorized | By: Howard Webb

Throughout Dynamics AX it mostly uses integer values as a way of storing colours. Sometimes we need to either accept in hex or return hex colour values. To do this we can use the code found below which converts a hex string into its RGB values, then in to the integer value for those RGB values and then converts it back in to hex:

 

Getting a pending invoice totals

27 November, 2015 (14:37) | Dynamics AX | By: Howard Webb

I needed to create a report which gathered the totals for a pending purchase invoice. Investigating the totals form You can see that it uses the class PurchTotals to calculate the total values. However this uses a PurchParmUpdate record, and at this point a permanent record in the table is not created yet. To get the PurchTotals class to work correctly we would need to create the temp PurchParmUpdate record, calculate the totals and then delete the temporary records. Below is the code to do this. Note that we have to declare the child class of PurchFormLetter to call the correct method:


PurchTable               purchTable;
PurchTotals              purchTotals;
real                     discAmount;
VendInvoiceInfoTable     vendInvoiceInfoTable;
VendInvoice              vendInvoice;
PurchParmUpdate          purchParmUpdate;
PurchFormLetter_Invoice  purchFormLetter;
ParmId                   parmId;
;

select firstOnly vendInvoiceInfoTable
    where vendInvoiceInfoTable.recID == 5637146096;

purchFormLetter = new PurchFormLetter_Invoice();

purchFormLetter.init();

purchFormLetter.parmCallerTable(vendInvoiceInfoTable);

purchFormLetter.initNewFromSaved(vendInvoiceInfoTable);

purchFormLetter.chooseLines();

parmId = purchFormLetter.parmId();

purchParmUpdate = purchFormLetter.purchParmUpdate();

purchTotals = PurchTotals::newParmTable( vendInvoiceInfoTable, purchParmUpdate.SpecQty, AccountOrder::None, parmId, '', vendInvoiceInfoTable.Ordering);

purchTotals.calc();

discAmount = purchTotals.purchTotalAmount();

purchFormLetter.deleteParmUpdate();

Creating a configured sales line

15 September, 2015 (09:59) | Dynamics AX | By: Howard Webb

Recently a customer has asked us to develop a way to produce a sales line which contained an item that fully configured via product configurator. For those that are unaware of the process if an item is selected that is configurable you can answer a number of “questions” to allow configuration of an item via the below button on the sales line:

This allows you to customise the item in a number of ways, for example if you are making a sofa which can have a number of coloured threads you can choose the thread colour.

The issue with this is that the configuration form uses .Net to produce the values via some .dll files which we cannot examine. In the end with a lot of debugging we found out that it used XML to get the configuration questions and build the form, then returned XML.

To replicate this we build a class that read the XML, then pulled the answers from a table in a Columbus solution (although this could be any place that stores information).

Firstly we need to make use of a number of framework classes (which will use the default configuration to get the questions) after the sales line has been created:

</p><p><span style="font-family:Consolas; font-size:8pt">    pcConfigurationProductVariantFactory    = pcConfigurationProductVariantFactory::construct();
</span></p><p><span style="font-family:Consolas; font-size:8pt">    vairentContainer                        = pcConfigurationProductVariantFactory.createVariant(ecoResProductMaster, SalesLine.ItemId, SalesLine.InventDimId);  <span style="color:green"><em>//This creates a new inventDIm so will need to update sales line post everything being done</em></span>
    </span></p><p><span style="font-family:Consolas; font-size:8pt">    newInventDimId                          = <span style="color:darkblue"><strong>conPeek</strong></span>(vairentContainer, <span style="color:red"><strong>2</strong></span>);
</span></p><p><span style="font-family:Consolas; font-size:8pt">    pcVariantConfiguration                  = pcVariantConfiguration::find(<span style="color:darkblue"><strong>conPeek</strong></span>(vairentContainer, <span style="color:red"><strong>1</strong></span>));  <span style="color:green"><em>//gets the new varient record</em></span>
    </span></p><p><span style="font-family:Consolas; font-size:8pt">    productModelVersion                     = PCProductModelVersion::findActiveVersion(ecoResProductMaster.RecId);
</span></p><p><span style="font-family:Consolas; font-size:8pt">    productConfigurationModel               = productModelVersion.getProductConfigurationModel();
</span></p><p><span style="font-family:Consolas; font-size:8pt">    xmlDocument                             = XmlDocument::newXml(productConfigurationModel.getXML());
</span></p><p>

The XML document will contain the list of attributes, operation lines and subparts. This will need looping through to get the individual questions. In my example I have used maps to record these items.

With the list of questions generated we can then start to generate the return XML with the questions and answers as per below. In the below example we get the answers from a Columbus solution’s table:

 </p><p><span style="font-family:Consolas; font-size:8pt"><span style="color:darkblue"><strong>private</strong></span>
      <span style="color:darkblue"><strong>void</strong></span> createAttributeElements()
</span></p><p><span style="font-family:Consolas; font-size:8pt">{
</span></p><p><span style="font-family:Consolas; font-size:8pt">    OSSProfileFieldValue        ossProfileFieldValue;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    OSSProfileField             ossProfileField;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    String255                   questionName, questionDisplayName, questionType;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    XmlNode                     attributeNode;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    XmlAttribute                attributeNameAttribute, attributeIdAttribute, attributeSelectedAttribute, attributeTypeAttribute, attributeValueAttribute;
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>int</strong></span>                         numOfDec;
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>utcDateTime</strong></span>                 dateTime;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    TimeHour24                  emptyTime;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    InventSizeConversionTable   inventSizeConversionTable;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    ;
</span></p><p>
 </p><p> </p><p><span style="font-family:Consolas; font-size:8pt">    mapEnumerator               = attributes.getEnumerator();
</span></p><p>
 </p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>while</strong></span>(mapEnumerator.moveNext())
</span></p><p><span style="font-family:Consolas; font-size:8pt">    {
</span></p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">        questionDetails             = mapEnumerator.currentValue();
</span></p><p><span style="font-family:Consolas; font-size:8pt">        questionName                = <span style="color:darkblue"><strong>conPeek</strong></span>(questionDetails, <span style="color:red"><strong>1</strong></span>);
</span></p><p><span style="font-family:Consolas; font-size:8pt">        questionDisplayName         = <span style="color:darkblue"><strong>conPeek</strong></span>(questionDetails, <span style="color:red"><strong>2</strong></span>);
</span></p><p><span style="font-family:Consolas; font-size:8pt">        questionType                = <span style="color:darkblue"><strong>conPeek</strong></span>(questionDetails, <span style="color:red"><strong>3</strong></span>);
</span></p><p>
 </p><p>
</p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>select</strong></span>
      <span style="color:darkblue"><strong>firstOnly</strong></span> RecId <span style="color:darkblue"><strong>from</strong></span> ossProfileField
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>where</strong></span>   ossProfileField.Withdrawn       == NoYes::No
</span></p><p><span style="font-family:Consolas; font-size:8pt">            &amp;&amp;      ossProfileField.EntityTableId   == <span style="color:darkblue"><strong>tableNum</strong></span>(SalesLine)
</span></p><p><span style="font-family:Consolas; font-size:8pt">            &amp;&amp;      ossProfileField.FieldName       == questionName
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>join</strong></span> ossProfileFieldValue
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>where</strong></span>   ossProfileFieldValue.EntityRecId        == salesLine.RecId
</span></p><p><span style="font-family:Consolas; font-size:8pt">                &amp;&amp;      ossProfileFieldValue.EntityTableId      == ossProfileField.EntityTableId
</span></p><p><span style="font-family:Consolas; font-size:8pt">                &amp;&amp;      ossProfileFieldValue.FieldName          == ossProfileField.FieldName;
</span></p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>if</strong></span>(ossProfileFieldValue)
</span></p><p><span style="font-family:Consolas; font-size:8pt">        {
</span></p><p><span style="font-family:Consolas; font-size:8pt">            attributeNode               = configuredXML.createElement   (#attribute);
</span></p><p><span style="font-family:Consolas; font-size:8pt">            attributeNameAttribute      = configuredXML.createAttribute (#nameAttribute);
</span></p><p><span style="font-family:Consolas; font-size:8pt">            attributeIdAttribute        = configuredXML.createAttribute (#uniqueIdAttribute);
</span></p><p><span style="font-family:Consolas; font-size:8pt">            attributeSelectedAttribute  = configuredXML.createAttribute (#isUserSelectedAttribute);
</span></p><p><span style="font-family:Consolas; font-size:8pt">            attributeTypeAttribute      = configuredXML.createAttribute (#typeAttribute);
</span></p><p><span style="font-family:Consolas; font-size:8pt">            attributeValueAttribute     = configuredXML.createAttribute (#valueAttribute);
</span></p><p>
 </p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">            attributeIdAttribute.       value(MapEnumerator.currentKey());
</span></p><p><span style="font-family:Consolas; font-size:8pt">            attributeSelectedAttribute. value(<span style="color:darkred">'1'</span>);
</span></p><p><span style="font-family:Consolas; font-size:8pt">            attributeNameAttribute.     value(questionName);
</span></p><p><span style="font-family:Consolas; font-size:8pt">            attributeTypeAttribute.     value(questionType);
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:green"><em>//This area needs further testing</em></span>
    </span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>switch</strong></span>(questionType)
</span></p><p><span style="font-family:Consolas; font-size:8pt">            {
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>case</strong></span> enum2Value(AttributeDataType::Text) :
</span></p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>if</strong></span>(questionName == #sizeProperty)
</span></p><p><span style="font-family:Consolas; font-size:8pt">                    {
</span></p><p><span style="font-family:Consolas; font-size:8pt">                        inventSizeConversionTable = inventSizeConversionTable::find(salesLine.itemID, ossProfileFieldValue.StringValue);
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>if</strong></span>(inventSizeConversionTable)
</span></p><p><span style="font-family:Consolas; font-size:8pt">                            attributeValueAttribute.value(inventSizeConversionTable.SizeConversionAXValue);
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>else</strong></span>
    </span></p><p><span style="font-family:Consolas; font-size:8pt">                            attributeValueAttribute.value(ossProfileFieldValue.StringValue);
</span></p><p><span style="font-family:Consolas; font-size:8pt">                    }
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>else</strong></span>
    </span></p><p><span style="font-family:Consolas; font-size:8pt">                        attributeValueAttribute.value(ossProfileFieldValue.StringValue);
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>break</strong></span>;
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>case</strong></span> #enumType :
</span></p><p><span style="font-family:Consolas; font-size:8pt">                    attributeValueAttribute.value(ossProfileFieldValue.StringValue);
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>break</strong></span>;
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>case</strong></span> enum2Value(AttributeDataType::Currency) :
</span></p><p><span style="font-family:Consolas; font-size:8pt">                    attributeValueAttribute.value(ossProfileFieldValue.StringValue);
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>break</strong></span>;
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>case</strong></span> enum2Value(AttributeDataType::Decimal) :
</span></p><p><span style="font-family:Consolas; font-size:8pt">                    numOfDec = numOfDec(ossProfileFieldValue.RealValue);
</span></p><p><span style="font-family:Consolas; font-size:8pt">                    attributeValueAttribute.value(<span style="color:darkblue"><strong>num2str</strong></span>(  ossProfileFieldValue.RealValue,
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:red"><strong>0</strong></span>,
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                                            numOfDec,
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                                            DecimalSeparator::Auto,
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                                            ThousandSeparator::Auto));
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>break</strong></span>;
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>case</strong></span> enum2Value(AttributeDataType::DateTime) :
</span></p><p><span style="font-family:Consolas; font-size:8pt">                    dateTime    = DateTimeUtil::newDateTime(ossProfileFieldValue.DateValue, emptyTime);
</span></p><p><span style="font-family:Consolas; font-size:8pt">                    attributeValueAttribute.value(<span style="color:darkblue"><strong>datetime2str</strong></span>(dateTime, DateFlags::FormatAll));
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>break</strong></span>;
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>case</strong></span> enum2Value(AttributeDataType::Integer) :
</span></p><p><span style="font-family:Consolas; font-size:8pt">                    attributeValueAttribute.value(<span style="color:darkblue"><strong>int2str</strong></span>(ossProfileFieldValue.IntValue));
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>break</strong></span>;
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>case</strong></span> enum2Value(AttributeDataType::TrueFalse) :
</span></p><p><span style="font-family:Consolas; font-size:8pt">                    attributeValueAttribute.value(<span style="color:darkblue"><strong>strfmt</strong></span>(<span style="color:darkred">"%1"</span>, ossProfileFieldValue.BoolValue));
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>break</strong></span>;
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>default</strong></span> :
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>break</strong></span>;
</span></p><p>
 </p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">            }
</span></p><p>
 </p><p>
 </p><p>
 </p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">            attributeNode.attributes().setNamedItem(attributeNameAttribute);
</span></p><p><span style="font-family:Consolas; font-size:8pt">            attributeNode.attributes().setNamedItem(attributeIdAttribute);
</span></p><p><span style="font-family:Consolas; font-size:8pt">            attributeNode.attributes().setNamedItem(attributeSelectedAttribute);
</span></p><p><span style="font-family:Consolas; font-size:8pt">            attributeNode.attributes().setNamedItem(attributeTypeAttribute);
</span></p><p><span style="font-family:Consolas; font-size:8pt">            attributeNode.attributes().setNamedItem(attributeValueAttribute);
</span></p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">            componentNode.appendChild(attributeNode);
</span></p><p><span style="font-family:Consolas; font-size:8pt">        }
</span></p><p><span style="font-family:Consolas; font-size:8pt">    }
</span></p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">}
</span></p><p>

This will produce an XML document with the questions and the answers needed. With that XML we can then configure and update the sales line:

</p><p><span style="font-family:Consolas; font-size:8pt"><span style="color:darkblue"><strong>private</strong></span>
      <span style="color:darkblue"><strong>void</strong></span> configureLine()
</span></p><p><span style="font-family:Consolas; font-size:8pt">{
</span></p><p><span style="font-family:Consolas; font-size:8pt">    PCAdaptorSourceDocumentLine             adaptorSourceDocumentLine;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    PCAdaptorFactory                        pcAdaptorFactory;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    PCAdaptor                               pcAdaptor;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    PCAdaptorProductConfigurationModel      adaptorProductConfigurationModel;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    PCXmlParseConfigurationInstance         pcXmlParseConfigurationInstance;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    PCBackEndConfiguration                  pcBackEndConfiguration;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    EcoResDistinctProductVariant            distinctProductVariant;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    PCSourceDocumentLineUtility             sourceDocumentLineUtility;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    InventDim                               inventDim;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    ;
</span></p><p>
 </p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt"><span style="color:white"><strong>    pcAdaptorFactory                    = PCAdaptorFactory::construct();</strong></span>
    </span></p><p><span style="font-family:Consolas; font-size:8pt">    adaptorProductConfigurationModel    = PCAdaptorProductConfigurationModel::construct(productConfigurationModel);
</span></p><p><span style="font-family:Consolas; font-size:8pt">    adaptorSourceDocumentLine           = PCAdaptorSourceDocumentLine::construct(salesLine);
</span></p><p><span style="font-family:Consolas; font-size:8pt">    pcAdaptor                           = pcAdaptorFactory.getAdaptorFromModelName(productConfigurationModel.Name);
</span></p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">    pcAdaptor.parmProductConfigurationModel(adaptorProductConfigurationModel);
</span></p><p><span style="font-family:Consolas; font-size:8pt">    pcAdaptor.parmSourceDocumentLine(adaptorSourceDocumentLine);
</span></p><p>
 </p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">    pcXmlParseConfigurationInstance     = pcXmlParseConfigurationInstance::construct();
</span></p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">    pcXmlParseConfigurationInstance.parse(productConfigurationModel, pcVariantConfiguration, <span style="color:darkred">''</span>, configuredXML.xml(), PCRuntimeMode::EditVariant, PCAdaptor);
</span></p><p><span style="font-family:Consolas; font-size:8pt">    pcAdaptor.run();
</span></p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">    inventDim                           = InventDim::find(newInventDimId);
</span></p><p><span style="font-family:Consolas; font-size:8pt">    pcBackEndConfiguration              = pcBackEndConfiguration::construct();
</span></p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">    pcBackEndConfiguration.setup(   pcVariantConfiguration,
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                    productModelVersion,
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                    salesLine.ItemId,
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>conPeek</strong></span>(vairentContainer, <span style="color:red"><strong>3</strong></span>),
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                    InventDim,
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                    InventDim,
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                    salesLine.SalesPrice,
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                    salesLine.ShippingDateRequested,
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                    pcAdaptor,
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                    PCRuntimeLibrary::docuGenerateNoYes());
</span></p><p><span style="font-family:Consolas; font-size:8pt"><span style="color:white"><strong>    pcBackEndConfiguration.run();</strong></span>
    </span></p><p>
 </p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">    InventDim.ConfigId                  = pcBackEndConfiguration.getAppliedConfigurationName();
</span></p><p><span style="font-family:Consolas; font-size:8pt">    inventDim                           = InventDim::findOrCreate(inventDim);
</span></p><p><span style="font-family:Consolas; font-size:8pt">    distinctProductVariant              = pcBackEndConfiguration.getAppliedDistinctProductVariant();
</span></p><p><span style="font-family:Consolas; font-size:8pt">    sourceDocumentLineUtility           = PCSourceDocumentLineUtility::construct();
</span></p><p>
 </p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>ttsBegin</strong></span>;
</span></p><p><span style="font-family:Consolas; font-size:8pt">    salesLine                   = sourceDocumentLineUtility.updateLineDetails(  salesLine,
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                                                                pcBackEndConfiguration.getGeneratedBOMId(),
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                                                                pcBackEndConfiguration.getGeneratedRouteId(),
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                                                                pcAdaptor.getPrice(),
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                                                                salesLine.ShippingDateRequested,
</span></p><p><span style="font-family:Consolas; font-size:8pt">                                                                                InventDim.inventDimId);
</span></p><p><span style="font-family:Consolas; font-size:8pt">
      <span style="color:darkblue"><strong>ttsCommit</strong></span>;
</span></p><p><span style="font-family:Consolas; font-size:8pt">}
</span></p>