Where Log Of Visual Studio Is Located

 

Hello everybody,

today I want to leave a short note on where to search for log file of Visual Studio. 

It lives here:

C:\Users\{USER}\AppData\Roaming\Microsoft\VisualStudio .....\{visual studio version}\ActivityLog.xml

Whenever I speak about log files with any kind of developer, I see round eyes and trembling voice with a question, why on earth should I look into log file?

The reason is simple, sometime you may get error message like this: "Error HRESULT E_FAIL has been returned from a call to a COM component" during adding reference to your class library. 

What may stand behind that error message? No ideas? The same was with me, but after looking into log file, we have found there following statement:

Microsoft.visualstudio.shell.interop.IVsReferenceManager2 within the Microsoft.VisualStudio.Shell.Interop.11.0.dll

and some additional close to abra cadabra statements, but googling that error message was much easier. 

Finally we applied following steps:

#1 Open "Developer Command Prompt for VS 2017" as Admin

#2 CD into "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\PublicAssemblies"

#3 Run "gacutil -i Microsoft.VisualStudio.Shell.Interop.11.0.dll"

Summary

Remember in which location lives log of Visual Studio. With it's help you'll be able to make miracles related to bugs. Another idea to keep in mind, take Microsoft as a pattern, and have your own log, especially of errors in your apps. 

 

How To Display Images In Grid Of Acumatica

 

Hello everybody,

today I want to leave a short post on how to display images in grid of Acumatica. 

I'll demonstrate it on sample of Sales Orders pages, which is known as SO301000.

End result will look like this:

 

In order to make it work, I've done the following:

  1. Created extension to SOOrderEntry and added there some kind of reading of url images
  2. Created DAC class for demo purposes
  3. Added few controls and css/js on customized so301000 page.

Now a bit more details. For SOOrderEntry I've created following extension:

    public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry>
    {
	    public PXSelect<SOImageItem> Images;
 
	    protected virtual IEnumerable images()
	    {
		    var result = new List<SOImageItem>();
			result.Add(new SOImageItem()
			{
				ID = 1,
				ImageUrl = "https://img.grouponcdn.com/deal/074cef17e1fc40c69e309870fcfbc8d7/d7/v1/c700x420.jpg",
				ID2 = 2,
				ImageUrl2 = "https://cdn1.shoebacca.com/catalog/product/P/K/PK54467_1l.jpg?quality=80&bg-color=255,255,255&fit=bounds&height=&width="
			});
 
		    
		    result.Add(new SOImageItem()
		    {
			    ID = 3,
			    ImageUrl = "https://cdn1.shoebacca.com/catalog/product/P/K/PK54467_2l.jpg?quality=80&bg-color=255,255,255&fit=bounds&height=&width=",
			    ID2 = 4,
			    ImageUrl2 = "https://cdn1.shoebacca.com/catalog/product/P/K/PK54467_4l.jpg?quality=80&bg-color=255,255,255&fit=bounds&height=&width="
			});
 
		    result.Add(new SOImageItem()
		    {
			    ID = 5,
			    ImageUrl = "https://cdn1.shoebacca.com/catalog/product/P/K/PK54467_6l.jpg?quality=80&bg-color=255,255,255&fit=bounds&height=&width=",
			    ID2 = 6,
			    ImageUrl2 = "https://cdn1.shoebacca.com/catalog/product/P/K/PK54477_1l.jpg?quality=80&bg-color=255,255,255&fit=bounds&height=&width="
			});
		    
		    result.Add(new SOImageItem()
		    {
			    ID = 7,
			    ImageUrl = "https://cdn1.shoebacca.com/catalog/product/P/K/PK54477_2l.jpg?quality=80&bg-color=255,255,255&fit=bounds&height=&width=",
				ID2 = null,
				ImageUrl2 = null
			});
 
			return result;
	    }
    }

and following DAC class:

[Serializable]
public class SOImageItem : IBqlTable
{
	public abstract class iD : IBqlField
	{
	}
 
	[PXInt(IsKey = true)]
	public virtual int? ID { getset; }
 
	public abstract class imageUrl : IBqlField
	{
	}
 
	[PXString(IsKey = true)]
	public virtual string ImageUrl { getset; }
 
	public abstract class iD2 : IBqlField
	{
	}
 
	[PXInt(IsKey = true)]
	public virtual int? ID2 { getset; }
 
	public abstract class imageUrl2 : IBqlField
	{
	}
 
	[PXString(IsKey = true)]
	public virtual string ImageUrl2 { getset; }
 
}

Then on page so301000, following staff was added:

<style>
    img {
        height100px;
        width100px;
        resizeboth;
    }
</style>

And final piece tab:

	<px:PXTabItem Visible="True" Text="Images"  BindingContext="form">
		<Template>
			<px:PXGrid runat="server" ID="grdImages" Width="100%" DataSourceID="ds">
				<Levels>
					<px:PXGridLevel DataMember="Images">
						<RowTemplate>
						    <px:PXNumberEdit runat="server" ID="CstPXNumberEdit5" DataField="ID"  Width="100"/>
							<px:PXTextEdit runat="server" ID="CstPXTextEdit4" DataField="ImageUrl"  />
                            
						    <px:PXNumberEdit runat="server" ID="PXNumberEdit2" DataField="ID2"  Width="100"/>
						    <px:PXTextEdit runat="server" ID="PXTextEdit1" DataField="ImageUrl2"  />
							
						</RowTemplate>
                        <Columns>
                            <px:PXGridColumn DataField="ID" Width="100" />
                            <px:PXGridColumn DataField="ImageUrl" Type="Icon" Width="350"   />
                            
                            <px:PXGridColumn DataField="ID2" Width="100" />
                            <px:PXGridColumn DataField="ImageUrl2" Type="Icon" Width="350"   />
                        </Columns>
					</px:PXGridLevel>
                </Levels>
			</px:PXGrid></Template></px:PXTabItem></Items>
        <CallbackCommands>
            <Search CommitChanges="True" PostData="Page" ></Search>
            <Refresh CommitChanges="True" PostData="Page" ></Refresh>
            <Refresh CommitChanges="True" PostData="Page" ></Refresh>
            <Search CommitChanges="True" PostData="Page" ></Search>
        </CallbackCommands>
        <AutoSize Enabled="True" Container="Window" ></AutoSize>
    </px:PXTab>

After that I was able to see result, that is shown on the screenshot of begining of the article.

 

 

How To Deal With Read Commited Snapshot Error Message In Acumatica

 

Hello everybody,

today I want to leave SQL fix for error message:

"There are problems on database server side:

READ_COMMITTED_SNAPSHOT is not set for current database.

On your Acumatica instance it may look liike this:

For fixing run this SQL:

ALTER DATABASE Saddleback_staging SET READ_COMMITTED_SNAPSHOT ON

 

How To Use Autofac In Acumatica With Global Graph And Single Registration

 

Hello everybody,

today I want to leave a short notice on how to use Autofac in Acumatica, but with single registration. In this article I've descirbed how to use Autofac for resolving interface < -- > class implementation. 

But one of my collegues Deebhan Hari pointed that with my usage registration of classes will happen on each roundtrip and for each graph leading to potential memory leack.

Definetely not something, we would like to have. After small conversation, we managed to add to Autofac singleton, which allowed us to have situation when only once per lifetime of the process Autofac 

registers class only once.

 

 

using System;

using PX.Data;

using Autofac;

 

namespace SingleRegistrationDemo

{

       public class AllGraphsExtension : PXGraphExtension<PXGraph>

       {

         public override void Initialize()

         {

             // Implemented Singleton, so that our KNCustomCaseCommonEmailProcessor is registered only once when needed.

             var single = SingletonCustomCaseCommon.Instance;

         }

       }

 

    public sealed class SingletonCustomCaseCommon

    {

        private static readonly Lazy<SingletonCustomCaseCommon>

            lazy =

            new Lazy<SingletonCustomCaseCommon>

                (() => new SingletonCustomCaseCommon());

 

        public static SingletonCustomCaseCommon Instance { get { return lazy.Value; } }

 

        private SingletonKNCustomCaseCommon()

        {

            PX.Objects.EP.EmailProcessorManager.Register(new ClosedCasesReOpenV2.CustomCaseCommonEmailProcessor());

        }

    }

}

 

 

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