How To React On Clicking On Abort Button In Acumatica Processing Screen

 

Hello everybody,

today I want to leave a notice with proposal on how to program reaction on clicking of Abort button in processing screen.

Recently it become needed for me to program some functionality to happen after user clicks on button Abort of processing screen. 

In my case I needed somehow to program exit from some threads which were running in parallel and I needed to finish one iteration of threads and then exit from each of those threds.

One more point was that just terminating of threads was also a bad idea. After a while I have found IPXCustomInfo interface. With that interface you can gain control on what to do in case of user 

clicking on Abort button. In my case I did the following:

  1. Created my own implementation of IPXCustomInfo like this:
public class PXImportCallback : IPXCustomInfo
{
    public void Complete(PXLongRunStatus status, PXGraph graph)
    {
        switch (status)
        {
            case PXLongRunStatus.Aborted:
                PXDatabase.Update<Sync>(
                    new PXDataFieldAssign<Sync.lastModifiedDateTime>(DateTime.Now),
                    new PXDataFieldAssign<Sync.wasExecuted>(false));
                break;
 
            case PXLongRunStatus.Completed:
                break;
 
            case PXLongRunStatus.InProcess:
                break;
 
            case PXLongRunStatus.NotExists:
                break;
        }
    }
}

As you can see, very simple interface, which requires of you to implement only one method: Complete, and inside of that method program supposed reaction.

Then for each StartOperation I've added following lines:

PXLongOperation.StartOperation(thisdelegate
{
    PXLongOperation.SetCustomInfo(new PXImportCallback());
 
    var orderFromDb = PXSelectReadonly<

and that's all! Each time users clicks on Abort, my programmed reaction appears, and I can clean memory and delete no longer needed files.

 

 

Create View If Not Exists In Acumatica Customization

 

Hello everybody,

today I want to leave a short post on how to create some custom view in Acumatica Customization if that view doesn't exist:

if not exists (select * from sysobjects where name='CustomerSelector' and xtype='V')
begin
exec (
    'CREATE view [CustomerSelector] as 
		select Distinct b.CompanyID, cc.Phone1, a.City, a.CountryID, b.TaxRegistrationID, c.CuryID, cc.Salutation, c.CustomerClassID, b.Status
		from BAccount b inner join customer c on b.BAccountID = c.BAccountID inner join [Address] a 
		on a.BAccountID = b.BAccountID inner join Contact cc on cc.BAccountID = b.BAccountID'
		)
 
end

This code will create view CustomerSelector only if such view doesn't exist in Acumatica

 

How To Avoid Echo During Usage Of Teamviewer

 

Hello everybody,

here I want to leave a short notice on how to avoid hearing yourself if you are connected over TeamViewer to somebodies computer. By defualt TeamViewer duplicates sound that goes through sound system of computer to which you've connected.

Not many people know, that such functionality can be disabled. For this purpose you need to navigate to the following: 

  1. Communicate
  2. Computer Sound
  3. Remove checkbox. 

After those steps you'll be able to avoid hearing sound of yourself via skype or other communication utility.

For convenience I have also screenshot:

 

How To Override Properly Createpaymentproc Method Of Soorderentry Graph In Acumatica

 

Hello everybody,

today I want to leave short note on how to override and call CreatePaymentProc method of Acumatica which I discovered today with Naveen from Kensium.

My favorite way of overriding methods in Acuamtica with usage of Action and passing there parameters doesn't work. That is because Acumatica uses in method CreatePaymentProc out modifier. Due to this, delegate declaration is needed. 

After some efforts and refactorings we found following code that is working:

public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
{
    public delegate void CreatePaymentBase(SOOrder order, out PXGraph target, string paymentType);
 
    [PXOverride]
    public virtual void CreatePaymentProc(SOOrder order, out PXGraph target, string paymentType,
        CreatePaymentBase baseAction)
    {
        baseAction(order, out target, paymentType);
 
        var resultGraph = target as ARPaymentEntry;
        resultGraph.Document.Current.ExtRefNbr = "12xx14";
    }
}

With such code you can call base method, and modify what it produces.

How To Shrink Database Of Acumatica With Rebuilding Of

 

Hello everybody,

today I want to leave another portion of SQL Server optimization for Acumatica database. In case if your db become huge, and you want to make it smaller, you can try following SQL code:

DECLARE @Table NVARCHAR(128)  
DECLARE @Database NVARCHAR(128)
DECLARE @Command NVARCHAR(500)
 
PRINT N'Shrinking database files'
DBCC SHRINKDATABASE(0)
 
PRINT N'Rebuilding all indexes'
SET @Database = DB_NAME()
SET @Command = 'DECLARE TableCursor CURSOR FOR SELECT ''['' + TABLE_CATALOG + ''].['' + TABLE_SCHEMA + ''].['' + 
    TABLE_NAME + '']'' as TableName FROM [' + @Database + '].INFORMATION_SCHEMA.TABLES 
    WHERE TABLE_TYPE = ''BASE TABLE'''   
EXEC (@Command)  
OPEN TableCursor   
 
FETCH NEXT FROM TableCursor INTO @Table   
WHILE @@FETCH_STATUS = 0   
BEGIN   
    PRINT 'Rebuilding all indexes on ' + @Table    
    SET @Command = 'ALTER INDEX ALL ON ' + @Table + ' REBUILD'
    EXEC (@Command) 
FETCH NEXT FROM TableCursor INTO @Table   
END   

this code does two steps:

  1. Shrink database
  2. Rebuild indexes

Remember for yourself forever that any shrink will destroy your indexes. So each time you decide, or risk to make shrink your database don't forget to rebuild indexes. Otherwise you'll be hugely surprised to discover that also db is small, but performance is terrribly bad.

Just want to say that I've tried that approach on db of one of mine customers and results were the following: 646 Gb got shrinked to 161 Gb. Pretty impressive, huh?

And of course, word of warning. Never try that SQL, without recent back up of your production.

How To Manage Default Order Of Columns In Selector Of Acumatica

 

Hello everybody,

today I want to leave a short notice on how to manage order of columns in Selector of Acumatica. Quite often customers has desire to manage default order of columns in selector. That is very easy to achieve with new Type[] parameter added to your selector. Take a look on how I've managed list of columns for Carrier with help of new Type[]:

[PXDBString(15, InputMask = ">aaaaaaaaaaaaaaa", IsKey = true, IsUnicode = true)]
[PXDefault]
[PXUIField(DisplayName = "Ship Via", Visibility = PXUIVisibility.SelectorVisible)]
[PXSelector(typeof(Search<Carrier.carrierID>), new Type[]
       {
         typeof(Carrier.description), typeof(CarrierExt.usrDeliveryLength), typeof(Carrier.confirmationRequired),
         typeof(Carrier.packageRequired), typeof(Carrier.isCommonCarrier)
       },
        CacheGlobal = true, DescriptionField = typeof(Carrier.description))]
protected void Carrier_CarrierID_CacheAttached(PXCache sender)
       {
       }
    }

And below goes screenshot of selector:

As you can see at screenshot, column Delivery duration appeared second in the list, but by default, if I've added that column later, it would be displayed in the end of list.

No Comments

Add a Comment
 

 

Pxdimensionselector And Pxselector In Acumatica

 

Hello everybody,

Today I want to describe PXDimensionSelector attribute usage in Acumatica. According to manual PXDimensionSelector attribute has following purpose:

Defines an input control that combines the functionality of the PXDimenstion attribute and the PXSelector attribute. A user can view the data set defined by the attribute and select a data record from this data set to assign its segmented key value to the field or to replace it with the surrogate key

After reading such purpose, I've decided to read purpose of PXDimension attribute:

Defines an input control that formats the input as a segmented key value and displays the list of allowed values for each key segment.

If those two descriptionss made you understand their pupose and way of usage, then congratulations, but for me it took muuuuuuuch longer. 

Business case ( some details intentionally changed )

I was asked to create table, which has some information about Accounts and their belonging to branches ( later I'll provide sql for table creation as well as filling of created table it with some data ). Then page Sales orders has column Branch, Account, SubAccount should filter list of Accounts and SubAccounts as you can see on the screenshot:

requirement is the following: values at column Account should be filtered by what is selected in the Column Branch by means of showing only those Accounts, which has the same branch in table CompanyAccount.

Preparation

In order to give you a better idea how data was looking like, I've created some sample data with DAC class. Also in order to have some data to play around, I've installed Acumatica with SalesDemo data in order to avoid headache of configuring all from scratch.

Below goes SQL for table creation and filling with some data.

Table creation sql:

SET ANSI_NULLS ON
GO
 
SET QUOTED_IDENTIFIER ON
GO
 
CREATE TABLE [dbo].[CompanyAccount](
	[CompanyID] [int] NOT NULL,
	[CmpBranchID] [int] NOT NULL,
	[AccountID] [int] NOT NULL,
	[Description] [nvarchar](60) NULL,
	[tstamp] [timestamp] NOT 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,
	[NoteID] [uniqueidentifier] NULL,
 CONSTRAINT [PK_CompanyAccount] PRIMARY KEY CLUSTERED 
(
	[CompanyID] ASC,
	[CmpBranchID] ASC,
	[AccountID] 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
 
ALTER TABLE [dbo].[CompanyAccount] ADD  CONSTRAINT [DF_CompanyAccount_CompanyID]  DEFAULT ((0)) FOR [CompanyID]
GO
 
ALTER TABLE [dbo].[CompanyAccount] ADD  CONSTRAINT [DF_Table_1_CNBranchID]  DEFAULT ((0)) FOR [CmpBranchID]
GO

Filling with data of CompanyAccount table:

INSERT INTO [dbo].[CompanyAccount] ([CompanyID],[CmpBranchID],[AccountID],[Description],[CreatedByID],[CreatedByScreenID],[CreatedDateTime]
		,[LastModifiedByID],[LastModifiedByScreenID],[LastModifiedDateTime],[NoteID])
     VALUES (
				2, 16,1195, 'Discount Taken', 'B5344897-037E-4D58-B5C3-1BDFD0F47BF9', 'AR301000', '2018-12-13 00:52:31.550', 
				'B5344897-037E-4D58-B5C3-1BDFD0F47BF9', 'AR301000', '2018-12-13 00:52:31.550', NULL)
 
INSERT INTO [dbo].[CompanyAccount] ([CompanyID],[CmpBranchID],[AccountID],[Description],[CreatedByID],[CreatedByScreenID],[CreatedDateTime]
		,[LastModifiedByID],[LastModifiedByScreenID],[LastModifiedDateTime],[NoteID])
     VALUES
           (
				2,16,1223,'Office Expense', 'B5344897-037E-4D58-B5C3-1BDFD0F47BF9', 'AR301000', '2018-12-13 00:52:31.550', 
				'B5344897-037E-4D58-B5C3-1BDFD0F47BF9', 'AR301000', '2018-12-13 00:52:31.550', NULL
		   )
INSERT INTO [dbo].[CompanyAccount] ([CompanyID],[CmpBranchID],[AccountID],[Description],[CreatedByID],[CreatedByScreenID],[CreatedDateTime]
		,[LastModifiedByID],[LastModifiedByScreenID],[LastModifiedDateTime],[NoteID])
     VALUES
           (
				2,16,1224,'Postage and Delivery', 'B5344897-037E-4D58-B5C3-1BDFD0F47BF9', 'AR301000', '2018-12-13 00:52:31.550', 
				'B5344897-037E-4D58-B5C3-1BDFD0F47BF9', 'AR301000', '2018-12-13 00:52:31.550', NULL
		   )
INSERT INTO [dbo].[CompanyAccount] ([CompanyID],[CmpBranchID],[AccountID],[Description],[CreatedByID],[CreatedByScreenID],[CreatedDateTime]
		,[LastModifiedByID],[LastModifiedByScreenID],[LastModifiedDateTime],[NoteID])
     VALUES
           (
				2,21,1166,'Computer & Office Equipment', 'B5344897-037E-4D58-B5C3-1BDFD0F47BF9', 'AR301000', '2018-12-13 00:52:31.550', 
				'B5344897-037E-4D58-B5C3-1BDFD0F47BF9', 'AR301000', '2018-12-13 00:52:31.550', NULL
		   )
INSERT INTO [dbo].[CompanyAccount] ([CompanyID],[CmpBranchID],[AccountID],[Description],[CreatedByID],[CreatedByScreenID],[CreatedDateTime]
		,[LastModifiedByID],[LastModifiedByScreenID],[LastModifiedDateTime],[NoteID])
     VALUES
           (
				2,21,1167,'Machinery & Equipment', 'B5344897-037E-4D58-B5C3-1BDFD0F47BF9', 'AR301000', '2018-12-13 00:52:31.550', 
				'B5344897-037E-4D58-B5C3-1BDFD0F47BF9', 'AR301000', '2018-12-13 00:52:31.550', NULL
		   )

As you can see from the sql, Branch with id 16

DAC class for CompanyTable:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using PX.Data;
using PX.Objects.GL;
 
namespace AcumaticaSamples
{
    [Serializable]
    public class CompanyAccount : IBqlTable
    {
        #region NoteID
 
        public abstract class noteID : PX.Data.IBqlField
        {
        }
 
        [PXNote]
        public virtual Guid? NoteID { getset; }
 
        #endregion
 
        #region BranchID
        public abstract class cmpBranchID : IBqlField
        {
        }
 
        [PXDBInt(IsKey = true)]
        [PXDefault]
        [PXUIField(DisplayName = "Branch ID")]
        public virtual int? CmpBranchID { getset; }
 
        #endregion
 
        #region AccountID
        public abstract class accountID : IBqlField
        {
        }
 
        [PXDBInt(IsKey = true)]
        [PXDefault]
        [PXUIField(DisplayName = "Account", IsReadOnly = true)]
        [PXSelector(typeof(Search<Account.accountID>), SubstituteKey = typeof(Account.accountCD))]
        public virtual int? AccountID { getset; }
 
        #endregion
 
 
        #region Description
        public abstract class description : IBqlField
        {
        }
        [PXDBString(60)]
        [PXUIField(DisplayName = "Description")]
        public virtual string Description { getset; }
        #endregion
        #region CreatedByID
        public abstract class createdByID : 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 : IBqlField
        {
        }
        protected DateTime? _CreatedDateTime;
        [PXDBCreatedDateTime()]
        public virtual DateTime? CreatedDateTime
        {
            get
            {
                return this._CreatedDateTime;
            }
            set
            {
                this._CreatedDateTime = value;
            }
        }
        #endregion
        #region LastModifiedByID
        public abstract class lastModifiedByID : 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 : 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
    }
}

Limit Accounts

When I was asked to limit Accounts selector in Sales orders, I thought it would be easy task, so I went to definition of SOLine, and found the following C# code:

    [PXDefault(PersistingCheck = PXPersistingCheck.Nothing)]
    [Account(typeof (SOLine.branchID), Visible = false)]
    public virtual int? SalesAcctID
    {
      get
      {
        return this._SalesAcctID;
      }
      set
      {
        this._SalesAcctID = value;
      }
    }

Every time I see custom attributes of Acumatica during development I have some thought in background like ups, surprises will come for sure.

And they come. But let's try to make substitution for SalesAcctID with help of ordinary PXSelector in SOORderEntryExt:

public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
    {
        [PXDefault(PersistingCheck = PXPersistingCheck.Nothing)]
        [PXDBInt]
        [PXSelector(typeof(Search2<Account.accountID, 
            InnerJoin<CompanyAccountOn<Account.accountIDEqual<CompanyAccount.accountID>>>, 
                Where<CompanyAccount.cmpBranchIDEqual<Current<SOLine.branchID>>>>),
            typeof(Account.accountID), typeof(Account.accountCD), typeof(Account.description))]
        [PXUIField(DisplayName = "Account CD")]
        protected void SOLine_SalesAcctID_CacheAttached(PXCache sender)
        {
        }
    }

nothing fancy. Take a look at final result:

as you can see from screen recording, filtering works wonderful, but after selection of Account, field remains empty. How to deal with that? In that case on rescue comes PXDimensionSelector!

I will tell you mine own definition of PXDimensionSelector: Attribute, that allows you to modify any custom Acumatica selector. For example you can use PXDimensionSelector for filtering Accounts, SubAccounts ( but for that you'll need to play with  Segmented keys), etc.

Take a look on the code, which replaces PXSelector with PXDimensionSelector:

public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
    {
        [PXUIField(DisplayName = "Account", FieldClass = "ACCOUNT", Visibility = PXUIVisibility.Visible)]
        [PXDBInt]
        [PXDimensionSelector(AccountAttribute.DimensionName,
            typeof(Search2<Account.accountIDInnerJoin<CompanyAccountOn<Account.accountIDEqual<CompanyAccount.accountID>>>,
                Where<CompanyAccount.cmpBranchIDEqual<Current<SOLine.branchID>>>>), typeof(Account.accountCD),
            new Type[] { typeof(Account.accountCD), typeof(CompanyAccount.description) })]
        protected void SOLine_SalesAcctID_CacheAttached(PXCache sender)
        {
        }
    }
And final result:

As you can see from the screen recording, PXDimensionSelector allows you to filter account as well as select, while PXSelector only filters accounts, but doesn't allow you to select proper account

No Comments

Add a Comment
 

 

Filters In Acumatica

 

Hello everybody,

in this article I want to describe how filtering in Acumatica work. When I say filtering, I mean following UI part:

In manuals T100 - T300 there is almost nothing about how filters work, so I want to share few bits of development information.

Storage

All information about filters is stored in database in tables FilterRow and FilterHeader.

Take a look on screenshot of FilterInformation for some custom applied filter:

 

As you can see from screenshot Acumatica stores information about filters by FilterID, and has information like Condition, values, Close brackets, operators and so on.

Also there are DAC classes FilterRow and FilterHeader, but you can find them only with help of reflector or any other "spy tool".

Modification

Acumatica allows you to work with filters with help of Search functionality in selector and similar to it ( for example Search2 ). Take a look on declaration of filter of APPayment column RefNbr:

        [PXDBString(15, InputMask = ">CCCCCCCCCCCCCCC", IsKey = true, IsUnicode = true)]
        [PXDefault]
        [PXUIField(DisplayName = "Reference Nbr.", TabOrder = 1, Visibility = PXUIVisibility.SelectorVisible)]
        [APInvoiceType.RefNbr(typeof(Search2<APRegisterAlias.refNbrInnerJoinSingleTable<APInvoiceOn<APInvoice.docType, 
            Equal<APRegisterAlias.docType>, And<APInvoice.refNbrEqual<APRegisterAlias.refNbr>>>, 
            InnerJoinSingleTable<VendorOn<APRegisterAlias.vendorIDEqual<Vendor.bAccountID>>>>, 
            Where<APRegisterAlias.docTypeEqual<Optional<APInvoice.docType>>, 
                And2<Where<APRegisterAlias.origModuleNotEqual<BatchModule.moduleTX>, Or<APRegisterAlias.released, 
                    Equal<True>>>, And<Match<VendorCurrent<AccessInfo.userName>>>>>, OrderBy<Desc<APRegisterAlias.refNbr>>>), 
            Filterable = true, IsPrimaryViewCompatible = true)]
        [APPaymentType.Numbering]
        [PXFieldDescription]

The same filtering is applied at APInvoice. And now you can ask, ok, so what it gives me? Actually it means that by default, if you make some filter as default for screen of invoices, then the same filter will be default and applied to Bills. If you wonder how to split those siameses twins?

The idea is simple, make your own duplicate of class APRegisterAlias, and via CacheAttached add it to needed screen. Below goes code, that I've used for this purpose:

public class APPaymentEntryExt : PXGraphExtension<APPaymentEntry>
    {
        [PXDBString(15, InputMask = ">CCCCCCCCCCCCCCC", IsKey = true, IsUnicode = true)]
        [PXDefault]
        [PXUIField(DisplayName = "Reference Nbr.1", TabOrder = 1, Visibility = PXUIVisibility.SelectorVisible)]
        [APInvoiceType.RefNbr(typeof(Search2<APRegisterAliasSep.refNbrInnerJoinSingleTable<APInvoiceOn<APInvoice.docType, 
            Equal<APRegisterAliasSep.docType>, And<APInvoice.refNbrEqual<APRegisterAliasSep.refNbr>>>, 
            InnerJoinSingleTable<VendorOn<APRegisterAliasSep.vendorIDEqual<Vendor.bAccountID>>>>, 
            Where<APRegisterAliasSep.docTypeEqual<Optional<APInvoice.docType>>, 
                And2<Where<APRegisterAliasSep.origModuleNotEqual<BatchModule.moduleTX>, Or<APRegisterAliasSep.released, 
                    Equal<True>>>, And<Match<VendorCurrent<AccessInfo.userName>>>>>, OrderBy<Desc<APRegisterAliasSep.refNbr>>>), 
            Filterable = true, IsPrimaryViewCompatible = true)]
        [APPaymentType.Numbering]
        [PXFieldDescription]
        protected void APPayment_RefNbr_CacheAttached(PXCache sender)
        {
        }
    }
 
    [PXHidden]
    [PXPrimaryGraph(new[] { typeof(APQuickCheckEntry), typeof(TXInvoiceEntry), typeof(APInvoiceEntry), typeof(APPaymentEntry) }, 
        new[] { typeof(Select<APQuickCheckWhere<APQuickCheck.docTypeEqual<Current<APQuickCheck.docType>>, 
            And<APQuickCheck.refNbrEqual<Current<APQuickCheck.refNbr>>>>>), typeof(Select<APInvoice, 
                Where<APInvoice.docTypeEqual<Current<APInvoice.docType>>, 
                    And<APInvoice.refNbrEqual<Current<APInvoice.refNbr>>, 
                        And<Where<APInvoice.releasedEqual<False>, And<APRegister.origModule, 
                            Equal<BatchModule.moduleTX>>>>>>>), typeof(Select<APInvoiceWhere<APInvoice.docType, 
                                Equal<Current<APInvoice.docType>>, And<APInvoice.refNbr, 
                                    Equal<Current<APInvoice.refNbr>>>>>),
            typeof(Select<PX.Objects.AP.APPaymentWhere<PX.Objects.AP.APPayment.docType, 
                Equal<Current<PX.Objects.AP.APPayment.docType>>, And<PX.Objects.AP.APPayment.refNbr, 
                    Equal<Current<PX.Objects.AP.APPayment.refNbr>>>>>) })]
    [Serializable]
    public class APRegisterAliasSep : APRegister
    {
        public abstract class selected : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class hidden : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class branchID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class docType : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class printDocType : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class refNbr : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class origModule : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class docDate : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class origDocDate : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class tranPeriodID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class finPeriodID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class vendorID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class vendorID_Vendor_acctName : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class vendorLocationID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class aPAccountID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class aPSubID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class lineCntr : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyInfoID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyOrigDocAmt : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class origDocAmt : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyDocBal : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class docBal : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class discTot : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyDiscTot : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class docDisc : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyDocDisc : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyOrigDiscAmt : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class origDiscAmt : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyDiscTaken : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class discTaken : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyDiscBal : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class discBal : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyOrigWhTaxAmt : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class origWhTaxAmt : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyWhTaxBal : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class whTaxBal : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyTaxWheld : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class taxWheld : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyChargeAmt : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class chargeAmt : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class docDesc : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class createdByID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class createdByScreenID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class createdDateTime : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class lastModifiedByID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class lastModifiedByScreenID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class lastModifiedDateTime : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class Tstamp : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class docClass : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class batchNbr : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class prebookBatchNbr : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class voidBatchNbr : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class released : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class openDoc : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class hold : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class scheduled : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class voided : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class printed : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class prebooked : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class noteID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class refNoteID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class closedFinPeriodID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class closedTranPeriodID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class rGOLAmt : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyRoundDiff : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class roundDiff : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyTaxRoundDiff : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class taxRoundDiff : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class status : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class scheduleID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class impRefNbr : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class isTaxValid : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class isTaxPosted : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class isTaxSaved : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class origDocType : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class origRefNbr : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class releasedOrPrebooked : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class taxCalcMode : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class approved : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class rejected : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class dontApprove : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class employeeWorkgroupID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class employeeID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class workgroupID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class ownerID : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyInitDocBal : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class initDocBal : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class displayCuryInitDocBal : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class isMigratedRecord : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyDiscountedDocTotal : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class discountedDocTotal : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyDiscountedTaxableTotal : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class discountedTaxableTotal : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class curyDiscountedPrice : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class discountedPrice : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class hasPPDTaxes : IBqlFieldIBqlOperand
        {
        }
 
        public abstract class pendingPPD : IBqlFieldIBqlOperand
        {
        }
    }

and as result, I've got two filters splitted. 

No Comments

Add a Comment
 

 

Acumatica Test Framework In Acumatica Part 2 Practice

 

In this part we implement real automation test for creating POOrder - screen PO301000;

Before to start, please read first part of rhis post. 

The main steps:

  1. Create solution and add nedeed components from SDK folder
  2. Add new project (Net library) to solution 
  3. Configure generator and run it for create .cs class with all components on screen PO301000
  4. Add POOrder.cs and describe some methods for test
  5. Add the main class (for example F100_Part1_Lesson1.cs) where we describe Execute() method
  6. Run solution, view result of test

 

       Create solution and add nedeed components from SDK folder

 

 

CCreate a test solution by using Visual Studio 2017 as follows:

  1. In Visual Studio 2017, create a new project TestSDKProject with the following parameters:
  2. Project template: Visual C#
  3. Framework version: .NET Framework 4.7
  4. Project type: Console Application
  5. Project name: TestSDKProject
  6. Solution name: TestSDKProject

 Include references to the following binary libraries (located in the TestProject subfolder of your Test SDK package) in the TestProject project: Execution.dll

At this point your solution should look like shown on the screenshot below:

 As you can see, I also add TestAcumatica.dll.

So create this project.

 

Add new project (Net library) to solution 

 

Add a new project TestAcumatica to the created solution with the following parameters:

Project template: Visual C#

Framework version: .NET Framework 4.7

Project type: Class Library

Project name: TestAcumatica

 Project name must start from the word Test

Include references to the following binary libraries (located in the TestProject subfolder of your Test SDK package) in the TestAcumatica project:

PX.QA.Tools.dll

Core.dll

WebDriver.dll

WebDriver.Support.dll

As you can see WebDriver.dll and WebDriver.Support.dll it is selenium web driver ( and you not need to dowload slelenium)

At this point your solution should look like shown on the screenshot below:

Do not forget include reference to the TestAcumatica project in the TestSDKProject project.

Open Program.cs file of the TestProject project (or create a new one if it was not created automatically on step 1) and modify it as follows:

public class Program
    {
        static int Main(string[] args)
        {
            return Execution.Launcher.Main(args);
        }
    }

 

 

Configure generator and run it for create .cs class with all components on screen PO301000

Open the ClassGenerator.exe.config file, which is located in the ClassGenerator folder of your Test SDK package, and modify it as follows:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <appSettings>
 <!--Local path to the Acumatica ERP instance installation directory-->
 <add key="SitePhysicalPath" value="C:\Program Files (x86)\Acumatica ERP\AcumaticaDBTest"/>
 <!--Output directory to store the generated page wrappers-->
 <add key="GenResultPath" value="C:\share\output"/>
 <!--User to be used for page wrapper generation-->
 <add key="UserName" value="admin@Demo"/>
 <!--IDs of the pages you want to run wrapper generation for; the wildcard * is supported-->
 <add key="FileNameFilter" value="PO301000"/>
 <!--Deletes all files in output directory before running the page wrapper generation process-->
 <add key="ClearOutput" value="true"/>
 <!--Namespace where wrapper classes will be defined. Use the template "GeneratedWrappers.<PartnerName>".-->
 <add key="Namespace" value="GeneratedWrappers.Acumatica"/>
 </appSettings>
</configuration>

If you have more than 1 visible companies in the application instance always specify the company name, e.g.: ; Company name is not required if you have only 1 visible company in the application instance, e.g.

In my situation I have created company - Demo;

After go to your download SDK project - ClassGenerator - and run ClassGenerator.exe

If all will be correct you can see input text - Process DONE - PO301000, and in folder you can see:

After it add folder - Wrappers to TestAcummatica project, and add this PO301000_POOrderEntry.cs file;

What is wrappers?

The page wrapper generation tool creates object mapping model for every page developed by Acumatica Framework. You can access any UI element such as Forms, Grids, Toolbars, data-fields available on a page which you use operating as an ordinary user with Acumatica ERP or any othe Acumatica based product. Test SDK uses built-in control wrapper to construct complex objects such as forms, grids and pages. You need to create wrappers for each page that you want to test.

And for this you use - page wrapper generation tool: You use this tool to create wrappers for the pages of your Acumatica-based product. 

 

 

    Add POOrder.cs and describe some methods for test

In this step, you will include extension classes for page wrappers generated earlier into project.

To include extension classes for page wrappers generated earlier into project, do the following:

  1. Create an Extensions folder in the TestAcumatica projec.
  2. 2. Include extension class for generated page wrappers: (just add new POOrder.cs)
public class POOrder : PO301000_POOrderEntry
    {
        public POOrder()
        {
            ToolBar.Save.WaitAction = Wait.WaitForPageToLoad;
        }
 
        public c_document_form Summary
        {
            get
            {
                return base.Document_form;
            }
        }
       public c_transactions_grid Details
        {
            get
            {
                return base.Transactions_grid;
            }
        }
    }

So here we describe constructot for load PO301000 page;

Also Summary and Details - what is it?

And this methods described, generated in PO301000_POOrderEntry (c_document_form, c_transaction_grid) and we return this elements;

 

 

Add the main class (for example F100_Part1_Lesson1.cs) where we describe Execute() method

So create F100_Part1_Lesso1.cs and add to Testcumatica project:

public class F100_Part1_Lesson1 : Check
   {
 
       public override void BeforeExecute()
       {
           using (TestExecution.CreateTestCaseGroup("Preparation"))
           {
               using (TestExecution.CreateTestStepGroup("Sign in"))
               {
                   Browser.StartingUrl = Config.SITE_DST_URL;
                   PxLogin LoginPage = new PxLogin();
                   LoginPage.Username.Type(Config.SITE_DST_LOGIN);
                   LoginPage.Password.Type(Config.SITE_DST_PASSWORD);
                   LoginPage.CompanyId.SelectValue("Demo");
                   LoginPage.SignIn.Click();
               }
         
               using (TestExecution.CreateTestStepGroup("Testing: Poorders"))
               {
                   POOrder poOrder = new POOrder();
                   poOrder.OpenScreen();
                   poOrder.Insert();
                   poOrder.Summary.OrderType.Select("Normal");
                   poOrder.Summary.VendorID.Type("BETAAIR");
                   poOrder.Summary.VendorRefNbr.Type("TEST Vendor REF");
                   poOrder.Details.ToolBar.New.Click();               
                   poOrder.Details.Row.BranchID.Type("HQ");
                   poOrder.Details.Row.InventoryID.Select("CARRENT");
                   poOrder.Details.Row.OrderQty.Type("100");
                   poOrder.Details.Row.CuryUnitCost.Type("20");
                   poOrder.Details.Row.ProjectID.Type("INTERNAL18");
                   poOrder.Details.Row.TaskID.Type("01OPS");
 
                   poOrder.Save();
               }
 
               
               }
           }
       }
 
       //Lesson 1: Introduction to General Ledger.
       //Add test-specific logic in this method.
       public override void Execute()
       {
           
}        //This method executes after the test.        public override void AfterExecute()        {                   }    }

Ok, in BeforeExecute() I described my test for creating POOrder, you can do it in Execute() method of course, now it is no matter)

And we ready to run this test.

 

 

Run solution, view result of test

You can run test in Chrome or Firefox browser,  in this case I use Firefox.

Fot it - Modify RunnerExample.xml located in Test SDK package as follows:

<?xml version="1.0" encoding="utf-8"?>
<config>
 <general>
 <browser>*firefox</browser>
 <browserbin>C:\Users\xarec\Downloads\TestSDK_17_209_0028_21\Firefox\firefox.exe</browserbin>
 <browser_downloads_folder>c:\share\download\</browser_downloads_folder>
 <site_dst>
 <url>http://localhost/AcumaticaDBTest</url>
 <login>admin</login>
 <pswd>Qwerty!123</pswd>
 </site_dst>
 <logging>
 <logStorage type="htmlfile" level="INFO" outputFolder="c:\share\logStorage" screenshotActive="true" />
 </logging>
 </general>
 <testing>
 <Check Name="F100_Part1_Lesson1"/>
 </testing>
</config>

After click rigth button on TestSDKProject, choose properties and go to Debug and past your path to RunnerExample in Command Line Arguments:

/TestSDKProject.exe

/config "C:\Users\xarec\Downloads\TestSDK_17_209_0028_21\RunnerExample.xml"

Build solution and click Start. If all configure ok, you will see that Firefox will open, open Acumatica, sign in, and create a new POOrder:

After you can go to share - logStorage and will see the result:

So here we can see that all ok, if some operation executed ok, so it will be black color and screensot

POOrder is save. But what will be, if we describe not correct data, or something not correct?

In the LOG we can view it, color will be read.

That`s all for first meet with Acumatica Test Framework, thank you for reading:)

8 Comments

  • Diva said

    Hi yuriy I have a question

    I'm try create PO301000_POOrderEntry.cs file but I can't it, I don't know that I did bad :(

    Can you help me?

  • Diva said

    Hi, me again

    well, I try this example but....


    I do not know what happened that my instance it have next error


    Server Error in '/rembolso' Application.
    Access is denied.
    Description: An error occurred while accessing the resources required to serve this request. The server may not be configured for access to the requested URL.

    Error message 401.2.: Unauthorized: Logon failed due to server configuration. Verify that you have permission to view this directory or page based on the credentials you supplied and the authentication methods enabled on the Web server. Contact the Web server's administrator for additional assistance.

    Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.7.3282.0

    :( Can you help me? :(

  • docotor said

    Do you can open your instance at manually in browser?

  • Diva said



    Hi, I can't

    I not finish my test yet,I think this causes the error.

    Actually but I can see the file PO301000_POOrderEntry.cs, the test finally created it I did not finish processing the program when I wrote you.

    but I was want open my instance in browser and showed me this error...

  • Diva said

    Excuse me but, I have another error. LOL

    I did modify my Runner Example.xml but when I want run my app. It show me this error

    core.exceptions.ExecitionException: The /config parameter is not specified

    en Exection.Laucher.Main(String[] args) en E:\Bld\AC-TESTSDK2018R109-JOB1\test\Selenium\Execution\Launcher.cs linea a 33

    But I don't get it, if Exection is an DLL

  • docotor said

    Sorry, but for me is a bit hard to follow you and what issue you are facing.

  • Diva said

    I'm sorry, I know there are many problems :(

    but my first problem is solved, I mean I cant see PO301000_POOrderEntry.cs file created by ClassGenerator.exe

    So I try the step "Run solution, view result of test" but when modify my RunnerExample.xml file I have this error:

    core.exceptions.ExecitionException: The /config parameter is not specified

    en Exection.Laucher.Main(String[] args) en E:\Bld\AC-TESTSDK2018R109-JOB1\test\Selenium\Execution\Launcher.cs

    Too my instance say Access deneged and I can't run it manually in browser :(

  • docotor said

    Such testings should be done initially in your local instance, and then applied to your instance to which you don't have access.

Add a Comment
 

 

Make Printing Of One Table Per Page With Css

 

Hello erybody,

today I want to share one important note, which took significant amount of time from me to finish.

I had page which as html output few tables. For me it was nessesary to make one page per table. In order to achieve it I've found following CSS:

<style>
    table,
    table tr td,
    table tr th {
        page-break-insideavoid;
    }
</style>

That was only one css that worked for me.

No Comments

Add a Comment