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.
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?)
'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.
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.
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.