RowInserting and RowInserted difference in Acumatica

Hello everybody,

now I want to share with you difference between RowInserting and RowInserted events.

The RowInserting event happens before the new data record is actually inserted into the cache but after all field events happen for this data record. The RowInserted event happens after the actual insertion. If you change the data fields of the data record that you insert in a RowInserting event handler, no field events will be raised for these data fields. You use the RowInserted event to do something after a data record is inserted into the cache. For example, you can use a RowInserted event handler of the master data record to add a default detail data record.

Sample of RowInserting:

protected virtual void APAdjust_RowInserting(PXCache sender, PXRowInsertingEventArgs e)

{

.................. 

}

Sample of RowInserted

protected virtual void APAdjust_RowInserted(PXCache sender, PXRowInsertedEventArgs e)

{

.................. 

}

The main purpose of RowInserted IMHO is to add default details to data record

How to add buttons Refresh Add Row, Delete Row, Fit To Screen in Grid of Acumatica

Hello everybody,

Imagine that you crated in your page Grid, and when opened it at page can't notice buttons Refresh Add Row, Delete Row, Fit To Screen in Grid. I believe it can be puzzling how to add those buttons to screen as it was for me at once.  Add to your grid following line:

 SkinID="Details"

For example it can look like this:

<px:PXGrid ID="PXGrid1" runat="server" DataSourceID="ds" TabIndex="-8036" AdjustPageSize="Auto" AllowPaging="True" Height="200px" Width="100%" SkinID="Details" KeepPosition="True" MatrixMode="True"  SyncPosition="True">

If you add those attributes, you'll see those buttons.

The Structure of a Strategy Program in LUA

Hi all,

I continue investiaging another area of knowledge of humankind, which is FXCM trading Station.

So, if you need to write strategy, you'll need following methods to be defined in your strategy: 

  1. Init()
  2. Prepare(nameOnly)
  3. Update()
  4. ReleaseInstance()
  5. AsyncOperationFinished(cookie, successful, message, message1, message2)
  6. CheckParameters(parameters)
  7. ChangeParameters()

Now little bit details:

The Init() function of the strategy is called once, when the host application has just loaded the strategy into the memory. In this function the strategy must create profile. Profile is the information which is required for creation of the strategy: strategy name and description and parameters.

When the function is being executed, there is a global variable called strategy. You must use the table kept in this variable to provide all necessary information.

Note: Values of any global variables, which are defined in the Init() function will not be available in the other strategies functions. Because the user can apply multiple instance of the same strategy at the same time, the new instance of the strategy is created every time when the user applies the strategy.

Prepare function is called every time when the user applies the strategy. At this moment, a copy of the strategy code is created. This copy is called strategy instance. At one moment, there can be as many strategy instances as many times the strategy is applied.

In this function you must:

  • Prepare all common data for further work of the strategy logic.

When the function is being executed, there is a global variable called instance. You must use this to access to the instrument and parameters data.

Parameter nameOnly indicates whether this function was called just for creating instance's name.

Important: strategy instance is always created in a new context. The Init() function is not called for an instance, so there is no way to pass data from the Init() function to the Prepare() function.

Update is a most important function in the strategy. The update function is called every time when a new bid or ask price appears on the instrument to which the strategy is attached.

Use the terminal table to get access to the trading application functions.

Release function is optional, you can omit it in your strategy.

In case this function exists, it is called before the strategy instance is destroyed. You can use this function to dispose objects created in the Prepare function.

 

Other functions will be described in another post

CompanyID may not be empty

Hello everybody.

In case if you want to add some table to Acumatica with compnayid field, and suddenly you can't save it, check weather you configured default Value or Binding in db. It should be ((0)) like on the screenshot:

I noticed it only recently while coping with error message about company id

PXDefault PXParent in Acumatica

Hello everybody,

today just quick fact. In master-detail DAC and Graph there are used attributes PXDBDefault and PXParent. For me it was interesting to know, that PXDBDefault is intended for initializing values and PXParent is intended for cascade deleting of rows.

Acumatica epic failed or captions at page not appear

Hello everybody.

I want to share with whole world a story. In the begining of this month I created web form. Added there some controls. Today I wanted to add one another control, and after my addition miracously controls at page disappeared. I was surprised, looked for ways to solve problem, and after wasted hours I decided to remove and add control again. Try to imagine my shock, when caption of DAC appeared back on page!!!!. So next time if caption disappears just remove and add control at "Page Layout" and it maybe will appear. I have no comments, just plenty of negative emotions.

How pages in acumatica work

Hello Everybody.

Today I will speak about some trivial matters, which I found during usage reflector while analyzed received source code of acumatica.

So lets go on.

Each page of acumatica as base class PX.Web.UI.PXPage.

Base page has the following declaration:

public PXDataSource DefaultDataSource; 

This explains why we need to describe graphe for DataSource. 

Inside of disassembled code I found method IsPageCustomized, which looks in folder CstPublished in order to ched did page was customized.

Another interesting feature is method OnInit which is overrided. It means that Acumatica adds controls to page by itself which is correct from viewpoint of ASP.Net pages lifecycle.

But let us add some goal in code digging. Which part of Acumatica responsible for loading data? I assumed that PXDataSource and moved to that part. For me it was interesting to notice there ExecuteSelect method.

If to look on it looks like it is used in order to get something from DB:

internal override IEnumerable ExecuteSelect(string viewName, DataSourceSelectArguments arguments, PXDSSelectArguments pxarguments)

What I noticed specially is string viewName, which maybe corresponds to view in Graph class, while I still not sure. I decided to search for property TypeName. And I found that it is located at class PXBaseDataSource. 

Also at that class my eyes were catched by such interesting string array declaration:

 private static readonly string[] ag = new string[10]

    {

      "cancel",

      "save",

      "insert",

      "delete",

      "first",

      "last",

      "next",

      "previous",

      "prev",

      "copypaste"

    };

And look at this!!!! We can find how Acumatica declared graph and how it loads it:

[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]

    [Browsable(false)]

    public PXGraph DataGraph

    {

              bla bla bla

     }

bla bla bla means the following:

1. If design mode, then goes some actions, which for now doesn't have much interest for me.

2. If not design mode, then goes some staff, which confuses me. 

But lets see by details:

if (this._DataGraph != null) 

and the goes code which created graph.

After that I went to class PXGraph, and found that PrimaryView property is declared there. 

Well. Still it wasn't point of interest for me. So I moved to another class: PXGrid. But that will be another post

Acumatica modification existing tables with prefix Usr

Hello everybody,

today just a short hint. In case if you want to add some column in Acumatica DB, you should start field from usr prefix. For exaple if you want to add field Payrollcat, you should name it UsrPayrollcat. If you'll not use this advice, next update of Acumatica will remove your field, and you'll be very sad.

For example you can use following template of code:

IF EXISTS (SELECT * FROM SYS.columns WHERE name = N'UsrPayrollcat' 
and object_id = OBJECT_ID(N'PRPayrollDetails')) BEGIN PRINT 'EXISTS' END ELSE BEGIN Alter table PRPayrollDetails ADD UsrPayrollcat nvarchar(1) NULL PRINT 'Column UsrPayrollcat was created' END GO

Enjoy

Add button to grid in Acumatica

Hello everybody,

today I want briefly share how to add button to Grid in Acumatica. Actually button without any dialogs. Just button with posibility to run C# code.

Lets say you have grid, and want name for button Calculate. 

Then in graph you should write something like this:

public PXAction<PRPayroll> Calculate;
[PXButton]
[PXUIField(DisplayName = "Calculate")]
public virtual IEnumerable calculate(PXAdapter adapter)
{
   return adapter.Get();
}

And in grid something like this:

 <ActionBar ActionsText="True">
    <CustomItems>
        <px:PXToolBarButton>
        <AutoCallBack Command="Calculate" Target="ds" >
             </AutoCallBack>
       </px:PXToolBarButton>
     </CustomItems>
 </ActionBar>
</px:PXGrid>

And you'll get in your grid nice button Calculate. Enjoy

Master details step by step

Hello everybody,

today I want to share how to implement master detail form step by step starting from small. So, recently I was in situation when I created form, generated DAC, created aspx page, and got some errors. When I asked support for help, they informed me that they will help me only one hour for free, and another hours for $$$$. I need to say that I appreciate communication with Acumatica support team, and I even ready to pay to them, but should admit it's not always easy to me. 

So I decided to make another option. 

1. Create very simple tables

2. Create very simple DAC

3. Add complication logic.

Few words what means very simple. In my terms very simple is Table with key and fields cratedbyid, lastmodifiedbyid, etc. The same structure of DAC. So let's go on. 

Here it goes first table:

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[PRPayroll](
    [RefNbr] [nvarchar](8) NOT NULL,
    [CompanyID] [int] NOT NULL,
    [tstamp] [timestamp] NULL,
    [CreatedByID] [uniqueidentifier] NOT NULL,
    [CreatedByScreenID] [char](8) NOT NULL,
    [CreatedDateTime] [datetime] NOT NULL,
    [LastModifiedByID] [uniqueidentifier] NOT NULL,
    [LastModifiedByScreenID] [char](8) NOT NULL,
    [LastModifiedDateTime] [datetime] NOT NULL,
 CONSTRAINT [PK_PRPayroll_1] PRIMARY KEY CLUSTERED 
(
    [RefNbr] ASC,
    [CompanyID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

Second table:

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[PRPayrollDetails](
    [CompanyID] [int] NOT NULL,
    [PRPayrollDetailID] [nvarchar](8) NOT NULL,
    [PRPayrollID] [nvarchar](8) NULL,
 CONSTRAINT [PK_PRPayrollDetails] PRIMARY KEY CLUSTERED 
(
    [CompanyID] ASC,
    [PRPayrollDetailID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

Next step is of course to generate DAC classes. After generating DAC classes copy/paste from coderepository.xml fields [PXDBCreatedByID()], [PXDBCreatedByScreenID()], ...., [PXDBTimestamp()].

Classes which you'll see should be the following:

namespace DS
{
    using PX.Objects.AP;
    using PX.Objects.CM;
    using PX.Objects.CS;
    using PX.Objects.EP;
    using PX.Objects.GL;
    using System;
    using PX.Data;
    
    [System.SerializableAttribute()]
    public class PRPayroll : PX.Data.IBqlTable
    {
        #region RefNbr
        public abstract class refNbr : PX.Data.IBqlField
        {
        }
        protected string _RefNbr;
        [PXDBString(8, IsKey = true, IsUnicode = true)]
        [PXDefault()]
        [PXUIField(DisplayName = "Reference Nbr.")]
        [PXSelector(typeof(Search<PRPayroll.refNbr>), new Type[] { typeof(PRPayroll.refNbr) })]
        public virtual string RefNbr
        {
            get
            {
                return this._RefNbr;
            }
            set
            {
                this._RefNbr = value;
            }
        }
        #endregion
        #region CreatedByID
        public abstract class createdByID : PX.Data.IBqlField
        {
        }
        protected Guid? _CreatedByID;
        [PXDBCreatedByID()]
        public virtual Guid? CreatedByID
        {
            get
            {
                return this._CreatedByID;
            }
            set
            {
                this._CreatedByID = value;
            }
        }
        #endregion
        #region CreatedByScreenID
        public abstract class createdByScreenID : PX.Data.IBqlField
        {
        }
        protected string _CreatedByScreenID;
        [PXDBCreatedByScreenID()]
        public virtual string CreatedByScreenID
        {
            get
            {
                return this._CreatedByScreenID;
            }
            set
            {
                this._CreatedByScreenID = value;
            }
        }
        #endregion
        #region CreatedDateTime
        public abstract class createdDateTime : PX.Data.IBqlField
        {
        }
        protected DateTime? _CreatedDateTime;
        [PXDBCreatedDateTime()]
        public virtual DateTime? CreatedDateTime
        {
            get
            {
                return this._CreatedDateTime;
            }
            set
            {
                this._CreatedDateTime = value;
            }
        }
        #endregion
        #region LastModifiedByID
        public abstract class lastModifiedByID : PX.Data.IBqlField
        {
        }
        protected Guid? _LastModifiedByID;
        [PXDBLastModifiedByID()]
        public virtual Guid? LastModifiedByID
        {
            get
            {
                return this._LastModifiedByID;
            }
            set
            {
                this._LastModifiedByID = value;
            }
        }
        #endregion
        #region LastModifiedByScreenID
        public abstract class lastModifiedByScreenID : PX.Data.IBqlField
        {
        }
        protected string _LastModifiedByScreenID;
        [PXDBLastModifiedByScreenID()]
        public virtual string LastModifiedByScreenID
        {
            get
            {
                return this._LastModifiedByScreenID;
            }
            set
            {
                this._LastModifiedByScreenID = value;
            }
        }
        #endregion
        #region LastModifiedDateTime
        public abstract class lastModifiedDateTime : PX.Data.IBqlField
        {
        }
        protected DateTime? _LastModifiedDateTime;
        [PXDBLastModifiedDateTime()]
        public virtual DateTime? LastModifiedDateTime
        {
            get
            {
                return this._LastModifiedDateTime;
            }
            set
            {
                this._LastModifiedDateTime = value;
            }
        }
        #endregion
        #region tstamp
        public abstract class Tstamp : PX.Data.IBqlField
        {
        }
        protected byte[] _tstamp;
        [PXDBTimestamp()]
        public virtual byte[] tstamp
        {
            get
            {
                return this._tstamp;
            }
            set
            {
                this._tstamp = value;
            }
        }
        #endregion
    }
}

namespace DS
{
    using System;
    using PX.Data;
    
    [System.SerializableAttribute()]
    public class PRPayrollDetails : PX.Data.IBqlTable
    {
        #region PRPayrollDetailID
        public abstract class pRPayrollDetailID : PX.Data.IBqlField
        {
        }
        protected string _PRPayrollDetailID;
        [PXDBString(8, IsKey = true, IsUnicode = true)]
        [PXDefault()]
        [PXUIField(DisplayName = "PRPayrollDetailID")]
        public virtual string PRPayrollDetailID
        {
            get
            {
                return this._PRPayrollDetailID;
            }
            set
            {
                this._PRPayrollDetailID = value;
            }
        }
        #endregion
        #region PRPayrollID
        public abstract class pRPayrollID : PX.Data.IBqlField
        {
        }
        protected string _PRPayrollID;
        [PXDBString(8, IsUnicode = true)]
        [PXUIField(DisplayName = "PRPayrollID")]
        public virtual string PRPayrollID
        {
            get
            {
                return this._PRPayrollID;
            }
            set
            {
                this._PRPayrollID = value;
            }
        }
        #endregion
        #region CreatedByID
        public abstract class createdByID : PX.Data.IBqlField
        {
        }
        protected Guid? _CreatedByID;
        [PXDBCreatedByID()]
        public virtual Guid? CreatedByID
        {
            get
            {
                return this._CreatedByID;
            }
            set
            {
                this._CreatedByID = value;
            }
        }
        #endregion
        #region CreatedByScreenID
        public abstract class createdByScreenID : PX.Data.IBqlField
        {
        }
        protected string _CreatedByScreenID;
        [PXDBCreatedByScreenID()]
        public virtual string CreatedByScreenID
        {
            get
            {
                return this._CreatedByScreenID;
            }
            set
            {
                this._CreatedByScreenID = value;
            }
        }
        #endregion
        #region CreatedDateTime
        public abstract class createdDateTime : PX.Data.IBqlField
        {
        }
        protected DateTime? _CreatedDateTime;
        [PXDBCreatedDateTime()]
        public virtual DateTime? CreatedDateTime
        {
            get
            {
                return this._CreatedDateTime;
            }
            set
            {
                this._CreatedDateTime = value;
            }
        }
        #endregion
        #region LastModifiedByID
        public abstract class lastModifiedByID : PX.Data.IBqlField
        {
        }
        protected Guid? _LastModifiedByID;
        [PXDBLastModifiedByID()]
        public virtual Guid? LastModifiedByID
        {
            get
            {
                return this._LastModifiedByID;
            }
            set
            {
                this._LastModifiedByID = value;
            }
        }
        #endregion
        #region LastModifiedByScreenID
        public abstract class lastModifiedByScreenID : PX.Data.IBqlField
        {
        }
        protected string _LastModifiedByScreenID;
        [PXDBLastModifiedByScreenID()]
        public virtual string LastModifiedByScreenID
        {
            get
            {
                return this._LastModifiedByScreenID;
            }
            set
            {
                this._LastModifiedByScreenID = value;
            }
        }
        #endregion
        #region LastModifiedDateTime
        public abstract class lastModifiedDateTime : PX.Data.IBqlField
        {
        }
        protected DateTime? _LastModifiedDateTime;
        [PXDBLastModifiedDateTime()]
        public virtual DateTime? LastModifiedDateTime
        {
            get
            {
                return this._LastModifiedDateTime;
            }
            set
            {
                this._LastModifiedDateTime = value;
            }
        }
        #endregion
        #region tstamp
        public abstract class Tstamp : PX.Data.IBqlField
        {
        }
        protected byte[] _tstamp;
        [PXDBTimestamp()]
        public virtual byte[] tstamp
        {
            get
            {
                return this._tstamp;
            }
            set
            {
                this._tstamp = value;
            }
        }
        #endregion
    }
}

The next step will be writing joins between master and detail. 

Below goes what I wrote for class PRPayrollDetails

#region PRPayrollID
        public abstract class pRPayrollID : PX.Data.IBqlField
        {
        }
        protected string _PRPayrollID;
        
        [PXDBString(8, IsUnicode = true)]
        [PXDBDefault(typeof(PRPayroll.refNbr))]
        [PXParent(typeof(Select<PRPayroll, Where<PRPayroll.refNbr, Equal<Current<PRPayrollDetails.pRPayrollID>>>>))]
        public virtual string PRPayrollID
        {
            get
            {
                return this._PRPayrollID;
            }
            set
            {
                this._PRPayrollID = value;
            }
        }
        #endregion

Next step goes for Graph. Below goes implementation of graph:

public class PayRollManager : PXGraph<PayRollManager, PRPayroll>
    {
        public PXSelect<PRPayroll> PayRolls;
        public PXSelectJoin <PRPayrollDetails, InnerJoin<PRPayroll, On<PRPayrollDetails.pRPayrollDetailID, Equal<Current<PRPayroll.refNbr>>>>> PayRollsDetails;

    }

The next step is formatting page. Below goes my code:

<%@ Page Language="C#" MasterPageFile="~/MasterPages/FormDetail.master" AutoEventWireup="true" ValidateRequest="false" CodeFile="PR301000_.aspx.cs" Inherits="Page_PR301000" Title="Untitled Page" %>
<%@ MasterType VirtualPath="~/MasterPages/FormDetail.master" %>

<asp:Content ID="cont1" ContentPlaceHolderID="phDS" Runat="Server">
    <px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%" PrimaryView="PayRolls" SuspendUnloading="False" TypeName="DS.PayRollManager">
    </px:PXDataSource>
</asp:Content>
<asp:Content ID="cont2" ContentPlaceHolderID="phF" Runat="Server">
    <px:PXFormView ID="form" runat="server" DataSourceID="ds" Width="100%" Height="170px" DataMember="PayRolls" TabIndex="11400">
        <Template>
            <px:PXSelector ID="edRefNbr" runat="server" CommitChanges="True" DataField="RefNbr">
            </px:PXSelector>
        </Template>
    </px:PXFormView>
</asp:Content>
<asp:Content ID="cont3" ContentPlaceHolderID="phG" Runat="Server">
         <px:PXGrid ID="grid" runat="server" DataSourceID="ds" AdjustPageSize="Auto" AllowPaging="True" Width="100%" Height="250px" SkinID="Details" TabIndex="13000">
        <Levels>
            <px:PXGridLevel DataMember="PayRollsDetails">
                <RowTemplate>
                    <px:PXMaskEdit ID="edPRPayrollDetailID" runat="server" DataField="PRPayrollDetailID">
                    </px:PXMaskEdit>
                    <px:PXSelector ID="edPRPayroll__RefNbr" runat="server" DataField="PRPayroll__RefNbr">
                    </px:PXSelector>
                </RowTemplate>
                <Columns>
                    <px:PXGridColumn DataField="PRPayrollDetailID">
                        <ValueItems MultiSelect="False">
                        </ValueItems>
                    </px:PXGridColumn>
                    <px:PXGridColumn DataField="PRPayroll__RefNbr">
                        <ValueItems MultiSelect="False">
                        </ValueItems>
                    </px:PXGridColumn>
                </Columns>
            </px:PXGridLevel>
        </Levels>
        <AutoSize Container="Window" Enabled="True" MinHeight="220" />
        <ActionBar ActionsText="True">
            <CustomItems>
                <px:PXToolBarButton Text="Update Salary" Key="cmdUpdateSalaryGrid">
                    <AutoCallBack Command="UpdateSalaryGrid" Target="ds" />
                    <PopupCommand Command="Cancel" Target="ds" />
                </px:PXToolBarButton>
                <px:PXToolBarButton Text="Create Pay Slip" >
                    <AutoCallBack Command="CreatePaySlip" Target="ds" />
                </px:PXToolBarButton>
                <px:PXToolBarButton Text="Calculate" >
                    <AutoCallBack Command="Calculate" Target="ds" />
                </px:PXToolBarButton>
                <px:PXToolBarButton Text="Validate">
                    <AutoCallBack Command="Validate" Target="ds" />
                </px:PXToolBarButton>
                <px:PXToolBarButton Text="View Doucment" >
                    <AutoCallBack Command="ViewDocument" Target="ds" />
                </px:PXToolBarButton>
            </CustomItems>
        </ActionBar>
    </px:PXGrid>
</asp:Content>

I ommited explanation how to use "Edit content layout" screen as it's out of the topic. 

So, if to open page, then you'll be able to notice possibility to save. Try to enter some values there, monitor db, and you'll see that staf goes saved in db, but with one limitation. For some unknown reason staf is not loaded. After watching more carefull, I noticed what is the source of problem, after instead of 

        public PXSelectJoin <PRPayrollDetails, InnerJoin<PRPayroll, On<PRPayrollDetails.pRPayrollDetailID, Equal<Current<PRPayroll.refNbr>>>>> PayRollsDetails;

I wrote

        public PXSelectJoin <PRPayrollDetails, InnerJoin<PRPayroll, On<PRPayrollDetails.pRPayrollID, Equal<Current<PRPayroll.refNbr>>>>> PayRollsDetails;

and rebuilding I got working grid, with working switching.

Summary. 

If you need automatic binding between your master and details, you need

1. PXParent and PXDBDefault attributes in your Details class.

2. Master and detail DAC's should have IsKeyAttribute=true, 

3. Write correct PXSelectJoin

My next step will be adding nesessary columns to grid

Download