Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Topics - Leo Chu

Pages: [1] 2
1
Mathematical calculation for different decimal notation

This topic instructs how to perform correct mathematical calculation for different decimal notation.
We'll also cover scenarios when Windows OS uses different decimal notation other than "1,234,567.89".


SAP Default Decimal Notation
SAP provides below decimal notation options for user profile default
   "X":   1,234,567.89
   "Y":   1 234 567,89
   " ":   1.234.567,89

In order for JavaScript to perform calculation, we need to first convert the format to raw format then calculate.
Once we have the result, we need to convert the format back to what SAP requests then assign the value.

Step 1: Create the function to convert specific format value to raw format
// Example: z_val = decimalNotationFormat("Y",Gweightitem.toString(),3);
function decimalNotationFormat(numberFormat,number,nDec){
   var str = "";

   if(nDec == void 0)   // Default for number of decimal places
      nDec = 2;
     
   switch(numberFormat) {
      case 'X':
         str = number.replace(/\,/g, '');      // Replace , with nothing
         break;
         
      case 'Y':
         str = number.replace(/\s+/g,'');      // Remove Blank Spaces
         str = str.replace(/\,/g, '.');         // Replace , with .
         break;
         
      default:
         str = number.replace(/\./g, '');      // Replace . with nothing
         str = str.replace(/\,/g, '.');         // Replace , with .
         break;
   }
   
   if(str.indexOf(".")>-1){
      if((str.length - (str.indexOf(".")+1)) > nDec)
         str = str.substring(0,(str.indexOf(".")+nDec+1));
   }

   return str;
}


Step 2: Create the function to convert raw format value to specific decimal notation format
// Example:  z_value = userSAPDecimalFormat(z_val,"Y");
function userSAPDecimalFormat(nStr,nSeparator){
   var fStr = nStr.toString().replace(/\,/g, '');

   var str = fStr.split('.');
   
   if(str[0].substring(0,1) == "-"){
      var negative_val_flag = true;
      str[0] = str[0].substring(1,str[0].length);
   }
   else{
      var negative_val_flag = false;
   }
   
   var offset = str[0].length % 3;

   if(nSeparator == ' ')
      str[0] = str[0].substring(0, offset) + str[0].substring(offset).replace(/([0-9]{3})/g, ".$1");
   if(nSeparator == 'X')
      str[0] = str[0].substring(0, offset) + str[0].substring(offset).replace(/([0-9]{3})/g, ",$1");
   if(nSeparator == 'Y')
      str[0] = str[0].substring(0, offset) + str[0].substring(offset).replace(/([0-9]{3})/g, " $1");
   
   if(offset == 0)
      str[0] = str[0].substring(1,str[0].length);

   if(negative_val_flag)
      str[0] = "-" + str[0];
   
   if(nSeparator == 'Y' || nSeparator == ' ') {
      return str.join(',');
   } else {
      return str.join('.');
   }     
}


Step 3: Use above functions to build the calculation logic
If Windows OS uses decimal notation setting as "1,234,567.89" but SAP requests"1 234 567,89":
   mm01_mat_weight = decimalNotationFormat("Y", mm01_mat_weight.toString(), 3);      //Convert material weight to raw format
   mm01_mat_qty = decimalNotationFormat("Y", mm01_mat_qty .toString(), 3);         //Convert material quantity to raw format
   mm01_total_weight = parseFloat(mm01_mat_weight) * parseFloat(mm01_mat_qty);      //Calculate material total quantity
   mm01_total_weight = userSAPDecimalFormat(mm01_total_weight, "Y");            //Convert material total quantity to SAP format "Y"


Step 4: If Windows OS doesn't use decimal notation as "1,234,567.89"
If Windows OS uses decimal notation setting as "1 234 567,89"
   mm01_total_weight = parseFloat(mm01_mat_weight.replace(/\s+/g,'')) * parseFloat(mm01_mat_qty.replace(/\s+/g,''));      //Calculate material total quantity
   mm01_total_weight = userSAPDecimalFormat(mm01_total_weight, "Y");            //Convert material total quantity to SAP format "Y"

If Windows OS uses decimal notation setting as "1.234.567,89"
   mm01_total_weight = parseFloat(mm01_mat_weight.replace(/\./g,'')) * parseFloat(mm01_mat_qty.replace(/\./g,''));      //Calculate material total quantity
   mm01_total_weight = userSAPDecimalFormat(mm01_total_weight, " ");            //Convert material total quantity to SAP format " "

Note: parseFloat() takes OS decimal notation format in order to convert, but it becomes raw format "1234567.89" after conversion

2
Diagnose/Check RFC User authorization error

This topic is to check RFC user authorization when the remote function call is returning authority exception or data is not matching with the result from SE37.
Normally is because the RFC user doesn't have proper authority when executing specific function module, so the detail of RFC authorization issue needs to be diagnose.
We'll use standard SAP transaction SU53 to get RFC user's authorization result, so the information can be passed to security team in order to grant proper authorization.

Step 1: Execute the function module call via script
After the RFC exception is occurred, now SAP has captured the authorization issue detail.


Step 2: Go to transaction SU53


Step 3: Switch user to RFC username
Select "Authorization Values" -> "Other User"

Put RFC username in the "User" field, then click "Execute"


Step 4: Check the result screen
If the result screen shows any failed authorization record, take a screen shot of this screen then send it to security team for further diagnose.


Note: If you have question on accessing SU53 transaction, please contact your security team to get access

3
Liquid UI: Use _notificationID to get unique device token

This topic is to use system variable "_notificationID" to get unique Android or iOS device token via Liquid UI Server.
The purpose of using device token is specifically for push notification service.
For more detail, please contact Synactive.

Requirement:
Liquid UI Server:         Version 3.5.567.0 and above
Liquid UI on Android:   Version 2.0.33.0 and above
Liquid UI on iOS:         Version 2.0.26.0 and above


Step 1: Create user interface
clearscreen();

inputfield([1,0], {"name":"z_notificationID", "size":180, "readonly":true, "nolabel":true});



Step 2: Add logic to display the value from _notificationID
set("V[z_notificationID]", _notificationID);

println("=====>> _notificationID="+_notificationID+"<==");



Step 3: Connect Android or iOS device to the system via Liquid UI Server
When screen script gets load, device token will show in the field and Cornelius window.

Example of an Android device token:
f5R8ErhYdBI:APA91bG1Le-6i3ArojW0jEEvfU3eZCKeenCCCHe_yn-0iJRKaLeQV3AWM60ds5DLMTdrAtMGhq7sy_f5Cq04TdyE7hA1DPIWSSZi9hZckMTR2DyaVvFMZDMajthyznHpb3INQgkGNgaN

Example of an iOS device token:
3b1459128815b0f63109df1559d53e6d0757fc2d845f36018ae7021bb374b9c8

Note: _notificationID only works for Android and iOS device which installs required Liquid UI application
         The value of _notificationID will show "undefined" if it's connect from SAP GUI


Note: For more detail about using push notification service, please contact Synactive



See attachments for script example

4
Liquid UI: Read specific columns from a list screen

This example is to create the logic which reads specific columns from a list screen.
The logic should adobt the window size of SAP GUI, or works for Android or iOS devices.

We have a generic function to read everything from a single list screen.
However, the challenge is to cover the scenario where the content is more than a page or wider than a screen displays.

Requirement:
Liquid UI WS Version:      1.2.327.0 and above
Liquid UI Server Version:   3.5.567.0 and above


Step 1: Create user interface

//Logic to accept user input of scrolling and trigger the scrolling action
pushbutton([TOOLBAR], "Read Entire List", "?", {"process":readEntireList});



Step 2: Include the generic function
//Function to trim a string value
String.prototype.trim = function () {
   return this.replace(/^\s+|\s+$/g, "");
}


//Function to check if a variable is blank
function isBlank(value) {   
   if (typeof(value) == string) {
      value = value();
   }
   
   var blank = (value == void 0 || value == "" || value == null || value == undefined);
   
   return blank;
}


//Function to read everything according to specified column headers from list screent
function listRowsRead(sColumns,allrowsFlg,numRows,retFlag,retArr,headerRow) {

   set('V[cursorPosition]','');
   var dbg = false;
   var arColnames = sColumns.split(',');
   var arCols = [];
   var iCol;
   if (headerRow == void 0 || isBlank(headerRow)){
      var iRowHeader = 1;
   } else {
      var iRowHeader = headerRow;
   }
   var arColVals  = [];
   var returnArr = retArr.split(',');
   
   // for each passed in col titls,
   // determine abs cols
   for(iCol=0; iCol<arColnames.length; iCol++) {
      //print('    '+arColnames[iCol]+'   ');
      for(rb=new Reebok([iRowHeader]); rb.pos.row==iRowHeader; rb=rb.nextSibling) {
         if(dbg) println('name=*'+rb.name.label+'*');
         if(arColnames[iCol] == rb.name.label) {
            arCols[iCol] = rb.pos.col;
         }   
      }
   }
   
   var iRow = iRowHeader+2;
   var nRows;
   if(allrowsFlg==true || numRows==0)
      nRows = _listlastvisiblerow;
   else
      nRows = numRows;
      
   for(iLRow=_listfirstvisiblerow; iLRow<=nRows; iLRow++,iRow++) {
      var rec = {};
      //var temp1="";
      var temp1=[];
      // now we'll yield each record
      for(iCol=0; iCol<arCols.length; iCol++){
         if(arCols[iCol] != void 0) {
            rec[ arColnames[iCol] ] = Reebok([iRow,arCols[iCol]]).name;
            temp=rec[arColnames[iCol]];
            rec.row = iLRow;
         }
         for(var ii=0; ii<returnArr.length; ii++){
            if(arColnames[iCol]==returnArr[ii]){
               temp=temp.substring(temp.lastIndexOf("@")+1,temp.length).trim();
               temp1.push(temp);
            }
         }
      }
      arColVals.push(temp1);
   }
   
   if(retFlag){
      return arColVals;
   }
}



Step 3: Create function logic to read through the list screen

//Function to read full list content
function readEntireList(){

   onscreen "RVKRED01.0120"
      //Specify all desired column headers in an array follow by display sequence
      var aryTargetColNames = ['Sold-to pt','Cred. acct','Name 1','Credit value','Total receivables','Credit limit','Purchase order no.','Document'];

      //Default all temporary variables
      var intHeaderRow=1;         
      arCols = [];
      arScrollCols = [];
      var   arScrnCols = [], aryAdvColNames = [], scrnResult = [];
      var iCol=0, iLastBorderCol=0, iLastDatalength=0;
      var boolHScrollCompleteFlg = false;
      var intHScrollCounter=0;

      arResult = [];
      iRecordCount = 0;
      enter("/80");      //First Page, make sure to read the list from top

   onscreen "RVKRED01.0120"
      var total_list_width = 0;
      //Logic to calculate the width of non-scrollable area
      for(rb=new Reebok([0]); rb.pos.row==0; rb=rb.nextSibling){
         total_list_width = total_list_width + rb.name.label.length;
      }
      
      var fixdatawidth = total_list_width - _listdatawidth;
      enter("/hscrollto=0");      //Scroll horizontally to the first column

   //Logic to determine header column positions and screen scroll count   
NEW_LIST_SCREEN:;   
   onscreen "RVKRED01.0120"
      //Reset temp array of column header position of each screen
      arScrnCols = [];
   
      //Loop to check each header in the array
      while(iCol<aryTargetColNames.length){
         for(rb=new Reebok([intHeaderRow]); rb.pos.row==intHeaderRow; rb=rb.nextSibling){
            //If column header matches data in the header array
            if(aryTargetColNames[iCol] == rb.name.label){
               //If next column header is still available
               if(rb.nextSibling != void 0){
                  //If next column header is a border, means curent column is fully displayed
                  if(rb.nextSibling.name.label == "5"){
                     //Set current column header position to temp column postion array
                     arScrnCols.push(rb.pos.col);
                     iCol++;
                  }
               }
            }
            
            //Set the column position of border if read
            if(rb.name.label == "5"){
               iLastBorderCol = rb.pos.col + _listfirstvisiblecol - fixdatawidth + 1;
            }
         }
         
         //Add read column header postion to data array
         arCols.push(arScrnCols);
         
         //If more columns need to be determined and there're more columns in the back   
         if(iCol<aryTargetColNames.length || (_listfirstvisiblecol+_listdatawidth)<_listtotalwidth){
            //Set scroll position value then horizontally scroll the screen
            arScrollCols.push(iLastBorderCol);
            enter("/hscrollto=" + iLastBorderCol);
            goto NEW_LIST_SCREEN;
         }
         //Or stop the loop
         else{
            break;
         }
      }

      //Convert original list header array to advanced header string array
      for(var jCtr=0, tmp_counter=0; jCtr<arCols.length; jCtr++){
         tmp_str = "";
         for(var kCtr=0; kCtr<arCols[jCtr].length; kCtr++){
            tmp_str = (kCtr==0)?(aryTargetColNames[tmp_counter]):(tmp_str + "," + aryTargetColNames[tmp_counter]);
            tmp_counter++;
         }
         aryAdvColNames.push(tmp_str);
      }
      
      enter("/hscrollto=0");   
      
   // List of SD Documents
CONTINUE_TO_CHECK_LIST:;   
   onscreen 'RVKRED01.0120'
      //If horizontal scroll is completed, continue to check vertical scroll
      if(boolHScrollCompleteFlg){
         goto CONTINUE_TO_V_SCROLL;
      }
   
      //Read detail based on current screen
      scrnResult = listRowsRead(aryAdvColNames[intHScrollCounter],true,0,true,aryAdvColNames[intHScrollCounter],intHeaderRow);

      //If the screen has not horizontal scrolled
      if(intHScrollCounter == 0){
         for(var jCtr in scrnResult){
            //If data matches bottom border line, set amount of record and stop adding new record to data array
            if(scrnResult[jCtr][0].substring(0,7) == '6444444' || scrnResult[jCtr][0].substring(0,7) == '1444444' || scrnResult[jCtr][0].substring(0,7) == '0444444'){
               iRecordCount = arResult.length;
               break;
            }
            
            //Add new record to data array
            arResult.push(scrnResult[jCtr]);
         }
      }
      //If the screen is horizontal scrolled
      else{   
         //Add current screen detail to each row of new added records
         for(var iCtr=iLastDatalength, jCtr=0; iCtr<arResult.length; iCtr++, jCtr++)
            arResult[iCtr] = arResult[iCtr].concat(scrnResult[jCtr]);
      }
      
      //If the screen has not been horizontally scrolled
      if(!boolHScrollCompleteFlg){
         //If next scoll col position is still available
         if(intHScrollCounter < aryAdvColNames.length){
            //Increment the horizontal scroll counter, then scroll
            intHScrollCounter++;
            
            if(intHScrollCounter == aryAdvColNames.length){
               //Mark horizontal scroll is completed and scroll back to 0 col
               boolHScrollCompleteFlg = true;
               enter("/hscrollto=0");
               goto CONTINUE_TO_CHECK_LIST;
            }
            else{
               enter("/hscrollto=" + arScrollCols[intHScrollCounter-1]);
               goto CONTINUE_TO_CHECK_LIST;
            }
         }
         //If no next scroll col position
         else {
            //Mark horizontal scroll is completed and scroll back to 0 col
            boolHScrollCompleteFlg = true;
            enter("/hscrollto=0");
            goto CONTINUE_TO_CHECK_LIST;
         }
      }
      
CONTINUE_TO_V_SCROLL:;

      //If record counter is not set and read record length is less than system value, continue to read next page
      if((arResult.length < _listlastrow) && (iRecordCount==0)){
         
         //Reset counter and flag then go to next page of list screen
         intHScrollCounter = 0;
         boolHScrollCompleteFlg = false;
         iLastDatalength = arResult.length;
         enter("/82");      //Next Page
         goto CONTINUE_TO_CHECK_LIST;
      }
      //If record counter is set or read record length matches system value, means ends of list read
      else {
         //Display read records in Cornelius window
         for(var jCtr in arResult){
            println("=====>>arResult[" + jCtr + "]=" + arResult[jCtr] + "<==");            
         }
         enter("?");
      }   
}


See attachments for script example

5
Liquid UI: Convert function module structure detail to type object

This example is to convert function module structure detail to a type object, which is required when Use call command to get native SAP function module detail.

For each function module structure, it needs specific type object for call command to reference.

Quote
   var z_STRUCTURE_NAME = {
      name:STRUCTURE_NAME,
      components:[
         { name:COMPONENT_NAME,   length:DATA_LENGTH,   decimalpl:DECIMAL_POINT_LENGTH,   type:DATA_TYPE },
         ...
      ]
   };

In type object, each component has different ABAP data type, which can be referenced from below table.

LUI Type    ABAP Type     Data Type           Description                                                                                                                   
b'b'INT1Cannot be substituted with 'C'
C'C'CHAR, UNIT, LANG, CUKY, LCHR
D'D'DATSCan be substituted with 'C'
When used as 'D', the Component will be a JavaScript Date object,
whereas, if specified as 'C', it will be a string returned by SAP in the format of "19941107"
F or f'F'FLTPCannot be substituted with 'C'
For BAPI FM, specify LUI type as "F"; for FM RFC_READ_TABLE, specify LUI type as "f"
T'T'TIMSCurrently same as 'C' and can be substituted with 'C'
N'N'NUMCCan be substituted with 'C'
P or f'P'QUAN, DEC, CURRCannot be substituted with 'C'
For BAPI FM, specify LUI type as "P"; for FM RFC_READ_TABLE, specify LUI type as "f"
s or i's'INT2Cannot be substituted with 'C'
For BAPI FM, specify LUI type as "s"; for FM RFC_READ_TABLE, specify LUI type as "i"
I'I'INT, INT4Cannot be substituted with 'C'


Step 1: Check output structure according to parameter

The structure of each output parameter needs to be specified individually.
Its detail can be referenced from SAP in transaction SE37 follow by below steps.
1.   On initial screen of SE37, enter the FM
2.   Click on "Display" button to check detail
3.   On the display screen, select "Export" or "Tables" tab according to your output
4.   In corresponding tab, find the row of output parameter and double click on it to see detail structure
5.   Stay in the structure detail screen, and prepare to create the script based on the screen

Use FM BAPI_ALM_ORDER_GET_DETAIL as an example, its ES_HEADER parameter has structure BAPI_ALM_ORDER_HEADER_E which SAP screen should shown as below.

Component                               Typing Method     Component Type                          Data Type     Length     Decimal Pl     Short Description                                 
ORDERID1AUFNRCHAR120Order Number
ORDER_TYPE1AUFARTCHAR40Order Type
PLANPLANT1IWERKCHAR40Maintenance Planning Plant
BUS_AREA1GSBERCHAR40Business Area
MN_WK_CTR1GEWRKCHAR80Main work center for maintenance tasks
PLANT1WERGWCHAR40Plant associated with main work center
MN_WKCTR_ID1LGWIDNUMC80Object ID of the Work Center
PMACTTYPE1ILACHAR30Maintenance activity type
PLANGROUP1INGRPCHAR30Planner Group for Customer Service and Plant Maintenance
SYSTCOND1ANLZUCHAR10Syst.Condition
FUNCT_LOC1TPLNRCHAR300Functional Location
EQUIPMENT1EQUNRCHAR180Equipment Number
SERIALNO1GERNRCHAR180Serial Number
MATERIAL1MATNRCHAR180Material Number
ASSEMBLY1BAUTLCHAR180Assembly
DEVICEDATA1DEVICEIDCHAR400Additional Device Data
MAINTPLANT1SWERKCHAR40Maintenance plant
LOCATION1STORTCHAR100Asset location
MAINTROOM1RAUMNRCHAR80Room
PLSECTN1BEBERCHAR30Plant section
LOC_WK_CTR1ARBPLCHAR80Work center
LOC_WKCTR_ID1PPSIDNUMC80Object ID of PP work center
ABCINDIC1ABCKZCHAR10ABC indicator for technical object
SORTFIELD1EQFNRCHAR300Sort field
COMP_CODE1BUKRSCHAR40Company Code
PROFIT_CTR1PRCTRCHAR100Profit Center
CO_AREA1KOKRSCHAR40Controlling Area
RESPCCTR1AUFKOSTVCHAR100Responsible cost center
FUNC_AREA1FKBERCHAR160Functional Area
SUPERIOR_NETWORK1CO_TEILNETCHAR120Number of superior network
SUPERIOR_ACTIVITY1CN_VORNRCHAR40Activity number in network and standard network
SUPERIOR_ROUTING_NO1CO_AUFPLNUMC100Routing number of operations in the order
SUPERIOR_COUNTER1CO_APLZLNUMC80General counter for order
WBS_ELEM1PS_PSP_PNRNUMC80Work Breakdown Structure Element (WBS Element)
PROJ_DEF1PS_PSP_PRONUMC80Project definition
PROCESSING_GROUP1AUFABKRSNUMC20Processing group
OBJECTCLASS1SCOPE_CVCHAR20Object Class
TAXJURCODE1TXJCDCHAR150Tax Jurisdiction
LOC_COMP_CODE1BUKRSCHAR40Company Code
LOC_CO_AREA1KOKRSCHAR40Controlling Area
ASSET_NO1ANLN1CHAR120Main Asset Number
SUB_NUMBER1ANLN2CHAR40Asset Subnumber
LOC_BUS_AREA1GSBERCHAR40Business Area
COSTCENTER1KOSTLCHAR100Cost Center
LOC_WBS_ELEM1PS_PSP_PNRNUMC80Work Breakdown Structure Element (WBS Element)
STANDORDER1DAUFNCHAR120Standing order number
SETTLORDER1ILOM_ORDSTCHAR120Settlement order
SALESORG1VKORGCHAR40Sales Organization
DISTR_CHAN1VTWEGCHAR20Distribution Channel
DIVISION1SPARTCHAR20Division
ORDPLANID1AUF_PLKNZCHAR10Maintenance order planning indicator
START_DATE1CO_GSTRPDATS80Basic start date
FINISH_DATE1CO_GLTRPDATS80Basic finish date
BASICSTART1CO_GSUZPTIMS60Basic start time
BASIC_FIN1CO_GLUZPTIMS60Basic finish (time)
PRIORITY1PRIOKCHAR10Priority
REVISION1REVNICHAR80Revision for Plant Maintenance and Customer Service
VERSION1KAPVERSANUMC20Version of Available Capacity
SCHED_TYPE1TERMKZCHAR10Scheduling type
AUTOSCHED1CO_AUTERMCHAR10Indicator: Schedule automatically
CAP_REQMTS1AUKBEDCHAR10Indicator: Calculate capacity requirements
SCHEDULING_EXACT_BREAK_TIMES1BREAKSCHAR10Indicator:  Scheduling allowing for breaks
MRP_RELEVANT1AUDISP_PLUSCHAR10Reservation Relevance/Generation of Purchase Requisition
PRODUCTION_START_DATE1CO_GSTRSDATS80Scheduled start
PRODUCTION_FINISH_DATE1CO_GLTRSDATS80Scheduled finish
PRODUCTION_START_TIME1CO_GSUZSTIMS60Scheduled Start (Time)
PRODUCTION_FINISH_TIME1CO_GLUZSTIMS60Scheduled finish time
ACTUAL_START_DATE1CO_GSTRIDATS80Actual start date
ACTUAL_FINISH_DATE1CO_GETRIDATS80Confirmed Order Finish Date
ACTUAL_START_TIME1CO_GSUZITIMS60Actual start time
ACTUAL_FINISH_TIME1CO_GEUZITIMS60Confirmed order finish (time)
REFDATE1ADDATDATS80PM Order: Reference Date
SALES_ORD1KDAUFCHAR100Sales Order Number
S_ORD_ITEM1KDPOSNUMC60Item Number in Sales Order
CALC_MOTIVE1BEMOTCHAR20Accounting Indicator
INVEST_PROFILE1IM_PROFILCHAR60Investment measure profile
SCALE1IM_SIZECLCHAR20Scale of investment objects
INV_REASON1IZWEKCHAR20Reason for investment
ENVIR_INVEST1AM_UMWKZCHAR50Reason for environmental investment
ESTIMATED_COSTS1AUFUSER4CURR112Estimated total costs of order
CURRENCY1AUFWAERSCUKY50Order Currency
CURRENCY_ISO1ISOCDCHAR30ISO currency code
CSTG_SHEET1AUFKALSMCHAR60Costing Sheet
OVERHEAD_KEY1AUFZSCHLCHAR60Overhead key
RES_ANAL_KEY1ABGR_SCHLCHAR60Results Analysis Key
NETWORK_PROFILE1PROFIDNZPLCHAR70Network profile
CSTGVAPPLN1KALKVARPLNCHAR40Costing variant for planned costs
CSTGVARACT1KALKVARISTCHAR40Costing variant for actual costs
TASK_LIST_GROUP1PLNNRCHAR80Key for Task List Group
GROUP_COUNTER1PLNALCHAR20Group Counter
TASK_LIST_TYPE1PLNTYCHAR10Task List Type
RESP_PLANNER_GROUP1VAGRPCHAR30Responsible planner group/department
MNTPLAN1WARPLCHAR120Maintenance Plan
MAINTITEM1WAPOSCHAR160Maintenance item
CALL_NO1ABNUMINT4100Maintenance Plan Call Number
LAST_ORD1LAUFNCHAR120Order number
ENTERED_BY1AUFERFNAMCHAR120Entered by
ENTER_DATE1AUFERFDATDATS80Created on
CHANGED_BY1AUFAENAMCHAR120Last changed by
CHANGE_DATE1AUFAEDATDATS80Change date for Order Master
SCENARIO1SCRTPCHAR40Scenario or Subscreen Category
SYS_STATUS1CO_STTXTCHAR400System Status
USER_ST1CO_ASTEXCHAR10User status active
USERSTATUS1CO_ASTTXCHAR400Field displaying user status
STAT_PROF1J_STSMACHAR80Status Profile
OBJECT_NO1J_OBJNRCHAR220Object number
ROUTING_NO1CO_AUFPLNUMC100Routing number of operations in the order
RESERV_NO1RSNUMNUMC100Number of Reservation/Dependent Requirement
SHORT_TEXT1AUFTEXTCHAR400Description
LONG_TEXT1CO_LTEXTCHAR10Long text exists
NOTIF_NO1QMNUMCHAR120Notification No
ASSEMBLY_EXTERNAL1MGV_ASSEMBLY_EXTERNALCHAR400Long Material Number for Field ASSEMBLY
ASSEMBLY_GUID1MGV_ASSEMBLY_GUIDCHAR320External GUID for ASSEMBLY Field
ASSEMBLY_VERSION1MGV_ASSEMBLY_VERSIONCHAR100Version Number for ASSEMBLY Field
MATERIAL_EXTERNAL1MGV_MATERIAL_EXTERNALCHAR400Long Material Number for MATERIAL Field
MATERIAL_GUID1MGV_MATERIAL_GUIDCHAR320External GUID for MATERIAL Field
MATERIAL_VERSION1MGV_MATERIAL_VERSIONCHAR100Version Number for MATERIAL Field
CALID1WFCIDCHAR20Factory Calendar
KALSN1KALSNNUMC20Calendar Selection for Order
SUPERIOR_ORDERID1MAUFNRCHAR120Number of superior order
LEADING_ORDERID1CO_LAUFNRCHAR120Leading order in current processing
START_POINT1EAML_START_POINTCHAR180Start Point
END_POINT1EAML_END_POINTCHAR180End Point
LINEAR_LENGTH1EAML_LINEAR_LENGTHCHAR180Length
LINEAR_UNIT1EAML_LINEAR_UNITUNIT30Unit of Measurement for Linear Data
LINEAR_UNIT_ISO1EAML_LINEAR_UNIT_ISOCHAR30Unit of Measurement for Linear Data in ISO Code
FIRST_OFFSET_TYPE_CODE1EAML_OFFSET1_TYPE_CODECHAR20Type of First Offset
FIRST_OFFSET_VALUE1EAML_OFFSET1_VALUECHAR180Value of Offset 1
FIRST_OFFSET_UNIT1EAML_OFFSET1_UNITUNIT30Unit of Measurement for Offset 1
FIRST_OFFSET_UNIT_ISO1EAML_OFFSET1_UNIT_ISOCHAR30Unit of Measurement for Offset 1 in ISO Code
SECOND_OFFSET_TYPE_CODE1EAML_OFFSET2_TYPE_CODECHAR20Type of Second Offset
SECOND_OFFSET_VALUE1EAML_OFFSET2_VALUECHAR180Value of Offset 2
SECOND_OFFSET_UNIT1EAML_OFFSET2_UNITUNIT30Unit of Measurement for Offset 2
SECOND_OFFSET_UNIT_ISO1EAML_OFFSET2_UNIT_ISOCHAR30Unit of Measurement for Offset 2 in ISO Code
FIRST_OFFSET_TYPE_CODE_NAME1EAML_OFFSET1_TYPE_CODE_NAMECHAR600First Offset Type Description
SECOND_OFFSET_TYPE_CODE_NAME1EAML_OFFSET2_TYPE_CODE_NAMECHAR600Second Offset Type Description
MARKER_START_POINT1EAML_MARKER_START_POINTCHAR180Marker for Start Point
MARKER_DISTANCE_START_POINT1EAML_MARKER_DISTANCE_STARTCHAR180Distance between Marker and Start Point
MARKER_END_POINT1EAML_MARKER_END_POINTCHAR180Marker for End Point
MARKER_DISTANCE_END_POINT1EAML_MARKER_DISTANCE_ENDCHAR180Length Spec for Distance between Marker and End Point
MARKER_DISTANCE_UNIT1EAML_MARKER_DISTANCE_UNITUNIT30Unit for the Distance from Marker
MARKER_DISTANCE_UNIT_ISO1EAML_MARKER_DISTANCE_UNIT_ISOCHAR30Unit for the Distance from Marker in ISO Code
PRIOTYPE1ARTPRCHAR20Priority Type

Note:    Use "Ctrl+Y" can select table content from SAP GUI, then use "Ctrl+C" to copy



Step 2: Create lookup dictionary for different data type

According to different ABAP data types, we need to create a lookup dictionary when generating the structure object.

Quote
DATA_TYPE_REF_ARY = {
               "CHAR":"C", "UNIT":"C", "LANG":"C", "CUKY":"C", "LCHR":"C",
               "DATS":"D", "FLTP":"F", "TIMS":"T", "NUMC":"N",
               "QUAN":"P", "DEC":"P", "CURR":"P",
               "INT":"I", "INT1":"b", "INT2":"s", "INT4":"I"
   };

   

Step 3: Create custom layout on the screen

Quote
//Make sure this layout will only show in transaction SE37
if(_transaction == "SE37"){
   
   if(!z_se37_display_structure_obj){
      pushbutton([TOOLBAR], "Get Structure Object", "?", {"process":se37GetStructureObject});      //Trigger the function to generate structure object detail
   }
   else {
      pushbutton([TOOLBAR], "Clear Structure Object", "?", {"process":se37Reset});      //Trigger the function to reset all realted variables
     
      clearscreen();
     
      textbox([0,0], [30,200], {"name":"z_se37_structure_obj_text"});      //Textbox to display result structure object detail
   }
}   

   
   
Step 4: Create function to reset SE37 related variables   

Quote
function se37Reset(){
   set("V[z_se37*]", "");
}



Step 5: Create function to read the component table and return structure object detail   

Quote
function se37GetStructureObject(){
   
   onscreen "SAPLSD41.2100"
      set("V[z_se37_structure]", "&F[Structure]");            //Read the structure value
      var relrow = 1;
      var absrow = 1;
      var z_se37_component_ary = [];
      var z_se37_row_ary = [];
     
NEW_SCREEN:;     
      enter("/ScrollToLine=&V[absrow]", {"table":"T[SAPLSD41_TC0]"});
     
   onscreen "SAPLSD41.2100"
      gettableattribute("T[SAPLSD41_TC0]", {"firstvisiblerow":"FVisRow", "lastvisiblerow":"LVisRow", "lastrow":"LRow"});   
      relrow = 1;
NEW_ROW:;   
      if(absrow > LVisRow){
         goto NEW_SCREEN;
      }
   
      if(absrow > LRow){
         goto END_OF_TABLE;
      }
   
      z_se37_row_ary = [];
     
      //Read each row of component table
      set("V[cur_component]", "&cell[SAPLSD41_TC0,Component,&V[relrow]]");
      set("V[cur_typing_method]", "&cell[SAPLSD41_TC0,Typing Method,&V[relrow]]");
      set("V[cur_component_type]", "&cell[SAPLSD41_TC0,Component Type,&V[relrow]]");
      set("V[cur_data_type]", "&cell[SAPLSD41_TC0,Data Type,&V[relrow]]");
      set("V[cur_length]", "&cell[SAPLSD41_TC0,Length,&V[relrow]]");
      set("V[cur_decimal_pl]", "&cell[SAPLSD41_TC0,Decimal Pl,&V[relrow]]");
      set("V[cur_short_desc]", "&cell[SAPLSD41_TC0,Short Description,&V[relrow]]");
   
      z_se37_row_ary.push(cur_component);
      z_se37_row_ary.push(cur_typing_method);
      z_se37_row_ary.push(cur_component_type);
      z_se37_row_ary.push(cur_data_type);
      z_se37_row_ary.push(cur_length);
      z_se37_row_ary.push(cur_decimal_pl);
      z_se37_row_ary.push(cur_short_desc);
   
      z_se37_component_ary.push(z_se37_row_ary);
   
      relrow++;
      absrow++;
      goto NEW_ROW;
END_OF_TABLE:;

      //Change flag to display the result textbox
      z_se37_display_structure_obj = true;

      //Get organized structure object detail
      var z_se37_result_struct_ary = setStructureObject(z_se37_structure,z_se37_component_ary);      //Get organized result array from function
     
      //Logic to copy content from organized result array to longtext variable
      for(var struct in z_se37_result_struct_ary){         
         tmp_str = z_se37_result_struct_ary[struct];
         
         if(struct==0)
            copytext({"totext":"z_se37_structure_obj_text", "fromstring":"tmp_str"});
         else
            copytext({"totext":"z_se37_structure_obj_text", "fromstring":"tmp_str", "appendline":true});

      }
     
      enter("/ScrollToLine=1", {"table":"T[SAPLSD41_TC0]"});
}



Step 6: Create function to organized fetched table data   

Quote
function setStructureObject(structure,component_ary){
   var result_ary = [];
   var component_str = "";

   
   result_ary.push("\tvar z_" + structure + " = {\n");         //Decalre the type object
   result_ary.push("\t\tname:'" + structure + "',\n");         //Add the name variable
   result_ary.push("\t\tcomponents:[\n");                  //Add the components object
   
   for(var i=0; i<component_ary.length; i++){               //For loop to form each component
      component_str = "\t\t\t{ name:'" + component_ary[0] +
                  "', \tlength:" + component_ary[4] +
                  ",\tdecimalpl:" + component_ary[5] +
                  ",\ttype:'" + DATA_TYPE_REF_ARY[component_ary[3]] +
                  "' }" + ((i!=component_ary.length-1)?",\n":"\n") +
                  "\t" + ((component_ary[3]=="CHAR")?"\t\t\t\t":("/** " + component_ary[3] + " **/\t\t\t")) +
                  "//" + component_ary[6];
     
      result_ary.push(component_str);
   }
   
   result_ary.push("\t\t]\n");                           //End component object
   result_ary.push("\t};");                           //End type object
   
   return   result_ary;
}




Step 7: Copy result to your script   

After execution, the result of structure object should displayed in the textbox.
Now select everything from the textbox and copy to the targeting script.


   
See attachments for full script example 

6
Liquid UI: Use call to get native SAP function module detail

This example is to use call command to get the detail from native SAP function module.
ex:   SAP_WAPI_COUNT_WORKITEMS
         BAPI_MATERIAL_GET_ALL
         ALM_ME_FUNCLOC_INSTALLED_EQUI
         BAPI_ALM_ORDER_GET_DETAIL
         BAPI_USER_GET_DETAIL
         BAPISDORDER_GETDETAILEDLIST
   
By using the command with extra structure object, it is able to get different type of data from FM.

Note: valid from WS version 1.2.326.0 or Server version 3.5.563.0 and above



Step 1: Prerequisite

The function module needs to be remote callable, or Liquid UI will not be able to execute it through RFC.



Step 2: Confirm ouput structure according to parameter

The structure of each output parameter needs to be specified individually.
Its detail can be referenced from SAP in transaction SE37 follow by below steps.
1.   On initial screen of SE37, enter the FM
2.   Click on "Display" button to check detail
3.   On the display screen, select "Export" or "Tables" tab according to your output
4.   In corresponding tab, find the row of output parameter and double click on it to see detail structure



Step 3: Define the type object of ouput structure

For each output parameter in the function module, we need to specify individual structure object.
See Convert function module structure detail into type object to generate ready-to-use type object for selected structure.

Use FM BAPI_ALM_ORDER_GET_DETAIL as an example, its ES_HEADER parameter has structure BAPI_ALM_ORDER_HEADER_E as below.

Quote
   var z_BAPI_ALM_ORDER_HEADER_E = {
      name:'BAPI_ALM_ORDER_HEADER_E',
      components:[
         { name:'ORDERID',    length:12,    decimalpl:0,  type:'C' },                               //Order Number
         { name:'ORDER_TYPE',    length:4,    decimalpl:0,  type:'C' },                               //Order Type
         { name:'PLANPLANT',    length:4,    decimalpl:0,  type:'C' },                               //Maintenance Planning Plant
         { name:'BUS_AREA',    length:4,    decimalpl:0,  type:'C' },                               //Business Area
         { name:'MN_WK_CTR',    length:8,    decimalpl:0,  type:'C' },                               //Main work center for maintenance tasks
         { name:'PLANT',    length:4,    decimalpl:0,  type:'C' },                               //Plant associated with main work center
         { name:'MN_WKCTR_ID',    length:8,    decimalpl:0,  type:'N' },    /** NUMC **/      //Object ID of the Work Center
         { name:'PMACTTYPE',    length:3,    decimalpl:0,  type:'C' },                               //Maintenance activity type
         { name:'PLANGROUP',    length:3,    decimalpl:0,  type:'C' },                               //Planner Group for Customer Service and Plant Maintenance
         { name:'SYSTCOND',    length:1,    decimalpl:0,  type:'C' },                               //Syst.Condition
         { name:'FUNCT_LOC',    length:30,    decimalpl:0,  type:'C' },                               //Functional Location
         { name:'EQUIPMENT',    length:18,    decimalpl:0,  type:'C' },                               //Equipment Number
         { name:'SERIALNO',    length:18,    decimalpl:0,  type:'C' },                               //Serial Number
         { name:'MATERIAL',    length:18,    decimalpl:0,  type:'C' },                               //Material Number
         { name:'ASSEMBLY',    length:18,    decimalpl:0,  type:'C' },                               //Assembly
         { name:'DEVICEDATA',    length:40,    decimalpl:0,  type:'C' },                               //Additional Device Data
         { name:'MAINTPLANT',    length:4,    decimalpl:0,  type:'C' },                               //Maintenance plant
         { name:'LOCATION',    length:10,    decimalpl:0,  type:'C' },                               //Asset location
         { name:'MAINTROOM',    length:8,    decimalpl:0,  type:'C' },                               //Room
         { name:'PLSECTN',    length:3,    decimalpl:0,  type:'C' },                               //Plant section
         { name:'LOC_WK_CTR',    length:8,    decimalpl:0,  type:'C' },                               //Work center
         { name:'LOC_WKCTR_ID',    length:8,    decimalpl:0,  type:'N' },    /** NUMC **/      //Object ID of PP work center
         { name:'ABCINDIC',    length:1,    decimalpl:0,  type:'C' },                               //ABC indicator for technical object
         { name:'SORTFIELD',    length:30,    decimalpl:0,  type:'C' },                               //Sort field
         { name:'COMP_CODE',    length:4,    decimalpl:0,  type:'C' },                               //Company Code
         { name:'PROFIT_CTR',    length:10,    decimalpl:0,  type:'C' },                               //Profit Center
         { name:'CO_AREA',    length:4,    decimalpl:0,  type:'C' },                               //Controlling Area
         { name:'RESPCCTR',    length:10,    decimalpl:0,  type:'C' },                               //Responsible cost center
         { name:'FUNC_AREA',    length:16,    decimalpl:0,  type:'C' },                               //Functional Area
         { name:'SUPERIOR_NETWORK',    length:12,    decimalpl:0,  type:'C' },                               //Number of superior network
         { name:'SUPERIOR_ACTIVITY',    length:4,    decimalpl:0,  type:'C' },                               //Activity number in network and standard network
         { name:'SUPERIOR_ROUTING_NO',    length:10,    decimalpl:0,  type:'N' },    /** NUMC **/      //Routing number of operations in the order
         { name:'SUPERIOR_COUNTER',    length:8,    decimalpl:0,  type:'N' },    /** NUMC **/      //General counter for order
         { name:'WBS_ELEM',    length:8,    decimalpl:0,  type:'N' },    /** NUMC **/      //Work Breakdown Structure Element (WBS Element)
         { name:'PROJ_DEF',    length:8,    decimalpl:0,  type:'N' },    /** NUMC **/      //Project definition
         { name:'PROCESSING_GROUP',    length:2,    decimalpl:0,  type:'N' },    /** NUMC **/      //Processing group
         { name:'OBJECTCLASS',    length:2,    decimalpl:0,  type:'C' },                               //Object Class
         { name:'TAXJURCODE',    length:15,    decimalpl:0,  type:'C' },                               //Tax Jurisdiction
         { name:'LOC_COMP_CODE',    length:4,    decimalpl:0,  type:'C' },                               //Company Code
         { name:'LOC_CO_AREA',    length:4,    decimalpl:0,  type:'C' },                               //Controlling Area
         { name:'ASSET_NO',    length:12,    decimalpl:0,  type:'C' },                               //Main Asset Number
         { name:'SUB_NUMBER',    length:4,    decimalpl:0,  type:'C' },                               //Asset Subnumber
         { name:'LOC_BUS_AREA',    length:4,    decimalpl:0,  type:'C' },                               //Business Area
         { name:'COSTCENTER',    length:10,    decimalpl:0,  type:'C' },                               //Cost Center
         { name:'LOC_WBS_ELEM',    length:8,    decimalpl:0,  type:'N' },    /** NUMC **/      //Work Breakdown Structure Element (WBS Element)
         { name:'STANDORDER',    length:12,    decimalpl:0,  type:'C' },                               //Standing order number
         { name:'SETTLORDER',    length:12,    decimalpl:0,  type:'C' },                               //Settlement order
         { name:'SALESORG',    length:4,    decimalpl:0,  type:'C' },                               //Sales Organization
         { name:'DISTR_CHAN',    length:2,    decimalpl:0,  type:'C' },                               //Distribution Channel
         { name:'DIVISION',    length:2,    decimalpl:0,  type:'C' },                               //Division
         { name:'ORDPLANID',    length:1,    decimalpl:0,  type:'C' },                               //Maintenance order planning indicator
         { name:'START_DATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Basic start date
         { name:'FINISH_DATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Basic finish date
         { name:'BASICSTART',    length:6,    decimalpl:0,  type:'T' },    /** TIMS **/      //Basic start time
         { name:'BASIC_FIN',    length:6,    decimalpl:0,  type:'T' },    /** TIMS **/      //Basic finish (time)
         { name:'PRIORITY',    length:1,    decimalpl:0,  type:'C' },                               //Priority
         { name:'REVISION',    length:8,    decimalpl:0,  type:'C' },                               //Revision for Plant Maintenance and Customer Service
         { name:'VERSION',    length:2,    decimalpl:0,  type:'N' },    /** NUMC **/      //Version of Available Capacity
         { name:'SCHED_TYPE',    length:1,    decimalpl:0,  type:'C' },                               //Scheduling type
         { name:'AUTOSCHED',    length:1,    decimalpl:0,  type:'C' },                               //Indicator: Schedule automatically
         { name:'CAP_REQMTS',    length:1,    decimalpl:0,  type:'C' },                               //Indicator: Calculate capacity requirements
         { name:'SCHEDULING_EXACT_BREAK_TIMES',    length:1,    decimalpl:0,  type:'C' },                               //Indicator:  Scheduling allowing for breaks
         { name:'MRP_RELEVANT',    length:1,    decimalpl:0,  type:'C' },                               //Reservation Relevance/Generation of Purchase Requisition
         { name:'PRODUCTION_START_DATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Scheduled start
         { name:'PRODUCTION_FINISH_DATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Scheduled finish
         { name:'PRODUCTION_START_TIME',    length:6,    decimalpl:0,  type:'T' },    /** TIMS **/      //Scheduled Start (Time)
         { name:'PRODUCTION_FINISH_TIME',    length:6,    decimalpl:0,  type:'T' },    /** TIMS **/      //Scheduled finish time
         { name:'ACTUAL_START_DATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Actual start date
         { name:'ACTUAL_FINISH_DATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Confirmed Order Finish Date
         { name:'ACTUAL_START_TIME',    length:6,    decimalpl:0,  type:'T' },    /** TIMS **/      //Actual start time
         { name:'ACTUAL_FINISH_TIME',    length:6,    decimalpl:0,  type:'T' },    /** TIMS **/      //Confirmed order finish (time)
         { name:'REFDATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //PM Order: Reference Date
         { name:'SALES_ORD',    length:10,    decimalpl:0,  type:'C' },                               //Sales Order Number
         { name:'S_ORD_ITEM',    length:6,    decimalpl:0,  type:'N' },    /** NUMC **/      //Item Number in Sales Order
         { name:'CALC_MOTIVE',    length:2,    decimalpl:0,  type:'C' },                               //Accounting Indicator
         { name:'INVEST_PROFILE',    length:6,    decimalpl:0,  type:'C' },                               //Investment measure profile
         { name:'SCALE',    length:2,    decimalpl:0,  type:'C' },                               //Scale of investment objects
         { name:'INV_REASON',    length:2,    decimalpl:0,  type:'C' },                               //Reason for investment
         { name:'ENVIR_INVEST',    length:5,    decimalpl:0,  type:'C' },                               //Reason for environmental investment
         { name:'ESTIMATED_COSTS',    length:11,    decimalpl:2,  type:'P' },    /** CURR **/      //Estimated total costs of order
         { name:'CURRENCY',    length:5,    decimalpl:0,  type:'C' },    /** CUKY **/      //Order Currency
         { name:'CURRENCY_ISO',    length:3,    decimalpl:0,  type:'C' },                               //ISO currency code
         { name:'CSTG_SHEET',    length:6,    decimalpl:0,  type:'C' },                               //Costing Sheet
         { name:'OVERHEAD_KEY',    length:6,    decimalpl:0,  type:'C' },                               //Overhead key
         { name:'RES_ANAL_KEY',    length:6,    decimalpl:0,  type:'C' },                               //Results Analysis Key
         { name:'NETWORK_PROFILE',    length:7,    decimalpl:0,  type:'C' },                               //Network profile
         { name:'CSTGVAPPLN',    length:4,    decimalpl:0,  type:'C' },                               //Costing variant for planned costs
         { name:'CSTGVARACT',    length:4,    decimalpl:0,  type:'C' },                               //Costing variant for actual costs
         { name:'TASK_LIST_GROUP',    length:8,    decimalpl:0,  type:'C' },                               //Key for Task List Group
         { name:'GROUP_COUNTER',    length:2,    decimalpl:0,  type:'C' },                               //Group Counter
         { name:'TASK_LIST_TYPE',    length:1,    decimalpl:0,  type:'C' },                               //Task List Type
         { name:'RESP_PLANNER_GROUP',    length:3,    decimalpl:0,  type:'C' },                               //Responsible planner group/department
         { name:'MNTPLAN',    length:12,    decimalpl:0,  type:'C' },                               //Maintenance Plan
         { name:'MAINTITEM',    length:16,    decimalpl:0,  type:'C' },                               //Maintenance item
         { name:'CALL_NO',    length:10,    decimalpl:0,  type:'I' },    /** INT4 **/      //Maintenance Plan Call Number
         { name:'LAST_ORD',    length:12,    decimalpl:0,  type:'C' },                               //Order number
         { name:'ENTERED_BY',    length:12,    decimalpl:0,  type:'C' },                               //Entered by
         { name:'ENTER_DATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Created on
         { name:'CHANGED_BY',    length:12,    decimalpl:0,  type:'C' },                               //Last changed by
         { name:'CHANGE_DATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Change date for Order Master
         { name:'SCENARIO',    length:4,    decimalpl:0,  type:'C' },                               //Scenario or Subscreen Category
         { name:'SYS_STATUS',    length:40,    decimalpl:0,  type:'C' },                               //System Status
         { name:'USER_ST',    length:1,    decimalpl:0,  type:'C' },                               //User status active
         { name:'USERSTATUS',    length:40,    decimalpl:0,  type:'C' },                               //Field displaying user status
         { name:'STAT_PROF',    length:8,    decimalpl:0,  type:'C' },                               //Status Profile
         { name:'OBJECT_NO',    length:22,    decimalpl:0,  type:'C' },                               //Object number
         { name:'ROUTING_NO',    length:10,    decimalpl:0,  type:'N' },    /** NUMC **/      //Routing number of operations in the order
         { name:'RESERV_NO',    length:10,    decimalpl:0,  type:'N' },    /** NUMC **/      //Number of Reservation/Dependent Requirement
         { name:'SHORT_TEXT',    length:40,    decimalpl:0,  type:'C' },                               //Description
         { name:'LONG_TEXT',    length:1,    decimalpl:0,  type:'C' },                               //Long text exists
         { name:'NOTIF_NO',    length:12,    decimalpl:0,  type:'C' },                               //Notification No
         { name:'ASSEMBLY_EXTERNAL',    length:40,    decimalpl:0,  type:'C' },                               //Long Material Number for Field ASSEMBLY
         { name:'ASSEMBLY_GUID',    length:32,    decimalpl:0,  type:'C' },                               //External GUID for ASSEMBLY Field
         { name:'ASSEMBLY_VERSION',    length:10,    decimalpl:0,  type:'C' },                               //Version Number for ASSEMBLY Field
         { name:'MATERIAL_EXTERNAL',    length:40,    decimalpl:0,  type:'C' },                               //Long Material Number for MATERIAL Field
         { name:'MATERIAL_GUID',    length:32,    decimalpl:0,  type:'C' },                               //External GUID for MATERIAL Field
         { name:'MATERIAL_VERSION',    length:10,    decimalpl:0,  type:'C' },                               //Version Number for MATERIAL Field
         { name:'CALID',    length:2,    decimalpl:0,  type:'C' },                               //Factory Calendar
         { name:'KALSN',    length:2,    decimalpl:0,  type:'N' },    /** NUMC **/      //Calendar Selection for Order
         { name:'SUPERIOR_ORDERID',    length:12,    decimalpl:0,  type:'C' },                               //Number of superior order
         { name:'LEADING_ORDERID',    length:12,    decimalpl:0,  type:'C' },                               //Leading order in current processing
         { name:'START_POINT',    length:18,    decimalpl:0,  type:'C' },                               //Start Point
         { name:'END_POINT',    length:18,    decimalpl:0,  type:'C' },                               //End Point
         { name:'LINEAR_LENGTH',    length:18,    decimalpl:0,  type:'C' },                               //Length
         { name:'LINEAR_UNIT',    length:3,    decimalpl:0,  type:'C' },    /** UNIT **/      //Unit of Measurement for Linear Data
         { name:'LINEAR_UNIT_ISO',    length:3,    decimalpl:0,  type:'C' },                               //Unit of Measurement for Linear Data in ISO Code
         { name:'FIRST_OFFSET_TYPE_CODE',    length:2,    decimalpl:0,  type:'C' },                               //Type of First Offset
         { name:'FIRST_OFFSET_VALUE',    length:18,    decimalpl:0,  type:'C' },                               //Value of Offset 1
         { name:'FIRST_OFFSET_UNIT',    length:3,    decimalpl:0,  type:'C' },    /** UNIT **/      //Unit of Measurement for Offset 1
         { name:'FIRST_OFFSET_UNIT_ISO',    length:3,    decimalpl:0,  type:'C' },                               //Unit of Measurement for Offset 1 in ISO Code
         { name:'SECOND_OFFSET_TYPE_CODE',    length:2,    decimalpl:0,  type:'C' },                               //Type of Second Offset
         { name:'SECOND_OFFSET_VALUE',    length:18,    decimalpl:0,  type:'C' },                               //Value of Offset 2
         { name:'SECOND_OFFSET_UNIT',    length:3,    decimalpl:0,  type:'C' },    /** UNIT **/      //Unit of Measurement for Offset 2
         { name:'SECOND_OFFSET_UNIT_ISO',    length:3,    decimalpl:0,  type:'C' },                               //Unit of Measurement for Offset 2 in ISO Code
         { name:'FIRST_OFFSET_TYPE_CODE_NAME',    length:60,    decimalpl:0,  type:'C' },                               //First Offset Type Description
         { name:'SECOND_OFFSET_TYPE_CODE_NAME',    length:60,    decimalpl:0,  type:'C' },                               //Second Offset Type Description
         { name:'MARKER_START_POINT',    length:18,    decimalpl:0,  type:'C' },                               //Marker for Start Point
         { name:'MARKER_DISTANCE_START_POINT',    length:18,    decimalpl:0,  type:'C' },                               //Distance between Marker and Start Point
         { name:'MARKER_END_POINT',    length:18,    decimalpl:0,  type:'C' },                               //Marker for End Point
         { name:'MARKER_DISTANCE_END_POINT',    length:18,    decimalpl:0,  type:'C' },                               //Length Spec for Distance between Marker and End Point
         { name:'MARKER_DISTANCE_UNIT',    length:3,    decimalpl:0,  type:'C' },    /** UNIT **/      //Unit for the Distance from Marker
         { name:'MARKER_DISTANCE_UNIT_ISO',    length:3,    decimalpl:0,  type:'C' },                               //Unit for the Distance from Marker in ISO Code
         { name:'PRIOTYPE',    length:2,    decimalpl:0,  type:'C' }                               //Priority Type
      ]
   };

It's able to include more than one output parameter in the same call command, which you need to specify the other structure object as well.
Here we have a table parameter ET_OPERATIONS has structure BAPI_ALM_ORDER_OPERATION_E as below.

Quote
   var z_BAPI_ALM_ORDER_OPERATION_E = {
      name:'BAPI_ALM_ORDER_OPERATION_E',
      components:[
         { name:'ACTIVITY',    length:4,    decimalpl:0,  type:'C' },                               //Operation/Activity Number
         { name:'SUB_ACTIVITY',    length:4,    decimalpl:0,  type:'C' },                               //Suboperation
         { name:'CONTROL_KEY',    length:4,    decimalpl:0,  type:'C' },                               //Control key
         { name:'WORK_CNTR',    length:8,    decimalpl:0,  type:'C' },                               //Work center
         { name:'PLANT',    length:4,    decimalpl:0,  type:'C' },                               //Plant
         { name:'STANDARD_TEXT_KEY',    length:7,    decimalpl:0,  type:'C' },                               //Standard text key
         { name:'DESCRIPTION',    length:40,    decimalpl:0,  type:'C' },                               //Operation short text
         { name:'LANGU',    length:1,    decimalpl:0,  type:'C' },    /** LANG **/      //Language Key
         { name:'LANGU_ISO',    length:2,    decimalpl:0,  type:'C' },                               //2-Character SAP Language Code
         { name:'NO_OF_TIME_TICKETS',    length:3,    decimalpl:0,  type:'P' },    /** DEC **/      //Number of Time Tickets
         { name:'WAGETYPE',    length:4,    decimalpl:0,  type:'C' },                               //Wage Type
         { name:'SUITABILITY',    length:2,    decimalpl:0,  type:'C' },                               //Suitability
         { name:'WAGEGROUP',    length:3,    decimalpl:0,  type:'C' },                               //Wage group
         { name:'SORT_FLD',    length:10,    decimalpl:0,  type:'C' },                               //Sort field
         { name:'VENDOR_NO',    length:10,    decimalpl:0,  type:'C' },                               //Account Number of Vendor or Creditor
         { name:'QUANTITY',    length:13,    decimalpl:3,  type:'P' },    /** QUAN **/      //Operation quantity in order unit of measure
         { name:'BASE_UOM',    length:3,    decimalpl:0,  type:'C' },    /** UNIT **/      //Base Unit of Measure
         { name:'BASE_UOM_ISO',    length:3,    decimalpl:0,  type:'C' },                               //ISO code for unit of measurement
         { name:'PRICE',    length:11,    decimalpl:2,  type:'P' },    /** CURR **/      //Price
         { name:'PRICE_UNIT',    length:5,    decimalpl:0,  type:'P' },    /** DEC **/      //Price Unit
         { name:'COST_ELEMENT',    length:10,    decimalpl:0,  type:'C' },                               //Cost Element
         { name:'CURRENCY',    length:5,    decimalpl:0,  type:'C' },    /** CUKY **/      //Currency Key
         { name:'CURRENCY_ISO',    length:3,    decimalpl:0,  type:'C' },                               //ISO currency code
         { name:'INFO_REC',    length:10,    decimalpl:0,  type:'C' },                               //Number of Purchasing Info Record
         { name:'PURCH_ORG',    length:4,    decimalpl:0,  type:'C' },                               //Purchasing Organization
         { name:'PUR_GROUP',    length:3,    decimalpl:0,  type:'C' },                               //Purchasing group for external processing
         { name:'MATL_GROUP',    length:9,    decimalpl:0,  type:'C' },                               //Material Group
         { name:'AGREEMENT',    length:10,    decimalpl:0,  type:'C' },                               //Number of Principal Purchase Agreement
         { name:'AGMT_ITEM',    length:5,    decimalpl:0,  type:'N' },    /** NUMC **/      //Item Number of Principal Purchase Agreement
         { name:'PREQ_NAME',    length:12,    decimalpl:0,  type:'C' },                               //Name of Requisitioner/Requester
         { name:'TRACKINGNO',    length:10,    decimalpl:0,  type:'C' },                               //Requirement Tracking Number
         { name:'NUMBER_OF_CAPACITIES',    length:3,    decimalpl:0,  type:'b' },    /** INT1 **/      //Number of capacities required
         { name:'PERCENT_OF_WORK',    length:3,    decimalpl:0,  type:'b' },    /** INT1 **/      //Work percentage
         { name:'CALC_KEY',    length:1,    decimalpl:0,  type:'C' },                               //Key for calculation
         { name:'ACTTYPE',    length:6,    decimalpl:0,  type:'C' },                               //Activity Type
         { name:'SYSTCOND',    length:1,    decimalpl:0,  type:'C' },                               //Syst.Condition
         { name:'ASSEMBLY',    length:18,    decimalpl:0,  type:'C' },                               //Assembly
         { name:'INT_DISTR',    length:8,    decimalpl:0,  type:'C' },                               //Distr.cap.reqmts (plant maint.,process order, network)
         { name:'GR_RCPT',    length:12,    decimalpl:0,  type:'C' },                               //Goods Recipient/Ship-To Party
         { name:'UNLOAD_PT',    length:25,    decimalpl:0,  type:'C' },                               //Unloading Point
         { name:'PERS_NO',    length:8,    decimalpl:0,  type:'N' },    /** NUMC **/      //Personnel number
         { name:'FW_ORDER',    length:10,    decimalpl:0,  type:'C' },                               //Framework Order
         { name:'ORDER_ITEM',    length:5,    decimalpl:0,  type:'N' },    /** NUMC **/      //Item of framework order
         { name:'PLND_DELRY',    length:3,    decimalpl:0,  type:'P' },    /** DEC **/      //Planned Delivery Time in Days
         { name:'DURATION_NORMAL',    length:5,    decimalpl:1,  type:'P' },    /** QUAN **/      //Normal duration of the activity
         { name:'DURATION_NORMAL_UNIT',    length:3,    decimalpl:0,  type:'C' },    /** UNIT **/      //Normal duration/unit
         { name:'DURATION_NORMAL_UNIT_ISO',    length:3,    decimalpl:0,  type:'C' },                               //ISO code for unit of measurement
         { name:'CONSTRAINT_TYPE_START',    length:1,    decimalpl:0,  type:'C' },                               //Constraint on the basic start date for the activity
         { name:'CONSTRAINT_TYPE_FINISH',    length:1,    decimalpl:0,  type:'C' },                               //Constraint on the finish date of the activity
         { name:'WORK_ACTIVITY',    length:7,    decimalpl:1,  type:'P' },    /** QUAN **/      //Work involved in the activity
         { name:'UN_WORK',    length:3,    decimalpl:0,  type:'C' },    /** UNIT **/      //Unit for work
         { name:'UN_WORK_ISO',    length:3,    decimalpl:0,  type:'C' },                               //ISO code for unit of measurement
         { name:'START_CONS',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Constraint for activity start (Basic)
         { name:'STRTTIMCON',    length:6,    decimalpl:0,  type:'T' },    /** TIMS **/      //Constraint for activity start time (Basic)
         { name:'FIN_CONSTR',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Constraint for finish of activity (Basic)
         { name:'FINTIMCONS',    length:6,    decimalpl:0,  type:'T' },    /** TIMS **/      //Basic finish time of the activity
         { name:'EXECFACTOR',    length:3,    decimalpl:0,  type:'P' },    /** DEC **/      //Execution Factor
         { name:'MRP_RELEVANT',    length:1,    decimalpl:0,  type:'C' },                               //Reservation Relevance/Generation of Purchase Requisition
         { name:'FIELD_KEY',    length:7,    decimalpl:0,  type:'C' },                               //Key word ID for user-defined fields
         { name:'USR00',    length:20,    decimalpl:0,  type:'C' },                               //User field with 20 characters
         { name:'USR01',    length:20,    decimalpl:0,  type:'C' },                               //User field with 20 characters
         { name:'USR02',    length:10,    decimalpl:0,  type:'C' },                               //User field with 10 characters
         { name:'USR03',    length:10,    decimalpl:0,  type:'C' },                               //User field with 10 characters
         { name:'USR04',    length:13,    decimalpl:3,  type:'P' },    /** QUAN **/      //User field for quantity (length 10.3)
         { name:'USE04',    length:3,    decimalpl:0,  type:'C' },    /** UNIT **/      //User field: Unit for quantity fields
         { name:'USE04_ISO',    length:3,    decimalpl:0,  type:'C' },                               //ISO code for unit of measurement
         { name:'USR05',    length:13,    decimalpl:3,  type:'P' },    /** QUAN **/      //User field for quantity (length 10.3)
         { name:'USE05',    length:3,    decimalpl:0,  type:'C' },    /** UNIT **/      //User field: Unit for quantity fields
         { name:'USE05_ISO',    length:3,    decimalpl:0,  type:'C' },                               //ISO code for unit of measurement
         { name:'USR06',    length:13,    decimalpl:3,  type:'P' },    /** CURR **/      //User-defined field for values (length 10,3)
         { name:'USE06',    length:5,    decimalpl:0,  type:'C' },    /** CUKY **/      //User field: Unit for value fields
         { name:'USE06_ISO',    length:3,    decimalpl:0,  type:'C' },                               //ISO currency code
         { name:'USR07',    length:13,    decimalpl:3,  type:'P' },    /** CURR **/      //User-defined field for values (length 10,3)
         { name:'USE07',    length:5,    decimalpl:0,  type:'C' },    /** CUKY **/      //User field: Unit for value fields
         { name:'USE07_ISO',    length:3,    decimalpl:0,  type:'C' },                               //ISO currency code
         { name:'USR08',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //User field for date
         { name:'USR09',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //User field for date
         { name:'USR10',    length:1,    decimalpl:0,  type:'C' },                               //User-defined field: Indicator for reports
         { name:'USR11',    length:1,    decimalpl:0,  type:'C' },                               //User-defined field: Indicator for reports
         { name:'CONF_NO',    length:10,    decimalpl:0,  type:'N' },    /** NUMC **/      //Completion confirmation number for the operation
         { name:'PREQ_NO',    length:10,    decimalpl:0,  type:'C' },                               //Purchase requisition number
         { name:'PREQ_ITEM',    length:5,    decimalpl:0,  type:'N' },    /** NUMC **/      //Item number of the purchase requisition in the order
         { name:'COMPLETE',    length:1,    decimalpl:0,  type:'C' },                               //Indicator: No Remaining Work Expected
         { name:'WORK_ACTUAL',    length:13,    decimalpl:3,  type:'P' },    /** QUAN **/      //Actual work
         { name:'EARL_SCHED_START_DATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Earliest scheduled start: Execution (date)
         { name:'EARL_SCHED_START_TIME',    length:6,    decimalpl:0,  type:'T' },    /** TIMS **/      //Earliest scheduled start: Execution (time)
         { name:'EARL_SCHED_FIN_DATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Earliest scheduled finish: Execution (date)
         { name:'EARL_SCHED_FIN_TIME',    length:6,    decimalpl:0,  type:'T' },    /** TIMS **/      //Earliest scheduled finish: Execution (time)
         { name:'LATE_SCHED_START_DATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Latest scheduled start: Execution (date)
         { name:'LATE_SCHED_START_TIME',    length:6,    decimalpl:0,  type:'T' },    /** TIMS **/      //Latest scheduled start: Execution (time)
         { name:'LATE_SCHED_FIN_DATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Latest scheduled finish: Execution (date)
         { name:'LATE_SCHED_FIN_TIME',    length:6,    decimalpl:0,  type:'T' },    /** TIMS **/      //Latest scheduled finish: Execution (time)
         { name:'ACT_START_DATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Actual start: Execution (date)
         { name:'ACT_START_TIME',    length:6,    decimalpl:0,  type:'T' },    /** TIMS **/      //Actual start: Execution/setup (time)
         { name:'ACT_END_DATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Actual finish: Execution (date)
         { name:'ACT_END_TIME',    length:6,    decimalpl:0,  type:'T' },    /** TIMS **/      //Actual finish: Execution (time)
         { name:'FCST_FIN_DATE',    length:8,    decimalpl:0,  type:'D' },    /** DATS **/      //Forecast finish date of operation from confirmation
         { name:'FCST_FIN_TIME',    length:6,    decimalpl:0,  type:'T' },    /** TIMS **/      //Forecast finish time of operation from confirmation
         { name:'FREE_BUFFER',    length:3,    decimalpl:0,  type:'P' },    /** DEC **/      //Free float
         { name:'TOTAL_BUFFER',    length:3,    decimalpl:0,  type:'P' },    /** DEC **/      //Total float
         { name:'WORK_FORECAST',    length:7,    decimalpl:1,  type:'P' },    /** QUAN **/      //Forecasted work (actual + remaining)
         { name:'SYSTEM_STATUS_TEXT',    length:40,    decimalpl:0,  type:'C' },                               //System Status
         { name:'ASSEMBLY_EXTERNAL',    length:40,    decimalpl:0,  type:'C' },                               //Long Material Number for Field ASSEMBLY
         { name:'ASSEMBLY_GUID',    length:32,    decimalpl:0,  type:'C' },                               //External GUID for ASSEMBLY Field
         { name:'ASSEMBLY_VERSION',    length:10,    decimalpl:0,  type:'C' },                               //Version Number for ASSEMBLY Field
         { name:'EQUIPMENT',    length:18,    decimalpl:0,  type:'C' },                               //Equipment Number
         { name:'FUNCT_LOC',    length:30,    decimalpl:0,  type:'C' },                               //Functional Location
         { name:'NOTIF_NO',    length:12,    decimalpl:0,  type:'C' },                               //Notification No
         { name:'START_POINT',    length:18,    decimalpl:0,  type:'C' },                               //Start Point
         { name:'END_POINT',    length:18,    decimalpl:0,  type:'C' },                               //End Point
         { name:'LINEAR_LENGTH',    length:18,    decimalpl:0,  type:'C' },                               //Length
         { name:'LINEAR_UNIT',    length:3,    decimalpl:0,  type:'C' },    /** UNIT **/      //Unit of Measurement for Linear Data
         { name:'LINEAR_UNIT_ISO',    length:3,    decimalpl:0,  type:'C' },                               //Unit of Measurement for Linear Data in ISO Code
         { name:'FIRST_OFFSET_TYPE_CODE',    length:2,    decimalpl:0,  type:'C' },                               //Type of First Offset
         { name:'FIRST_OFFSET_VALUE',    length:18,    decimalpl:0,  type:'C' },                               //Value of Offset 1
         { name:'FIRST_OFFSET_UNIT',    length:3,    decimalpl:0,  type:'C' },    /** UNIT **/      //Unit of Measurement for Offset 1
         { name:'FIRST_OFFSET_UNIT_ISO',    length:3,    decimalpl:0,  type:'C' },                               //Unit of Measurement for Offset 1 in ISO Code
         { name:'SECOND_OFFSET_TYPE_CODE',    length:2,    decimalpl:0,  type:'C' },                               //Type of Second Offset
         { name:'SECOND_OFFSET_VALUE',    length:18,    decimalpl:0,  type:'C' },                               //Value of Offset 2
         { name:'SECOND_OFFSET_UNIT',    length:3,    decimalpl:0,  type:'C' },    /** UNIT **/      //Unit of Measurement for Offset 2
         { name:'SECOND_OFFSET_UNIT_ISO',    length:3,    decimalpl:0,  type:'C' },                               //Unit of Measurement for Offset 2 in ISO Code
         { name:'FIRST_OFFSET_TYPE_CODE_NAME',    length:60,    decimalpl:0,  type:'C' },                               //First Offset Type Description
         { name:'SECOND_OFFSET_TYPE_CODE_NAME',    length:60,    decimalpl:0,  type:'C' },                               //Second Offset Type Description
         { name:'MARKER_START_POINT',    length:18,    decimalpl:0,  type:'C' },                               //Marker for Start Point
         { name:'MARKER_DISTANCE_START_POINT',    length:18,    decimalpl:0,  type:'C' },                               //Distance between Marker and Start Point
         { name:'MARKER_END_POINT',    length:18,    decimalpl:0,  type:'C' },                               //Marker for End Point
         { name:'MARKER_DISTANCE_END_POINT',    length:18,    decimalpl:0,  type:'C' },                               //Length Spec for Distance between Marker and End Point
         { name:'MARKER_DISTANCE_UNIT',    length:3,    decimalpl:0,  type:'C' },    /** UNIT **/      //Unit for the Distance from Marker
         { name:'MARKER_DISTANCE_UNIT_ISO',    length:3,    decimalpl:0,  type:'C' },                               //Unit for the Distance from Marker in ISO Code
         { name:'PCKG_NO',    length:10,    decimalpl:0,  type:'N' },    /** NUMC **/      //Package number
         { name:'COMP_CODE',    length:4,    decimalpl:0,  type:'C' },                               //Company Code
         { name:'MAINTENANCE_ACTIVITY_TYPE',    length:3,    decimalpl:0,  type:'C' },                               //Maintenance activity type
         { name:'BUS_AREA',    length:4,    decimalpl:0,  type:'C' },                               //Business Area
         { name:'WBS_ELEM',    length:8,    decimalpl:0,  type:'N' },    /** NUMC **/      //Work breakdown structure element (WBS element)
         { name:'PROFIT_CTR',    length:10,    decimalpl:0,  type:'C' },                               //Profit Center
         { name:'CSTG_SHEET',    length:6,    decimalpl:0,  type:'C' },                               //Costing Sheet
         { name:'OVERHEAD_KEY',    length:6,    decimalpl:0,  type:'C' },                               //Overhead key
         { name:'TAXJURCODE',    length:15,    decimalpl:0,  type:'C' },                               //Tax Jurisdiction
         { name:'OBJECTCLASS',    length:2,    decimalpl:0,  type:'C' },                               //Object Class
         { name:'FUNC_AREA',    length:16,    decimalpl:0,  type:'C' },                               //Functional Area
         { name:'OFFSET_START',    length:5,    decimalpl:0,  type:'P' },    /** QUAN **/      //Offset to sub-operation start
         { name:'OFFSET_START_UNIT',    length:3,    decimalpl:0,  type:'C' },    /** UNIT **/      //Unit for offset to start
         { name:'OFFSET_START_UNIT_ISO',    length:3,    decimalpl:0,  type:'C' },                               //ISO code for unit of measurement
         { name:'OFFSET_END',    length:5,    decimalpl:0,  type:'P' },    /** QUAN **/      //Offset to sub-operation finish
         { name:'OFFSET_END_UNIT',    length:3,    decimalpl:0,  type:'C' },    /** UNIT **/      //Unit for offset to finish
         { name:'OFFSET_END_UNIT_ISO',    length:3,    decimalpl:0,  type:'C' }                               //ISO code for unit of measurement
      ]
   };   



Step 4: Specify the call command

Compare to regular call command, specify structure type object with output parameter.

Quote
   rfcResult = call("BAPI_ALM_ORDER_GET_DETAIL", {"in.NUMBER":"000000815289",
                                    "out.ES_HEADER(type:z_BAPI_ALM_ORDER_HEADER_E)":"z_bapi_order_header",
                                    "table.ET_OPERATIONS(width:3000,type:z_BAPI_ALM_ORDER_OPERATION_E)":"z_bapi_order_operations"});
   println("=====>> BAPI_ALM_ORDER_GET_DETAIL Exception:"+rfcResult.exception+"<==");
Note:   "rfcResult.exception" will carry any exception for RFC if happen

Note:   Notice the two output paramters have different type objects specified.

Note:   More detail of how to use call command please see
call()



Step 5: Read result data

If output is export parameter with specified type(out.ES_HEADER(type:z_BAPI_ALM_ORDER_HEADER_E)), the result variable is an object.
If output is table parameter with specified type(table.ET_OPERATIONS(width:3000,type:z_BAPI_ALM_ORDER_OPERATION_E)), the result variable is an object array.

Use below logic to print out values from object or object array in Cornelius window.

Quote
   for (var idx in z_bapi_order_header){
      println("=====>>" + idx + "\t==>"+ z_bapi_order_header[idx]+"<==");
   }

   println("\n\n\n========================================================\n\n\n");
   
   for (var j in z_bapi_order_operations){
      for (var k in z_bapi_order_operations[j]){
         println("=====>>" + j + "-" + k + "\t==>" + z_bapi_order_operations[j][k]+"<==");
      }
   }

To get specific value from object:
   z_bapi_order_header.ORDERID      //Order Number
   z_bapi_order_header.ORDER_TYPE      //Order Type
   
To get specific value from object array:
   z_bapi_order_operations[0].ACTIVITY      //Operation/Activity Number
   z_bapi_order_operations[0].CONTROL_KEY      //Control key



Step 6: Use function to wrap the logic

Since the type object construct logic will only be required when we try to execute the call command, it is more efficient to put entire logic into a function.
Whenever we need this logic in the script of function, we can directly execute the function to get returned result.
Quote
function callBAPI_ALM_ORDER_GET_DETAIL(){
   var z_BAPI_ALM_ORDER_HEADER_E = {
      ......
   };

   var z_BAPI_ALM_ORDER_OPERATION_E = {
      ......
   };
   
   rfcResult = call("BAPI_ALM_ORDER_GET_DETAIL", {"in.NUMBER":"000000815289",
                                    "out.ES_HEADER(type:z_BAPI_ALM_ORDER_HEADER_E)":"z_bapi_order_header",
                                    "table.ET_OPERATIONS(width:3000,type:z_BAPI_ALM_ORDER_OPERATION_E)":"z_bapi_order_operations"});
   println("=====>> BAPI_ALM_ORDER_GET_DETAIL Exception:"+rfcResult.exception+"<==");

   for (var idx in z_bapi_order_header){
      println("=====>>" + idx + "\t==>"+ z_bapi_order_header[idx]+"<==");
   }

   println("\n\n\n========================================================\n\n\n");
   
   for (var j in z_bapi_order_operations){
      for (var k in z_bapi_order_operations[j]){
         println("=====>>" + j + "-" + k + "\t==>" + z_bapi_order_operations[j][k]+"<==");
      }
   }
}


Step 7: Create a button to trigger the function

pushbutton([8,0], "BAPI_ALM_ORDER_GET_DETAIL", "?", {"process":callBAPI_ALM_ORDER_GET_DETAIL, "size":[1,30]});




See attachments for full script example

7
Liquid UI: Use gettableattribute for Liquid UI table

This example is to use gettableattribute command for Liquid UI table.
By using the command, it returns the first visible row, last visible row, and last row numbers of the table.


Step 1: Create user interface
clearscreen();

//Button to trigger action
pushbutton([1,15], "Get Table Attribute", "?", {"process":getLUITableAttribute, "size":[1,20]});

//Logic to render Liquid UI table
table([3,2], [15,50], {"name":"z_table", "title":"BOM Detail", "rows":100});
column("Item", {"table":"z_table", "name":"item_no", "position":1, "size":4, "numerical":true});
column("Document", {"table":"z_table", "name":"doc_no", "position":2, "size":25, "uppercase":true});
column("Type", {"table":"z_table", "name":"doc_type", "position":3, "size":3, "uppercase":true});
column("Part", {"table":"z_table", "name":"doc_part", "position":4, "size":3, "uppercase":true});
column("Version", {"table":"z_table", "name":"doc_version", "position":5, "size":2, "uppercase":true});



Step 2: Create function to get table attribute
//Function to get table attribute
function getLUITableAttribute(){
   gettableattribute("z_table", {"firstvisiblerow":"FVisRow", "lastvisiblerow":"LVisRow", "lastrow":"LRow"});
   println("=====>> FVisRow:"+FVisRow);
   println("=====>> LVisRow:"+LVisRow);
   println("=====>> LRow:"+LRow);
}


After execution, FVisRow, LVisRow, and LRow will carry the value and able to be checked in Cornelius window by using println commands.


Note: To get table attribute for Liquid UI table, the getableattribute command needs to be used before the first onscreen section.
            Or use it directly in a pure JavaScript function or in the screen script when the table is actually rendered.


            

See attachments for script example

8
WS aka Web Scripts (Attended RPA for SAP) / Scroll Liquid UI Table
« on: June 20, 2017, 01:44:38 PM »
Liquid UI: Scroll Liquid UI Table

This example is to create the logic which scrolls Liquid UI table according to user input.
By changing the scroll position value of table object, to achieve table scrolling behavior.


Step 1: Create user interface
clearscreen();

//Logic to accept user input of scrolling and trigger the scrolling action
inputfield([1,2], "Scroll to Line:", [1,20], {"name":"z_scroll_to", "size":2, "numerical":true});
pushbutton([1,24], "Scroll", "?", {"process":scrollLUITable, "size":[1,10]});

//Logic to render Liquid UI table
table([3,2], [15,50], {"name":"z_table", "title":"BOM Detail", "rows":100});
column("Item", {"table":"z_table", "name":"item_no", "position":1, "size":4, "numerical":true});
column("Document", {"table":"z_table", "name":"doc_no", "position":2, "size":25, "uppercase":true});
column("Type", {"table":"z_table", "name":"doc_type", "position":3, "size":3, "uppercase":true});
column("Part", {"table":"z_table", "name":"doc_part", "position":4, "size":3, "uppercase":true});
column("Version", {"table":"z_table", "name":"doc_version", "position":5, "size":2, "uppercase":true});



Step 2: Create function to scroll
//Function to scroll Liquid UI table according to input
function scrollLUITable(){
   z_table.vscrollpos = parseInt(z_scroll_to);
}



See attachments for script example

9
Install Liquid UI Server service with different service name

Purpose:
1.   To install Liquid UI Server as a service with a custom service name.
Note:   Feature is supported after Liquid UI Server version 3.5.535.0

Scenarios:
1.   Run 32bit and 64bit servers on the same machine to handle GuiXT and Liquid UI connections separately (GuiXT instance only works via 32bit server)
2.   Run two 64bit server services on the same machine to handle different loads from Liquid UI server to different SAP system

Syntax:
   Installation:
      sapproxy.exe -ins <servicename>

   Removal:
      sapproxy.exe -rem <servicename>

Note: Detail service installation/removal steps, please see attached documentation


10
Liquid UI: Display count of new work items

This example is to create the logic to do RFC and get the list of work items for current user, in order to provide the count of returned data.
Allow user to notice if there is any new work item without checking inbox.


Step 1: Create logic to do RFC
//Specify read table rules
tmp_options = [("USER_ID = '" + _user + "'")];
tmp_fields = ["WI_ID"];
tmp_data = [];

//RFC
rfcResult = call("RFC_READ_TABLE", {"in.QUERY_TABLE":"SWWUSERWI",
                     "table.OPTIONS":"tmp_options",
                     "table.FIELDS":"tmp_fields",
                     "table.DATA(width:3000)":"tmp_data"});

//Verify potential RFC exception and returned data count                           
println("=====>>RFC_READ_TABLE Exception="+rfcResult.exception+"<==");
println("=====>>WI Count="+tmp_data.length+"<==");   



Step 2: Create logic to reflect result on the screen
//Logic to change screen title to represent the work item count
if(tmp_data.length > 0){      //If returned data count is larger than 0
   title(_title + ": New Work Item(s): " + tmp_data.length);
}



Note: By reading data from table SWWUSERWI, we're able to locate the ID of each work item.
            Combine other FM to display general description on the screen can be another useful scenario.




See attachments for code samples

11
Liquid UI: Validate Japanese Half-Width(Hankaku) Katakana Character

This example is to create the logic validate if there's any Half-width (Hankaku) Katakana character in the user input.
From Unicode, the Half-width Katakana characters are within certain range.
The logic is to convert all characters into Unicode then determine if any result is within the range of Half-width Katakana.



Step 1: Create user interface
//User interface

//Delete all existing pushbuttons on toolbar
del("P[User menu]");
del("P[SAP menu]");
del("P[SAP Business Workplace]");
del("P[Other menu]");
del("P[Add to Favorites]");
del("P[Delete Favorites]");
del("P[Change Favorites]");
del("P[Move Favorites down]");
del("P[Move Favorites up]");
del("P[Create role]");
del("P[Assign users]");
del("P[Documentation]");

clearscreen();

inputfield([2,2], "Input Data", [2,20], {"name":"z_input", "size":40});

pushbutton([TOOLBAR], "Validate Input", "?", {"process":validateHalfWidthKatakana});



Step 2: Create function to validate the converted unicode result
//Function to validate the converted unicode result
function validateHalfWidthKatakana(){
      
      onscreen "*"
            var splited_ary = getSplitedTextAry(z_input);            //Logic to split the input data into an array by actual characters
            
            var regular_char_counter = 0;
            var half_width_char_counter = 0;
            var full_width_char_counter = 0;
            var other_char_counter = 0;
            var cur_unicode = "";
            
            //Logic to calculate how many Half-Width Katakana/regular character/number/syntax in the input data
            for(var k=0; k<splited_ary.length; k++){
                  
                  if(splited_ary[k].length > 1){
                        cur_unicode = getUnicode(splited_ary[k]);      //Logic to get Unicode for each character
                        
                        if(      (cur_unicode >= "3000" && cur_unicode <= "30ff") ||             //Punctuation, Hiragana, Katakana
                                 (cur_unicode >= "FF00" && cur_unicode <= "FF9F") ||             //Full-width Roman, Half-width Katakana
                                 (cur_unicode >= "4E00" && cur_unicode <= "9FAF") ||             //CJK (Common & Uncommon)
                                 (cur_unicode >= "3400" && cur_unicode <= "4DBF")){                  //CJK Ext. A (Rare)
                              if(cur_unicode >= "FF61" && cur_unicode <= "FF9F"){            //It's a Half width katakana (Hankaku)
                                    half_width_char_counter++;
                              } else{
                                    //It's either Punctuation, Hiragana, Katakana, Full-width Roman, CJK, or CJK Ext. A
                                    full_width_char_counter++;
                              }
                        } else {
                              other_char_counter++;      //Unhandled characters
                        }
                  } else {
                        regular_char_counter++;            //It's regular character/number/syntax
                  }
            }
            
            println("=====>> Half-Width count: "+half_width_char_counter);
            println("=====>> Full-Width count: "+full_width_char_counter);
            
            message("S:Input Data contain " + half_width_char_counter + " Half-Width Katakana");
            
            enter("?");
}



Step 3: Create function to splite the string based on actual characters
//Function to return an array with splitted text
function getSplitedTextAry(str){
      var result_ary = [];
      var ref_str = str;
      
      var converted_str = encodeURI(str);            //Converted the string becomes encoded result
      var converted_str_ary = [];
      var ref_str_ary = [];
      
      //Loop until the converted string becomes nothing
      while(converted_str.length > 0){
            //If Unicode character is found in the string
            if(converted_str.indexOf("%") > -1){
                  //If Unicode character is not from the first character
                  if(converted_str.indexOf("%") != 0){            
                        converted_str_ary.push(converted_str.slice(0,converted_str.indexOf("%")));
                        ref_str_ary.push(ref_str.slice(0,converted_str.indexOf("%")));
                        
                        ref_str = ref_str.slice(converted_str.indexOf("%"));
                        converted_str = converted_str.slice(converted_str.indexOf("%"));
                  }
                  //When Unicode character is from the first character
                  else {
                        converted_str_ary.push(converted_str.slice(0,18));            //Every actual character are 18 char long after encoded
                        ref_str_ary.push(ref_str.slice(0,3));                              //Every actual character are 3 char long before encoded
                        
                        converted_str = converted_str.slice(18);            //Subtract the string
                        ref_str = ref_str.slice(3);                                    //Subtract the string
                  }
            }
            //When Unicode character is not found in the string
            else {
                  converted_str_ary.push(converted_str);            //Push the rest of the string
                  ref_str_ary.push(ref_str);                              //Push the rest of the string
                  converted_str = "";                                          //Clear string
            }
      }
      
      //Reform the return string
      for(var k=0; k<converted_str_ary.length; k++){
            if(converted_str_ary[k].indexOf("%") < 0){                  //If it's a regular character/string
                  for(var j=0; j<ref_str_ary[k].length; j++){            //Push individual characters into the array
                        result_ary.push(ref_str_ary[k].charAt(j));
                  }
            } else {                                                                  //If it's a multi-byte character
                  result_ary.push(ref_str_ary[k]);                        //Push entire multi-byte character into the array
            }
      }
      
      return result_ary;
}

Note: This logic is required to recognize individual actuual character in the string.
            Here we only consider the 3-byte characters for UTF-8 encoding.


            

Step 4: Create function to converted multi-byte character to its Unicode value
//Function to return Unicode in string
function getUnicode(str){
      var result_ary = [];
      
      //Logic to get the actual byte value for each unencoded character
      for(var k=0; k<str.length; k++){      
            println("===>>"+str.charCodeAt(k).toString(2).substring(24,32)+"<==");            
            result_ary.push(str.charCodeAt(k).toString(2).substring(24,32));
      }
      
      //Logic to form multi-byte data become 16-bit hex value
      switch(result_ary.length){
            case 1:            // U+00000000 - U+0000007F  0xxxxxxx
                  var ucode = "";
                  break;
            
            case 2:            // U+00000080 - U+000007FF  110xxxxx 10xxxxxx
                  var ucode = "";
                  break;
            
            case 3:            //U+00000800 - U+0000FFFF   1110xxxx 10xxxxxx 10xxxxxx
                  var c1 = parseInt(result_ary[0],2); 
                  var c2 = parseInt(result_ary[1],2); 
                  var c3 = parseInt(result_ary[2],2);
                  
                  var b1 = (c1 << 4) | ((c2 >> 2) & 0x0F); 
                  var b2 = ((c2 & 0x03) << 6) | (c3 & 0x3F);
                  var ucode = ((b1 & 0x00FF) << 8) | b2;
                  break;
            
            case 4:            // U+00010000 - U+001FFFFF  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
                  var ucode = "";
                  break;
            
            case 5:            // U+00200000 - U+03FFFFFF  111110xx 10xxxxxx 10xxxxxx 10xxxxx
                  var ucode = "";
                  break;
            
            case 6:            // U+04000000 - U+7FFFFFFF  1111110x 10xxxxxx 10xxxxxx 10xxxxx
                  var ucode = "";
                  break;
      }
      
      println("===>>Unicode="+ucode.toString(16).toUpperCase()+"<==");
      
      return ucode.toString(16).toUpperCase();            //Return Unicode value in hex
}

Note: Unicode conversion should include 6 different cases.
            Here we only consider the 3-byte characters for UTF-8 encoding.




See attachments for code samples

12
LiquidUI: Dynamic create table, and read/write value into table cell

This example is to create the logic of displaying multiple LiquidUI table in the screen, and read or write value from the table.
User is able to enter any value into the inputfield at top, then clicks on "Set Table Default" button on toolbar to write the value to all displayed table.

To read from table, user can click "Read Table Content" button on toolbar, then the content from editable table will be copied to corresponding readonly table.
User is able to click "Add" or "Delete" button at the bottom of last table to change the quantity of displayed table on the screen.


Step 1: Create user interface
//User interface

//Delete all existing pushbuttons on toolbar
del("P[User menu]");
del("P[SAP menu]");
del("P[SAP Business Workplace]");
del("P[Other menu]");
del("P[Add to Favorites]");
del("P[Delete Favorites]");
del("P[Change Favorites]");
del("P[Move Favorites down]");
del("P[Move Favorites up]");
del("P[Create role]");
del("P[Assign users]");
del("P[Documentation]");

clearscreen();

title("Dynamic Table Data Handling");

pushbutton([TOOLBAR], "Set Table Default", "?", {"process":setTableDefault});
pushbutton([TOOLBAR], "Read Table Content", "?", {"process":readTableContent});
pushbutton([TOOLBAR], "Clear All Table", "?", {"process":clearTableContent});


//Default the counter value to 1 if it's undefined
if(!z_table_counter){
   z_table_counter = 1;
}

//Create a field on the screen to accept user default value to table
inputfield([0,10], "Default Doc Type", [0,30], {"name":"z_default_type", "size":3});

//Logic to dynamic create editable/readonly tables
for(var i=1; i<=z_table_counter; i++){
   start_row = 2 + (i-1) * 7;         //Specify the dynamic value of row
   
   table([start_row,10], [start_row+5,40], {"name":"z_table_"+i, "title":"Record "+i, "rows":50});
   column("Document", {"table":"z_table_"+i, "name":"doc_no", "position":1, "size":12});
   column("Prt", {"table":"z_table_"+i, "name":"doc_part", "position":2, "size":3});
   column("Typ", {"table":"z_table_"+i, "name":"doc_type", "position":3, "size":3});
   column("Vr", {"table":"z_table_"+i, "name":"doc_version", "position":4, "size":2});
   
   table([start_row,60], [start_row+5,90], {"name":"z_table_readonly_"+i, "title":"Readonly Record "+i, "rows":50});
   column("Document", {"table":"z_table_readonly_"+i, "name":"doc_no", "position":1, "size":12, "readonly":true});
   column("Prt", {"table":"z_table_readonly_"+i, "name":"doc_part", "position":2, "size":3, "readonly":true});
   column("Typ", {"table":"z_table_readonly_"+i, "name":"doc_type", "position":3, "size":3, "readonly":true});
   column("Vr", {"table":"z_table_readonly_"+i, "name":"doc_version", "position":4, "size":2, "readonly":true});
}


btn_start_row = 1 + z_table_counter * 7;      //Specify the dynamic value of pushbutton's row   
pushbutton([btn_start_row,10], "@04\\QAdd Table@", "?", {"process":changeTableAmount, "size":[1,2], "using":{"l_cmd":"ADD"}});

//Logic to display the pushbutton which decreases counter by 1, if there's more than one table showing on the screen
if(z_table_counter>1)
   pushbutton([btn_start_row,14], "@05\\QDelete Table@", "?", {"process":changeTableAmount, "size":[1,2], "using":{"l_cmd":"SUB"}});


Step 2: Create function to change the counter of displaying table
//Function to change the amount of display table
function changeTableAmount(param){
   if(param.l_cmd == "ADD"){      //If passed value is "Add", increment the counter
      z_table_counter++;
   } else {                  //If passed value is "SUB", decrease the counter
      z_table_counter--;
   }
}


Step 3: Create function to set entered value to all editable tables
//Function to set default document type to all editable tables
function setTableDefault(){
   onscreen "*"
      var tmp_dyn_doc_type_str = "";
      //Make the value of variable z_default_type becomes a string type after conversion
      var tmp_default_value = "'" + z_default_type + "'";      
      
      for(var j=1; j<=z_table_counter; j++){
         for(var k=0; k<50; k++){
            tmp_dyn_doc_type_str = "z_table_" + j + ".doc_type[" + k + "]";   
            eval(tmp_dyn_doc_type_str + "=" + tmp_default_value + ";");
         }      
      }
      enter("?");
}

Note: "eval" is a JavaScript function to evaluate the string value then process.
      Content within "eval" command becomes "z_table_1.doctype[0] = 'ZEO'" for example.



Step 4: Create function to read value from editable table then write to readonly table
//Function to copy values from editable tables to readonly tables
function readTableContent(){
   onscreen "*"
      var tmp_dyn_table_copy_from_str = "";
      var tmp_dyn_table_copy_to_str = "";
      
      for(var j=1; j<=z_table_counter; j++){
         for(var k=0; k<50; k++){
            tmp_dyn_table_copy_from_str = "z_table_" + j + ".doc_no[" + k + "]";   
            tmp_dyn_table_copy_to_str = "z_table_readonly_" + j + ".doc_no[" + k + "]";   
            //If the content of cell is "undefined", set the cell value to blank
            if(!eval(tmp_dyn_table_copy_from_str)){                  
               eval(tmp_dyn_table_copy_from_str + "= '';");
            }
            eval(tmp_dyn_table_copy_to_str + "=" + tmp_dyn_table_copy_from_str + ";");
            
            tmp_dyn_table_copy_from_str = "z_table_" + j + ".doc_part[" + k + "]";   
            tmp_dyn_table_copy_to_str = "z_table_readonly_" + j + ".doc_part[" + k + "]";
            if(!eval(tmp_dyn_table_copy_from_str)){
               eval(tmp_dyn_table_copy_from_str + "= '';");
            }            
            eval(tmp_dyn_table_copy_to_str + "=" + tmp_dyn_table_copy_from_str + ";");
            
            tmp_dyn_table_copy_from_str = "z_table_" + j + ".doc_type[" + k + "]";   
            tmp_dyn_table_copy_to_str = "z_table_readonly_" + j + ".doc_type[" + k + "]";
            if(!eval(tmp_dyn_table_copy_from_str)){
               eval(tmp_dyn_table_copy_from_str + "= '';");
            }            
            eval(tmp_dyn_table_copy_to_str + "=" + tmp_dyn_table_copy_from_str + ";");
            
            tmp_dyn_table_copy_from_str = "z_table_" + j + ".doc_version[" + k + "]";   
            tmp_dyn_table_copy_to_str = "z_table_readonly_" + j + ".doc_version[" + k + "]";
            if(!eval(tmp_dyn_table_copy_from_str)){
               eval(tmp_dyn_table_copy_from_str + "= '';");
            }            
            eval(tmp_dyn_table_copy_to_str + "=" + tmp_dyn_table_copy_from_str + ";");
         }      
      }
      enter("?");
}


Step 5: Create function to clear the content of all display tables
//Function to clear all table content
function clearTableContent(){
   var tmp_dyn_doc_str = "";
   
   for(var j=1; j<=z_table_counter; j++){
      tmp_dyn_doc_str = "z_table_" + j + ".doc_no";   
      eval(tmp_dyn_doc_str + "= [];");

      tmp_dyn_doc_str = "z_table_" + j + ".doc_part";   
      eval(tmp_dyn_doc_str + "= [];");
      
      tmp_dyn_doc_str = "z_table_" + j + ".doc_type";   
      eval(tmp_dyn_doc_str + "= [];");
      
      tmp_dyn_doc_str = "z_table_" + j + ".doc_version";   
      eval(tmp_dyn_doc_str + "= [];");
      
      tmp_dyn_doc_str = "z_table_readonly_" + j + ".doc_no";   
      eval(tmp_dyn_doc_str + "= [];");

      tmp_dyn_doc_str = "z_table_readonly_" + j + ".doc_part";   
      eval(tmp_dyn_doc_str + "= [];");
      
      tmp_dyn_doc_str = "z_table_readonly_" + j + ".doc_type";   
      eval(tmp_dyn_doc_str + "= [];");
      
      tmp_dyn_doc_str = "z_table_readonly_" + j + ".doc_version";   
      eval(tmp_dyn_doc_str + "= [];");
   }
}


See attachments for code samples

13
WS aka Web Scripts (Attended RPA for SAP) / Sort Content in Order
« on: February 18, 2016, 04:06:13 PM »
LiquidUI: Sort Content in Order

This example is to provide sorted view of array content.
User is able to click on each column-header-like button to sort the data set with different order based on each column.
In the example, the sorting order follows non-> ascending-> descending-> non for each button.


Step 1: Create user interface
//User interface

//If default flag is not set to true, execute setDefault function
if(!z_default_flag){
   setDefault();
}

clearscreen();

pushbutton([1,5], "&V[btn_sort_icon_0]Make", "?", {"process":sortContent, "size":[1,15], "using":{"col":"0"}});
pushbutton([1,21], "&V[btn_sort_icon_1]Model", "?", {"process":sortContent, "size":[1,20], "using":{"col":"1"}});
pushbutton([1,42], "&V[btn_sort_icon_2]Year", "?", {"process":sortContent, "size":[1,10], "using":{"col":"2"}});
pushbutton([1,53], "&V[btn_sort_icon_3]Color", "?", {"process":sortContent, "size":[1,10], "using":{"col":"3"}});
pushbutton([1,64], "&V[btn_sort_icon_4]Door", "?", {"process":sortContent, "size":[1,10], "using":{"col":"4"}});
pushbutton([1,75], "&V[btn_sort_icon_5]HatchBack", "?", {"process":sortContent, "size":[1,15], "using":{"col":"5"}});

box([2,5], [12,19]);
box([2,21], [12,40]);
box([2,42], [12,51]);
box([2,53], [12,62]);
box([2,64], [12,73]);
box([2,75], [12,89]);

for(var i=start_row; i<end_row; i++){
   text([2+i,7], data_ary[0]);
   text([2+i,23], data_ary[1]);
   text([2+i,45], data_ary[2]);
   text([2+i,55], data_ary[3]);
   text([2+i,69], data_ary[4]);
   
   set("V[check_&V]", data_ary[5]);
   checkbox([2+i,82], "", {"name":"check_&V", "readonly":true});
}
Note: Variables "btn_sort_icon_0" to "btn_sort_icon_5" are defaulted to be empty, and they are used to display corresponding icon when sort
      Variable "start_row" is always 0, but "end_row" is dynamically changing based on the length of data set



Step 2: Create function to default data set and variables
//Function to default all variables and data
function setDefault(){
   //Default all button icons to be empty
   btn_sort_icon_0 = "";
   btn_sort_icon_1 = "";
   btn_sort_icon_2 = "";
   btn_sort_icon_3 = "";
   btn_sort_icon_4 = "";
   btn_sort_icon_5 = "";
   
   //Default data array for display
   data_ary = [];
   data_ary.push(["Honda", "Accord", "2009", "Metallic", "5", " "]);
   data_ary.push(["Acura", "RSX", "2002", "Red", "2", "X"]);
   data_ary.push(["Scion", "tC", "2014", "White", "2", "X"]);
   data_ary.push(["Honda", "Accord", "2014", "Black", "5", " "]);
   data_ary.push(["Toyota", "Corolla", "2014", "Red", "5", " "]);
   data_ary.push(["Mazda", "5", "2008", "Blue", "5", "X"]);
   data_ary.push(["BMW", "X1", "2015", "Black", "5", "X"]);
   data_ary.push(["Mini", "Cooper", "2014", "Green", "5", "X"]);
   
   //Create a backup for data array
   data_ary_bkup = [];
   for(var i=0; i<data_ary.length; i++){
      data_ary_bkup.push(data_ary);
   }
   
   //Set start and end value for display
   start_row = 0;
   end_row = data_ary.length;
   
   //Default an array to store sorting status for each column
   sort_order_ary = [];
   for(var i=0; i<6; i++){
      sort_order_ary.push("X");
   }
   
   //Set default flag to true fore only run default function one time
   z_default_flag = true;
}
Note: Array "data_ary_bkup " is to resume data set


Step 3: Create function to sort content
//Function to sort content
function sortContent(param){
   set("V[btn_sort_icon_*]", " ");         //Reset icon for all the button

   var column = param.col;               //Copy the value of passed column number
   if(sort_order_ary[column]=='X')         //Set the column of status array to 0 if it's defaulted to "X"
      sort_order_ary[column] = 0;
   
   if(sort_order_ary[column] == 1){
      set("V[btn_sort_icon_&V[column]]", '@0H@');
      sort_order_ary[column] = 2;         //Change column of status array to 2 (descending order)
      data_ary.sort(function (a,b) {
         a = a[column].toLowerCase();   //Convert both value to lower case for following comparison
         b = b[column].toLowerCase();
         return a == b ? 0 : (a < b ? 1 : -1);   //Compare elements. if it's the same, don't switch; else if b larger than a, switch; else, reverse
      });
   } else if(sort_order_ary[column] == 0){
      set("V[btn_sort_icon_&V[column]]", '@0I@');
      sort_order_ary[column] = 1;         //Change column of status array to 1 (ascending order)
      data_ary.sort(function (a,b) {
         a = a[column].toLowerCase();
         b = b[column].toLowerCase();
         return a == b ? 0 : (a < b ? -1 : 1);
      });   
   } else {
      set("V[btn_sort_icon_&V[column]]", ' ');   
      sort_order_ary[column] = 0;         //Change column of status array to 0 (non-sorted order)
      data_ary = [];
      for(var i=0; i<data_ary_bkup.length; i++){         //Copy data from backup array
         data_ary.push(data_ary_bkup);
      }
   }
}


See attachments for code samples

14
Liquid UI: Update Text Content with Editor

This example is to copy text content to text editor screen to handle particular SAP long text control which can't use "copytext" command.
For example, the long text control in Header Data screen -> Texts tab of Create Sales Order (VA01).
The function in this example copies text content to text editor screen in order to save the content for LiquidUI text box. 


Step 1: Create user interface
//user Interface
box([0,87], [5,164], "Term of Delivery");
textbox([1,89], [5,159], {"name":"z_va01_term_of_delivery_text"});
pushbutton([4,161], "@2L\\QSave Text@", "?", {"process":va01SaveText, "size":[1,2], "using":{"l_seq":6}});

Note: The parameter "l_seq"passes "6" to the function is because Term of Delivery is the 7th item in the list of Texts.
          And it requires to navigate down to that item from the 1st item for 6 times.



Step 2: Create generic functions
//Function to trim string
String.prototype.trim = function() {
   return this.replace(/^\s+|\s+$/g,"");
}


//Function to check if the string blank
function isBlank(jvar) {
   println(jvar);
    if (jvar=="undefined" || jvar== void 0 || jvar.toString().trim()=="" || jvar==null) {
       return true;
    } else {
       return false;
    }
}


//Function to split a string into an array with specified char size   
function splitText(longText, charSize){
   charSize = parseInt(charSize);
   var textArray = longText.split('\n');
   var resultArray = [], matchedArray = [];
   var new_line_content = "", remained_line_content = "", cur_line = "";
   
   for(var iCount=0; iCount<textArray.length; iCount++){
      new_line_content = textArray[iCount].replace(/\t+/g," ").trim();      //Text editor doesn't support Tab content
      
      do{
         remained_line_content = "";
         if(new_line_content.length > charSize){
            cur_line = new_line_content.substring(0,charSize);
            matchedArray = cur_line.match(/((\S*\s+\S*)+\s)|(\S*)/g);
            remained_line_content = new_line_content.substring(matchedArray[0].length, new_line_content.length)
            new_line_content = matchedArray[0];
         }
         resultArray.push(new_line_content);
         new_line_content = remained_line_content;
      } while(new_line_content.length > 0)
   }
                  
   return resultArray;
}


//Function to check if longtext is blank
function isTextBoxBlank(longText) {
   var bTextExist = true;
   var arrText = longText.split('\n');
   for(var iCount=0; iCount<arrText.length; iCount++) {
      if(!isBlank(arrText[iCount].trim())) {
         bTextExist = false;
         break;
      }
   }
   return bTextExist;
}

Note: The generic functions can be all put in one script file and load once in esession.sjs file


Step 3: Create function to copy text to SAP
//Function to copy text content to SAP text editor screen
function va01SaveText(param){
   var z_va0x_find_text_counter = param.l_seq;               //Copy passed sequence parameter

   onscreen "SAPMV45A.4001"
      if(isTextBoxBlank(z_va01_term_of_delivery_text.trim())){      //Skip copy text if textbox is blank
         enter('?');
         goto FUNC_END;
      }
      enter('/Menu=3,2,11');                        //Go to Header Data-> Text tab
      onmessage                              //Error handling
         if(_message.substring(0,2) == "E:"){
            message(_message);
            enter('?');
            goto FUNC_END;
         } else
            enter();
      
   onscreen 'SAPMV45A.4002'
      enter('=TP_FIRST');                           //Go to first option in the list
      
RECHECK_FIND_TEXT:;
   onscreen 'SAPMV45A.4002'
      if(z_va0x_find_text_counter > 0){                  //According to sequence value
         z_va0x_find_text_counter--;
         enter('=TP_NEXT');                        //Go to next text in the list
         goto RECHECK_FIND_TEXT;
      } else{
         z_longtextarray = splitText(z_va01_term_of_delivery_text,72);
         
         println('array--'+z_longtextarray);
         var start_line = 1;
         var save_line_count = 0;
         var line_count = z_longtextarray.length; 
         
         enter('=TP_DETAIL');                        //Go to text editor
      }
      
   onscreen 'SAPLSTXX.2102'
      enter('/Menu=3,3');                           //Change editor
      
   onscreen 'SAPLSTXX.1100'
      enter("/Menu=1,7");                           //Cancel existing content
      
   onscreen 'SAPLSPO1.0100'
      enter("=YES");                              //Select "yes" in the popup
      
NEXT_LINE:;
   onscreen "SAPLSTXX.1100"      
      //Form editable lines according to the length of operation long text 
      if(!(start_line > line_count)){
         save_line_count = start_line + 1;
         if(save_line_count<10){
            setcursor("cell[SAPLSTXX_EDITAREA,3,"+save_line_count+"]");
         } else{
            setcursor("cell[SAPLSTXX_EDITAREA,3,10]");
         }
      
         start_line++;
         enter();
         goto NEXT_LINE;
      } else {
         var ltrelrow = 1;
         var ltabsrow = 1;
         enter("/ScrollToLine=1", {"table":"T[SAPLSTXX_EDITAREA]"});         // Scroll table to top before copying value
      }
                               
NEW_SCREEN:;
   onscreen 'SAPLSTXX.1100'      
      gettableattribute("T[SAPLSTXX_EDITAREA]", {"firstvisiblerow":"FVisRow", "lastvisiblerow":"LVisRow", "lastrow":"LRow"});

      for(ltrelrow=1; ltrelrow<11; ltrelrow++, ltabsrow++){         // Copy long text to table
         if(ltabsrow > line_count){                  // If ltabsrow counter exceeds the total line value, Click "Back" to exit editing
            enter("/11");
            goto COPY_TEXT_END;
         }                                             
         start_copy_line = ltrelrow + 1;                              // Set targeting row
         tmp_line = z_longtextarray[ltabsrow-1];                                   // Copy current line content to a temp variable                  
         if(!isBlank(tmp_line))
            set('cell[SAPLSTXX_EDITAREA,3,&V[start_copy_line]]', '&V[tmp_line]');
         else
            set('cell[SAPLSTXX_EDITAREA,3,&V[start_copy_line]]', '');
      }
      enter("/ScrollToLine="+((ltabsrow--)-1), {"table":"T[SAPLSTXX_EDITAREA]"});                 // Scroll to show more editable line
      goto NEW_SCREEN;
 
COPY_TEXT_END:;
   onscreen 'SAPLSTXX.1100'
      enter('/3');
   
   onscreen 'SAPLSPO1.0100'                                       // Popup handling
      enter('=YES');    
      
   onscreen 'SAPLSTXX.2101'
      enter('/3');
      
FUNC_END:;
}


See attachments for code samples

15
Liquid UI: Dynamic Readonly Status Handling

Purpose:
The example is to change readonly status for created elements between Create/Change/Display transactions.
Those transactions will load same script according to the same screen number and screen name.
In order to create multiple commands for different readonly status of elements, this can save amount of scripts in large screen modification efficiently.

Below example uses IW31 - Create Maintenance Order/IW32 - Change Maintenance Order/IW33 - Display Maintenance Order as example.
The script merges some fields from "Location" tab to "HeaderData" tab so user don't need to go to extra tab to enter data.
It demonstrates how created inputfields can change its readonly status dynamically according to correspond conditions.


Step 1: Create user interface for the initial screen of IW32 - Change and IW33 - Display
//User Interface
onUIEvents["Enter"] = {"fcode":"?", "process":iw32iw33Execute};      //Execute when user hits enter on screen

Note: No need to create the functionality for IW31 is because it doesn't require pre-fetch data when create


Step 2: Create the function to fetch data from initial screen
//Function to execute and fetch data from "Location" tab
function iw32iw33Execute(){
   onscreen "SAPLCOIH.0101"
      enter();
      onmessage               //Error handling
         if(_message.substring(0,2) == "E:"){
            message(_message);
            enter("?");
            goto FUNC_END;
         }
         else
            enter();
            
   onscreen "SAPLCOIH.0101"      //Error Handling
      if(_message.substring(0,2) == "E:"){
         message(_message);
         enter("?");
         goto FUNC_END;
      }
      else
         enter();

   onscreen "SAPLCOIH.3000"
      set("V[iw3x_status]", "&F[Sys.Status]");
      enter("=ILOA");         //Go to "Location" tab
      
   onscreen "SAPLCOIH.3000"
      set("V[iw3x_maint_plant]", "&F[MaintPlant]");
      set("V[iw3x_location]", "&F[Location]");
      set("V[iw3x_room]", "&F[Room]");
      set("V[iw3x_plant_section]", "&F[Plant section]");
      set("V[iw3x_work_center]", "&F[Work center]");
      enter("=IHKZ");         //Go to "HeaderData" tab
      
FUNC_END:;      
}


Step 3: Create condition to change status flag in detail screen
//If transaction is IW31 or IW32 but system status doesn't contain "CLSD"
if(_transaction == "IW31" || (_transaction == "IW32" && iw3x_status.indexOf("CLSD")<0)){
   iw3x_readonly_flag = false;
}
//If transaction is IW33 or system status contains "CLSD"
else if(_transaction == "IW33" || iw3x_status.indexOf("CLSD")>-1){
   iw3x_readonly_flag = true;
}

Note: Variable "iw3x_readonly_flag" will be use to change readonly status


Step 4: Create user interface in detail screen
del("P[Location]");               //Remove "Location" tab on screen

if(_page.exists("HeaderData")){
   box("G[First operation]+[6,0]", "G[First operation]+[10,80]", "Location");
   inputfield("G[Location]+[1,1]", "MntPlant", "G[Location]+[1,12]",
      {"name":"iw3x_maint_plant", "size":4, "readonly":iw3x_readonly_flag});
   inputfield("G[Location]+[2,1]", "Location", "G[Location]+[2,12]",
      {"name":"iw3x_location", "size":10, "readonly":iw3x_readonly_flag});
   inputfield("G[Location]+[3,1]", "Room", "G[Location]+[3,12]",
      {"name":"iw3x_room", "size":8, "readonly":iw3x_readonly_flag});
   inputfield("G[Location]+[1,41]", "Plant section", "G[Location]+[1,57]",
      {"name":"iw3x_plant_section", "size":3, "readonly":iw3x_readonly_flag});
   inputfield("G[Location]+[2,41]", "Work center", "G[Location]+[2,57]",
      {"name":"iw3x_work_center", "size":8, "readonly":iw3x_readonly_flag});
}

Note: Use variable "iw3x_readonly_flag" as the value of readonly option for each create element command


Step 5: Conditionalize save function will only be trigger when readonly status is false
if(!iw3x_readonly_flag){
   onUIEvents["/11"] = {"fcode":"?", "process":iw3xSave};      //Execute when user hits save on screen
}

Note: Process "iw3xSave" should be designed to copy created variables to SAP fields in "Location" tab and save


See attachments for code samples

Pages: [1] 2