Remarks: This is an old post that I haven’t gotten around to publish. I haven’t checked if this is still the best approach…

I recently spoke with one of my colleagues about using details from the source document to calculate without having the end-user fill them out.
The product configurator (PC) is handled entirely outside F&O in MSF, and XML files are used to exchange information. So, to get the calculated values over to MSF, it was required to change these XML files.
When the document configurator adds default values, it first looks at defaults from the product configuration model. If they are not defined, it then looks at the attribute types. This is our entry point.
XML Information about PC components is handled in PCXmlSessionWriterComponent, and the defaulted values are initialized in createAttributeDefaultValuesMap.
The issue is that this class needs to learn about the source document!
Here is where Price Models in PC come into the frame – We can “abuse” it to transfer values between the PC classes…
Price Models instances (PCRuntinePriceModelInstance) are initialized directly from the source document, which is a good entry into getting the references.
Start by adding a RecId and TableId variable to PCRuntimePriceModelInstance
[ExtensionOf(classStr(PCRuntimePriceModelInstance))]
public final class PCRuntimePriceModelInstance_Class_JFM_Extension
{
public TableId sourceTableId;
public RecId sourceRecId;
#define.CurrentVersion(1)
#localmacro.CurrentList
sourceTableId,
sourceRecId
#endmacro
public container pack()
{
....
To initialize the variables, make a CoC method for PCRuntimePriceModelInstanceFactory.create
[ExtensionOf(classStr(PCRuntimePriceModelInstanceFactory))]
public final class PCRuntimePriceModelInstanceFactory_Class_JFM_Extension
{
public PCRuntimePriceModelInstance create(
CurrencyCode _currencyCode,
RefRecId _priceModel,
Common _sourceDocumentLine,
PCproductConfigurationModel _productConfigurationModel,
PCRuntimeMode _runtimeMode,
boolean _isRunningMultiCompany)
{
PCRuntimePriceModelInstance priceModelInstance = next create(
_currencyCode,
_priceModel,
_sourceDocumentLine,
_productConfigurationModel,
_runtimeMode,
_isRunningMultiCompany);
priceModelInstance.sourceRecId = _sourceDocumentLine.RecId;
priceModelInstance.sourceTableId = _sourceDocumentLine.TableId;
return priceModelInstance;
}
}
Finaly, create an extension class for PCXmlSessionWriteComponent, and add your own logic.
In my case, I have added a new enumerator to attribute types
[ExtensionOf(classStr(PCXmlSessionWriterComponent))]
public final class PCXmlSessionWriterComponent_Class_JFM_Extension
{
private TableId sourceTableId;
private RecId sourceRecId;
public void write( PCClass _component,
str _assignedComponentID,
PCXmlWriter _attributeTypeWriter,
PCXmlWriter _componentTreeWriter,
PCComponentInstance _componentInstance,
PCConfigurationControl _modelUIControl,
PCComponentControl _componentControl,
Set _parsedTypes,
PCXmlSessionDatabaseRelationTypes _parsedSystemTableConstraintTypes,
Map _parsedIntegerDomains,
LanguageId _sessionLanguage,
StackBase _subComponentPath,
PCRuntimeInstanceIdManager _runtimeInstanceIdManager,
PCPriceMethod _priceMethod,
PCRuntimePriceModelInstance _runtimePriceModelInstance)
{
sourceTableId = _runtimePriceModelInstance.sourceTableId;
sourceRecId = _runtimePriceModelInstance.sourceRecId;
next write(
_component,
_assignedComponentID,
_attributeTypeWriter,
_componentTreeWriter,
_componentInstance,
_modelUIControl,
_componentControl,
_parsedTypes,
_parsedSystemTableConstraintTypes,
_parsedIntegerDomains,
_sessionLanguage,
_subComponentPath,
_runtimeInstanceIdManager,
_priceMethod,
_runtimePriceModelInstance);
}
protected Map createAttributeDefaultValuesMap(PCClass _component, PCComponentInstance _componentInstance)
{
EcoResAttributeDefaultValue defaultValue;
EcoResAttribute ecoResAttribute;
EcoResCategoryAttribute categoryAttribute;
EcoResAttributeType ecoResAttributeType;
InventLocationId inventLocationId;
SalesQty salesQty;
ItemId itemId;
ProjId projId;
Map attributeDefaultValueMap = next createAttributeDefaultValuesMap(_component, _componentInstance);
switch (sourceTableId)
{
case tableNum(SalesQuotationLine):
var salesQuotationLine = SalesQuotationLine::findRecId(sourceRecIdPGS);
inventLocationId = salesQuotationLine.inventDim().inventLocationId;
salesQty = salesQuotationLine.SalesQty;
itemId = salesQuotationLine.ItemId;
projId = salesQuotationLine.QuotationId;
break;
case tableNum(SalesLine):
var salesLine = SalesLine::findRecId(sourceRecIdPGS);
inventLocationId = salesLine.inventDim().InventLocationId;
salesQty = salesLine.SalesQty;
itemId = salesLine.ItemId;
projId = salesLine.ProjId;
break;
default:
return attributeDefaultValueMap;
}
while select EcoResAttributePCDefaultingTypePGS from ecoResAttributeType
where ecoResAttributeType.EcoResAttributePCDefaultingType != EcoResAttributePCDefaultingType::None
join ecoResAttribute
where ecoResAttribute.AttributeType == ecoResAttributeType.RecId
join RecId from categoryAttribute
where categoryAttribute.Attribute == ecoResAttribute.RecId
&& categoryAttribute.Category == _component.RecId
{
switch(ecoResAttributeType.EcoResAttributePCDefaultingType)
{
case EcoResAttributePCDefaultingType::Warehouse:
attributeDefaultValueMap.insert(categoryAttribute.RecId, inventLocationId);
break;
case EcoResAttributePCDefaultingType::SalesQuantity:
attributeDefaultValueMap.insert(categoryAttribute.RecId, any2Str(salesQty));
break;
case EcoResAttributePCDefaultingType::ItemId:
attributeDefaultValueMap.insert(categoryAttribute.RecId, itemId);
break;
case EcoResAttributePCDefaultingType::ProjId:
attributeDefaultValueMap.insert(categoryAttribute.RecId, projId);
break;
}
}
return attributeDefaultValueMap;
}
}
The way I implemented the logic, was to extend the product attribute type with a new enumerated value:
