Skip to main content
Answer

Copy Qty. to Produce Value for Child Production Orders in Production Order Maintenance Screen (AM201500)

  • October 24, 2024
  • 12 replies
  • 179 views

Forum|alt.badge.img+2

Hi, I have a requirement as follows and need to know how this can be catered via customizing the action.

When I click on “Generate Orders for Subassemblies” for a parent item such as given below with a qty to produce of 10.00 

When the button is pressed the child items also need to be set to a qty to produce of 10.00, currently the system sets it to 1.00 as shown below

How can I also copy over the qty to produce as well over to the child items once the button is pressed?

Best answer by TharidhiP

Thanks ​@darylbowman for the suggestions. I also tried the row inserted event handler for this and it works as well. Is the method used below correct?

 //ADD ROWINSERTED LOGIC
protected virtual void _(Events.RowInserted<AMProdItem> e)
{
UpdateChildOrder(e.Cache, e.Row);
}

protected virtual void _(Events.RowUpdated<AMProdItem> e)
{
UpdateChildOrder(e.Cache, e.Row);
}

private void UpdateChildOrder(PXCache cache, AMProdItem row)
{
if (row == null || cache == null)
return;

// Ensure this is a child order with a valid parent
if (string.IsNullOrEmpty(row.ParentOrdID))
return;

// Query the parent order using PK.Find
var parentOrder = AMProdItem.PK.Find(Base, row.ParentOrderType, row.ParentOrdID);
if (parentOrder == null)
return;

// Update the child order fields using parent order values
cache.SetValueExt<AMProdItem.qtytoProd>(row, parentOrder.QtytoProd);

if (!string.IsNullOrEmpty(parentOrder.BOMRevisionID))
{
cache.SetValueExt<AMProdItem.bOMRevisionID>(row, parentOrder.BOMRevisionID);
}

PXTrace.WriteInformation($"Child order updated: {row.ProdOrdID}, Parent: {parentOrder.ProdOrdID}, QtyToProd: {row.QtytoProd}");
}


//END OF ROW INSERTED LOGIC

 

12 replies

lauraj46
Captain II
Forum|alt.badge.img+8
  • Captain II
  • October 24, 2024

Hi @TharidhiP ,

That should already be the default behavior.  This is the documentation for 2024 R1:

Do you have any customizations on this screen?

Laura


Forum|alt.badge.img+2
  • Author
  • Pro III
  • October 24, 2024

Hi @lauraj46 thank you for this information! I am currently using Build 2023.115.301.7250 which would explain why this function is not available. Would you be able to  provide a ink to his documentation please? 

I do have customizations on this screen as well.


Forum|alt.badge.img+2
  • Author
  • Pro III
  • November 17, 2024

Hi All,

Is the logic given below fulfill my requirement? 

 namespace PX.Objects.AM
{
public class ProdMaint_Extension : PXGraphExtension<ProdMaint.ItemLineSplittingExtension,PX.Objects.AM.ProdMaint>
{ protected virtual void _(Events.RowInserting<ProdMaintRecord> e)
{
if (e.Row == null || string.IsNullOrEmpty(e.Row.ParentProdOrdID)) return;

// Get the parent production order
ProdMaintRecord parentOrder = PXSelect<ProdMaintRecord,
Where<ProdMaintRecord.prodOrdID, Equal<Required<ProdMaintRecord.prodOrdID>>>>
.Select(Base, e.Row.ParentProdOrdID);

if (parentOrder != null)
{
e.Row.QtyToProduce = parentOrder.QtyToProduce;
e.Row.QtyRemaining = parentOrder.QtyToProduce;
}
}
}
}

I currently get this error: 

The type or namespace name 'ProdMaintRecord' could not be found (are you missing a using directive or an assembly reference?)

Any help would be appreciated, thank you!


Forum|alt.badge.img+2
  • Author
  • Pro III
  • November 18, 2024

Adding to the above comment I have also tried this override method but it has no effect. 

   [PXOverride]
public virtual IEnumerable CreateLinkedOrders(PXAdapter adapter, Func<PXAdapter, IEnumerable> baseMethod)
{
var result = baseMethod.Invoke(adapter); // Call the original logic to create child orders

// Ensure the current production order is loaded
if (Base.ProdMaintRecords.Current == null)
return result;

// Get the parent production order's QtyToProduce
decimal? parentQtyToProduce = Base.ProdMaintRecords.Current.QtytoProd;

// Update the QtyToProduce for child items
foreach (AMProdItem childOrder in PXSelect<AMProdItem,
Where<AMProdItem.parentOrdID, Equal<Required<AMProdItem.parentOrdID>>>>.Select(Base, Base.ProdMaintRecords.Current.ProdOrdID))
{
if (childOrder != null && parentQtyToProduce.HasValue)
{
childOrder.QtytoProd = parentQtyToProduce;
Base.ProdMaintRecords.Update(childOrder); // Update the record in the cache
}
}

Base.Actions.PressSave(); // Save the changes to the database

return result;
}

 


DipakNilkanth
Pro III
Forum|alt.badge.img+13

Hi ​@TharidhiP,

As ​@lauraj46  said, This is the standard behavior of copying the same quantity from the production order, as it shows the total quantity for the specific production order linked to the project.

If you are trying to achieve this with code, that's fine. Are you able to debug the code? Is it hitting the overridden method?

One more suggestion: if the debugger is hitting inside the method, try updating the view ProjectProdOrders Ensure that this change does not affect functionality elsewhere, as you are overriding the standard behavior.

Hope, it helps!


darylbowman
Captain II
Forum|alt.badge.img+15

Assuming your research is thorough, I think this override signature is wrong. It should be:

public delegate IEnumerable CreateLinkedOrdersDelegate(PXAdapter adapter);

[PXOverride]
public IEnumerable CreateLinkedOrders(PXAdapter adapter, CreateLinkedOrdersDelegate baseMethod)
        {

var result = baseMethod(adapter);


Forum|alt.badge.img+2
  • Author
  • Pro III
  • November 18, 2024

Hi ​@darylbowman ​@Dipak Nilkanth I have stepped through the code below and updated as per your suggestions: 

namespace PX.Objects.AM
{
public class ProdMaint_Extension : PXGraphExtension<ProdMaint.ItemLineSplittingExtension,PX.Objects.AM.ProdMaint>
{
public delegate IEnumerable CreateLinkedOrdersDelegate(PXAdapter adapter);

[PXOverride]
public IEnumerable CreateLinkedOrders(PXAdapter adapter, CreateLinkedOrdersDelegate baseMethod)
{
var result = baseMethod(adapter); // Call the original logic to create child orders

// Ensure the current production order is loaded
if (Base.ProdMaintRecords.Current == null)
return result;

// Get the parent production order's QtyToProduce
decimal? parentQtyToProduce = Base.ProdMaintRecords.Current.QtytoProd;
string parentBOMRevisionID = Base.ProdMaintRecords.Current.BOMRevisionID;

// Start with the first child orders
string currentParentID = Base.ProdMaintRecords.Current.ProdOrdID;

// Continue finding and updating children until no more are found
while (!string.IsNullOrEmpty(currentParentID))
{
// Find child order for current parent
var childOrder = (AMProdItem)PXSelect<AMProdItem,
Where<AMProdItem.parentOrdID, Equal<Required<AMProdItem.parentOrdID>>>>
.Select(Base, currentParentID)
.FirstOrDefault();

if (childOrder != null && parentQtyToProduce.HasValue)
{
// Update quantity for this child
childOrder.QtytoProd = parentQtyToProduce;
if (!string.IsNullOrEmpty(parentBOMRevisionID))
childOrder.BOMRevisionID = parentBOMRevisionID;

Base.ProdMaintRecords.Update(childOrder);

// Move to next parent in the chain
currentParentID = childOrder.ProdOrdID;
}
else
{
// No more children found, exit the loop
break;
}
}

Base.Actions.PressSave(); // Save all changes to the database

return result;
}
}
}

I encounter a null reference error when modified: 

Exception Type:
System.NullReferenceException
 Message:
Object reference not set to an instance of an object.
 Stack Trace:
 at PX.Data.PXFirstChanceExceptionLogger.ProfilerFirstChanceException(Object o, FirstChanceExceptionEventArgs args) at PX.Objects.AM.GLAccountHelper.GetAcctID[Field](PXGraph graph, String AcctDefault, InventoryItem inventoryItem, INSite site, INPostClass postClass, AMOrderType amOrderType) at PX.Objects.AM.GLAccountHelper.GetAccountDefaults[Field](PXGraph graph, InventoryItem inventoryItem, INSite site, INPostClass postclass, AMOrderType amOrderType) at PX.Objects.AM.ProdMaint.SetAccountDefaults(PXCache cache) at PX.Objects.AM.ProdMaint.AMProdItem_InventoryID_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e) at PX.Data.PXFieldUpdated.Invoke(PXCache sender, PXFieldUpdatedEventArgs args) at PX.Objects.AM.ProdMaint_Extension.AMProdItem_InventoryID_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e, PXFieldUpdated baseHandler) at PX.Data.PXCache.OnFieldUpdated(String name, Object row, Object oldValue, Boolean externalCall) at PX.Data.PXCache`1.FillWithValues(TNode item, TNode copy, TNode newitem) at PX.Data.PXCache`1.Update(Object data, Boolean bypassinterceptor) at PX.Data.PXSelectBase`1.Update(Table item) at PX.Objects.AM.CriticalMaterialsInq.CreateNewProdItem(AMProdItem parentAMProdItem, Decimal qtyToProduce, AMProdMatl prodMatl, CreateProdFilter filter, Numbering numbering, ProdMaint prodMaintGraph) at PX.Objects.AM.CriticalMaterialsInq.CreateProductionOrder(AMProdItem prodCreatedFrom, Decimal qtyToProduce, AMProdMatl prodMatl, CreateProdFilter filter, Numbering numbering, ProdMaint prodMaintGraph, Int32 level, List`1 ordersList, Nullable`1 splitPlanID) at PX.Objects.AM.CriticalMaterialsInq.CreateLinkedProductionOrders(AMProdItem prodCreatedFrom, CreateProdFilter filter, ProdMaint prodMaintGraph, Numbering numbering, Int32 level, List`1 ordersList) at PX.Objects.AM.CriticalMaterialsInq.CreateLinkedProductionOrders(AMProdItem prodCreatedFrom, List`1 ordersList) at PX.Objects.AM.ProdMaint.<>c__DisplayClass107_0.<CreateLinkedOrders>b__0() at PX.Concurrency.CancellationIgnorantExtensions.RunWithCancellationViaThreadAbort(Action method, CancellationToken cancellationToken) at PX.Concurrency.CancellationIgnorantExtensions.<>c__DisplayClass1_0.<ToCancellationViaThreadAbort>b__0(CancellationToken cancellationToken) at PX.Concurrency.Internal.PXLongOperationPars.PopAndRunDelegate(CancellationToken cancellationToken) at PX.Concurrency.Internal.RuntimeLongOperationManager.PerformOperation(PXLongOperationPars p) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at PX.Concurrency.Internal.PXThreadPool.RunItem(RequestItem item) at PX.Concurrency.Internal.PXThreadPool.Run() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx) at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Threading.ThreadHelper.ThreadStart()

darylbowman
Captain II
Forum|alt.badge.img+15

Have you tried debugging to see how far your code is getting?


Forum|alt.badge.img+2
  • Author
  • Pro III
  • November 22, 2024

Hi ​@darylbowman I have stepped through the code and it stops at the BQL statement →

var childOrder = (AMProdItem)PXSelect<AMProdItem, Where<AMProdItem.parentOrdID, Equal<Required<AMProdItem.parentOrdID>>>> .Select(Base, currentParentID) .FirstOrDefault();


darylbowman
Captain II
Forum|alt.badge.img+15

There's only a couple things it could be. If currentParentID isn't null, I'm guessing it's returning no record, and it may be the cast causing the issue.


Forum|alt.badge.img+2
  • Author
  • Pro III
  • Answer
  • November 26, 2024

Thanks ​@darylbowman for the suggestions. I also tried the row inserted event handler for this and it works as well. Is the method used below correct?

 //ADD ROWINSERTED LOGIC
protected virtual void _(Events.RowInserted<AMProdItem> e)
{
UpdateChildOrder(e.Cache, e.Row);
}

protected virtual void _(Events.RowUpdated<AMProdItem> e)
{
UpdateChildOrder(e.Cache, e.Row);
}

private void UpdateChildOrder(PXCache cache, AMProdItem row)
{
if (row == null || cache == null)
return;

// Ensure this is a child order with a valid parent
if (string.IsNullOrEmpty(row.ParentOrdID))
return;

// Query the parent order using PK.Find
var parentOrder = AMProdItem.PK.Find(Base, row.ParentOrderType, row.ParentOrdID);
if (parentOrder == null)
return;

// Update the child order fields using parent order values
cache.SetValueExt<AMProdItem.qtytoProd>(row, parentOrder.QtytoProd);

if (!string.IsNullOrEmpty(parentOrder.BOMRevisionID))
{
cache.SetValueExt<AMProdItem.bOMRevisionID>(row, parentOrder.BOMRevisionID);
}

PXTrace.WriteInformation($"Child order updated: {row.ProdOrdID}, Parent: {parentOrder.ProdOrdID}, QtyToProd: {row.QtytoProd}");
}


//END OF ROW INSERTED LOGIC

 


darylbowman
Captain II
Forum|alt.badge.img+15

It seems fine to me