Sie sind auf Seite 1von 23

March 2012

On Technical/
Customizing EnterpriseOne

Business Function Error Handling in UBEs


by Brian Oster
Editor's Note: What happens when a Business Function (BSFN) throws an error in a UBE?
It's like that proverbial fallen tree in the woods; even if your users don't see it, it's still there.
Wouldn't it be nice, though, if you could get a warning on those BSFN errors in a UBE, just like
you do in an APPL, before further processing occurs? Brian Oster has a way to make that
happen. In this article, he outlines the steps to take and offers plenty of codes to help you get
started.

Overview
Business Functions (BSFNs), both C and NERs, are very tightly coupled with Interactive
Applications (APPLs) when it comes to error handling. When a function is called from an APPL,
any errors or warnings thrown by that function are immediately displayed to the user in the APPL;
and depending on which event the function was called from, may stop processing the action that
initiated the event (see Figure 1). In addition to the error message, information about the error or
warning is also displayed such as the Error ID and the functions source file and line number
where the error was thrown (see Figure 2).

Figure 1: Error Displayed in Address Book When User Tries to Delete a Record that is In
Use, and Stopping the Delete Action.

Copyright 2012 by Klee Associates, Inc.


www.JDEtips.com

Business Function Error Handling in UBEs

Figure 2: Expanded Error Information for the Error Displayed in Figure 1


Furthermore, when a function is called from an APPL, all this error handling behavior happens by
default without any additional code or configuration by the developer. Unfortunately, this default
behavior does NOT extend to UBEs. There is not any built in BSFN error handling mechanism in
the UBE engine. If an error or warning is thrown from a function that is called from a UBE, nothing
is displayed in the output of the UBE to the user and nothing prevents further processing in the
UBEs event or the UBE itself. Obviously this can pose a pretty big problem if the developer is
calling BSFNs from the UBE to do data validation, or worse, create transactions. Some BSFNs try
to address this functionality gap by returning the error ID as a return parameter of the function so
that the caller of the function can check to see if an error occurred. However, this is a very poor
work around for several reasons:

This only returns the error ID without any error message or additional error information.

Only one error ID can be returned; if there were multiple errors there is no simple way to
return the entire list of error IDs to the caller.

The error may not be thrown directly in the called function at all, but instead by a function
call made inside of the called function or even further down the call stack. More than
likely this error ID will not be returned by the top level function call made from the UBE.

Finally, and probably more importantly, most functions do not employ this work-around of
returning the error ID as a parameter. In other words, you are completely at the mercy of
the function you are calling as to whether or not you can check for errors and warnings
when the function is called from your UBE.

In this article I will show you how you can implement a simple technique to create an error
handling mechanism inside of UBEs that works much the same way the default error handling
mechanism works inside of APPLs. Additionally this tip will work with calls to any BSFN, doesnt
require any special code or return parameters by the called BSFN, and can catch any error,
regardless of where in the call stack the error is thrown.

Example
To illustrate, I have set up a very simple example; we'll
start by using this custom table I created with three
fields (see Figure 3).
Figure 3: Example Table Structure
I then created a simple APPL with a Search and Select form over the table as well as a
Headerless Detail form to add/edit the data in the table. In the Row Exited & Changed Inline
event of the Headerless Detail form, I call two functions as per the following ER code:

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 2

Business Function Error Handling in UBEs


=======================================================================
FORM: JDE Tips Error Handling (Edit) [HEADERLESS DETAIL] (W59JDET1B)
=======================================================================
CONTROL: GRID Grid
EVENT: Row Exit & Changed - Inline
----------------------------------------------------------------------OPT: Using Defaults
F0101 Get Address Book Info
GC Address Number -> BF mnAddressNumber
JDETipsValidateEffectiveDates
GC Beg Eff Date -> BF jdBeginningEffectiveDate
GC End Eff Date -> BF jdEndingEffectiveDate

The F0101 Get Address Book Info function will throw a warning if the passed Address Number
is not in the Address Book. The JDETipsValidateEffectiveDates function is a custom function I
created for this example that will throw an error if the Ending Effective Date is prior to the
Beginning Effective Date. For this example, I will enter in a mix of good records and erroneous
ones (see Figure 4).

Figure 4: Mix of Good and Bad Records for Example


The default error handling of the APPL requires that these errors be dealt with before the records
can be saved to the table. Normally the user would be forced to correct the errors to save the
edits or cancel the transaction. Here I have added a button to clear all the errors so we can save
the data in its erroneous state to our custom table.
For the next part of our example, I have created a UBE that will print out the records entered
above. The code in the Do Section event of the UBE is shown below:

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 3

Business Function Error Handling in UBEs

=======================================================================
SECTION: JDEips UBE Error Handling Example - F59JDET1 - All Fields [COLUMNAR SECTION]
(S1)
=======================================================================
OBJECT: SECTION
EVENT: Do Section
----------------------------------------------------------------------OPT: Using Defaults
F0101 Get Address Book Info
BC Address Number (F59JDET1)(AN8) -> BF mnAddressNumber
JDETipsValidateEffectiveDates
BC Beginning Effective Date - Julian (F59JDET1)(BEFD) -> BF jdBeginningEffectiveDate
BC Ending Effective Date - Julian (F59JDET1)(EEFD) -> BF jdEndingEffectiveDate

Notice that the code is identical to the code in the APPL, making the exact same function calls for
each record in the table. When we run the UBE we get the output shown in Figure 5.

Figure 5: UBE Output with No Errors or Warnings Displayed


Obviously you can see that even though we called the same BSFNs with the same data the UBE
did NOT display any errors or warnings thrown inside the functions for the erroneous records.

Solution
Before I describe the solution to our UBE BSFN error problem, I should note that this solution was
created on tools release 8.98.4.x. I currently implement this solution in numerous UBEs running
on the 8.98.4.x tools release. I have also used this solution in Xe.
In order to catch the errors and warnings and display them in the output, we will need to write a
little Error Utility BSFN which, once created, can be used in any UBE. Because the central focus
of this article is not so much on this utility BSFN, but rather how to use it, I will give you the code
and instructions to create this utility BSFN a little later. For now we'll start with the parameters
and the documentation for each function call in the Error Utility BSFN along with detailed
instructions on how to use it in your UBEs.

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 4

Business Function Error Handling in UBEs


Documentation for the JDETips Errors utility BSFN:
Function: JDETipsViewErrorStackInit
Saves a copy of the current error stack information to jdeCache.
Parameter

ALIAS

Type

In

Out

Description

mnHandle

JOBS

MATH_NUMERIC

nWarningCount

INT01

int

The number or warnings on the error stack.


Includes warnings classified as Informational.

nErrorCount

INT01

int

The number of errors on the error stack.

nTotalCount

INT01

int

The total number of warnings and errors on


the error stack.

Function: JDETipsViewErrorStackIterate
Iterates the saved error stack information created by JDETipsViewErrorStackInit.
Parameter

ALIAS

Type

In

mnHandle

JOBS

MATH_NUMERIC

idCursorHandle

GENLNG

unsigned long

cEndOfList

CHAR

JCHAR

Out

Description
Pass the value returned by
JDETipsViewErrorStackInit.

Pass zero on first call, pass returned


value on subsequent calls.

0 = End of list not yet reached, record


is returned.
1 = Cursor advanced beyond the end
of the list or the list was empty. No
record retuned, any passed open
cursor has been closed.

cCloseCursor

CHAR

JCHAR

0 = Normal call to return first/next


cache record.
1 = Close the passed cursor and exit.
No record is returned.
Use this to close an open cursor before
the entire list has been iterated.

szErrorID

DTAI

String(10)

The error or warning ID.

nErrorLevel

INT01

int

The error level (Error, Warning). See


JDE_ERR_LEVEL in JDEKDCL.h for
values.
Note: Error records set as
Informational will be scrubbed to
warning type records to be consistent
with the values in JDE_ERR_LEVEL.

szErrorMsg

DL011

String(100)

The error or warning message.

szSourceFile

CDCFPATH

String(255)

Complete path to the function source


file where the error was thrown.

nLineNo

INT01

int

The line number in the source file


where the error was thrown.

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 5

Business Function Error Handling in UBEs

Function: JDETipsViewErrorStackFree
Frees all resources allocated by JDETipsViewErrorStackInit. Note: This does NOT free or clear
the actual error stack, it only frees the copy of the error stack created by
JDETipsViewErrorStackInit. To clear the actual error stack, please use the function
JDETipsClearErrorStack.
Parameter

ALIAS

Type

In

mnHandle

JOBS

MATH_NUMERIC

Out

Description
Pass the value returned by
JDETipsViewErrorStackInit.

Function: JDETipsClearErrorStack
Clears the actual JDE error stack of all errors and warnings.
Parameter

ALIAS

Type

cNotUsed

CHAR

JCHAR

In

Out

Description
Parameter is not used

We are now going to create the exact same UBE to display the records in our custom table and
make the exact same BSFN calls to validate the data, but we are going to add in a little code to
catch and display any errors and warnings using the function calls listed above.
The first thing we need to do is create two new group sections in our UBE. The properties for
each section are listed below:
UBE Sections for Error Display
Section Name

Section Type

Business View

Visible

Conditional

errorCheckStack

Group

None

No

Yes

errorDisplay

Group

None

Yes

Yes

In the errorDisplay section add the following global report variables:


errorDisplay section Report Variables
Report Variable
Name

ALIAS

Label
Text

Global

errorID

DTAI

Error ID

Yes

errorMsg

DL011

Msg

Yes

errorSourceFile

CDCFPATH

Source
File

Yes

errorLineNo

INT01

Line

Yes

Description

You will probably need to adjust the display


length in the variable properties to get it to fit on
your UBE.

When you are done, your UBE should look something like what is shown in Figure 6.

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 6

Business Function Error Handling in UBEs

Figure 6: UBE with Error Checking Sections


Note the errorCheckStack and errorDisplay sections. These are the sections that you would add
to any UBE when enabling error check capabilities.
Next we will need to add a little code to call our Error Utility Functions to check the error stack
and return error information. Add the following ER variables and ER Code to the Do Section
event of the errorCheckStack section:
Variables for the Do Section event in the errorCheckStack section
ER Variable Name

ALIAS

Scope

cEndOfList

CHAR

Event

idCursorHandle

GENLNG

Event

mnHandle

JOBS

Event

=======================================================================
SECTION: errorCheckStack [GROUP SECTION] (S3)
=======================================================================
OBJECT: SECTION
EVENT: Do Section
----------------------------------------------------------------------evt_cEndOfList
evt_idCusorHandle
evt_mnHandle
OPT: Using Defaults
VA evt_idCusorHandle = "0"
VA evt_cEndOfList = "0"
JDETipsViewErrorStackInit
VA evt_mnHandle <- BF mnHandle
While VA evt_cEndOfList is not equal to "1"
JDETipsViewErrorStackIterate
VA evt_mnHandle -> BF mnHandle
VA evt_idCusorHandle <> BF idCursorHandle
VA evt_cEndOfList <- BF cEndOfList
<Blank> -> BF cCloseCursor
RV errorID <- BF szErrorID
RV errorMsg <- BF szErrorMsg
RV errorSourceFile <- BF szSourceFile
RV errorLineNo <- BF nLineNo
If VA evt_cEndOfList is not equal to "1"
Do Custom Section(RS errorDisplay)
End If
End While
JDETipsViewErrorStackFree
VA evt_mnHandle -> BF mnHandle
JDETipsClearErrorStack
<Blank> X BF cNotUsed

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 7

Business Function Error Handling in UBEs


At this point, anytime you want to check the error stack and display any errors and warnings, you
simply use the system function to call the custom section errorCheckStack from any section in
your UBE. In our example, we want to check the error stack after each record from our custom
table is printed. To do this, we'll use the system function to call the errorCheckStack custom
section in the After Last Object Printed event of our record output section. Here is the complete
ER code listing for our new UBE:
Listing of ER for Report: Report : JDETips UBE Error Handling Example with BSFN Error
Display (R59JDET2)
=======================================================================
SECTION: JDETips UBE Error Handling Example - F59JDET1 - All Fields [COLUMNAR SECTION] ()
=======================================================================
OBJECT: SECTION
EVENT: Do Section
----------------------------------------------------------------------OPT: Using Defaults
F0101 Get Address Book Info
BC Address Number (F59JDET1)(AN8) -> BF mnAddressNumber
JDETipsValidateEffectiveDates
BC Beginning Effective Date - Julian (F59JDET1)(BEFD) -> BF jdBeginningEffectiveDate
BC Ending Effective Date - Julian (F59JDET1)(EEFD) -> BF jdEndingEffectiveDate
----------------------------------------------------------------------EVENT: After Last Object Printed
----------------------------------------------------------------------OPT: Using Defaults
Do Custom Section(RS errorCheckStack)
=======================================================================
SECTION: errorCheckStack [GROUP SECTION] ()
=======================================================================
OBJECT: SECTION
EVENT: Do Section
----------------------------------------------------------------------evt_cEndOfList
evt_idCusorHandle
evt_mnHandle
OPT: Using Defaults
VA evt_idCusorHandle = "0"
VA evt_cEndOfList = "0"
JDETipsViewErrorStackInit
VA evt_mnHandle <- BF mnHandle
While VA evt_cEndOfList is not equal to "1"
JDETipsViewErrorStackIterate
VA evt_mnHandle -> BF mnHandle
VA evt_idCusorHandle <> BF idCursorHandle
VA evt_cEndOfList <- BF cEndOfList
<Blank> -> BF cCloseCursor
RV errorID <- BF szErrorID
RV errorMsg <- BF szErrorMsg
RV errorSourceFile <- BF szSourceFile
RV errorLineNo <- BF nLineNo
If VA evt_cEndOfList is not equal to "1"
Do Custom Section(RS errorDisplay)
End If
End While
JDETipsViewErrorStackFree
VA evt_mnHandle -> BF mnHandle
JDETipsClearErrorStack
<Blank> X BF cNotUsed

After every record is processed, the errorCheckStack section is called. This section will check
for any errors, and if present, output them by calling the errorDisplay section. At this point it is
worth noting that in the ER code in the Do Section event of the errorCheckStack section we
Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 8

Business Function Error Handling in UBEs


call the function JDETipsClearErrorStack. This call will clear the error stack. If we didnt make this
call after every record, the errors for one record will still be present on the error stack when we
process the next record. Even if the next record didnt have any errors, it would appear that it did
since errors from previous records would be re-displayed when we checked the error stack.
Figure 7 shows the output from our new UBE.

Figure 7: UBE Output with BSFN Error Handling Code


In this simple example all we did was display the errors and warnings for each erroneous record.
With some modification in the Do Section event of the errorCheckStack section we could have
set a global variable flag that could be used by other sections and events to stop processing or
invoke additional error handling logic. Alternatively, if you dont need to display the errors in the
output, you could simple call the JDETipsViewErrorStack[Init,Iterate,Free] functions from
your ER code in various events to interrogate the error stack and invoke error handling logic.

Utility Error BSFN Code Listing and Notes


You should be able to use this code listing to manually recreate the Error Utility BSFN. I have
listed the data dictionary items used to create the data structures as comments in the .h file code
listing. As an alternative, I will provide this Error Utility BSFN along with all the examples in this
article as a .PAR file. If your version of JDE supports it, this .PAR file can be used to import all the
objects and code using OMW (for more information on OMW .PAR files, please see article
654394.1 on Oracles support site). Please note that you are free to use any code provided in this
article in any manner you choose. However, the code supplied in this solution is provided without
any warranty either expressed or implied.
If you choose to import/restore from the .PAR file, Figure 8 shows the OMW project name and the
contents of the .PAR file.

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 9

Business Function Error Handling in UBEs

Figure 8: PAR File Contents and OMW Project Name


After importing the objects from the PAR file, the only change you will probably need to make is
the DLL (BSFN Library) assignment for the BSFNs. Currently they are assigned to CTEST.
Reassign to any DLL you choose and then build the BSFNs.
In the code listing below, you will see that the key to this BSFN is the following two JDE C API
calls:
KRNL_RTN(void) JDEWINAPI jdeErrorSetToFirstEx(LPBHVRCOM lpBhvrCom, LPVOID lpVoid);
KRNL_RTN(LPJDEERROR_RECORD) JDEWINAPI jdeErrorGetNextDDItemNameInfoEx( LPBHVRCOM lpBhvrCom, LPVOID
lpVoid);

The jdeErrorSetToFirstEx call sets the pointer to the first error on the error stack and repeated
calls to jdeErrorGetNextDDItemNameInfoEx iterates the error stack and returns information about the
error. I simply store this error information in JDE cache.
No doubt many of you have started to realize that the uses for these API calls and this Error
Utility BSFN are not limited to UBEs. While this is correct, there are some limitations and
differences depending on your tools release. When used in UBEs in the manner that I have
described, the behavior should be consistent across multiple tools releases and E1 versions. As
previously stated, I have used this in both Xe and 9.0. In Xe, I also used this utility BSFN (or a
version of it) inside of APPLs to interrogate the error stack in order to enhance the default error
handling of APPLs. For example, it could be used to look for a specific error ID and invoke certain
error handling logic accordingly. In Xe, in which all of our clients ran on Citrix, this Error Utility
function worked fine when called directly from an APPL running as a Win32 application.
However, when we moved to 9.0 and HTML clients, this technique no longer worked the way it
was coded in Xe. It turns out that if you call this Utility BSFN directly from an APPL running on an
HTML client, it will never return any error stack information. We were still able to use this
technique in 9.0 but it required some modification. It appears that when called from an APPL
running on an HTML client, the jdeErrorGetNextDDItemNameInfoEx API call will only return error
information for BSFN calls on the current call stack of the APPL. To use this from an APPL, you
have to create a wrapper function that calls the target function and then checks the error stack
from within the wrapper function.

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 10

Business Function Error Handling in UBEs


For example, the following pseudo code will NOT work for an APPL running on an HTML client:
P4210
F4211FSBeginDoc
JDETipsViewErrorStackInit
//called from APPL will not save any error stack
information
//
While(no more errors)
JDETipsViewErrorStackIterate
End While
//
JDETipsViewErrorStackFree

The following pseudo code WILL work correctly for an APPL running on an HTML client:
P4210
MyBeginDocWrapperFunction
F4211FSBeginDoc
JDETipsViewErrorStackInit
//called from wrapper BSFN - will correctly save error
stack information
//
While(no more errors)
JDETipsViewErrorStackIterate //called from wrapper BSFN
End While
//
JDETipsViewErrorStackFree
//called from wrapper BSFN

Or optionally:
P4210
MyBeginDocWrapperFunction
F4211FSBeginDoc
JDETipsViewErrorStackInit
saved error stack
//
While(no more errors)
JDETipsViewErrorStackIterate
End While
//
JDETipsViewErrorStackFree

//return mnHandle to P4210 and let P4210 iterate the

//called from P4210


//called from P4210

Error Utility BSFN (B59JDET1) - Code Listing


As previously stated, the source code listing below should provide you with all the necessary
information to manually create the BSFN and associated data structures if your tools release
doesnt support PAR files or you choose not to import the provided PAR file. If you use different
object names for the BSFN source file and/or associated data structures you will need to make
some minor modifications to the code accordingly. Additionally, this code is Unicode compliant
and conforms to the JDE Unicode standards. If your tools release doesnt support Unicode (Xe
for example), you will have to make some additional code changes that are compliant with nonUnicode tools releases.
/* B59JDET1.h file Code Listing Start */
/*****************************************************************************
*
Header File: B59JDET1.h
*
*
Description: JDETips Error Utilities Header File
*
*
History:
*
Date
Programmer SAR# - Description
*
---------- ---------- ------------------------------------------*
Author 1/15/2012
BOSTE
Unknown - Created
*
*

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 11

Business Function Error Handling in UBEs


* Copyright (c) J.D. Edwards World Source Company, 1996
*
* This unpublished material is proprietary to J.D. Edwards World Source
* Company. All rights reserved. The methods and techniques described
* herein are considered trade secrets and/or confidential. Reproduction
* or distribution, in whole or in part, is forbidden except by express
* written permission of J.D. Edwards World Source Company.
****************************************************************************/
#ifndef __B59JDET1_H
#define __B59JDET1_H
/*****************************************************************************
* Table Header Inclusions
****************************************************************************/
/*****************************************************************************
* External Business Function Header Inclusions
****************************************************************************/
/*****************************************************************************
* Global Definitions
****************************************************************************/
/*****************************************************************************
* Structure Definitions
****************************************************************************/
/*****************************************************************************
* DS Template Type Definitions
****************************************************************************/
/*****************************************
* TYPEDEF for Data Structure
*
Template Name: Clear Error Stack
*
Template ID:
D59JDET1D
*
Generated:
Sun Jan 15 09:44:58 2012
*
* DO NOT EDIT THE FOLLOWING TYPEDEF
*
To make modifications, use the OneWorld Data Structure
*
Tool to Generate a revised version, and paste from
*
the clipboard.
*
**************************************/
#ifndef DATASTRUCTURE_D59JDET1D
#define DATASTRUCTURE_D59JDET1D
typedef struct tagDSD59JDET1D
{
JCHAR
cNotUsed;
} DSD59JDET1D, *LPDSD59JDET1D;

/* Alias
/* -------/* CHAR

#define IDERRcNotUsed_1

Required In
-------- ---No

Out */
---- */
*/

1L

#endif
/*****************************************
* TYPEDEF for Data Structure
*
Template Name: Free Error Stack
*
Template ID:
D59JDET1C
*
Generated:
Sun Jan 15 09:45:09 2012
*

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 12

Business Function Error Handling in UBEs


* DO NOT EDIT THE FOLLOWING TYPEDEF
*
To make modifications, use the OneWorld Data Structure
*
Tool to Generate a revised version, and paste from
*
the clipboard.
*
**************************************/
#ifndef DATASTRUCTURE_D59JDET1C
#define DATASTRUCTURE_D59JDET1C
typedef struct tagDSD59JDET1C
{

MATH_NUMERIC
mnHandle;
} DSD59JDET1C, *LPDSD59JDET1C;

/* Alias
/* -------/* JOBS

#define IDERRmnHandle_1

Required In
-------- ---Yes
X

Out */
---- */
*/

1L

#endif
/*****************************************
* TYPEDEF for Data Structure
*
Template Name: Save Error Stack State
*
Template ID:
D59JDET1A
*
Generated:
Sun Jan 15 09:45:17 2012
*
* DO NOT EDIT THE FOLLOWING TYPEDEF
*
To make modifications, use the OneWorld Data Structure
*
Tool to Generate a revised version, and paste from
*
the clipboard.
*
**************************************/
#ifndef DATASTRUCTURE_D59JDET1A
#define DATASTRUCTURE_D59JDET1A
typedef struct tagDSD59JDET1A
{
MATH_NUMERIC
mnHandle;
int
nWarningCount;
int
nErrorCount;
int
nTotalCount;
} DSD59JDET1A, *LPDSD59JDET1A;
#define
#define
#define
#define

/*
/*
/*
/*
/*
/*

IDERRmnHandle_1
IDERRnWarningCount_2
IDERRnErrorCount_3
IDERRnTotalCount_4

Alias
-------JOBS
INT01
INT01
INT01

Required In
-------- ---No
No
No
No

Out */
---X
X
X
X

*/
*/
*/
*/
*/

1L
2L
3L
4L

#endif
/*****************************************
* TYPEDEF for Data Structure
*
Template Name: Iterate Error Stack
*
Template ID:
D59JDET1B
*
Generated:
Sun Jan 15 09:45:24 2012
*
* DO NOT EDIT THE FOLLOWING TYPEDEF
*
To make modifications, use the OneWorld Data Structure
*
Tool to Generate a revised version, and paste from
*
the clipboard.

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 13

Business Function Error Handling in UBEs


*
**************************************/
#ifndef DATASTRUCTURE_D59JDET1B
#define DATASTRUCTURE_D59JDET1B
typedef struct tagDSD59JDET1B
{
MATH_NUMERIC
mnHandle;
ID
idCursorHandle;
JCHAR
cEndOfList;
JCHAR
cCloseCursor;
JCHAR
szErrorID[11];
int
nErrorLevel;
JCHAR
szErrorMsg[101];
JCHAR
szSourceFile[256];
int
nLineNo;
} DSD59JDET1B, *LPDSD59JDET1B;
#define
#define
#define
#define
#define
#define
#define
#define
#define

/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*

IDERRmnHandle_1
IDERRidCursorHandle_2
IDERRcEndOfList_3
IDERRcCloseCursor_4
IDERRszErrorID_5
IDERRnErrorLevel_11
IDERRszErrorMsg_12
IDERRszSourceFile_13
IDERRnLineNo_14

Alias
-------JOBS
GENLNG
CHAR
CHAR
DTAI
INT01
DL011
CDCFPATH
INT01

Required
-------Yes
No
No
No
No
No
No
No
No

In
---X
X
X

Out */
---- */
*/
X
*/
X
*/
*/
X
*/
X
*/
X
*/
X
*/
X
*/

1L
2L
3L
4L
5L
11L
12L
13L
14L

#endif
/*****************************************
* TYPEDEF for Data Structure
*
Template Name: Set Error - DSCA
*
Template ID:
DE0022
*
Generated:
Tue May 24 17:41:04 2005
*
* DO NOT EDIT THE FOLLOWING TYPEDEF
*
To make modifications, use the OneWorld Data Structure
*
Tool to Generate a revised version, and paste from
*
the clipboard.
*
**************************************/
#ifndef DATASTRUCTURE_DE0022
#define DATASTRUCTURE_DE0022
typedef struct tagDSDE0022
{
JCHAR
szDescription[41];
} DSDE0022, *LPDSDE0022;
#define IDERRszDescription_1

1L

#endif
/*****************************************************************************
* Source Preprocessor Definitions
****************************************************************************/
#if defined (JDEBFRTN)
#undef JDEBFRTN
#endif

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 14

Business Function Error Handling in UBEs


#if defined (WIN32)
#if defined (WIN32)
#define JDEBFRTN(r) __declspec(dllexport) r
#else
#define JDEBFRTN(r) __declspec(dllimport) r
#endif
#else
#define JDEBFRTN(r) r
#endif
/*****************************************************************************
* Business Function Prototypes
****************************************************************************/
JDEBFRTN (ID) JDEBFWINAPI JDETipsClearErrorStack
(LPBHVRCOM lpBhvrCom,
LPDSD59JDET1D lpDS);
JDEBFRTN (ID) JDEBFWINAPI JDETipsViewErrorStackFree
(LPBHVRCOM lpBhvrCom,
LPDSD59JDET1C lpDS);
JDEBFRTN (ID) JDEBFWINAPI JDETipsViewErrorStackInit
(LPBHVRCOM lpBhvrCom,
LPDSD59JDET1A lpDS);
JDEBFRTN (ID) JDEBFWINAPI JDETipsViewErrorStackIterate
(LPBHVRCOM lpBhvrCom,
LPDSD59JDET1B lpDS);

LPVOID lpVoid,
LPVOID lpVoid,
LPVOID lpVoid,
LPVOID lpVoid,

/*****************************************************************************
* Internal Function Prototypes
****************************************************************************/
#endif
/* __B59JDET1_H */
/* B59JDET1.h file Code Listing End */

/* B59JDET1.c file Code Listing Start */


#include <jde.h>
#define b59jdet1_c
/*****************************************************************************
*
Source File: b59jdet1
*
*
Description: JDETips Error Utilities Source File
*
*
History:
*
Date
Programmer SAR# - Description
*
---------- ---------- ------------------------------------------*
Author 1/15/2012
Brian Oster JDETips.com
*
* Copyright (c) J.D. Edwards World Source Company, 1996
*
* This unpublished material is proprietary to J.D. Edwards World Source Company.
* All rights reserved. The methods and techniques described herein are
* considered trade secrets and/or confidential. Reproduction or
* distribution, in whole or in part, is forbidden except by express
* written permission of J.D. Edwards World Source Company.
****************************************************************************/
/**************************************************************************
* Notes:
*
**************************************************************************/
#include <b59jdet1.h>
/*****************************************************************************
* Private Definitions
****************************************************************************/
#define B59JDET1_CACHE_NAME_ERRORS
_J("B59JDET1A")

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 15

Business Function Error Handling in UBEs

typedef struct tagD59JDET1_ERROR


{
int
idx;
/* cache key */
DSD59JDET1B
data;
} D59JDET1_ERROR, *LPD59JDET1_ERROR;
/*****************************************************************************
* Private Function Prototypes
****************************************************************************/
static ID I59JDET1_InitCacheErrors(LPBHVRCOM lpBhvrCom, LPVOID lpVoid, HUSER hUser,
LPMATH_NUMERIC pmnHandle, HCACHE *hCache);

/**************************************************************************
* Business Function: JDETipsClearErrorStack
*
*
Description: JDETipsClearErrorStack
*
*
Parameters:
*
LPBHVRCOM
lpBhvrCom
Business Function Communications
*
LPVOID
lpVoid
Void Parameter - DO NOT USE!
*
LPDSD59JD
lpDS
Parameter Data Structure Pointer
*
*************************************************************************/
JDEBFRTN (ID) JDEBFWINAPI JDETipsClearErrorStack (LPBHVRCOM lpBhvrCom, LPVOID lpVoid,
LPDSD59JDET1D lpDS)
{
/************************************************************************
* Variable declarations
************************************************************************/
/************************************************************************
* Check for NULL pointers
************************************************************************/
if ((lpBhvrCom == (LPBHVRCOM) NULL) ||
(lpVoid
== (LPVOID)
NULL) ||
(lpDS
== (LPDSD59JDET1D)NULL))
{
jdeErrorSet (lpBhvrCom, lpVoid, (ID) 0, _J("4363"), (LPVOID) NULL);
return ER_ERROR;
}
/************************************************************************
* Main Processing
************************************************************************/
jdeErrorClearEx(lpBhvrCom, lpVoid);
return ER_SUCCESS;
}
/**************************************************************************
* Business Function: JDETipsViewErrorStackFree
*
*
Description: JDETipsViewErrorStackFree
*
*
Parameters:
*
LPBHVRCOM
lpBhvrCom
Business Function Communications
*
LPVOID
lpVoid
Void Parameter - DO NOT USE!
*
LPDSD59JD
lpDS
Parameter Data Structure Pointer
*
*************************************************************************/

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 16

Business Function Error Handling in UBEs


JDEBFRTN (ID) JDEBFWINAPI JDETipsViewErrorStackFree (LPBHVRCOM lpBhvrCom, LPVOID lpVoid,
LPDSD59JDET1C lpDS)
{
/************************************************************************
* Variable declarations
************************************************************************/
ID
idReturn = ER_SUCCESS;
HUSER
hUser = (HUSER)NULL;
HCACHE
hCache = (HCACHE)NULL;
/************************************************************************
* Check for NULL pointers
************************************************************************/
if ((lpBhvrCom == (LPBHVRCOM) NULL) ||
(lpVoid
== (LPVOID)
NULL) ||
(lpDS
== (LPDSD59JDET1C)NULL))
{
jdeErrorSet (lpBhvrCom, lpVoid, (ID) 0, _J("4363"), (LPVOID) NULL);
return ER_ERROR;
}
/************************************************************************
* Main Processing
************************************************************************/
if(MathZeroTest(&lpDS->mnHandle) == 0)
return ER_SUCCESS;
/********** Get User Handle **********/
if( JDB_InitBhvr(lpBhvrCom, &hUser, (JCHAR *) NULL, JDEDB_COMMIT_AUTO) != JDEDB_PASSED )
{
jdeErrorSet(lpBhvrCom, lpVoid, (ID) 0, _J("139F"), (LPVOID) NULL);
idReturn = ER_ERROR;
goto FunctionCleanUp;
}
/********** Destroy Cache **********/
idReturn = I59JDET1_InitCacheErrors(lpBhvrCom, lpVoid, hUser, &lpDS->mnHandle, &hCache);
if(idReturn == ER_ERROR)
goto FunctionCleanUp;
jdeCacheTerminateAll(hUser, hCache);
/************************************************************************
* Function Clean Up
************************************************************************/
FunctionCleanUp:
/* free user */
if(hUser)
JDB_FreeBhvr(hUser);
return idReturn;
}
/**************************************************************************
* Business Function: JDETipsViewErrorStackInit
*
*
Description: JDETipsViewErrorStackInit
*
*
Parameters:
*
LPBHVRCOM
lpBhvrCom
Business Function Communications
*
LPVOID
lpVoid
Void Parameter - DO NOT USE!

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 17

Business Function Error Handling in UBEs


*
LPDSD59JD
lpDS
Parameter Data Structure Pointer
*
*************************************************************************/
JDEBFRTN (ID) JDEBFWINAPI JDETipsViewErrorStackInit (LPBHVRCOM lpBhvrCom, LPVOID lpVoid,
LPDSD59JDET1A lpDS)
{
/************************************************************************
* Variable declarations
************************************************************************/
ID
idReturn = ER_SUCCESS;
HUSER
hUser = (HUSER)NULL;
HCACHE
hCache = (HCACHE)NULL;
int idx;
/************************************************************************
* Check for NULL pointers
************************************************************************/
if ((lpBhvrCom == (LPBHVRCOM) NULL) ||
(lpVoid
== (LPVOID)
NULL) ||
(lpDS
== (LPDSD59JDET1A)NULL))
{
jdeErrorSet (lpBhvrCom, lpVoid, (ID) 0, _J("4363"), (LPVOID) NULL);
return ER_ERROR;
}
/************************************************************************
* Main Processing
************************************************************************/
lpDS->nErrorCount = 0;
lpDS->nWarningCount = 0;
/********** Get User Handle and Init Cache **********/
if( JDB_InitBhvr(lpBhvrCom, &hUser, (JCHAR *) NULL, JDEDB_COMMIT_AUTO) != JDEDB_PASSED )
{
jdeErrorSet(lpBhvrCom, lpVoid, (ID) 0, _J("139F"), (LPVOID) NULL);
idReturn = ER_ERROR;
goto FunctionCleanUp;
}
LongToMathNumeric(JDB_GetInternalNextNumber(), &lpDS->mnHandle);
idReturn = I59JDET1_InitCacheErrors(lpBhvrCom, lpVoid, hUser, &lpDS->mnHandle, &hCache);
if(idReturn == ER_ERROR)
goto FunctionCleanUp;
/********** Iterate Error Stack and save data to cache **********/
jdeErrorSetToFirstEx(lpBhvrCom, lpVoid);
for(idx = 0;;idx++)
{
LPJDEERROR_RECORD
D59JDET1_ERROR

perr = (LPJDEERROR_RECORD)NULL;
errorRec={0};

/* 1 million test is really just there to prevent infinite loop in the event of any kind of
bug in JDE api...
if there over 1 million errors on the error stack, you have bigger issues...
*/
if(idx > 1000000)
{
jdeErrorSet(lpBhvrCom, lpVoid, (ID) 0, _J("018Y"), (LPVOID)NULL);
idReturn = ER_ERROR;

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 18

Business Function Error Handling in UBEs


break;
}
perr = jdeErrorGetNextDDItemNameInfoEx(lpBhvrCom, lpVoid);
if(!perr)
break;
/* scrub iErrLevel and set return counts
- There is a known bug in the JDE API where the the iErrLevel is
set to '1' instead of 1 and '2' instead of 2, etc.
this will scrub to match the JDE_ERR_LEVEL enum members.
- JDE doesn't define a const for informational type so we will
scrub to warn to be consistent with JDE enum
and returned counts
*/
if(perr->iErrLevel == ERROR_LEVEL_ERROR || perr->iErrLevel == _J('1'))
{
errorRec.data.nErrorLevel = ERROR_LEVEL_ERROR;
lpDS->nErrorCount++;
}
else /* everything else s/b either a warning or informational */
{
errorRec.data.nErrorLevel = ERROR_LEVEL_WARNING;
lpDS->nWarningCount++;
}

/* set rest of cache rec data and save to cache */


errorRec.idx = idx;
jdeStrncpy(errorRec.data.szErrorID,
perr->szDict, DIM(errorRec.data.szErrorID)-1);
jdeStrncpy(errorRec.data.szErrorMsg,perr->lpszShortDesc, DIM(errorRec.data.szErrorMsg)-1);
jdeStrncpy(errorRec.data.szSourceFile,perr->szFileName,DIM(errorRec.data.szSourceFile)-1);
errorRec.data.nLineNo = perr->iLineNo;
if(jdeCacheAdd(hCache, (void *)&errorRec, sizeof(errorRec)) != JDECM_PASSED)
{
DSDE0022
msg={0};
jdeStrncpy(msg.szDescription, B59JDET1_CACHE_NAME_ERRORS, DIM(msg.szDescription)-1);
jdeErrorSet(lpBhvrCom, lpVoid, (ID) 0, _J("078O"), (LPVOID)&msg);
idReturn = ER_ERROR;
break;
}
}
jdeErrorSetToFirstEx(lpBhvrCom, lpVoid);
/***** if cache is empty or there was an error destroy cache and
return 0 for handle (caller can still call iterate or term function w/o any issues)
*****/
if(idReturn == ER_ERROR || jdeCacheGetNumRecords(hCache) == 0)
{
jdeCacheTerminateAll(hUser, hCache);
memset((void *)lpDS, 0x0, sizeof(DSD59JDET1A));
}
/************************************************************************
* Function Clean Up
************************************************************************/
FunctionCleanUp:
lpDS->nTotalCount = lpDS->nErrorCount + lpDS->nWarningCount;
/* free user */

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 19

Business Function Error Handling in UBEs


if(hUser)
JDB_FreeBhvr(hUser);
return idReturn;
}
/**************************************************************************
* Business Function: JDETipsViewErrorStackIterate
*
*
Description: JDETipsViewErrorStackIterate
*
*
Parameters:
*
LPBHVRCOM
lpBhvrCom
Business Function Communications
*
LPVOID
lpVoid
Void Parameter - DO NOT USE!
*
LPDSD59JD
lpDS
Parameter Data Structure Pointer
*
*************************************************************************/
JDEBFRTN (ID) JDEBFWINAPI JDETipsViewErrorStackIterate (LPBHVRCOM lpBhvrCom, LPVOID lpVoid,
LPDSD59JDET1B lpDS)
{
/************************************************************************
* Variable declarations
************************************************************************/
ID
idReturn = ER_SUCCESS;
HUSER
hUser = (HUSER)NULL;
HCACHE
hCache = (HCACHE)NULL;
HCURSOR
hCursor = (HCURSOR)NULL;
D59JDET1_ERROR
errorRec={0};
DSD59JDET1B
dsTmp;
/************************************************************************
* Check for NULL pointers
************************************************************************/
if ((lpBhvrCom == (LPBHVRCOM) NULL) ||
(lpVoid
== (LPVOID)
NULL) ||
(lpDS
== (LPDSD59JDET1B)NULL))
{
jdeErrorSet (lpBhvrCom, lpVoid, (ID) 0, _J("4363"), (LPVOID) NULL);
return ER_ERROR;
}
/************************************************************************
* Main Processing
************************************************************************/
lpDS->cEndOfList = _J('1');
if(MathZeroTest(&lpDS->mnHandle) == 0)
return ER_SUCCESS;
/********** Get User and Cache Handle **********/
if( JDB_InitBhvr(lpBhvrCom, &hUser, (JCHAR *) NULL, JDEDB_COMMIT_AUTO) != JDEDB_PASSED )
{
jdeErrorSet(lpBhvrCom, lpVoid, (ID) 0, _J("139F"), (LPVOID) NULL);
idReturn = ER_ERROR;
goto FunctionCleanUp;
}
idReturn = I59JDET1_InitCacheErrors(lpBhvrCom, lpVoid, hUser, &lpDS->mnHandle, &hCache);
if(idReturn == ER_ERROR)
goto FunctionCleanUp;
/********* Close Cursor (if requested by user) **********/

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 20

Business Function Error Handling in UBEs


hCursor = (HCURSOR)lpDS->idCursorHandle;
if(lpDS->cCloseCursor == _J('1'))
{
if(hCursor)
jdeCacheCloseCursor(hCache, hCursor);
hCursor = (HCURSOR)NULL;
goto FunctionCleanUp;
}

/********** Open a new Cursor if not all ready open **********/


if(!hCursor)
{
if(jdeCacheOpenCursor(hCache, &hCursor) != JDECM_PASSED)
{
if(jdeCacheGetNumRecords(hCache) > 0)
{
DSDE0022
msg={0};
jdeStrncpy(msg.szDescription, B59JDET1_CACHE_NAME_ERRORS, DIM(msg.szDescription)-1);
jdeErrorSet(lpBhvrCom, lpVoid, (ID) 0, _J("078M"), (LPVOID)&msg);
idReturn = ER_ERROR;
}
goto FunctionCleanUp;
}
}

/********** Return First/Next Record **********


(if cursor advanced beyond end of list, close cursor and exit w/o record return)
*/
if(jdeCacheFetch(hCache, hCursor, (void *)&errorRec, (void *)NULL) != JDECM_PASSED)
{
jdeCacheCloseCursor(hCache, hCursor);
hCursor = (HCURSOR)NULL;
goto FunctionCleanUp;
}
/* record found, return values */
memcpy(&dsTmp, lpDS, sizeof(DSD59JDET1B));
/* preserve meta-data fields */
memcpy(lpDS, &errorRec.data, sizeof(DSD59JDET1B));
/* reset metadata fields */
lpDS->cEndOfList = _J('0');
MathCopy(&lpDS->mnHandle, &dsTmp.mnHandle);
lpDS->cCloseCursor = dsTmp.cCloseCursor;
/************************************************************************
* Function Clean Up
************************************************************************/
FunctionCleanUp:
lpDS->idCursorHandle = (ID)hCursor;
/* free user */
if(hUser)
JDB_FreeBhvr(hUser);
return idReturn;
}
/* Internal function comment block */

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 21

Business Function Error Handling in UBEs


/**************************************************************************
*
Function: I59JDET1_InitCacheErrors
*
*
Notes:
*
*
Returns:
*
* Parameters:
**************************************************************************/
static ID I59JDET1_InitCacheErrors(LPBHVRCOM lpBhvrCom, LPVOID lpVoid, HUSER hUser,
LPMATH_NUMERIC pmnHandle, HCACHE *hCache)
{
/************************************************************************
* Variable declarations
************************************************************************/
JCHAR
szCacheName[50]={0}, szJobNum[MAXLEN_MATH_NUMERIC + 1]={0};
ushort
i = 0;
JDECMINDEXSTRUCT dsIndex[1]={0};
D59JDET1_ERROR
dsCache;
/************************************************************************
* Main Processing
************************************************************************/
FormatMathNumeric(szJobNum, pmnHandle);
jdeStrcpy(szCacheName, B59JDET1_CACHE_NAME_ERRORS);
jdeStrncat(szCacheName, szJobNum, DIM(szCacheName) - jdeStrlen(szCacheName) - 1);

/* set index 1*/


dsIndex[0].nKeyID
dsIndex[0].nNumSegments

= 1;
= 1;

dsIndex[0].CacheKey[i].nOffset
dsIndex[0].CacheKey[i].nSize
dsIndex[0].CacheKey[i++].idDataType

= offsetof(D59JDET1_ERROR, idx);
= sizeof(dsCache.idx);
= EVDT_INT;

if(jdeCacheInit(hUser, hCache, szCacheName, dsIndex) != JDECM_PASSED)


{
DSDE0022
msg={0};
jdeStrncpy(msg.szDescription, B59JDET1_CACHE_NAME_ERRORS, DIM(msg.szDescription)-1);
jdeErrorSet(lpBhvrCom, lpVoid, (ID) 0, _J("078L"), (LPVOID)&msg);
return ER_ERROR;
}
return ER_SUCCESS;
}
/* B59JDET1.c file Code Listing End */

Summary
I add the error handling logic I described in this article to just about every UBE that I create that
makes Business Function calls. By implementing the techniques I describe, you too can quickly
add BSFN error handling capabilities to almost any UBE in a matter of minutes. With some slight
modification, you can display errors and invoke error handling logic in your UBEs to prevent
further processing or stop data updates and other transactional type logic. Additionally, once you
understand how the Utility Business Function works, you can implement the underlying JDE C
API calls to expand error handling capabilities in your own Business Functions and Interactive
Applications as well.

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 22

Business Function Error Handling in UBEs

Brian Oster is a JDE developer who performs three different roles at Acme Brick. Brian is the
manager of the development team, creates the code for various business needs, and architects
the system (or solution). He specializes in solutions that promote ease of use, while also following
IT best practices, such as fully normalized data models. You may contact the author at
JDEtips.Authors@ERPtips.com. Be sure to mention the authors name and/or the article title.

License Information: The use of JDE is granted to Klee Associates, Inc. by permission from J.D. Edwards World Source
Company. The information on this website and in our publications is the copyrighted work of Klee Associates, Inc. and is
owned by Klee Associates, Inc. NO WARRANTY: This documentation is delivered as is, and Klee Associates, Inc. makes
no warranty as to its accuracy or use. Any use of this documentation is at the risk of the user. Although we make every
good faith effort to ensure accuracy, this document may include technical or other inaccuracies or typographical errors.
Klee Associates, Inc. reserves the right to make changes without prior notice. NO AFFILIATION: Klee Associates, Inc.
and this publication are not affiliated with or endorsed by J.D. Edwards & Company. J.D. Edwards software referenced on
this site is furnished under license agreements between J.D. Edwards & Company and their customers and can be used
only within the terms of such agreements. J.D. Edwards is a registered trademark of J.D. Edwards & Company. JDE and
OneWorld are registered trademarks of J.D. Edwards World Source Company. WorldSoftware is a trademark of J.D.
Edwards World Source Company. PeopleSoft,the PeopleSoft logo, PeopleTools, PS/inVision, PeopleCode, PeopleBooks,
PeopleTalk, and Pure Internet Architecture are registered trademarks, and Intelligent Context Manager and The RealTime Enterprise are trademarks of PeopleSoft, Inc. Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Klee Associates, Inc. is not affiliated with or endorsed by Oracle Corporation.

Copyright 2012 by Klee Associates, Inc.

www.JDEtips.com

Page 23

Das könnte Ihnen auch gefallen