Welcome back to the "Tasting the mix of PHP and SAP" weblog series. This time, we're going to develop an SM30 Simulator, which is the next logical step after having developed and Tasting the mix of PHP and SAP.
Before we start, I have two things to say:
""Interfase local *" IMPORTING *" VALUE(QUERY_TABLE) LIKE DD02L-TABNAME *" VALUE(DELIMITER) LIKE SONV-FLAG DEFAULT SPACE *" VALUE(NO_DATA) LIKE SONV-FLAG DEFAULT SPACE *" VALUE(ROWSKIPS) LIKE SOID-ACCNT DEFAULT 0 *" VALUE(ROWCOUNT) LIKE SOID-ACCNT DEFAULT 0 *" TABLES *" OPTIONS STRUCTURE RFC_DB_OPT *" FIELDS STRUCTURE RFC_DB_FLD *" DATA STRUCTURE TAB512 *" KEYS STRUCTURE DD03P *" EXCEPTIONS *" TABLE_NOT_AVAILABLE *" TABLE_WITHOUT_DATA *" OPTION_NOT_VALID *" FIELD_NOT_VALID *" NOT_AUTHORIZED *" DATA_BUFFER_EXCEEDED *"----
As you can see, I just add a new table named KEYS and called FM DDIF_TABL_GET. Now, we know which fields are primary keys.
Next thing we need is a custom FM that allows our application to INSERT, UPDATE or DELETE any record from ANY TABLE (be very careful here as many tables are cluster tables or otherwise complex). Sounds cool right? Yeah...it's really cool.
You can create the ZTABLE_OPERATION, just using an 255 characters domain data.
ZRFC_TABLE_OPERATIONS_LINE
FUNCTION ZRFC_TABLE_OPERATIONS_LINE. *"----
""Interfase local *" IMPORTING *" VALUE(QUERY_TABLE) LIKE DD02L-TABNAME *" VALUE(OPERATION) LIKE BDLDATCOL-TYPE *" VALUE(FIELDS) LIKE ZTABLE_OPERATION-FIELDS *" VALUE(DATA) LIKE ZTABLE_OPERATION-DATA *" EXCEPTIONS *" RECORD_NOT_INSERTED *" RECORD_NOT_UPDATED *" RECORD_NOT_DELETED *" NOT_AUTHORIZED *" TABLE_NOT_AVAILABLE *"----
Before we start, I have two things to say:
- Remember that this app is just for testing purposes, don't implement this on a production system...unless you want to get fired.
Ok, now we can start. You may remember, in the firt volume of the series, I used RFC_READ_TABLE RFC in order to read the FIELDS and DATA from any table. It works fine, however I have two problems with it. You don't know which fields are primary keys and of course it's not released for customer use (see SAP Note 382318). Both of these are bad; for an SM30 the fact that I can't find the primary key is particularly bad. So, my first task was to modify the FM, by creating my own version in the "customer namespace" thus I also avoid using the one from SAP that could be changed, removed or what not at anytime. Here's the code:
ZRFC_READ_TABLE
""Interfase local *" IMPORTING *" VALUE(QUERY_TABLE) LIKE DD02L-TABNAME *" VALUE(DELIMITER) LIKE SONV-FLAG DEFAULT SPACE *" VALUE(NO_DATA) LIKE SONV-FLAG DEFAULT SPACE *" VALUE(ROWSKIPS) LIKE SOID-ACCNT DEFAULT 0 *" VALUE(ROWCOUNT) LIKE SOID-ACCNT DEFAULT 0 *" TABLES *" OPTIONS STRUCTURE RFC_DB_OPT *" FIELDS STRUCTURE RFC_DB_FLD *" DATA STRUCTURE TAB512 *" KEYS STRUCTURE DD03P *" EXCEPTIONS *" TABLE_NOT_AVAILABLE *" TABLE_WITHOUT_DATA *" OPTION_NOT_VALID *" FIELD_NOT_VALID *" NOT_AUTHORIZED *" DATA_BUFFER_EXCEEDED *"----
CALL FUNCTION 'VIEW_AUTHORITY_CHECK' EXPORTING VIEW_ACTION = 'S' VIEW_NAME = QUERY_TABLE EXCEPTIONS NO_AUTHORITY = 2 NO_CLIENTINDEPENDENT_AUTHORITY = 2 NO_LINEDEPENDENT_AUTHORITY = 2 OTHERS = 1.
IF SY-SUBRC = 2. RAISE NOT_AUTHORIZED. ELSEIF SY-SUBRC = 1. RAISE TABLE_NOT_AVAILABLE. ENDIF.
- ----------------------------------------------------------------------
- find out about the structure of QUERY_TABLE
- ----------------------------------------------------------------------
CALL FUNCTION 'DDIF_FIELDINFO_GET' EXPORTING TABNAME = QUERY_TABLE
- FIELDNAME = ' '
- LANGU = SY-LANGU
- LFIELDNAME = ' '
- ALL_TYPES = ' '
- GROUP_NAMES = ' '
- X030L_WA =
- DFIES_WA =
- LINES_DESCR =
- FIXED_VALUES =
- ----------------------------------------------------------------------
- isolate first field of DATA as output field
- (i.e. allow for changes to structure DATA!)
- ----------------------------------------------------------------------
- ----------------------------------------------------------------------
- if FIELDS are not specified, read all available fields
- ----------------------------------------------------------------------
- ----------------------------------------------------------------------
- for each field which has to be read, copy structure information
- into tables FIELDS_INT (internal use) and FIELDS (output)
- ----------------------------------------------------------------------
LINE_CURSOR = 0.
- for each field which has to be read ...
READ TABLE TABLE_STRUCTURE WITH KEY FIELDNAME = FIELDS-FIELDNAME. IF SY-SUBRC NE 0. RAISE FIELD_NOT_VALID. ENDIF.
- compute the place for field contents in DATA rows:
- if not first field in row, allow space for delimiter
- ... copy structure information into tables FIELDS_INT
- (which is used internally during SELECT) ...
- compute the place for contents of next field in DATA rows
- ... and into table FIELDS (which is output to the caller)
ENDLOOP.
- end of loop at FIELDS
- ----------------------------------------------------------------------
- read data from the database and copy relevant portions into DATA
- ----------------------------------------------------------------------
- output data only if NO_DATA equals space (otherwise the structure
- information in FIELDS is the only result of the module)
DATA: BEGIN OF WORK, BUFFER(30000), END OF WORK.
FIELD-SYMBOLS: WHERE (OPTIONS).
IF SY-DBCNT GT ROWSKIPS.
- copy all relevant fields into DATA (output) table
- end of loop at FIELDS_INT
IF ROWCOUNT > 0 AND SY-DBCNT GE ROWCOUNT. EXIT. ENDIF.
ENDIF.
ENDSELECT.
ENDIF.
- ----------------------------------------------------------------------
- Get the primary key fields
- ----------------------------------------------------------------------
MOVE QUERY_TABLE TO NAME.
CALL FUNCTION 'DDIF_TABL_GET' EXPORTING NAME = NAME
- STATE = 'A'
- LANGU = ' '
- IMPORTING
- GOTSTATE =
- DD02V_WA =
- DD09L_WA =
- DD05M_TAB =
- DD08V_TAB =
- DD12V_TAB =
- DD17V_TAB =
- DD35V_TAB =
- DD36M_TAB =
ENDFUNCTION.
As you can see, I just add a new table named KEYS and called FM DDIF_TABL_GET. Now, we know which fields are primary keys.
Next thing we need is a custom FM that allows our application to INSERT, UPDATE or DELETE any record from ANY TABLE (be very careful here as many tables are cluster tables or otherwise complex). Sounds cool right? Yeah...it's really cool.
You can create the ZTABLE_OPERATION, just using an 255 characters domain data.
ZRFC_TABLE_OPERATIONS_LINE
FUNCTION ZRFC_TABLE_OPERATIONS_LINE. *"----
""Interfase local *" IMPORTING *" VALUE(QUERY_TABLE) LIKE DD02L-TABNAME *" VALUE(OPERATION) LIKE BDLDATCOL-TYPE *" VALUE(FIELDS) LIKE ZTABLE_OPERATION-FIELDS *" VALUE(DATA) LIKE ZTABLE_OPERATION-DATA *" EXCEPTIONS *" RECORD_NOT_INSERTED *" RECORD_NOT_UPDATED *" RECORD_NOT_DELETED *" NOT_AUTHORIZED *" TABLE_NOT_AVAILABLE *"----
CALL FUNCTION 'VIEW_AUTHORITY_CHECK' EXPORTING VIEW_ACTION = 'S' VIEW_NAME = QUERY_TABLE EXCEPTIONS NO_AUTHORITY = 2 NO_CLIENTINDEPENDENT_AUTHORITY = 2 NO_LINEDEPENDENT_AUTHORITY = 2 OTHERS = 1.
IF SY-SUBRC = 2. RAISE NOT_AUTHORIZED. ELSEIF SY-SUBRC = 1. RAISE TABLE_NOT_AVAILABLE. ENDIF.
*=======================================================================
- Types.
*=======================================================================
- Internal Tables.
DATA: W_LINES TYPE STRING.
*=======================================================================
- Variables.
*=======================================================================
- Field-Symbols.
*=======================================================================
- Create a dynamic internal table.
ASSIGN DATAREF->* TO CREATE_DYNAMIC_TABLE EXPORTING IT_FIELDCATALOG = IT_FIELDCATALOG IMPORTING EP_TABLE = DATAREF.
- EXCEPTIONS
- GENERATE_SUBPOOL_DIR_FULL = 1
- OTHERS = 2.
ASSIGN DATAREF->* TO .
*=======================================================================
- Fill the dynamic internal table.
*============================== ========================================
- Perform task depending of the choose operation.
Comments