I have a strange problem. Due to a conflict (I guess) with a 3rd party vendor, my customer sometimes experiences very odd behavior.
What happens is when they create an appointment from a service order, and save the appointment, it changes the UnitPrice on the Sales Order Detail.
I know that sounds weird… but, in example:
If line 3 of the sales order detail has a unitPrice of $20, and they add an appointment, its detail line 3 also shows a UnitPrice of $20. But when they save the appointment, the service order line 3 now has a price of $34.95.
It doesn’t always happen. When it happens the price is not always the same amount. It’s not always the same inventory item. But it happens enough to be a problem.
Fortunately, we have a data set that, when following the same steps, will show the same problem every time. So I have to run it, roll it back, run it again, etc. Long process to debug, but at least it is repeatable.
That is why I know it has something to do with a conflict elsewhere. I can put a debugger on the RowInserting event, and the price is $20. At the start of RowInserted, it is still $20. But, coming out of RowInserted, it has inexplicitly changed. There is nothing in my code that changes it... It happens somewhere else. But, it does not happen if I unpublish my code. It also does not happen if I only publish my code.
So, unable to trace it to ground, I am trying a hack-y fix. When the appointment is entered, on the (Events.RowPersisted<FSAppointment> e) event, I want to check the unitPrice, correct it if necessary, and then save it back out with the correct value.
And, I want to save the changes by calling the pressSave event in the ServiceOrderEntry Graph, because it does total and tax calculations, and I don’t want to have to code all of that by hand.
I have code that runs fine, but the update is not working and I am at a loss as to why. Here is the code:
protected virtual void _(Events.RowPersisted<FSAppointment> e, PXRowPersisted BaseHandler)
{
var accessScreen = base.Base.Accessinfo.ScreenID;
var fsAppointmentDetCache = Base.CachesBtypeof(FSAppointmentDet)];
var fsSODetCache = Base.CachesBtypeof(FSSODet)];
// Acuminator disable once PX1045 PXGraphCreateInstanceInEventHandlers nJustification]
if (serviceOrderGraph == null)
{
serviceOrderGraph = GetNewServiceOrderEntryGraph(clearGraph: true);
}
// Capture original values before any changes are persisted
string onRefNbr = e.Row.SORefNbr;
string onSoType = e.Row.SrvOrdType;
FSServiceOrder onServiceOrder = FSServiceOrder.PK.Find(e.Cache.Graph, onSoType, onRefNbr);
List<LinePrice> linePrices = new List<LinePrice>();
foreach (FSSODet line in fsSODetCache.Cached)
{
LinePrice lp = new LinePrice
{
LineNbr = line.LineNbr.Value,
Price = line.UnitPrice.GetValueOrDefault(),
SoType = onSoType,
RefNbr = onRefNbr
};
linePrices.Add(lp);
}
// ensure the Base method logic is executed:
BaseHandler?.Invoke(e.Cache, e.Args);
List<int?> ChangedLines = new List<int?>();
foreach (LinePrice lp in linePrices)
{
// Find the corresponding FSSODet row based on LineNbr.
FSSODet matchingDetail = PXSelect<FSSODet,
Where<FSSODet.srvOrdType, Equal<Required<FSSODet.srvOrdType>>,
And<FSSODet.refNbr, Equal<Required<FSSODet.refNbr>>,
And<FSSODet.lineNbr, Equal<Required<FSSODet.lineNbr>>>>>>
.Select(serviceOrderGraph, lp.SoType, lp.RefNbr, lp.LineNbr);
serviceOrderGraph.ServiceOrderRecords.Current = onServiceOrder;
if (matchingDetail != null && matchingDetail.UnitPrice != lp.Price)
{
ChangedLines.Add(matchingDetail.LineNbr);
// If the prices are different, update the UnitPrice.
matchingDetail.UnitPrice = lp.Price;
serviceOrderGraph.ServiceOrderDetails.Cache.Update(matchingDetail);
}
}
// Save changes after updating all necessary lines.
// Acuminator disable once PX1043 SavingChangesInEventHandlers nJustification]
serviceOrderGraph.Save.Press();
// Confirm changes after updating all necessary lines.
foreach (int? linnbr in ChangedLines)
{
var FSDetail = FSSODet.PK.Find(serviceOrderGraph, onSoType, onRefNbr, linnbr);
if (FSDetail != null)
{
var chkPrice = FSDetail.UnitPrice;
int i = 0;
}
}
}
It is a little wonky because I added some things in there for debugging. This code uses a simple class:
internal class LinePrice
{
internal decimal? Price;
internal int? LineNbr;
internal String RefNbr;
internal String SoType;
public LinePrice() { }
}
And I also copied GetServiceOrderEntry from the Acumatica source code to get the ability to create a ServiceOrderENtry graph from within AppointmentEnrty.
internal ServiceOrderEntry GetNewServiceOrderEntryGraph(bool clearGraph)
{
ServiceOrderEntry _ServiceOrderEntryGraph = null; // this will always make new
if (_ServiceOrderEntryGraph == null)
{
_ServiceOrderEntryGraph = PXGraph.CreateInstance<ServiceOrderEntry>();
}
else if (clearGraph == true && _ServiceOrderEntryGraph.RunningPersist == false)
{
_ServiceOrderEntryGraph.Clear();
}
if (_ServiceOrderEntryGraph.RunningPersist == false)
{
_ServiceOrderEntryGraph.RecalculateExternalTaxesSync = true;
_ServiceOrderEntryGraph.GraphAppointmentEntryCaller = this.Base;
}
return _ServiceOrderEntryGraph;
}
Any help would be greatly appreciated. The only other thing I know to try is using PXDatabase.Update() -- which WILL update it, but then I have to code the totaling and tax totals myself which I REALLY don’t want to do.