You are on page 1of 22

ABL to C# BPM

Conversion
Ahmed Hamidi
Lead Software Architect

Topics

3 ABL to C# conversion exercises


Common Errors
Common Progress Commands
Questions

Exercise 1 Default Field

s example will default todays date in Sale Order Entry, after Get New is called.
ost processing method directive on Sales Order GetNew.
ABL Code:
FOR FIRST ttOrderHed WHERE ttOrderHed.RowMod <>
NO-LOCK.

ASSIGN ttOrderHed.OrderDate = TODAY.

END.
C# Code:
foreach(var ttOrderHed_iterator in(from ttOrderHed_Row in
ttOrderHed
where ttOrderHed_Row.RowMod !=
select ttOrderHed_Row))
{
var ttOrderHedRow = ttOrderHed_iterator;
ttOrderHedRow.OrderDate = DateTime.Now;
}
2013 Epicor Software
Corporation

Exercise 1 Translation
ABL Code:
FOR FIRST ttOrderHed WHERE ttOrderHed.RowMod <>
NO-LOCK.

ASSIGN ttOrderHed.OrderDate = TODAY.

END.
C# Code:
foreach(var ttOrderHed_iterator in(from ttOrderHed_Row
in
ttOrderHed where ttOrderHed_Row.RowMod !=
select ttOrderHed_Row))
{
var ttOrderHedRow = ttOrderHed_iterator;
ttOrderHedRow.OrderDate = DateTime.Now;
}

2013 Epicor Software


Corporation

Exercise 1 ERROR

Converted C# syntax:
foreach(var ttOrderHed_iterator in(from ttOrderHed_Row in
ttOrderHed where ttOrderHed_Row.RowMod !=
select ttOrderHed_Row))
Proper C# syntax:
foreach(var ttOrderHed_iterator in(from ttOrderHed_Row in
ttOrderHed where String.Equals( ttOrderHed_Row.RowMod, )
select ttOrderHed_Row))

2013 Epicor Software


Corporation

Exercise 2 Cross reference


validation

is example will validate the State entered in the Customer Maintenance screen,
ich is free form, against a UD table.
eProcessing directive on Customer Update method

ABL Code:
FOR FIRST ttCustomer WHERE ttCustomer.RowMod = A NO-LOCK.

IF CAN-FIND(UD40 WHERE UD40.Company = CUR-COMP


AND UD40.Key1 = ttCustomer.State AND UD40.Key2 =
AND UD40.Key3 = AND UD40.Key4 = AND UD40.Key5 = ) = FALSE THEN
DO:

{lib/PublishEx.i &ExMsg = "'Invalid State'"}

{&THROW_PUBLIC}.

END.

END .

C# Code:
object THROW_PUBLIC = null;
Ice.Tables.UD40 UD40;
foreach(var ttCustomer_iterator in(from ttCustomer_Row in ttCustomer
where ttCustomer_Row.RowMod == A
select ttCustomer_Row))
{
var ttCustomerRow = ttCustomer_iterator;
if(((from UD40_Row in Db.UD40
where UD40_Row.Company == Session.CompanyID && UD40_Row.Key1 == ttCustomerRow.State &&
UD40_Row.Key2 == && UD40_Row.Key3 == && UD40_Row.Key4 == && UD40_Row.Key5 ==
select UD40_Row).Count() != 0) == false)
{
CallContext.Current.ExceptionManager.AddBLException("Invalid State");
THROW_PUBLIC = null;
}
}
2013 Epicor Software
Corporation

Exercise 2 Translation

ABL Code:
FOR FIRST ttCustomer WHERE ttCustomer.RowMod = A NO-LOCK.

IF CAN-FIND(UD40 WHERE UD40.Company = CUR-COMP


AND UD40.Key1 = ttCustomer.State AND UD40.Key2 =
AND UD40.Key3 = AND UD40.Key4 = AND UD40.Key5 = ) = FALSE THEN
DO:

{lib/PublishEx.i &ExMsg = "'Invalid State'"}

{&THROW_PUBLIC}.

END.

END .

C# Code:
object THROW_PUBLIC = null;
Ice.Tables.UD40 UD40;
foreach(var ttCustomer_iterator in(from ttCustomer_Row in ttCustomer
where ttCustomer_Row.RowMod == A
select ttCustomer_Row))
{
var ttCustomerRow = ttCustomer_iterator;
if(((from UD40_Row in Db.UD40 where UD40_Row.Company == Session.CompanyID
&& UD40_Row.Key1 == ttCustomerRow.State && UD40_Row.Key2 ==
&& UD40_Row.Key3 == && UD40_Row.Key4 == && UD40_Row.Key5 ==
select UD40_Row).Count() != 0) == false)
{
CallContext.Current.ExceptionManager.AddBLException("Invalid State");
THROW_PUBLIC = null;
}
}

2013 Epicor Software


Corporation

Exercise 3 Run Persistent

his example will use a business object to create a new row in UD01 when an
BCCode entry is updated. Action File to BPM Custom Code.
PreProcessing directive on ABCCode Update method
ABL:
{Bpm/Bpm.i &OBJECT_NAME=AbcCode}
{bo/UD01/UD01_ds.i}
procedure UpdateBefore:
define input-output parameter table for ttAbcCode.
{&TRY_PRIVATE}
define variable hUD01 as handle.
find first ttAbccode no-lock no-error.
RUN bo\UD01\UD01.p PERSISTENT SET hUD01.
IF VALID-HANDLE (hUD01) THEN DO:
/*Create new record*/
RUN GETANEWUD01 IN hUD01 ( {&input-output_dataset_UD01DataSet} )
NO-ERROR.
find first ttUD01 where ttUD01.ROWMOD = 'A' no-lock no-error.
IF available ttUD01 THEN DO:
/*Populate fields*/
assign ttUD01.Company = CUR-COMP
ttUD01.Key1
= "AbcCode"
/*Table that was
affected*/
ttUD01.Key2
= "CountFreq"
/*Field that was
affected*/
ttUD01.Key3
= STRING(TIME,"HH:MM:SS") /*Time*/
ttUD01.Key4
= STRING(RECID(ttUD01)) /*To make the
records unique*/
ttUD01.Date01
= TODAY
/*Date*/
ttUD01.Number01 = ttAbcCode.CountFreq
/*New Value*/
ttUD01.ShortChar01 = DCD-USERID.
/*User id*/
/*Commit record*/
RUN UPDATE IN hUD01 ( {&input-output_dataset_UD01DataSet} ) NOERROR.
END. /*IF available ttUD01*/
/*Clean up*/
delete object hUD01.
END. /*IF VALID-HANDLE (hUD01)*/
{&CATCH_PRIVATE}
end procedure.
2013 Epicor Software
Corporation

C#:
/* TO DO: replace DataSet variables with typed datasets. Add contract
reference if necessary. */
var UD01DataSet = new System.Data.DataSet();
var ttAbccode_xRow = (from ttAbccode_Row in ttAbccode
select ttAbccode_Row).FirstOrDefault();
hUD01 =
Ice.Assemblies.ServiceRenderer.GetService<Ice.Contracts.UD01SvcContr
act>(Db);
if (hUD01 != null)
{
hUD01.GETANEWUD01(UD01DataSet);
var ttUD01_xRow = (from ttUD01_Row in ttUD01
where string.Equals(ttUD01_Row.RowMod,
IceRow.ROWSTATE_ADDED, StringComparison.OrdinalIgnoreCase)
select ttUD01_Row).FirstOrDefault();
if (ttUD01_xRow != null)
{
ttUD01_xRow.Company = Session.CompanyID;
ttUD01_xRow.Key1 = "AbcCode";
/*Table that was
affected*/
ttUD01_xRow.Key2 = "CountFreq";
/*Field that was affected*/
ttUD01_xRow.Key3 = System.Convert.TimeToString(DateTime.Now,
"HH:MM:SS"); /*Time*/
ttUD01_xRow.Key4 = ttUD01_xRow.SysRowID; /*To make the
records unique*/
ttUD01_xRow["Date01"] = DateTime.Now;
/*Date*/
ttUD01_xRow["Number01"] = ttAbccode_xRow.CountFreq;
/*New
Value*/
ttUD01_xRow["ShortChar01"] = Session.UserID;
/*User id*/
/*Commit record*/
hUD01.UPDATE(UD01DataSet);
}/*IF available ttUD01*/
/*Clean up*/
hUD01.Dispose();
}

Exercise 3 After Correction


Corrected C#:
/* TO DO: replace DataSet variables with typed datasets. Add contract reference
if necessary. */

Converted C#:
/* TO DO: replace DataSet variables with typed datasets. Add contract
reference if necessary. */

var UD01DataSet = new System.Data.DataSet();


var ttAbccode_xRow = (from ttAbccode_Row in ttAbccode
select ttAbccode_Row).FirstOrDefault();
hUD01 =
Ice.Assemblies.ServiceRenderer.GetService<Ice.Contracts.UD01SvcContract>(D
b);
if (hUD01 != null)
{
hUD01.GETANEWUD01(UD01DataSet);
var ttUD01_xRow = (from ttUD01_Row in ttUD01
where string.Equals(ttUD01_Row.RowMod,
IceRow.ROWSTATE_ADDED, StringComparison.OrdinalIgnoreCase)
select ttUD01_Row).FirstOrDefault();
if (ttUD01_xRow != null)
{
ttUD01_xRow.Company = Session.CompanyID;
ttUD01_xRow.Key1 = "AbcCode";
/*Table that was affected*/
ttUD01_xRow.Key2 = "CountFreq";
/*Field that was affected*/
ttUD01_xRow.Key3 = System.Convert.TimeToString(DateTime.Now,
"HH:MM:SS"); /*Time*/
ttUD01_xRow.Key4 = ttUD01_xRow.SysRowID; /*To make the records
unique*/
ttUD01_xRow["Date01"] = DateTime.Now;
/*Date*/
ttUD01_xRow["Number01"] = ttAbccode_xRow.CountFreq;
/*New Value*/
ttUD01_xRow["ShortChar01"] = Session.UserID;
/*User id*/
/*Commit record*/
hUD01.UPDATE(UD01DataSet);
}/*IF available ttUD01*/
/*Clean up*/
hUD01.Dispose();
}

var UD01DataSet = new System.Data.DataSet();


var ttAbccode_xRow = (from ttAbccode_Row in ttAbccode
select ttAbccode_Row).FirstOrDefault();
hUD01 =
Ice.Assemblies.ServiceRenderer.GetService<Ice.Contracts.UD01SvcContr
act>(Db);
if (hUD01 != null)
{
hUD01.GETANEWUD01(UD01DataSet);
var ttUD01_xRow = (from ttUD01_Row in ttUD01
where string.Equals(ttUD01_Row.RowMod,
IceRow.ROWSTATE_ADDED, StringComparison.OrdinalIgnoreCase)
select ttUD01_Row).FirstOrDefault();
if (ttUD01_xRow != null)
{
ttUD01_xRow.Company = Session.CompanyID;
ttUD01_xRow.Key1 = "AbcCode";
/*Table that was
affected*/
ttUD01_xRow.Key2 = "CountFreq";
/*Field that was affected*/
ttUD01_xRow.Key3 = System.Convert.TimeToString(DateTime.Now,
"HH:MM:SS"); /*Time*/
ttUD01_xRow.Key4 = ttUD01_xRow.SysRowID; /*To make the
records unique*/
ttUD01_xRow["Date01"] = DateTime.Now;
/*Date*/
ttUD01_xRow["Number01"] = ttAbccode_xRow.CountFreq;
/*New
Value*/
ttUD01_xRow["ShortChar01"] = Session.UserID;
/*User id*/
/*Commit record*/
hUD01.UPDATE(UD01DataSet);
}/*IF available ttUD01*/
/*Clean up*/
hUD01.Dispose();
}

2013 Epicor Software


Corporation

Common Errors
Case Sensitivity
Use Of Unassigned Local Variable Message
E10 Equivalent to Row Mod = U or A

2013 Epicor Software


Corporation

Case Sensitivity
For those who do not work with C# often, remember that it is case sensitive. For example,
look at the below simple query:
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 produce the following error message when you attempt to compile the BPM:
Description: There is at least one compilation error.
Details:
Error CS1061: 'Erp.ErpContext' does not contain a definition for 'customer' and no
extension method 'customer' accepting a first argument of type 'Erp.ErpContext' could be
found (are you missing a using directive or an assembly reference?)
Notice that the error message has customer with a lower case C? Change the following line
Change the first line to an upper case C as below and the BPM compiles without error.
var CustomerRecs = (from r in Db.Customer

2013 Epicor Software


Corporation

Unassigned Local Variable


The compiler will give an error message referring to an
Unassigned Local Variable when the BPMs logic is such that a
variable may never get set. The below 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;
Details:
Db.Validate();
Error CS0165: Use of unassigned local variable
txscope.Complete();
}
'Customer_UD'
}
Notice that in the above example, the Customer_UD
will generate the following error upon compilation
variable is only set when
Server Side Exception
There is at least one compilation error.
if (Customer != null)
Exception caught in: Epicor.ServiceModel
We can correct this error by changing the second line
Error Detail
the example to
============
Description: There is at least one compilation error
Erp.Tables.Customer_UD Customer_UD = null;

This change ensures that the variable will be set to


something regardless of the if statement.

2013 Epicor Software


Corporation

of

E10 Equivalent to Row Mod = U


or A
In many BPMs the user wants to peform some action if
the tt record is a new record or updated record. In 9.05
the RowMod = A or U was used.
In 10, the equivalent is 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))

2013 Epicor Software


Corporation

Common Progress Commands


FOR EACH (used to loop through a table)
FOR FIRST (used to find the first record in a table
which meets where clause)
CAN-FIND (used to identify existence of record)
RUN PERSISTENTLY (used to run business objects
on the sever)
EXCLUSIVE LOCK direct database updates
Issue an exception
Show informational message
Send an email
2013 Epicor Software
Corporation

FOR EACH

2013 Epicor Software


Corporation

FOR FIRST

2013 Epicor Software


Corporation

RUN PERSISTENTLY

Get an example of this


.p calling into ud01
- Client Side uses adapter as before
- Server Side use Service Renderer

2013 Epicor Software


Corporation

Raise an exception

2013 Epicor Software


Corporation

Show in informational message custom code

Use PublishInfoMessage

2013 Epicor Software


Corporation

Send an email
// EMAIL BEGIN
var splitter = new[] { ';' };
var mailer = new Epicor.Customization.Bpm.Implementation.AsyncSmtpMailer();
var message = new System.Net.Mail.MailMessage();
var from = BPM_from; 17 Revision 3
message.From = new System.Net.Mail.MailAddress(from);
var to = BPM_to;
foreach (var address in to.Split(splitter, StringSplitOptions.RemoveEmptyEntries))
{
message.To.Add(new System.Net.Mail.MailAddress(address));
}
var cc = BPM_cc;
foreach (var address in cc.Split(splitter, StringSplitOptions.RemoveEmptyEntries))
{
message.CC.Add(new System.Net.Mail.MailAddress(address));
}
var subject = BPM_subject;
message.Subject = subject;
var body = BPM_body;
message.Body = body;
mailer.Send(this.Db, message, this.CompanyID);
//

2013 Epicor Software


Corporation

Programming Guide
Accessible on code converter about page
Helpful hints and how-to info

2013 Epicor Software


Corporation

Questions

2013 Epicor Software


Corporation

You might also like