The table ds.OrderDtl has more than one record

Hi,

Scratching my head on this one for 2 weeks now! I created a code-based function to add a SO line to an existing SO and getting the below error when SO already has 1 or more lines…but only in my Production environment.

It seems to always work in two other different Dev environments and one those two is a last Sunday backup of Prod (on a different SQL Server than Prod DB). The other is a much older backup but runs on the same SQL Server than Prod DB.

Error:

Exception executing library 'SalesOrderAddQuoteLines' function 'SalesOrderAddQuoteLines':
Ice.Common.BusinessObjectException:   The table ds.OrderDtl has more than one record
   at EFx.SalesOrderAddQuoteLines.Implementation.SalesOrderAddQuoteLinesImpl.A002_RaiseExceptionAction()
   at EFx.SalesOrderAddQuoteLines.Implementation.SalesOrderAddQuoteLinesImpl.RunStep(Int32 workflowStep)
   at Epicor.Functions.FunctionBase`3.Run() in C:\_releases\ICE\ICE4.2.200.0\Source\Server\Internal\Lib\Epicor.Functions.Core\FunctionBase.cs:line 92
   at Epicor.Functions.FunctionBase`3.Run(TInput input) in C:\_releases\ICE\ICE4.2.200.0\Source\Server\Internal\Lib\Epicor.Functions.Core\FunctionBase.cs:line 75
   at EFx.SalesOrderAddQuoteLines.Implementation.SalesOrderAddQuoteLinesImpl.AdapterRun(Object[] input)
   at Epicor.Functions.FunctionBase`3.Epicor.Functions.IFunctionAdapter.Run(Object[] input) in C:\_releases\ICE\ICE4.2.200.0\Source\Server\Internal\Lib\Epicor.Functions.Core\FunctionBase.Adapter.cs:line 18
   at Ice.Internal.Task.ScheduledFunction.ExecuteFunction.RunFunction(IFunctionAdapter functionAdapter, Object[] parameters) in C:\_releases\ICE\ICE4.2.200.0\Source\Server\Internal\Task\ScheduledFunction\ExecuteFunction.cs:line 128
   at Ice.Internal.Task.ScheduledFunction.ExecuteFunction.RunProcess(Int64 instanceTaskNum, String outputFileName) in C:\_releases\ICE\ICE4.2.200.0\Source\Server\Internal\Task\ScheduledFunction\ExecuteFunction.cs:line 57

Function code (just replace myCompany and myOrderNum to test):

var soTable = new Erp.Tablesets.SalesOrderTableset();

string strCompany = myCompany;
int intOrderNum = myOrderNum;

        using (CallContext.Current.TemporarySessionCreator.SetCompanyID(strCompany).Create()) {
        
              this.CallService<Erp.Contracts.SalesOrderSvcContract>(soSvc => {
              
                soTable = new Erp.Tablesets.SalesOrderTableset();
                soTable = soSvc.GetByID(intOrderNum);
              
                if (soTable != null) 
                { 
         
                       soSvc.GetNewOrderDtl(ref soTable, intOrderNum);

                       var qDS = soTable.OrderDtl.Where(o => o.OrderNum == intOrderNum && o.OrderLine == 0 && o.Company == strCompany); //tested, only 1 record returns

                                                                     
                       foreach(var r in qDS) //tested, only 1 pass occurs
                       {       
                               r.PartNum = "MyTest";
                               r.LineDesc = "test";
                               r.IUM = "EA";
                               //r.RowMod = "A"; //no change if uncommented

                               soSvc.Update(ref soTable);  //returns "The table ds.OrderDtl has more than one record" (but in Production environment only!)
                        }  
                
                }

            });
         };

Im calling my function using the “Schedule Epicor Function” screen. I tried moving the code to a BPM (Method Pre or Post, Data In or Std), same error in Production. I also tried disabling all BPMs in case some were conflicting, same error.

All the environments mentioned are running the same version of Kinetic (2022.2.29).

I found this topic with similar issue but the opposite of my case (only for development environment, not Production) but unfortunately no solution was found.

Any help is greatly welcomed! Thanks

Use this code

Thanks Aman. However, those two lines dont compile:

soSvc.Savechanges(ref soTable); //'SalesOrderSvcContract' does not contain a definition for 'Savechanges' and no accessible extension method 'Savechanges' accepting a first argument of type 'SalesOrderSvcContract' could be found (are you missing a using directive or an assembly reference?)

Db.Savechanges(ref soTable); //'ILibraryContext' does not contain a definition for 'Savechanges' and no accessible extension method 'Savechanges' accepting a first argument of type 'ILibraryContext' could be found (are you missing a using directive or an assembly reference?)

Use only
this.Db.SaveChanges();
this.Db.Validate();

'ILibraryContext' does not contain a definition for 'SaveChanges' and no accessible extension method 'SaveChanges' accepting a first argument of type 'ILibraryContext' could be found (are you missing a using directive or an assembly reference?)

'ILibraryContext' does not contain a definition for 'Validate' and no accessible extension method 'Validate' accepting a first argument of type 'ILibraryContext' could be found (are you missing a using directive or an assembly reference?)

I think what Aman is suggesting is to bring your soSvc.Update(ref soTable); line out of the foreach, although I don’t understand the code he’s using to save - looks like saving directly to the database, instead of using the REST BO methods like we ought to… but I think that you need to update only one OrderDtl at a time… and your foreach is mostly just skipping code if there is not a new OrderDtl row in the dataset, and should never have more than one anyway

When adding a new line to a sales order in the application, it also calls ChangePartNumMaster to grab all the pricing and details for that Part - might that make a difference here? Instead of just setting the PartNum, call soSvc.ChangePartNumMaster and then set LineDesc, change IUM if necessary, then soSvc.Update

Also - and this is not a big deal - you set soTable to new Erp.Tablesets.SalesOrderTableset(); twice before calling GetByID … you don’t need either of those instantiations - and unless you need that variable later (outside of the temporary session, you could declare it in the same line you call GetByID and remove the two prior references to it..

string strCompany = myCompany;
int intOrderNum = myOrderNum;

using (CallContext.Current.TemporarySessionCreator.SetCompanyID(strCompany).Create()) {
        
  this.CallService<Erp.Contracts.SalesOrderSvcContract>(soSvc => {
              
    var soTable = soSvc.GetByID(intOrderNum);
              
    if (soTable != null) 
    { 
      soSvc.GetNewOrderDtl(ref soTable, intOrderNum);
...etc.
1 Like

If the exact same code works on one environment but fails in another I think there might be another bpm conflicting, expecting a single row and finding two(the new one and the existing one).

Aside from that, as others mentioned try getting only the row with RowMod A using FirstOrDefault, ideally you should be calling all methods the Kinetic UI calls while entering a new line, you can check those in the browser developer tools and also try using MasterUpdate instead of Update, as that is the one the ERP uses.

Thanks everybody for the feedback. However, getting the same error with this new code:

string strCompany = myCompany;
int intOrderNum = myOrderNum;


        using (CallContext.Current.TemporarySessionCreator.SetCompanyID(strCompany).Create()) {
        
              this.CallService<Erp.Contracts.SalesOrderSvcContract>(soSvc => {
              
                var soTable = soSvc.GetByID(intOrderNum);
                
                if (soTable != null) 
                { 
         
                       soSvc.GetNewOrderDtl(ref soTable, intOrderNum);

                       var newOrderDtl  = soTable.OrderDtl.FirstOrDefault(r => r.RowMod == "A");

                                                                     
                      if (newOrderDtl != null)
                      {    
                               newOrderDtl.PartNum = "MyTest";
                               newOrderDtl.LineDesc = "test";
                               newOrderDtl.IUM = "EA";
                      }  
                      soSvc.Update(ref soTable);
                
                }

            });
         };

Forgot to precise it originally: I also tried disabling all BPMs in case some were conflicting, same error. It was actually one of my first theory to validate.

MasterUpdate gives the same error.

1 Like

Can you export/import functions? Maybe try doing that from dev to prod.
Also try deleting the current one and trying with a new one.

Also, are the bpms enabled in dev? maybe try that and see what happens, just in case they weren’t really off in prod.

Yes, I believe there is a thread about this out there from Jose, or Haso, or Josh, or another one of the greats.

Export/Import was already attempted as it is how I migrated my function originally after having it tested in dev.

Just tried creating a new library+function from scratch. Same result.

And yes BPMs are all enabled in dev.

Thanks again

The only workaround I can think of, is to remove all other OrderDtl records from the tableset and leave only the new one, or even skip the GetByID.

1 Like

Do you have an idea on how to do that without deleting the potential other lines in the SO?

1 Like

Do not call GetByID, or remove them from soTable.OrderDtl, nothing will happen to the existing lines.

3 Likes

Not calling GetByID seems to do the trick. Thank you very much!!

1 Like