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

Acumatica投影DAC选中行数据空白,仅返回键值问题排查求助

问题描述

这是此前《Projection brings in duplicate data on DAC》问题的跟进,因属于同一用例下的独立问题故单独提出。

我定义了一个合并FSAppointmentDet与FSSODet的Projection DAC,并基于FSAppointmentDet的主键设置了SrvOrdType、LineNbr、RefNbr作为键值,以确保数据正确显示。流程页面加载时能正常展示记录,但选中任意行后,该行除LineNbr列外其余数据均变为空白。调试时发现,执行处理例程时,DAC实例中仅键值字段有值。请问我遗漏了什么配置?


DAC代码

[PXProjection(typeof(Select2<FSAppointmentDet,
    InnerJoin<FSSODet,
    On<FSSODet.refNbr, Equal<FSAppointmentDet.origSrvOrdNbr>,
       And<FSSODet.lineNbr, Equal<FSAppointmentDet.origLineNbr>>>>,
    Where<FSSODet.unitPrice, NotEqual<FSAppointmentDet.unitPrice>>>), Persistent = true)]
[PXCacheName("FixPriceProjection")]
public class PriceProjDAC : IBqlTable
{
    #region Selected
    public abstract class selected : IBqlField
    {
    }
    protected bool? _Selected = false;
    [PXBool]
    [PXDefault(false, PersistingCheck = PXPersistingCheck.Nothing)]
    [PXUIField(DisplayName = "Selected")]
    public virtual bool? Selected
    {
        get
        {
            return _Selected;
        }
        set
        {
            _Selected = value;
        }
    }
    #endregion       

    #region From the FSAppointmentDet DAC

    #region SrvOrdType
    public abstract class srvOrdType : PX.Data.BQL.BqlString.Field<srvOrdType> { }
    [PXDBString(IsKey = true, BqlField = typeof(FSAppointmentDet.srvOrdType))]
    public virtual string SrvOrdType { get; set; }
    #endregion

    #region LineNbr
    public abstract class lineNbr : PX.Data.BQL.BqlInt.Field<lineNbr> { }
    [PXDBInt(IsKey = true, BqlField = typeof(FSAppointmentDet.lineNbr))]
    public virtual int? LineNbr { get; set; }
    #endregion

    #region RefNbr
    public abstract class refNbr : PX.Data.BQL.BqlString.Field<refNbr> { }
    [PXDBString(IsKey = true, BqlField = typeof(FSAppointmentDet.refNbr))]
    public virtual string RefNbr { get; set; }
    #endregion

    #region OrigSrvOrdNbr
    public abstract class origSrvOrdNbr : PX.Data.BQL.BqlString.Field<origSrvOrdNbr> { }
    [PXDBString(BqlField = typeof(FSAppointmentDet.origSrvOrdNbr))]
    public virtual string OrigSrvOrdNbr { get; set; }
    #endregion

    #region FDUnitPrice
    [PXDBDecimal(4, BqlField = typeof(FSAppointmentDet.unitPrice))]
    [PXUIField(DisplayName = "Appt Price")]
    public virtual decimal? FDUnitPrice { get; set; }
    public abstract class fDUnitPrice : PX.Data.BQL.BqlDecimal.Field<fDUnitPrice> { }
    #endregion

    #endregion

    #region From the FSSODet DAC
    #region FSRefNbr
    [PXDBString(20, IsUnicode = true, BqlField = typeof(FSSODet.refNbr))]
    public virtual string FSRefNbr { get; set; }
    public abstract class fSRefNbr : PX.Data.BQL.BqlString.Field<fSRefNbr> { }
    #endregion

    #region FSLineNbr
    [PXDBInt(BqlField = typeof(FSSODet.lineNbr))]
    public virtual int? FSLineNbr { get; set; }
    public abstract class fSLineNbr : PX.Data.BQL.BqlInt.Field<fSLineNbr> { }
    #endregion

    #region FSUnitPrice
    [PXDBDecimal(4, BqlField = typeof(FSSODet.unitPrice))]
    [PXUIField(DisplayName = "SO Price")]
    public virtual decimal? FSUnitPrice { get; set; }
    public abstract class fSUnitPrice : PX.Data.BQL.BqlDecimal.Field<fSUnitPrice> { }
    #endregion

    #region TranDesc
    [PXDBString(255, IsUnicode = true, BqlField = typeof(FSSODet.tranDesc))]
    public virtual string TranDesc { get; set; }
    public abstract class tranDesc : PX.Data.BQL.BqlString.Field<tranDesc> { }
    #endregion
    #endregion
}

流程页面Graph代码

public class PSFixPrice : PXGraph<PSFixPrice>
{
    // Define the processing view which will select all   
    // correct records baswd on the definition in the projection
     public PXProcessing<PriceProjDAC> Processing;


    public PSFixPrice()
    {
        var tst = new PriceProjDAC();
        int i = 0;
        // Acuminator disable once PX1008 LongOperationDelegateSynchronousExecution [Justification]
        Processing.SetProcessDelegate(
                delegate (List<PriceProjDAC> list)
                {
                    DoLogic(list, Processing.Cache);
                });
    }
    static public void DoLogic(IList records, PXCache onCache)
    {
        var onGraph = onCache.Graph;
        PXLongOperation.StartOperation(onGraph, delegate ()
        {
            var cacheName = onCache.GetName();
            using (PXTransactionScope FSSODETScope = new PXTransactionScope())
            {
                foreach (var item in records ) 
                {
                    try
                    {
                        PriceProjDAC projDAC = item as PriceProjDAC;
                        if (projDAC != null) 
                        {
                            ProcessItem(projDAC, onCache);
                        }
                    } catch { }
                }
                // Save all changes after processing all items
                // onGraph.Actions.PressSave();
                FSSODETScope.Complete();

            } // End PXTransactionScope
        }); // PXLongOperation end brace
    }  // doLogic end brace
    // Define the processing logic for each item
    static private void ProcessItem(PriceProjDAC item, PXCache onCache)
    {

        if (item.Selected == true)
        {
            // Update the FSSODet UnitPrice with FSAppointmentDet UnitPrice
      //      item.FSUnitPrice = item.FDUnitPrice;

            // Update the record in the cache
      //      onCache.Update(item);
        }

    }

}

ASPX代码

<%@ Page Language="C#" MasterPageFile="~/MasterPages/ListView.master" AutoEventWireup="true" ValidateRequest="false" CodeFile="PS305020.aspx.cs" Inherits="Page_PS305020" Title="SO Price Fix Page" %>
<%@ MasterType VirtualPath="~/MasterPages/ListView.master" %>

<asp:Content ID="cont1" ContentPlaceHolderID="phDS" Runat="Server">
    <px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%"
        TypeName="ASGFix.PSFixPrice"
        PrimaryView="Processing"
        >
        <CallbackCommands>

        </CallbackCommands>
    </px:PXDataSource>
</asp:Content>
<asp:Content ID="cont2" ContentPlaceHolderID="phL" runat="Server">
    <px:PXGrid ID="grid" runat="server" DataSourceID="ds" Width="100%" Height="150px" SkinID="Primary" 
    AdjustPageSize="Auto" AllowPaging="True" AllowAutoHide="false">
    <AutoSize Enabled="True" />
        <Levels>
            <px:PXGridLevel DataMember="Processing">
                <Columns>
                    <px:PXGridColumn DataField="Selected" Type="CheckBox" Width="60" ></px:PXGridColumn>
                    <px:PXGridColumn DataField="OrigSrvOrdNbr" Width="150" ></px:PXGridColumn>
                    <px:PXGridColumn DataField="LineNbr" Width="70" ></px:PXGridColumn>
                    <px:PXGridColumn DataField="InventoryID" Width="75" ></px:PXGridColumn>
                    <px:PXGridColumn DataField="FDUnitPrice" Width="75" ></px:PXGridColumn>                                     
                    <px:PXGridColumn DataField="FSUnitPrice" Width="75" ></px:PXGridColumn>                                     
                </Columns>
            </px:PXGridLevel>
        </Levels>
        <AutoSize Container="Window" Enabled="True" MinHeight="150" />
        <ActionBar >
        </ActionBar>
    </px:PXGrid>
</asp:Content>

解决方案

1. 修正DAC字段的抽象类顺序

Acumatica要求DAC的抽象类必须在对应属性之前定义,否则BQL无法正确解析字段,导致非键字段加载失败。调整所有字段的定义顺序:

#region FDUnitPrice
public abstract class fDUnitPrice : PX.Data.BQL.BqlDecimal.Field<fDUnitPrice> { }
[PXDBDecimal(4, BqlField = typeof(FSAppointmentDet.unitPrice))]
[PXUIField(DisplayName = "Appt Price")]
public virtual decimal? FDUnitPrice { get; set; }
#endregion

#region From the FSSODet DAC
#region FSRefNbr
public abstract class fSRefNbr : PX.Data.BQL.BqlString.Field<fSRefNbr> { }
[PXDBString(20, IsUnicode = true, BqlField = typeof(FSSODet.refNbr))]
public virtual string FSRefNbr { get; set; }
#endregion

#region FSLineNbr
public abstract class fSLineNbr : PX.Data.BQL.BqlInt.Field<fSLineNbr> { }
[PXDBInt(BqlField = typeof(FSSODet.lineNbr))]
public virtual int? FSLineNbr { get; set; }
#endregion

#region FSUnitPrice
public abstract class fSUnitPrice : PX.Data.BQL.BqlDecimal.Field<fSUnitPrice> { }
[PXDBDecimal(4, BqlField = typeof(FSSODet.unitPrice))]
[PXUIField(DisplayName = "SO Price")]
public virtual decimal? FSUnitPrice { get; set; }
#endregion

#region TranDesc
public abstract class tranDesc : PX.Data.BQL.BqlString.Field<tranDesc> { }
[PXDBString(255, IsUnicode = true, BqlField = typeof(FSSODet.tranDesc))]
public virtual string TranDesc { get; set; }
#endregion
#endregion

2. 移除ASPX中的无效列

ASPX网格中配置了InventoryID字段,但DAC中未定义该字段,会导致页面渲染错误,直接移除该列:

<Columns>
    <px:PXGridColumn DataField="Selected" Type="CheckBox" Width="60" ></px:PXGridColumn>
    <px:PXGridColumn DataField="OrigSrvOrdNbr" Width="150" ></px:PXGridColumn>
    <px:PXGridColumn DataField="LineNbr" Width="70" ></px:PXGridColumn>
    <px:PXGridColumn DataField="FDUnitPrice" Width="75" ></px:PXGridColumn>                                     
    <px:PXGridColumn DataField="FSUnitPrice" Width="75" ></px:PXGridColumn>                                     
</Columns>

3. 在处理例程中重新加载完整记录

PXProcessing传递给委托的对象仅包含键值字段,需要通过键值重新从缓存或数据库获取完整的Projection记录:

static private void ProcessItem(PriceProjDAC item, PXCache onCache)
{
    if (item.Selected == true)
    {
        // 通过键值重新加载完整记录
        PriceProjDAC fullItem = onCache.GetItem(item) as PriceProjDAC;
        if (fullItem == null)
        {
            // 缓存中无数据时从数据库查询
            fullItem = PXSelect<PriceProjDAC, 
                Where<PriceProjDAC.srvOrdType, Equal<Required<PriceProjDAC.srvOrdType>>,
                And<PriceProjDAC.refNbr, Equal<Required<PriceProjDAC.refNbr>>,
                And<PriceProjDAC.lineNbr, Equal<Required<PriceProjDAC.lineNbr>>>>>>
                .Select(onCache.Graph, item.SrvOrdType, item.RefNbr, item.LineNbr);
        }

        if (fullItem != null)
        {
            // 注意:Projection是只读视图,需直接更新源DAC(FSSODet)
            FSSODet soDet = PXSelect<FSSODet, 
                Where<FSSODet.refNbr, Equal<Required<FSSODet.refNbr>>,
                And<FSSODet.lineNbr, Equal<Required<FSSODet.lineNbr>>>>>
                .Select(onCache.Graph, fullItem.OrigSrvOrdNbr, fullItem.FSLineNbr);
            
            if (soDet != null)
            {
                soDet.UnitPrice = fullItem.FDUnitPrice;
                onCache.Graph.Caches[typeof(FSSODet)].Update(soDet);
                onCache.Graph.Actions.PressSave();
            }
        }
    }
}

关键原因总结

  • DAC字段的抽象类顺序错误,导致BQL无法解析非键字段,加载时丢失数据;
  • ASPX中存在未定义的列,干扰页面数据渲染;
  • PXProcessing默认仅传递键值字段,需手动重新加载完整记录才能获取非键字段的值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 01:37:03