Write product configuration model attributes to BOM and ProdBOM

pctn

https://www.youtube.com/watch?v=BaHYbDQJB_A

The product configuration model in Dynamics 365 for Finance and Operations is a great tool dynamically generate production orders including bill of materials (ProdBOM) and routes. In the product configuration model, the user is free to define attributes of different types, and use these attributes for calculations and conditions within the model. However, I had to come up with a solution to write the attribute value from the production configuration wizard to the generated ProdBOM. Here is an example code how this can be done:

Data type, table and form extensions

  1. Create a new string EDT and call it ERPPCStringAttribute
  2. Create table extensions for the BOM and ProdBOM and add a new field based on the ERPPCStringAttribute EDT
  3. Create a form extension for the ProdBOM and add the new field in the grid (Tab > Overview > Grid)

Extend the PCBOMLineDetails form

This is a tricky part. In the form for the BOM line details in the product configuration model, create a section for the attribute that looks the same like all the others. Here is a screenshot from Visual Studio:

image

You need to create a group, hat contains another group, with a checkbox and group with a string edit field and a radio button. Make sure to name the elements like shown in the screenshot.  If you are not sure, compare it with the other existing groups e.g. the SubContractor. You will need to compare the properties of each element with the corresponding properties of an existing element to make it look similar. 

image

Extend PCBOMLineDetails Form UI logic

You cannot overwrite methods on UI elements in form extensions e.g. clicked() . Therefore you have to implement the logic for the UI elements in a separate class. Create a new class ERPPCBomLineDetailsEventHandler and implement the following UI logic for clicked(), modified() and lookup()

class ERPPCBomLineDetailsEventHandler
{
   
    [FormControlEventHandler(formControlStr(PCBOMLineDetails, 
                             AllocateStringAttribute),
                             FormControlEventType::Clicked)]
    public static void AllocateAttributeString1_OnClicked(FormControl sender,
                                                     FormControlEventArgs e)
    {
        FormCheckBoxControl chx = sender;

        FormControl allocationGroup_AttributeString1 =
                sender.formRun().design().controlName(formControlStr(
                                             PCBOMLineDetails,
                                             allocationGroup_StringAttribute)
                                                       );
        FormControl attributeString1_Allocation =
                 sender.formRun().design().controlName(formControlStr(
                                             PCBOMLineDetails,
                                             StringAttribute_Allocation)
                                                       );

        PCModelingLibrary::templateSetEnabledStatus(
                                            (chx.value() == NoYes::Yes), 
                                            allocationGroup_AttributeString1,
                                            attributeString1_Allocation
                                                   );
    }

    [FormControlEventHandler(formControlStr(PCBOMLineDetails, 
                             StringAttribute_Allocation),
                             FormControlEventType::Modified)]
    public static void AttributeString1_Allocation_OnModified(
                                                      FormControl sender,
                                                      FormControlEventArgs e)
    {
        FormRadioControl radio = sender;
        FormStringControl attributeString1 =
                      sender.formRun().design().controlName(formControlStr(
                                                            PCBOMLineDetails,
                                                            StringAttribute)
                                                             );

        str label = attributeString1.labelText();

        attributeString1.text(”);

        if (radio.selection() == PCAllocation::Value)
        {
            attributeString1.extendedDataType(
                                        extendedTypeNum(ERPPCStringAttribute)
                                              );
        }
        else
        {
            attributeString1.extendedDataType(
                                        extendedTypeNum(ERPPCStringAttribute)
                                              );
            attributeString1.label(label);
        }
    }

     
    [FormControlEventHandler(formControlStr(PCBOMLineDetails, 
                             StringAttribute),
                             FormControlEventType::Lookup)]
    public static void AttributeString1_OnLookup(FormControl sender,
                                                 FormControlEventArgs e)
    {
        Object PCBOMLineDetails = sender.formRun();
        PCClass component = PCBOMLineDetails.component();
        FormRadioControl radio = 
           
sender.formRun().design().controlName(formControlStr(
                                                  PCBOMLineDetails,
                                                  StringAttribute_Allocation)
                                                 
);

        if(radio.selection() == PCAllocation::Attribute)
        {
            PCModelingLibrary::attributeLookup(sender, component);
        }       
    }

}

Extend the PCBOMLineDetails class-behind

Like many forms in Dynamics 365 for Finance and Operations the form has a class-behind that implements the business logic. You need to extend this class in order to deal with the newly created  Attribute group. Create a new class ERPPCBomLineDetails_Extension and impelement the following logic:

[ExtensionOf(formStr(PCBOMLineDetails))]
final class ERPPCBomLineDetails_Extension
{
    public PCClass component()
    {
        return component;
    }

    [ExtensionOf(FormMethodStr(PCBOMLineDetails,loadAllocations))]
    public void loadAllocations()
    {
        PCTemplateAttributeBinding      templateAttributeBinding;
        PCTemplateAttribute             fieldReference;


        PCTemplateAttributeBinding    findBindingByIDs(TableId _tableId, 
                                                       FieldId _fieldId)
        {
            PCTemplateAttributeBinding binding;

            fieldReference =
                 templateFind.findTemplateAttributeByTableIdAndFieldId(
                                                                _tableId,
                                                                _fieldId
                                                                );
           
            select  firstonly binding
            where   binding.TemplateAttribute == fieldReference.RecId
            &&      binding.TemplateComponent == templateComponent.RecId;

            return binding;
        }

        next loadAllocations();

        templateAttributeBinding = findBindingByIDs(tableNum(BOM), 
                                                    fieldNum(BOM,
                                                    ERPPCStringAttribute));


        PCModelingLibrary::templateLoadStringAllocation(component,
                                            templateAttributeBinding,
                                            StringAttribute,
                                            AllocateStringAttribute,
                                            StringAttribute_Allocation,
                                            AllocationGroup_StringAttribute);


    }

    [ExtensionOf(FormMethodStr(PCBOMLineDetails,updateRadioControls))]
    public void updateRadioControls()
    {
        next updateRadioControls();

        PCModelingLibrary::templateSetEnabledStatus(
                            (AllocateStringAttribute.value() == NoYes::Yes), 
                            AllocationGroup_StringAttribute,
                            StringAttribute_Allocation);

    }

    [ExtensionOf(FormMethodStr(PCBOMLineDetails,writeAllocations))]
    public void writeAllocations()
    {
        PCTemplateAttribute             fieldReference;

        next writeAllocations();

        if ((AllocateStringAttribute.value() == NoYes::Yes))
        {
            fieldReference = 
                   templateFind.findTemplateAttributeByTableIdAndFieldId(
                                      tableNum(BOM),
                                      fieldNum(BOM, ERPPCStringAttribute));


            PCModelingLibrary::templateSaveStringAllocation(component, 
                                               templateComponent,
                                               fieldReference,
                                               StringAttribute,
                                               StringAttribute_Allocation);
        }
    }

}

Extend the product configuration model framework

Implement the following classes to extend the product configuration model framework in Dynamics 365 Finance and Operations:

PcAdaptorBOMLine class:

[ExtensionOf(classStr(PcAdaptorBOMLine))]
final class ERPPcAdaptorBOMLine_Extension
{
    public ERPPCStringAttribute parmWANPCStringAttribute(
            ERPPCStringAttribute _stringAttribute = bom.ERPPCStringAttribute)
    {
        EcoResTextValue value;

        value.TextValue = _stringAttribute;
        this.fieldAssignment(bom.TableId,
                             fieldnum(BOM, ERPPCStringAttribute),
                             value);

        bom.ERPPCStringAttribute = _stringAttribute;                   
        return bom.ERPPCStringAttribute;
    }

}

PCGenerateBOMLine

[ExtensionOf(classStr(PCGenerateBOMLine))]
final class ERPPCGenerateBOMLine_Extension
{
    protected void setupBOM(
        BOMId       _bomId,
        InventDim   _inventDim,
        InventTable _inventTable,
        boolean     _isProduction
    )
    {
        next setupBom(_bomId,_inventDim,_inventTable,_isProduction);

        bom.ERPPCStringAttribute = templateFind.getBindingValueAsString(
                                       tableNum(BOM),
                                       fieldNum(BOM,ERPPCStringAttribute));
    }

}

Re-Initialize the framework template records

By default the framework does not recognize to create template records for the newly added attribute field. Therefore you have to delete the existing templates and trigger the framework to reinitialize. Be aware, this might harm your existing models!

PCTemplateInitialize class:

[ExtensionOf(classStr(PcTemplateInitialize))]
final class ERPPCTemplateInitialize_Extension
{
public static void main(Args _args)
{
PCTemplate tableTemplate;
PcTemplateInitialize init = PcTemplateInitialize::construct();
delete_from tableTemplate;
init.run();
}
protected void createTemplatesForBOM()
{
next createTemplatesForBom();
PCTemplate tableTemplate;
select firstonly tableTemplate where
tableTemplate.ReferencedTableId == tableNum(BOM);
this.createFieldTemplate(tableTemplate,fieldNum(BOM,
ERPPCStringAttribute ));
}
}

Start this class from Class Runner or add a menu item to call it by hand.

Test your Implementation

  1. Create a new product configuration model
  2. Add a new attribute “notes” of type string
  3. Add a BOM line to the model
  4. Open the BOM line details and assign the notes attribute to the string attribute 1
    image
  5. Save the model
  6. Create and approve the model version
  7. Create a new production order
  8. Use the configuration wizard to provide a text value for the notes attribute
    image
  9. Create the production order
  10. Verify that you can see the attribute value in the ProdBOM of your production order
    image

About erpcoder
Azure Cloud Architect and Dynamics 365 enthusiast working in Research & Development for InsideAx

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: