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

Acumatica自定义字段计算存储及订单总毛利显示异常求助

Hey Krunal, let's break down and fix the two issues you're facing with your Acumatica customization:

Issue 1: UsrGrossProfit not saving to the database

The core problems here are event selection, incorrect currency field configuration, and unsafe persistence calls:

  • You’re using FieldSelecting to set the field value—this event only controls what’s displayed, not what gets persisted to the database. Changes made here won’t be marked as modified in the cache.
  • Your PXDBCurrency attribute references SOOrder fields instead of SOLine (where the custom field lives), and you’re missing a required base currency field for proper multi-currency handling.
  • There’s a typo in your calculation (invc.AvgCost should be inv.AvgCost), and calling Base.Save.Press()/Base.Persist() directly in an event risks infinite loops and unintended data saves.
Issue 2: UsrTotalGrossProfit not displaying on the order summary

The main issues here are incorrect initialization of the total value and misconfigured currency attributes:

  • You never reset the total to 0 before summing lines, so each time RowSelected fires, the total keeps accumulating old values.
  • The PXCurrency attribute uses unrelated fields (orderWeight), and the total field should either be bound with a base currency pair or marked as unbound (since it’s a calculated summary).

Fixed Full Code

using PX.Data;
using PX.Objects.IN;
using PX.Objects.SO;

public class SOOrderExtension : PXCacheExtension<SOOrder>
{
    #region UsrTotalGrossProfit
    public abstract class usrTotalGrossProfit : PX.Data.IBqlField { }
    
    // Use SOOrder's currency info and link to base currency field
    [PXCurrency(typeof(SOOrder.curyInfoID), typeof(usrTotalGrossProfitBase))]
    [PXDefault(TypeCode.Decimal, "0.0")]
    [PXUIField(DisplayName = "Total Gross Profit")]
    // Auto-sum line-level gross profit (cleaner than manual looping)
    [PXUnboundFormula(typeof(Sum<SOLineExtension.usrGrossProfit>), typeof(SumCalc<usrTotalGrossProfit>))]
    public virtual Decimal? UsrTotalGrossProfit { get; set; }
    
    // Required base currency field for total gross profit
    public abstract class usrTotalGrossProfitBase : PX.Data.IBqlField { }
    [PXDBDecimal(19, 4)]
    [PXDefault(TypeCode.Decimal, "0.0")]
    public virtual Decimal? UsrTotalGrossProfitBase { get; set; }
    #endregion
}

public class SOLineExtension : PXCacheExtension<SOLine>
{
    #region UsrGrossProfit
    public abstract class usrGrossProfit : PX.Data.IBqlField { }
    
    // Correct currency binding: use SOLine's curyInfoID and base currency field
    [PXDBCurrency(typeof(SOLine.curyInfoID), typeof(usrGrossProfitBase))]
    [PXDefault(TypeCode.Decimal, "0.0")]
    [PXUIField(DisplayName = "Gross Profit")]
    public virtual Decimal? UsrGrossProfit { get; set; }
    
    // Required base currency field for line-level gross profit
    public abstract class usrGrossProfitBase : PX.Data.IBqlField { }
    [PXDBDecimal(19, 4)]
    [PXDefault(TypeCode.Decimal, "0.0")]
    public virtual Decimal? UsrGrossProfitBase { get; set; }
    #endregion
}

public class SOOrderEntryExtension : PXGraphExtension<SOOrderEntry>
{
    // Recalculate when key line fields change
    protected virtual void SOLine_CuryUnitPrice_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
    {
        CalculateGrossProfit(cache, e.Row as SOLine);
    }

    protected virtual void SOLine_Qty_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
    {
        CalculateGrossProfit(cache, e.Row as SOLine);
    }

    protected virtual void SOLine_InventoryID_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
    {
        CalculateGrossProfit(cache, e.Row as SOLine);
    }

    protected virtual void SOLine_RowInserted(PXCache cache, PXRowInsertedEventArgs e)
    {
        CalculateGrossProfit(cache, e.Row as SOLine);
    }

    private void CalculateGrossProfit(PXCache cache, SOLine row)
    {
        if (row == null || Base.Document.Current?.Status == "C") return;

        SOLineExtension lineExt = cache.GetExtension<SOLineExtension>(row);
        InventoryItem inv = PXSelect<InventoryItem, Where<InventoryItem.inventoryID, Equal<Required<InventoryItem.inventoryID>>>>.Select(Base, row.InventoryID);

        if (inv?.ItemType == "F") // Stock Item
        {
            // Use CuryUnitPrice for currency-aware calculation
            lineExt.UsrGrossProfit = (row.CuryUnitPrice - inv.AvgCost) * row.Qty;
        }
        else
        {
            lineExt.UsrGrossProfit = 0; // Reset for non-stock items (adjust as needed)
        }

        // Mark field as modified to ensure persistence
        cache.SetValueExt<SOLineExtension.usrGrossProfit>(row, lineExt.UsrGrossProfit);
    }

    // Optional: Manual total calculation (if you don't want to use PXUnboundFormula)
    protected virtual void SOOrder_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
    {
        SOOrder row = e.Row as SOOrder;
        if (row == null) return;

        SOOrderExtension orderExt = cache.GetExtension<SOOrderExtension>(row);
        decimal total = 0;

        foreach (SOLine line in Base.Transactions.Select())
        {
            SOLineExtension lineExt = PXCache<SOLine>.GetExtension<SOLineExtension>(line);
            total += lineExt.UsrGrossProfit ?? 0;
        }

        orderExt.UsrTotalGrossProfit = total;
        cache.SetValueExt<SOOrderExtension.usrTotalGrossProfit>(row, total);
    }
}

Key Changes Explained

  1. Gross Profit Persistence Fixes:

    • Switched to FieldUpdated (for relevant fields) and RowInserted events—these mark fields as modified, ensuring changes are saved to the database.
    • Added base currency fields (UsrGrossProfitBase and UsrTotalGrossProfitBase) to comply with Acumatica’s multi-currency requirements for PXDBCurrency.
    • Fixed the invc typo and removed unsafe Save/Persist calls—Acumatica handles persistence automatically when fields are marked as modified.
  2. Total Gross Profit Display Fixes:

    • Used PXUnboundFormula to auto-sum line values (cleaner than manual looping), or included a corrected manual calculation in RowSelected that resets the total to 0 before summing.
    • Updated currency attributes to reference the correct parent fields for proper UI rendering.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 06:47:51