donderdag 16 december 2010

Delete payroll results

I had the problem on a DEV-environment that the PY-results were a mess. I had double keys in the PCL2 table that made it impossible to remove the data via the standard SAP-programs RPUDEL20 or RPCDPU01.

That is why I created my own program that is based on the Logical database PNP.

Please handle with care, you don't want to execute this program on you Productive-systems

REPORT  ZHR_DEL_PY.

TABLES: pernr.

DATA: BEGIN OF rgdir OCCURS 0.
        INCLUDE STRUCTURE pc261.
DATA: END OF rgdir.
DATA: C_PERNR(8), C_SEQNR(5).
DATA: SORTFIELD(40).

RANGES: SORTFD FOR SORTFIELD.

START-OF-SELECTION.

GET PERNR.

* READ THE RGDIR
  REFRESH RGDIR.


  CALL FUNCTION 'CU_READ_RGDIR'
    EXPORTING
      persnr             = pernr-pernr
      no_authority_check = 'X'
    TABLES
      in_rgdir           = rgdir
    EXCEPTIONS
      no_record_found    = 1
      OTHERS             = 2.

  LOOP AT rgdir.
    MOVE pernr-pernr TO C_PERNR.
    MOVE rgdir-SEQNR  TO C_SEQNR.

    CONCATENATE C_PERNR C_SEQNR INTO SORTFD-LOW.
    SORTFD-SIGN = 'I'.    "in
    SORTFD-OPTION = 'EQ'. "equal
    APPEND SORTFD.
  ENDLOOP.

  CHECK SY-SUBRC = 0.

  DELETE FROM PCL2
    WHERE RELID = 'RB'
    AND  SRTFD IN SORTFD.

  DELETE FROM PCL2
    WHERE RELID = '$$'
    AND  SRTFD IN SORTFD.

  DELETE FROM PCL2
    WHERE RELID = 'CU'
      AND SRTFD = pernr-pernr.

  IF SY-SUBRC = 0.
    LOOP AT RGDIR.

      DELETE FROM HRPY_RGDIR WHERE
                           PERNR = pernr-pernr AND
                           SEQNR = rgdir-SEQNR.

      DELETE FROM HRPY_WPBP WHERE
                           PERNR = pernr-pernr AND
                           SEQNR = RGDIR-SEQNR.

      DELETE FROM HRPY_GROUPING WHERE
                           PERNR = pernr-pernr AND
                           SEQNR = RGDIR-SEQNR.

      DELETE FROM P01O_ZVB_RR WHERE
                           PERNR = pernr-pernr AND
                           EAPER = RGDIR-FPPER.

    ENDLOOP.
  ENDIF.

  COMMIT WORK.

  WRITE: / 'You deleted <', pernr-pernr, '>'.

END-OF-SELECTION.

Example subroutine programming: Delete content of a table

Below you can find an example of dynamically creating source-code.
For my example, I created a program that allows you to fill in a tablename and that removes all the content of the table that you've filled in.

Don't try this @ home, unless you know what you are doing.

REPORT  ZHR_DELETE_TABLE_CONTENT.

  DATA:  lt_code(100)     TYPE c OCCURS 0 WITH HEADER LINE,
         lv_repid         LIKE sy-repid,
         mess TYPE string,
         sid  TYPE string.

  PARAMETERS: p_table TYPE string OBLIGATORY.

  INITIALIZATION.

    IF sy-uname NE 'VERSTOCT'.
      MESSAGE 'PROGRAM NOT ALLOWED FOR THIS USER' TYPE 'E'.
    ENDIF.

    IF sy-sysid NE 'DEV'.
      MESSAGE 'PROGRAM NOT ALLOWED ON THIS SYSTEM' TYPE 'E'.
    ENDIF.

  START-OF-SELECTION.

    REFRESH lt_code.

    lt_code = 'PROGRAM TEMP.'.
    APPEND lt_code.

    lt_code = 'FORM DELETE_TAB.'.
    APPEND lt_code.

    CONCATENATE 'DELETE FROM' p_table '.' INTO lt_code SEPARATED BY space.
    APPEND lt_code.

    MOVE 'COMMIT WORK.' TO lt_code.
    APPEND lt_code.

    lt_code = 'ENDFORM.'.
    APPEND lt_code.

    GENERATE SUBROUTINE POOL lt_code NAME lv_repid
           MESSAGE mess
           SHORTDUMP-ID sid.

    PERFORM ('DELETE_TAB') IN PROGRAM (lv_repid).

    WRITE: / 'The content of table <', p_table, '> is gone.'.

GENERATE_SUBPOOL_DIR_FULL in cl_alv_table_create=>create_dynamic_table

Because the standard SAP method "Create_dynamic_table" can give the runtime error GENERATE_SUBPOOL_DIR_FULL, it should be avoided to use it.

  CALL FUNCTION 'REUSE_ALV_FIELDCATALOG_MERGE'
    EXPORTING
      i_structure_name = 'YSDXX_CLST_DATA'
    CHANGING
      ct_fieldcat = lta_fcat.

   CALL METHOD cl_alv_table_create=>create_dynamic_table
     EXPORTING
       it_fieldcatalog = lta_fcat
     IMPORTING
       ep_table        = dr_content. 

Instead use the code below,

lo_struct ?= cl_abap_typedescr=>describe_by_name( 'YSDXX_CLST_DATA' ).
lt_comp  = lo_struct->get_components( ).
APPEND LINES OF lt_comp TO lt_tot_comp.
l_struc = cl_abap_structdescr=>create( lt_tot_comp ).
l_struc_table = cl_abap_tabledescr=>create(
                        p_line_type  = l_struc
                        p_table_kind = cl_abap_tabledescr=>tablekind_std
                        p_unique     = abap_false ).

CREATE DATA lw_global_data-datatab TYPE HANDLE l_struc_table.

maandag 8 november 2010

shortdump GENERATE_SUBPOOL_DIR_FULL

There's a limit on the number of sub-routines that can be generated within a program. This limit is 36 and anythng over this will create a short-dump(GENERATE_SUBPOOL_DIR_FULL).

The reason why SAP has put this limitation on it is, is to avoid an overflow of the program memory. Changing this number is not possible because it is a kernel constant and therefore, not variable and removing memory segments from the PXA is currently not supported by the kernel...

So you need to look for another solution.
There are 2 main reasons why you would want to generate a subroutine:
1. To create a dynamic structure
2. To create a dynamic program

Let me give you the solution for those 2 types of creating subroutines.

1. Create a dynamic structure:

Old Code
  DATA: lt_code(100) TYPE c OCCURS 0 WITH HEADER LINE,
        lv_type(100),
        lv_repid LIKE sy-repid,
        lw_fcat TYPE lvc_s_fcat.


* DYNAMISCH TYPES GENEREREN IN PROGRAMMA'S
  lt_code = 'REPORT GENERATE_TYPES.'.
  APPEND lt_code.

  lt_code = 'TYPES: BEGIN OF T_DATA,'.
  APPEND lt_code.

  LOOP AT p_fcattab INTO lw_fcat.
    CONCATENATE lw_fcat-fieldname 

                '(' 
                lw_fcat-intlen 
                ') TYPE c,' 
           INTO lt_code.
    APPEND lt_code.
  ENDLOOP.

  lt_code = 'END OF T_DATA.'.
  APPEND lt_code.

  GENERATE SUBROUTINE POOL lt_code NAME lv_repid.
  CONCATENATE '\PROGRAM=' lv_repid '\TYPE=T_DATA' INTO p_type.



New Code
*&--------------------------------------------------------------*
*&      Form  CREATE_INTERNAL_STRUCTURE

*&--------------------------------------------------------------*
*       Create internal structure
*---------------------------------------------------------------*
FORM CREATE_INTERNAL_STRUCTURE TABLES p_fcattab TYPE lvc_t_fcat
                  CHANGING p_struc TYPE REF TO cl_abap_structdescr
                           p_struc_table TYPE REF TO cl_abap_tabledescr.

  DATA: ls_comp TYPE abap_componentdescr,
        lw_fcat TYPE lvc_s_fcat,
        lt_comp TYPE cl_abap_structdescr=>component_table,
        lv_integer TYPE i.

  LOOP AT p_fcattab INTO lw_fcat.
    ls_comp-name = lw_fcat-fieldname.
    lv_integer = lw_fcat-intlen.
    ls_comp-type = cl_abap_elemdescr=>get_c( lv_integer ).
    APPEND ls_comp TO lt_comp.
  ENDLOOP.

  TRY.
   p_struc = 

           cl_abap_structdescr=>create( p_components = lt_comp ).
  CATCH cx_sy_struct_creation .
  ENDTRY.

  TRY.
    p_struc_table = cl_abap_tabledescr=>create( p_struc ).
    CATCH cx_sy_table_creation .
  ENDTRY.

ENDFORM.                    " CREATE_INTERNAL_STRUCTURE


2. Create a dynamic program

Main Program
  ...
EXPORT <whatever is needed> TO MEMORY ID 'ABC'.
* Execute the code generation logic in new internal mode
SUBMIT <Sub Program> AND RETURN.

Sub Program
  ...
IMPORT <whatever is needed> FROM MEMORY ID 'ABC'.
* Logic to generate the internal table of ABAP code
...
GENERATE SUBROUTINE POOL <codetab> NAME gv_program.
IF SY-SUBRC EQ 0.
PERFORM <generated form> IN PROGRAM (gv_program).
ENDIF.