T300 Acumatica Customization Platform
Contents 2 Contents How to Use the Training Course... 4 Getting Started with the Acumatica Customization Platform...5 What is an Acumatica Customization Project?...6 Add-On Solution... 8 Creating an Add-On Solution... 10 Developing an Add-On Solution... 14 Scopes of the Acumatica Customization Platform... 30 User Interface Customization...31 Acumatica Customization Engine...43 Acumatica Extensibility Framework...62 Customization Stages... 77 Source Code Browser... 77 Exploring the Source Code... 80 Development Stage... 88 Developing a Customization Project...90 Granting User Access Rights for Customization... 96 Final Testing and Deployment Stage...98 Acumatica Customization Project... 102 Content of a Customization Project...102 Customization Menu and Sessions... 108 Understanding the Publication of a Project... 112 Managing Multiple Projects... 115 Updating a Customization Project... 116 Deleting a Customization Project... 119 UI Customization...123 Performing UI Customization...123 Page Design Mode... 123 Aspx Pages Editor... 127 PXLayoutRule Components...129 Placing UI Elements in Multiple Rows and Columns and Widening these Elements...133 Setting Label Width and Aligning UI Elements Horizontally... 138 Creating a Group of UI Elements and Hiding Their Labels... 141 Creating Nested Input Controls... 144 Setting Widths and Sizes of UI Elements... 147 Adding Items... 153 Adding Advanced Controls... 157 Nested Advanced Controls... 166
Contents 3 Acumatica Extensibility Framework In Detail...180 AEF Extensibility Generic Classes... 182 Creating an Extension Library Solution by Using the Bespoke Approach... 183 DAC Extensions... 185 Customizing DAC Attributes... 185 Creating a DAC Extension of an Extension... 188 DAC Extension Model...190 Extension Tables...193 Using an Extension Table... 193 Extension Table Declaration... 198 BLC Extensions... 206 Modifying a BLC Data View... 207 Declaring or Altering a BLC Data View Delegate...208 Extending BLC Initialization... 210 Creating a BLC Extension of an Extension... 212 BLC Extension Model... 213 Adding or Altering BLC Event Handlers... 219 AEF Event Handler Model... 221 Altering BLC Virtual Methods... 226 Accessing Customization Objects... 228 Customization Best Practices... 238 Using Numbering Sequences...238 Overriding a BLC Data View... 277 Overriding a BLC Data View Delegate... 281 Adding a Button onto the Form Control... 288 Adding Buttons to the Grid Control... 294 Calculating Unbound DAC Fields at Run Time... 303 Extending the Base Business Logic... 308 Extending the Multi-Level Document Workflow... 321 Extending the Persist() Method... 333 Extending the Release Operation... 338
How to Use the Training Course 4 How to Use the Training Course The T300 Acumatica Customization Platform course is intended for application developers who start learning how to customize Acumatica ERP or other Acumatica Framework-based applications. It is required from you to previously work through the lessons of the T100 Introduction to Acumatica Framework and the T200 Acumatica Framework Fundamentals courses and to pass certification tests on these courses. The Acumatica Customization Platform course consists of six parts which take about 24 hours to complete. Before starting to work with the T300 Acumatica Customization Platform course: Locally download the installer, and in the Acumatica Framework Configuration Wizard that appears, select the Deploy Acumatica Framework Tools item. For details, see ConfiguringDevEnvironment.pdf attached to the training materials. Locally download the Acumatica ERP installer, and in the Acumatica ERP Configuration Wizard that appears, by sequential selecting twice the Deploy New Application Instance item, create two Acumatica ERP instances for customization and for final testing. For details, see Deploying an Acumatica ERP Instance. Before starting to perform instructions of the topics placed under UI Customization, upload the AdvUI.zip file attached to the training materials. Before starting to perform instructions of the topics placed under Acumatica Extensibility Framework In Detail, you may need to upload the IntroAEF.zip file attached to the training materials.
Getting Started with the Acumatica Customization Platform 5 Getting Started with the Acumatica Customization Platform The Acumatica Customization Platform is designed for the developers who have advanced experience of using Acumatica Framework and specialized in: Development of vertical and add-on solutions on top of Acumatica ERP or other Acumatica Framework-based software product as independent software vendors (ISVs). Delivering the customization of Acumatica ERP or other Acumatica Framework-based software product to the end user as value-added resellers (VARs). Each customization task must be resolved within an Acumatica Customization Project. An Acumatica Customization Project is a set of changes and additional files that modifies Acumatica ERP or an Acumatica Framework-based application. To deliver the content of an add-on solution or customization of an Acumatica Framework-based software product, you should add all the changes and external files to the customization project and deploy it to the production application. Specialists who will work on customization projects should be assigned the Customizer or Administrator role in the separate application instance that is to be customized and tested, as well as in the production application that should be updated with the customization project. With this role assigned, the specialists can use the customization tools and facilities of the Acumatica Customization Platform, and upload and publish customization projects. You can choose one of the following kinds of customization projects: Those that are based on an add-on solution, which is used, as a rule, when you must create new webpages Those that are based on the use of the tools and facilities of the Acumatica Customization Platform, which you use in most other cases Add-On Solutions To create new pages for the Acumatica ERP application or another Acumatica Framework-based application, you should create an add-on solution. You use the standard interface of MS Visual Studio and the tools and facilities of Acumatica Framework for developing an add-on solution, as if you were developing a standalone application. To deploy the paths to your new pages within the navigation pane, you need to place site map changes to the content of the current Acumatica Customization Project. Depending on the set of tasks resolved within the customization project, you may need to perform the following actions: Adding new inquiry conditions to the Generic Inquiry system webpage Creating new reports or modifying existing ones by using the Acumatica Report Designer. Acumatica Customization Platform Each customization task must be resolved within an Acumatica Customization Project. This means that you cannot make any customization without creating a new customization project or modifying an existing one. You can divide any customization task into three scopes: user interface (UI) customization, functional customization, and auxiliary customization. Depending on the complexity of the particular customization task, you can use the tools and facilities of any or all of the customization scopes.
Getting Started with the Acumatica Customization Platform 6 UI customization includes changing the look and behavior of webpages, tweaking webpage design, and manually editing the.aspx code. All UI modifications, except for manual editing of the.aspx code, are performed in page design mode. UI customization is always related to the.aspx code of the corresponding webpages and represents aspx changesets that are added to the existing.aspx code. Functional customization means various kinds of modifications of the data structure and original application business logic. All these modifications are related to the code of the appropriate data access classes (DACs) or business logic controllers (BLCs, also referred to as graphs). The Acumatica Customization Platform includes two technologies for functional customization: the Acumatica Customization Engine (ACE) and the Acumatica Extensibility Framework (AEF). The ACE supports a set of tools and facilities designed for the consultants who implement customization of Acumatica ERP or other Acumatica Framework-based applications. AEF is designed primarily for independent software vendors (ISVs) that develop vertical and add-on solutions on top of Acumatica ERP or other Acumatica Framework-based applications, and want to extend or modify the base product behavior. Auxiliary customization facilities, that are compatible with both add-on solutions and the tools and facilities of the Acumatica Customization Platform, are used to perform the following changes: Adding new inquiry conditions to the Generic Inquiry system webpage Modifying the application site map by using the Site Map system webpage Creating new reports or modifying existing ones by using the Acumatica Report Designer The following underlying topics provide you an overview of the Acumatica Customization Platform and all main actions necessary to create and develop an add-on solution or resolve most common customization tasks by using different approaches and technologies: What is an Acumatica Customization Project? Add-On Solution Scopes of the Acumatica Customization Platform What is an Acumatica Customization Project? An Acumatica Customization Project is a set of changes and additional files that modifies your Acumatica ERP application instance or another Acumatica Framework-based software product. Customization facilities are primarily designed for independent software vendors (ISVs) and valueadded resellers (VARs) that either develop vertical and add-on solutions on top of Acumatica Framework-based software or deliver the customization of Acumatica Framework-based software products to the end users. To deliver an add-on solution or an Acumatica Framework-based software product customization, you should add all the changes and additional files to the customization project and provide it as a deployment package to the end users. You can create as many customization projects as you need. Acumatica Framework provides the mechanism to publish, upgrade, or unpublish your customization project deployment packages at any time. Each customization task must be resolved within an Acumatica Customization Project. This means that you cannot make any customization without creating a new customization project or modifying an existing one. When you create an add-on project, you must use the customization project only for creating a deployment package. This topic defines and briefly describes an Acumatica Customization Project.
Getting Started with the Acumatica Customization Platform 7 Acumatica Customization Project To deliver the content of an add-on solution or customization of an Acumatica Framework-based software product, you add all the changes and external files to the customization project and deploy it to the end users. You can create any number of customization projects. Acumatica Framework provides the mechanism to upload, publish, upgrade, or unpublish (that is, cancel publishing) your customization project deployment packages at any time, either on the customizer or tester's application instance or on the real production application. Each project has specific content that depends on the task resolved within the project. You can see and edit the internal part of this content by using the Customization Projects webpage, while the external files are most often represented by assemblies,.aspx pages, and images. To explore the content of a project, you should first navigate to System > Customization > Manage > Customization Projects. The window under the details table of this webpage displays the content of the selected record. In the Customization Projects webpage, the whole content of a customization project represents a set of records of different object types. Once you have finished working on the project, you download it as a deployment package. A deployment package is a zip file that includes both internal content and external files that were included in the customization project. Specialists who will work on customization projects should be assigned the Customizer or Administrator role in the separate application instance that is to be customized and tested, as well as in the production application that should be updated with the customization project (see Granting User Access Rights for Customization). With this role assigned, the specialists can use the customization tools and facilities of the Acumatica Customization Platform, and upload and publish customization projects. You can choose one of the following kinds of customization projects: Those that are based on an add-on solution (see Add-On Solution), which is used, as a rule, when you must create new webpages Those that are based on the use of the tools and facilities of the Acumatica Customization Platform (see Scopes of the Acumatica Customization Platform), which you use in most other cases Auxiliary Customization Facilities An add-on solution is used for the development of additional webpages, while the Acumatica Customization Platform provides user interface (UI) and functional customization. Both types of application improvement are compatible with the facilities of the auxiliary customization scope. Auxiliary customization provides the following changes: Adding new inquiry conditions to the Generic Inquiry system webpage Modifying the application site map by using the Site Map system webpage Creating new reports or modifying existing ones by using the Acumatica Report Designer Because the scope of auxiliary customization includes different types of customization, each facility is used separately for the specified customization task or along with add-on solutions or tools and facilities of the Acumatica Customization Platform. You can use the following facilities: The Generic Inquiry system webpage. Also, the Add Generic Inq dialog of the Customization Projects system webpage is used, which supports adding selected new inquiries from the Generic Inquiry webpage to the content of the current customization project. The Site Map system webpage. Also, you can use the Add Site Map dialog of the Customization Projects system webpage, which supports adding selected site map changes to the content of the current customization project.
Getting Started with the Acumatica Customization Platform 8 Acumatica Report Designer, which enables you to modify existing reports or create new ones. Also, the Add Reports from database dialog of the Customization Projects system webpage supports adding selected reports (which were previously saved to the database) to the content of the current customization project. Add-On Solution To create new pages for the Acumatica ERP application or another Acumatica Framework-based application, you should create an add-on solution. You employ the standard interface of MS Visual Studio and the tools and facilities of Acumatica Framework for developing an add-on solution, as if you were developing a standalone application. Depending on the complexity of the task and the number of developers to be involved in resolving it, you can choose one of the following development approaches: An approach that is based on the customization project with the preconfigured initial add-on solution. From this point, we will name this approach as the templated approach. An approach that is based on a manually created add-on solution. From this point, we will name this approach as the bespoke approach. The templated approach can be used when a relatively simple task is to be resolved and only one developer is involved, while the bespoke approach is recommended for more complex tasks with two or more developers involved. The first section of this topic describes a typical task that can be resolved by using either of the two approaches. The second section describes the templated approach, and the third section describes the bespoke approach. A Typical Task that Is Resolved by Using an Add-On Suppose that you need to create a new webpage that is based on a new database table. As a final result of using either approach to resolve this task, the following items must be created (which have been added to the content of the customization project): The external files related to the add-on solution (assembly,.aspx, and aspx.cs) The script that creates the new database table (see the screenshot below) New records in the site map indicating the position and properties of the new webpage
Getting Started with the Acumatica Customization Platform 9 Figure: Exploring the content of the customization project As a result of implementing the code of the add-on solution and modifying the site map of the current application instance, the Registry of Discounts webpage has been created, which uses the appropriate business logic. This webpage is accessible for possible users from the navigation pane. (The screenshot below shows the webpage with a few added data records.) Figure: Analyzing the Registry of Discounts webpage Templated Approach to the Add-On Development To pursue this approach of add-on development, you should first create a customization project and then create the preconfigured add-on solution. (See Creating an Add-On Solution, which describes the whole cycle of developing a templated add-on solution and preparing the final deployment package.) You then perform all the necessary development steps, build the add-on project, map the new webpage to the site map, and include in the content of the project the site map changes, the compiled assembly, the code of the.aspx and.aspx.cs files, and the SQL script for the new database table. The screenshot above illustrates the content of the customization project (of the SQL-type object in this case). Thus, to resolve the add-on task by using the first approach, you must perform the following steps: Create the new customization project Create the preconfigured add-on solution Make necessary development actions, build the solution, and register the new page as a webpage Add the solution to the content of the customization project Download the deployment package
Getting Started with the Acumatica Customization Platform 10 The main advantage of this approach is a simpler initial configuration in comparison with the bespoke approach, because the Acumatica Customization Platform preconfigures the original add-on so that you do not need to make references for project and application library files. Bespoke Approach to the Add-On Development With this approach of add-on development, you manually create an add-on solution, develop it, and add all the add-on changes to the content of the new customization project. (See Developing an Add-On Solution.) To resolve the add-on task by using the bespoke approach, you must perform the following steps: Manually create the add-on solution and set up all necessary references to the application instance Make necessary development actions, build the solution, and register the new page as a webpage Create the new customization project within this instance Add the solution to the content of the customization project Download the deployment package The main advantage of this approach in comparison with the templated one is that more complex tasks can be resolved and more than one developer can be involved in the process of developing the add-on solution. Creating an Add-On Solution As Add-On Solution described, before you create an add-on solution, you must choose the approach that best fits your customization task: A customization project based on the preconfigured add-on solution provided by Acumatica (templated approach) A manually created add-on solution (bespoke approach) If you select the templated approach, to create an add-on solution for Acumatica ERP or another Acumatica-based application, you should first create a customization project and then start the system procedure, which gives you the ability to adjust the initial configuration of an add-on solution. The first section of this topic describes how to do this and what the preconfigured add-on solution is. When the development has been finished, you include the add-on solution in the created customization project, whose content is supplemented by the external files you created by using MS Visual Studio during the development steps. As a result, you have a deployment package, which comprises the whole content of the add-on solution that is encapsulated into the appropriate customization project. With the bespoke approach, first you create and develop an add-on solution; after the development has been finished, you create the customization project and include in the content of this project the assembly, webpages, and other required files. Creating of a customization project is necessary to prepare a deployment package. In both cases, you employ the standard interface of MS Visual Studio for developing an add-on solution, as if you were developing a standalone solution. You can also use auxiliary customization facilities to make some additional application modifications. Don't forget to include these modifications in the content of this project. The sections below describe actions that you have to perform if you use each approach to create and develop an add-on solution.
Getting Started with the Acumatica Customization Platform 11 Using the Templated Approach If you are using the templated approach listed above, first you create a customization project, and then you create the add-on solution. To create an add-on solution for an Acumatica ERP application, perform the following actions: 1. Start the Acumatica ERP application. 2. Navigate to System > Customization > Manage > Customization Projects. 3. On the Customization Projects webpage, click New. 4. On the New Project dialog box that appears, type your project name, such as AddOn_Demo, and click OK. 5. Click Visual Studio, and select Create Add-on Project, as shown in the screenshot below. Figure: Creating an add-on project 6. On the Opening OpenSolution.lnk dialog that appears, select Open with, select an integrated development environment such as MS Visual Studio, and click OK. 7. Build the AddOn_Demo project of the add-on solution. MS Visual Studio is started with the solution that contains the AddOn_Demo project name (that is, the project name is the same as the name of the customization project). This project contains the Examples.cs file as the first step reference. You can use this file as a starting point or simply remove it from the project. Notice that the AddOn_Demo project has predefined references to the PX.Common.dll, PX.Data.dll, and PX.Objects.dll assemblies located in the Bin folder of the website; also, the website has the reference to the AddOn_Demo project. When the AddOn_Demo.dll assembly is compiled, it's automatically copied to the Bin folder of the website (see the screenshot below).
Getting Started with the Acumatica Customization Platform 12 Figure: Exploring the structure of the new project 8. Develop and test the add-on solution. 9. Return to the Acumatica ERP application, and reopen the Customization Projects webpage if it is closed. You can again open the MS Visual Studio solution from the Customization Projects webpage after clicking Visual Studio and selecting Open Solution. 10. Click Add on the details table toolbar, and select File from File System. 11. On the Add Files pop-up window that appears, select check boxes left of the names of the created files (webpages, assembly files, and other external files) you want to add to the project, and click Save, as shown in the screenshot below. Figure: Adding the external assembly file to the customization project Use the Add Files option when you need to place additional external files into the customization project; to update the content of the customization project with changed external files, see the Updating the Content of the Customization Project with Modified External Files below.
Getting Started with the Acumatica Customization Platform 13 If you upgrade the application instance, you may have to update the content of the customization project. (For details, see Updating a Customization Project.) Using the Bespoke Approach If you are using the bespoke approach listed above, you manually create an add-on solution. To manually create an add-on solution for an Acumatica ERP application, perform the following actions: 1. Start MS Visual Studio, and create a new Class Library solution with a name of your choosing. 2. Manually add new add-on project references to the PX.Common.dll, PX.Data.dll, and PX.Objects.dll assemblies located in the Bin folder of the customization application instance. 3. Manually add the reference to the created project from the website. (See MS Visual Studio documentation.) 4. Build the project and ensure that the compiled assembly has been automatically copied to the Bin folder of the website. 5. When you finish the development of the add-on solution, start the Acumatica ERP application. 6. Navigate to System > Customization > Manage > Customization Projects. 7. On the Customization Projects webpage, click New. 8. On the New Project dialog box that appears, type your customization project name and click OK. 9. Click the Add button and select File from File System. 10. On the Add Files pop-up window that appears, select check boxes left of the names of the created files (webpages, assembly files, and other external files) you want to add to the project, and click Save. Using the Auxiliary Customization Tools with an Add-On Solution To deploy the paths to your new pages within the navigation pane, you need to make site map changes to the content of the current customization project as follows: Modify the site map of the locally installed application instance by using the Site Map system webpage (see Site Map). Open the Customization Project webpage, click Add on the details table toolbar, and select Site Map to open the Add Site Map dialog. Select and add site map changes to the content of the current customization project. Depending on the set of tasks resolved within the customization project, you may need to perform the following actions: Adding new inquiry conditions to the Generic Inquiry system webpage (see Generic Inquiry). Creating new reports or modifying existing ones by using the Acumatica Report Designer. Open the Customization Projects webpage. Depending on the type of auxiliary customization, perform the following actions: Click Add on the details table toolbar, select Generic Inq to open the Add Generic Inq dialog, select new or modified inquiries from the Generic Inquiry webpage, and save them to the content of the current customization project. Click Add on the details table toolbar, select Report from Database to open the Select Report dialog, select the new reports (which had been previously saved to the database), and save them to the content of the current customization project. After all the described actions have been fulfilled, download the deployment package.
Getting Started with the Acumatica Customization Platform 14 Updating the Content of the Customization Project with Modified External Files If you have made changes to any file of the add-on solution placed within the customization project, you need to update the content of the customization project by clicking Check in Files before you download the project. In the Check in Files dialog that appears, you can analyze and resolve content conflicts with the external files. For files with conflicts, the Conflict check box is selected. Content conflicts take place when you have modified any external file that is placed within the project. To update external files stored in the project, you should select the Selected check box for the appropriate conflict records and then click Check in Files. Only the selected files get updated within the project. If you do not want to update all of the files with conflicts, update appropriate external files with conflicts and then click Remove all Conflicts. After you click Remove all Conflicts, the system won't flag any customization project conflicts until an external file placed within the project is modified again. We strongly recommend that you click RefreshDBTables on the details table toolbar every time you've made changes to any database table whose schema had been imported from the database to the customization project. Otherwise, you may encounter unpredictable results during the testing stage. The refreshing procedure regenerates the database scripts of the tables placed within the project with the selected Import Table Schema from Database option. Developing an Add-On Solution As was said earlier, to create new pages for the Acumatica ERP application or another Acumatica Framework-based application, you should create an add-on solution. This topic describes in details an example of developing an add-on solution. The task is to create a new (user) database table and then add a page to the Acumatica ERP, generate a data access class (DAC) based on fields of the added user database table, and create a business logic controller (BLC, also referred to as graph), with a data view, which must support the new page registered as an application webpage. Adding a User Database Table and Creating an Add-On Solution Install a new Acumatica ERP application instance. First you should add the new (user) database table to the instance, TaskTemplate. To do this, perform the following SQL script for the application instance database. SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[tasktemplate]( [CompanyID] [int] NOT NULL, [TemplateID] [int] IDENTITY(1,1) NOT NULL, [TemplateCD] [nvarchar](30) NOT NULL, [CaseClassID] [nvarchar](10) NOT NULL, [GroupID] [int] NOT NULL, [Priority] [int] NULL, [Status] [char](2) NULL, [CategoryID] [int] NULL, [Descr] [nvarchar](255) 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 [CRTaskTemplate_PK] PRIMARY KEY CLUSTERED (
Getting Started with the Acumatica Customization Platform 15 [CompanyID] ASC, [TemplateID] 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 ALTER TABLE [dbo].[tasktemplate] ADD DEFAULT ((0)) FOR [CompanyID] GO Then create the CRMAddOn add-on solution by using the bespoke approach. For details, see first four instructions in Using the Bespoke Approach. After creating needed references and building the added project to the solution, the Solution Explorer will get the structure, displayed in the following screenshot. Figure: Viewing the structure of the CRMAddOn solution Creating a BLC, Adding a Page, and Generating DAC Fields Now you should create a BLC file to bind the page, which is to be added, to this BLC, and then to generate the DAC based on the user database table. After performing these actions, you should modify the BLC code to make its data view based on the generated DAC, then change DAC attributes, define the new page layout and, finally, register this page in the Site Map as the Task Template webpage. To do this, proceed as follows: 1. Add the TaskTemplateMaint BLC file based on the PXGraph template and build the project. 2. Add the CR206050.aspx page based on the FormView template to the CR subfolder of the website. 3. Open the added page in Design mode and specify the TypeName by selecting CRMAddOn.TaskTemplateMaint from the drop-down list. If the CRMAddOn.TaskTemplateMaint item is not displayed on the drop-down list, right-click the ds control and select Refresh, then open the drop-down list again. 4. Allocate the PXDataSource (ds) control and select the Generate Class item, as the following screenshot illustrates.
Getting Started with the Acumatica Customization Platform 16 Figure: Specifying the TypeName and starting to generate the TaskTemplate DAC fields 5. In the Data Access Class Generator window that appears, select the TaskTemplate user table name from the Table Properties drop-down list (or type first few symbols of this name), and click Generate (see the following screenshot). Figure: Generating fields of the TaskTemplate DAC 6. Analyze the generated DAC code in the TaskTemplates.cs file (see the screenshot below).
Getting Started with the Acumatica Customization Platform 17 Figure: Analyzing the generated DAC code Modify the DAC code as follows. namespace using using using using using CRMAddOn System; PX.Data; PX.Objects.CR; PX.Objects.EP; PX.TM; [Serializable] public class TaskTemplate : PX.Data.IBqlTable #region TemplateID public abstract class templateid : PX.Data.IBqlField protected int? _TemplateID; [PXDBIdentity] public virtual int? TemplateID get return this._templateid; set this._templateid = value; #endregion #region TemplateCD public abstract class templatecd : PX.Data.IBqlField protected string _TemplateCD; [PXDBString(30, IsUnicode = true, IsKey = true)] [PXDefault]
Getting Started with the Acumatica Customization Platform 18 [PXUIField(DisplayName = "Template ID", Visibility = PXUIVisibility.SelectorVisible)] public virtual string TemplateCD get return this._templatecd; set this._templatecd = value; #endregion #region CaseClassID public abstract class caseclassid : PX.Data.IBqlField protected string _CaseClassID; [PXDBString(10, IsUnicode = true)] [PXDefault] [PXUIField(DisplayName = "Case Class ID")] [PXSelector( typeof(search<crcaseclass.caseclassid>), DescriptionField = typeof(crcaseclass.description))] public virtual string CaseClassID get return this._caseclassid; set this._caseclassid = value; #endregion #region GroupID public abstract class groupid : PX.Data.IBqlField protected int? _GroupID; [PXDBInt()] [PXDefault] [PXUIField(DisplayName = "Default Workgroup", Visibility = PXUIVisibility.SelectorVisible)] [PXSelector( typeof(search<epcompanytree.workgroupid>), SubstituteKey = typeof(epcompanytree.description))] public virtual int? GroupID get return this._groupid; set this._groupid = value; #endregion #region Descr public abstract class descr : PX.Data.IBqlField protected string _Descr; [PXDBString(255, IsUnicode = true)] [PXUIField(DisplayName = "Description", Visibility = PXUIVisibility.SelectorVisible)] public virtual string Descr
Getting Started with the Acumatica Customization Platform 19 get set return this._descr; this._descr = value; #endregion #region Priority public abstract class priority : PX.Data.IBqlField protected int? _Priority; [PXDBInt()] [PXDefault(1, PersistingCheck = PXPersistingCheck.Nothing)] [PXUIField(DisplayName = "Priority", Visibility = PXUIVisibility.SelectorVisible)] [PXIntList( new int[] 0, 1, 2, new string[] "Low", "Normal", "High" )] public virtual int? Priority get return this._priority; set this._priority = value; #endregion #region Status public abstract class status : PX.Data.IBqlField protected string _Status; [PXDBString(2, IsFixed = true)] [PXUIField(DisplayName = "Status")] [ActivityStatusList] public virtual string Status get return this._status; set this._status = value; #endregion #region CategoryID public abstract class categoryid : PX.Data.IBqlField protected int? _CategoryID; [PXDBInt()] [PXUIField(DisplayName = "Category", Visibility = PXUIVisibility.SelectorVisible)] [PXSelector( typeof(search<epeventcategory.categoryid>),
Getting Started with the Acumatica Customization Platform 20 DescriptionField = typeof(epeventcategory.description), SelectorMode = PXSelectorMode.DisplayModeText)] public virtual int? CategoryID get return this._categoryid; set this._categoryid = 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 #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
Getting Started with the Acumatica Customization Platform 21 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
Getting Started with the Acumatica Customization Platform 22 7. Open the TaskTemplateMaint.cs file and modify the BLC code to create the Templates data view as follows and build the project (see also the screenshot below). using PX.Data; namespace CRMAddOn public class TaskTemplateMaint : PXGraph<TaskTemplateMaint, TaskTemplate> public PXSelect<TaskTemplate> Templates; Figure: Modifying the initial BLC code 8. Open the new page in Design mode and set the PrimaryView property value of the ds control to Templates; select the form area of the page and also set to Templates the DataMember property value. If the Templates item is not displayed on the drop-down list, right-click the ds control and select Refresh, then open the drop-down list again. 9. Click the smart tag of the form area and select Edit Content Layout. 10. By using the Layout Editor that appears, open the Fields tab of the right window of the editor, select check boxes for all UI controls and click Generate, so they are added onto the FormView template of the new page. 11. Add two PXLayoutRule components to allocate UI controls within two columns, and, after opening the Properties tab of the right window of the editor, perform the following actions (see also the screenshot below):
Getting Started with the Acumatica Customization Platform 23 Figure: Adding UI controls and adjusting their properties by using the Layout Editor a. Select the header of the first column and specify the following properties through the appropriate drop-down lists: StartRow: True ControlSize: M LabelsWidth: SM b. Add one more PXLayoutRule component and place it above the eddescr UI control to widen it by setting the ColumnSpan property so that its value equals 2. c. Select the PXLayoutRule component to define the second column and specify the following properties through the appropriate drop-down lists: StartColumn: True ControlSize: SM LabelsWidth: XS 12. Click OK to close the Layout Editor. You can see the page with added controls, as shown in the following screenshot:
Getting Started with the Acumatica Customization Platform 24 Figure: Viewing the layout of the new page 13. Navigate to System > Customization > Manage > Site Map and register the new added page as the Task Template webpage. To do this, select the Setup fourth level subnode of the Organization node in the site map tree, click the Add Row button, and manually enter the following values (see also the screenshot below): ScreenID: CR.20.60.50 Title: Task Templates Url: ~/Pages/CR/CR206050.aspx The system automatically assigns the Graph Type value by using the Type Name value of the specified page. The Expanded check box must be cleared, as it is by default.
Getting Started with the Acumatica Customization Platform 25 Figure: Registering the added page as the Task Templates webpage Click Save to save the registered webpage in the Site Map. Preparing the Content of the Customization Project After you perform necessary actions within the add-on solution, you have to start the application instance, create a new customization project, and get the current content to this project, including appropriate external files. You need to do this, if you want to easily and reliably deploy the addon solution for the remote application, which, for instance, represents the customer's enterprise application. Proceed as follows: 1. Navigate to System > Customization > Manage > Customization Projects and click New on the form area of the Customization Projects webpage; add the new project name, such as CRMAddOn, that is, the name of the customization project is similar to one of the add-on project. 2. Click Add and select File from File System, as the following screenshot illustrates.
Getting Started with the Acumatica Customization Platform 26 Figure: Starting to add the content of the new customization project 3. In the Add Files pop-up window, select.dll,.aspx, and.aspx.cs files, created during the development stage, and click Save, as the following screenshot illustrates. Figure: Selecting external files for including them in the content of the customization project As a result, the File type content objects have been included in the customization project, as the screenshot below illustrates.
Getting Started with the Acumatica Customization Platform 27 Figure: Analyzing the added content of the customization project 4. Click Add and select Site Map. 5. In the Site Map pop-up window, select check box left of the Task Templates item, as the following screenshot illustrates. As the result, the Data type content object has been included in the customization project, with the new position of the Site Map. Figure: Selecting the Task Templates item of the Site Map 6. Click Add and select Database Table or Script; in the Edit Sql Script dialog that appears, select TaskTemplate as the DBObject Name, select the Import Table Schema from Database check box, and click Save, as the following screenshot illustrates.
Getting Started with the Acumatica Customization Platform 28 Figure: Adding the TaskTemplate Sql type object to the content of the customization project You may perform the described set of actions of adding and updating the content of the customization project only once, before starting the final testing and deployment stage. However, you may want to improve the content after its uploading by modifying the page layout, DAC attributes, data views, or business logic. After making any changes in the add-on solution and before creating the final deployment package, do not forget to perform the following actions in the Customization Projects webpage: Select Check in Files; in the Check in Files pop-up window, the changed files are highlighted with the Selected and Conflict check boxes. If you want to update the current customization content, click Check in Files; otherwise, you can clear the Select check boxes for files, which content needn't to be updated that moment and then click Check in Files. Click RefreshDBTables every time you have made changes to any database table whose schema had been imported from the database to the customization project. Otherwise, you may encounter unpredictable results during the testing stage. The refreshing procedure regenerates the database script of the tables placed within the project with the selected Import Table Schema from Database option. Testing the Add-On Solution and Downloading the Deployment Package Now you should test the new Task Templates webpage before preparing the deployment package of the add-on solution. Navigate to Organization > Customer Management > Configuration > Setup > Task Templates and view the new webpage (see the screenshot below), try to open lookup windows, drop-down lists, and enter data to the webpage.
Getting Started with the Acumatica Customization Platform 29 Figure: Analyzing the added webpage To download the deployment package, return to the Customization Projects webpage and click Get Package on the form toolbar. In the Opening CRMAddOn.zip dialog that appears, select Save File (see the screenshot below), then specify path and change the zip-file name, if necessary, in the navigation window that appears, and click OK. Figure: Downloading the deployment package To observe the whole XML content of the customization project and download the package, return to the Customization Projects webpage and click Edit XML on the form toolbar. In the window with the whole XML content of the customization project (the name of the project is displayed in the upper part of the window), select Download Package. The next actions are the same (they have been described in the previous paragraph).
Getting Started with the Acumatica Customization Platform 30 Figure: Downloading the deployment package Scopes of the Acumatica Customization Platform This topic describes the three scopes of the Acumatica Customization Platform, as well as gives the basic concepts of customization of Acumatica ERP or other applications developed with Acumatica Framework. The sections of this topic also describe user interface customization tools and facilities and key terms used throughout this guide, such as Acumatica Customization Engine and Acumatica Extensibility Framework. Three Scopes of Customization You can divide any customization task into three scopes: user interface (UI) customization, functional customization, and auxiliary customization. Depending on the complexity of the particular customization task, you can use the tools and facilities of any or all of the customization scopes. UI customization includes changing the look and behavior of webpages, tweaking webpage design, and manually editing the.aspx code. All UI modifications, except for manual editing of the.aspx code, are performed in page design mode (see Page Design Mode). UI customization is always related to the.aspx code of the corresponding webpages and represents aspx changesets that are added to the existing.aspx code. Functional customization means various kinds of modifications of the data structure and original application business logic. All these modifications are related to the code of the appropriate data access classes (DACs) or business logic controllers (BLCs, also referred to as graphs). Auxiliary customization is briefly described in Auxiliary Customization Facilities. The auxiliary customization facilities are compatible with both add-on solutions and the tools and facilities of the Acumatica Customization Platform. Each scope involves the use of appropriate tools and facilities of the Acumatica Customization Platform. UI Customization Tools and Facilities The tools and facilities for the UI customization are described in details in UI Customization Tools and Facilities.
Getting Started with the Acumatica Customization Platform 31 By using UI customization tools and facilities, you can resolve UI customization tasks of any complexity level. See also User Interface Customization. Two Technologies of Functional Customization The Acumatica Customization Platform includes two technologies for functional customization: the Acumatica Customization Engine (ACE) and the Acumatica Extensibility Framework (AEF). Both technologies have their own tools and facilities. The Acumatica Customization Engine supports a set of tools and facilities designed for the consultants who implement customization of Acumatica ERP or other Acumatica Framework-based applications. You can use it to make some quick changes to the database structure and business logic. The Acumatica Customization Engine is designed primarily for value-added resellers (VARs) that deliver the customization of Acumatica ERP or other Acumatica Framework-based software products to the end user and need to quickly modify the base product behavior. When you use the Acumatica Customization Engine, you get the following key benefits: The ACE tools and facilities are available in your web browser; you don't need to have Microsoft Visual Studio installed. You can make changes to the database structure by using web-based visual designers. You can review and modify source code at any time. ACE contains a mechanism that makes customization projects fairly resistant to upgrades. The tools and facilities for the functional customization by using ACE are described in details in Tools and Facilities of the Acumatica Customization Engine. The Acumatica Extensibility Framework (AEF) is an integral part of the Acumatica platform. You can use it to create extensions and deploy them with minimal configuration required. AEF also lets you encapsulate code and avoid fragile hard dependencies. AEF is designed primarily for independent software vendors (ISVs) that develop vertical and add-on solutions on top of Acumatica ERP or other Acumatica Framework-based applications, and want to extend or modify the base product behavior. Also, VARs can use AEF for implementing customization instead of ACE. The Acumatica Extensibility Framework provides the following key benefits: You can deploy multiple projects that extend DACs or BLCs. Extensions are precompiled, which provides a measure of protection for your source code and intellectual property. AEF provides an advanced level of control over the business logic and a multilevel extension model. AEF implements an auto-discovery mechanism, which makes the deployment and upgrade processes straightforward. The tools and facilities for the functional customization by using AEF are described in details in Tools and Facilities of the Acumatica Extensibility Framework. You can customize add-on solutions too. However, we don't recommend that you use both AEF customization and add-on solution within the same assembly because of possible complexity in maintenance and deployment. Instead, you should create a separate assembly for each approach. User Interface Customization As a rule, when we speak of user interface (UI) customization, we are implying the use of UI customization tools and facilities of the Acumatica Customization Platform. The result of the UI
Getting Started with the Acumatica Customization Platform 32 customization is the cumulative changeset to the.aspx code encapsulated in the XML content of the customization project. With UI customization, you can do the following: Manually drag controls on a page within their placeholders (container controls) Add controls of different types (such as NumberEdit, Selector, and ComboBox) onto a page Add advanced controls (such as tab, panel, grid, and button) onto a page Manage the control layout of a page, including the capability to delete controls or hide their labels Manually adjust a wide range of UI properties of controls Manually edit the.aspx code The system adds the CST prefix to the ID of any webpage that had been customized (that is, if the.aspx code of a webpage had been modified with UI customization tools or facilities, or manually). You can see the webpage ID (or ScreenID) after opening the webpage and clicking Help in the upper part of the webpage. The following screenshot illustrates the new ID of the Purchase Orders customized webpage. Figure: Viewing the ID of the customized webpage We mean under the customized webpage term the fact that the customization project, whose content includes the modified.aspx code, is published. If you still don't publish the project or cancel publishing the project (by using the Undo Publish procedure), the webpage has its original ID. The underlying topics cover what capabilities the UI customization provides and which UI customization tools and facilities are used in the underlying topics: Dragging, Moving, and Deleting UI Controls and Grid Columns Adding Columns to a Selector Below given references to the other topics that also describe capabilities of UI customization tools and facilities: PXLayoutRule Components Adding Items Adding Advanced Controls
Getting Started with the Acumatica Customization Platform 33 Almost all UI customization actions, except for the manual editing of the.aspx code, are performed in page design mode. The topic Page Design Mode defines and briefly describes this mode. The Aspx Pages Editor describes how to manually edit the.aspx code. The Acumatica Customization Engine (ACE) and Acumatica Extensibility Framework (AEF) technologies of functional customization are compatible with the UI customization tools and facilities. See Acumatica Customization Engine and Acumatica Extensibility Framework to gain knowledge about the functional customization tools and facilities. Dragging, Moving, and Deleting UI Controls and Grid Columns This topic describes in detail how to change the placement of UI controls by dragging them and how to use the facilities of the Aspx Control Tree to delete or move UI controls and grid columns. Dragging and Dropping UI Controls Perform the following actions: 1. Navigate to Distribution > Inventory > Work Area > Manage > Stock Items, and on the Customization menu, select Open Customization Project. 2. On the Select Working Project dialog that appears, click New and add the new project name, such as IntroUI, to the Project Name field of the New Project dialog that appears, and then click OK. 3. On the Select Working Project dialog that appears again, select the Unpublish Existing Customization check box, if it is displayed, and click OK, as the following screenshot illustrates. (The Unpublish Existing Customization check box is being displayed on the Select Working Project dialog only when the current application instance has at least one published customization project.) Figure: Creating a new customization project By selecting the Unpublish Existing Customization check box after adding a new customization project's name, you revoke the procedure that cancels publication of all previously published customization projects. You need this procedure to have possibility to work with a single application instance with different customization projects without any risk to meet issues related to possible incompatibility with published projects. This is important for training purposes only.
Getting Started with the Acumatica Customization Platform 34 After the unpublish procedure, which can take rather long time, finishes, you possibly have to select Open Customization Project again, select the created project name, and click OK to open the project. 4. On the Customization menu, select Enter Page Design Mode. (For details, see Page Design Mode.) The appearance of the webpage changes to the webpage template, which is further named simply page. Now you can start to manually move UI controls by dragging them. But you should note the following simple rules before: To move a UI control within a form area, you should click and hold the left button and then drag the element to the required place. You can move a UI control anywhere within its container control. Any UI control moved within a form area is automatically aligned according to the nearest PXLayoutRule component placed above it (see PXLayoutRule Components). By dragging UI controls, you can perform only restricted UI customization by moving UI controls within their placeholders. For more advanced UI movement customization, you should use the Aspx Control Tree dialog box, illustrated in the next section. On the Stock Items webpage, suppose that you want to move up the Is a Kit check box, to make it the first control in the first column of the General Settings tab area, as the following screenshot illustrates. Figure: Viewing the webpage before dragging the UI control Proceed as follows: 5. Click the Is a Kit check box on the General Settings tab, click and hold the left button, and then drag the element to the required place. In a few seconds (required for the page autorefresh), the check box appears in the new position (see the following screenshot).
Getting Started with the Acumatica Customization Platform 35 Figure: Noticing the new placement of the check box 6. On the Customization menu, select Close Project. If you don't save a customization project, you changes are lost (that is correct in this case). If you want to save the changes, before closing the project, on the Customization menu, select Save Project to Database. If you want to continue UI customization without closing the project, you can save the intermediate changes to the project to have possibility to rollback to the previously saved customization state by selecting Reload Project from Database on the Customization menu. Moving and Deleting UI Controls and Grid Columns You use the Aspx Control Tree to implement advanced customization of a webpage for instance, if you need to delete a UI control or table column from a page area, to shift a column within a grid, or to change the position of a UI control on a form while having the capability to change its original placeholder. Suppose that you must customize the General Settings and Warehouse Details tabs of the Stock Items webpage as follows: 1. Navigate to Distribution > Inventory > Work Area > Manage > Stock Items, and on the Customization menu, select Open Customization Project. 2. On the Select Working Project dialog that appears,select the IntroUI, the name of the earlier created project, to the Project Name field, and then click OK. 3. On the Customization menu, select Enter Page Design Mode. If you performed the actions described in the first section of this topic and saved the UI customization data of the previous customization steps to the database, the page design mode would stars automatically for the customized webpage, which is the Stock Items webpage in this case, because the changeset for its aspx code had been already created and saved to the database. Now you need: To delete the Auto-Incremental Value input UI field. (See the first screenshot below.) To move down the Tax Category UI field so that it is placed under the Lot/Serial Class UI field, as the first screenshot also illustrates.
Getting Started with the Acumatica Customization Platform 36 To move to the left the Status column of the details table so that it is placed at the right of the Default column, as shown in the second screenshot below). Figure: Viewing the General Settings tab before changes Figure: Viewing the details table of the Warehouse Details tab before changes Proceed as follows: 4. Right-click the Auto-Incremental Value UI field on the General Settings tab, and select Control Tree, as shown in the following screenshot.
Getting Started with the Acumatica Customization Platform 37 Figure: Opening the Aspx Control Tree 5. In the Aspx Control Tree that appears with the LotSerNumVal highlighted subnode, click Remove to delete the Auto-Incremental Value UI field name (LotSerNumVal subnode), as the screenshot below illustrates. The subnode disappears. Thus, you can delete any unnecessary control by selecting the corresponding subnode. The expanded tab node PXTabItem corresponds to the General Settings tab, that contains the selected Auto-Incremental Value UI field (LotSerNumVal subnode). Figure: Removing the subnode 6. Select the TaxCategoryID subnode and click twice the Down button, as shown in the screenshot below. Click Apply to Page to apply your changes and close the Aspx Control Tree. By doing this you move down the TaxCategoryID subnode, which corresponds to the Tax Category UI selector field, to place it under the LotSerClassID subnode, which corresponds to the Lot/Serial Class UI selector field.
Getting Started with the Acumatica Customization Platform 38 Figure: Moving down the Tax Category field 7. Right-click the details table area of the Warehouse Details tab and select Control Tree to again open the Aspx Control Tree. 8. In the Aspx Control Tree that appears with the item tree and with the PXGrid node highlighted (this node corresponds to the Warehouse Details tab), expand the node, select the SiteStatus subnode (which corresponds to the Status column), and click the Up button three times. It should now be placed under the IsDefault subnode (the Default column), as the screenshot below illustrates. Figure: Moving the grid column to the left 9. Click Apply to Page to apply your changes and close the Aspx Control Tree. Thus, to change the placement of a grid column, you select the appropriate subnode and use the Up or Down button to move the subnode to the left (Up) or right (Down). You can move grid columns only in this way. However, you can move UI controls placed on forms and tabs in this way and by dragging them within their placeholders, as described above. 10. On the Customization menu, select Validate and Publish. After the publishing process finishes successfully, you will see the modified General Settings tab on the Stock Items webpage, as the following screenshot illustrates.
Getting Started with the Acumatica Customization Platform 39 Figure: Viewing the modified General Settings tab of the webpage If you open the Warehouse Details tab of this webpage, you will see that the Status column of the details table has been moved to the left from its former position, as the screenshot below illustrates. Figure: Viewing the moved column on the Warehouse Details tab If you open the Customization Projects webpage, you can analyze the added content (changeset) of the current customization project (see the screenshot below).
Getting Started with the Acumatica Customization Platform 40 Figure: Analyzing the added content of the customization project Adding Columns to a Selector If you add columns to a selector, you give the user the capability to see more information about each item in the lookup window so that the user can select the appropriate data record. The screenshot below illustrates the original view of the Location lookup window (which is named selector in page design mode). Location is a column of the details table on the Vendor Details tab of the Stock Items webpage. The lookup window of this column includes two columns. The UI customization task is to add one more column to this window. Figure: Viewing the current structure and content of the lookup window To resolve this customization task, perform the following actions: 1. Navigate to Distribution > Inventory > Work Area > Manage > Stock Items, and on the Customization menu, select Open Customization Project. 2. On the Select Working Project dialog that appears, select the IntroUI project that already exists, and click OK.
Getting Started with the Acumatica Customization Platform 41 The page design mode starts automatically for the previously customized webpage, which is the Stock Items webpage in this case, because its aspx code had been already modified (see Dragging, Moving, and Deleting UI Controls and Grid Columns). 3. Right-click the Vendor Details tab and select Control Tree. 4. In the Aspx Control Tree that appears, select the VendorLocationID subnode of the PXSegmentMask type (see the following screenshot). The PXSegmentMask type extends the functionality of the UI controls of the PXSelector type. Figure: Adding the column to the selector by using the Add Selector Column dialog 5. Click Add and select Add Column to Selector. 6. In the Add Selector Column dialog that appears, click the Magnifier icon, select the VBranchID item in the lookup window, and click OK to close the dialog. 7. In the Aspx Control Tree that appears again, click Apply to Page. 8. On the Customization menu, select Validate and Publish. After the publishing process finishes successfully, you should open the Stock Items webpage, select the Vendor Details tab, click the Magnifier icon of the Location column, and view the lookup window. Notice the additional column, as shown in the screenshot below.
Getting Started with the Acumatica Customization Platform 42 Figure: Viewing the modified structure and content of the lookup window As you can see, the Receiving Branch column has been added. This column can help the user to select the appropriate Location ID item value, which might depend on the branch where this inventory item is being accepted. On the Customization menu, select View Customization Manager. You can see the new Page type object, which contains the.aspx code changes, as the screenshot below illustrates. Figure: Analyzing the added content of the customization project On the Customization menu, select View Source Code. On the Page Aspx tab, you can see the fragment of the.aspx code that is highlighted in yellow, as the screenshot below illustrates. This changeset represents the result of the UI customization.
Getting Started with the Acumatica Customization Platform 43 Figure: Analyzing the changeset of the.aspx code Acumatica Customization Engine The Acumatica Customization Engine (ACE) is a set of tools and facilities designed for the consultants who implement customization of Acumatica ERP or other Acumatica Framework-based applications. You can use it to make some quick changes to the database structure and business logic in your web browser. When resolving typical functional customization tasks by using ACE, you generally solve the following tasks: 1. Alter or extend data access class (DAC) fields by declaring a customization DAC code file containing a dummy class with a special name convention. During an application start-up it is being recognized by the runtime and injected directly into the CIL code of the library that contains the original DAC. 2. Alter or extend business logic controller (BLC, also referred to as a graph) behavior by generating a customization BLC code file containing a class derived from the base (original) BLC class. The customization BLC class is recognized and dynamically compiled by the runtime. The runtime replaces the base BLC with the customization one. 3. Generate new DACs, BLCs, and other classes needed by your customization. 4. Making customization changes to the database structure. The topics beneath this topic, listed below, describe examples of using various kinds of tools and facilities of the Acumatica Customization Engine for resolving typical functional customization tasks. The following ACE customization tasks are described in details: Adding Data Fields Customizing DAC Attributes Extending Business Logic This training includes only the brief description of ACE technology. The tools and facilities for the functional customization by using ACE are described in details in partner's portal: see Tools and Facilities of the Acumatica Customization Engine.
Getting Started with the Acumatica Customization Platform 44 Adding Data Fields At times, you need to add to a webpage a UI control based on a new data field. To do this, you should use the Create control designer to add the control onto the required area of a page (UI customization), and the Create data field designer of the Acumatica Customization Engine to add the user data field (functional customization). By data field, we mean a database-bound (mapped) or unbound field of a data access class (DAC). If you create a database-bound data field (called a user data field) by using the Create data field designer, the system automatically creates a changeset (SQL script) for the appropriate database table. Suppose that you must add an input UI field with a bound data field onto the Stock Items webpage that is, you must add the following fields: An input UI text field onto the form page area (the UI customization step) A data field to the code of the appropriate DAC (the functional customization step) A field to a database table (a customization change to the database structure that is a part of functional customization) Perform the following actions: 1. Navigate to Distribution > Inventory > Work Area > Manage > Stock Items, and on the Customization menu, select Open Customization Project. 2. On the Select Working Project dialog that appears, click New and add the new project name, such as IntroACE, to the Project Name field of the New Project dialog that appears, and then click OK. 3. On the Select Working Project dialog that appears again, select the Unpublish Existing Customization check box, if it is displayed, and click OK, as the following screenshot illustrates. (The Unpublish Existing Customization check box is being displayed on the Select Working Project dialog only when the current application instance has at least one published customization project.) Figure: Creating a new customization project By selecting the Unpublish Existing Customization check box after adding a new customization project's name, you revoke the procedure that cancels publication of all previously published customization projects. You need this procedure to have possibility to work with a single application
Getting Started with the Acumatica Customization Platform 45 instance with different customization projects without any risk to meet issues related to possible incompatibility with published projects. This is important for training purposes only. After the unpublish procedure, which can take rather long time, finishes, you possibly have to select Open Customization Project again, select the created project name, and click OK to open the project. 4. On the Customization menu, select Enter Page Design Mode. (For details, see Page Design Mode.) The appearance of the webpage changes to the webpage template, which is further named simply page. Proceed as follows: 5. Right-click the form area of the Stock Items webpage, and select Add Input Control, as shown in the screenshot below. Figure: Starting to add an input UI control and a bound data field 6. In the Create control designer that appears, click Add to create a new data field. 7. In the Create data field designer, enter the following property values for the added field (see also the screenshot below): Field Name: SearchKeywords DisplayName: Search Keywords Field Type: DBString(nvarchar) Length: 255
Getting Started with the Acumatica Customization Platform 46 Figure: Working with the Create data field designer When you select the field type with the DB prefix, the Mapped to database check box (which cannot be edited) is selected, and the system automatically creates a changeset (SQL script) to the database table whose name is specified in the Create data field designer (in this example, the InventoryItem database table). 8. Specify the value of the Length property, if this property is available for the selected field type, and then click OK. 9. When you return to the Create control designer, notice the Usr prefix added for the DAC column (and also for the appropriate database table field) to be created, and the ControlType value defined by the system as the default value (see the screenshot below). You can select another field type from the drop-down list (for instance, Selector) and click OK. Figure: Saving property values and the data field of the UI control being added
Getting Started with the Acumatica Customization Platform 47 You can further independently check that the system automatically appends the Usr prefix to each user data field (by analyzing the changes in the DAC code) and to the appropriate database table field (by analyzing the content of the created Table object) in the Customization Projects webpage (see the following instructions). 10. Suppose that you also want to adjust a size property for the added UI field. Right-click this field and select Control Tree. 11. In the Aspx Control Tree that appears, move the added input control node below the Descr subnode, click Add and select Layout Rule. Type 2 for the ColumnSpan property of the added PXLayoutRule component, as the following screenshot illustrates. By using this property, you define number of columns that the subnode located beneath the rule (the UsrSearchKeywords subnode in this case) must span. Figure: Adjusting the number of the columns spanned by the added control For details, see PXLayoutRule Components. 12. Click Apply to Page to save the column span adjustment of the UI field. 13. On the Customization menu, click Validate and Publish; if the validation process finishes successfully, click Publish to publish the customization project. 14. After publishing completes, close the customization project, open the Stock Items webpage, and view the added text field, as the following screenshot illustrates.
Getting Started with the Acumatica Customization Platform 48 Figure: Viewing the customized webpage 15. On the Customization menu, select View Customization Manager. Among the three listed objects, you can see the object of the DAC type that contains the DAC code changeset, the object of the Page type that contains the.aspx code changeset, and the object of the Table type that contains the database changeset. The following three screenshots illustrate the content of each of these changesets. Figure: Analyzing the content of the DAC customization code
Getting Started with the Acumatica Customization Platform 49 Figure: Analyzing the changeset of the.aspx page code Figure: Analyzing the added content of the customization project related to the database table If you right-click the added UI control in page design mode and select Attributes, you can see the properties of the added DAC field. (For details, see Customizing DAC Attributes.) If you open the InventoryItem database table, you can see the added UsrSearchKeywords field of this database table. After publishing the customization project, the system creates the DAC code file within the customized website, in the App_Code/Caches subfolder. When you open this file by using any text editor (NotePad, for instance), notice the Append_ prefix in the new DAC field definition (see the highlighted code line in the following screenshot).
Getting Started with the Acumatica Customization Platform 50 Figure: Viewing the customization DAC code file that contains the new field definition For every customization DAC, the system creates a separate file that includes all new and modified fields. You can open this customization DAC code file in MS Visual Studio, add or replace DAC field attributes, and then include the modified content of these files in the customization project by using the checkin procedure. The guidelines of editing and checking in the modified DAC and BLC code are similar. See also http://partner.acumatica.com/main.aspx?screenid=wp000000&pageid=e3975e62e309-426d-9941-f8ea5296398a Customizing DAC Attributes To modify or extend the behaviour that is defined within a data access class (DAC), you should append or override the field attributes declared within the DAC. Suppose that you have a customization task to modify the Invoices webpage, and that this task consists of the following requirements: 1. The Customer Ref. Nbr. data field on the form area (see the screenshot below) must have the None value when the user has not specified the reference number of the customer. 2. The values of the Discount column must be limited to a range of -30 to 25 percent instead of the original range of -100 to 100 percent.
Getting Started with the Acumatica Customization Platform 51 Figure: Viewing the original webpage To resolve this customization task, perform the following actions: 1. Navigate to Distribution > Sales Orders > Work Area > Enter > Invoices, and on the Customization menu, select Open Customization Project. 2. On the Select Working Project dialog that appears, select the IntroACE project that already exists, and click OK. The page design mode starts automatically for the previously customized webpage, which is the Stock Items webpage in this case, because its aspx code had been already modified (see Adding Data Fields). But the Invoices webpage hasn't been UI customized earlier. Because you need to work with page design mode now, you should enter this mode. 3. On the Customization menu, select Enter Page Design Mode. (For details, see Page Design Mode.) The appearance of the webpage changes to the webpage template, which is further named simply page. Proceed as follows: 4. Right-click the Customer Ref. Nbr. UI control and select Attributes, as shown in the screenshot below.
Getting Started with the Acumatica Customization Platform 52 Figure: Starting to customize the attributes of the Customer Ref. Nbr. field When the DataField Attributes window appears, notice the following properties in its upper area (shown in the screenshot below): View Name: The SOInvoiceEntry business logic controller (BLC, also referred to as a graph) and the Document data view to which this form area is bound. The BLC name is displayed before the colons, and the data view name is displayed after the colons. Cache Type: The ARInvoice DAC name, which contains the selected DAC field and is the main DAC for the Document data view. The main DAC is the DAC that had been specified as the first type parameter of the BQL expression. Field Name: The InvoiceNbr DAC field. Original attributes: The set of the original attributes attached to the InvoiceNbr field within the ARInvoice DAC declaration.
Getting Started with the Acumatica Customization Platform 53 Figure: Appending an attribute to the DAC field 5. In the lower area of the dialog, in the Custom attributes text box, type the [PXDefault("None")] parameter (as the screenshot above illustrates). In the How to Apply drop-down list, select Append to Original. Then click OK. The How to Apply drop-down list includes two options: Append to Original (to append the set of custom field attributes to the original one), and Replace Original (to replace the original set of DAC field attributes with the custom set). To implement the second requirement of the task, proceed as follows: 6. In page design mode, right-click the grid on the Document Details tab and select Control Tree, as shown in the screenshot below. Figure: Starting to customize the attributes of the DiscPct field
Getting Started with the Acumatica Customization Platform 54 7. On the Aspx Control Tree that appears, perform the following actions (see the screenshot below, noticing the numbers on the screenshot, which correspond to these numbered actions): a. Expand the PXGrid node of the tree. b. Select the DiscPct subnode of the tree, which corresponds to the Discount column that you want to customize. c. Click Attributes in the upper area of the tree. When the DataField Attributes window appears, notice the following settings in the fields in its upper area (see also the screenshot below): d. View Name: The SOInvoiceEntry BLC and the Transactions data view to which the grid area is bound. (The BLC name is displayed before the colons, and the data view name is displayed after them.) Cache Type: The ARTran DAC name; this DAC contains the selected DAC field and is the main DAC for the Transactions data view. Field Name: The DiscPct DAC field. Original attributes: The set of the original attributes attached to the DiscPct field within the ARTran DAC declaration. Type the following set of custom field attributes in the Custom Attributes text box (see also the following screenshot). [PXDBDecimal(6, MinValue =-30, MaxValue=25)] [PXUIField(DisplayName = "Discount")] [PXDefault(TypeCode.Decimal, "0.0")] In this case, you can copy and paste content from the Original attributes window to the Custom attributes window, and then correct only two parameter values of the first attribute. Figure: Replacing the DAC field attributes e. In the How to Apply drop-down list on the lower area of the dialog, select Replace Original. f. Click OK to save the new code fragment and close the dialog. g. Click Apply to Page to close the Aspx Control Tree and save your changes.
Getting Started with the Acumatica Customization Platform 55 8. On the Customization menu, click Validate and Publish; if the validation process finishes successfully, click Publish to publish the customization project. To test the results of the customization, open the Invoices webpage, insert a new document, and add a line to the details table, as shown in the screenshot below. Notice the following changes: In the Customer Ref. Nbr. field, the None value now appears by default. If you try to add a Discount value that is greater than 25 percent, as the first screenshot below illustrates, the system replaces the entered value with 25 (see the second screenshot below). Figure: Analyzing customization changes Figure: Watching the corrected result when you try to add a too-high percent value Similarly, if you try to enter a value that is less than -30 percent, the system will retrieve the resulting percent from the DiscPct data field, and its value will be -30. On the Customization menu, select View Customization Manager to open the Customization Projects webpage. In the lower window, view the content of the two objects of the DAC type with the customization changes, as the screenshots below illustrate.
Getting Started with the Acumatica Customization Platform 56 Figure: Analyzing the content of the customization project with the added attributes Notice the highlighted fragments of the content code for added and overriden attributes. For the DAC field whose attributes must be replaced, the OverrideGeneratedAttributes property is explicitly set to True. Figure: Analyzing the content of the customization project with the substituted attributes After publishing the customization project, the system creates two DAC code files within the customized website, in the App_Code/Caches subfolder, as the following screenshot illustrates.
Getting Started with the Acumatica Customization Platform 57 Figure: Viewing the two created DAC code files When you open each of these files by using any text editor (NotePad, for instance), notice the Append_ and Replace_ prefixes in the customized DAC field definitions (see the highlighted code lines in two following screenshots). Figure: Viewing the customization DAC code file with the appended field attribute
Getting Started with the Acumatica Customization Platform 58 Figure: Viewing the customization DAC code file with the overridden field attributes The system creates a separate code file for every customization DAC, which includes all new and modified fields. Also, you can open the customization DAC code files in MS Visual Studio, add or replace DAC field attributes, and then include the modified content of these files in the customization project by using the check-in procedure. The guidelines of editing and checking in the modified DAC and BLC code are similar. See also in the partner portal Generating and Editing Customization Code Files. Extending Business Logic By declaring event handlers to a business logic controller (BLC, also referred to as a graph), you can implement additional business logic or extend the basic logic. Declaring a Business Logic Controller Event Handler The example below illustrates implementing the logic for updating the value of the customization UsrSearchKeywords field of the InventoryItem DAC when the ItemClassID field of the same DAC is changed. You must alter the InventoryItemMaint BLC by declaring the FieldUpdated event handler for the ItemClassID DAC field. Proceed as follows: 1. Start the Acumatica ERP application. 2. Navigate to Distribution > Inventory > Work Area > Manage > Stock Items. 3. On the Customization menu, click Open Customization Project. 4. On the Select Working Project dialog box that appears, select IntroACE and click OK. The page design mode starts automatically for the previously customized webpage, which is the Stock Items webpage in this case, because its aspx code had been already modified (see Adding Data Fields). 5. On the Stock Items page in page design mode, right-click the Item Class field on the General Settings tab of the webpage, and click Add Data Event.
Getting Started with the Acumatica Customization Platform 59 6. On the Data Event Handler dialog that appears, click the Magnifier icon, select FieldUpdated as the Event Type value, and click OK. (Do not change the default values of the two other field values of the dialog, as shown in the screenshot below.) Figure: Defining the event type If you select a Row event type (one with the Row prefix), the Field Name property field is not visible in the dialog. If you select a Field event type or the CommandPreparing or ExceptionHandling events, the Field Name is available in the dialog. To add an event handler, you should right-click the appropriate area in page design mode and click Add Data Event. In the Add Event Handler dialog that appears, select the Row or Field event type. If you selected an input UI control and the Field Name property field is visible, the DAC field name to which this control is bound is automatically displayed in the Field Name property field; otherwise, you should select it manually. Alternatively, you can add an event handler by using the Aspx Control Tree. On the toolbar of the Aspx Control Tree, click Events to open the Add Event Handler dialog. If you selected an input control or a grid column node and the Field Name edit is visible, the DAC field name to which this control is bound is automatically displayed as the Field Name property value, otherwise you should select it from the lookup window. 7. On the Custom Code editor that appears in a separate window, modify the predefined code of the event handler as shown in the code block and screenshot below. (In the upper part of the code that is not shown below, keep the using operators as they have been generated.) namespace PX.Objects.IN [PXCustomization] public class Cst_InventoryItemMaint : InventoryItemMaint #region Event Handlers protected override void InventoryItem_ItemClassID_FieldUpdated( PXCache sender, PXFieldUpdatedEventArgs e) base.inventoryitem_itemclassid_fieldupdated(sender, e); var row = (InventoryItem)e.Row; var ItemClassID = row.itemclassid; INItemClass itemclass = PXSelectorAttribute.Select<InventoryItem.itemClassID>( sender, row, ItemClassID) as INItemClass; if (itemclass!= null)
Getting Started with the Acumatica Customization Platform 60 row.usrsearchkeywords = itemclass.descr; else row.usrsearchkeywords = null; #endregion Figure: Modifying the generated code of the event handler 8. Click Save, and then click Validate and Publish to publish the project. 9. To test the result of the customization, close the project, select any Inventory ID item on the Stock Items webpage, and then try to change the value of the Item Class field. Before you change the value of the Item Class field, note the initial values of the Search KeyWords and Item Class fields. 10. On the warning dialog box that appears, click OK (see the screenshot below).
Getting Started with the Acumatica Customization Platform 61 Figure: Trying to change the value of the Item Class field 11. Notice that the values of both the Item Class and Search KeyWords fields have been changed, as the following screenshot illustrates. This means that the event handler is implemented correctly. Figure: Exploring the test result 12. On the Customization menu, select View Customization Manager to open the Customization Projects webpage. In the lower window, view the content of the one new object of the Code type with the customization changes, as the screenshot below illustrates.
Getting Started with the Acumatica Customization Platform 62 Figure: Analyzing the content of the customization project with the new Code type object Acumatica Extensibility Framework The Acumatica Extensibility Framework (AEF) is an integral part of the Acumatica Framework. AEF is designed primarily for independent software vendors (ISVs) that develop vertical and add-on solutions on top of Acumatica ERP or other Acumatica Framework-based applications, and want to extend or modify the base product behavior. Also, value-added resellers (VARs) can use AEF for implementing customization instead of ACE. When resolving typical functional customization tasks by using AEF, you generally create data access class (DAC) and business logic controller (BLC) extensions. You should first create an extension library solution that will contain extensions on DACs and BLCs. Extensions are auto-discovered by the runtime during the first base (original) class initialization. The base class is replaced at run time with the merged result of the base class and every extension discovered. Unlike ACE, AEF supports multi-level extensions, required when you develop off-the-shelf software, distributed in several editions. Extensions are precompiled and therefore provide a measure of protection for your source code and intellectual property. In addition, deployed extensions can be later customized for the end user by using ACE tools and facilities or DAC and BLC extensions. The topics beneath this topic, listed below, describe examples of using various kinds of tools and facilities of the Acumatica Extensibility Framework for resolving typical functional customization tasks. AEF is based on so-named extension models that are described in the referenced topics. The following topics give the first representation on the AEF technology: These topics will give you a basic understanding of AEF capabilities. Additional concepts with more complicated examples are covered in Acumatica Extensibility Framework In Detail. Creating an Extension Library Solution By Using the Templated Approach Adding a Column to a Grid Control Adding a Selector onto the Form Area of a Webpage Modifying a BLC Action Altering the BLC of a Processing Webpage Extending Business Logic
Getting Started with the Acumatica Customization Platform 63 Creating an Extension Library Solution By Using the Templated Approach To use AEF, you first create an extension library project, to make it possible to declare extensions for DACs and BLCs. Before creating a new custyomization project, you have to revoke the unpublish procedure that cancels publication of the previously published customization project (or projects). You need this procedure to have possibility to work with a single application instance with different customization projects without any risk to meet issues related to possible incompatibility with published projects. This is important for training purposes only. Creating an Extension Library Solution Perform the following actions: 1. Start the Acumatica ERP application. 2. Navigate to System > Customization > Manage > Customization Projects. 3. On the Customization Projects webpage, click New. 4. On the New Project dialog box that appears, type your project name, such as IntroAEF, and click OK. 5. Click Visual Studio, and click Create Addon Project, as shown in the screenshot below. This generates the new project and opens it in MS Visual Studio. Figure: Creating the new extensibility project 6. In the Solution Explorer window, delete the Examples.cs file. 7. Build the IntroAEF project. 8. Notice that the IntroAEF project has predefined references to the PX.Common.dll, PX.Data.dll, and PX.Objects.dll assemblies located in the Bin directory of the website. Also, the IntroAEF assembly is automatically placed into the Bin folder of the website (see the screenshot below).
Getting Started with the Acumatica Customization Platform 64 Figure: Exploring the structure of the new project Adding a Column to a Grid Control To add a column to a grid control, which is a common task of customizing Acumatica Framework-based software, you use the Create a data field dialog box. Performing the Task The following task demonstrates using the Acumatica Extensibility Framework to add a new DAC field and database field. Perform the following actions: 1. Start MS Visual Studio and open the solution you created when you created the extension library project by using the templated approach. 2. Right-click the IntroAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type POVendorInventoryExtension as the Name, and click Add. 4. Modify the added file code as follows. using PX.Data; using PX.Objects.PO; namespace IntroAEF public class POVendorInventoryExtension : PXCacheExtension<POVendorInventory> #region UsrAdditionalInfo public abstract class usradditionalinfo : PX.Data. IBqlField [PXDBString(255)] [PXUIField(DisplayName = "Additional Info")] public string UsrAdditionalInfo get; set; #endregion The Usr prefix must be added to the name of the database-mapped DAC field that extends the existing database table; otherwise, the database column will be deleted during the application upgrade.
Getting Started with the Acumatica Customization Platform 65 Data type attributes with the PXDB prefix denote that a DAC field is mapped to the database (DB). To declare an unbound DAC field, you should apply a non-db type attribute to the field. 5. Build the IntroAEF project. 6. Start or open the Acumatica ERP application. 7. Navigate to System > Customization > Manage > Customization Projects. 8. Select Add and click Database Table Field. 9. On the Add UsrField to Database Table dialog box that appears, select POVendorInventory as the Table Name, type AdditionalInfo as the Field Name, select DBString(nvarchar) as the Field Type, type 255 as the Length property value of the database field, and click OK, as the screenshot below illustrates. You shouldn't add the Usr prefix when you add a new column to a database table, because the system automatically adds this prefix. Figure: Adding the column to the database table 10. Click Validate and publish on the form toolbar of the Customization Projects webpage; after the validation process finishes successfully, click Publish. 11. Navigate to Distribution > Inventory > Work Area > Manage > Stock Items. 12. On the Customization menu, click Enter Page Design Mode. 13. On the Stock Items webpage in page design mode, select the Vendor Details tab, right-click the details table under the Vendor Details tab, and then click Control Tree. 14. On the Aspx Control Tree window that appears, perform the following actions: a. On the Add menu, click Add Column to Grid, as shown in the screenshot below.
Getting Started with the Acumatica Customization Platform 66 Figure: Starting to add a column to the grid control b. In the Add Grid Column dialog box that appears, click the Magnifier icon of Data field and select UsrAdditionalInfo, which represents the name of the new DAC field. Keep TextEdit selected as the Column Editor value, and click OK, as the screenshot below illustrates. Figure: Searching the new DAC field to be added as a table column 15. Click Apply to Page. 16. On the Customization menu, click Save Project to Database. 17. Again navigate to System > Customization > Manage > Customization Projects. 18. On the Add menu, click File from File System. 19. On the Add Files window that appears, select the IntroAEF extension library and click Save, as shown in the screenshot below.
Getting Started with the Acumatica Customization Platform 67 Figure: Adding the extension library file to the customization project 20. Validate and publish the project, and then close it. 21. Navigate to Distribution > Inventory > Work Area > Manage > Stock Items and select the Vendor Details tab to ensure that the Additional Info new column has been added to the tab table, as the screenshot below illustrates. Figure: Verifying that the column has been added The position of the added column is the rightmost. You can drag it to the left, as the screenshot above illustrates.
Getting Started with the Acumatica Customization Platform 68 Figure: Analyzing the content of the extensibility customization project 22. Return to the Customization Projects webpage and notice the three objects (see the screenshot above): Of the File type, which contains the path to the library file and ID of the library file Of the Page type, which consists of the aspx changeset in the XML format Of the Table, which contains the database changeset. Adding a Selector onto the Form Area of a Webpage To add a selector onto a form area, another common task of customizing Acumatica Framework-based software, you use the Create a data field dialog box. Performing the Task This task illustrates the use of AEF to add a new DAC field and database field. Perform the following actions: 1. Start MS Visual Studio and open the solution you created while you created an extension library project by using the templated approach. 2. Right-click the IntroAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type InventoryItemExtension as the Name, and click Add. 4. Modify the new added file code as follows. using PX.Data; using PX.Objects.IN; using PX.Objects.TX; namespace IntroAEF public class InventoryItemExtension : PXCacheExtension<InventoryItem>
Getting Started with the Acumatica Customization Platform 69 #region UsrLocalTaxCategoryID public abstract class usrlocaltaxcategoryid : PX.Data.IBqlField [PXDBString(10, IsUnicode = true)] [PXUIField(DisplayName = "Local Tax Category")] [PXSelector(typeof(TaxCategory.taxCategoryID), DescriptionField = typeof(taxcategory.descr))] public string UsrLocalTaxCategoryID get; set; #endregion 5. Build the IntroAEF project. 6. Start or open the Acumatica ERP application. 7. Navigate to System > Customization > Manage > Customization Projects. 8. Select Add and click Database Table Field. 9. On the Add UsrField to Database Table dialog box that appears, select InventoryItem as the Table Name, type LocalTaxCategoryID as the Field Name, select DBString(nvarchar) as the Field Type, type 10 as the Length property value of the database field, and click OK, as the screenshot below illustrates. You shouldn't add the Usr prefix when you add a new column to a database table, because the system automatically adds this prefix. Figure: Adding the column to the database table 10. Validate and publish the project. If you changed the extension library file before validating the customization project, the Checkin Files pop-up window appears with the Conflict check box selected (see the screenshot below). If you want to update the project with this modified file, you should click Check in Files to update the project and close the Check-in Files window, and then click Validate and Publish again.
Getting Started with the Acumatica Customization Platform 70 Figure: Updating the customization project with the modified extensibility file 11. Navigate to Distribution > Inventory > Work Area > Manage > Stock Items. 12. On the Stock Items webpage in page design mode, select the General Settings tab, right-click the tab area, and then click Add Input Control. 13. On the Create control dialog box that appears, select UsrLocalTaxCategoryID as the Data field, keep Selector as the ControlType, and click OK, as shown in the screenshot below. Figure: Adding the selector field 14. Drag the added field under the Tax Category field (see the screenshot below).
Getting Started with the Acumatica Customization Platform 71 Figure: Moving the added field to the final place 15. Validate and publish the project. 16. Open the Stock Items webpage and select the General Settings tab; make sure that the Local Tax Category selector has been added to the tab table, as the screenshot below illustrates. Click the Magnifier icon to the right of this field and explore the new selector window with the columns that were defined in the extensibility code. Figure: Exploring the new added selector field Modifying a BLC Action To modify the behavior of an action that is defined within a business logic controller (BLC, also referred to as a graph), you should override the action attributes declared within the BLC. By using the Acumatica Extensibility Framework (AEF), you can override the action delegate and redeclare action attributes within a BLC extension. Suppose that you need to change the display name of the action declared within the INReceiptEntry BLC from Release to Extended Release. To do this, you should create the extension on the INReceiptEntry BLC and override the delegate for this action. If you open the original Receipts webpage, you can see the Release button on the form toolbar, as the screenshot below illustrates.
Getting Started with the Acumatica Customization Platform 72 Figure: Viewing the original Receipts webpage To change the display name of the release action, proceed as follows: 1. Start MS Visual Studio and open the solution that you created while you created an extension library project by using the templated approach. 2. Right-click the IntroAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type INReceiptEntryExtension as the Name, and click Add. 4. Modify the added file code as follows. using System.Collections; using PX.Data; using PX.Objects.IN; namespace IntroAEF public class INReceiptEntryExtension : PXGraphExtension<INReceiptEntry> public PXAction<INRegister> release; [PXUIField(DisplayName = "Extended Release", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update)] [PXProcessButton] protected IEnumerable Release(PXAdapter adapter) return Base.release.Press(adapter); You should always override an action delegate to alter either its delegate or the attributes attached to the action. To use the action declared within the base BLC or a lower-level extension from the action delegate, you should redeclare a generic PXAction<TNode> data member within a BLC extension. You do not need to redeclare an action when it is not meant to be used from the action delegate. For details, see BLC Extension Model.
Getting Started with the Acumatica Customization Platform 73 The override action delegate must have exactly the same signature that is, the return value, the name of the method, and any method parameters as the overridden base action delegate. 5. Build the IntroAEF project. 6. Start or open the application, navigate to System > Customization > Manage > Customization Projects, and click the Check in Files button to update the project with this modified extension library file. 7. To test the results of the customization, open the Receipts webpage and ensure that the button is now named Extended Release (see the screenshot below). Figure: Checking the modified button name on the Receipts webpage Altering the BLC of a Processing Webpage In most cases of using the Acumatica ERP application, you configure the functionality of the process webpage during BLC initialization. To modify a process webpage during BLC instance initialization, you should create a BLC extension and override the Initialize() method. For example, to disable the Release All button and modify the functionality of the Release button of the Release IN Documents process webpage, you create an extension on the INDocumentRelease BLC and override the Initialize() method. To modify a process webpage functionality, proceed as follows: 1. Start MS Visual Studio and open the solution that you created while you created an extension library project by using the templated approach. 2. Right-click the IntroAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type INDocumentReleaseExtension as the Name, and click Add. 4. Modify the new added file code as follows. using System.Collections.Generic; using PX.Data; using PX.Objects.IN; namespace IntroAEF
Getting Started with the Acumatica Customization Platform 74 public class INDocumentReleaseExtension : PXGraphExtension<INDocumentRelease> public override void Initialize() Base.INDocumentList.SetProcessAllEnabled(false); Base.INDocumentList.SetParametersDelegate( delegate(list<inregister> documents) return Base.INDocumentList.Ask( "IN Document Release", "Are you sure?", MessageButtons.YesNo) == WebDialogResult.Yes; ); Base.INDocumentList.SetProcessDelegate(delegate(INRegister doc) INDocumentRelease.ReleaseDoc(new List<INRegister>()doc,true); ); 5. Build the IntroAEF project. 6. Start or open the application, navigate to System > Customization > Manage > Customization Projects, and click the Check in Files button to update the project with this modified extension library file. Navigate to Distribution > Inventory > Processes > Daily > Release IN Documents. Ensure that the Release All button is unavailable while the Release button is available (see the screenshot below). Select any document and click Release to see the message box that appears, which displays the message that you have added to the BLC extension code. Figure: Exploring the use of the Release button Extending Business Logic By using an appropriate event handler in the code of the business logic controller (BLC, also referred to as a graph) extension, you can modify the basic behavior of a webpage. Implementing an Event Handler Within a BLC Extension To implement the event handler capability, perform the following actions: 1. Start the Acumatica ERP application, if it is not started yet. 2. Navigate to Distribution > Inventory > Work Area > Manage > Stock Items. 3. Select the item with 301CMPST01 as the Inventory ID, and try to change the value of the Lot/Serial Class field from NN to LL. Notice that the error icons appear left of the labels of the General Settings tab and Lot/Serial Class field. If you point to either of these two icons, you can see the message explaining why the lot class cannot be changed, as the screenshot below illustrates.
Getting Started with the Acumatica Customization Platform 75 Figure: Checking the warning message Suppose that you want to change the error icon to a warning icon, while keeping the message as it is. You should find and explore the basic InventoryItem_LotSerClassID_FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e) method declared within the InventoryItemMaint BLC. Then you should override the InventoryItem_LotSerClassID_FieldVerifying event handler within the InventoryItemMaintExtension BLC extension. To override this event handler, which is declared within the base InventoryItemMaint BLC, proceed as follows: 1. Start MS Visual Studio and open the solution that you created while you created an extension library project by using the tempated approach. 2. Right-click the IntroAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type InventoryItemMaintExtension as the Name, and click Add. 4. Modify the new file code as follows. using PX.Data; using PX.Objects.IN; namespace IntroAEF public class InventoryItemMaintExtension : PXGraphExtension<InventoryItemMaint> protected void InventoryItem_LotSerClassID_FieldVerifying( PXCache sender, PXFieldVerifyingEventArgs e, PXFieldVerifying del) try if (del!= null) del(sender, e); catch (PXSetPropertyException ex) sender.raiseexceptionhandling<inventoryitem.lotserclassid>(
Getting Started with the Acumatica Customization Platform 76 e.row, e.newvalue, new PXSetPropertyException( ex.messagenoprefix, PXErrorLevel.Warning)); 5. Build the IntroAEF project. 6. Start or open the application, navigate to System > Customization > Manage > Customization Projects, and click the Check in Files button to update the project with this modified extension library file. 7. Navigate again to Distribution > Inventory > Work Area > Manage > Stock Items. 8. Again select the item with 301CMPST01 as the Inventory ID and try to change the value of the Lot/Serial Class field from NN to LL. Notice that warning icons appear instead of error ones left of the labels of the General Settings tab and Lot/Serial Class field. If you point to either icon, you can see the same warning message that was previously the error message (see the screenshot below). Figure: Checking the explanatory message
Customization Stages 77 Customization Stages As a rule, the process of resolving a customization task by developing an add-on solution or completing a customization project can be divided into three stages: Exploration stage: During this stage, you explore the original code of the application. (See the example of exploring the original code in Exploring the Source Code.) This stage is executed on the locally installed standard application instance; if the production application is to be customized, the local installation must be customized first by the same published customization projects as the production application. Development stage:this stage includes the steps to develop an add-on solution or a customization project (see Development Stage); this stage is also executed on the locally installed standard application instance, which must be customized by the same published customization projects as the production application. Final testing and deployment stage: In this stage, you check the results of resolving the customization task by using the add-on solution or the customization project (see Final Testing and Deployment Stage). The final testing part of this stage must be performed on the local copy of the customized production application with the copy of production database if you can access it; otherwise, use the same original (standard) application instance configuration as you used during the development stage. The deployment part of this stage must be executed on the production application instance. First, the version number of the development and production application instances must be the same. If they are not, before deploying and publishing the last project on the production instance, you should update one or both instances to the same version. If the production instance has been updated (or will be updated soon) to the particular version, you should update the development instance, repeat all testing steps, and then, if the tests are successful, again create the deployment package to upload it to the production application instance. Second, keep in mind that to carry out the final testing (with the production database) and deployment parts of the third stage, the developer must be registered as an internal user included in the Administrator or Customizer roles (See the topic in the last link listed below). See the following topics for more information: Source Code Browser Developing a Customization Project Granting User Access Rights for Customization: Describes how to grant access rights for customization Source Code Browser You use the Source Code browser for exploring the source code of the Acumatica ERP application and the added UI customization code. The Source Code browser displays by default the.aspx code for the selected page and the appropriate business logic controller (BLC also referred to as a graph) code; you can also view the code of data access classes (DACs). The.aspx code includes all modifications concerning the UI elements of the selected page. To activate the Source Code browser, open the customized page and select View Source Code on the Customization menu. Alternatively, you can open the Source Code browser as follows: Navigate to System > Customization > Explore > Source Code. In this case, to see the code on the Page Aspx tab,
Customization Stages 78 you must manually select the page ID in the lookup window. You can also select any base BLC or DAC object on the appropriate tab to see its code. The Source Code browser displays the code in various views on the different tabs: The Page Aspx tab (see the screenshot below) displays the.aspx code for the selected page. The changes related to the UI elements of the page are highlighted in yellow. Figure: Viewing the Page Aspx tab The Business logic tab (shown in the screenshot below) displays the BLC code, also referred to as the graph code, associated with the selected page.
Customization Stages 79 Figure: Viewing the Business logic tab The Data table declaration tab (see the screenshot below) shows the source code of DACs. A DAC represents an individual instance of objects (such as Product or Order) to which the information pertains. A DAC can be simple, with the data represented with a single database record in one table, or complex. With a complex DAC, data is typically held in multiple tables and associated through a complex hierarchy and relationship rules. Figure: Viewing the Data Table Declaration tab
Customization Stages 80 On the Find in Files tab (shown in the screenshot below), you can find Acumatica ERP application code fragments that contain the text you type into the Find Text box. Notice that the search is case-sensitive. Figure: Viewing the Find in Files tab The Source Code browser must be used at the very beginning of the customization process. After finding needed DACs and BLCs, you can explore their original code before making any customization changes. For details, see Exploring the Source Code. In addition, you can find two subfolders under the App_Data\CodeRepository subfolder of the website, which contains the code of the system classes and attributes, as well as the code of the whole Acumatica ERP application. To create the development application instance, you might need to download the production application and deploy it on a local web server. To get the exact copy of the website, open the Source Code browser and click the Download Website button, define or confirm the name of the file, and click OK. The content of the site will be archived and downloaded to the specified location. Exploring the Source Code Before you start to create an add-on solution for an Acumatica ERP application or to customize an Acumatica ERP application instance, we recommend that you learn about the structure of the involved webpages by exploring.aspx pages, the code of the business logic controllers (BLCs, also referred to as graphs), and the data access classes (DACs) that are used within BLCs. To help you gain the needed knowledge, this topic covers how to do the following: Get access to the Acumatica ERP source code Analyze attributes of the DACs that are used within the BLC Find and analyze the data views that are used on the webpage Explore the structure of the webpage by viewing the.aspx code To facilitate your understanding, this topic includes an example.
Customization Stages 81 Getting Access to the Source Code for the Employees Webpage Suppose that you need to implement a dependency between two Date type fields Hire Date and Date Of Birth of the Employees webpage, for instance to check the age of the hired employee (which shouldn't be less than 16 years old, in compliance with company and legal restrictions). If the employee is too young, the data displayed on the Employees webpage won't be saved unless the user enters an allowed hire date. First, you should start the Acumatica ERP application and open the webpage to be used in this example by navigating to Organization > Organization Structure > Manage > Employees (which is shown in the screenshot below). Figure: Exploring the structure of the Employees webpage As you can see in the screenshot, the General Info tab of the Employees webpage has four groups of input controls. The Hire Date and Date of Birth fields, indicated with green frames, are organized in different groups. Notice that splitting the webpage into groups (to ease the user's work) is not the same thing as placing input controls onto container controls (the main or auxiliary PXFormView or the auxiliary PXTabItem and PXListView). Each input UI control belongs to a data view, while this data view depends on the container control, onto which the particular input UI control is placed. For details, see the Analyzing the Webpage Structure Through the.aspx Code section at the end of this topic.
Customization Stages 82 Searching for the DAC and BLC Code To get optimal results, you should first analyze the corresponding original source code fragments to find out which data access classes contain the fields that are to be validated, as well as how these DACs depend on each other within the appropriate business logic controller data views. Open the Employees page in page design mode, right-click the Date Of Birth UI field, and select Attributes. In the DataField Attributes window that appears (shown in the following screenshot), note the exact Field Name value (DateOfBirth). Also notice the View Name and Cache Type values: These values appropriately denote the data view of the BLC, to which the container of the UI field is bound, and its main DAC containing the selected UI field. (The main DAC is the DAC that has been specified as the first type parameter of the BQL expression.) Click Cancel to close the window. Figure: Checking the attributes of the Date Of Birth field The full data view and DAC names are displayed: PX.Objects.EP.EmployeeMaint::Contact and PX.Objects.CR.Contact, where PX.Objects is the name of the assembly, EP and CR are the names of namespaces, and EmployeeMaint::Contact and Contact are the names of the data view declared within the BLC and the DAC that includes the DateOfBirth field. The Contact DAC is the main DAC of the EmployeeMaint::Contact data view. On the Employees page, right-click the Hire Date field and select Attributes. In the DataField Attributes window that appears again (illustrated in the following screenshot), note the Field Name, View Name, and Cache Type values. Click Cancel to close the window.
Customization Stages 83 Figure: Checking the attributes of the Hire Date field On the Employees page, select Customization and then View Source Code to open the Source Code browser, a special customization facility for UI and functional customization. This gives you access to the source code of the Employees webpage. As the screenshot below illustrates, the Source Code browser has four tabs and the Business logic tab is open by default. The Graph Name value indicates that the Employees webpage is bound to the PX.Objects.EP.EmployeeMaint BLC. Figure: Opening the Business logic tab of the Source Code browser
Customization Stages 84 Open the Find in Files tab. Type DateOfBirth in the Find Text field and click Find, as shown in the screenshot below. For each search result, you can see the DAC name, the code line number, and the content fragment that includes the DateOfBirth text. Figure: Searching for the DAC that includes the DateOfBirth field When more than one DAC is found, you should choose the DAC that includes the required field name (that is, the field name of the required DAC) in a content fragment with the #region declaration, because this means that this DAC actually has the BQL field and property with the same name. Next, open the Data table declaration tab, click the Magnifier button of the Table Name lookup field, and type within the quick search box (at the bottom of the lookup window) the DAC name in this case, Contact, and select the PX.Objects.CR.Contact item (because of the CR namespace), as shown in the screenshot below. Figure: Searching for the DAC
Customization Stages 85 Click the PX.Objects.CR.Contact full DAC name to specify this name in the Table Name field. As a result, the DAC code appears (see the screenshot below). Find the DateOfBirth field name in the list of the regions of the DAC, and expand the region with the same name to look through the field definition code. You can expand any region to analyze the code of the region. Pay attention to the whole structure of the DAC that is, the fields it includes and the order of field declaration. Figure: Expanding the DAC region Repeat these actions to find the PX.Objects.EP.EPEmployee DAC and analyze the whole structure of the DAC. Searching for the Data Views that Contain the Specified DAC Names Now you should allocate the data views of the PX.Objects.EP.EmployeeMaint BLC code to find out the possible dependence between the Hire Date and Date of Birth fields. Open the Business logic tab and expand the Selects Declaration code region. Find the PXSelect BQL expressions (data views) with the CurrentEmployee and Contact names that contain, respectively, the EPEmployee and Contact main DACs, as the screenshot below illustrates.
Customization Stages 86 Figure: Searching for the views that contain the specified DAC names as main DACs The BQL expression of the second view (Contact) shows the dependency between the Contact and EPEmployee DACs (through the EPEmployee.parentBAccountID field). This means that you can add logic to the PX.Objects.EP.EmployeeMaint BLC code to implement any kind of restriction, including the age one. Analyzing the Webpage Structure Through the.aspx Code By using the Page Aspx tab of the Source Code browser, you can see the webpage structure that is, the layout of the container controls and the types and properties of the input UI controls. Open the Page Aspx tab, and the EP203000 screen ID (Employees webpage) is displayed because we opened the Source Code browser from this page. The screenshot illustrates a code fragment that belongs to the cont3 content ID placeholder. The.aspx code lines of all the tabs of the Employees webpage can be found within this placeholder.
Customization Stages 87 Figure: Exploring the first.aspx code fragment of the Employees webpage If you look for the DataMember string within the current webpage and look through the content of the cont3 placeholder within the General Info tab item, you should notice the following: The General Info tab has the CurrentEmployees value for the DataMember property. The DataMember property value is the name of the data view to which the current container control is bound (the tab control in this case). As you will see, three auxiliary PXFormView container controls had been added onto the PXTabItem control located within the PXTab container control with the following IDs: ContactInfo, AddressInfo, and PersonalInfo. Every PXTabItem control gets the binding from the PXTab container control. Don't confuse the IDs of container controls with the names of the input UI control groups. On the Employees page, the PXFormView (or original PXFormView) advanced controls have a DataMember property value that differs from the DataMember value of the selected tab item section. Each DataMember property value can correspond to any data view name of the BLC. Any container control (such as form or grid) or advanced control (such as the PXFormView, PXTabView, or PXListView ) must be bound to a data view declared within a BLC. Any data view except for the primary data view, can be used by an unlimited number of containers. The primary data view must be connected to the datasource control (ds) and no more than to one container control. The second fragment of the.aspx code of the Employees webpage, shown in the screenshot below, illustrates the layout and main properties of the Hire Date and Date of Birth fields. These fields are placed within the px:pxtabitem tag with the General Info header. Within this tag, each of the four input control groups is defined. If you know the data view and DAC field names, you can find out to which group (the GroupCaption property of the PXLayoutRule class) or placeholder (the Caption property of the FormView class) this field belongs. (In this example, four different groups and three placeholders are used in the Employees webpage.) Notice that the DataMember value is defined only for the px:pxformview tag that contains the Date of Birth field Contact, as highlighted in the screenshot below. As you can see from this and the previous screenshots, the Hire Date field is placed on the main container control and therefore has the CurrentEmployee parent data member.
Customization Stages 88 Figure: Exploring the second code fragment of the Employees webpage You use the Aspx Control Tree or the Layout Editor to specify auxiliary container controls and groups of input controls. The main difference between a container (or advanced) control and a group of UI controls is their usage: A container control is used both for placing UI controls and for binding them to a data view, while a group is used only for placing UI controls to improve the user interface and make the user's work more convenient. Development Stage During the development stage, you develop the customization, which involves implementing your planned changes by using customization tools and facilities. You modify the appearance and logic of the Acumatica ERP application or another Acumatica Framework-based application to meet the requirements you have defined. Although this topic describes the customization process primarily by using examples with an Acumatica ERP application instance, the customization guidelines are similar for Acumatica Framework-based applications. For more details about add-on solutions, see Add-On Solution. Follow this recommended workflow during the development process: Create the development application instance, which is a standard installation of Acumatica ERP (or your Acumatica Framework-based application) that consists of the website and the database. All further actions must be performed on this instance. If the production instance had been customized before, you must update the development application instance with all the customization packages, which are currently published on the production instance. Start the development application instance, create a new customization project or select an existing one, and open the project.
Customization Stages 89 Split the customization process into discrete steps, such as adding a UI element or extending business logic, and perform the needed changes step by step. (For details, see Customization Steps and Session Cycles.) Depending on the customization scope, you use appropriate tools and facilities. As a rule, you enter page design mode mostly to use the tools and facilities of UI customization (see Page Design Mode). While working within a customization project, you can also use the following: Tools and facilities designed to support functional customization: Acumatica Customization Engine or Acumatica Extensibility Framework Auxiliary customization facilities: The Generic Inquiry and Site Map webpages, and the Report Designer After the completion of every step, validate the changes you have performed. Before the validation process begins, the system automatically saves the inner content of the project and all the external files of the project to the database of the application instance that is currently being customized. Therefore, we recommend that you download a package that may be used as an archive copy of the current state of the project before you make changes within the current customization session; you may also create an archive copy before saving new changes to database or before validating the changes. This copy may be useful if the validation process fails. In this case, you can upload the latest archive file to restore the previous state of the customization project. Publish the project, and perform local testing of the customized application instance (for details, see Understanding the Publication of a Project). The content of each customization project available within the application is stored in the database, including all external files. The Published status of every customization project is also stored in the database. When you publish a customization project, the system generates or regenerates the common package and stores it in the database (if the database has been successfully applied on the website). You can view the successfully stored and applied common customization package by selecting View Published Customization XML on the Customization menu. When you publish a customization project by using the Validate and Publish item of the Customization menu or the Validate and Publish (or Publish without Validation) button of the Customization Projects webpage, the application creates the common customization package content. This content represents the joined content of the current project and all previously published ones. You can use the Publish without Validation option during the application upgrade process to resolve possible issues with functional customization that were created while you used the Acumatica Customization Engine. (For details, see Updating a Customization Project.) When you publish multiple projects by using the Publish Customization webpage (as described in Managing Multiple Projects), the common customization package content is created with only the projects you have selected. Pay attention to the use of the Level column of this webpage, which is described in the referenced topic. In some cases, you may need to upload an archive copy after you have saved the customization project to the database (and therefore, cannot restore the content of the project) for instance, when you have found out that saved content is erroneous and decided to cancel all changes of the most recent development step. To learn the best actions in these cases, see how to upload the customization code of the project from the archive file (or deployment package) in Final Testing and Deployment Stage. To start the final testing and deployment stage, download the deployment package after the successful local testing. We recommend that you use a separate application instance for each developer within a single project or a group of projects. Multiple developers should not work simultaneously with the same project or projects.
Customization Stages 90 You should divide customization tasks among the developers so that they are radically different and each developer works with a separate application instance. Created customization projects (with different names) can be sequentially uploaded to the customer's production application and employed as if all divided tasks had been resolved as a single common one. Developing a Customization Project This topic includes an example of developing a customization project. In this example, a custom event will be used for the Employees webpage. Suppose that you need to specify the Date Of Birth UI field as mandatory and organize a possible dependency between two Date type fields Hire Date and Date Of Birth, for instance to check the age of the hired employee (which shouldn't be less than 16 years old, in compliance with company and legal restrictions). If the employee is too young, the saving of the Employees webpage must be blocked unless the user enters an allowed hire date. Analyzing the Source Code To get optimal validation results, you must first analyze the corresponding original source code fragments to find out which data access classes (DACs) contain the fields that are to be validated, as well as how these fields are bound with one another within the appropriate business logic controller (BLC) views. See Exploring the Source Code. Creating the New Project Take the following actions to start creating validation logic: 1. Create a new project (see a project's definition in What is an Acumatica Customization Project?) by doing the following actions: a. Navigate to Organization > Organization Structure > Manage > Employees, and on the Customization menu, select Open Customization Project. b. On the Select Working Project window that appears, click New to add the new project. c. On the New Project window that appears, add the project name, such as EP1. d. Click OK to close the New Project window; then select the Unpublish Existing Customization option (if one is available) and click OK again to close the Select Working Project window and create the new project. By selecting the Unpublish Existing Customization check box after adding a new customization project's name, you revoke the procedure that cancels publication of all previously published customization projects. You need this procedure to have possibility to work with a single application instance with different customization projects without any risk to meet issues related to possible incompatibility with published projects. This is important for training purposes only. After the unpublish procedure, which can take rather long time, finishes, you possibly have to select Open Customization Project again, select the created project name, and click OK to open the project. 2. On the Customization menu, select Enter Page Design Mode to begin customization. Starting to Add Validation Logic Proceed as follows to add validation logic: 1. On the General Info tab of the Employees page, right-click the Date of Birth input field and select Attributes.
Customization Stages 91 2. In the Custom attributes section of the DataField Attributes window that appears, add the default attribute as follows: [PXDefault(PersistingCheck = PXPersistingCheck.Nothing)], select Append to Original from the How to Apply dropdown list, and click OK (see the following screenshot). Figure: Adding the default attribute for the Date of Birth field For the PXDefault attribute, the PXPersistingCheck parameter set to Nothing defines that by default, no additional validation logic is implemented to the Date of Birth input field throughout the application. 3. Right-click the Date of Birth input field and select Add Data Event. 4. In the Add Event Handler window that appears, select RowSelected as the event type, keep Contact as the table name, and click OK. 5. In the Custom Code editor, which appears in a separate browser window, modify the initial custom event code so that the resulting code will be the following.... #region Event Handlers protected override void Contact_RowSelected( PXCache sender, PXRowSelectedEventArgs e) base.contact_rowselected(sender, e); var row = (Contact)e.Row; if (row == null) return; PXDefaultAttribute.SetPersistingCheck<Contact.dateOfBirth>( sender, row, PXPersistingCheck.NullOrBlank); #endregion... For the PXDefault attribute the PXPersistingCheck parameter set to NullOrBlank at run time defines that the Date of Birth input field value cannot equal to null or an empty string only when working with the Employees webpage. 6. Click Save and close the window with the Custom Code editor.
Customization Stages 92 7. Return to the Employees webpage and on the Customization menu, select Save Project to Database (EP1). Implementing the Validation Logic To implement the validation logic, proceed as follows: 1. Right-click the area of the General Info tab and select Add Data Event. 2. In the Add Event Handler window that appears, select RowPersisting as the event type, keep EPEmployee as the table name, and click OK, as shown in the following screenshot. Figure: Selecting the event type 3. On the Custom Code editor window that opens again, modify the initial custom event code so that the resulting code will be the following. using using using using using using using using using using using using using using using using using System.Collections; PX.Common; PX.Objects.AP; PX.Objects.GL; PX.SM; PX.TM; System; PX.Data; PX.Objects.CS; PX.Objects.CR; PX.Objects.CA; PX.Objects.CM; System.Collections.Generic; PX.Objects.AR; PX.Objects.PM; PX.Objects; PX.Objects.EP; namespace PX.Objects.EP [PXCustomization] public class Cst_EmployeeMaint: EmployeeMaint #region Event Handlers protected override void Contact_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
Customization Stages 93 base.contact_rowselected(sender, e); var row = (Contact)e.Row; if (row == null) return; PXDefaultAttribute.SetPersistingCheck<Contact.dateOfBirth> (sender, row, PXPersistingCheck.NullOrBlank); protected override void EPEmployee_RowPersisting(PXCache sender, PXRowPersistingEventArgs e) base.epemployee_rowpersisting(sender, e); var row = (EPEmployee)e.Row; DateTime? hiredate = row.hiredate; DateTime? dateofbirth = Contact.Current.DateOfBirth; if (dateofbirth == null hiredate == null) return; DateTime alloweddate = dateofbirth.value.addyears(16); if (hiredate < alloweddate) throw new PXRowPersistingException (typeof(epemployee.hiredate).name, hiredate,"the employee's hire date must be+" "at least 16 years after birthdate."); #endregion The EPEmployee_PXRowPersisting event handler checks the condition to warn the user if the new employee is younger than 16. This validation approach prevents a record from being saved by throwing the PXSetPropertyException. If the date of birth is null or empty, the common error message is displayed by the PXDefaultAttribute (such as Error: 'Date Of Birth' may not be empty.) 4. Click Save, then click Validate and Publish. After the validation process has been finished successfully, click Publish to publish the customization project (see the screenshot below). Figure: Validating and publishing the project 5. Close the Custom Code editor window.
Customization Stages 94 Testing the Results Now you should test the results of the implemented validation logic to ensure that the logic works properly. Perform the following actions: 1. Return to the Employees webpage, close the project, refresh the webpage, if necessary, and try to add a new employee record without entering the Date Of Birth value. Enter values for all the other required fields (marked with the asterisk left of these field names). 2. Click Save: The error message appears that the Lead/Contact record raised one or more errors and the record is not saved. After clicking OK in the error message window, you can position the cursor on the error label left of the Date of Birth label and read the reason of this error (that the Date of Birth may not be empty), as the following screenshot illustrates. Figure: Trying to save a record with no Date of Birth value 3. Add the date of birth so that the difference between it and the hire date is less than 16 years, and the second error message appears (see the following screenshot). This is the message text added by you to the event code as a parameter of the PXSetPropertyException method.
Customization Stages 95 Figure: Entering the Birth Date for the person younger than 16 years 4. Make the hire date at least 16 years later than the date of birth, and click Save. The new record has been saved, as the following screenshot illustrates. Figure: Viewing the entered and saved data record for the 16 years old employee
Customization Stages 96 To download the deployment package, navigate to System > Customization > Manage > Customization Projects and click Get Package on the form toolbar of the Customization Projects webpage. In the Opening EP1.zip dialog that appears, select Save File (see the screenshot below), then specify path and change the zip-file name, if necessary, in the navigation window that appears, and click OK. Figure: Downloading the deployment package To observe the whole XML content of the customization project and download the package, return to the Customization Projects webpage and click Edit XML on the form toolbar. In the window with the whole XML content of the customization project (the name of the project is displayed in the upper part of the window), select Download Package. The next actions are the same (they have been described in the previous paragraph). Granting User Access Rights for Customization Users who manage the deployment of customization packages on the production application instance should be granted the Customizer role. Only this category of users should be granted the rights for customization. If you have the Administrator role in an Acumatica ERP application instance, you automatically get the Customizer role in this instance, so you don't need to assign your user account the Customizer role. If you are a developer who is going to work with a separate customization instance, you can just install the application by logging onto the admin user account, which is assigned to the Administrator role, and define the password. Don't forget to grant appropriate rights in the application instance that is a copy of the real production application and is used for the final testing and, if needed, in the real production application. The users who will be granted the Customizer role must be Acumatica ERP internal users. You cannot assign to a user the Customizer role if either (or both) of the following conditions is true: The user has a guest account that is, on the Users webpage with the user selected, the Guest Account check box is selected on the upper part of the webpage. The user is assigned to a guest role: a role for which the Guest Role check box is selected on the User Roles webpage. When you click the Magnifier button in the Role Name box on this
Customization Stages 97 webpage, the Select - Role Name window is shown, which has a table that shows the Guest Role check box for each role. In the screenshot below, notice that three roles have the Guest Role check box selected: Anonymous, Guest, and Portal User. Figure: Noticing the guest roles Including a User in the Customizer Role If you have administrative rights within the application instance to be customized, you can assign the Customizer role to a user. To do this, proceed as follows: 1. Navigate to Configuration > User Security > Manage > Users to open the Users webpage. 2. In the Username selector field, select the Acumatica ERP user to whom the role should be assigned (see the screenshot below). Figure: Granting the Customizer role to a user 3. On the Roles tab, select the check box for the Customizer row. 4. On the webpage toolbar, click Save to save the changes you have made for this user.
Customization Stages 98 To users who handle the deployment of customization projects, you must grant the Customizer role in both the test application instance and the production application instance, unless these users have the Administrator role. Final Testing and Deployment Stage During the final testing and deployment stage, you first upload the deployment package to a separate instance, which is a copy of the production application with all previously published customization projects and with the production database (if it's accessible for you; otherwise, you can use the same original application instance configuration as you used during the development stage). To upload the deployment package: 1. Start the new application instance, which represents a copy of the production application (if this copy is available). 2. Navigate to System > Customization > Manage > Customization Projects and on the form area of the Customization Projects webpage, click From File (see item 1 in the following screenshot). Alternatively, you can navigate to System > Customization > Process > Publish Customization and click Open From File. 3. In the Open Package dialog that appears, click Browse (item 2). 4. In the File Upload window that appears, select the required deployment zip package file (such as EP1.zip, shown as item 3). 5. Click Open (item 4) to return to the Open Package dialog. 6. In the Open Package dialog, click Upload (item 5) to upload the deployment package. Figure: Uploading the deployment package by using the Customization Projects webpage If the customization project with the same name already exists on a separate instance, you may need to update its content (see below for the instructions that you should perform to update a project).
Customization Stages 99 The Customization Projects webpage, which appears again, has the uploaded content split into different objects of specified types. (For details, see Content of a Customization Project.) Click Validate and Publish to start the process of validating the uploaded project (see item 6 on the following screenshot). Figure: Starting to validate and publish procedures If the validation process finishes successfully, you may publish the project. After you publish the project, you should carefully test at run time the results of all the changes that you made through the developing a customization project. If you have found any issues during the testing stage, you should find and fix them on the original customization application instance and then update the content of the project on the testing instance. To update the content of the current customization project or to rollback to its previous state: 1. Navigate to System > Customization > Manage > Customization Projects, and select the project by its name. 2. Click Edit XML. 3. In the window with the project name that appears, click Browse. 4. In the File Upload dialog box that appears, select the updated zip file with the same project name, and then click Open. 5. In the window with the project name, click Upload Package and then Save to database to update the content of the project (see the screenshot below).
Customization Stages 100 Figure: Updating the content of the project If needed, you can also replace the content of any customization project with the content of another customization project or with any earlier archive copy of the current customization project (to rollback to the required previous state of the project). In this case, be careful and previously download the archive copy of the current project. 6. Validate and publish the updated project so that you can perform further testing. If you develop a complex project for the application with multiple customization projects published and you can't address some issues that have emerged during this stage, try to perform the original installation without previous updates and then upload the saved content of the current project. If the problems disappear, it denotes the absence of compatibility with some previous update package. Try to address and fix issues after performing consequent updates of the original installation updated with the current project with each previous update. Once the tests are successful, do the following: Upload the deployment package to the production application or update the content of an existing project. Validate and publish the content of the project in the production application. Monitor users' data processing in the production application to ensure that they encounter no problems. The version number of the customization and production instances must be the same. Otherwise, before deploying and publishing the last project on the production instance, you should upgrade one or both instances to the same version. If the production instance has been upgraded or will be upgraded soon to the particular version, you should upgrade the customization instance and repeat all testing steps. If the tests are successful, you should again create the deployment package to upload it to the production application instance. See also Updating a Customization Project. Notice that after you have published a customization project (or multiple projects), a project name (or project names, separated by commas) are displayed on the log-on form of the application instance, as the screenshot below illustrates.
Customization Stages 101 Figure: Viewing published project names on the log-on form
Acumatica Customization Project 102 Acumatica Customization Project An Acumatica Customization Project is a set of changes and additional files that modifies your Acumatica ERP application instance or other Acumatica Framework-based software product. Customization facilities are primarily designed for independent software vendors (ISVs) and valueadded resellers (VARs) that either develop vertical and add-on solutions on top of Acumatica Framework-based software or deliver the customization of Acumatica Framework-based software products to the end users. To deliver an add-on solution or an Acumatica Framework-based software product customization, you should add all the changes and additional files to the customization project and provide it as a deployment package to the end users. You can create as many customization projects as you need. Acumatica Framework provides the mechanism to publish, upgrade, or unpublish your customization project deployment packages at any time. The following underlying topics describe the typical content of a customization project and all main actions necessary to manage a customization project and its content: Content of a Customization Project Customization Menu and Sessions Understanding the Publication of a Project Managing Multiple Projects Updating a Customization Project Deleting a Customization Project Content of a Customization Project This topic covers the most important themes that concern the internal and external Acumatica Customization Project content. Typical Customization Project Content Each project has specific content that depends on the task to be resolved within the project and on the using customization technology. You can see the internal part of this content through the Customization Projects webpage, while the external files are represented by assemblies,.aspx pages, and other files that are created either during the development of an add-on solution or by implementing a customization of the application. Once you have finished working on the project, you download it as a deployment package. A deployment package is a zip file that includes both internal content and external files included in the customization project. The full content of a customization project, including external files, is stored in the database of the appropriate application instance after you have saved the content to the database. Customization project content is automatically saved to the database before you start the validation process prior to publishing the project or right before you publish the project without validation. Also, you can save the content by selecting Save Project to Database on the Customization menu. To explore the internal content of an existing project of an Acumatica ERP application, you should start this application, navigate to System > Customization > Manage > Customization Projects, and select Project Name. The details table of this webpage displays the content of the selected customization project through records of different object types.
Acumatica Customization Project 103 All the actions illustrated below can be performed without starting a customization session (for details, see Customization Sessions; that is, you shouldn't open a project. Instead, you select the required project in the Project Name field. If you need to publish multiple customization projects, just open the Publish Customization webpage, select the required projects, and validate and then publish them. You can also publish any selected project by using the Customization Projects webpage. In both cases, you shouldn't open the customization project before starting to publish it. (For details, see Managing Multiple Projects and Final Testing and Deployment Stage.) The screenshot below illustrates the add-on project, with three external files have been placed in: the add-on assembly, and also the.aspx and.aspx.cs files that represent the new added webpage. Notice the site map modifications under the header of the Data-type object. The system will create two other objects (also shown in the following screenshot) as a result of the object-adding actions described in the next section: An object of the Report type that was added from the database An object of the Sql type with the code that describes the structure of the new database table used within the add-on assembly Figure: Exploring the object of the Sql type of the add-on project The screenshot below illustrates a simple customization project implemented with user interface customization and functional customization by using the Acumatica Customization Engine (ACE). The project resolves these basic tasks: declaring the KeyWords field within the data access class (DAC), adding the appropriate database table column to which this new field is bound, adding the corresponding UI input field onto the form area of the Stock Items webpage, changing the name of the existing input field in the form area, and making a column mandatory for data entry in the details table of the tab area.
Acumatica Customization Project 104 Figure: Exploring the content of the object with the Page type Notice the content records of four objects: Two objects of the Code File (DAC) type, which represent the additional code of the DACs. The first object belongs to the DAC used on the form area of the page, and the second one belongs to the DAC used within the details table on the tab area of the page. One object of the Page type, which is the changeset to the.aspx code and includes the properties of the additional field. One object of the Table type, with the code that defines the properties of the new database table column to which the new DAC field is bound. The screenshot below illustrates a more complicated customization project, implemented by using UI customization and the Acumatica Extensibility Framework (AEF), that alters both the UI and the business logic of the Stock Items webpage. This project resolves the following tasks: declaring the UsrAdditionalDescr field within the DAC, adding the database table column to which this field is bound and the corresponding control onto the form area of the Stock Items webpage, and validating the UsrAdditionalDescr DAC field values.
Acumatica Customization Project 105 Figure: Exploring the content of the object with the File type The content record of an object with the File type represents the assembly, which is the extension library solution created when you use MS Visual Studio or another development environment and which is intended to be used by the AEF at run time. This file resides in the Bin folder of the website, as you can see from the path expression within the content of the File-type object in the screenshot above. The system created the following objects as a result of performing the actions described in the next section: One object of the Page type that is the changeset to the.aspx code and includes the properties of the additional field One object of the Table type that describes the properties of the new database table column to which new DAC field is bound Managing the Content of a Customization Project By using the Customization Projects webpage, you can analyze and manage the content of any inprogress or completed customization project on the current application instance.
Acumatica Customization Project 106 Figure: Noting the menu items of the Add button The screenshot above shows the commands of the Add menu, which you can use as follows: If you click File from File System, the Add Files dialog appears with all the external files that have been placed in the folder of the website. To include any needed file in the current customization project, select the check box left of the file name and click Save. (As a result, the system creates at least one object of the File type with the File name and the relative path to the application folder. The system encloses this new content in <File/> tags.) If you click New Graph, DAC, Code File, the Create Code File dialog appears. Select PXGraph, IBqlTable, or Code File as the File Template and type the Class Name to generate the code of the business logic controller (BLC, also referred to as a graph), DAC, or class template; you can generate the DAC code template or the DAC code based on a user database table if you select the Generate Members from Database check box. (As a result, the system creates an object of the Codetype with the graph, DAC, or class template code. The system encloses this new content in <Graph/> tags.) If you click Generic Inquiry, you can prepare for export the needed set of saved conditions on the Generic Inquiry webpage. (As a result, the system creates an object of the Export type. The system encloses the exporting code in <Export/> tags.) If you click Site Map, the Site Map pop-up window appears with the altered (added, modified, or removed) site map items. To include an updated site map item in the current customization project, you can select the check box left of the site map record. (As a result, the system creates an object of the Data type. The system encloses the new node and position numbers in <Data/> tags.) If you click Report from Database, the Select Report dialog appears; you can select the report in the Name lookup UI field. (As a result, the system creates an object of the Report type. The system encloses the report structure in <Report/> tags.)
Acumatica Customization Project 107 If you click Database Table or Script, the Edit Sql Script dialog appears. You select the DBObject Name from the DBObjectName lookup window (and select the Import Table Schema from Database check box if needed) and then click OK. You can manually add (or paste from your clipboard) the SQL script, which will be executed every time the customization project is published, into the Custom Script text area. You must select the DBObject Name so that the record will be added to the currently selected customization project. (As a result, the system creates an object of the Sql type. The system encloses the new content with the SQL script in <Sql/> tags.) If you click Database Table Field, the Edit UsrField to Database Table dialog appears. You can select the table name from the Table Name lookup window, type the Field Name, and select the Field Type from the drop-down list. (As a result, the system creates an object of the Table type with the set of properties for the database column to be added. The system encloses this content in <Table/> tags.) If you click Edit XML, you will see all the inner customization code in XML format. The system encloses the content of each object in the appropriate tags. You can see this XML code structure within the appropriate tags on the Customization Projects webpage, but in the window that this webpage contains, the content of each object is displayed separately under each type of object, as fragments of the whole customization content. See the next section for details. When other customization tasks are resolved, the system creates the following objects: Page type objects if you are working with UI customization tools and facilities (for instance, see Adding Columns to a Selector). The system encloses this content in <Page/> tags. DAC-type objects if you are using the Create Data Field designer (for instance, see Customizing DAC Attributes) or DataField Attributes window of ACE. The system encloses this content in <DAC/ > tags. Code-type objects for the customization BLC code files (for instance, see Extending Business Logic) created by using the tools and facilities of ACE. The system encloses this content in <Graph/> tags. You can manually edit any currently selected customization project record. However, manual editing of the content is not recommended, because you can change the content more reliably by using Acumatica Framework tools and facilities. If you select a record with the File, Code, Data, or Sql type and click Edit, the appropriate pop-up window or webpage appears. You can modify the selected record and save the changes to the customization project. We strongly recommend that you click RefreshDBTables every time you have made changes to any database table whose schema had been imported from the database to the customization project. Otherwise, you may encounter unpredictable results during the testing stage. The refreshing procedure regenerates the database script of the tables placed within the project with the selected Import Table Schema from Database option. We also recommend that you click Check in Files before you start the validation of a project if you have made changes to any external file included in the project or file created in the Caches subfolder of the App_Code folder of the website that contains changes made with ACE. Otherwise, you will get the Some files have been modified manually by the user on the file system. Please resolve conflicts message, which will redirect you automatically to the Check in Files dialog.
Acumatica Customization Project 108 Figure: Initializing the process of checking in files In the Check in Files dialog (see the screenshot above), you can analyze and resolve content conflicts with the external files. For files with conflicts, the Conflict check box is selected. Content conflicts take place when you have modified any external file of the website that is included in the project. To update these files, you should select the Selected check box for the appropriate records and then click Check in Files. If you do not want to update some files with conflicts, do not select them for checkin. After check-in, click Remove all Conflicts. Make sure you update appropriate files with conflicts before removing all remaining conflicts. After you click Remove all Conflicts, you won't get any customization project conflicts until a file included in the customization project is modified again. Customization Menu and Sessions You use the Customization menu, along with the Customization Projects webpage (see Content of a Customization Project), to manage customization projects. You can access this menu if you open any Acumatica ERP webpage. Every time you log in and then open (or create and open) a customization project, the system starts a new customization session or continues a temporarily interrupted session. During a customization session, you can extend or alter an Acumatica ERP application to your specific business requirements by employing appropriate customization tools and facilities. The Customization Menu By using the Customization menu, you can perform such actions as opening and closing customization projects, selecting the project manager (represented by the Customization Projects webpage), initializing various kinds of customization tools, and saving the content of a customization project to a database or reloading its content from a database to roll back the project to the previous state. You can access the Customization menu on an opened Acumatica ERP webpage if you have the Administrator or Customizer role (for details, see Granting User Access Rights for Customization). You can use the following menu commands to resolve customization tasks. Customization Menu Commands Command Description Save Project to Database Saves to the database the current content of the customization project that is, modifications that you have made during all previous customization sessions. (See the more thorough description of a customization session in the next section of this topic.) Reload Project from Database Reloads the content of the customization project from the database. If you haven't saved new modifications to the database within the complete or temporarily interrupted customization session, your
Acumatica Customization Project 109 Command Description current modifications will be replaced by the most recent modifications that had been saved. Open Customization Project Gives you the option to open the existing customization project or to create a new one. Close Project Closes the current project: ends the whole or temporarily interrupted current customization session and returns you to working with the Acumatica ERP application with the last published changes implemented. If you want to avoid losing your most recent changes, save these changes before closing the project by saving the project to the database or by using the validation process. Enter Page Design Mode Enters page design mode and provides access to the UI customization designers and facilities (such as Create control, Advanced control, and Aspx Control Tree). To modify multiple webpages, you can open each required webpage in page design mode. (See Page Design Mode.) Exit Page Design Mode Exits page design mode for only the current page without losing UI changes. To end the whole or temporarily interrupted customization session and return to working with the application, you should close the current project. (Do not forget to save your changes before closing the project.) Validate and Publish Validates current changes and publishes the project to the application (if you click Publish after successful validation). The content of the current project is automatically saved to the database before the validation process begins. You do not need to manually save the content of the project before selecting this option. View Published Customization XML Displays the published customization content in XML format. The contents of all published customization projects are placed within the common XML code. Undo Publish Removes the customization from the application, and restores its original state. This action discards the published modifications of not only the current project but also every published project. All of the published project names are listed in parentheses after this menu item name, such as Undo Publish (GL_01, SO_03, AEF_Demo). View Customization Manager Opens the Customization Projects webpage, where you can explore, manually edit, and remove (if needed) all changes within the content of the current project. View Source Code Opens the Source Code browser, which displays the original source code of the current webpage and its BLC. You view source code to analyze the application code before you make customization changes. (See Exploring the Source Code.) Code Editor Opens the Custom Code editor so that you can add or manually change the business logic controller (BLC) code by using the Acumatica Customization Engine technology.
Acumatica Customization Project 110 Command Description Aspx Editor Opens the Aspx Pages editor so that you can manually change the.aspx code of the current page. Customization Sessions You perform most customization actions during a customization session. The customization session is a set of time ranges within a workday during which you perform customization steps (see the next section of this topic) to resolve the specified customization task within the particular customization project. You must open the project to perform UI customization steps and some functional customization steps by using the Acumatica Customization Engine (ACE). But before testing the results of any completed step at run time, you should close the project; if necessary, you can open it again after testing. A customization session includes the testing time, when the customization project is temporarily closed. A customization session can also include time ranges when you perform customization by using the Acumatica Extensibility Framework (AEF) without opening the project. When we speak of a customization session, we are talking only about the development stage of customization. For details, see Customization Stages. Some actions related to a customization project can be performed after the interruption of the current customization session that is, when the project is closed but are still parts of the customization session. These actions include the following: Developing extension libraries with AEF Testing customization project changes Other actions related to a customization project concern the project but are executed outside of customization sessions, such as the following: Developing add-on solutions Performing customization actions by using the tools and facilities of the auxiliary customization scope Publishing the customization project by using the Customization Projects webpage Publishing a group of customization projects or unpublishing all previously published ones by using the Publish Customization webpage As a rule, you resolve only a part of a customization task within a workday session so that the whole customization within a particular project may be fulfilled during multiple workday customization sessions. The number of workday sessions needed to resolve the task depends on its complexity. Moreover, you may work with multiple projects during your workday. In this case, you open multiple sessions, one for each project, sharing the work time with each session, because you resolve different customization tasks by using different projects. When each customization session begins, the customization content may be empty (for a new project) or may include appropriate customization object types (for an existing project). With the initial UI content, page design mode starts right after you select the project name and open the project, for all the pages already modified (with changes saved to the database) during all previous customization sessions. (See Page Design Mode.) The whole content of a customization project, including external files, is stored in the database. You can save changes to the database manually, by clicking Save Project to Database on the Customization menu, or automatically, when you select Validate and Publish on the same menu (the system saves the content before it begins the validation process). The customization session ends when a workday (or the part of it devoted to the particular project) is over. When you continue the customization process and start the next customization session on the
Acumatica Customization Project 111 next workday, the system automatically loads the customization content of the project, which includes changes that had been saved to the database during all previous customization sessions. Customization Steps and Session Cycles You can divide actions to be performed during the current customization session into separate customization steps. We recommend that you define as a step each group of actions that completes some part of a customization task. You may publish the project and test the results of the step to ensure that it is successful. For instance, adjusting the UI properties of an input text field and positioning it may be considered a step, and adding a combo box and further tweaking it by altering field attributes (with appropriate modifications of the data access) may be considered another step. When defining customization steps, you don't need to include UI and functional customization actions in different steps; as a rule, to obtain a result to be tested, you perform UI and then functional customization (as in the second example above). Therefore, you can include actions of both customization scopes in one step. You can instead include all the mentioned modifications within a single step. The way you define steps is fully up to you: You can define simple or more complex steps. We recommend only that you define each step as the content of the project that represents some milestone, that you test its customization results at run time, and that you back up changes of the steps that are not completed (because the workday ends or the session is interrupted) before closing the project. A customization session may include one step or multiple steps. Some steps may not be completed, if you have no time to finish them within the current session or if the session is interrupted. When you start the next workday session or return to the interrupted one, you have to first complete these unfinished steps. The main idea of dividing a customization session into steps is that you check the results of each step and update the database with the current content of the customization project. To make it possible to roll back the project to the previous step (for example, if some issues weren't addressed), before saving the project, you should download the content that includes the changes made during all previous successful steps. If you have the set of external archive packages of the project, you can roll back the project to any previous successful customization step. To better organize your work within a customization session, follow the instructions below: 1. Create a new project (or open an existing one). 2. Ensure that you have the last archive file made during the previous session; if it's absent, download the current content of the project. 3. Perform the necessary changes of the customization step. 4. Check modifications of external files in. (This instruction is enough to be performed once, at the end of the final step of the customization project and is compatible with AEF and add-on solutions.) 5. Update scripts of modified database structure. (This instruction is enough to be performed once, at the end of the final step of the customization project and is compatible with any customization technology.) 6. Validate and publish the project when the current customization step is completed. 7. Test the results of the current customization step. 8. If the test results of the step are successful, download the project to an external archive package (within the complete or partial customization session). If the results are unsuccessful, analyze the changes made within the current step to address the problems and then validate and publish the project again, or roll back the project to the last state by uploading the appropriate archive package. 9. Repeat instructions 3, 6, 7, and 8 for each additional step.
Acumatica Customization Project 112 10. Optional: If any steps of the customization session have not been completed, but you want to save your changes before closing or interrupting the session, click Save Project to Database on the Customization menu, so that the next workday session or the continuation of the interrupted session will start with the saved content. 11. Optional: To always have a new archive with recent changes, you can download the archive copy if any steps of the session have not been completed after you save the current content of the project to the database further for instance, at the beginning of the next workday customization session or after you continue the interrupted one. When the customization task is resolved, download the deployment package of the project for the final testing and deployment stage. Although the main part of an add-on solution is the program code you develop outside of customization sessions without using the tools and facilities of the Acumatica Customization Platform, you may also divide the customization (development) task into separate steps to consistently obtain reliable results. Understanding the Publication of a Project As described in Development Stage, you can publish the current customization project (or multiple projects) in one of the following ways: If the project is opened and you are working with a webpage, select Validate and Publish on the Customization menu. After the validation process finishes successfully, click Publish. On the Customization Projects webpage, select the name of a project and click the Validate and Publish button; after the validation process finishes successfully, click Publish. To publish a customization project without validation, on the Customization Projects webpage, click the Validate and Publish button to view the menu, and click Publish without Validation (see Publishing a Customization Project Without Validation). If you need to publish multiple projects simultaneously, open the Publish Customization webpage and perform the actions described in Managing Multiple Projects. Working in Page Design Mode When you enter page design mode (see Page Design Mode), the.aspx and aspx.cs files are created in the appropriate subfolder of the CstDesigner website folder, as shown in the screenshot below. Figure: Observing the placement of the customized webpage files The subfolder name is compound, consisting of the user name and the name of the customization project. During the customization process, when you move UI controls or apply other changes to the page, the related.aspx file is automatically changed. When you enter page design mode next time, this modified.aspx code from this subfolder is used unless you remove the file or close the customization project without saving it to the database.
Acumatica Customization Project 113 Understanding the Structure of the Customized Website The customized.aspx files, which were modified in page design mode or by using the Aspx Pages editor, are created by applying the XML changesets over the original webpages and are placed in the pages_xx subfolder of the CstPublished folder. The original page subfolder name replaces xx, and the merged result of the original page and the changeset applied is used instead of the original webpage. In the screenshot below, you can see that the Pages\IN relative path has been converted to the pages_in subfolder. Figure: Noticing the placement of the published files The functional changes that you made by using the Acumatica Customization Engine (ACE) are included in the files that the system places in the Caches subfolder of the App_Code folder of the customized application instance (see the following screenshot). The code of the files in the screenshot is visible in the Source window of the Customization Projects webpage, within the appropriate XML tags of the two objects of the Data Code type. Figure: Exploring the Caches subfolder Business logic controllers (BLCs, also referred to as graphs) that were customized by using the ACE are recognized by the system in the Caches subfolder of the App_Code folder and dynamically compiled during website startup. The base BLC is replaced at run time with this customized one. Changes to the original data access classes (DACs) created by using the ACE are recognized by the runtime and injected directly into the CIL code of the library that contains the original DAC. For instance, to customize the PX.Objects.IN.InventoryItem DAC (as shown in the screenshot above), the CIL code of the PX.Objects.dll assembly is modified, as shown in the screenshot below. The original library containing the base DAC declaration is placed in the App_Data\RollbackFiles\Bin folder (also shown in the following screenshot).
Acumatica Customization Project 114 Figure: Viewing the modified PX.Objects assembly Extension libraries created by using the Acumatica Extensibility Framework (AEF) as well as any other external files, such as assembly files or webpages of an add-on solution are added to the testing or production application with the same path they had within the customization application instance. Extension and add-on libraries are placed in the Bin folder of the customization application instance (as shown in the following screenshot). Webpages created by using an add-on solution are added to Pages/XX subfolder of the website, where XX is the abbreviation of the module name. Pages/XX represents the relative path to the created webpage. Figure: Exploring the Bin folder of the customization application instance
Acumatica Customization Project 115 Every time you publish a customization project, changes to the application database, including changes made with the auxiliary customization tools, are executed on the database. Keep this in mind when you prepare and store custom database scripts within a customization project. Restoring the Original Application State You can unpublish the current customization project or multiple projects (that is, cancel the customization changes) in one of the following ways: By clicking Undo Publish on the Customization menu By clicking Undo Website Customization on the Publish Customization webpage (see Managing Multiple Projects) With either way, all of the published customization projects will be unpublished. If the unpublish procedure fails, you can manually restore the original state of a customized application as follows: 1. Restore the content of the App_Data\RollbackFiles folder to the root folder of the website. 2. Clear the content of the CstPublished folder. 3. Delete the files placed in the Caches subfolder of the App_Code website folder. 4. Remove all external files deployed on the website. After you unpublish a customization project, the application database in which the content of the project and all the table structure changes are stored, including changes you made with the auxiliary customization tools, doesn't return to its original state, and no added tables or fields are deleted. If you delete the content of the customization project (see Deleting a Customization Project), this content will be removed from the database, but the changes of the table structure still remain. Managing Multiple Projects You can manage the publication of multiple customization projects after navigating to System > Customization > Process > Publish Customization. (In this case, you don't need to start a customization session.) The Publish Customization webpage displays the list of customization projects available for the current application instance. You can perform the following actions: Select the check box left of the record of each project you want to manage and click Publish Selected Projects to validate and publish the selected projects (see the screenshot below). If you do not select a previously published project for publishing, this project becomes unpublished after this procedure.
Acumatica Customization Project 116 Figure: Publishing multiple projects You can simultaneously publish projects with modifications of the same webpage and data access class (DAC). You use the Level column of the Publish Customization webpage to set up the order in which possible contradictions are resolved during the publish procedure (the greater the level number a project has, the higher priority it gets). The system uses the level of a project only when the concurrency takes place, that is, when both of the following conditions exist: Webpages and DACs had been customized by using the Acumatica Customization Engine Webpages or DACs with the same names had been customized in more than one customization project Click Undo Website Customization to roll back the application to its initial state by unpublishing all customization projects. If you select a project record in the list of projects and click the Edit button, the system redirects you to the Customization Project webpage, which displays the content of the selected project. Also, on the Publish Customization webpage, you can upload a new project to the website: Click Open from file, click Browse, select the required zip file, and click Upload. The project is added to the application instance. Updating a Customization Project When you upgrade to a newer version of the Acumatica ERP application, you generally need to update the published customization projects of the application instance. Updating a published customization project involves making changes to the customization project or to your add-on solutions to make the common customization content compatible with the upgraded version of Acumatica ERP. These changes may be minor, and in some cases no changes may be needed. However, in some cases, especially when you upgrade a very old version of the application, you will have to update the common customization content of this project. The common customization content is the joined content of all the published customization projects and add-ons. The common customization content was generated and stored in the database when the application was last successfully customized and published before the upgrade took place. The upgrade process negates the common customization content by restoring the original application state but keeping the customization changes to the database, including the table structure and the changes made through the auxiliary customization tools. The first time the upgraded application instance is started, the system validates and applies the common customization content from the database.
Acumatica Customization Project 117 If the validation process fails, you should perform the appropriate actions, which depend on the type of error that occurred. To find out which of the customization projects contain issues to be fixed, you should first activate the Undo Publish procedure and then validate and publish the appropriate customization projects one by one. Notice that the system stores in the database not only the common customization content, but also the content of each project, including content that had not been published. Therefore, through the Customization Projects webpage you can see content of any project if it hadn't been deleted. (For details, see Deleting a Customization Project.) After the successful publishing of a project, the system adds the content of this published project to the common customization content. You can use the following procedures, described in the sections of this topic, to fix the issues that have appeared after the application upgrade: Convert Layout, which you use as the starting point to fix issues in the.aspx code. Publish without Validation, which you use if the validation of a project that was created by employing the Acumatica Customization Engine (ACE) fails. Convert Layout You need to convert the layout only if you have used Acumatica ERP 3.0 or lower (or had published an add-on solution with pages created by employing old page layout technology) and now have upgraded the application to 4.0 or higher. You proceed slightly differently in each of these cases: 1. You (or any other developer) created webpages by using the add-on solution with the old page layout technology, and this solution had been published. 2. You (or any other developer) customized webpages for the version of the application that supported the old page layout technology, and the customizion project had been published. In the first case, you navigate to System > Customization > Explore > Source Code and open the Page Aspx tab of the Source Code webpage. Click the Magnifier button of the Screen ID field, select one of the pages created in the add-on solution, and click Convert Page, as shown in the screenshot below. The conversion procedure changes the.aspx code of the page so that it fits the new page layout technology. Repeat this conversion procedure for all pages that you (or any other developer) created.
Acumatica Customization Project 118 Figure: Starting to convert the page created in add-on solution During the upgrade process, all the original pages are automatically replaced by the new ones. You must convert only pages that were created within an add-on solution. You may have to alter the design of the converted pages in Visual Studio. If you do, don't forget to check in the changes of the.aspx files to the appropriate customization projects. In the second case (you customized webpages for the older version of the application and the customization project had been published), you have to navigate to System > Customization > Explore > Customization Projects and click the Convert Layout button on the Customization Projects webpage (shown below, in the next section). When you click this button, all the page changesets related to the current project content objects of the Page type are converted. After the conversion procedure has completed, you can see the new changeset for each page in the lower window of the Customization Projects webpage. You should then check each of the customized pages. You may need to open the appropriate customization project and then make minor changes to the design of the customized pages in page design mode. Publish Without Validation If the failing of the upgrade validation takes place, which is automatically performed (during the first start of application after the upgrade) for a project that you (or any other developer) created by using ACE, to address issues through additional deep testing, you can publish the project without validation. The screenshot below illustrates the Customization Projects webpage with the Convert Layout button (described in the previous section) and the Publish without Validation option of the Validate and Publish menu. In this case, you select a project that should be published to the upgraded application and click Publish without Validation. After publishing the project, you should fix the issues found within it and test the results. When all of these issues are fixed and the changes to the code files are checked in to the project, you should start working on the next project. Continue this process until all projects are successfully published and tested.
Acumatica Customization Project 119 Figure: Viewing the Customization Projects webpage You may need to publish the customization project without validation in some other cases for instance, if the project fails validation when you use ACE, but you want to publish it anyway to save the data access class (DAC) and business logic controller (BLC) files and then correct their code by using MS Visual Studio. Deleting a Customization Project This topic describes how to delete a customization project from an application instance and what happens after its deletion. Once you have decided to delete the customization project, you must check whether it is published or not. We highly recommend that you delete only those customization projects that are not published. You can look at the second column (Published) on the details table of the Publish Customization webpage: If the check box is selected, the project has been published. Unpublish the project before trying to delete it with the whole content. If you anyway have deleted a published project or projects, later you can get error messages shown at the end of this topic, in two final screenshots below. See also recommendations for this case. To delete a customization project, navigate to System > Customization > Explore > Customization Projects. On the Customization Projects webpage, click the Magnifier icon right of the Project Name field and select the project to be deleted. On the form toolbar, click Delete, as the screenshot below illustrates. The selected project and its content is removed from the database.
Acumatica Customization Project 120 Figure: Deleting the GL_01 customization project Instead of deleting the entire content of the customization project, you can delete parts of the customization content whole objects or separate fragments of the objects. To delete a whole object, on the details table of the Customization Projects webpage, select the record with the erroneous or unused content and click Delete on the details table toolbar. To delete separate fragments of an object, select the record on the details table of the Customization Projects webpage with the erroneous or unused content, and then manually delete or edit its fragments in the Source area of the webpage. You can delete the project or multiple projects in another way: Navigate to System > Customization > Explore > Publish Customization. On the Publish Customization webpage, select the project that is to be deleted; on the details toolbar, click Delete Row, as the following screenshot illustrates. Figure: Deleting the customization project on the Publish Customization webpage
Acumatica Customization Project 121 Figure: Viewing the error message while publishing the current project through the Customization menu Figure: Viewing the error message while publishing the current project through the Customization Projects webpage In this case, to publish other customization projects currently stored in the database, you should click the Publish Selected Projects button on the Publish Customization webpage (for details, see Managing Multiple Projects). You can instead first cancel publication of the other projects you want to publish (undo their publishing) by selecting Undo Publish on the Customization menu, and then publish the selected projects again. During one of these procedures, the system deletes records concerning the deleted published projects, which were still temporarily being kept.
Acumatica Customization Project 122 Restoring the Original Application State describes how to restore the original state of a customized application, as well as what to do with external files of the customization project, if the undo publish procedure fails.
UI Customization 123 UI Customization With UI customization, you can do the following: Manually drag controls on a page within their placeholders (container controls) Add controls of different types (such as NumberEdit, Selector, and ComboBox) onto a page Add advanced controls (such as tab, panel, grid, and button) onto a page Manage the control layout of a page, including the capability to delete controls or hide their labels Manually adjust a wide range of UI properties of controls Manually edit the.aspx code The following topics cover what capabilities the UI customization provides and which UI customization tools and facilities are used: Performing UI Customization PXLayoutRule Components Adding Items Adding Advanced Controls Performing UI Customization You perform UI customization either in page design mode or by using the Aspx Pages editor (see Aspx Pages Editor), which is used for manual editing of.aspx pages. In page design mode, you can use the one of the main customization tools, the Aspx Contol Tree. Almost all UI customization actions, except for the manual editing of the.aspx code, are performed in page design mode. The topic Page Design Mode defines and briefly describes this mode. Page design mode is used to: Drag, move, and delete UI controls and grid columns (see Dragging, Moving, and Deleting UI Controls and Grid Columns) Add selector columns (see Adding Columns to a Selector) Create controls and columns (see Adding Data Fields and Adding a Column to a Grid Control) Manage PXLayoutRule components (see PXLayoutRule Components) Add items (see Adding Items) Add advanced controls (see Adding Advanced Controls) Page Design Mode When you enter page design mode, you can perform most user interface (UI) customization adjustments listed in User Interface Customization (except for the manual editing of.aspx pages) and a few functional customization changes by using the Acumatica Customization Engine facilities. These UI adjustments might include adding custom data fields and adjusting the field attributes of data access classes (DACs). You use this mode while working with a page to adjust the layout of a page, change the simple properties and internal collections of UI controls, add new controls onto the page, and delete controls or hide their labels, if needed.
UI Customization 124 To enter page design mode, you first create a new customization project or open the existing one that is, begin a new customization session (see Customization Sessions). You then open the webpage to be changed and on the Customization menu, select Enter Page Design Mode. You can modify any number of pages, entering page design mode for each page that must be customized. Before you close the session, on the Customization menu, select Save Project to Database or select Validate and Publish; otherwise, your changes will be lost when you close the current customization project. After performing and saving necessary changes, you can do any of the following: Close the current customization session: On the Customization menu, select Close Project. Exit page design mode for separate pages without closing the customization session (for instance, to continue customization session with functional customization): On the Customization menu, select Exit Page Design Mode for each or for particular customized page. Discard the changes, if you haven't saved them to the database: On the Customization menu, select Reload Project from Database. Your current modifications will be replaced by the most recent modifications that had been saved. When you open a new customization session, the system automatically enters page design mode for all Acumatica ERP webpages that have been modified within the current customization project during the current or any previous customization session. In this guide, we use webpage to describe an Acumatica ERP webpage that is opened before you enter page design mode and is used at run time, while we use page to describe a webpage that is being customized in page design mode. Thus, once you enter page design mode, the webpage is transformed to a set of standard page template areas, its appearance is changed, and the webpage becomes the page as a subject of UI modifications. Opening a Customization Project and Entering Page Design Mode The table below illustrates typical actions you can take to enter and exit page design mode in various kinds of scenarios. Scenario Description Entering Page Design Mode for a New Project To customize an Acumatica ERP application webpage within a new project, perform the following actions: To create a project, open the webpage you want to change, and on the Customization menu, select Open Customization Project (see the screenshot below).
UI Customization 125 Scenario Description Figure: Starting to add a customization project In the Select Working Project window that appears, click New (as shown in the screenshot below, near the red 1). In the New Project window that appears, type the new project name (see item 2 in the screenshot below) and then click OK (item 3). Figure: Assigning a name to a new project If the Select Working Project window that is still opened, click OK (item 4) to open the new project. To work in page design mode, on the Customization menu, select Enter Page Design Mode.
UI Customization 126 Scenario Description Perform the required customization actions. For instance, right-click the upper area of the page (the main form of the PXFormDetails or PXFormTab page template) and select Add Input Control. Add the UI input field. Right-click the added field and select Control Tree to specify the UI properties of the field. The following screenshot illustrates the process of adjusting the added input field properties. To save the customization changes, on the Customization menu, click Save Project to Database. Figure: Adjusting the properties of the added input field Entering Page To customize an Acumatica ERP application page within an existing project, do the Design Mode following: for an Existing To open an existing project, on the Customization menu, select Open Project Customization Project. The system automatically enters page design mode for all webpages that have been modified within the current customization project during the current or any previous customization session. Exiting Page Design Mode Optional: To work in page design mode for a webpage to be customized within the current project (but one that has not yet been changed), on the Customization menu, select Enter Page Design Mode. Perform the required customization actions on pages to be changed. To save the customization changes, on the Customization menu, click Save Project to Database. To exit page design mode for the current page, on the Customization menu, select one of the following: Exit Page Design Mode (see item 2 in the screenshot below): This option cancels page design mode for the current page, while the other pages modified within the current project remain in this mode. Close Project (see the screenshot below, item 3): This option closes the current customization session and exits page design mode for all pages. Your current changes are lost when you close the current customization session if you have not already saved them to the database. Validate and Publish (see item 4 in the screenshot below): By selecting this option, you save the changes to database and validate and publish them. If
UI Customization 127 Scenario Description changes are successfully applied on the website, all pages exit page design mode. Figure: Exiting page design mode and closing the project Working in Page Design Mode When you enter page design mode, the.aspx and aspx.cs files are created in the appropriate subfolder of the CstDesigner website folder, as shown in the following screenshot. Figure: Observing the placement of the customized webpage files The subfolder name is compound, consisting of the user name and the name of the customization project. During the customization process, when you move UI controls or apply other changes to the page, the related.aspx file is automatically changed. When you enter page design mode next time, this modified.aspx code from this subfolder is used unless you remove the file or close the customization project without saving it to the database. Aspx Pages Editor You can manually change the.aspx code within the content of a project. See below for an example of how to modify the code of the Stock Items page. Perform the following actions:
UI Customization 128 1. Navigate to Distribution > Inventory > Work Area > Manage > Stock Items, and open an existing customization project. 2. On the Customization menu, select Aspx Editor. 3. In the Aspx Pages window that appears, make a few changes (for instance, replace the false property value with true for two properties, as the screenshot below illustrates) and click Check in. Figure: Manually correcting the.aspx page code 4. Close the Aspx Pages window. 5. On the Customization menu, select Validate and Publish. When the validation process finishes successfully, publish the modified project. 6. On the Customization menu, select View Source Code. 7. On the Source Code browser that appears, click the Page Aspx tab, as shown in the screenshot below. The red type indicates manually changed property values. Figure: Exploring the Page Aspx tab of the Source Code browser
UI Customization 129 As a result of any webpage changes that you made either manually or by using UI customization tools, the changeset is generated. You can view this changeset as the content of the Page type object on the Customization Projects webpage, which you can open by navigating to System > Customization > Manage > Customization Projects. PXLayoutRule Components The following underlying topics describe the use of the PXLayoutRule component for UI customization: Placing UI Elements in Multiple Rows and Columns and Widening these Elements Setting Label Width and Aligning UI Elements Horizontally Creating a Group of UI Elements and Hiding Their Labels Creating Nested Input Controls Setting Widths and Sizes of UI Elements Introduction The PXLayoutRule component, which you access from the Aspx Control Tree, facilitates relative positioning layout. You use relative positioning layout to adjust the UI within a data record area in the following ways: Placing UI controls in multiple columns to uniformly distribute them on the form or tab area of a page Spanning UI controls across multiple columns Merging UI controls into one row to align them horizontally Adjusting the widths of controls and labels Hiding the labels of controls Grouping UI controls for users' convenience In this guide, we use the term data record area to describe a form or tab webpage area, as well as the form area displayed within a grid in FormEdit mode. A data record represents one record of the recordset obtained from the business logic controller (BLC, also referred to as a graph) after retrieving the data; the recordset contains a record if a form or tab is bound to the data view, or a set of records if a grid is bound to the data view. The record displayed on a data record area includes both the main data access class (DAC) object and the joined DAC objects. The data record area is generally used to display the field values of a record; however, if it is represented by a form or tab, the area can contain nested form or grid areas that are used to display field values of a record or multiple related records. Nested forms or grids displayed in FormEdit mode also represent a data record area. Because panels or groups are always placed on a form, they are always contained and displayed within a data record area. Dynamic Behavior of the UI Controls' Relative Positioning Layout Each data record area consists of rows and columns (not to be confused with rows and columns of a grid). While working with a webpage on different devices, the user can get different sizes of the window, which can be named a parent area. If the parent area cannot fit all the columns of UI controls, the columns are moved down to the left border of the parent area and arranged horizontally to fit the parent area size. (If any of the moved columns cannot fit the parent area, the columns are moved down to the left border again.) Hence, at run time, UI controls are dynamically arranged within every separate data record area (see the definition of this term in the previous paragraph). The following examples illustrate the dynamic behavior of the relative positioning layout of UI controls:
UI Customization 130 The size of the parent area: UI controls are displayed within the number of columns that fit the parent area size. If the next column of controls or the next UI control does not fit the parent area size, it is displayed underneath the visible controls contained within the current row. (Compare the first and second screenshots below.) Figure: Viewing two columns on the tab Figure: Viewing one common column on the tab The visibility of UI controls: If a UI control is hidden, the controls are displayed in the same order as you'd specified, but there are no spaces representing the hidden UI controls. To see this,
UI Customization 131 compare the previous screenshot (which has the Search Keywords UI field) and the following one (which does not). Notice in the following screenshot that all the controls that had been placed under Search Keywords, which has been adjusted as hidden, have been dynamically moved up. Figure: Viewing one common column on the tab after the UI field has been hidden Prerequisites First you should upload the AdvUI customization project with its primary content, from the AdvUI.zip archive file. To upload the customization project from the archive file perform the following actions: 1. Start your application instance and navigate to System > Customization > Manage > Customization Projects and on the form area of the Customization Projects webpage, click From File (see item 1 in the following screenshot). Alternatively, you can navigate to System > Customization > Process > Publish Customization and click Open From File. 2. In the Open Package dialog that appears, click Browse (item 2). 3. In the File Upload window that appears, select the required deployment zip package file, AdvUI.zip, shown as item 3. 4. Click Open (item 4) to return to the Open Package dialog. 5. In the Open Package dialog, click Upload (item 5) to upload the deployment package.
UI Customization 132 Figure: Uploading the deployment package by using the Customization Projects webpage 6. After the project has been uploaded, click Validate and Publish to publish the current state of the customization project (see the screenshot below). If you have any customization projects applied, before publishing the AdvUI customization project select Undo Publish on the Customization menu. Figure: Viewing and publishing the uploaded customization project's content
UI Customization 133 Placing UI Elements in Multiple Rows and Columns and Widening these Elements This topic begins the description of how to optimally define a page layout and how to adjust the wide range of common and individual properties of input UI controls. Because the customization task is very complicated, you will be resolving the task step by step during configuring the page layout while reading this and four next topics. You should perform all the instructions exactly and carefully to obtain needed results. Placing UI Elements in Multiple Rows By default, the system places all input UI controls into one column. To optimize the layout, you need to initially set to True the StartRow property value for the uppermost PXLayoutRule subnode. UI controls are placed within a single column until you add the PXLayoutRule component with the StartColumn or Merge property value set to True. Every PXLayoutRule component that has the StartRow or StartColumn property value set to True must have one of the following sets of properties defined: LabelsWidth and ControlSize LabelsWidth and ColumnWidth You should not set both ColumnWidth and ControlSize property values for the same PXLayoutRule component; if you set both values, the system will use the value of the ControlSize property. To best use the area of a placeholder, you can place UI controls in multiple columns within a row of a form or tab area by setting the StartColumn property value of the PXLayoutRule subnode to True. This property creates a new column of UI controls within the current row. The first subnode under this rule corresponds to the topmost UI control in the column. Pay special attention to the dynamic behavior of the UI controls' relative positioning layout within the parent area. For details, see Dynamic Behavior of the UI Controls' Relative Positioning Layout. Every PXLayoutRule component that has the StartRow property value set to True initializes a new independent set of columns. To place UI controls in multiple columns within the new row, you should add the PXLayoutRule subnode with the StartColumn property value set to True. For all PXLayoutRule components with the StartRow or StartColumn property value set to True: You can assign the ColumnWidth and LabelsWidth property values from the predefined lists of options (see The predefined options for the ColumnWidth property and The predefined options for the LabelsWidth property) or type a width in pixels. The values of the ColumnWidth, ControlSize, and LabelsWidth properties must be defined for every PXLayoutRule component; they are never inherited from the previously declared one. You must assign the StartRow property for only the topmost subnode; however, for a complex page (such as Stock Items), this does not yieldan optimal layout. Therefore, for this task, you will set this property value to True for three other subnodes with the corresponding UI controls (UsrExtraShipFee, UsrActive, and UsrSprTitle). To establish the layout of this page as described above, complete the following steps: 1. Start the Acumatica ERP application. 2. Navigate to Distribution > Inventory > Work Area > Manage > Stock Items. 3. On the Customization menu, click Open Customization Project. 4. On the Select Working Project dialog that appears, select AdvUI in the Project Name box, and then click OK.
UI Customization 134 5. On the Stock Items webpage, right-click the Relative Positioning Layout tab, and then click Control Tree. 6. On the Aspx Control Tree window that appears, divide the input UI elements within the Relative Positioning Layout tab into four groups by performing the following actions: a. On the control tree in the left pane, expand the Relative Positioning Layout node and select the UsrExtraShipFee subnode. b. On the Add menu, click Add Layout Rule. c. Select the added node, Rulexx, where xx is the order number of the rule record; the filter box above the table (upper right) displays the Base Props default value. Set the value of the StartRow property to True, type SM as the ControlSize property value, and type 120px as the LabelsWidth property value, as shown in the screenshot below. Figure: Setting the StartRow layout rule d. Repeat instructions 6.1 through 6.3 for each of the following nodes of the control tree: UsrActive UsrSprTitle 7. On the toolbar (upper left), click Apply to Page. 8. On the Customization menu, click Save Project to Database.
UI Customization 135 Figure: Exploring the added tab and its layout The screenshot above illustrates the results of the instructions you have performed. Placing UI Elements in Multiple Columns In this example, you will make the Last Cost input UI field the highest one in the second column. However, you need to keep the positioning of two controls now placed under the Last Cost in the first column (and move them up on one position). To resolve this part of the customization task, you should perform the following actions (most of which are shown in the screenshot below): 1. Open the Stock Items webpage in page design mode within the AdvUI customization project, right-click the Relative Positioning Layout tab, and then click Control Tree. 2. On the control tree in the left pane, expand the Relative Positioning Layout node and select the LastStdCost subnode. 3. By using the Down button, move down the LastStdCost subnode, which corresponds to the Last Cost UI field, under the UsrSearchWord subnode to keep the positioning in the first column of the Search Keywords UI control that corresponds to this subnode, and also the two upper UI controls (and move them up on one position). 4. On the Add menu, select Add Layout Rule. 5. Set the StartColumn property value to True, type S as the ControlSize property value, and type S as the LabelsWidth property value.
UI Customization 136 Figure: Adding the PXLayoutRule component and setting the StartColumn property 6. Click Apply to Page. 7. On the Customization menu, click Save Project to Database. As the screenshot below illustrates, the Last Cost input UI field is now placed in the second column of the Relative Positioning Layout tab, while the Description (long) and Search Keywords input UI fields still keep their positions in the first column (upper on one position). Figure: Viewing the UI field that was placed in the new column Because you set the StartRow property value to True for the UsrExtraShipFee subnode, the Extra Ship Fee input UI field and all underlying UI controls stayed in the first column. Widening Input UI Elements to Span Multiple Columns Next, you need to widen the Description (long) and Search Keywords input UI fields so that they span two columns. To do this, you use the ColumnSpan property. You specify the ColumnSpan property value for a PXLayoutRule subnode by manually typing the number of columns spanned by the first UI control after the rule. Hence, you should specify this property value twice for each subnode that corresponds to the Description (long) and Search Keywords UI fields. Each of these UI fields (or other types of UI control) spans the specified number of columns, starting from the column to which it originally belongs. For all PXLayoutRule components with the ColumnSpan property value specified:
UI Customization 137 The LabelsWidth property value is always inherited from the previously declared PXLayoutRule component that has the StartRow or StartColumn property value set to True. The values for the ColumnWidth and ControlSize properties are never applied to these PXLayoutRule components. To adjust the ColumnSpan property of the UI controls, you should perform the following actions (some of which are shown in the screenshot below): 1. Open the Stock Items webpage in page design mode within the AdvUI customization project, right-click the Relative Positioning Layout tab, and then click Control Tree. 2. On the control tree in the left pane, expand the Relative Positioning Layout node and select the UsrDescrLong subnode, which corresponds to the Description (long) UI field. 3. On the Add menu, select Add Layout Rule. 4. Type 2 as the ColumnSpan property value. 5. Repeat instructions 2 through 4 for the UsrSearchWords node of the control tree, which corresponds to the Search Keywords UI field. Figure: Setting the ColumnSpan value 6. Click Apply to Page. 7. On the Customization menu, click Save Project to Database. As the screenshot below illustrates, the Description (long) and Search Keywords UI fields now span two columns.
UI Customization 138 Figure: Viewing the widened UI fields Setting Label Width and Aligning UI Elements Horizontally You can adjust the label width of an input UI element. You can also align UI elements horizontally so that UI elements placed under a selected one become arranged in the same row as it. Setting the Label Width of an Input UI Element Proceed as follows: 1. Open the Stock Items webpage in page design mode within the AdvUI customization project, right-click the Relative Positioning Layout tab, and then click Control Tree. 2. On the control tree in the left pane, expand the Relative Positioning Layout node and select the LastStdCost subnode. 3. Change the filter box above the table (far right) to the All Props value through the drop-down list. 4. Set the value of the LabelWidth property to 75px, as shown in the screenshot below.
UI Customization 139 Figure: Setting the width of the UI element's label 5. Click Apply to Page. Notice that the Last Cost label is displayed closer to its UI field, as the screenshot below illustrates, because the default label width has been decreased. Figure: Exploring the field label width 6. On the Customization menu, click Save Project to Database. Aligning UI Elements Horizontally Merging means placing UI controls so that they are horizontally aligned. You should align UI controls horizontally by merging the Extra Ship Fee input UI field with the Use On Entry check box, and the Default Postal Code input UI field with the View On Map button. To do this, you set the Merge property value to True for the corresponding PXLayoutRule components placed above the UsrExtraShipFee and UsrDefaultPostalCode subnodes. Horizontal alignment is performed for the UI controls that are placed between this and any other subsequent PXLayoutRule component; the first PXLayoutRule component discovered breaks the merging. To cancel merging for all of the following controls, you must add the PXLayoutRule component without the adjusted property value For all PXLayoutRule components that have the Merge property value set to True: The ColumnWidth property value is never applied to the UI controls if the Merge property is set to True for the same PSLayoutRule component. The values for the ControlSize and LabelsWidth properties are inherited by default from the previously declared PXLayoutRule component with the StartRow or StartColumn property values set to True. You can override these property values if necessary by specifying the ControlSize and LabelsWidth property values from the predefined list of options (see The predefined options for the ControlSize property and The predefined options for the LabelsWidth property).
UI Customization 140 To merge UI controls, you should perform the following actions (see also the screenshot below): 1. Open the Stock Items webpage in page design mode within the AdvUI customization project, right-click the Relative Positioning Layout tab, and then click Control Tree. 2. On the control tree in the left pane, expand the Relative Positioning Layout node and select the existing PXLayoutRule component placed above the UsrExtraShipFee subnode, which corresponds to the Extra Ship Fee UI field. 3. Change the filter box above the table (far right) to the Base Props value through the drop-down list, if the value differs from this. 4. Set the Merge property value to True. Figure: Setting the Merge property 5. Select the UsrDefaultPostalCode subnode, which corresponds to the Default Postal Code UI field. 6. On the Add menu, select Add Layout Rule. 7. Set the Merge property value to True. 8. Click Apply to Page. 9. On the Customization menu, click Save Project to Database. Because of the previously added PXLayoutRule component (with the StartRow property value set to True) above the UsrActive subnode, you do not need to add a PXLayoutRule component below the last merged UI controls. As the screenshot below illustrates, the two pairs of UI controls are horizontally aligned now.
UI Customization 141 Figure: Viewing the horizontally aligned UI controls Creating a Group of UI Elements and Hiding Their Labels Now you will first create a group with a caption and include the Active and Featured check boxes in this group. Organizing UI controls on a page within groups makes users' work more logical. The first section of this topic describes how to organize UI elements in groups. Every input UI control contains both a label and a user input control, except for buttons and check boxes. A check box has a label, which is displayed right of it. When you add a check box onto a page, it is automatically aligned toward other input controls within the appropriate column. As a result, the area left of a check box is empty. To hide UI control labels placed within a column, you should set the SupressLabel property value to True of the PXLayoutRule subnode. All UI control labels placed within the column are hidden and check boxes are placed without any space to the left of the input control. If needed, you can place any separate check box to the left by setting the AlignLeft property value to True, or you can set the SupressLabel property value to True for any other UI control to hide its label. The second section of this topic describes how to use the SupressLabel property. Creating a Group of UI Elements By specifying the GroupCaption property value for the corresponding PXLayoutRule components placed above the first subnode, you create the group of controls and set up the header for the group. You should also add a PXLayoutRule component with the EndGroup property value set to True below the last UI control that is included in the group. For all PXLayoutRule components with the GroupCaption property value specified: The ColumnWidth property value is never applied to the UI controls if the GroupCaption or EndGroup property is set to True for the same PSLayoutRule component. The values for the ControlSize and LabelsWidth properties are inherited by default from the previously declared PXLayoutRule component with the StartRow or StartColumn property values set to True. You can override these property values if necessary by specifying the ControlSize and LabelsWidth property values from the predefined list of options (see The predefined options for the ControlSize property and The predefined options for the LabelsWidth property). To create the group of UI controls, you should perform the following actions: 1. Open the Stock Items webpage in page design mode within the AdvUI customization project, right-click the Relative Positioning Layout tab, and then click Control Tree.
UI Customization 142 2. On the control tree in the left pane, expand the Relative Positioning Layout node and select the existing PXLayoutRule component placed above the UsrActive subnode, which corresponds to the Active check box. 3. Type Status as the GroupCaption property value. 4. Select the UsrFeatured subnode, which corresponds to the Featured check box. 5. On the Add menu, select Add Layout Rule. 6. Set the EndGroup property value to True (as shown in the following screenshot). 7. Click the Down button to place the added PXLayoutRule component under the UsrFeatured subnode. 8. Click Apply to Page. 9. On the Customization menu, click Save Project to Database. Figure: Adjusting the properties of the last UI control in the group As the screenshot below illustrates, the Active and Featured check boxes are now organized in the added Status group.
UI Customization 143 Figure: Viewing the group of UI controls SupressLabel Property In this example, you use the SupressLabel property to hide the empty area to the left of the check boxes included in the Status group. The SuppressLabel property affects all UI controls of the group that are placed under the PXLayoutRule component with the True value of this property. The SupressLabel property value must be defined for every PXLayoutRule component for the UI controls placed beneath the component and included in the same column; this property is never inherited from the previously declared property. The SupressLabel property value is never applied to PXLayoutRule components with the ColumnSpan property value specified. To supress labels for the check boxes within the same column, you should perform the following actions (shown in the following screenshot): 1. Open the Stock Items webpage in page design mode within the AdvUI customization project, right-click the Relative Positioning Layout tab, and then click Control Tree. 2. On the control tree in the left pane, expand the Relative Positioning Layout node and select the existing PXLayoutRule component placed above the UsrActive subnode, which corresponds to the Active check box. 3. Clear the Customized check box for the LabelsWidth property and set the SuppressLabel property value to True. 4. Click Apply to Page. 5. On the Customization menu, click Save Project to Database.
UI Customization 144 Figure: Setting the SuppressLabel property for the Status group of check boxes As the screenshot below illustrates, the Active and Featured check boxes have been shifted to the left. Figure: Viewing the shifted check boxes Creating Nested Input Controls Next, you need to add a panel (the PXPanel control) onto the Relative Positioning Layout tab, add a few UI controls onto the panel, place them into two columns, and hide the empty area to the left of check boxes.
UI Customization 145 After you add the PXPanel control, the system creates a node. All the aforementioned layout rules apply to this node, because it also consists of the separate set of rows and columns. Hence, every nested container control is arranged toward the parent node layout but contains the separate layout of subnodes within. Creating Nested Input Controls Proceed as follows: 1. Open the Stock Items webpage in page design mode within the AdvUI customization project, right-click the Relative Positioning Layout tab, select Advanced Controls, and then click Panel. 2. Right-click the new added PXPanel control and click Add Input Control. 3. On the Create control dialog box that appears,select PendingBasePrice as the Data field name, keep the automatically entered ControlType value, and click OK. 4. Repeat Instructions 2 and 3 to create and add onto the panel each of the following UI elements (see the screenshot below): PendingStdCost UsrUseOnEntryPndPrice UsrUseOnEntryPndCost Figure: Adding the check box onto the PXPanel container control 5. Again right-click the PXPanel control and click Control Tree. 6. On the Aspx Control Tree window that appears, perform the following actions: a. Set the filter box above the table to Ext Props, clear the Customized check box for the height property, and set the RenderStyle property to Simple, as the screenshot below illustrates.
UI Customization 146 Figure: Defining the PXPanel values b. Expand the PXPanel virtual container node and by using the Up and Down buttons, put subnodes within the PXPanel node in the following order: PendingBasePrice PendingStdCost UsrUseOnEntryPndPrice UsrUseOnEntryPndCost c. Select the PendingBasePrice subnode. On the Add menu, click Add Layout Rule. d. On the control tree, set the filter box above the table to Base Props, and then set the StartRow property to True, type S as the ControlSize property value, and type S as the LabelsWidth property value. e. Select the UsrUseOnEntryPndPrice subnode. On the Add menu, click Add Layout Rule, and set the StartColumn and SupressLabel properties to True, then type S as the ControlSize property value. f. Select the PXPanel node and click Up several times so that this node is placed under the Status group of UI elements. g. On the Add menu, click Add Layout Rule. h. Set the StartColumn property value to True, type Pending Values as the GroupCaption value, S as the ControlSize property value, and S as the LabelsWidth property value, as shown in the screenshot below.
UI Customization 147 Figure: Defining the group caption name i. Select the PXPanel node. On the Add menu, click Add Layout Rule. j. Set the EndGroup property value to True. k. Click the Down button to place the added PXLayoutRule component under the PXPanel subnode. l. Click Apply to Page. Notice the PXPanel control with the Pending Values group name and four UI elements. (See the screenshot below.) Figure: Analyzing the final view of the page 7. On the Customization menu, click Save Project to Database. Setting Widths and Sizes of UI Elements Within a created column, group, or merged set of controls, the PXLayoutRule components define sizes for every input control and its label. You can change the size of any input control or its label by defining values for the Size, Width or LabelWidth property. Both the PXDateTimeEdit and PXNumberEdit input controls have a predefined Width property value, which you cannot change by setting the ColumnWidth or ControlSize property values for the appropriate PXLayoutRule component. To change the predefined value, use the Size and Width properties of the PXDateTimeEdit or PXNumberEdit control. This topic sequentially describes how to implement each of the following properties: Common width value for multiple UI elements placed one under another Common label width value for multiple UI elements placed one under another Width value for each UI element
UI Customization 148 Size value for multiple UI elements placed one under another Setting the Column Width Value for Multiple UI Elements This example illustrates setting a column width for two check boxes from the Status group. To set the common width value, proceed as follows: 1. Open the Stock Items webpage in page design mode within the AdvUI customization project, right-click the Relative Positioning Layout tab, and then click Control Tree. 2. On the Aspx Control Tree window that appears, perform the following actions: a. On the control tree, expand the Relative Positioning Layout node and select the first Rulexx subnode above the UsrActive subnode. b. Change the filter box above the table (far right) to the Base Props value through the drop-down list, if the value differs from this. c. Clear the Customized check box for the ControlSize property and set the ColumnWidth property value to XXS, as shown in the screenshot below. Figure: Setting the column width value for two UI elements 3. Click Apply to Page. Notice the decreased common column width (within the yellow borders) of the check boxes from the Status group (as shown in the screenshot below).
UI Customization 149 Figure: Verifying the decreased column column width of the check boxes 4. On the Customization menu, click Save Project to Database. The predefined options for the ColumnWidth property include the following values: XXS (100px) XS (150px) S (200px) M (250px) XM (300px) L (350px) XL (400px) XXL (450px) You can manually specify a value for the ColumnWidth property of the PXLayoutRule by typing any value in pixels, such as 55px (this format is mandatory if you don't use abbreviations, because the property value can be defined only in pixels). Notice that the property value set for an input control have higher priority than the corresponding property of the PXLayoutRule component. Setting the Label Width Value for Multiple UI Elements This example illustrates setting the common label width for fields from the Pending Values group within the PXPanel control. To set a common label width value, proceed as follows: 1. Open the Stock Items webpage in page design mode within the AdvUI customization project, right-click the Relative Positioning Layout tab, and then click Control Tree. 2. On the Aspx Control Tree window that appears, perform the following actions: a. On the control tree, expand the Relative Positioning Layout node, expand the PXPanel subnode, and select the Rulexx subnode above the PendingBasePrice subnode. b. Change the filter box above the table (far right) to the Base Props value through the drop-down list, if the value differs from this.
UI Customization 150 c. Type 90px as the LabelsWidth property value. 3. Click Apply to Page. Notice the label widths of fields from the Pending Values group within the PXPanel control. 4. On the Customization menu, click Save Project to Database. For the LabelsWidth property, you can select one of the following values (with the most frequently used options highlighted in bold): XXS (40px) XS (70px) S (100px) SM (150px) M (200px) XM (250px) L (300px) XL (350px) XXL (400px) You can manually specify a value for the LabelsWidth property of the PXLayoutRule by typing any value in pixels, such as 55px (this format is mandatory if you don't use abbreviations, because the property value can be defined only in pixels). Notice that the property value set for an input control have higher priority than the corresponding property of the PXLayoutRule component. To override the width of an input control label, you should use the LabelWidth property of the appropriate UI element. Setting the Width of a particular UI Element You can adjust the width of a particular input UI element. Proceed as follows: 1. Open the Stock Items webpage in page design mode within the AdvUI customization project, right-click the Relative Positioning Layout tab, and then click Control Tree. 2. On the Aspx Control Tree window that appears, perform the following actions: a. On the control tree, expand the Relative Positioning Layout node and select the first Rulexx subnode above the UsrSprTitle subnode. b. Change the filter box above the table (far right) to the Base Props value through the drop-down list, if the value differs from this. c. Set the Merge property value to True, type Supervisor as the GroupCaption property, and type XS as the LabelsWidth property. d. Select the UsrSprLastName subnode; then on the Add menu, click Add Layout Rule. e. Select the UsrSprTitle subnode. f. Set the filter box above the table to Ext Props, type 84px as the width property value, set the SupressLabel property value to True, and type Empty as the Size property value. g. Select the UsrSprFirstName subnode. h. Type 300px as the width property value, and type Empty as the Size property value, as shown in the screenshot below.
UI Customization 151 Figure: Adjusting the width property of the UI element You can double-click the appropriate column header to select the required sort order in Ext Props and All Props filter modes, when too many properties are displayed. In the screenshot above, the descending order by the Customized column is set (notice the highlighted arrow at the right of the Customized column header). i. Select the UsrSprLastName subnode. On the Add menu, click Add Layout Rule. j. Change the filter box above the table to the Base Props value through the drop-down list and set the EndGroup property to True. k. Click Down to move the last added PXLayoutRule subnode under the UsrSprLastName subnode. 3. Click Apply to Page. Notice two first UI fields from the Supervisor group (see the screenshot below). 4. On the Customization menu, click Save Project to Database. Explore the result of the performed actions, as shown in the screenshot below. Figure: Checking the adjusted width values of the UI elements For the Size property, you can select one of the following values (with the most frequently used options again highlighted in bold): XXS (40px) XS (70px) S (100px) SM (150px) M (200px)
UI Customization 152 XM (250px) L (300px) XL (350px) XXL (400px) Empty Before specifying the Width property value for the selected control, you must define the Size property value for the control as Empty. Setting the Control Size Values for UI Elements This example illustrates setting the control size of a field. You can also apply this property to multiple fields. Proceed as follows: 1. Open the Stock Items webpage in page design mode within the AdvUI customization project, right-click the Relative Positioning Layout tab, and then click Control Tree. 2. On the Aspx Control Tree window that appears, perform the following actions: a. On the control tree, expand the Relative Positioning Layout node and select the Rulexx subnode above the UsrSprTitle subnode. b. Change the filter box above the table to the Base Props value through the drop-down list, if the value differs from this. c. Type XXL as the ControlSize property value. 3. Click Apply to Page. Notice the width of the Last Name UI field (see the screenshot below). 4. On the Customization menu, click Save Project to Database. The screenshot below illustrates the final view of the Relative Positioning Layout tab. Figure: Analyzing the final view of the Relative Positioning Layout tab For the ControlSize property, you can select one of the following values (with the most frequently used options again highlighted in bold): XXS (40px) XS (70px) S (100px)
UI Customization 153 SM (150px) M (200px) XM (250px) L (300px) XL (350px) XXL (400px) Adding Items Use the New Item dialog when you need to add a new item to a collection of container controls. For instance, you might want to add a PXTabItem control to an Items collection of the PXTab container control. This topic describes how to create a new tab item on an existing tab control area. Adding a New Tab Item Suppose that you need to add the Relative Positioning Layout tab item to the Stock Items webpage and set its position as the leftmost tab item. To do this, perform the following actions: 1. Navigate to Distribution > Inventory > Manage > Stock Items. 2. On the Customization menu, click Open Customization Project. 3. On the Select Working Project dialog that appears, click New and add the new project name, PriorAdvUI, to the Project Name field of the New Project dialog that appears, and then click OK. 4. On the Select Working Project dialog that appears again, select the Unpublish Existing Customization check box, if it is displayed, and click OK, as the following screenshot illustrates. (The Unpublish Existing Customization check box is being displayed on the Select Working Project dialog only when the current application instance has at least one published customization project.)
UI Customization 154 Figure: Creating a new customization project By selecting the Unpublish Existing Customization check box after adding a new customization project's name, you revoke the procedure that cancels publication of all previously published customization projects. You need this procedure to have possibility to work with a single application instance with different customization projects without any risk to meet issues related to possible incompatibility with published projects. This is important for training purposes only. After the unpublish procedure, which can take rather long time, finishes, you possibly have to select Open Customization Project again, select the created project name, and click OK to open the project. 5. On the Customization menu, select Enter Page Design Mode. (For details, see Page Design Mode.) The appearance of the webpage changes to the webpage template, which is further named simply page. 6. Right-click the General Settings tab area and select Control Tree. 7. In the Aspx Control Tree that appears, select the ItemSettings node, which corresponds to the PXTab container control. Click Add, and select Add Item. 8. In the New Item dialog that appears, keep the default value for the Collection property (Items) and the Item Type property (PXTabItem), and click OK, as the screenshot below illustrates. By default, the PXTabItem will be added as the last node among the tab item nodes, which means that at run time, the tab will be the rightmost tab.
UI Customization 155 Figure: Adding a tab item onto the tab area of the Stock Items webpage In this case, the New Item dialog enables you to add the node of the lower level, which corresponds to the PXTabItem container control. 9. For the PXTabItem node that has been added, select the Text property and enter Relative Positioning Layout to specify the name of the new tab. 10. By repeatedly clicking the Up button, move the node up to the position immediately under the ItemSettings node, as the following screenshot illustrates. Moving the node to the top of the tab item nodes will cause it to be displayed at run time as the first tab on the left. Figure: Typing the name of the added tab item and moving up its node 11. Click Apply to Page to save the UI customization changes and close the Aspx Control Tree (see the following screenshot).
UI Customization 156 Figure: Saving the UI customization changes On the Stock Items page in page design mode, notice that the new tab is displayed as the leftmost tab (as the following screenshot illustrates). On the Customization menu, click Save Project to Database. Further you can use the new Relative Positioning Layout tab item as a container for various UI input controls (see Adding Advanced Controls, and also subtopics of the PXLayoutRule Components topic). Figure: Viewing the added tab and saving the customization changes On the Customization menu, select View Customization Manager (or navigate to System > Customization > Manage > Customization Projects). Notice the added content of the Page type customization object, as shown in the following screenshot.
UI Customization 157 Figure: Analyzing the content of the customization project Adding Advanced Controls The term Advanced controls is appliable for form (PXFormView), grid (PXGrid), tab (PXTabItem), and panel (PXPanel) container controls as well as for buttons. Suppose that you need to customize the Stock Items webpage and that you have divided the customization task into the following steps: Add a new tab item onto the webpage. Add a PXFormView container control onto the tab and specify the data member for this control. Add an input UI control onto the added PXFormView container control. Add a panel onto the added PXFormView container control. Add UI controls onto the new panel. To view the original webpage, navigate to Distribution > Inventory > Manage > Stock Items (see the screenshot below).
UI Customization 158 Figure: Viewing the Stock Items webpage before making changes Add a tab, named, for instance, Relative Positioning Layout (see Adding Items), and then start the next customization step. Adding a FormView Container Control onto the Tab Item You add a PXFormView container control onto the created PXTabItem control (that is, onto the new tab) so that you can place the UI controls on the form placeholder and bind it to the specified data member. The Stock Items webpage is open: On the Customization menu, select Open Customization Project. On the Select Working Project dialog that appears, select the PriorAdvUI customization project that already exists, and click OK. The page design mode starts automatically for the previously customized webpage, which is the Stock Items webpage in this case, because its aspx code had been already modified after adding the tab item. By using the Advanced Controls designer, you should first add a PXFormView container control: In page design mode (for more information, see Page Design Mode), open the new tab, right-click the yellow area under this tab, select Advanced controls, and then select FormView, as shown in the following screenshot.
UI Customization 159 Figure: Adding a PXFormView Container Control onto the new tab item Right-click the yellow area under the Relative Positioning Layout tab and click Control Tree. In the Aspx Control Tree window that appears, expand the ItemSettings:PXTab and Relative Positioning Layout:PXTabItem nodes, select PXLayoutRule component, click the Remove button to delete this component, type the SkinID property as Transparent for the PXFormView virtual container subnode, and click Apply to Page, as the screenshot below illustrates. Figure: Adjusting property values for the FormView container control Right-click the visible part of the added form and select DataMember, as the following screenshot illustrates.
UI Customization 160 Figure: Starting to bind the added form to a data view In the Select data member dialog that appears, click the Magnifier icon; in the lookup window, select the required data view such as ItemCosts, and in the dialog, click OK, as shown in the following screenshot. Figure: Binding the new form to the ItemCosts data view Adding a UI Control onto the FormView Container Control In page design mode, right-click the added form and select Add Input Control, as the following screenshot illustrates.
UI Customization 161 Figure: Starting to add an input UI control In the Create control dialog that appears, select the Data field value (such as LastCost) and the ControlType value (such as NumberEdit) and click OK (see the screenshot below). Right-click the added UI field and click Control Tree. In the Aspx Control Tree that appears with the LastCost:PXNumberEdit subnode highlighted, click Add and select Add Layout Rule. Select the added PXLayoutRule object and set the StartRow property to True (the property filter condition must be set to Base Props), as the following screenshot illustrates. (For details, see PXLayoutRule Components.) Type S as the ControlSize property value and S as the LabelsWidth property value. Figure: Adjusting the properties of the PXLayoutRule component In the Aspx Control Tree, select the ItemCosts:PXFormView low-level container node, clear the check box that precedes the height property and then click Apply to Page, as the following screenshot illustrates. With these settings, at run time the PXFormView container control can calculate height based on the nested content size (that is, based on the sizes of the container controls and input UI controls that will be added onto the PXFormView).
UI Customization 162 Figure: Resetting the height property value for the FormView container control Adding a Panel onto the FormView Container Control By using the Advanced Controls designer, you should now add a panel onto the previously added form control. While remaining in page design mode, right-click the new form control, select Advanced controls, and then select Panel. Right-click the added panel and select Control Tree, as shown in the following screenshot. Figure: Opening the Aspx Control Tree for the added panel When the Aspx Control Tree appears for the PXPanel node, make sure the LastCost:PXNumberEdit subnode is placed above the PXPanel subnode; clear the check box for the height property, type the Caption property value as Cost Info and set the RenderStyle property value to FieldSet (the property filter condition must be set to Ext Props), and click Apply to Page, as the following screenshot illustrates. With these settings, at run time the PXPanel container control can calculate height, based on the nested content size (that is, based on the sizes of the UI controls that will be added onto the PXPanel).
UI Customization 163 Figure: Adjusting the properties of the added panel We recommend that you use this dynamic adjustment of the height panel property by resetting this property value because the panel height will depend on the sizes (heights) of the UI controls added onto the panel. As a result, the UI controls are being rationally placed on the panel, while the panel is being rationally placed on the appropriate placeholder. Adding Input UI Controls onto the New Panel This step is necessary for the check out, how the new panel that contains UI controls is displayed on the webpage. In page design mode, right-click the added panel and select Add Input Control, as the following screenshot illustrates. Figure: Starting to add input UI controls
UI Customization 164 In the Create control dialog that appears, select the Data field value (such as AvgCost) and the ControlType value (such as NumberEdit) and click OK (see the screenshot below). Figure: Adding the UI control onto the panel Create and add onto the panel each of the following UI elements: MinCost MaxCost Again right-click the PXPanel control and click Control Tree. Expand the PXPanel virtual container node and by using the Up and Down buttons, put subnodes within the PXPanel node in the following order: AvgCost MinCost MaxCost In the Aspx Control Tree, select the AvgCost:PXNumberEdit subnode, click Add and select Add Layout Rule. Select the added PXLayoutRule object and set the StartRow property to True (the property filter condition must be set to Base Props), as the following screenshot illustrates. (For details, see PXLayoutRule Components.) Type S as the ControlSize property value and S as the LabelsWidth property value. Click Apply to Page to apply your changes and close the Aspx Control Tree. Figure: Setting property values for the added PXLayoutRule object Validate and publish the project, close it, and open the new tab of the Stock Items webpage.
UI Customization 165 Now you can see at run time the Last Cost UI field added onto the PXFormView placeholder (added onto the new PXTabItem tab, named Relative Positioning Layout), and three UI controls (Average Cost, Min. Cost, and Max. Cost) added onto the panel, which had been added onto the PXFormView placeholder (see the screenshot below). Figure: Viewing the added UI controls on the panel at run time On the Customization menu, select View Customization Manager. In the lower area of the Customization Projects webpage that opens, you can see the new Page-type object, which contains the.aspx code changeset, as the following screenshot illustrates. Figure: Analyzing the added content of the customization project On the Customization menu, select View Source Code. On the Page Aspx tab of the Source Code browser, you can see the fragment of the.aspx code, which is highlighted in yellow, as the screenshot below illustrates. This changeset represents the result of the UI customization of the Stock Items webpage.
UI Customization 166 Figure: Analyzing the changeset of the.aspx code Nested Advanced Controls This topic describes and illustrates how the content of the nested advanced controls and properties of these controls affect to the structure and appearance of a webpage at run time. The term Advanced controls is appliable for form (PXFormView), grid (PXGrid), tab (PXTabItem), and panel (PXPanel) container controls as well as for buttons. To add an advanced (container) control onto the required area of a webpage, you should right-click this area and select the required item of the Advanced controls sub-menu of the context menu, which you can open in page design mode. The following cases are described, explained, illustrated, and provided by the necessary instructions you should perform: The simplest case, with one transparent PXFormView container control (with an input UI field); you will use this control (with the added input UI field) in each following case as a placeholder of the second container control. A PXFormView container control (with caption and with the FieldSet RenderStyle property); you will add this control onto the transparent PXFormView container control. A PXFormView container control (with the Simple (borderless) RenderStyle property); by the moment of considering this case, you will have added this control onto the transparent PXFormView container control while performing instructions of the previous case. A PXPanel container control (with caption and with the RoundBorder RenderStyle property); you will add this panel instead of the second PXFormView container control onto the transparent PXFormView container control. A PXPanel container control with the Simple (borderless) RenderStyle property; by the moment of considering this case, you will have added this panel onto the transparent PXFormView container control while performing instructions of the previous case. To check out, how the upper advanced control that contains UI controls is displayed on the webpage at run time, you will add three controls onto each upper advanced control.
UI Customization 167 For simplicity, you can name the PXFormView, PXPanel, and PXTabItem controls correspondingly FormView (or form), Panel (or panel), TabItem (or tab item, or simply tab). Suppose that you have added the Relative Positioning Layout tab item to the Stock Items webpage (as described in Adding Items). To obtain required results, you should carry out all the needed actions (described below) within the PriorAdvUI customization project, which already contains the aspx changeset, which corresponds to the previously created Relative Positioning Layout tab item. As you can see, the Relative Positioning Layout tab item of the Stock Items webpage will be the common placeholder, onto which you should add the advanced controls listed above. As the results, you will obtain five layouts with nested container controls, which consist of up to three layers of these controls. To consider each of the cases listed above, you will perform appropriate adjustments by using Aspx Control Tree. To view results, you will validate and publish the customization project, and then unpublish the project after viewing, make necessary changes to fit the next case, validate and publish, and review the new webpage appearance. You also will have to download and upload the current customization project to rollback to a few steps, when it will be necessary. You will repeatedly perform the most part of instructions that you have already performed in the Adding Advanced Controls topic. Adding the Transparent FormView Container Control This section includes the same instructions, as includes the first section of the Adding Advanced Controls topic. The Stock Items webpage is open: On the Customization menu, select Open Customization Project. You will add a PXFormView container control onto the previously created PXTabItem control (that is, onto the Relative Positioning Layout tab item) to place the UI controls on the form placeholder and bind it to the specified data view as a DataMember. On the Select Working Project dialog that appears, select the PriorAdvUI customization project that already exists, and click OK. (Otherwise, first create the PriorAdvUI project and perform all instructions of the Adding Items topic to add the Relative Positioning Layout tab item.) The page design mode starts automatically for the previously customized webpage, which is the Stock Items webpage in this case, because its aspx code had been already modified after adding the tab item. By using the Advanced Controls designer, you should first add a PXFormView container control: In page design mode (for more information, see Page Design Mode), open the Relative Positioning Layout tab item, right-click the yellow area under it, select Advanced controls, and then select FormView, as shown in the following screenshot.
UI Customization 168 Figure: Adding the first FormView container control onto the tab item Right-click the yellow area under the Relative Positioning Layout tab and click Control Tree. In the Aspx Control Tree window that appears, expand the ItemSettings:PXTab and Relative Positioning Layout:PXTabItem nodes, select the PXLayoutRule component, click the Remove button to delete this component, type the SkinID property as Transparent for the PXFormView virtual container subnode, and click Apply to Page, as the screenshot below illustrates. Figure: Adjusting property values for the first FormView container control Right-click the visible part of the added form and select DataMember, as the following screenshot illustrates.
UI Customization 169 Figure: Starting to bind the added form to a data view In the Select data member dialog that appears, click the Magnifier icon; in the lookup window, select the required data view such as ItemCosts, and in the dialog, click OK, as shown in the following screenshot. Figure: Binding the new form to the ItemCosts data view Adding a UI Control onto the First FormView Container Control This section includes the same instructions, as includes the second section of the Adding Advanced Controls topic. You will also download the archive copy of the current state of this customization project. In page design mode, right-click the added form and select Add Input Control, as the following screenshot illustrates.
UI Customization 170 Figure: Starting to add an input UI control In the Create control dialog that appears, select Data field (such as LastCost) and ControlType (such as NumberEdit) and click OK. Right-click the added UI field and click Control Tree. In the Aspx Control Tree that appears with the LastCost:PXNumberEdit subnode highlighted, click Add and select Add Layout Rule. Select the added PXLayoutRule object and set the StartRow property to True (the property filter condition must be set to Base Props), as the following screenshot illustrates. (For details, see PXLayoutRule Components.) Type S as the ControlSize property value and S as the LabelsWidth property value. Figure: Adjusting the properties of the PXLayoutRule component In the Aspx Control Tree, select the ItemCosts:PXFormView low-level container node, clear the check box that precedes the height property and then click Apply to Page, as the following screenshot illustrates. With these settings, at run time the PXFormView container control can calculate height based on the nested content size (that is, based on the sizes of the container controls and input UI controls that will be added onto the PXFormView).
UI Customization 171 Figure: Resetting the height property value for the first FormView container control Validate and publish the project, close it, open the Relative Positioning Layout tab of the Stock Items webpage, and check out new view of the tab. Before performing next steps, you should download the archive copy of the PriorAdvUI customization project. To download the archive copy of the current customization project, do the following: On the Customization menu, click View Customization Manager. In the Customization Projects webpage that appears, select the PriorAdvUI project name (if it is still not selected) and click the Get Package button on the form toolbar. In the Opening PriorAdvUI.zip dialog that appears, select Save File and click OK, as the following screenshot illustrates.
UI Customization 172 Figure: Downloading the archive copy of the current customization project In the Enter name of file to save to window that appears, navigate to the required folder and click Save, keeping the file name as it is by default (such as PriorAdvUI.zip file) You will need this copy later, when you start to perform actions of the Adding the Panel onto the First FormView Container Control section below. Adding the Second FormView Container Control with the Fieldset RenderStyle This section includes the instructions similar to those in the third section of the Adding Advanced Controls topic. By using the Advanced Controls designer, you should now add the second FormView container control onto the previously added FormView control. While remaining in page design mode, right-click the new form control, select Advanced controls, and then select FormView. Right-click the added form. When the Aspx Control Tree appears for the second (low-level) PXFormView node, clear the check box for the height property, type the Caption property value as Cost Info, define the DataMember name as ItemCosts, set the RenderStyle property value to Fieldset (the property filter condition must be set to Ext Props), and click Apply to Page, as the following screenshot illustrates.
UI Customization 173 Figure: Adjusting the properties of the second FormView container control Both FormView container controls are bound to the same data view (value of the DataMember property) for the training purpose only. As a rule, each container control is added onto a webpage to be bound with a different data view. Adding UI Controls onto the Second FormView Container Control This section includes the instructions similar to those in the fourth section of the Adding Advanced Controls topic. This step is necessary for the check out, how the new form that contains UI controls is displayed on the webpage. In page design mode, right-click the added form and select Add Input Control. In the Create control dialog that appears, select Data field (such as AvgCost) and ControlType (such as NumberEdit) and click OK (see the screenshot below). Figure: Adding the UI control onto the panel Create and add onto the second (low-level) form container the following UI elements: MinCost MaxCost Again right-click the added form and click Control Tree. Expand the added second form node and by using the Up and Down buttons, put subnodes within the PXFormView node in the following order: AvgCost MinCost MaxCost
UI Customization 174 In the Aspx Control Tree, select the AvgCost:PXNumberEdit subnode, click Add and select Add Layout Rule. Select the added PXLayoutRule object and set the StartRow property to True (the property filter condition must be set to Base Props), as the following screenshot illustrates. Type S as the ControlSize property value and S as the LabelsWidth property value. Click Apply to Page to apply your changes and close the Aspx Control Tree. Figure: Setting property values for the added PXLayoutRule object You have obtained two additional placeholders with input UI controls, both based on the PXFormView container controls, the first of which has the Transparent value of the SkinID property. Validate and publish the project, close it, and open the Relative Positioning Layout tab of the Stock Items webpage. Now you can see at run time the Last Cost UI field added onto the first PXFormView placeholder (which had been added onto the new PXTabItem tab, named Relative Positioning Layout), and three UI controls (Average Cost, Min. Cost, and Max. Cost) added onto the second PXFormView placeholder (which you previously added onto the first (transparent) PXFormView placeholder). See the screenshot below. Notice the view of the second form placeholder with the Fieldset value of the RenderStyle property. Figure: Viewing the added UI controls and their placeholders at run time Changing the RenderStyle Property Value for the Second FormView Container Control Again open the PriorAdvUI customization project.
UI Customization 175 In page design mode, right-click area of the second added form. When the Aspx Control Tree appears for the second (low-level) PXFormView node, clear the check box for the Caption property, change the RenderStyle property value to Simple, and click Apply to Page, as the following screenshot illustrates. Figure: Changing the value of the RenderStyle property value to Simple Validate and publish the project, close it, and open the new tab of the Stock Items webpage. Notice the view of the second form placeholder with the Simple value of the RenderStyle property and compare this view with the view that was before you changed this property value: The second PXFormView container control became borderless. Figure: Viewing the second borderless placeholders at run time Adding the Panel onto the First FormView Container Control Before performing instructions of this section, you should upload the archive copy of the PriorAdvUI customization project, downloaded after performing instructions of the Adding a UI Control onto the First FormView Container Control section above, to rollback to the state, when existed only one added container control with the LastCost input UI control. In How to update the content of the current customization project or to rollback to its previous state given instructions for completing an update or rollback of a current customization project. After uploading the archive copy of the project, do the following: By using the Advanced Controls designer, you should now add a panel onto the previously added form control.
UI Customization 176 Open the project and in page design mode, right-click the first FormView container control, select Advanced controls, and then select Panel. Right-click the added panel and select Control Tree, as shown in the following screenshot. Figure: Opening the Aspx Control Tree for the added panel When the Aspx Control Tree appears for the PXPanel node, make sure the LastCost:PXNumberEdit subnode is placed above the PXPanel subnode; clear the check box for the height property, type the Caption property value as Cost Info, set the RenderStyle property value to RoundBorder (the property filter condition must be set to Ext Props), and click Apply to Page, as the following screenshot illustrates. With these settings, at run time the PXPanel container control can calculate height, based on the nested content size (that is, based on the sizes of the UI controls that will be added onto the PXPanel). Figure: Adjusting the properties of the added panel We recommend that you use this dynamic adjustment of the height panel property by resetting this property value because the panel height will depend on the sizes (heights) of the input UI controls added onto the panel.
UI Customization 177 Adding Input UI Controls onto the New Panel This section includes the same instructions as in the fourth section of the Adding Advanced Controls topic. This step is necessary for the check out, how the new panel that contains UI controls is displayed on the webpage. In page design mode, right-click the added panel and select Add Input Control, as the following screenshot illustrates. Figure: Starting to add input UI controls In the Create control dialog that appears, select Data field (such as AvgCost) and ControlType (such as NumberEdit), and click OK (see the screenshot below). Create and add onto the panel each of the MinCost and MaxCost UI elements. Again right-click the PXPanel control and click Control Tree. Expand the PXPanel virtual container node and by using the Up and Down buttons, put subnodes within the PXPanel node in the following order: AvgCost MinCost MaxCost In the Aspx Control Tree, select the AvgCost:PXNumberEdit subnode, click Add and select Add Layout Rule. Select the added PXLayoutRule object and set the StartRow property to True (the property filter condition must be set to Base Props), as the following screenshot illustrates. (For details, see PXLayoutRule Components.) Type S as the ControlSize property value and S as the LabelsWidth property value. Click Apply to Page to apply your changes and close the Aspx Control Tree.
UI Customization 178 Figure: Setting property values for the added PXLayoutRule object Validate and publish the project, close it, and open the new tab of the Stock Items webpage. Now you can see at run time the Last Cost UI field added onto the PXFormView placeholder (added onto the new PXTabItem tab, named Relative Positioning Layout), and three UI controls (Average Cost, Min. Cost, and Max. Cost) added onto the panel, which had been added onto the PXFormView placeholder (see the screenshot below). Figure: Viewing the added UI controls on the panel at run time Changing the RenderStyle Property Value for the Panel Again open the PriorAdvUI customization project. In page design mode, right-click area of the panel. When the Aspx Control Tree appears for the PXPanel node, clear the check box for the Caption property and change the RenderStyle property value to Simple, and click Apply to Page, as the following screenshot illustrates.
UI Customization 179 Figure: Changing the value of the RenderStyle property to Simple Validate and publish the project, close it, and open the Relative Positioning Layout tab of the Stock Items webpage. Notice the view of the panel placeholder with the Simple value of the RenderStyle property and compare this view with the view that was before you have changed this property value: The PXPanel container control became borderless. Figure: Viewing the borderless panel at run time
Acumatica Extensibility Framework In Detail 180 Acumatica Extensibility Framework In Detail Acumatica Extensibility Framework (AEF) is an integral part of the Acumatica Framework. It can be used to extend both data access classes (DACs) and business logic controllers (BLCs). In this part of the training, you will learn how to create and modify DAC and BLC extensions of different levels by using AEF. You will create a new extension project and fill it with simple examples that illustrate the basic tasks related to AEF. Once you finish this part, you will proceed to the practice part, which explores more complex samples of AEF usage based on specific business cases. This part of the training consists of the following tasks and theory topics: AEF Extensibility Generic Classes Creating an Extension Library Solution by Using the Bespoke Approach DAC Extensions Extension Tables BLC Extensions Accessing Customization Objects Prerequisites First you should unpublish previous customization projects, if any, and then publish the IntroAEF customization project. You should publish the IntroAEF project created earlier or uploaded from the Acumatica University portal to perform instructions in this part of examples devoted to the AEF technology. To unpublish existing projects (which you developed and published before starting to perform instructions in examples devoted to the AEF technology), you should revoke the undo publish procedure that cancels publication of all previously published customization projects. You need this procedure to have possibility to work with a single application instance with different customization projects without any risk to meet issues related to possible incompatibility with published projects. This is important for training purposes only. 1. Navigate to System > Customization > Process > Publish Customization. If at least one customization project is published, click Undo Website Customization, as the screenshot below illustrates. Figure: Starting the Undo Website Customization procedure
Acumatica Extensibility Framework In Detail 181 The second column of the Publish Cistomization webpage represents the Published check box. If this check box is selected, the appropriate customization project is published. You shouldn't use the undo publish procedure, if no project is published. 2. Select the check box left of the IntroAEF project and click Publish Selected Projects to validate and publish the project. If you need, to upload the IntroAEF deployment package, that is attached to the training materials, proceed as follows: 1. Navigate to System > Customization > Manage > Customization Projects and on the form area of the Customization Projects webpage, click From File (see item 1 in the following screenshot). 2. In the Open Package dialog that appears, click Browse (item 2). 3. In the File Upload window that appears, select the required deployment zip package file (such as IntroAEF.zip, shown as item 3). 4. Click Open (item 4) to return to the Open Package dialog. 5. In the Open Package dialog, click Upload (item 5) to upload the deployment package. Figure: Uploading the IntroAEF deployment package by using the Customization Projects webpage 6. Click Validate and publish on the form toolbar of the Customization Projects webpage; after the validation process finishes successfully, click Publish. To update content of the existing IntroAEF customization project, proceed as follows: 1. Navigate to System > Customization > Manage > Customization Projects, and select the IntroAEF project. 2. Click Edit XML. 3. In the window with the project name that appears, click Browse. 4. In the File Upload dialog box that appears, select the IntroAEF deployment package, and then click Open.
Acumatica Extensibility Framework In Detail 182 5. In the window with the IntroAEF project name, click Upload Package and then Save to database to update the content of the project. 6. Validate and publish the updated IntroAEF project. All the customization examples in the topics beneath this topic are described in relation to the IntroAEF customization project. AEF Extensibility Generic Classes To make it possible to declare extensions, the Acumatica Extensibility Framework (AEF) provides the PXCacheExtension<T> and PXGraphExtension<T> generic classes (defined in the PX.Data assembly). To declare a data access class (DAC) extension, you derive a class from PXCacheExtension<T>, as shown in the following example. public class BaseDACExtension : PXCacheExtension<BaseDAC>... To declare an extension of a business logic controller (BLC, also referred to as graph), you derive a class from PXGraphExtension<T>, as shown in the following example. public class BaseBLCExtension : PXGraphExtension<BaseBLC>... You should first create an extension library solution, which will contain extensions of DACs and BLCs; for details, see Creating an Extension Library Solution by Using the Bespoke Approach. The runtime automatically discovers extensions during the first base class initialization. At run time, the base class is replaced with the merged result of the base class and every extension discovered. The auto-discovery mechanism does not guarantee the sequence in which extensions are loaded and alter the base (original) class. Although you can implement as many extensions as you need, you may not modify the same member of a class in extensions of the same level. The higher-level extension has the higher priority during the merge operation, so if two or more extensions of the same level alter the same member of a class, the last loaded one will receive higher priority. Because the system does not contain any configurable mechanism to control the load sequence, you must avoid such conflicts. The figure below illustrates the DAC extension schema. Figure: DAC extension levels in AEF The figure below illustrates the BLC extension schema.
Acumatica Extensibility Framework In Detail 183 Figure: BLC extension levels in AEF AEF is designed for the development of vertical and add-on solutions, although it can be used to customize the end real production application. Multi-level extensions are required when you develop offthe-shelf software that is distributed in several editions, or applications that extend the functionality of Acumatica ERP or other software based on Acumatica Framework in multiple markets. You may have a base extension that contains the solution common to all markets as well as multiple market-specific extensions. Every market-specific solution is deployed along with the base extension. Moreover, you can later customize deployed extensions for the end user by using Acumatica Customization Engine (ACE) tools and facilities or DAC and BLC extensions. In cases when extensions can be deployed separately, the application developer should use multiple extension levels. Otherwise, we recommend using a single extension level. Creating an Extension Library Solution by Using the Bespoke Approach To create data access class (DAC) and business logic controller (BLC, also referred to as a graph) extensions, you should first create an extension library solution that will contain extensions of DACs and BLCs. Before you create an extension library solution, you must choose the approach that best fits your customization task: A customization project based on the preconfigured extension library solution provided by Acumatica Framework A manually created extension library solution (bespoke approach) The ways to create an extension library solution are similar to the ways to prepare an add-on solution. (For details, see Creating an Add-On Solution.) In Creating an Extension Library Solution By Using the Templated Approach the first approach is described. With the bespoke approach, you first create an extension library solution; you then create the customization project and include the extension library file in the content of this project. The creation of a customization project is necessary to prepare a deployment package. In both cases, you use the standard interface of MS Visual Studio for developing an extension library solution, as if you were developing a standalone solution. You can also use auxiliary customization facilities to make some application modifications. Don't forget to include these modifications in the content of the customization project. The section below describes the actions that you have to perform if you use the bespoke approach to create and develop an extension library solution.
Acumatica Extensibility Framework In Detail 184 Creating the Extension Solution If you are using the bespoke approach listed above, you manually create an extension library solution. To manually create an extension library solution for an Acumatica ERP application, perform the following actions: 1. Start MS Visual Studio, and create the new AdvAEF solution. 2. Manually add new extension library project references to the PX.Common.dll, PX.Data.dll, and PX.Objects.dll assemblies, which are located in the Bin folder of the customization application instance. In this particular case you should add reference to the IntroAEF.dll assembly too, which is also located in the Bin folder of the customization application instance. 3. Manually add the reference to the created extension library project from the website. (See MS Visual Studio documentation.) 4. Build the project and ensure that the compiled assembly has been automatically copied to the Bin folder of the website., as the following screenshot illustrates. Figure: Exploring the structure of the new project 5. When you finish the development of the extension library solution, start the Acumatica ERP application. 6. Navigate to System > Customization > Manage > Customization Projects. 7. On the Customization Projects webpage, click New. 8. On the New Project dialog that appears, type your customization project name and click OK. 9. Click the Add button and select File from File System. 10. On the Add Files pop-up window that appears, select the check box left of the created extension library file name you need to add to the project, and click Save, as the screenshot below illustrates.
Acumatica Extensibility Framework In Detail 185 Figure: Adding the extension library file to the customization project DAC Extensions When you need to declare a new data field or modify the behavior that is defined within an existing data access class (DAC), you should either declare a data field or override field attributes within the DAC extension. To do this, you first create an extension library solution that will contain DAC extensions (see Creating an Extension Library Solution By Using the Templated Approach). This part of the training consists of the following tasks and theory topics: Customizing DAC Attributes Creating a DAC Extension of an Extension DAC Extension Model Customizing DAC Attributes To modify the behavior that is defined within a data access class (DAC), you should override the field attributes within the DAC extension. Suppose that you have a customization task to modify the Invoices webpage, and that this task consists of the following requirements: The Customer Ref. Nbr. data field on the form area (see the screenshot below) must have the None value when the user has not specified the reference number of the customer. The values of the Discount column must be limited to a range of -30 to 25 percent instead of the original range of -100 to 100 percent.
Acumatica Extensibility Framework In Detail 186 Figure: Viewing the original webpage To begin implementing the requirements, perform the following actions: 1. Start MS Visual Studio and open the AdvAEF solution created in Creating an Extension Library Solution by Using the Bespoke Approach. 2. Right-click the AdvAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type ARInvoiceExtension as the Name, and click Add. 4. Modify the added file code as follows. using PX.Data; using PX.Objects.AR; namespace AdvAEF public class ARInvoiceExtension : PXCacheExtension<ARInvoice> #region InvoiceNbr [PXDBString(40, IsUnicode = true)] [PXUIField(DisplayName = "Customer Ref. Nbr.", Visibility = PXUIVisibility.SelectorVisible, Required = false)] [PXDefault("None")] public string InvoiceNbr get; set; #endregion To modify DAC field attributes, you should always declare the same named field within a DAC extension. When altering field attributes within DAC extension, you completely redeclare the attributes attached to the field. You do not need to declare an abstract class when you're overriding DAC field attributes within DAC extensions. 5. Again right-click the AdvAEF project caption, select Add, and click New Item. 6. On the Add New Item window that appears, select the Class template, type ARTranExtension as the Name, and click Add.
Acumatica Extensibility Framework In Detail 187 7. Modify the added file code as follows. using PX.Data; using PX.Objects.AR; using System; namespace AdvAEF public class ARTranExtension : PXCacheExtension<ARTran> #region DiscPct [PXDBDecimal(6, MinValue = -30, MaxValue = 25)] [PXUIField(DisplayName = "Discount")] [PXDefault(TypeCode.Decimal, "0.0")] public decimal? DiscPct get; set; #endregion 8. Build the AdvAEF project. 9. Start or open the Acumatica ERP application. If you are not sure that no previously published customization project (or projects) already exists, navigate to System > Customization > Process > Publish Customization and if necessary, perform undo publish procedure, as described in Prerequisites. 10. Navigate to System > Customization > Manage > Customization Projects, select the AdvAEF customization project, and click the Check in Files button to update the project with this modified extension library file. To test the results of the customization, open the Invoices webpage (through Distribution > Sales Orders > Work Area > Enter), insert a new document, and add a record to the details table, as shown in the screenshot below. Notice the following changes: In the Customer Ref. Nbr. field, the None value now appears by default. If you try to add a Discount value that is greater than 25 percent, as the first screenshot below illustrates, the system replaces the entered value with 25 (see the second screenshot below). Figure: Analyzing customization changes
Acumatica Extensibility Framework In Detail 188 Figure: Watching the result when you try to add a too-high percent value Similarly, if you try to enter a value that is less than -30 percent, the system retrieves the resulting percent (-30) according to the restriction of the DiscPct data field. On the Customization menu, select View Customization Manager to open the Customization Projects webpage. In the lower window, view the content of one object of the File type with the customization changes, as the screenshot below illustrates. Figure: Analyzing the content of the customization project with the added attribute Creating a DAC Extension of an Extension To modify the behavior that is defined within a data access class (DAC) extension, you should create an extension of an existing DAC extension. This extension will be a second-level extension. In this task, you will change the display name of the UsrLocalTaxCategoryID field declared within the InventoryItemExtension DAC extension from Local Tax Category to Tax Category (Local) and restrict the list of tax categories to show only active categories. For this purpose, you will create an extension of the InventoryItemExtension DAC extension and override the attributes of the UsrLocalTaxCategoryID field. Navigate to Distribution > Inventory > Work Area > Manage > Stock Items webpage of the Acumatica ERP application. The screenshot below shows the Stock Items webpage with the previously added Local Tax Category UI field.
Acumatica Extensibility Framework In Detail 189 Figure: Viewing the Stock Items webpage with the previously added Local Tax Category UI field Perform the following actions: 1. Start MS Visual Studio and open the AdvAEF solution created in Creating an Extension Library Solution by Using the Bespoke Approach. 2. Right-click the AdvAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type InventoryItemExtensionOnExtension as the Name, and click Add. 4. Modify the added file code as follows. using using using using IntroAEF; PX.Data; PX.Objects.IN; PX.Objects.TX; namespace AdvAEF public class InventoryItemExtensionOnExtension : PXCacheExtension<InventoryItemExtension, InventoryItem> #region UsrLocalTaxCategoryID [PXDBString(10, IsUnicode = true)] [PXUIField(DisplayName = "Tax Category (Local)")] [PXSelector( typeof(search<taxcategory.taxcategoryid, Where<TaxCategory.active, Equal<True>>>), DescriptionField = typeof(taxcategory.descr))] public string UsrLocalTaxCategoryID get; set; #endregion You should specify an existing DAC extension as the first type parameter and the base (original) DAC as the second type parameter. You should always declare the same named field within a DAC extension to alter its attributes. You do not need to redeclare an abstract class used in BQL statements. When altering field attributes within a DAC extension, you completely redeclare the attributes attached to the field. In this code sample, you derive the extension of an extension from PXCacheExtension, which has two type parameters. You set the first one to the first-level extension class and the second one to the base DAC. In the extension of an extension, you define the UsrLocalTaxCategoryID
Acumatica Extensibility Framework In Detail 190 data field. To change the display name for the field in the user interface, you set the DisplayName property value of the PXUIFieldAttribute to Tax Category (Local) and attach the PXSelectorAttribute with a BQL statement containing the Where clause to restrict the list of available tax categories to choose from. 5. Build the AdvAEF project. 6. Start or open the application, navigate to System > Customization > Manage > Customization Projects, select the AdvAEF customization project, and click the Check in Files button to update the project with this modified extension library file. You can observe the result of customization by navigating to Distribution > Inventory > Work Area > Manage > Stock Items. On the General Settings tab; make sure that the Local Tax Category selector added to the tab table has been renamed to Tax Category (Local), as the following screenshot illustrates. Click the Magnifier icon right of this field and explore the selector window, which this time should contain only active Tax Category ID columns. Figure: Exploring the added selector field DAC Extension Model This topic explores the ways provided by Acumatica Extensibility Framework (AEF) to define data access class (DAC) extensions of different levels and shows how different DAC extensions can interact. First-Level DAC Extension The example below shows the declaration of a first-level DAC extension. class BaseDACExtension : PXCacheExtension<BaseDAC> public void SomeMethod() BaseDAC dac = Base; The extension class includes the read-only Base property, which returns an instance of the base DAC.
Acumatica Extensibility Framework In Detail 191 Second-Level DAC Extension The example below shows the declaration of a second-level DAC extension. class BaseDACExtensionOnExtension : PXCacheExtension<BaseDACExtension, BaseDAC> public void SomeMethod() BaseDAC dac = Base; BaseDACExtension dacext = Base1; The extension class includes: The read-only Base property, which returns the instance of the base DAC. The read-only Base1 property, which returns the instance of the first-level DAC extension. Two Variants of a Higher-Level DAC Extension A definition of an extension of a higher level has two possible forms. In the first one, you derive the extension class from the PXCacheExtension generic class with two type parameters, where the first type parameter is set to an extension of the previous level. In the second one, you derive the extension class from the PXCacheExtension generic class with the same number of type parameters as the level of the extension of the new class. In this case, you set type parameters to extension classes from all lower extension levels, from the previous level down to the base class. First Variant of a Higher-Level DAC Extension The example below shows the declaration of a third- or higher-level DAC extension that derives from the PXCacheExtension generic class with two type parameters. class BaseDACMultiExtensionOnExtension : PXCacheExtension<BaseDACExtensionOnExtension, BaseDAC> public void SomeMethod() BaseDAC dac = Base; BaseDACExtensionOnExtension dacextonext = Base1; An extension class defined this way includes the following: The read-only Base property, which returns the instance of the base DAC. The read-only Base1 property, which returns the instance of the instance of the DAC extension from the previous level. Second Variant of a Higher-Level DAC Extension The example below shows a declaration of a third- or higher-level DAC extension that derives from the PXCacheExtension generic class with three or more type parameters. class BaseDACAdvMultiExtensionOnExtension : PXCacheExtension<BaseDACExtensionOnExtension, BaseDACExtension, BaseDAC> public void SomeMethod() BaseDAC dac = Base; BaseDACExtension dacext = Base1;
Acumatica Extensibility Framework In Detail 192 BaseDACExtensionOnExtension dacextonext = Base2; An extension class defined in this way includes the following: The read-only Base property, which returns the instance of the base DAC. The read-only BaseN properties for all extension levels below the current level, where N is the sequence number of an extension level. Attributes Hierarchy of a DAC Field Conceptually, a DAC extension is a substitution of the base DAC. The base DAC is replaced at run time with the merged result of the base DAC and every extension discovered. Each extension on another extension completely overrides the DAC field attributes. The attributes declared on the highest-level extension override all previously declared attributes for the DAC field of the same name. For example, suppose that the attributes of the same DAC field are defined on different extension levels as follows: In the base DAC, as shown below. [PXUIField(DisplayName = "Base Name")] [PXDefault("Default base value")] In the first-level DAC extension, as the following code shows. [PXUIField(DisplayName = "Level1 Name", Visible = false)] In the second-level DAC extension, as shown below. [PXUIField(DisplayName = "Level2 Name")] [PXDefault("Default 2nd level value")] If all the extensions are applied, the particular field of the second-level DAC extension will have the following properties specified by attributes: Display name: Level2 Name Visible: true Default value: Default 2nd level value You may not modify the same member of the base DAC in the DAC extensions of the same level. You should use DAC extensions of different levels that gradually extend one another. DAC Field Read and Write Operations The Acumatica Framework can access the value of a DAC field only through an instance of the base DAC. The instance of the DAC extension is used to get the field value only when the field is declared in the extension and is not present in the base DAC. To assign a value to a field, the application developer should use the instance of the base DAC. You must use the instance of a DAC extension to assign a field value when the field is declared within the extension and is not present in the base DAC.
Acumatica Extensibility Framework In Detail 193 Extension Tables When you need to extend a data access class (DAC) with a new database-bound field, you can add a column to the original database table and declare the field in the extension of the base (original) DAC. In some cases, you may need to store new field values in a separate table or use this table independently from the original database table. You can use the Acumatica Extensibility Framework to create a separate extension database table. This table must include all key fields from the original database table and your new fields. You should create a DAC extension that holds the new fields and is mapped to the extension table. The Acumatica Data Access Layer will automatically synchronize the extension table with the original database table by expanding every create, retrieve, update, and delete operation on the base (original) DAC to each discovered DAC extension that is mapped to an extension table. This part of the training consists of the following task and theory topic: Using an Extension Table Extension Table Declaration Using an Extension Table To declare a data access class (DAC) extension mapped to an extension table, you should first create extension table in the database and then declare the DAC extension mapped to the created table. By using PXTableAttribute, you specify that the DAC extension is mapped to the same named extension table. In this task, you will create an extension table for the InventoryItem database table and then declare the same-named DAC extension, which is mapped to the extension table and contains the ExtTableDescr field. The created DAC extension won't be used independently from the base InventoryItem class. This means that the base and extended tables are not necessarily synchronized. Notice the mandatory CompanyID database field that is used for the multy-tenant support. This field is used by the system, and no need to add this field to a DAC. Also pay attention to the DeletedDatabaseRecord database field, which is not to be visible for users and is necessary for the system monitoring of each existing and deleted record. Follow the instructions below: 1. Create the InventoryItemTableExtension database table by executing the following script in the database of the Acumatica ERP application. CREATE TABLE [dbo].[inventoryitemtableextension] ( [CompanyID] [int] NOT NULL, [InventoryID] [int] NOT NULL, [DeletedDatabaseRecord] [bit] NOT NULL, [ExtTableDescr] [nvarchar](256) NULL CONSTRAINT [InventoryItemTableExtension_PK] PRIMARY KEY CLUSTERED ( [CompanyID] ASC, [InventoryID] ASC ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] ALTER TABLE [dbo].[inventoryitemtableextension] ADD DEFAULT ((0)) FOR [DeletedDatabaseRecord] GO
Acumatica Extensibility Framework In Detail 194 2. Start MS Visual Studio and open the AdvAEF solution created in Creating an Extension Library Solution by Using the Bespoke Approach. 3. Right-click the AdvAEF project caption, select Add, and click New Item. 4. On the Add New Item window that appears, select the Class template, type InventoryItemTableExtension as the Name, and click Add. 5. Modify the added file code as follows. using PX.Data; using PX.Objects.IN; namespace AdvAEF [PXTable(typeof(InventoryItem.inventoryID), IsOptional = true)] public class InventoryItemTableExtension : PXCacheExtension<InventoryItem> #region ExtTableDescr public abstract class exttabledescr : PX.Data.IBqlField [PXDBString(255)] [PXDefault("Additional description")] [PXUIField(DisplayName = "Ext_Table Description")] public string ExtTableDescr get; set; #endregion The definition of InventoryItemTableExtension is annotated with the PXTable attribute, which maps the DAC extension to the extension table. The DAC extension defines the ExtTableDescr field. About the key fields of the extension table see Extension Table Declaration. 6. Build the AdvAEF project. 7. Start or open the Acumatica ERP application. 8. Navigate to System > Customization > Manage > Customization Projects and select the AdvAEF customization project. 9. Select Add and click Database Table or Script. 10. On the Edit Sql Script dialog that appears, select InventoryItemTableExtension as the DBObject Name, select the Import Table Schema from Database check box, and click Save, as the screenshot below illustrates.
Acumatica Extensibility Framework In Detail 195 Figure: Adding the SQL script that creates the user database table to the content of the current customization project 11. Navigate to Distribution > Inventory > Work Area > Manage > Stock Items, open the AdvAEF customization project, and enter page design mode. 12. On the Stock Items webpage, right-click the header form area and click Add Input control. 13. On the Create control dialog that appears, select ExtTableDescr as the Data field, keep TextEdit as the ControlType, and click OK, as shown in the following screenshot. Figure: Adding the input UI field that is bound with the user database table 14. Through the Aspx Control Tree, by using the PXLayoutRule component, place the ExtTableDescr field input control to span two columns, as the following screenshot illustrates. (For details on using the properties of the PXLayoutRule component, see PXLayoutRule Components.)
Acumatica Extensibility Framework In Detail 196 Figure: Adjusting the ColumnSpan property for the added input UI field 15. Click Validate and Publish; if the validation process finishes successfully, publish the customization project by clicking Publish. If you changed the extension library file before validating the customization project, the validation process displays warning message that some files have been changed. In this case open the Check-in Files window with the Conflict check box selected. If you want to update the project with this modified file, you should click Check in Files to update the project, close the Check-in Files window, and then click Validate and Publish again. 16. After publishing completes, close the customization project, open the Stock Items webpage; make sure that the Ext_Table Description UI field is displayed on the header form area, as the screenshot below illustrates. Figure: Exploring the added UI input field For the new Ext_Table Description input UI field, you defined PXDefaultAttribute (Additional description), as you can see in the screenshot above. After the first saving, a new record with
Acumatica Extensibility Framework In Detail 197 this value is inserted to InventoryItemTableExtension. This value will be updated when the user changes the added extension description. When the user deletes a record from the original InventoryItem database table, the system automatically deletes the appropriate record from the InventoryItemTableExtension extension table. On the Customization menu, select View Customization Manager. Among the three listed objects, you can see the object of the File type that contains the extension library file (whose content is shown in the first screenshot below), the object of the Page type that contains the.aspx code changeset (see the second screenshot below), and the object of the Sql type that contains the database table script (see the third screenshot below). Figure: Analyzing the content of the File type Figure: Analyzing the changeset of the.aspx page code
Acumatica Extensibility Framework In Detail 198 Figure: Analyzing the added SQL content of the customization project related to the extension table Extension Table Declaration If you need to extend a data access class (DAC) with a new database-bound field, you can add a column to the original database table and declare the field in the extension of the base (original) DAC. In some cases, you may need to store new field values in a separate table or use this table independently from the original database table. You can use the Acumatica Extensibility Framework to create a separate extension database table. This table must include all key fields from the original database table and your new fields. You should create a DAC extension that holds the new fields and is mapped to the extension table. The Acumatica Data Access Layer will automatically synchronize the extension table with the original database table by expanding every create, retrieve, update, and delete operation on the base (original) DAC to each discovered DAC extension that is mapped to an extension table. To declare a DAC extension that is mapped to an extension table, you should first create an extension table in the database and then declare a DAC extension mapped to the created table. By using PXTableAttribute, you specify that the DAC extension is mapped to the extension table with the same name. For details on adding extension tables to a customization project, see Using an Extension Table. Requirements for Extension Table Columns You should ensure that the following requirements are met when you create an extension table: The extension table must satisfy the following conditions: The table should have the same set of key columns as the original database table has. The table must include the following columns if they are declared within the original database table: [CompanyID][int] NOT NULL [DeletedDatabaseRecord][bit] NOT NULL For an extension table that can be used separately, you should also declare the following audit columns: [tstamp][timestamp] NULL
Acumatica Extensibility Framework In Detail 199 [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 The example below shows the declaration of the InventoryItemTableExtension extension table. Notice that we are not going to use this table independently from the original database table. CREATE TABLE [dbo].[inventoryitemtableextension] ( [CompanyID] [int] NOT NULL, [InventoryID] [int] NOT NULL, [DeletedDatabaseRecord] [bit] NOT NULL, [ExtTableDescr] [nvarchar](256) NULL CONSTRAINT [InventoryItemTableExtension_PK] PRIMARY KEY CLUSTERED ( [CompanyID] ASC, [InventoryID] ASC ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] ALTER TABLE [dbo].[inventoryitemtableextension] ADD DEFAULT ((0)) FOR [DeletedDatabaseRecord] GO Ways to Define a DAC Extension Mapped to an Extension Table If the DAC contains surrogate and natural keys, then the PXTable attribute attached to the DAC extension should reference the surrogate key as well as other database key fields (but not the natural key). If the DAC doesn't have surrogate and natural keys, no key fields should be specified in the PXTable attribute. See below the code example of declaration of the PXTable attribute with key references. [PXTable(typeof(BaseDAC.surrogateKey), typeof(basedac.otherdbkeyfield), IsOptional = value)] public class TableExtension : PXCacheExtension<BaseDAC>... The natural key is a user-friendly value that is not used as a key in the database. The surrogate key, which is the internal value corresponding to the natural key, is not shown to the user and is assigned by the system. When you use a natural key, the DAC field that serves as a surrogate key is bound to the database key column, but is not marked as key within its attributes. In the sample code shown below, the Location database table contains both the surrogate LocationID key and the natural LocationCD key. The Location database table key contains the BAccountID and LocationID columns. Because the LocationСD is a natural key, we need to specify the corresponding
Acumatica Extensibility Framework In Detail 200 surrogate key, LocationID, as well as the other database key field, BAccountID, in the PXTable attribute. [PXTable(typeof(Location.locationID), typeof(location.baccountid))] public class LocationTableExtension : PXCacheExtension<Location>... The Left Outer Join Way You can define a DAC extension mapped to an extension table by using either the default Inner Join way or the optional Left Outer Join way. To specify which way to use, you need to set the value for the IsOptional property of the PXTable attribute. By default, the system sets this property value to false. You need to specify the IsOptional property value only if you need to set its value to true. [PXTable(IsOptional = value)] public class TableExtension : PXCacheExtension<BaseDAC>... The following example shows the declaration of a DAC extension mapped to an extension table by using the Left Outer Join way. Notice that the IsOptional parameter of the PXTable attribute is set to true. [PXTable(IsOptional = true)] class LeftJoinTableExtension : PXCacheExtension<BaseDAC> The Left Outer Join way covers the common steps required to add an extension tables to a customization project. The Left Outer Join way: Can be used when the original and extension database tables are not necessarily synchronized. Causes an extension table record to be created when the appropriate original table record is created or updated. Never excludes an original database table record from the result set. Calculates the default field values if no extension table record is found. Can be used as a standalone DAC. The following example shows the declaration of the InventoryItemTableExtension DAC extension mapped to the extension table with the Left Outer Join way. [PXTable(typeof(InventoryItem.inventoryID), IsOptional = true)] public class InventoryItemTableExtension : PXCacheExtension<InventoryItem> #region ExtTableDescr public abstract class exttabledescr : PX.Data.IBqlField [PXDBString(255)] [PXDefault("Additional description")] [PXUIField(DisplayName = "Ext_Table Description")] public string ExtTableDescr get; set; #endregion Suppose that you have added the corresponding Ext_Table Description UI field to the header area of the Stock Items webpage. If you navigate to Distribution > Inventory > Work Area > Manage >
Acumatica Extensibility Framework In Detail 201 Stock Items, by clicking navigation buttons on the form toolbar of the Stock Items webpage, you can ensure that all the stock items are visible, while the Ext_Table Description UI field has the default Additional description value set, as the screenshot below illustrates. Figure: Exploring the behavior of the Stock Items webpage If you update a data record (by changing the value of any UI field, including Ext_Table Description), a new database record is added to the extension table, as the two following screenshots illustrate. Figure: Entering and saving the description in the new UI field
Acumatica Extensibility Framework In Detail 202 Figure: Analyzing the record added to the new database table When you use the Left Outer Join way, data records within the original and extension tables are not necessarily synchronized (as the figure below illustrates); data record synchronization works as follows: If an appropriate data record does not exist within an extension table when the system queries the original table, the system automatically generates and assigns default values to every field of the DAC extension that is mapped to the extension table; otherwise, DAC extension field values are read from the database. When a new record is inserted into the original table, the system automatically inserts a new record into the extension table. If a data record does not exist within an extension table when the system updates the original table, the system automatically inserts a data record into the extension table. Otherwise, if there are no modified fields of the DAC extension that is mapped to the extension table, the system does not update the extension table data record. When the user deletes the data record in the original table, the system automatically deletes the appropriate data record from the extension table, if such a record exists. To use an extension table independently from the original database table, you should declare a data view using a DAC extension that is mapped to an extension table as a main DAC, as shown below. public class BaseBLCExt : PXGraphExtension<BaseBLC> public PXSelect<TableExtension> Objects; In the example of the data view declaration above, extension table data records have no reference to the original database table records. You can work with these data records just as you would work with any other DAC instance.
Acumatica Extensibility Framework In Detail 203 Figure: Exploring data record synchronization when the Left Outer Join way is used The Inner Join Way The Inner Join is the default way. The following example shows the declaration of a DAC extension mapped to an extension table with the default Inner Join way. [PXTable] class InnerJoinTableExtension : PXCacheExtension<BaseDAC> The Inner Join way: Can be used only when the original table and extension table are always synchronized. Causes an extension table record to be automatically created only when the appropriate original database table record is created. Requires the key column values to be copied from the original database table to each of its extension table. Excludes an original database table record from the result set when no corresponding extension table record is found. Can be used as a standalone DAC. The sample code below shows the declaration of the InventoryItemTableExtension DAC extension, which is mapped to the extension table by using the default Inner Join way.
Acumatica Extensibility Framework In Detail 204 [PXTable(typeof(InventoryItem.inventoryID))] public class InventoryItemTableExtension : PXCacheExtension<InventoryItem> #region ExtTableDescr public abstract class exttabledescr : PX.Data.IBqlField [PXDBString(255)] [PXDefault("Additional description")] [PXUIField(DisplayName = "Ext_Table Description")] public string ExtTableDescr get; set; #endregion If you again navigate to Distribution > Inventory > Work Area > Manage > Stock Items after redefining the extension code as shown above, by clicking navigation buttons on the form toolbar of the Stock Items webpage, you will see that only one stock item is visible, and it has the modified Ext_Table Description, as the following screenshot illustrates. Figure: Viewing the single record in the InventoryID lookup window To have access to the other database table records, you need to populate the extension table with the appropriate records. You can do this by using the following script. INSERT INTO [dbo].[inventoryitemtableextension] SELECT CompanyID, InventoryID, 0, N'Additional description' FROM [dbo].[inventoryitem] WHERE NOT EXISTS ( SELECT * FROM [dbo].[inventoryitemtableextension] AS t WHERE t.companyid = [dbo].[inventoryitem].companyid AND t.inventoryid = [dbo].[inventoryitem].inventoryid ) GO To add a custom script to manipulate data or create table indexes and views (see the screenshot below), you should select the DBObject Name and add (or paste from your clipboard) the SQL script, which will be executed every time the customization project is published, into the Custom Script text area. You can add a script to any database table or view, including tables whose schema had been imported to the
Acumatica Extensibility Framework In Detail 205 customization project. You should always add a script specifying the name of the related database table or view. We strongly recommend that you add a script to manipulate data or perform database structure changes separately for every database table or view. Figure: Adding the SQL script to the related table After you copy data records from the InventoryItem database table to the InventoryItemTableExtension user extension table, you will notice that all stock items are visible again. When you use the Inner Join way, the data records within the base and extended tables must always be synchronized (see the screenshot below). With this way, data record synchronization works as follows: If an appropriate data record does not exist within an extension table when the system queries the original table, the system excludes original table record from the result set. When a new record is inserted into the original table, the system automatically inserts a new record into the extension table. When it updates the data record in the original table, the system does not update the extension table if there are no modified fields of the DAC extension that is mapped to the extension table. When it deletes the original table data record, the system automatically deletes the appropriate data record from the extension table, if such a record exists. To use the extension table independently from the original database table, you should declare a data view by using a DAC extension, which is mapped to an extension table, as the main DAC (for details, see the The Left Outer Join Way section).
Acumatica Extensibility Framework In Detail 206 Figure: Exploring data record synchronization when you use the Inner Join way BLC Extensions Functional customization means various kinds of modifications of the data structure and original application business logic. All these modifications are related to the code of the appropriate data access classes (DACs) or business logic controllers (BLCs, also referred to as graphs). The Acumatica Extensibility Framework (AEF) is an integral part of the Acumatica platform designed primarily for independent software vendors (ISVs) that develop vertical and add-on solutions on top of Acumatica ERP or other Acumatica Framework-based applications. Also, value-added resellers (VARs) can use AEF for implementing customization. When you need to declare an action or a data view, extend business logic controller (BLC) initialization or modify the application business logic defined within an existing BLC, you should create a BLC extension. To do this, you first create an extension library solution that will contain BLC extensions. The runtime automatically discovers extensions during the first base class initialization. At run time, the base class is replaced with the merged result of the base class and every extension discovered. This part of the training consists of the following tasks and theory topics: Modifying a BLC Data View Declaring or Altering a BLC Data View Delegate Extending BLC Initialization Creating a BLC Extension of an Extension BLC Extension Model Adding or Altering BLC Event Handlers AEF Event Handler Model
Acumatica Extensibility Framework In Detail 207 Altering BLC Virtual Methods Modifying a BLC Data View A data view is a PXSelect BQL expression declared in a business logic controller (BLC) for accessing and manipulating data. A data view may contain a delegate that is used to extend data access. To modify the data view, you should redeclare the appropriate PXSelect BQL expression. The Acumatica Extensibility Framework(AEF) provides a way to alter a data view and declare or alter a delegate of a data view within a BLC extension. (For details, see Declaring or Altering a BLC Data View Delegate.) Suppose that you need to filter the rows on the Vendor Details tab of the Stock Items webpage. To resolve this customization task, you should redeclare the VendorItems data view within the extension on the InventoryItemMaint BLC. Open the original Stock Items webpage and find an item that has multiple vendor records on the Vendor Details tab, one or more of which has a Lot Size column value that equals 0.00, as the screenshot below illustrates. Suppose that you must filter records with this Lot Size value. Figure: Viewing the original Stock Items webpage, which shows records with a Lot Size value of 0.00 Your task is to filter the data to display only vendors for which the Lot Size column value is greater than 0.00. To redeclare a data view, proceed as follows: 1. Start MS Visual Studio and open the AdvAEF solution created in Creating an Extension Library Solution by Using the Bespoke Approach. 2. Right-click the AdvAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type InventoryItemMaintExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using using using using using using PX.Data; PX.Objects.AP; PX.Objects.CR; PX.Objects.CS; PX.Objects.IN; PX.Objects.PO;
Acumatica Extensibility Framework In Detail 208 namespace AdvAEF public class InventoryItemMaintExtension : PXGraphExtension<InventoryItemMaint> public POVendorInventory.Select<POVendorInventory, LeftJoin<InventoryItem, On<InventoryItem.inventoryID, Equal<POVendorInventory.inventoryID>>, LeftJoin<Vendor, On<Vendor.bAccountID, Equal<POVendorInventory.vendorID>>, LeftJoin<Location, On<Location.bAccountID, Equal<POVendorInventory.vendorID>, And<Location.locationID, Equal<POVendorInventory.vendorLocationID>>>>>>, Where<POVendorInventory.inventoryID, Equal<Current <InventoryItem.inventoryID>>,And<POVendorInventory.lotSize, Greater<decimal0>>>, InventoryItem> VendorItems; A data view redeclared within a BLC extension completely replaces the base data view within the Views collection of a BLC instance, including all attributes attached to the data view declared within the base BLC. You can either attach the same set of attributes to the data view or completely redeclare the attributes. For details, see BLC Extension Model. 5. Build the AdvAEF project. 6. Start or open the application, navigate to System > Customization > Manage > Customization Projects, and click the Check in Files button to update the customization project with this modified extension library file. To test the results of the customization, open the Stock Items webpage, select the same inventory item, and notice that the records are now filtered, as the following screenshot illustrates. Figure: Exploring the customization results Declaring or Altering a BLC Data View Delegate The Acumatica Extensibility Framework (AEF) gives you the ability to declare or alter a delegate of a data view declared within a business logic controller (BLC). The data view invokes the delegate while retrieving records. In this task, you will declare a delegate for the itemsiterecords data view, which is declared in the InventoryItemMaint BLC. You will alter the extension on the InventoryItemMaint BLC, created in Modifying a BLC Data View and declare the itemsiterecords() delegate.
Acumatica Extensibility Framework In Detail 209 If you open the Warehouse Details tab of the Stock Items webpage, you can see its original view, as the screenshot below illustrates. Figure: Viewing the original Stock Items webpage To declare a delegate for a data view, proceed as follows: 1. Start MS Visual Studio and open the AdvAEF solution created in Creating an Extension Library Solution by Using the Bespoke Approach. 2. Modify the InventoryItemMaintExtension.cs file code as follows. using using using using using using using System.Collections; PX.Data; PX.Objects.AP; PX.Objects.CR; PX.Objects.CS; PX.Objects.IN; PX.Objects.PO; namespace AdvAEF public class InventoryItemMaintExtension : PXGraphExtension<InventoryItemMaint>... public PXSelectJoin<INItemSite, InnerJoin<INSite, On<INSite.siteID, Equal<INItemSite.siteID>>, LeftJoin<INSiteStatusSummary, On<INSiteStatusSummary.inventoryID, Equal<INItemSite.inventoryID>, And<INSiteStatusSummary.siteID, Equal<INItemSite.siteID>>>>>> itemsiterecords; protected IEnumerable itemsiterecords() int? dfltreceiptlocationid = null; foreach (var res in Base.itemsiterecords.Select()) INItemSite site = (INItemSite)res; if (site.dfltreceiptlocationid!= null && site.isdefault == true) dfltreceiptlocationid = site.dfltreceiptlocationid; else if (site.dfltreceiptlocationid == null && dfltreceiptlocationid!= null) site.dfltreceiptlocationid = dfltreceiptlocationid; yield return res;
Acumatica Extensibility Framework In Detail 210 When you declare or alter a data view delegate within a BLC extension, the new delegate is attached to the corresponding data view. To query a data view declared within the base BLC or lower-level extension from the data view delegate, you should redeclare the data view within the BLC extension. You do not need to redeclare a generic PXSelect<Table> data member when it will not be used from the data view delegate. For details, see BLC Extension Model. The override data view delegate must have exactly the same signature the return value, the name of the method, and any method parameters as the overridden base data view delegate. 3. Build the AdvAEF project. 4. Start or open the application, navigate to System > Customization > Manage > Customization Projects, and click the Check in Files button to update the project with this modified extension library file. To test the results of the customization, open the Stock Items webpage and notice that all rows of the Default Receipt To column now have the same value that the row with the default warehouse does, as the following screenshot illustrates. This means that the DfltReceiptLocation values have been copied from the record with the active Default property. Figure: Exploring the customization results Extending BLC Initialization You usually change field layout settings, configure the functionality of a processing webpage, or fetch some additional preferences during business logic controller (BLC, also referred to as a graph) initialization. The Acumatica Extensibility Framework (AEF) provides a way to extend BLC initialization by creating a BLC extension. You extend the initialization process by overriding the Initialize() method of the PXGraphExtension<T> class. During BLC initialization, the system calls the Initialize() methods of all extension levels, from the lowest to the highest. In this task, you will create an extension of the InventoryItemMaintExtension BLC, which overrides the Initialize() method to retrieve the INSetup data record and change the display name of the InventoryItem data access class (DAC) field from Inventory ID to Inventory Item ID during the initialization of the base (original) BLC instance. To resolve the customization task, follow the instructions below: 1. Start MS Visual Studio and open the solution created in Creating an Extension Library Solution by Using the Bespoke Approach.
Acumatica Extensibility Framework In Detail 211 2. Modify the InventoryItemMaintExtension file code as follows. using using using using using using using System.Collections; PX.Data; PX.Objects.AP; PX.Objects.CR; PX.Objects.CS; PX.Objects.IN; PX.Objects.PO; namespace AdvAEF public class InventoryItemMaintExtension : PXGraphExtension<InventoryItemMaint>... public override void Initialize() INSetup setup = Base.insetup.Current; if (setup.updategl!= true) // Do some actions here PXUIFieldAttribute.SetDisplayName<InventoryItem.inventoryCD>( Base.Item.Cache, "Inventory Item ID"); In this method, you obtain the INSetup DAC instance by using the Current property of the INSetup data view. You may need to perform some actions that depend on field values of this setup DAC instance. Also, you change the display name of the InventoryCD field of the InventoryItem DAC. You do this by calling the SetDisplayName() static method of PXUIFieldAttribute. You do not need to explicitly invoke the Initialize() method on the previous extension levels; these methods are called automatically. Invoking base.initialize() makes no sense, because the base variable points to the base class, which is PXGraphExtension (not the base graph). The PXGraphExtension class defines Initialize() as an empty method. Declaring constructors within a BLC extension is not acceptable and may lead to unpredictable errors at run time. 3. Build the AdvAEF project. 4. Start or open the application, navigate to System > Customization > Manage > Customization Projects, and click the Check in Files button to update the project with this modified extension library file. You can check the result by navigating to Distribution > Inventory > Work Area > Manage > Stock Items. The display name of the lookup field that selects InventoryItem instances should now be Inventory Item ID (as shown on the following screenshot).
Acumatica Extensibility Framework In Detail 212 Figure: Viewing the modified name of the Inventory ID field Creating a BLC Extension of an Extension To override a delegate of an action altered within a business logic controller (BLC, also referred to as a graph) extension, you should create a new extension of the existing extension. The new extension will be a second-level BLC extension. In this task, you will override the delegate of the action declared within the INReceiptEntry BLC and altered within the INReceiptEntryExtension BLC extension. (The display name of the action was changed from Release to Extended Release; for details, see Modifying a BLC Action.) To override the action delegate, proceed as follows: 1. Start MS Visual Studio and open the solution created in Creating an Extension Library Solution by Using the Bespoke Approach. 2. Right-click the AdvAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type INReceiptEntryExtensionOnExtension as the Name, and click Add. 4. Modify the added file code as follows. using using using using System.Collections; IntroAEF; PX.Data; PX.Objects.IN; namespace AdvAEF public class INReceiptEntryExtensionOnExtension : PXGraphExtension<INReceiptEntryExtension, INReceiptEntry> [PXUIField(DisplayName = "Extended Release", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update)] [PXProcessButton] protected IEnumerable Release(PXAdapter adapter) if (Base.receipt.Ask("Confirm release", "Are you sure?", MessageButtons.YesNo) == WebDialogResult.Yes) // The actual processing logic could be placed here. // In this example, a message is displayed to the user. throw new PXException("Extended Release has been invoked."); else return adapter.get();
Acumatica Extensibility Framework In Detail 213 You should always override an action delegate to alter either its delegate or the attributes attached to the action. To use an action declared within the base BLC or the lower-level extension from the action delegate, you should redeclare a generic PXAction<TNode> data member within a BLC extension. You do not need to redeclare an action that is not meant to be used from the action delegate. (For details, see BLC Extension Model.) The override action delegate must have exactly the same signature that is, the return value, the name of the method, and any method parameters as the overridden base action delegate. This code overrides the release action delegate and displays a message asking the user to confirm the action. This message is displayed when the user clicks the Extended Release action button. If the user confirms the action (that is, the Ask() method returns WebDialogResult.Yes), the action logic is executed. Here the action delegate only shows another message box that indicates that the processing has started. Note that when the user clicks Yes or No in the dialog displayed after the Ask() method, the action delegate is invoked once more. During the first invocation of the action delegate, the execution was interrupted on the Ask() method. This time, the execution continues, and the Ask() method returns the value indicating the user's choice. 5. Build the AdvAEF project. 6. Start or open the application, navigate to System > Customization > Manage > Customization Projects, and click the Check in Files button to update the project with this modified extension library file. You can observe the result by navigating to Distribution > Inventory > Work Area > Enter > Receipts. Create a new receipt, add multiple transaction detail records, and click Extended Release. You will be asked to confirm the action (see the screenshot below). Click Yes. Figure: Viewing the message shown after clicking the Extended Release button BLC Extension Model This topic explores the ways provided by the Acumatica Extensibility Framework (AEF) to define business logic controller (BLC, also referred to as a graph) extensions of different levels, and shows how different BLC extensions can interact.
Acumatica Extensibility Framework In Detail 214 First-Level BLC Extension The example below shows the declaration of a first-level BLC extension. class BaseBLCExtension : PXGraphExtension<BaseBLC> public void SomeMethod() BaseBLC baseblc = Base; The extension class includes the read-only Base property, which returns an instance of the base BLC. Second-Level BLC Extension The example below shows declaration of a second-level BLC extension. class BaseBLCExtensionOnExtension : PXGraphExtension<BaseBLCExtension, BaseBLC> public void SomeMethod() BaseBLC baseblc = Base; BaseBLCExtension ext = Base1; The extension class includes: The read-only Base property, which returns the instance of the base BLC. The read-only Base1 property, which returns the instance of the first-level BLC extension. Two Variants of a Higher-Level BLC Extension A definition of a higher-level BLC extension has two possible forms. In the first one, you derive the extension class from the PXGraphExtension generic class with two type parameters, where the first type parameter is set to an extension of the previous level. In the second one, you derive the extension class from the PXGraphExtension generic class with the same number of type parameters as the level of the extension of the new class. In this case, you set type parameters to extension classes from all lower extension levels, from the previous level down to the base class. First Variant of a Higher-Level BLC Extension The example below shows a declaration of a third- or higher-level BLC extension that is derived from the PXGraphExtension generic class with two type parameters. class BaseBLCMultiExtensionOnExtension : PXGraphExtension<BaseBLCExtensionOnExtension, BaseBLC> public void SomeMethod() BaseBLC BLC = Base; BaseBLCExtensionOnExtension prevext = Base1; An extension class defined in this way includes the following: The read-only Base property, which returns the instance of the base BLC.
Acumatica Extensibility Framework In Detail 215 The read-only Base1 property, which returns the instance of the instance of the BLC extension from the previous level. Second Variant of a Higher-Level BLC Extension The example below shows a declaration of a third- or higher-level BLC extension that is derived from the PXGraphExtension generic class with three or more type parameters. class BaseBLCAdvMultiExtensionOnExtension : PXGraphExtension<BaseBLCExtensionOnExtension, BaseBLCExtension, BaseBLC> public void SomeMethod() BaseBLC BLC = Base; BaseBLCExtension ext = Base1; BaseBLCExtensionOnExtension extonext = Base2; An extension class defined in this way includes the following: The read-only Base property, which returns the instance of the base BLC. The read-only BaseN properties for all extension levels below the current level, where N is the sequence number of an extension level. Extensions of Data Views and Actions Conceptually, a BLC extension is a substitution of the base BLC. The base BLC is replaced at run time with the merged result of the base BLC and every extension discovered. The higher level of a declaration an extension has, the higher priority it obtains in the merge operation. AEF provides a way to alter or extend both the data views and the actions defined in a BLC. A data view is a PXSelect BQL expression declared in a BLC for accessing and manipulating data. A data view may contain a delegate that is used to extend data access. Every data view is represented by the PXView object and placed in the Views collection of the appropriate BLC. To construct an instance of the PXView class, you use a PXSelect BQL expression and an optional delegate from the highest-level extension discovered. An action is a BLC member of the PXAction type. An action always has the delegate defined. Every action is represented by the PXAction object and placed in the Actions collection of the appropriate BLC. To construct an instance of the PXAction class, you use a BLC member of the PXAction type and a delegate from the highest-level extension discovered. Suppose that you have declared the Objects data view and the ValidateObjects action within the base BLC, as shown below. public class BaseBLC : PXGraph<BaseBLC, DAC> public PXSelect<DAC> Objects; public PXAction<DAC> ValidateObjects; [PXButton] [PXUIField(DisplayName = "Validate Objects")] protected virtual void validateobjects() You often alter data views within BLC extensions in the following ways:
Acumatica Extensibility Framework In Detail 216 By altering a data view within a BLC extension. A data view redeclared within a BLC extension replaces the base data view in the Views collection of the BLC instance. Consider the following example of a first-level BLC extension. public class BaseBLCExt : PXGraphExtension<BaseBLC> public PXSelectOrderBy<DAC, OrderBy<Asc<DAC.field>>> Objects; The Views collection of a BLC instance contains the PXView object that uses the Objects data view declared within the first-level extension, instead of the data view declared within the base BLC. A data view redeclared within a BLC extension completely replaces the base data view within the Views collection of a BLC instance, including all attributes attached to the data view declared within the base BLC. You can either attach the same set of attributes to the data view or completely redeclare the attributes. By declaring or altering the data view delegate in a BLC extension. The new delegate is attached to the corresponding data view. Consider the following example of a second-level BLC extension. public class BaseBLCExtOnExt : PXGraphExtension<BaseBLCExt, BaseBLC> protected IEnumerable objects() return PXSelect<DAC>.Select(Base); The Views collection of a BLC instance contains the PXView object that uses the Objects data view declared within the first-level extension, with the objects() delegate declared within the second-level extension (see the screenshot below). Figure: Analyzing the interaction among the levels of a BLC extension To query a data view declared within the base BLC or a lower-level extension from the data view delegate, you should redeclare the data view within a BLC extension. You do not need to
Acumatica Extensibility Framework In Detail 217 redeclare a data member when it is not meant to be used from the data view delegate. Consider the following example of a second-level BLC extension. public class BaseBLCExtOnExt : PXGraphExtension<BaseBLCExt, BaseBLC> protected IEnumerable objects() return Base.Objects.Select(); The new delegate queries the data view declared within the base BLC. Having redeclared the data view within the first-level extension, you prevent the data view execution from infinite loops (see the following screenshot). Figure: The view of the base BLC from the second-level delegate If a data view declared within the base BLC contains a delegate, this delegate also gets invoked when the data view is queried from the new delegate (see the following screenshot). Figure: The view with the delegate of the base BLC from the second-level delegate You often alter actions within BLC extensions in the following ways: By overriding action attributes within a BLC extension.
Acumatica Extensibility Framework In Detail 218 To override action attributes in a BLC extension, you should declare both the BLC member of the PXAction type and the delegate. You should attach a new set of attributes to the action delegate, declared within the BLC extension. Also, you need to invoke the Press method on the base BLC action. Having redeclared the member of PXAction<>, you prevent the action delegate execution from infinite loops. Consider the following example of a first-level BLC extension. public class BaseBLCExt : PXGraphExtension<BaseBLC> public PXAction<DAC> ValidateObjects; [PXButton] [PXUIField(DisplayName = "Validate All Objects")] protected void validateobjects() Base.ValidateObjects.Press(); The Actions collection of a BLC instance contains the ValidateObjects action, which consists of the PXAction type member and the delegate, both of which were declared within the first-level extension (see the following figure). When overriding an action delegate within a BLC extension, you completely redeclare the attributes attached to the action. Every action delegate must have PXButtonAttribute (or the derived attribute) and PXUIFieldAttribute attached. Figure: The interaction among the first-level and base actions By overriding the action delegate within the BLC extension. The new delegate is used by the appropriate action. Consider the following example of a secondlevel BLC extension. public class BaseBLCExtOnExt : PXGraphExtension<BaseBLCExt, BaseBLC> [PXButton] [PXUIField(DisplayName = "Validate Objects")] protected void validateobjects() The Actions collection of a BLC instance contains the ValidateObjects action, which consists of the PXAction<> type member declared within the first-level extension and the delegate declared within the second-level extension. To use an action declared within the base BLC or the lowerlevel extension from the action delegate, you should redeclare the action within a BLC extension. You do not need to redeclare an action when it is not meant to be used from the action delegate.
Acumatica Extensibility Framework In Detail 219 Figure: Analyzing the case when you don't need to redeclare an action To modify the same member of the base BLC or any BLC extension, you should use extensions of higher levels. Adding or Altering BLC Event Handlers In this customization task, you will learn how to use the Acumatica Extensibility Framework (AEF) to define event handlers within business logic controller (BLCs, also referred to as graphs) extensions. See also Extending Business Logic. Adding an Event Handler to the Base BLC Event Handler Collection When you define an event handler as it is defined in the base (original) BLC, this handler is added to the appropriate event handler collection. Depending on the event type, the event handler is added to either the end of the collection or the start of it. When the event occurs, all event handlers in the collection are executed, from the first one to the last one. For details, see AEF Event Handler Model. In this task, you will add the RowUpdating event handler. The handler will implement validation for the TaxCategoryID and UsrLocalTaxCategoryID fields (the UsrLocalTaxCategoryID field was declared within a DAC extension; for details, see Adding a Selector onto the Form Area of a Webpage). To resolve the customization task, follow the instructions below: 1. Start MS Visual Studio and open the solution created in Creating an Extension Library Solution by Using the Bespoke Approach. 2. Modify the InventoryItemMaintExtension file code as follows. using using using using using using using using System.Collections; IntroAEF; PX.Data; PX.Objects.AP; PX.Objects.CR; PX.Objects.CS; PX.Objects.IN; PX.Objects.PO; namespace AdvAEF public class InventoryItemMaintExtension : PXGraphExtension<InventoryItemMaint>... protected virtual void InventoryItem_RowUpdating(PXCache sender, PXRowUpdatingEventArgs e)
Acumatica Extensibility Framework In Detail 220 InventoryItem row = e.newrow as InventoryItem; InventoryItemExtension rowext = sender.getextension<inventoryitemextension>(row); InventoryItem originalrow = e.row as InventoryItem; if (row.taxcategoryid!= null && rowext.usrlocaltaxcategoryid!= null && row.taxcategoryid == rowext.usrlocaltaxcategoryid) sender.raiseexceptionhandling<inventoryitemextension. usrlocaltaxcategoryid>( row,rowext.usrlocaltaxcategoryid, new PXSetPropertyException<InventoryItemExtension. usrlocaltaxcategoryid>( "Tax category and local tax category should differ", PXErrorLevel.Warning)); The event handler checks whether the TaxCategoryID and UsrLocalTaxCategoryID field values are not null and do not equal each other. If these conditions are not satisfied, the handler issues the warning, which will be shown on the UsrLocalTaxCategoryID field. With the current configuration, the event will occur when you attempt to save the InventoryItem record. Note how the extension field is accessed by invocation of the GetExtension() method on the cache. 3. Build the AdvAEF project. 4. Start or open the application, navigate to System > Customization > Manage > Customization Projects, and click the Check in Files button to update the project with this modified extension library file. You can observe the result by navigating to Distribution > Inventory > Work Area > Manage > Stock Items. Select an existing record, and set the Tax Category and Tax Category (Local) fields to the same value. If you try to save the changes, you will see the message box with the warning. You can also see the warning message by pointing to the warning sign next to the field (as shown in the screenshot below). Figure: Analyzing the warning message
Acumatica Extensibility Framework In Detail 221 Declaring an Event with an Additional Parameter The event handler with an additional parameter replaces the base BLC event handler collection. When the event is raised, the system calls the event handler with an additional parameter of the highest-level BLC extension. The system passes a link to the event handler with an additional parameter from the extension of the previous level, if such an event handler exists, or to the first item in the event handler collection (also described in AEF Event Handler Model). For example, to override the InventoryItem_LotSerClassID_FieldVerifying(PXCache, PXFieldVerifyingEventArgs) event handler defined within the InventoryItemMaint BLC, you should declare the InventoryItem_LotSerClassID_FieldVerifying(PXCache, PXFieldVerifyingEventArgs, PXFieldVerifying) event handler within the extension of the InventoryItemMaint BLC. Follow the instructions below: 1. Start MS Visual Studio and open the solution created in Creating an Extension Library Solution by Using the Bespoke Approach. 2. Right-click the AdvAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type InventoryItemMaintExtensionOnExtension as the Name, and click Add. 4. Modify the new added file code as follows. using PX.Data; using PX.Objects.IN; namespace AdvAEF public class InventoryItemMaintExtensionOnExtension : PXGraphExtension<InventoryItemMaintExtension, InventoryItemMaint> protected void InventoryItem_LotSerClassID_FieldVerifying( PXCache sender, PXFieldVerifyingEventArgs e, PXFieldVerifying del) // Event handler body before the base BLC event handler collection if (del!= null) del(sender, e); // Event handler body after the base BLC event handler collection You have created the extension of the second level, as this event handler had been already modified in the IntroAEF.dll. (See also Extending Business Logic.) Notice the three parameters within the event handler declaration. When the event is raised, the system calls the event handler, passing a link to the event handler with an additional parameter of the first level BLC extension. You can execute del() to invoke the event handler to which del points, or you can decide not to invoke it. 5. Build the AdvAEF project. 6. Navigate to System > Customization > Manage > Customization Projects, and click the Check in Files button to update the project with this modified extension library file. AEF Event Handler Model The business logic associated with data modifications is implemented through event handlers. Event handlers are methods that are executed when the PXCache objects of a particular data access class (DAC) raise data manipulation events.
Acumatica Extensibility Framework In Detail 222 Every business logic controller (BLC, also referred to as a graph) instance has a collection of event handlers for each type of data manipulation event. Every collection is filled automatically with event subscribers that are declared within the base (original) BLC and that meet Acumatica Framework event handler naming conventions. With the Acumatica Extensibility Framework (AEF), you can define new event handlers within BLC extensions. You can define an event handler in two possible ways: You define the event handler in the same way as it is defined in the base BLC. As a result, the event handler is added to the appropriate event handler collection. Depending on the event type, the event handler is added to either the end of the collection or the start of it. When the event occurs, all event handlers in the collection are executed, from the first to the last one. You define the event handler with an additional parameter, which represents the delegate for one of the following: The event handler with an additional parameter from the previous level extension, if such an event handler exists. The first item in the collection of event handlers, if no handlers with additional parameters declared within lower-level extensions exist. The collection contains event handlers without the additional parameter from extensions discovered at all levels. In either case, you can decide whether to invoke the delegate. The CacheAttached(PXCache sender) event handler declared in the highest-level BLC extension is used to replace base DAC field attributes. Attributes attached to the CacheAttached event handlers within the base BLC or its extensions are attached to the PXCache object, each time completely replacing previous ones starting from the base BLC to the highest extension discovered. Event Handler Added to the End of the Collection The following event handlers are added to the end of the collection: FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e) RowSelecting(PXCache sender, PXRowSelectingEventArgs e) RowSelected(PXCache sender, PXRowSelectedEventArgs e) RowInserted(PXCache sender, PXRowInsertedEventArgs e) RowUpdated(PXCache sender, PXRowUpdatedEventArgs e) RowDeleted(PXCache sender, PXRowDeletedEventArgs e) RowPersisted(PXCache sender, PXRowPersistedEventArgs e) The system executes event handlers from the base (original) event handler up to the highest extension level (referred to as the bubbling strategy). The lower the BLC extension's level of declaration, the earlier the event subscriber is called. The figure below illustrates this principle.
Acumatica Extensibility Framework In Detail 223 Figure: Exploring the event execution schema by using the bubbling strategy Event Handlers Added to the Beginning of the Collection The following event handlers are added to the beginning of the collection: FieldSelecting(PXCache sender, PXFieldSelectingEventArgs e) FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e) FieldUpdating(PXCache sender, PXFieldUpdatingEventArgs e) FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e) RowInserting(PXCache sender, PXRowInsertingEventArgs e) RowUpdating(PXCache sender, PXRowUpdatingEventArgs e) RowDeleting(PXCache sender, PXRowDeletingEventArgs e) RowPersisting(PXCache sender, PXRowPersistingEventArgs e) CommandPreparing(PXCache sender, PXCommandPreparingEventArgs e) ExceptionHandling(PXCache sender, PXExceptionHandlingEventArgs e) Event handlers are added by the system to the beginning of the collection by using the tunneling strategy. The system executes event handlers from the highest extension level down to the base event handler. The higher the BLC extension's level of declaration, the earlier the event subscriber is called. The figure below illustrates this principle.
Acumatica Extensibility Framework In Detail 224 Figure: Exploring the event execution schema by using the tunneling strategy The system executes event handlers from the highest extension level down to the base event handler. The higher the BLC extension's level of declaration, the earlier the event subscriber is called. The figure below illustrates this principle. Event Handlers with an Additional Parameter The event handler with an additional parameter replaces the base BLC event handler collection. When the event is raised, the system calls the event handler with an additional parameter of the highest-level BLC extension. The system passes a link to the event handler with an additional parameter from the extension of the previous level, if such an event handler exists, or to the first item in the event handler collection. You use a delegate as an additional parameter to encapsulate the appropriate event handler. The Acumatica Framework provides the following delegates to encapsulate event handlers: PXFieldSelecting(PXCache sender, PXFieldSelectingEventArgs e) PXFieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e) PXFieldUpdating(PXCache sender, PXFieldUpdatingEventArgs e) PXFieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e) PXFieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e) PXRowSelecting(PXCache sender, PXRowSelectingEventArgs e) PXRowSelected(PXCache sender, PXRowSelectedEventArgs e) PXRowInserting(PXCache sender, PXRowInsertingEventArgs e) PXRowInserted(PXCache sender, PXRowInsertedEventArgs e) PXRowUpdating(PXCache sender, PXRowUpdatingEventArgs e) PXRowUpdated(PXCache sender, PXRowUpdatedEventArgs e) PXRowDeleting(PXCache sender, PXRowDeletingEventArgs e) PXRowDeleted(PXCache sender, PXRowDeletedEventArgs e) PXRowPersisting(PXCache sender, PXRowPersistingEventArgs e) PXRowPersisted(PXCache sender, PXRowPersistedEventArgs e) PXCommandPreparing(PXCache sender, PXCommandPreparingEventArgs e) PXExceptionHandling(PXCache sender, PXExceptionHandlingEventArgs e)
Acumatica Extensibility Framework In Detail 225 For example, for the FieldVerifying event, the event handler with an additional parameter looks like FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e, PXFieldVerifying del). You can execute del() to invoke the event handler to which del points, or you can decide not to invoke it. When del() points to the base BLC event handler collection, its invocation causes the execution of the whole collection. All other event handlers in the collection are invoked sequentially after the first handler is executed. Suppose that you have declared event handlers as follows. public class BaseBLC : PXGraph<BaseBLC, DAC> protected void DAC_RowUpdated(PXCache cache, PXRowUpdatedEventArgs e) protected void DAC_Field_FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e) public class BaseBLCExt : PXGraphExtension<BaseBLC> protected void DAC_RowUpdated(PXCache cache, PXRowUpdatedEventArgs e) protected void DAC_Field_FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e) protected void DAC_RowUpdated(PXCache cache, PXRowUpdatedEventArgs e, PXRowUpdated del) if (del!= null) del(sender, e); protected void DAC_Field_FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e, PXFieldVerifying del) if (del!= null) del(sender, e); public class BaseBLCExtOnExt : PXGraphExtension<BaseBLCExt, BaseBLC> protected void DAC_RowUpdated(PXCache cache, PXRowUpdatedEventArgs e) protected void DAC_Field_FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e) protected void DAC_RowUpdated(PXCache cache, PXRowUpdatedEventArgs e, PXRowUpdated del)
Acumatica Extensibility Framework In Detail 226 if (del!= null) del(sender, e); protected void DAC_Field_FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e, PXFieldVerifying del) if (del!= null) del(sender, e); In this case, the RowUpdated and FieldVerifying event handlers are invoked in the appropriate sequences, explained below. The following figure illustrates the order of the RowUpdated event handler execution. Figure: Exploring the order of the RowUpdated event handler execution The following figure illustrates the order in which the FieldVerifying events are invoked. Figure: Exploring the order of the FieldVerifying event handler invocation Altering BLC Virtual Methods The Acumatica Extensibility Framework gives you a way to override virtual methods within a business logic controller (BLC, also referred to as a graph) extension. As with the event handlers, you have two options: You can define the override method with exactly the same signature that is, the return value, the name of the method, and all method parameters as the overridden base virtual method. As a result, the method is added to the queue of all override methods. When the system invokes the base method, all methods in the queue are executed sequentially, from the first to the last one. The lower the level the BLC extension has, the earlier the system invokes the override method. You can define the override method with an additional parameter, which represents the delegate for one of the following:
Acumatica Extensibility Framework In Detail 227 The override method with an additional parameter from the extension of the previous level, if such a method exists. The base virtual method, if no override methods with additional parameters declared within lower-level extensions exist. In both cases, you should attach the PXOverrideAttribute to the override method declared within the BLC extension. Method Added to the Override Method Queue The first option means extending a virtual method declared in a BLC. Conceptually, the base BLC method is replaced at run time by the queue of methods that starts with the base BLC method. Each method calls the next method declared in a BLC extension of a higher extension level. For example, to extend the PrepareItems(string, IEnumerable) virtual method declared in the JournalWithSubEntry graph, you should create an extension and declare the PrepareItems(string, IEnumerable) method in it, attaching the PXOverride attribute to this method, as follows. public class JournalWithSubEntryExtension : PXGraphExtension<JournalWithSubEntry> [PXOverride] public void PrepareItems(string viewname, IEnumerable items) // Method body Override Method with an Additional Parameter The second option results in the overriding of the virtual method declared in the base BLC. The method replaces the base BLC method and receives the delegate of either of the following with the additional parameter: The previous level method with additional parameter If no such methods are defined, the queue of all overriden versions of the method without the additional parameter You can decide whether to call the method pointed to by the delegate. In the case of the queue, invocation of the delegate will trigger execution of the whole queue. For example, to extend the PrepareImportRow(string, IDictionary, IDictionary) virtual method declared in the JournalWithSubEntry BLC, you should add the PrepareImportRow(string, IDictionary, IDictionary, Func<string, IDictionary, IDictionary, bool>) method with the PXOverride attribute attached to it, as follows. public class JournalWithSubEntryExtension : PXGraphExtension<JournalWithSubEntry> [PXOverride] public bool PrepareImportRow(string viewname, IDictionary keys, IDictionary values, Func<string, IDictionary, IDictionary, bool> method) // Method body before the base queue of methods bool result = method(viewname, keys, values); // Method body after the base queue of methods return result;
Acumatica Extensibility Framework In Detail 228 Accessing Customization Objects This topic describes how to access objects of data access class (DAC) and business logic controller (BLC, also referred to as a graph) extensions, and how to use these customization objects. Suppose that you will declare two new user fields (which can also be called DAC user fields or simply fields) within the POLine DAC extension and add the corresponding columns, Expense Account and Expense Sub., to the details table of the Purchase Orders webpage (see the screenshot below). Then you will create the BLC extension for the webpage. Figure: Viewing the user columns to be added to the Purchase Orders webpage Completing the Prerequisites You should first declare the UpdateSubs action of the Non-Stock Items webpage within the BLC extension and add this action to the action menu. Also, you should unpublish any previously published customization projects, create a new customization project, and name it AccessExtObjectsAEF. To unpublish each previously published project, you invoke the undo publish procedure, which cancels its publication. You must do this to avoid issues related to possible incompatibility with published projects. Performing this task is important for training purposes only. To unpublish existing projects, proceed as follows: 1. Navigate to System > Customization > Process > Publish Customization. If at least one customization project is published, select the page and click Undo Website Customization, as the screenshot below illustrates.
Acumatica Extensibility Framework In Detail 229 Figure: Clicking the Undo Website Customization button The untitled column of the Publish Customization webpage (with a check box) reflects whether a project is published. If this check box is selected for a customization project, the project is published. You don't need to use the undo publish procedure if no project is published. 2. Navigate to the Customization Projects webpage, click New, and type the new project name, AccessExtObjectsAEF, to the Project Name field of the New Project dialog that appears, and then click OK, as the following screenshot illustrates. Figure: Creating a new customization project Implementing the Update Sub. Accounts Menu Action You will now create a new menu action and define the action delegate by using Acumatica Extensibility Framework (AEF)/ Perform the following actions: 1. Start MS Visual Studio, and create the AccessExtObjectsAEF extension library solution as shown in Creating an Extension Library Solution by Using the Bespoke Approach.
Acumatica Extensibility Framework In Detail 230 2. Right-click the AccessExtObjectsAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type NonStockItemMaintExtension as the Name, and click Add. 4. Modify the added file code as follows. using PX.Data; using PX.Objects.IN; namespace AccessExtObjectsAEF public class NonStockItemMaintExtension : PXGraphExtension<NonStockItemMaint> public override void Initialize() Base.action.AddMenuAction(UpdateSubs); public PXAction<InventoryItem> UpdateSubs; [PXButton(CommitChanges = true)] [PXUIField(DisplayName = "Update Sub. Accounts")] protected void updatesubs() InventoryItem item = Base.ItemSettings.Current; if (item!= null && item.cogssubid!= null && item.nonstockreceipt == true) if (item.invtacctid!= null) item.invtsubid = item.cogssubid; if (item.poaccrualacctid!= null) item.poaccrualsubid = item.cogssubid; if (item.salesacctid!= null) item.salessubid = item.cogssubid; Base.ItemSettings.Update(item); Base.Actions.PressSave(); 5. Build the AccessExtObjectsAE extension library project. 6. Start or open the application, navigate to System > Customization > Manage > Customization Projects, select the AccessExtObjectsAEF customization project, and add the extension library file to the customization project. The code shown above implements the UpdateSubs action and places the Update Sub. Accounts command on the Actions menu of the Non-Stock Items webpage. By opening the GL Accounts tab of this webpage, changing the Expense Sub. value, and invoking this command, the user can make all the subaccounts the same as the expense subaccount, as the following screenshot illustrates.
Acumatica Extensibility Framework In Detail 231 Figure: Testing the UpdateSubs action Using Customization DAC Objects You can customize a DAC in either of the following ways: By altering the attributes of existing fields. You can use an altered field just as you would any other existing field. By declaring new (user) fields. In this task, you will analyze how to work with new fields declared within a DAC extension. Every field that is declared within a DAC extension code is accessible only through the DAC extension object. In AEF, you can access an instance of a DAC extension through the base (original) DAC object by using one of the following generic methods: The GetExtension<T>(object) static generic method of the PXCache<T> generic class The GetExtension<T>(object) static generic method declared within the non-generic PXCache class There are no differences between these generic methods. You can choose either one. In this task, you will add a BLC extension that uses both methods to access instances of DAC extensions. As a rule, you use fields declared within a DAC extension in the following ways: You can use new fields within DAC field attributes. To practice using new fields in this way, do the following : 1. Start MS Visual Studio and open the AccessExtObjectsAEF extension library solution that you created. 2. Right-click the AccessExtObjectsAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type POLineExtension as the Name, and click Add.
Acumatica Extensibility Framework In Detail 232 4. Modify the added file code as follows. using PX.Data; using PX.Objects.GL; using PX.Objects.PO; namespace AccessExtObjectsAEF public class POLineExtension : PXCacheExtension<POLine> #region UsrExpenseAcctExtID public abstract class usrexpenseacctextid : PX.Data.IBqlField [Account(typeof(POLine.branchID), DisplayName = "Expense Account", Visibility = PXUIVisibility.Visible, Filterable = false, DescriptionField = typeof(account.description))] public int? UsrExpenseAcctExtID get; set; #endregion #region UsrExpenseSubExtID public abstract class usrexpensesubextid : PX.Data.IBqlField [SubAccount(typeof(POLineExtension.usrExpenseAcctExtID), typeof(poline.branchid), DisplayName = "Expense Sub.", Visibility = PXUIVisibility.Visible, Filterable = true)] public int? UsrExpenseSubExtID get; set; #endregion Notice that to use the new UsrExpenseAcctExtID field (declared within the POLine DAC extension) to set up the USRExpenseSubExtID field attribute, you use the name of the DAC extension, typeof(polineextension.usrexpenseacctextid). 5. Build the AccessExtObjectsAEF extension library project. 6. Start or open the Acumatica ERP application. 7. Navigate to System > Customization > Manage > Customization Projects. 8. Select Add and click Database Table Field. 9. On the Add UsrField to Database Table dialog that appears, select POLine as the Table Name, type ExpenseAcctExtID as the Field Name, select DBInt(int) as the Field Type, and click OK, as the screenshot below illustrates. You shouldn't add the Usr prefix when you add a new column to a database table, because the system automatically adds this prefix. 10. Repeat instructions 8 and 9 to add the ExpenseSubExtID field to the POLine table. Use the same type and table for both fields; only the field name changes. 11. Validate and publish the project. If you changed the extension library before validating the customization project, the Checkin Files pop-up window appears with the Conflict check box selected (see the screenshot below). If you want to update the project with this modified file, you should click Check in Files to update the project and close the Check-in Files window, and then click Validate and Publish again. 12. Navigate to Distribution > Purchase Orders > Work Area > Enter > Purchase Orders, and on the Customization menu, select Open Customization Project.
Acumatica Extensibility Framework In Detail 233 13. On the Select Working Project dialog that appears, select the AccessExtObjectsAEF project that already exists, and click OK. 14. On the Customization menu, click Enter Page Design Mode. 15. On the Purchase Orders webpage in page design mode, select the Document Details tab, right-click the details table under the Document Details tab, and then click Control Tree. 16. On the Aspx Control Tree window that appears, perform the following actions: a. On the Add menu, click Add Column to Grid. b. In the Add Grid Column dialog box that appears, click the Magnifier icon of Data field and select UsrExpenseAcctExtID, which represents the name of the new DAC field. Set SegmentMask as the Column Editor value, and click OK. c. Expand the UsrExpenseAcctExtID node, select the SegmentMask subnode and set value to True for the AutoRefresh and CommitChanges properties (the property filter condition must be set to Ext Props). d. Repeat instructions 16.1 and 16.2 to add a column for the UsrExpenseSubExtID field. Use the same column editor for both fields; only the field name changes. e. Expand the UsrExpenseSubExtID node, select the SegmentMask subnode and set value to True for the AutoRefresh property (the property filter condition must be set to Ext Props). f. On the Add menu, click Add Item. g. In the New Item dialog that appears, set value to Parameters for the Collection property and PXSyncGridParam for the Item Type property, click OK. h. Set value to grid for the ControlID property of the PXSyncGridParam subnode (the property filter condition must be set to Base Props). 17. Click Apply to Page. 18. On the Customization menu, select Validate and Publish. You can work with fields declared within a DAC extension by setting values and invoking methods on PXCache objects for the new fields. To practice using new fields in this way, do the following : 1. Start MS Visual Studio, and open the AccessExtObjectsAEF extension library solution. 2. Right-click the AccessExtObjectsAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type POOrderEntryExtension as the Name, and click Add. 4. Modify the added file code as follows. using PX.Data; using PX.Objects.IN; using PX.Objects.PO; namespace AccessExtObjectsAEF public class POOrderEntryExtension : PXGraphExtension<POOrderEntry> protected void POLine_InventoryID_FieldUpdated(PXCache sender, PXFieldUpdatedEventArgs e) POLine row = (POLine)e.Row; POLineExtension rowext = sender.getextension<polineextension>(row); InventoryItem item = PXSelectorAttribute.Select<POLine.inventoryID>
Acumatica Extensibility Framework In Detail 234 (sender, row) as InventoryItem; if (item!= null && item.stkitem == false && item.nonstockreceipt == true) rowext.usrexpenseacctextid = item.cogsacctid; else rowext.usrexpenseacctextid = null; sender.setdefaultext<polineextension.usrexpensesubextid>(row); 5. Build the AccessExtObjectsAEF extension library project. 6. Navigate to System > Customization > Manage > Customization Projects, and click the Check in Files button to update the customization project with this modified extension library. Notice in the code above that the POLine_InventoryID_FieldUpdated event handler is added to the end of the appropriate collection of the base BLC. This event handler is used to update the UsrExpenseAcctExtID user DAC field with the expense account that is displayed in the COGSID DAC field. UsrExpenseAcctExtID corresponds to the Expense Account UI field of the Non-Stock Items webpage; this field sets the default value for the UsrExpenseSubExtID user data field by invoking the generic SetDefaultExt method of the PXCache object. The system updates the value of the UsrExpenseAcctExtID user data field if all of the following conditions take place: The inventory item exists, this item is a non-stock item, and the item has the Require Receipt option, which causes the system to generate purchase receipts. If at least one of these conditions is not met, the expense account value is set to null. Also notice that to set a value for a user field declared within a DAC extension, you use an instance of the DAC extension, but to invoke a method of the PXCache object, you create an instance of the base DAC. To invoke the generic PXCache method for a user DAC field, you should also use the name of the DAC extension, typeof(polineextension.usrexpensesubextid). You can use non-generic PXCache object methods. To invoke the non-generic SetDefaultExt method for the UsrExpenseSubExtID user field, you should use an instance of the base DAC and pass the name of the user field as one of parameters, as follows:sender.setdefaultext(row, "UsrExpenseSubExtID"); Also in the code above, pay attention to the use of the GetExtension<T>(object) generic method on the PXCache object to access an instance of a DAC extension. You use an instance of the base DAC to retrieve the DAC extension object as follows: sender.getextension<polineextension>(row); Another way that you work with fields declared within a DAC extension is by declaring event handlers for the user fields. To practice declaring these event handlers, perform the following actions: 1. Declare the FieldDefaulting event handler for the new UsrExpenseAcctExtID DAC user field of the POLine DAC extension within the POOrderEntryExtension BLC extension, as shown in the following code. protected void POLine_UsrExpenseSubExtID_FieldDefaulting(PXCache sender, PXFieldDefaultingEventArgs e) POLine row = (POLine)e.Row; if (row == null) return; InventoryItem item = PXSelectorAttribute.Select<POLine.inventoryID> (sender, row) as InventoryItem; if (item!= null && item.stkitem == false && item.nonstockreceipt == true && sender.getextension<polineextension>(row). UsrExpenseAcctExtID!= null)
Acumatica Extensibility Framework In Detail 235 e.newvalue = item.cogssubid; else e.newvalue = null; Notice in the preceding code that the default value of the expense subaccount is copied from the appropriate UI field of the Non-Stock Items webpage. The value is set if the expense account has been added previously and the selected inventory item is a non-stock item for which purchase receipts must be generated. Also notice that to declare an event handler for a user field of a DAC extension, you use the name of the base DAC. To use fields declared within a DAC extension, you can configure the new field attributes at run time as follows: 1. Declare the RowSelected event handler for the POLine DAC within the POOrderEntryExtension BLC extension, as shown in the following code. protected void POLine_RowSelected(PXCache sender, PXRowSelectedEventArgs e) POLine row = (POLine)e.Row; if (row == null) return; if (Base.Transactions.Cache.AllowUpdate == true) InventoryItem item = PXSelectorAttribute. Select<POLine.inventoryID>(sender, row) as InventoryItem; if (item!= null && item.stkitem == false && item.nonstockreceipt == true) PXUIFieldAttribute.SetEnabled<POLineExtension. usrexpenseacctextid>(sender, row, true); PXUIFieldAttribute.SetEnabled<POLineExtension. usrexpensesubextid>(sender, row, true); return; PXUIFieldAttribute.SetEnabled<POLineExtension. usrexpenseacctextid>(sender, row, false); PXUIFieldAttribute.SetEnabled<POLineExtension. usrexpensesubextid>(sender, row, false); Again, the expense account and subaccount fields on the Purchase Orders webpage become available if the inventory item exists, this item is a non-stock item, and purchase receipts must be generated for the item. Notice in the code above that to configure a user field attribute at run time, you use an instance of the base DAC. To invoke a static method of a DAC field attribute for a user DAC field, however, you should use the name of the DAC extension, POLineExtension.usrExpenseAcctExtID. One way that you work with fields declared within a DAC extension is by using user fields to write BQL statements, which you should do as follows: 1. Declare the FieldVerifying event handler with an additional parameter for the Hold field of the POOrder DAC within the POOrderEntryExtension BLC extension as follows. protected void POOrder_Hold_FieldVerifying(PXCache sender, PXFieldVerifyingEventArgs e, PXFieldVerifying del)
Acumatica Extensibility Framework In Detail 236 if ((bool?) e.newvalue!= true) bool erroroccured = false; foreach (POLine line in PXSelect<POLine, Where<POLine.orderType, Equal<Current<POOrder.orderType>>, And<POLine.orderNbr, Equal<Current<POOrder.orderNbr>>, And<POLineExtension.usrExpenseAcctExtID, IsNotNull, And<POLineExtension.usrExpenseSubExtID, IsNull>>>>>.Select(Base)) erroroccured = true; POLineExtension lineext = PXCache<POLine>. GetExtension<POLineExtension>(line); Base.Transactions.Cache.RaiseExceptionHandling <POLineExtension.usrExpenseSubExtID>( line, lineext.usrexpensesubextid, new PXSetPropertyException( "Expense Sub. Account may not be empty")); if (erroroccured) e.newvalue = sender.getvalue<poorder.hold>(e.row); throw new PXSetPropertyException ("At least one line has no Expense Sub. Account" "+specified"); if (del!= null) try del(sender, e); catch (PXException exc) e.newvalue = sender.getvalue <POOrder.hold>(e.Row); throw new PXSetPropertyException (exc.messagenoprefix); If the user doesn't enter a valid subaccount if he enters the expense account, the user cannot change the Hold status to the Open one until the subaccount is entered, and the user gets appropriate error messages. In the preceding code, notice that to access the new UsrExpenseAcctExtID and UsrExpenseSubExtID data fields of the POLine DAC extension within the BQL query, you use the name of the DAC extension, POLineExtension: The fields are POLineExtension.usrExpenseAcctExtID and POLineExtension.usrExpenseSubExtID. Also, the code illustrates the use of the GetExtension<T>(object) static generic method of the PXCache<T> generic class to access an instance of a DAC extension. You still use an instance of the base DAC to retrieve the DAC extension object as follows: PXCache<POLine>.GetExtension<POLineExtension>(line); Using Customization BLC Objects The members that are declared in a BLC extension are accessible only through the BLC extension object. In AEF, you can access an instance of a BLC extension through the base BLC object by using the GetExtension<T>(object) generic method of the PXGraph class. To alter or extend the behavior of the base BLC, you should declare a BLC extension. Extensions are automatically discovered by the runtime during the first base (original) class initialization. The
Acumatica Extensibility Framework In Detail 237 base BLC is replaced at run time with the merged result of the base BLC and every extension that has been discovered. You do not need to create an instance of the BLC extension explicitly or in addition to the base BLC instance. To access a BLC extension instance, you should either invoke the GetExtension<T>(object) method on the existing base class instance, or first create a new instance of the base BLC and then invoke the GetExtension<T>(object) method on the instance. To create an instance of the base BLC, you should use the generic or non-generic static CreateInstance method of the core PXGraph class. Two similar statements of creating the base BLC instance are the following: PXGraph.CreateInstance<BaseBLC>(); PXGraph.CreateInstance(typeof(BaseBLC)); Suppose that you need to declare the UpdateSubs action within the extension of the BLC of the Purchase Orders webpage to invoke the previously declared UpdateSubs action of the extension of the BLC for the Non-Stock Items webpage. Declare the UpdateSubs action within the POOrderEntryExtension BLC extension as follows. public PXAction<POOrder> UpdateSubs; [PXButton(CommitChanges = true)] [PXUIField(DisplayName = "Update Sub. Accounts")] protected void updatesubs() POLine line = Base.Transactions.Current; InventoryItem item = PXSelectorAttribute.Select<POLine.inventoryID> (Base.Transactions.Cache, line) as InventoryItem; if (item == null) return; NonStockItemMaint graph = PXGraph.CreateInstance<NonStockItemMaint>(); NonStockItemMaintExtension graphext = graph.getextension <NonStockItemMaintExtension>(); graph.item.current = item; graph.itemsettings.current.cogssubid = line.expensesubid; graphext.updatesubs.press(); Notice that within the UpdateSubs action delegate, an instance of the base NonStockItemMaint BLC is created. To invoke the UpdateSubs action on the NonStockItemMaint BLC extension, you should invoke the GetExtension<T>(object) method on the base class instance and explicitly invoke the Press method on the action declared within the BLC extension as follows. NonStockItemMaintExtension graphext = PXGraph.CreateInstance <NonStockItemMaint>().GetExtension<NonStockItemMaintExtension>(); graphext.updatesubs.press();
Customization Best Practices 238 Customization Best Practices This part of the training presents examples of best practices related to customization. The examples include a number of standard tasks and give instructions on how to best implement these tasks by using the Acumatica Extensibility Framework (AEF). In this part of the training, you will do the following tasks: Using Numbering Sequences Overriding a BLC Data View Overriding a BLC Data View Delegate Adding a Button onto the Form Control Adding Buttons to the Grid Control Calculating Unbound DAC Fields at Run Time Extending the Base Business Logic Extending the Multi-Level Document Workflow Extending the Persist() Method Extending the Release Operation Using Numbering Sequences To create new pages for the Acumatica ERP application or another Acumatica Framework-based application, you will create an add-on solution. To provide automatic numbering for data records of a document type within Acumatica ERP, you can use the sequence numbering feature. Before users start to enter data, you define the starting and ending number to be used for records of the type and the numbering step (the increment from one record number to the next). This topic describes in detail how to create an add-on solution with new pages and how to implement the numbering sequence on one of the webpages. You will also register these pages in the site map as webpages. Before you perform these tasks, you have to create four new user database tables. You will also create data access classes (DACs) based on fields of each user database table, and create two business logic controllers (BLCs, also referred to as graphs) to support user webpages and implement a new numbering sequence. Adding User Database Tables and Creating an Add-On Solution First, you will add the following user database tables to the instance: SalesOrder, OrderLine, Product, and Setup. To do this, perform the following SQL script on the application instance database. IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[SalesOrder]') AND type in (N'U')) DROP TABLE [SalesOrder] GO IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[OrderLine]') AND type in (N'U')) DROP TABLE [OrderLine] GO IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Product]')
Customization Best Practices 239 AND type in (N'U')) DROP TABLE [Product] GO IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Setup]') AND type in (N'U')) DROP TABLE [Setup] GO /****** Object: Table [dbo].[salesorder] ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[salesorder]( [OrderNbr] nvarchar(15) NOT NULL, [OrderDate] smalldatetime NOT NULL, [Status] char(1) NULL, [CustomerID] int NOT NULL, [RequiredDate] smalldatetime NULL, [ShippedDate] smalldatetime NULL, [Description] nvarchar(50) NULL, [LinesTotal] decimal(19, 4) NULL, [TaxTotal] decimal(19, 4) NULL, [OrderTotal] decimal(19, 4) NULL, [TStamp] [timestamp], CONSTRAINT [SalesOrder_PK] PRIMARY KEY CLUSTERED ( [OrderNbr] 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 /****** Object: Table [dbo].[orderline] ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[orderline]( [OrderNbr] [nvarchar](15) NOT NULL, [ProductID] [int] NOT NULL, [UnitPrice] [decimal](19, 6) NULL, [OrderQty] [decimal](25, 6) NULL, [StockUnit] [nvarchar](20) NULL, [TaxAmt] [decimal](9, 6) NULL, [DiscPct] [decimal](9, 6) NULL, [LinePrice] [decimal](19, 6) NULL, [TStamp] [timestamp], CONSTRAINT [OrderLine_PK] PRIMARY KEY CLUSTERED ( [OrderNbr] ASC, [ProductID] 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 /****** Object: Table [dbo].[product] ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[product](
Customization Best Practices 240 ( [ProductID] [int] IDENTITY(1,1) NOT NULL, [ProductCD] [nvarchar](15) NOT NULL, [ProductName] [nvarchar](50) NOT NULL, [Active] [bit] NOT NULL, [StockUnit] [nvarchar](20) NOT NULL, [UnitPrice] [decimal](19, 6) NOT NULL, [MinAvailQty] [decimal](25, 6) NOT NULL, [TStamp] [timestamp], CONSTRAINT [Product_PK] PRIMARY KEY CLUSTERED [ProductID] 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 /****** Object: Table [dbo].[setup] ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO SET ANSI_PADDING ON GO CREATE TABLE [dbo].[setup]( [CompanyID] [int] NOT NULL default 0, [SalesOrderNumSeqID] [nvarchar](10) NOT 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, CONSTRAINT [Setup_PK] PRIMARY KEY CLUSTERED ( [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 IF ((SELECT COUNT(*) FROM [dbo].[product]) = 0) BEGIN INSERT [dbo].[product] ( [ProductCD], [ProductName], [Active], [StockUnit], [UnitPrice], [MinAvailQty] ) VALUES ( N'NORTHWOODS CRAN', N'Northwoods Cranberry Sauce', 1, N'12 oz jars', CAST(55.000000 AS Decimal(19, 6)), CAST(30.000000 AS Decimal(25, 6)) ) INSERT [dbo].[product] ( [ProductCD], [ProductName], [Active], [StockUnit],
Customization Best Practices 241 [UnitPrice], [MinAvailQty] ) VALUES ( N'GRANDMA S BO', N'Grandma''s Boysenberry Spread', 1, N'18 oz jars', CAST(45.000000 AS Decimal(19, 6)), CAST(100.000000 AS Decimal(25, 6)) ) INSERT [dbo].[product] ( [ProductCD], [ProductName], [Active], [StockUnit], [UnitPrice], [MinAvailQty] ) VALUES ( N'UNCLE BOB S O', N'Uncle Bob''s Org', 1, N'1 lb pkgs.', CAST(42.000000 AS Decimal(19, 6)), CAST(20.000000 AS Decimal(25, 6)) ) INSERT [dbo].[product] ( [ProductCD], [ProductName], [Active], [StockUnit], [UnitPrice], [MinAvailQty] ) VALUES ( N'IKURA', N'Ikura', 1, N'100 ml jars', CAST(800.000000 AS Decimal(19, 6)), CAST(25.000000 AS Decimal(25, 6)) ) INSERT [dbo].[product] ( [ProductCD], [ProductName], [Active], [StockUnit], [UnitPrice], [MinAvailQty] ) VALUES ( N'QUESO CABRALE', N'Queso Cabrales', 1, N'1 kg pkgs', CAST(21.000000 AS Decimal(19, 6)), CAST(35.000000 AS Decimal(25, 6)) ) INSERT [dbo].[product] ( [ProductCD], [ProductName],
Customization Best Practices 242 [Active], [StockUnit], [UnitPrice], [MinAvailQty] ) VALUES ( N'QUESO MANCHEGO', N'Queso Manchego La Pastora', 1, N'1 kg pkgs', CAST(38.000000 AS Decimal(19, 6)), CAST(20.000000 AS Decimal(25, 6)) ) INSERT [dbo].[product] ( [ProductCD], [ProductName], [Active], [StockUnit], [UnitPrice], [MinAvailQty] ) VALUES ( N'CHOCKOLADE', N'Chockolade for kids', 1, N'500 g', CAST(4.500000 AS Decimal(19, 6)), CAST(500.000000 AS Decimal(25, 6)) ) END GO You then create the AutoNumSeqAddOn add-on solution. For details, see first four instructions in Using the Bespoke Approach. After you create needed references and build the added project, the Solution Explorer will have the structure displayed in the following screenshot.
Customization Best Practices 243 Figure: Viewing the structure of the AutoNumSeqAddOn solution Creating a BLC, Generating DACs, and Adding and Registering the Sales Orders Webpage Now you will create the SalesOrderEntry BLC file (to which you will bind the Sales Orders webpage, which is to be added) and then generate three DACs based on the appropriate user database tables. After performing these actions, you will change DAC field attributes, modify the initial (template) BLC code to declare data views based on the generated DACs, define the user page layouts, and register the page in the site map. To do this, proceed as follows: 1. Add the SalesOrderEntry BLC file (which is based on the PXGraph template) and build the project. 2. Add the DM subfolder under the Page folder of the website. 3. Add the DM301000.aspx page (based on the FormDetail template) to the DM subfolder. 4. Open the added page in design mode, and as the TypeName, select AutoNumSeqAddOn.SalesOrderEntry from the drop-down list. If AutoNumSeqAddOn.SalesOrderEntry is not displayed on the drop-down list, right-click the ds control and select Refresh, and then open the drop-down list again. 5. Point to the smart tag of the PXDataSource (ds) control, and click Generate Class (see the screenshot below).
Customization Best Practices 244 Figure: Specifying the TypeName and starting to generate the SalesOrder DAC fields 6. In the Data Access Class Generator window that appears, select the SalesOrder user table name from the Table Properties drop-down list (or begin typing this name), and click Generate. Repeat the actions of this instruction, instead selecting the OrderLine name and then the Product name. Two following screenshots illustrate the generation of the SalesOrder and Product DACs. Figure: Generating the fields of the SalesOrder DAC
Customization Best Practices 245 Figure: Generating the fields of the Products DAC In the screenshot above, notice the message with the full path to the DAC file, which is displayed after each DAC has been successfully generated. 7. Modify the SalesOrder.cs DAC code file as follows. namespace using using using using AutoNumSeqAddOn System; PX.Data; PX.Objects.AR; PX.Objects.CS; [Serializable] public class SalesOrder : PX.Data.IBqlTable #region OrderNbr public abstract class ordernbr : PX.Data.IBqlField protected string _OrderNbr; [PXDBString(15, IsKey = true, IsUnicode = true, InputMask = ">CCCCCCCCCCCCCCC")] [PXDefault()] [PXUIField(DisplayName = "Order Nbr.")] [PXSelector( typeof(search<salesorder.ordernbr>), typeof(salesorder.ordernbr), typeof(salesorder.orderdate), typeof(salesorder.status), typeof(salesorder.customerid))] public virtual string OrderNbr get return this._ordernbr;
Customization Best Practices 246 set this._ordernbr = value; #endregion #region OrderDate public abstract class orderdate : PX.Data.IBqlField protected DateTime? _OrderDate; [PXDBDate()] [PXDefault(typeof(AccessInfo.businessDate))] [PXUIField(DisplayName = "Order Date")] public virtual DateTime? OrderDate get return this._orderdate; set this._orderdate = value; #endregion #region Status public abstract class status : PX.Data.IBqlField protected string _Status; [PXDBString(1, IsFixed = true)] [PXDefault("O")] [PXUIField(DisplayName = "Status")] [PXStringList( new string[] "O", "H", "A", "C", new string[] "Open", "On Hold", "Approved", "Completed" )] public virtual string Status get return this._status; set this._status = value; #endregion #region CustomerID public abstract class customerid : PX.Data.IBqlField protected int? _CustomerID; [PXDBInt()] [PXDefault] [PXUIField(DisplayName = "Customer ID")]
Customization Best Practices 247 [PXSelector( typeof(search<customer.baccountid>), typeof(customer.acctcd), typeof(customer.acctname), SubstituteKey = typeof(customer.acctcd))] public virtual int? CustomerID get return this._customerid; set this._customerid = value; #endregion #region RequiredDate public abstract class requireddate : PX.Data.IBqlField protected DateTime? _RequiredDate; [PXDBDate()] [PXUIField(DisplayName = "Required Date")] [PXDefault( typeof(accessinfo.businessdate), PersistingCheck = PXPersistingCheck.Nothing)] public virtual DateTime? RequiredDate get return this._requireddate; set this._requireddate = value; #endregion #region ShippedDate public abstract class shippeddate : PX.Data.IBqlField protected DateTime? _ShippedDate; [PXDBDate()] [PXUIField(DisplayName = "Shipped Date")] public virtual DateTime? ShippedDate get return this._shippeddate; set this._shippeddate = value; #endregion #region Description public abstract class description : PX.Data.IBqlField protected string _Description; [PXDBString(50, IsUnicode = true)] [PXUIField(DisplayName = "Description")] public virtual string Description get return this._description;
Customization Best Practices 248 set this._description = value; #endregion #region LinesTotal public abstract class linestotal : PX.Data.IBqlField protected decimal? _LinesTotal; [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Lines Total")] public virtual decimal? LinesTotal get return this._linestotal; set this._linestotal = value; #endregion #region TaxTotal public abstract class taxtotal : PX.Data.IBqlField protected decimal? _TaxTotal; [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Tax Total")] public virtual decimal? TaxTotal get return this._taxtotal; set this._taxtotal = value; #endregion #region OrderTotal public abstract class ordertotal : PX.Data.IBqlField protected decimal? _OrderTotal; [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Order Total")] [PXFormula(typeof(Add<SalesOrder.linesTotal, SalesOrder.taxTotal>))] public virtual decimal? OrderTotal get return this._ordertotal; set this._ordertotal = value; #endregion #region TStamp public abstract class tstamp : PX.Data.IBqlField
Customization Best Practices 249 protected byte[] _TStamp; [PXDBTimestamp()] public virtual byte[] TStamp get return this._tstamp; set this._tstamp = value; #endregion 8. Modify the OrderLine.cs DAC file code as follows. namespace using using using AutoNumSeqAddOn System; PX.Data; PX.Objects.CS; [Serializable] public class OrderLine : PX.Data.IBqlTable #region OrderNbr public abstract class ordernbr : PX.Data.IBqlField protected string _OrderNbr; [PXDBString(15, IsKey = true, IsUnicode = true)] [PXDBDefault(typeof(SalesOrder.orderNbr))] [PXParent(typeof(Select<SalesOrder, Where<SalesOrder.orderNbr, Equal<Current<OrderLine.orderNbr>>>>))] public virtual string OrderNbr get return this._ordernbr; set this._ordernbr = value; #endregion #region ProductID public abstract class productid : PX.Data.IBqlField protected int? _ProductID; [PXDBInt(IsKey = true)] [PXDefault()] [PXUIField(DisplayName = "Product ID")] [PXSelector( typeof(search<product.productid, Where<Product.active, Equal<True>>>), typeof(product.productcd), typeof(product.productname), typeof(product.stockunit), typeof(product.unitprice), SubstituteKey = typeof(product.productcd))]
Customization Best Practices 250 public virtual int? ProductID get return this._productid; set this._productid = value; #endregion #region UnitPrice public abstract class unitprice : PX.Data.IBqlField protected decimal? _UnitPrice; [PXDBDecimal(6)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Unit Price")] public virtual decimal? UnitPrice get return this._unitprice; set this._unitprice = value; #endregion #region OrderQty public abstract class orderqty : PX.Data.IBqlField protected decimal? _OrderQty; [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Quantity")] public virtual decimal? OrderQty get return this._orderqty; set this._orderqty = value; #endregion #region StockUnit public abstract class stockunit : PX.Data.IBqlField protected string _StockUnit; [PXDBString(20, IsUnicode = true)] [PXUIField(DisplayName = "Stock Unit")] public virtual string StockUnit get return this._stockunit; set this._stockunit = value;
Customization Best Practices 251 #endregion #region TaxAmt public abstract class taxamt : PX.Data.IBqlField protected decimal? _TaxAmt; [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Tax Amount")] [PXFormula(null, typeof(sumcalc<salesorder.taxtotal>))] public virtual decimal? TaxAmt get return this._taxamt; set this._taxamt = value; #endregion #region DiscPct public abstract class discpct : PX.Data.IBqlField protected decimal? _DiscPct; [PXDBDecimal(6)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Discount")] public virtual decimal? DiscPct get return this._discpct; set this._discpct = value; #endregion #region LinePrice public abstract class lineprice : PX.Data.IBqlField protected decimal? _LinePrice; [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Ext. Price", Enabled = false)] [PXFormula( typeof(mult<mult<orderline.orderqty, OrderLine.unitPrice>, Div<Sub<decimal100, OrderLine.discPct>, decimal100>>), typeof(sumcalc<salesorder.linestotal>))] public virtual decimal? LinePrice get return this._lineprice; set this._lineprice = value; #endregion #region TStamp public abstract class tstamp : PX.Data.IBqlField
Customization Best Practices 252 9. protected byte[] _TStamp; [PXDBTimestamp()] public virtual byte[] TStamp get return this._tstamp; set this._tstamp = value; #endregion Modify the Product.cs DAC file code as follows. namespace AutoNumSeqAddOn using System; using PX.Data; [System.SerializableAttribute()] public class Product : PX.Data.IBqlTable #region ProductID public abstract class productid : PX.Data.IBqlField protected int? _ProductID; [PXDBIdentity] public virtual int? ProductID get return this._productid; set this._productid = value; #endregion #region ProductCD public abstract class productcd : PX.Data.IBqlField protected string _ProductCD; [PXDBString(15, IsUnicode = true, IsKey = true)] [PXDefault] [PXUIField(DisplayName = "Product ID")] public virtual string ProductCD get return this._productcd; set this._productcd = value; #endregion #region ProductName public abstract class productname : PX.Data.IBqlField
Customization Best Practices 253 protected string _ProductName; [PXDBString(50, IsUnicode = true)] [PXDefault] [PXUIField(DisplayName = "Product Name")] public virtual string ProductName get return this._productname; set this._productname = value; #endregion #region Active public abstract class active : PX.Data.IBqlField protected bool? _Active; [PXDBBool()] [PXDefault(true)] [PXUIField(DisplayName = "Active")] public virtual bool? Active get return this._active; set this._active = value; #endregion #region StockUnit public abstract class stockunit : PX.Data.IBqlField protected string _StockUnit; [PXDBString(20, IsUnicode = true)] [PXDefault] [PXUIField(DisplayName = "Stock Unit")] public virtual string StockUnit get return this._stockunit; set this._stockunit = value; #endregion #region UnitPrice public abstract class unitprice : PX.Data.IBqlField protected decimal? _UnitPrice; [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Unit Price")] public virtual decimal? UnitPrice get return this._unitprice;
Customization Best Practices 254 set this._unitprice = value; #endregion #region MinAvailQty public abstract class minavailqty : PX.Data.IBqlField protected decimal? _MinAvailQty; [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Min. Avail. Qty")] public virtual decimal? MinAvailQty get return this._minavailqty; set this._minavailqty = 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 10. Open the SalesOrderEntry.cs BLC file, and modify its code as shown below to create the Orders and OrderDetails data views (see also the screenshot below). using PX.Data; namespace AutoNumSeqAddOn public class SalesOrderEntry : PXGraph<SalesOrderEntry, SalesOrder> public PXSelect<SalesOrder> Orders; public PXSelect<OrderLine, Where<OrderLine.orderNbr, Equal<Current<SalesOrder.orderNbr>>>> OrderDetails;
Customization Best Practices 255 Figure: Modifying the initial BLC code 11. Build the AutoNumSeqAddOn project. 12. Open the DM301000.aspx page in design mode, and set the PrimaryView property value of the ds control to Orders. Select the form area of the page and also set the DataMember property value to Orders. Finally, select the grid area of the page and set the DataMember property value to OrderDetails. If the Orders item is not displayed on the drop-down list, right-click the ds control and select Refresh, and then open the drop-down list again. 13. Click the smart tag of the form area and select Edit Content Layout. 14. In the right pane of the Layout Editor that appears, open the Fields tab, select the check boxes for all UI controls, and click Generate, so all fields will be added onto the form area the new page. 15. Modify properties of the existing PXLayoutRule component and add new PXLayoutRule components to adjust size properties of the UI controls and to split them into three columns on the form area of the Sales Orders webpage. On the Properties tab of the right pane of the editor, perform the following actions (also shown in the screenshot below): a. Select the header of the first column, and set the following properties for it: StartRow: True ControlSize: S LabelsWidth: S
Customization Best Practices 256 Figure: Adding UI controls and adjusting their properties by using the Layout Editor b. Add one more PXLayoutRule component and place it above the eddescription UI control; widen this UI control by setting the ColumnSpan property for the added PXLayoutRule component so that the span value equals 3. c. Select the PXLayoutRule component to define the second column, and select the following properties for it: d. StartColumn: True ControlSize: XM LabelsWidth: S Select the PXLayoutRule component to define the third column, and select the following properties for it: StartColumn: True ControlSize: XM LabelsWidth: S 16. Click OK to close the Layout Editor and save your changes. 17. Click the smart tag of the grid area and select Edit Content Layout. 18. In the right pane of the Layout Editor that appears again, open the Fields tab, select the check boxes for all visible UI controls that are to be added onto the grid area of the new page, select the Columns check box, and click Generate, as the following screenshot illustrates. (After you click Generate, the selected columns are moved to the lower left window of the editor.)
Customization Best Practices 257 Figure: Adding grid columns by using the Layout Editor 19. Click OK to close the Layout Editor and save your changes. The screenshot below illustrates the layout of the new page. Figure: Viewing the layout of the new page
Customization Best Practices 258 20. Navigate to System > Customization > Manage > Site Map and register the new added page as the Sales Orders webpage. To do this: a. On the Site Map webpage, select the Rapid Byte Solutions Inc. node in the site map tree (see item 1 in the following screenshot). Click the Add Row button (see item 2), and type the following values (item 3): ScreenID: DM.00.00.00 Title: Demo Url: ~/Frames/Default.aspx Because the first-level subnode, Demo, will be used as a main menu item, you don't need to specify the Graph Type value here. The Expanded check box must be cleared (as it is by default). If you want to shift the Demo menu item nearer to the center of the main menu, click the Up button (item 4) several times to move this item up. Click Save to save the added menu item. Figure: Adding the new item to the main menu b. Select the new Demo first-level subnode in the site map tree, click the Add Row button, and type the following values (as shown in the following screenshot): ScreenID: DM.00.00.00 Title: Demo Url: ~/Frames/Default.aspx Because the second-level subnode, also Demo, will be used as a submenu item, you don't need to specify the Graph Type value here. The Expanded check box must be cleared (as it is by default). Click Save to save the added subnode.
Customization Best Practices 259 Figure: Adding the new item for the submenu c. Select the new Demo second-level subnode in the site map tree. Click Add Row, and enter the following values (shown in the screenshot below): ScreenID: Empty Title: Work Area Icon: main@dataentryf (select from the drop-down list) Add the third-level subnode, Configuration. Click Add Row, and enter the following values: ScreenID: Empty Title: Configuration Icon: main@settingsf Click Save to save the added subnodes. Figure: Adding the new items for the second-level subnode d. Select the new third-level subnode, Work Area, in the site map tree. Click Add Row, and enter the following values (shown in the following screenshot): ScreenID: Empty Title: Enter
Customization Best Practices 260 Url: Empty Expanded: Selected Because the fourth-level subnode, Enter, will be used as a navigation pane node, you don't need to specify the Graph Type value here. The Expanded check box must be selected so that after the user navigates to the subnode of the previous level, Work Area, the navigation pane node is expanded and displays all pane subnodes (which are webpages). Click Save to save the added fourth-level subnode. Figure: Adding the new item for the fourth-level subnode e. Select the fourth-level subnode, Enter, in the site map tree. Click the Add Row button, and type the following values: ScreenID: DM.30.10.00 Title: Sales Orders Url: ~/Pages/DM/DM301000.aspx The system automatically assigns the Graph Type value by using the Type Name value of the specified page. The Expanded check box must be cleared (as it is by default). Click Save to save the registered webpage in the site map.
Customization Best Practices 261 Figure: Registering the added page as the Sales Orders webpage 21. Navigate to Demo > Demo > Work Area > Enter to ensure that the Sales Orders webpage is visible on the navigation pane (as shown in the screenshot above); if it is, open the webpage, add a record, and try to save it. If the record has been saved, you have successfully performed this part of the automatic numbering customization task related to the add-on project. Preparing and Registering the Demo Setup Webpage To optimize the implementation of automatic numbering of the Sales Orders records, you need to create a simple setup webpage (called Demo Setup), which will contain the ID of the preconfigured numbering sequence and other key information about the sequence. This sequence must be defined before sales orders are added. To facilitate the addition and identification of the new sequence, you will create the SetupMaint.cs BLC to support the Demo Setup webpage, the Save and Cancelactions for appropriate toolbar buttons, and the SetupRecord data view to support the identification of the auto-numbered documents. Proceed as follows: 1. Add the SetupMaint BLC file (which is based on the PXGraph template) and build the project. 2. Add the DM101000.aspx page (based on the FormView template) to the DM subfolder of the website. 3. Open the added page in design mode and as the TypeName property, select AutoNumSeqAddOn.SetupMaint from the drop-down list. If AutoNumSeqAddOn.SetupMaint is not displayed in the drop-down list, right-click the ds control and select Refresh, and then open the drop-down list again. 4. Click the smart tag of the PXDataSource (ds) control and click Generate Class, as the following screenshot illustrates.
Customization Best Practices 262 Figure: Specifying the TypeName property and starting to generate the Setup DAC fields 5. In the Data Access Class Generator window that appears, select the Setup user table name from the Table Properties drop-down list (or begin typing this name), and click Generate, as shown in the following screenshot. Figure: Generating the fields of the Setup DAC 6. Modify the generated DAC code in the Setup.cs file as follows. namespace AutoNumSeqAddOn
Customization Best Practices 263 using System; using PX.Data; using PX.Objects.CS; [System.SerializableAttribute()] public class Setup : PX.Data.IBqlTable #region SalesOrderNumSeqID public abstract class salesordernumseqid : PX.Data.IBqlField protected string _SalesOrderNumSeqID; [PXDBString(10, IsUnicode = true)] [PXDefault] [PXSelector(typeof(Numbering.numberingID))] [PXUIField(DisplayName = "Sales Order Numbering Sequence")] public virtual string SalesOrderNumSeqID get return this._salesordernumseqid; set this._salesordernumseqid = 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 #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()]
Customization Best Practices 264 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
Customization Best Practices 265 7. protected DateTime? _LastModifiedDateTime; [PXDBLastModifiedDateTime()] public virtual DateTime? LastModifiedDateTime get return this._lastmodifieddatetime; set this._lastmodifieddatetime = value; #endregion Open the SetupMaint.cs file, and modify the BLC code to create the SetupRecord data view and two actions as follows (see also the screenshot below). using PX.Data; namespace AutoNumSeqAddOn public class SetupMaint : PXGraph<SetupMaint> public PXSave<Setup> Save; public PXCancel<Setup> Cancel; public PXSelect<Setup> SetupRecord; Figure: Modifying the initial BLC code 8. Build the AutoNumSeqAddOn project. 9. Open the DM101000.aspx page in design mode, and set the PrimaryView property value of the ds control to SetupRecord; select the form area of the page and also set the DataMember property value to SetupRecord.
Customization Best Practices 266 10. Click the smart tag of the form area and select Edit Content Layout. 11. In the right pane of the Layout Editor that appears, open the Fields tab, select the SalesOrderNumSeqID UI control that is to be added onto the form area of the new page, and click Generate. 12. Open the Properties tab of the right pane of the editor, add a PXLayoutRule component, and perform the following actions (see also the screenshot below): a. Select the PXLayoutRule component, and select the following properties for it: StartRow: True ControlSize: SM LabelsWidth: M Figure: Adding UI controls and adjusting their properties by using the Layout Editor 13. Click OK to close the Layout Editor and save your changes.
Customization Best Practices 267 Figure: Viewing the layout of the new page 14. Navigate to System > Customization > Manage > Site Map and perform the following actions: a. Select the new Demo/Demo/Configuration third-level subnode in the site map tree. Click Add Row, and enter the following values (shown in the following screenshot): ScreenID: Empty Title: Setup Url: Empty Expanded: Selected Because the fourth-level subnode, Setup, will be used as a navigation pane node, you don't need to specify the Graph Type value here. The Expanded check box must be selected, so that after navigating to the subnode of the previous level, Configuration, the navigation pane node is expanded and displays all pane subnodes (which are webpages). Click Save to save the added fourth-level subnode.
Customization Best Practices 268 Figure: Adding the new item for the fourth-level subnode b. Select the Setup fourth-level subnode in the site map tree. Click Add Row, and type the following values (shown in the screenshot below): ScreenID: DM.10.10.00 Title: Demo Setup Url: ~/Pages/DM/DM101000.aspx The system automatically assigns the Graph Type value by using the Type Name value of the specified page. The Expanded check box must be cleared (as it is by default). Click Save to save the registered webpage in the site map. Figure: Registering the added Demo Setup webpage 15. Navigate to Demo > Demo > Configuration > Setup to ensure that the Demo Setup webpage is visible on the navigation pane.
Customization Best Practices 269 Resolving the Autonumbering Task: Final Steps Now you will add AutoNumberAttribute to the OrderNbr field and declare the PXSetup<Setup> data view within the SalesOrderEntry BLC file. Finally, you will add a new SALESORDER sequence and test the correctness of the implemented automatic numbering feature by adding a new sales order. Proceed as follows: 1. Open the SalesOrder.cs DAC file, and add AutoNumberAttribute to the OrderNbr field with the parameters shown in the following code snippet. [AutoNumber(typeof(Setup.salesOrderNumSeqID), typeof(salesorder.orderdate))] 2. Build the AutoNumSeqAddOn project. 3. Navigate to Demo > Demo > Work Area > Enter > Sales Order, and try to create and save a new sales order. The following text and screenshot illustrate the error message you will see after you click Save. CS Error#29: Cannot generate the next number for this sequence. Figure: Viewing the error message after you click Save 4. Open the SalesOrderEntry.cs BLC file, and declare a data view and a constructor, as shown in the code snippet below. public PXSetup<Setup> SetupRecord; public SalesOrderEntry() Setup setuprecord = SetupRecord.Current; 5. Build the AutoNumSeqAddOn project. 6. Refresh the website and try to reopen the Sales Orders webpage. Another error message appears instead, as the following text and screenshot illustrate. Requested resource is not available. Required configuration data is not entered into the Setup screen.
Customization Best Practices 270 Figure: Viewing the error message after you try to open the Sales Orders webpage 7. To add a link to the error message, attach the PXPrimaryGraphAttribute to the Setup DAC, as shown in the code snippet below. [Serializable] [PXPrimaryGraph(typeof(SetupMaint))] public class Setup : PX.Data.IBqlTable 8. Open the DM101000.aspx page file in source mode and type AllowEdit="True", as shown in the screenshot below. Figure: Modifying the DM101000.aspx page file code 9. Again try to open the Sales Orders webpage. Notice that the message appears (also shown in the following screenshot) with the suggestion to navigate to the Setup webpage through the provided link and enter the required configuration data : Navigate to the Setup screen and enter required configuration data.
Customization Best Practices 271 Figure: Viewing the message about entering configuration data 10. Click the Setup link. 11. On the Demo Setup webpage that appears, click Edit (see item 1 in the screenshot below). 12. In the form area of the Numbering Sequences webpage that appears, type SALESORDER as the Numbering ID, Sales Order Sequence as the Description, and <NEW> as the New Number Symbol (see item 2). 13. In the details table, add a row with the following settings (item 3): (type 000001 as the Start Number, 999999 (the default value) as the End Number, 000000 (the default value) as the Last Number, 999899 (the default value) as the Warning Number, and 1 (the default value) as the Numbering Step. Figure: Adding the SALESORDER numbering sequence 14. Click Save item 4) and close the Numbering Sequences webpage. 15. On the Demo Setup webpage, click the Magnifier icon (item 5) and find the new SALESORDER numbering sequence. 16. Click Save (item 6). 17. To test the automatic numbering that you have implemented, again open the Sales Orders webpage and add a new sales order with any data. After you click Save, the system fills in the correct Order Nbr., 000001, as the following screenshot illustrates.
Customization Best Practices 272 Figure: Testing the automatic numbering When you add and save new sales orders, the system will give them the numbers of 000002, 000003, and so on. Preparing the Content of the Customization Project After you have finished to develop the add-on solution, you have to start the application instance, create a new customization project, and upload the current content to this project: the created pages and the library. You need to fulfil these steps if you need to easily and reliably deploy the add-on solution for the remote application, which represents the customer's enterprise application, for instance. Proceed as follows: 1. Navigate to System > Customization > Manage > Customization Projects, and click New on the form area of the Customization Projects webpage. Add the new customization project name, such as AutoNumSeqAddOn; because the name of the customization project should be similar to the name of the add-on project. 2. Click Add and select File from File System. 3. In the Add Files pop-up window, select the.dll,.aspx, and.aspx.cs files created during the development stage, and click Save, as the following screenshot illustrates. Figure: Selecting external files for inclusion in the customization project As a result, the File type content objects have been included in the customization project. 4. Click Add and select Site Map (see the following screenshot).
Customization Best Practices 273 Figure: Starting to add site map changes 5. In the Site Map pop-up window, select the check boxes left of the two Demo items and the System, Work Area, Configuration, Enter, Setup, Sales Orders, and Demo Setup items. As the result, the Data type content objects have been included in the customization project, with the new positions defined by the site map. 6. Click Add and select Database Table or Script. In the Edit Sql Script dialog that appears, select SalesOrder as the DBObject Name, select the Import Table Schema from Database check box, and click Save. 7. Repeat the actions described in the instruction 6 twice, selecting OrderLine and then Setup as the DBObject Name. The following screenshot illustrates the creation of the Sql type object for the Setup database table. Figure: Adding the Setup SQL object to the content of the customization project 8. Click Add and select Database Table or Script. In the Edit Sql Script dialog that appears, select Product as the DBObject Name, select the Import Table Schema from Database check box, paste the following script in the Custom Script text area, and click Save. IF ((SELECT COUNT(*) FROM [dbo].[product]) = 0) BEGIN INSERT [dbo].[product]
Customization Best Practices 274 ( [ProductCD], [ProductName], [Active], [StockUnit], [UnitPrice], [MinAvailQty] ) VALUES ( N'NORTHWOODS CRAN', N'Northwoods Cranberry Sauce', 1, N'12 oz jars', CAST(55.000000 AS Decimal(19, 6)), CAST(30.000000 AS Decimal(25, 6)) ) INSERT [dbo].[product] ( [ProductCD], [ProductName], [Active], [StockUnit], [UnitPrice], [MinAvailQty] ) VALUES ( N'GRANDMA S BO', N'Grandma''s Boysenberry Spread', 1, N'18 oz jars', CAST(45.000000 AS Decimal(19, 6)), CAST(100.000000 AS Decimal(25, 6)) ) INSERT [dbo].[product] ( [ProductCD], [ProductName], [Active], [StockUnit], [UnitPrice], [MinAvailQty] ) VALUES ( N'UNCLE BOB S O', N'Uncle Bob''s Org', 1, N'1 lb pkgs.', CAST(42.000000 AS Decimal(19, 6)), CAST(20.000000 AS Decimal(25, 6)) ) INSERT [dbo].[product] ( [ProductCD], [ProductName], [Active], [StockUnit], [UnitPrice], [MinAvailQty] ) VALUES ( N'IKURA', N'Ikura', 1, N'100 ml jars', CAST(800.000000 AS Decimal(19, 6)), CAST(25.000000 AS Decimal(25, 6))
Customization Best Practices 275 ) INSERT [dbo].[product] ( [ProductCD], [ProductName], [Active], [StockUnit], [UnitPrice], [MinAvailQty] ) VALUES ( N'QUESO CABRALE', N'Queso Cabrales', 1, N'1 kg pkgs', CAST(21.000000 AS Decimal(19, 6)), CAST(35.000000 AS Decimal(25, 6)) ) INSERT [dbo].[product] ( [ProductCD], [ProductName], [Active], [StockUnit], [UnitPrice], [MinAvailQty] ) VALUES ( N'QUESO MANCHEGO', N'Queso Manchego La Pastora', 1, N'1 kg pkgs', CAST(38.000000 AS Decimal(19, 6)), CAST(20.000000 AS Decimal(25, 6)) ) INSERT [dbo].[product] ( [ProductCD], [ProductName], [Active], [StockUnit], [UnitPrice], [MinAvailQty] ) VALUES ( N'CHOCKOLADE', N'Chockolade for kids', 1, N'500 g', CAST(4.500000 AS Decimal(19, 6)), CAST(500.000000 AS Decimal(25, 6)) ) END GO You may add and update the content of the customization project (as described in this section) only once, before starting the final testing and deployment stage. However, you may want to improve the content after uploading it by modifying the page layout, DAC attributes, data views, or business logic. Don't forget that after making any changes in the add-on solution and before creating the final deployment package, you should perform the following actions: In the Customization Projects webpage, select Check in Files; in the Check in Files pop-up window, the changed files are highlighted with the Select and Conflict check boxes. If you want to update the current customization content, click Check in Files; otherwise, you can clear the Select check boxes for files whose content doesn't need to be updated at that moment and then click Check in
Customization Best Practices 276 Files. The following screenshot illustrates the simplest case: when you need to update one modified file. Figure: Checking in the modified file Downloading the Deployment Package Now you will download the deployment package. To download the deployment package, return to the Customization Projects webpage and click Edit XML on the form toolbar. In the window with the whole XML content of the customization project (the name of the project is displayed in the upper part of the window), select Download Package; in the Opening AutoNumSeqAddOn.zip dialog that appears, select Save File (see the screenshot below). In the navigation window that appears, specify the path (and change the zip file name, if necessary) and click OK. Figure: Downloading the deployment package
Customization Best Practices 277 You can download the package from the Customization Projects webpage instead of doing it from the window with the XML content, by clicking Get Package in the form toolbar of the Customization Projects webpage. Overriding a BLC Data View Suppose that you need to customize the Sales Orders webpage (see the screenshot below) to get the following results: The customer must be selected for the order Default or any other location of the customer must be specified The additional information must be displayed for the selected location: Postal Code City Figure: Viewing the original Sales Orders webpage The example of a customization below helps to resolve the tasks, listed over. This example shows how to override a business logic controller (BLC, also referred to as a graph) data view to select additional data and display it on the webpage. In this example, you will perform the following actions: Creating a new extension library solution Creating the SOOrderEntryExtension BLC extension, which overrides the Document data view defined in the SOOrderEntry BLC Customizing the Sales Orders webpage to display two more fields that are to be selected by the Document data view Creating a New Extension Library First you should create the OverrideViewAEF extension library solution for an Acumatica ERP application. For details, see first four instructions in Creating an Extension Library Solution by Using the Bespoke Approach.
Customization Best Practices 278 If you have any customization projects applied, before creating the OverrideViewAEF extension library solution, on the Acumatica ERP website select Undo Publish on the Customization menu. Extending the SOOrderEntry BLC 1. Start MS Visual Studio and open the created OverrideViewAEF solution. 2. Right-click the OverrideViewAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type SOOrderEntryExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using using using using PX.Data; PX.Objects.AR; PX.Objects.CR; PX.Objects.SO; namespace OverrideViewAEF public class SOOrderEntryExtension : PXGraphExtension<SOOrderEntry> [PXViewName(PX.Objects.SO.Messages.SOOrder)] public PXSelectJoin<SOOrder, LeftJoin<Customer, On<Customer.bAccountID, Equal<SOOrder.customerID>>, LeftJoin<Location, On<Location.locationID, Equal<SOOrder.customerLocationID>>, LeftJoin<Address, On<Address.addressID, Equal<Location.defAddressID>>>>>, Where<SOOrder.orderType, Equal<Optional<SOOrder.orderType>>, And<Where<Customer.bAccountID, IsNull, Or<Match<Customer, Current<AccessInfo.userName>>>>>>> Document; The Document data view retrieves the primary SOOrder data record for the SOOrderEntry BLC. The extension data view also joins the Location and Address tables; the base data view joins only the Customer table. The fields that you also need to display are in the Address table. However, the SOOrder data record contains only the ID of the Location data record. So you first join the Location table by the CustomerLocationID field of SOOrder and then join the Address table by the DefAddressID of Location. 5. Build the OverrideViewAEF project. 6. On the Acumatica ERP website, open the System > Customization > Manage > Customization Projects page. 7. On the Customization Projects webpage, click New. 8. On the New Project dialog box that appears, type your project name, such as OverrideViewAEF, and click OK. 9. Click the Add button and select File from File System. 10. On the Add Files pop-up window that appears, select the check box left of the created extension library file name you need to add to the project, and click Save. Customizing the Sales Orders Page 1. On the Acumatica ERP website, open the Distribution > Sales Order > Work Area > Enter > Sales Orders page, and enter page design mode.
Customization Best Practices 279 2. Right-click the form area and click Add Input Control. 3. In the Create Control window, select Address PostalCode as the Data field and click OK, as the following screenshot shows. Figure: Adding the input control for the Address PostalCode field 4. Right-click the form area and click Add Input Control. 5. In the Create Control window, select Address City as the Data field and click OK, as the following screenshot shows. Figure: Adding the input control for the Address City field 6. Right-click the form area and click Control Tree. 7. In the Aspx Control Tree window, expand the Document:PXFormView node. 8. Select the Address PostalCode:PXTextEdit node and position it right below the CustomerID:PXSegmentMask node by using the Up button. 9. Select the Address City:PXTextEdit node and position it right below the CustomerLocationID:PXSegmentMask node by using the Up button. The nodes in the control tree should now be arranged as they are in the following screenshot. Figure: Adjusting required arrangement of the nodes in the control tree 10. Add three layout rules by using the Add > Add Layout Rule menu item:
Customization Best Practices 280 a. Select the CustomerID:PXSegmentMask node and add a layout rule. b. Select the CustomerLocationID:PXSegmentMask and add a layout rule. c. Select the CuryID:PXCurrencyRate node and add a layout rule. The nodes should now be arranged as they are on the following screenshot. Figure: Required arrangement of the nodes in the control tree 11. Select the layout rule node above the CustomerID:PXSegmentMask node and set the Merge property to True. This will cause the two fields that follow this layout rule node to be displayed on the same line. 12. Select the CustomerID:PXSegmentMask node and set the Size property to M. 13. Select the Address PostalCode:PXTextEdit node and set the following properties (as the screenshot below shows): Label Width: 80px Size: XS Figure: Setting the properties of the Address PostalCode field 14. Select the layout rule node above the CustomerLocationID:PXSegmentMask and set the Merge property to True. 15. Select the CustomerLocationID:PXSegmentMask node and set the Size property to M. 16. Select the Address City:PXTextEdit node and set the following properties: Label Width: 50px Size: S 17. Click Apply to Page.
Customization Best Practices 281 18. By using the Customization menu, validate and publish the project. 19. On the Sales Orders page, select ABARTENDE as the Customer. The Location setting will be automatically populated, as will the Postal Code and City (which you just added) values, as the following screenshot shows. Figure: Viewing the added Postal Code and City UI fields Overriding a BLC Data View Delegate Suppose that you need to add a new field to the SOShipment data access class (DAC) so that all of the following criteria are met: The Process Shipments filter is extended by this field. The data view uses this field to display the set of shipments to process. This field is not used within long-running operations. The original view of the Process Shipment webpage, which will also be customized to implement filtering, is shown in the screenshot below. Figure: Viewing the original Process Shipment webpage
Customization Best Practices 282 In this example, you will override the data view delegate for a filterable data view by performing the following actions: Creating a new extension library solution Extending the SOShipment DAC with a new field Extending the SOShipmentFilter DAC with a new field that corresponds to the field that you added to the SOShipment DAC Extending the SOInvoiceShipment business logic controller (BLC, also referred to as a graph) by overriding the Orders data view delegate and implementing the logic to filter the SOShipment data records based on the new field Adding the extension library file to the customization project Creating a New Extension Library Solution First, you should create the OverrideViewDelegateAEF extension library solution for an Acumatica ERP application. For details, see first four instructions in Creating an Extension Library Solution by Using the Bespoke Approach. Extending the SOShipment DAC 1. Start MS Visual Studio and open the OverrideViewDelegateAEF extension library solution that you created. 2. Right-click the OverrideViewDelegateAEF project caption and select Add, and then click New Item. 3. On the Add New Item window that appears, select the Class template, type SOShipmentExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using PX.Data; using PX.Objects.SO; namespace OverrideViewDelegateAEF public class SOShipmentExtension : PXCacheExtension<SOShipment> #region UsrShipType public abstract class usrshiptype : IBqlField [PXDBInt] [PXIntList( new int[] 1, 0, new string[] "Demo", "Not Demo" )] [PXUIField(DisplayName = "Type")] public int? UsrShipType get; set; #endregion In this code, notice that the SOShipmentExtension extension class defines the UsrShipType field. The field has the PXIntList attribute, which configures the combo box input control for this field. The Usr prefix in the field name is required for every new field that is mapped to a new database column. 5. Build the OverrideViewDelegateAEF project.
Customization Best Practices 283 6. Reopen or start your application. If you have any customization projects applied, on the Acumatica ERP application, select Undo Publish on the Customization menu of any webpage. 7. On the Acumatica ERP application, open the System > Customization > Manage > Customization Projects page. 8. On the Customization Projects webpage, click New. 9. On the New Project dialog that appears, type your project name, OverrideViewDelegateAEF, and click OK. 10. Select Add and click Database Table, and then select SOShipment as the table, enter ShipType as the field name, and select DBInt(int) as the field type. Finally, click OK, as the following screenshot shows. Figure: Adding the UsrShipType database field 11. Save, validate, and publish the project. 12. Open the Distribution > Sales Orders > Work Area > Enter > Shipments webpage. 13. By using the Customization menu, open the OverrideViewDelegateAEF customization project, and enter page design mode. 14. Right-click the top form area and click Add Input Control. Then enter UsrShipType as the data field, select ComboBox as the control type, and click OK, as the following screenshot shows. Figure: Adding the UsrShipType input control
Customization Best Practices 284 15. Right-click the Type input control and click Control Tree. Then type S as the value of the Size property, and click Apply to Page (upper left), as the following screenshot shows. Figure: Changing the Size property of the input control 16. By using the Customization menu on any application webpage, validate and publish the project. Extending the SOShipmentFilter DAC Follow the instructions below to extend thesoshipmentfilter DAC with the ShipType field and add a new input control representing this field on the Process Shipments page: 1. Start MS Visual Studio and open the created OverrideViewDelegateAEF solution. 2. Right-click the OverrideViewDelegateAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type SOShipmentFilterExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using PX.Data; using PX.Objects.SO; namespace OverrideViewDelegateAEF public class SOShipmentFilterExtension : PXCacheExtension<SOShipmentFilter> #region ShipType public abstract class shiptype : IBqlField [PXInt] [PXIntList( new int[] 1, 0, new string[] "Demo", "Not Demo" )] [PXUIField(DisplayName = "Type")] public int? ShipType get; set; #endregion
Customization Best Practices 285 In this code, you extend the SOShipmentFilter DAC, which is not bound to a database table but is used to wrap filtering information for the SOShipment DAC. You define the ShipType field, which will be used to filter records by values of the UsrShipType field, which you defined in the SOShipmentExtension extension. 5. Build the OverrideViewDelegateAEF project. 6. On the Acumatica ERP application, open the Distribution > Sales Orders > Processes > Daily > Process Shipments webpage and enter page design mode. 7. Right-click the top form area and click Add Input Control. Then e enter ShipType as the data field, select ComboBox as the control type, and click OK, as the following screenshot shows. Figure: Adding the ShipType input control 8. Right-click the Type input control and click Control Tree. 9. Type S as the value of the Size property, set the CommitChanges property to True, click the Up button as needed to position the control node right above ShowPrinted:PXCheckBox, and click Apply to Page, as the following screenshot shows.
Customization Best Practices 286 Figure: Changing the Size and CommitChanges properties of the input control 10. By using the Customization menu, validate and publish the project. Extending the SOInvoiceShipment BLC Follow the instructions below to override the BLC data view delegate for the Orders data view, which is defined in the SOInvoiceShipment BLC: 1. Start MS Visual Studio and open the OverrideViewDelegateAEF extension library solution. 2. Right-click the OverrideViewDelegateAEF project caption, select Add, and click New Item. 3. In the Add New Item window that appears, select the Class template, type SOInvoiceShipmentExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using System.Collections; using PX.Data; using PX.Objects.SO; namespace OverrideViewDelegateAEF public class SOInvoiceShipmentExtension : PXGraphExtension<SOInvoiceShipment> [PXFilterable] public PXFilteredProcessing<SOShipment, SOShipmentFilter> Orders; protected IEnumerable orders() SOShipmentFilterExtension filterext = PXCache<SOShipmentFilter>.GetExtension <SOShipmentFilterExtension>( Base.Filter.Current); var list = new ArrayList(); foreach (PXResult record in Base.Orders.Select()) SOShipment shipment = record.getitem<soshipment>(); if (shipment!= null) SOShipmentExtension shipmentext = PXCache<SOShipment>.GetExtension <SOShipmentExtension>(
Customization Best Practices 287 shipment as SOShipment); if (filterext.shiptype == 1 && shipmentext.usrshiptype == 1 filterext.shiptype!= 1 && shipmentext.usrshiptype!= 1) list.add(shipment); else if (filterext.shiptype == null) list.add(shipment); return list; In this code, the SOInvoiceShipmentExtension extension class redefines the Orders data view and its orders() delegate. Because the data view is redefined on the current extension level, you can safely execute the same data view through the base class by using Base.Orders.Select(). The code filters, which the data set returned by the base data view, represent the following: SOShipment records that have UsrShipType set to Demo (value 1) if ShipType is set to Demo in the filter SOShipment records that have UsrShipType not set or set to Not Demo if ShipType is set to Not Demo in the filter All SOShipment records if ShipType is not set in the filter 5. Build the OverrideViewDelegateAEF project. 6. Again open the Process Shipments webpage and notice the Type drop-down list. Sequentially select each of the types, Demo and Not Demo, to ensure that the new filter works properly. The following screenshot illustrates how the new filter works when the Prepare Invoice action and Not Demo type have been selected. Figure: Testing the new added filter
Customization Best Practices 288 Adding the Extension Library File to the Customization Project 1. In the Acumatica ERP application, open the System > Customization > Manage > Customization Projects webpage, and select the OverrideViewDelegateAEF customization project. 2. Select Add and click File from File System. 3. In the Add Files pop-up window that appears, select the check box on the line with Bin \OverrideViewDelegateAEF.dll, and click Save. Adding a Button onto the Form Control Suppose that you need to view on the map the address of the customer who is to pay your bill (sonamed Bill-To Address), and it is necessary to do by one click on the action button, which must be placed near the address fields on the Sales Orders webpage. This Bill-To Address: must be the same as the customer location by default can be optionally overridden This topic covers such kind of UI and functional customization. The screenshot below illustrates the original view of the Financial Settings tab of the Sales Orders webpage. To this tab, near the Postal Code input UI field, you should add the View Bill-to Address action button. Figure: Viewing the original Financial Settings tab of the Sales Orders webpage The example shows how to create an action in a business logic controller (BLC, also referred to as a graph) extension and display the action button on the form control. By default, the action button is displayed on the form toolbar.
Customization Best Practices 289 You will add an action to the form control by performing the following actions: Creating a new extension library solution Extending the SOOrderEntry BLC with a new action button Customizing the Sales Orders webpage to display the action on the form instead of the toolbar Creating a New Extension Library First you should create the FormActionAEF extension library solution for an Acumatica ERP application. For details, see first four instructions in Creating an Extension Library Solution by Using the Bespoke Approach. Extending the SOOrderEntry BLC 1. Start MS Visual Studio and open the created FormActionAEF solution. 2. Right-click the FormActionAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type SOOrderEntryExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using PX.Data; using PX.Objects.SO; namespace FormActionAEF public class SOOrderEntryExtension : PXGraphExtension<SOOrderEntry> public PXAction<SOOrder> ViewOnMap; [PXButton] [PXUIField(DisplayName = "View Bill-To Address", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)] protected void viewonmap() MapRedirector map = SitePolicy.CurrentMapRedirector; SOBillingAddress addr = Base.Billing_Address.Select(); if (map!= null && addr!= null) map.showaddress(addr.countryid, addr.state, addr.city, addr.postalcode, addr.addressline1, addr.addressline2, addr.addressline3); 5. Build the FormActionAEF project. 6. Reopen or start your Acumatica ERP application instance. If you have any customization projects applied, on the Acumatica ERP application, select Undo Publish on the Customization menu of any webpage. 7. On the Acumatica ERP website, open the System > Customization > Manage > Customization Projects page. 8. In the Customization Projects webpage, click New. 9. In the New Project dialog that appears, type your project name, FormActionAEF, and click OK.
Customization Best Practices 290 10. Click the Add button and select File from File System. 11. In the Add Files pop-up window that appears, select the check box left of the created extension library file name you need to add to the project, and click Save. After adding a library file to a customization project, you do not need to publish the project to test the customization results, because the built extension library file is accessible for the current application instance through the BIN website folder. 12. On the Acumatica ERP website, open the Distribution > Sales Order > Work Area > Enter > Sales Orders page. The View Bill-To Address action should appear on the toolbar, as the following screenshot shows. Figure: Viewing the new action button on the toolbar In the following steps, you will change the page layout to display the action on the form next to a specific field rather than on the toolbar. Moving the Action Button to the Form 1. On the Acumatica ERP website, open the Distribution > Sales Order > Work Area > Enter > Sales Orders page, and enter page design mode. 2. On the Financial Settings tab, right-click the Bill-To Info area of the form, select Advanced controls, and click Button, as the following screenshot shows. Figure: Starting to add a button to a form
Customization Best Practices 291 3. Right-click the new button and click Control Tree, as the following screenshot shows. Figure: Adding a button to a form 4. In the Aspx Control Tree window, select Ext Props in the dropdown list at the right end of the toolbar and set the following properties of the selected PXButton control (see the screenshot below): AutocallBack-Enabled: True AutocallBack-Target: ds AutocallBack-Command: ViewOnMap Figure: Setting the properties of the PXButton control 5. Use the Down button to move the VirtualContainer1:PXButton node below the PostalCode:PXMaskEdit node in the control tree. 6. Select the PostalCode:PXMaskEdit node in the control tree, and click Add > Add Layout Rule. 7. Select the new PXLayoutRule node and set the following properties (as shown in the screenshot below):
Customization Best Practices 292 ControlSize: S Merge: True Figure: Setting the properties of the PXLayoutRule node 8. Select the VirtualContainer1:PXButton node in the control tree, and click Add > Add Layout Rule. 9. Click Down to move the new PXLayoutRule node below the VirtualContainer1:PXButton node. 10. Scroll to the top of the control tree, expand the phds:content node, expand the ds:pxdatasource node, and then select the CallBackCommands node. 11. Select Add > Add Item on the toolbar, ensure that CallbackCommands is specified as the collection and PXDSCallbackCommand as the item type, and click OK, as the following screenshot shows. Figure: Adding an item to the CallbackCommands collection 12. Select the new PXDSCallbackCommand node in the CallbackCommands list and set the following properties (see the screenshot below): Name: ViewOnMap Visible: False
Customization Best Practices 293 Figure: Setting the properties of the PXDSCallbackCommand node 13. Click Apply to Page. 14. By using the Customization menu validate and publish the project. As you can see on the page, the action button has moved from the toolbar to the form, on the Financial Settings tab next to the Postal Code box. Select any customer and click the Bill-To Address button to open the map, as the following screenshot illustrates. Figure: Viewing the Bill-To Address on the map
Customization Best Practices 294 Adding Buttons to the Grid Control A segment key is extended by the following segment: Description: Size Length: 5 Edit Mask: Unicode This example shows you how to manually reorder segment values by adding actions to the grid toolbar. You will add the actions to the grid of segmented key values. The user can use these actions to move a selected grid row up or down, thus changing the order of the segmented key values. By changing the order of segmented key values, the user can also change the order of the data records that use these segmented key values and are sorted by the segmented key values. You will create the described action by implementing the following steps: Creating a new extension library solution Extending the SegmentValue data access class (DAC) with a new field Extending the SegmentMaint business logic controller (BLC) with a new action and event handlers Customizing the Segmented Values webpage to display the action on the grid toolbar Testing the results Adding the extension library file to the customization project Creating a New Extension Library First you should create the GridActionsAEF extension library solution for an Acumatica ERP application. For details, see first four instructions in Creating an Extension Library Solution by Using the Bespoke Approach. Extending the SegmentValue DAC Follow the instructions below to extend the SOShipment DAC with the UsrSortOrder field and add this field to the database table: 1. Start MS Visual Studio and open the created GridActionsAEF solution. 2. Right-click the GridActionsAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type SegmentValueExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using PX.Data; using PX.Objects.CS; namespace GridActionsAEF public class SegmentValueExtension : PXCacheExtension<SegmentValue> #region UsrSortOrder public abstract class usrsortorder : IBqlField [PXDBInt()] public virtual int? UsrSortOrder get; set; #endregion
Customization Best Practices 295 5. Build the GridActionsAEF project. 6. Start or reopen your Acumatica ERP application. If you have any customization projects applied, on the Acumatica ERP application, select Undo Publish on the Customization menu of any webpage. 7. On the Acumatica ERP website, open the System > Customization > Manage > Customization Projects page. 8. On the Customization Projects webpage, click New. 9. On the New Project dialog box that appears, type your project name, such as GridActionsAEF, and click OK. 10. Select Add > Database Table Field; select SegmentValue as the table, SortOrder as the field name, and DBInt(int) as the field type; and click OK. See the following screenshot. Figure: Adding the SortOrder database field This new database field corresponds to the UsrSortOrder field, which you declared in the SegmentValueExt extension. 11. Save, validate and publish the project. Extending the SegmentMaint BLC 1. Start MS Visual Studio and open the GridActionsAEF extension library solution. 2. Right-click the GridActionsAEF project caption, select Add, and click New Item. 3. In the Add New Item window that appears, select the Class template, type SegmentMaintExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using PX.Data; using PX.Objects.CS; using System.Collections; namespace GridActionsAEF public class SegmentMaintExtension : PXGraphExtension<SegmentMaint> 5. Add the following code to the SegmentMaintExtension class to override the Values data view. public PXSelect<SegmentValue, Where<SegmentValue.dimensionID, Equal<Optional<Segment.dimensionID>>, And<SegmentValue.segmentID, Equal<Optional<Segment.segmentID>>>>,
Customization Best Practices 296 OrderBy<Asc<SegmentValueExtension.usrSortOrder>>> Values; This data view selects the SegmentValue data records related to the Segment data record through the DimensionID and SegmentID fields. The Optional parameter inserts the value from the Current property of the Segment cache (unless specific values are passed explicitly to the Select() method in the code). The retrieved SegmentValue data records are sorted by the UsrSortOrder field, which you declared in the SegmentValueExtension extension. 6. Add the following SegmentValue_UsrSortOrder_FieldDefaulting() event handler to the SegmentMaintExtension class. protected void SegmentValue_UsrSortOrder_FieldDefaulting( PXCache sender, PXFieldDefaultingEventArgs e) var row = (SegmentValue)e.Row; if (row!= null && Base.Segment.Current!= null && Base.Segment.Current.DimensionID == "INVENTORY" && Base.Segment.Current.Descr == "Size") e.newvalue = 1; PXResultset<SegmentValue> values = Values.Select(); if (values.count > 0) SegmentValue last = values[values.count - 1]; SegmentValueExtension lastext = PXCache<SegmentValue>.GetExtension<SegmentValueExtension>(last); e.newvalue = lastext.usrsortorder + 1; This event handler will be executed during assignment of the default value to the UsrSortOrder field. The logic works only for the segmented key with the INVENTORY dimension ID and the description set to Size. The event handler calculates the default value by incrementing the last assigned value. 7. Add the following ResortValues() method to the SegmentMaintExtension class. private PXResultset<SegmentValue> ResortValues( int? sortorder, out int currentindex) currentindex = -1; PXResultset<SegmentValue> values = Values.Select(); int i = 0; foreach (SegmentValue value in values) SegmentValueExtension valueext = PXCache<SegmentValue>.GetExtension<SegmentValueExtension>(value); if (valueext.usrsortorder == sortorder) currentindex = i; i++; valueext.usrsortorder = i; Values.Update(value); return values; This auxiliary method resets the UsrSortOrder to consecutive values in the data records retrieved by the Values data view. The values may not be consecutive if some SegmentValue data records were deleted. The method also returns an out parameter holding the actual value of the UsrSortOrder DAC field for the currently selected data record.
Customization Best Practices 297 8. Declare the Down action in the SegmentMaintExtension class in the following way. public PXAction<Segment> Down; [PXUIField(DisplayName = " ", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)] [PXButton] public virtual void down() if (Values.Current!= null) int currentvalueindex; SegmentValueExtension ext = PXCache<SegmentValue>.GetExtension<SegmentValueExtension>( Values.Current); PXResultset<SegmentValue> values = ResortValues( ext.usrsortorder, out currentvalueindex); if (currentvalueindex >= 0 && currentvalueindex < values.count - 1) SegmentValue current = values[currentvalueindex]; SegmentValue next = values[currentvalueindex + 1]; SegmentValueExtension currentext = PXCache<SegmentValue>.GetExtension<SegmentValueExtension>(current); currentext.usrsortorder++; SegmentValueExtension nextext = PXCache<SegmentValue>.GetExtension<SegmentValueExtension>(next); nextext.usrsortorder--; Values.Update(current); Values.Update(next); Values.View.Clear(); This action moves the selected data record down by one line in the grid, by swapping the position of the data record with that of the next data record. 9. Declare the Up action in the SegmentMaintExtension class in the following way. public PXAction<Segment> Up; [PXUIField(DisplayName = " ", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)] [PXButton] public virtual void up() if (Values.Current!= null) int currentvalueindex; SegmentValueExtension ext = PXCache<SegmentValue>.GetExtension<SegmentValueExtension>( Values.Current); PXResultset<SegmentValue> values = ResortValues(ext.UsrSortOrder, out currentvalueindex); if (currentvalueindex > 0) SegmentValue current = values[currentvalueindex]; SegmentValue prev = values[currentvalueindex - 1]; SegmentValueExtension currentext = PXCache<SegmentValue>.GetExtension<SegmentValueExtension>(current); currentext.usrsortorder--; SegmentValueExtension prevext = PXCache<SegmentValue>.GetExtension<SegmentValueExtension>(prev); prevext.usrsortorder++;
Customization Best Practices 298 Values.Update(current); Values.Update(prev); Values.View.Clear(); This action moves the selected data record up by one line in the grid, by swapping the data record's position with that of the previous data record. 10. Declare the following Segment_RowSelected() event handler in the SegmentMaintExtension class, overriding the event handler of the base SegmentMaint BLC. protected void Segment_RowSelected( PXCache sender, PXRowSelectedEventArgs e, PXRowSelected del) var row = (Segment)e.Row; if (row!= null && row.dimensionid == "INVENTORY" && row.descr == "Size") Up.SetVisible(true); Down.SetVisible(true); else Up.SetVisible(false); Down.SetVisible(false); if (del!= null) del(sender, e); This RowSelected event is raised after each request sent from the webpage to the server. This event handler usually tweaks the user interface settings of the fields. In this example, the event handler makes the Up and Down actions visible if the selected segmented key ID is INVENTORY and the description is set to Size. For all other filtering values, the event handler hides the actions. 11. Build the GridActionsAEF project. Customizing the Segmented Values Page 1. On the Acumatica ERP website, open the Configuration > Common Settings > Segmented Keys > Segmented Values page. 2. By using the Customization menu, open the GridActionsAEF customization project, and enter page design mode. 3. Right-click the details table, and then click Control Tree. 4. In the Aspx Control Tree window, select Ext Props in the drop-down list at the right end of the toolbar and set the KeepPosition property to True for the grid:pxgrid node, as the following screenshot shows.
Customization Best Practices 299 Figure: Setting the properties of the grid 5. By using Add > New Item, add two new items with ActionBar-CustomItems specified as collection and PXToolBarButton as item type, as the following screenshot shows. Figure: Adding buttons to the grid toolbar By implementing this step, you create two PXToolBarButton controls, for the Up and Down actions. In the following steps, you will set their properties, pointing these controls to invoke actions declared within the SegmentMaintExtension BLC extension. 6. For the first added item, set the following properties (as shown in the screenshot below): AutoCallBack-Command: Down AutoCallBack-Enabled: True AutoCallBack-Target: ds Image-Normal: main@arrowdown Text: Down Tooltip: Move Down
Customization Best Practices 300 Figure: Setting the properties of a PXToolBarButton node 7. For the second added item, set the following properties (as shown on the screenshot below): AutoCallBack-Command: Up AutoCallBack-Enabled: True AutoCallBack-Target: ds Image-Normal: main@arrowup Text: Up Tooltip: Move Up Figure: Setting the properties of a PXToolBarButton node 8. In the control tree, expand the phds:content node, expand the ds:pxdatasource node, and select the CallbackCommands node. 9. By using the Add > New Item command, add two new items with CallbackCommands specified as the collection and PXDSCallbackCommand as the item type. 10. For the first added item, set the following properties (as shown in the screenshot below): CommitChanges: True
Customization Best Practices 301 DependOnGrid: grid Name: Down Visible: False Figure: Setting the properties of a PXDSCallbackCommand node Setting the CommitChanges property to True enables sending changes from data records to the server before action delegate invocation. By setting the DependOnGrid property, you force the specified PXGrid control to synchronize the selected data record with the Current property value of the appropriate cache before invoking the action delegate. Note that Visible is set to False to hide the button from the page-level toolbar. If a property is not in the Base Props list, select Ext Props in the drop-down list on the toolbar. 11. For the second added item, set the following properties (as shown in the screenshot below): CommitChanges: True DependOnGrid: grid Name: Up Visible: False
Customization Best Practices 302 Figure: Setting the properties of a PXDSCallbackCommand node The properties for the Up action button are analogous to that of Down. 12. Click Apply to Page. 13. By using the Customization menu validate and publish the project. Testing the Results To test the extension that you developed, you need to create a new segment with the description set to Size for the segmented key with INVENTORY ID, as follows. 1. Open the Configuration > Common Settings > Segmented Keys > Segmented Keys page. 2. Select the INVENTORY segmented key. 3. Click Add Row on the grid toolbar and set the fields of the new data record as follows: Description: Size Length: 5 Edit Mask: Unicode 4. Save changes and click View Segment. 5. In the Segmented Values page that appears, the grid toolbar will include the action buttons that have Move Down and Move Up tooltips. 6. Add several segment values and use the action buttons to move the items up and down, as shown on the following screenshot.
Customization Best Practices 303 Figure: Viewing the Down and Up action buttons on the grid toolbar Adding the Extension Library File to the Customization Project 1. In the Acumatica ERP application, open the System > Customization > Manage > Customization Projects webpage and select the GridActionsAEF customization project. 2. Click the Add button and select File from File System. 3. On the Add Files pop-up window that appears, select the check box on the line with Bin \GridActionsAEF.dll, and click Save. Calculating Unbound DAC Fields at Run Time The screenshot below displays the original view of the Document Details tab of the Sales Order webpage. If you shift the columns of the details table to the left, you would see the PONbr column as the rightmost column in the table.
Customization Best Practices 304 Figure: Viewing the Document Details tab of the Sales Orders webpage SOLine is to be extended by the following unbound data access class (DAC) fields at the right side of the details table: Profit Margin Total Cost This example shows how to calculate unbound fields (fields that are not bound to the database) in a RowSelecting event handler. The event happens when the data record is retrieved from the database while the connection is still open. As the example demonstrates, to retrieve additional records from the database in the RowSelecting event handler, you should use a separate connection scope. In this example, you will implement the following steps: Creating a new extension library solution Extending the SOLine data access class (DAC) with the unbound Cost, Margin, and Profit fields Customizing the Sales Orders webpage to add the columns for the new fields to the grid Extending the SOOrderEntry business logic controller (BLC, also referred to as a graph) with the SOLine_RowSelecting event handler, which calculates the new field values Adding the extension library file to the customization project Creating a New Extension Library Solution First you should create the ConnectionScopeAEF extension library solution for an Acumatica ERP application. For details, see first four instructions in Creating an Extension Library Solution by Using the Bespoke Approach. Extending the SOLine DAC 1. Start MS Visual Studio and open the created ConnectionScopeAEF solution. 2. Right-click the ConnectionScopeAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type SOLineExtension as the Name, and click Add.
Customization Best Practices 305 4. Modify the code of the added file as follows. using PX.Data; using PX.Objects.SO; namespace ConnectionScopeAEF public class SOLineExtension : PXCacheExtension<SOLine> #region Cost public abstract class cost : IBqlField [PXDecimal(2)] [PXUIField(DisplayName = "Total Cost", IsReadOnly = true)] public decimal? Cost get; set; #endregion #region Margin public abstract class margin : IBqlField [PXString] [PXUIField(DisplayName = "Margin", IsReadOnly = true)] public string Margin get; set; #endregion #region Profit public abstract class profit : IBqlField [PXDecimal(2)] [PXUIField(DisplayName = "Profit", IsReadOnly = true)] public decimal? Profit get; set; #endregion The SOLineExtension DAC extension defines three unbound fields that are not present in the SOLine DAC. The fields are not bound to the database columns and their values will be calculated at run time in the RowSelecting event handler, which you will define in the next step. 5. Build the ConnectionScopeAEF project. Customizing the Sales Orders Page 1. Reopen or start your Acumatica ERP application instance. If you have any customization projects applied, on the Acumatica ERP application, select Undo Publish on the Customization menu of any webpage. 2. Open the Distribution > Sales Orders > Work Area > Enter > Sales Orders webpage. 3. On the Customization menu, select Open Customization Project. 4. In the Select Working Project dialog that appears, click New and add the new project name, ConnectionScopeAEF, to the Project Name field of the New Project dialog that appears, and then click OK. 5. On the Customization menu, select Enter Page Design Mode. 6. On the Sales Orders webpage in page design mode, right-click the details table under the Document Details tab, and then click Control Tree. 7. In the Aspx Control Tree window that appears, click Add > Add Column to Grid. In the popup window, select Cost as the data field and click OK, as the following screenshot shows. The column for the Cost field will be added to the grid.
Customization Best Practices 306 Figure: Adding the column for the Cost field to the grid 8. In the same way, add columns for the Margin and Profit fields. 9. Click Apply to Page. 10. By using the Customization menu, validate and publish the project. Extending the SOOrderEntry BLC 1. Start MS Visual Studio and open the created ConnectionScopeAEF solution. 2. Right-click the ConnectionScopeAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type SOOrderEntryExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using using using using System; PX.Data; PX.Objects.IN; PX.Objects.SO; namespace ConnectionScopeAEF public class SOOrderEntryExtension : PXGraphExtension<SOOrderEntry> protected void SOLine_RowSelecting(PXCache sender, PXRowSelectingEventArgs e) SOLine row = (SOLine)e.Row; if (row!= null) var extension = PXCache<SOLine>.GetExtension<SOLineExtension>(row); using (PXConnectionScope cs = new PXConnectionScope()) INItemSite rec = PXSelectReadonly<INItemSite, Where<INItemSite.inventoryID, Equal<Required<SOLine.inventoryID>>, And<INItemSite.siteID, Equal<Required<SOLine.siteID>>>>>.Select(Base, row.inventoryid, row.siteid);
Customization Best Practices 307 if (rec!= null) decimal avgcost = (rec.avgcost.hasvalue)? rec.avgcost.value : 0; extension.cost = (Math.Round(avgCost, 2) * row.orderqty).getvalueordefault(); else extension.cost = 0; if (row.unitprice!= null && row.unitprice!= 0) decimal? margin = (row.unitprice - (extension.cost / row.orderqty)) / row.unitprice; decimal? profit = (row.unitprice - (extension.cost / row.orderqty)) * row.orderqty; extension.margin = margin.getvalueordefault(). ToString("#0.##%"); extension.profit = profit.getvalueordefault(); else extension.margin = 0.ToString("#0.##%"); extension.profit = 0; The RowSelecting event handler is invoked while the connection is still open. To retrieve additional data from the database in the RowSelecting event handler, you have to use a separate connection scope. Otherwise, execution of a selection command will cause an error (because of the limitations imposed by ADO.NET). Note the use of the PXSelectReadonly BQL statement, which retrieves the data and doesn't merge it with the caches. 5. Build the ConnectionScopeAEF project. 6. Select a specific sales order record by using the Order Nbr. UI field, and scroll to see the new Profit, Margin, and Total Cost columns, as the following screenshot shows.
Customization Best Practices 308 Figure: The new columns on the grid Adding the Extension Library File to the Customization Project 1. In the Acumatica ERP application, open the System > Customization > Manage > Customization Projects webpage and select the ConnectionScopeAEF customization project. 2. Click the Add button and select File from File System. 3. In the Add Files pop-up window that appears, select the check box on the line with Bin \ConnectionScopeAEF.dll, and click Save. Extending the Base Business Logic This topic covers two separate examples of ways to extend the basic logic: 1. You will modify the Non-Stock Items webpage to: Verify each field value at the moment it is changed Verify field values just before the changes are saved to database The screenshot below displays the original view of the form area and the Packaging tab of the Non-Stock Items webpage.
Customization Best Practices 309 Figure: Viewing the Non-Stock Items webpage before changes 2. You will extend the base business logic of the Journal Transaction webpage to stop the automatic population of the debit or credit amounts in the adjusting transaction lines. Before you begin, you should explore the base behavior of the Journal Transactions webpage. Start by adding a new journal transaction, selecting an account, and setting the Debit Amount to 2000.00. Then add one more journal transaction, select a different account, and notice that the Credit Amount is set by default to 2000.00 to balance the batch, as the following screenshot illustrates. You will modify this webpage to work differently, so that if the user first inserts a debit entry, by default the Credit Amount value of the next transaction equals 0.00, and the user needs to add the required values manually. If the user first inserts a credit entry, the Debit Amount value of the next transaction should also equal 0.00 by default, and the user must add the needed values. Figure: Exploring the base behavior of the Journal Transactions webpage To implement the examples, you will complete the following steps: Creating a new extension library solution
Customization Best Practices 310 Extending the InventoryItem data access class (DAC) with new fields and adding these fields to the database table Extending the INComponent DAC with new fields and adding these fields to the database table Customizing the Non-Stock Items webpage Extending the NonStockItemMaint business logic controller (BLC) with new event handlers for the InventoryItem and INComponent DACs Overriding a method in the extension of the JournalEntry BLC Adding the extension library file to the customization project Creating a New Extension Library First, you should create the ExtendLogicAEF extension library solution for an Acumatica ERP application. For details, see the first four instructions in Creating an Extension Library Solution by Using the Bespoke Approach. Extending the InventoryItem DAC 1. Start MS Visual Studio and open the ExtendLogicAEF extension library solution that you created. 2. Right-click the ExtendLogicAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type InventoryItemExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using System; using PX.Data; using PX.Objects.IN; namespace ExtendLogicAEF public class InventoryItemExtension : PXCacheExtension<InventoryItem> #region UsrBaseItemMinWeight public abstract class usrbaseitemminweight : IBqlField [PXDBDecimal(6)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Min. Weight")] public decimal? UsrBaseItemMinWeight get; set; #endregion #region UsrBaseItemMinVolume public abstract class usrbaseitemminvolume : IBqlField [PXDBDecimal(6)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Min. Volume")] public decimal? UsrBaseItemMinVolume get; set; #endregion #region UsrProjectItem public abstract class usrprojectitem : IBqlField [PXDBBool] [PXUIField(DisplayName = "Project Item")]
Customization Best Practices 311 public bool? UsrProjectItem get; set; #endregion #region UsrSpecification public abstract class usrspecification : IBqlField [PXDBString(30, IsUnicode = true)] [PXUIField(DisplayName = "Specification")] public string UsrSpecification get; set; #endregion 5. Build the ExtendLogicAEF project. 6. Reopen or start your Acumatica ERP application instance. If you have any customization projects applied, on the Acumatica ERP application, select Undo Publish on the Customization menu of any webpage. 7. Open the System > Customization > Manage > Customization Projects webpage. 8. On the Customization Projects webpage, click New. 9. On the New Project dialog that appears, type your project name, ExtendLogicAEF, and click OK. 10. Click Add > Database Table Field, specify the following values in the boxes of the pop-up window, and click OK: Table Name: InventoryItem Field Name: BaseItemMinWeight Field Type: DBDecimal(decimal) Length & Precision: 25 Precision: 6 11. Click Add > Database Table Field, specify the following values in the boxes of the pop-up window, and click OK: Table Name: InventoryItem Field Name: BaseItemMinVolume Field Type: DBDecimal(decimal) Length & Precision: 25 Precision: 6 12. Click Add > Database Table Field, specify the following values in the boxes of the pop-up window, and click OK: Table Name: InventoryItem Field Name: ProjectItem Field Type: DBBool(bit) 13. Click Add > Database Table Field, specify the following values in the boxes of the pop-up window, and click OK: Table Name: InventoryItem Field Name: Specification
Customization Best Practices 312 Field Type: DBString(nvarchar) Length: 30 14. Validate and publish the project. Extending the INComponent DAC 1. Start MS Visual Studio and open the ExtendLogicAEF extension library solution that you created. 2. Right-click the ExtendLogicAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type INComponentExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using System; using PX.Data; using PX.Objects.IN; namespace ExtendLogicAEF public class INComponentExtension : PXCacheExtension<INComponent> #region UsrMinQty public abstract class usrminqty : IBqlField [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Min. Quantity")] public decimal? UsrMinQty get; set; #endregion #region UsrMaxQty public abstract class usrmaxqty : IBqlField [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Max. Quantity")] public decimal? UsrMaxQty get; set; #endregion 5. Build the ExtendLogicAEF project. 6. Reopen or start your Acumatica ERP application instance. 7. Open the System > Customization > Manage > Customization Projects page and select the ExtendLogicAEF project. 8. Click Add > Database Table Field, specify the following values in the boxes of the pop-up window, and click OK: Table Name: INComponent Field Name: MinQty Field Type: DBDecimal(decimal) Length & Precision: 25 Precision: 6
Customization Best Practices 313 9. Click Add > Database Table Field, specify the following values in the boxes of the pop-up window, and click OK: Table Name: INComponent Field Name: MaxQty Field Type: DBDecimal(decimal) Length & Precision: 25 Precision: 6 10. Validate and publish the project. Customizing the Non-Stock Items Webpage In this step, you add input controls and grid columns to represent the fields added in DAC extensions: 1. On the Acumatica ERP application, open the Distribution > Inventory > Work Area > Manage > Non-Stock Items webpage. 2. By using the Customization menu, open the ExtendLogicAEF customization project, and enter page design mode. 3. Add the input control for the UsrBaseItemMinWeight field to the Packaging tab by right-clicking the tab area and selecting Add Input Control, as the following screenshot shows. Figure: Adding the input control for the UsrBaseItemMinWeight field 4. Add the input control for the UsrBaseItemMinVolume field to the Packaging tab, as the following screenshot shows. Figure: Adding the input control for the UsrBaseItemMinVolume field 5. Right-click the tab area and select Control Tree. 6. In Aspx Control Tree, arrange the input controls under the Packaging:PXTabItem node, as the following screenshot shows.
Customization Best Practices 314 Figure: Arranging input controls on the Packaging tab 7. Select the UsrBaseItemMinWeight:PXNumberEdit subnode, and set the CommitChanges property to True. Setting CommitChanges to True enables information to be posted to the server when the UI field is updated. You set CommitChanges to True when you need to trigger server logic and display an error or warning for the field as soon as its input control loses focus. If CommitChanges is set to False, the server logic will be executed anyway (for example, when the user saves the changes). 8. Select the UsrBaseItemMinVolume:PXNumberEdit subnode, and set the CommitChanges property to True. 9. Select the BaseItemVolume:PXNumberEdit subnode, and set the CommitChanges property to True. 10. Expand the Deferred Revenue:PXTabItem node, and select the PXGridComponents:PXGrid node. 11. Add the grid column for the UsrMinQty subnode by clicking Add > Add Column to Grid, entering UsrMinQty as the data field, and clicking OK. 12. Add the grid column for the UsrMaxQty subnode. 13. Move the new grid column subnodes up so that the columns are arranged as shown in the screenshot below.
Customization Best Practices 315 Figure: Arranging grid columns 14. Click Apply to Page. 15. Add the input control for the UsrProjectItem data field to the main form by right-clicking the form area and selecting Add Input Control, as the following screenshot shows. Figure: Adding the input control for the UsrProjectItem data field 16. Add the input control for the UsrSpecification data field to the main form, as the following screenshot shows. Figure: Adding the input control for the UsrSpecification data field
Customization Best Practices 316 17. Open the Aspx Control Tree window, and move the UsrProjectItem:PXCheckBox subnode below ItemStatus:PXDropDown and the UsrSpecification:PXTextEdit subnode below Descr:PXTextEdit. 18. Add two layout rules (by selecting Add and clicking Add Layout Rule), and arrange them as the following screenshot shows. Figure: Arranging input controls on the form 19. Set the Merge property to True for the first layout rule (the Rule7:PXLayoutRule subnode on the screenshot above) and the ColumnSpan property to 2 for the third layout rule (Rule8:PXLayoutRule). 20. Click Apply to Page. 21. By using the Customization menu, validate and publish the project. Extending the NonStockItemMaint BLC 1. Start MS Visual Studio and open the ExtendLogicAEF extension library solution. 2. Right-click the ExtendLogicAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type NonStockItemMaintExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using PX.Data; using PX.Objects.IN; namespace ExtendLogicAEF public class NonStockItemMaintExtension : PXGraphExtension<NonStockItemMaint> 5. Add the definitions of the event handlers to the NonStockItemMaintExtension class as follows. protected void InventoryItem_UsrBaseItemMinWeight_FieldVerifying( PXCache sender, PXFieldVerifyingEventArgs e) if (e.newvalue!= null && (decimal)e.newvalue < 0)
Customization Best Practices 317 throw new PXSetPropertyException( "Min. Weight must be greater or equal to 0"); protected void InventoryItem_UsrBaseItemMinVolume_FieldVerifying( PXCache sender, PXFieldVerifyingEventArgs e) InventoryItem row = e.row as InventoryItem; if (e.newvalue!= null && row.baseitemvolume!= null && (decimal)e.newvalue > row.baseitemvolume) e.newvalue = row.baseitemvolume; throw new PXSetPropertyException<InventoryItem.baseItemVolume>( "Volume must be greater or equal to Min. Volume", PXErrorLevel.Warning); These two event handlers verify the values of the UsrBaseItemMinWeight and UsrBaseItemMinVolume data fields. If the UsrBaseItemMinWeight value is not greater than zero, the event handler raises an error. The UsrBaseItemMinVolume value should be less or equal to the BaseItemVolume value; otherwise, the event handler raises a warning. Note how the exception instance is created in the second event handler. Because the warning is attached to a field other than the current one, the target field is specified in the type parameter. PXErrorLevel.Warning is passed as the second parameter in the constructor to make the error level warning instead of the default error. 6. Add the definition of the RowUpdating event handler to the NonStockItemMaintExt class as follows. protected void INComponent_RowUpdating( PXCache sender, PXRowUpdatingEventArgs e) INComponent row = e.newrow as INComponent; INComponentExtension ext = PXCache<INComponent>.GetExtension<INComponentExtension>(row); INComponent oldrow = e.row as INComponent; if (!sender.objectsequal<incomponent.qty, INComponentExtension.usrMinQty, INComponentExtension.usrMaxQty>(row, oldrow)) if (row.qty!= null && ext.usrminqty!= null && row.qty < ext.usrminqty) sender.raiseexceptionhandling<incomponent.qty>( row, row.qty, new PXSetPropertyException( "Quantity must be greater or equal to Min. Quantity.")); e.cancel = true; if (row.qty!= null && ext.usrmaxqty!= null && row.qty > ext.usrmaxqty) row.qty = ext.usrmaxqty; sender.raiseexceptionhandling<incomponent.qty>( row, row.qty, new PXSetPropertyException( "Specified Quantuty was too big.", PXErrorLevel.Warning));
Customization Best Practices 318 The above code implements verification logic that involves multiple data fields and, therefore, should be placed in the RowUpdating event handler rather than the FieldVerifying one. If any of the Qty, UsrMinQty, and UsrMaxQty fields has changed (which is checked through the ObjectsEqual<>() method), the event handler verifies that Qty is not less than UsrMinQty and not greater than UsrMaxQty. If Qty is less than UsrMinQty, the event handler considers this situation an error and sets e.cancel to true, to stop the processing of the event handler's chain for this data record. However, if Qty is greater than UsrMaxQty, the event handler issues only a warning and doesn't stop the processing of other event handlers in the chain. 7. Add the definition of the RowPersisting event handler to the NonStockItemMaintExt class as follows. protected void InventoryItem_RowPersisting( PXCache sender, PXRowPersistingEventArgs e) InventoryItem row = e.row as InventoryItem; InventoryItemExtension ext = PXCache<InventoryItem>.GetExtension<InventoryItemExtension>(row); if (ext.usrprojectitem == true && string.isnullorwhitespace(ext.usrspecification)) throw new PXRowPersistingException( typeof(inventoryitemextension.usrspecification).name, ext.usrspecification, "For Project Item there must be a Specification Entered!"); if (row.issplitted == true) if (string.isnullorempty(row.deferredcode)) sender.raiseexceptionhandling<inventoryitem.deferredcode>( e.row, row.deferredcode, new PXSetPropertyException<InventoryItem.deferredCode>( PX.Data.ErrorMessages.FieldIsEmpty)); if (row.totalpercentage!= 100) sender.raiseexceptionhandling<inventoryitem.totalpercentage>( e.row, row.totalpercentage, new PXSetPropertyException( Messages.SumOfAllComponentsMustBeHundred)); This event handler fires when changes are being saved to the database. It ensures that the UsrSpecification field is set to a value when the UsrProjectItem field is true, and throws a PXRowPersistingException exception otherwise. Also, when IsSplitted is true, the event handler verifies that DeferredCode is set and TotalPercentage equals 100, and it raises exceptions otherwise. Any exception raised by this event handler results in the commit operation being canceled. 8. Build the ExtendLogicAEF project. To test the extension, open the Distribution > Inventory > Work Area > Manage > Non-Stock Items webpage of the Acumatica ERP application. As an example that raises one of the exceptions of the RowPersisting event handler, on the Packaging tab, set Min. Weight to a value less than zero, or attempt to set Min. Volume to a value greater than the Volume. You will see error and warning marks, as shown in the following screenshot.
Customization Best Practices 319 Figure: Triggering the error and warning indications on the Packaging tab If you select the Project Item check box on this form, leave the Specification UI field empty, and click Save, you will see the error message box, as the following screenshot shows, and the data record won't be saved. Figure: Invoking an error indication when you save the data record Finally, open the Deferred Revenue tab. If you select a value in the Deferral Code UI field, select the Split into Components check box, clear the Deferral Code value, and click Save, you will see the appropriate error indication, as shown on the following screenshot.
Customization Best Practices 320 Figure: Viewing the error indication when you save the data record Overriding a Method in the Extension of the JournalEntry BLC As described earlier, you also need to change the business logic on the Journal Transactions webpage to stop the automatic population of the debit or credit amounts in the adjusting transaction lines. To implement the new behavior of this webpage, you should declare the PopulateSubDescr override method. To do this, follow the instructions below: 1. Start MS Visual Studio and open the created ExtendLogicAEF solution. 2. Right-click the ExtendLogicAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type JournalEntryExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using System; using PX.Data; using PX.Objects.GL; namespace ExtendLogicAEF public class JournalEntryExtension : PXGraphExtension<JournalEntry> [PXOverride] public void PopulateSubDescr (PXCache sender, GLTran Row, bool ExternalCall) decimal difference = (Base.BatchModule.Current.CuryCreditTotal?? decimal.zero) (Base.BatchModule.Current.CuryDebitTotal?? decimal.zero); if (difference!= 0) if (Row.CuryCreditAmt == Math.Abs(difference)) Row.CuryCreditAmt = 0; else if (Row.CuryDebitAmt == Math.Abs(difference)) Row.CuryDebitAmt = 0;
Customization Best Practices 321 This code extends the PopulateSubDescr() method, which is defined in the JournalEntry BLC. The method is called by the RowUpdated and RowInserted event handlers for the GLTran DAC cache. Because the method does not have an additional parameter, it is called after the base version of the method is called. 5. Build the ExtendLogicAEF project. You can test the result on the Finance > General Ledger > Work Area > Enter > Journal Transactions webpage of the Acumatica ERP application. Add a new journal transaction, select an account, and set the Debit Amount to 2000.00. Add one more journal transaction and select a different account. Without the extension, Credit Amount would have been set to 2000.00 to balance the batch. However, the extension sets it to 0.00, so that the batch will be saved only if the user enters the balancing value manually. The error will be indicated near the Debit Total UI field, as shown in the following screenshot. Figure: Testing the results on the Journal Transactions page You should also test the case when the user first adds a transaction with a Credit Amount. Then the Debit Amount must equal 0.00 by default. Adding the Extension Library File to the Customization Project 1. In the Acumatica ERP application, open the System > Customization > Manage > Customization Projects webpage and select the ExtendLogicAEF customization project. 2. Click the Add button and select File from File System. 3. In the Add Files pop-up window that appears, select the check box on the line with Bin \ExtendLogicAEF.dll, and click Save. Extending the Multi-Level Document Workflow You can analyze and extend the typical multi-level document workflow, shown in the figure below, while performing the instructions in this topic.
Customization Best Practices 322 Figure: Analyzing the multi-level document workflow The screenshot below displays the original view of the Sales Order webpage and the Document Details tab. (In this case, sales order number 000586 is being displayed along with its order lines.) If you scroll to the right, you can see PO Nbr. as the rightmost column in the table. Figure: Viewing the Sales Orders webpage before customization The screenshot below displays the original view of the Journal Transactions webpage with the transaction generated for the Sales Order. If you shift the columns of the details table to the left, you would see the Non Billable check box column as the rightmost column in the table.
Customization Best Practices 323 Figure: Viewing the Journal Transactions webpage before changes The example in this topic shows how to copy a field from a sales order record to a journal transaction record. Since no process directly connects a sales order and journal transactions, you have to extend three actions and then pass the field first from the sales order to the shipment, then to the invoice, and finally to the journal transactions. So you will add the field to all these data records by extending data access classes (DACs) and override the processes that connect them in the application. To implement this example, you will complete the following steps: Creating a new extension library solution Extending the SOLine, SOShipLine, ARTran, and GLTran DACs with the UsrCountryOfOrigin user data field and adding the field to the corresponding database tables Customizing the user interface of the Sales Orders, Shipments, Invoices, and Journal Transactions webpages to show the UsrCountryOfOrigin user data field as a grid column Extending the SOOrderEntry business logic controller (BLC, also called as a graph) by overriding an action Extending the SOShipmentEntry BLC by overriding an action Extending the ARReleaseProcess BLC by overriding a method Testing the results Adding the extension library file to the customization project Creating a New Extension Library Solution First, you should create the MultiLevelDocsAEF extension library solution for the Acumatica ERP application. For details, see first four instructions in Creating an Extension Library Solution by Using the Bespoke Approach. Extending the DACs with a New Field 1. Start MS Visual Studio and open the MultiLevelDocsAEF solution that you created. 2. Right-click the MultiLevelDocsAEF project caption, select Add, and click New Item.
Customization Best Practices 324 3. In the Add New Item window that appears, select the Class template, type SOLineExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using PX.Data; using PX.Objects.CS; using PX.Objects.SO; namespace MultiLevelDocsAEF public class SOLineExtension : PXCacheExtension<SOLine> #region UsrCountryOfOrigin public abstract class usrcountryoforigin : IBqlField [PXDBString(2, IsFixed = true, IsUnicode = true)] [PXUIField(DisplayName = "Country of Origin")] [PXSelector(typeof(Country.countryID), DescriptionField = typeof(country.description))] public string UsrCountryOfOrigin get; set; #endregion The code above (as well as the similar code in the next three code samples) defines a databasebound string field that holds the identifier of the Country data record. In the user interface, the field will be represented by a lookup control because of the PXSelector attribute. 5. Right-click the MultiLevelDocsAEF project caption, select Add, and click New Item. 6. In the Add New Item window that appears, select the Class template, type SOShipLineExtension as the Name, and click Add. 7. Modify the code of the added file as follows. using PX.Data; using PX.Objects.CS; using PX.Objects.SO; namespace MultiLevelDocsAEF public class SOShipLineExtension : PXCacheExtension<SOShipLine> #region UsrCountryOfOrigin public abstract class usrcountryoforigin : IBqlField [PXDBString(2, IsFixed = true, IsUnicode = true)] [PXUIField(DisplayName = "Country of Origin")] [PXSelector(typeof(Country.countryID), DescriptionField = typeof(country.description))] public string UsrCountryOfOrigin get; set; #endregion 8. Right-click the MultiLevelDocsAEF project caption, select Add, and click New Item. 9. In the Add New Item window that appears, select the Class template, type ARTranExtension as the Name, and click Add.
Customization Best Practices 325 10. Modify the code of the added file as follows. using PX.Data; using PX.Objects.AR; using PX.Objects.CS; namespace MultiLevelDocsAEF public class ARTranExtension : PXCacheExtension<ARTran> #region UsrCountryOfOrigin public abstract class usrcountryoforigin : IBqlField [PXDBString(2, IsFixed = true, IsUnicode = true)] [PXUIField(DisplayName = "Country of Origin")] [PXSelector(typeof(Country.countryID), DescriptionField = typeof(country.description))] public string UsrCountryOfOrigin get; set; #endregion 11. Right-click the MultiLevelDocsAEF project caption, select Add, and click New Item. 12. In the Add New Item window that appears, select the Class template, type GLTranExtension as the Name, and click Add. 13. Modify the code of the added file as follows. using PX.Data; using PX.Objects.CS; using PX.Objects.GL; namespace MultiLevelDocsAEF public class GLTranExtension : PXCacheExtension<GLTran> #region UsrCountryOfOrigin public abstract class usrcountryoforigin : IBqlField [PXDBString(2, IsFixed = true, IsUnicode = true)] [PXUIField(DisplayName = "Country of Origin")] [PXSelector(typeof(Country.countryID), DescriptionField = typeof(country.description))] public string UsrCountryOfOrigin get; set; #endregion 14. Build the MultiLevelDocsAEF project. 15. Reopen or start your Acumatica ERP application instance. If you have any customization projects applied, on the Acumatica ERP application, select Undo Publish on the Customization menu of any webpage. 16. Open the System > Customization > Manage > Customization Projects webpage. 17. On the Customization Projects webpage, click New. 18. On the New Project dialog that appears, type your project name, MultiLevelDocsAEF, and click OK.
Customization Best Practices 326 19. Click Add and select Database Table Field, and then specify the following values in the pop-up window boxes (click OK to save changes), as the screenshot below shows: Table Name: SOLine Field Name: CountryOfOrigin Field Type: DBString(nchar) Length: 2 Figure: Adding the CountryOfOrigin field to the database Note that the database field name is specified without the Usr prefix. 20. Repeat instruction 19 for the SOShipLine, ARTran, and GLTran tables to add the CountryOfOrigin field to these tables. Use the specified field name, field type, and length values for all the tables; only the table name changes. 21. Validate and publish the project. Customizing the Webpages 1. Open the Distribution > Sales Orders > Work Area > Enter > Sales Orderswebpage. 2. On the Sales Orders webpage, by using the Customization menu, open the MultiLevelDocsAEF customization project, and enter page design mode. 3. Right-click the details table on the Document Details tab, and then click Control Tree. 4. Click Add > Add Column to Grid, and in the pop-up window, select UsrCountryOfOrigin as the data field and click OK, as the following screenshot shows.
Customization Best Practices 327 Figure: Adding the UsrCountryOfOrigin column to the grid 5. Click Apply to Page. 6. On the Customization menu, click Save Project to Database. 7. Open the Distribution > Sales Orders > Work Area > Enter > Shipments webpage. 8. Repeat instructions 2 6. 9. Open the Distribution > Sales Orders > Work Area > Enter > Invoices webpage. 10. Repeat instructions 2 6. 11. Open the Finance > General Ledger > Work Area > Enter > Journal Transactions webpage. 12. Repeat instructions 2 6. 13. By using the Customization menu, validate and publish the project. Extending the SOOrderEntry BLC 1. Start MS Visual Studio and open the MultiLevelDocsAEF extension library solution. 2. Right-click the MultiLevelDocsAEF project caption, select Add, and click New Item. 3. On the Add New Item window that appears, select the Class template, type SOOrderEntryExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using using using using using System; System.Collections; PX.Data; PX.Objects.IN; PX.Objects.SO; namespace MultiLevelDocsAEF public class SOOrderEntryExtension : PXGraphExtension<SOOrderEntry> public PXAction<SOOrder> action; [PXButton]
Customization Best Practices 328 [PXUIField(DisplayName = "Actions", MapEnableRights = PXCacheRights.Select)] protected IEnumerable Action(PXAdapter adapter, [PXIntList(new int[] 1, 2, 3, 4, 5, new string[] "Create Shipment", "Apply Assignment Rules", "Create Invoice", "Post Invoice to IN", "Create Purchase Order" ), PXInt] int? actionid, [PXDate] DateTime? shipdate, [PXSelector(typeof(INSite.siteCD))] string sitecd, [SOOperation.ListAttribute] string operation, [PXString] string ActionName) if (actionid == 1) PXGraph.InstanceCreated.AddHandler<SOShipmentEntry>((graph) => graph.rowinserting.addhandler<soshipline>((sender, e) => SOLine line = PXResult<SOLine>.Current; SOLineExtension lineext = PXCache<SOLine>.GetExtension<SOLineExtension>(line); if (line!= null && lineext.usrcountryoforigin!= null) SOShipLineExtension ext = PXCache<SOShipLine>. GetExtension<SOShipLineExtension>((SOShipLine)e.Row); ext.usrcountryoforigin = lineext.usrcountryoforigin; ); ); return Base.action.Press(adapter); This code overrides the action that is defined in the SOOrderEntry BLC. The second parameter of the Action() implementation method is the ID of the action selected by the user. You need to extend the logic of the Create Shipment action, so in the method, you make sure that actionid equals 1. You extend the logic of the action by adding the RowInserting event handler for the SOShipLine DAC to the SOShipmentEntry graph. Because the SOShipmentEntry BLC instance is created within the base action delegate and is not available until it is executed, you add a delegate to the static InstanceCreated collection of the PXGraph class. Within the delegate, which executes when the SOShipmentEntry BLC instance is initialized, you add an event handler to the RowInserting collection of the SOShipmentEntry BLC instance. In the RowInserting event handler, you take the SOLine data record that is being processed, obtain the extension record for it and for the SOShipLine data record, and copy the UsrCountryOfOrigin value from one extension record to another. Finally, you invoke the base action. During its execution, the action creates an instance of the SOShipmentEntry graph, to which the RowInserting handler for SOShipLine is attached. 5. Build the MultiLevelDocsAEF project. Extending the SOShipmentEntry BLC 1. Start MS Visual Studio and open the created MultiLevelDocsAEF solution.
Customization Best Practices 329 2. Right-click the MultiLevelDocsAEF project caption, select Add, and click New Item. 3. In the Add New Item window that appears, select the Class template, type SOShipmentEntryExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using using using using System.Collections; PX.Data; PX.Objects.AR; PX.Objects.SO; namespace MultiLevelDocsAEF public class SOShipmentEntryExtension : PXGraphExtension<SOShipmentEntry> public PXAction<SOShipment> action; [PXButton] [PXUIField(DisplayName = "Actions", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select)] protected IEnumerable Action( PXAdapter adapter, [PXIntList(new int[] 1, 2, 3, 4, 5, 6, 7, 8, 9, new string[] "Confirm Shipment", "Create Invoice", "Post Invoice to IN", "Apply Assignment Rules", "Correct Shipment", "Create Drop-Ship Invoice", "Print Labels", "Get Return Labels", "Cancel Return" ), PXInt] int? actionid, [PXString] string ActionName) if (actionid == 2) PXGraph.InstanceCreated.AddHandler<SOInvoiceEntry>((graph) => graph.rowinserting.addhandler<artran>((sender, e) => SOShipLine line = PXResult<SOShipLine>.Current; SOShipLineExtension lineext = PXCache<SOShipLine>.GetExtension<SOShipLineExtension>(line); if (line!= null && lineext.usrcountryoforigin!= null) ARTranExtension ext = PXCache<ARTran>. GetExtension<ARTranExtension>((ARTran)e.Row); ext.usrcountryoforigin = lineext.usrcountryoforigin; ); ); return Base.action.Press(adapter); This event handler replicates the pattern that the SOOrderEntryExtension extension employs to override the action. This time, you add the InstanceCreated handler for the SOInvoiceEntry graph and, in this handler, the RowInserting handler for the ARTran DAC. The RowInserting
Customization Best Practices 330 handler again copies the UsrCountryOfOrigin value this time from the extension of SOShipLine to the extension of ARTran. 5. Build the MultiLevelDocsAEF project. Extending the ARReleaseProcess BLC 1. Start MS Visual Studio and open the MultiLevelDocsAEF solution. 2. Right-click the MultiLevelDocsAEF project caption, select Add, and click New Item. 3. In the Add New Item window that appears, select the Class template, type ARReleaseProcessExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using using using using using System; System.Collections.Generic; PX.Data; PX.Objects.AR; PX.Objects.GL; namespace MultiLevelDocsAEF public class ARReleaseProcessExtension : PXGraphExtension<ARReleaseProcess> [PXOverride] public List<ARRegister> ReleaseDocProc( JournalEntry je, ARRegister ardoc, List<Batch> pmbatchlist, Func<JournalEntry, ARRegister, List<Batch>, List<ARRegister>> del) je.rowinserting.addhandler<gltran>((sender, e) => ARTran tran = PXResult<ARTran>.Current; if (tran!= null) ARTranExtension tranext = PXCache<ARTran>.GetExtension<ARTranExtension>(tran); if (tranext.usrcountryoforigin!= null) GLTranExtension ext = PXCache<GLTran>.GetExtension <GLTranExtension>((GLTran)e.Row); ext.usrcountryoforigin = tranext.usrcountryoforigin; ); return del(je, ardoc, pmbatchlist); ReleaseDocProc is a virtual method, not an action (although it is invoked by the release action). So to be able to invoke the base method after your code, you define the extension method with the additional del parameter, in which you receive the delegate of the base method. This time, the graph instance to which you add the event handler is available as the je parameter. You add the RowInserting event handler for the GLTran DAC to this graph instance and invoke the base method. The RowInserting handler again copies the UsrCountryOfOrigin value this time from the extension of ARTran to the extension of GLTran. Thus, the UsrCountryOfOrigin value finally has been copied from the sales order record to the journal transaction record. 5. Build the MultiLevelDocsAEF project.
Customization Best Practices 331 Testing the Results 1. In the Acumatica ERP application, open the Distribution > Sales Orders > Work Area > Enter > Sales Orders webpage, and select the sales order number 000586 (see 1 in the screenshot below). 2. On the Document Details tab, enter values in the Country of Origin column (2). Save the document. 3. Select Actions > Create Shipment (3). Figure: Creating a shipment from the Sales Orders webpage Once you click Create Shipment, you will be redirected to the Shipments webpage. You can see that the Country of Origin column in the grid on the Document Details tab contains the values that you specified on the Sales Orders webpage. 4. On the Shipments webpage, select Actions > Confirm Shipment, as the following screenshot illustrates.
Customization Best Practices 332 Figure: Confirming the shipment 5. Select Actions > Prepare Invoice. Once the invoice is created, you will be redirected to the Invoices webpage. You can see that the Country of Origin column of the grid contains the values that you specified on the Sales Orders webpage. 6. On the Invoices webpage, select Actions > Release, as the following screenshot illustrates. Figure: Releasing the invoice 7. Open the Finance > General Ledger > Work Area > Enter > Journal Transactions webpage.
Customization Best Practices 333 8. Select AR as the Module and select the largest number as the Batch Number (see the screenshot below). The grid will include the journal transactions created during the release of the invoice. You can see that the Country of Origin column of the grid contains the values that you specified on the Sales Orders webpage. Figure: Analyzing the Journal Transactions webpage Adding the Extension Library File to the Customization Project 1. In the Acumatica ERP application, open the System > Customization > Manage > Customization Projectswebpage and select the MultiLevelDocsAEF customization project. 2. Click the Add button, and select File from File System. 3. In the Add Files pop-up window that appears, select the check box on the line with Bin \MultiLevelDocsAEF.dll, and click Save. Extending the Persist() Method The screenshot below displays the original view of the Activities tab of the Cases webpage. The new activity is not created when the Case is updated.
Customization Best Practices 334 Figure: Viewing the Activities tab of the Cases webpage before changes This example shows how to create a new activity every time when Status, Contact or Owner field values are updated for a Case. As the example demonstrates, to safely override the Persist() method of a business logic controller (BLC, also referred to as a graph), you have to execute the code in the new transaction scope. In this example, you will implement the following steps: Creating a new extension library solution Extending the CRCase data access class (DAC) with the unbound CreateActivity field Extending the CRCaseMaint BLC with the new event handlers and the overriden Persist() method Testing the results on the Cases webpage Adding the extension library file to the customization project Creating a New Extension Library First you should create the OverridePersistAEF extension library solution for an Acumatica ERP application. For details, see first four instructions in Creating an Extension Library Solution by Using the Bespoke Approach. Extending the CRCase DAC 1. Start MS Visual Studio and open the created OverridePersistAEF extension library solution. 2. Right-click the OverridePersistAEF project caption, select Add, and click New Item. 3. In the Add New Item window that appears, select the Class template, type CRCaseExtension as the Name, and click Add. 4. Modify the code of the added file as follows. using PX.Data; using PX.Objects.CR; namespace OverridePersistAEF public class CRCaseExtension : PXCacheExtension<CRCase>
Customization Best Practices 335 #region CreateActivity public abstract class createactivity : IBqlField [PXBool] public virtual bool? CreateActivity get; set; #endregion In this extension on the CRCase DAC, you add one new Boolean field, CreateActivity. The field is not bound to the database, because it is marked with the PXBool attribute (not PXDBBool). Since the field is not bound to the database, you don't have to add the Usr prefix to its name. 5. Build the OverridePersistAEF project. Extending the CRCaseMaint BLC 1. Within the OverridePersistAEF solution, right-click the OverridePersistAEF project caption, select Add, and click New Item. 2. In the Add New Item window that appears, select the Class template, type CRCaseMaintExtension as the Name, and click Add. 3. Modify the code of the added file as follows. using PX.Data; using PX.Objects.CR; namespace OverridePersistAEF public class CRCaseMaintExtension : PXGraphExtension<CRCaseMaint> protected void CRCase_ContactID_FieldUpdated( PXCache sender, PXFieldUpdatedEventArgs e) CRCaseExtension ext = PXCache<CRCase>.GetExtension<CRCaseExtension>((CRCase)e.Row); ext.createactivity = true; protected void CRCase_OwnerID_FieldUpdated( PXCache sender, PXFieldUpdatedEventArgs e) CRCaseExtension ext = PXCache<CRCase>.GetExtension<CRCaseExtension>((CRCase)e.Row); ext.createactivity = true; protected void CRCase_Status_FieldUpdated( PXCache sender, PXFieldUpdatedEventArgs e) CRCaseExtension ext = PXCache<CRCase>.GetExtension<CRCaseExtension>((CRCase)e.Row); ext.createactivity = true; With these event handlers, you subscribe to the FieldUpdated event for the ContactID, OwnerID, and Status fields of the CRCase DAC. Each of these event handlers obtains the instance of the CRCaseExtension extension DAC and sets the CreateActivity field to true. The extension instance is obtained as usual by invocation of the GetExtension<>() method (the static one in this case) of the PXCache class. Because of the code above, a modification of any of the ContactID, OwnerID, and Status fields sets the CreateActivity field to true.
Customization Best Practices 336 4. Add the following definition of the Persist() method to the CRCaseMaintExtension class. [PXOverride] public void Persist(Action del) EPActivity activity = null; CRCase caserow = Base.Case.Current; CRCaseExtension ext = PXCache<CRCase>.GetExtension<CRCaseExtension>(caseRow); try using (PXTransactionScope ts = new PXTransactionScope()) if (ext.createactivity == true) activity = Base.Activities.Cache.Insert() as EPActivity; activity.classid = CRActivityClass.Activity; activity.refnoteid = caserow.noteid; activity.parentrefnoteid = null; activity.priority = 1; activity.type = "N"; activity.isprivate = true; activity.subject = string.format("case was modified by: 0", Base.Accessinfo.UserName); activity.body = string.format("case was modifed by: 0 at 1 ", Base.Accessinfo.UserName, DateTime.Now); activity.startdate = DateTime.Now; activity.percentcompletion = 100; activity.isreminderon = false; activity.owner = Base.Accessinfo.UserID; activity.projectid = PX.Objects.PM.ProjectDefaultAttribute.NonProject(Base); activity.enddate = DateTime.Now; activity.timespent = 0; activity.overtimespent = 0; activity.timebillable = 0; activity.overtimebillable = 0; activity.isbillable = false; activity.billed = false; activity.isincome = false; activity.format = "H"; Base.Activities.Cache.Update(activity); ext.createactivity = false; Base.Case.Update(caseRow); del(); ts.complete(); catch (Exception exc) if (activity!= null) Base.Activities.Cache.Delete(activity); ext.createactivity = true; Base.Case.Update(caseRow); throw exc; This code overrides the Persist() method. Note the use of the PXOverride attribute. It must be placed on the definition of a method in a BLC extension in order to override the BLC method. The Persist() method above checks the value of the CreateActivity field and creates a new EPActivity data record if the value equals true. Once the method populates the EPActivity
Customization Best Practices 337 data record fields and adds the data record to the caches, the method invokes the base logic of Persist() by executing del(). The base logic of Persist() creates a transaction scope. You need to execute the code of EPActivity creation in one transaction scope with the base logic, so you create another transaction scope that wraps EPActivity creation and the base logic invocation. In Acumatica Framework, transaction scopes can be nested, and all code executed inside the outmost transaction scope is canceled whenever an error occurs in any of the nested transaction scopes. You create a new transaction through the using operator to safely dispose the resources allocated for the transaction scope once the code in the using block is executed. The Persist() method in the example encloses the code that deals with the transaction into a try-catch block. This is done to remove the EPActivity instance, if this code fails to commit changes to the database. 5. Build the OverridePersistAEF project. Testing the Results 1. In the Acumatica ERP application, open the Organization > Customer Management > Work Area > Enter > Cases webpage. 2. Select an existing case (see 1 on the screenshot below) with the Open status. 3. Modify the Contact, Owner, or Status setting (2), and then click Save (3). A new activity should appear on the Activities tab (4). Figure: Analyzing the new EPActivity data record in the Cases webpage Adding the Extension Library File to the Customization Project 1. In the Acumatica ERP application, open the System > Customization > Manage > Customization Projects webpage. 2. In the Customization Projects webpage, click New. 3. In the New Project dialog that appears, type your project name, OverridePersistAEF, and click OK. 4. Click the Add button and select File from File System.
Customization Best Practices 338 5. On the Add Files pop-up window that appears, select the check box on the line with Bin \OverridePersistAEF.dll, and click Save. Extending the Release Operation The screenshot below displays the view of the Documents to Apply tab of the Checks and Payments webpage. Figure: Viewing the Documents to Apply tab of the Checks and Payments webpage How to update the check that is being released? How to set TranDescr (appropriately Debit Line or Credit Line) depending on debit or credit amount? This example shows how to define a complex action that invokes the base action and extends its logic by adding event handlers at run time. In this example, you will implement the following steps: Creating a new extension library solution Defining the APPaymentEntryExt business logic controller (BLC, also referred to as a graph) extension, which overrides the release action defined in the APPaymentEntry BLC Testing the new release action Adding the extension library file to the customization project Creating a New Extension Library First you should create the ExtendReleaseAEF extension library solution for an Acumatica ERP application. For details, see first four instructions in Creating an Extension Library Solution by Using the Bespoke Approach. Extending the APPaymentEntry BLC 1. Start MS Visual Studio and open the created ExtendReleaseAEF extension library solution. 2. Right-click the ExtendReleaseAEF project caption, select Add, and click New Item. 3. In the Add New Item window that appears, select the Class template, type APPaymentEntryExtension as the Name, and click Add.
Customization Best Practices 339 4. Modify the code of the added file as follows. using using using using using System.Collections; PX.Common; PX.Data; PX.Objects.AP; PX.Objects.GL; namespace ExtendReleaseAEF public class APPaymentEntryExtension : PXGraphExtension<APPaymentEntry> This class is an extension of the APPaymentEntry BLC. 5. Add the following declaration of the nested ProcessBatch class to the APPaymentEntryExt class. protected class ProcessBatch : IPrefetchable private Batch _Batch; public Batch Batch get return _Batch; public ProcessBatch(Batch batch) _Batch = batch; void IPrefetchable.Prefetch() The ProcessBatch class is a simple wrapper of the Batch data access class (DAC). It will be used below to store a Batch DAC instance in the user session through the use of the PXContext class. The SetSlot<T>() method allocates an area in the user session for storing a single data record of the T type. In the next step, by using this method, you will store a Batch data record in the user session to pass it from one event handler to another and ensure that the data records from different users do not interfere. 6. Add the following declaration of the nested ProcessTran class to the APPaymentEntryExt class. protected class ProcessTran : IPrefetchable private GLTran _Tran; public GLTran Tran get return _Tran; private string _TranDesc; public string TranDesc get
Customization Best Practices 340 return _TranDesc; public ProcessTran(GLTran tran, string trandesc) _Tran = tran; _TranDesc = trandesc; void IPrefetchable.Prefetch() The ProcessTran class, which is analogous to ProcessBatch, is a wrapper of the GLTran DAC. 7. Add the following auxiliary UpdateTranDesc() method to the APPaymentEntryExt class. The method will be called from event handlers that will be added in the next step for the GLTran DAC. private void UpdateTranDesc(GLTran tran, bool storeinslot = false) if (storeinslot) PXContext.SetSlot<ProcessTran>(new ProcessTran(tran, tran.trandesc)); else ProcessTran processtran = PXContext.GetSlot<ProcessTran>(); if (processtran!= null && tran == processtran.tran) tran.trandesc = processtran.trandesc; if (tran.curydebitamt > 0) tran.trandesc += "Debit Line"; else if (tran.curycreditamt > 0) tran.trandesc += "Credit Line"; The method stores or retrieves the provided GLTran data record from the user session and modifies the TranDesc field in this data record. You need to store the data record in the user session in this example only to pass the data record from one event handler, which will call the UpdateTranDesc() method, to the other event handler (see the code in the next step). 8. Add the following release action to the APPaymentEntryExt class. public PXAction<APPayment> release; [PXUIField(DisplayName = "Extended Release", MapEnableRights = PXCacheRights.Update, MapViewRights = PXCacheRights.Update)] [PXProcessButton] public IEnumerable Release(PXAdapter adapter) PXGraph.InstanceCreated.AddHandler<JournalEntry>((graph) => graph.rowinserted.addhandler<gltran>((cache, args) => UpdateTranDesc((GLTran)args.Row, true); );
Customization Best Practices 341 ); graph.rowupdated.addhandler<gltran>((cache, args) => UpdateTranDesc((GLTran)args.Row); ); PXGraph.InstanceCreated.AddHandler<JournalEntry>((graph) => graph.rowinserting.addhandler<batch>((cache, args) => PXContext.SetSlot<ProcessBatch>(null); ); ); PXGraph.InstanceCreated.AddHandler<JournalEntry>((graph) => graph.rowpersisted.addhandler<batch>((cache, args) => if (args.transtatus == PXTranStatus.Completed) PXContext.SetSlot<ProcessBatch>( new ProcessBatch((Batch)args.Row)); ); ); PXGraph.InstanceCreated.AddHandler<APReleaseProcess>((graph) => graph.rowupdated.addhandler<apregister>((cache, args) => ProcessBatch processbatch = PXContext.GetSlot<ProcessBatch>(); if (processbatch == null processbatch.batch == null) return; ((APRegister)args.Row).DocDesc += string.format( "Modified by Batch: 0", processbatch.batch.batchnbr); PXContext.SetSlot<ProcessBatch>(null); PXContext.SetSlot<ProcessTran>(null); ); ); return Base.release.Press(adapter); The extension action modifies the base action not by replacing the code of the base action, but by adding event handlers. Since you want these event handlers to be executed only during the release action, you add the event handlers at run time when the action is invoked. You can add event handlers to a graph instance by calling the AddHandler() method on the appropriate collection of event handlers. However, in this example, you need to attach event handlers to graph instances that do not exist yet, because the base action will create them. So you first add a handler to the InstanceCreated static collection of PXGraph class. This handler will be invoked once an instance of the graph is created. By receiving this instance in the handler parameter, you add the event handlers to common collections of data manipulation events (such as RowUpdated). You add the following event handlers to the JournalEntry graph: RowInserted and RowUpdated handlers for GLTran: The release action creates a GLTran data record for each document. In the event handlers, you invoke the UpdateTranDesc() method, which defines a string, either to Debit Line or Credit Line, to the TranDesc value. RowInserting handler for Batch: You clear the user session from the previously stored Batch data record. RowPersisted handler for Batch: You check whether the Batch data record has been saved to the database successfully (TranStatus is Completed) and then you store the Batch data record to the user session, to pass it safely to the next event handler. You add the following event handler to the APReleaseProcess graph:
Customization Best Practices 342 9. RowUpdated handler for APRegister: The base handler of this type changes the status of the APPayment data record (the APPayment DAC derives from APRegister). You chain another event handler that obtains the Batch data record from the user session, sets a string with the Batch number to the value in the DocDesc field, and clears the slot for Batch data records in the user session. Build the ExtendReleaseAEF project. Testing the New Release Action 1. In the Acumatica ERP application, open the Finance > Accounts Payable > Work Area > Enter > Checks and Payments webpage. 2. Add a new check (see 1 on the screenshot below) and set the Vendor to ADPSERVICE (2). 3. On the Documents to Apply tab, click Add Row to add a new data record (3), and then select 00620 as the Reference Nbr. (4). 4. Type the exact Unapplied Balance value as the Payment Amount value (5). As a result, the value in the Unapplied Balance box will be recalculated and become 0.00. 5. Click Save to save the check (6). Figure: Creating a check 6. Select Actions > Print Check. 7. On the Process Payments/Print Checks webpage (to which you are redirected), click Process. A printable check is loaded in a separate tab, which you can close. 8. Return to the Checks and Payments webpage, confirming that you want to leave the webpage. 9. Click Extended Release to invoke the action that you extended. Once the release operation finishes, the Description value will be set with the string Modified by Batch: followed by the number of the batch that was created during the release. The following screenshot illustrates the result of the action.
Customization Best Practices 343 Figure: Viewing the result of the Extended Release action 10. To see the created journal transactions, open the Finance > General Ledger > Work Area > Enter > Journal Transactions webpage, and select AP as the Module and the number of the batch as the Batch Number. 11. Scroll the grid to the Transaction Description column. Note that its values contain the Credit Line and Debit Line strings (as the following screenshot shows) that you added in the extension code. Figure: The result of the Extended Release action Adding the Extension Library File to the Customization Project 1. In the Acumatica ERP application, open the System > Customization > Manage > Customization Projects webpage. 2. In the Customization Projects webpage, click New. 3. In the New Project dialog that appears, type your project name, ExtendReleaseAEF, and click OK. 4. Click the Add button and select File from File System.
Customization Best Practices 344 5. On the Add Files pop-up window that appears, select the check box on the line with Bin \ExtendReleaseAEF.dll, and click Save.