1
votes

I am trying to create a process that will run daily that will import records from another database as Inventory Items. In order to do this, I need to create an extension of the InventoryItemMaint graph (to give me my custom action), as well as an extension of the InventoryItem DAC (to give me a custom field). I have tried to follow the guidelines laid out specifically in the T-300 manual to do this.

Here is the code for my InventoryItemMaint extension:

    namespace PX.Objects.IN
{

  public class InventoryItemMaint_Extension:PXGraphExtension<InventoryItemMaint>
  {

    public PXAction<PX.Objects.IN.InventoryItem> DailyOnixImport;

        [PXButton(CommitChanges = true)]
        [PXUIField(DisplayName = "Daily Onix Import")]
        protected void dailyOnixImport()
        {           
            var invItemMaintExtInstance = Base.GetExtension<InventoryItemMaint_Extension>();

            string todaysDate = DateTime.Today.ToString("MM/dd/yyyy");                        

            foreach (STOnixItem currentOnixItem in PXSelect<STOnixItem,
                        Where<STOnixItem.addedDate, Equal<Required<STOnixItem.addedDate>>>>
                        .Select(this.Base, todaysDate))
            {                                
                InventoryItem currentInventoryItem = invItemMaintExtInstance.Base.Item.Current;

                PXCache inventoryItemCache = invItemMaintExtInstance.Base.Item.Cache;
                InventoryItemExt inventoryItemExtension = inventoryItemCache.GetExtension<InventoryItemExt>(currentInventoryItem);

                inventoryItemCache.Clear();

                currentInventoryItem.InventoryCD = currentOnixItem.ISBN13;                

                currentInventoryItem.Descr = currentOnixItem.Title;
                currentInventoryItem.ItemClassID = currentOnixItem.ItemClass;
                currentInventoryItem.RecPrice = decimal.Parse(currentOnixItem.MSRP);
                currentInventoryItem.BasePrice = decimal.Parse(currentOnixItem.DefaultPrice);
                currentInventoryItem.BaseItemWeight = decimal.Parse(currentOnixItem.Weight);
                currentInventoryItem.WeightUOM = "POUND";
                currentInventoryItem.ImageUrl = currentOnixItem.ImageLink;

                //Assigning to the custom DAC Extension
                inventoryItemExtension.UsrFromOnixFile = currentOnixItem.FromFile;

                inventoryItemCache.Update(currentInventoryItem);
                Base.Actions.PressSave();

            }
        }
  }
}

I am currently getting an error that reads:

Error: Another process has updated the 'InventoryItem' record. Your changes will be lost.

And here is the error trace text:

9/20/2018 3:26:05 PM Error: Error: Another process has added the 'InventoryItem' record. Your changes will be lost.

at PX.Data.PXCache1.PersistInserted(Object row) at PX.Data.PXCache1.Persist(PXDBOperation operation) at PX.Data.PXGraph.Persist(Type cacheType, PXDBOperation operation)
at PX.Data.PXGraph.Persist() at PX.Objects.IN.InventoryItemMaint.Persist() at PX.Data.PXSave1.d__2.MoveNext() at PX.Data.PXAction1.d__31.MoveNext() at PX.Data.PXAction1.d__31.MoveNext() at PX.Data.PXActionCollection.PressSave(PXAction caller) at PX.Objects.IN.InventoryItemMaint_Extension.dailyOnixImport() at PX.Data.PXAction1.<>c__DisplayClass3_0.<.ctor>b__0(PXAdapter adapter) at PX.Data.PXAction1.a(PXAdapter A_0) at PX.Data.PXAction1.d__31.MoveNext() at PX.Data.PXAction`1.d__31.MoveNext() at PX.Web.UI.PXBaseDataSource.tryExecutePendingCommand(String viewName, String[] sortcolumns, Boolean[] descendings, Object[] searches, Object[] parameters, PXFilterRow[] filters, DataSourceSelectArguments arguments, Boolean& closeWindowRequired, Int32& adapterStartRow, Int32& adapterTotalRows) at PX.Web.UI.PXBaseDataSource.ExecuteSelect(String viewName, DataSourceSelectArguments arguments, PXDSSelectArguments pxarguments)

I have done a lot of searching around StackOverflow and other places, but haven't found any answers that seem to address my issue exactly. Tweaks I've made have resulted in other errors like variations on what I'm getting now (another process added vs another process updated) and MoveNext errors.

If anyone is able to help me out, I would be very appreciative.

4
Is there a purpose to 'inventoryItemCache.Clear()' ? It seems inappropriate to clear the cache at that moment. - Hugues Beauséjour
@HB_ACUMATICA I had that there because at first I had initially created the currentInventoryItem and inventoryItemCache objects outside of the foreach loop and used the Clear() at the beginning of each loop to reset it. While it now seems redundant, I can't imagine it messing anything up. Just to confirm, I commented out the clearing of the cache and updated the site. Same result. - Case Silva

4 Answers

1
votes

guys, I faced the same exception in the EmployeeMaint when we tried to update one of the complex fields in the DAC.

enter image description here

baseAction() throws this exception even though no code runs before saving. I followed your suggestions @Hugues Beauséjour and found out that reason may be: enter image description here

Means, the cache is dirty. So, to resolve my issue I just needed to clear the cache for the object that throws the exception. IN my case the exception was:

Update employee class error - Another process has added the 'VendorPaymentMethodDetail' record. Your changes will be lost

So I needed to clear the cache for VendorPaymentMethodDetail:

Base.Caches<VendorPaymentMethodDetail>().Clear();

Be careful to clear the cache in the case you need to read from the cache during your code after you clear the cache. In that case, you need to copy objects from the cache before cleaning and used this copy afterwards.

Hope it will be helpful to someone.

0
votes

There seem to be a logical flaw in the code. You are updating the same current object in a loop. This serve no purpose as it will always overwrite with the last item returned by the loop. Invoking the Save action in a loop can also lead to errors if you're not careful.

As I mentioned in the comment, clearing the cache seems wrong. You want to keep the current data there. When you call clear you're invalidating the main document of the graph, that will lead to errors.

Changing fields closely tied to the key like InventoryCD can lead to the document being cleared and invalidated. If you have to modify key fields considering inserting new record instead of updating the current one.

There are other changes I would recommend.

Code:

// Consider replacing the default namespace to avoid conflicts
namespace MyNamespace
{
  public class InventoryItemMaint_Extension:PXGraphExtension<InventoryItemMaint>
  {
    public PXAction<PX.Objects.IN.InventoryItem> DailyOnixImport;

    // '(CommitChanges = true)' is not necessary
    [PXButton]
    [PXUIField(DisplayName = "Daily Onix Import")]
    protected void dailyOnixImport()
    {           
      InventoryItemMaint_Extension invItemMaintExtInstance = Base.GetExtension<InventoryItemMaint_Extension>();

      string todaysDate = DateTime.Today.ToString("MM/dd/yyyy");                        

      // You need to rethink that 'foreach' logic
      STOnixItem currentOnixItem in PXSelect<STOnixItem,
                                    Where<STOnixItem.addedDate, Equal<Required<STOnixItem.addedDate>>>>.Select(Base, todaysDate);    

      // You can access Base directly, no need to fetch it from the extension
      InventoryItem currentInventoryItem = Base.Item.Current;

      // Consider using more null check
      if (currentOnixItem != null && currentInventoryItem != null)
      {
        // Consider using similar names for similar variables
        InventoryItemExt currentInventoryItemExt = currentInventoryItem.GetExtension<InventoryItemExt>();

        // Avoid setting key related fields like InventoryCD when updating
        currentInventoryItem.Descr = currentOnixItem.Title;
        currentInventoryItem.ItemClassID = currentOnixItem.ItemClass;
        currentInventoryItem.RecPrice = decimal.Parse(currentOnixItem.MSRP);
        currentInventoryItem.BasePrice = decimal.Parse(currentOnixItem.DefaultPrice);
        currentInventoryItem.BaseItemWeight = decimal.Parse(currentOnixItem.Weight);
        currentInventoryItem.WeightUOM = "POUND";
        currentInventoryItem.ImageUrl = currentOnixItem.ImageLink;

        currentInventoryItemExt.UsrFromOnixFile = currentOnixItem.FromFile;

        // You fetched the item from the DataView 
        // you can update it in the DataView too.
        Base.Item.Update(currentInventoryItem);

        // Is it really needed to save here?
        // This coupled with cache clearing and the loop updating 
        // the same record triggers the error in your question.
        Base.Actions.PressSave();
      }
    }
  }
}
0
votes

I found another reason that can cause the same exception: enter image description here

That may happen when you select from the same table in different caches, use one for select and another for insert. for instance:

we have a view:

 public PXSelect<MPEmployeeWorkSchedule, Where<MPEmployeeWorkSchedule.employeeID, Equal<Current<MPEmployeeTermination.employeeID>>>> EmployeeWorkSchedule;

and in event we have code's segment: enter image description here we use the same DAC as in the view above and then we insert into the view:

EmployeeWorkSchedule.Cache.Insert(workSchedule);

Afterwards Persist() throws exception.

Solution was again to create another view instead of query and clear the cache: enter image description here enter image description here

This may be helpful, I hope.

0
votes

Want to share one more way of finding the same error message. In my case the problem was in structure of database. In order to fix the error message, I've did those steps:

  1. dropped my table
  2. generated sql script from existing table
  3. deleted not needed columns
  4. copy/pasted existing columns, with replacing names

As outcome, I've got error message to disappear.