Mass Checkout Efx

So i am working on a application that will pull in all of the parent parts that a user will want to update a single mtl on ie pull as view as data on the parts. I have created a user interface for them to query and review the changes that they will make. Once that is done they check the check boxes they need to update ie pullas. see screenshot for UI. I am using a BAQ to feed the UI and a function in the background same baq.

My issue is related to using the Mass Checkout BO in a function. i am not sure how to get the row to be added in the dataset i think im close but from what i can see there is no data going into my dataset. I know it passes the first if statement as it is the same baq running in the ui. Below the Ui is my problematic Function. this function is feed from another function in the library first. my dynamic query one.

if (ListOfPartsTOUpd.Tables.Count >= 0)
{
  //variables
  string groupID = "ProdCon";

  //Call Engworkbench service
  this.CallService<Erp.Contracts.EngWorkBenchSvcContract>(engWBSVC =>
    {
      //declare tablesets
     //var engDS = new Erp.Tablesets.EngWorkBenchTableset();
      var engDSMass = new Erp.Tablesets.MassCheckoutTableset();
     
      
      try
      {
        foreach (DataRow partListtoCheckOut in ListOfPartsTOUpd.Tables["Results"].Rows)
          {
            //engWBSVC.GetMassCheckout(ref engDSMass,groupID,partListtoCheckOut["PartMtl_PartNum"].ToString());
             
             var massCheckoutTS = (Erp.Tablesets.MassCheckoutRow)engDSMass.MassCheckout.NewRow();
             massCheckoutTS.Company = "SEC";
             massCheckoutTS.CreateNewRev = false;
             massCheckoutTS.GroupID = groupID;
             massCheckoutTS.PartNum = partListtoCheckOut["PartMtl_PartNum"].ToString();
             massCheckoutTS.RevisionNum = partListtoCheckOut["PartMtl_RevisionNum"].ToString();
             massCheckoutTS.SourcePartNum = partListtoCheckOut["PartMtl_PartNum"].ToString();
             massCheckoutTS.SourceRevisionNum = partListtoCheckOut["PartMtl_RevisionNum"].ToString();
             massCheckoutTS.RevShortDesc = "Import Rev";
             massCheckoutTS.SysRowID = Guid.NewGuid();
             massCheckoutTS.RowMod = "A";
            
          }
          
          engWBSVC.ProcessMassCheckout(ref engDSMass);
          int numberofrecords = engDSMass.MassCheckout.ToList().Count;
          Ice.Diagnostics.Log.WriteEntry("Test Complete" + " " + numberofrecords.ToString());
      }
      catch
      {
      } 
    });  
}
else
{
  return;
}
      

Not sure if I am following but don’t you want to check count of the results table not the count of tables?

something like

ListOfPartsTOUpd.Tables[“Results”].Rows.Count >= 0

1 Like

I have updated and made that change on the check i have 751 rows in the data table results so that is not really my issue. the issue is down below i believe in my foreach loop. Just not sure.

General pattern with these things is:

this.CallService<Erp.Contracts.ModuleSvcContract>(svc =>
{
            //Create tableset, skip if one is already provided by BPM or whatnot.
            var ts= new Erp.Tablesets.WhateverTableset();
            //NewRow creats a row object with the fields from your table.
            //It will usually prepopulate some fields like Company.
            //It does not add it to the table.
            var newRow = ts.Table.NewRow();
            //Set your fields. Don't forget the Rowmod
            newRow.Field1 = stuff
            newRow.Field2 = moreStuff
            newRow.Rowmod = "A";
            //Add the new row to your Tableset
            ts.Table.Add(newRow);
            //run update, though it looks like the specific method here is "ProcessMassCheckout"
            //Still, same basic design pattern.
            svc.Update(ref ts)
}

Note that some BO’s get angry if you try to add or update multiple rows. Generally the Engineering modules will let you do it all in one go. So you’d put the Update call outside the loop.

Thanks i was missing the add to the data table which now i am checking out 751 parts lol. Once i get that done i will be updating specific mtl and seq to update parts then check in. i will share my library once im done. here is the work in process code right now. Found an error for parts already checked out need to account for that first lol

if (ListOfPartsTOUpd.Tables["Results"].Rows.Count >= 0)
{
  Ice.Diagnostics.Log.WriteEntry(ListOfPartsTOUpd.Tables["Results"].Rows.Count.ToString());
  //variables
  string groupID = "ProdCon";

  //Call Engworkbench service
  this.CallService<Erp.Contracts.EngWorkBenchSvcContract>(engWBSVC =>
    {
      //declare tablesets
     //var engDS = new Erp.Tablesets.EngWorkBenchTableset();
      var engDSMass = new Erp.Tablesets.MassCheckoutTableset();
     
      
      try
      {
          foreach (DataRow partListtoCheckOut in ListOfPartsTOUpd.Tables["Results"].Rows)
          {
             //engWBSVC.GetMassCheckout(ref engDSMass,groupID,partListtoCheckOut["PartMtl_PartNum"].ToString());
             
             var massCheckoutTS = (Erp.Tablesets.MassCheckoutRow)engDSMass.MassCheckout.NewRow();
             massCheckoutTS.Company = "SEC";
             massCheckoutTS.CreateNewRev = false;
             massCheckoutTS.GroupID = "ProdCon";
             massCheckoutTS.PartNum = partListtoCheckOut["PartMtl_PartNum"].ToString();
             massCheckoutTS.RevisionNum = partListtoCheckOut["PartMtl_RevisionNum"].ToString();
             massCheckoutTS.SourcePartNum = partListtoCheckOut["PartMtl_PartNum"].ToString();
             massCheckoutTS.SourceRevisionNum = partListtoCheckOut["PartMtl_RevisionNum"].ToString();
             massCheckoutTS.RevShortDesc = "Import Rev";
             massCheckoutTS.SysRowID = Guid.NewGuid();
             massCheckoutTS.RowMod = "A";
             Ice.Diagnostics.Log.WriteEntry(massCheckoutTS.ToString());
             //Ice.Diagnostics.Log.WriteEntry("Part Number:" + " " + partListtoCheckOut["PartMtl_PartNum"].ToString());
             engDSMass.MassCheckout.Add(massCheckoutTS);
          }
         
          engWBSVC.ProcessMassCheckout(ref engDSMass);
          //int numberofrecords = engDSMass.MassCheckout.ToList().Count;
          //Ice.Diagnostics.Log.WriteEntry("Test Complete" + " " + numberofrecords.ToString());
      }
      catch(Exception e)
      {
        Ice.Diagnostics.Log.WriteEntry("Catch:" + " " + e.Message);
      } 
    });  
}
else
{
  return;
}
      

So im to a new point on my wonderful process here i have the mass checkout working like it should may need to be reviewed and tweaked for more error handling.

I now have records to update i just cant seem to get the right rows to update getting the error message. I guessing i have to tie something together with sysrowid but not sure.

Catch: Record for update was not found by its SysRowID value [00000000-0000-0000-0000-000000000000].

so here is my last part of my code i believe that i am having an issue with.

if (partUpdateIn.Tables["Results"].Rows.Count >= 0)
{
  this.CallService<Erp.Contracts.EngWorkBenchSvcContract>(engWBSVC =>
    {
      //declare tablesets
      var dataUpDateDS = new Erp.Tablesets.EngWorkBenchTableset();
      //var updateData = new Erp.Tablesets.EngWorkBenchTableset();
      //var updateECORev = new Erp.Tablesets.EngWorkBenchTableset(); ---remove
      //declare more varaiables in service
      string ipGroupID = "ProdCon";
      bool ipProposedPullAsAsm = inPullAs;
      bool ipProposedViewAsAsm = inViewAs;
      bool continueProcessingOnError = false;
      bool rollBackParentOnChildError =  true;
      bool errorOccurred = false;
      string msg = "";
      
      // start doing things here. 
      try
      {
          //get by id and fill the data set to get all of the information ready to rock and roll. 
          dataUpDateDS = engWBSVC.GetByID(ipGroupID);

          foreach(DataRow partUpdate in partUpdateIn.Tables["Results"].Rows)
          {
             var updateDS = (Erp.Tablesets.ECOMtlRow)dataUpDateDS.ECOMtl.NewRow();
             updateDS.Company = this.callContextClient.CurrentCompany;
             updateDS.GroupID = ipGroupID;
             updateDS.PartNum = partUpdate["PartMtl_PartNum"].ToString();
             updateDS.RevisionNum = partUpdate["PartMtl_RevisionNum"].ToString();
             updateDS.MtlPartNum = partUpdate["PartMtl_MtlPartNum"].ToString();
             updateDS.MtlSeq = Convert.ToInt32(partUpdate["PartMtl_MtlSeq"]);
             updateDS.RowMod = "U";
             //Ice.Diagnostics.Log.WriteEntry(updateDS.ToString());
             dataUpDateDS.ECOMtl.Add(updateDS);
             //updateData.ECORev.Add(updateECORevds);              
          }

            
            //BufferCopy.Copy(dataUpDateDS.ECOMtl,updateData.ECOMtl);
            
            Ice.Diagnostics.Log.WriteEntry(dataUpDateDS.ToString());
            
            //engWBSVC.CheckECOMtlPullAsAsm(ipProposedPullAsAsm,out msg,out msg,ref updateData);
            //engWBSVC.ChangeECOMtlPullAsAsm(ref dataUpDateDS); 
            //engWBSVC.CheckECOMtlViewAsAsm(ipProposedViewAsAsm, ref updateData);
            //BufferCopy.Copy(dataUpDateDS.Tables[],updateData);
            //Ice.Diagnostics.Log.WriteEntry(dataUpDateDS.ToString());
            
            //sends update to the BO to update the data records.
            engWBSVC.Update(ref dataUpDateDS);
            
           
          
      }
      catch(Exception e)
      {
        Ice.Diagnostics.Log.WriteEntry("Catch:" + " " + e.Message);
      } 
    });  

}
else
{
   Ice.Diagnostics.Log.WriteEntry("nothing to do quiting this process");
  return;
}

I don’t have the answer, but a couple of things:

  • sometimes Epicor wants both rows in the datatset (unchanged row with RowMod=‘’ and the updated row with RowMod=‘U’). I can see you’re already on this from the BufferCopy you commented out there in your code
  • instead of using the NewRow() to insert the records, have a look at the GetNew methods (possibly GetNewEcoMtl)
    Hope that helps.

If you have a GetNew() method, it usually combines the lower level NewRow and Add methods in one nice package. Downside is that it just adds the mostly-blank row to the dataset, so you have to go find it if you want to

example.

//LINQ query to find the row added by GetNew
var newRow = ts.table.Where(x => x.Rowmod = "A")
//Then you can update the fields
newRow.Field1 = stuff
newRow.Field2 = moreStuff

This is probably obvious stuff, but just throwing it out there for the lurkers and newbies.

So i have tried this with widget and still not getting the data to update if feel like i am missing a flag that gets set. any other assistance. I have checked the trace a few times by making the changes once or twice. @klincecum or @josecgomez any other ideas.

Have you run through the process of adding a row in BL Tester? That’s the easiest way to tell what, exactly, the system expects from the dataset/tableset.

unfortunately i cant get mine to work right not sure how to set it up.

BL Tester is just a stripped down frontend for the client DLL’s. So you just have to set it to the same URI as your client deployments, point it to your LocalClients folder and set the connection/login info as needed.

There’s also the REST Swagger page, which I probably use more often these days.

You’re basically running around blind without either of those tools. Behavior is not consistent across, or even within, modules.

1 Like

thanks for the information now. I was able to get the process down. Only issue is getting it in the right code can anyone help with that?

  1. do a get by id for the ECO Group
  2. Filter the ECORev table to where approved is false and checked out is true.
  3. Update my ECOMTL data part mtlseq and mtlpartnum set pull as, view as from variable.

let me know your thoughts on this. I will have a start on this tomorrow but could use some assistance thanks for you all of you patient and help.

This is next rev of code that i have. Will need some more polishing but i think im getting there. but im close

if (partUpdateIn.Tables["Results"].Rows.Count >= 0)
{
  this.CallService<Erp.Contracts.EngWorkBenchSvcContract>(engWBSVC =>
    {
      //declare tablesets
      var dataUpDateDS = new Erp.Tablesets.EngWorkBenchTableset();
      var getByID = new Erp.Tablesets.EngWorkBenchTableset();
      //declare more varaiables in service
      string ipGroupID = "ProdCon";
      string ecoGroupDesc = "Production Control  Where Used";
      string company = this.callContextClient.CurrentCompany;
      bool ipProposedPullAsAsm = inPullAs;
      bool ipProposedViewAsAsm = inViewAs;
      bool continueProcessingOnError = false;
      bool rollBackParentOnChildError =  true;
      bool errorOccurred = false;
      string altMethod = "";
      string processMfgID = "";
      string msg = "";
      
      // start doing things here. 
      try
      {
      
        //get by id and fill the data set to get all of the information ready to rock and roll.
         getByID = engWBSVC.GetByID(ipGroupID); 
         
             
        
          //Not doing this either this is bad need to use get by ID!!!!!
        var ecoGroup = (Erp.Tablesets.ECOGroupRow)dataUpDateDS.ECOGroup.NewRow();
        ecoGroup.Company = company;
        ecoGroup.GroupID = ipGroupID;
        ecoGroup.Description = ecoGroupDesc;
          
        dataUpDateDS.ECOGroup.Add(ecoGroup);
            

          
          
          foreach (DataRow partUpdate in partUpdateIn.Tables["Results"].Rows)
          {

             var ecoRev = (Erp.Tablesets.ECORevRow)dataUpDateDS.ECORev.NewRow();
             var ecoMtl = (Erp.Tablesets.ECOMtlRow)dataUpDateDS.ECOMtl.NewRow();
             
             //Updates ECO Rev Table from whereUsed BAQ!!!!!!
             //this is bad not doing neeed to use get by ID !!!!!
             ecoRev.Company = company;
             ecoRev.PartNum = partUpdate["PartMtl_PartNum"].ToString();
             ecoRev.Approved = false;
             ecoRev.CheckedOut = true;
             ecoRev.RowMod = "U";
             
             //dataUpDateDS.ECORev.Add(ecoRev);
             
             
             //update date ECO MTL table from where used BAQ!!!!!!
             
             
             ecoMtl.Company = company;
             ecoMtl.GroupID = ipGroupID;
             ecoMtl.PartNum = partUpdate["PartMtl_PartNum"].ToString();
             ecoMtl.MtlSeq = Convert.ToInt32(partUpdate["PartMtl_MtlSeq"]);
             ecoMtl.PullAsAsm = ipProposedPullAsAsm;
             ecoMtl.ViewAsAsm = ipProposedViewAsAsm;
             ecoMtl.RowMod = "U";

             dataUpDateDS.ECOMtl.Add(ecoMtl);
             
          }   
         
         
         BufferCopy.Copy(getByID,dataUpDateDS);
         
         
         engWBSVC.Update(ref dataUpDateDS);
          
          
          
      }
      catch(Exception e)
      {
        Ice.Diagnostics.Log.WriteEntry("Catch:" + " " + e.Message);
      } 
    });  

}
else
{
   Ice.Diagnostics.Log.WriteEntry("nothing to do quiting this process");
  return;
}

Not sure why you think it’s a bad idea to populate a TableSet from a source other than GetBy. Most of the time I’m running a LINQ query to grab the data directly from the DB, but a BAQ is cool too. Probably a bit less prone to breaking, though it takes longer to build BAQ than write a query.

When people say “use the BO, not the DB” they mean don’t write to the DB. Reads are fine.

In terms of your code, you’re casting a lot. You should probably do one for the whole ts at the beginning.

var ewbts = (Erp.Tablesets.EngineeringWorkbenchTableSet)dataUpDateDS;

Regardless, LINQ is what you’re going to want to use to filter your data.

var checkedOutRevs = ewbts.ECORev.Where(er => 
                                        er.Approved == false &&
                                        er.CheckedOut == true);
1 Like

@jtownsend Thanks for you help and patients with me i have finally have it working for start to finish completely. as a function i have to update and apply codes and do some error handling. need to check if it is already checked just update don’t try to check it out again. Without further ado here is the completed update of an ecomtl.

Also i am open for some code review. and possible moving this completed function and application to the share and caring post.

if (partUpdateIn.Tables["Results"].Rows.Count >= 0)
{
  this.CallService<Erp.Contracts.EngWorkBenchSvcContract>(engWBSVC =>
    {
      //declare tablesets
      //var dataUpDateDS = new Erp.Tablesets.EngWorkBenchTableset();
      var getByID = new Erp.Tablesets.EngWorkBenchTableset();
      //declare more varaiables in service
      string ipGroupID = "ProdCon";
      string ecoGroupDesc = "Production Control  Where Used";
      string company = this.callContextClient.CurrentCompany;
      string altMethod = "";
      string processMfgID = "";
      string msg = "";
      bool approved = false; 
      bool ipProposedPullAsAsm = inPullAs;
      bool ipProposedViewAsAsm = inViewAs;
      bool continueProcessingOnError = false;
      bool rollBackParentOnChildError =  true;
      bool errorOccurred = false;
      bool checkedOut = true;

      
      // start doing things here. 
      try
      {
      
        //get by id and fill the data set to get all of the information ready to rock and roll.
         getByID = engWBSVC.GetByID(ipGroupID); 
         
         if(getByID != null)
         {
         
          foreach (DataRow partUpdate in partUpdateIn.Tables["Results"].Rows )
          {
              var checkedOutRevs = getByID.ECORev.Where(er => er.Approved == approved && 
                                                             er.CheckedOut == checkedOut).FirstOrDefault();
                                                             
                                                             
              var ecoMtl = getByID.ECOMtl.Where(em => em.Company == company && 
                                                      em.GroupID == ipGroupID &&
                                                      em.PartNum == partUpdate["PartMtl_PartNum"].ToString() &&
                                                      em.MtlSeq == Convert.ToInt32(partUpdate["PartMtl_MtlSeq"]) && 
                                                      em.MtlPartNum == partUpdate["PartMtl_MtlPartNum"].ToString()).FirstOrDefault();
              ecoMtl.PullAsAsm = ipProposedPullAsAsm; 
              ecoMtl.ViewAsAsm = ipProposedViewAsAsm;                                     
              ecoMtl.RowMod = "U";                                                                                              
                                                      
              engWBSVC.Update(ref getByID);
           }
           
           this.ThisLib.ApproveAndCheckInParts();
         }
         else
         {
            Ice.Diagnostics.Log.WriteEntry("Catch:" + " " + "Have Data Good");
         }

      }
      catch(Exception e)
      {
        Ice.Diagnostics.Log.WriteEntry("Catch:" + " " + e.Message);
      } 
    });  
}
else
{
   Ice.Diagnostics.Log.WriteEntry("nothing to do quiting this process");
  return;
}

Some thoughts, I’m always learning to so.

  • Not sure if you should add using{} around your service call or not.
  • Your writing to the log, but if the data is good then why allude that it has hit the catch?
  • Consider using pre-processor directives for debugging then you can turn them on or off as you require. Here’s a comprehensive link, not sure if all of them work, but DEBUG definitely does and you can turn it on and off in directives and functions. C# preprocessor directives - C# | Microsoft Learn. If you are using widgets you can also make messages show based on the debug setting as well. Here is another link on the debug option https://www.epiusers.help/t/bpm-logging-snippet/93004/7

Hope that helps…