Row has been modified by another user - JobEntry after upgrade to 10.2

We’re using Business Objects from an external application to interact with Epicor jobs. Up until last weekend, we were on 10.0.700 and our objects worked well. We upgraded to 10.2.200.4 and are running into a real puzzler with some of them. When we re-pull the job details, the process includes unreserving material, un-engineering the job (which unreleases also), deleting the details, getting the details and then engineering, releasing and reserving at the end. The problem is that after I call GetDetails(), the next time Update() is called (after JobHeadJobEngineered in this case), it returns the “Row has been modified by another user…” error. If I don’t call GetDetails(), it doesn’t fail but doesn’t accomplish the objective either.

I also see this after splitting a job as well when it goes to re-engineer the original job.

I have enabled tracing in Epicor and done these same steps and the object calls look the same.

Any insight into what changed from Epicor 10.0 to 10.2 that would cause these issues would be appreciated.

No ideas why… just wanted to mention that after upgrading to 10.2
I’ve started hearing complaints about the “Row has been modified by another user…” error too.
In this case it’s in the shipment entry screen - appears to be “direct from job” shipments.

I’m not sure what would have changed, but I would consider looking into any Job related Method Directives you have. Usually when I have that problem, it has to do with my code updating the wrong table (temporary vs non-temporary) or utilizing the wrong kind of lock.

Thanks for the response Kevin.

We don’t have any Method Directives that pertain to Job Entry. I tried disabling the two Data Directives that we have for JobHead just to test and am still getting the error.

Sorry for the delayed response. This upgrade has us hopping around here! :slight_smile:

Did you recompile the External Applications with the latest DLL’s?

It sounds like the second update appears to the BO that it was requested before the previous Update.

Would you be able to post the code?

We did pull in the 10.2 references and recompiled. We also upgraded our application (actually a ASP.NET website) to .NET 4.6.1 to be compatible with the new objects.

I’m attaching a file that contains the relevant code.

Thanks for the help.EpicorJobActionToShare.cs (25.5 KB)

As you step through the code, which line(s) trip the error?

Line 586 in this file. When we call Update() after Engineering the job.

If I comment out the call to GetDetails() inside of ProcessChangeRev(), then it does not fail when it’s engineered.

Here is a log of the error happening. The Erp:BO:JobEntry lines seem to be the relevant ones.

Epicor Error Log.txt (9.6 KB)

We found a work-around for this error. We suspect it has to do with the Suspended Cost Roll-Ups feature that was introduced in 10.1.400 to reduce transaction time between the client/server when pulling engineering details for large MOMs. Unfortunately, we don’t benefit greatly from this “feature” and instead it causes us a lot of problems by putting jobs into a suspended state which then makes them vulnerable to being completed with incorrect costs.

Epicor has notified us that there is no way to turn off the ‘Suspend Cost Roll-Ups’ feature, but that a job shouldn’t go into that state if it is Engineered. So we took the following steps to get around the problem:

  1. In Company Configuration->Production Module, uncheck the box that Prevents changes to Engineered Jobs. This allows you to update Engineered Jobs (change demand details, reschedule, repull engineering details, change revision, etc…)
  2. We then created a BPM that sets a job as Engineered whenever it is saved, provided it is currently Unengineered & has all the necessary data to Engineer (Part Number & Required Date)
  3. We then hid the Engineer status from the JobEntry screen since it no longer provides us with useful information.

Before we went down this route, we tested and found out that MRP will run against unengineered jobs, meaning the Engineering flag has no impact on what jobs MRP considers when running.

1 Like

Are you sure about that? If you make a change to JobMtl, does MRP recognize that or is it just grabbing the most current MOM?

Mark W.

I assume that you all know what causes the “Row has been modified by another user” error, but just in case you are not familiar…

That error message is the result of the Epicor Optimistic lock checking which ensures record ownership. You are allowed to update a record only if the record did not change since the time that you read the record. Epicor does the check by comparing the SysRevID that is on the record to be updated (coming from the Client) with the SysRevID on the same record in the database. If they are the same, the record can be safely updated. If they are different, the record has changed since it was read and you are not allowed to update. The SysRevID column is a rowversion type (aka Timestamp) and it is automatically updated by SQL every time a record is updated.

The error message is always the result of the original record having been updated in between a record read and a record write. In Epicor that can occur for a few reasons:

  1. Someone else really did change the record since you read it.
  2. The processing for another record changed a related record - it happens fairly often that updating a detail line will update the header record.
  3. A post processing BPM updates a record that is already in the dataset about to be returned to the client and that record is then modified on the client and sent to the server for update. This is really just a variation on 1 but in this case the process did it to itself. Best practice for post processing BPMs that change data in the return dataset is to re-update not only the data elements being changed in the post process BPM but to also update the SysRevID to match the value in the database row for any table/record touched.

A change made (maybe 10.1.400 / 10.1.500) to allow Updatable BAQs to have the same Optimistic lock checking as the standard client may also cause integrations to get the error. Prior to that change, calls made to UpdateExt methods would intentionally not map the SysRevID from the Client data into the record being updated so the SysRevID on any Update was always the database value - so no optimistic lock check error would occur. After the change to 400/500 was made, if there was a value for SysRevID passed from the Client into UpdateExt, that value would be used on the Update attempt and it now became possible to get the lock check error. You will see this issue on Integrations that call UpdateExt repeatedly for the same base record but for updating child rows. That can trigger reason 2 above when the child update causes the header to be “touched” and now the SysRevID for the Header row in the Client data does not match the Server value. The way to resolve this issue is to not pass SysRevID into UpdateExt for any record (unless you want the optimistic lock checking performed). If using Service Connect, just don’t map/copy the SysRevID from a Read into the Update.

BTW - the SysRevID is a special type of value and can be used to see the order of updates in a database as each record updated will get a SysRevID value that is one higher than the last record updated. This can be useful in Integrations where you want to get all the records that changed since the last time you retrieved the records. For that usage, you would initially get all the records in a table and you would store off the highest SysRevID “bookmark” value. The next time you wanted to update the integration table, you use the saved bookmark value to select records in the original table that had a higher SysRevID value - those records would be new or updated.

3 Likes

We are on 10.0.7.4 and get this error on job entry - for us I believe the root cause is that we manually schedule the job, engineer it and then go to print the route card - this printing activity updates the last printed field on the job header table automatically. We then click the released check box and quite correctly get the row has been modified by another user message because what the ‘system’ has updated the job header - user clicks refresh, job header updated and hopefully they remember to click released.

This is exactly in line with what Rich has outlined - it is disappointing that this can still happen in later releases.

@Mark_Wonsil – Thanks for checking on this! My original testing was not complete. Actually, the impact on MRP is mixed. I just tested this by adding both a make part & purchase part as material to an un-engineered job & scheduled the job (which requires the job to be engineered, but I un-engineered afterwards.) I then ran MRP against the part the job was for (love that filter capability), and also ran Generate Suggestions. MRP did not create a purchase suggestion for the purchase part but did create an unfirm job for the make part as well as purchase suggestions to satisfy the make part’s material demand.

@Richard Riley – Thanks for the detailed explanation on optimistic locking functionality! I believe that is what is causing our issue, the trigger being the ‘Suspend Cost Roll-Up’ functionality modifying the record underneath us.