Sök…


PXFormula-attribut

Allmän beskrivning

En formel i Acumatica är ett DAC-fält som beräknas baserat på värdena på andra objektfält.

För att beräkna en formel, ger Aсumatiсa-ramverk en uppsättning olika operationer och funktioner (som aritmetiska, logiska och jämförande operationer och strängbehandlingsfunktioner; se Lista över inbyggda gemensamma formler ). Förutom fältvärdena kan en formel använda olika konstanter tillhandahållna av både kärnan i Acumatica och applikationslösningarna. Dessutom kan en formel erhålla värden för beräkningen inte bara från den aktuella posten utan också från andra källor (se Formelkontext och dess modifierare ).

Det fina med formlerna är att de automatiskt kommer att beräkna värdet vid rätt tidpunkt:

  • På standardfält (infoga en ny rad; FieldDefaulting händelsehanterare av formelfältet)
  • Vid uppdatering av beroende fält ( FieldUpdated event handler för varje beroende fält)
  • Vid val av databas (endast för obundna fält; RowSelecting event handler)
  • På databasen kvarstår vid behov (utvecklare ska ange det uttryckligen; RowPersisted händelseshanterare)

Omberäkning av ett formelfältvärde vid uppdateringen av ett beroende fält höjer en FieldUpdated händelse för FieldUpdated . Detta gör att du kan skapa en kedja med beroende formler (se Direkt och Medierade cirkulära referenser i formler).

Programutvecklare kan skriva sina egna formulär på applikationssidan.

Lägen för användning

En formel kan användas i tre huvudlägen:

  • Beräkna helt enkelt värdet och tilldela det till formelfältet (se Grundläggande användning )
  • Beräkna det sammanlagda värdet från befintliga värden på formelfält och tilldela det till det angivna fältet i moderobjektet (se Aggregate Usage )
  • Blandat läge: Beräkna formelvärdet, tilldela det till formelfältet, beräkna det sammanlagda värdet och tilldela det till fältet i moderobjektet (se Kombinerad användning )

Det finns ett annat hjälpläge, obundna formler, som liknar mycket blandat läge, men de beräknade värdena för formeln tilldelas inte formelfältet. Det aggregerade värdet beräknas omedelbart och tilldelas fältet för överordnade objekt. Se Användning av obundna formler för mer information.

PXFormulaAttribute egenskaper och konstruktionsparametrar

PXFormulaAttribute implementeras av PXFormulaAttribute . Konstruktören av PXFormulaAttribute har följande signaturer:

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

Den enda parametern formulaType är en typ av formeluttryck för att beräkna fältvärdet från andra fält i samma datapost. Denna parameter måste uppfylla ett av följande villkor:

  • Måste implementera IBqlField-gränssnittet
  • Måste vara en BQL-konstant
  • Måste implementera IBqlCreator-gränssnittet (se Lista över inbyggda vanliga formler )
public PXFormulaAttribute(Type formulaType, Type aggregateType)
{
    // ...
}

Den första parametern, formulaType , är densamma som i den första konstruktorn. Den andra parametern, aggregateType , är en typ av aggregeringsformel för att beräkna fältet för överordnad dataregistrering från fältet för underordnad dator. En aggregeringsfunktion kan användas, såsom SumCalc, CountCalc, MinCalc och MaxCalc. Programutvecklare kan skapa sina egna sammanställningsformler.

En IBqlAggregateCalculator måste vara en generisk typ och måste implementera IBqlAggregateCalculator gränssnittet. Den första generiska parametern av den IBqlField måste implementera IBqlField gränssnittet och måste ha IBqlField för överordnad objekt.

public virtual bool Persistent { get; set; }

PXFormulaAttribute.Persistent anger om attributet beräknar formeln om efter att ändringar har sparats i databasen. Du kan behöva beräkna om fälten formeln beror på uppdateras på RowPersisting händelsen. Som standard är egenskapen lika false .

Användande

I de flesta fall används formler för direkt beräkning av värdet på formelfältet från andra fält i samma datapost.

Det enklaste exemplet på formelanvändning:

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

I det här exemplet tilldelas värdet på fältet ReceiptDate till fältet DepreciateFromDate vid införandet av en ny post och vid uppdateringen av fältet ReceiptDate.

Ett lite mer komplext exempel:

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

Här beräknas dokumentets icke tillämpade saldo som skillnaden mellan dokumentets saldo och det tillämpade beloppet.

Exempel på flerval med ett standardvärde:

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

Fältordning

Fältets ordning i DAC är viktig för att korrigera formelberäkningen. Alla källfält (från vilka formeln beräknas) inklusive andra formler måste definieras i DAC före formelfältet. Annars kan fältet beräknas felaktigt eller orsaka ett körtidsfel.

Formelkontext och dess modifierare

Som standard begränsas sammanhanget för formelberäkningen av det aktuella objektet (posten) i klassen som innehåller formeldeklarationen. Det är också tillåtet att använda konstanter (ättlingar till klassen Constant<> ).

En formel som bara använder fälten för objektet:

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

Det är dock möjligt att erhålla inmatningsvärden för formelberäkningen från andra källor:

  • En aktuell post av någon cache i BLC (om tilldelad).
  • En utländsk post specificerad av PXSelectorAttribute .
  • En överordnad post som specificeras av PXParentAttribute .

Formeln stöder följande kontextmodifierare.

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

Hämtar fältvärdet från posten som är lagrad i den Current egenskapen i TRecord-cachen.

Om cachens Current egenskap eller fältet i sig innehåller noll:

  • Aktuell <> tvingar fältets standard och returnerar standardfältvärdet.
  • Current2 <> returnerar noll.

Exempel:

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

Hämtar fältvärdet från moderdataposten som definieras av PXParentAttribute som är bosatt på den aktuella DAC.

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>

Returnerar true om DB-tabellen som motsvarar den angivna DAC innehåller inga poster, false annars.

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>

Hämtar en PXSelectorAttribute definierad i det främmande nyckelfältet (KeyField) för det aktuella DAC.

Hämtar den utländska dataposten som för närvarande refereras av väljaren.

Beräknar och returnerar ett uttryck på den dataposten som definierats av 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; }
}

Använda formler på obundna fält

Om formelfältet är ett obundet fält markerat med en av PXFieldAttribute efterkommarna (t.ex. PXIntAttribute eller PXStringAttribute ), utlöses dess beräkning dessutom under RowSelecting händelsen.

Lista över inbyggda vanliga formler

TBD

Direkta och medierade cirkulära referenser i formler

TBD

Kontrollflöde i villkorade formler

TBD

Använda flera formler på ett fält

TBD

PXRestrictor-attribut

Introduktion

Attributet PXSelectorAttribute (även kallad selector), även om det är viktigt och ofta används, har dock två stora nackdelar:

  • Det ger ett oinformativt meddelande "<object_name> cannot be found in the system" om det inte finns några objekt som uppfyller väljartillståndet.
  • Den producerar samma felmeddelande om du uppdaterar andra fält i posten men objektet som referensen har valt har redan ändrats och inte längre uppfyller dess villkor. Detta beteende är uppenbart fel eftersom lagen inte får vara retroaktiv.

PXRestrictorAttribute (även kallad restriktorn) kan användas för att lösa dessa problem.

detaljer

PXRestrictorAttribute fungerar inte ensam; det ska alltid kopplas ihop med en PXSelectorAttribute . Att använda begränsaren utan väljaren har ingen effekt.

Begränsaren hittar väljaren i samma fält och injicerar i det ett ytterligare tillstånd och motsvarande felmeddelande. Begränsningsvillkoret läggs till väljartillståndet via ett booleskt OCH och ett lämpligt felmeddelande genereras om det refererade objektet bryter mot begränsningsbegränsningen. Om det refererade objektet har ändrats och inte längre uppfyller begränsningsvillkoret, produceras inga felmeddelanden när du ändrar andra fält i referensobjektet.

Allmän användning:

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

Flera begränsare kan användas med ett väljande attribut. I detta fall tillämpas alla ytterligare begränsningsvillkor i en icke bestämd ordning. När något villkor bryts genereras rätt felmeddelande.

Where<> -tillståndet för själva väljaren tillämpas efter alla begränsningsvillkor.

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

alternativ

Konstruktören för PXRestrictorAttribute tar tre parametrar:

  1. Begränsarens ytterligare villkor. Denna BQL-typ måste implementera IBqlWhere gränssnittet.
  2. Rätt felmeddelande. Meddelandet kan innehålla formatelement (lockiga parenteser) för att visa sammanhang. Meddelandet måste vara en strängkonstant definierad i en lokaliserbar statisk klass (som PX.Objects.GL.Messages ).
  3. En rad fälttyper. Dessa fält måste tillhöra det aktuella objektet och måste implementera IBqlField gränssnittet. Fältens värden kommer att användas för formatering av felmeddelanden.

Det finns också flera alternativ som anger restriktorns beteende.

Överträffar ärvda begränsningar

ReplaceInherited anger om den nuvarande begränsaren ska åsidosätta de ärvda begränsarna. Om den här egenskapen är inställd på sann, kommer alla ärvda begränsare (placerade på alla samlade attribut eller basattribut) att ersättas.

Ersätter ärvda begränsare:

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

Observera att vi inte rekommenderar att du använder egenskapen ReplaceInherited i applikationskod när det finns rimliga alternativ. Den här egenskapen är främst avsedd att användas i anpassningar.

Global caching

CacheGlobal stöder global ordboksfunktionalitet på samma sätt som i PXSelectorAttribute .

Rekommendationer för användning

Använd endast begränsningsvillkor

När begränsare och en väljare används tillsammans bör den senare inte innehålla IBqlWhere klausulen. Helst bör alla villkor flyttas till begränsare. Detta tillvägagångssätt ger mer användarvänliga felmeddelanden och eliminerar onödiga retroaktiva fel.

Ett idealiskt exempel:

[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öjliga retroaktiva fel:

[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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow