acumatica
Acumaticaプラットフォームの属性リファレンス
サーチ…
PXFormula属性
概要
Acumaticaの式は、他のオブジェクトフィールドの値に基づいて計算されるDACフィールドです。
数式を計算するために、Aсumatiсaフレームワークは、さまざまな演算と関数のセット(算術演算、論理演算、比較演算、文字列処理関数など)を提供します( 組み込み式のリストを参照)。数式は、フィールド値に加えて、Acumaticaのコアとアプリケーションソリューションの両方で提供されるさまざまな定数を使用できます。さらに、式は、現在のレコードだけでなく、他のソースから計算の値を取得することもできます(「 式のコンテキストとその修飾子 」を参照)。
数式の美しさは、適切な時期に値を自動的に再計算することです。
- フィールドのデフォルト設定(新しい行の挿入、数式フィールドの
FieldDefaultingイベントハンドラ) - 従属フィールドの更新(各従属フィールドの
FieldUpdatedイベントハンドラ) - データベース選択時(バインドされていないフィールド、
RowSelectingイベントハンドラのみ) - 必要に応じてデータベースを永続化する(開発者は明示的に指定する必要がある、
RowPersistedイベントハンドラ)
依存フィールドの更新時に式フィールド値を再計算すると、式フィールドのFieldUpdatedイベントが発生します。これにより、従属式の連鎖を作成することができます(数式での直接および媒介循環参照を参照)。
アプリケーション開発者は、独自のアプリケーション側の数式を書くことができます。
使い方のモード
式は3つの主なモードで使用できます。
- 値を計算して式フィールドに代入するだけです( 基本的な使用法を参照)
- 数式フィールドの既存の値から集計値を計算し、それを親オブジェクトの指定されたフィールドに割り当てます( 集約使用法を参照)。
- 混合モード:数式値を計算し、数式フィールドに割り当て、集計値を計算し、親オブジェクトのフィールドに割り当てます( 複合使用法を参照)。
混合モードと非常によく似ていますが、数式の計算値は数式フィールドに割り当てられていません。集計された値は直ちに計算され、親オブジェクトのフィールドに割り当てられます。詳細については、未結合式の使用法を参照してください。
PXFormulaAttributeプロパティとコンストラクタのパラメータ
数式機能はPXFormulaAttributeによって実装されPXFormulaAttribute 。 PXFormulaAttributeのコンストラクタには、次のシグネチャがあります。
public PXFormulaAttribute(Type formulaType)
{
// ...
}
単一のパラメータformulaTypeは、同じデータレコードの他のフィールドからフィールド値を計算する式式のタイプです。このパラメータは、次のいずれかの条件を満たす必要があります。
- IBqlFieldインターフェイスを実装する必要があります
- BQL定数でなければならない
- IBqlCreatorインターフェイスを実装する必要があります( 組み込み式の一覧を参照)
public PXFormulaAttribute(Type formulaType, Type aggregateType)
{
// ...
}
最初のパラメーター、 formulaTypeは、最初のコンストラクターと同じです。 2番目のパラメータaggregateTypeは、子データレコードフィールドから親データレコードフィールドを計算する集計式のタイプです。 SumCalc、CountCalc、MinCalc、およびMaxCalcなどの集計関数を使用できます。アプリケーション開発者は、独自の集計式を作成できます。
集計式タイプは汎用タイプでなければならず、 IBqlAggregateCalculatorインターフェイスを実装する必要があります。集計式タイプの最初の汎用パラメータは、 IBqlFieldインターフェイスを実装する必要があり、親オブジェクトのフィールドタイプを持つ必要があります。
public virtual bool Persistent { get; set; }
PXFormulaAttribute.Persistentプロパティは、変更がデータベースに保存された後に属性が式を再計算するかどうかを示します。数式が依存するフィールドがRowPersistingイベントで更新される場合は、再計算が必要になることがあります。デフォルトでは、このプロパティはfalseなり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>
返しtrue指定されたDACに対応するDBのテーブルにレコードが含まれていない場合は、 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子孫の1つ( PXIntAttributeまたはPXStringAttribute )でマークされたバインドされていないフィールドである場合、その計算はRowSelectingイベント中に追加でトリガーされRowSelecting 。
組み込み式のリスト
未定
数式による直接および媒介循環参照
未定
条件式の制御フロー
未定
1つのフィールドで複数の数式を使用する
未定
PXRestrictor属性
前書き
しかし、重要かつ頻繁に使用されるPXSelectorAttribute属性(セレクタとも呼ばれます)には、2つの大きな欠点があります。
- セレクター条件を満たす項目が見つからない場合
"<object_name> cannot be found in the system"という情報"<object_name> cannot be found in the system"。 - レコードの他のフィールドを更新するが、セレクタによって参照されているオブジェクトが既に変更され、その条件を満たしていない場合、同じエラーメッセージが生成されます。法律は遡及的であってはならないので、この動作は明らかに間違っています。
PXRestrictorAttribute (リストリクターとも呼ばれます)は、これらの問題を解決するために使用できます。
詳細
PXRestrictorAttributeは単独では機能しません。常にPXSelectorAttributeとペアにするPXSelectorAttributeます。セレクターなしでリストリクターを使用しても効果はありません。
リストリクタは、同じフィールドにセレクタを見つけ、それに追加の条件と対応するエラーメッセージを挿入します。リストリクタ条件はブールANDを介してセレクタ条件に追加され、参照先オブジェクトがリストリクタ制約に違反すると、適切なエラーメッセージが生成されます。また、参照先オブジェクトが変更され、制限条件を満たさなくなった場合、参照オブジェクトの他のフィールドを変更するとエラーメッセージは生成されません。
一般的な使用法:
[PXDBInt]
[PXSelector(typeof(Search<FAClass.assetID, Where<FAClass.recordType, Equal<FARecordType.classType>>>),
typeof(FAClass.assetCD), typeof(FAClass.assetTypeID), typeof(FAClass.description), typeof(FAClass.usefulLife),
SubstituteKey = typeof(FAClass.assetCD),
DescriptionField = typeof(FAClass.description), CacheGlobal = true)]
[PXRestrictor(typeof(Where<FAClass.active, Equal<True>>), Messages.InactiveFAClass, typeof(FAClass.assetCD))]
[PXUIField(DisplayName = "Asset Class", Visibility = PXUIVisibility.Visible)]
public virtual int? ClassID { get; set; }
1つのセレクタ属性で複数のリストリクタを使用できます。この場合、すべての追加のリストリクター条件は、未定義の順序で適用されます。いずれかの条件に違反すると、適切なエラーメッセージが生成されます。
セレクター自体のWhere<>条件は、すべてのリストリクター条件の後に適用されます。
[PXDefault]
// An aggregate attribute containing the selector inside.
// -
[ContractTemplate(Required = true)]
[PXRestrictor(typeof(Where<ContractTemplate.status, Equal<Contract.status.active>>), Messages.TemplateIsNotActivated, typeof(ContractTemplate.contractCD))]
[PXRestrictor(typeof(Where<ContractTemplate.effectiveFrom, LessEqual<Current<AccessInfo.businessDate>>,
Or<ContractTemplate.effectiveFrom, IsNull>>), Messages.TemplateIsNotStarted)]
[PXRestrictor(typeof(Where<ContractTemplate.discontinueAfter, GreaterEqual<Current<AccessInfo.businessDate>>,
Or<ContractTemplate.discontinueAfter, IsNull>>), Messages.TemplateIsExpired)]
public virtual int? TemplateID { get; set; }
オプション
PXRestrictorAttributeのコンストラクタには、次の3つのパラメータがあります。
- リストリクターの追加条件。このBQL型は、
IBqlWhereインタフェースを実装する必要があります。 - 該当するエラーメッセージ。メッセージには、コンテキストを表示するための書式要素(中括弧)を含めることができます。メッセージは、ローカライズ可能な静的クラス(
PX.Objects.GL.Messagesなど)で定義された文字列定数である必要があります。 - フィールド型の配列。これらのフィールドは、現在のオブジェクトに属する必要があり、
IBqlFieldインターフェイスを実装する必要があります。フィールドの値は、エラーメッセージの書式設定に使用されます。
また、リストリクタの動作を指定するいくつかのオプションがあります。
継承された制限子のオーバーライド
ReplaceInheritedプロパティは、現在のリストリクタが継承されたリストリクタをオーバーライドするかどうかを示します。このプロパティがtrueに設定されている場合、継承されたリストリクタ(すべての集約属性またはベース属性に配置されます)が置き換えられます。
継承されたリストリクタの置き換え:
[CustomerActive(Visibility = PXUIVisibility.SelectorVisible, Filterable = true, TabOrder = 2)]
[PXRestrictor(typeof(Where<Customer.status, Equal<CR.BAccount.status.active>,
Or<Customer.status, Equal<CR.BAccount.status.oneTime>,
Or<Customer.status, Equal<CR.BAccount.status.hold>,
Or<Customer.status, Equal<CR.BAccount.status.creditHold>>>>>), Messages.CustomerIsInStatus, typeof(Customer.status),
ReplaceInherited = true)] // Replaced all restrictors from CustomerActiveAttribute
[PXUIField(DisplayName = "Customer")]
[PXDefault()]
public override int? CustomerID { get; set; }
妥当な選択肢が存在する場合、アプリケーションコードでReplaceInheritedプロパティを使用することはお勧めしません。このプロパティは、主にカスタマイズで使用されることを意図しています。
グローバルキャッシング
CacheGlobalは、 PXSelectorAttributeと同じ方法でグローバル辞書機能をサポートしていPXSelectorAttribute 。
使用上の推奨事項
制限条件のみを使用する
リストリクタとセレクタを併用する場合、後者にはIBqlWhere句をIBqlWhereないでください。理想的には、すべての条件をリストリクターに移す必要があります。このアプローチは、より使いやすいエラーメッセージを提供し、不要な遡及的なエラーを排除します。
理想的な例:
[PXDBString(5, IsFixed = true, IsUnicode = false)]
[PXUIField(DisplayName = "Type", Required = true)]
[PXSelector(typeof(EPActivityType.type), DescriptionField = typeof(EPActivityType.description))]
[PXRestrictor(typeof(Where<EPActivityType.active, Equal<True>>), Messages.InactiveActivityType, typeof(EPActivityType.type))]
[PXRestrictor(typeof(Where<EPActivityType.isInternal, Equal<True>>), Messages.ExternalActivityType, typeof(EPActivityType.type))]
public virtual string Type { get; set; }
考えられる遡及的なエラー:
[PXDBInt]
[PXUIField(DisplayName = "Contract")]
[PXSelector(typeof(Search2<Contract.contractID,
LeftJoin<ContractBillingSchedule, On<Contract.contractID, Equal<ContractBillingSchedule.contractID>>>,
Where<Contract.isTemplate, NotEqual<True>,
And<Contract.baseType, Equal<Contract.ContractBaseType>,
And<Where<Current<CRCase.customerID>, IsNull,
Or2<Where<Contract.customerID, Equal<Current<CRCase.customerID>>,
And<Current<CRCase.locationID>, IsNull>>,
Or2<Where<ContractBillingSchedule.accountID, Equal<Current<CRCase.customerID>>,
And<Current<CRCase.locationID>, IsNull>>,
Or2<Where<Contract.customerID, Equal<Current<CRCase.customerID>>,
And<Contract.locationID, Equal<Current<CRCase.locationID>>>>,
Or<Where<ContractBillingSchedule.accountID, Equal<Current<CRCase.customerID>>,
And<ContractBillingSchedule.locationID, Equal<Current<CRCase.locationID>>>>>>>>>>>>,
OrderBy<Desc<Contract.contractCD>>>),
DescriptionField = typeof(Contract.description),
SubstituteKey = typeof(Contract.contractCD), Filterable = true)]
[PXRestrictor(typeof(Where<Contract.status, Equal<Contract.status.active>>), Messages.ContractIsNotActive)]
[PXRestrictor(typeof(Where<Current<AccessInfo.businessDate>, LessEqual<Contract.graceDate>, Or<Contract.expireDate, IsNull>>), Messages.ContractExpired)]
[PXRestrictor(typeof(Where<Current<AccessInfo.businessDate>, GreaterEqual<Contract.startDate>>), Messages.ContractActivationDateInFuture, typeof(Contract.startDate))]
[PXFormula(typeof(Default<CRCase.customerID>))]
[PXDefault(PersistingCheck = PXPersistingCheck.Nothing)]
public virtual int? ContractID { get; set; }