Transcript Document

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
This example will default today’s date in Sale Order Entry, after Get New is called.
- post 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
This example will validate the State entered in the Customer Maintenance screen,
which is free form, against a UD table.
PreProcessing 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
This example will use a business object to create a new row in UD01 when an
ABCCode 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} ) NO-ERROR.
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.UD01SvcContract>(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. */
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>(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
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>(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();
}
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 BPM’s 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;
Db.Validate();
Details:
txscope.Complete();
Error CS0165: Use of unassigned local variable 'Customer_UD'
}
}
Notice that in the above example, the Customer_UD variable is only
will generate the following error upon compilation
set when
Server Side Exception
if (Customer != null)
There is at least one compilation error.
Exception caught in: Epicor.ServiceModel
We can correct this error by changing the second line of the example
Error Detail
to
============
Erp.Tables.Customer_UD Customer_UD = null;
Description: There is at least one compilation error
This change ensures that the variable will be set to something
regardless of the if statement.
© 2013 Epicor Software Corporation
E10 Equivalent to Row Mod = U or A
In many BPM’s 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