Zoeken…


PXFormula-kenmerk

Algemene beschrijving

Een formule in Acumatica is een DAC-veld dat wordt berekend op basis van de waarden van andere objectvelden.

Om een formule te berekenen, biedt het Aсumatiсa-raamwerk een aantal verschillende bewerkingen en functies (zoals rekenkundige, logische en vergelijkingsbewerkingen en tekenreeksverwerkingsfuncties; zie Lijst met ingebouwde algemene formules ). Naast de veldwaarden kan een formule verschillende constanten gebruiken die worden geleverd door zowel de kern van Acumatica als de applicatie-oplossingen. Bovendien kan een formule waarden voor de berekening verkrijgen, niet alleen uit het huidige record, maar ook uit andere bronnen (zie Formula Context and Its Modifiers ).

Het mooie van de formules is dat ze de waarde op het juiste moment automatisch opnieuw berekenen:

  • Op veld standaard (invoegen van een nieuwe rij; FieldDefaulting gebeurtenishandler van formuleveld)
  • Bij het bijwerken van afhankelijke velden ( FieldUpdated gebeurtenishandler van elk afhankelijk veld)
  • Bij selectie van database (alleen voor ongebonden velden; RowSelecting )
  • Op database blijft bestaan indien nodig (ontwikkelaar moet dit expliciet specificeren; RowPersisted event handler)

Herberekening van een waarde van een formuleveld bij het bijwerken van een afhankelijk veld verhoogt een gebeurtenis FieldUpdated voor het formuleveld. Hiermee kunt u een reeks afhankelijke formules maken (zie Directe en gemedieerde circulaire verwijzingen in formules).

Applicatieontwikkelaars kunnen hun eigen applicatie-formules schrijven.

Gebruikswijzen

Een formule kan in drie hoofdmodi worden gebruikt:

  • Eenvoudig de waarde berekenen en toewijzen aan het formuleveld (zie Basisgebruik )
  • De geaggregeerde waarde berekenen op basis van bestaande waarden van formulevelden en deze toewijzen aan het opgegeven veld in het bovenliggende object (zie Gebruik samenvoegen)
  • Gemengde modus: de formulewaarde berekenen, toewijzen aan het formuleveld, de aggregatiewaarde berekenen en toewijzen aan het veld in het bovenliggende object (zie Gecombineerd gebruik )

Er is nog een hulpmodus, ongebonden formules, die erg lijkt op de gemengde modus, maar de berekende waarden van de formule zijn niet toegewezen aan het formuleveld. De geaggregeerde waarde wordt onmiddellijk berekend en toegewezen aan het veld van het bovenliggende object. Zie Gebruik van niet-gebonden formules voor meer informatie.

PXFormulaAttribute Properties en Constructor Parameters

De formulefunctionaliteit wordt geïmplementeerd door PXFormulaAttribute . De constructor van PXFormulaAttribute heeft de volgende handtekeningen:

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

De enkele parameter formulaType is een type formule-expressie om de veldwaarde te berekenen uit andere velden van hetzelfde gegevensrecord. Deze parameter moet aan een van de volgende voorwaarden voldoen:

  • Moet de IBqlField-interface implementeren
  • Moet een BQL-constante zijn
  • Moet de IBqlCreator-interface implementeren (zie Lijst met ingebouwde gemeenschappelijke formules )
public PXFormulaAttribute(Type formulaType, Type aggregateType)
{
    // ...
}

De eerste parameter, formulaType , is dezelfde als in de eerste constructor. De tweede parameter, aggregateType , is een type aggregatieformule om het bovenliggende gegevensrecordveld uit de onderliggende gegevensrecordvelden te berekenen. Een aggregatiefunctie kan worden gebruikt, zoals SumCalc, CountCalc, MinCalc en MaxCalc. Applicatieontwikkelaars kunnen hun eigen aggregatieformules maken.

Een aggregaatformuletype moet een generiek type zijn en moet de interface IBqlAggregateCalculator implementeren. De eerste generieke parameter van het geaggregeerde IBqlField moet de IBqlField interface implementeren en moet het veldtype van het bovenliggende object hebben.

public virtual bool Persistent { get; set; }

De eigenschap PXFormulaAttribute.Persistent geeft aan of het kenmerk de formule opnieuw berekent nadat de wijzigingen in de database zijn opgeslagen. U moet mogelijk opnieuw worden berekend als de velden waarvan de formule afhankelijk is, worden bijgewerkt bij de RowPersisting gebeurtenis. Standaard is de eigenschap gelijk aan false .

Gebruik

In de meeste gevallen worden formules gebruikt voor directe berekening van de waarde van het formuleveld uit andere velden van hetzelfde gegevensrecord.

Het eenvoudigste voorbeeld van formulegebruik:

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

In dit voorbeeld wordt de waarde van het veld ReceiptDate toegewezen aan het veld DepreciateFromDate bij het invoegen van een nieuw record en bij de update van het veld ReceiptDate.

Een iets complexer voorbeeld:

[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 wordt het niet-toegepaste saldo van het document berekend als het verschil tussen het saldo van het document en het toegepaste bedrag.

Voorbeeld van meerkeuze met een standaardwaarde:

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

Volgorde van velden

De volgorde van velden in de DAC is belangrijk om de formuleberekening te corrigeren. Alle bronvelden (waaruit de formule wordt berekend) inclusief andere formules moeten in de DAC worden gedefinieerd vóór het formuleveld. Anders kan het veld onjuist worden berekend of kan het een runtime-fout veroorzaken.

Formula Context en zijn modificaties

Standaard wordt de context van de formuleberekening beperkt door het huidige object (record) van de klasse die de formuleverklaring bevat. Het is ook toegestaan om constanten (afstammelingen van de klasse Constant<> ) te gebruiken.

Een formule die alleen de velden van het object gebruikt:

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

Het is echter mogelijk om invoerwaarden voor de formuleberekening uit andere bronnen te verkrijgen:

  • Een actueel record van elke cache in de BLC (indien toegewezen).
  • Een buitenlands record gespecificeerd door PXSelectorAttribute .
  • Een bovenliggend record opgegeven door PXParentAttribute .

De formule ondersteunt de volgende contextmodificaties.

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

Haalt de veldwaarde op uit het record dat is opgeslagen in de eigenschap Current van de TRecord-cache.

Als de eigenschap Current de cache of het veld zelf null bevat:

  • Huidige <> dwingt veld standaard in en retourneert de standaard veldwaarde.
  • Current2 <> retourneert null.

Voorbeeld:

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

Haalt de veldwaarde op uit het bovenliggende gegevensrecord zoals gedefinieerd door PXParentAttribute dat zich op de huidige DAC bevindt.

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>

Retourneert true als de DB-tabel die overeenkomt met de opgegeven DAC geen records bevat, anders 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>

Haalt een PXSelectorAttribute op dat is gedefinieerd in het veld voor de externe sleutel (KeyField) van de huidige DAC.

Haalt het buitenlandse gegevensrecord op waarnaar momenteel door de selector wordt verwezen.

Berekent en retourneert een uitdrukking op dat gegevensrecord zoals gedefinieerd door 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; }
}

Formules gebruiken op niet-gebonden velden

Als het formuleveld een ongebonden veld is gemarkeerd met een van de PXFieldAttribute afstammelingen (zoals PXIntAttribute of PXStringAttribute ), wordt de berekening ervan bovendien geactiveerd tijdens de RowSelecting gebeurtenis.

Lijst met ingebouwde gemeenschappelijke formules

TBD

Directe en gemedieerde circulaire verwijzingen in formules

TBD

Regelingsstroom in voorwaardelijke formules

TBD

Meerdere formules op één veld gebruiken

TBD

PXRestrictor Attribuut

Invoering

Het kenmerk PXSelectorAttribute (ook wel de selector genoemd), hoewel essentieel en vaak gebruikt, heeft echter twee belangrijke nadelen:

  • Het geeft een niet-informatieve melding "<object_name> cannot be found in the system" als er geen items worden gevonden die voldoen aan de selectorvoorwaarde.
  • Het produceert hetzelfde foutbericht als u andere velden van het record bijwerkt, maar het object waarnaar door de selector wordt verwezen, is al gewijzigd en voldoet niet meer aan de voorwaarde. Dit gedrag is duidelijk verkeerd omdat de wet niet met terugwerkende kracht mag zijn.

Het PXRestrictorAttribute (ook wel de restrictor genoemd) kan worden gebruikt om deze problemen op te lossen.

Details

PXRestrictorAttribute werkt niet alleen; het moet altijd worden gekoppeld met een PXSelectorAttribute . Het gebruik van de restrictor zonder de selector heeft geen effect.

De restrictor vindt de selector op hetzelfde veld en injecteert er een aanvullende voorwaarde en het bijbehorende foutbericht. De restrictorvoorwaarde wordt via een Booleaanse EN aan de selectorvoorwaarde toegevoegd en er wordt een passend foutbericht gegenereerd als het object waarnaar wordt verwezen de restrictorbeperking schendt. Als het object waarnaar wordt verwezen, is gewijzigd en niet langer aan de beperkende voorwaarde voldoet, worden er geen foutmeldingen geproduceerd wanneer u andere velden van het object waarnaar wordt verwezen, wijzigt.

Algemeen gebruik:

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

Meerdere restrictors kunnen worden gebruikt met één selectorkenmerk. In dit geval worden alle aanvullende beperkende voorwaarden toegepast in een niet-vastgestelde volgorde. Zodra een voorwaarde is geschonden, wordt het juiste foutbericht gegenereerd.

De Where<> -voorwaarde van de selector zelf wordt toegepast na alle restrictorvoorwaarden.

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

opties

De constructor van PXRestrictorAttribute heeft drie parameters:

  1. De aanvullende voorwaarde van de restrictor. Dit BQL-type moet de IBqlWhere interface implementeren.
  2. Het juiste foutbericht. Het bericht kan opmaakelementen (accolades) bevatten om de context weer te geven. Het bericht moet een tekenreeksconstante zijn die is gedefinieerd in een lokaliseerbare statische klasse (zoals PX.Objects.GL.Messages ).
  3. Een reeks veldtypen. Deze velden moeten bij het huidige object horen en de IBqlField interface implementeren. De waarden van de velden worden gebruikt voor het formatteren van foutmeldingen.

Er zijn ook verschillende opties die het restrictorgedrag specificeren.

Overgenomen beperkingen overgenomen

De eigenschap ReplaceInherited geeft aan of de huidige restrictor de overgeërfde restrictors moet overschrijven. Als deze eigenschap is ingesteld op true, worden alle geërfde restrictors (geplaatst op geaggregeerde attributen of basisattributen) vervangen.

Overgeërfde restrictors vervangen:

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

Houd er rekening mee dat we niet adviseren dat u de eigenschap ReplaceInherited in de toepassingscode gebruikt wanneer er redelijke alternatieven zijn. Deze eigenschap is voornamelijk bedoeld om te worden gebruikt in aanpassingen.

Wereldwijde caching

CacheGlobal ondersteunt globale woordenboekfunctionaliteit op dezelfde manier als in PXSelectorAttribute .

Aanbevelingen voor gebruik

Gebruik alleen restrictievoorwaarden

Wanneer restrictors en een selector samen worden gebruikt, mag deze laatste de clausule IBqlWhere niet bevatten. In het ideale geval moeten alle voorwaarden worden verplaatst naar restrictors. Deze aanpak biedt meer gebruikersvriendelijke foutmeldingen en elimineert onnodige retroactieve fouten.

Een ideaal voorbeeld:

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

Mogelijke retroactieve fouten:

[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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow