PXFormula 属性

一般说明

Acumatica 中的公式是 DAC 字段,它是根据其他对象字段的值计算的。

为了计算公式,Aсumatiсa 框架提供了一组各种操作和函数(例如算术,逻辑和比较运算以及字符串处理函数;请参阅内置公式列表 )。除了字段值之外,公式还可以使用 Acumatica 核心和应用程序解决方案提供的各种常量。此外,公式不仅可以从当前记录中获得计算值,还可以从其他来源获得计算值(请参阅公式上下文及其修饰符 )。

公式的美妙之处在于它们会在适当的时间自动重新计算价值:

  • 字段默认(插入新行;公式字段的 FieldDefaulting 事件处理程序)
  • 关于依赖字段的更新(每个依赖字段的 FieldUpdated 事件处理程序)
  • 关于数据库选择(仅适用于未绑定字段; RowSelecting 事件处理程序)
  • 在需要时保持数据库持久化(开发人员应明确指定它; RowPersisted 事件处理程序)

在依赖字段的更新上重新计算公式字段值会引发公式字段的 FieldUpdated 事件。这允许你创建一系列相关公式(请参阅公式中的直接和中介循环引用)。

应用程序开发人员可以编写自己的应用程序端公式

使用方式

公式可以用于三种主要模式:

  • 只需计算值并将其分配给公式字段(请参阅基本用法
  • 从公式字段的现有值计算聚合值并将其分配给父对象中的指定字段(请参阅聚合用法
  • 混合模式:计算公式值,将其分配给公式字段,计算聚合值,并将其分配给父对象中的字段(请参阅组合使用

还有另一种辅助模式,未绑定的公式,与混合模式非常相似,但公式的计算值未分配给公式字段。立即计算聚合值并将其分配给父对象的字段。有关详细信息,请参阅未绑定公式的用法

PXFormulaAttribute 属性和构造函数参数

公式功能由 PXFormulaAttribute 实现。PXFormulaAttribute 的构造函数具有以下签名:

public PXFormulaAttribute(Type formulaType)
{
    // ...
}

单个参数 formulaType 是一种公式表达式,用于计算来自同一数据记录的其他字段的字段值。此参数必须满足以下条件之一:

  • 必须实现 IBqlField 接口
  • 必须是 BQL 常量
  • 必须实现 IBqlCreator 接口(参见内置常用公式列表
public PXFormulaAttribute(Type formulaType, Type aggregateType)
{
    // ...
}

第一个参数 formulaType 与第一个构造函数中的相同。第二个参数 aggregateType 是一种聚合公式,用于计算子数据记录字段中的父数据记录字段。可以使用聚合函数,例如 SumCalc,CountCalc,MinCalc 和 MaxCalc。应用开发人员可以创建自己的聚合公式。

聚合公式类型必须是泛型类型,并且必须实现 IBqlAggregateCalculator 接口。聚合公式类型的第一个通用参数必须实现 IBqlField 接口,并且必须具有父对象的字段类型。

public virtual bool Persistent { get; set; }

PXFormulaAttribute.Persistent 属性指示在将更改保存到数据库后属性是否重新计算公式。如果在 RowPersisting 事件中更新了公式所依赖的字段,则可能需要重新计算。默认情况下,该属性等于 false

用法

在大多数情况下,公式用于直接计算来自同一数据记录的其他字段的公式字段的值。

最简单的公式用法示例:

[PXDBDate]
[PXFormula(typeof(FADetails.receiptDate))]
[PXDefault]
[PXUIField(DisplayName = Messages.PlacedInServiceDate)]
public virtual DateTime? DepreciateFromDate { get; set; }

在此示例中,ReceiptDate 字段的值在插入新记录和 ReceiptDate 字段更新时分配给 DepreciateFromDate 字段。

一个稍微复杂的例子:

[PXCurrency(typeof(APPayment.curyInfoID), typeof(APPayment.unappliedBal))]
[PXUIField(DisplayName = "Unapplied Balance", Visibility = PXUIVisibility.Visible, Enabled = false)]
[PXFormula(typeof(Sub<APPayment.curyDocBal, APPayment.curyApplAmt>))]
public virtual Decimal? CuryUnappliedBal { get; set; }

这里,文档的未应用余额计算为文档余额与应用金额之间的差额。

具有默认值的多项选择示例:

[PXUIField(DisplayName = "Class Icon", IsReadOnly = true)]
[PXImage]
[PXFormula(typeof(Switch<
    Case<Where<EPActivity.classID, Equal<CRActivityClass.task>>, EPActivity.classIcon.task,
    Case<Where<EPActivity.classID, Equal<CRActivityClass.events>>, EPActivity.classIcon.events,
    Case<Where<EPActivity.classID, Equal<CRActivityClass.email>,
        And<EPActivity.isIncome, NotEqual<True>>>, EPActivity.classIcon.email,
    Case<Where<EPActivity.classID, Equal<CRActivityClass.email>,
        And<EPActivity.isIncome, Equal<True>>>, EPActivity.classIcon.emailResponse,
    Case<Where<EPActivity.classID, Equal<CRActivityClass.history>>, EPActivity.classIcon.history>>>>>,
    Selector<Current2<EPActivity.type>, EPActivityType.imageUrl>>))]
public virtual string ClassIcon { get; set; }

领域顺序

DAC 中的字段顺序对于校正公式计算很重要。必须在公式字段之前的 DAC 中定义包括其他公式的所有源字段(从中计算公式)。否则,可能会错误地计算字段,或者可能导致运行时错误。

公式语境及其修饰语

默认情况下,公式计算的上下文受包含公式声明的类的当前对象(记录)限制。它也允许使用常量(Constant<> 类的后代)。

仅使用其对象字段的公式:

public partial class Contract : IBqlTable, IAttributeSupport
{
    //...
    [PXDecimal(4)]
    [PXDefault(TypeCode.Decimal, "0.0", PersistingCheck = PXPersistingCheck.Nothing)]
    [PXFormula(typeof(Add<Contract.pendingRecurring, Add<Contract.pendingRenewal, Contract.pendingSetup>>))]
    [PXUIField(DisplayName = "Total Pending", Enabled=false)]
    public virtual decimal? TotalPending { get; set; }
    //...
}

但是,可以从其他来源获取公式计算的输入值:

  • BLC 中任何缓存的当前记录(如果已分配)。
  • PXSelectorAttribute 指定的外国记录。
  • PXParentAttribute 指定的父记录。

该公式支持以下上下文修饰符。

Current<TRecord.field>Current2<TRecord.field>

从存储在 TRecord 缓存的 Current 属性中的记录中获取字段值。

如果缓存的 Current 属性或字段本身包含 null:

  • 当前<>强制字段默认并返回默认字段值。
  • Current2 <>返回 null。

例:

[PXFormula(typeof(Switch<
    Case<Where<
        ARAdjust.adjgDocType, Equal<Current<ARPayment.docType>>,
        And<ARAdjust.adjgRefNbr, Equal<Current<ARPayment.refNbr>>>>,
        ARAdjust.classIcon.outgoing>,
    ARAdjust.classIcon.incoming>))]
protected virtual void ARAdjust_ClassIcon_CacheAttached(PXCache sender)

Parent<TParent.field>

从驻留在当前 DAC 上的 PXParentAttribute 定义的父数据记录中获取字段值。

public class INTran : IBqlTable
{
    [PXParent(typeof(Select<
        INRegister, 
        Where<
            INRegister.docType, Equal<Current<INTran.docType>>,
            And<INRegister.refNbr,Equal<Current<INTran.refNbr>>>>>))]
    public virtual String RefNbr { ... }
  
    [PXFormula(typeof(Parent<INRegister.origModule>))]
    public virtual String OrigModule { ... }
}

IsTableEmpty<TRecord>

如果对应于指定 DAC 的 DB 表不包含记录,则返回 true,否则返回 false

public class APRegister : IBqlTable
{
    [PXFormula(typeof(Switch<
        Case<Where<
            IsTableEmpty<APSetupApproval>, Equal<True>>,
            True,
        Case<Where<
            APRegister.requestApproval, Equal<True>>,
            False>>,
        True>))]
    public virtual bool? DontApprove { get; set; }
}

Selector<KeyField, ForeignOperand>

获取在当前 DAC 的外键字段(KeyField)上定义的 PXSelectorAttribute

获取选择器当前引用的外部数据记录

计算并返回 ForeignOperand 定义的数据记录上的表达式

public class APVendorPrice : IBqlTable
{
    // Note: inventory attribute is an
    // aggregate containing a PXSelectorAttribute
    // inside, which is also valid for Selector<>.
    // -
    [Inventory(DisplayName = "Inventory ID")]
    public virtual int? InventoryID
  
    [PXFormula(typeof(Selector<
        APVendorPrice.inventoryID, 
        InventoryItem.purchaseUnit>))]
    public virtual string UOM { get; set; }
}

在未绑定字段上使用公式

如果公式字段是标记有 PXFieldAttribute 后代之一的未绑定字段(例如 PXIntAttributePXStringAttribute),则在 RowSelecting 事件期间另外触发其计算。

内置常用公式列表

TBD

公式中的直接和中介循环引用

TBD

条件公式中的控制流程

TBD

在一个场上使用多个公式

TBD