Question

SOOrderEntry with Action for Multiple Shipments Throws Exception

  • 13 October 2022
  • 5 replies
  • 364 views

Userlevel 3
Badge +1

Hello,

 

My goal is to create multiple shipments from the SalesOrder SO Workflow, one for each associated warehouse. My first step was to copy over the existing action/function and ensure that it functioned as the original.

The below code has been copied and slightly modified to work in an extension. I have disabled the PXLongOperation as well for two reasons, 1) it caused a PX1008 error, 2) is allows easier debugging. The code should be functionally equivalent to the default implantation, however I get the following exception:

“Another process has updated the 'SOOrder' record. Your changes will be lost.”

   at PX.Data.PXAction`1.<Press>d__38.MoveNext()
   at PX.Data.PXAction`1.PressImpl(Boolean internalCall, Boolean externalCall)
   at PX.Objects.SO.SOShipmentEntry.CreateShipment(CreateShipmentArgs args)
   at PX.Objects.PM.MaterialManagement.SOShipmentEntryMaterialExt.CreateShipment(CreateShipmentArgs args, Action`1 baseMethod)
   at PX.Objects.SO.SOOrderEntry_Extension.CreateShipment(PXAdapter adapter, Nullable`1 shipDate, Nullable`1 siteID, String operation) in C:\Program Files\Acumatica ERP\A-Dev3\App_Data\Projects\GPGeneral\GPGeneral\SO\SOOrderEntry_Extension.cs:line 232
   at Wrapper.PX.Objects.SO.Cst_SOOrderEntry.CreateShipmentGeneratedWrapper(SOOrderEntry g, PXAdapter , Nullable`1 , Nullable`1 , String )
   at Wrapper.PX.Objects.SO.Cst_SOOrderEntry.CreateShipment(PXAdapter adapter, Nullable`1 shipDate, Nullable`1 siteID, String operation)
   at PX.Objects.SO.SOOrderEntry.CreateShipmentIssue(PXAdapter adapter, Nullable`1 shipDate, Nullable`1 siteID)

 

The odd part is that the shipment is created, but why do I get this error when the code should be the same functionally?

Furthermore, why would the code produce a PX1008 when it is identical in function to the default? What am I missing?

 

Also, from reviewing the code I think I can my plan will be to add the following lines at the start of the function. Do you have any concern with this? 

adapter.MassProcess = true; //Allows for multiple Site Shipments to be created.
siteID = null; //Causes the population of a list of all Sites where shipments are needed

 

 

[PXOverride]
public virtual IEnumerable CreateShipment(PXAdapter adapter,
[PXDate] DateTime? shipDate,
[PXInt] int? siteID,
[SOOperation.List] string operation)
{
List<SOOrder> list = adapter.Get<SOOrder>().ToList();
if (shipDate != null)
Base.soparamfilter.Current.ShipDate = shipDate;
if (siteID != null)
Base.soparamfilter.Current.SiteID = siteID;

if (Base.soparamfilter.Current.ShipDate == null)
Base.soparamfilter.Current.ShipDate = Base.Accessinfo.BusinessDate;

if (!adapter.MassProcess)
{
if (Base.soparamfilter.Current.SiteID == null)
Base.soparamfilter.Current.SiteID = GetPreferedSiteID();
if (adapter.ExternalCall)
Base.soparamfilter.AskExt(true);
}
if (Base.soparamfilter.Current.SiteID != null || adapter.MassProcess)
{
try
{
Base.RecalculateExternalTaxesSync = true;
Base.Save.Press();
}
finally
{
Base.RecalculateExternalTaxesSync = false;
}
PXAutomation.RemovePersisted(Base, typeof(SOOrder), new List<object>(list));

SOParamFilter filter = Base.soparamfilter.Current;
var adapterSlice = (adapter.MassProcess, adapter.QuickProcessFlow, adapter.AllowRedirect);
// PXLongOperation.StartOperation(Base, delegate ()
//{
bool anyfailed = false;
var shipmentEntry = PXGraph.CreateInstance<SOShipmentEntry>();
var created = new DocumentList<SOShipment>(shipmentEntry);

//address AC-92776
for (int i = 0; i < list.Count; i++)
{
SOOrder order = list[i];
if (adapterSlice.MassProcess)
PXProcessing<SOOrder>.SetCurrentItem(order);

List<int?> sites = new List<int?>();

if (filter.SiteID != null)
{
sites.Add(filter.SiteID);
}
else
{
foreach (SOShipmentPlan plan in PXSelectJoinGroupBy<SOShipmentPlan,
LeftJoin<SOOrderShipment,
On<SOOrderShipment.orderType, Equal<SOShipmentPlan.orderType>,
And<SOOrderShipment.orderNbr, Equal<SOShipmentPlan.orderNbr>,
And<SOOrderShipment.siteID, Equal<SOShipmentPlan.siteID>,
And<SOOrderShipment.confirmed, Equal<boolFalse>>>>>>,
Where<SOShipmentPlan.orderType, Equal<Current<SOOrder.orderType>>,
And<SOShipmentPlan.orderNbr, Equal<Current<SOOrder.orderNbr>>,
And<SOOrderShipment.orderNbr, IsNull>>>,
Aggregate<GroupBy<SOShipmentPlan.siteID>>,
OrderBy<Asc<SOShipmentPlan.siteID>>>.SelectMultiBound(shipmentEntry, new object[] { order }))
{
INSite inSite = INSite.PK.Find(shipmentEntry, plan.SiteID);

// AC-144778. We can't use Match<> inside long run operation
if (GroupHelper.IsAccessibleToUser(shipmentEntry.Caches[typeof(INSite)], inSite, Base.Accessinfo.UserName, forceUnattended: true))
sites.Add(plan.SiteID);
}
}

foreach (int? SiteID in sites)
{
SOOrder ordercopy = (SOOrder)Base.Caches[typeof(SOOrder)].CreateCopy(order);
try
{
using (var ts = new PXTransactionScope())
{
PXTransactionScope.SetSuppressWorkflow(true);
shipmentEntry.CreateShipment(new CreateShipmentArgs
{
Graph = Base,
MassProcess = adapterSlice.MassProcess,
Order = order,
SiteID = SiteID,
ShipDate = filter.ShipDate,
UseOptimalShipDate = adapterSlice.MassProcess,
Operation = operation,
ShipmentList = created,
QuickProcessFlow = adapterSlice.QuickProcessFlow,
});
ts.Complete();
}

if (adapterSlice.MassProcess)
PXProcessing<SOOrder>.SetProcessed();
}
catch (SOShipmentException ex)
{
Base.Caches[typeof(SOOrder)].RestoreCopy(order, ordercopy);
if (!adapterSlice.MassProcess)
throw;

order.LastSiteID = SiteID;
order.LastShipDate = filter.ShipDate;

shipmentEntry.Clear();

var ordergraph = PXGraph.CreateInstance<SOOrderEntry>();
ordergraph.Document.Current = order;
ordergraph.Document.Cache.MarkUpdated(order);

try
{
SOOrder.Events.Select(e => e.ShipmentCreationFailed).FireOn(ordergraph, order);
ordergraph.Save.Press();

PXTrace.WriteInformation(ex);
PXProcessing<SOOrder>.SetWarning(ex);
}
catch (Exception inner)
{
Base.Caches[typeof(SOOrder)].RestoreCopy(order, ordercopy);
PXProcessing<SOOrder>.SetError(inner);
anyfailed = true;
}
}
catch (Exception ex)
{
Base.Caches[typeof(SOOrder)].RestoreCopy(order, ordercopy);
shipmentEntry.Clear();

if (!adapterSlice.MassProcess)
throw;

PXProcessing<SOOrder>.SetError(ex);
anyfailed = true;
}
}
}
if (adapterSlice.AllowRedirect && !adapterSlice.MassProcess && created.Count > 0)
{
using (new PXTimeStampScope(null))
{
shipmentEntry.Clear();
shipmentEntry.Document.Current = shipmentEntry.Document.Search<SOShipment.shipmentNbr>(created[0].ShipmentNbr);
throw new PXRedirectRequiredException(shipmentEntry, "Shipment");
}
}
if (anyfailed)
throw new PXOperationCompletedWithErrorException(ErrorMessages.SeveralItemsFailed);
//});
}
return list;
}

 

 

Thanks

 


5 replies

Userlevel 7
Badge +5

Before looking at the code, I’d challenge your requirements. 

In Acumatica ERP it is not allowed to create multiple open shipments for the same SO at the same time. 

You can only create one shipment, process it, then create another one. 

There is a reason for that: If you have multiple shipments open, it becomes really hard to track every change and keep the data between the SO and multiple Shipments in sync since you can modify all of them.

 

So, the system is designed around that and you’ll face a lot of limitations in different places in the code that would prevent that. I’m not saying it’s completely impossible, but at some point it’ll be easier to write your own SO module.

Userlevel 3
Badge +1

Even if the shipments are being sourced in different warehouses? 

Userlevel 7
Badge +5

@Leif good point, in that case it is allowed. I’ve missed this part.

 

As for PX1008, I’d not care about this one too much if you just copy pasted the base code. There was something inconsistency in the diagnostic, I think it’s fixed now in Acuminator v 3.0

https://github.com/Acumatica/Acuminator/blob/dev/docs/ReleaseNotes.md

Maybe try upgrading Acuminator version.

 

As for the Another Record Updated error, it may be caused by removing the LongOperation. LongOperation implicitly covers everything in PXTimeStampScope, and that changes the way the system works with the concurrent updates. You can try wrapping it in using(new PXTimeStampScope (Base.tstamp)) where there was a long operation

 

 

Userlevel 5
Badge +1

First off I agree with Dmitri, our team tried supporting multiple open shipments before and found it very problematic to accomplish. We actually determined it was easier to split the orders into an order per warehouse to ultimately make it work.

 

that being said, what I suspect is that part of what happens when a shipment is created is that the SOLines are updated to indicate what was shipped. since the graph doesn’t see this change before you move the next create shipment the data is stale. this would cause the error you are experiencing. Not sure if there is a way to “refresh” the order between warehouses. 

Userlevel 3
Badge +1

@Dmitrii Naumov The PXTimeStampScope  wrapper did not change the exception.

@Shawn Burt I understand you comment, but my issue is that the code looks functionally the same as the version that does not cause the error. IE I have just copied and pasted it into the override action handler, only making changes to reference the Base object ect.

Reply


About Acumatica ERP system
Acumatica Cloud ERP provides the best business management solution for transforming your company to thrive in the new digital economy. Built on a future-proof platform with open architecture for rapid integrations, scalability, and ease of use, Acumatica delivers unparalleled value to small and midmarket organizations. Connected Business. Delivered.
© 2008 — 2024  Acumatica, Inc. All rights reserved