2019 R1 Framework Dev Guide
2019 R1 Framework Dev Guide
DEVELOPER GUIDE
Acumatica Framework 2019 R1
| Contents | 2
Contents
Copyright......................................................................................................6
Accessing Data........................................................................................... 73
Querying Data in Acumatica Framework........................................................................... 73
BQL and LINQ...................................................................................................... 73
Data Access Classes.............................................................................................. 75
PXView and PXCache of the Data View.................................................................... 76
Data Query Execution............................................................................................77
Translation of a BQL Command to SQL.................................................................... 80
Comparison of Fluent BQL, Traditional BQL, and LINQ............................................... 85
Fluent BQL and Traditional BQL Equivalents..............................................................86
To Execute BQL Statements................................................................................... 93
To Process the Result of the Execution of the BQL Statement..................................... 96
Creating Fluent BQL Queries........................................................................................... 97
Fluent Business Query Language............................................................................ 97
Data Access Classes in Fluent BQL..........................................................................99
Search and Select Commands and Data Views in Fluent BQL.................................... 100
Constants in Fluent BQL...................................................................................... 102
Parameters in Fluent BQL.....................................................................................103
To Select Records by Using Fluent BQL.................................................................. 104
To Use Parameters in Fluent BQL Queries...............................................................107
Creating Traditional BQL Queries.................................................................................... 111
| Contents | 4
Glossary................................................................................................... 219
| Copyright | 6
Copyright
No part of this document may be reproduced, copied, or transmitted without the express prior consent
of Acumatica, Inc.
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. HubSpot is a registered trademark of HubSpot,
Inc. Microsoft Exchange and Microsoft Exchange Server are registered trademarks of Microsoft
Corporation. All other product names and services herein are trademarks or service marks of their
respective companies.
In This Guide
• Accessing Data
• Glossary
| Acumatica Framework Overview | 8
In This Part
Role-based access A set of components responsible for user authorization, access rights
verification, and audit on the data access and business logic levels. For more
information, see Role-Based Access in the System Administration Guide.
Localization tools The tools that help you to perform the localization of the application to
multiple languages. For more information about localization, see Translation
Process.
| Acumatica Framework Overview | 10
Help management The integrated wiki-based help content editing, management, and search
system system. For details about the help management system, see Wiki Overview.
Document management The integrated document storage and management system. For details, see
system Managing Attached Files.
Report Designer A separate utility (which can be installed along with Acumatica ERP or
Acumatica Framework) that you can use to design custom reports. For
details on this tool, see Acumatica Report Designer Guide.
Acumatica Framework
Acumatica Framework provides the platform API, web controls, and other tools for the development
of the UI and business logic of an ERP application. The platform API is used for the development of
Acumatica ERP and any embedded applications (that is, customizations of Acumatica ERP). Acumatica
Framework can also be used to develop an ERP application from scratch. You can find detailed
information about the development of applications with Acumatica Framework in this guide.
Acumatica Framework consists of the development tools listed in the following table.
Component Description
Web controls A set of web controls implementing access to business logic through the
web UI.
Template application The application and database structures providing frameset, layout, and
navigation services. You can deploy a template application along with the
template website by using the Acumatica Framework Configuration Wizard.
Visual Studio templates The project template for the creation of a new application and a set of page
templates that automate the creation of typical page layouts.
Application creation A set of components to automate the creation of the application data access
wizards classes from the database tables and the web forms during application
development.
For details about the development tools, see Acumatica Framework Development Tools.
Acuminator
Acuminator is a static code analysis and colorizer tool for Visual Studio that simplifies development
with Acumatica Framework. Acuminator provides diagnostics and code fixes for common developer
challenges related to the platform API. Also, Acuminator can colorize and format business query
language (BQL) statements, and can collapse attributes and parts of BQL queries. You can find related
information and download Acuminator at Visual Studio Marketplace.
exported, or imported. Because customizations are stored separately, they are preserved with the
updates and upgrades of the core application.
For details on Acumatica Customization Platform, see Acumatica Customization Platform.
Mobile API
Acumatica ERP provides the Acumatica mobile application, which allows a user to work with Acumatica
ERP through the mobile devices. You can customize the mobile application by using the mobile API. For
details on the mobile API, see Working with Mobile Framework.
Platform API
The Platform API provided with Acumatica Framework is an event-driven programming API, which is
traditional in rich GUI applications. This model covers database access, business logic, GUI behavior,
and error handling. All coding is done with only C#.
The following code gives an example of the business logic implemented in the business logic controller:
The code updates the receipt total when one of the transactions related to the receipt is updated.
When a user selects a document transaction in the table on a webpage and updates the settings of the
transaction, the RowUpdated event is triggered, and the code above is executed and updates the receipt
total, as shown in the following screenshots.
• A set of page templates that automate the creation of typical page layouts
The master pages mechanism in ASP.NET allows for the creation of an application that looks and feels
consistent. Master pages define the standard appearance and behavior that is common in all application
pages. You create individual content pages that refer to the master page. When a content page is
requested, it merges with the master page to produce output that combines the layout and base
functionality of the master page with the content of the requested page.
Acumatica Framework fully supports the master pages mechanism and provides you with a set of
predefined master pages. You can design your own master pages or modify existing ones.
A web application written with Acumatica Framework supports style modification through Cascading
Style Sheets (CSS).
• Reading the data structure from a table, SQL query, or external data source
• Creating a data access class based on the data structure received from external data source
• Reading the data access class structure from its definition and merging this structure with the
data structure received from the external data source
• Automatic mapping of application-specific attributes based on the names of the properties of the
external data source
The Layout Editor, which is shown in the following screenshot, automates the creation of new web
forms. It uses metadata stored in the business logic controller and data access class to help the
application developer create new web forms or to modify existing ones. The Layout Editor wizard
implements the following features:
| Acumatica Framework Overview | 14
• Reading metadata from the business logic controller and the data access class and creating a list
of controls that could be added to the webpage
• Updating the webpage controls with the changed business logic controller and the data access
class metadata
• Data views, which include the references to the required data access classes, their relationships,
and other meta information
• Business logic, which consists of actions and events associated with the modified data.
Each business logic controller can be accessed from the presentation layer or from the application code
that is implemented within another business logic controller. When the business logic controller receives
an execution request, it extracts the data required for request execution from the data access classes
included in the data views, triggers business logic execution, returns the result of the execution to the
requesting party, and updates the data access classes instances with the modified data.
For details on working with the business logic layer, see Implementing Business Logic.
Presentation Layer
The presentation layer provides access to the application business logic through the UI, web services,
and Acumatica mobile application. The presentation layer is completely declarative and contains no
business logic.
The UI consists of ASPX webpages (which are based on the ASP.NET Web Forms technology) and
reports created with Acumatica Report Designer. The ASPX webpages are bound to particular business
logic controllers. You create webpages from the templates provided with Acumatica Framework and
modify them with the help of the Layout Editor, which utilizes metadata information extracted from
the business logic controller. Reports created with Acumatica Report Designer are loaded and executed
by using the predefined business logic controller included in Acumatica Framework. For details about
processing of the reports in the system, see Display of Reports.
When the user requests a new webpage, the presentation layer is responsible for processing this
request. Webpages are used for generating static HTML page content and providing additional service
information required for the dynamic configuration of the web controls. When the user receives the
requested page and starts browsing or entering data, the presentation layer is responsible for handling
asynchronous HTTP requests. During processing, the presentation layer submits a request to the
business logic layer for execution. Once execution is completed, the business logic layer analyzes
any changes in the business logic container state and generates the response that is sent back to the
browser as an XML document.
For details on the configuration of ASPX webpages, see Configuring ASPX Webpages and Reports.
Web services and mobile app pages provide alternative interfaces to the application business logic.
From the side of the business logic controller, a request from a webpage, the web services, or an mobile
app page are identical and, thus, cause the execution of exactly the same business logic.
| Getting Started with Acumatica Framework | 17
System Requirements
You can find the full list of system requirements in System Requirements for Acumatica Framework
2019 R1 in the Installation Guide.
Installation
For detailed information about the installation of Acumatica Framework, see Installing Acumatica
Framework in the Installation Guide.
Application Design
For the information about the design of the database structure and user interface of applications based
on Acumatica Framework, see Designing the Application in this guide.
For a quick overview of application programming, refer to the topics in this part of the guide.
In the Acumatica Framework Guide, you can find reference information and additional information that
is not covered in the training courses. This information is provided in the following parts of the guide:
• Configuring ASPX Webpages and Reports: About the development of ASPX pages
• Accessing Data: About business query language (BQL) and working with data in cache and session
• Implementing Business Logic: About events, attributes, long-running operations, and other topics
related to business logic development
For a detailed description of the Acumatica Framework API, see API Reference.
Website Management
For the classic UI, if you want to adjust the position of a webpage in the site map, add a new page
to the site map, or remove a page from the site map, you configure the site map of the website, as
described in Managing the Site Map in the System Administration Guide.
If you want to modify the position of a webpage in the modern UI, add a page to a workspace, or
remove a page in the modern UI, you configure the modern UI as described in Customizing the Modern
User Interface in the System Administration Guide.
You need to grant access rights to each new page. For details on the configuration of access rights, see
Managing User Access Rights in the System Administration Guide.
| Getting Started with Acumatica Framework | 18
You can create help topics for any application you have developed with Acumatica Framework by using
the built-in wiki-based content management system. For details on creating help topics, see Managing
Wikis.
In This Part
• Data Querying
Data Querying
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, as shown in the following example.
PXSelect<Product,
Where<Product.availQty, IsNotNull,
And<Product.availQty, Greater<Product.bookedQty>>>>
If the database provider is MIcrosoft SQL Server, the framework translates this expression into the
following SQL query.
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 shown in the following code fragment.
using System;
using PX.Data;
• A public abstract class (which is also referred to as class field) to reference a field in the
BQL command
• A public virtual property (which is also referred to as property field) to hold the table field
data
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.
The code below demonstrates an example of how to get data records from the database.
• Products – the data view that can be used for querying and modifying the data
| Getting Started with Acumatica Framework | 20
• Cancel – the action that discard all the changes made to the data and reloads it from the
database
• Save – the action that commits the changes made to the data to the database and then reloads
the committed data
Based on this declaration, the system automatically instantiates the DAC cache.
A DAC cache object in the Acumatica Framework is the primary interface for working with individual
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
collection is instantiated based on the corresponding DAC declaration and managed by the cache.
• The controller: The cache component that implements basic CRUD (create, read, update, delete)
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 the data modification.
The diagram below shows the internal graph structure and responsibilities of the data view and the
cache.
If the updated record exists in the Cached collection the cache controller locates it and updates it with
the new values. The diagram below illustrates this scenario.
| Getting Started with Acumatica Framework | 23
If the deleted record is found in the Cached collection, the cache controller locates it and marks as
deleted. The diagram below illustrates this scenario.
| Getting Started with Acumatica Framework | 26
• Save the new version of data to the database using the Persist() method of the graph
• Discard all in-memory changes and load the original data version using the Clear() method of
the graph
From the user interface 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 the changes to the database.
| Getting Started with Acumatica Framework | 28
Preserving the Data 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 data 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.
There are a number of predefined attributes implemented in the framework. For example, in the
following declaration of a data field for a column, the PXDBDecimal attribute binds this field to a
database column of the decimal type.
[PXDBDecimal(2)]
public virtual string AvailQty { get; set; }
The attributes that bind a field to a specific data type 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). The following example shows the use of the
PXUIField attribute.
[PXDBDecimal(2)]
[PXUIField(DisplayName = "Available Qty", Enabled = false)]
public virtual string AvailQty { get; set; }
You can also defined your own attributes, as shown in the following code.
These custom attributes can also be added to the DAC declaration, as shown in the following example.
[PXDBDecimal(2)]
[PXUIField(DisplayName = "Available Qty", Enabled = false)]
[MyAttribute]
public virtual string AvailQty { get; set; }
In This Part
In This Chapter
• Primary Key
• Audit Fields
• Multitenancy Support
• System tables: Those that are created by default for the application template and not used to
store your application data
• Application tables: Acumatica ERP tables (which exist if you have implemented customization) and
your own tables
Do not add columns to system tables or modify them in any other way. Such modifications could
corrupt the application and would be lost during the next database upgrade.
Regarding your own application tables, you have to design and create the needed tables that store your
application data. You then map these application tables to data access classes (DACs) that define the
object model of the application. In one table, you can keep data records of multiple entities, each of
which is defined as a separate data access class in the application object model.
• Make sure that table and column names are valid C# identifiers, because these names match the
names of the classes and properties you declare in the application. Do not start a table or column
name with a digit.
• Do not use the underscore symbol (_) in table or column names, because it is a reserved
symbol in Acumatica Framework. For example, TenantType is a valid column name, while
Tenant_Type is invalid.
• Use singular nouns for table names. Typically, a table is mapped to a data access class that
represents the entity. For instance, the SOShipment table contains data records that represent
instances of the SOShipment entity.
: Acumatica Framework generates SQL statements with table and column names in the same letter
case (that is, uppercase or lowercase) as the corresponding data access classes and fields are
declared in the application. Also, the DAC Generator tool produces data access class declarations in
the same letter case as the tables and columns are defined in the database schema.
• Use two prefixes in table names: a two-letter tenant name and then a two-letter application
module prefix. For example, the MTSVAppointment table can be used in the Services (SV)
module for the MyTenant tenant (which corresponds to the MT prefix). These prefixes help to
distinguish your application tables from Acumatica ERP tables and tables of other vendors if you
create an add-on project or extension library.
• If you add a column to an Acumatica ERP table, start the column name with the Usr prefix
followed by the two-letter tenant name. For instance, you could use UsrMTColumn for the
column of the MyTenant tenant. In this case, the column will be preserved during upgrades. In
your own application tables, there are no strict requirements to start column names with any
prefixes.
• Be sure that custom indexes on Acumatica ERP tables start with the Usr prefix followed by the
two-letter tenant name, so that the indexes will be preserved during upgrades.
Value Data Type (SQL Data Type Type Attribute on the Data
Server) (MySQL) Field
Primary Key
You have to define the primary key in each application table that you create. The primary key may
consist of one column or multiple columns. The primary key must include the CompanyID column if one
is defined in the table. For details on the CompanyID column, see Multitenancy Support.
For each table, you can use one of the following typical variants of primary keys:
• One key column included in the primary key in the table and set as the key in the data access
class
• A pair of columns, with one column included in the primary key in the table and the other column
set as the key in the data access class
• Multiple columns that are included in the primary key and set as the compound key in the data
access class
: In a setup table, only the CompanyID column must be included in the primary key.
For instance, you can define two columns in the Product table, ProductID and ProductCD.
ProductID is the identity column that is the only column included in the primary key of the table.
ProductCD is the string key of a product instance, which is entered by the user through the UI. The
ProductCD column isn't included in the primary key and is handled as the unique key column by
Acumatica Framework.
| Designing the Application | 36
Audit Fields
Audit fields keep meta information on the creation and the last change of a database record. Audit fields
are updated automatically by the framework.
To enable the tracking of audit data for a particular table, you should add the columns listed below
to the table and declare the corresponding audit data fields in the data access class. You have to add
the corresponding type attribute to each audit field. If the audit columns are properly created in the
database table and the corresponding data fields are declared in the data access class, Acumatica
Framework automatically updates audit data in these fields every time a data record is modified from
the application. The audit column parameters and DAC attributes are given below.
Audit Columns
Database Column Data Type (SQL Data Type Type Attribute on the Data
Name Server) (MySQL) Field
Database Column Data Type (SQL Data Type Type Attribute on the Data
Name Server) (MySQL) Field
Database Column Data Type (SQL Data Type Type Attribute on the Data
Name Server) (MySQL) Field
Database Column Data Type (SQL Data Type Type Attribute on the Data
Server) (MySQL) Field
Database Column Data Type (SQL Data Type Type Attribute on the Data
Server) (MySQL) Field
DeletedDatabaseRecord
bit; not null TINYINT(1); not Not declared in DAC
null
Multitenancy Support
Multiple tenants can work on the same instance of an Acumatica Framework-based application with
completely isolated data. The application looks identical to all tenants, but each tenant has exclusive
access to its data only. Data is isolated at the lowest level of the application, in the data access layer
that executes SQL queries for the tenant of the user who is currently signed in.
Multitenancy Support
The following graphic illustrates how different logical tenants work with the Acumatica Framework-
based application in a multitenant configuration. They work with the same application but have isolated
data access, as if they are working with different database instances.
Multitenancy support is turned on or off for each particular table individually. To turn on multitenancy
support for a table, add the CompanyID column to it and include the column in the primary key
(see the column parameters in the table below) and all indexes. The CompanyID column is handled
automatically by the framework and should not be declared in data access classes. If a table doesn't
have the CompanyID column, all data from the table is fully accessible to all tenants that exist in the
database. For more information, see Support of Multiple Tenants.
Database Column Data Type (SQL Data Type Type Attribute on the Data
Name Server) (MySQL) Field
CompanyID int; not null; INT; not null; Not declared in DAC
included in primary included in primary
key and all indexes key and all indexes
from each of the other two tenants have access to their company's individual data only. Physically, the
data of all three tenants is stored in a single database instance.
Support for shared data access is turned on or off for each particular table individually. To turn on
support for shared data access for a table, add the CompanyMask column to the table (see the column
parameters in the table below). The CompanyMask column is handled automatically by the framework
and should not be declared in data access classes. If a table doesn't have the CompanyMask column,
shared data access is not available for this table.
Database Column Data Type (SQL Data Type Type Attribute on the Data
Name Server) (MySQL) Field
CompanyMask is a 32-bit mask. In this mask, each two bits correspond to each tenant. The first of
these two bits specifies whether the record may be read by this tenant, and the second bit specifies
whether the record may be written to by this tenant. For example, suppose that CompanyMask is set
to 0xBE02 for a record. That is, it specifies the following mask: 10 11 11 10 00 00 00 10, which
designates that the record may be both read and written to by the tenants with company IDs 2 and 3,
the record may be read by the tenants with IDs 4 and 5 and the system tenant (which has ID 1), and
the record may not be read or written to by other tenants.
CompanyMask: 10 11 11 10 00 00 00 10
CompanyID: 4 3 2 1 8 7 6 5
The default value of CompanyMask is 0xAA, which means that the record may be read by all tenants.
In This Chapter
1. Under the Tools > Options > Web Forms Designer > CSS section, set the following options:
2. Under the Tools > Options > Web Forms Designer > CSS Styling section, select Auto Style
Application, and specify the following settings:
• Use width and height attributes for image instead of CSS: Selected
• Use <strong> and <em> for bold and italic text: Cleared
• Change positioning to absolute for controls added using Toolbox, paste, or drag
and drop: Selected
Form Numbering
When you are numbering forms in Acumatica ERP, use the following conventions:
XX999999
| | | |_ Subscreen Sequential Number
| | |___ Screen Sequential Number
| |_____ Screen Type:
| 10: Setup
| 20: Maintenance
| 30: Data Entry
| 40: Inquiry
| 50: Processing
| 60: Reports
|_______ Two-Letter Module Code
Report Numbering
When you are numbering reports in Acumatica ERP, use the following conventions in addition to those
outlined above:
XX6X9999
| Designing the Application | 41
|
|____ Report Type:
61: Review Reports (Reports for document review prior to release)
62: Register Reports (Reports used to print audit information
on processed documents or entities)
63: Balance Reports (Reports reflecting current or historical
balance information)
64: Forms (Printed webpages)
65: Inquiry Reports (Reports that provide status information
required for operational management)
66: Statistical Reports (Reports that provide statistical or
historical information)
• Actions: Use this menu to group the operations that give the user the ability to process the
document, including the actions that navigate to related data entry forms (with the system filling
in appropriate settings) so users can quickly create linked documents. For example, see the
Enter Payment/Apply Memo action on the Invoices and Memos (AR301000) form. The most
frequently used operations can be placed on the toolbar outside any groups as separate buttons
that provide quick access to the actions. For example, notice the Release action on the Invoices
and Memos form.
• Reports: Use this menu to group the actions that open related Report Designer reports and
printable forms of documents.
• Inquiries: Use this menu to group the actions that navigate to related inquiry forms.
Inquiry Forms
• Actions: Use this menu to group the operations that give the user the ability to navigate to
related data entry forms.
• Reports: Use this menu to group the actions that open the related Report Designer reports.
Maintenance Forms
• Actions: Use this menu to group the operations that update the settings of the master record and
navigate to related data entry forms.
• Reports: Use this menu to group the actions that open related Report Designer reports.
• Inquiries: Use this menu to group the actions that navigate to related inquiry forms.
In This Chapter
• Graph Naming
Graph Naming
When you are creating business logic controllers (graphs), use the following suffixes in the names of the
graphs, depending on the types of the webpages they are used for:
• Maint: For the graphs for maintenance pages that are helper pages used for the input of data
on the data entry and processing pages, and for the graphs for the setup pages that provide the
configuration parameters for the application. For example, CountryMaint can be the name of
the graph for the Countries maintenance page, which provides editing of the list of countries.
• Entry: For the graphs for data entry pages that are used for the input of business documents.
For example, SalesOrderEntry can be the name of the graph for the Sales Order data entry
page, which provides the basic functionality for working with sales orders.
• Inq: For the graphs for inquiry pages, which display a list of data records selected by the
specified filter. For example, SalesOrderInq can be the name of the inquiry page named Sales
Order Inquiry, which provides the list of documents selected by the specified customer.
• Process: For the graphs for processing pages that provide mass processing operations. For
example, SalesOrderProcess can be the name of the Approve Sales Orders processing page,
which provides mass approval of sales orders.
Therefore, the name of a data record event handler must be in the following format:
DACName_EventName (such as SOOrder_RowSelected).
The name of a data field event handler must have the following segments, which are separated by the _
symbol:
2. The name of the data field declared within the DAC whose name is specified in the first segment
Therefore, for a field event handler, the name must be in the following format:
DACName_FieldName_EventName (such as SOOrder_CustomerID_FieldUpdated).
| Configuring ASPX Webpages and Reports | 43
In This Part
• Configuring Containers
• Configuring Tables
• Configuring Tabs
• Configuring Boxes
• Maintaining Reports
• To configure the layout (PXLayoutRule) and size of controls: Configuring Layout and Size
| Configuring ASPX Webpages and Reports | 44
• The values of the key fields of the record currently displayed on the page
• The changes that have been made to the data on the page
• The information on the command that was initiated by the user—the data source ID and the
callback name
The PXDataSource.TypeName property defines the graph that processes data for the page. When the
application server receives the request, the server creates a new instance of the graph to process
the data of the request. The properties of PXDSCallbackCommand, such as CommitChanges and
RepaintControls, indicate which operations should be performed on the server in addition to the
operation initiated by the user.
The diagram below shows how the HTTP request is sent to the server.
| Configuring ASPX Webpages and Reports | 45
2. The application server executes the Select method for each data view of the graph.
<Controls>
<Control ID="ctl00_phF_form_edDocType"
Props="{items:"INV|Invoice|1;DRM|Debit Memo|1;CRM|
Credit Memo|1;FCH|Overdue Charge|1;SMC|Credit WO|1",
| Configuring ASPX Webpages and Reports | 47
value:"FCH"}" />
<Control ID="ctl00_phF_form_edRefNbr" Props="{value:"AR005254"}" />
...
</Controls>
The scripts in the browser (the scripts from PX.Web.UI.Scripts) find the controls to be repainted
by IDs and repaint these controls by using the data provided in the response. Most of the scripts in
PX.Web.UI.Scripts contain a class that works with one control. For example, px_textEdit.js includes
the PXTextEdit class, which works with the PXTextEdit control.
Request of main.cshtml
The browser retrieves main.cshtml, which is an ASP.NET MVC view, by sending the HTTP GET
request. On the server side, this request is processed by the MainController.Main() method
(PX.Web.UI.Frameset.Controllers), which creates a System.Web.Mvc.ViewResult object that
renders a view to the response. The returned view contains the basic skeleton of the webpage, which
includes the calendar control, the search control, and the empty menu.
: To match the incoming request to the appropriate processing classes, the system uses the
ASP.NET MVC attribute routing. For example, the SiteMapController class is annotated with the
[FramesetRoutePrefix("sitemap")] attribute, which defines the "frameset/sitemap" route.
To get the site map structure, the SiteMapController class uses the SiteMapRepository class, which
implements the ISiteMapRepository interface. The SiteMapRepository class fetches different entities
of the modern UI and assembles them in one structure, which is then passed to the browser. The
system serializes the structure to JSON format by using the standard ASP.NET Web API classes.
The SiteMapRepository class uses other classes that have the Repository suffix in their names,
such as TileRepository and WorkspaceRepository, to retrieve the entities that are used in the
modern UI. These classes are completely independent from the database. To fetch the entities from
the database, the Repository classes use the classes that implement the IEntitySet interface
(PX.Web.UI.Frameset.Model), such as ScreenEntitySet. The classes use the MUIGraph graph to fetch
data from the database. (The graph performs only simple data operations, and does not contain any
complicated business logic). For each entity, there is a DAC that is used to access data in the database.
The DACs correspond to the following database tables, which are used to store data for the elements of
the modern UI.
Database Tables
Table Description
MUIWorkspace Stores information about the workspaces in the application. For more information
on the workspaces, see Workspaces in the Modern UI in the Interface Guide.
Stores information about the workspaces that have been pinned to the main
MUIFavoriteWorkspace
menu. The workspaces that are not included in this list are displayed when a user
clicks the More Items menu item. For details about the main menu, see Main
Menu in the Modern UI in the Interface Guide.
MUIArea Stores information about the areas to which workspaces belong. Areas are used
to group workspaces in the More Items menu by types.
MUISubcategory Stores information about the categories of Acumatica ERP forms. Categories are
used to group forms in a workspace by types. For details on the categories, see
Categories.
MUIScreen Stores information about the locations of the Acumatica ERP forms in the modern
UI. The table is connected to the SiteMap table by the NoteID column.
MUIPinnedScreen Stores information about the Acumatica ERP forms pinned to workspaces.
MUIFavoriteScreen Stores information about the Acumatica ERP forms that have been added to
favorites.
MUITile Stores information about the tiles in workspaces. A tile is a special button on a
workspace that you click to open a form or report with predefined settings. For
details on the tiles, see Tiles.
MUIFavoriteTile Stores information about the tiles that have been added to favorites.
MUIUserPreferencesStores information about the position of the main menu, which can be on the left
of the browser page (default) or on the top of the browser page.
The following diagram illustrates the process of retrieving data for the modern UI.
| Configuring ASPX Webpages and Reports | 50
Class Description
MenuModules Renders the main menu (which contains the list of workspaces). For more
information on the main menu, see Main Menu in the Modern UI in the Interface
Guide.
In addition to the render method, the class has the following methods:
• onClick: Processes the clicking of the Edit and Delete buttons for the items
of the main menu in Menu Editing mode.
For details on menu editing, see Menu Editing Mode in the Modern UI.
TopLinks Renders the tiles in the workspaces. For details about the tiles, see Tiles in the
Interface Guide.
In addition to the render method, the class has the following methods:
| Configuring ASPX Webpages and Reports | 51
Class Description
• onClick: Processes the clicking of the Edit and Delete buttons for the tiles
in Menu Editing mode and clicking of the Favorite button.
For details on menu editing, see Menu Editing Mode in the Modern UI.
MenuColumn Renders a list of forms in a workspace. For the information about workspaces,
see Workspaces in the Modern UI in the Interface Guide.
In addition to the render method, the class has the following methods:
• onClick: Processes the clicking of the Edit and Delete buttons for a form in
a workspace in Menu Editing mode.
For more information on menu editing, see Menu Editing Mode in the Modern UI.
ModuleMenu Renders all lists of forms in a workspace. For details about workspaces, see
Workspaces in the Modern UI in the Interface Guide.
In addition to the render method, the class has the following methods:
The site.js script also contains webpage event handlers, such as handlers for button-clicking events,
which use jQuery to handle the events.
Configuring Containers
In this chapter, you can find information about the configuration of different types of containers, such as
PXFormView, PXGrid, PXTab, PXTreeView, and PXPanel.
| Configuring ASPX Webpages and Reports | 52
In This Chapter
• PXFormView
• PXGrid
• PXTab
• PXTreeView
: The PXTreeView container is not supported by the tools of the Acumatica Customization Platform.
By default, a nested container inherits the DataMember property from the parent container. If a nested
container is PXFormView, PXGrid, or PXTab, it can be bound to another data view.
If the DataMember property is available for other ASPX objects, it has a special purpose. For example,
you can specify the DataMember property for a PXSelector lookup control to define the appropriate
data view for the grid of the lookup window.
Property Value
Each DataMember property value can correspond to any data view name of the BLC. Any data view
except for the main data view can be used by an unlimited number of containers. The main data view
must be bound to a single container.
For a container to contain a box for a data field, the container must be bound to a data view declared
within a BLC for the following reasons:
• A data field is declared in a data access class (DAC). An instance of the DAC record can exist in
the cache of a BLC that contains the declaration of a data view with the DAC reference in the BQL
statement.
• Each time a data record is selected in the container, the container creates a callback to the
PXDataSource control that is specified in the DataSourceID property of the container. The data
source control creates a remote procedure call to the application server to execute the Display
operation on the data view that is specified as the DataMember for the container. The data view
checks the existence of the record in the cache; if the check fails, the data view executes the BQL
request and stores the obtained record in the cache.
• The data view provides all data exchange operations with the database, cache, and PXDataSource
control.
| Configuring ASPX Webpages and Reports | 53
Predefined Skins
The following table lists and describes the predefined skins that are recommended to use for the
PXFormView, PXGrid, and PXPanel containers.
PXFormView Transparent Is used to display a simple form container that The form container
has no caption and cannot be collapsed. on the Financial
Details tab item of the
Bills and Adjustments
(AP301000) form
PXGrid Attributes Is used to display a simple grid without a toolbar. The Attributes grid on
The grid contains a predefined set of rows, which the Attributes tab item
can be edited. of the Non-Stock Items
(IN202000) form
Details Is used to render a detail grid in a master-detail The grid on the 1099
data entry page. The grid has a toolbar that Settings tab item of
holds the default actions, such as Refresh, Add, the Accounts Payable
Remove, Fit to Screen, and Export to Excel; Preferences (AP101000)
it can also hold custom actions. The grid has no form
caption and paging is allowed.
Inquire Is used to display data without rows being added The grid on the
or removed. The grid has a toolbar that contains Attributes tab item
the Refresh, Fit to Screen, and Export to Excel of the Customers
default actions and can contain custom actions. (AR303000) form
The grid has no caption, and paging is allowed.
Primary Is used to display an editable primary grid that The grid on the Entry
does not contain its own toolbar. To work with the Types (CA203000) form
grid, the user applies the action buttons of the
form toolbar. The grid has no caption, and paging
is allowed.
PrimaryInquire
Is used to display a primary grid without the The grid on the
availability to edit data. The grid does not contain Release AP Documents
its own toolbar. To work with the grid, the user (AP501000) form
applies the action buttons of the form toolbar,
which does not contain the Add, Delete, and
Switch Between Grid and Form buttons. The
grid has no caption, and paging and filtering are
allowed.
ShortList Is used to display a small grid with a few records The Sales Categories
inside a form view. The grid has a toolbar that grid on the Attributes
contains the Refresh, Add, and Remove default tab item of the Non-
actions. Stock Items (IN202000)
form
| Configuring ASPX Webpages and Reports | 54
PXPanel Buttons Is used in dialog boxes to display a horizontal row The group of buttons in
of buttons with right alignment. the Add PO Receipt
dialog box, which opens
if you click the Add
PO Receipt button
in the toolbar of the
Document Details
tab item of the Bills
and Adjustments
(AP301000) form
Transparent Is used to group controls in a form container. The The group of controls on
panel has no caption. the Template Settings
tab item of the Order
Types (SO201000) form
<sm:Container Name="ValueOfTheCaptionProperty">
...
</sm:Container>
Otherwise, in the WSDL schema, the API web service assigns to the container the name of the first child
element. If you use this name in the mobile site map, an error may occur after the container content is
reordered because the container name might be changed in the WSDL schema.
The following code snippet shows how to use the MyDataView, AnotherDataView, and
OnceMoreDataView data views declared in the same graph or in extensions for the graph to define
boxes for data fields in a PXFormView container on an Acumatica ERP form.
Configuring Tables
In this chapter, you can learn how to configure tables (grids) on webpages by using the PXGrid and
PXGridColumn ASPX objects.
In This Chapter
Value Description
Value Default value. An indicator that the column cell contains the value of the field.
Text If there is a description defined for the field, an indicator that the column cell
contains the description of the field.
Hint If there is a description defined for the field, an indicator that the column cell
contains both the value and the description of the field.
: The priority of the Type property is higher than the priority of the DisplayMode property. If the Type
property is set, for example, to CheckBox, the DisplayMode property is ignored.
Value Description
NotSet The default value. An indicator that the field value is displayed in the column as a
plain string that is formed based on the field data format.
CheckBox An indicator that the field value is displayed in the column as a check box, which
is selected if the field value is True.
HyperLink An indicator that the field value is displayed in the column as a hyperlink.
DropDownList An indicator that the column cell is rendered as a drop-down list that contains all
the values specified for the referred data field.
| Configuring ASPX Webpages and Reports | 56
Value Description
Icon An indicator that the field value contains an image URL and is displayed in the
column as the referred image.
Example
The following code fragment defines the grid columns on the Automation Schedule Statuses
(SM205030) form.
...
<px:PXGridColumn AllowUpdate="False" DataField="LastRunStatus" Width="40px"
Type="Icon" TextAlign="Center" />
<px:PXGridColumn DataField="ScreenID" DisplayFormat="CC.CC.CC.CC"
Label="Screen ID" LinkCommand="AUScheduleExt_View" />
<px:PXGridColumn DataField="Description" Label="Description" Width="200px" />
<px:PXGridColumn AllowNull="False" DataField="IsActive" Label="Active"
TextAlign="Center" Type="CheckBox" Width="60px" />
...
In the code, the Type property for the LastRunStatus data field (which corresponds to the Status
column shown in the screenshot below) is set to Icon. Because the field value contains the image URL,
the column cell displays the referred image.
For the IsActive data field (which corresponds to the Active column), the Type property is set to
CheckBox. As you can see in the screenshot, the column cells are rendered as check boxes.
Configuring Tabs
In this chapter, you can learn how to configure tables on webpages by using the PXTab and PXTabItem
ASPX objects.
In This Chapter
VisibleExp Property
The VisibleExp property contains a condition expression that defines a Boolean value used to set the
visibility of the tab item. The expression must consist of two parts and an operator to compare these
| Configuring ASPX Webpages and Reports | 57
parts. The expression can contain the values of controls that belong to the container specified in the
BindingContext property.
BindingContext Property
The BindingContext property defines the ID of the container of the controls whose values can be used
in the expression of the VisibleExp property.
Example
As an example of the conditional hiding of a tab item, on a form with form and tab containers, if
you need to set the visibility of a tab item to depend on a check box of the form container, you can
define the VisibleExp and BindingContext properties of the PXTabItem element, as illustrated in the
following ASPX code snippet.
In the code above, the expression uses the DataControls .NET property of the form object as a
dictionary to find the needed control by the specified ID.
Configuring Boxes
In this chapter, you can find information about the configuration of different types of boxes, such as
PXTextEdit, PXCheckBox, and PXGroupBox.
In This Chapter
Attention: The group box must contain a radio button for each value defined in the list of the field.
In the DataField property of the PXGroupBox element, you can specify the name of a data field that is
accessible through another data view of the same graph. See Use of Multiple Data Views for Boxes in
Containers for details.
If the RenderStyle property of a PXGroupBox element is set to Simple, the Caption property is
ignored. See Use of the RenderStyle Property of PXGroupBox for details.
| Configuring ASPX Webpages and Reports | 59
The Acumatica Framework supports the following RenderStyle values for the PXGroupBox element.
In This Chapter
Predefined Values
The following table shows the values in pixels that correspond to the predefined constants.
| Configuring ASPX Webpages and Reports | 60
XS 150px 70px
S 200px 100px
SM - 150px
M 250px 200px
XM 300px 250px
L 350px 300px
XL 400px 350px
• For any property for which there are predefined values, you can specify a value in pixels, such as
55px. (This format is mandatory if you don't use abbreviations, because the property value can be
defined only in pixels.)
• There is no predefined value for the Width property of a control. Therefore, you can specify a
value for this property by typing any value in pixels, such as 55px. Before specifying the Width
property value for a control, you must define the Size property value for the control as Empty.
: The Width property is declared in ASP.NET. The Size property is declared in Acumatica
Framework, so you can use the predefined values.
Default Layout
By default, the system places all the controls of a container into a column within the first row, as shown
in the diagram below. To do this, the system initially sets to True the StartRow property value for the
uppermost PXLayoutRule component in a container.
| Configuring ASPX Webpages and Reports | 61
The controls continue to be placed within a single column until you add a layout rule with the
StartColumn or Merge property value set to True.
Important: For the proper layout, the StartRow property value must be set to True for the uppermost
PXLayoutRule component of a container.
The first control under this rule is the highest control in the column.
multiple columns within the new row, you should include in the placeholder a new layout rule with the
StartColumn property value set to True, as shown in the following diagram.
Required Properties
Every PXLayoutRule component that has the StartRow or StartColumn property value set to True must
have one of the following sets of properties defined:
The following diagram illustrates the meaning of the LabelsWidth, ControlSize, and ColumnWidth
properties.
: You should not set property values for both ColumnWidth and ControlSize for the same
PXLayoutRule component; in this case, the system would use the value of the ControlSize property.
| Configuring ASPX Webpages and Reports | 63
• The values of the ColumnWidth, ControlSize, and LabelsWidth properties must be defined
exclusively for every PXLayoutRule component; they are never inherited from the previously
declared one.
• You can change the size of a single control or its label by defining values for the Size, Width, and
LabelsWidth properties of the control. Property values that are set for a control have a higher
priority than the property values of the PXLayoutRule component.
• You can assign a predefined size abbreviation (such as XXS, L, or XL) for the ColumnWidth,
LabelsWidth, and ControlSize properties of a layout rule and the LabelsWidth and Size
properties of a control. (See Predefined Size Values for details.)
• The PXDateTimeEdit and PXNumberEdit control types have a predefined Width property value,
which you cannot change by setting the ColumnWidth or ControlSize property values for the
appropriate PXLayoutRule component. To change the width of this control, set a value for the
Size or Width property of the control.
Example
As an example of the use of the ColumnSpan property, the form container on the Customers
(AR303000) form has three columns of boxes, and there is a layout rule with the ColumnSpan property
set to 2 in the first column. This property forces the system to make the box span two columns, as
shown in the following screenshot.
Dependencies
A PXLayoutRule component with the ColumnSpan property value specified is handled as follows:
• The LabelsWidth property value is always inherited from the previously declared PXLayoutRule
component that has the StartRow or StartColumn property value set to True.
• If a value for the ColumnWidth or ControlSize property is specified for the component, this value
is ignored.
Example
As an example of the use of the Merge property, the Billing Settings tab item on the Customers
(AR303000) form has three pairs of merged check boxes in the Print and Email Settings group. This
property forces the system to render the boxes in one column, as shown in the following screenshot.
Dependencies
A PXLayoutRule component with the Merge property value set to True is handled as follows:
• If the ColumnWidth property value is set for the same PXLayoutRule component, the value is
ignored.
• The default values for the ControlSize and LabelsWidth properties are inherited from the
previously declared PXLayoutRule component with the StartRow or StartColumn property value
set to True. You can override these property values, if necessary, by specifying the ControlSize
and LabelsWidth property values from the predefined list of options. (See Predefined Size Values
for details.)
Grouping of Controls
To group multiple controls within a column, generally you have to add two PXLayoutRule components
that have the following properties set to define the first and the last controls in the group, respectively:
• GroupCaption and EndGroup: To create a group with the caption specified in the GroupCaption
property
: You can specify both the GroupCaption property and the StartGroup property for a PXLayoutRule
component that starts a group.
For example, by specifying the GroupCaption property value for the corresponding PXLayoutRule
components placed above a control, you start the group of controls and set up the header for the
group. You should also add a PXLayoutRule component with the EndGroup property value set to True
below (in the code) the last control that is included in the group.
You end a group by using a PXLayoutRule component with a GroupCaption, StartGroup, or EndGroup
property specified. Therefore, if there is another group that starts immediately below a group, you can
omit the layout rule that ends the upper group, as shown in the third column of the row displayed in the
example in following diagram.
Dependencies
The system works as follows for all PXLayoutRule components with the GroupCaption or StartGroup
property value specified:
• The default values for the ControlSize and LabelsWidth properties are inherited from the
previously declared PXLayoutRule component with the StartRow or StartColumn property value
set to True. You can override these property values, if necessary, by specifying the ControlSize
and LabelsWidth property values in the layout rule that starts a group. (See Predefined Size
Values for details.)
SuppressLabel Property
To hide the labels of the controls placed within a column, you should set the SuppressLabel property
value of the PXLayoutRule component of the column to True. Then within the column, all check boxes
are placed without any space to the left of the input control.
: If needed, you can left-align a check box in the column by setting to True the AlignLeft property of the
control. Also, you can set the SuppressLabel property value to True for any other control to hide its label.
The SuppressLabel property affects all of the controls of the group that are placed under the
PXLayoutRule component with the True value of this property. The SuppressLabel property value must
be defined for every PXLayoutRule component for the controls placed beneath the component and
included in the same column; this property is never inherited from the previously declared property.
: The SuppressLabel property value is never applied to PXLayoutRule components that have the
ColumnSpan property value specified.
Example
The Parent Info group on the Billing Settings tab item on the Customers (AR303000) form is initially
displayed with Parent Account displayed, as shown in the following screenshot.
If you set the SuppressLabel property of the group layout rule to True, the label of the Parent
Account box is hidden and all check boxes are displayed without any space to the left of the check
boxes, as shown in the following screenshot.
| Configuring ASPX Webpages and Reports | 67
Figure: The same group of controls after applying the SuppressLabel property to the group
Maintaining Reports
In Acumatica Report Designer, you can create custom reports or modify existing reports and then use
these reports in Acumatica ERP or an Acumatica Framework-based application. For more information on
the creation of the custom reports with the Report Designer, see Acumatica Report Designer Guide.
In this chapter, you can find details about how the system renders reports in Acumatica ERP.
In This Chapter
• Display of Reports
Display of Reports
In this topic, you can find information about how Acumatica ERP displays reports that are created with
the Acumatica Report Designer.
What a Report Is
A report is an RPX file (which is created with the Acumatica Report Designer) that contains the report
schema in XML format—that is, the description of the data that should be displayed in the report and
the description of the report layout.(See the following diagram.) The description of the data of the
report includes the following: the database tables that provide data for the report, the relationships
between these tables, the parameters that can be specified before the report is run, and the filtering
and grouping parameters. The report layout is a tree of headers, details, and footers.
| Configuring ASPX Webpages and Reports | 68
Reports can be saved in files on disk, or in the UserReport table of the database of an Acumatica ERP
instance. (The UserReport table uses the file name of the report as a key and stores the schema of the
report in XML format in the Xml column.)
The saved report can be published on an Acumatica ERP site—that is, added to the site map and to any
applicable workspaces so that the users can work with the report.
For details on creating reports with the Acumatica Report Designer, see the S130 Reporting: Inquiry,
Report Writing, Dashboards training course.
Acumatica ERP provides the following ways to run a report that you have created by using the
Acumatica Report Designer:
• From a report form, which is added to the site map, when the user clicks the Run Report button
on the form toolbar
• From a maintenance or entry form, when the user clicks the action button whose name is
associated with the report name
| Configuring ASPX Webpages and Reports | 69
http://localhost/AcumaticaDB/frames/ReportLauncher.aspx?
id=YF123456.rpx&HideScript=On
The ReportLauncher.aspx page contains the PXReportViewer control, whose JavaScript objects
and functions are designed to obtain the report data and display the data on the form, and the
PXSoapDataSource control, which is used to retrieve data for the report.
On the server side, an instance of the PX.Web.UI.PXReportViewer class processes the request as
follows:
1. Loads the report schema from the file on disk or from the database (by using the LoadReport
method) to a PX.Reports.Controls.Report object (which stores the report schema in memory
and provides the methods for working with this schema).
b. Binds the Report object to the data source that is specified by the PXSoapDataSource
control of the ASPX page. The PX.Web.UI.PXSoapDataSource class instantiates a
SoapNavigator object, which will be then used to retrieve data for the report from the
database.
The server returns an XML response with the report parameters to display and with the ID of the
instance of WebReport in the session. The browser displays the report parameters and other options on
the report form.
The following diagram illustrates how the report form is launched.
http://localhost/AcumaticaDB/PX.ReportViewer.axd?
InstanceID=10bf4a13a38c4af39cafc80926e407f2&OpType=Report
&PageIndex=0&Refresh=True
To process the request, the server invokes the Render method of the WebReport class, which launches
the generation of the report as a long-running operation in a separate thread. For details on how the
long-running operation is processed for a report, see Processing a Report as a Long-Running Operation.
To retrieve the data of the report from the database, the system uses the
PX.Data.Reports.SoapNavigator object (to which a reference is stored in the Report
object). SoapNavigator instantiates a PXGraph object (without a type parameter) and
composes a BQL command as an instance of the PX.Data.Reports.BqlSoapCommand class.
BqlSoapCommand inherits from the PX.Data.BqlCommand class and is optimized for retrieving
data for the reports. BqlSoapCommand has the IndexReportFields method, which uses the
PX.Data.PXDependsOnFieldsAttribute attribute to get the dependent fields of the report recursively.
For details on how BQL commands are used to retrieve data from the database, see Translation of a
BQL Command to SQL.
The system processes the data and creates a ReportNode object. That is, the system creates the
sections of the report based on the data retrieved from the database and on the report schema from
the Report object, and calculates all formulas inside the sections. Then the system uses the resulting
ReportNode object, which contains all sections with all needed values, to render data in the needed
format.
http://localhost/AcumaticaERP/PX.ReportViewer.axd?
InstanceID=008f2a8afb1a4998ade98bc64fc30ad9&OpType=Report
&PageIndex=0
The format in which the report is displayed (either PDF or HTML) is specified in the OpType query string
parameter of the request, as shown in the following request URL.
http://localhost/AcumaticaERP/PX.ReportViewer.axd?
InstanceID=008f2a8afb1a4998ade98bc64fc30ad9&OpType=PdfReport&Refresh=True
If a user turns the pages of the report or changes the format of the report, the system creates the
results of the report from the ReportNode object stored in the session by using the renderer for the
needed format. That is, the system does not retrieve the data of the report from the database and does
not processes this data to create a ReportNode object once again.
• Financial reports that display data that is posted to the general ledger accounts and accumulated
in the General Ledger module. The system gets the data of the general ledger accounts from the
GLHistory table of the database.
• Project accounting reports that display data that is accumulated in the Project module. The
system gets the data for these reports from the PMHistory table of the database.
For details on analytical reports, see the F350 Reporting: Analytical Reports training course.
The schema of analytical reports are stored in a set of database tables that have RM prefix in their
names, such as RMReport, RMRowSet, and RMColumnSet. The analytical report is identified in the system
by its code, which you specify in the Code box on the Report Definitions (CS206000) form. The report
code is stored in the ReportCode column of the RMReport table.
The schema of an analytical report can include the position of the report in the site map so that the
users can work with the report. To run the report, a user clicks the Run Report button on the form
toolbar of the report form.
http://localhost/AcumaticaDB/frames/rmlauncher.aspx?id=dbsp.rpx&HideScript=On
The RMLauncher.aspx page contains the PXReportViewer control, whose JavaScript objects
and functions are designed to obtain the report data and display the data on the form, and the
ARmDataSource control, which is used to retrieve data for the analytical report.
On the server side, the system (by using an instance of the PX.Web.UI.PXReportViewer class)
processes the request as follows:
1. Loads the parameters of the analytical report from the database (by using the LoadReport
method) to a PX.Reports.Controls.Report object as follows:
a. The system retrieves the data source of the report viewer as an instance of the
PX.CS.RMReportReader class, which is a graph (derived from PXGraph<RMReportMaint,
RMReport>) that implements the PX.Report.ARm.Data.IARmDataSource interface.
b. By using the GetReport method of IARmDataSource, the system retrieves the parameters
of the analytical report from the database to the PX.Reports.ARm.ARmReport object.
| Configuring ASPX Webpages and Reports | 72
a. The system instantiates a PX.Reports.Web.WebReport object that will store data of the
launched report and assigns an instance ID to WebReport.
b. The system initializes the ProcessMethod field of the WebReport object with the
processing function for analytical reports.
The server returns an XML response with the report parameters to display and with the ID of the
instance of WebReport in the session. The browser displays the report parameters on the report form.
Accessing Data
The topics in this part of the guide explain how an application based on Acumatica Framework can
access data from the application database and the data stored in the session.
In This Part
In This Chapter
the queries to be executed to retrieve the requested data by using business query language (BQL),
which is provided by Acumatica Framework. You also use BQL to define the data queries directly in code
and in attributes.
BQL is written in C#; it is based on generic class syntax, which is similar to SQL syntax. Thus, BQL has
almost the same keywords as SQL does, placed in the order in which they are used in SQL. BQL offers
several benefits to the application developer. BQL does not depend on the specifics of the database
provider, and it is object-oriented and extendable. Also, BQL provides compile-time syntax validation,
which helps to prevent SQL syntax errors.
You can also use language-integrated query (LINQ) provided by the System.Linq library when you
need to select records from the database in the code of Acumatica Framework-based applications or if
you want to apply additional filtering to the data of a BQL query. However, you still have to use BQL to
define the data views in graphs and to specify the data queries in the attributes of data fields.
• It is easier to read and edit fluent BQL queries than traditional BQL queries because each section
of a fluent BQL query does not depend on the others and can appear in only specific places of
the query. Also, fluent BQL queries contain fewer commas and angle brackets and do not use
numbered classes (such as Select2 or Select6).
• You do not need to select a suitable class for a fluent BQL query (such as PXSelectOrderBy<,> or
PXSelectJoinOrderBy<,,>); instead, you simply start typing the command, and IntelliSense in
Visual Studio offers continuations that are relevant for the current query state.
For a detailed list of differences between the dialects, see Comparison of Fluent BQL, Traditional BQL,
and LINQ.
The following code shows an example of a data view written in fluent BQL.
SelectFrom<Product>.
Where<Product.availQty.IsNotNull.
And<Product.availQty.IsGreater<Product.bookedQty>>>.View products;
The following code shows the same data view written in traditional BQL.
PXSelect<Product,
Where<Product.availQty, IsNotNull,
And<Product.availQty, Greater<Product.bookedQty>>>> products;
Suppose the database provider is Microsoft SQL Server. Acumatica Framework translates the fluent and
traditional BQL queries shown above into the following SQL query, where [list of columns] is the
list of columns of the Product table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For
details on which columns are enumerated, see Translation of a BQL Command to SQL.
LINQ
To configure a LINQ query, you can use the following variants of syntax:
• Query expressions, which use standard query operators from the System.Linq namespace
(such as where or orderby) or Acumatica Framework-specific operators from the
| Accessing Data | 75
• Explicit (method-based) syntax. The arguments of the methods used in this syntax are lambda
expressions. In these expressions, you can use the standard C# operators and Acumatica
Framework-specific operators from the PX.Data.SQLTree namespace (such as SQL.BinaryLen,
which is shown in the following code). The code below is equivalent to the query expression
shown above.
You bind the data field to the table column by specifying the type attribute that is derived from
the PXDBFieldAttribute class, such as PXDBString, and specifying the name of the column
as the name of the property. If you don't need to bind the property to a database column (for
example, if you want the value of the property to be calculated from the database fields), you
specify an unbound type attribute, such as PXDBCalced. You assign the property a name that
starts with an uppercase letter. For the lists of bound and unbound type attributes, see Bound
Field Data Types and Unbound Field Data Types.
You use the property, which, in the system, holds the column data of the table, in the queries
defined with LINQ. In the SQL command generated from BQL, the framework explicitly lists
columns for all bound data fields defined in the DAC. For the unbound data fields whose property
attribute defines a BQL command, if this data field is used in a BQL query, the system translates
the BQL command of the property to SQL when the BQL query is translated to SQL. For more
information on the translation of BQL to SQL, see Translation of a BQL Command to SQL.
The following code shows an example of the Product DAC declaration in the fluent BQL style.
using System;
using PX.Data;
[Serializable]
public class Product : PX.Data.IBqlTable
{
// The class used in BQL statements to refer to the ProductID column
public abstract class productID : PX.Data.BQL.BqlInt.Field<productID>
{
}
// The property holding the ProductID value in a record
[PXDBIdentity(IsKey = true)]
public virtual int? ProductID { get; set; }
• The PXView object, which contains the BQL command that corresponds to the data view
• The PXCache<Node> objects whose type parameter is defined by the data access classes (DACs)
that are used in the BQL command
The PXView object uses the BQL command to retrieve data from the database and stores the retrieved
data in the PXCache object. The data view stores references to the corresponding PXView object and the
PXCache object of the main DAC of the data view, as shown in the following diagram.
| Accessing Data | 77
• Stage 1: When a developer executes a BQL statement in code, Acumatica Framework configures a
delayed query.
• Stage 3: When the developer casts the result of the query to a data access class (DAC) or an
array of DACs, the system does the following:
• 3a: If the result of the query contains the expression tree created by LINQ, the system
configures the SQL query tree that corresponds to the LINQ expression tree, and executes
the SQL query tree.
• 3b: If the result of the query is created only by BQL, the system configures the SQL query
tree for the delayed query and executes this query tree.
• You declare a data view (a PXSelectBase-derived class) as a member in a graph, and you specify
this data view as the data member of the webpage control. The system uses this data view for
basic data manipulation (inserting a data record, updating a data record, and deleting a data
record) and executes the data view by calling the Select() method.
• You use the static Select() method of a PXSelectBase-derived class with a graph object as
the parameter.
• You dynamically instantiate a PXSelectBase-derived class in code and execute it by using its
Select() method. (You provide the graph object as a parameter to the class constructor.)
• You instantiate a class derived from the BqlCommand class (such as a Select class in traditional
BQL or FromSelect in fluent BQL), create a PXView object that uses this BqlCommand class, create
a graph object, and call one of the view's Select() methods.
| Accessing Data | 79
When the Select() method is executed, Acumatica Framework does the following:
2. Caches the delayed query by using the PXContext.SetSlot method. (For details on the slots,
see Use of Slots to Cache Data Objects.)
3. Returns a PXResultset<T> object whose type parameter is set to the DAC specified as the
type parameter of the SelectFrom class (in fluent BQL) or as the first type parameter of the
PXSelect class (in traditional BQL). This result set contains information about the delayed query.
You can iterate through the result set in a foreach loop, obtaining either DAC instances or PXResult<>
instances. A PXResult<> instance represents a tuple of joined records from the result set. PXResult<>
can be cast to any of the DAC types joined in the BQL statement. For more information on the use of
the PXResultset<T> class, see To Process the Result of the Execution of the BQL Statement.
1. The system calls the PXDelayedQuery.GetRows method for the delayed query of the result set.
This method internally calls the PXView.Select() method for the data view referred to in the
delayed query.
2. If the select delegate is provided, inside the PXView.Select() method, the system invokes the
select delegate by using the PXView.InvokeDelegate method and saves the result in the query
cache of the graph. (The query cache stores the result set obtained by the execution of a specific
BQL command.)
3. Inside the PXView.Select() method, the system searches for the requested records in the
query cache by using the PXView.LookupCache method. If no records are found, the system
requests data from the database by using the PXView.GetResult method. For details on the
retrieval of records from the database, see Translation of a BQL Command to SQL.
4. The system merges the records retrieved from the database or from the query cache with the
modified records stored in PXCache by using the PXView.MergeCache method. For details about
the merge, see Merge of the Records with PXCache.
5. The system saves the result of the query in the query cache by using the PXView.StoreCached
method.
2. The system builds the SQL query tree from the Remotion.Linq.QueryModel by calling the
SQLinqExecutor.ExecuteCollection<T>() method. In this method, the system executes the
SQLinqQueryModelVisitor.VisitQueryModel() method, which does the following:
b. Builds the SQL query tree for the rest of the Remotion.Linq expression tree by calling
the methods of SQLinqQueryModelVisitor for particular clauses and the columns
included in the result of the query. If the system cannot build the SQL query tree for
particular elements of the Remotion.Linq expression tree, the system falls back to the
execution of the delayed query for the base BQL statement. For details about the fallback,
see Fallback to the LINQ to Objects Mode.
3. Within the SQLinqExecutor.ExecuteCollection<T>() method, the system uses the built SQL
query tree to request the data from the database. For details about how the SQL query tree is
translated to the SQL text that is passed to the database, see Translation of the SQL Query Tree
to SQL Text.
4. The system merges the records retrieved from the database with the modified records stored in
PXCache. For details about the merge, see Merge of the Records with PXCache.
1. PXView.GetResult
2. PXGraph.ProviderSelect
3. PXDatabaseProviderBase.Select
4. BqlCommand.GetQuery
The BqlCommand.GetQuery method calls the BqlCommand.GetQueryInternal method, which uses other
methods as follows to prepare the SQL query tree:
2. For each Join clause, the IBqlJoin.AppendQuery method adds to the Query instance the
Joiner instance that corresponds to the Join clause.
| Accessing Data | 81
The IBqlJoin.AppendQuery method obtains the type of Join and, for all classes in
the On clause that implement the IBqlCreator interface, successively executes the
IBqlCreator.AppendExpression method, starting from the On class and then proceeding
with enclosed classes, such as the Where classes and comparison classes. For the DAC fields
(IBqlField-derived classes), the BqlCommand.GetSingleExpression method obtains the SQL
expression.
3. For all classes in the Where and GroupBy clauses that implement the IBqlCreator interface,
the system successively executes the IBqlCreator.AppendExpression method, which appends
to the Query instance the SQL expression that corresponds to the classes. For the DAC fields
(IBqlField-derived classes), the BqlCommand.GetSingleExpression method obtains the SQL
expression.
4. The IBqlOrderBy.AppendQuery method adds to the Query instance the list of OrderSegment
instances that corresponds to the OrderBy clause.
For each sorting class (a IBqlSortColumn-derived class) in the OrderBy clause, the
IBqlSortColumn.AppendQuery method adds to the Query instance the OrderSegment instance
that corresponds to the sorting column. For the DAC fields (IBqlField-derived classes), the
BqlCommand.GetSingleExpression method obtains the SQL expression. If the original BQL
statement does not specify ordering, the system adds to the Query instance sorting by the DAC
key fields (in ascending order).
The following diagram shows the conversion of a BQL command to an SQL query tree.
| Accessing Data | 82
(which specifies the type of the database operation). The attribute assigned to the DAC field (that
is, the attribute that implements the IPXCommandPreparingSubscriber interface) handles the event
and returns the PX.Data.SQLTree.SQLExpression instance that corresponds to the field or to the
BQL statement that is defined by the field attribute. (For example, the PXDBCalced and PXDBScalar
attributes define BQL statements.)
The following diagram shows how BqlCommand obtains the SQL tree expression for the fields of a BQL
command.
Figure: Retrieval of the SQL tree expression for the fields of a BQL command
• For a field with FromCurrent appended (in fluent BQL) or specified in the Current parameter
(in traditional BQL), the PXView object retrieves the field value from the Current object
of the PXCache object. If the current field value is null, the PXView object triggers the
FieldDefaulting event handlers and retrieves the default value from the PXDefault attribute
value (if any).
: The default value is not retrieved if FromCurrent.NoDefault is appended to the field (in fluent
BQL) or if the Current2 parameter is used (in traditional BQL).
• For a field with AsOptional appended (in fluent BQL) or specified in the Optional parameter, if
the explicit field value is specified, the PXView object triggers the FieldUpdating event, whose
handlers can transform the external presentation of the field value to an internal value (for
example, transform ProductCD to ProductID). If the field value is not specified, the PXView
object retrieves the field value from the Current object of the PXCache object. If the current field
value is null, the PXView object triggers the FieldDefaulting event and retrieves the default
value from the PXDefault attribute value (if any).
: The default value is not retrieved if AsOptional.NoDefault is appended to the field (in fluent
BQL) or if the Optional2 parameter is used.
When BqlCommand creates the SQL query tree that corresponds to the BQL command,
IBqlCreator.AppendExpression (which is implemented by the ParameterBase<Field> class)
includes the parameters in the SQL query tree. After BqlCommand has created the SQL query tree that
corresponds to the BQL command, the system inserts into the SQL query tree the actual values of the
parameters retrieved by PXView.
| Accessing Data | 84
1. Calls the SQLDialect.GetConnection method of the graph instance to retrieve the needed
SQLTree.Connection.
2. Passes this Connection instance to the SQLTree.Query.SQLQuery method, which converts the
SQL query tree to the text for Microsoft SQL or MySQL, depending on the passed Connection.
1. If the query is read-only, the result set is not merged with any PXCache object. The system
returns the data records as they are currently stored in the database.
: A query is read-only if the IsReadOnly property of the underlying PXView object is true.
For example, the traditional BQL statements that use aggregation or are based on one of the
PXSelectReadonly classes are read-only. The fluent BQL statements that have .ReadOnly
appended are read-only.
2. If the query is not read-only and contains filtering by data access class (DAC) fields by using
LINQ (that is, only the values in specific columns of the database tables are returned in the
results of the query), the system checks whether the primary key fields of the primary DAC
(the first DAC in the BQL statement) are returned in the results of the query. If the primary key
fields are not in the results, no merge with any PXCache object is performed. If all primary key
fields are in the results, the result set is merged with the PXCache object that corresponds to the
primary DAC.
3. If the query is not read-only, does not contain filtering of DAC fields by using LINQ, and does not
contain joins, the result set is merged with the contents of the appropriate PXCache object, and
the system returns the result set updated with the modifications stored in PXCache.
4. If the query is not read-only, does not contain filtering by DAC fields by using LINQ, and joins
data from multiple tables, the result set is merged with only the PXCache object that corresponds
to the first table of the BQL statement. The PXResultset<> object, which represents the
result set, contains objects of the generic PXResult<> type. This type can be cast to the data
access classes (DACs) that represent the joined tables. The instance of the primary DAC to
which PXResult<> is cast contains the records from the database that are updated with the
modifications stored in PXCache. Casting PXResult<> to a joined DAC returns the instance
that contains values from the database and has no relation with the PXCache instances of the
corresponding DAC types.
The following diagram illustrates the database records being merged with PXCache.
| Accessing Data | 85
The queries can contain explicit Yes No; the Where Yes
brackets in conditions. clause can be used
instead
You can specify particular columns of Yes; you have to Yes; you have to Yes
the tables to be selected. use PXFieldScope use PXFieldScope
SelectFrom<>.View PXSelect<>
SelectFrom<>.View.ReadOnly PXSelectReadonly<>
SelectFrom<>.OrderBy<>.View PXSelectOrderBy<,>
SelectFrom<>.OrderBy<>.View.ReadOnly PXSelectReadonly3<,>
SelectFrom<>.AggregateTo<>.View.ReadOnly PXSelectGroupBy<,>
SelectFrom<>.AggregateTo<>.OrderBy<>.View.ReadOnly PXSelectGroupByOrderBy<,,>
SelectFrom<>.Where<>.View PXSelect<,>
SelectFrom<>.Where<>.View.ReadOnly PXSelectReadonly<,>
SelectFrom<>.Where<>.OrderBy<>.View PXSelect<,,>
SelectFrom<>.Where<>.OrderBy<>.View.ReadOnly PXSelectReadonly<,,>
SelectFrom<>.Where<>.AggregateTo<>.View.ReadOnly PXSelectGroupBy<,,>
SelectFrom<>.Where<>.AggregateTo<>.OrderBy<>.View.ReadOnly PXSelectGroupBy<,,,>
| Accessing Data | 87
SelectFrom<>.[Joins].View PXSelectJoin<,>
SelectFrom<>.[Joins].View.ReadOnly PXSelectReadonly2<,>
SelectFrom<>.[Joins].OrderBy<>.View PXSelectJoinOrderBy<,,>
SelectFrom<>.[Joins].OrderBy<>.View.ReadOnly PXSelectReadonly3<,,>
SelectFrom<>.[Joins].AggregateTo<>.View.ReadOnly PXSelectJoinGroupBy<,,>
SelectFrom<>. PXSelectGroupByOrderBy<,,,>
[Joins].AggregateTo<>.OrderBy<>.View.ReadOnly
SelectFrom<>.[Joins].Where<>.View PXSelectJoin<,,>
SelectFrom<>.[Joins].Where<>.View.ReadOnly PXSelectReadonly2<,,>
SelectFrom<>.[Joins].Where<>.OrderBy<>.View PXSelectJoin<,,,>
SelectFrom<>.[Joins].Where<>.OrderBy<>.View.ReadOnly PXSelectReadonly2<,,,>
SelectFrom<>.[Joins].Where<>.AggregateTo<>.View.ReadOnly PXSelectJoinGroupBy<,,,>
SelectFrom<>. PXSelectJoinGroupBy<,,,,>
[Joins].Where<>.AggregateTo<>.OrderBy<>.View.ReadOnly
Select Commands
SelectFrom<> Select<>
SelectFrom<>.OrderBy<> Select3<,>
SelectFrom<>.AggregateTo<> Select4<,>
SelectFrom<>.AggregateTo<>.OrderBy<> Select6<,,>
SelectFrom<>.Where<> Select<,>
SelectFrom<>.Where<>.OrderBy<> Select<,,>
SelectFrom<>.Where<>.AggregateTo<> Select4<,,>
SelectFrom<>.Where<>.AggregateTo<>.OrderBy<> Select4<,,,>
SelectFrom<>.[Joins] Select2<,>
SelectFrom<>.[Joins].OrderBy<> Select3<,,>
SelectFrom<>.[Joins].AggregateTo<> Select5<,,>
SelectFrom<>.[Joins].AggregateTo<>.OrderBy<> Select6<,,,>
SelectFrom<>.[Joins].Where<> Select2<,,>
SelectFrom<>.[Joins].Where<>.OrderBy<> Select2<,,,>
SelectFrom<>.[Joins].Where<>.AggregateTo<> Select5<,,,>
SelectFrom<>.[Joins].Where<>.AggregateTo<>.OrderBy<> Select5<,,,,>
| Accessing Data | 88
Search Commands
SelectFrom<>.SearchFor<> Search<>
SelectFrom<>.OrderBy<>.SearchFor<> Search3<,>
SelectFrom<>.AggregateTo<>.SearchFor<> Search4<,>
SelectFrom<>.AggregateTo<>.OrderBy<>.SearchFor<> Search6<,,>
SelectFrom<>.Where<>.SearchFor<> Search<,>
SelectFrom<>.Where<>.OrderBy<>.SearchFor<> Search<,,>
SelectFrom<>.Where<>.AggregateTo<>.SearchFor<> Search4<,,>
SelectFrom<>.Where<>.AggregateTo<>.OrderBy<>.SearchFor<> Search4<,,,>
SelectFrom<>.[Joins].SearchFor<> Search2<,>
SelectFrom<>.[Joins].OrderBy<>.SearchFor<> Search3<,,>
SelectFrom<>.[Joins].AggregateTo<>.SearchFor<> Search5<,,>
SelectFrom<>.[Joins].AggregateTo<>.OrderBy<>.SearchFor<> Search6<,,,>
SelectFrom<>.[Joins].Where<>.SearchFor<> Search2<,,>
SelectFrom<>.[Joins].Where<>.OrderBy<>.SearchFor<> Search2<,,,>
SelectFrom<>.[Joins].Where<>.AggregateTo<>.SearchFor<> Search5<,,,>
SelectFrom<>. Search5<,,,,>
[Joins].Where<>.AggregateTo<>.OrderBy<>.SearchFor<>
Join Clauses
.InnerJoin<Table>.On<> InnerJoin<Table,On>
.InnerJoin<Table>.On<>.NextJoin InnerJoin<Table,On,NextJoin>
.InnerJoin<Table>.On<>.SingleTableOnly InnerJoinSingleTable<Table,On>
.InnerJoin<Table>.On<>.SingleTableOnly.NextJoin
InnerJoinSingleTable<Table,On,NextJoin>
.LeftJoin<Table>.On<> LeftJoin<Table,On>
.LeftJoin<Table>.On<>.NextJoin LeftJoin<Table,On,NextJoin>
.LeftJoin<Table>.On<>.SingleTableOnly LeftJoinSingleTable<Table,On>
.LeftJoin<Table>.On<>.SingleTableOnly.NextJoin
LeftJoinSingleTable<Table,On,NextJoin>
.RightJoin<Table>.On<> RightJoin<Table,On>
.RightJoin<Table>.On<>.NextJoin RightJoin<Table,On,NextJoin>
.RightJoin<Table>.On<>.SingleTableOnly RightJoinSingleTable<Table,On>
.RightJoin<Table>.On<>.SingleTableOnly.NextJoin
RightJoinSingleTable<Table,On,NextJoin>
.FullJoin<Table>.On<> FullJoin<Table,On>
.FullJoin<Table>.On<>.NextJoin FullJoin<Table,On,NextJoin>
| Accessing Data | 89
.FullJoin<Table>.On<>.SingleTableOnly FullJoinSingleTable<Table,On>
.FullJoin<Table>.On<>.SingleTableOnly.NextJoin
FullJoinSingleTable<Table,On,NextJoin>
.CrossJoin<Table> CrossJoin<Table>
.CrossJoin<Table>.NextJoin CrossJoin<Table,NextJoin>
.CrossJoin<Table>.SingleTableOnly CrossJoinSingleTable<Table>
.CrossJoin<Table>.SingleTableOnly.NextJoin CrossJoinSingleTable<Table,NextJoin>
Where Clause
.Where<UnaryOperator> Where<UnaryOperator>
.Where<Operand.Comparison> Where<Operand,Comparison>
.Where<Operand.Comparison.NextOperator> Where<Operand,Comparison,NextOperator>
.Where<UnaryOperator.NextOperator> Where2<UnaryOperator,NextOperator>
Aggregate Clause
.AggregateTo<Function> Aggregate<Function>
.AggregateTo<TFunctions>.THaving Aggregate<TFunctions,THaving>
.Having<TCondition> Having<TCondition>
GroupBy<Field> GroupBy<Field>
Max<Field> Max<Field>
Min<Field> Min<Field>
Sum<Field> Sum<Field>
Avg<Field> Avg<Field>
Count Count
Count<Field> Count<Field>
OrderBy Clause
.OrderBy<List> OrderBy<List>
| Accessing Data | 90
Field.Asc Asc<Field>
Field.Desc Desc<Field>
Parameters
Field.FromCurrent Current<Field>
Field.FromCurrent.NoDefault Current2<Field>
Field.AsOptional Optional<Field>
Field.AsOptional.NoDefault Optional2<Field>
Argument.AsBool Argument<bool?>
Argument.AsByte Argument<byte?>
Argument.AsShort Argument<short?>
Argument.AsInt Argument<int?>
Argument.AsLong Argument<long?>
Argument.AsFloat Argument<float?>
| Accessing Data | 91
Argument.AsDouble Argument<double?>
Argument.AsDecimal Argument<decimal?>
Argument.AsGuid Argument<Guid?>
Argument.AsDateTime Argument<DateTime?>
Argument.AsString Argument<string>
And<UnaryOperator> And<UnaryOperator>
And<Operand.Comparison> And<Operand,Comparison>
And<Operand.Comparison>.NextOperator And<Operand,Comparison,NextOperator>
And<UnaryOperator.NextOperator> And2<UnaryOperator,NextOperator>
Or<UnaryOperator> Or<UnaryOperator>
Or<Operand.Comparison> Or<Operand,Comparison>
Or<Operand.Comparison>.NextOperator Or<Operand,Comparison,NextOperator>
Or<UnaryOperator.NextOperator> Or2<UnaryOperator,NextOperator>
Not<UnaryOperator> Not<UnaryOperator>
Not<Operand.Comparison> Not<Operand,Comparison>
Not<Operand.Comparison.NextOperator> Not<Operand,Comparison,NextOperator>
Not<UnaryOperator.NextOperator> Not2<UnaryOperator,NextOperator>
Brackets<UnaryOperator> Where<UnaryOperator>
Brackets<Operand.Comparison> Where<Operand,Comparison>
Brackets<Operand.Comparison.NextOperator> Where<Operand,Comparison,NextOperator>
Brackets<UnaryOperator.NextOperator> Where2<UnaryOperator,NextOperator>
Comparisons
Operand1.When<Condition1>.Else<Operand2>.When<Condition2>.
Switch<Cases>
[...]
Operand1.When<Condition1>. Switch<Cases,Default>
[...].Else<Default>
Operand.When<Condition> Case<Condition,Operand>
Operand.When<Condition>.Else<Operand2>.When<Condition2>.
Case<Condition,Operand,NextCase>
[...]
2. Execute the data view by calling the Select() method of a PXSelectBase-derived class.
3. Cast the result of the query execution to a DAC or an array of DACs, or iterate through DACs
in the result by using the foreach statement. The following sample code shows the approach
of iterating through DACs. For details, see To Process the Result of the Execution of the BQL
Statement.
2. Cast the result of the query execution to a DAC or an array of DACs, or iterate through DACs
in the result by using the foreach statement. The following sample code shows the approach
of iterating through DACs. For details, see To Process the Result of the Execution of the BQL
Statement.
1. Dynamically instantiate a data view in code. You should also provide the graph object as a
parameter to the data view constructor.
2. Execute the data view by using the Select() method of the instance of a PXSelectBase-derived
class.
3. Cast the result of the query execution to a DAC or an array of DACs, or iterate through DACs
in the result by using the foreach statement. The following sample code shows the approach
of iterating through DACs. For details, see To Process the Result of the Execution of the BQL
Statement.
// Declaration of a BLC
public class ReceiptDataEntry : PXGraph<ReceiptDataEntry, Document>
{
// When a screen associated with this BLC is first opened,
// the Optional parameter is replaced with the default DocType value.
public SelectFrom<Document>.
Where<Document.docType.IsEqual<Document.docType.AsOptional>> Receipts;
• The data view delegate must have the same name as the data view except for the first letter,
which must be lowercase.
• The data view delegate must return IEnumerable, as shown in the following example.
| Accessing Data | 96
: If the data view delegate is not defined or it returns null, the standard Select() logic is
executed.
1. In the foreach loop, cast each PXResult<T0> object in the collection to the needed
PXResult<T0, T1, T2, ...> object, where T0, T1, T2, and other type parameters are joined
DACs from the BQL statement. The PXResult<T0, T1, T2, ...> type must be specialized with
the DACs of all joined tables.
2. Cast each PXResult<T0, T1, T2, ...> item to any of the listed types to get the object of this
type.
The following sample code shows how to process the result set of a BQL statement joining two tables.
In This Chapter
• Data Access Classes in Fluent BQL • To Use Parameters in Fluent BQL Queries
SelectFrom<>.[Joins].Where<>.AggregateTo<>.OrderBy<>
SelectFrom<> is the only mandatory part of the query. You can add to the query any number of Join
sections and the Where<>, AggregateTo<>, and OrderBy<> sections of the query, depending on whether
you need the corresponding clauses of the query.
The query defined with fluent BQL as described above is equivalent to the Select command in
traditional BQL. To compose a query for different purposes (such as to define a data view or to define
a Search command in an attribute constructor), you need to prepend additional elements to the query
or append them to the query, as described in Search and Select Commands and Data Views in Fluent
BQL. You can find the equivalents of traditional BQL in fluent BQL in Fluent BQL and Traditional BQL
Equivalents.
SelectFrom<> Section
In the SelectFrom<> section of the query, you use the SelectFrom<> class, which uses a data access
class (DAC) as the type parameter. For details on DACs, see Data Access Classes in Fluent BQL.
Join Sections
Each Join section of the fluent BQL query consists of the following components:
• The join type (InnerJoin<>, LeftJoin<>, RightJoin<>, FullJoin<>, CrossJoin<>) with the
joined DAC as the type parameter.
• The joining condition (On<>). This condition is not specified for CrossJoin<>.
• The single table modifier (SingleTableOnly). This optional part of each Join section forces
optimization if a DAC used in the query has the PXProjection attribute.
The following code fragments show the Join sections with different types of joins.
.InnerJoin<TBqlTable>.On<TJoinCondition>.SingleTableOnly
.LeftJoin<TBqlTable>.On<TJoinCondition>.SingleTableOnly
.RightJoin<TBqlTable>.On<TJoinCondition>.SingleTableOnly
.FullJoin<TBqlTable>.On<TJoinCondition>.SingleTableOnly
.CrossJoin<TBqlTable>.SingleTableOnly
• And<> subsections
• Or<> subsections
• Brackets<> subsections
| Accessing Data | 99
The following code fragments show examples of an On<> subsection and a Where<> section.
.On<PMTask.projectID.IsEqual<PMProject.contractID>.
And<
PMTask.approverID.IsEqual<EPActivityFilter.approverID.FromCurrent>>>
.Where<PMProject.isActive.IsEqual<True>.
And<PMTask.taskID.IsNotNull.
Or<PMProject.approverID.IsEqual<
EPActivityFilter.approverID.FromCurrent>>>>
bool BqlBool.Field<TSelf>
byte BqlByte.Field<TSelf>
short BqlShort.Field<TSelf>
int BqlInt.Field<TSelf>
long BqlLong.Field<TSelf>
float BqlFloat.Field<TSelf>
double BqlDouble.Field<TSelf>
decimal BqlDecimal.Field<TSelf>
Guid BqlGuid.Field<TSelf>
DateTime BqlDateTime.Field<TSelf>
| Accessing Data | 100
String BqlString.Field<TSelf>
byte[] BqlByteArray.Field<TSelf>
using System;
using PX.Data;
[Serializable]
public class Product : PX.Data.IBqlTable
{
// The class used in BQL statements to refer to the ProductID column
public abstract class productID : PX.Data.BQL.BqlInt.Field<productID>
{
}
// The property holding the ProductID value in a record
[PXDBIdentity(IsKey = true)]
public virtual int? ProductID { get; set; }
SelectFrom<Product>.
Where<Use<Product.availQty>.AsDecimal.IsNotEqual<Zero>>.
View AvailableProducts;
Though the DAC fields in the traditional BQL style can be used in fluent BQL queries, we recommend
that you use the fluent BQL style of DAC declaration for simplicity.
Data Views
You can use any of the following approaches to define a data view:
• Use the PXViewOf<> class before the fluent BQL query, as shown in the following code example.
PXViewOf<Product>.BasedOn<
SelectFrom<Product>.
Where<Product.isActive.IsEqual<True>>>.ReadOnly ActiveProducts;
The DACs that are specified as type parameters in PXViewOf<> and SelectFrom<> must be the
same; this is checked by the compiler. You can omit .BasedOn<> if you want to declare a view
that selects all records from one table. You append .ReadOnly to the view definition if you need to
define a read-only data view.
• Append .View to the fluent BQL query, as shown in the following code example.
SelectFrom<Product>.
Where<Product.isActive, Equal<True>>.View.ReadOnly ActiveProducts;
You append .ReadOnly to the view definition if you need to define a read-only data view.
The data views defined with fluent BQL are equivalent to the corresponding traditional BQL data views.
For the full list of equivalents, see Fluent BQL and Traditional BQL Equivalents. Also, the fluent BQL data
views have the same static methods as the traditional BQL data views have.
Select Commands
The query defined with fluent BQL, as described in Fluent Business Query Language, is equivalent to the
Select BQL command. For the full list of equivalents, see Fluent BQL and Traditional BQL Equivalents.
Search Commands
You can use any of the following approaches to define a Search BQL command:
• Use the SearchFor<> class before the fluent BQL query, as shown in the following code example.
SearchFor<Product.productId>.In<
SelectFrom<Product>.
Where<Product.isActive.IsEqual<True>>>
• Append .SearchFor<> to the fluent BQL query, as shown in the following code example.
SelectFrom<Product>.
Where<Product.isActive.IsEqual<True>>.SearchFor<Product.productId>
The Search commands defined with fluent BQL are equivalent to the corresponding traditional BQL
commands. For the full list of equivalents, see Fluent BQL and Traditional BQL Equivalents.
bool BqlBool.Constant<TSelf>
byte BqlByte.Constant<TSelf>
short BqlShort.Constant<TSelf>
int BqlInt.Constant<TSelf>
long BqlLong.Constant<TSelf>
float BqlFloat.Constant<TSelf>
double BqlDouble.Constant<TSelf>
decimal BqlDecimal.Constant<TSelf>
Guid BqlGuid.Constant<TSelf>
DateTime BqlDateTime.Constant<TSelf>
String BqlString.Constant<TSelf>
SelectFrom<Table>.
Where<Table.decimalField.AsDecimal.IsEqual<Use<decimal_0>.AsDecimal>>.
| Accessing Data | 103
View records;
Although the constants in the traditional BQL style can be used in fluent BQL queries, we recommend
that you use the fluent BQL style of constant declaration for simplicity.
By using the current field value from PXCache in the declaration of a data view, you can refer to another
view to relate these data views to each other. A typical example is referencing the current master
record on master-detail webpages. For details on how the current field value is used, see To Relate
Data Views to Each Another.
bool @P.AsBool
byte @P.AsByte
short @P.AsShort
int @P.AsInt
long @P.AsLong
float @P.AsFloat
double @P.AsDouble
decimal @P.AsDecimal
Guid @P.AsGuid
DateTime @P.AsDateTime
String @P.AsString
By using these classes, you can pass values to the SQL query, as described in To Pass a Field Value to
the SQL Query and To Pass Multiple Field Values to the SQL Query.
By using AsOptional, you can pass the external presentations of the values to the SQL query, as
described in To Provide External Presentation of the Field Value to the SQL Query.
bool Argument.AsBool
byte Argument.AsByte
short Argument.AsShort
int Argument.AsInt
long Argument.AsLong
float Argument.AsFloat
double Argument.AsDouble
decimal Argument.AsDecimal
Guid Argument.AsGuid
DateTime Argument.AsDateTime
String Argument.AsString
By using the Argument classes, you can pass values to the data view delegates. For more information
on how to use the Argument classes, see To Pass a Value from a UI Control to a Data View.
: In a SelectFrom<> class, you configure a query to the database. The actual request to the database
is performed once you cast the result of the query execution to a DAC or an array of DACs, or when you
iterate through DACs in the result with the foreach statement. For details, see Data Query Execution.
• Make sure that the application database has the database tables from which you are going to
request data, and that the application defines the data access classes (DACs) for these tables. For
more information on defining DACs, see Data Access Classes in Fluent BQL.
using PX.Data.BQL.Fluent;
using PX.Data.BQL;
1. Type the SelectFrom<> class with the needed DAC as the type parameter.
For example, suppose that you need to convert the following SQL statement to fluent BQL.
SelectFrom<Product>
2. If you need to include JOIN clauses in the query, for each table that you want to join, do the
following:
b. Append to the statement the On<> clause with the joining conditions. Adhere to the
following rules when you specify the conditions:
• Use the And<>, Or<>, and Brackets<> classes to logically connect the conditions
and comparisons.
• To specify the fields that should be used in the conditions, use the class fields
defined in the DAC, such as Product.productID. (The name of the field class starts
with a lowercase letter. Do not confuse it with the property field, which has the
same name but starts with an uppercase letter.)
• If you need to use constants in the fluent BQL statement, use one of the predefined
BQL constants or your own constant. (For details on using constants, see Constants
in Fluent BQL.)
| Accessing Data | 106
• If you need to specify the values of the parameters at run time, use the fluent BQL
parameters. For information about parameters, see Parameters in Fluent BQL. For
information about how to use parameters, see To Use Parameters in Fluent BQL
Queries.
In the sample code that has been presented in this topic, you would add two Join classes to the
statement, as follows.
SelectFrom<Product>.
InnerJoin<SupplierProduct>.
On<SupplierProduct.productID.IsEqual<Product.productID>>.
InnerJoin<Supplier>.
On<Supplier.accountID.IsEqual<SupplierProduct.accountID>>
3. If you need to include a WHERE clause in the query, append the Where<> clause to the statement
and specify the conditions as follows:
• Use the And<>, Or<>, and Brackets<> classes to logically connect the conditions and
comparisons.
• To specify the fields that should be used in the conditions, use the class fields defined in
the DAC, such as Product.productID. (The name of the field class starts with a lowercase
letter. Do not confuse it with the property field, which has the same name but starts with
an uppercase letter.)
• If you need to use constants in the fluent BQL statement, use one of the predefined BQL
constants or your own constant. (For details on using constants, see Constants in Fluent
BQL.)
• If you need to specify the values of the parameters at run time, use the fluent BQL
parameters. For information about parameters, see Parameters in Fluent BQL. For
information about how to use parameters, see To Use Parameters in Fluent BQL Queries.
In the sample code that has been presented in this topic, you would append the Where<> clause
to the statement, as follows.
SelectFrom<Product>.
InnerJoin<SupplierProduct>.
On<SupplierProduct.productID.IsEqual<Product.productID>>.
InnerJoin<Supplier>.
On<Supplier.accountID.IsEqual<SupplierProduct.accountID>>.
Where<
Brackets<Product.bookedQty.IsNotNull.
And<Product.availQty.IsNotNull>.
And<Product.minAvailQty.IsNotNull>.
And<Product.active.IsEqual<True>.
Or<Product.active.IsNull>>.
And<Product.bookedQty.IsGreater<Product.availQty>.
Or<Product.availQty.IsLess<Product.minAvailQty>>>>.
Or<Product.availQty.IsNotNull>>
4. If you need to group or aggregate records, append the AggregateTo<> clause to the statement
and specify the grouping conditions and aggregation functions by using the GroupBy clauses and
the Min, Max, Sum, Avg, and Count aggregation functions.
In the sample code that has been presented in this topic, you would append the AggregateTo<>
clause to the statement as follows.
SelectFrom<Product>.
InnerJoin<SupplierProduct>.
On<SupplierProduct.productID.IsEqual<Product.productID>>.
InnerJoin<Supplier>.
On<Supplier.accountID.IsEqual<SupplierProduct.accountID>>.
Where<
| Accessing Data | 107
Brackets<Product.bookedQty.IsNotNull.
And<Product.availQty.IsNotNull>.
And<Product.minAvailQty.IsNotNull>.
And<Product.active.IsEqual<True>.
Or<Product.active.IsNull>>.
And<Product.bookedQty.IsGreater<Product.availQty>.
Or<Product.availQty.IsLess<Product.minAvailQty>>>>.
Or<Product.availQty.IsNotNull>>.
AggregateTo<GroupBy<Product.categoryCD>,
Min<Product.bookedQty>>
5. If you need to order records, append to the statement the OrderBy<> clause with the Asc<> and
Desc<> classes as the type parameters.
In the sample code that has been presented in this topic, you would append the OrderBy<>
clause to the statement as follows.
SelectFrom<Product>.
InnerJoin<SupplierProduct>.
On<SupplierProduct.productID.IsEqual<Product.productID>>.
InnerJoin<Supplier>.
On<Supplier.accountID.IsEqual<SupplierProduct.accountID>>.
Where<
Brackets<Product.bookedQty.IsNotNull.
And<Product.availQty.IsNotNull>.
And<Product.minAvailQty.IsNotNull>.
And<Product.active.IsEqual<True>.
Or<Product.active.IsNull>>.
And<Product.bookedQty.IsGreater<Product.availQty>.
Or<Product.availQty.IsLess<Product.minAvailQty>>>>.
Or<Product.availQty.IsNotNull>>.
AggregateTo<GroupBy<Product.categoryCD>,
Min<Product.bookedQty>>.
OrderBy<Product.unitPrice.Asc,
Product.availQty.Desc>
using PX.Data;
using PX.Data.BQL.Fluent;
In this code, there is a many-to-one relationship between the DocTransaction and Document data
access classes (DACs), and this relationship is implemented through the DocNbr and DocType key fields.
The data views in the code connect the Document and DocTransaction records.
| Accessing Data | 108
: Acumatica Framework translates the fluent BQL query of the second view in the sample code to the
following SQL statement. In this SQL query, [parameter1] is the DocNbr value and [parameter2]
is the DocType value retrieved from the Current property of the DocTransaction cache; [list of
columns] is the list of columns of the DocTransaction table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For details
on which columns are enumerated, see Translation of a BQL Command to SQL.
1. Use the @P.As[Type] class of the needed type in the BQL statement, where [Type] is one of
the following: Bool, Byte, Short, Int, Long, Float, Double, Decimal, Guid, DateTime,
or String.
2. Specify the needed value as the Select() method argument. The value passed to Select()
must be of the same type as the type of the specified field.
: The @P.As[Type] class must be used only in the BQL statements that are directly executed in the
application code. The data views that are queried from the UI will not work if they contain this class.
The code below shows the execution of a BQL statement with a specific value passed in code.
using PX.Data;
using PX.Data.BQL.Fluent;
using PX.Data.BQL;
: Acumatica Framework translates the previous fluent BQL query to the following SQL statement. In
this SQL query, [parameter] is the value of the product.CategoryCD variable at the moment the
Select() method is invoked; [list of columns] is the list of columns of the Category table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For details
on which columns are enumerated, see Translation of a BQL Command to SQL.
1. Use multiple @P.As[Type] classes of the needed type in the fluent BQL statement, where
[Type] is one of the following: Bool, Byte, Short, Int, Long, Float, Double, Decimal,
Guid, DateTime, or String.
| Accessing Data | 109
2. Specify the needed values as the Select() method arguments in the order in which the
parameters are specified in the BQL statement. The number of @P.As[Type] classes must match
the number of parameters passed to the Select() method.
: The @P.As[Type] classes should be used in only the BQL statements that are executed in the
application code.
The following code shows an example of a fluent BQL statement with two Required parameters.
using PX.Data;
using PX.Data.BQL.Fluent;
using PX.Data.BQL;
Document doc =
SelectFrom<Document>.
Where<Document.docNbr.IsEqual<@P.AsString>.
And<Document.docType.IsEqual<@P.AsString>>>.View.
Select(this, line.DocNbr, line.DocType);
: Acumatica Framework translates the previous fluent BQL query to the following SQL statement, where
[list of columns] is the list of the columns of the Document table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For details
on which columns are enumerated, see Translation of a BQL Command to SQL.
1. Define a data view with the Argument.As[Type] class, where [Type] specifies the data type of
the expected value, as shown in the following sample BQL query.
using PX.Data;
using PX.Data.BQL.Fluent;
SelectFrom<TreeViewItem>.
Where<TreeViewItem.parentID.IsEqual<Argument.AsInt>>.
OrderBy<Asc<TreeViewItem.parentID>>.View GridDataSource;
: Acumatica Framework translates the previous fluent BQL query to the following SQL statement.
In this SQL query, [parameter] will contain the value received from the UI control and passed to
the Select() method; [list of columns] is the list of columns of the TreeViewItem table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query.
For details on which columns are enumerated, see Translation of a BQL Command to SQL.
| Accessing Data | 110
2. Define the data view delegate with parameters through which you can access the values passed
from the UI. (You can find more information on how to use data view delegates in To Execute a
BQL Statement in a Data View Delegate.)
: When a BQL statement with the Argument parameter is executed in code, the value must be
specified in the parameters of the Select() method.
1. Add the PXSelector attribute with a substitute key to a DAC field, as shown in the following
example.
using PX.Data;
using PX.Data.BQL.Fluent;
[PXSelector(typeof(SearchFor<Product.productID>.In<SelectFrom<Product>>,
new Type [] {
typeof(Product.productCD),
typeof(Product.productName)
},
SubstituteKey = typeof(Product.productCD))]
public virtual int? ProductID { get; set; }
In this example, PXSelector replaces the ProductID field in the user interface with the human-
readable ProductCD field. In the UI control for this field, the user enters a ProductCD value.
The PXSelector attribute implements the FieldUpdating event handler, which replaces the
ProductCD value with the corresponding ProductID value.
2. Use the AsOptional class to select records by the external presentation of the field value, as
shown in the following code for OrderLine records.
using PX.Data;
using PX.Data.BQL.Fluent;
3. In the Select() method, provide values for all AsOptional, @P.As[Type], and
Argument.As[Type] parameters up to the last @P.As[Type] or Argument.As[Type] parameter in
the fluent BQL statement, as shown in the following sample code.
using PX.Data;
using PX.Data.BQL.Fluent;
And<OrderLine.taxRate.IsEqual<OrderLine.taxRate.AsOptional>>>.View.
.Select(this, p.ProductCD, od.ExtPrice, od.UnitPrice);
: Acumatica Framework translates the fluent BQL query in the code to the following SQL statement,
where [list of columns] is the list of columns of the OrderLine table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query.
For details on which columns are enumerated, see Translation of a BQL Command to SQL.
In This Chapter
• The classes that correspond to database tables (data access classes) and columns. For details on
data access classes, see Data Access Classes in Traditional BQL.
• The classes that define data views in a graph and select data from the database in code
(PXSelect classes). For more information on these classes, see PXSelect Classes.
• The classes that compose BQL statements, such as Select, Search, Where, OrderBy, And, and
Add. For more information on these classes, see The Classes That Compose BQL Statements.
| Accessing Data | 112
• The classes that pass parameters to BQL statements, such as Current, Required, Optional,
Argument. For details on BQL parameters, see Parameters in Traditional BQL Statements.
using System;
using PX.Data;
[Serializable]
public class Product : PX.Data.IBqlTable
{
// The class used in BQL statements to refer to the ProductID column
public abstract class productID : PX.Data.IBqlField
{
}
// The property holding the ProductID value in a record
[PXDBIdentity(IsKey = true)]
public virtual int? ProductID { get; set; }
PXSelect Classes
In traditional business query language (BQL), you define a data view or request database data in code
by using one of the PXSelect classes (that is, the classes derived from PXSelectBase).
PXSelect Classes
The instances of PXSelect classes are complex objects containing the following:
• A reference (through the PXView object) to the Select object, which is the business query
language (BQL) command to be executed
• A reference to the cache of the data access class (DAC) type that is specified in the first type
parameter of PXSelect
That is, through the PXSelect classes, you can execute the BQL command and interact with the cache,
as illustrated in the following diagram.
| Accessing Data | 113
: Do not confuse the PXSelect classes with the Select classes. PXSelect is an aggregate of the data
view, cache, and graph. You can use PXSelect classes to read, write, update, and delete records in the
scope of a graph. Select classes simply represent BQL commands. You cannot read records by using a
BQL command without instantiating a data view. For more information on the Select classes, see The
Classes That Compose BQL Statements.
PXSelectJoin<Table1,
LeftJoin<Table2, On<Table2.field2, Equal<Table1.field1>>>,
Where<Table1.field3, IsNotNull>,
OrderBy<Asc<Table1.field1>>>
: Acumatica Framework translates this statement to the following SQL query, where [list of
columns] is the list of columns of the joined tables.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For details
on which columns are enumerated, see Translation of a BQL Command to SQL.
For more information on how to use the BQL clauses, see To Select Records By Using Traditional BQL.
If you need to retrieve data as it is currently stored in the database, you use one of the PXSelect
classes that has Readonly in its name, such as the PXSelectReadonly<Table> class, or any of the
PXSelect classes that use aggregation, such as the PXSelectGroupBy<Table, Aggregate> class.
Otherwise, the data retrieved from the database can be merged with the data currently stored in the
| Accessing Data | 114
cache. For more information on how the data is merged with the cache, see Merge of the Records with
PXCache.
• PXSelect<Table, Where>
• PXSelect<Table>
• PXSelectGroupBy<Table, Aggregate>
• PXSelectJoin<Table, Join>
• PXSelectOrderBy<Table, OrderBy>
• PXSelectReadonly<Table, Where>
• PXSelectReadonly<Table>
• PXSelectReadonly2<Table, Join>
• PXSelectReadonly3<Table, OrderBy>
the database maintenance server. For more information on how this method is used during BQL
statement execution, see Translation of a BQL Command to SQL.
• IBqlVerifier.Verify(): Used during the merge of the records with PXCache to evaluate a
condition on a record retrieved from the database or calculate an expression with the record. For
details on the merge, see Merge of the Records with PXCache.
Depending on the purpose of each BQL class, the class also implements the methods of the interfaces
derived from the IBqlCreator interface. For example, the aggregation functions—such as Sum, Avg,
Min, and Max—implement the methods of the IBqlFunction interface.
The high-level overview of BQL class inheritance is illustrated in the following diagram. For descriptions
of the interfaces and classes, see the API Reference.
The sections below describe the classes derived from the BqlCommand class.
Select Classes
The Select classes, which are derived from the BqlCommand class, represent BQL commands and select
all bound fields of the DAC and the unbound fields with specific attributes, such as PXDBCalced.
: More specific, the Select classes select all DAC fields that are decorated with the attributes that
subscribe to the PXCommandPreparing event. For details on which fields are selected, see Translation of a
BQL Command to SQL.
In a BQL expression based on Select, the first type parameter is a DAC, as shown in the following
sample BQL statement.
Select<Product>
The Select classes can parse themselves into SQL and provide methods for modifying the BQL
command. However, you cannot directly use the Select class to execute the BQL query. Typically, you
use Select in attributes in DACs, such as the PXProjection attribute.
Search Classes
The Search classes, which are derived from the BqlCommand class, select one field of a DAC (while the
Select classes select multiple fields).
In a Search-based statement, the first type parameter is a DAC field, as shown in the following sample
BQL expression. This expression selects the Product.unitPrice field.
Search<Product.unitPrice>
These classes can parse themselves into SQL and provide methods for modifying the BQL command.
However, you cannot directly use the Search class to execute the BQL query. Typically, you use Search
in attributes in DACs, such as the PXSelector attribute. (PXSelectorAttribute requires a Search class
and not a Select because the lookup control, which is configured by this attribute, displays precisely
one field (usually a key field), which is what Search returns.)
BqlCommand Classes
The BqlCommand classes represent BQL commands. The system uses the following types of BqlCommand
classes:
• BqlCommand: This base class for the Select and Search classes is used by the system during the
processing of data queries on the data entry forms.
• BqlGenericCommand: This class, which is derived from the BqlCommand class, is used by the
system during the processing of generic inquiries.
• BqlSoapCommand: This class, which is derived from the BqlCommand class, is used by the system
during the processing of reports. For details on report processing, see Display of Reports.
The main purpose of BqlCommand classes is to convert BQL commands to SQL text. The
BqlGenericCommand and BqlSoapCommand classes provide additional methods for generic inquiry and
report processing.
Required
The Required parameter inserts a specific value into the SQL query.
By using the Required parameters, you can pass values to the SQL query, as described in To Pass a
Field Value to the SQL Query and To Pass Multiple Field Values to the SQL Query.
Argument
The Argument parameter passes values from UI controls to the SQL query.
By using the Argument parameters, you can pass values to the data view delegates. For more
information on how to use the Argument parameter, see To Pass a Value from a UI Control to a Data
View.
PXSelect<Product>
In this BQL statement, you are selecting all data records (with the values of all bound fields) from the
Product table.
: For example, suppose that the Product table has two columns, ProductID and UnitPrice. In
this case, Acumatica Framework translates the previous BQL statement to the following SQL query. The
| Accessing Data | 120
framework adds ordering by the DAC key field (in ascending order) to the end of the SQL query because
the BQL statement does not specify ordering.
To Filter Records
To filter records in the database table to be retrieved, construct a BQL statement with conditions by
doing the following:
1. Use one of the PXSelect classes that has the Where type parameter, such as PXSelect<Table,
Where>. For the full list of PXSelect classes, see PXSelect Classes.
2. Specify the filtering conditions by using the Where clause, as described in To Filter Records.
3. To specify the fields that should be used for filtering, use the class fields defined in the DACs,
such as Product.productID. (The name of the field class starts with a lowercase letter. Do not
confuse it with the property field, which has the same name but starts with uppercase letter.)
The following sample BQL statement selects all data records from the Product table that have the
specified value in the ProductID column.
PXSelect<Product,
Where<Product.productID, Equal<Required<Product.productID>>>>
: Acumatica Framework translates the previous BQL statement to the following SQL query. In this SQL
query, [list of columns] is the list of columns of the Product table; [parameter] is the value
passed to the Select() method of the PXSelect class, which is called when the BQL query is executed.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For details
on which columns are enumerated, see Translation of a BQL Command to SQL.
To Order Records
To order records in the database table to be retrieved, construct a BQL statement with ordering by
doing the following:
1. Use one of the PXSelect classes that has the OrderBy type parameter, such as
PXSelectOrderBy<Table, OrderBy> or PXSelect<Table, Where, OrderBy>. For the full list of
PXSelect classes, see PXSelect Classes.
3. To specify the field that should be used for filtering, use the class field defined in the DAC,
such as Product.productID. (The name of the field class starts with a lowercase letter. Do not
confuse it with the property field, which has the same name but starts with uppercase letter.)
The following sample BQL statement selects all Product data records and sorts them by the UnitPrice
field in ascending order.
PXSelectOrderBy<Product, OrderBy<Asc<Product.unitPrice>>>
: Acumatica Framework translates the previous BQL statement to the following SQL query, where [list
of columns] is the list of columns of the Product table.
ORDER BY Product.UnitPrice
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For details
on which columns are enumerated, see Translation of a BQL Command to SQL.
1. Use one of the PXSelect classes that has the Join type parameter, such as
PXSelectJoin<Table, Join> or PXSelectReadonly2<Table, Join>.
2. In the Join type parameter of the PXSelect class, use one of the Join clauses—such as
InnerJoin, LeftJoin, RightJoin, FullJoin, or CrossJoin—that are directly mapped to SQL
JOIN clauses, as shown in the following sample BQL statement. For more information on the use
of Join clauses, see To Query Multiple Tables.
PXSelectJoin<SalesOrder,
InnerJoin<OrderDetail,
On<OrderDetail.orderNbr, Equal<SalesOrder.orderNbr>>>>
: Acumatica Framework translates the previous BQL statement to the following SQL query, where
[list of columns] is the list of columns of the joined tables.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query.
For details on which columns are enumerated, see Translation of a BQL Command to SQL.
To Aggregate Records
To group or aggregate records, construct a BQL statement by doing the following:
1. Use one of the PXSelect classes with the Aggregate type parameter, such as
PXSelectGroupBy<Table, Aggregate>.
2. In the Aggregate type parameter of the PXSelect class, specify the grouping conditions and
aggregation functions by using the Aggregate<Function> class, the GroupBy clauses, and
the Min, Max, Sum, Avg, and Count aggregation functions, as shown in the following sample
BQL statement. For more information on the use of the grouping conditions and aggregation
functions, see To Group and Aggregate Records.
PXSelectGroupBy<Product,
Aggregate<GroupBy<Product.categoryCD>>>
: Acumatica Framework translates the previous BQL statement to the following SQL query.
SELECT Product.CategoryCD,
[MAX(Field) for other fields]
FROM Product
GROUP BY Product.CategoryCD
To Filter Records
You construct business query language (BQL) statements with filtering conditions by using the Where
clause in a PXSelect class that has the Where type parameter. (For more information on selecting
the PXSelect class, see To Select Records By Using Traditional BQL.) One Where clause can contain
multiple conditions chained to one another by logical operators (Or, And, and Not) and nested Where
clauses (these nested clauses are equivalent to placing conditions in brackets).
| Accessing Data | 122
Typically, you construct a BQL statement with a condition to compare one field with another field or a
constant, or to check if the field value has been specified (that is, to compare the field value with null).
You can also use multiple conditions in the Where clause.
1. Select the comparison class that you need, such as NotEqual, Greater, or Less.
2. Specify the compared field in the first type parameter of the Where class and the comparison in
the second type parameter, as shown in the following sample BQL statement.
: Acumatica Framework translates the previous BQL statement to the following SQL query, where
[list of columns] is the list of columns of the Product table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query.
For details on which columns are enumerated, see Translation of a BQL Command to SQL.
1. Select the comparison class that you need, such as NotEqual, Greater, or Less.
2. Select one of the predefined constants—that is, the BQL class derived from the Constant<Type>
class (such as Boolean values True and False, integer Zero, datetime Now, Today, and
MaxDate, and string StringEmpty), or define your own constant as a class derived from the
Constant<Type> class.
3. Specify the compared field in the first type parameter of the Where class and the comparison in
the second type parameter, as shown in the following sample BQL statement.
: Acumatica Framework translates this BQL statement to the following SQL query, where [list
of columns] is the list of columns of the Product table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query.
For details on which columns are enumerated, see Translation of a BQL Command to SQL.
• To check that the field is null, use the Where<Operand, Comparison> class, as shown in the
following sample BQL statement.
: Acumatica Framework translates the previous BQL statement to the following SQL query, where
[list of columns] is the list of columns of the Product table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For
details on which columns are enumerated, see Translation of a BQL Command to SQL.
• Use the Where<Operator> class and the logical operator Not, as shown in the following
sample BQL statement.
: Acumatica Framework translates the previous BQL statement to the following SQL query,
where [list of columns] is the list of columns of the Product table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL
query. For details on which columns are enumerated, see Translation of a BQL Command to
SQL.
• Use the Where<Operand, Comparison class, as shown in the following sample BQL
statement..
: Acumatica Framework translates the previous BQL statement to the following SQL query,
where [list of columns] is the list of columns of the Product table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL
query. For details on which columns are enumerated, see Translation of a BQL Command to
SQL.
Important: The predefined constant Null cannot be used in the Where clause with Equal to select
records with null fields. The Null constant is used in Switch conditions.
• To specify multiple comparisons that are connected with the same logical operator, use the
Where<Operand, Comparison, NextOperator> class and specify its type parameters as follows:
• In the second type parameter, specify the first comparison, such as NotEqual, Greater, or
Less.
• In the third type parameter, specify the logical operator, such as And, And2, Or, or Or2. You
can chain any number of comparisons to one another by using binary operators with three
type parameters, as shown in the following sample BQL statement.
PXSelect<Product,
Where<Product.bookedQty, Greater<Product.availQty>,
Or<Product.availQty, Less<Product.minAvailQty>,
Or<Product.availQty, IsNull>>>>
: Acumatica Framework translates the previous BQL statement to the following SQL query,
where [list of columns] is the list of columns of the Product table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL
query. For details on which columns are enumerated, see Translation of a BQL Command to
SQL.
• To write more complex conditional expressions with logical operators of different types, use
nested Where or Where2 clauses. For more information on writing complex BQL statements, see To
Compose a BQL Statement from an SQL Statement.
To Order Records
You construct business query language (BQL) statements that include ordering of records by using
the OrderBy clause in one of the PXSelect classes that has the OrderBy type parameter. (For more
information on selecting the PXSelect class, see To Select Records By Using Traditional BQL.)
By default, if the BQL statement does not specify ordering, Acumatica Framework adds ordering by the
data access class (DAC) key fields (in the order of field declaration) in ascending order to the end of the
SQL query. You can order the records by one column or multiple columns, or by a condition.
PXSelectOrderBy<Product, OrderBy<Asc<Product.unitPrice>>>
In this statement, all Product data records are selected and are sorted by the UnitPrice field in
ascending order.
: Acumatica Framework translates this BQL statement to the following SQL query, where [list of
columns] is the list of columns of the Product table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For details
on which columns are enumerated, see Translation of a BQL Command to SQL.
PXSelectOrderBy<Product,
OrderBy<Asc<Product.unitPrice, Desc<Product.availQty>>>>
: Acumatica Framework translates the previous BQL statement to the following SQL query, where [list
of columns] is the list of columns of the Product table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For details
on which columns are enumerated, see Translation of a BQL Command to SQL.
| Accessing Data | 125
PXSelectOrderBy<Product,
OrderBy<Asc<
Switch<Case<Where<Product.availQty, Greater<Product.bookedQty>>, True>,
False>>>>
In this statement, the records with AvailQty values less or equal to BookedQty values are ordered first.
: Acumatica Framework translates the previous BQL statement to the following SQL query, where [list
of columns] is the list of columns of the Product table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For details
on which columns are enumerated, see Translation of a BQL Command to SQL.
• InnerJoin returns all records where there is at least one match in both tables.
• LeftJoin returns all records from the left table, and the matched records from the right table.
Where there are no matched records from the right table, null values are inserted.
• RightJoin returns all records from the right table, and the matched records from the left table.
Where there are no matched records from the left table, null values are inserted.
• FullJoin returns all records when there is a match in one of the tables.
To Join Two Tables (Inner Join, Left Join, Right Join, or Full Join)
To join two tables, use one of the Join clauses with two type parameters (such as InnerJoin<Table,
On>) and the On<Operand, Comparison> or On<Operator> class to specify a conditional expression for
joining, as shown in the following sample BQL statement.
PXSelectJoin<SalesOrder,
InnerJoin<OrderDetail,
On<OrderDetail.orderNbr, Equal<SalesOrder.orderNbr>>>>
: Acumatica Framework translates the previous BQL statement to the following SQL query, where [list
of columns] is the list of columns of the joined tables.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For details
on which columns are enumerated, see Translation of a BQL Command to SQL.
| Accessing Data | 126
PXSelectJoin<Product, CrossJoin<Supplier>>
: Acumatica Framework translates the previous BQL statement to the following SQL query, where [list
of columns] is the list of columns of the joined tables.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For details
on which columns are enumerated, see Translation of a BQL Command to SQL.
• Use a Join clause with three type parameters (such as InnerJoin<Table, On, NextJoin>).
Each subsequent join clause is specified as the last type parameter of the previous join clause, as
shown in the following sample BQL statement.
PXSelectJoin<SalesOrder,
InnerJoin<OrderDetail,
On<OrderDetail.orderNbr, Equal<SalesOrder.orderNbr>>,
LeftJoin<Employee,
On<Employee.employeeID, Equal<SalesOrder.employeeID>>>>>
: Acumatica Framework translates this BQL statement to the following SQL query, where [list of
columns] is the list of columns of the joined tables.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For
details on which columns are enumerated, see Translation of a BQL Command to SQL.
• Use the On conditions to specify conditional expressions for joining, as shown in the following
sample BQL statement. In subsequent join clauses, the On conditions can refer to fields from any
joined table, and can contain any number of conditions chained by logical operators as in filtering
conditions.
PXSelectJoin<SalesOrder,
InnerJoin<OrderDetail,
On<OrderDetail.orderNbr, Equal<SalesOrder.orderNbr>>,
LeftJoin<Employee,
On<Employee.employeeID, Equal<SalesOrder.employeeID>>,
RightJoin<Product,
On<Product.productID, Equal<OrderDetail.productID>,
And<Product.unitPrice, Equal<OrderDetail.unitPrice>>>>>>>
: Acumatica Framework translates the previous BQL statement to the following SQL query, where
[list of columns] is the list of columns of the joined tables.
Product.UnitPrice = OrderDetail.UnitPrice)
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For
details on which columns are enumerated, see Translation of a BQL Command to SQL.
1. Specify all grouping conditions (the GroupBy clause) and aggregation functions (such as Min,
Max, Sum, Avg, and Count) in the Aggregate clause, as shown in the following sample BQL
statement. Fields specified in GroupBy clauses are selected as is; an aggregation function is
applied to all other fields. The default Max function is used if no function is specified for a field. If
a data field has the PXDBScalar attribute, NULL is inserted for that field.
PXSelectGroupBy<Product,
Aggregate<GroupBy<Product.categoryCD>>>
: Acumatica Framework translates the previous BQL statement to the following SQL query.
SELECT Product.CategoryCD,
[MAX(Field) for other fields]
FROM Product
GROUP BY Product.CategoryCD
2. If necessary, insert another GroupBy clause or aggregation function as the second type
parameter of the previous GroupBy clause or aggregation function, as shown in the following
sample BQL statement.
PXSelectGroupBy<Product,
Aggregate<GroupBy<Product.categoryCD,
Sum<Product.availQty,
Sum<Product.bookedQty,
GroupBy<Product.stockUnit,
Min<Product.unitPrice>>>>>>>
: Acumatica Framework translates the previous BQL statement to the following SQL query.
PXSelect<DocTransaction,
Where<DocTransaction.docNbr, Equal<Current<Document.docNbr>>,
And<DocTransaction.docType, Equal<Current<Document.docType>>>>>
DocTransactions;
In this code, there is a many-to-one relationship between the DocTransaction and Document data
access classes (DACs), and this relationship is implemented through the DocNbr and DocType key fields.
The views in the code connect the Document and DocTransaction records.
: Acumatica Framework translates the BQL query of the second view in the sample BQL code to the
following SQL statement. In this SQL query, [parameter1] is the DocNbr value and [parameter2]
is the DocType value taken from the Current property of the DocTransaction cache; [list of
columns] is the list of columns of the DocTransaction table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For details
on which columns are enumerated, see Translation of a BQL Command to SQL.
The code below shows the execution of a BQL statement with the Required parameter.
: Acumatica Framework translates the previous BQL query to the following SQL statement. In this SQL
query, [parameter] is the value of the product.CategoryCD variable at the moment the Select()
method is invoked; [list of columns] is the list of columns of the Category table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For details
on which columns are enumerated, see Translation of a BQL Command to SQL.
: The Required parameters should be used in only the BQL statements that are executed in the
application code.
The following code shows an example of a BQL statement with two Required parameters.
: Acumatica Framework translates the previous BQL query to the following SQL statement, where [list
of columns] is the list of columns of the Document table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query. For details
on which columns are enumerated, see Translation of a BQL Command to SQL.
1. Add the PXSelector attribute with a substitute key to a DAC field, as shown in the following
example.
[PXSelector(typeof(Search<Product.productID>),
new Type [] {
typeof(Product.productCD),
typeof(Product.productName)
},
SubstituteKey = typeof(Product.productCD))]
public virtual int? ProductID { get; set; }
In this example, PXSelector replaces the ProductID field in the user interface with the human-
readable ProductCD field. In the UI control for this field, the user enters a ProductCD value.
The PXSelector attribute implements the FieldUpdating event handler, which replaces the
ProductCD value with the corresponding ProductID value.
2. Use the Optional parameter to select records by the external presentation of the field value, as
shown in the following code for OrderDetail records.
3. In the Select() method, provide values for all Optional, Required, and Argument parameters
up to the last Required or Argument parameter in the BQL statement, as shown in the following
sample code.
OrderDetail od = ...
Product p = ...
: Acumatica Framework translates the BQL query in the code to the following SQL statement,
where [list of columns] is the list of columns of the OrderDetail table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query.
For details on which columns are enumerated, see Translation of a BQL Command to SQL.
1. Define the PXSelect data view with the Argument parameter whose type parameter specifies the
data type of the expected value, as shown in the following sample BQL query.
PXSelect<TreeViewItem,
Where<TreeViewItem.parentID, Equal<Argument<int?>>>,
OrderBy<Asc<TreeViewItem.parentID>>> GridDataSource;
: Acumatica Framework translates the previous BQL query to the following SQL statement. In this
SQL query, [parameter] will contain the value received from the UI control and passed to the
Select() method; [list of columns] is the list of columns of the TreeViewItem table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query.
For details on which columns are enumerated, see Translation of a BQL Command to SQL.
2. Define the data view delegate with the parameters through which you can access the values
passed from the UI. (You can find more information on how to use data view delegates in
#unique_124/unique_124_Connect_42_optional_method.)
: When a BQL statement with the Argument parameter is executed in code, the value must be
specified in the parameters of the Select() method.
| Accessing Data | 131
1. Compose the expression by using arithmetic operations. For example, you can calculate product
reorder discrepancy by using the following BQL expression, where the decimal_0 constant
represents the 0 decimal value. IsNull returns the first argument if it is not null or the second
argument otherwise.
Minus<
Sub<Sub<IsNull<Product.availQty, decimal_0>,
IsNull<Product.bookedQty, decimal_0>>,
Product.minAvailQty>>
: Acumatica Framework translates the previous BQL statement to the following SQL query.
2. Use the calculated expression in an attribute (such as PXDBCalced) to define a calculated field
that is not bound to a database column, as shown in the following sample code.
1. Compose the expression by using arithmetic operations. For example, you can calculate product
reorder discrepancy by using the following BQL expression, where the decimal_0 constant
represents the 0 decimal value. IsNull returns the first argument if it is not null or the second
argument otherwise.
Minus<
Sub<Sub<IsNull<Product.availQty, decimal_0>,
IsNull<Product.bookedQty, decimal_0>>,
Product.minAvailQty>>
: Acumatica Framework translates the previous BQL statement to the following SQL query.
2. Use the calculated expression in a BQL statement, as shown in the following example.
PXSelect<Product,
Where<Minus<
Sub<Sub<IsNull<Product.availQty, decimal_0>,
IsNull<Product.bookedQty, decimal_0>>,
| Accessing Data | 132
Product.minAvailQty>>,
NotEqual<decimal_0>>>
: Acumatica Framework translates the previous BQL statement to the following SQL query, where
[list of columns] is the list of columns of the Product table.
Acumatica Framework explicitly enumerates the columns of the database table in the SQL query.
For details on which columns are enumerated, see Translation of a BQL Command to SQL.
2. Replace the names of columns with the names of class fields that correspond to the columns in
data access classes (DACs). That is, change the uppercase letter in the name of each column
to the lowercase, as shown in the following sample code. In this sample code, the changes are
shown in bold type.
3. If your SQL statement contains constants, replace it with either one of the predefined constants
or your own constant. (For details on using constants, see To Compare a Field with a Constant
and the description of the Constant<ConstType> class.) If you need to change the values of the
constants at runtime, replace the constants with parameters, as described in To Use Parameters
in Traditional BQL.
4. Find the JOIN, WHERE, GROUP BY, and ORDER BY clauses that you have in the SQL statement.
Depending on the included clauses, select one of the PXSelect classes, and replace SELECT *
FROM with this class in your SQL statement. For details on selection of the PXSelect class, see
To Select Records By Using Traditional BQL. For the list of all PXSelect classes, see PXSelect
Classes.
In the sample code that has been presented in this topic, you would use the
PXSelectJoin<Table, Join, Where, OrderBy> class, and you would change the sample code
as follows. (The changes are shown in bold type.)
PXSelectJoin<Product,
INNER JOIN SupplierProduct
ON SupplierProduct.productID = Product.productID
INNER JOIN Supplier
ON Supplier.accountID = SupplierProduct.accountID,
WHERE (Product.bookedQty IS NOT NULL
AND Product.availQty IS NOT NULL
AND Product.minAvailQty IS NOT NULL
AND(Product.active = 1
OR Product.active IS NULL)
AND(Product.bookedQty > Product.availQty
OR Product.availQty < Product.minAvailQty))
OR Product.availQty IS NOT NULL,
ORDER BY Product.unitPrice, Product.availQty DESC>
a. Replace the last JOIN clause with the corresponding BQL Join clause. You would change
the sample code of this topic as follows. (The changes are shown in bold type.)
PXSelectJoin<Product,
INNER JOIN SupplierProduct
ON SupplierProduct.productID = Product.productID
InnerJoin<Supplier,
ON Supplier.accountID = SupplierProduct.accountID>,
WHERE (Product.bookedQty IS NOT NULL
AND Product.availQty IS NOT NULL
AND Product.minAvailQty IS NOT NULL
AND(Product.active = 1
OR Product.active IS NULL)
AND(Product.bookedQty > Product.availQty
OR Product.availQty < Product.minAvailQty))
OR Product.availQty IS NOT NULL,
ORDER BY Product.unitPrice, Product.availQty DESC>
b. Chain other JOIN clauses to one another, as described in To Query Multiple Tables. You
would change the sample code of this topic as follows. (The changes are shown in bold
type.)
PXSelectJoin<Product,
InnerJoin<SupplierProduct,
ON SupplierProduct.productID = Product.productID,
InnerJoin<Supplier,
ON Supplier.accountID = SupplierProduct.accountID>>,
WHERE (Product.bookedQty IS NOT NULL
AND Product.availQty IS NOT NULL
AND Product.minAvailQty IS NOT NULL
AND(Product.active = 1
OR Product.active IS NULL)
| Accessing Data | 134
• For a single condition or groups that start with a simple condition, replace the ON
clause with On.
• For groups that start with a group of conditions, replace the ON clause with On2.
With these replacements, the sample code used in this topic would be changed to the
following code. (The changes are shown in bold type.)
PXSelectJoin<Product,
InnerJoin<SupplierProduct,
On<SupplierProduct.productID = Product.productID>,
InnerJoin<Supplier,
On<Supplier.accountID = SupplierProduct.accountID>>>,
WHERE (Product.bookedQty IS NOT NULL
AND Product.availQty IS NOT NULL
AND Product.minAvailQty IS NOT NULL
AND(Product.active = 1
OR Product.active IS NULL)
AND(Product.bookedQty > Product.availQty
OR Product.availQty < Product.minAvailQty))
OR Product.availQty IS NOT NULL,
ORDER BY Product.unitPrice, Product.availQty DESC>
6. If your SQL statement includes a WHERE clause, replace the WHERE clause and each pair of
parentheses that encloses each group of conditions in the WHERE clause with a Where, Where2,
Not, or Not2 clause, as follows:
• Not is used for groups that start with a simple condition but are preceded with the logical
NOT.
• Where2 is used for groups that start with a group of conditions.
• Not2 is used for groups that start with a group of conditions but preceded with the logical
NOT.
With these replacements, the sample code used in this topic would be changed to the following
code. (The changes are shown in bold type.)
PXSelectJoin<Product,
InnerJoin<SupplierProduct,
On<SupplierProduct.productID = Product.productID>,
InnerJoin<Supplier,
On<Supplier.accountID = SupplierProduct.accountID>>>,
Where2<Where<Product.bookedQty, IS NOT NULL
AND Product.availQty IS NOT NULL
AND Product.minAvailQty IS NOT NULL
AND Where<Product.active = 1,
OR Product.active IS NULL>
AND Where<Product.bookedQty > Product.availQty,
OR Product.availQty < Product.minAvailQty>>,
OR Product.availQty IS NOT NULL>,
OrderBy<Asc<Product.unitPrice, Desc<Product.availQty>>>>
7. In each BQL Where or On clause, replace the logical operators (either AND or OR) to And, Or,
And2, or Or2, as follows:
| Accessing Data | 135
a. Replace the last AND or OR in each BQL Where or On clause with the And or Or operator,
respectively, as shown in the following code. (The changes are shown in bold type.)
PXSelectJoin<Product,
InnerJoin<SupplierProduct,
On<SupplierProduct.productID = Product.productID>,
InnerJoin<Supplier,
On<Supplier.accountID = SupplierProduct.accountID>>>,
Where2<Where<Product.bookedQty, IS NOT NULL
AND Product.availQty IS NOT NULL
AND Product.minAvailQty IS NOT NULL
AND Where<Product.active = 1,
Or<Product.active IS NULL>>
And<Where<Product.bookedQty > Product.availQty,
Or<Product.availQty < Product.minAvailQty>>>>,
Or<Product.availQty IS NOT NULL>>,
OrderBy<Asc<Product.unitPrice, Desc<Product.availQty>>>>
b. In each BQL Where or On clause, if the AND or OR is located before a simple condition,
replace it with And or Or, respectively. If the condition is preceded by NOT, wrap it in Not.
With these replacements, the sample code used in this topic would be changed to the
following code. (The changes are shown in bold type.)
PXSelectJoin<Product,
InnerJoin<SupplierProduct,
On<SupplierProduct.productID = Product.productID>,
InnerJoin<Supplier,
On<Supplier.accountID = SupplierProduct.accountID>>>,
Where2<Where<Product.bookedQty, IS NOT NULL,
And<Product.availQty IS NOT NULL,
And<Product.minAvailQty IS NOT NULL,
AND Where<Product.active = 1,
Or<Product.active IS NULL>>
And<Where<Product.bookedQty > Product.availQty,
Or<Product.availQty < Product.minAvailQty>>>>>>,
Or<Product.availQty IS NOT NULL>>,
OrderBy<Asc<Product.unitPrice, Desc<Product.availQty>>>>
c. In each BQL Where or On clause, if the AND or OR is located before a group of conditions,
replace it with And2<Operator, NextOperator> or Or2<Operator, NextOperator>,
respectively. The first parameter in a logical operator is Where (or Where2). If the
condition is preceded by NOT, place Not before a group in a Where clause. The following
sample code implements these changes (shown in bold type).
PXSelectJoin<Product,
InnerJoin<SupplierProduct,
On<SupplierProduct.productID = Product.productID>,
InnerJoin<Supplier,
On<Supplier.accountID = SupplierProduct.accountID>>>,
Where2<Where<Product.bookedQty, IS NOT NULL,
And<Product.availQty IS NOT NULL,
And<Product.minAvailQty IS NOT NULL,
And2<Where<Product.active = 1,
Or<Product.active IS NULL>>,
And<Where<Product.bookedQty > Product.availQty,
Or<Product.availQty < Product.minAvailQty>>>>>>>,
Or<Product.availQty IS NOT NULL>>,
OrderBy<Asc<Product.unitPrice, Desc<Product.availQty>>>>
8. In each Where or On clause, replace the groups that use arithmetic operations with the
corresponding BQL operators, as described in To Use Arithmetic Operations.
| Accessing Data | 136
9. In each Where or On clause, replace each comparison with the corresponding comparison
operator, such as Equal, Greater, or IsNull. For more information on constructing comparisons,
see To Filter Records.
The following sample code includes these changes (shown in bold type).
PXSelectJoin<Product,
InnerJoin<SupplierProduct,
On<SupplierProduct.productID, Equal<Product.productID>>,
InnerJoin<Supplier,
On<Supplier.accountID, Equal<SupplierProduct.accountID>>>>,
Where2<Where<Product.bookedQty, IsNotNull,
And<Product.availQty, IsNotNull,
And<Product.minAvailQty, IsNotNull,
And2<Where<Product.active, Equal<True>,
Or<Product.active, IsNull>>,
And<Where<Product.bookedQty, Greater<Product.availQty>,
Or<Product.availQty, Less<Product.minAvailQty>>>>>>>>,
Or<Product.availQty, IsNotNull>>,
OrderBy<Asc<Product.unitPrice, Desc<Product.availQty>>>>
10. Align logical operators of the same level so that they have the same indentation and so that
each simple condition is placed on a separate line. Do not add line breaks before nested Where
clauses.
11. If your SQL statement includes the GROUP BY clause, do the following:
b. Chain the GroupBy clause and aggregation functions (such as Min, Max, Sum, Avg, and
Count) to one another as described in To Group and Aggregate Records.
12. If your SQL statement includes the ORDER BY clause, do the following:
a. Replace the ORDER BY clause with the OrderBy clause. The following sample code shows
this change (with changes shown in bold type).
PXSelectJoin<Product,
InnerJoin<SupplierProduct,
On<SupplierProduct.productID, Equal<Product.productID>>,
InnerJoin<Supplier,
On<Supplier.accountID, Equal<SupplierProduct.accountID>>>>,
Where2<Where<Product.bookedQty, IsNotNull,
And<Product.availQty, IsNotNull,
And<Product.minAvailQty, IsNotNull,
And2<Where<Product.active, Equal<True>,
Or<Product.active, IsNull>>,
And<Where<Product.bookedQty, Greater<Product.availQty>,
Or<Product.availQty, Less<Product.minAvailQty>>>>>>>>,
Or<Product.availQty, IsNotNull>>,
OrderBy<Product.unitPrice, Product.availQty DESC>>
b. Chain the Asc and Desc operators to one another, as described in To Order Records. The
following sample code shows this change (with changes shown in bold type).
PXSelectJoin<Product,
InnerJoin<SupplierProduct,
On<SupplierProduct.productID, Equal<Product.productID>>,
InnerJoin<Supplier,
On<Supplier.accountID, Equal<SupplierProduct.accountID>>>>,
Where2<Where<Product.bookedQty, IsNotNull,
And<Product.availQty, IsNotNull,
And<Product.minAvailQty, IsNotNull,
And2<Where<Product.active, Equal<True>,
Or<Product.active, IsNull>>,
And<Where<Product.bookedQty, Greater<Product.availQty>,
| Accessing Data | 137
Or<Product.availQty, Less<Product.minAvailQty>>>>>>>>,
Or<Product.availQty, IsNotNull>>,
OrderBy<Asc<Product.unitPrice, Desc<Product.availQty>>>>
13. Check that the final statement is correct by doing the following:
• Check that all lines except the last line of the BQL statement end with a comma.
• Ensure that the number of closing angle brackets equals the number of opening angle
brackets.
In This Chapter
Query Syntax
To configure a LINQ query, you can use the following variants of syntax:
• Query expressions, which use standard query operators from the System.Linq namespace
(such as where or orderby) or Acumatica Framework-specific operators from the
PX.Data.SQLTree namespace (such as SQL.BinaryLen, which is shown in the following
example of this syntax).
p.ProductCD.Length == 5 &&
p.GroupMask.Length == 4 &&
(p.WorkGroupID & 0b10) != 0
select new
{
p.ProductID,
p.ProductCD,
p.ProductName,
Len = p.ProductName.Length,
BLen = SQL.BinaryLen( p.ProductName) + 1,
p.GroupMask,
p.WorkGroupID
};
• Explicit (method-based) syntax. The arguments of the methods used in this syntax are lambda
expressions. In these expressions, you can use the standard C# operators and Acumatica
Framework-specific operators from the PX.Data.SQLTree namespace (such as SQL.BinaryLen,
which is shown in the following code). The code below is equivalent to the query expression
shown above.
For details about composing LINQ queries, see To Select Records by Using LINQ. In the code examples
of this guide, we use explicit syntax.
//BQL statement
var Products = new PXSelect<Product,
Where<Product.productCD, Like<string_D>>>(graph);
//Use of LINQ for the result of the BQL query
var goods = Products.Select()
.Where(p => p.GetItem<Product>().StockUnit == "item");
//Execution of the query
foreach (var good in goods) {
var prod = good.GetItem<Product>();
}
However, you cannot work with the query defined with LINQ by using BQL.
For details about how to use LINQ and BQL simultaneously, see To Append LINQ Expressions to BQL
Statements.
| Accessing Data | 139
• Call the ToList or ToArray method for the query, as shown in the following code.
• Iterate the query by using the foreach statement, as shown in the following code.
You also can specify that the query should not be merged with PXCache by using the ReadOnly method,
as shown in the following code example.
then, in memory, orders the retrieved records by the Date column and selects the records that satisfy
the condition specified by using the MyHelpers.IsHighPriority function.
: If the system falls back to LINQ to Objects, only the results of the base PXSelectBase query are
merged with PXCache as described in Merge of the Records with PXCache. The Merge and ReadOnly
methods do not affect the merge of records with PXCache for the queries that caused fallback.
The LINQ fallback is supported in Acumatica Framework for compatibility with previous versions. The
system writes to the trace log about all situations in which the system falls back to LINQ to Objects
mode. Therefore, we strongly recommend that you investigate the trace log for such issues and fix the
issues in one of the following ways:
• Remove the custom C# functions that cause fallback so that the full query is executed in the
database.
• Append the AsEnumerable() method to the part of the query that can be converted to SQL, and
add after it the conditions that include custom C# functions. In this case, the system does not
waste resources trying to build the SQL query tree for the whole query. Instead, the system builds
the SQL query tree for the part of the query that has AsEnumerable() appended and performs
the corresponding request to the database, while the custom C# conditions of the query are
processed in memory. For example, the code example above can be modified as follows.
: The system executes the following SQL query for the code above, where [list of columns] is
the list of columns of the CRCase table.
For details about the execution of LINQ expressions, see Deferred LINQ Query Execution.
using PX.Data;
using PX.Data.SQLTree;
using System.Linq;
• Make sure that the application database has the database tables from which you are going to
request data, and that the application defines the data access classes (DACs) for these tables. For
more information on defining DACs, see Data Access Classes.
To Filter Records
To filter records in the database table to be retrieved, construct the LINQ expression by using the
Where LINQ method and the needed conditions. In the conditions, use the property field defined in the
DAC, such as Product.ProductID.
The following LINQ expression uses the C# logical operators (||, &&, and !) to define multiple
conditions.
: This LINQ expression is equivalent to the following SQL query. In this SQL query, [list of columns]
is the list of columns of the Product table.
To Order Records
To order records to be retrieved from the database table, construct the LINQ expression by using the
OrderBy or OrderByDescending LINQ method and the needed property fields of the DAC, such as
Product.ProductID.
The following sample LINQ expression selects all Product data records and sorts them by the
UnitPrice field in ascending order.
: This LINQ expression is equivalent to the following SQL query, where [list of columns] is the list of
columns of the Product table.
The following sample LINQ expression performs an inner join of the SalesOrder and OrderDetail DACs
by the OrderNbr field.
: This LINQ expression is equivalent to the following SQL query, where [list of columns] is the list of
columns of the Product table.
1. Construct the LINQ expression by using the GroupBy LINQ method and the needed property
fields of the DAC, such as Product.CategoryCD. (The name of the property field starts with an
uppercase letter. Do not confuse it with the class field, which has the same name but starts with
lowercase letter.)
The following sample LINQ expression groups the records of the Product table by the CategoryCD
field.
SELECT Product.CategoryCD
FROM Product
GROUP BY Product.CategoryCD
: The system executes the following SQL query for the code above.
using PX.Data;
using PX.Data.SQLTree;
using System.Linq;
• Make sure that the application database has the database tables from which you are going to
request data, and that the application defines the data access classes (DACs) for these tables. For
more information on defining DACs, see Data Access Classes.
1. Configure a BQL query derived from PXSelectBase either in fluent BQL or in traditional BQL.
2. Call the Select() method of PXSelectBase, and append the LINQ query to the result. Because
the result of the Select() method call is a PXResultset<> object, you need to cast it to a DAC
type by using the PXResult.GetItem<DacType>() method or direct casting.
The following example appends LINQ joining and filtering to a BQL query.
using PX.Data;
using PX.Data.SQLTree;
using System.Linq;
using PX.Data.BQL.Fluent;
: The system executes the following SQL query for the code above. In this SQL query, [list of
columns] is the list of columns of the Product and SupplierProduct tables.
In This Chapter
• To define a primary key of a table, for the set of key fields of the data access class (DAC) that
corresponds to the table, you set the IsKey property of the data type attribute to true.
• To define a foreign key of a table, in the DAC that corresponds to the table, you mark the
field that contains the foreign key with one of the following attributes: PXForeignReference,
PXSelector, or PXParent.
To select a record from the database by its primary or foreign key, you can use a Select statement in
business query language (BQL) or use the methods of the attributes mentioned above.
Another way to define a relationship between two tables is to use the classes and methods that are
specially designed for the definition of primary and foreign keys. This approach, which is described in
this topic, provides the following advantages:
• These classes provide static information that a compiler can use to identify errors in the code.
• You can use runtime information about primary keys to select records by their keys.
• These classes and methods have no other meanings and use cases; conversely, the
PXForeignReference, PXSelector, and PXParent attributes can be used for other purposes.
• These classes and methods are optimized for the selection of records from the database;
therefore, using them improves database access performance on record selection.
using PX.Data.ReferentialIntegrity.Attributes;
You can also select a record by using a record of the same type with the key fields specified, as shown
in the following example.
If a foreign key is defined for a DAC, you can select the parent and child records, as shown in the
following code.
[...]
[PXParent(typeof(SOOrderFK))]
public virtual String OrderNbr { get; set; }
public abstract class orderNbr : PX.Data.IBqlField { }
[...]
[PXForeignReference(typeof(InventoryFK))]
public virtual Int32? InventoryID { get; set; }
public abstract class inventoryID : PX.Data.IBqlField { }
}
Because all primary keys, which are defined as described in this topic, implement the IPrimaryKey
interface, you can use primary and foreign keys in the scope of custom attributes, as shown in the
following example.
using PX.Data.ReferentialIntegrity.Attributes;
2. Use the primary key to select a record, as shown in the following code.
using PX.Data.ReferentialIntegrity.Attributes;
2. Use the compound primary key to select a record, as shown in the following code.
1. In the DAC of the parent table, define the primary key, as described in To Define a Primary Key.
The following code shows an example of the definition of the primary key that is used in the
following code examples in this instruction.
2. In the DAC of the child table, define the foreign key based on the primary key of the parent
table, as shown below.
3. Use the foreign key to select the parent record or child records, as shown in the following code.
In This Chapter
• Session
Session
The Acumatica ERP server creates a separate session for each browser tab or window that opens an
Acumatica ERP form.
The server creates the first session for a user after the user authorization when the starting form is
loading. Then the server does the following:
• Saves the user authorization data (.ASPXAUTH) and the session ID (ASP.NET_SessionID) in the
browser cookies for the website URL
• Creates the shared session data to be used for the Acumatica ERP forms opened in new browser
tabs and windows
• Saves the shared session data in the storage that is specified in the website configuration
When the user opens a form of Acumatica ERP in a new browser tab, the server creates a new session
that is based on the previous session data. To access the shared data, the server uses the session ID
from the cookies, which are added to the request by the browser.
The following diagram shows how the server of Acumatica ERP manages the shared session data that is
used for multiple sessions of a single user.
| Accessing Data | 149
If the session data has been changed during the processing of a request, the server updates the data in
the shared session data store. For example, if the user clicks Copy on a form toolbar to copy the form
data, the data is stored in the shared session, so that it is accessible for the Paste action in another
session of the same user.
To distinguish different sessions that have the same ASP.NET_SessionID, the server adds to each
new session a unique identifier that consists of the W character and a number value wrapped in
parentheses. In the browser, you can see such an identifier in the site URL, as with the bolded part in
the following example: http://localhost/MySite/(W(3))/Main.aspx?ScreenId=AR301000.
The cost of serialization and the amount of data that need to be shared between application servers is
often the main challenge to scaling complex business applications horizontally.
Acumatica Framework implements the following techniques to address issues related to session-state
management without sacrificing performance, fault tolerance, or scalability:
• Objects on the application server are created on each request and disposed after the request
execution. The application state is preserved in the session through the serialization mechanism.
• Data serialized into the session is minimized to store only modified data (inserted, deleted, held
and modified records). (Serialization and retrieval times are directly proportional to the size of the
serialized data.)
• The rest of the data is extracted from the database on demand and built around the session data.
(A custom algorithm that extracts only the data required for the current request execution from
the database is implemented.)
• A custom serialization mechanism is implemented to serialize only relevant data and reduce
the amount of service information. (The standard serialization mechanism implemented in the
Microsoft .NET platform is generic and cannot be optimized when used for a specific task.)
• Hash tables, constraints, relations, and indexes concerned with the execution of business logic are
created strictly on demand. This technique allows the user to avoid execution of these operations
on each request if not needed. (Creation of indexes, constrains, hash tables, and relations
consumes a significant amount of CPU and runtime memory.)
| Accessing Data | 151
• The PXAutoSaveAttribute attribute is defined for a data access class. As a result, the
PXCache<>.Unload method automatically stores in the database all the changes of the appropriate
data records at the end of each round trip.
A graph instance exists on the server only while a user request is being processed, and it is destroyed
right after this processing. The following diagram shows that a graph instance is created to process a
user request on the Acumatica ERP server and destroyed once processing is completed.
In the diagram above, the blue rectangles labeled 1, 2, 3, and 4 indicate the lifetime of graph instances.
After a graph instance completes the processing of a request, the system stores the graph state in the
session. It also stores the inserted, deleted, held, and modified records of the cache that are required
to restore the state and data of the graph for the processing of the subsequent user request on the
same Acumatica ERP form.
: On a stand-alone Acumatica ERP server, session data is stored in the server memory. In a cluster of
Acumatica ERP servers, session data must be serialized and stored in external high-performance session
state storage. (For more information on storing session data in a cluster, see Session Sharing Between
Application Servers.)
For a user request on an Acumatica ERP form, the following operations are executed in the system:
1. The application server creates a graph instance that is specified in the TypeName property of the
PXDataSource control of the form. (For more information about the initialization of graph views,
caches, actions, and event handlers, see Initialization of an Event Handler Collection.)
| Accessing Data | 152
2. If the user session contains graph data that has been stored during a previous request, the
system loads the graph state and the cache data from the session.
3. The graph instance processes the requested data on the data view that is specified in the ASPX
code in the DataMember property of the control container for the data to be processed. To
process the data, the system invokes the ExecuteSelect, ExecuteInsert, ExecuteUpdate,
or ExecuteDelete method of the graph, based on the request type. The invoked method
implements the logic of the appropriate scenario to add the request data to the cache and to
execute the event handlers defined for the data fields and records in the cache. (See Data
Manipulation Scenarios for details.) The cache then merges the data retrieved from the database
with the data restored from the session, and the application accesses the data as if the entire
data set had been preserved from the time of the previous request.
4. The graph instance returns the request results to the PXDatasource control of the form.
5. The system stores in the session the graph state and the modified data of the cache.
: Because the graph instance is no longer being used by the application server, the .NET
Framework garbage collector then clears the memory allocated for the graph instance.
While a graph is instantiated, all the cached data of the graph is saved in the appropriate PXCache
objects that are created in the graph instance based on the data access class (DAC) declarations. To
preserve the modified entity data between user requests, the cache controller saves the Updated,
Inserted, Deleted, and Held collections of each PXCache object in the session.
The following diagram shows how the graph state and cache data are stored in the Session object.
Method Description
public static ObjectType Stores the specified data object under the key that is created on
SetSlot<ObjectType> the base of the object type.
(ObjectType value)
public static ObjectType Stores the specified data object under the key that is defined by
SetSlot<ObjectType> the first parameter.
(string key, ObjectType
value)
The following example shows how you can save the MyData object in the slot of the current HTTP
context under the key that is the same as the object type.
PXContext.SetSlot<MyDataType>(MyData);
To get a data object that is cached in the current HTTP context, you can use the following methods of
the PXContext class.
Method Description
public static ObjectType Returns the data object that is cached under the key that is created
GetSlot<ObjectType>() on the base of the object type.
| Accessing Data | 154
Method Description
public static ObjectType Returns the data object that is cached under the specified key.
GetSlot<ObjectType>
(string key)
The following example shows how you can get from the slot of the current HTTP context the MyData
object that is cached under the MyData22 key.
The following diagram illustrates how you can use a data object cached by using a slot provided by the
PXContext class.
You do not need to delete the data saved in the PXContext class slots, because the system deletes
these slots from the server memory along with the data of the current HTTP context created for the
current request.
Method Description
public static ObjectType If the PXDatabase slots contain a valid data object of the specified
GetSlot<ObjectType> type saved under the key defined by the first parameter, returns
(string key, params this data object. Otherwise, the method creates a new object of the
Type[] tables) specified type, saves this empty object in the slot under the key
defined by the first parameter, and returns the data object that is
used by the calling code to save the needed data. The list of the
table types specified in the params parameter is used to invalidate
the slot if any table of the list has been changed in the database.
| Accessing Data | 155
Method Description
: If this method is used to cache a data object of an ObjectType
class inherited from the IPrefetchable<> interface, the
GetSlot<> method invokes the Prefetch method of the object
without a parameter.
public static ObjectType Is used for caching a data object of an ObjectType class inherited
GetSlot<ObjectType, from the IPrefetchable<> interface to provide automatic update
Parameter> (string key, of the object in the slot. If the PXDatabase slots contain a valid
Parameter parameter, data object of the specified type saved under the key defined by
params Type[] tables) the first parameter, the method returns this data object. Otherwise,
the method does the following:
3. Saves this object in the slot under the key defined by the
first parameter
The list of the table types specified in the params parameter is used
to invalidate the slot in the case if any table of the list has been
changed in the database. The use of this method is described below
in the Automatically Updating Data in a PXDatabase Slot section.
The following example shows how you can use the GetSlot<ObjectType> (string key, params
Type[] tables) method to cache data under the MyData key in the slot of the PXDatabase class.
...
Dictionary<string, string[]> dict =
PXDatabase.GetSlot<Dictionary<string, string[]>>(
"MyData", typeof(Table1), typeof(Table2), typeof(Table3));
lock (((System.Collections.ICollection)dict).SyncRoot)
{
...
List<string> myList = new List<string>();
...
string key = "myListKey";
dict[key] = myList.ToArray();
}
...
After the data object has been cached, you can access the object by using the following instruction.
You can clear a slot provided by the PXDatabase class by means of the following public static methods
of the class.
Method Description
public static void Sets to null the value of the slot that has the specified key.
ResetSlot<ObjectType>
(string key, params
Type[] tables)
| Accessing Data | 156
Method Description
public static void Sets to null the value of each slot that is provided by the
ResetSlots() PXDatabase class.
The following example shows how you can clear the slot created in the example above.
PXDatabase.ResetSlot<MyDataType>(
"MyData", typeof(Table1), typeof(Table2), typeof(Table3));
// Here you need to add the code for all the methods that are defined
// in the MyProvider abstract class.
// These methods can be used to manage the MyData object.
...
}
For the code above, the following diagram shows how the data object is cached and automatically
updated in the PXDatabase slot.
: If you have discovered that a PXDatabase slot returns legacy data, you can invoke the
SelectTimeStamp() public static method of the PXDatabase class to invalidate all the PXDatabase
slots that contain data obtained from the database tables that have been changed. Then the GetSlot
method invokes the Prefetch method and updates the data in the slot.
| Implementing Business Logic | 158
In This Part
• Localizing Applications
In This Chapter
• Event Handlers
• List of Events
Event Handlers
The Acumatica Framework raises events in the context of a graph. An event handler can be
implemented in a graph, as well as in an attribute of a data field.
| Implementing Business Logic | 159
event handler, you invoke the AddHandler<>() method on the corresponding collection. For example,
if the event is related to a row, it is invoked as follows.
RowEventName.AddHandler<DACName>(MethodName);
FieldEventName.AddHandler<DACName.fieldName>(MethodName);
When the AddHandler<>() method is invoked, event handlers are added to the collection as follows:
• Event handlers are added to the beginning of the collection for any event whose name ends with
ing except the RowSelecting event.
• Event handlers are added to the end of the collection for any event whose name ends with ed and
for the RowSelecting event.
1. Creates the Cashes, Views, and Actions collections and other required collections. All of these
collections are initially empty.
2. If the graph instance is being created on the Acumatica ERP server for the first time:
a. Obtains the metadata of this graph from the appropriate assembly (which is PX.Objects
for most graphs in the application).
The ProcessMethods method processes the metadata of the methods that are declared
in the graph and all extensions of the graph. According to the naming convention for
event handlers, the _ symbol is a separator, so this method tries to split the name of each
processed method into segments. If the name of the processed method has fewer than two
segments or more than three segments, the processed method is skipped.
If the name of the processed method adheres to the naming convention for record
event handlers, the processed method is added to the _EventsRow collection of the
PXCache<DACName> object that is instantiated in the graph instance based on the DAC
declaration. For example, the SOOrder_RowSelected event handler is added to the
_EventsRow collection of the PXCache<SOOrder> cache object as an element with the
RowSelected key.
If the name of the processed method adheres to the naming convention for field event
handlers, the processed method is added to the EventNameEvents collection of the
PXCache<DACName> object. For example, the SOOrder_CustomerID_FieldUpdated
event handler is added to the FieldUpdatedEvents collection of the
PXCache<SOOrder> cache object as an element with the CustomerID key.
c. Saves the graph metadata and the InitializeDelegate emitted method in the
Acumatica ERP server memory as the GraphStaticInfo static object shared for the entire
application instance.
| Implementing Business Logic | 161
3. From the GraphStaticInfo static object, invokes the InitializeDelegate method, which
initializes graph views, caches, and actions; the method also adds event handler delegates to the
appropriate event handler collections of the relevant PXCache objects.
The following diagram shows how an instance of the PX.Objects.SO.SOOrderEntry graph uses the
PX.Objects assembly metadata to add the SOOrder_CustomerID_FieldUpdated()
event handler (described in the graph and graph extensions) to the FieldUpdatedEvents collection of
the PXCache<SOOrder> cache object.
In the collection, the CustomerID field name is used as a key, and the delegate of the event handler
sequence for the field processing is used as a value.
Figure: Use of event handlers while the basic data operations are processed
For details on how Acumatica Framework processes the basic data operations, see the following topics:
• Sequence of Events: Insertion of a Data Record
• Sequence of Events: Update of a Data Record
• Sequence of Events: Deletion of a Data Record
• Sequence of Events: Display of a Data Record
• Sequence of Events: Saving of Changes to the Database
The system inserts a data record—as an instance of a data access class (DAC)—when a user creates
a new data record in the user interface, a request to insert a record is sent to the web services API,
or the Insert() method of a data view is called in code. The data record is actually inserted into
the PXCache object that corresponds to the DAC of the data record. The inserted data record has the
Inserted status and is available through the Inserted and Dirty collections of the PXCache object.
| Implementing Business Logic | 164
When a data record is inserted, data field events are raised for each data field in the following order:
1. FieldDefaulting
2. FieldUpdating if the e.Cancel property equals true
3. FieldVerifying
4. FieldUpdated
Next, the following data record events are raised:
1. RowInserting (If the e.Cancel property is true, no further events are raised.)
2. RowSelected
3. RowInserted
The instance of the inserted data record is available in the e.Row property of event arguments.
A data record is updated when a user modifies the data record on the user interface, the request is
sent through the Web Service API, or the Update() method is invoked on the data view. Updated data
records, which the system gives the Updated status, are later available through the Updated and Dirty
collections of the appropriate PXCache object.
| Implementing Business Logic | 166
The RowUpdating event is fired before the update happens, while the RowUpdated event is fired after
the update. The developer can handle these events and has access to the updated data record and
the previous version of the data record that is kept in the PXCache object. The actual update happens
between these two events when the data record is copied to the PXCache object.
When a data record is updated, the following data field events are raised for each updated data field:
1. FieldUpdating
2. FieldVerifying
3. FieldUpdated
Next, data record events are raised as follows:
1. RowUpdating is raised. At this moment, in the e variable, which represents event data, e.Row
holds the data record version from the cache, while e.NewRow holds the updated data record.
You can still stop the update by throwing a PXException instance.
a. RowSelected is raised. Only the updated data record can be accessed through e.Row.
b. RowUpdated is raised. e.Row now holds the updated instance, while e.OldRow holds a
copy of the old data record with the previous values.
A data record is deleted when a user deletes the record on the user interface, the deletion request is
sent through the Web Service API, or the Delete() method of a data view is invoked in code. As a
result of the deletion, the data record gets the Deleted status if it already exists in the database, or
the InsertedDeleted status if the record has just been inserted into the PXCache object and deletion
| Implementing Business Logic | 168
from the database is not required. The data record is later available through the Deleted and Dirty
collections of the PXCache object.
If the deletion has been initiated by a user on the UI or through the web services API, the following field
events are raised for each key data field before any other events are raised:
1. FieldUpdating
2. FieldUpdated
Next, regardless of how the deletion was initiated, data record events are raised as follows:
1. RowDeleting is raised. At this point, the developer can still stop the deletion by throwing a
PXException instance. In the e variable representing event data, e.Row holds the data record
being deleted.
While a user is inserting, updating, or deleting a data record, no changes are committed to the
database. The system stores the modified data records in the session, and you can access them
through the appropriate PXCache object. The system commits the changes to the database when
the user clicks Save in the user interface, the save request is sent through the Web Service API, or
Actions.PressSave() is invoked on the business logic controller (BLC) instance. In both cases,
the Persist() method of the graph is invoked. The Actions.PressSave() method additionally checks
that the Save action exists in the graph and is enabled. The Save action then invokes the Persist()
method.
When changes are saved to the database, events are raised as follows:
| Implementing Business Logic | 170
1. RowPersisting is raised. At this moment, a database transaction has already been opened.
If any of the handlers sets e.Cancel to true, the process will be canceled for the currently
processed data record without an error being reported to the user. To cancel the process of
committing changes and indicate the error to the user, you should throw the PXException
exception.
a. RowPersisted is raised. The commit operation for the current data record (available
through e.Row in the handler) is completed, but the transaction is still open:
e.TranStatus equals Open.
b. RowPersisted is raised one more time, either with e.TranStatus equal to
Completed (if all changes have been saved successfully) or with e.TranStatus equal
to Aborted if an error has occurred and all changes have been canceled.
List of Events
In this topic, you can find the list of all events by category. You can get more information about any of
these events by navigating to the applicable topic in the API Reference.
• PXFieldDefaulting
• PXFieldVerifying
• PXFieldUpdating
• PXFieldUpdated
• PXFieldSelecting
• PXRowSelected
• PXRowInserting
• PXRowInserted
• PXRowUpdating
• PXRowUpdated
• PXRowDeleting
• PXRowDeleted
Database-Related Events
• PXCommandPreparing
• PXRowSelecting
• PXRowPersisting
• PXRowPersisted
Exception-Handling Event
• PXExceptionHandling
| Implementing Business Logic | 171
• CacheAttached
In This Chapter
• Mandatory Attributes
• Use of Attributes
• UI Field Configuration
• Default Values
• Audit Fields
• Data Projection
• Access Control
• Notes
• Report Optimization
• Attributes on DACs
• Action Attributes
{
DocTransaction old = e.OldRow as DocTransaction;
DocTransaction trn = e.Row as DocTransaction;
if ((trn != null) && (trn.TranQty != old.TranQty ||
trn.UnitPrice != old.UnitPrice))
{
Document doc = Receipts.Current;
if (doc != null)
{
doc.TotalAmt -= old.TranQty * old.UnitPrice;
doc.TotalAmt += trn.TranQty * trn.UnitPrice;
Receipts.Update(doc);
}
}
}
This logic can be used in multiple forms of the application, and therefore can be moved into an
Attribute class. The attribute is used to annotate a data field in the data access class. Then it can be
reused anywhere in the code, as in the example below.
In this example, the logic of updating the receipt total on an update of the transaction is implemented
inside the DeltaMultiply attribute. This logic is triggered after each update, delete, or insert
operation on the DocTransaction data access class instance and updates totals on the receipt level
in the appropriate Document data access class instance.
Acumatica Framework provides a wide range of predefined attributes that can be used for defining data
types, database mapping, referential integrity, data format validation, and default values for the field.
The following code shows an example of how you can implement the logic from the above example by
using the predefined PXFormula attribute, which is used for implementing calculations of data fields.
Because the data access classes are shared within an application, formatting, custom logic, and any
constraints implemented in attributes are reused in each business logic controller that utilizes each data
| Implementing Business Logic | 173
access class. By reusing code through attributes, you can move shared application functionality into
attributes and avoid code duplication, while still enforcing application integrity.
Mandatory Attributes
In this topic, you can learn about the mandatory attributes of data access class (DAC) fields and
actions.
• A data type attribute, which is either a bound field data type attribute that binds the field to a
database column of a particular data type, or an unbound field data type attribute that indicates
that the field is unbound. For lists of these attributes, see Bound Field Data Types and Unbound
Field Data Types.
• The PXUIField attribute, which is mandatory for all fields that are displayed in the user interface.
For details on the PXUIField attribute, see UI Field Configuration.
The example below demonstrates a declaration of a DAC field bound to a database column and
displayed in the user interface.
Use of Attributes
To apply the attribute business logic to an entity, you should place the attribute on the entity
declaration. At run time, you can call the static methods of a particular attribute to adjust the
attribute's behavior.
Here the PXDefault attribute is created with the constructor that takes a Boolean-type parameter (set
to false). Additionally, the PersistingCheck property is specified.
When calling such a method, you typically specify the cache object, a data record related to this cache
object, and the data access class (DAC) field. The method affects the attribute instance created for this
field and the specified data record. If you pass null as the data record, the method affects attribute
instances related to all data records in the specified cache object.
Acumatica Framework also includes other attributes that are used in special cases to bind a DAC field to
database columns.
PXTimeSpan int? Date and time value represented by minutes passed from
01/01/1900
UI Field Configuration
By using the PXUIField attribute, you can configure the layout of input controls and buttons. The
attribute is mandatory for all data access class (DAC) fields displayed in the user interface.
• To a DAC field declaration to configure the field input control, as shown in the following example
[PXDBDate()]
[PXUIField(DisplayName = "Pay Date")]
public virtual DateTime? PayDate { get; set; }
• To the declaration of a method that implements an action to configure the action button, as shown
in the following sample code
The attribute's properties determine the control layout in the user interface. You can specify the display
name, specify whether the control is visible and available, set the error marker, and specify the access
rights to view and use the control.
| Implementing Business Logic | 177
If you want to modify the Visible, Enabled, and Required properties for all detail rows in a grid, you
use the RowSelected event handler of the primary view DAC. If you want to set the Enabled property
of a field in particular row in a grid, you use the RowSelected event handler of the DAC that includes
this field.
If the grid column layout is configured at run time, you set the data parameter of the corresponding
method to null. This indicates that the property should be set for all data records shown in the grid. If
a specific data record is passed to the method rather than null, the method invocation has no effect.
: If you want to change the Visible or Enabled property of PXUIFieldAttribute for a button at
run time, you use the corresponding static methods of PXAction. You usually use these methods in the
RowSelected event handler of the primary view DAC.
Default Values
You can set the default values to data access class (DAC) fields by using the following attributes:
• PXDefault: This attribute sets the default value and validates the field value when the value is
saved to the database. The following attributes are derived from the PXDefault attribute:
• PXUnboundDefault behaves in the same way as PXDefault does, but the default value is
assigned to the field when a data record is retrieved from the database.
• PXDefaultValidate.
• PXDBDefault: This attribute sets the default value by using the value of some source field, and
updates the value if the source field value changes in the database before the data record is
saved.
PXDefault Attribute
The PXDefault attribute provides the default value for a DAC field. The default value is assigned to the
field when the cache raises the FieldDefaulting event. This happens when a new row is inserted in
code or through the user interface.
A value specified as a default can be a constant or the result of a BQL query. If you provide a BQL
query, the attribute executes it on the FieldDefaulting event. You can specify both a constant and
a BQL query; the attribute first executes the BQL query and then uses the constant if the BQL query
returns an empty set. If you provide a DAC field as the BQL query, the attribute retrieves the value of
this field from the Current property of the cache object. The attribute uses the cache object of the DAC
type in which the field is defined.
The PXDefault attribute also checks that the field value is not null before saving a record to the
database. You can adjust this behavior by using the PersistingCheck property. Its value indicates
whether the attribute should verify that the value is not null, verify that the value is not null or a
blank string, or not perform any verification.
The attribute can redirect the error that happened on the field to another field if you set the
MapErrorTo property.
You can use the static methods of the attribute to change the attribute properties for a particular data
record in the cache or for all data records in the cache.
| Implementing Business Logic | 178
Differences
You usually set the default value to a DAC field by using the PXDefault attribute. You can set a
constant as the default value or provide a BQL query to obtain a value from the database or data
records from the cache. The default value is assigned to the field when a data record that includes this
field is inserted into the cache.
You can use the PXDefault attribute just to make the field mandatory for input by using the attribute
without parameters.
The PXDefault attribute is not suitable when the default value is retrieved from a field that can
be auto-generated by the database (such as the identity field). In this case, you should use the
PXDBDefault attribute. It updates the value assigned to the field as the default with the value
generated by the database.
For example, if you implement a master-detail relationship, you should use the PXDBDefault attribute
to bind the detail data record fields to the master data record key fields. If the master data record is
new, its identity field is set to a real value by the database when the master record is saved. So if a
detail data record is created before the master data record is saved, the detail data record field is set
to the temporary value of the master identity field. However, the PXDBDefault attribute replaces the
temporary value with the real value when the detail data record is saved to the database.
You can use the PXUnboundDefault attribute to set the default value to an unbound field. The value is
assigned when a data record is retrieved from the database (on the RowSelecting event).
Drop-Down Lists
You can use the following attributes to configure a drop-down list that represents a data access class
(DAC) field in the user interface:
• PXStringList: Configures a drop-down list from which a user can select from a fixed set of
strings.
• PXIntList: Configures a drop-down list where a user can select from a fixed set of values. The
control displays strings, while the field is assigned the integer value corresponding to the selected
string.
• PXDecimalList: Configures a drop-down list where a user can select from a fixed set of strings
converted to decimal values.
• PXImagesList: Configures a drop-down list where a user can select from a fixed set of images.
• PXDBIntList: Configures a drop-down control for an integer field. The values and labels for the
drop-down control are retrieved from the specified database table.
• PXDBStringList: Configures a drop-down control for a string field. The values and labels for the
drop-down control are retrieved from the specified database table.
Lookup Controls
You can use the following attributes to configure a lookup control that represents a field in the user
interface:
• PXSelector: Defines a lookup control for a DAC field that references a data record from a
particular table by holding its key.
• PXCustomSelector: Serves as the base class to derive custom attributes used to configure lookup
controls.
| Implementing Business Logic | 179
• PXRestrictor: Adds a restriction to a BQL command that selects data for a lookup control, and
displays an error message when the value entered does not fit the restriction. The attribute works
only with PXSelector and cannot be used with PXCustomSelector.
• PXDimension: Defines an input control that formats the input as a segmented key value and
displays the list of allowed values for each key segment.
• PXParent: Creates a reference to a parent data record. By default, when the parent data record is
deleted, all child data records that reference it are also deleted. (You can change this behavior by
using the LeaveChildren property.)
• PXFormula: Calculates a field from other fields of the same data record or sets an aggregation
expression to calculate a parent data record field from its child data record fields. These
calculations happen only when the parent field is changed or a new record is inserted.
• PXUnboundFormula: Calculates the value from the child data record fields and aggregates all such
values computed for the child data records into the parent data record field. These calculations
happen on every data reading.
• PXDBChildIdentity: Indicates that a DAC field references an auto-generated key field from
another table, and ensures that the field value is correct after changes have been committed to
the database.
• PXLineNbr: Generates unique line numbers that identify child data records in the parent-child
relationship.
Note that all the attributes in the list above add server-side logic used at run time. The referential
integrity is implemented on the server side, as are the calculations. See the Ad Hoc SQL for Fields topic
for information about the attributes that enable the calculation of fields on the database side.
• PXDBCalced: Defines an SQL expression that calculates an unbound field from the fields of the
same DAC whose values are taken from the database.
| Implementing Business Logic | 180
• PXDBScalar: Defines an SQL subrequest that retrieves a value for an unbound DAC field. The
subrequest can retrieve data from any bound field in any DAC.
The attributes add the provided expression and the subrequest into the SQL query that selects data
records of the given DAC.
Audit Fields
The following attributes are placed on data access class (DAC) fields used for data audit:
• PXDBLastChangeDateTime: Maps a DAC field to the database column and automatically sets the
field value to the data record's last modification date and time.
• PXDBCreatedByID: Maps a DAC field to a database column and automatically sets the field value
to the ID of the user who created the data record.
• PXDBCreatedByScreenID: Maps a DAC field to a database column and automatically sets the field
value to the string ID of the application screen from which the data record was created.
• PXDBCreatedDateTime: Maps a DAC field to a database column and automatically sets the field
value to the data record's creation date and time.
• PXDBCreatedDateTimeUtc: Maps a DAC field to a database column and automatically sets the
field value to the data record's creation UTC date and time.
• PXDBLastModifiedByID: Maps a DAC field to a database column and automatically sets the field
value to the ID of the user who last modified the data record.
• PXDBLastModifiedDateTime: Maps a DAC field to a database column and automatically sets the
field value to the data record's last modification date and time.
The framework binds the DAC fields to database columns and automatically assigns field values.
Data Projection
The following attributes implement the projection of data from one table or multiple tables into a single
data access class (DAC):
• PXProjection: Binds the DAC to a random data set. The attribute thus defines a named view, but
is implemented by the server side rather than by the database.
• PXExtraKey: Indicates that the field implements a relationship between two tables. The use of
this attribute enables the update of the referenced table when the projection is updated.
Access Control
The group mask value indicates the access rights a user should have in order to use a data record. To
be able to set access rights for particular data records, you should use the PXDBGroupMask attribute to
mark the data access class (DAC) field that holds the group mask value.
On a substitute form, to define the inheritance of access rights for an action that is implemented in the
corresponding entry form, you can use the PXEntryScreenRights attribute.
Notes
By using the PXNote attribute, you can give users the ability to attach text notes, files, and activity
items to data records, and to search for an entity by using the full-text search index.
| Implementing Business Logic | 181
You should use the PXNote attribute in the data access class of these data records to mark the field that
stores the identifier of a note in the Note table. Notes are used to attach text to a data record. This text
is stored in the note data record in the Note table. Additionally, you can attach files or other entities
to a data record through a note. This feature is implemented through additional tables that store the
identifiers of a note and the attached entity.
The PXNote attribute can also be configured to save the specified table fields in a note. In this case,
by using the Acumatica Framework application website search, the user will be able to search the data
records by the values saved in the note.
Report Optimization
The value of an unbound data access class (DAC) field can be calculated in the property getter. The
calculation can involve other fields of the same DAC. However, when the value of the DAC field is
requested, other fields are not guaranteed to be calculated or assigned their values. These situations
are normal when the integration services are used, copy-paste functionality is used, or the field is used
in reports.
To ensure that the fields referenced in the property getter have values when it is executed, you should
use the PXDependsOnFields attribute.
Attributes on DACs
You can place the following attributes on the data access class (DAC) declaration:
• PXPrimaryGraph: Specifies the graph that is used by default to edit a data record.
• PXCacheName: Specifies the user-friendly name of the DAC. The name is displayed in the user
interface.
• PXTable: Binds a DAC that derives from another DAC to the table having the name of the derived
table. Without the attribute, the derived DAC will be bound to the same table as the DAC that
starts the inheritance hierarchy.
• PXAccumulator: Updates the values of a data record in the database according to the policies
specified in the attribute parameters.
• PXHidden: Gives the developer the ability to hide a DAC, graph, or view from the selectors of
DACs and graphs, and from generic inquiries, reports, and the web services API.
The PXProjection and PXTable attributes can also mark a DAC. See Data Projection for more details.
Action Attributes
You use the following attributes to configure a button that will represent an action in the user interface:
• PXButton: Serves as the base attribute for all other attributes, which give you the ability to
configure buttons. The successor attributes only set the base class properties to specific values.
• PXSaveButton
• PXSaveCloseButton
• PXCancelButton
• PXCancelCloseButton
• PXInsertButton
• PXDeleteButton
• PXFirstButton
• PXPreviousButton
• PXNextButton
| Implementing Business Logic | 182
• PXLastButton
• PXSendMailButton
• PXReplyMailButton
• PXForwardMailButton
• PXTemplateMailButton
• PXLookupButton
• PXProcessButton
• PXEntryScreenRights: On a List as Entry Point screen, to define the inheritance of access rights
for an action that is implemented in the appropriate entry screen
• PXFilterable: Adds the control that makes it possible for a user to create filters and save them
in the database. The control is added to the grid that uses the data view to retrieve data.
• PXImport: Adds the grid toobar button that a user clicks to load data from the file to the grid. The
attribute is placed on the data view the grid uses to retrieve the data.
• PXHidden: Hides the data view from the selectors of data access classes (DACs) and graphs, and
from the web service API clients.
• PXCopyPasteHiddenView: Indicates that the cache corresponding to the primary DAC of the data
view is not copied when the copy-paste feature is used on the webpage.
• PXCopyPasteHiddenFields: Indicates that the specific fields of the primary DAC of the data view
are not copied when the copy-paste feature is used on the webpage.
In This Chapter
: The extensions of the files that can be uploaded to webpages must be registered on the File Upload
Preferences (SM202550) form. If the required file types are not registered on this form, you have to add
them and save your changes. On this form, you can also define the maximum size of an uploaded file (in
kilobytes).
1. In the data access class (DAC) that provides data for the webpage, add the NoteID field, as the
following code shows.
#region NoteID
public abstract class noteID : PX.Data.IBqlField { }
[PXNote]
public virtual Guid? NoteID { get; set; }
#endregion
: The database table that corresponds to the DAC must contain the NoteID column with the
uniqueidentifier data type.
3. Optional: To change whether each of these elements is displayed on the title bar, in the ASPX
code of the webpage, specify the values of the following properties of the PXFormView control:
• NoteIndicator: Indicates (if set to True) that the Notes button is displayed on the title
bar.
• FilesIndicator: Indicates (if set to True) that the Files button is displayed on the title
bar.
• ActivityIndicator: Indicates (if set to True) that the Activities button is displayed
on the title bar. This button, which users click to attach activities to the webpage, is not
displayed by default.
: The LinkIndicator property, which controlled whether the Help > Get Link element was
displayed on the title bar, is now obsolete. Help > Get Link is always displayed on the title bar.
1. In the data access class (DAC) that provides data for the table rows, add the NoteID field, as the
following code shows.
#region NoteID
public abstract class noteID : PX.Data.IBqlField { }
[PXNote]
public virtual Guid? NoteID { get; set; }
#endregion
: The database table that corresponds to the DAC must contain the NoteID column with the
uniqueidentifier data type.
Once you have added the NoteID field to the DAC and rebuilt the project, the following columns
appear in the table:
•
: The Notes column, which users click to attach notes to the webpage
• : The Files column, which users click to attach files to the webpage
3. Optional: To change whether each of these columns is displayed in the table, in the ASPX
code of the webpage, specify the values of the following properties of the PXGrid control that
corresponds to the table:
• NoteIndicator: Indicates (if set to True) that the Notes column is displayed in the table
• FilesIndicator: Indicates (if set to True) that the Files column is displayed in the table
1. Add the NoteID field and the field that stores the path to the image to the data access class
(DAC) that provides data for the webpage on which you want to display the image, as shown in
the following code.
#region NoteID
public abstract class noteID : PX.Data.IBqlField { }
[PXNote]
public virtual Guid? NoteID { get; set; }
#endregion
#region ImageUrl
public abstract class imageUrl : PX.Data.IBqlField { }
[PXDBString(255)]
[PXUIField(DisplayName = "Image")]
public virtual string ImageUrl { get; set; }
#endregion
: The database table that provides data for the webpage on which you want to display the image
must contain the following columns:
• NoteID with the uniqueidentifier data type, to make it possible to attach images
• The field (in this example, ImageUrl) with the varchar(255) data type, to store the
internal path to the attached image
2. In the ASPX code of the webpage that works with this DAC, add the PXImageUploader control
linked to the ImageUrl data field, as shown in the following code.
In This Chapter
1. In the data access class (DAC), add a new field or modify an existing string field as follows:
a. Add one of the string attributes (PXDBString or PXString) to the property field.
b. Specify the value of the InputMask property of the attribute. Use the following
conventions to define the mask:
: The value is stored in the database without any formatting characters. That is, for
the code example above, if a user enters 1234567890, the field in the database for the
corresponding record will contain the same value (1234567890).
2. In the ASPX code of the webpage, add a new PXMaskEdit control or modify the control that
corresponds to the field so that it has the PXMaskEdit type, as shown in the following code
example.
To Specify an Input and a Display Mask for a String Field at Run Time
To specify the input mask for a string field at run time, use a SetInputMask method of the PXDBString
or PXString attribute. You use the same conventions to define the mask as those described for the
InputMask property in To Specify an Input Mask and a Display Mask for a String Field.
In the following example, the input mask of the AccountMask field is changed at run time.
1. In the data access class (DAC), add a new field or modify an existing data and time field as
follows:
a. Add one of the date and time attributes (PXDate, PXDateAndTime, PXDBDate, PXDBTime,
or PXDBDateAndTime) to the property field.
b. Specify the value of the InputMask or DisplayMask property of the attribute. Use the
standard and custom date and time format strings.
The following example shows the use of the InputMask and DisplayMask properties.
2. In the ASPX code of the webpage, add a new PXDateTimeEdit control or modify the control that
corresponds to the field so that it has the PXDateTimeEdit type, as shown in the following code
example.
: You can change how the PXDateTimeEdit control is displayed (whether the control shows a
calendar selector or a drop-down list with time values) by specifying the value of the TimeMode
property. The following example causes the system to display the list of time values.
1. In the graph that corresponds to the webpage, add the action and the delegate for the button
that opens the dialog box, as shown in the following example.
In this example, the MainDAC DAC is the main DAC of the primary view of the graph; therefore,
the action is added to the toolbar of the form by default.
2. If you want to specify additional properties of the action (such as CommitChanges), in the
ASPX code of the webpage, add the PXDSCallbackCommand tag for the action and specify the
necessary properties of the tag, as shown in the following example.
3. In the graph that corresponds to the webpage, add the data view for the dialog box, as shown in
the following code.
[Serializable]
public class DialogBoxParameters : IBqlTable
{
public abstract class parameter1 : IBqlField { }
[PXString(10, IsUnicode = true)]
public virtual string Parameter1 { get; set; }
PXFilter<DialogBoxParameters> OpenDialogBoxView;
In this code, we add a simple DAC with two unbound fields and use a data view based on the
PXFilter class.
4. In the ASPX code of the webpage, add the PXSmartPanel container with the Key property equal
to the name of the data view you created for the dialog box, as shown in the following code. In
the PXPanel container inside PXSmartPanel, add the commit buttons of the dialog box (such as
OK, Cancel).
5. In the button delegate, perform a call to an AskExt method of the view specified in the Key
property of the PXSmartPanel container.
When the user clicks the button on the webpage, the execution interrupts on the AskExt call and
the dialog box is displayed. After the user clicks a button in the dialog box, the AskExt method
returns the dialog box result.
In This Chapter
• Asynchronous Execution
Asynchronous Execution
An instance of a graph is created on each round trip to process a request created by the user on the
appropriate form. After the request is processed, the graph instance must be cleared from the memory
of the Acumatica ERP server. If you implement code that might require a long time to execute an action
or to process a document or data, you should execute this code asynchronously in a separate thread.
PXLongOperation.StartOperation(this, delegate()
{
// insert the delegate method code here
...
GraphName graph = PXGraph.CreateInstance<GraphName>();
foreach (... in ...)
{
...
}
...
});
...
}
...
}
If you need to start a long-running operation in a method of a graph extension, you have to use the
Base property instead of the this keyword in the first parameter of the StartOperation method, as
shown in the following code snippet.
Figure: Location of custom data in the memory of the Acumatica ERP server
Until the form that is still opened in the browsers obtains the request results, it generates requests to
the site URL every five seconds to get these results. On every such request, the load balancer selects
a server to be used to process the request and forwards the request to the server. The server uses the
long-running operation ID, which is usually equal to the graph UID, to check the operation's status. If
the operation is completed, the server creates an instance of the graph to finish processing the action
delegate and to return results to the form.
The following diagram shows how the data of a user session and of a long-running operation are stored
in the remote session storage of a cluster.
• For the action that corresponds to the button, in the graph, set the value of the
VisibleOnProcessingResults property of PXButtonAttribute or its descendant to true, as
shown in the following code example.
[PXUIField(DisplayName = Messages.ShowDocuments)]
[PXButton(VisibleOnProcessingResults = true)]
public virtual IEnumerable showDocuments(PXAdapter adapter)
{
ShowOpenDocuments(SelectedItems);
return adapter.Get();
| Implementing Business Logic | 194
• In the ASPX file that corresponds to the form, set the value of the VisibleOnProcessingResults
property of PXDSCallbackCommand to True, as shown in the following example.
• To not display the processing dialog box for a custom page, override the IsProcessing property
of the graph that corresponds to the form, as shown in the following code.
• To not display the processing dialog box for a customized Acumatica ERP form, configure the
IsProcessing property of the graph that corresponds to the form in a graph extension as shown
in the following code.
{
public override void Initialize()
{
Base.IsProcessing = false;
}
}
• To not display the processing dialog box for all processing forms, add the
ProcessingProgressDialog key in the appSettings section of the web.config file of the
application set to False, as shown in the following example.
Localizing Applications
Acumatica Framework provides built-in localization tools that you can use to translate the user interface
and application messages to different languages. This chapter provides guidelines on how to prepare
the Acumatica Framework application for localization efforts.
| Implementing Business Logic | 195
To get the application ready for localization, you must prepare data access classes (DACs) and the
application code.
In This Chapter
• Localization
Localization
Applications created with Acumatica Framework can be localized on the presentation, business logic,
and database level owing to:
• Standard Microsoft.NET localization mechanism is implemented for localizing the presentation
layer.
• All messages returned from the business logic layer can be localized through the dictionary
mechanism. For details about how to implement message localization in your code, see To
Localize Application Messages.
• The runtime environment of Acumatica Framework supports the Unicode standard to store and
operate with data in a non-ANSI format.
• Information like addresses or product descriptions can be stored in special, language-specific,
database fields and presented in the user selected language. For details on implementation of
such fields, see To Work with Multi-Language Fields.
Acumatica Framework provides a built-in utility that enables localization of the product by the end
user. Once localization is entered and applied, the application does not require any recompilation or re-
installation. Also, localization can also be exported, imported, and merged. For more information about
how to use the built-in localization mechanism, see Translation Process in the System Administration
Guide.
You can also translate user input to multiple languages and store translations in the database. For more
information on the localization of user input, see To Work with Multi-Language Fields.
• The AllowedLabels property of the PXStringList attribute or PXIntList attribute of the fields of
DACs
To prepare each DAC for localization, you need to perform the steps that are described in this topic.
1. Make sure the DisplayName parameter of the PXUIField attribute is specified for each visible
field in the DAC, as shown in the following example.
: If you change the DisplayName value of the PXUIField attribute on the fly (by creating your
own PXFieldState), you should localize the string independently.
2. Specify the values that should be displayed in drop-down lists by using the PXStringList
attribute, as shown in the following example.
1. Move all strings that should be translated to the public static Messages class and specify the
PXLocalizable attribute for this class, as shown in the following code.
: The exceptions to this requirement are field descriptions and list attributes in the data access
classes, which are handled separately. For details on how to make field descriptions and list
attributes localizable, see To Prepare DACs for Localization.
using System;
using PX.Data;
[PXLocalizable()]
public static class Messages
{
| Implementing Business Logic | 197
: No hyphenation is provided by the system. During the acquisition process of localizable data, all
the new-line symbols (\n\r) are to be removed. You can use the reserved symbol (~) to cause the
insertion of a new line.
2. If the message from the Messages class is used in an error or warning message, which is
displayed when an exception of the PXException type or of a type derived from PXException
is thrown, provide a non-localized message, as shown in the following example. The system
displays the localized message automatically if there is a translation for this message in the
database.
if (field == null)
{
throw new PXException(Messages.FieldNotFound);
}
3. If you need to receive the translation of a message from the Messages class within the
application code (for example, if the message is displayed in the confirmation dialog box, which
is displayed if you use the Ask() method of a data view in the code), use one of the following
methods:
• PXMessages.Localize(): The method searches for the translation of the provided string in
the database and returns the first translation found.
• PXLocalizer.Localize(): The method returns the translation with the given key,
which you specify in the second parameter. A string may have multiple translations; one
translation for each occurrence of the string in the application. For each of the occurrences,
a key value is created. For example, if the string is declared in a class marked with the
PXLocalizable attribute, the full qualified name of the class is the key, as the following
code shows.
1. In the data access class (DAC) that you want to contain a multi-language field, define the
NoteID field with the PXNote attribute, as follows.
[PXNote]
public virtual Guid? NoteID { get; set; }
2. If you want to configure a field to have values in multiple languages, annotate this field with
the PXDBLocalizableString attribute. The following code shows an example of the use of the
PXDBLocalizableString attribute.
The PXDBLocalizableString attribute works similarly to the PXDBString attribute, but unlike
the PXDBString attribute, the PXDBLocalizableString attribute can be used instead of the
PXDBText and PXString attributes.
3. If you need to give values in multiple languages to a field with the PXDBText attribute, replace
this attribute with the PXDBLocalizableString attribute and do not specify the length
parameter, as shown in the following example.
[PXDBLocalizableString(IsUnicode = true)]
4. If you need to configure a field that has the PXString attribute, which is used in conjunction
with the PXDBCalced attribute, replace the PXString attribute with the PXDBLocalizableString
attribute and set the value of the NonDB parameter to true, as shown in the following example.
[PXLocalizableDefault(typeof(Search<InventoryItem.descr,
Where<InventoryItem.inventoryID,
Equal<Current<SOLine.inventoryID>>>>),
typeof(Customer.languageName),
PersistingCheck = PXPersistingCheck.Nothing)]
PXDBLocalizableStringAttribute.GetValueSelect("Numbering",
"NewSymbol", false),
newPXDataField<Numbering.userNumbering>()))
{
...
}
tran.TranDesc =
PXDBLocalizableStringAttribute.GetTranslation(
Caches[typeof(InventoryItem)], item, typeof(InventoryItem.descr).Name,
customer.Current?.LanguageName);
2. Place the assembly file with the new provider in the Bin directory of the Acumatica ERP instance,
and add the assembly to the customization project as a File element.
3. Register the new provider in the pxtranslate element of the web.config file, as described in To
Register the New Provider in Web.config in this topic.
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using PX.Data;
using PX.Translation;
namespace Demo.Translation
{
public class DemoTranslationProvider : PXTranslationProvider
{
private struct TranslationKey
{
public Guid id;
public string resKey;
public string locale;
}
StringBuilder localizationTranslationSelectBld =
new StringBuilder("Select IDlt, ResKey, Value, Locale" +
"From LocalizationTranslation");
if (!string.IsNullOrEmpty(locale))
{
localizationTranslationSelectBld.AppendFormat(
" Where Locale = '{0}'", locale);
}
localizationTranslationSelect =
localizationTranslationSelectBld.ToString();
}
command.CommandText = localizationTranslationSelect;
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
TranslationKey newTranslationKey = new TranslationKey()
{
id = reader.GetGuid(0),
resKey = reader.GetString(1),
locale = reader.GetString(3)
};
localizationTranslation.Add(newTranslationKey,
reader.GetString(2));
}
}
| Implementing Business Logic | 202
}
}
if (localizationTranslation.Count != 0)
{
foreach (Guid id in localizationValue.Keys)
{
IEnumerable<TranslationKey> localizationTranslationKeys =
from translationRowKey in localizationTranslation.Keys
where translationRowKey.id == id
select translationRowKey;
foreach (TranslationKey key in localizationTranslationKeys)
{
string translationResKey = key.resKey;
string translationLocale = key.locale;
string translationValue = localizationTranslation[key];
string value = escapeStrings ?
PXLocalizer.EscapeString(translationValue) :
translationValue;
if (string.IsNullOrEmpty(translationResKey))
{
dictionary.Append(
localizationValue[id],
new PXCultureValue(translationLocale, value));
}
else
{
dictionary.AppendException(
localizationValue[id],
new PXCultureEx(translationResKey,
translationLocale, value));
}
}
}
}
return dictionary;
}
}
}
To Disable Localization
To disable localization, implement a custom translation provider with the LoadCultureDictionary()
method that returns null, as the following code shows.
<pxtranslate defaultProvider="DemoTranslationProvider">
<providers>
<!--The default translation provider-->
<remove name="PXDBTranslatonProvider" />
<add name="PXDBTranslatonProvider"
type="PX.Data.PXDBTranslatonProvider, PX.Data" />
In This Chapter
• Reusable Business Logic Implementation • To Insert Reusable Business Logic That Has
Already Been Declared
• Mapped Cache Extensions and the
Application Database • To Sort Multiple Generic Graph Extensions
• Reusable Business Logic and the Application • To Implement Reusable Business Logic
Website
data access classes (DACs) and implement business logic through event handlers, actions, and other
methods.
You encapsulate the business logic that you want to reuse in a generic graph extension, which is a
graph extension that does not relate to any particular graph and can be used with any base graph. The
generic graph extension operates with data by using the mapped cache extensions, which are cache
extensions that are not bound to any particular DAC and can extend any DAC.
To connect the mapped cache extensions to a particular DAC, you use a mapping class, which maps
the fields of a mapped cache extension to the fields of a DAC. To connect the generic graph extension
to a particular base graph, in the base graph, you define an implementation class, which inherits the
generic graph extension. The following diagram shows in yellow rectangles the classes that you need to
implement to reuse the business logic.
These classes are described in detail in the sections below. (For details on the implementation of the
classes, see To Implement Reusable Business Logic and To Insert Reusable Business Logic That Has
Already Been Declared.)
//CuryID field
public abstract class curyID : IBqlField
{
}
protected String _CuryID;
...
}
Mapping Class
A mapping class is a protected class that defines the mapping between the fields of a mapped cache
extension and the fields of a DAC. In a generic graph extension, you declare a mapping class for each
mapped cache extension that you need to use in the reusable logic implementation.
A mapping class implements the IBqlMapping interface, which has the following two properties:
In the declaration of the mapping class, you also include declarations of the properties for each field of
the mapped cache extension that you want to map to a field of the DAC, as the following code shows.
If the name of a property field of the DAC is the same as the name of the mapping class property,
the DAC field will be automatically mapped to the field of the mapped cache extension by the
implementation class (which is described below). If the name of a property field of the DAC differs
from the name of the mapping class field, you redefine the mapping manually in the implementation
class. If no field in the DAC has the name of the mapping class field, and no mapping is defined in the
implementation class, the field of the mapped cache extension is not mapped to any base DAC field, as
shown in the following diagram.
• The protected abstract methods that return the mapping classes. You have to override
these methods in the implementation class.
• The views that can have either mapping-based declaration or standard declaration. You declare a
mapping-based view by using the PXSelectExtension<Table> class.
Implementation Class
An implementation class defines the implementation of a generic graph extension for a particular graph.
You declare the implementation class as a class that derives from the generic graph extension class
with the following type parameters:
• The main DAC of the primary data view of the base graph
In this class, you can override the mapping defined by the mapping class, override other the methods
of the base class, and insert your own views, methods, and event handlers, as the following code
shows.
is, the database table must include the fields bound to a database column that are defined both in the
base DAC and the mapped cache extension, as shown in the following diagram.
The actions that are defined in the base graph, generic graph extension, and implementation class
are automatically added by the system on the website page, as shown in the following diagram. The
implementation class can override the actions declared in the generic graph extension.
| Implementing Business Logic | 210
Multi-Currency Extension
If you need to work with multiple currencies on a form, you can insert an implementation of the
MultiCurrencyGraph<TGraph, TPrimary> abstract class in the graph that provides business logic
for the form. For more information on the use of multiple currencies in the system, see Currency
Management in the Financial Management Guide.
The MultiCurrencyGraph<TGraph, TPrimary> class works with the following mapped cache
extensions:
Discount Extension
If you need to work with discounts on a form, you can insert an implementation of the
DiscountGraph<TGraph, TPrimary> abstract class in the graph that provides business logic for
the form. For more information on discounts, see Managing Customer Discounts in the Financial
Management Guide.
The DiscountGraph<TGraph, TPrimary> class works with the following mapped cache extensions:
1. Review the generic graph extension that provides the business logic that you want to reuse as
follows:
a. Identify the mapped cache extensions the generic graph extension works with and the list
of their fields, and decide whether the default mapping (which is defined by the mapping
class of the generic graph extension) is suitable for the base data access class (DAC) that
you are going to use.
b. Identify the fields of the mapped cache extension that are bound to columns of a
database table, and make sure the database table that corresponds to the base DAC
includes the columns to store the data from the mapped cache extension.
2. In the code of the graph you need to add the reusable business logic to, add the public
implementation class that derives from the generic graph extension of the needed type. Use the
following types in the type parameters of the generic graph extension:
| Implementing Business Logic | 213
• The main DAC of the primary data view of the base graph
3. In the added class, override the abstract methods of the generic graph extension as follows:
• In the overridden methods of the generic graph extension that return the mapping classes,
either use the default mapping of the fields of the mapped cache extension to the fields of
the base DAC or adjust the mapping.
• In the other overridden methods, implement the required business logic. For details on the
implementation of the methods in the generic graph extension declared in Acumatica ERP,
see API Reference.
4. In the added class, adjust the reused business logic by doing any of the following:
The following code shows a sample implementation of the MultiCurrency class, which reuses
the multi-currency business logic defined in the MultiCurrencyGraph generic graph extension.
1. In the code of your application, define the mapped cache extensions, which inherit from the
PXMappedCacheExtension abstract class. For details on the mapped cache extensions, see
Mapped Cache Extension.
//CuryID field
| Implementing Business Logic | 215
...
}
2. In the code of your application, define the generic graph extension as follows:
a. Define a class inherited from the PXGraphExtension<TGraph> class. The following code
shows a declaration of a generic graph extension.
b. In the generic graph extension, for each mapped cache extension that you defined in the
first step, declare the protected mapping class, as shown in the following code. For
details on the mapping classes, see Mapping Class.
c. In the generic graph extension, for each mapping class, declare the protected
abstract method that returns the mapping class, as shown in the following code.
d. In the generic graph extension, define the views that use the mapped cache
extensions, as the following code shows. To define each view, you use the
PXSelectExtension<Table> : PXSelectBase<Table> class, where Table is a mapped
cache extension.
: In the generic graph extension, you can define standard views as well as the views that
use the mapped cache extensions.
e. In the generic graph extension, define the reusable event handlers, as the following code
shows.
f. In the generic graph extension, implement any other business logic that you want to
reuse, such as filters and actions.
Once you have defined the mapped cache extensions and the generic graph extension, you can insert
reusable business logic to any part of your application, as described in To Insert Reusable Business
Logic That Has Already Been Declared.
| Troubleshooting Acumatica Framework-Based Applications | 217
In This Part
3. In the connectionStrings section of the file, modify the connection string by specifying the
credentials to your development database as follows:
• For a locally installed Microsoft SQL Server that uses SQL Server authentication (line
breaks are for display purposes only)
<connectionStrings>
<remove name="ProjectX" />
<add name="ProjectX" providerName="System.Data.SqlClient"
connectionString="Data Source=(local);Initial
Catalog=Project_Catalog;
User Id=User_ID; Password=User_Password"
</connectionStrings>
• For a locally installed Microsoft SQL Server that uses Windows authentication (line breaks
are for display purposes only)
<connectionStrings>
<remove name="ProjectX" />
<add name="ProjectX" providerName="System.Data.SqlClient"
connectionString="Data Source=(local);Initial
Catalog=Project_Catalog;
Integrated Security=True"/>
</connectionStrings>
• For a remote Microsoft SQL Server that uses SQL Server authentication (line breaks are for
display purposes only)
connectionStrings>
<remove name="ProjectX" />
<add name="ProjectX" providerName="System.Data.SqlClient"
connectionString="Data Source=Server_Name; Initial
Catalog=Project_Catalog;
User Id=User_ID; Password=User_Password"
</connectionStrings>
| Troubleshooting Acumatica Framework-Based Applications | 218
4. In the system.web section of the file, set the debug attribute of the compilation element to
True, as shown in the following example.
5. In Visual Studio, right-click the Site folder of the solution, and click Set as StartUp Project.
6. Right-click the Main.aspx file in the Site folder, and click Set as Start Page.
7. Optional: If you need to debug a server error that throws an exception, do the following:
b. In the Exception Settings panel, which opens, expand Common Language Runtime
Exceptions, and select the check box for the exception that is thrown (such as
System.ArgumentOutOfRangeException).
Glossary
The following table contains definitions of the basic terms used in Acumatica Framework.
Term Definition
Action An interface for executing a specific operation with data that is
implemented in a business logic controller (BLC). An action is
represented by the corresponding button on the user interface.
Acumatica Cloud xRP The platform for the development of cloud ERP applications (such as
Platform Acumatica ERP and customizations of it), the mobile application for
Acumatica ERP, and applications integrated with Acumatica ERP by
means of the web services API.
Acumatica Customization The part of the Acumatica Cloud xRP Platform that provides
Platform customization tools for the development of applications embedded in
Acumatica ERP (also called customizations of Acumatica ERP).
Acumatica Framework The part of the Acumatica Cloud xRP Platform that provides the platform
API, web controls, and other tools for building ERP applications.
Acumatica Framework A set of Visual Studio templates provided as a part of Acumatica
Templates Framework for creating application pages and business logic controllers.
Acumatica Framework- An application created by means of Acumatica Framework tools.
based application
Analytical report A report created with the Analytical Report Manager. For details on this
tool, see Analytical Report Manager.
Bound field A data field that represents a column from a database table. Compare to
Unbound field.
BQL statement A generic BQL class specialization that represents a specific query to the
database. The type parameters specified in the BQL statement are BQL
operator classes and DACs.
Business logic controller A stateless controller class that is intended for the execution of business
(BLC) logic on a particular application page. A BLC (also called a graph) is
derived from the PXGraph generic class.
Business query language A set of generic classes for querying data records from the database.
(BQL)
Cache A collection of modified data records from the same table stored in the
user session and shared between requests.
Custom report A report created on the custom report form.
Customization of A modification of the user interface, business logic, and the database
Acumatica ERP scheme without recompilation and re-installation of Acumatica ERP. This
modification is packed in a customization project.
Customization project A container that holds the changes you have made during a particular
customization of Acumatica ERP.
DAC field See Field.
Data access class (DAC) A class that represents a database table.
Data entry form An application webpage that is used for the input of business
documents.
Data member A data view specified as the data source for a container of UI controls (a
form, a tab, or a grid).
| Glossary | 220
Data record A specific record retrieved from the database or created in code and
wrapped in a DAC instance.
Data view A BQL statement that the BLC uses to access and manipulate data. A
developer defines a data view in code by using PXSelect classes.
Datasource control A service control on a page that is used to bind the page to a particular
BLC. This control represents the page toolbar, which contains action
buttons.
Embedded application See Customization of Acumatica ERP.
Event A way to provide notifications from Acumatica Framework to the
application. Most business logic is implemented in event handlers.
Event handler A method that is invoked by Acumatica Framework when the
corresponding event is raised.
Field (DAC field) A part of the DAC definition that typically represents a database column.
A DAC field consists of an abstract class that is used to refer to the field
in BQL and a property holding the actual field value.
Form See Webpage.
Graph See Business logic controller.
Inquiry form An application webpage that displays a list of data records selected by
the specified filter.
Integrated application A third-party application integrated with Acumatica ERP by means of
web services API.
Maintenance form An application helper webpage that is used for the input of data on the
data entry and processing pages.
Mobile API The API that is used for customization of the Acumatica mobile
application. For a description of the API, see Mobile Site Map Reference.
Multitenant application An application in which multiple tenants use the same Acumatica
Framework-based application. For each tenant, the website looks
identical and provides the same business logic. However, each tenant
has exclusive access to the tenant's individual data and can have
restricted access to the data of other tenants.
Page template A Visual Studio template that is provided by Acumatica Framework and
used for creating application pages.
Platform API The API that is used to develop Acumatica Framework-based
applications and customizations of Acumatica ERP.
Primary BLC The BLC that corresponds to the default editing page of the data record.
This BLC is specified in the PXPrimaryGraph attribute.
Primary DAC The first data access class specified in a BQL statement.
Primary data view The first data view defined in a business logic controller.
Processing form An application webpage that provides mass processing operations.
Report Designer A visual editor for creating report forms and printable pages.
Report form An RPX page created in Report Designer that defines the form used for
generating reports in the application.
Screen See Webpage.
Setup form An application webpage that provides the configuration parameters for
the application.
| Glossary | 221
Unbound field A data field that exists only on the model level in a DAC definition, and
that is not bound to a column of the database table. Compare to Bound
field.
Webpage A page that provides the UI of the application. Each webpage used
in the application is a declarative ASPX page created from one of the
Acumatica Framework Templates or a report form.
Web services API The API for development of applications integrated with Acumatica ERP
through SOAP or REST.