I am migrating a 2023 project from another developer to get it to work in 2024.
Due to the requirements for this customization, the previous developer made a complete copy of the CreateShipment action and injected some changes to add logic to the action.
The code in this action has changed from 2023.
To start with, I “re-copied” the Acumatica source code for the action. I have not injected any changes into the override at this time. The signature of the action did not change from 2023 to 2024.
When I attempt to Create Shipment from Sales Order Entry, I get this error message:

At this point, I simply have an overridden action with no changes to the base code other than to put “Base.” in front of the various objects that need to reference the base graph.
When I am in debug mode, I cannot hit a breakpoint at the very beginning of this code.
public delegate IEnumerable CreateShipmentDelegate(PXAdapter adapter,
[PXDate] DateTime? shipDate,
[PXInt] int? siteID,
[SOOperation.List] string operation);
[PXOverride]
protected virtual IEnumerable CreateShipment(PXAdapter adapter,
[PXDate] DateTime? shipDate,
[PXInt] int? siteID,
[SOOperation.List] string operation, CreateShipmentDelegate baseMethod)
{
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;
}
SOParamFilter filter = Base.soparamfilter.Current;
var adapterSlice = (adapter.MassProcess, adapter.QuickProcessFlow, adapter.AllowRedirect);
string userName = Base.Accessinfo.UserName;
PXLongOperation.StartOperation(this, delegate ()
{
bool anyfailed = false;
var orderEntry = PXGraph.CreateInstance<SOOrderEntry>();
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, userName, forceUnattended: true))
sites.Add(plan.SiteID);
}
}
foreach (int? SiteID in sites)
{
SOOrder ordercopy = PXCache<SOOrder>.CreateCopy(order);
try
{
using (var ts = new PXTransactionScope())
{
PXTransactionScope.SetSuppressWorkflow(true);
shipmentEntry.CreateShipment(new CreateShipmentArgs
{
Graph = orderEntry,
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)
{
PXCache<SOOrder>.RestoreCopy(order, ordercopy);
if (!adapterSlice.MassProcess)
throw;
order.LastSiteID = SiteID;
order.LastShipDate = filter.ShipDate;
shipmentEntry.Clear();
orderEntry.Clear();
orderEntry.Document.Current = order;
orderEntry.Document.Cache.MarkUpdated(order, assertError: true);
try
{
SOOrder.Events.Select(e => e.ShipmentCreationFailed).FireOn(orderEntry, order);
orderEntry.Save.Press();
PXTrace.WriteInformation(ex);
PXProcessing<SOOrder>.SetWarning(ex);
}
catch (Exception inner)
{
PXCache<SOOrder>.RestoreCopy(order, ordercopy);
PXProcessing<SOOrder>.SetError(inner);
anyfailed = true;
}
}
catch (Exception ex)
{
PXCache<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;
}
There are two places that new logic is being injected. 1 of the items I can probably do in a different event. But there is one part that needs to intercept the popup. I think that is probably why this entire action is being overridden.
Base.soparamfilter.AskExt(true);
Is there something wrong with the Delegate part of this? It works in 2023 and the only changes is the actual code copied from the Acumatica source.
I know that this is not a good thing to do, but I think that the previous developer found there was no alternative other than to completely override the source.