Create new record in Acumatica or which insert works faster

Hello everybody,

today I want to share results of my investigations of two similar pieces of functionality in Acumatica.

Have you ever wondered which Insert in cache works faster this:

a) var temp = (PRPayrollDetails)PayRollsDetails.Cache.Insert();

or this:

b)          var temp = new PRPayrollDetails();

             temp = (PRPayrollDetails)PayRollsDetails.Cache.Insert(temp);

If to believe to results of Stopwatch, the second part of code works faster. For my set of data, the first one took 5.5 seconds, while the second option took 4.3 seconds. And this is difference for 300 records. If you have any ideas why it can be, you are welcome to share them.

And if I had 250 records then the difference between option a and b is ~4 seconds

How to read from stored procedure in Acumatica

Hello readers,

today I want to share hwo it is possible to read from stored procedure in Acumatica. Keep in mind, that reading from sp is against best practices of Acumatica, because if you read from sp, then you'll reading will not be cached and other side effects, but sometime you don't have a choice. So, for such cases you can try following approach:

 

create procedure uspWow

@invnbr nvarchar(6),

@money decimal(19,4) OUTPUT

AS

       SELECT @money = ARInvoice.CuryLineTotal

       FROM ARInvoice

       WHERE ARInvoice.RefNbr = @invnbr;

       RETURN

GO

 

 

var invNbr = new PXSPInParameter("invnbr""000595");

var money = new PXSPOutParameter("money"PXDbType.Decimal, 19, 4, null);

var results = PXDatabase.Execute("uspWow"new PXSPParameter[] { invNbr, money});

 

var sb = new System.Text.StringBuilder("PROC RESULT:\n");

foreach(var r in results)

{

    sb.AppendLine(r.ToString());

}

throw new PXException(sb.ToString());

Get extension of basic DAC class in Acumatica

Helllo,

today I want to share how to get extension of basic DAC class iin Acumatica. Lets say you have class ARTran, and extenstion for it ARTranExt and you want to get it in the code of ARTran_RowUpdated event

Then you can get extenstion in the following way:

 

 

   protected virtual void ARTran_RowUpdated(PXCache sender, PXRowUpdatedEventArgs e)

        {

            var newRow = (ARTran)e.Row;

            var newRowE = PXCache<ARTran>.GetExtension<ARTranExt>(newRow);

            .

            .

            .

       }

How to get companyid in Acumatica

Hello everybody,

today I want to share how to find current company id in Acumatica:

Here it is:

int? companyid = PX.Common.PXContext.GetSlot<Int32?>("singleCompanyID")

 

for me it was long way to find it

Enumerate Cached objects

Hello everybody,

today I want to share how to enumerate cahed objects according to some type. 

Imagine you have following declaration:

public PXSelectJoin<PRTran> PaySlipDetails;

And that you need to enumerate them as list of PRTran's in some special way.

In my case I used following instruction:

 

var prTrans = PaySlipDetails.Cache.Cached.Cast<PRTran>().ToList().Where(a => a.IsDeleted.HasValue && !a.IsDeleted.Value).ToList();// we have list of all undeleted pr trans

var sumOtherAdj = prTrans.Where(a => a.EarningType == EarningType.OA.GetEnumDescription() || a.EarningType == EarningType.OD.GetEnumDescription()).Sum(a => a.TotalAmount);

Rename column at screen in Acumatica

Hello everybody,

today I want to share answer at simple question how to change the caption at some screen. 

For example you want to change caption at screen AR202000 for Inventory ID to caption INVENTORY ID.

For this purpose we need to understand which DAC is responsible for Grid. For this puprose we can open page AR202000.

There we can see the following line:

<px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%" PrimaryView="Filter" TypeName="PX.Objects.AR.ARSalesPriceMaint">

So it means that for Graph or controller we will check ARSalesPriceMaint.

For field inventory id responsible code 

<px:PXSegmentMask ID="edInventoryID" runat="server" DataField="InventoryID" AllowEdit="True">

and view which we need is named 

 <px:PXGridLevel DataMember="Records">

Lets navigate to class PX.Objects.AR.ARSalesPriceMaint with help of reflector ( one of my favorite tools ).

Find there member Records which is declared in the following way:

public SalesPriceProcessing<ARSalesPrice, InnerJoin<InventoryItem, On<InventoryItem.inventoryID, Equal<ARSalesPrice.inventoryID>>, LeftJoin<INItemCost, On<INItemCost.inventoryID, Equal<InventoryItem.inventoryID>>>>, Where<ARSalesPrice.custPriceClassID, Equal<Current<ARSalesPriceFilter.custPriceClassID>>, And2<Where<Current<ARSalesPriceFilter.curyID>, IsNull, Or<ARSalesPrice.curyID, Equal<Current<ARSalesPriceFilter.curyID>>>>, And<ARSalesPrice.isPromotionalPrice, Equal<Current<ARSalesPriceFilter.promotionalPrice>>, And<InventoryItem.itemStatus, NotEqual<INItemStatus.inactive>, And<InventoryItem.itemStatus, NotEqual<INItemStatus.toDelete>, And<Where2<Where<Current<ARSalesPriceFilter.itemClassID>, IsNull, Or<Current<ARSalesPriceFilter.itemClassID>, Equal<InventoryItem.itemClassID>>>, And2<Where<Current<ARSalesPriceFilter.inventoryPriceClassID>, IsNull, Or<Current<ARSalesPriceFilter.inventoryPriceClassID>, Equal<InventoryItem.priceClassID>>>, And2<Where<Current<ARSalesPriceFilter.ownerID>, IsNull, Or<Current<ARSalesPriceFilter.ownerID>, Equal<InventoryItem.priceManagerID>>>, And2<Where<Current<ARSalesPriceFilter.myWorkGroup>, Equal<boolFalse>, Or<InventoryItem.priceWorkgroupID, InMember<CurrentValue<ARSalesPriceFilter.currentOwnerID>>>>, And<Where<Current<ARSalesPriceFilter.workGroupID>, IsNull, Or<Current<ARSalesPriceFilter.workGroupID>, Equal<InventoryItem.priceWorkgroupID>>>>>>>>>>>>>>, OrderBy<Asc<ARSalesPrice.inventoryID, Asc<ARSalesPrice.uOM, Asc<ARSalesPrice.lastDate>>>>> Records;

Looks scarry, huh? For me in the begining of my way also. But lets continue. 

DataField="InventoryID" means that this field is declared in class ARSalesPrice.

Just for example lets say that DataField looked in the following way:

DataField="InventoryItem__InventoryID". That mean that we need to search for class InventoryItem.

Lets navigate with reflector to class ARSalesPrice and find there field InventoryID.

It's declared in the following way:

        [Inventory(DisplayName="Inventory ID"), PXDefault, PXParent(typeof(Select<InventoryItem, Where<InventoryItem.inventoryID, Equal<Current<inventoryID>>>>))]
        public virtual int? InventoryID
        {
            get
            {
                return this._InventoryID;
            }
            set
            {
                this._InventoryID = value;
            }
        }

Then do the following:

1. create class ARSalesPriceExt like this:

 public class  ARSalesPriceExt : PXCacheExtension< ARSalesPrice>
    {

2. In the class declare the following member:

        [Inventory(DisplayName="INVENTORY ID"), PXDefault, PXParent(typeof(Select<InventoryItem, Where<InventoryItem.inventoryID, Equal<Current<inventoryID>>>>))]
        public virtual int? InventoryID
        {
            get
            {
                return this._InventoryID;
            }
            set
            {
                this._InventoryID = value;
            }
        }

3. Build class library and reference it in your Acumatica web site and congratulations, you have changed caption in grid

 

FieldUpdated behaviour

Hello everybody,

today  I want to share one interesting feature of Acumatica. 

I revelaed that after inserting of new row executes FieldUpdated even if field wasn't updated. After reading documentation I found that it is gotcha and inteded to be so.

Acumatica and ObjectsEqual lack of parameters

Hello everybody,

today I want to share how I deal with ObjectsEqual limitation. In one of my tasks I had requirement to check for equality more then eight parameters and found that ObjectsEqual takes only 8 parameters. But I needed more. 

In order to fix it I decided to use extension classes of C# which allows inject into existing class new functionality.

Here is the way how I did it:

public static class PXCacheExtensions
    {
        public static bool ObjectsEqualExt(this PXCache cache, object a, object b, params IBqlField[] values)
        {
            var result = true;
            foreach (var bqlField in values)
            {
                result = result && object.Equals(cache.GetValue<IBqlField>(a), cache.GetValue<IBqlField>(b));
            }

            return result;
        }

        public static bool ObjectsEqual<Field1, Field2, Field3, Field4, Field5, Field6, Field7, Field8, Field9>(this PXCache cache, object a, object b)
            where Field1 : IBqlField
            where Field2 : IBqlField
            where Field3 : IBqlField
            where Field4 : IBqlField
            where Field5 : IBqlField
            where Field6 : IBqlField
            where Field7 : IBqlField
            where Field8 : IBqlField
            where Field9 : IBqlField
        {
            return (cache.ObjectsEqual<Field9>(a, b) && cache.ObjectsEqual<Field1, Field2, Field3, Field4, Field5, Field6, Field7, Field8>(a, b));
        }

        public static bool ObjectsEqual<Field1, Field2, Field3, Field4, Field5, Field6, Field7, Field8, Field9, Field10>(this PXCache cache, object a, object b)
            where Field1 : IBqlField
            where Field2 : IBqlField
            where Field3 : IBqlField
            where Field4 : IBqlField
            where Field5 : IBqlField
            where Field6 : IBqlField
            where Field7 : IBqlField
            where Field8 : IBqlField
            where Field9 : IBqlField
            where Field10 : IBqlField
        {
            return (cache.ObjectsEqual<Field10>(a, b) && cache.ObjectsEqual<Field1, Field2, Field3, Field4, Field5, Field6, Field7, Field8, Field9>(a, b));
        }

        public static bool ObjectsEqual<Field1, Field2, Field3, Field4, Field5, Field6, Field7, Field8, Field9, Field10, Field11>(this PXCache cache, object a, object b)
            where Field1 : IBqlField
            where Field2 : IBqlField
            where Field3 : IBqlField
            where Field4 : IBqlField
            where Field5 : IBqlField
            where Field6 : IBqlField
            where Field7 : IBqlField
            where Field8 : IBqlField
            where Field9 : IBqlField
            where Field10 : IBqlField
            where Field11 : IBqlField
        {
            return (cache.ObjectsEqual<Field11>(a, b) && cache.ObjectsEqual<Field1, Field2, Field3, Field4, Field5, Field6, Field7, Field8, Field9, Field10>(a, b));
        }

        public static bool ObjectsEqual<Field1, Field2, Field3, Field4, Field5, Field6, Field7, Field8, Field9, Field10, Field11, Field12>(this PXCache cache, object a, object b)
            where Field1 : IBqlField
            where Field2 : IBqlField
            where Field3 : IBqlField
            where Field4 : IBqlField
            where Field5 : IBqlField
            where Field6 : IBqlField
            where Field7 : IBqlField
            where Field8 : IBqlField
            where Field9 : IBqlField
            where Field10 : IBqlField
            where Field11 : IBqlField
            where Field12 : IBqlField
        {
            return (cache.ObjectsEqual<Field12>(a, b) && cache.ObjectsEqual<Field1, Field2, Field3, Field4, Field5, Field6, Field7, Field8, Field9, Field10, Field11>(a, b));
        }


    }

After those modifications my cache accepts twelve parameters for comparing

Enums and strings

Hello everybody,

I want to share some pieces of code how to work with enums. 

Some time it is needed to have list of string constants in your code, which can feet to some strings with spaces. 

enum EarningType
    {
        [Description("Virginina")]
        VL,
        [Description("Salt and Lasso")]
        SL,
        [Description("TO")]
        TO,
        [Description("RG")]
        RG
    }

One of easy ways to use description is usage of extension methods like this:

 public static class EnumsProcessor
    {
        public static string GetEnumDescription(this Enum value)
        {
            var fi = value.GetType().GetField(value.ToString());

            var attributes =
                (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attributes.Length > 0)
                return attributes[0].Description;
            return value.ToString();
        }
}

and then in code you can write something like this:

EarningType.VL.GetEnumDescription() which will return you description from description field.

Execute stored procedure in Acumatica

Hello everybody.

I want to share if you need to execute stored procedure in Acumatica how to achieve it. 

It's simple.

1. Create Stored procedure in db.

2. Use in your code PXDatabase.Execute.

For example let's say you created stored procedure which has name DeletePrTranByPrPayrollAndPayrollDetailsId and takes as parameters @prPayrollRefNbr and @prPayrollDetailsID which are of type nvarchar.

Then you can call stored procedure in the following way:

var pars = new List<PXSPParameter>();

PXSPParameter p1 = new PXSPInParameter("@prPayrollRefNbr", PXDbType.NChar, details.PayrollRefNbr);

PXSPParameter p2 = new PXSPInParameter("@prPayrollDetailsID", PXDbType.NChar, details.PRPayrollDetailID);

pars.Add(p1);

pars.Add(p2);

PXDatabase.Execute("DeletePrTranByPrPayrollAndPayrollDetailsId", pars.ToArray());