Skip to main content
Answer

Extracting Production Order Reference into Inventory Lot/Serial History Screen

  • July 14, 2025
  • 3 replies
  • 70 views

Forum|alt.badge.img+2

I have added a field called ‘Production Order No.’ which is meant to pull the production order number for the given line in Inventory Lot/ Serial History screen without having to click on reference number → go to manufacturing tab on issues screen → view production number. 

Field Production Order No. in Inventory Lot/ Serial History Screen
Manufacturing Tab of Issues Screen
Access Production Order Number

I have added the code however it pulls incorrect details for some inventory SKUs.

namespace PX.Objects.IN
{
public class InventoryLotSerInq_Extension : PXGraphExtension<PX.Objects.IN.InventoryLotSerInq>
{
#region Event Handlers

protected virtual void INTranSplit_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
if (e.Row == null)
return;

var row = (InventoryLotSerInq.INTranSplit)e.Row;

if (string.IsNullOrEmpty(row.RefNbr) || string.IsNullOrEmpty(row.DocType))
return;

PXTrace.WriteInformation($"Processing INTranSplit_RowSelected for DocType: {row.DocType}, RefNbr: {row.RefNbr}");

try
{
// Get the INRegister for this transaction
INRegister inRegister = PXSelect<INRegister,
Where<INRegister.docType, Equal<Required<INRegister.docType>>,
And<INRegister.refNbr, Equal<Required<INRegister.refNbr>>>>>
.Select(Base, row.DocType, row.RefNbr)
.RowCast<INRegister>()
.FirstOrDefault();

if (inRegister == null)
{
PXTrace.WriteWarning($"No INRegister found for DocType: {row.DocType}, RefNbr: {row.RefNbr}");
return;
}

// Get the AMBatNbr from the INRegister extension
INRegisterExt inRegisterExt = PXCache<INRegister>.GetExtension<INRegisterExt>(inRegister);
if (inRegisterExt == null)
{
PXTrace.WriteWarning("INRegisterExt is null");
return;
}

string amBatNbr = inRegisterExt.AMBatNbr;
PXTrace.WriteInformation($"Found INRegister with AMBatNbr: {amBatNbr}");

if (string.IsNullOrEmpty(amBatNbr))
return;

// Find the AMMTran record using AMBatNbr
AMMTran amMTran = PXSelect<AMMTran,
Where<AMMTran.batNbr, Equal<Required<AMMTran.batNbr>>>>
.Select(Base, amBatNbr)
.RowCast<AMMTran>()
.FirstOrDefault();

if (amMTran == null)
{
PXTrace.WriteWarning($"No AMMTran found for AMBatNbr: {amBatNbr}");
return;
}

// Get the Production Order ID and set it to our extension field
string prodOrdID = amMTran.ProdOrdID;
PXTrace.WriteInformation($"Found Production Order ID: {prodOrdID}");

if (!string.IsNullOrEmpty(prodOrdID))
{
INTranSplitExt rowExt = sender.GetExtension<INTranSplitExt>(row);
if (rowExt != null)
{
rowExt.UsrMAINProdOrdID = prodOrdID;
PXTrace.WriteInformation($"Successfully set UsrMAINProdOrdID to {prodOrdID}");
}
else
{
PXTrace.WriteWarning("INTranSplitExt is null");
}
}
}
catch (Exception ex)
{
PXTrace.WriteError($"Error in INTranSplit_RowSelected: {ex.Message}\n{ex.StackTrace}");
}
}

Please let me know if I have applied the correct filtering to get the production order number as mentioned in the steps above.

Thank you!

Best answer by TharidhiP

Thank you ​@aiwan and ​@MichaelShirk for the help. I have taken the suggestions into concern and the code modified below works to pull values specifically for transaction types ‘Issue’ and ‘Receipt’. Please find code below for reference, thank you!

 protected virtual void INTranSplit_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
if (e.Row == null)
return;

var row = (InventoryLotSerInq.INTranSplit)e.Row;

// Get the extension first
INTranSplitExt splitRowExt = sender.GetExtension<INTranSplitExt>(row);

// Only process receipts and issues
if (row.DocType != INDocType.Receipt && row.DocType != INDocType.Issue)
{
splitRowExt.UsrMAINProdOrdID = null;
return;
}

if (string.IsNullOrEmpty(row.RefNbr) || string.IsNullOrEmpty(row.DocType))
return;

PXTrace.WriteInformation($"Processing INTranSplit_RowSelected for DocType: {row.DocType}, RefNbr: {row.RefNbr}");

try
{
string prodOrdID = null;

if (row.DocType == INDocType.Receipt)
{
// Handle receipts
prodOrdID = GetProductionOrderFromReceipt(row);
}
else if (row.DocType == INDocType.Issue)
{
// Handle issues
prodOrdID = GetProductionOrderFromIssue(row);
}

// Set the production order ID if found
if (!string.IsNullOrEmpty(prodOrdID))
{
splitRowExt.UsrMAINProdOrdID = prodOrdID;
PXTrace.WriteInformation($"Successfully set UsrMAINProdOrdID to {prodOrdID}");
}
else
{
splitRowExt.UsrMAINProdOrdID = null;
PXTrace.WriteInformation("No production order found for this transaction");
}
}
catch (Exception ex)
{
PXTrace.WriteError($"Error in INTranSplit_RowSelected: {ex.Message}\n{ex.StackTrace}");
}
}

private string GetProductionOrderFromReceipt(InventoryLotSerInq.INTranSplit row)
{
try
{
// Get the INRegister for this transaction
INRegister inRegister = PXSelect<INRegister,
Where<INRegister.docType, Equal<Required<INRegister.docType>>,
And<INRegister.refNbr, Equal<Required<INRegister.refNbr>>>>>
.Select(Base, row.DocType, row.RefNbr)
.RowCast<INRegister>()
.FirstOrDefault();

if (inRegister == null)
{
PXTrace.WriteWarning($"No INRegister found for DocType: {row.DocType}, RefNbr: {row.RefNbr}");
return null;
}

// Get the AMBatNbr from the INRegister extension
INRegisterExt inRegisterExt = PXCache<INRegister>.GetExtension<INRegisterExt>(inRegister);
if (inRegisterExt == null)
{
PXTrace.WriteWarning("INRegisterExt is null");
return null;
}

string amBatNbr = inRegisterExt.AMBatNbr;
PXTrace.WriteInformation($"Found INRegister with AMBatNbr: {amBatNbr}");

if (string.IsNullOrEmpty(amBatNbr))
return null;

// Find the AMMTran record using AMBatNbr AND matching InventoryID
AMMTran amMTran = PXSelect<AMMTran,
Where<AMMTran.batNbr, Equal<Required<AMMTran.batNbr>>,
And<AMMTran.inventoryID, Equal<Required<AMMTran.inventoryID>>>>>
.Select(Base, amBatNbr, row.InventoryID)
.RowCast<AMMTran>()
.FirstOrDefault();

if (amMTran == null)
{
PXTrace.WriteWarning($"No AMMTran found for AMBatNbr: {amBatNbr}, InventoryID: {row.InventoryID}");
return null;
}

PXTrace.WriteInformation($"Found Production Order ID from receipt: {amMTran.ProdOrdID}");
return amMTran.ProdOrdID;
}
catch (Exception ex)
{
PXTrace.WriteError($"Error in GetProductionOrderFromReceipt: {ex.Message}");
return null;
}
}

private string GetProductionOrderFromIssue(InventoryLotSerInq.INTranSplit row)
{
try
{

// Look for AMMTran records that match this inventory transaction using INBatNbr and INDocType
AMMTran amMTran = PXSelect<AMMTran,
Where<AMMTran.inventoryID, Equal<Required<AMMTran.inventoryID>>,
And<AMMTran.iNBatNbr, Equal<Required<AMMTran.iNBatNbr>>,
And<AMMTran.iNDocType, Equal<Required<AMMTran.iNDocType>>>>>>
.Select(Base, row.InventoryID, row.RefNbr, row.DocType)
.RowCast<AMMTran>()
.FirstOrDefault();

if (amMTran != null)
{
PXTrace.WriteInformation($"Found Production Order ID from issue AMMTran: {amMTran.ProdOrdID}");
return amMTran.ProdOrdID;
}

//Check if the INRegister has manufacturing batch number
INRegister inRegister = PXSelect<INRegister,
Where<INRegister.docType, Equal<Required<INRegister.docType>>,
And<INRegister.refNbr, Equal<Required<INRegister.refNbr>>>>>
.Select(Base, row.DocType, row.RefNbr)
.RowCast<INRegister>()
.FirstOrDefault();

if (inRegister != null)
{
INRegisterExt inRegisterExt = PXCache<INRegister>.GetExtension<INRegisterExt>(inRegister);
if (inRegisterExt != null && !string.IsNullOrEmpty(inRegisterExt.AMBatNbr))
{
// Find AMMTran with this batch number
AMMTran batchMTran = PXSelect<AMMTran,
Where<AMMTran.batNbr, Equal<Required<AMMTran.batNbr>>,
And<AMMTran.inventoryID, Equal<Required<AMMTran.inventoryID>>>>>
.Select(Base, inRegisterExt.AMBatNbr, row.InventoryID)
.RowCast<AMMTran>()
.FirstOrDefault();

if (batchMTran != null)
{
PXTrace.WriteInformation($"Found Production Order ID from issue batch: {batchMTran.ProdOrdID}");
return batchMTran.ProdOrdID;
}
}
}

PXTrace.WriteWarning($"No production order found for issue transaction: {row.RefNbr}");
return null;
}
catch (Exception ex)
{
PXTrace.WriteError($"Error in GetProductionOrderFromIssue: {ex.Message}");
return null;
}
}

 

3 replies

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

@TharidhiP 

 

You may want to try to filter the doc type to just be a ‘Receipt’ I believe these are the only ones which would contain the correct production order number as per the ‘Move’ they relate to.

 


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

Hey ​@TharidhiP , 

I believe you could simplify this quite a bit by using the PXDBScalar attribute on your custom column. This effectively allows you to insert a simple subquery into the main data query for the DAC, and retrieve related information from the Database. 

I haven’t taken the time to write up the solution for your use case, but here is an example of one that I’ve created to add the Production Order Status to the “Create Purchase Orders” screen. 
 

 

Any reason this wouldn’t work for your use case?


Forum|alt.badge.img+2
  • Author
  • Pro III
  • Answer
  • July 28, 2025

Thank you ​@aiwan and ​@MichaelShirk for the help. I have taken the suggestions into concern and the code modified below works to pull values specifically for transaction types ‘Issue’ and ‘Receipt’. Please find code below for reference, thank you!

 protected virtual void INTranSplit_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
if (e.Row == null)
return;

var row = (InventoryLotSerInq.INTranSplit)e.Row;

// Get the extension first
INTranSplitExt splitRowExt = sender.GetExtension<INTranSplitExt>(row);

// Only process receipts and issues
if (row.DocType != INDocType.Receipt && row.DocType != INDocType.Issue)
{
splitRowExt.UsrMAINProdOrdID = null;
return;
}

if (string.IsNullOrEmpty(row.RefNbr) || string.IsNullOrEmpty(row.DocType))
return;

PXTrace.WriteInformation($"Processing INTranSplit_RowSelected for DocType: {row.DocType}, RefNbr: {row.RefNbr}");

try
{
string prodOrdID = null;

if (row.DocType == INDocType.Receipt)
{
// Handle receipts
prodOrdID = GetProductionOrderFromReceipt(row);
}
else if (row.DocType == INDocType.Issue)
{
// Handle issues
prodOrdID = GetProductionOrderFromIssue(row);
}

// Set the production order ID if found
if (!string.IsNullOrEmpty(prodOrdID))
{
splitRowExt.UsrMAINProdOrdID = prodOrdID;
PXTrace.WriteInformation($"Successfully set UsrMAINProdOrdID to {prodOrdID}");
}
else
{
splitRowExt.UsrMAINProdOrdID = null;
PXTrace.WriteInformation("No production order found for this transaction");
}
}
catch (Exception ex)
{
PXTrace.WriteError($"Error in INTranSplit_RowSelected: {ex.Message}\n{ex.StackTrace}");
}
}

private string GetProductionOrderFromReceipt(InventoryLotSerInq.INTranSplit row)
{
try
{
// Get the INRegister for this transaction
INRegister inRegister = PXSelect<INRegister,
Where<INRegister.docType, Equal<Required<INRegister.docType>>,
And<INRegister.refNbr, Equal<Required<INRegister.refNbr>>>>>
.Select(Base, row.DocType, row.RefNbr)
.RowCast<INRegister>()
.FirstOrDefault();

if (inRegister == null)
{
PXTrace.WriteWarning($"No INRegister found for DocType: {row.DocType}, RefNbr: {row.RefNbr}");
return null;
}

// Get the AMBatNbr from the INRegister extension
INRegisterExt inRegisterExt = PXCache<INRegister>.GetExtension<INRegisterExt>(inRegister);
if (inRegisterExt == null)
{
PXTrace.WriteWarning("INRegisterExt is null");
return null;
}

string amBatNbr = inRegisterExt.AMBatNbr;
PXTrace.WriteInformation($"Found INRegister with AMBatNbr: {amBatNbr}");

if (string.IsNullOrEmpty(amBatNbr))
return null;

// Find the AMMTran record using AMBatNbr AND matching InventoryID
AMMTran amMTran = PXSelect<AMMTran,
Where<AMMTran.batNbr, Equal<Required<AMMTran.batNbr>>,
And<AMMTran.inventoryID, Equal<Required<AMMTran.inventoryID>>>>>
.Select(Base, amBatNbr, row.InventoryID)
.RowCast<AMMTran>()
.FirstOrDefault();

if (amMTran == null)
{
PXTrace.WriteWarning($"No AMMTran found for AMBatNbr: {amBatNbr}, InventoryID: {row.InventoryID}");
return null;
}

PXTrace.WriteInformation($"Found Production Order ID from receipt: {amMTran.ProdOrdID}");
return amMTran.ProdOrdID;
}
catch (Exception ex)
{
PXTrace.WriteError($"Error in GetProductionOrderFromReceipt: {ex.Message}");
return null;
}
}

private string GetProductionOrderFromIssue(InventoryLotSerInq.INTranSplit row)
{
try
{

// Look for AMMTran records that match this inventory transaction using INBatNbr and INDocType
AMMTran amMTran = PXSelect<AMMTran,
Where<AMMTran.inventoryID, Equal<Required<AMMTran.inventoryID>>,
And<AMMTran.iNBatNbr, Equal<Required<AMMTran.iNBatNbr>>,
And<AMMTran.iNDocType, Equal<Required<AMMTran.iNDocType>>>>>>
.Select(Base, row.InventoryID, row.RefNbr, row.DocType)
.RowCast<AMMTran>()
.FirstOrDefault();

if (amMTran != null)
{
PXTrace.WriteInformation($"Found Production Order ID from issue AMMTran: {amMTran.ProdOrdID}");
return amMTran.ProdOrdID;
}

//Check if the INRegister has manufacturing batch number
INRegister inRegister = PXSelect<INRegister,
Where<INRegister.docType, Equal<Required<INRegister.docType>>,
And<INRegister.refNbr, Equal<Required<INRegister.refNbr>>>>>
.Select(Base, row.DocType, row.RefNbr)
.RowCast<INRegister>()
.FirstOrDefault();

if (inRegister != null)
{
INRegisterExt inRegisterExt = PXCache<INRegister>.GetExtension<INRegisterExt>(inRegister);
if (inRegisterExt != null && !string.IsNullOrEmpty(inRegisterExt.AMBatNbr))
{
// Find AMMTran with this batch number
AMMTran batchMTran = PXSelect<AMMTran,
Where<AMMTran.batNbr, Equal<Required<AMMTran.batNbr>>,
And<AMMTran.inventoryID, Equal<Required<AMMTran.inventoryID>>>>>
.Select(Base, inRegisterExt.AMBatNbr, row.InventoryID)
.RowCast<AMMTran>()
.FirstOrDefault();

if (batchMTran != null)
{
PXTrace.WriteInformation($"Found Production Order ID from issue batch: {batchMTran.ProdOrdID}");
return batchMTran.ProdOrdID;
}
}
}

PXTrace.WriteWarning($"No production order found for issue transaction: {row.RefNbr}");
return null;
}
catch (Exception ex)
{
PXTrace.WriteError($"Error in GetProductionOrderFromIssue: {ex.Message}");
return null;
}
}