Introduction to Acumatica Framework

Similar documents
Copyright About the Customization Guide Introduction Getting Started...13

Copyright...6. Acumatica Framework Guide...7. Acumatica Framework Overview... 8

Copyright...9. About the Guide Introduction Acumatica Customization Platform...12

Copyright...4. Overview Managing Snapshots... 6

Copyright...3. Plug-In Development Guide Creating Widgets for Dashboards... 5

Copyright...6. Overview Preparing Data for Import and Export by Using Scenarios... 10

F350 Analytical Reports

Copyright...7. Overview of General Ledger Processes Configuration...11

CHAPTER 1: INTRODUCING C# 3

COGNOS (R) 8 COGNOS CONNECTION USER GUIDE USER GUIDE THE NEXT LEVEL OF PERFORMANCE TM. Cognos Connection User Guide

DOT NET Syllabus (6 Months)

Talend Open Studio for MDM Web User Interface. User Guide 5.6.2

INTRODUCTION TO.NET. Domain of.net D.N.A. Architecture One Tier Two Tier Three Tier N-Tier THE COMMON LANGUAGE RUNTIME (C.L.R.)

ECM Extensions xcp 2.2 xcelerator Abstract

Management Reports Centre. User Guide. Emmanuel Amekuedi

COGNOS (R) ENTERPRISE BI SERIES COGNOS REPORTNET (TM)

You can also check the videos at the bottom of this page:

Roadmap. Mike Chtchelkonogov Founder & Chief Technology Officer Acumatica

Getting Started Tutorial - Eclipse Edition. Sybase Unwired Platform 1.2

Microsoft Visual Basic 2005: Reloaded

Black-Belt Development

10267A CS: Developing Web Applications Using Microsoft Visual Studio 2010

Cognos Connection User Guide USER GUIDE. Cognos (R) 8 COGNOS CONNECTION USER GUIDE

ADF Mobile Code Corner

SAP Workforce Performance Builder

Advance Dotnet ( 2 Month )

MobileFast SyncStudio. A Complete Mobile Database Synchronization Solution. Quick-Start Manual. Release 1.61, May 2014

DOT NET SYLLABUS FOR 6 MONTHS

Oracle. SCM Cloud Configurator Modeling Guide. Release 13 (update 17D)

BEA WebLogic. Server. MedRec Clustering Tutorial

End User s Guide Release 5.0

Information Design Tool User Guide SAP BusinessObjects Business Intelligence platform 4.0 Support Package 4

COPYRIGHTED MATERIAL. Contents. Part I: C# Fundamentals 1. Chapter 1: The.NET Framework 3. Chapter 2: Getting Started with Visual Studio

IBM. Database Database overview. IBM i 7.1

Mail & Deploy Reference Manual. Version 2.0.5

BUILD YOUR OWN SAP FIORI APP IN THE CLOUD Exercise Week 5

Oracle. Sales Cloud Integrating with Oracle Marketing Cloud. Release 13 (update 18B)

Beginning ASP.NET. 4.5 in C# Matthew MacDonald

BEAWebLogic. Portal. MobileAware Interaction Server Installation Guide

IBM Leads Version 9 Release 1 October 25, User Guide

GETTING STARTED WITH CODE ON TIME Got data? Generate modern web apps in minutes. Learn to create sophisticated web apps with Code On Time application

Skill Area 336 Explain Essential Programming Concept. Programming Language 2 (PL2)

Connector for Microsoft SharePoint Product Guide - On Demand. Version

Using SAP NetWeaver Business Intelligence in the universe design tool SAP BusinessObjects Business Intelligence platform 4.1

SAS Infrastructure for Risk Management 3.4: User s Guide

User Defined Fields for MAS 500 SM-1004

20486: Developing ASP.NET MVC 4 Web Applications (5 Days)

Pricing Cloud: Upgrading to R13 - Manual Price Adjustments from the R11/R12 Price Override Solution O R A C L E W H I T E P A P E R A P R I L

SAS Web Report Studio 3.1

Interstage Business Process Manager Analytics V12.1 Studio Guide

This document does not represent a commitment to implement any portion of this specification in any company s products.

Creating Reports in Access 2007 Table of Contents GUIDE TO DESIGNING REPORTS... 3 DECIDE HOW TO LAY OUT YOUR REPORT... 3 MAKE A SKETCH OF YOUR

Accounts Payable Workflow Guide. Version 14.6

[ Getting Started with Analyzer, Interactive Reports, and Dashboards ] ]

Contents I Introduction 1 Introduction to PL/SQL iii

10264A CS: Developing Web Applications with Microsoft Visual Studio 2010

Connector for Microsoft SharePoint Product Guide - On Premise. Version

DbSchema Forms and Reports Tutorial

Océ DS10. Operator s manual

Report Designer Report Types Table Report Multi-Column Report Label Report Parameterized Report Cross-Tab Report Drill-Down Report Chart with Static

EMC Documentum Composer

Table of Contents. Table of Contents

HotDocs Document Services. Administrator s Guide

Access Review. 4. Save the table by clicking the Save icon in the Quick Access Toolbar or by pulling

SAS Data Integration Studio 3.3. User s Guide

Developing ASP.NET MVC 5 Web Applications. Course Outline

Oracle Database: Program with PL/SQL Ed 2

Oracle Database: Program with PL/SQL

Apex TG India Pvt. Ltd.

Service Manager. Ops Console On-Premise User Guide

6 Months Training Module in.net Module 1-Total Days-20

T-SQL Training: T-SQL for SQL Server for Developers

Introduction to Computer Science and Business

Oracle Database 12c: Program with PL/SQL Duration: 5 Days Method: Instructor-Led

Programming in Visual Basic with Microsoft Visual Studio 2010

Microsoft Dynamics GP. Extender User s Guide

Using the VMware vcenter Orchestrator Client. vrealize Orchestrator 5.5.1

DbSchema Forms and Reports Tutorial

If this is the first time you have run SSMS, I recommend setting up the startup options so that the environment is set up the way you want it.

Oracle Database 12c R2: Program with PL/SQL Ed 2 Duration: 5 Days

Use Case 2: Extending object/application to support a new object attribute and a validation for that attribute using either Scripting or Java.

SECURED PROGRAMMING IN.NET DETAILED TRAINING CONTENT INDUSTRIAL TRAINING PROGRAM ( )

Conditionally control code flow (loops, control structures). Create stored procedures and functions.

Microsoft Developing ASP.NET MVC 4 Web Applications

PROGRAMMING IN VISUAL BASIC WITH MICROSOFT VISUAL STUDIO Course: 10550A; Duration: 5 Days; Instructor-led

SCM380 SAP MII - Manufacturing Integration and Intelligence Fundamentals

A Guide to Quark Author Web Edition 2015

Teamcenter 11.1 Systems Engineering and Requirements Management

Webnodes Developers Quick Guide

B I Z I N S I G H T Release Notes. BizInsight 7.3 December 23, 2016

CA IdentityMinder. Glossary

Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners.

COURSE 20486B: DEVELOPING ASP.NET MVC 4 WEB APPLICATIONS

IBM i Version 7.2. Database Database overview IBM

Introduction to Microsoft.NET Programming Using Microsoft Visual Studio 2008 (C#) Course Overview. Prerequisites. Audience.

BMC Remedy Action Request System Using a BIRT Editor to Create or Modify Web Reports

Oracle Financial Services Governance, Risk, and Compliance Workflow Manager User Guide. Release February 2016 E

ADF Code Corner How-to bind custom declarative components to ADF. Abstract: twitter.com/adfcodecorner

BEA WebLogic. Integration. Tutorial: Building Your First Data Transformation

DEVELOPING WEB APPLICATIONS WITH MICROSOFT VISUAL STUDIO Course: 10264A; Duration: 5 Days; Instructor-led

Transcription:

Introduction to Acumatica Framework Training Guide T100 Introduction to Acumatica Framework 5.0

Contents 2 Contents Copyright...4 Introduction... 5 Getting Started...7 Application Programming Overview... 8 Querying the Data... 10 Entity Model Declaration... 12 RapidByte Application Overview... 22 Functional Requirements... 23 Application Pages... 24 Preparation of the Development Environment... 28 Summary: Application Overview and Preparations... 30 Part 1: Maintenance Pages... 31 Lesson 1: Creating the First Page Countries...32 Adding the CountryMaint Graph... 33 Adding Page RB201000... 34 Adding the Country Class... 36 Modifying the CountryMaint Graph... 41 Configuring the Grid on the Page...42 Adding the Page to the Site Map... 44 Conclusion: Creation of Pages... 47 Lesson 2: Creating the Customers Maintenance Page... 48 Adding the CustomerMaint Graph and Page RB202000... 49 Adding the Customer Class... 50 Configuring the CustomerMaint Graph and Page RB202000... 51 Adding Selectors to the Page... 53 Conclusion: Simple Form Edit Pages...56 Lesson 3: Adding Simple Business Logic to the Customers Page...57 Adding Logic on Update of the CountryCD Field... 58 Conclusion: Business Logic...60 Part 2: Data Entry Pages... 61 Lesson 4: Creating the Sales Order Data Entry Page... 62 Adding the SalesOrderEntry Graph and Page RB301000...63 Adding the Product Class... 64 Adding the SalesOrder Class... 65 Adding the OrderLine Class... 68 Configuring the SalesOrderEntry Graph...70 Configuring Page RB301000... 71 Conclusion: Master-Detail Pages... 75 Lesson 5: Adding Simple Business Logic to the Sales Orders Page...76 Configuring a Combo Box... 77 Setting the Combo Box Value at Run Time... 79 Inserting Product Details into an Order Line...81 Conclusion: Usage of Combo Boxes and Autoinsertion of Values...83 Lesson 6: Calculating Totals... 84 Calculating the Extended Price of an Order Line... 85 Calculating the Lines Total and Tax Total of a Sales Order... 87 Calculating the Order Total...90

Contents 3 Conclusion: Calculations...91 Part 3: Inquiry Pages...92 Lesson 7: Creating the Sales Order Inquiry Page... 93 Adding the SalesOrderInq Graph...94 Adding Page RB401000... 97 Conclusion: Inquiry Pages... 99 Part 4: Processing Pages... 100 Lesson 8: Creating the Approve Sales Orders Page... 101 Implementing the Approval Operation...102 Adding the SalesOrderProcess Graph... 104 Adding Page RB501000...105 Conclusion: Processing Pages... 107 Lesson 9: Setting Up the UI for Approved Sales Orders... 108 Making Approved Sales Orders Read-only in the UI... 109 Conclusion: UI Setup... 111 Part 5: Reports...112 Lesson 10: Creating the Sales Orders Printable Form...113 Preparing Data for the Report...114 Adding a Variable to the Report... 119 Configuring the Printable Page Layout... 120 Adding the Report URL to the Site Map... 126 Running the Report Form... 127 Conclusion: Reports... 129 Appendix: Application Design...130 Database Schema...131 Data Access Classes... 134 Application Pages... 135 Business Logic... 138

Copyright 4 Copyright 2015 Acumatica, Inc. ALL RIGHTS RESERVED. No part of this document may be reproduced, copied, or transmitted without the express prior consent of Acumatica, Inc. 4030 Lake Washington Blvd NE, Suite 100 Kirkland, WA 98033 Restricted Rights The product is provided with restricted rights. Use, duplication, or disclosure by the United States Government is subject to restrictions as set forth in the applicable License and Services Agreement and in subparagraph (c)(1)(ii) of the Rights in Technical Data and Computer Software clause at DFARS 252.227-7013 or subparagraphs (c)(1) and (c)(2) of the Commercial Computer Software-Restricted Rights at 48 CFR 52.227-19, as applicable. Disclaimer Acumatica, Inc. makes no representations or warranties with respect to the contents or use of this document, and specifically disclaims any express or implied warranties of merchantability or fitness for any particular purpose. Further, Acumatica, Inc. reserves the right to revise this document and make changes in its content at any time, without obligation to notify any person or entity of such revisions or changes. Trademarks Acumatica is a registered trademark of Acumatica, Inc. All other product names and services herein are trademarks or service marks of their respective companies. Software Version - 5.0 Last updated: January 28, 2015

Introduction 5 Introduction The course consists of step-by-step lessons in which you develop a complete Acumatica Frameworkbased application. The course gives you an idea of how to develop your own applications by using Acumatica Framework. It demonstrates key components of the platform and their usage for typical tasks in application development. As you go through the course, you will create pages of all main types that are used in Acumatica Framework. After you complete all the lessons, you will be acquainted with basic techniques of programming with Acumatica Framework, such as working with data, implementing business logic and processing operations, and building reports. We recommend that you follow the lessons in the order in which they are provided in the course, because some lessons use the result of the previous ones. System Requirements For detailed system and browser requirements, see ConfiguringDevEnvironment.pdf attached to the training materials. To develop and debug your application, use Internet Information Services (IIS) hosted locally on your machine. You can debug an application running on IIS by attaching the Visual Studio debugger to the server process. For more information on how to debug Acumatica Framework applications, see Acumatica Framework > Debugging Applications in the documentation. Required Knowledge and Background To complete the course successfully, you should have the required knowledge: 1. 2. 3. 4. Proficiency with C# language, including but not limited to the following language features: Class structure OOP (inheritance, interfaces, polymorphism) Usage and creation of attributes Generics Delegates, anonymous methods and lambda expressions Knowledge of the main concepts of ASP.NET and Web Development: Application State Debugging ASP.NET Applications Using Visual Studio Attaching to IIS using Visual Studio Debugging Tools Client and Server-Side development Web Forms Structure Web Services Experience with SQL Server: Writing and debugging complex SQL queries (Where Clause, Aggregates, Subqueries) Understanding of database structure (Primary Keys, Data Types, Denormalization) Expericence with IIS: Configuration and deployment of ASP.NET websites Configuring and Securing IIS

Introduction 6 Review the following Microsoft Training Classes to see what training is covered or get up to speed with these technologies Programming in C# Querying Microsoft SQL Server 2012 Developing Microsoft SQL Server 2012 Databases Introduction to Web Development with Microsoft Visual Studio 2010 Acumatica Framework Documentation You can find the framework documentation: Online at the http://partner.acumatica.com portal in the Documentation section. On the website of the Acumatica Framework-based application template that you will install locally as you go through this course. In the single AcumaticaFramework_DevelopmentGuide.pdf PDF documentation file, which is located in the folder where Acumatica Framework is installed. By default, the folder is C:\Program Files (x86)\acumatica Framework.

Getting Started 7 Getting Started In this part of the course, you will get an overview of application programming with Acumatica Framework and prepare to build the RapidByte application. After you see the requirements of the RapidByte application, you will set up the template to start programming the application. The Acumatica Framework application template includes the default database schema, which contains only system tables, the website, the Visual Studio solution, and an empty Class Library project for your application code. However, you will install the template for the T100 Introduction to Acumatica Framework course, which also adds application tables for RapidByte. Thus, after you complete the first part of the course, you will have the initial web application that is ready for development of a new business module.

Getting Started 8 Application Programming Overview Acumatica Framework provides the platform and tools for developing cloud business applications. This document explains Acumatica Framework runtime structure, introduces main components, and illustrates their relationships on simple examples. The chapter is a starting point for application developers who are going to develop and customize applications with the help of Acumatica Framework. This document is a mandatory reading before starting the Acumatica Framework Tutorial. Runtime Structure and Components An application written with Acumatica Framework has n-tier architecture with a clear separation of the presentation, business, and data access layers. The picture below illustrates the application component model from the point of view of the application programmer. Figure: Application architecture. Data Access Layer Acumatica Framework relies on object relationship mapping (ORM) technology to access the database from the business logic. Acumatica Framework implements own, proprietary ORM technology. This technology provides an application developer with a set of standard CRUD operations to execute on database tables and methods to execute complex SQL queries. An important feature of the Acumatica ORM technology is a high-performance serialization mechanism that stores modified but not persisted database records in the session state. Modified data are merged with the result of the query execution to emulate stateful data access behavior for the application developer and minimize the amount of data stored in the session.

Getting Started 9 Business Logic Layer Business Logic Layer is implemented as a set of business logic controllers (graphs). Each business logic controller consists of two parts: Entity Model that declares data access classes the entities are stored in, their relationships, and actions that can be executed over the entities Entity Business Logic that implements the business logic of the actions and events associated with modifying entity data Business logic controllers implement the interfaces for Presentation Layer to retrieve the entity data and execute the actions over the entity. Business Logic Layer relies on Data Access Layer to retrieve data from the database and execute CRUD operation. Presentation Layer Presentation Layer is responsible for providing: The user interface based on the ASPX technology and implemented as a set of declarative Web Forms The alternative interface for accessing the business logic in the form of auto-generated Web Service API Presentation Layer is completely declarative and contains no business logic. Related Links Querying the Data Entity Model Declaration Handling Entity Data Implementing Business Logic

Getting Started 10 Querying the Data This system implements a custom language for writing database queries called BQL (business query language). It is not LINQ and doesn't use it. BQL is written in C# and based on generic classes syntax, but still is very similar to SQL syntax. It has almost the same keywords placed in the order they are used in SQL. For example: PXSelect<Product, Where<Product.availQty, IsNotNull, And<Product.availQty, Greater<Product.bookedQty>>>> If the database provider is MS SQL Server, the framework will translate this expression into the following SQL query: SELECT * FROM Product WHERE Product.AvailQty IS NOT NULL AND Product.AvailQty > Product.BookedQty BQL gives several benefits to the application developer. It does not depend on database-provider specifics, is object-oriented and extendable. An important benefit is compile-time syntax validation, which helps to prevent SQL syntax errors. Since BQL is implemented on top of generic classes, you need types that would represent database tables. In the context of Acumatica Framework, they are called data access classes (DACs). For example, to execute the SQL query from the example above, you should define the Product data access class as: using System; using PX.Data; // Types used in BQL statements should derive from special interfaces: // table - IBqlTable, column - IBqlField. [System.SerializableAttribute()] public class Product : PX.Data.IBqlTable // The type used in BQL statements to reference the ProductID column public abstract class productid : PX.Data.IBqlField // The property holding ProductID value in a record [PXDBIdentity(IsKey = true)] public virtual int? ProductID get; set; // The type used in BQL statements to reference the AvailQty column public abstract class availqty : PX.Data.IBqlField // The property holding AvailQty value in a record [PXDBDecimal(2)] public virtual decimal? AvailQty get; set; // The type used in BQL statements to reference the BookedQty column public abstract class bookedqty : PX.Data.IBqlField // The property holding BookedQty value in a record [PXDBDecimal(2)] public virtual decimal? BookedQty get; set; Each table field is declared in a data access class twice: As a type to reference a field in the BQL command As a value to hold the table field data

Getting Started 11 If the DAC is bound to the database, it must have the same class name as the database table. Fields are bound to the database by means of data mapping attributes (such as PXDBIdentity and PXDBDecimal), using the same naming convention. A complete code sample that queries the database is given below: using System; using System.Collections; using PX.Data; public static void Main() // Select Product records PXResultSet<Product> res = PXSelect<Product, Where<Product.availQty, IsNotNull, And<Product.availQty, Greater<Product.bookedQty>>>>.Select(new PXGraph()); // You can iterate through the result set foreach(pxresult<product> rec in res) // A record from the result set can be cast to the DAC Product p = (Product)rec; Console.WriteLine("ID: 0, available: 0, booked: 0", p.productid, p.availqty, p.bookedqty); BQL library also supports such advanced features as: DACs that are not bound to the database Virtual fields that are not bound to the database Scalar sub-selects Projections Stored procedures execution Server-side calculated fields Non-blocking updates of statistical data records

Getting Started 12 Entity Model Declaration Business Entity or simply Entity in Acumatica Framework represents an individual instance of the objects (such as Product, Order) to which the information pertains. Entity can be simple, where the data are represented with a single database record in a single table, or complex. With the complex entity, data are typically held in multiple tables and associated through a complex hierarchy and relationship rules. Working with the business entities in Acumatica Framework is implemented through the business logic controller object also referred as graph (graph is a mathematical term for a set of objects where some pairs of objects are connected by links). A graph provides the interface for the presentation logic to operate with the business entity and relies on Data Access Layer components to store and retrieve the business entity from the database. Let s first take a look at the declaration of a simple business entity: //Declaration of the graph public class ProductMaint : PXGraph<ProductMaint> //Declaraion of the data view public PXSelect<Product> Products; //Declaration of the actions public PXCancel<Product> Cancel; public PXSave<Product> Save; In this example the graph implements the following interfaces: Products the data view that can be used for querying and modifying entity data Cancel the action that discard all the changes made to the entity and reloads it from the database Save the action that commits the changes made to the entity to the database and then reloads the committed data Handling Entity Data Data View and Entity Cache Data views implement the interfaces for querying entity data from the business logic controller and submitting modified data back to the entity. Data views are declared as public fields of PXSelect command type: public PXSelect<Product> Products; Based on this declaration, the system automatically instantiates the DAC entity cache. An entity cache object in the Acumatica Framework is the primary interface for working with individual entity records from the graph business logic. It has two components and two primary responsibilities: The Cached collection in-memory cache that contains modified entity records. The Cached collecton is instantiated based on the corresponding DAC declaration and managed by the cache. The controller the cache component that implements basic CRUD operations on the Cached collection and triggers a sequence of data manipulation events when modifying or accessing the data in the Cached collection. These events can be later subscribed from the graph to implement the business logic associated with entity data modification. The diagram below helps to understand the internal graph structure and responsibilities of the data view and the entity cache.

Getting Started 13 Figure: The graph structure a data view and an entity cache. Data Modification Scenarios Now lets consider basic entitiy data manipulation scenarious that can be executed from the graph business logic or from the user interface. Entity data manupulation through the user interface indirectly invokes the same methods as the direct call from the business logic. Querying Entity Data for the First Time Entity data can be requested through the Products.Select() method. During this operation, the systems will execute BQL command from the data view declaration. Data returned by the BQL command will be returned to the requestor. See the diagram below. Figure: Querying entity data for the first time. Updating an Existing Entity Record An existing business entity record can be updated through the Products.Update(record) method. This method places the modified record into the cache. If the data record is not found in the Cached collection, the cache controller will load the data record from the database, add it to the Cached collection, mark it as updated, and update it with the new values. The search of the data record in the Cached collection and loading of the data record from the database is based on the DAC key fields. The diagram below illustrates this scenario.

Getting Started 14 Figure: Updating the entity record for the first time. If the updated record exists in the Cached collection the cache controller will locate it and update it with the new values. The diagram below illustrates this scenario.

Getting Started 15 Figure: Updating the cached (previously modified) entity record. Inserting a New Entity Record A new record can be inserted into the business entity through the Products.Insert(record) method. The new inserted record will be added to the Cached collection and marked as inserted. The diagram below illustrates this scenario.

Getting Started 16 Figure: Inserting the new entity record. Deleting an Existing Entity Record An existing record can be deleted from the business entity using the Products.Delete(record) method. If the data record is not found in the Cached collection, the cache controller will load the data record from the database, add it to the Cached collection, and mark it as deleted. The search of the data record in the Cached collection and loading of the data record from the database is based on the DAC key fields. The diagram below illustrates this scenario.

Getting Started 17 Figure: Deleting the non-cached (unmodified) entity record. If the deleted record is found in the Cached collection, the cache controller will locate it and mark as deleted. The diagram below illustrates this scenario. Figure: Deleting of the cached (previously modified) entity record.

Getting Started 18 Querying an Updated Entity Data Entity data can be modified and then queried again. In this scenario, the data records stored in the caches memory will be merged with the result of the BQL command execution. Data records merge is based on DAC key fields. The final result of the Select() execution will incorporate all the earlier entity records modifications that has not been preserved to the database yet. The diagram below illustrates this scenario. Figure: Querying the modified entity data reading and merging with the cached data. Persisting Entity Changes to the Database When entity data are modified, the system has two different entity versions, the new one stored in the caches memory and the original one persisted in the database. At this point a programmer has two options: Save the new entity version to the database using the Persist() method of the graph Discard all in-memory changes and load the original entity version using the Clear() method of the graph From the Presentation Layer these methods are called by invocation of the Save and Cancel actions. These actions are predefined and mapped to the Persist() and Clear() methods. The diagram below illustrated saving of entity changes to to the database.

Getting Started 19 Figure: Saving the entity changes to the database. The diagram below illustrated discarding of all in-memory entity changes. Figure: Discarding the changes and loading the original entity data.

Getting Started 20 Preserving the Entity Version Between the Round Trips and Handling the Subsequent Selects from the Views It is important to understand that a graph is a stateless object. It is discarded after each data request. In order to preserve the modified entity version between the requests, the cache controller serializes the Cached collection into the session state and restores it later when the graph is instantiated on the subsequent request. In this scenario, it is very important that the cache contains only the modified entity records, not the complete entity record set. Implementing Business Logic Business logic is implemented by overloading certain methods invoked by the system in the process of manipulating data. For such procedures as inserting a data record or updating a data record, the PXCache controllers generate series of events causing invocation of the methods called event handlers. The application is able to interfere in the series of events on different stages. For this purpose, the application impements methods that are executed as event handlers. There are 18 events raised on all stages of data processing. Business logic can be divided into common logic relevant to different parts of the application and the logic specific to an application screen (web page). The common logic is implemented through event handler methods defined in attributes, while the screen-specific logic is implemented as methods in the associated graph. Common Business Logic The common business logic is implemented by defining event handlers in attributes. If such attribute is added to the declaration of a data access class, attribute logic is applied to the data records of this type for any graph used to access this table. There are a number of predefined attributes implemented in the framework. For example, in the following declaration of a data field for a column [PXDBDecimal(2)] public virtual string AvailQty get; set; PXDBDecimal is an attribute binding this field to a database column of the decimal type. The attributes of this form exist for most database data types. Another typical example of an attribute is PXUIField. It is used to configure the input control for the column in the user interface. This allows having the same visual representation of the column on all application screens (unless a screen redefines it). For example: [PXDBDecimal(2)] [PXUIField(DisplayName = "Available Qty", Enabled = false)] public virtual string AvailQty get; set; Application can also define its own attributes, in the following way: // Application-defined attribute implementing common business logic public class MyAttribute : PXEventSubscriberAttribute, IPXEventNameSubscriber // An event handler protected virtual void EventName(PXCache sender, PXRowEventNameEventArgs e)......

Getting Started 21 Such attributes are also added to the DAC declaration: [PXDBDecimal(2)] [PXUIField(DisplayName = "Available Qty", Enabled = false)] [MyAttribute] public virtual string AvailQty get; set; Screen-Specific Business Logic For a specific screen, the application can redefine the common logic or extend it. For this purpose, you should define event handlers in the graph associated with the screen. Each event hanlder method is tied to a particular table or a table field via the naming convention. For example, you can verify a value of a column: public class ProductRecalc : PXGraph<ProductRecalc>... // Event handler verifying that the value of the AvailQty column // in Product records is greater than 0. // It is triggered when, for instance, a Product record is updated. protected virtual void Product_AvailQty_FieldVerifying( PXCache sender, PXFieldVerifyingEventArgs e) Product p = (Product)e.Row; if (p!= null && p.availqty!= null) if (p.availqty < 0) throw new PXSetPropertyException<Product.availQty>( "Value must be greater than 0.");

Getting Started 22 RapidByte Application Overview In this section, you will get an overview of the RapidByte application that you will build as you complete the course. You will see the application requirements and the webpages that you will create in further lessons.

Getting Started 23 Functional Requirements The RapidByte application should provide the following functionality for working with sales orders: 1. A user should be able to enter sales orders and add products to them. A sales order is a document that contains the information about the products ordered by a particular customer. Each sales order has a unique number, a reference to the customer, and a reference to the company that provides the shipment of the order. The products added to a sales order (which can be only active products) are the order details. For each sales order, the application should also provide the following features: a. b. Calculation of the sales order totals: The tax total is the sum of the tax amounts of all order details (products added to the order). The lines total is the sum of the extended prices of all order details. The order total is the sum of the lines total, tax total, and freight amount specified in the order. Calculation of the extended price of an order detail: extended price = product quantity * unit price * (1 - discount / 100), where thediscount is specified as a percent in each order detail individually. The calculated values should be saved to the database. 2. The user should be able to get the list of all existing sales orders and the list of sales orders filtered by a particular customer. 3. The user should be able to approve sales orders. Once approved, a sales order becomes readonly and cannot be deleted from the database. 4. The user should be able to print sales orders. 5. The user should be able to edit customers. Each sales order should be associated with a customer. By using the listed requirements, we can plan the application pages that we will create in this course. For more information about the database structure and application classes, see Appendix: Application Design.

Getting Started 24 Application Pages In the RapidByte application, users must be able to enter sales orders, select them by the specified criteria, and approve them. We will create a separate page for each of these activities: a data entry page, an inquiry page, and a processing page for working with sales orders in the application. Based on the requirements, we also see that we need a page for sales order printing and a helper page for editing products. To start with a very simple Acumatica Framework page as an example, we will create the Countries page, which provides editing of the list of countries. In summary, we will create the following application pages: 1. Countries 2. Customers 3. Sales Orders 4. Sales Order Approval 5. Sales Order Inquiry 6. Sales Order Printing See below for the screenshots and brief descriptions of of the pages (including their Functional Requirements, where applicable): 1. Countries (no specific functional requirement) By using this page (shown below), users can edit the list of countries. The instructions for creating this application page demonstrate how to start building the application. In the application, the list of countries would help if you created a page for working with customers, because each customer has a country attribute. Figure: The Countries page 2. Customers (functional requirement #5) This helper page, as shown in the following screenshot, gives users the ability to add new customers and edit their parameters.

Getting Started 25 Figure: The Customers page 3. Sales Orders (functional requirement #1) The main page of the application, which should look like it does in the following screenshot, is this data entry page for sales orders. On this page, the user enters the sales order parameters and adds products to the sales order. The page should have the Approve button, which can be invoked to change the sales order status to Approved. Figure: The Sales Orders page 4. Approve Sales Orders (functional requirement #3) On this processing page (which is shown below), the user can select one sales order or multiple orders and run the approval operation for the selected orders or all listed orders. Figure: The Approve Sales Orders page 5. Sales Order Inquiry (functional requirement #2)

Getting Started 26 The page will display the list of sales orders, as shown below. The user can view all sales orders or only those of the selected customer. Figure: The Sales Order Inquiry page 6. Sales Orders printable form (functional requirement #4) The printable version of the sales order must include all sales order parameters and the list of order details, as the following screenshot illustrates.

Getting Started 27 Figure: The Sales Order printable document Users will be able to navigate to these pages from the application menu. To provide a complete navigation menu in the application, you will add these pages to the site map as you create them. Now that you understand what pages should be implemented in the application, go to the next lesson to set up the application template that will be used for creating of the application pages. Related Links Application Pages Appendix: Application Design

Getting Started 28 Preparation of the Development Environment You will now create the RapidByte application from the application template that is provided with Acumatica Framework. Do the following by instructions given in the ConfiguringDevEnvironment.pdf attached to the training materials: 1. Install Acumatica Framework. 2. Deploy a new application instance for a training course. On the Instance Configuration screen, specify the following values (see the screenshot below): Instance Name: RapidByte Visual Studio Solution Name: RB Select the training course: Introduction to Acumatica Framework Figure: Instance Configuration screen 3. Install the Visual Studio Templates. 4. Prepare the solution in Visual Studio by creating additional folders (see the screenshot below): Create the RapidByte folder in the Site\Pages folder (1) Create the RapidByte and RapidByte > DAC folders in the RB application project (2) The existing SM folder in the Site project contains system pages of the website; do not modify this folder or the files in it.

Getting Started 29 Figure: Solution structure for the RapidByte application To find the location of the solution, you can go to the Application Maintenance screen of the Acumatica Framework Configuration Wizard and see the SitePath column for the RapidByte instance. Now you are ready to start application development.

Getting Started 30 Summary: Application Overview and Preparations In this part of the course, you have learned that you will create the RapidByte demo application, which consists of six pages: 1. The Countries maintenance page 2. The Customers maintenance page 3. The Sales Orders data entry page 4. The Approve Sales Orders processing page 5. The Sales Order Inquiry inquiry page 6. The Sales Order Printable Form report form Based on the requirements identified in this lesson, you will design the application, which includes the database schema, the application classes, and the UI. In this course, we have designed the demo application for you; you can see the detailed description of the application components in Appendix: Application Design. To deploy a new instance of the Acumatica Framework-based application template and then maintain the deployed applications, you use the Acumatica Framework Configuration Wizard. The wizard also provides installation of specific Visual Studio templates that are used for creating ASP.NET pages in the site and classes in the application project. Now you have the template application ready for development of RapidByte pages. In the next lesson, you will create your first Acumatica Framework application page and test it on the website.

Part 1: Maintenance Pages 31 Part 1: Maintenance Pages In this part of the course, you will start with creating the first very simple pages of the application. You will create the maintenance pages that are helper pages used for the input of data on the main pages of the application, data entry and processing pages. The pages of these types will be created further in this course.

Part 1: Maintenance Pages 32 Lesson 1: Creating the First Page Countries In this lesson, you will create the first and simplest page in the application. The Countries maintenance page holds the list of countries that can be used for specifying the country attribute of customers. You will start with preparing the generator that produces DAC code from the specified database table. As you advance through this lesson, you will create the CountryMaint business logic controller (BLC, also referred to as graph), add the Countries webpage associated with this graph, and generate the Country data access class (DAC). As a result you will get a page that displays a list of countries in a grid (see the screenshot below). Finally, you will configure navigation to the created page on the website. Figure: The Countries page on the website We recommend that you use the Data Access Class Generator to generate the initial code of data access classes (DACs) in the application. The generator helps you to create the declaration of data access classes, which may have multiple fields and, thus, be time-consuming to code manually. The Data Access Class Generator saves you from typos and confusion. You can then easily maintain the DAC declaration manually once it has been generated. To use the Data Access Class Generator, you need a page with a business logic controller specified in its data source. This lesson provides detailed steps on how to prepare the generator. Once you complete the lesson, you will have the first page added to the RapidByte website. The page provides standard functionality for working with data records, including selection, insertion, edit, and deletion of data records through the grid. Lesson Objective In this lesson, you will create and test the first page of the RapidByte application, which displays a list of countries in the grid. You will see how to generate the initial declaration of a data access class by the table description in the database. You will also know how to configure the application page and create the business logic controller for it.

Part 1: Maintenance Pages 33 Adding the CountryMaint Graph To get access to the Data Access Class Generator tool in your application project, you need an application page linked to a business logic controller, so-called graph. In this step, you will start with creating the CountryMaint graph for the Countries page. Do the following: 1. In the RB application project, right-click the RapidByte folder and select Add > New Item. 2. In the window, select the PXGraph template. 3. Type the name of the created class, CountryMaint.cs, and click Add. The definition of the CountryMaint graph will be added to the project. The CountryMaint graph is empty for now, because at this moment it is just needed to link the application page that you will create on the next step to the application project. The Data Access Class Generator opens from the application page in design mode from any application page linked to a graph from the application project where the code should be generated. Later you will add the graph members that provide data and actions for the page. 4. Rebuild the project to be able to specify the graph for an application page. Now create an application page and set the CountryMaint graph in the datasource control on the page.

Part 1: Maintenance Pages 34 Adding Page RB201000 In this step, you will add the first ASP.NET page to be able to open the Data Access Class Generator. To add the page, perform the following instructions: 1. In the Pages folder of Site, right-click RapidByte and select Add New Item. 2. In the window, select the ListView template. All application pages must be created from one of the special Acumatica Framework templates. The ListView template has the only grid container to represent a list of data records. 3. Type the name of the new page, RB201000.aspx, and click Add. The new page will be added to the Site > Pages > RapidByte folder and opened in source mode. The page name complies with the Acumatica Framework convention where RB is a two-letter module identifier, 20 means maintenance type of the page, and 10 is the starting sequential number of the maintenance page in the module. For more information on naming conventions, see Acumatica Framework > Design Guidelines > Application Design Guidelines in the documentation. 4. Click the Design button to switch to design mode. Figure: Switch to design mode in Visual Studio By clicking the Source button, you can switch back to source mode.

Part 1: Maintenance Pages 35 Figure: View the page in design mode 5. When the visual elements appear, select the ds PXDataSource control. 6. In the Properties window, set TypeName:RB.RapidByte.CountryMaint. Every page should be associated with a graph. If you do not see RB.RapidByte.CountryMaint in the TypeName combo box, make sure that you have built the project before switching to design mode. If the graph is still not visible, right-click any page area and click Refresh to again render the page. 7. Save changes on the pages.

Part 1: Maintenance Pages 36 Adding the Country Class In this step, you will generate the first data access class in the project, Country. To add the class, you need to open the Data Access Class Generator, select the database table, select fields, and then generate the class declaration. Proceed as follows: 1. Open RB201000.aspx in design mode. 2. After the visual elements appear, select the ds datasource control. 3. Click the smart tag at the top right corner of the datasource control, as shown below. Figure: Click the datasource control smart tag The Acumatica Framework code generator and layout editors for controls are opened from the smart tag menu of the control. Sometimes it is difficult to click the proper tag. In this case, you can try the following alternative way of opening the smart tag menu: Select the control in the Properties window (see 1 on the screenshot below). Right-click the control header in the design area to open the context menu related to the control (2). Select Show Smart Tag from the menu (3).

Part 1: Maintenance Pages 37 Figure: Open smart tag menu 4. In the PXDataSource Tasks dialog box, click Generate Class to open the Data Access Class Generator dialog box. Figure: Open the Data Access Class Generator 5. In the dialog box, select Country in the Name combo box (see 1 on the screenshot below). The list of columns available in the Country database table appears. You have created all database tables used in the RapidByte application when you deployed the training application template. 6. Make sure all columns are selected (2), and click Generate (3). The Country data access class (DAC) is added to the project in the RapidByte\DAC\Country.cs file.

Part 1: Maintenance Pages 38 Figure: Generate the Country data access class 7. Open Country.cs to view the generated code, which is shown below. namespace RB.RapidByte using System; using PX.Data; [System.SerializableAttribute()] public class Country : PX.Data.IBqlTable #region CountryCD public abstract class countrycd : PX.Data.IBqlField protected string _CountryCD; [PXDBString(2, IsKey = true, IsUnicode = true)] [PXDefault()] [PXUIField(DisplayName = "CountryCD")] public virtual string CountryCD get return this._countrycd; set this._countrycd = value; #endregion #region Description public abstract class description : PX.Data.IBqlField protected string _Description; [PXDBString(50, IsUnicode = true)]

Part 1: Maintenance Pages 39 8. [PXUIField(DisplayName = "Description")] public virtual string Description get return this._description; set this._description = value; #endregion Correct DisplayName in the CountryCD property annotation to Country ID. The DisplayName parameter of PXUIField defines the label text that is displayed for the data field in the UI. CountryCD is the key field for the class, which is defined by the IsKey=true parameter of the PXDBString attribute as shown below. The PXDefault attribute here marks the field as mandatory. [PXDBString(2, IsKey = true, IsUnicode = true)] [PXDefault()] [PXUIField(DisplayName = "Country ID")] public virtual string CountryCD... In Acumatica ERP, CD is used for natural keys (such as CountryCD), which means keys that are human-readable and can have additional meaning. ID is used for surrogate keys (such as CountryID), which are pure identifiers. You typically display CD keys in the UI and hide ID keys. However, to avoid confusing users with differences between CD and ID keys, we recommend you to specify ID in the label of any key field. 9. Save changes and rebuild the project. Now the data access class that represents countries is ready. In the next step, you will modify the CountryMaint graph for working with Country data records on the application page. Declaration of Data Access Classes Every data access class (DAC) is declared as implementing the PX.Data.IBqlTable interface. Each data access class must have the System.SerializableAttribute attribute. The Country data access class contains two DAC fields: CountryCD and Description. The DAC field declaration consists of two members: A public abstract class that implements the PX.Data.IBqlField interface. This abstract class is used in BQL statements specified in views and attributes added to data field declarations. A public virtual property of a nullable data type that corresponds to the data type of this field. This property keeps the value of the data field. The attributes are added to the property and not to the abstract class of the data field declaration. The abstract class and property have the same name that differ by the case of the first letter. By convention, the abstract class name starts with a lowercase letter, while the property name starts with the same uppercase letter. In BQL statements, the data field is referred to by the abstract class name, while in generated SQL, the columns are referred by the property name. The attributes are specified for properties and not for abstract classes. Use of attributes is one of key techniques in Acumatica Framework-based applications. On DAC fields, you use attributes to define the data field specification that includes multiple parameters such as the data type, default value, and

Part 1: Maintenance Pages 40 field caption in the UI. The type attribute, such as PXDBString on the Country.CountryCD field from the code above, is the required attribute of a DAC field. For more information about attributes, see Acumatica Framework > API Reference > Attributes in the documentation.

Part 1: Maintenance Pages 41 Modifying the CountryMaint Graph In this lesson, you will add members to the graph that provide data and actions for the Countries maintenance page. Do the following: 1. Open the CountryMaint.cs file. 2. In the CountryMaint business logic controller, declare the Cancel and Save data manipulation actions and the Countries data view, as shown below. public class CountryMaint : PXGraph<CountryMaint> public PXCancel<Country> Cancel; public PXSave<Country> Save; public PXSelect<Country> Countries; Since the Countries page is intended for working with a country list, the Countries data view, which selects Country records from the database, will be specified as the primary data view for the page. The first type parameter of a data view specifies the main DAC. So, Country is the main DAC of the Countries data view. If you set the type parameter of an action to the main DAC of the primary view, the system automatically adds the button corresponding to this action to the page toolbar. So the Cancel and Save buttons will appear on the page toolbar. 3. Save changes and rebuild the project. Data views, which are declared in a graph, provide data for displaying in the container controls on the page. Now you can configure the page to display the list of countries obtained through the Countries data view. Data Views A data view is a graph member of one of PXSelectBase-derived types, such as PXSelect. The data view type is a BQL statement that selects data to be manipulated through the data view. The main DAC of a data view is the first type parameter in the declaration. The data view that is specified as the primary view for the page must be defined the first one in the graph. For more information on PXSelectBase types and BQL, see Acumatica Framework > API Reference > Core Classes > PXSelectBase and Acumatica Framework > API Reference > BQL in the documentation.

Part 1: Maintenance Pages 42 Configuring the Grid on the Page In this step, you will add columns to the grid on the page that displays the country list. Do the following: 1. Open the RB201000.aspx page in design mode. 2. Select the ds PXDataSource control. 3. In the Properties window, set the following properties for the ds control: PrimaryView:Countries The TypeName and PrimaryView properties are the only required properties of the control (TypeName has been earlier specified for ds). The buttons are automatically added to the toolbar by the definition of graph actions that have the type parameter set to the main DAC of the primary view. The CountryMaint graph contains the declaration of two buttons, Save and Cancel, so these buttons appears on the page toolbar. If you do not see Countries in the PrimaryView combo box, make sure that you have built the project before switching to design mode. If the view is still not visible, right-click any page area and select Refresh to refresh controls on the page. Now you can configure the data source for the grid and generate columns to the control. 4. Select the grid PXGrid control. 5. In the Properties window, set the following property for the grid PXGrid control: DataMember:Countries DataMember and DataSourceID are two required properties of container controls. The DataMember property specifies the data view to obtain data records into the grid. 6. Click the smart tag of the grid, at the top right corner of the control, as shown below. Figure: Click the Smart Tag of the Grid 7. In the PXGrid Tasks window, click Edit Content Layout, as shown below. The Layout Editor window appears. Figure: Open the Grid Layout Editor 8. In Layout Editor, switch to the Fields tab at the right side of the window. The list of the data fields available through the data view appears. 9. Check both CountryCD and Description (see 1 on the screenshot below), and select the only Columns check box at the bottom of the window (2).

Part 1: Maintenance Pages 43 10. Click Generate (3). The columns corresponding to the selected data fields are added to the bottom left Grid Columns list. Columns are required elements of a grid. When a user edits a row in a grid, they work with default controls generated automatically according the data field definition. 11. Click Ok to save changes to the grid and close Layout Editor. Figure: Generate Columns for the Grid 12. Save changes and refresh the page if the grid is not displayed correctly. Now you can add the URL of the Countries application page to the site map.

Part 1: Maintenance Pages 44 Adding the Page to the Site Map In this step, you will prepare the initial site map of the application and add navigation to the created RB201000.aspx page by using the System > Customization > Site Map page. The site map already includes the top-level nodes for the RapidByte application as the following screenshot shows. Figure: Top-level menu nodes in the site map Proceed as follows: 1. Add the grouping nodes to the site map: a. Select the RapidByte > RapidByte > Configuration node in the tree and add the following nodes to it, by clicking Add Row (+): Setup Manage These are the grouping headers for the Work Area tab of the navigation pane, as planned in Application Pages. These headers are nodes to which you will add pages in the following lessons. b. For each of these grouping nodes, select the Expanded check box (the rightmost column in the grid) and leave other parameters by default. The Expanded check box controls whether the given node in the application menu is expanded or collapsed. In this step, you prepare the initial site map for further addition of application pages. You don't add all the application pages to the site map, because they don't exist at the moment. You will add the pages to the site map one by one as you create them while working through the lessons. In your own application, you are free to create any number of tabs and name them as you need. c. Click Save to save changes to the site map. You can see the resulting site map (that you get after the completion of the course) in Application Pages. 2. Now you can add the Countries page (RB201000) to the site map. a. Select the RapidByte > RapidByte > Configuration > Manage node and click Add Row(+) to add a new nested node.

Part 1: Maintenance Pages 45 b. Specify the following parameters for the new node: Screen ID: RB.20.10.00 Title: Countries Icon: Empty URL: ~/Pages/RapidByte/RB201000.aspx Expanded: Cleared Leave the default settings for the other columns, and click Save (see the screenshot below). Figure: Add the URL of the Countries page to the site map Now the Countries maintenance page is added to the site, and you can find the page on the Configuration tab of the RapidByte navigation pane (see the screenshot below). Figure: Open the Countries page on the website You can open the Countries page. Notice that the Save and Cancel buttons appear at the top of the page. These buttons correspond to the actions declared in the CountryMaint BLC.

Part 1: Maintenance Pages 46 To apply changes to the row that you are editing in a grid, press Ctrl+Enter. On pressing of this combination, the modified row is saved to the current session but not yet to the database. To save the changes to the database, click Save on the page toolbar.

Part 1: Maintenance Pages 47 Conclusion: Creation of Pages To create an Acumatica Framework application page, you have to: In the application project, define the data access classes that are used on the application page. In the application project, define the business logic controller (graph) that serves as the controller for the application page. The graph contains the definition of data views and actions that can be used on the application page. In the Site\Pages\<Module folder> folder, from one of provided templates, create the ASP.NET page and configure it for working with data provided by the graph. On the System > Customization > Site Map page of the website, add the page URL to the site map, which provides navigation to the page for users. Data access classes (DAC) consist of definition of data fields annotated with attributes. Attributes configure DAC fields in the object model of the application, including the data type, default value, and UI presentation. To generate the initial declaration of a data access class, you can use the Data Access Class Generator provided by Acumatica Framework. You can open the generator from any ASP.NET application page with the TypeName property specified, from the smart tag menu of the datasource control. Graphs, which are application classes derived from PXGraph, provide controllers for logic performed on application pages. A graph contains the definition of data views that provide data retrieval and manipulation and the definition of actions represented by toolbar buttons on the page. To configure ASP.NET container controls on a page, such as forms and grids, you can use the Layout Editor provided by Acumatica Framework. You can open the Layout Editor from the smart tag menu of the control. Layout Editor is the visual editor for the source code of the page. Now you have the first page of the RapidByte application, which represents the list of countries in a grid. In the next lesson, you will create a form for editing of customers.

Part 1: Maintenance Pages 48 Lesson 2: Creating the Customers Maintenance Page In this lesson, you will add another maintenance page, a page with a single form for working with customers. To create the page, you will define the Customer data access class and controller that provides data and actions to be used on the page, and then create the ASPX page that provides the user interface for working with Customer data records. The screenshot below shows the final Customers page on the RapidByte website. Figure: The Customers page on the website Lesson Objective In this lesson, you will create a form for editing of customers that can be selected for a sales order. You will also configure the very simple layout by arranging the controls into two columns on the form.

Part 1: Maintenance Pages 49 Adding the CustomerMaint Graph and Page RB202000 In this step, you will create a new ASP.NET page to work with customers in the application and the business logic controller (graph) that works with this page. Do the following: 1. In the RapidByte folder of the application project, create the new CustomerMaint.cs file from the PXGraph template. namespace RB.RapidByte public class CustomerMaint : PXGraph<CustomerMaint> 2. Build the project. 3. In the Pages > RapidByte folder of Site, create the new RB202000.aspx page from the FormView template. 4. Open the page in the design mode. 5. Specify the following property for the ds datasource control to bind the page to the gpaph: TypeName:RB.RapidByte.CustomerMaint Now you are ready to generate data access classes to work with the page.

Part 1: Maintenance Pages 50 Adding the Customer Class In this step, you will add the new Customer data access class (DAC) to the project. 1. Open Data Access Class Generator and select the Customer table (see the screenshot below). 2. Click CustomerID in the Columns And Attributes list and modify the attributes of this field: a. Remove IsKey = true from the constructor of the DB Identity attribute. b. Delete the UI Field attribute. The CustomerID field is not intended for the user. Figure: Configuring the Customer class in Data Access Class Generator 3. Click CustomerCD in the Columns And Attributes list and modify the following attributes: a. Add IsKey = true to the constructor of the DB String attribute. b. Remove the empty string from the constructor of the Default attribute to prevent insertion of empty strings into the database table. c. Change the display name in the constructor of the UI Field attribute to Customer ID. 4. Click CompanyName and change the display name in the UI Field attribute to Company Name. 5. Click ContactName and change the display name in the UI Field attribute to Contact Name. 6. Click CountryCD and change the display name in the UI Field attribute to Country ID. 7. Click PostalCode and change the display name in the UI Field attribute to Postal Code. 8. Click Generate. The new DAC will be added to DAC\Customer.cs. DAC fields will have the attributes that you have configured in Data Access Class Generator. You could click Generate at once and modify the attributes in code. The PXDefault attribute (the Default attribute in the Data Access Class Generator) specifies the default value for the data field. In PXDefault, you can also specify whether the data field is required for input. By default, the PXDefault attribute makes the field required. You can make the field optional by specifying the PersistentCheck = PXPersistentCheck.Nothing parameter in the attribute constructor. For more information on PXDefault, see Acumatica Framework > API Reference > Attributes > Default Values in the documentation. 9. Rebuild the project. The Customer DAC is ready for now. Go to the next step to add a data view to select data.

Part 1: Maintenance Pages 51 Configuring the CustomerMaint Graph and Page RB202000 In this step, you will create a new ASP.NET page to work with products in the application. To add the page, perform the following instructions: 1. In the CustomerMaint class, declare the Customers data view that selects data to be represented on the page (see the code below). public PXSelect<Customer> Customers; The Customers data view provides retrieval and manipulation with instances of the Customer data access class and is used for the form control on the page. 2. Save changes to the file and rebuild the project. 3. Open the RB202000 page in design mode. 4. Specify the following property for the ds datasource control: PrimaryView:Customers 5. Select the form PXFormView control on the page. 6. In the Properties window, set DataMember:Customers for the form control. 7. Save the changes you've made to the page. 8. Open the Layout Editor for the form control (by clicking the smart tag at the top right corner of the control and selecting Edit Content Layout). 9. On the Fields tab, select all fields. 10. Click Generate. The controls are added to the form under the form node. 11. Set the following properties for the Row PXLayoutRule object: ControlSize: XM LabelsWidth: S The Row layout rule adjusts the size and position of controls on the form and arranges the controls into a column. The Row layout rule should start any form layout. 12. Add the Column PXLayoutRule object as follows: a. Select the Row node in the tree and click Layout Rule on the toolbar. b. Move the new LayoutRule node up under the edfax node. c. Set the following properties for the LayoutRule node: StartColumn: True ControlSize: M LabelsWidth: S The resulting layout of the form looks as follows.

Part 1: Maintenance Pages 52 Figure: Review the resulting form layout 13. Click Ok to close Layout Editor. Save your changes to the page. 14. Add the page to the site map. To do this, open the System > Customization > Site Map page and add the RB202000.aspx page under the Acumatica Company > RapidByte > RapidByte > Work Area > Manage node with the following parameters: Screen ID: RB.20.20.00 Title: Customers Icon: Empty URL: ~/Pages/RapidByte/RB202000.aspx Graph Type: Completed automatically Expanded: Cleared Now the page is ready. Open the Customers page in the RapidByte application in web browser to see how the form looks. The page doesn't include any toolbar buttons and all input controls are text boxes. In the next step, you will modify the graph and page to add standard toolbar buttons and selector controls. Figure: View the Customers page in a web browser

Part 1: Maintenance Pages 53 Adding Selectors to the Page In this step, you will modify the business logic controller (graph) of the Customers page and the page itself to add standard buttons to the page and add selector controls. Do the following: 1. In the declaration of the CustomerMaint class, specify Customer as the second type parameter of PXGraph as the following code shows. public class CustomerMaint : PXGraph<CustomerMaint, Customer> public PXSelect<Customer> Customers; Specifying a data access class in this way implicitly adds definition of stadard actions to the graph. For more details, see Standard Buttons of the Page Toolbar. 2. Save changes to the file and rebuild the project. Open the Customers page in the browser. Now it includes a toolbar with the buttons that can be used to save or revert changes, add and delete data records, and navigate back and forth over existing customers. See the screenshot below. Figure: The Customers page with the standard toolbar 3. In the Customer class, add the PXSelector attribute to the CustomerCD field as follows. [PXDBString(15, IsUnicode = true, IsKey = true)] [PXDefault] [PXUIField(DisplayName = "Customer ID")] [PXSelector( typeof(search<customer.customercd>), typeof(customer.customercd), typeof(customer.companyname))] public virtual string CustomerCD... The PXSelector attribute configures a lookup (selector) control that will let the user select existing customers and load them into the form. In the first parameter of the PXSelector attribute, you specify the key value that is inserted into the field. The next positional parameters is the list of data fields that should be displayed in the lookup. Since the application object model is represented by classes that are complex types, you specify the data fields by using the typeof operator. You can list any data fields retrieved by the Search<> BQL query specified in the first parameter of the attribute. [PXSelector(typeof(SalesOrder.orderNbr))] and [PXSelector(typeof(Search<SalesOrder.orderNbr>))] are equivalent definitions. 4. In the Customer class, add the PXSelector attribute to the CountryCD field as follows. [PXDBString(2, IsUnicode = true)]

Part 1: Maintenance Pages 54 [PXUIField(DisplayName = "Country ID")] [PXSelector( typeof(search<country.countrycd>), typeof(country.countrycd), typeof(country.description), DescriptionField = typeof(country.description))] public virtual string CountryCD... The PXSelector attribute here configures a selector that can be used to select from the list of existing Country data records. Here you additionally specify the DescriptionField property. The combination of DescriptionField and SelectorMode values defines the UI presentation of the value in the control. In the given case, the Description value is displayed in the UI next to the value that is assigned to the field. 5. Rebuild the project. 6. Open the RB202000 page in design mode, click the form, and open Layout Editor. 7. Delete the edcustomercd and edcountrycd nodes from the tree. The corresponding fields will appear on the Fields tab. The value in the Control type column is Selector now because of the PXSelector attribute. 8. Select both CustomerCD and CountryCD field on the Fields tab and click Generate. 9. Position the new CustomerCD and CountryCD nodes in the tree as they were positioned before (CustomerCD under Row and CountryCD under edcity). 10. Click Ok and save changes to the page. If you open the Customers page in the browser, you will see that the Customer ID and Country ID fields are selectors now. You can click the search button to open a window with the list of data records, customers or countries. When you select a customer data record in the Customer ID selector, the fields of the selected customer are loaded into the form. Figure: Final look of the Customers page Standard Buttons of the Page Toolbar The standard page toolbar buttons include data manipulation (Insert, Delete, Save, Cancel), navigation (Next, Previous, First, Last), and clipboard buttons (Copy/Paste). Every toolbar button corresponds to the action declared in the graph. You can use explicit or implicit declaration of actions to add standard buttons to the toolbar.

Part 1: Maintenance Pages 55 In any declaration, the DAC that you specify for actions must be the same as the main DAC of the primary view of the page. The page toolbar manipulates with data records of the main DAC of the primary view. If an action has the DAC other than the main DAC of the primary view, the button will not appear on the toolbar. In the example of the CountryMaint graph, we used the explicit declaration of two actions that correspond to the standard toolbar buttons, Cancel and Save (see the code below). Users need only these two buttons on the Countries page. The users work with a table of records on the page and don't need the other standard buttons, such as Previous and Next for navigation. // Explicit definition of the needed standard buttons public class CountryMaint : PXGraph<CountryMaint> public PXCancel<Country> Cancel; public PXSave<Country> Save;... To add an implicit declaration of standard buttons, specify the second type parameter in the base PXGraph class as the following code shows. By this declaration, the system automatically adds the standard buttons for manipulating the Customer records to the page toolbar, if the main DAC of the primary view specified in the datasource control on the page is also Customer. Below are two equivalent declarations of actions for standard buttons that work with data records of the Customer DAC. All standard buttons are usually used on form edit pages. // Implicit declaration of standard actions public class CustomerMaint : PXGraph<CustomerMaint, Customer> public PXSelect<Customer> Customers; // Explicit declaration of standard actions public class CustomerMaint : PXGraph<CustomerMaint> public PXSave<Customer> Save; public PXCancel<Customer> Cancel; public PXInsert<Customer> Insert; public PXCopyPasteAction<Customer> CopyPaste; public PXDelete<Customer> Delete; public PXFirst<Customer> First; public PXPrevious<Customer> Previous; public PXNext<Customer> Next; public PXLast<Customer> Last; public PXSelect<Customer> Customers; The standard buttons on the page toolbar look like as the following screenshot shows. Figure: Standard buttons on the page toolbar

Part 1: Maintenance Pages 56 Conclusion: Simple Form Edit Pages Now you have one more application page, which is a form for editing of customers. You can configure form layout by using Layout Editor for the form. In Layout Editor, add layout rules to the tree of controls that define the size and positions of controls relatively to each other. Start any layout with a Row layout rule added to the topmost position in the tree of controls. Each Row starts a group of controls that are aligned in a column by default. You can add nested layout rules to get more complex layout and override size and positions of controls. In the next lesson, you will add code that clears the Region field once the CountryCD field changes.

Part 1: Maintenance Pages 57 Lesson 3: Adding Simple Business Logic to the Customers Page Until now, you've been implementing the application logic only declaratively, by adding attributes to DAC fields, defining graph members, and configuring properties of ASPX controls. For the first time, in this lesson, you will add procedural logic that modifies the current data record when a user modifies its Country ID field. Procedural logic is implemented in event handlers that are triggered by the system when the client posts new or modified data to the server. Lesson Objective Get acquainted with the way you implement business logic in Acumatica Framework-based applications Learn how to use the FieldUpdated event to modify values of the same data record. Get the first introduction to cache objects, which store the data modified by the user between round-trips

Part 1: Maintenance Pages 58 Adding Logic on Update of the CountryCD Field In this step, you will add code that clears the Customer.Region value when the Customer.CountryCD value changes. Do the following: 1. In the CustomerMaint.cs file, define the Customer_CountryCD_FieldUpdated() method in the CustomerMaint class as follows. protected void Customer_CountryCD_FieldUpdated( PXCache sender, PXFieldUpdatedEventArgs e) Customer row = (Customer)e.Row; row.region = null; The Customer_CountryCD_FieldUpdated() method is an event handler that meets the delegate type and complies with the event handler naming convention. At run time, the platform analyzes the method name and adds it as a handler of the FieldUpdated event raised on the Customer.CountryCD data field. For more information about events, see Acumatica Framework > API Reference > Event Model in the documentation. The platform triggers events when the data is committed to the server. Events are raised on cache objects each of which works with data records of a certain DAC. The cache object is created by the platform within the graph and provides the intermediate storage of data that is being viewed or modified by the user before it is updated in the database. The data stored in the cache object is preserved between the round-trips of client-server interaction. The cache object on which the event is raised is one of input parameters of the event handler (the PXCache Sender object). FieldUpdated is raised in a chain of row insertion and row updating events and intended for modification of other fields of the same data record. In Customer_CountryCD_FieldUpdated(), you get the currently processed Customer data record from the e.row property and set its Region field to null. 2. Rebuild the project. 3. On the RB202000.aspx page, select the form control and open Layout Editor for it. Set the CommitChanges:true property for CountryCD to enable callback for the control. When the callback is enabled, the page posts the updated data immediately after the user changes the value in the control and moves focus out of the control. The server gets the updated data from the UI and triggers the events according to the event model. 4. Save your changes to the page. Run the Customers page in a web browser. To test the added business logic, open an existing customer, see that the Region value is not null, and select a new Country ID value. The Region value is immediately cleared (see the screenshot below).

Part 1: Maintenance Pages 59 Figure: Test the business logic on the Customers page If you do not enable callback for the Country ID control on the form, the platform will not raise the FieldUpdated event when the user updates the value in the control, because the page doesn't submit the data to the server. In this case, the logic will be executed when a user changes a value in another control with the enabled callback (all data from the form is posted to the server on callback), or initiates saving the row to the database (clicks the Save button on the page toolbar).

Part 1: Maintenance Pages 60 Conclusion: Business Logic Acumatica Framework-based applications work by the event-driven model. To implement procedural logic (validation of values, processing of data records), you have to handle the appropriate events. On the FieldUpdated event for Customer.CountryCD, you clear the Customer.Region value. Field events are used for validation and insertion of default values. Row events are used for controlling of UI presentation, calculations and complex validation of dependent values of the same row. A chain of events is raised on the server when the client submits the specific data. For more information on events, see Acumatica Framework > API Reference > Event Model in the documentation. In the next lesson, you will create the Sales Orders data entry page, which is the main page of the application.

Part 2: Data Entry Pages 61 Part 2: Data Entry Pages In this part of the course, you will create the main data entry page of the application, Sales Orders. On the page, you will add the logic of calculation of sales order totals and insertion the shipment information as soon the customer is specified for the sales order.

Part 2: Data Entry Pages 62 Lesson 4: Creating the Sales Order Data Entry Page In this lesson, you will create the Sales Orders page with basic functionality for working with sales orders. The screenshot below shows the final look of the Sales Orders webpage. Figure: The Sales Orders page on the website Lesson Objective Get acquainted with more attributes on DAC fields, PXDBDefault and PXParent, and learn more about PXSelector. Learn how to create master-detail pages based on the FormDetail template. Learn how to define data views that return master-detail data records.

Part 2: Data Entry Pages 63 Adding the SalesOrderEntry Graph and Page RB301000 In this step, you will create a new ASP.NET page to work with sales orders in the application and the business logic controller that works with this page. Do the following: 1. In the RapidByte folder of the application project, create the new SalesOrderEntry.cs file from the PXGraph template. namespace RB.RapidByte public class SalesOrderEntry : PXGraph<SalesOrderEntry> 2. Build the project. 3. In the Pages > RapidByte folder of Site, create the new RB301000.aspx page from the FormDetail template. The Acumatica Framework provides the following default Visual Studio templates for.aspx pages: Name Description FormDetail The master-detail editing page with FormView and Grid controls FormTab The record-editing page with FormView and Tab controls FormView The record-editing page with one FormView control ListView The record-editing page with one Grid control TabDetail The master-detail page with Tab and Grid controls TabView The record-editing page with one Tab control 4. Open the page in design mode. 5. Specify the following property for the ds datasource control to bind the page to the graph: TypeName:RB.RapidByte.SalesOrderEntry Now you are ready to generate data access classes to work with the page.

Part 2: Data Entry Pages 64 Adding the Product Class In this step, you will add the new Product data access class (DAC) to the project. The procedure is the same as for the Customer DAC. Do the following: 1. Open Data Access Class Generator and select the Product table. 2. Click ProductID (2) in the Columns And Attributes list and modify the attributes of this field: 3. 4. a. Remove IsKey = true from the constructor of the DB Identity attribute (3). b. Delete the UI Field attribute. The ProductID field is not intended for the user. Click ProductCD in the Columns And Attributes list and modify the following attributes: a. Add IsKey = true to the constructor of the DB String attribute. b. Remove the empty string from the constructor of the Default attribute to prevent insertion of empty strings into the database table. c. Change the display name in the constructor of the UI Field attribute to Product ID. Click ProductName in the Columns And Attributes list and modify the following attributes. a. Remove the empty string from the constructor of the Default attribute to prevent insertion of empty strings into the database table. b. Change the display name in the constructor of the UI Field attribute to Product Name. 5. Click Active in the Columns And Attributes list, change the parameter value to true and add PersistingCheck = PXPersistingCheck.Nothing to the constructor of the Default attribute. 6. Click StockUnit in the Columns And Attributes list and modify the following attributes. 7. 8. 9. a. Remove the empty string from the constructor of the Default attribute to prevent insertion of empty strings into the database table. b. Change the display name in the constructor of the UI Field attribute to Stock Unit. Click UnitPrice in the Columns And Attributes list and modify the following attributes: a. Change the parameter value in the constructor of the DB Decimal attribute to 2. b. Change the display name in the constructor of the UI Field attribute to Unit Price. Click MinAvailQty in the Columns And Attributes list and modify the following attributes: a. Change the parameter value in the constructor of the DB Decimal attribute to 2. b. Change the display name in the constructor of the UI Field attribute to Min. Avail. Qty. Click Generate. The new DAC will be added to DAC\Product.cs. Now the Product class is ready. In the following parts of the lesson, you will modify attributes of DAC fields in code.

Part 2: Data Entry Pages 65 Adding the SalesOrder Class In this step, you will add the SalesOrder data access class, which provide the master data for the Sales Orders page. 1. Open the Data Access Class Generator, and generate the SalesOrder DAC with all fields from the SalesOrder table. The new class is added in the DAC\SalesOrder.cs file. 2. In the SalesOrder.cs file, add the PXSelector attribute to the OrderNbr data field and change the field display name to Order Nbr. as follows. [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... The PXSelector attribute defines the selection of data to be displayed in the Order Nbr. lookup control. In the first parameter, you specify the key field whose value is inserted into the SalesOrder.OrderNbr data field. 3. Modify the PXDefault attribute for the OrderDate data field with the typeof(accessinfo.businessdate) parameter, which enables insertion of the current business date for each new sales order. The OrderDate data field holds the creation date of the sales order. [PXDBDate()] [PXDefault(typeof(AccessInfo.businessDate))] [PXUIField(DisplayName = "Order Date")] public virtual DateTime? OrderDate... 4. For the PXDefault attribute of the Hold data field, set the PersistingCheck property to PXPersistingCheck.Nothing, so that the data field has a default value, but is not required for input. [PXDBBool()] [PXDefault(false, PersistingCheck = PXPersistingCheck.Nothing)] [PXUIField(DisplayName = "Hold")] public virtual bool? Hold... By default, the PersistentCheck parameter is set to PXPersistingCheck.Null and doesn't allow saving the null value in the field. 5. On the CustomerID data field, add the PXDefault and PXSelector attributes as shown below. [PXDBInt()] [PXDefault] [PXUIField(DisplayName = "Customer ID")]

Part 2: Data Entry Pages 66 [PXSelector( typeof(customer.customerid), typeof(customer.customercd), typeof(customer.companyname), SubstituteKey = typeof(customer.customercd))] public virtual int? CustomerID... 6. Add the PXDefault attribute with the PersistingCheck = PXPersistingCheck.Nothing parameter to the RequiredDate data field, to specify the default value without making the data field required for input. The Required Date contains the expected date of the order delivery. [PXDBDate()] [PXDefault( typeof(accessinfo.businessdate), PersistingCheck = PXPersistingCheck.Nothing)] [PXUIField(DisplayName = "Required Date")] public virtual DateTime? RequiredDate... Since the PXPersistingCheck.Nothing is specified for the data field, the user can delete the value from the field, which is equal to the current date but default, and then save the sales order without the required date specified. The PersistingCheck parameter can be set to one of the following values: Null - Used to check that the field value is not null. NullOrBlank - Used to check that the field value is not null and is not a string that contains only whitespace characters. Nothing - Used to do not check the field value. By default, the PersistentCheck parameter is set to PXPersistingCheck.Null and doesn't allow to save the null value in the field. 7. Change the display name of the ShippedDate field to Shipped Date. [PXDBDate()] [PXUIField(DisplayName = "Shipped Date")] public virtual DateTime? ShippedDate... 8. Set the decimal scale to 2, default value to 0.0, and disable editing of the LinesTotal and TaxTotal data fields. [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Lines Total")] public virtual decimal? LinesTotal... [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Tax Total")] public virtual decimal? TaxTotal...

Part 2: Data Entry Pages 67 The LinesTotal data field contains the total sales order cost without taxes. The TaxTotal data field contains the sum of taxes in the sales order. 9. On the OrderTotal data field, set the decimal scale to 2 and add the PXDefault attribute with the default value equal to 0.0. The default value simplifies handling of the null value in the OrderTotal field, which is the decimal field used in calculations. [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Order Total")] public virtual decimal? OrderTotal... 10. Save changes and rebuild the project. Now the SalesOrder data access class is ready and you can go to the next step to add the OrderLine DAC to the project.

Part 2: Data Entry Pages 68 Adding the OrderLine Class In this step, you will add the OrderLine DAC that provides detail records for a sales order. Do the following: 1. In Data Access Class Generator, select the OrderLine table and generate the class with all fields from the table. The new class is added in the DAC\OrderLine.cs file. 2. Delete the UI Field attribute and add the PXDBDefault and PXParent attributes to the OrderNbr data field as shown below. [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... The PXDBDefault attribute specifies the value that is inserted into the OrderLine.OrderNbr field obtained from the SalesOrder.OrderNbr field of the master data record, from the one that is current at run time. Without the attribute, the user gets an error on insertion of an order detail, which says that the required OrderNbr value isn't specified. The PXParent attribute defines the master-detail relationship between the data access classes. In particular, the attribute enables cascade deletion of the detail records when the master data record is deleted. When a sales order is deleted, the corresponding order detail records that match the specified query will also be deleted. The PXParent attribute can be added to any data field of the class. However, we recommend to add it to the declaration of the first foreign key. 3. Add the PXSelector attribute to the ProductID data field that selects only active products, as shown below. [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))] public virtual int? ProductID... The SubstituteKey property specifies the field whose value should be shown in the control in the UI instead of the field that is specified in the Search<> command. Usually the property is used to replace a surrogate key for the corresponding natural key, which is presented in the user interface. 4. Specify 0.0 as the default value for the UnitPrice data field, since it is a decimal field used in calculations. [PXDBDecimal(6)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Unit Price")]

Part 2: Data Entry Pages 69 public virtual decimal? UnitPrice... 5. Specify 0.0 as the default value and reduce the decimal scale to 2 for the OrderQty data field as shown below. [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Quantity")] public virtual decimal? OrderQty... 6. Change the display name of the StockUnit field to Stock Unit. [PXDBString(20, IsUnicode = true)] [PXUIField(DisplayName = "Stock Unit")] public virtual string StockUnit... 7. Specify 0.0 as the default value and reduce the decimal scale to 2 for the TaxAmt data field as shown below. [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Tax Amount")] public virtual decimal? TaxAmt... 8. Specify 0.0 as the default value for the DiscPct data field as shown below. [PXDBDecimal(6)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Discount")] public virtual decimal? DiscPct... 9. Specify 0.0 as the default value, reduce the decimal scale to 2, and disable editing of the LinePrice data field in the UI, because the data field will be calculated automatically. [PXDBDecimal(2)] [PXDefault(TypeCode.Decimal, "0.0")] [PXUIField(DisplayName = "Ext. Price", Enabled = false)] public virtual decimal? LinePrice... 10. Rebuild the project. Now the two classes representing the master-detail data of a sales order are ready. Go to the next step, to configure a graph for working with sales orders.

Part 2: Data Entry Pages 70 Configuring the SalesOrderEntry Graph In this step, you will modify the business logic controller (graph) that works with the Sales Orders page. Do the following: 1. In the declaration of the SalesOrderEntry class, specify SalesOrder as the second type parameter of PXGraph as the following code shows. public class SalesOrderEntry : PXGraph<SalesOrderEntry, SalesOrder> 2. Define the Orders and OrderDetails data views in the graph as shown below. These data views will be used as data members for the form and grid controls on the application page. public PXSelect<SalesOrder> Orders; public PXSelect<OrderLine, Where<OrderLine.orderNbr, Equal<Current<SalesOrder.orderNbr>>>> OrderDetails; The framework automatically executes each data view that returns the selected data when the data view is requested from the UI. The Orders data view returns the master record, while the OrderDetails returns the list of details by the specified key, OrderNbr. The relationship between the data sets is defined by the Current<> BQL query parameter. The OrderDetails data view returns the detail data records for the current sales order obtained through the first data view. For more information, see the section below. Now the graph is ready for working with the application page. Go to the next step, to configure the ASP.NET page. Master-Detail Relationship Between Data Views The framework executes data views in the order requested by the webpage. You don't have to execute a data view to retrieve data for the UI. In case of the Sales Orders page, the framework first executes the Orders data view to retrieve the master data record, and then executes the OrderDetails data view. To pass the OrderNbr field value as a parameter to the OrderDetails data view, we use the Current property value of the Orders data view. The last data record retrieved by the Orders data view is available through the Current property of the data view. (We expect to have only one master record available at a time.) Also, when you create the new master data record, it also gets available through the Current property value of the Orders data view.

Part 2: Data Entry Pages 71 Configuring Page RB301000 In this step, you will configure the ASP.NET page that works with sales orders in the application. To add the page, perform the following instructions: 1. Open the RB301000.aspx page in design mode. 2. Specify the following property for the ds datasource control: PrimaryView:Orders 3. Select the form PXFormView control on the page. 4. In the Properties window, set DataMember:Orders. Now you can generate the controls for the form control. 5. Open Layout Editor for the form PXFormView control (by clicking the smart tag in the top right corner of the control and selecting Edit Content Layout). 6. On the Fields tab, select all fields and then click Generate. The corresponding controls will be added to form. 7. Organize the controls into three columns, as the screenshot below shows. Figure: Organize controls on the form To organize the controls into three columns, you have to add a row and two column layout rules to the tree of controls and split the data fields between them. The first column starts with the Row layout rule. To add a column, click Layout Rule and set StartColumn:True for the appeared LayoutRule. Drag and drop the controls into the columns or move the controls by using the Up and Down arrow buttons on the toolbar (see the screenshot below). Figure: Click Up or Down buttons to Move a control in the tree 8. Set the following additional properties for Row:

Part 2: Data Entry Pages 72 9. ControlSize: S LabelsWidth: S Set the following additional properties for both Column node: ControlSize: XM LabelsWidth: S 10. Add one more LayoutRule object, place it above the eddescription node, and set the ColumnSpan property to 2 (see the screenshot below). Figure: Adding the ColumnSpan object A LayoutRule object with ColumnSpan set to 2 stretches the following control onto two columns. 11. Set the following property for the edshippeddate, edlinestotal, edtaxtotal and edordertotal nodes: Enabled: False 12. Click Ok to save the generated controls and to close Layout Editor. Now you can generate the columns and controls for the grid. 13. Select the grid PXGrid control at the bottom of the page. In the Properties window, set DataMember:OrderDetails. 14. Open Layout Editor for the grid PXGrid control. 15. On the Fields tab (see the screenshot below), select all fields by clicking Select All (a) and select the Columns check box below the field list (b). Clear the Controls check box (c), because we don't need to generate controls for this grid. Click Generate (d).

Part 2: Data Entry Pages 73 Figure: Adding columns to the grid Columns are the only required items for a grid. Controls are needed in certain cases. You can learn more about columns and controls of the grid in the T200 Acumatica Framework Fundamentals course. 16. Click Ok to save the generated controls and to close Layout Editor. Save changes. 17. Add the page to the site map. To do this, open the System > Customization > Site Map page and add the RB301000.aspx page under the Acumatica Company > RapidByte > RapidByte > Work Area > Enter node with the following parameters: Screen ID: RB.30.10.00 Title: Sales Orders Icon: Empty URL: ~/Pages/RapidByte/RB301000.aspx Graph Type: Completed automatically Expanded: Cleared Now the page is ready and you can view it in a web browser. The Sales Orders data entry page provides only basic functionality. It allows a user to create new sales orders, edit or delete them, and add products to the order as order details.

Part 2: Data Entry Pages 74 Figure: The Sales Orders data entry page

Part 2: Data Entry Pages 75 Conclusion: Master-Detail Pages When you work with master-detail data access classes, the master key field is specified in the PXDBDefault attribute on the foreign key field of the detail class. To enable cascade deletion of detail records when a master record is being deleted, you have to add the PXParent attribute to one of foreign key fields of the detail class. For the page, the master-detail data is provided by two data views, in which the data view that retrieves the details is linked to the master data record by the Current parameter of the BQL query. Now you have created the Sales Orders page with two containers bound to data views of the SalesOrderEntry graph, as the figure below shows. Figure: Configuring the Sales Orders page The Orders is the first view in the SalesOrderEntry graph, so it is the primary data view. The SalesOrder is the main DAC of the Orders data view and the OrderLine is the main DAC of the OrderDetails data view. In the next lesson, you will add automatic insertion of product data taken from the product parameters specified for an order line.

Part 2: Data Entry Pages 76 Lesson 5: Adding Simple Business Logic to the Sales Orders Page In this short lesson, you will implement automatic insertion and update of product data, which is executed every time a user specifies the product for an order line. Lesson Objective Get aquainted how to configure a combo box by using the PXStringList attribute See once more how to use FieldUpdated event to autocomplete values of the same data record. Learn one of possible ways to retrieve a data record from the database in code by using the static PXSelectorAttribute.Select<>() method.

Part 2: Data Entry Pages 77 Configuring a Combo Box In this step, you will configure a combo box control for the Status field. Do the following: 1. In the SalesOrder.cs file, add the following OrderStatus class, which defines possible value for the status of a sales order, below the SalesOrder class. public class OrderStatus public const string Open = "O"; public const string Hold = "H"; public const string Approved = "A"; public const string Completed = "C"; 2. public class UI public const public const public const public const string string string string Open = "Open"; Hold = "On Hold"; Approved = "Approved"; Completed = "Completed"; Add the PXStringList attribute to the Status field of the SalesOrder data access class (DAC) and set the default value to OrderStatus.Open as follows. [PXDBString(1, IsFixed = true)] [PXDefault(OrderStatus.Open)] [PXUIField(DisplayName = "Status")] [PXStringList( new string[] OrderStatus.Open, OrderStatus.Hold, OrderStatus.Approved, OrderStatus.Completed, new string[] OrderStatus.UI.Open, OrderStatus.UI.Hold, OrderStatus.UI.Approved, OrderStatus.UI.Completed )] public virtual string Status... In the first parameter of the PXStringList constructor, you specify the list of possible values for the field, while in the second parameter, you specify the labels corresponding to the values and displayed in the UI. 3. Rebuild the project. 4. Open Layout Editor for the form control on the RB301000.aspx page to regenerate the input control for the Status field. 5. Delete the edstatus node from the tree, check the box for the Status field on the Fields tab, and click Generate. 6. Move the new edstatus node in the tree to its former position and click Ok. 7. Save the changes to the page.

Part 2: Data Entry Pages 78 If you open the Sales Orders page in the browser now, you will see that the input control for the Status field is a combo box. You can select a value from Open, On Hold, Approved, and Completed (see the screenshot below). Figure: The combo box control for the Status field on the Sales Orders page

Part 2: Data Entry Pages 79 Setting the Combo Box Value at Run Time In this step, you will disable the Status field on the Sales Orders page and define an event handler that automatically sets the Status value when a user changes the Hold value. Do the following: 1. In the SalesOrderEntry.cs file, add the following SalesOrder_Hold_FieldUpdated() method to the SalesOrderEntry class. protected virtual void SalesOrder_Hold_FieldUpdated( PXCache sender, PXFieldUpdatedEventArgs e) SalesOrder order = (SalesOrder)e.Row; if (order.hold == true) order.status = OrderStatus.Hold; else order.status = OrderStatus.Open; The SalesOrder_Hold_FieldUpdated() method is a handler of the FieldUpdated event raised for the Hold field. The method is executed each time the Hold value changes, and the changes are committed to the server from the page. The framework treats the SalesOrder_Hold_FieldUpdated() method as the event handler by the method name and definition. The event triggers on the model level on the DAC field in the cache object that holds the SalesOrder records and not on the ASP.NET control. FieldUpdated is one of events raised in a chain according to the 'Updating a data record' scenario. The FieldUpdated event is used to insert values into the fields that depend from a field of the same DAC. When the server obtains the updated record from the UI, the application triggers the events according to the 'Updating a data record' scenario. For the event scenarios and the description of events, see in Acumatica Framework > API Reference > Event Model in the documentation. 2. Build the project. 3. Open Layout Editor for the form control on the RB301000.aspx page. 4. Select the edhold node in the tree and set the CommitChanges property to True on the Properties tab. 5. Select the edstatus node in the tree and set the Enabled property to False. 6. Click Ok and save the changes to the page. Because you've set the CommitChanges property to true for the Hold field, each time you change the Hold value in the UI and move focus from the field the page sends a callback to the server. As a result, the FieldUpdated event handler is executed and sets the Status value (see the screenshot below). Notice that the Status field is disabled and you can't change its value manually.

Part 2: Data Entry Pages 80 Figure: Setting the status by changing the Hold value on the Sales Orders page

Part 2: Data Entry Pages 81 Inserting Product Details into an Order Line In this step, you will implement insertion of product data taken from the product parameters every time a user updates Product ID in an order line on the Sales Orders page. To do this, you will add the OrderLine_ProductID_FieldUpdated() event handler. Do the following: 1. In SalesOrderEntry.cs, add the OrderLine_ProductID_FieldUpdated() event handler to the SalesOrderEntry class as follows. protected virtual void OrderLine_ProductID_FieldUpdated( PXCache sender, PXFieldUpdatedEventArgs e) OrderLine line = (OrderLine)e.Row; Product product = PXSelectorAttribute.Select<OrderLine.productID>( sender, line) as Product; if (product!= null) line.unitprice = product.unitprice; line.stockunit = product.stockunit; else line.unitprice = null; line.stockunit = null; In the OrderLine_ProductID_FieldUpdated() method, you update the fields with values obtained from the database. In the method, you obtain the data record, which is being updated, from the event arguments passed in the e object. To get the product unit price and stock unit values, you invoke the static Select<>() method of the PXSelector attribute that executes the database query. In the type parameter of the method, you specify the OrderLine.productID data field, whose value is taken from the order object and passed to the SELECT query. On PXSelectorAttribute.Select<>(), the framework selects the data record from the database, where Product.productID (the key field of the selector on OrderLine.productID), is equal to the value obtained from the same field of the line data record. The PXSelectorAttribute.Select<>() method retrieves the data record as an object that you have to cast to the needed DAC, which is Product here. If no data record has been found in the database, the method returns null, and you also insert nulls into the shipment details. On the FieldUpdated event, you modify the data record in memory before it is updated in the cache object (see the 'Updating a data record' scenario) and do not save anything to the database. 2. Rebuild the project. 3. On the RB301000.aspx page, select the grid control and open Layout Editor for it. Set the CommitChanges:true property for ProductID to enable callback for the column. This enables the event to trigger every time the user changes the value within the column and moves focus out of it. 4. Save the changes. Open the Sales Orders page in a web browser. Create a new sales order (see item 1 on the screenshot below), insert a new order line (2), and select the product for it (3). Check whether the product details are inserted correctly: unit price and stock unit. Select a different product in the Product ID column and check whether the order line details have been updated.

Part 2: Data Entry Pages 82 Figure: Compare the inserted values with values displayed in the selector

Part 2: Data Entry Pages 83 Conclusion: Usage of Combo Boxes and Autoinsertion of Values To configure a combo box as an input control for a data field, you use the PXStringList attribute. You place the attribute on the definition of the data field in the data access class. You specify the list of possible value assigned to the field and the list of labels that are displayed in the UI. Acumatica Framework events are raised at the model level and not on the UI. The events are raised on DAC fields and rows in cache objects of the graph, and have nothing to do with ASP.NET controls. To update fields of the same data record, you can handle the FieldUpdated event for the data field from which the other ones depend. The FieldUpdated event is raised in the data record insertion and update event chains before the row events are raised and the data record is updated in the cache object. If you want the event to trigger immediately after the user changes the value in the UI, enable the callback for the control or column by specifying the CommitChanges property to True for the control or column on the ASP.NET page. To obtain a data record in an event handler, you can invoke the PXSelectorAttribute.Select<>() method that uses the BQL query from PXSelector on the specified field. This method enables you to reuse the BQL query defined on the data field and supports maintainability of the code. You can also select a data record by using the Select() method on a data view, or the static Select<>() method of the needed data view type. You can learn more about retrieving data records from the database in code in the T200 Acumatica Framework Fundamentals course. In the next lesson, you will add calculation of sales order and detail totals that is automatically executed while the user edits the document.

Part 2: Data Entry Pages 84 Lesson 6: Calculating Totals In this lesson, you will implement automatic calculation of sales order and detail totals displayed on the Sales Orders page. Course Objective Get the basic understanding of the events to handle for calculation and update of data fields from code.

Part 2: Data Entry Pages 85 Calculating the Extended Price of an Order Line In this step, you will add calculation of the extended price of a order line by the given formula: extended price = product quantity * unit price * (1 - discount / 100). Since you need to update the LinePrice data field that depends on the data fields of the same record, UnitPrice, OrderQty, and DiscPct, you will recalculate the extended price on FieldUpdated events for the latter fields. Do the following: 1. In the SalesOrderEntry graph, define the CalcLinePrice() method as follows. protected decimal? CalcLinePrice(decimal? unitprice, decimal? qty, decimal? discount) return unitprice * qty * (1 - discount / 100); The CalcLinePrice method enables you to reuse the calculation code. Though the method takes nullable parameters, you don't have to check variables for null before they are used, because you've defined the default 0.0 value for each of these data fields in the OrderLine DAC. 2. In the SalesOrderEntry graph, invoke the CalcLinePrice() method in the FieldUpdated() event handlers for the UnitPrice, OrderQty, and DiscPct data fields as follows. protected virtual void OrderLine_UnitPrice_FieldUpdated( PXCache sender, PXFieldUpdatedEventArgs e) OrderLine line = (OrderLine)e.Row; line.lineprice = CalcLinePrice(line.UnitPrice, line.orderqty, line.discpct); protected virtual void OrderLine_OrderQty_FieldUpdated( PXCache sender, PXFieldUpdatedEventArgs e) OrderLine line = (OrderLine)e.Row; line.lineprice = CalcLinePrice(line.UnitPrice, line.orderqty, line.discpct); protected virtual void OrderLine_DiscPct_FieldUpdated( PXCache sender, PXFieldUpdatedEventArgs e) OrderLine line = (OrderLine)e.Row; line.lineprice = CalcLinePrice(line.UnitPrice, line.orderqty, line.discpct); As in the case of initialization of dependent fields (Inserting Product Details into an Order Line), in the code above, you implement calculation of each field, which depends from other fields of the same DAC, in the FieldUpdated event. 3. Rebuild the project. 4. On the RB301000.aspx page, enable the callback by setting the CommitChanges:True property for the OrderQty column of the grid (as the screenshot demonstrates):

Part 2: Data Entry Pages 86 Figure: Enable the callback on the grid column 5. Save the changes. Open the Sales Orders page in a web browser. Create a new sales order and add a product to it. Specify the order line quantity and check whether the extended price has been updated. Because you've set CommitChanges to true for the OrderQty field, when you change its value and move the focus to the next field in the same line, the modifications to the line are immediately sent to the server. As a result, the system raises events on the OrderLine cache object and the extended price of the line recalculated. The page automatically commits changes to the server when you move to the next line or press the Ctrl +Enter key combination, which finishes the editing of the current line. For example, specify the discount and select another line or press Ctrl+Enter. You can see that the discount is immediately applied and the Ext. Price value is updated (see the screenshot below). Figure: Test the extended price calculation

Part 2: Data Entry Pages 87 Calculating the Lines Total and Tax Total of a Sales Order In this step, you will add calculation of the lines and tax totals of a sales order by the given formulas: lines total = sum of the extended prices on all order details, tax total = sum of the tax amounts on all order details. Since you update the master data fields, SalesOrder.LinesTotal and SalesOrder.TaxTotal, that depend on the data fields of the detail data record, OrderLine.LinePrice and OrderLine.TaxAmt you implement update in three event handlers for the detail data record, OrderLine_RowInserted(), OrderLine_RowUpdated(), and OrderLine_RowDeleted(). In these events, you update sales order totals after an order detail has been inserted, updated, or marked as deleted in the cache object. If you update master data fields before the order detail is modified in the cache object, you may get inconsistent data in the master record. For instance, if an order detail hasn't been added to the document due to a validation error, the lines total may have already been increased. Do the following: 1. In the SalesOrderEntry graph, define the OrderLine_RowInserted(), OrderLine_RowUpdated(), and OrderLine_RowDeleted() as follows. protected virtual void OrderLine_RowInserted( PXCache sender, PXRowInsertedEventArgs e) OrderLine line = (OrderLine)e.Row; SalesOrder order = Orders.Current; bool isupdated = false; if (line.lineprice!= null) order.linestotal += line.lineprice; isupdated = true; if (line.taxamt!= null) order.taxtotal += line.taxamt; isupdated = true; if (isupdated) Orders.Update(order); protected virtual void OrderLine_RowUpdated( PXCache sender, PXRowUpdatedEventArgs e) OrderLine newline = (OrderLine)e.Row; OrderLine oldline = (OrderLine)e.OldRow; SalesOrder order = Orders.Current; bool isupdated = false; if (!sender.objectsequal<orderline.lineprice>(newline, oldline)) if (oldline.lineprice!= null) order.linestotal -= oldline.lineprice; if (newline.lineprice!= null) order.linestotal += newline.lineprice; isupdated = true; if (!sender.objectsequal<orderline.taxamt>(newline, oldline)) if (oldline.taxamt!= null)

Part 2: Data Entry Pages 88 order.taxtotal -= oldline.taxamt; if (newline.taxamt!= null) order.taxtotal += newline.taxamt; isupdated = true; if (isupdated) Orders.Update(order); protected virtual void OrderLine_RowDeleted( PXCache sender, PXRowDeletedEventArgs e) OrderLine line = ( OrderLine)e.Row; SalesOrder order = Orders.Current; PXEntryStatus orderstatus = Orders.Cache.GetStatus(order); bool isdeleted = orderstatus == PXEntryStatus.InsertedDeleted orderstatus == PXEntryStatus.Deleted; if (isdeleted) return; bool isupdated = false; if (line.lineprice!= null) order.linestotal -= line.lineprice; isupdated = true; if (line.taxamt!= null) order.taxtotal -= line.taxamt; isupdated = true; if (isupdated) Orders.Update(order); In each of these events, you obtain the sales order to be updated through the Current property of the Orders data view that works with the SalesOrder cache object. The Current property of the Orders data view retrieves the data record that is currently selected in the UI. The order detail data record that has been inserted, updated, or marked as deleted in the cache object is obtained from the event arguments, the e.row object. At the end if each method, you invoke the Orders.Update(order) method to update the data record in the cache object that works with instances of the SalesOrder DAC. 2. Rebuild the project. Open the Sales Orders page in a web browser. Create a new sales order and add several products to it. Check whether the lines total and tax total are correctly calculated when you add products, modify their parameters, or delete products from the order (see the screenshot below).

Part 2: Data Entry Pages 89 Figure: Test calculation of the lines and tax totals

Part 2: Data Entry Pages 90 Calculating the Order Total Finally, you will add calculation of the sales order total by the given formula: order total = lines total + tax total. Since the SalesOrder.OrderTotal data field depends on the fields of the same record, SalesOrder.LinesTotal and SalesOrder.TaxTotal, you will recalculate the order total on FieldUpdated events for these fields. Do the following: 1. Add the SalesOrder_LinesTotal_FieldUpdated() and SalesOrder_TaxTotal_FieldUpdated() event handlers to the SalesOrderEntry class as follows. protected virtual void SalesOrder_LinesTotal_FieldUpdated( PXCache sender, PXFieldUpdatedEventArgs e) SalesOrder order = (SalesOrder)e.Row; order.ordertotal = order.linestotal + order.taxtotal; protected virtual void SalesOrder_TaxTotal_FieldUpdated( PXCache sender, PXFieldUpdatedEventArgs e) SalesOrder order = (SalesOrder)e.Row; order.ordertotal = order.linestotal + order.taxtotal; 2. Rebuild the project. Open the Sales Orders page in a web browser. Create a new sales order and add several products to it. Check whether the order total is calculated correctly as soon as either of Lines Total or Tax Total field is changed (see the screenshot below). Figure: Test the order total calculation

Part 2: Data Entry Pages 91 Conclusion: Calculations For the Sales Orders page, you've added the calculation logic of sales order and detail totals. To calculate a field of the same data record, you implemented the FieldUpdated event handlers for the data fields on which the calculated field depends. To find the sum of extended prices for the order total data field, you've implemented calculation in three RowInserted, RowUpdated, and RowDeleted events on the detail DAC. For calculations, instead of implementing a bunch of event handlers, you can use the PXFormula attribute. PXFormula handles the RowInserted, RowUpdated, and RowDeleted events in a similar way as you've handled these events to update SalesOrder.LinesTotal and SalesOrder.TaxTotal fields. You can see the examples of PXFormula use in the T200 Acumatica Framework Fundamentals course and in the documentation: Acumatica Framework > API Reference > Attributes > Referential integrity and Calculations.

Part 3: Inquiry Pages 92 Part 3: Inquiry Pages In this part of the course, you will create the Sales Order Inquiry page. Inquiry pages usually display a list of data records selected by the specified filter.

Part 3: Inquiry Pages 93 Lesson 7: Creating the Sales Order Inquiry Page In this lesson, you will create the Sales Order Inquiry page that provides the list of documents selected by the specified customer (see the screenshot of the page below). Figure: The Sales Orders Inquiry page on the website

Part 3: Inquiry Pages 94 Adding the SalesOrderInq Graph In this step, you will add the business logic controller that works with the Sales Order Inquiry page. Do the following: 1. In the RapidByte folder of the application project, create the new SalesOrderInq.cs file from the PXGraph template. The graph definition should look as follows. namespace RB.RapidByte public class SalesOrderInq : PXGraph<SalesOrderInq> 2. In the SalesOrderInq graph, define the SalesOrderFilter data access class with two unbound data fields,customerid and Status. Data fields of the SalesOrderFilter class will be used as filtering parameters on the inquiry page. The definition of the SalesOrderFilter DAC looks as follows. [Serializable] public class SalesOrderFilter : IBqlTable #region CustomerID public abstract class customerid : IBqlField [PXInt] [PXDefault] [PXUIField(DisplayName = "Customer ID")] [PXSelector( typeof(customer.customerid), typeof(customer.customercd), typeof(customer.companyname), SubstituteKey = typeof(customer.customercd))] public virtual int? CustomerID get; set; #endregion #region Status public abstract class status : PX.Data.IBqlField [PXString(1, IsFixed = true)] [PXUIField(DisplayName = "Status")] [PXStringList( new string[] OrderStatus.Open, OrderStatus.Hold, OrderStatus.Approved, OrderStatus.Completed, new string[] OrderStatus.UI.Open, OrderStatus.UI.Hold, OrderStatus.UI.Approved, OrderStatus.UI.Completed )] public virtual string Status get; set; #endregion The filter class is defined as an ordinary DAC that consists of only unbound data fields. Data fields are unbound because we use them only for UI and we do not retrieve data records of

Part 3: Inquiry Pages 95 this class from the database. We will use this class to compose the BQL query that returns data records selected by the customer specified in the filtering parameter displayed in the UI. 3. In the SalesOrderInq graph, define the Filter data view of the specific PXFilter type as follows. Define the Cancel action to add the Cancel button to the page that clears the filter. public PXCancel<SalesOrderFilter> Cancel; public PXFilter<SalesOrderFilter> Filter; The PXFilter type of data views is used to provide data selection parameters on pages. The data view of this type always returns one record that consists of the filtering parameters specified by the user in the UI. The PXFilter data view never requests data from the database. The type parameter of the PXFilter data view must be the DAC that provides filtering parameters, which is SalesOrderFilter in our case. The Filter data view must be declared as the first data view in the graph and specified as the primary view for the datasource control on the page. 4. In the SalesOrderInq graph, define the SalesOrders data view that retrieves all data records if no customer and status are specified in the filter, or the data records selected by the specified customer and status. public PXSelectReadonly<SalesOrder, Where2<Where<Current<SalesOrderFilter.customerID>, IsNull, Or<SalesOrder.customerID, Equal<Current<SalesOrderFilter.customerID>>>>, And<Where<Current<SalesOrderFilter.status>, IsNull, Or<SalesOrder.status, Equal<Current<SalesOrderFilter.status>>>>>>> Orders; The PXSelectReadonly type of the data view specifies that the data is displayed in read-only mode in the UI. Because of the data view type, the framework disables edit controls in the UI for data fields retrieved through the data view. The resulting definition of the SalesOrderInq graph is given below. public class SalesOrderInq : PXGraph<SalesOrderInq> [Serializable] public class SalesOrderFilter : IBqlTable #region CustomerID public abstract class customerid : IBqlField [PXInt] [PXDefault] [PXUIField(DisplayName = "Customer ID")] [PXSelector( typeof(search<customer.customerid>), typeof(customer.customercd), typeof(customer.companyname), SubstituteKey = typeof(customer.customercd))] public virtual int? CustomerID get; set; #endregion #region Status public abstract class status : PX.Data.IBqlField [PXString(1, IsFixed = true)] [PXUIField(DisplayName = "Status")] [PXStringList( new string[] OrderStatus.Open, OrderStatus.Hold,

Part 3: Inquiry Pages 96, OrderStatus.Approved, OrderStatus.Completed new string[] OrderStatus.UI.Open, OrderStatus.UI.Hold, OrderStatus.UI.Approved, OrderStatus.UI.Completed )] public virtual string Status get; set; #endregion public PXCancel<SalesOrderFilter> Cancel; public PXFilter<SalesOrderFilter> Filter; public PXSelectReadonly<SalesOrder, Where2<Where<Current<SalesOrderFilter.customerID>, IsNull, Or<SalesOrder.customerID, Equal<Current<SalesOrderFilter.customerID>>>>, And<Where<Current<SalesOrderFilter.status>, IsNull, Or<SalesOrder.status, Equal<Current<SalesOrderFilter.status>>>>>>> Orders; 5. Rebuild the project. Now the graph that works with the inquiry page is ready. Go to the next step to add the ASP.NET page.

Part 3: Inquiry Pages 97 Adding Page RB401000 In this step, you will create a new ASP.NET page that provides inquiry of sales orders by the specified customer. To add the page, perform the following instructions: 1. In the Pages > RapidByte folder of Site, create the new RB401000.aspx page from the FormDetail template. 2. Open the created page in design mode and specify the following properties for the ds datasource control: 3. TypeName: RB.RapidByte.SalesOrderInq PrimaryView: Filter Select the form PXFormView control on the page and set the DataMember:Filter property for it. Now you can generate controls for the form, which represent filtering parameters. 4. Open Layout Editor for the form control. 5. On the Fields tab, click Select All and Generate. 6. Select the Row node in the tree and set the following properties for it: 7. 8. 9. Merge:True ControlSize: S LabelsWidth: S Set the following properties for the edcustomerid node: CommitChanges: True Size: XM Set the following properties for the edstatus node: CommitChanges: True LabelWidth: 50px Click Ok to close Layout Editor. 10. Select the grid PXGrid control on the page and set the following properties for it: DataMember: Orders SkinID: Inquire 11. Open Layout Editor for the grid. 12. On the Fields tab, select any fields that you want to see for each sales order on the inquiry page. In our example, we select the OrderNbr, OrderDate, Status, CustomerID, ShippedDate, and OrderTotal data fields. 13. Select the only Columns check box below the field list. Click Generate. 14. Close Layout Editor and save changes on the page. 15. Add the page to the site map. To do this, open the System > Customization > Site Map page and add the RB401000.aspx page under the Acumatica Company > RapidByte > RapidByte > Work Area > Explore node with the following parameters: Screen ID: RB.40.10.00 Title: Sales Order Inquiry Icon: Empty

Part 3: Inquiry Pages 98 URL: ~/Pages/RapidByte/RB401000.aspx Graph Type: Completed automatically Expanded: Cleared Open the Sales Order Inquiry page in a web browser. Select a customer in the Customer ID filter and the status in the Status filter and check whether the list has been filtered by the specified values (see the screenshot below). Click Cancel to clear the filter. Figure: Test the filter on the Sales Order Inquiry Page

Part 3: Inquiry Pages 99 Conclusion: Inquiry Pages To provide filter for an inquiry page, you can use the specific PXFilter data view type. In the Filter data view of this type, you specify the class that provides filtering parameters. Typically, you define a separate DAC that consist of only unbound data fields to specify the class in the PXFilter data view. On the ASPX page, you have to enable the callbacks for controls that display the filtering parameters. In the data view that provides data for the grid on the inquiry page, you compose the BQL query that selects data records by the specified filter. Avoid using the PXFilter data view type with DACs which have at least one key field defined, i.e. contain fields having the IsKey=true parameter in the type attribute.

Part 4: Processing Pages 100 Part 4: Processing Pages In this part of the course, you will create the processing page that provides mass approval of sales orders.

Part 4: Processing Pages 101 Lesson 8: Creating the Approve Sales Orders Page In this lesson, you will create a page for processing of sales orders. The processing operation changes the status of a sales order to Approved. The screenshot below shows the Approve Sales Orders page on the RapidByte website. Figure: The Approve Sales Orders page on the website

Part 4: Processing Pages 102 Implementing the Approval Operation In this step, you will define the ApproveOrder() method that processes a sales order and sets the status to Approved. Also, you will add the Approve button to the Sales Orders page that invokes the method to process the sales order currently opened on the data entry page. The same ApproveOrder() method will be used on the processing page. Do the following: 1. In the SalesOrderEntry graph, define the ApproveOrder() method as follows. public void ApproveOrder(SalesOrder order, bool ismassprocess = false) Orders.Current = order; if (order.status == OrderStatus.Hold) throw new PXException(String.Format( "Order 0 is On Hold and cannot be approved.", order.ordernbr)); else if (order.status!= OrderStatus.Open) throw new PXException(String.Format( "Order 0 is already approved.", order.ordernbr)); order.status = OrderStatus.Approved; Orders.Update(order); Persist(); if (ismassprocess) PXProcessing.SetInfo(String.Format( "Order 0 has been successfully approved.", order.ordernbr)); In the ApproveOrder() method, you set the Status field to Approved flag for the order obtained from the input parameter of the method. To work with a SalesOrder data record, you use the SalesOrderEntry graph; after the status is set to Approved for the object in memory, you invoke the Orders.Update() method to update the order in the cache object, and then call the Persist() method to save changes to the database. The ismassprocess flag means that the method is invoked from the mass processing page, where a user can select multiple sales orders and run the operation for them. In the case of mass processing, you return the successful processing message to the UI by using the static PXProcessing.SetInfo() method. To return a processing error to the UI, you throw the PXException with the specified error message. For internationalization support and better maintainability of the application, you should use localizable constants instead of hard-coded strings in the application. For more information, see Acumatica Framework > Programming Tasks > Localizing Applications in the documentation. 2. In the SalesOrderEntry graph, define the Approve action as follows and invoke the processing method in the approve() handler for this action. public PXAction<SalesOrder> Approve; [PXProcessButton] [PXUIField(DisplayName = "Approve")] protected virtual IEnumerable approve(pxadapter adapter) foreach (SalesOrder order in adapter.get()) Actions.PressSave();

Part 4: Processing Pages 103 PXLongOperation.StartOperation(this, delegate() SalesOrderEntry graph = PXGraph.CreateInstance<SalesOrderEntry>(); graph.approveorder(order); ); yield return order; This is the typical definition of a processing action that is invoked from a data entry page. The approve() method meets the delegate type of an action handler. Also, the action handler must have the same name as the PXAction field but with another case of the first letter, as the code above shows. To run the ApproveOrder() processing method within the approve() action handler, you invoke the PXLongOperation.StartOperation() that takes the anonymous method in which you create a separate instance of the graph and invoke the ApproveOrder() method for processing of a sales order. The delegate you pass to StartOperation() is executed in a separate thread. Before you run the operation, you invoke the Actions.PressSave() method to save the last changes made on the data entry page to be sure to process the latest version of the sales order. Use the PXGraph.CreateInstance<T>() method to instantiate graphs. 3. Rebuild the project. Open the Sales Orders page in a web browser. Select a sales order and click Approve. As soon the document has been processed, the status of the sales order becomes Approved (see the screenshot below). Figure: Test the approval operation

Part 4: Processing Pages 104 Adding the SalesOrderProcess Graph In this step, you will add the business logic controller that works with the Approve Sales Order page. Do the following: 1. In the RapidByte folder of the application project, create the new SalesOrderProcess.cs file from the PXGraph template. 2. Define the Cancel action for the toolbar and the Orders data view that provides data records to be processed on the page, as follows. public class SalesOrderProcess : PXGraph<SalesOrderProcess> public PXCancel<SalesOrder> Cancel; public PXProcessing<SalesOrder> Orders; The Orders data view will be the primary view for the page. 3. In the SalesOrderProcess graph, define the constructor as follows. public SalesOrderProcess() Orders.SetProcessCaption("Approve"); Orders.SetProcessAllCaption("Approve All"); Orders.SetProcessDelegate<SalesOrderEntry>( delegate(salesorderentry graph, SalesOrder order) graph.clear(); graph.approveorder(order, true); ); The constructor overrides captions of two standard buttons that will be automatically added to the toolbar due to the PXProcessing type of the primary view of the page. By default, the framework adds the Process and Process All buttons to the page toolbar. To override the button captions, you use the SetProcessCaption() and the SetProcessAllCaption() methods. Also, the constructor specifies the processing delegate that is the required definition in graph. To specify the processing method, you invoke the Orders.SetProcessDelegate() method that takes the anonymous method with two input parameters. For such type of the delegate, the framework creates the specified graph and gets the list of orders selected on the page. For each order, the framework executes the specified anonymous method in which you invoke the ApproveOrder() method. Every time the anonymous method gets the same instance of the graph, so we first call the graph.clear() method, which deletes all data from cache objects within the graph, before we process the next order. 4. Rebuild the project. Now the graph is ready and you can create the ASP.NET page that displays the list of sales orders to process.

Part 4: Processing Pages 105 Adding Page RB501000 In this step, you will create a new ASP.NET page for mass approval of sales orders. To add the page, perform the following instructions: 1. In the SalesOrder.cs file, add the unbound Selected data field to the SalesOrder data access class. #region Selected public abstract class selected : IBqlField [PXBool] [PXUIField(DisplayName = "Selected")] public virtual bool? Selected get; set; #endregion The Selected data field is unbound, which is defined by the PXBool type attribute. Selected will be used only for UI to display the check box for selection of multiple sales orders on the page. Selected is the default name of a DAC field that the framework uses for a check box that marks records on processing pages. You can learn more about processing pages in the T200 Acumatica Framework Fundamentals course. 2. Save the changes and rebuild the project. Now you can create a new ASP.NET page that displays the list of sales orders for processing. 3. In the Pages > RapidByte folder of Site, create the new RB501000.aspx page from the ListView template. 4. Open the page in design mode and specify the following properties for the ds datasource control: 5. TypeName: RB.RapidByte.SalesOrderProcess PrimaryView: Orders Select the grid PXGrid control at the bottom of the page. In the Properties window, specify the following properties for it: DataMember:Orders SkinID: Inquire 6. Open Layout Editor for the grid PXGrid control. 7. On the Fields tab, select any fields that you'd want to see for each sales order in the list for processing. In our example, we select the Selected, OrderNbr, OrderDate, Status, CustomerID, ShippedDate, and OrderTotal fields from the list. 8. Select the Columns check box below the field list and clear the Controls check box. Click Generate. 9. Move the Selected column to the topmost position in the Grid Columns list and set AllowCheckAll: True for this column. As a result, the column header will contain a check box which you can use to select all data records in the grid. 10. Click Ok to save the generated controls and to close Layout Editor. 11. Save the changes. 12. Add the Daily node as a child of the Acumatica Company > RapidByte > RapidByte > Processes node.

Part 4: Processing Pages 106 13. Add the page to the site map. To do this, open the System > Customization > Site Map page and add the RB501000.aspx page under the Acumatica Company > RapidByte > RapidByte > Processes > Daily node with the following parameters: Screen ID: RB.50.10.00 Title: Approve Sales Order Icon: Empty URL: ~/Pages/RapidByte/RB501000.aspx Graph Type: Completed automatically Expanded: Cleared Open the Approve Sales Order page in a web browser. On the page, you can select one or multiple sales orders to launch processing for all of them at once. Select several sales orders and click Approve. The processing result appears on the page (see the screenshot below). The Cancel button clears the selection and refreshes the list of sales orders on the page. Notice that you can select all data records in the grid by checking the box in the header of the first column. Figure: Test the mass sales order approval

Part 4: Processing Pages 107 Conclusion: Processing Pages For the graph that works with the processing page, you have to: Use the specific PXProcessing (or derived) data view type to provide data records for the page. Define the processing delegate for the PXProcessing (or derived) data view in the graph constructor. The processing delegate is a method that executes for each data record selected on the page. To add a check box to the page that enables users to select multiple data records for processing, you have to define the unbound boolean field in the DAC whose data records are processed. Also, you have to add the column for this data field to the grid that lists the processed data records. The Process and Process All buttons are automatically added to the toolbar of the page whether you specify the PXProcessing data view as the primary view for the page or not.

Part 4: Processing Pages 108 Lesson 9: Setting Up the UI for Approved Sales Orders In this short lesson, you will set up the graph to display approved sales orders in read-only mode on the Sales Orders page. Lesson Objective See how to use the RowSelected event to dynamically set up the UI depending on the current data record.

Part 4: Processing Pages 109 Making Approved Sales Orders Read-only in the UI To make the page display approved sales orders in read-only mode, do the following: 1. In the SalesOrderEntry graph, define the SalesOrder_RowSelected() event handler as follows. protected virtual void SalesOrder_RowSelected(PXCache sender, PXRowSelectedEventArgs e) SalesOrder order = (SalesOrder)e.Row; if (order == null) return; bool editable = order.status!= OrderStatus.Approved && order.status!= OrderStatus.Completed; Orders.Cache.AllowUpdate = editable; Orders.Cache.AllowDelete = editable; PXUIFieldAttribute.SetEnabled(sender, order, editable); OrderDetails.Cache.AllowDelete = editable; OrderDetails.Cache.AllowInsert = editable; OrderDetails.Cache.AllowUpdate = editable; Approve.SetEnabled(editable && order.hold!= true); The framework raises the RowSelected() event for every data record displayed on the page. The event is raised in the end in all scenarios of events. The RowSelected event is used to set up the UI for a particular data record or the details grid. In the SalesOrder_RowSelected() event, you disable or enable editing of the currently selected data record, which depends on the Status value. If a sales order has the Approved or Completed status, the page displays the data record in read-only mode: users cannot update or delete the read-only data record. Otherwise, the Approve button is enabled only if the Hold value isn't true. The PXUIFieldAttribute.SetEnabled() method disables all controls on the page except for the first declared control that displays the key value. The user can select another key value to navigate between data records in the UI. The AllowDelete, AllowUpdate, AllowInsert properties disable the corresponding toolbar buttons that works with data records kept in the corresponding cache objects. Thus, the Orders.Cache.AllowDelete = false; property disables the Delete button for the sales order; the OrderDetails.Cache.AllowDelete = false; property disables the Delete button for the order details. To configure the Approve button, we use the Approve.SetEnabled(...) method. 2. Rebuild the project. Open the Sales Orders page in a web browser and select an approved document. Make sure the UI is disabled for the sales order and doesn't allow the user to edit the document (see the screenshot below).

Part 4: Processing Pages 110 Figure: View an approved sales order in read-only mode

Part 4: Processing Pages 111 Conclusion: UI Setup To make certain data records display in read-only mode, implement the UI setup in the RowSelected() event handler. By the specified condition, you can dynamically enable/disable controls and buttons for the currently selected data record on the page. Use the PXUIFieldAttribute.SetEnabled() method to disable/enable controls, and AllowDelete, AllowUpdate, and AllowInsert properties of the cache object to enable/disable buttons on the page. You have to implement both UI disabling and enabling logic, because the controls are not automatically enabled once you've disabled them from the code.

Part 5: Reports 112 Part 5: Reports In this part of the course, you will see how to create a report by using Report Designer, the visual tool provided with Acumatica Framework, on the example of the printable sales order page. The printable layout definition of a page is created in the same way as a report.

Part 5: Reports 113 Lesson 10: Creating the Sales Orders Printable Form In this lesson, you will design the report form for sales order printing. Printable pages are created in Report Designer in the same.rpx format as report forms. On the website, the form for generating printable documents looks the same as the form for building reports. While making the report, you will also add a report variable that provides row numbers in the detail section of the report. Additionally, you will add the status of a sales order to the printable page. The screenshot below shows the report that you will be able to generate by the end of this lesson. Figure: The Sales Orders form on the website

Part 5: Reports 114 Preparing Data for the Report The first step of creating a report is preparing the report query, which is called the 'report schema' in Report Designer. To configure the report schema, you have to complete the following steps: 1. Add the data access classes to the report that will provide the data required for the report, SalesOrder, OrderLine, Product, and Customer. 2. Specify the relationships between the selected DACs, which are left and inner joins. 3. Add the report parameters intended for user input, OrderNbr. 4. Specify filtering conditions based on the parameters and other requirements to the report. 5. Save the report form to the file in the web site folder, /Site/ReportsDefault. 1. Adding Data Access Classes to the Report To start configuring the report schema, you need to add the required data access classes to the report. 1. Start Report Designer located by default under All Programs > Acumatica in the Windows Start menu. The Report Designer visual editor opens a new blank report form. To create a new report, you can select File -> New from the main menu of Report Designer. 2. In the File menu, select Build schema. The Schema Builder dialog box appears. 3. In the dialog, select the Tables tab and enter the application URL, http://localhost/rapidbyte/, in the Web service URL field, as it is shown in the screenshot (1). 4. Set Login=admin (2) and Password=123 (3) in the text boxes at the right side of the dialog. You can specify any account to log in to the web site for loading of the DAC schema. In this lesson, you use the admin account with unrestricted access rights. 5. Click Load schema (4). The list of data access classes loaded from the web site appears. The list includes all data access classes of your application, including the system DACs that you do not need to add to the report. 6. In the list of DACs, select the SalesOrder, OrderLine, Product, and Customer (5) data access classes and move them to the selected list by clicking the right-arrow button. 7. Click Apply to apply the selected data access classes to the report schema (6).

Part 5: Reports 115 Figure: Selecting DACs for the report Your changes aren't saved to the file until you save the report by selecting File -> Save in the main menu. 2. Defining Relationships Between DACs After you have added the data access classes to the report, you need to specify the relationships between these DACs that represent data joining conditions. 1. On the Relationships tab of the Schema Builder dialog box, specify of the selected DACs. Row 1 (see the screenshot below): Parent Table: SalesOrder (1) Join type: Left (2) Child Table: OrderLine (3) After you select the child table, click at the table below to switch to the second table for specifying the join condition. Parent Field: SalesOrder.OrderNbr (4) Link Condition: Equal (5) Child Field: OrderLine.OrderNbr (6) Operator: And (7) Row 2: Parent Table: OrderLine Join type: Left Child Table: Product

Part 5: Reports 116 Parent Field: OrderLine.ProductID Link Condition: Equal Child Field: Product.ProductID Operator: And Row 3: Parent Table: SalesOrder Join type: Inner Child Table: Customer Parent Field: SalesOrder.CustomerID Link Condition: Equal Child Field: Customer.CustomerID Figure: Specifying join relationships for DACs The Join type parameter of a condition is equivalent to a join operation in SQL Server. You may use inner, left, right, full and cross joins to retrieve data records based on the corresponding logical relationships. The parent-child order of DACs is important. Do not specify the same data access class as the child in more than one relationship. To delete a relationship, select it from the first table and press Del. 2. Click Apply to apply changes to the report schema. 3. Adding Report Parameters Report parameters are usually intended for manual input on the page before a user runs the report. 1. On the Parameters tab of the Schema Builder dialog box, add the OrderNbr report parameter. To add a parameter, click Add below the list of parameters at the left of the dialog box (see 1 on the screenshot). Set the following properties for the OrderNbr parameter:

Part 5: Reports 117 Name: OrderNbr (2) Data Type: String (3) View name: =[SalesOrder.OrderNbr] (4) Prompt: Order Number (5) Allow Null: selected (6) Visible: selected (7) Required: selected (8) Figure: Adding the report parameters The Allow Null property determines whether the parameter must be provided by the user before running of the report. An optional parameter may have a null value during report execution. The Required property specifies that the parameter must be provided by the user before running the report. Otherwise, the report won't be generated. 2. Click Apply to apply the parameter to the report schema. 4. Specifying Data Filtering Conditions Report filters define which data is selected for the report based on the parameters and other conditions required for the report. 1. On the Filters tab of the Schema Builder dialog box, specify the data filtering condition (see the screenshot). SalesOrder.OrderNbr Equal @OrderNbr The report parameter name is preceded by '@' in the filter expression.

Part 5: Reports 118 Figure: Specifying filtering conditions In the filtering condition, you select the sales order by its number taken from the required report parameter, @OrderNbr. 2. Click OK to apply changes and to close the Schema Builder dialog box. Now the report schema provides the data to be displayed in the report. Save changes in the report file as it is described below. 5. Saving the Report to a File You can save the report to a file in the web site folder or to the database. Users who have no direct access to the site folder can save the report to the database. In this example, you save the report to the file. Do the following: 1. Select File > Save As from the main menu. To save the report to the database, you select Save On Server from the main menu. To save the created report form to the server, you need to specify an account having the Customizer user role. The admin account has unrestricted access rights, and so you can use this account for saving the report forms to the server. The report form (.rpx file) is saved to the UserReport table of the application database. In this case, you will not see the file in the /Site/ReportsDefault folder, but the report form is available for adding to the site map. To edit the report, you can open it from server by selecting Open From Server in the main menu. 2. In the dialog, open the /Site/ReportsDefault/ folder and save the report with the RB601000 file name. The RB601000.rpx report is saved to the web site folder. The website engine searches for reports and style template files in the /Site/ReportsDefault/ folder, which is specified in the PX.Reports.ReportFileManager.ReportsDir property in Site/ App_Code/Auxiliary/Initialization.cs. Now you can go to the next step to add a variable to the report.

Part 5: Reports 119 Adding a Variable to the Report In this step, you will add the RowNumber variable to the detail section of the report. Adding a Report Variable By using report variables, you can insert some calculated values related to the document, such as row numbers in the detail section. To sequentially numerate order detail rows in the printed document, add the RowNumber variable to the report. 1. On the Properties tab, select the detailsection1 DetailSection object from the drop-down list (see 1 on the screenshot). 2. In the Variables property, click the three ellipses button to open the variable collection editor (2). 3. In the ReportVariable Collection Editor dialog box, click Add (3). 4. Set the following properties for the new variable (4): 5. Name: RowNumber ProcessOrder: Always ResetGroup: OrderNbr Click OK to close the dialog box and to add the variable to the report. Save changes. Now you can use the variable in a text box value expression referred by its name preceded with the dollar $ sign, as follows: $RowNumber. This variable will be used for printing detail row numbers on the form. You can declare the same variable in several sections. In this case, the variable will be shared between them. Once modified in one section, the new value will be passed to the next section where the variable is used. The variable is initiated at the top-most section where it is referred. Then the variable is sequentially modified in the following sections by the order as they declared in the report. Figure: Adding the RowNumber variable to the detail section Now the schema is ready and the report is ready for layout design. Go to the next step to add text boxes to the form that display the report data.

Part 5: Reports 120 Configuring the Printable Page Layout In this step, you will add one group for the sales order information and place text boxes on the form that displays data on the printable page. You can also apply style templates to the text boxes that you add to the report (see the details below). Adding a Group to the Form Add the OrderNbr group for displaying the sales order information. Do the following: 1. Open the RB601000.rpx report form in Report Designer. 2. On the Properties tab, select the report1 Report object from the drop-down list. To select the report form, you can also click the top left box located under the toolbar on the report form. The black marker will appear in the box. Figure: Select the report form by clicking the box at the top left corner of the form 3. On the Properties tab, click the three ellipses button (...) in the Groups property to open the Group Collection Editor. Figure: Open the Group Collection Editor 4. In the Group Collection Editor window, click Add to add a new group (see 1 on the screenshot below). 5. Specify the following properties of the group (2): KeepTogether:WholeGroup PrintEmpty:False (Name):OrderNbr 6. Click the three ellipses button (...) in the Grouping property of the OrderNbr group (3). 7. In the GroupExp Collection Editor window, click Add (4) and set DataField:SalesOrder.OrderNbr (5), by which the nested data records will be grouped. Click OK to apply the property and to close the window.

Part 5: Reports 121 Figure: Add the OrderNbr group to the report The OrderNbr group has been added to the report. Adding Text Boxes to Sections A report form consists of the page header and footer, group sections, and the detail section nested within the most internal group. You can group data by several parameters and nest groups one in another. The data records of each nested group are grouped by the data field specified in the higherlevel group. Each group consists of the two sections on the form, header and footer. The header section contains the group caption and captions for the detail data records. The footer is used to display sums and other aggregated values calculated by the detail data. Add the text boxes to the report sections as described below: 1. Add the Delivery Note text box to the printable page header and place a line at the border of the page header section. Adjust the height to 24px of this and of all other text boxes on the form. Figure: Page Header Elements

Part 5: Reports 122 To add a text box to the report form: Click TextBox on the toolbar and then click on the place where you want to position the box. Place the report name text box at the top right corner of the report. Double-click the added text box and type the displayed value. Type Delivery Note for the first text box. This is the value of the Value property of the text box. Enlarge the text box height by 8px by pulling the handle at the bottom of the box, so that the text box height becomes equal to 24px. Adjust the text box width so that the whole text is visible in the box. You can also specify the text box height on the Properties tab in the Size group. The style parameters, including the font and text align, are applied by using the style templates, which is demonstrated further. It is also possible to adjust the style in the Style group of the properties, but we don't recommend you to specify every style property for each text box individually. Figure: Enlarge the height of a text box 2. Select the OrderNbr group header section and set the PrintOnEveryPage property to True. Group header and footer sections has this property that enables repeating of the section content on every page in the report. 3. Add the text boxes that display the sales order information to the OrderNbr group header section and set the following values for them: To: =IsNull([Customer.CompanyName],'') + IsNull('br '+[Customer.Address],'') + ISNull('br '+[Customer.City],'') + IsNull('br '+[Customer.CountryCD],'') + IsNull('br '+[Customer.Region],'') + IsNull('br '+[Customer.PostalCode],'') Attn: =[Customer.ContactName] Order ID: =[SalesOrder.OrderNbr] Customer ID: =[Customer.CustomerCD] Order Date: =[SalesOrder.OrderDate] Shipment Date: =[SalesOrder.ShippedDate] Status: =[SalesOrder.Status]

Part 5: Reports 123 No. Product Item Stock Unit Quantity Unit Price Discount Extended Price Figure: Add the OrderNbr group header elements 4. Add the text boxes that display the order details to the detail section of the report and set the following values for them: =Assign('$RowNumber',$RowNumber+1) =[Product.ProductName] =[OrderLine.StockUnit] =[OrderLine.OrderQty] =[OrderLine.UnitPrice] =[OrderLine.DiscPct] =[OrderLine.LinePrice] Figure: Detail Section Elements 5. On the Properties tab, select the OrderNbr group footer section and specify the following properties for it: PrintAtBottom: True PrintOnEveryPage: True ProcessOrder: Always ResetPageNumber: True The PrintAtBottom property makes the group footer printed always at the bottom of a page. A page break is inserted after the section. The ProcessOrder property is commonly used for sections and particular text boxes. Once specified for a section, the property is applied to every text box in this section unless the property is overridden for this text box. The ProcessOrder property specifies when the text box value is processed during the report generation. A text box value can be processed when

Part 5: Reports 124 the data is read from the database (WhileRead), when the report is printed (WhilePrint), or in both cases (Always). In most cases, when a value is just read from the database and then displayed in the report, WhileRead is enough, for instance, for the employee's name. However, if the value is calculated depending on data that will be known only at the report rendering stage, the WhilePrint mode should be specified. In our example, the sales order total will have the conditional visibility depending on the page count. The sales order total should be printed on the last page of the report. You should use the WhilePrint mode for the sales order total, because the last page number will be known only at the report rendering stage. Document totals are typical data fields with the WhilePrint processing mode. The ProcessOrder property is also applied to sections. This property should be set to Always for those sections that have at least one text box with the WhilePrint processing mode. The ResetPageNumber property specifies whether the page number is reset after the section is printed. 6. Add the text boxes to the OrderNbr group footer section and specify the following values for them: ***Continued*** Lines Total: =[SalesOrder.LinesTotal] Tax Total: =[SalesOrder.TaxTotal] Order Total: =[SalesOrder.OrderTotal] ='Page: '+[PageOf] Figure: OrderNbr Group Footer Elements 7. On the Properties tab, set the following properties for the ***Continued*** text box: ProcessOrder: WhilePrint VisibleExpr: =([PageIndex]<[PageCount]) The ProcessOrder property makes the text box processed at the report rendering stage. The visibility condition specifies that the label appears on all pages except for the last page in the report. 8. Set the following properties for all text boxes from the group footer section, except for ***Continued*** and Page: ProcessOrder: WhilePrint VisibleExpr:=([PageIndex]=[PageCount]) These properties make the values printed on only the last page of the report. 9. Save the changes.

Part 5: Reports 125 Now the printable page is ready for running on the web site. Before you test the page, see how to format the report by using the style templates. Applying Style Templates Style templates are extremely useful for quick and uniform formatting of text displayed in a report. There are two files with default style templates located in the Site/ReportsDefault/ folder, TemplateReport.rpx and TemplateForm.rpx. The TemplateReport.rpx file contains styles that you can use for reports, while the styles from TemplateForm.rpx file are intended for printable pages. You can use these default styles, as well as define your own custom styles required for the application. Specify the styles template file for the report and apply style templates to text boxes, to make the report look neat. 1. On the Properties tab, select the report1 Report object from the drop-down list. 2. In the StyleTemplate property, click the three ellipses button (...) and select the TemplateForm.rpx file. You have specified the file containing the style templates for the printable page. Now you can apply styles defined in this file to text boxes on the form. 3. Select the Delivery Note text box on the form. 4. On the Properties tab, set StyleName:Report Name for the text box (see the screenshot below). Figure: Specify the Report Name style for the text box The appearance of the text box value has changed according to the applied style. In the same way, you can specify styles for other text boxes on the form. We recommend that you follow the design guidelines on creating reports in Report Designer, see Acumatica Framework > Report Designer > Recommendations in the documentation. Go to the next step to test the printable page on the website.

Part 5: Reports 126 Adding the Report URL to the Site Map In this step, you will add the URL to the report to the site map to make users able to run the report. Complete the following steps: 1. Open the System > Customization > Site Map page and add the Forms node under Acumatica Company > RapidByte > RapidByte > Reports. 2. Add a new node under Acumatica Company > RapidByte > RapidByte > Reports > Forms with the following parameters: 3. Screen ID: RB.60.10.00 Title: Sales Orders Icon: Empty URL: ~/Frames/ReportLauncher.aspx?ID=RB601000.rpx Graph Type: Completed automatically Expanded: Cleared Save the changes to the site map. Now you can open the Sales Orders page on the website.

Part 5: Reports 127 Running the Report Form Open the Sales Orders report launcher page on the website. To get a printable version of a sales order, select the Report Parameters tab item, specify the Order Number parameter and click Run Report (see the screenshot below). Figure: Run the report form The framework opens the sales order page for printing. The web browser Print dialog appears by clicking Print on the toolbar (see the screenshot).

Part 5: Reports 128 Figure: Run the report to get the printable document

Part 5: Reports 129 Conclusion: Reports A report or printable page is a separate.rpx file that is created in the visual report builder, Report Designer, a tool provided with Acumatica Framework. While making the report, you construct the query that is called the schema of the report; you design the position and style of text fields in the report. You can save the report file to the web site folder or application database. The report has the specific URL in the site map. Users run reports from the specific ReportLauncher page defined in the site map with a particular report ID. To build the report, the user specifies the parameters and clicks Run. Based on the parameters and layout defined in the.rpx file, the platform generates the report and then outputs the result in printable format.

Appendix: Application Design 130 Appendix: Application Design In this course, we have already designed the application for you. During the development of Acumatica Framework-based applications, you have to perform the following steps of application design: Analyse the requirements, plan the entity model of the application. Prepare the ORM that consists of the database schema and the data access class design. Plan the webpages that provide the user interface of the application. You create application pages from specific Acumatica Framework page templates. Plan the business logic controller (a graph) for each page, which encapsulate business processes and use-cases that should be implemented in the application. Each of these steps is iterated for multiple times as the development is progress.

Appendix: Application Design 131 Database Schema During the database design, you define how the main business objects will be stored in the database. Entity-Relationship Model Since the requirements, a sales order is the main object the application works with. Sales orders are in one-to-many relationship with order lines. Order lines are in many-to-one relationship with products. Multiple products can be associated with an order line, and the same product can be added to multiple order lines. Each order has a reference to a customer; these objects are in one-to-many relationship with sales orders. The customer is a person or company who ordered the products. The resulting model is shown in the diagram below. Figure: The entity-relationship model of the RapidByte application Database Schema You have to design the database schema that stores the application data. You have to create and maintain your own application tables in the database during the design and development of the application. The Acumatica Framework-based application template contains only the system tables that are required for the platform. You can modify your own application tables but not the Acumatica Framework system tables, because any changes to the system tables would be lost on Acumatica Framework upgrade. We have already designed the database of the RapidByte application. The RapidByte application uses five application tables: SalesOrder, OrderLine, Customer, Product, and Country (see the schema below).

Appendix: Application Design 132 Figure: The database schema of the RapidByte application In each application table, we have to set the primary key and define nullable columns. Foreign keys are not necessary; it depends on the database design approach you use. We do not define foreign keys in the database since we will use the approach according to which the database schema has only the primary key restrictions and all other restrictions are defined at the higher level of the application object model. The SalesOrder table uses the string OrderNbr column as the primary key. Users can manually input the sales order number and then search for a sales order by this number. The corresponding OrderNbr data field is set as the key in the data access class. Non-null columns are only the absolute required sales order attributes, OrderNbr, OrderDate, and CustomerID. The primary key of the OrderLine table consists of two columns, OrderNbr and ProductID. The corresponding data fields is set as the key fields in the data access class. The OrderNbr column links an order detail to the master record from the SalesOrder table. This link is set up in the data access class. The ProductID column is included in the primary key to support multiple products on a sales order. As for nullable columns, we consider that all columns can be null except for the primary key. The OrderLine.ProductID value is taken from the ProductID column of the Product table. The ProductID is the identity column set as the primary key in the Product table. Unlike the table,

Appendix: Application Design 133 the string ProductCD value, which is the product code, set as the key in the data access class. The different database-level and model-level keys are used to provide a user-friendly key of a product in the UI, which is the product code. Acumatica Framework keeps the ProductCD value unique for each product and handle the ProductID column automatically. Users work only with ProductCD values that identify products in the UI. As for nullable columns, we consider all product attributes as required and make all columns not-null. The Customer table has the CustomerID as the primary key, while the key of the data access class is CustomerCD. The Country table is a country dictionary that is referred from the Customer table. The Country table uses the string CountryCD column as the primary key since the list of dictionaries is rather fixed and short. In our application, the country identifier is a two-letter code from ISO 3166. At the same time as you plan the database schema, you can design the object model of your application. The object model is represented by data access classes that are mapped to the database tables of your application. For more information on database design, see Acumatica Framework > Design Guidelines > Database Design Conventions in the documentation.

Appendix: Application Design 134 Data Access Classes Planning and designing of database tables is performed simultaneously with designing of the application data access classes (DACs). DACs encapsulate working with database tables in the application. The application class diagram (which follows this paragraph) shows the relationships between data access classes of the RapidByte application. We could define these relationships as foreign keys at the database level, but we use the generic database schema approach and define these restrictions only at the object model level. In the application, you work only with classes that represent data on a higher, database-abstract level. To retrieve data you write BQL statements, which are transparently translated into SQL statements by the Acumatica Data Access Layer. Unique key fields for each data access class are marked on the diagram with gray. The relationships between DACs are defined declaratively in attributes on the DAC data fields. As you can see from the diagram, the data access classes directly correspond to the database tables we created at the previous step. A sales order may exist without any details, so the relationship is one-to-many (1-0..*). The relationship between the Product and OrderLine entities is also oneto-many (actually, 0-0..*), because an order detail cannot be saved without the specified product: OrderLine.ProductID is a required reference to a product from the Product table. In the Product and Customer classes, the CD data field is included in the key instead of ID. The ID field corresponds to the database identity column in each table; in the data access class, these identity fields are annotated with the PXDBIdentity attribute and not included in the key fields of the class. Also, an additional SalesOrderFilter filter class (not shown on the diagram) is used in the application. Filter classes are used only for data representation and they don't used for queries to the database or implementation of any business logic. Figure: The data access class diagram of the RapidByte application

Appendix: Application Design 135 Application Pages In Acumatica Framework-based applications, the user interface is provided through webpages. The user works with webpages and doesn't need to install any specific client. You create application pages from the specific Acumatica Framework ASP.NET templates. You can use the visual editors provided by Acumatica Framework to add controls to ASP.NET pages and configure properties of the controls. The sales order printable form is not an ASP.NET page, it is an.rpx definition file. The file contains the data definition and the page layout description for printing. The definition file is created in Report Designer. To create a printable form or report, you have to compose just the definition file, add the file to the website, and then add the specific report URL to the site map. To get the printable document, you run the printable form from the specific report launcher page provided by Acumatica Framework. The report launcher renders the output and generates the document for printing. Support for generating printable forms and reports is built-in in Acumatica Framework. You don't have to create any application pages or business logic controllers for rendering of reports and printable forms. In Acumatica Framework, every page has a unique identifier that encodes the module, type and number of the page. We will assign the following identifiers to the RapidByte pages, in which the first two symbols denote the module, the second two symbols denote the page type, and the last two symbols denote the sequential number of the page: Countries RB201000 (maintenance) Customers RB202000 (20 means maintenance) Sales Orders RB301000 (30 means data entry) Sales Order Inquiry RB401000 (40 means inquiry) Approve Sales Order RB501000 (50 means processing) Sales Orders Form RB601000 (60 means reports and printable layouts) We recommend the page identification convention for use in Acumatica Framework-based applications. For more information about the convention, see Acumatica Framework > Design Guidelines > Application Design Guidelines in the documentation. In Acumatica Framework-based applications, you usually create the following types of pages: data entry, maintenance, inquiry, and processing. Data entry pages are the most often used pages in the application. These pages provide input of business documents, such as sales orders. Maintenance pages are also used for data input, but more rare than the data entry pages. Maintenance pages are used to input of helper objects mostly during configuration and less used over time. Inquiry pages provide selection of data by the specified parameters. On inquiry pages, users specify the selection criteria and retrieve the records that match the criteria. Processing pages are dedicated to mass processing of records for instance, provides changing of the document status for a list of selected documents at once. You can learn more about pages of different types in the T200 Acumatica Framework Fundamentals course. The Navigation Menu and the Site Map In the user interface, all pages are typically grouped into four tabs of the navigation pane according to their purpose and frequency of use. The menu structure is composed in the site map that is configured on the System > Customization > Manage > Site Map page. For the RapidByte application, we suggest the following menu: Work Area tab ( ): Enter: Sales Orders

Appendix: Application Design 136 Manage: Customers Explore: Sales Order Inquiry Processes tab ( Daily: Approve Sales Orders Reports tab ( ): ): Forms: Sales Orders Configuration tab ( ): Manage: Countries For instance, the Work Area tab of the navigation pane will look as follows. Figure: Work Area Tab In the site map, the menu structure is configured as shown in the following screenshot.

Appendix: Application Design 137 Figure: The site map of the RapidByte application module menu