3

I am creating a tool that allows "User A" to enter the known information about an material for it to be setup and then "User B" to massage that data into approved standards before loading in as a valid part. In this tool, I have to call upon multiple graphs to create various auxiliary data points. All of these work, until I reached Kit Specifications.

The format of my various calls to each graph for processing is:

INItemSiteMaint siteGraph = PXGraph.CreateInstance<INItemSiteMaint>();

INItemSite iNItemSite = new INItemSite();
iNItemSite.InventoryID = item.InventoryID;
iNItemSite.SiteID = newItem.SiteID;
...
siteGraph.itemsitesettings.Insert(iNItemSite);
siteGraph.Actions.PressSave();

For the INKitSpecMaint graph, I can find an existing kit via PXSelect, but Search does not find a record...

Works:

INKitSpecMaint kitGraph = PXGraph.CreateInstance<INKitSpecMaint>();
kitGraph.Hdr.Current = PXSelect<INKitSpecHdr, Where<INKitSpecHdr.kitInventoryID, Equal<Required<INKitSpecHdr.kitInventoryID>>>,
    OrderBy<Desc<SSINSetup.createdDateTime>>>
    .SelectSingleBound(this, null, asset.AssetID);

Does not work (returns null when the above returns a kit):

iNKitSpecHdr = kitGraph.Hdr.Search<INKitSpecHdr.kitInventoryID>(asset.AssetID);

Using this methodology to create the records:

iNKitSpecHdr = new INKitSpecHdr();
iNKitSpecHdr = kitGraph.Hdr.Insert(iNKitSpecHdr);

iNKitSpecHdr.KitInventoryID = asset.AssetID;
iNKitSpecHdr.RevisionID = setup.DefaultKitRevisionID;
iNKitSpecHdr.IsActive = true;

iNKitSpecHdr = kitGraph.Hdr.Update(iNKitSpecHdr);


INKitSpecStkDet kitDetail = new INKitSpecStkDet();
kitDetail = kitGraph.StockDet.Insert(kitDetail);

kitDetail.KitInventoryID = iNKitSpecHdr.KitInventoryID;
kitDetail.RevisionID = iNKitSpecHdr.RevisionID;
kitDetail.CompInventoryID = item.InventoryID;
kitDetail.CompSubItemID = item.DefaultSubItemID;
kitDetail.AllowSubstitution = asset.AllowSubstitution;
kitDetail.DfltCompQty = asset.CompQty;
kitDetail.UOM = newItem.BaseUnit;

kitDetail = kitGraph.StockDet.Update(kitDetail);

I am wrapping the creation with a foreach to cycle through my list of kits to add the "new item" to, so the high level view is...

INKitSpecMaint kitGraph = PXGraph.CreateInstance<INKitSpecMaint>();
foreach (MyAsset asset in assets)
{
    //Check if the Kit exists and create it if necessary
    ...

    //Add the new item to the Kit (item was just created, so cannot possibly exist in the kit)
}
kitGraph.Actions.PressSave();

But when I use Actions.PressSave(), I get an error that a Non-Stock Kit may have only one revision. I only add a kit/revision when the kit does not exist at all. I have tried various variations including initializing the graph and saving it all within the foreach loop (poor performance) and clearing the graph at the top of the foreach with a multiple saves throughout, just to see if anything would work.

I'm confused how Search is not working on the main view of the graph when it is defined with an optional parameter, and I'm also confused why Acumatica would think I am adding a 2nd revision to the kit when the kit is being created.

Brian Stevens
  • 1,826
  • 1
  • 7
  • 16
  • Can you please try to remove `iNKitSpecHdr = kitGraph.Hdr.Insert(iNKitSpecHdr);` line and replace `iNKitSpecHdr = kitGraph.Hdr.Update(iNKitSpecHdr);` with `iNKitSpecHdr = kitGraph.Hdr.Insert(iNKitSpecHdr);`? – Samvel Petrosov Aug 15 '19 at 02:14
  • That was my first attempt... create a hdr object with all the data filled in and go straight for an insert. But I can try again after the AUG meeting. – Brian Stevens Aug 15 '19 at 02:17

2 Answers2

1

I went looking through the Code Repository in the INKitSpecMaint.cs file for clues. The solution only pops up one mention of that error code, Messages.SingleRevisionForNS, which is in the INKitSpecHdr_RowPersisting function:

protected virtual void INKitSpecHdr_RowPersisting(PXCache sender, PXRowPersistingEventArgs e)
{
    INKitSpecHdr row = e.Row as INKitSpecHdr;
    if (row != null && e.Operation == PXDBOperation.Insert)
    {
        InventoryItem item = InventoryItem.PK.Find(this, row.KitInventoryID);
        if (item.StkItem != true)
        {
            INKitSpecHdr existing = PXSelectReadonly<INKitSpecHdr, Where<INKitSpecHdr.kitInventoryID, Equal<Current<INKitSpecHdr.kitInventoryID>>>>.Select(this);
            if (existing != null)
            {
                if (sender.RaiseExceptionHandling<INKitSpecHdr.revisionID>(e.Row, row.RevisionID, new PXSetPropertyException(Messages.SingleRevisionForNS)))
                {
                    throw new PXRowPersistingException(typeof (INKitSpecHdr.revisionID).Name, null, Messages.SingleRevisionForNS);
                }
            }
        }
    }
}

I would debug your solution with an Extension Library with Visual Studio and step through your code and see why this would fail with the data you are creating. This would hit when the item that you are sending is a non-stock item and there is an existing revision, so I would validate that the KitInventoryID is being set is the correct item.

To see what is being sent to the persist method, I would create a quick graph extension and put a breakpoint before it calls the base delegate. For example:

public class InKitSpecMaintExt : PXGraphExtension<INKitSpecMaint>
{
    protected void INKitSpecHdr_RowPersisting(PXCache sender, PXRowPersistingEventArgs e, PXRowPersisting del)
    {
        //perform code checks/breakpoints first...
        INKitSpecHdr Row = (INKitSpecHdr)e.Row;
        //execute the base method...
        del?.Invoke(sender, e); //set a breakpoint here
    }
}
KRichardson
  • 990
  • 7
  • 12
  • That is consistent with what I se in debug. It does not fail as I create... it fails at the “save”. – Brian Stevens Aug 14 '19 at 18:28
  • The Row_Persisting fires when saving to the database, not inserting the object into the DAC. I would also try setting the values (RevisionID, KitInventoryID) before calling the insert into the cache, similar to the call that you have listed above for INItemSite. – KRichardson Aug 14 '19 at 18:47
  • I edited the post with a graph extension that you can use to inspect the row. I have tested with the UI that it is called after pushing save, but before the base method is called, which is the last line of code. – KRichardson Aug 14 '19 at 19:12
  • Great idea. I am headed to Atlanta for a AUG meeting but will try that when I can get back to it. – Brian Stevens Aug 14 '19 at 19:15
1

Below is working code for creating Kit Specification record:

INKitSpecMaint kitSpecMaint = PXGraph.CreateInstance<INKitSpecMaint>();
INKitSpecHdr newSpecHdr = new INKitSpecHdr()
{
    KitInventoryID = 10045,
    RevisionID = "SOMEREV"
};
newSpecHdr = kitSpecMaint.Hdr.Insert(newSpecHdr);
newSpecHdr.Descr = "Some test non-stock kit item";
newSpecHdr = kitSpecMaint.Hdr.Update(newSpecHdr);
var newStockDet = kitSpecMaint.StockDet.Insert();
newStockDet.CompInventoryID = 691;
newStockDet.DfltCompQty = 2m;
newStockDet = kitSpecMaint.StockDet.Update(newStockDet);
kitSpecMaint.Save.Press();

The tricky part is with the specification of both keys before inserting the record. Also, I have noticed that you are saving only once after the loop is completed, but it can create the following problem for each asset you are trying to create a Kit Specification record which is changing the current of the Hdr Data View, which can cause issues with the keys at the moment when you will try to save everything. Try saving changes after every Kit Specification record and its details have been created and clearing the Graph after that.

Below is an example of how to get the INKitSpecHdr record either using PK or using the Search of the Data View:

INKitSpecMaint kitSpecMaint = PXGraph.CreateInstance<INKitSpecMaint>();
INKitSpecHdr record = INKitSpecHdr.PK.Find(graph, 10045, "SOMEREV");
INKitSpecHdr findRecordBySearch = kitSpecMaint.Hdr.Search<INKitSpecHdr.kitInventoryID, INKitSpecHdr.revisionID>(10045,"SOMEREV",new object[] { 10045}).FirstOrDefault();

The tricky part with this one is that the Hdr Data View contains Optional parameter and you have to specify it for making Search working correctly.

I haven't found any document saying that the Optional parameters must be specified but this example shows the Search is not finding the record without that.

Samvel Petrosov
  • 7,580
  • 2
  • 22
  • 46
  • Building the header and detail records before insert seems to have solved it. I'm still baffled at why I cannot get the Search to work, but I simply used PXSelect and then set Hdr.Current = the record I found. Oddly, this still returns null for me and by all accounts should work great... kitGraph.Hdr.Current = kitGraph.Hdr.Search(asset.AssetID, (iNKitSpecHdr != null) ? iNKitSpecHdr.RevisionID : setup.DefaultKitRevisionID, new object[] { asset.AssetID }).FirstOrDefault(); – Brian Stevens Aug 16 '19 at 14:04
  • 1
    @BrianStevens try to pass skme hardcoded values to search to check if it works at all, then if it works start checking the parameters you are passing. – Samvel Petrosov Aug 16 '19 at 14:25
  • Still working out the kinks, but I did not have a mask on my setup table "DefaultKitRevisionID" field.. "1" and "1 " are not the same thing! Interestingly enough, the PXSelect can find it without the right padding whitespace. – Brian Stevens Aug 16 '19 at 15:32