Programmatically save part configurator (or any form)

So I’ve been trying to save a part configuration programmatically to trigger a data directive on PcInputValue with BO but I couldn’t get a DataSet (ConfigurationSummaryDataSet) to use the saving method of ConfiguratorRuntimeSvcContract.

I found a solution which is very interesting. It opens for me all kind of doors from now on. Thanks to the power of reflection. I hope it is an acceptable way of solving my problem!

I put my code in Configurator User Defined Methods in a method of Client type but I assume it can be used in any form customization. I have tried to customize a form yet.

var oTrans = this.ServerUD.Trans;

//This is why I assume it could work from any customization
var epiBaseForm = oTrans.GetType().GetProperty("EpiBaseForm").GetValue(oTrans,null);
	
var performUpdateMethod = epiBaseForm.GetType().GetMethod("performUpdate",System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);

performUpdateMethod.Invoke(epiBaseForm , new object[]{false});	

@timshuwy

UPDATE:
this code actually causes an error.

What is the ServerUD referencing?

why don’t you create a document rule on your configuration that updates a field in the order Del or Quoted. Then you can create a data directive on either one of these tables to achieve what you need

Sorry for the late answer @mattpeters. It is referencing UserDefinedFunctionsLib which has :

    public EpiTransaction Trans;
    public Guid TestID;
    public string ConfigID;
    private ConfigurationRuntimeExtImpl configurationExtBO;
    public event UserDefinedFunctionsLib.UpdateDataFromServer OnUpdateDataFromServer; 

@Christian_Pouchoulen
Will the document rule save the input values in PcInputValues ?

Here is what you have to do. On this example, I created a Data Directive for the pcValueSet table and are saving all inputs in a comments field in the sales order line. The table that keeps all cfg inputs is not available to BPMs / BAQs. I hope this helps you

var pcValueSet = (from row in ttPcValueSet where row.Company == Session.CompanyID select row).FirstOrDefault();
if(pcValueSet != null)
  {
    string strComments = "";
    string strXML = pcValueSet.FieldValues;
    
    var dbPCValueGrp = (from row in Db.PcValueGrp 
                        where row.Company == Session.CompanyID && row.GroupSeq == pcValueSet.GroupSeq && row.HeadNum == pcValueSet.HeadNum && row.RelatedToTableName == "OrderDtl"
                        select row).FirstOrDefault();
    if(dbPCValueGrp != null)
    {     
       System.Xml.Linq.XDocument doc = System.Xml.Linq.XDocument.Parse(strXML);
       IEnumerable<System.Xml.Linq.XElement> fieldVal = from el in doc.Descendants() select el;   
                             
        foreach (System.Xml.Linq.XElement f in fieldVal)
        {
          if((f.Name.ToString().Contains("ComboBox") || f.Name.ToString().Contains("TextBox")) && f.Value != "")
          {
            
            string strName = "";            
            int iPosition = strXML.IndexOf("fieldRule:" + f.Name);            
            string []sArr = strXML.Substring(iPosition, 100).Split('|');
            if(sArr.Length > 1)
            {   
              strName = sArr[1];              
            } 
            else
            {
              strName = f.Name.ToString();
            }
            strComments += strName + ": " + f.Value + System.Environment.NewLine;
          }
        }       
        
        var dbOrderDtl = (from row in Db.OrderDtl where row.Company == Session.CompanyID && row.SysRowID == dbPCValueGrp.RelatedToSysRowID select row).FirstOrDefault();
        if(dbOrderDtl != null)
          {
              dbOrderDtl.OrderComment = strComments;
              Db.Validate();
          }
                           
    }
 }
1 Like

@Christian_Pouchoulen thanks for your quick reply I did something similar to that But I really needed to have that field saved in PcInputValues which are actually accessible via External BAQ :slight_smile: but is not modifiable since it is filled by stored procedures I think.

Here is my code, It’s not that bad but in this case I only have one field to change and save. But if I have a lot lot more I would prefer a Different solution. So by changing the xml string, the stored procedures do what I need and I get my data directive on PcInputValues triggered.

var groupSeq = Context.OrderNumber > 0 ? System.Convert.ToInt32(Inputs.orderGroupSeq.Value) : System.Convert.ToInt32(Inputs.quoteGroupSeq.Value);

try {
using(var txScope = IceContext.CreateDefaultTransactionScope()) {

var inputValues = Db.PcValueSet.Where(pc => pc.ConfigID == Context.ConfigurationID && pc.GroupSeq == groupSeq).FirstOrDefault();

var xml = inputValues.FieldValues;

var doc = new System.Xml.XmlDocument();

doc.LoadXml(xml);

doc.DocumentElement.SelectNodes("//_CfgPage1Table/_CfgConfiguratorPage1Row/status").Item(0).InnerText = "Configuring";

doc.DocumentElement.SelectNodes("//_CfgConfiguratorPage1Table/_CfgConfiguratorPage1Row/manualRequestMessage").Item(0).InnerText = Inputs.manualRequestMessage.Value;

inputValues.FieldValues = doc.OuterXml;

Db.Validate();

txScope.Complete();
}
} catch (Exception e) {
Epicor.Customization.Bpm.InfoMessage.Publish("There was an error trying to update the state. Please press Save button to try again.", Ice.Common.BusinessObjectMessageType.Error, Ice.Bpm.InfoMessageDisplayMode.Individual);
}
1 Like

The table is available and readable in 10.2.500 with a BAQ. You can use the code below to loop through all your controls and values. In any version you can access these tables this way and can access in a normal BAQ if on 10.2.500. If you are on a sub 10.2.500 version you can use an external BAQ that uses PCInputValue without doing anything special to see the fields since it is a view over these tables.

foreach (var PCValueSetRecord in (from row in Db.PcValueSet where row.GroupSeq == GroupSeq
select row))
{
  string XMLValue = PCValueSetRecord.FieldValues;
  XmlReader xr = XmlReader.Create(new StringReader(XMLValue));
  var xMembers = from members in XElement.Load(xr).Elements() select members;
  var children = xMembers.Elements();
  var AllElements = children.Where(e => e.Value != string.Empty).Select(e => new 
  {
    Name = e.Name.ToString(), // Control Name
    Value = e.Value.ToString(), // Input Value
    Type = (string)e.Attribute("Type") // Type (i.e. System.Int32)
  }).ToList();
2 Likes

Hello @danbedwards thanks for your answer however I dont have any difficulty getting data from a configuration. I am already using am external BAQ for PcInputValues. I want to trigger the save button of the toolbar. I found the code in EpiBaseForm that does that but when I call it with reflection I get an error.

So you just are looking to mimic what the toolbar Save is doing in a Button click?

yes I was able to save indirectly to PcInputValues through the xml string of input values in PcValuesSet but I don’t really like this solution. if I have lets say 100 inputs it’s a lot of work. I haven’t investigated yet the infragistics toolbar manager properly. Maybe I’ll find my answer there.

Sorry, I am not following. Why not just click the save button?

Because I’m doing stateful processing and I cannot trust the user that he would click save. That’s why I want to programmatically save the configurator.