You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Acumatica生产订单维护:父订单生产数量同步至子订单

需求说明
  • 在Acumatica生产订单维护界面,为生产数量为10.00的父生产订单点击「Generate Orders for Subassemblies(生成子装配件订单)」按钮时,需将生成的子生产订单的**Qty. to Produce(生产数量)**同步设置为10.00,当前系统默认设置为1.00。
已尝试的实现方案及问题

方案一:通过RowInserted/RowUpdated事件更新子订单

通过监听AMProdItem的RowInserted和RowUpdated事件,在子订单创建后更新其生产数量:

namespace PX.Objects.AM
{
  public class ProdMaint_Extension : PXGraphExtension<ProdMaint.ItemLineSplittingExtension,PX.Objects.AM.ProdMaint>
  {  
    //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
  }
}

问题:不确定该逻辑是否正确覆盖子订单生成的场景,可能存在子订单创建时事件未触发或执行顺序问题。

方案二:重写CreateLinkedOrders方法

通过PXOverride重写CreateLinkedOrders方法,在子订单生成后批量更新生产数量:

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;
    }      
  }
}

问题:触发NullReferenceException异常,错误栈如下:

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()
正确实现方案

最佳方式是在子订单创建的源头修改生产数量,避免事后更新触发的字段事件异常。通过重写CriticalMaterialsInq中的CreateNewProdItem方法,直接传递父订单的生产数量:

namespace PX.Objects.AM
{
    public class CriticalMaterialsInq_Extension : PXGraphExtension<CriticalMaterialsInq>
    {
        public delegate AMProdItem CreateNewProdItemDelegate(AMProdItem parentAMProdItem, decimal qtyToProduce, AMProdMatl prodMatl, CreateProdFilter filter, Numbering numbering, ProdMaint prodMaintGraph);

        [PXOverride]
        public AMProdItem CreateNewProdItem(AMProdItem parentAMProdItem, decimal qtyToProduce, AMProdMatl prodMatl, CreateProdFilter filter, Numbering numbering, ProdMaint prodMaintGraph, CreateNewProdItemDelegate baseMethod)
        {
            // 替换默认的1.00为父订单的生产数量
            qtyToProduce = parentAMProdItem.QtytoProd ?? qtyToProduce;
            
            // 调用原方法创建子订单
            var childOrder = baseMethod(parentAMProdItem, qtyToProduce, prodMatl, filter, numbering, prodMaintGraph);

            // 同步父订单的BOM版本号(如果需要)
            if (!string.IsNullOrEmpty(parentAMProdItem.BOMRevisionID))
            {
                prodMaintGraph.ProdMaintRecords.Cache.SetValueExt<AMProdItem.bOMRevisionID>(childOrder, parentAMProdItem.BOMRevisionID);
            }

            return childOrder;
        }
    }
}

说明:

  • 该方案直接在子订单创建时使用父订单的生产数量,无需事后更新,避免了缓存状态不一致和字段事件异常。
  • 同步BOM版本号的逻辑可选,根据实际需求保留。
异常原因分析(方案二)
  1. 字段更新事件触发空引用:调用Base.ProdMaintRecords.Update(childOrder)时,子订单的关联数据(如INSite、AMOrderType的GL账户配置)未完全加载,导致AMProdItem_InventoryID_FieldUpdated事件触发时,GLAccountHelper.GetAcctID方法中出现空对象引用。
  2. 循环逻辑缺陷:代码中每次仅查询第一个子订单,无法处理一个父订单对应多个子订单的场景,且递归更新的逻辑不符合子订单层级关系。
  3. 手动保存冲突:原CreateLinkedOrders方法内部已处理保存操作,额外调用Base.Actions.PressSave()会导致重复保存,引发缓存状态冲突。

内容的提问来源于stack exchange,提问作者Tharidhi P

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 02:49:51