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

Acumatica中PXDBCalced用于PXRestrictor致AR303000栈溢出求助

问题背景

在CustomerExt扩展中添加了以下PXDBCalced字段,用于判断客户是否为父账户:

public abstract class isParent : PX.Data.BQL.BqlBool.Field<isParent> { }

[PXBool]
[PXUIField(DisplayName = "Is Parent")]
[PXDBCalced(typeof(IIf<
Exists<Select<BAccount,
       Where<BAccount.parentBAccountID, Equal<Customer.bAccountID>>>>,
True, False>), typeof(bool))]
public virtual bool? IsParent { get; set; }

随后在SOInvoiceExt图扩展中通过PXRestrictor使用该字段,阻止选择父客户:

[PXMergeAttributes(Method = MergeMethod.Append)]
[PXRestrictor(
    typeof(Where<CustomerExt.isParent, Equal<False>>),
    "Account cannot be selected because it is a parent account.",
    typeof(Customer.acctCD))]
protected void _(Events.CacheAttached<ARInvoice.customerID> e)
{
}

该限制可正常生效,但会导致客户维护页面(AR303000)出现栈溢出。推测存在循环引用,但无法理解为何该字段在SOInvoiceEntry的CustomerID及Customer GI中可正常运行,需寻找替代实现方式避免破坏AR303000页面。

替代实现方案

方案1:改用非计算字段,通过事件动态赋值

把IsParent从PXDBCalced改为普通PXBool字段,在Customer缓存的事件中计算赋值,避免数据库层面的循环查询:

1. 修改CustomerExt字段定义

public abstract class isParent : PX.Data.BQL.BqlBool.Field<isParent> { }

[PXBool]
[PXUIField(DisplayName = "Is Parent")]
public virtual bool? IsParent { get; set; }

2. 在CustomerMaint扩展中添加事件赋值

public class CustomerMaint_Extension : PXGraphExtension<CustomerMaint>
{
    protected void Customer_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
    {
        Customer customer = e.Row as Customer;
        if (customer == null) return;

        // 查询是否存在子账户
        bool hasChild = PXSelect<BAccount, 
            Where<BAccount.parentBAccountID, Equal<Required<BAccount.bAccountID>>>>
            .Select(Base, customer.BAccountID).Any();
        
        CustomerExt ext = cache.GetExtension<CustomerExt>(customer);
        ext.IsParent = hasChild;
    }
}

3. 保留原PXRestrictor逻辑

原SOInvoiceExt中的PXRestrictor可以继续使用,因为字段值已通过事件动态计算,不会触发数据库层面的循环查询。

方案2:直接在PXRestrictor中使用Exists条件,不依赖计算字段

去掉IsParent计算字段,直接在PXRestrictor中内嵌判断逻辑,从根源避免Customer缓存层面的循环引用:

[PXMergeAttributes(Method = MergeMethod.Append)]
[PXRestrictor(
    typeof(Where<
        Not<Exists<Select<BAccount, 
            Where<BAccount.parentBAccountID, Equal<Current<ARInvoice.customerID>>>>>>),
    "Account cannot be selected because it is a parent account.",
    typeof(Customer.acctCD))]
protected void _(Events.CacheAttached<ARInvoice.customerID> e)
{
}

这种方式不需要在CustomerExt中添加任何字段,直接在限制器中判断当前选中的客户是否有子账户,完全避开了可能引发循环的计算逻辑。

方案3:使用PXFormula替代PXDBCalced

如果需要保留字段用于显示(比如GI),可以改用PXFormula而非PXDBCalced——PXFormula是内存层面计算,不会触发数据库查询循环:

public abstract class isParent : PX.Data.BQL.BqlBool.Field<isParent> { }

[PXBool]
[PXUIField(DisplayName = "Is Parent")]
[PXFormula(typeof(IIf<
Exists<Select<BAccount,
       Where<BAccount.parentBAccountID, Equal<Customer.bAccountID>>>>,
True, False>))]
public virtual bool? IsParent { get; set; }

PXFormula仅在内存中计算字段值,不会像PXDBCalced那样在数据库查询时自动拼接SQL,从而避免AR303000页面加载时的循环查询栈溢出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 00:22:07