Skip to main content
Answer

The given key was not present in the dictionary - clicking on shipments tab of SOOrderEntry

  • July 15, 2025
  • 5 replies
  • 58 views

Forum|alt.badge.img+7

We’re getting this error when clicking on the shipments tab of SOOrderEntry. This is an upgraded system, coming from 24R2, upgrading to 25.101.0153.

Through the project editor we have added a state to SOShipment. We have a workflow that takes the new state into account. It’s been working in 24R2 from the beginning.

Edit: In 24R2, the shipments tab does not display the correct status value, it’s showing the database value, not the text for that value:

When I check the trace, the last line is:

 at PX.Objects.SO.GraphExtensions.SOOrderEntryExt.Status.GetValue(SOOrderShipment row)

This error only appears if the order has shipments that have a Status value equal to the new status that we added.

If I put the shipment on hold/off hold to change the status to Open, I can view the shipment on the Sales Order Entry’s shipments tab.

I have unpublished all customizations and the error persists. This suggests to me that the issue is within the workflow somewhere but I have no way to debug it (that I know of).

Any suggestions on how to find where to add the new status in 25R1 so that this error isn’t happening?

More of the trace:

at System.ThrowHelper.ThrowKeyNotFoundException()
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at PX.Objects.SO.GraphExtensions.SOOrderEntryExt.Status.GetValue(SOOrderShipment row)
at PX.Objects.Common.PXFieldAttachedTo`1.By`1.As`1.FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
at PX.Data.PXCache.OnFieldSelecting(String name, Object row, Object& returnValue, Boolean forceState, Boolean externalCall) in C:\build\code_repo\NetTools\PX.Data\Cache\ModelEventHandling.cs:line 1028
at PX.Data.PXCache`1.GetValueInt(Object data, String fieldName, Boolean forceState, Boolean externalCall) in C:\build\code_repo\NetTools\PX.Data\Cache\Model.cs:line 1467
at PX.Data.PXCache`1.GetValueExt(Object data, String fieldName) in C:\build\code_repo\NetTools\PX.Data\Cache\Model.cs:line 1285
at PX.Data.PXView.<>c__DisplayClass221_0.<FilterResult>b__0(Object item) in C:\build\code_repo\NetTools\PX.Data\Database\Cached.cs:line 5705
at PX.Data.PXView.<>c__DisplayClass221_6.<FilterResult>b__27(Object item) in C:\build\code_repo\NetTools\PX.Data\Database\Cached.cs:line 5998
at System.Collections.Generic.List`1.FindAll(Predicate`1 match)
at PX.Data.PXView.FilterResult(List`1 list, PXFilterRow[] filters) in C:\build\code_repo\NetTools\PX.Data\Database\Cached.cs:line 6263
at PX.Data.PXView.Select(Object[] currents, Object[] parameters, Object[] searches, String[] sortcolumns, Boolean[] descendings, PXFilterRow[] filters, Int32& startRow, Int32 maximumRows, Int32& totalRows, String[] sortAsImplicitColumns) in C:\build\code_repo\NetTools\PX.Data\Database\Cached.cs:line 7057
at PX.Data.PXView.Select(Object[] currents, Object[] parameters, Object[] searches, String[] sortcolumns, Boolean[] descendings, PXFilterRow[] filters, Int32& startRow, Int32 maximumRows, Int32& totalRows) in C:\build\code_repo\NetTools\PX.Data\Database\Cached.cs:line 6722
at PX.Data.PXGraph.ExecuteSelect(String viewName, Object[] currents, Object[] parameters, Object[] searches, String[] sortcolumns, Boolean[] descendings, PXFilterRow[] filters, Int32& startRow, Int32 maximumRows, Int32& totalRows) in C:\build\code_repo\NetTools\PX.Data\Graph\Graph.cs:line 2932
at PX.Data.PXGraph.ExecuteSelect(String viewName, Object[] parameters, Object[] searches, String[] sortcolumns, Boolean[] descendings, PXFilterRow[] filters, Int32& startRow, Int32 maximumRows, Int32& totalRows) in C:\build\code_repo\NetTools\PX.Data\Graph\Graph.cs:line 2895
at PX.Web.UI.PXBaseDataSource.ExecuteSelect(String viewName, DataSourceSelectArguments arguments, PXDSSelectArguments pxarguments)
at PX.Web.UI.PXDataSource.ExecuteSelect(String viewName, DataSourceSelectArguments arguments, PXDSSelectArguments pxarguments)
at PX.Web.UI.PXDataSourceView.Select(DataSourceSelectArguments arguments, PXDSSelectArguments swarguments, DataSourceViewSelectCallback callback)
at PX.Web.UI.PXGrid.PerformSelect()
at PX.Web.UI.PXGrid.GetCallbackResult(PXCallbackCommand cmd)

 

Best answer by Keith Richardson

@Django Here is the code that fixed that issue… thanks for reminding me to post it as a blog post linking it to the other threads!

 

 

using PX.Data;
using PX.Objects.PO;
using PX.Objects.SO;
using System;
using System.Reflection;

namespace HaunWelding.Delivery
{
[PXProtectedAccess]
//you need to put this attribute on, or it will show the display name as Cst_ on the grid
[PXUIField(DisplayName = "Status", Enabled = false)]
//abstract due to having abstract due to PXProtectedAccess
public abstract class SOOrderShipmentStatusAttachedToExt : PXGraphExtension<PX.Objects.SO.GraphExtensions.SOOrderEntryExt.Status, SOOrderEntry>
{
#region ProtectedAccess
[PXProtectedAccess(typeof(PX.Objects.SO.GraphExtensions.SOOrderEntryExt.Status))]
protected abstract PXFieldState DefaultState(PXCache sender, PXFieldSelectingEventArgs e);
[PXProtectedAccess(typeof(PX.Objects.SO.GraphExtensions.SOOrderEntryExt.Status))]
protected abstract PXFieldState AdjustByAttribute(PXFieldState state, PXUIFieldAttribute uiAttribute);
[PXProtectedAccess(typeof(PX.Objects.SO.GraphExtensions.SOOrderEntryExt.Status))]
protected abstract PXFieldState AdjustStateBySelf(PXFieldState state);
[PXProtectedAccess(typeof(PX.Objects.SO.GraphExtensions.SOOrderEntryExt.Status))]
protected abstract PXFieldState AdjustStateByRow(PXFieldState state, SOOrderShipment row);
#endregion


//these properties are getters only, no need to protected access.
protected PXUIFieldAttribute FieldAttribute => GetType().GetCustomAttribute<PXUIFieldAttribute>();
protected virtual bool SuppressValueSetting => false;
protected virtual String ValueForEmptyRow => default(String);

public override void Initialize()
{
// base method initialize code that we will not call, as I can't get it to remove that event with a proper signature
/* FieldName = GetType().Name.LastSegment('+');
base.Base.Caches<TTable>().Fields.Add(FieldName);
base.Base.FieldSelecting.AddHandler(typeof(TTable), FieldName, FieldSelecting);*/

//do not call initialize
//base.Initialize();

//call code from the base method that we need
Base.Caches<SOOrderShipment>().Fields.Add("Status");
base.Base.FieldSelecting.AddHandler(typeof(SOOrderShipment), "Status", FieldSelecting);
}

//copy of field selecting
public virtual void FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
PXFieldState state = DefaultState(sender, e);
if (FieldAttribute != null)
{
state = AdjustByAttribute(state, FieldAttribute);
}

state = AdjustStateBySelf(state);
state = AdjustStateByRow(state, (SOOrderShipment)e.Row);
e.ReturnState = state;

if (!SuppressValueSetting)
{
e.ReturnValue = ((e.Row == null) ? ValueForEmptyRow : GetValue((SOOrderShipment)e.Row, sender));
}
}
//copy the base function for GetValue, as I cannot PXOverride it.
public virtual string GetValue(SOOrderShipment row, PXCache sender)
{
if (row.ShipmentType == SOShipmentType.DropShip)
{
using var receipt = PXDatabase.SelectSingle<POReceipt>(new PXDataField<POReceipt.status>(),
new PXDataFieldValue<POReceipt.receiptType>(PXDbType.Char, null,
row.Operation == SOOperation.Issue ? POReceiptType.POReceipt : POReceiptType.POReturn, PXComp.EQ),
new PXDataFieldValue<POReceipt.receiptNbr>(PXDbType.NVarChar, null, row.ShipmentNbr, PXComp.EQ));
if (receipt == null)
{
return null;
}
return (new POReceiptStatus.ListAttribute()).ValueLabelDic[receipt.GetString(0)];
}
else
{
using (new PXReadThroughArchivedScope())
{
using var shipment = PXDatabase.SelectSingle<SOShipment>(new PXDataField<SOShipment.status>(),
new PXDataFieldValue<SOShipment.shipmentType>(PXDbType.Char, null, row.ShipmentType, PXComp.EQ),
new PXDataFieldValue<SOShipment.shipmentNbr>(PXDbType.NVarChar, null, row.ShipmentNbr, PXComp.EQ));
if (shipment != null)
{
//changed to use my new list attribute
return (new SOShipmentStatusExt.ListAttribute()).ValueLabelDic[shipment.GetString(0)];
}
else
{
return PXMessages.LocalizeNoPrefix(PX.Objects.SO.Messages.AutoGenerated);
}
}
}
}
}
}

 

5 replies

Forum|alt.badge.img+8
  • Captain II
  • July 16, 2025

Hey ​@Django 

 

When adding a new status to a shipment, I had this too.

You should add the status to the status field in the customisation project too.

 


Forum|alt.badge.img+7
  • Author
  • Captain II
  • July 17, 2025

@aiwan - thank you for your response!  That’s actually the only place that I’ve added the status.

I’m considering adding it, instead, through code.

@Keith Richardson posted this thread which is pretty much what our customization is doing - allowing the shipment to be flagged as ‘Delivered’ before they proceed with invoicing.

 

On a whim I looked through the ACM code for references to .Status and found the SOShipment.Status field has this attribute:

[SOShipmentStatusVerifier]

It looks like I might have to override that attribute as it has specific code for the default Shipment status field values and no provision for new status codes. And it’s throwing an exception when we put documents on hold that asks the user to contact support. That’s coming from this attribute.


Keith Richardson
Semi-Pro I
Forum|alt.badge.img+2

@Django Here is the code that fixed that issue… thanks for reminding me to post it as a blog post linking it to the other threads!

 

 

using PX.Data;
using PX.Objects.PO;
using PX.Objects.SO;
using System;
using System.Reflection;

namespace HaunWelding.Delivery
{
[PXProtectedAccess]
//you need to put this attribute on, or it will show the display name as Cst_ on the grid
[PXUIField(DisplayName = "Status", Enabled = false)]
//abstract due to having abstract due to PXProtectedAccess
public abstract class SOOrderShipmentStatusAttachedToExt : PXGraphExtension<PX.Objects.SO.GraphExtensions.SOOrderEntryExt.Status, SOOrderEntry>
{
#region ProtectedAccess
[PXProtectedAccess(typeof(PX.Objects.SO.GraphExtensions.SOOrderEntryExt.Status))]
protected abstract PXFieldState DefaultState(PXCache sender, PXFieldSelectingEventArgs e);
[PXProtectedAccess(typeof(PX.Objects.SO.GraphExtensions.SOOrderEntryExt.Status))]
protected abstract PXFieldState AdjustByAttribute(PXFieldState state, PXUIFieldAttribute uiAttribute);
[PXProtectedAccess(typeof(PX.Objects.SO.GraphExtensions.SOOrderEntryExt.Status))]
protected abstract PXFieldState AdjustStateBySelf(PXFieldState state);
[PXProtectedAccess(typeof(PX.Objects.SO.GraphExtensions.SOOrderEntryExt.Status))]
protected abstract PXFieldState AdjustStateByRow(PXFieldState state, SOOrderShipment row);
#endregion


//these properties are getters only, no need to protected access.
protected PXUIFieldAttribute FieldAttribute => GetType().GetCustomAttribute<PXUIFieldAttribute>();
protected virtual bool SuppressValueSetting => false;
protected virtual String ValueForEmptyRow => default(String);

public override void Initialize()
{
// base method initialize code that we will not call, as I can't get it to remove that event with a proper signature
/* FieldName = GetType().Name.LastSegment('+');
base.Base.Caches<TTable>().Fields.Add(FieldName);
base.Base.FieldSelecting.AddHandler(typeof(TTable), FieldName, FieldSelecting);*/

//do not call initialize
//base.Initialize();

//call code from the base method that we need
Base.Caches<SOOrderShipment>().Fields.Add("Status");
base.Base.FieldSelecting.AddHandler(typeof(SOOrderShipment), "Status", FieldSelecting);
}

//copy of field selecting
public virtual void FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e)
{
PXFieldState state = DefaultState(sender, e);
if (FieldAttribute != null)
{
state = AdjustByAttribute(state, FieldAttribute);
}

state = AdjustStateBySelf(state);
state = AdjustStateByRow(state, (SOOrderShipment)e.Row);
e.ReturnState = state;

if (!SuppressValueSetting)
{
e.ReturnValue = ((e.Row == null) ? ValueForEmptyRow : GetValue((SOOrderShipment)e.Row, sender));
}
}
//copy the base function for GetValue, as I cannot PXOverride it.
public virtual string GetValue(SOOrderShipment row, PXCache sender)
{
if (row.ShipmentType == SOShipmentType.DropShip)
{
using var receipt = PXDatabase.SelectSingle<POReceipt>(new PXDataField<POReceipt.status>(),
new PXDataFieldValue<POReceipt.receiptType>(PXDbType.Char, null,
row.Operation == SOOperation.Issue ? POReceiptType.POReceipt : POReceiptType.POReturn, PXComp.EQ),
new PXDataFieldValue<POReceipt.receiptNbr>(PXDbType.NVarChar, null, row.ShipmentNbr, PXComp.EQ));
if (receipt == null)
{
return null;
}
return (new POReceiptStatus.ListAttribute()).ValueLabelDic[receipt.GetString(0)];
}
else
{
using (new PXReadThroughArchivedScope())
{
using var shipment = PXDatabase.SelectSingle<SOShipment>(new PXDataField<SOShipment.status>(),
new PXDataFieldValue<SOShipment.shipmentType>(PXDbType.Char, null, row.ShipmentType, PXComp.EQ),
new PXDataFieldValue<SOShipment.shipmentNbr>(PXDbType.NVarChar, null, row.ShipmentNbr, PXComp.EQ));
if (shipment != null)
{
//changed to use my new list attribute
return (new SOShipmentStatusExt.ListAttribute()).ValueLabelDic[shipment.GetString(0)];
}
else
{
return PXMessages.LocalizeNoPrefix(PX.Objects.SO.Messages.AutoGenerated);
}
}
}
}
}
}

 


Forum|alt.badge.img+8
  • Captain II
  • July 17, 2025

 

On a whim I looked through the ACM code for references to .Status and found the SOShipment.Status field has this attribute:

[SOShipmentStatusVerifier]

It looks like I might have to override that attribute as it has specific code for the default Shipment status field values and no provision for new status codes. And it’s throwing an exception when we put documents on hold that asks the user to contact support. That’s coming from this attribute.

Also had issues with this one! if your transition goes from ‘On Hold’ you need to ensure your transition sets ‘Hold’ as false.

 

Also, bizarre that that’s where you have added the status and it’s still only showing the ‘Value’ rather than ‘Description’!

Through code may be easier. I much prefer working with code rather than ‘no-code’ for the workflows.


Forum|alt.badge.img+7
  • Author
  • Captain II
  • July 17, 2025

@aiwan - yeah, as I look more into this, the no-code option in this case isn’t a good option.

@Keith Richardson - thank you for the code!