Overview of Dynamics 365 ERP and CRM
Overview of Dynamics 365 ERP and CRM
The CRM applications help manage sales, marketing, and customer service,
The ERP applications connect finance, inventory, and HR to streamline operations.
What Size Of Business Does Dynamic 365 Work
For?
Choose one, some, or all. Dynamics 365 applications are made to work together
What is CRM?
Because CRMs help centralize customer data, these systems make it easier for your team to
manage relationships ,track progress, and ultimately drive growth.
they also help you build stronger, more personalized connections with clients.
Here are the key features to look for in a CRM:
Centralized database: All customer information is stored in one place, making it easy to access and
update, ensuring everyone on your team is on the same page.
Sales tracking: CRMs help track customer interactions, sales activities, and opportunities, so you can
monitor the sales pipeline and forecast revenue more accurately.
Automation: Modern CRM systems help allow your teams to automate routine tasks like follow-up
emails or data entry. This saves time, reduces errors, and empowers employees to focus on more
important, strategic goals.
Customer segmentation: CRMs allow you to organize customers based on various criteria, such as
industry, location, and purchase history. This helps your team send more targeted communications and
improve conversion rates.
AI capabilities: Modern CRMs incorporate AI-powered tools to provide insights into customer behavior,
predict sales trends, and suggest actions that can improve customer engagement.
Benefits of a CRM
A CRM system is a central tool that gathers all customer data in one
place, allowing your team to access information quickly and efficiently. It
doesn’t just store data—it makes customer-related processes faster,
smarter, and more organized.
Practical benefits:
Increase sales.
Get real-time performance data through sales monitoring
Plan your next steps.
Optimize workflows with automation.
Track customer interactions for greater impact.
Connect across multiple platforms for superior customer engagement.
Accelerate growth and gain a competitive advantage.
What is ERP ?
ERP systems help businesses integrate and streamline operations across various
departments.
ERP centralizes data and processes, enabling better collaboration, accuracy, and
decision-making.
If you are upgrading code from previous versions (AX2012 and earlier),
note that the CIL helper methods such as Global::runClassMethodIL have been removed, because they're no longer
relevant.
The Ignore list
A variable is an identifier that points to a memory location where information of a specific data type is
stored. The size, precision, default value, implicit and explicit conversion functions, and range depend
on the variable's data type.
The scope of a variable defines the area in the code where an item can be accessed.
Instance variables are declared in class declarations, and can be accessed from any methods in the
class or from methods that extend the class.
Local variables can be accessed only in the block where they were defined.
When a variable is declared, memory is allocated, and the variable is initialized to the default value.
You can assign values to both static fields and instance fields as part of the declaration statement.
Variables can be declared anywhere in a code block in a method. They don't have to be declared at the
beginning of a method.
Constants are variables where the value can't be changed when the variable is declared. They use
the const or readonly keyword.
var
You can declare a variable without explicitly providing the type of the variable
if the compiler can determine the type from the initialization expression.
You can use var only on declarations where initialization expressions are provided.
Don't use var when the type isn't clear from the initialization expression.
The lifetime of the variable is the scope that it’s declared in.
Variable Scope
You can provide declarations immediately before the variable is used, and you don’t have to
declare all the variables in one place. Therefore, you have precise control over the scope of
your variables.
You can give variables smaller scopes, outside which the variables can’t be referenced.
The lifetime of the variable is the scope that it’s declared in
The concept of macros is fully supported. Constants have the following advantages over
macros:
You can add a documentation comment to a constant but not to the value of a macro. Eventually, the
language service will pick up this comment and provide useful information to the user.
A constant is known by IntelliSense.
A constant is cross-referenced. Therefore, you can find all references for a specific constant but not for
a macro.
A constant is subject to access modifiers. You can use the private, protected, and public modifiers.
The accessibility of macros isn't rigorously defined.
Constant variables have scope, whereas macros don't have scope.
You can see the value of a constant or a read-only variable in the debugger.
Null values for data types
Casting refers to assignments between variables whose declared types are both in the
same inheritance chain.
A cast is either a down-cast or an up-cast.
The is operator can be used if the expression provided is of a particular type (including derived
types).
The as operator will perform casting into the given type and return null if a cast isn't possible.
X++ Syntax Overview
X++ has a set of reserved keywords and operators that are fundamental to the language. You cannot
use these for variable names or custom identifiers.
Data Types & Modifiers
int, int64, real, str, date, datetime
Operat
abstract, final, static, public, private, protected Meaning Example
or
Expressions & Statements
+ Addition A+B
Expressions: Can be mathematical or logical.
- Subtraction A-B
Arithmetic: A + B / C
Logical: (A > 3 ? true : false) Multiplication / A * B or select * from
*
Static method call: Debtor::Find("1")
wildcard CustTable
Statements: == Equality check if (A == B)
Conditional: if, else, switch != Not equal if (A != B)
Loops: for, while, do...while
&& Logical AND if (A && B)
Database: select, insert_recordset, update_recordset, delete_from
:: Call static method ClassName::methodName()
Exception handling: try, catch, throw
X++ Primitive data types
The composite data types in X++ are arrays, containers, classes as data types, delegates as data
types, and tables as data types.
An array is a variable that contains a list of items that have the same data type.
There are three kinds of arrays: dynamic arrays, fixed-length arrays, and partly on disk
arrays.
• Dynamic arrays – These arrays are declared by using an empty array option. In other words, they have only brackets ([]).
• Fixed-length arrays – These arrays can hold the number of items that is specified in the declaration.
• Partly on disk arrays – These arrays are declared as either dynamic arrays or fixed-length arrays that have an extra option
• that declares how many items should be held in memory.
A class is a type definition that describes both variables and methods for instances of the
class. (The instances of a class are also known as objects.)
A class is only a definition for objects, and all objects are null when they are declared.
X++ composite data types
Collection classes are used to store objects. They let you create arrays, lists, sets, maps, and structs that
can hold any data type, even objects. For maximum performance, the classes are implemented in C++
(they are system classes).
Collection classes were previously known as foundation classes.
The collection classes are Array, List, Map, Set, and Struct.
Array – This class resembles the array type in the X++ language, but it can hold values of any single type, even
objects and records. Objects are accessed in a specific order.
List – This class contains elements that are accessed sequentially. Unlike an array, the List class provides an
addStart method. Like the Set class, the List class provides the getEnumerator and getIterator methods. You can use
an iterator to insert and delete items from a List object.
Map – This class associates a key value with another value.
Set – This class holds values of any single type. Values aren't stored in the sequence in which they are added.
Instead, the Set object stores the value in a manner that optimizes performance for the in method. A Set object
ignores any attempt to add a value that the Set object is already storing. Unlike the Array class, the Set class
provides the in and remove methods.
Struct – This class can contain values of more than one type. It's used to group information about a specific entity.
Extended data types
The compiler detects the keyword TODO at the start of a comment and lists it in
Visual Studio’s Task List, showing where unfinished tasks exist in the code.
Works with both comment styles: // and /* */.
Unsupported statements:
pause and window are no longer valid in X++.
Using them will cause compilation errors.
Ignored statements:
server and client keywords (used in AX 2012 to control execution location) are now
ignored,
since all X++ code runs as .NET CIL on the server.
They don’t cause errors but shouldn’t be used in new code.
Using clauses:
Allow you to avoid typing full namespace paths.
Must appear before the class definition.
You can create aliases for namespaces or classes.
Using statements:
The using statement helps guarantee that objects that implement IDisposable are disposed of
correctly.
When you use an IDisposable object, you should declare and instantiate it in a using statement.
Simplifies resource management (like files or streams).
if and if...else statements
The switch statement is a multibranch language construct that has the same behavior as nested if statement
The expression after the switch keyword is evaluated and checked against each case value.
If a case constant matches the switch expression, the case statement is executed.
If the case contains a break statement, the program then jumps out of the switch.
If the case doesn't contain a break statement, the program continues and executes the next case statements.
If no matches are found, the default statement is executed.
If there are no matches and no default statement, none of the statements inside the switch statement are executed.
The for loop repeatedly executes statement for as long as the conditional
expression test is true
statement can be a block of statements.
The body of the for loop (statement) might be executed zero or more times, depending on
the results of test.
throw statements
You use the throw keyword to throw an Exception enum value.
For example, the following statement throws an error exception.
best practice is to use the output of the Global::error method as the
operand for throw.
try, catch, finally, and retry
statements
When an exception is thrown, it's first processed through the catch list
of the innermost try block.
if a catch block is found that handles the kind of exception that is
being thrown, program control jumps to that catch block.
Assignment operators
An assignment changes the value of a variable or field. The following table shows
the X++ assignment operators. There is no difference between prefix and postfix
operators.
Arithmetic operators
The following table lists the relational operators that can be used in X+
+.
Most of the operators are binary and take two operands.
the not (!) operator is unary and takes only one operand
Syntax for binary operators:
expression1 relationalOperator expression2
Syntax for unary operators:
relationalOperator expression1
Operator precedence
A class defines the data and methods of the instances that are later
constructed from that class. The class is an abstraction of an object in
the problem domain.
The instances that can be constructed from the class are known
as instances or objects.
The data represents the state of the object and the methods represent
the behavior of the object.
All classes are public by default.
You can specify other modifiers on the class declaration, such
as final, internal, static, and abstract.
Fields
Field attributes
You can decorate a field with an attribute, in the same way that attributes can
decorate classes and methods.
example
decorates the myField field with the MyAtribute attribute.
Methods
Instance methods
Instance methods are called with an instance of the class so you must instantiate
the object before you can use the method.
Static methods
Static methods, which are also known as class methods,
belong to a class and are defined by using the static keyword.
Static methods can access the values of the class static fields and call static methods.
Main methods
A main method is a class method that can be run directly from a menu option.
The _args parameter lets you transfer data to the method.
Method
Method modifiers
display - The method's return value should be shown on a page or a report. The value can't be modified on
the page or report. Typically, the return value is a calculated value, such as a sum.
edit - The method's return type used to provide information for a field that is used on a page. The value in the
field can be modified.
final - The method can't be overridden in any class that derives from its class.
public - Methods that are declared as public can be accessed anywhere that the class is accessible, and they
can be overridden by subclasses. Methods without an access modifier are implicitly public.
protected - Methods that are declared as protected can only be called from methods in the class and in
subclasses that extend the class where the method is declared.
private - Methods that are declared as private can only be called from methods in the class where the
private method is declared.
internal - Methods that are declared as internal can only be called from methods in the class that are
defined in the same model as the class where the method is defined.
static - The method is a class method. Static methods can't refer to instance fields. They aren't invoked on an
instance of the class. Instead, they're invoked by using the class name (for example,
abstract - The method is declared but isn't implemented. The method must be overridden in all subclasses. If
you try to create an object from a subclass where one or more abstract methods that belong to the parent
class haven't been overridden,
Local functions
You can declare functions inside a method. These are called local functions.
Code that's inside the local function can access variables that are declared before
it's in the method that contains the local function.
Code that's outside the local function can't access variables that are declared in
the local function.
Extension methods
The extension method feature adds extension methods to a target class by writing
the methods in a separate extension class.
The following rules apply:
The extension class must be static.
The name of the extension class must end with the ten-character suffix _Extension.
Every extension method in the extension class must be declared as public static.
The first parameter in every extension method is the type that the extension method
extends
Aggregate functions
The aggregate functions perform calculations on a single field over a
group of records.
Grouping and ordering the query results
A query can have multiple group by clauses, but the fields can be qualified
by a table name in only one group by clause.
The order by clause follows the same syntax patterns as group by
Join tables
Example
Asc,Avg keyword
asc keyword
The asc keyword is an option on the order by or group by clause.
avg keyword
The avg keyword returns the average of the fields.
Count, crossCompany keyword
count keyword
The count keyword returns the number of records.
crossCompany keyword
The crossCompany keyword returns data for all companies that the user is
authorized to read from.
You can add a container to reduce the number of companies that are
involved.
Desc,exists keyword
desc keyword
The desc keyword is an option on the order by or group by clause. It specifies
a descending sort order. If neither asc nor desc is specified, the sort is
ascending.
exists keyword
The exists keyword is a method that returns a Boolean value and a join clause.
firstFast keyword
firstFast keyword
The firstFast keyword is a priority hint. The first row appears more quickly,
but the total return time for this option might be slower. The firstFast hint is
automatically issued from all pages.
firstOnly keywords
The index keyword instructs the database to sort the selected records
as specified by the index.
The index hint keyword forces SQL Server to use a specific index.
Using index hint keyword, means you take away the control of which
index to use from the Query Optimizer.
join keyword
The join keyword is used to join tables on a column that is shared by both tables.
Maxof,minof keyword
The notExists keyword checks for the absence of related records in the
joined table. If a match does exist, teh record is excluded from the
results. If no match is found, the record is included in the results.
optimisticLock,pessimisticLock keyword
The outer keyword returns all rows from the table that is named first,
even rows that have no match in the table that is named second.
repeatableRead,reverse keyword
The sum keyword returns the sum of the fields. It can be used to sum
all accounts, order lines, and so on.
The where keyword filters rows from a table where the expression is true.
The following example finds a customer that has an AccountNum value that is more
than 100.
Insert , Update, Delete example
Example
inserts a new record into the CustTable table. The AccountNum column of the new
record is set to 2000, and the CustGroup column is set to 1.
Example
Only the record where the value of the AccountNum field equals 2000 are updated.
Because there is no call to next
Example,
all records in the CustTable table where the AccountNum field equals 2000 are
deleted
While select statement
The results of the while select statement are returned in a table buffer variable.
If you use a field list in the select statement, only those fields are available in the table
variable.
Any Boolean expressions that are added to the while select statement (for example, iCounter < 1)
are tested only one time.
select statement
You can use a select statement as an expression. This type of select statement is known as
an expression select statement.
You can't use a table buffer variable in an expression select statement.
The name of the table must be used in the from clause.
The join keyword isn't supported.
The table name can't be used to qualify a field name in the order by clause.
In a where clause, the table name must be used as a qualifier of the field.
You can mention only one table in an expression select statement. Therefore, subselects aren't supported as a
workaround for the unsupported join keyword.
The only column that can be filled with data is the column that is named before the from clause in the select clause.
After the closing parenthesis, the name of a column is used to reference the data value.
Here is a simpler way to achieve the same result as the previous example.
Insert data
to insert one or more rows into tables that are stored in the database.
insert method – Insert one row at a time.
doInsert method – Insert one row at a time.
insert_recordset statement – Copy multiple records directly from one or more tables into
another table in one database trip.
[Link] – Insert multiple rows at the same time in one database trip.
Use this construct when you don't have to sort the data.
[Link] – Insert multiple rows at the same time in one database trip.
insert method
The insert method inserts one record at a time. It generates values for
the RecId field and system fields, and then inserts the contents of the buffer (that
is, the column values) into the database.
doInsert method
The doInsert method generates values for the RecId field and other system
fields, and then inserts the contents of the buffer into the database. Use this
method when the insert method on the table must be bypassed.
insert_recordset statement
Example
shows that the insert_recordset statement can insert variable data.
Include the firstonly keyword to insert only one new record. If you omit firstonly, a record
is inserted for each record in the CustTable table.
Literals, such as 128 or "this literal string", can't be used in the query as a source of data
that is inserted.
The columns in the source table don't have to correspond to the target table.
insert_recordset:
Inserting data by using a join
Example
shows a join of three tables on an insert_recordset statement that has a subselect. It
also shows a while select statement that has a similar join. A variable is used to
supply the inserted value for one column. The str variable must be declared, and it
must have a length that is less than or equal to the maximum length of the
corresponding database field.
Update data
You can use SQL statements, either interactively or in source code, to update
one or more rows in a table that is stored in the database.
update method – Update the current record with the contents of the buffer. Also
update the appropriate system fields.
doUpdate method – Update one row at a time.
update_recordset statement – Update multiple records in one database trip. By
using the update_recordset statement,
you reduce communication between the application and the database. Therefore, you
help increase performance. In some situations, record set–based operations can fall
back to record-by-record operations.
update method
The update method updates the current record with the contents of
the buffer. It also updates the appropriate system fields.
doUpdate method
To override the behavior of the update method, use the doUpdate method.
The doUpdate method updates the current record with the contents of the buffer.
It also updates the appropriate system fields. You should use
the doUpdate method when the update method on the table must be bypassed.
update_recordset statement
The delete method deletes the current record from the database. To
use this method, use a where clause to specify the rows to delete. One
record at a time is then removed from the specified table.
doDelete method
Like the delete table method, the doDelete table method deletes the current record from
the database. Use the doDelete method if the delete table method has been overridden,
and you want to run the original (base) version of that method instead of the overridden
version.
delete_from statement
The extensible SysDa API provides almost all the data access possibilities
that are available in X++. In fact, the APIs are wrappers around the code that
the X++ compiler would generate.
The SysDa APIs include an extensive set of APIs for creating custom queries.
there is a smaller set of types that drives the primary query activities:
Select: SysDaQueryObject, SysDaSearchObject, and SysDaSearchStatement
Update: SysDaUpdateObject and SysDaUpdateStatement
Insert: SysDaInsertObject and SysDaInsertStatement
Delete: SysDaQueryObject, SysDaDeleteObject, and SysDaDeleteStatement
Select query
Example
Retrieves all records where intField <= 5.
Update statement
Because updates modify the data in the database, you must call to execute in ttsbegin and ttscommit statements.
Example
Updates stringField to "fifty" for all rows where intField = 50.
Insert statement
To run an insert statement, follow these steps.
1. Create and configure a SysDaInsertObject object to specify which fields are updated during the insertion.
2. Create and configure a SysDaQueryObject object that specifies the source of the rows to insert. The order of the fields
in [Link]() must match the order of the fields in [Link]().
3. Assign the SysDaQueryObject object to the SysDaInsertObject object.
4. Insert the new row by passing the SysDaInsertObject object
to the [Link]() method.
Example
Inserts a new record with stringField = "en-us" and intField = 40.
Delete statement
Example
Deletes all records where intField is even.
SysDa queries
An SQL injection attack occurs when malicious data values are passed to
Microsoft SQL Server in a query string.
SQL injection can occur if you aren't careful about how you use a query to pass
data that comes from an uncontrolled source, such as user input, to SQL Server.
When you build SQL queries by concatenating user input directly into strings.
The database executes the input as part of the SQL command.
Developers use Visual Studio to create and customize elements and perform other
developer operations.
The Element Designer is used to create elements, while the Application Explorer is
used to manage elements.
It is important to synchronize any data changes with the database to Ensure that all
elements are up to date in the Application Object Tree (AOT).
Create and build projects
Creating a project in Visual Studio is essential for organizing, managing, and deploying your code
effectively.
It groups related elements, manages dependencies, and controls builds, making it easier to navigate
your solution.
solution is a container that organizes one or more related projects.
Solutions represent different parts of an application, such as business logic, UI components, and
services.
To create a new project,
Go to File > New > Project, or hold Ctrl + Shift while pressing the N key.
Under the project templates, select Finance Operations, then select Next.
In the project wizard, name your project and make sure to select the model where it belongs. The model is
difficult to change when you start adding files to the project, so getting it right early on is important.
Build, rebuild, and clean
To compile your code, you have multiple options: you can build, rebuild,
or clean your project from the Solution Explorer.
A build compiles any changes.
A rebuild compiles all components from scratch.
clean removes previous build outputs.
Create and use label files
Using label files is the best practice for all text in the user interface (UI) to support multiple languages
seamlessly.
Do not modify standard labels; create custom ones.
If you need to change a standard label (e.g., Sys:StdLabel), you can override it using an extension:
1. Create a new custom label file using the wizard, adding the postfix _Extension (e.g., Sys_Extension).
2. Create a new label in this file with the exact same Label ID as the standard label you want to change.
3. Provide the new text string. The system will now use your custom label text instead of the standard one.
metadata
In Visual Studio, all objects that define the application's structure and functionality-such as tables,
forms, classes, and workflows-are stored within the Application Object Tree (AOT).
The AOT serves as a centralized repository, organizing these elements for easy access and
management during development and customization.
Metadata types
Extended Data Types (EDTs) - If you have programmed before, you might think of these as primitive types:
Boolean, String, Real, Integer, Globally Unique Identifier (GUID), Container, Time, Date, and Enum.
Enums - A type with a value that is chosen from a predefined list.
Tables - Organize data into rows and columns.
Forms - User interface elements that get their own page in finance and operations apps.
Data entities - An abstraction of database tables that are used for integrations, data import/export, and more.
Views Types
The Application Explorer contains the Application Object Tree (AOT), which has two views: the
model view and the classic view.
To access these views, right-click the AOT node and select model view or classic view.
To apply your code changes to a non-development environment, build a Software Deployable Package
(SDP) and upload it to Lifecycle Services.
To build an SDP from Visual Studio, follow these steps.
Go to Builds in Team Explorer.
Right-click the desired build definition.
Select Queue New Build.
In the browser window that opens, sign in to Azure DevOps (unless you are automatically signed in).
Use the Queue button to open the build queue dialog box.
Enter any parameters that you need to in the dialog box.
Select Confirm to queue the build.
After the build completes:
Logs and built code are available to download.
The code is available as just the models it includes or as an AX Deployable Runtime.
All downloads are accessible via the Artifacts button on the build’s status page.
Synchronize data changes with the database
When you add or change elements, you must synchronize the database so that it
knows in which fields (columns) that it needs to store these elements.
To do that:
select Synchronize Database from the Dynamics 365 menu and then use
the Synchronize button in the dialog box.
select Synchronize Database on Build on the Options tab of the Build Models dialog
box to automatically synchronize the database after a successful build.
Create new elements
A developer for Dynamics 365 may need to create new elements to meet the requirements of a
customization or modification being made to the application.
The Element Designer is the central development tool in Visual Studio, where you use it to
edit and configure elements.
New elements can be created and modified here, and existing elements within the AOT can
be extended to fit specific needs within this window.
The appearance of the Element Designer varies by element type but generally:
Contains a hierarchical view of element data and related elements or code.
Allows drag and drop of elements where structurally appropriate.
Lets you view properties of any selected node in the Properties window.
By right-clicking code elements, you can open them in a code editor and modify them using familiar
Visual Studio features like Intellisense.
Forms Layout
Forms have three main windows:
1. Form Designer – Shows a hierarchical view of the form's structure.
2. Normal Element Designer – Displays the selected node in the view.
3. Pattern Designer – Helps match existing form layouts and UI conventions.
Extended Data Types, Enumerations
EDTs extend primitive data types with custom properties, making data structures more consistent and
maintainable.
Enumerations simplify the management of predefined sets of values. Together
While base enums and EDTs are not new to all developers, it's important to understand how these
data types are used in development for finance and operations apps.
The following primitive data types exist in the AOT and can be created in Visual Studio:
Booleans - Can only contain the values "false" and "true."
Dates - Contain the numbers for day, month, and year.
Enums - An abbreviation for "enumerated text," or a set of literals.
GUIDs - Globally unique identifiers.
Integers - Numbers without a decimal point.
Real - Numbers with a decimal point and are sometimes called decimals.
String - A series of characters, such as a text block.
TimeofDay - Contain the numbers for hour, minute, and second.
Benefits of using EDTs
1. Improved Readability
Code becomes easier to understand with meaningful variable names.
Example: Using a string variable named “Name” for a user input field
4. Reusability
Once created, an EDT can be used across multiple tables and forms.
5. Enhanced Behavior and UI Features
EDTs can enable lookup and drop-down list behaviors.
Support for multiline string controls with lookup capabilities.
base enum
A base Enum is a list of literals with an assigned value. You will need to create base
enumerations in Visual Studio before you can use them in X++ code.
Enum values are represented internally as integers, and they are stored in the
database as a number instead of the value.
To add a base enum to your project,
right-click your project,
select Add > New Item.
under Dynamics 365 Items, select Data Types > Base Enum.
Data Models
The Table Designer in Dynamics 365 shows the table name and its main parts:
Fields: Store data in the table, usually using base enums or extended data types (EDTs).
Field Groups: Group related fields for easy reuse in forms and reports.
Indexes: Speed up data searches.
Relations: Link the table to others.
Delete Actions: Define what happens when related records are deleted.
State Machines: Handle state changes in workflows without extra code.
Mappings: Connect fields between different tables.
Methods: Code that runs when certain actions occur.
Events: Trigger code before or after methods run.