Suche…


PXFormula-Attribut

Allgemeine Beschreibung

Eine Formel in Acumatica ist ein DAC-Feld, das basierend auf den Werten anderer Objektfelder berechnet wird.

Um eine Formel zu berechnen, stellt Aсumatiсa Framework eine Reihe verschiedener Operationen und Funktionen bereit (z. B. arithmetische, logische Operationen, Vergleichsoperationen und Funktionen zur Verarbeitung von Zeichenfolgen; siehe Liste der integrierten allgemeinen Formeln ). Zusätzlich zu den Feldwerten kann eine Formel verschiedene Konstanten verwenden, die sowohl vom Kern von Acumatica als auch von den Anwendungslösungen bereitgestellt werden. Außerdem kann eine Formel Werte für die Berechnung nicht nur aus dem aktuellen Datensatz erhalten, sondern auch aus anderen Quellen (siehe Formelkontext und seine Modifikatoren ).

Das Schöne an den Formeln ist, dass sie den Wert zum richtigen Zeitpunkt automatisch neu berechnen:

  • On Field Defaulting (Einfügen einer neuen Zeile; FieldDefaulting Ereignishandler des FieldDefaulting )
  • Beim Aktualisieren abhängiger Felder ( FieldUpdated Ereignishandler jedes abhängigen Felds)
  • Bei Datenbankauswahl (nur für ungebundene Felder; RowSelecting Ereignishandler)
  • Wenn die Datenbank bei Bedarf persistiert (der Entwickler sollte dies explizit angeben; RowPersisted Ereignishandler)

Die Neuberechnung eines FieldUpdated bei der Aktualisierung eines abhängigen Felds löst ein FieldUpdated Ereignis für das FieldUpdated aus. Auf diese Weise können Sie eine Kette abhängiger Formeln erstellen (siehe direkte und vermittelte Zirkelreferenzen in Formeln).

Anwendungsentwickler können eigene anwendungsseitige Formeln schreiben.

Nutzungsarten

Eine Formel kann in drei Hauptmodi verwendet werden:

  • Berechnen Sie einfach den Wert und weisen Sie ihn dem Formelfeld zu (siehe Grundlegende Verwendung )
  • Berechnen des Aggregatwerts aus vorhandenen Werten von Formelfeldern und Zuweisen zu einem angegebenen Feld im übergeordneten Objekt (siehe Aggregatverwendung )
  • Mischmodus: Berechnen des Formelwerts, Zuordnen zu dem Formelfeld, Berechnen des Aggregatwerts und Zuweisen zu dem Feld im übergeordneten Objekt (siehe Kombinierte Verwendung )

Es gibt einen weiteren Hilfsmodus, ungebundene Formeln, der dem gemischten Modus sehr ähnlich ist, aber die berechneten Werte der Formel werden nicht dem Formelfeld zugewiesen. Der aggregierte Wert wird sofort berechnet und dem Feld des übergeordneten Objekts zugewiesen. Weitere Informationen finden Sie unter Verwendung von nicht gebundenen Formeln .

PXFormulaAttribute Eigenschaften und Konstruktorparameter

Die PXFormulaAttribute wird von PXFormulaAttribute implementiert. Der Konstruktor von PXFormulaAttribute hat folgende Signaturen:

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

Der Einzelparameter formulaType ist ein Typ von Formelausdruck, der den Feldwert aus anderen Feldern des gleichen Datensatzes berechnet. Dieser Parameter muss eine der folgenden Bedingungen erfüllen:

  • Muss die IBqlField-Schnittstelle implementieren
  • Muss eine BQL-Konstante sein
  • Muss die IBqlCreator-Schnittstelle implementieren (siehe Liste der integrierten allgemeinen Formeln ).
public PXFormulaAttribute(Type formulaType, Type aggregateType)
{
    // ...
}

Der erste Parameter, formulaType , ist derselbe wie im ersten Konstruktor. Der zweite Parameter, aggregateType , ist eine Art Aggregationsformel zur Berechnung des Felds des übergeordneten Datensatzes aus den Feldern des untergeordneten Datensatzes. Eine Aggregationsfunktion kann verwendet werden, z. B. SumCalc, CountCalc, MinCalc und MaxCalc. Anwendungsentwickler können ihre eigenen Aggregationsformeln erstellen.

Ein Aggregatformeltyp muss ein generischer Typ sein und die IBqlAggregateCalculator Schnittstelle implementieren. Der erste generische Parameter des Aggregatformeltyps muss die IBqlField Schnittstelle implementieren und muss den IBqlField des übergeordneten Objekts haben.

public virtual bool Persistent { get; set; }

Die Eigenschaft PXFormulaAttribute.Persistent gibt an, ob das Attribut die Formel neu berechnet, nachdem Änderungen in der Datenbank gespeichert wurden. Möglicherweise müssen Sie eine Neuberechnung durchführen, wenn die Felder, von denen die Formel abhängig ist, im RowPersisting Ereignis aktualisiert werden. Die Eigenschaft entspricht standardmäßig " false .

Verwendungszweck

In den meisten Fällen werden Formeln zur direkten Berechnung des Wertes des Formelfelds aus anderen Feldern des gleichen Datensatzes verwendet.

Das einfachste Beispiel für die Verwendung von Formeln:

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

In diesem Beispiel wird der Wert des ReceiptDate-Felds beim Einfügen eines neuen Datensatzes und bei der Aktualisierung des ReceiptDate-Felds dem DepreciateFromDate-Feld zugewiesen.

Ein etwas komplexeres Beispiel:

[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; }

Hier wird der nicht belegte Restbetrag des Belegs als Differenz zwischen dem Restbetrag des Belegs und dem verwendeten Betrag berechnet.

Beispiel für Multiple Choice mit einem Standardwert:

[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; }

Reihenfolge der Felder

Die Reihenfolge der Felder im DAC ist wichtig, um die Formelberechnung zu korrigieren. Alle Quellfelder (aus denen die Formel berechnet wird) einschließlich anderer Formeln müssen vor dem Formelfeld im DAC definiert werden. Andernfalls kann das Feld falsch berechnet werden oder einen Laufzeitfehler verursachen.

Formelkontext und seine Modifikatoren

Standardmäßig ist der Kontext der Formelberechnung durch das aktuelle Objekt (Datensatz) der Klasse eingeschränkt, die die Formeldeklaration enthält. Es ist auch erlaubt, Konstanten (Nachkommen der Constant<> -Klasse) zu verwenden.

Eine Formel, die nur die Felder ihres Objekts verwendet:

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; }
    //...
}

Es ist jedoch möglich, Eingabewerte für die Formelberechnung aus anderen Quellen zu erhalten:

  • Ein aktueller Datensatz eines Cache im BLC (falls zugewiesen).
  • Ein von PXSelectorAttribute angegebener PXSelectorAttribute .
  • Ein durch PXParentAttribute angegebener übergeordneter Datensatz.

Die Formel unterstützt die folgenden Kontextmodifizierer.

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

Ruft den Feldwert aus dem Datensatz ab, der in der Current Eigenschaft des TRecord-Caches gespeichert ist.

Wenn die Current Eigenschaft des Caches oder das Feld selbst null enthält:

  • Current <> erzwingt die Standardeinstellung des Felds und gibt den Standardfeldwert zurück.
  • Current2 <> gibt null zurück.

Beispiel:

[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>

Ruft den Feldwert aus dem übergeordneten Datensatz ab, wie von PXParentAttribute definiert, das sich auf dem aktuellen DAC befindet.

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>

Gibt " true wenn die dem angegebenen DAC entsprechende DB-Tabelle keine Datensätze enthält, andernfalls " 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>

Ruft ein PXSelectorAttribute ab, das für das Fremdschlüsselfeld (KeyField) des aktuellen DAC definiert ist.

Ruft den aktuell vom Selektor referenzierten Fremddatensatz ab.

Berechnet einen Ausdruck für diesen Datensatz und gibt diesen zurück, wie von ForeignOperand definiert.

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; }
}

Verwenden von Formeln für nicht gebundene Felder

Wenn das PXFieldAttribute ein ungebundenes Feld ist, das mit einer der Nachkommen von PXFieldAttribute (z. B. PXIntAttribute oder PXStringAttribute ) PXStringAttribute , wird die Berechnung zusätzlich während des RowSelecting Ereignisses ausgelöst.

Liste der integrierten allgemeinen Formeln

TBD

Direkte und vermittelte Zirkelreferenzen in Formeln

TBD

Kontrollfluss in bedingten Formeln

TBD

Mehrere Formeln in einem Feld verwenden

TBD

PXRestrictor-Attribut

Einführung

Das PXSelectorAttribute-Attribut (auch als Selektor bezeichnet) ist zwar wichtig und häufig verwendet, hat jedoch zwei Hauptnachteile:

  • Wenn keine Elemente gefunden werden, die die Selektor-Bedingung erfüllen, wird eine nicht "<object_name> cannot be found in the system" Nachricht "<object_name> cannot be found in the system" .
  • Die gleiche Fehlermeldung wird angezeigt, wenn Sie andere Felder des Datensatzes aktualisieren, das vom Selector referenzierte Objekt jedoch bereits geändert wurde und seine Bedingung nicht mehr erfüllt. Dieses Verhalten ist eindeutig falsch, da das Gesetz nicht rückwirkend sein darf.

Das PXRestrictorAttribute (auch als Beschränkung bezeichnet) kann verwendet werden, um diese Probleme zu lösen.

Einzelheiten

PXRestrictorAttribute funktioniert nicht alleine. Es sollte immer mit einem PXSelectorAttribute gepaart werden. Die Verwendung der Drossel ohne den Wahlschalter hat keine Wirkung.

Der Begrenzer findet den Selektor auf demselben Feld und fügt ihm eine zusätzliche Bedingung und die entsprechende Fehlermeldung ein. Die Beschränkungsbedingung wird über eine boolesche UND-Verknüpfung an die Auswahlbedingung angehängt, und eine entsprechende Fehlermeldung wird generiert, wenn das referenzierte Objekt die Beschränkungsbeschränkung verletzt. Wenn das referenzierte Objekt geändert wurde und die Einschränkungsbedingung nicht mehr erfüllt, werden keine Fehlermeldungen ausgegeben, wenn Sie andere Felder des referenzierenden Objekts ändern.

Allgemeine Verwendung:

[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; }

Mit einem Selektorattribut können mehrere Restriktoren verwendet werden. In diesem Fall werden alle zusätzlichen Begrenzungsbedingungen in einer nicht bestimmten Reihenfolge angewendet. Sobald eine Bedingung verletzt wird, wird die entsprechende Fehlermeldung generiert.

Die Where<> Bedingung des Wahlschalters selbst wird nach allen Begrenzerbedingungen angewendet.

[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; }

Optionen

Der Konstruktor von PXRestrictorAttribute benötigt drei Parameter:

  1. Der zusätzliche Zustand der Drossel. Dieser BQL-Typ muss die IBqlWhere Schnittstelle implementieren.
  2. Die entsprechende Fehlermeldung Die Nachricht kann Formatelemente (geschweifte Klammern) enthalten, um den Kontext anzuzeigen. Die Nachricht muss eine Zeichenfolgekonstante sein, die in einer lokalisierbaren statischen Klasse (z. B. PX.Objects.GL.Messages ) definiert ist.
  3. Ein Array von Feldtypen. Diese Felder müssen zum aktuellen Objekt gehören und die IBqlField Schnittstelle implementieren. Die Werte der Felder werden für die Formatierung von Fehlernachrichten verwendet.

Außerdem gibt es mehrere Optionen, die das Verhalten der Drossel festlegen.

Vererbte Restriktoren überschreiben

Die ReplaceInherited Eigenschaft gibt an, ob die aktuelle Beschränkung die geerbten Einschränkungen einschränken soll. Wenn diese Eigenschaft auf true festgelegt ist, werden alle geerbten Restriktoren (in Aggregatattributen oder Basisattributen platziert) ersetzt.

Vererbte Restriktoren ersetzen:

 [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; }

Beachten Sie, dass wir nicht empfehlen, die ReplaceInherited Eigenschaft im Anwendungscode zu verwenden, wenn sinnvolle Alternativen vorhanden sind. Diese Eigenschaft soll in erster Linie in Anpassungen verwendet werden.

Globales Caching

CacheGlobal unterstützt globale Wörterbuchfunktionen auf dieselbe Weise wie in PXSelectorAttribute .

Empfehlungen zur Verwendung

Verwenden Sie nur Restriktorbedingungen

Wenn Restriktoren und ein Selektor zusammen verwendet werden, sollte dieser nicht die IBqlWhere Klausel enthalten. Im Idealfall sollten alle Bedingungen in Restriktoren verschoben werden. Dieser Ansatz bietet benutzerfreundlichere Fehlermeldungen und beseitigt unnötige rückwirkende Fehler.

Ein ideales Beispiel:

[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; }

Mögliche rückwirkende Fehler:

[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; }


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow