I have a BPM I am working on to delete a PO Line automatically under certain circumstances. The delete is working great, but after the line is deleted- the PO Header Total Order Amount is not recalculating.
For example, 1 line on PO for $20. Total Order = $20. Delete Line, should be $0 now on header-- but it still shows $20. Then if the line is added again, the total goes to $40 even though it should just be $20. Etc and so forth.
I traced the process in both Classic Trace Log and Kinetic Dev Tools-- both show essentially the same thing business-object wise: A single PO.Update() call where the given PO Line has RowMod = āDā. This works if you follow the tracelog to delete the line, but some kind of Header-Recalculation is not occuring.
Timing could be part of it, I have it on a Standard Data Directive on PORel, when a duplicate Rel is added that references the same job mtl as another rel, the entire line will be deleted using the business objects. Since itās on Standard (not in-trans), it should be executing after the transaction is done and all appears fine except for the header not recalculating the total.
Has anyone seen this before when deleting PO Lines using C# and been able to get around it? If so- what am I missing?
Kevin! You always point me in the right direction. I originally was testing the business object code in functions and so I already had the deleting portion of this written out. Great idea-- I just tested it using the Function alone (same exact code for the portion that deletes the line), and it recalculates the header amount just fine⦠there IS something wrong with the timing, since I am executing the command off of a Standard Data Directive.
I am going to play around a bit-- I will report back.
Iām curious to hear what you find.
No matter how you approach the subject, a line deletion should update a header.
Iāve seen similar behavior when calling a function in a sales order (not deleting but updating lines) when not in the form. Triggered by a bpm.
Followingā¦
So, I am trying to delete a PO Line when a Line/Release is added for a Job Mtl demand that is a duplicate of an existing PO Line/Release. Normally I would never want to do something like this, but I am working to solve a difficult / multi-faceted issue weāre having with our purchasing dept.
The buyers run Generate POās on hundreds at a time and it locks up their system for an hour or more at times- and what seems to be happening is when more than 1 person is generating a massive amount of POās at a time (started at different intervals but overlapping processing), somehow wires are getting crossed and occasionally a duplicate is created.
The users are very resistant to any change in process etc. so it has made the approach to fixing it complicated.
I found that in the Kinetic App for New PO Suggestion Entry, the āProcess Suggestionsā button (same as the classic Actions ā Generate action) calls a different method: āGenerateShowCreatedā versus the classic screen which just calls āGenerateā in the tracelog. The method called by Kinetic allows me to attach the required logic on Post-Processing and everything would be hunky-dory, but the users wonāt just use the kinetic app so I have to approach it a little differently still.
I had not considered just closing the line- I wanted it to be fully deleted right after it was created if possible. But, I may try the route of just closing it and see if that achieves the desired results.
The reason I am trying to delete it right after itās created is because I donāt want to throw any exceptions during the Generate process since this seems to be a potential cause of more issues and potentially more duplicates. Exceptions or errors thrown during this āGenerateā POās process seems to make things worse in my testing so far.
It really has been a hell of a catch-22 situation Iām in currently, lol. The code itself to perform the desired actions is simple from a business object contract service standpoint, but the timing and triggering the execution has proved to be far more difficult than expected.
Yeah, from what I can tell from testing using different triggers (therefore timing), it seems that although the execution of the BO method on a Standard Data Directive IS after the PORel Db transaction- itās NOT after the header record was done doing the initial update for the added line/release. So in essence, by the time the new line/release is deleted from my code, the Header record is finishing itās initial total amount recalculation from when the line was added
Iām certainly curious and want to hear from the experts on this topic, however if the actions your users are taking are causing problems, I would keep pushing for change, instead of fighting the system to fix something they may have broken.
Quick update, the code to close the line is again exceedingly simple, tested in a separate function. But, the timing when executing the CloseOrderLine method from a PORel data directive causes it to not work in my testing.
The reason I was trying to trigger a directive this way is because the classic āGenerateā method (Generate POās process from New PO Suggestion Entry) leaves me really only this option. Normally, if the line were being added manually, I would just have a method directive on PO.Update or similar and call it a day. But I am severely limited by the classic POSugg.Generate() method as far as what triggers I can hit during that, while new POās are getting created by that process.
As far as I can tell, the classic āGenerateā process / method is a black box. You can see the dataset in Post-Processing which contains the list of suggestions to be processed, but you canāt see what method the dataset is sent to after Generate() as it fires once you select āCurrent Buyer: [X Buyer] ā OKā from the dialog popup. I tried triggering on the obvious methods like PO.Update, but the Generate() process doesnāt seem to call the regular BO.PO methods like if you were creating a PO through the UI. Thatās why I tried resorting to a data directive to catch it on the Db transaction, and the PORel would contain the necessary JobNum/AsmSeq/JobSeq info I needed to determine if it was a duplicate.
Bah! I am ready to give up on this one haha. I may just have to give an ultimatum so that they have to use the Kinetic UI after all.
and WOAH- it worked. I used a Pre Proc on the classic Generate() and was able to call the kinetic method AND use the results returned from the GenereateShowCreated method. Here was the code so far (TESTING only lol):
//vars for kinetic method:
string buyerID = ipCurBuyer;
var purAgent = Db.PurAgent.Where(r => r.Company == this.callContextClient.CurrentCompany && r.BuyerID == ipCurBuyer).FirstOrDefault();
string buyerIDName = purAgent == null ? "" : purAgent.Name;
DateTime? cutOffDate = ipCutOffDate;
bool generatePurchaseOrders = true;
bool generateRFQSuggestions = true;
string generateType = "S";
bool performValidation = true;
this.CallService<Erp.Contracts.POSuggSvcContract>(poSuggSvc => {
var poSuggTs = new Erp.Tablesets.POSuggTableset();
var genOpt = new Erp.Tablesets.GenerateOptionsTable();
var genOptRow = new Erp.Tablesets.GenerateOptionsRow();
genOptRow.BuyerID = buyerID;
genOptRow.BuyerIDName = buyerIDName;
genOptRow.CutOffDate = cutOffDate;
genOptRow.GeneratePurchaseOrders = generatePurchaseOrders;
genOptRow.GenerateRFQSuggestions = generateRFQSuggestions;
genOptRow.GenerateType = generateType;
genOptRow.PerformValidation = performValidation;
genOpt.Add(genOptRow);
var sugPoCreatedListTs = new Erp.Tablesets.SugPoCreatedListTableset();
string opPartRunOutMessage = "";
poSuggSvc.GenerateShowCreated(ref poSuggTs, genOpt, ref sugPoCreatedListTs, out opPartRunOutMessage);
//setup message box parameters
var s1 = "GenerateShowCreated() sugPoCreatedList: " + sugPoCreatedListTs.SugPoCreatedList.Count.ToString() + "\n" + "First PO Num: " + sugPoCreatedListTs.SugPoCreatedList.FirstOrDefault().PONum.ToString(); // text to appear on popup
var s2 = Ice.Common.BusinessObjectMessageType.Information;
var s3 = Ice.Bpm.InfoMessageDisplayMode.Individual;
var s4 = ""; // text that appears in popup Details button ->program field
var s5 = ""; // text that appears in popup Details button ->method field
//call popup message
this.PublishInfoMessage(s1, s2, s3, s4, s5);
});
Yes, I did- thank you for the heads up. I am genuinely surprised it worked this well. Now I would just have to get the Classic UI to refresh basically and drop the processed suggs off the tree view/list like the Generate() method normally does when itās done.
So for all intents and purposes, substituting the Kinetic GenerateShowCreated() method for the classic Generate() method solved my original issue. Iāll mark that as the solution.
Now it has raised some other difficulties because I need the suggestions that got Generated into POās to fall off of the Classic UI, otherwise it remains in the UI dataset and can be the source of a duplicate PO getting created, even though the SugPoDtl record is deleted from the database. Basically I need to figure out how to get the Classic UI to GetRowsPlant() and attach the new ds. In the classic UI, even āRefreshā action using the refresh button does not cause the deleted suggestion to drop off the list in the UI, you have to actually redo your entire search and select all, click ok to bring in the existing Database Suggestion records. Working on this part now lol.
I strongly dislike the classic āNew PO Suggestion Entryā screen, the way it behaves and the way itās methods behave lol.