You are on page 1of 56

Epicor Kinetic

C# Programming Guide
(conversion from ABL)
Classic
Disclaimer
This document is for informational purposes only and is subject to change without notice. This document and its
contents, including the viewpoints, dates and functional content expressed herein are believed to be accurate as of its
date of publication. However, Epicor Software Corporation makes no guarantee, representations or warranties with
regard to the enclosed information and specifically disclaims any applicable implied warranties, such as fitness for a
particular purpose, merchantability, satisfactory quality or reasonable skill and care. As each user of Epicor software is
likely to be unique in their requirements in the use of such software and their business processes, users of this document
are always advised to discuss the content of this document with their Epicor account manager. All information contained
herein is subject to change without notice and changes to this document since printing and other important information
about the software product are made or published in release notes, and you are urged to obtain the current release
notes for the software product. We welcome user comments and reserve the right to revise this publication and/or
make improvements or changes to the products or programs described in this publication at any time, without notice.
The usage of any Epicor software shall be pursuant to an Epicor end user license agreement and the performance of
any consulting services by Epicor personnel shall be pursuant to Epicor's standard services terms and conditions. Usage
of the solution(s) described in this document with other Epicor software or third party products may require the purchase
of licenses for such other products. Where any software is expressed to be compliant with local laws or requirements
in this document, such compliance is not a warranty and is based solely on Epicor's current understanding of such laws
and requirements. All laws and requirements are subject to varying interpretations as well as to change and accordingly
Epicor cannot guarantee that the software will be compliant and up to date with such changes. All statements of
platform and product compatibility in this document shall be considered individually in relation to the products referred
to in the relevant statement, i.e., where any Epicor software is stated to be compatible with one product and also
stated to be compatible with another product, it should not be interpreted that such Epicor software is compatible
with both of the products running at the same time on the same platform or environment. Additionally platform or
product compatibility may require the application of Epicor or third-party updates, patches and/or service packs and
Epicor has no responsibility for compatibility issues which may be caused by updates, patches and/or service packs
released by third parties after the date of publication of this document. Epicor® is a registered trademark and/or
trademark of Epicor Software Corporation in the United States, certain other countries and/or the EU. All other
trademarks mentioned are the property of their respective owners. Copyright © Epicor Software Corporation 2021.
All rights reserved. Not for distribution or republication. Information in this document is subject to Epicor license
agreement(s).

Classic
Revision: June 08, 2021 12:27 a.m.
Total pages: 56
sys.ditaval
C# Programming Guide (conversion from ABL) Contents

Contents
Introduction............................................................................................................................5
ERP and ICE Tables..................................................................................................................6
Case Sensitivity.......................................................................................................................7
Case Sensitivity in String Comparisons.............................................................................................................7
Compilation Errors...........................................................................................................................................7
Calling Method From Business Object..................................................................................9
Calling BOs From Custom Code...........................................................................................10
Calling Method From Standard Assembly..........................................................................11
Accessing ERP Context From Within System Directive......................................................12
Displaying an Informational Message................................................................................13
Throwing Exceptions............................................................................................................14
Writing Messages to Log.....................................................................................................15
Compatibility Class...............................................................................................................16
Replacing Resulting Dataset................................................................................................17
BufferCopy Considerations..................................................................................................18
Working With Custom Data.................................................................................................19
Create User-Defined Fields.............................................................................................................................19
Access User-Defined Fields.............................................................................................................................21
Loading User-Defined Data Into Various Objects............................................................................................22
Transition Path...............................................................................................................................................22
Obsolete ABL Code...............................................................................................................24
Using External Libraries.......................................................................................................25
Create Project in Visual Studio.......................................................................................................................25
Call External Assembly Using BPM..................................................................................................................26
Debugging Using Visual Studio...........................................................................................29
Prerequisites..................................................................................................................................................29
Debug BPM Directive.....................................................................................................................................29
Debug Custom Project...................................................................................................................................32
Adding and Subtracting Date..............................................................................................35
Subtracting Two Dates and Comparing to an Integer Value............................................36
Migrating ABL Expressions..................................................................................................37
ABL Find Last Conversion.....................................................................................................38
Using Unassigned Local Variable Message.........................................................................39
Epicor 10 Equivalent to Row Mod = A or U........................................................................40
Converting a Number to a String in LINQ Expression........................................................41
Outputting Data to a File.....................................................................................................42
Updating Database Tables...................................................................................................44

Epicor Kinetic | Classic 3


Contents C# Programming Guide (conversion from ABL)

Sample of BPM calling a .p file and the final Epicor 10 version........................................45


Sample of a BPM That Sends an Email................................................................................49
Querying the Database........................................................................................................50
Setting a Value on the Payload...........................................................................................51
Calling Methods in the Same Service..................................................................................52
Calling Other Services..........................................................................................................53
Using RowMod.....................................................................................................................54
Naming Conventions in BPM Sources.................................................................................55

4 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Introduction

Introduction

The main perspective of the guide is aimed at ABL code users from previous Epicor ERP releases. It provides
guidelines on what corrections and modifications users may need to perform when converting snippets of Progress
ABL code to the .NET C# language used by the Epicor ERP 10 framework. ERP users that have only ever used ERP
version 10 and later can also benefit from seeing the C# examples in this guide.
Important Epicor highly recommends you recreate obsolete directives utilizing ABL code using the standard
BPM functionality and only use custom directives for specific requirements not achieved by the out-of
the box BPM features. Using standard BPM functionality ensures easier maintenance and upgrade of your
directives across Epicor ERP releases.

Tip For more information on the available BPM tools, please review the below Epicor ERP resources:
• Epicor ERP Help:
• System Management > Business Process Management
• Epicor ERP User Guides > Tools User Guide.

• Help and Support center panel > Embedded Education Courses:


• Business Process Management
• Advanced Business Process Management
• Auto Print

• Epicor ERP Help and Support center panel > Epicor Learning Center:
• Sign into your account, choose the ERP Product and search for the current on-demand and live BPM
course offerings.

Epicor Kinetic | Classic 5


ERP and ICE Tables C# Programming Guide (conversion from ABL)

ERP and ICE Tables

Epicor programs belong to either the application system (ERP) or the tools system (ICE). In Epicor ERP 10, certain
schema changes were made. Make sure to reference the correct part of the Epicor application.
In the following example, a user-defined table references "ICE" schema, where it belongs.
Ice.Tables.UD01 UD01;
Tip You can use the Data Dictionary Viewer to find and review details of each field and table within
the database.

6 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Case Sensitivity

Case Sensitivity

This section deals with usage of case sensitivity in C# language.

Case Sensitivity in String Comparisons

By default, code written in C# treats string comparisons as case sensitive, which was not the case in ABL code.
Note When you convert a statement or create new custom code, be sure to take appropriate casing into
account.

string.Compare((string)ttJobHead_xRow["ShortChar02"], “Education”, true)!=0

Compilation Errors

Remember that C# is a case-sensitive language, and it may result in compilation errors. For example, look at the
simple below:
Notice that the error message has "customer" starting with a lower-case letter c.
var CustomerRecs = (from r in Db.customer
from r_UD in Db.Customer_UD
where r.SysRowID == r_UD.ForeignSysRowID &&
r.Company == Session.CompanyID &&
r.CustNum == ttOrderHedRow.CustNum
select r_UD).FirstOrDefault();
This query will cause the following error message when you try to compile the BPM:

In order for the BPM to compile without errors, change the first line of the query to use an upper-case letter C
as shown below:
var CustomerRecs = (from r in Db.Customer
from r_UD in Db.Customer_UD

Epicor Kinetic | Classic 7


Case Sensitivity C# Programming Guide (conversion from ABL)

where r.SysRowID == r_UD.ForeignSysRowID &&


r.Company == Session.CompanyID &&
r.CustNum == ttOrderHedRow.CustNum
select r_UD).FirstOrDefault();

8 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Calling Method From Business Object

Calling Method From Business Object

In some cases, you need to call a method from a business object (BO).
For this purpose, use the ServerRenderer and add a reference to the contract containing the particular business
object. ServerRenderer is a helper class residing in Epicor.System.dll. You can use this class to make calls to any
application service with the defined contract. This class returns an instance of the contract, which you can use
to invoke any method available on the contract.
The signature of the method is displayed below:
/// <summary>
/// Returns a class instance for the given contract
/// </summary>
/// <param name="ignoreFacade">if set to <c>true</c> method returns instance of
service instead of service facade.
</param>
/// <typeparam name="TService">The contract type. The contract must have a Serv
iceContract attribute and the service namespace must follow the Ice naming conv
ention
</typeparam>
/// <returns>A class instance for the given contract
</returns>
public static TService GetService<TService>(bool ignoreFacade = false)
In the following example, the GetByID() method is called from the Tip Service. In order to make a call to the
TipService method from another directive, you first add TipService contract assembly to the reference, and then
make the call. The svc variable holds the Tip contract instance.
var svc = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.TipSvcContrac
t>(Db);
svc.GetByID(…);
If within one code scope (for example, one action) several calls are made to the same contract, it is recommended
to call ServiceRenderer just once. For example:
var svc = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.TipSvcContrac
t>(Db);
svc.GetByID(…);
...some other code here
svc.GetList(…);
...some other code here
svc.Update(…);
The following example displays a correct conversion of ABL code to C#.
• ABL Code:
DEF VAR opNextJobNum AS CHARACTER NO-UNDO.
DEF VAR vh-JobEntry AS HANDLE NO-UNDO.
RUN bo/JobEntry/JobEntry.p PERSISTENT SET vh-JobEntry.
RUN GetNextMntJobNum IN vh-JobEntry (OUTPUT opNextJobNum) NO-ERROR.

• C# Code:
string opNextJobNum = string.Empty;
Erp.Contracts.JobEntrySvcContract vh_JobEntry = null;
vh_JobEntry = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.JobEntr
ySvcContract>(Db);
vh_JobEntry.GetNextMntJobNum(out opNextJobNum);

Epicor Kinetic | Classic 9


Calling BOs From Custom Code C# Programming Guide (conversion from ABL)

Calling BOs From Custom Code

Use the following approach when calling Business Objects from the Custom Code Action or Condition.
The proper way of performing a call to other BO is to dispose service instance retrieved through the ServiceRenderer.
Note When you retrieve a service instance through the ServiceRenderer, the service is called in a way that
triggers all BPM directives attached to the method being called. Therefore, if a directive calls the same
method it is attached to, the current directive and all other directives utilizing the same method should be
designed to avoid endless loops. For example:

using (var tip = ServiceRenderer.GetService<Ice.TipSvcContract>.GetService(this


.Db))
{
var loadedTipTS = tip.GetByID(147);
var loadedTip = loadedTipTS.Tip.First();
// ...Code utilizing the data from loadedTip
//...
}

10 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Calling Method From Standard Assembly

Calling Method From Standard Assembly

Use the following steps when calling a method from another assembly (non-external assembly).

1. Add a Reference to required assemblies:

2. Add the necessary Using clauses:

3. You can now construct the code that calls a method from another assembly. For example:
Erp.ErpContext DbErp = CallContext.Current.GetMainContext<Erp.ErpContext>()
;
Erp.Internal.MR.CPMethod _CPMethod = new Erp.Internal.MR.CPMethod(dbErp);
List<Erp.Internal.MR.TgtJob> tgtJobRows = new List<Erp.Internal.MR.TgtJob>
();
Guid p1 = Guid.Empty;
bool crossplant = false;
DateTime? copyDate = null;
string copyRev = string.Empty;
string copyJob = string.Empty;
int pCount = 0;
_CPMethod._CPMethod(p1, "", "", "", null, true, false, false, false, out c
rossplant,
out copyDate, out copyJob, out copyRev, ref pCount, ref tgtJobRows);

Epicor Kinetic | Classic 11


Accessing ERP Context From Within System Directive C# Programming Guide (conversion from ABL)

Accessing ERP Context From Within System Directive

You can access Erp Context from within a system directive. For example, review the following code:
Erp.ErpContext DbErp = CallContext.Current.GetMainContext<Erp.ErpContext>();
In a BPM directive created for system BO "UD10", you need to access Part table which belongs to Erp context.
The code should look like the following:
Erp.ErpContext DbErp = CallContext.Current.GetMainContext<Erp.ErpContext>();
if ((from Part_Row in DbErp.Part
where string.Compare(Part_Row.Company, ttUD10_xRow.Company, true) == 0
select Part_Row).Any())
{
...
}
Note At the moment, the BPM context is either ErpContext (for application directives) or IceContext (for
system directives). Therefore, within the IceContext, you will not find certain ERP tables (for example, Part),
and you will need to use the process described above.
Epicor is working towards enhancing the BPM and making the default context unified. Once this is
implemented, the need of accessing another context within the BPM code will be eliminated.

12 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Displaying an Informational Message

Displaying an Informational Message

Informational messages are useful when debugging BPMs and presenting non-exception type messages to the
users.
You can publish an informational message to the user from the execute custom code node in the BPM designer.
The syntax example is shown below:
this.PublishInfoMessage(Variable or String of the message,
Ice.Common.BusinessObjectMessageType.Information,
Ice.Bpm.InfoMessageDisplayMode.Individual, "YourBO", "YourMethod");
The last two parameters are optional, you can leave the strings empty.
Note that the possible values for message type are: Information, Question, Warning, Error, UpdateConflict. The
possible values for the InfoMessageDisplayMode are: Individual & Grid.
Example message was placed in vMiscInfo variable. Note that you should not use all capitalized "BPM" in the
reference to the Display Mode parameter, it should be "Bpm".
this.PublishInfoMessage(vMiscInfo, Ice.Common.BusinessObjectMessageType.Informa
tion,
Ice.Bpm.InfoMessageDisplayMode.Individual, "OrderHed", "ChangeNeedB
yDate");

Epicor Kinetic | Classic 13


Throwing Exceptions C# Programming Guide (conversion from ABL)

Throwing Exceptions

Exceptions are used to indicate that an error has occurred while the program is running.
The following example displays how you can throw a business exception from within the code:
throw new Ice.BLException("My exception");
Note Within the BPM Workflow Designer, the Raise Exception workflow element is the preferred way of
throwing exceptions.

14 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Writing Messages to Log

Writing Messages to Log

This topic discusses how you can write a message to the server log.
In Epicor 9, the following syntax was used:
Message "test1" "test2".
In Epicor ERP 10, writing to the server log is approached using the following syntax:
Ice.Diagnostics.Log.WriteEntry("TEST1" + " " + "test2");

Epicor Kinetic | Classic 15


Compatibility Class C# Programming Guide (conversion from ABL)

Compatibility Class

If the Compatibility class is present in the converted code, you need to add a reference to Compatibility.dll, which
can be found in the Assemblies folder.
This class contains conversion routines and helper methods to comply with Progress ABL code functionality.
• ABL Code:
FIND FIRST ttUD08 NO-LOCK NO-ERROR.
IF Available ttUD08 THEN DO:
DEFINE VAR va-value AS INTEGER NO-UNDO.
ASSIGN va-value = INTEGER(ttUD08.Key3) NO-ERROR.
IF error-status:error THEN ASSIGN ttCallContextBpmData.Character01 = "invali
d".
END.

• C# Code:
var ttUD08_xRow = (from ttUD08_Row in ttUD08 select ttUD08_Row).FirstOrDefaul
t();
if (ttUD08_xRow != null)
{
int va_value = 0;
try
{
Compatibility.ErrorStatus.Clear();
va_value = System.Convert.ToInt32(ttUD08_xRow.Key3);
}
catch (Exception e)
{
Compatibility.ErrorStatus.SetError(e);
}
if (Compatibility.ErrorStatus.Error)
{
this.callContextBpmData["Character01"] = "invalid";
}
}

16 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Replacing Resulting Dataset

Replacing Resulting Dataset

This topic describes how to replace your directive's resulting record with another record.
For example, you may have a method directive action on the GetByID() method which returns a Part ID. The usual
BPM procedure provides the directive code with retrieved Part record, which is held in the ttPart table. If, for
some reason, you want to replace the whole resulting record with another record that you may have retrieved
yourself using another method, then you have the following options:
• Set the values on ttPart
• Replace the whole dataset
Note Fetching the dataset and replacing the whole dataset using the Attach method eliminates the need
of writing a lot of code. This is especially useful when the resulting dataset contains several tables.

In the following example, the record is retrieved using a sample method FetchRecord() and the result is replaced
with myRec.
var myRec = myService.FetchRecord(id);
this.dsHolder.Attach(myRec);

Epicor Kinetic | Classic 17


BufferCopy Considerations C# Programming Guide (conversion from ABL)

BufferCopy Considerations

BufferCopy is used to copy from source buffer to target buffer.


For example, in the below declaration, the b2 reference is not initialized:
BufferCopy .CopyExceptFor(b1, b2)
The declaration of b2 then looks like the following:
Erp.Tables.Part b2;
As a resolution, correct the declaration as following:
Erp.Tables.Part b2 = new Erp.Tables.Part();
If BufferCopy is used in a loop, then b2 needs to be assigned a new instance on each iteration, otherwise it will
lose the values from the previous iteration.

18 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Working With Custom Data

Working With Custom Data

The following section describes what you should consider when working custom columns and their usage within
the BPM functionality.
In the previous versions of the Epicor application, each table included a series of user-defined fields, such as
Character01, Date03, Number02. While these additional columns were mostly sufficient for customizations,
some users ran out of available columns. Then, during an upgrade, these user-defined fields were overwritten,
so users needed to export customizations and, after the upgrade was complete, import them back into the
application. This process sometimes required users to additionally update the tables/columns to make the
customization compatible with the new application version. In other Epicor installations, the predefined user-defined
columns might not have been used at all, and were causing unnecessary system load.
To address these issues, the Epicor application now incorporates this functionality as a database extension. If you
are building a BPM directive that needs unique fields, you create user-defined tables and add columns to these
tables. Since these tables are extensions from parent tables, they are not overwritten during an upgrade.

Create User-Defined Fields

In Epicor ERP 10, use the following process to create user-defined fields.
Note The existing Epicor 9.05 user-defined columns that were utilized (contained data) are automatically
migrated to Epicor ERP 10.

1. Create the user-defined tables and columns (fields) within the Epicor application. To do this, navigate to
System Setup > System Maintenance and open User Defined Column Maintenance.

2. Find and select the table you need to extend. Click Save. The application adds a "_UD" suffix to the end
of the table identifier.

3. Add the needed custom columns to the user-defined table that you have created.
When you save each column, the application adds a "_c" suffix to the end of this table column. This suffix
will help you identify this custom column when you create BPM directives that reference this column.

Epicor Kinetic | Classic 19


Working With Custom Data C# Programming Guide (conversion from ABL)

4. Add the user-defined table to the database. To do this, regenerate the data model for the Epicor database.
You or your system administrator runs this task on the server that hosts your database. The data model is
regenerated using the Epicor Administration Console.
Important To regenerate data model and recycle application pools in Epicor Cloud ERP (Dedicated
Tenant) environment, you must promptly submit a request via an EpicCare case and specify the
location of the newly added user-defined data (table/column) - System, Live, or Pilot, - and the best
time the Cloud Operations team can run these processes.

As a result, the base and the user-defined tables are joined in the data model.

5. Now to complete this process, you must pull the latest data model from the database and copy it to the
local application server by recycling the application pool. Recycling the application pool is a mandatory task
after the data model successfully regenerates. To do this, click Start > Programs > Administrative Tools
> Internet Information Services (IIS) Manager.

6. Select the Application Pools node.


The center pane displays the application pools available on your system.

7. Right-click on the application pool for your application server; from the context menu, select Recycle.
Tip Optionally you can also recycle the application pool within the Epicor Administration Console.
To do this, expand the Server Management node and select your application server. From the
Actions pane, select the Recycle IIS Application Pool option.

The user-defined table and columns are now fully integrated with the database. You can now monitor and update
these custom columns through BPM directives.
Tip Unlike previous versions of the Epicor application, running the Directive Update program and Refreshing
Signatures of directives referencing affected table(s) with UD columns is no longer needed. This process is
performed automatically.

20 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Working With Custom Data

Access User-Defined Fields

This topic describes how you can reference user-defined columns within BPM in Epicor ERP 10.

1. User defined tables and primary tables merge into one for the database context. When you construct a Data
or Method Directive workflow, user-defined columns display as part of the base table.

2. When you design a business activity query (BAQ) in BPM from within Data and Method Directives, user-defined
columns also appear as part of the base table.

3. When working with dataset tables for Data Directives, user-defined fields appear as part of the primary
table, and can be directly accessed. For example:
ttABCCode.MyNewColumn_c

4. Method directives work with dataset tables using service method parameters of tableset type, and so you
access them through different syntax. These tableset parameters are defined in the business object's (BO)
contract .dll file, so their format is fixed and they are not regenerated with the data model. When building
a method directive that refers to a user-defined field in a dataset table, reference it using the following
syntax: <MethodParameterName>.<TableName>.["UDField<DataType>(UDFieldName)"]. For example:
ds.ABCCode.UDField<System.String>("MyNewColumn _c")

Epicor Kinetic | Classic 21


Working With Custom Data C# Programming Guide (conversion from ABL)

Loading User-Defined Data Into Various Objects

This topic discusses available extensions you can use to load User-Defined (UD) Data into various objects.
Note UD data is loaded automatically into table entities when data is retrieved from the database. You
may only need to manually load UD data into IceTablesets, IceTables or IceRow. Examples are below:

• IceRow:
Ice.Tablesets.TipRow iceRow = new TipRow();

iceRow.LoadExtendedData(Db);

• IceTable:
Ice.Tablesets.TipTable iceTable = new TipTable();

iceTable.LoadExtendedData(Db);

• List<IceRow>:
List<IceRow> list = new List<IceRow>();

list.LoadExtendedData(Db);

• IceTableset:
Ice.Tablesets.TipTableset ts = new TipTableset();

ts.LoadExtendedData(Db);

Transition Path

This topic explains actions you need to take when you migrate UD Fields from Epicor ERP 10 Beta code prior to
10.0.600 into Epicor ERP version 10.0.600 or later.
Note You only need to perform the following steps if you are migrating from Epicor ERP 10 Beta prior to
version 10.0.600.

1. As part of the installation process, Regeneration of the Data Model is performed. This ensures the UD fields
are included in the data model.

2. The next step involves running mandatory BPM conversion. The conversion process upgrades BPM directives
to follow the new extended data approach.

3. After you log into the application, Epicor recommends to review the following:
• BPM directives that have become Outdated (as it is usual when upgrading from older builds)
• BPM directives that are known to contain UD Field references in queries or custom codes. It is highly
recommended to review these directive to ensure their performance is optimized in the new environment.

22 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Working With Custom Data

Example
• Previously for database queries a join between the user-defined table and base table was used,
for example:
Ice.Tip join Ice.Tip_UD on t.SysRowID = tu.ForeignSysRowID
The migration process is likely to convert such code as follows:
Ice.Tip join Ice.Tip_UD on t.SysRowID = tu.SysRowID
If the above code compiles, it is sub-optimal. Because the data model now merges UD Table
columns with the respective base table automatically, you should refer to the Tip entity object only
to eliminate references or joins to Tip_UD altogether.
• Custom code performing [loop on Tip from the DB] with inner [loop on Tip_UD from the DB]
This code will be converted to [loop on Tip from the DB] with inner [loop on Tip from the DB]. The
most optimal way is [loop on Tip from the DB], however the body of the loop will need to be
adjusted.

4. When importing BPM directives referencing UD fields created in the code prior to 10.0.600, these directives
are also automatically converted and should be reviewed for optimal performance.

Epicor Kinetic | Classic 23


Obsolete ABL Code C# Programming Guide (conversion from ABL)

Obsolete ABL Code

Certain calls that existed in Epicor 9.05 became obsolete in Epicor ERP 10 and need to be removed.
For example, you may have an Epicor 9.05 BPM directive that uses an ABL action with code that calls
lib\UpdateTableBuffer.p.
To avoid compilation errors when converting such directive, remove the below call in your converted code:
lib\UpdateTableBuffer.p

24 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Using External Libraries

Using External Libraries

Please consider the following information when selecting the approach for migrating external .p routines from
Epicor 9.05 to Epicor ERP 10.
In certain cases when Epicor 9.05 customizations contained many calls to external .p progress code files, it may
be reasonable to combine these .p routines into a single .NET assembly (library) of external methods. These
methods can then be called from within a BPM flowchart using the Invoke External Method BPM element.
You can also consider creating an external library, when you called a method in the Epicor 9.05 external .p file,
which subsequently invoked other methods within the same or different .p code files.
However, if the external .p contained simple code in one or few methods, it is recommended to convert such
code into a snippet and incorporate it into the BPM Execute Custom Code element, rather than creating an
external library assembly. Please note that this approach may be preferred as:
• It does not require .NET Visual Studio
• It does not require maintaining and updating external assemblies

Create Project in Visual Studio

The following section describes how to create a new project using the Visual Studio.
In the example below, the project utilizes the Update Method Directive for Tip Service.

1. Create an empty C# Class Library Type Project. In this example, we create the project called "ExternalBpm".

2. Add the following references from the Server Assemblies Folder:

Epicor Kinetic | Classic 25


Using External Libraries C# Programming Guide (conversion from ABL)

Note that the Ice.Contracts.BO.Tip is added, as we are going to make a call from Tip Update BPM Method
Directive.
Important Make sure to set "Copy Local=False" for all added references.

3. The project adds the Class by default. You can rename it, if you want. In this example, MyTip.cs is used.

Call External Assembly Using BPM

1. In your Epicor EPM 10 application, open Method Directives. To do this, navigate to System Management
> Business Process management > Method Directives Maintenance.

2. Locate the Ice.Tip.Update method.

3. From the Actions menu, select Create Programming Interface.

4. The Programming Interface Generator Form window displays the method signature.

5. Copy the whole code (or method, if you already have other methods there).

6. Add your logic into the method and compile the project.

7. Place the library assembly file to the folder for external assemblies. Usually, it is the folder located within
the Server\Customization\Externals folder, but this setting can be changed in the application's
web.config, within the CustomizationSettings property.

26 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Using External Libraries

8. You can now use this library and call it using the Invoke External Method workflow element.

9. Select the external assembly by clicking the external link.

10. Select the method using the specified method link.

The external Update method from ExternalBpm assembly will now be called by the BPM.

Epicor Kinetic | Classic 27


Using External Libraries C# Programming Guide (conversion from ABL)

Tip For more detailed example, review the Custom Business Process Management chapter found
within the Epicor ICE 3.0 Tools User Guide.

28 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Debugging Using Visual Studio

Debugging Using Visual Studio

If you have Microsoft® Visual Studio™ 2010 or higher, you can debug execution of custom code directives.
Debugging can be particularly useful when you need to review execution of a complex custom code.

Prerequisites

This topic discusses steps you need take to before you start debugging.
The Epicor Customization Framework (ECF) supports two ways of storing generated assemblies. The preferred
method, which is either SQL BLOB (Binary Large Object) or File System Storage is defined in the Epicor ERP 10
web.config file within the customizationStorage provider property.
Before you start debugging, do the following:
• In order to load the program database (pdb) file that holds debugging symbols, verify the loadPdb property
found in the web.config is set to true.
loadPdb ="true"

• Verify the intermediateFolder, where directive sources are generated contains in a valid path. For example:
intermediateFolder="C:\_projects\2012R\Current\Deployment\Server\BPM">
Example Your web.config settings may look as follows:
<customizationSettings
loadPdb ="true"
disabled="false"
intermediateFolder="C:\_projects\2012R\Current\Deployment\Server\BPM">

<customizationStorage provider="SqlBlob" settings="" />


<externalsStorage provider="FileSystem" settings="C:\_projects\2012R\Cu
rrent\Deployment\Server\Customization\Externals" />
....

• To reload customization assembly and debug symbols, restart IIS. Alternatively, only restart the Epicor ERP
application pool.

Debug BPM Directive

This topic explains how you can debug customization assembly compiled by the Epicor Customization Framework.

1. By default, sources are found in the BPM folder of the Server directory.

Epicor Kinetic | Classic 29


Debugging Using Visual Studio C# Programming Guide (conversion from ABL)

Note A different sources folder can be specified using the intermediateFolder attribute in the server
web.config file.
Make sure that the folder specified in the intermediateFolder attribute exists at the time IIS AppPool
used by the Epicor 10 application starts. Also, verify the account used by that AppPool has read and
write access to that folder. Otherwise, the setting is ignored and sources are saved in the system's
TEMP folder.

2. Notice each folder contains all BPM revisions.

Important New sources are generated each time you save the directive.

3. Make sure you are working with the latest BPM sources when debugging a directive. Drag and drop all files
into the Microsoft Visual Studio.

30 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Debugging Using Visual Studio

4. For debugging Options, make sure the Enable Just My Code and Require source files to exactly match
the original version options are clear.

5. Attach the debugger to the w3wp.exe process the application pool is running under.

6. Now you can set the breakpoint in the custom code.

7. Run the routine in the Epicor client. When the BPM customization is fired, the breakpoint is activated and
you can verify each step in the Visual Studio.

Epicor Kinetic | Classic 31


Debugging Using Visual Studio C# Programming Guide (conversion from ABL)

8. If the breakpoint is not hit, do the following:

a. Close the Epicor client.

b. Restart IIS, or Epicor server application pool.

c. Launch the Epicor client again and regenerate the directive to update directive sources.

Tip By default, IIS7 app pool can only use 90 seconds for a non-responsive application. During IIS
web application or website debugging time, you may want to change its corresponding application
Pool advanced setting's "Ping Maximum Response Time" to a time much longer, or turn off "Ping
Enabled" setting.

Debug Custom Project

This topic outlines how you can debug custom project or solution created in Visual Studio.
In this example, a project is used to define the programming logic for a custom AbcCode.GetList() external method
written in C# .NET.

1. When building an external assembly project, make sure that:


• Project assembly name and namespace are specified. Assembly must be the same as initial customization
assembly name, for example Erp.Bpm.BO.ABCCode.GetList. A common practise is assembly name and
assembly filename be the same.
• All project references and relative paths are properly specified. In this example, project references from
Server\Bin and Server\Assemblies folders located in the Epicor ERP 10 server installation are used.
• A project is compiled and external method .dll file is placed in the External Storage folder. Typically its
directory path is the Server\Customization\Externals location.

32 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Debugging Using Visual Studio

2. Invoke the .NET external method you created using a directive. In this example, a post-processing directive
for ABC.GetList BO method is used.

3. In Visual Studio, attach the debugger to the w3wp.exe process the application pool is running under.

4. Set the breakpoint in the custom code and run the routine you want to debug in the Epicor client.

5. At this point the debugger stops at the specified break point and you can the follow code execution, examine
variable values and so on.

Epicor Kinetic | Classic 33


Debugging Using Visual Studio C# Programming Guide (conversion from ABL)

34 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Adding and Subtracting Date

Adding and Subtracting Date

To add or subtract days from a given date field, use the syntax as displayed below. Note that subtraction is done
by adding a negative number of days.
In the following example, 6 days are substracted from NeedByDate:
ttOrderHedRow.NeedByDate.Value.AddDays(-6)

Epicor Kinetic | Classic 35


Subtracting Two Dates and Comparing to an Integer Value C# Programming Guide (conversion from ABL)

Subtracting Two Dates and Comparing to an Integer Value

Use the following syntax to subtract one date from another and then compare the number of days difference to
an integer value.
Difference and Days are variable names and can be replaced with any variable name of your choice.
var difference = DateTime.Today - PartTran.TranDate;
var days = difference.Value.Days;
if ( days > 90)

36 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Migrating ABL Expressions

Migrating ABL Expressions

The BPM Migration Tool is capable of migrating most of the valid Epicor 9.05 ABL expressions.
To ensure a successful migration of expressions, verify the following:
• ABL expressions are functional in your Epicor 9.05 application.
• All tables, arguments and functions used in expressions are known.

Epicor Kinetic | Classic 37


ABL Find Last Conversion C# Programming Guide (conversion from ABL)

ABL Find Last Conversion

This topic discusses code adjustments when converting ABL Find Last statement.
Assume the below statement exists in a BPM.
FIND LAST Parttran where
Parttran.company = CUR-COMP and
Parttran.partnum = Quotemtl.partnum and
(Parttran.trantype = "STK-MTL" OR
Parttran.trantype = "MFG-STK" OR
Parttran.trantype = "PUR-MTL" OR
Parttran.trantype = "MFG-CUS" )
no-lock no-error.
Below is an example of a converted statement. Note that OrderBy clause is used to sort the data. In this example,
the last record becomes the first record in the returned rows:
PartTran = (from PartTran_Row in Db.PartTran
where PartTran_Row.Company == Session.CompanyID &&
string.Compare(PartTran_Row.PartNum, QuoteMtl.PartNum, true) == 0
&&
(string.Compare(PartTran_Row.TranType, "STK-MTL", true) == 0 ||
string.Compare(PartTran_Row.TranType, "MFG-STK", true) == 0 ||
string.Compare(PartTran_Row.TranType, "PUR-MTL", true) == 0 ||
string.Compare(PartTran_Row.TranType, "MFG-CUS", true) == 0)
orderby PartTran_Row.TranDate descending
select PartTran_Row).FirstOrDefault();

38 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Using Unassigned Local Variable Message

Using Unassigned Local Variable Message

Within the BPM logic, it may happen that a variable is not set. In that case, the compiler displays an error message,
reporting use of an unassigned local variable.
Review the following example:
Erp.Tables.Customer Customer;
Erp.Tables.Customer_UD Customer_UD;

foreach (var ttCustomer_Row in ttCustomer)


{
using (var txscope = IceDataContext.CreateDefaultTransactionScope())
{

Customer = (from Customer_Row in Db.Customer.With(LockHint.UpdLock)


where Customer_Row.Company == ttCustomer_Row.Company &&
string.Compare(Customer_Row.CustID, ttCustomer_Row.CustID, true) == 0
select Customer_Row).FirstOrDefault();

if (Customer != null)

Customer_UD = (from Customer_UD_Row in Db.Customer_UD.With(LockHint.UpdLock)

where Customer.SysRowID == Customer_UD_Row.ForeignSysRowID


select Customer_UD_Row).FirstOrDefault();

Customer_UD.CheckBox01 = true;
Customer.CreditLimit = 0;
Customer.CreditHold = false;
Db.Validate();
txscope.Complete();
}
}
This code will generate the following error upon compilation:
Server Side Exception
There is at least one compilation error.
Exception caught in: Epicor.ServiceModel
Error Detail
============
Description: There is at least one compilation error.
Details:
Error CS0165: Use of unassigned local variable 'Customer_UD'
Notice that in the code example, the Customer_UD variable is only set when the following condition is met:
if (Customer != null)
You can correct the error by changing the second line of the example to:
Erp.Tables.Customer_UD Customer_UD = null;
This change ensures that the variable gets set to some value regardless of the IF statement.

Epicor Kinetic | Classic 39


Epicor 10 Equivalent to Row Mod = A or U C# Programming Guide (conversion from ABL)

Epicor 10 Equivalent to Row Mod = A or U

In many BPMs a user wants to perform a certain action if the tt record is a new record or updated record. In
Epicor 9.05 the status of the RowMod = 'A' or 'U' was used in this case. The Epicor 10 equivalent is displayed
below:
foreach (var ttAPInvHed_iterator in (from ttAPInvHed_Row in ttAPInvHed
where (string.Equals(ttAPInvHed_Row.RowMod, IceRow.ROWSTATE_ADDED,
StringComparison.OrdinalIgnoreCase) ||
string.Equals(ttAPInvHed_Row.RowMod, IceRow.ROWSTATE_UPDATED,
StringComparison.OrdinalIgnoreCase))
select ttAPInvHed_Row))

40 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Converting a Number to a String in LINQ Expression

Converting a Number to a String in LINQ Expression

Review an example of a LINQ query below:


foreach (var XFileAttch_Recs in (from XFileAttch_Row in Db.XFileAttch
where XFileAttch_Row.Key1 == ttInvcHeadRow.InvoiceNum.ToString()
&&(string.Compare(XFileAttch_Row.RelatedToFile,"InvcHead",true)==0)
select XFileAttch_Row))
You may receive the following exception error at runtime:
Server Side Exception
LINQ to Entities does not recognize the method 'System.String ToString()' metho
d, and this method cannot be translated into a store expression.
Exception caught in: Epicor.ServiceModel
Error Detail
============
Description: LINQ to Entities does not recognize the method 'System.String ToS
tring()' method, and this method cannot be translated into a store expression.
Program: System.Data.Entity.dll
Method: Translate
Original Exception Type: NotSupportedException
The corrected code looks like the following:
foreach (var XFileAttch_Recs in (from XFileAttch_Row in Db.XFileAttch
where XFileAttch_Row.Key1 ==
SqlFunctions.StringConvert((double)ttInvcHeadRow.InvoiceNum).Trim()
&&(string.Compare(XFileAttch_Row.RelatedToFile,"InvcHead",true)==0)
select XFileAttch_Row))
You should also add the using reference below in the BPM Designer > Usings tab:
using System.Data.Objects.SqlClient;

Epicor Kinetic | Classic 41


Outputting Data to a File C# Programming Guide (conversion from ABL)

Outputting Data to a File

The syntax shown below can be used for BPMs that write data out to a file. You can create a new file as in the
example below, or replace Create with Append to append to an existing file.
using (var MyFile = new System.IO.StreamWriter(new System.IO.FileStream(Path to
file goes here), System.IO.FileMode.Create))
{ // MyFile Scope starts
MyFile.WriteLine( The data you wish to export goes here)
} // MyFile Scope ends
The relevant syntax is bolded in the example below:
using (var MyFile = new System.IO.StreamWriter(new
System.IO.FileStream(Company.UDField<string>( "Character01"),
System.IO.FileMode.Create)))
{ // MyFile Scope starts
if (!String.IsNullOrEmpty(OrderList))
{
for (i = 1; i <= OrderList.NumEntries("~"); i++)
{
foreach (var OrderRel_iterator in (from OrderRel_Row in Db.OrderRel
where OrderRel_Row.Company == Session.CompanyID
&& OrderRel_Row.OrderNum ==
System.Convert.ToInt32(OrderList.Entry(i - 1, '~'))
&& OrderRel_Row.NeedByDate >= FromDate
&& OrderRel_Row.NeedByDate <= ToDate
select OrderRel_Row))
{
OrderRel = OrderRel_iterator;
PartNum = OrderRel.PartNum;
PlantID = OrderRel.Plant;
Balance = OrderRel.OurStockQty - OrderRel.OurStockShippedQty;
if (Balance > 0)
{
Quantity = Balance.ToString("99999999");
}
OutOrderNum = OrderRel.OrderNum.ToString("99999999999");
LineNum = OrderRel.OrderLine.ToString("999");
if (Balance > 0)
{
foreach (var PlantWhse_iterator in (from PlantWhse_Row in
Db.PlantWhse
where PlantWhse_Row.Company ==
Session.CompanyID
&&
string.Compare(PlantWhse_Row.WarehouseCode, "13120", true) == 0
&&
string.Compare(PlantWhse_Row.PartNum, PartNum, true) == 0
&&
string.Compare(PlantWhse_Row.PrimBin, "CARMAN", true) == 0
select PlantWhse_Row))
{
PlantWhse = PlantWhse_iterator;
MyFile.WriteLine(@" ; ; ; ; ;" + PartNum + ";" +
Quantity + "; ; ; ; ; ;
; ;SO" + OutOrderNum + ";" + OutOrderNum + ";" + LineNum + "; ;" +
PlantID + ";CARMAN ;");
}
}

42 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Outputting Data to a File

}
}
}
else
{
foreach (var OrderRel_iterator in (from OrderRel_Row in Db.OrderRel
where OrderRel_Row.Company == Session.CompanyID
&& OrderRel_Row.NeedByDate >= FromDate &&
OrderRel_Row.NeedByDate <= ToDate
select OrderRel_Row))
{
OrderRel = OrderRel_iterator;
PartNum = OrderRel.PartNum;
PlantID = OrderRel.Plant;
Balance = OrderRel.OurStockQty - OrderRel.OurStockShippedQty;
if (Balance > 0)
{
Quantity = Balance.ToString("99999999");
foreach (var PlantWhse_iterator in (from PlantWhse_Row in
Db.PlantWhse
where PlantWhse_Row.Company ==
Session.CompanyID
&&
string.Compare(PlantWhse_Row.WarehouseCode, "13120", true) == 0
&&
string.Compare(PlantWhse_Row.PartNum, PartNum, true) == 0
&&
string.Compare(PlantWhse_Row.PrimBin, "CARMAN", true) == 0
select PlantWhse_Row))
{
PlantWhse = PlantWhse_iterator;
MyFile.WriteLine(@" ; ; ; ; ;" + PartNum + ";" +
Quantity + "; ; ; ; ; ; ;
;SO" + OutOrderNum + ";" + OutOrderNum + ";" + LineNum + "; ;" +
PlantID + ";CARMAN ;");
}
}
}
}
} // MyFile Scope ends

Epicor Kinetic | Classic 43


Updating Database Tables C# Programming Guide (conversion from ABL)

Updating Database Tables

This example shows how to scope the transaction for updating the InvcDetail_UD table's Number 01 field.
The Using statement starts the transaction, the Db.Validate statement does the update, and the txscope statement
ends the transaction. Note that the select query adds a With(LockHint.UpdLock) phrase to the select statement
for the table to be updated (the InvcDtl_UD table in this example).
using (var txscope = IceDataContext.CreateDefaultTransactionScope())
{
var InvcDtlRecs = (from InvcDtl_UD_Row in Db.InvcDtl_UD.With(LockHint.UpdLock)

where InvcDtl.SysRowID == InvcDtl_


UD_Row.ForeignSysRowID
select InvcDtl_UD_Row).FirstOrDefault();
foreach (var OrderDtlRecs in (from r in Db.OrderDtl

from r_UD in Db.OrderDtl_UD


where r.SysRowID == r_UD.ForeignSysRowID &&
r.Company == Session.CompanyID &&
r.OrderNum == InvcDtl.OrderNum &&
r.OrderLine == InvcDtl.OrderLine
select r_UD))
{
if (InvcDtlRecs.Number01 != OrderDtlRecs.Number01)

{
InvcDtlRecs.Number01 = OrderDtlRecs.Number
01;
Db.Validate();
}
}
txscope.Complete();
}

44 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Sample of BPM calling a .p file and the final Epicor 10 version

Sample of BPM calling a .p file and the final Epicor 10 version

Epicor 9.05, ABL Code was calling a .p file that called the UD14 business object and added certain data to it. In
Epicor 10, the BPM was modified to call the business objects and the .p code moved into the BPM's Execute
Custom Code action.
• Epicor 9.05 ABL Code:
FOR EACH ttOrderHed:
RUN DSTICustom\BPMLive\SalesOrder\StoreSORepsInUD14.p (input ttOrderHed.Order
Num, input ttOrderHed.SalesRepList,
input ttOrderHed.RepSplit1, input ttOrderHed.RepSplit2, input ttOrderHed.RepS
plit3, input ttOrderHed.RepSplit4,
input ttOrderHed.RepSplit5, input ttOrderHed.RepRate1, input ttOrderHed.RepRa
te2, input ttOrderHed.RepRate3,
input ttOrderHed.RepRate4, input ttOrderHed.RepRate5).
END.

• Epicor 9.05 .P Files Contents:


{bo/UD14/UD14_ds.i}
{core/CallContext/CallContext.i}

define input parameter iOrderNum as integer.


define input parameter cRepList as character.
define input parameter iRepSplit1 as integer.
define input parameter iRepSplit2 as integer.
define input parameter iRepSplit3 as integer.
define input parameter iRepSplit4 as integer.
define input parameter iRepSplit5 as integer.
define input parameter dRepRate1 as decimal.
define input parameter dRepRate2 as decimal.
define input parameter dRepRate3 as decimal.
define input parameter dRepRate4 as decimal.
define input parameter dRepRate5 as decimal.

{&TRY_PRIVATE}
define variable morePages as logical.
define variable i as integer.
define variable hUD14 as handle.
RUN bo\UD14\UD14.p PERSISTENT SET hUD14.

if VALID-HANDLE (hUD14) THEN DO:


RUN GetRows IN hUD14 (input "Key1 = '" + STRING(iOrde
rNum) + "'", input "", {&output_dataset_UD14DataSet}, input 0, input 0, outpu
t morePages) NO-ERROR.

/* Delete existing rows for this sales order before r


ebuilding */
for each ttUD14:
RUN DeleteByID IN hUD14 (input ttUD14.Key
1, input ttUD14.Key2, input ttUD14.Key3, input ttUD14.Key4, input ttUD14.Key5
) NO-ERROR.
end.

/* rerun to clear out temp tables */

RUN GetRows IN hUD14 (input "Key1 = '" + STRING(iOrde


rNum) + "'", input "", {&output_dataset_UD14DataSet}, input 0, input 0, outpu
t morePages) NO-ERROR.

Epicor Kinetic | Classic 45


Sample of BPM calling a .p file and the final Epicor 10 version C# Programming Guide (conversion from ABL)

repeat i = 1 TO num-entries(cRepList, "~~"):


RUN GetaNewUD14 IN hUD14 ( {&input-output
_dataset_UD14DataSet} ) NO-ERROR.

for each ttUD14 where ttUD14.RowMod = "A"


:
assign ttUD14.Key1 = STRING(i
OrderNum).
assign ttUD14.Key2 = STRING(i
).
assign ttUD14.ShortChar01 = e
ntry(i, cRepList, "~~").
assign ttUD14.Number01 = iOrd
erNum.
if i = 1 then do:
assign ttUD14.Number02 = iRepSplit1.
assign ttUD14.Number03 = dRepRate1.
end.
else if i = 2 then do:
assign ttUD14.Number02 = iRepSplit2.
assign ttUD14.Number03 = dRepRate2.
end.
else if i = 3 then do:
assign ttUD14.Number02 = iRepSplit3.
assign ttUD14.Number03 = dRepRate3.
end.
else if i = 4 then do:
assign ttUD14.Number02 = iRepSplit4.
assign ttUD14.Number03 = dRepRate4.
end.
else if i = 5 then do:
assign ttUD14.Number02 = iRepSplit5.
assign ttUD14.Number03 = dRepRate5.
end.
end.

RUN Update IN hUD14 ({&input-output_datas


et_UD14DataSet}) NO-ERROR.
end.
end.
else do:
message "Could not create handle to UD14.p!".
end.

delete object hUD14.

{&CATCH_PRIVATE}

• Final Epicor 10 C# Code that Compiled in BPM designer:


foreach (var ttOrderHed_xRow in ttOrderHed)
{
var ttOrderHedRow = ttOrderHe
d_xRow;
UD14Tableset UD14DataSet = ne
w UD14Tableset();
UD14Tableset output_dataset_UD14DataSet = null;
int iOrderNum = 0;
string cRepList = string.Empty;
int iRepSplit1 = 0;
int iRepSplit2 = 0;
int iRepSplit3 = 0;
int iRepSplit4 = 0;

46 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Sample of BPM calling a .p file and the final Epicor 10 version

int iRepSplit5 = 0;
decimal dRepRate1 = decimal.Zero;
decimal dRepRate2 = decimal.Zero;
decimal dRepRate3 = decimal.Zero;
decimal dRepRate4 = decimal.Zero;
decimal dRepRate5 = decimal.Zero;
bool morePages = false;
int i = 0;
Ice.Contracts.UD14SvcContract hUD14 = Ice.Assemblies.ServiceRende
rer.GetService<Ice.Contracts.UD14SvcContract>(Db);
if (hUD14 != null)
{
output_dataset_UD14DataSet = hUD14.GetRows("Key1 = '" + Syste
m.Convert.ToString(iOrderNum) + "'", 0, 0, out morePages);
/* Delete existing rows for this sales order before rebuildin
g */
foreach (var ttUD14_xRow in output_dataset_UD14DataSet.UD14)
{
var ttUD14Row = ttUD14_xRow;
hUD14.DeleteByID(ttUD14Row.Key1, ttUD14Row.Key2, ttUD14Ro
w.Key3, ttUD14Row.Key4, ttUD14Row.Key5);
}
/* rerun to clear out temp tables */
output_dataset_UD14DataSet = hUD14.GetRows("Key1 = '" + Syste
m.Convert.ToString(iOrderNum) + "'", 0, 0, out morePages);
for (i = 1; i <= cRepList.NumEntries("~"); i++)
{
hUD14.GetaNewUD14(ref UD14DataSet);
foreach (var ttUD14_iterator in (from ttUD14_Row in outpu
t_dataset_UD14DataSet.UD14
where string.Equals(ttUD
14_Row.RowMod, IceRow.ROWSTATE_ADDED, StringComparison.OrdinalIgnoreCase)
select ttUD14_Row))
{
var ttUD14Row = ttUD14_iterator;
ttUD14Row.Key1 = System.Convert.ToString(iOrderNum);
ttUD14Row.Key2 = System.Convert.ToString(i);
ttUD14Row["ShortChar01"] = cRepList.Entry(i - 1, '~')
;
ttUD14Row["Number01"] = iOrderNum;
if (i == 1)
{
ttUD14Row["Number02"] = iRepSplit1;
ttUD14Row["Number03"] = dRepRate1;
}
else if (i == 2)
{
ttUD14Row["Number02"] = iRepSplit2;
ttUD14Row["Number03"] = dRepRate2;
}
else if (i == 3)
{
ttUD14Row["Number02"] = iRepSplit3;
ttUD14Row["Number03"] = dRepRate3;
}
else if (i == 4)
{
ttUD14Row["Number02"] = iRepSplit4;
ttUD14Row["Number03"] = dRepRate4;
}
else if (i == 5)
{
ttUD14Row["Number02"] = iRepSplit5;
ttUD14Row["Number03"] = dRepRate5;

Epicor Kinetic | Classic 47


Sample of BPM calling a .p file and the final Epicor 10 version C# Programming Guide (conversion from ABL)

}
}
hUD14.Update(ref UD14DataSet);
}
}
else
{
Ice.Diagnostics.Log.WriteEntry("Could not create handle to UD
14.p!");
}
//hUD14.Dispose();
}

48 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Sample of a BPM That Sends an Email

Sample of a BPM That Sends an Email

The following is an example of a C# code that sends email. This code has been generated through the BPM
wizard; you can use it within the BPM Custom Code action.
var mailer = this.GetMailer(async: true);

var message = new Ice.Mail.SmtpMail();

var from =
"from@from.com";

message.SetFrom(from);

var to =
"to@to.com";

message.SetTo(to);

var cc = "";

message.SetCC(cc);

var subject =
"subject line";

message.SetSubject(subject);

var body =
"E-mail message can include scalar and table parameters of the
business method";

message.SetBody(body);

mailer.Send(message);

Epicor Kinetic | Classic 49


Querying the Database C# Programming Guide (conversion from ABL)

Querying the Database

The ICE Framework leverages the Entity Framework that handles all the data connections and represents all
database tables as objects off the Database Context.
The Database object is named Db. Tables are variables off of the Db object which can be used to construct LINQ
queries.
Example

//Query the SysUser Table


var query = from userRow in Db.SysUserFile
where userRow.UserID == Session.UserID
select userRow.SecurityMgr;

50 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Setting a Value on the Payload

Setting a Value on the Payload

BPM custom code can access the payload variables as defined in the CommonTypes file.
There are no "record centric" variables, only set based variables so you will need to query into the sets accordingly.
Example

//Set Any Changes Records To Active


foreach (TipRow ttTipRow in ttTip.Where(row =>
row.RowMod == IceRow.ROWSTATE_ADDED
|| row.RowMod == IceRow.ROWSTATE_UPDATED)
{
ttTipRow.Active = true;
}

Epicor Kinetic | Classic 51


Calling Methods in the Same Service C# Programming Guide (conversion from ABL)

Calling Methods in the Same Service

A service can be accessed via it's contract either from a client or a service.
The service is accessed via the ServiceRenderer helper.
//Get the other service via it's contract
var tipBO =
Ice.Assemblies.ServiceRenderer.GetService<Ice.Contracts.TipSvcContract>();

//Make calls to other service


var ttTipRow = tipBO.GetByID(mfgsys, tipNum);

52 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Calling Other Services

Calling Other Services

Calling other Services requires referencing the other service contract assembly.
Example

Use the References sheet to add the assembly containing the service to be called, for example
Ice.Contracts.BO.UserFile.dll.

You then create the other service via its contract and call it as per the below example.
//Get the other service via it's contract
var userFileBO =
Ice.Assemblies.ServiceRenderer.GetService<Ice.Contracts.UserFileSvcContract>();

//Make calls against the other service


var currentUser = userFileBO.GetByID(Session.UserID);

Epicor Kinetic | Classic 53


Using RowMod C# Programming Guide (conversion from ABL)

Using RowMod

All temp tables contain a column called RowMod at the bottom of the record. This property defines if a record
has been Added, Updated, Changed, or Deleted.
In Epicor 9, RowMod = "A", "U", "D" or "" indicated the action performed against the Row.
The Epicor 10 equivalent may look as follows:
foreach (var ttAPInvHed_iterator in (from ttAPInvHed_Row in ttAPInvHed
where (string.Equals(ttAPInvHed_Row.RowMod, IceRow.ROWSTATE_ADDED, StringCompar
ison.OrdinalIgnoreCase) || string.Equals(ttAPInvHed_Row.RowMod, IceRow.ROWSTATE
_UPDATED, StringComparison.OrdinalIgnoreCase))
select ttAPInvHed_Row))
However, a better shorthand ways are available for use by utilizing the Unchanged() Added() Deleted() or Updated()
methods, for example:
foreach (var ttAPInvHed_iterator in (from ttAPInvHed_Row in ttAPInvHed
where ( ttAPInvHed_Row.Added() || ttAPInvHed_Row.Updated() )
select ttAPInvHed_Row))
The above code can even be written as:
foreach (var ttAPInvHed_iterator in (from ttAPInvHed_Row in ttAPInvHed
where ( ! ttAPInvHed_Row.Unchanged() )
select ttAPInvHed_Row))

54 Epicor Kinetic | Classic


C# Programming Guide (conversion from ABL) Naming Conventions in BPM Sources

Naming Conventions in BPM Sources

As of Epicor ERP 10.1, the naming convention for workflow items within BPM sources has changed.
The naming pattern now looks as follows:
A for action or C for condition + internal element number with leading zeros (e.g. 001) + underscore +
Action/Condition type name
Example

start: // Name = "Raise Exception 1", Id = "a4000ed6-ea42-44d3-879e-a8a42c2c881f"

this.UseDataFilter = true;
this.A001_RaiseExceptionAction();
this.RefreshData(matched: false);

Epicor Kinetic | Classic 55


Additional information is available at the Education and
Documentation areas of the EPICweb Customer Portal. To access
this site, you need a Site ID and an EPICweb account. To create an
account, go to http://support.epicor.com.

You might also like