Tuesday 23 May 2017

PO Service line item quantity exceed validation against PR quantity…

Scenario


Service PO creation / change scenario – Service Quantity increasing more than requested in PR.

If we are creating PO with respective to RFQ, we can increase the any service line item quantity whatever we wanted. There is no proper SAP validation exist in PO for the POs which are created with reference to RFQ. This scenario is related to only service POs.

Steps for replication


Create PR with multiple service line items,

Example,
  1. Service line item keep the quantity as “100” for any line item (in this case consider I have 2nd service line item quantity as “100”.
  2. Release the PR.
  3. Create RFQ with reference to PR and release RFQ.
  4. Now create PO with reference to RFQ created in Step 3, where PR number will be auto populated from RFQ. Now save the PO.
    • Now come back to the same PO which is created in step 4, change 2nd service line item quantity to 200 or 300 or whatever number you want. System will not stop you.
    • Other scenario is in first PO it is 100 quantity, now I will create another PO with same RFQ and quantity would be populated with 100. Try to save now it will allow us to save even though we are creating multiple POs with more than requested PR Quantity.
Reason is actually quantity field is not updated in the PRs for the POs created using RFQ. And its more complex as it deals with pack no and sub pack numbers.

For some of the business this is key validation as PR quantity must validate during the PO creation.

Solution:

Used BADI “ME_PROCESS_PO_CUST” implementation and method “Check” where it will validate PO items.

Implement below logic –

Declarations -

    TYPES:
    lty_t_esll TYPE STANDARD TABLE OF esll WITH DEFAULT KEY.

    TYPES:
*// To read PR details and its package number
      BEGIN OF ty_eban,
        banfn  TYPE banfn,
        bnfpo  TYPE bnfpo,
        bsart  TYPE bbsrt,
        loekz  TYPE eloek,
        packno TYPE packno,
      END OF ty_eban,

      BEGIN OF ty_esll,
        packno     TYPE packno,
        introw     TYPE numzeile,
        srvpos     TYPE asnum,
        sub_packno TYPE sub_packno,
        menge      TYPE mengev,
      END OF ty_esll,

*// To get all POs which are used this PR and line item
      BEGIN OF ty_ekpo_val,
        ebeln  TYPE ebeln,
        ebelp  TYPE ebelp,
        loekz  TYPE eloek,
        banfn  TYPE banfn,
        bnfpo  TYPE bnfpo,
        packno TYPE packno,
      END OF ty_ekpo_val.

    FIELD-SYMBOLS:
      <lfs_lw_esll_sub>    TYPE esll.

    DATA:
      lv_err_flg       TYPE char1,
      lv_srv_nt_pr     TYPE char1,
      lv_curr_po_qty   TYPE mengev,
      lv_pr_qty        TYPE mengev,
      lv_bal_qty       TYPE mengev,
      lv_pr_qty_by     TYPE mengev,
      lv_bal_qtyc      TYPE char20,
      lv_pr_qty_byc    TYPE char20,
      lv_cur_ser_qtyc  TYPE char20,
      lv_exis_po_qty   TYPE mengev,
      lv_cur_ser_qty   TYPE mengev,
      lv_new_po_qty    TYPE mengev,
      lv_extrow        TYPE extrow,
      lv_ebelps        TYPE ebelp,
      lv_service       TYPE asnum,
      lt_ekpo_val      TYPE STANDARD TABLE OF ty_ekpo_val INITIAL SIZE 0,
      lw_ekpo_val      TYPE ty_ekpo_val,
      lt_items_tmp     TYPE TABLE OF mepoitem,
      lt_items_ser     TYPE TABLE OF mepoitem,
      lt_items_ser_tmp TYPE TABLE OF mepoitem,
      lw_items_ser_tmp TYPE mepoitem,
      lw_items_ser     TYPE mepoitem,
      ls_item          TYPE meout_item,
      lr_esll          TYPE REF TO esll,
      ls_srv           TYPE if_mmbsi_gui_factory=>type_s_srv_key,
      lt_esll_sub      TYPE lty_t_esll,
      lt_esll_loop     TYPE lty_t_esll,
      lw_esll_loop     TYPE esll,
      lt_esll          TYPE lty_t_esll,
      lw_eban          TYPE ty_eban,
      lt_eban          TYPE STANDARD TABLE OF ty_eban INITIAL SIZE 0,
      lt_esll_pr       TYPE STANDARD TABLE OF ty_esll INITIAL SIZE 0,
      lw_esll_pr       TYPE ty_esll,
      lw_esll_subpack  TYPE ty_esll,
      lt_esll_subpack  TYPE STANDARD TABLE OF ty_esll INITIAL SIZE 0,
      lt_esll_popack   TYPE STANDARD TABLE OF ty_esll INITIAL SIZE 0,
      lw_esll_popack   TYPE ty_esll,
      lw_esll_posubp   TYPE ty_esll,
      lt_esll_posubp   TYPE STANDARD TABLE OF ty_esll INITIAL SIZE 0,
      lt_esll_curpac   TYPE lty_t_esll,
      lt_esll_cursubp  TYPE lty_t_esll,
      lw_esll_cursubp  TYPE esll.


    CALL METHOD im_header->if_dcm_adapter~get_manager
      RECEIVING
        re_mgr = re_mgr.

    IF re_mgr IS BOUND.
      CALL METHOD re_mgr->get_current_item
        RECEIVING
          re_item = re_item.
    ENDIF.

    IF re_item IS BOUND.
      CALL METHOD re_item->get_data
        RECEIVING
          re_data = re_data.
    ENDIF.

    CALL METHOD im_header->get_items
      RECEIVING
        re_items = re_items.

    LOOP AT re_items INTO wa_items.
      CALL METHOD wa_items-item->get_data
        RECEIVING
          re_data = re_idata.

      APPEND re_idata TO ist_items.

    ENDLOOP.

      REFRESH:
      lt_esll_curpac,
      lt_esll_cursubp,
      lt_eban,
      lt_esll_pr,
      lt_esll_subpack,
      lt_ekpo_val,
      lt_items_ser,
      lt_items_tmp.


*// consider only service line item for validation...
      APPEND LINES OF ist_items TO lt_items_ser.
      DELETE lt_items_ser WHERE pstyp NE '9'.
*// If we have entered PR only - it must valiate
*// IF PR is not entered do not required to validate..
      DELETE lt_items_ser WHERE banfn IS INITIAL.
*// Do not considere delete (marked) line items
      DELETE lt_items_ser WHERE loekz IS NOT INITIAL.

*// At least one service line item must exist..
      CLEAR:
      lw_items_ser.

      READ TABLE lt_items_ser INTO lw_items_ser INDEX 1.
      IF sy-subrc IS INITIAL.

*// Get unique PR and PR line item number to get all the POs,
*// which are already used these PR&PR line items
        APPEND LINES OF lt_items_ser TO lt_items_tmp.
        SORT lt_items_tmp BY banfn bnfpo.
        DELETE ADJACENT DUPLICATES FROM lt_items_tmp
        COMPARING banfn bnfpo.

*// Get the selected PRs and its line item details -> GET PACK number to read all service lines
*// under that PR line items
        IF lt_items_tmp IS NOT INITIAL.

          SELECT
                  banfn
                  bnfpo
                  bsart
                  loekz
                  packno
            FROM eban
            INTO TABLE lt_eban
            FOR ALL ENTRIES IN lt_items_tmp
            WHERE banfn = lt_items_tmp-banfn
            AND bnfpo = lt_items_tmp-bnfpo.
          IF sy-subrc IS INITIAL.
            SORT lt_eban BY banfn bnfpo.
          ENDIF.

          IF lt_eban IS NOT INITIAL.
*// Get PR package number
            SELECT
             packno
             introw
             srvpos
             sub_packno
             menge
              FROM esll
              INTO TABLE lt_esll_pr
              FOR ALL ENTRIES IN lt_eban
             WHERE packno EQ lt_eban-packno.
            IF sy-subrc IS INITIAL.
              SORT lt_esll_pr BY packno.
*// Get PR sub package number, where you will have activity/service number

              SELECT
               packno
               introw
               srvpos
               sub_packno
               menge
                FROM esll
                INTO TABLE lt_esll_subpack
                FOR ALL ENTRIES IN lt_esll_pr
               WHERE packno EQ lt_esll_pr-sub_packno.
              IF sy-subrc IS INITIAL.
                SORT lt_esll_subpack BY sub_packno.
              ENDIF.

            ENDIF.

          ENDIF.

*// Get all PO detals from PR
          SELECT
                  ebeln
                  ebelp
                  loekz
                  banfn
                  bnfpo
                  packno
            FROM ekpo
            INTO TABLE lt_ekpo_val
            FOR ALL ENTRIES IN lt_items_tmp
            WHERE loekz = space
            AND banfn = lt_items_tmp-banfn
            AND bnfpo = lt_items_tmp-bnfpo.
          IF sy-subrc IS INITIAL.
*// Delete PO details of current PO
            DELETE lt_ekpo_val WHERE ebeln = lw_items_ser-ebeln.
            SORT lt_ekpo_val BY packno.
          ENDIF.

*// Get PO line items pack number
          IF lt_ekpo_val IS NOT INITIAL.

            SELECT
             packno
             introw
             srvpos
             sub_packno
             menge
              FROM esll
              INTO TABLE lt_esll_popack
              FOR ALL ENTRIES IN lt_ekpo_val
             WHERE packno EQ lt_ekpo_val-packno.
            IF sy-subrc IS INITIAL.
*// Get PO line item sub pack number
              SELECT
               packno
               introw
               srvpos
               sub_packno
               menge
                FROM esll
                INTO TABLE lt_esll_posubp
                FOR ALL ENTRIES IN lt_esll_popack
               WHERE packno EQ lt_esll_popack-sub_packno.
              IF sy-subrc IS INITIAL.
                SORT lt_esll_posubp BY sub_packno.
              ENDIF.
            ENDIF.
          ENDIF.
        ENDIF.

******************************************************
*// Read current PO line item services details
******************************************************
*// Take unique PR and line items to process the validation
        REFRESH:
        lt_items_ser_tmp.

        APPEND LINES OF lt_items_ser TO lt_items_ser_tmp.
        SORT lt_items_ser_tmp BY banfn bnfpo.
        DELETE ADJACENT DUPLICATES FROM lt_items_ser_tmp
        COMPARING banfn bnfpo.

        CLEAR:
        lv_err_flg,
        lw_items_ser_tmp,
        wa_items_final.

        LOOP AT lt_items_ser_tmp INTO lw_items_ser_tmp.

          CLEAR:
          wa_items_final.

          REFRESH:
          lt_esll_cursubp.

          LOOP AT lt_items_ser INTO wa_items_final
            WHERE banfn = lw_items_ser_tmp-banfn
            AND bnfpo = lw_items_ser_tmp-bnfpo.

            REFRESH:
            lt_esll_loop,
            lt_esll.

*// read esll data, as it is not available in my_items
            CALL FUNCTION 'MS_READ_SERVICES'
              EXPORTING
                i_packno = wa_items_final-packno
              TABLES
                t_esll   = lt_esll.

            APPEND LINES OF lt_esll TO lt_esll_curpac.

*// Get SUB PACK for this package..
            LOOP AT lt_esll REFERENCE INTO lr_esll.
              REFRESH:
              lt_esll_sub.

*//read esll for sub_packno
              CLEAR lt_esll_sub.
              CALL FUNCTION 'MS_READ_SERVICES'
                EXPORTING
                  i_packno = lr_esll->sub_packno
                TABLES
                  t_esll   = lt_esll_sub.

              APPEND LINES OF lt_esll_sub TO lt_esll_cursubp.
              APPEND LINES OF lt_esll_sub TO lt_esll_loop.
            ENDLOOP.  "LOOP AT lt_esll REFERENCE INTO lr_esll.

            REFRESH:
            lt_esll_sub.
            APPEND LINES OF lt_esll_cursubp TO lt_esll_sub.

***********************/******************************
*// PO loaded data..
*// Check each service line qty - in case if the service line is repeated
*// take the consolidated qty to compare PR qty
***********************/******************************
            CLEAR:
            lw_esll_loop.
*// loop only current line item services to compare the values
            LOOP AT lt_esll_loop INTO lw_esll_loop
              WHERE srvpos IS NOT INITIAL.

              UNASSIGN:
              <lfs_lw_esll_sub>.

              READ TABLE lt_esll_sub ASSIGNING <lfs_lw_esll_sub>
              WITH KEY srvpos = lw_esll_loop-srvpos.
              IF sy-subrc IS INITIAL.

                CLEAR:
                      lv_curr_po_qty,
                      lv_pr_qty,
                      lv_cur_ser_qty,
                      lv_exis_po_qty,
                      lv_ebelps,
                      lv_extrow,
                      lv_service.


                lv_extrow = lw_esll_loop-extrow.   "Current service line item#
                lv_service = lw_esll_loop-srvpos.  "Current Service number
                lv_ebelps = wa_items_final-ebelp.
                SHIFT lv_ebelps LEFT DELETING LEADING c_0.
                SHIFT lv_extrow LEFT DELETING LEADING c_0.
                SHIFT lv_service LEFT DELETING LEADING c_0.

                lv_cur_ser_qty = lw_esll_loop-menge.
*// Service line item total qty in current PO
                LOOP AT lt_esll_cursubp INTO lw_esll_cursubp
                  WHERE srvpos = <lfs_lw_esll_sub>-srvpos.
*// Get Service qty till the current line for accurate results on error message
                  IF lw_esll_cursubp-introw > lw_esll_loop-introw.
                    EXIT.
                  ENDIF.
                  lv_curr_po_qty = lw_esll_cursubp-menge + lv_curr_po_qty.
                ENDLOOP.


*// service line item qty in PR
                CLEAR:
                lv_srv_nt_pr,
                lw_eban.
                LOOP AT lt_eban INTO lw_eban
                  WHERE banfn = wa_items_final-banfn
                  AND bnfpo = wa_items_final-bnfpo.

*// Get PR pack number
                  CLEAR:
                  lw_esll_pr.
                  READ TABLE lt_esll_pr
                  INTO lw_esll_pr
                  WITH KEY packno = lw_eban-packno.
                  IF sy-subrc IS INITIAL.
                    CLEAR:
                    lw_esll_subpack.
                    LOOP AT lt_esll_subpack INTO lw_esll_subpack
                      WHERE packno = lw_esll_pr-sub_packno
                      AND srvpos = <lfs_lw_esll_sub>-srvpos.
                      lv_pr_qty = lw_esll_subpack-menge + lv_pr_qty.
                      lv_srv_nt_pr = abap_true.
                    ENDLOOP.
                  ENDIF.
                ENDLOOP.    "service line item qty in PR

                IF lv_srv_nt_pr IS INITIAL.
                  CLEAR:
                  lv_message,
                  lv_message1.

                  lv_err_flg = abap_true.
                  CONCATENATE
                  'For line item' lv_ebelps
                  'service line' lv_extrow
                  '/' lv_service '-'
                  INTO
                  lv_message SEPARATED BY space.


                  lv_message1 = 'Service line not existing in PR'.

                  mmpur_message_forced 'E' 'ME' '303' lv_message lv_message1 space space.

                ELSE.
*// service line item qty in existing/already created POs
                  CLEAR:
                  lw_ekpo_val.
                  LOOP AT lt_ekpo_val INTO lw_ekpo_val
                   WHERE banfn = wa_items_final-banfn
                   AND bnfpo = wa_items_final-bnfpo.

                    CLEAR:
                    lw_esll_popack.

                    READ TABLE lt_esll_popack
                    INTO lw_esll_popack
                    WITH KEY packno = lw_ekpo_val-packno.
                    IF sy-subrc IS INITIAL.
                      LOOP AT lt_esll_posubp INTO lw_esll_posubp
                      WHERE  packno = lw_esll_popack-sub_packno
                        AND srvpos = <lfs_lw_esll_sub>-srvpos.
                        lv_exis_po_qty = lv_exis_po_qty + lw_esll_posubp-menge.
                      ENDLOOP.
                    ENDIF.
                  ENDLOOP.   "service line item qty in existing/already created POs

                  CLEAR:
                  lv_new_po_qty.

*// All values exist - Compare and calculate...
*// If not add new PO service line qty and see if it exceeds again
                  lv_new_po_qty = lv_curr_po_qty + lv_exis_po_qty.

                  IF lv_new_po_qty > lv_pr_qty.
*// New Service Qty is exceeding the PR qty.
                    lv_err_flg = abap_true.
                    <lfs_lw_esll_sub>-menge = lv_pr_qty - lv_exis_po_qty.

                    CLEAR:
                    lv_message,
                    lv_message1,
                    lv_bal_qty,
                    lv_pr_qty_by,
                    lv_bal_qtyc,
                    lv_cur_ser_qtyc,
                    lv_pr_qty_byc,
                    lv_message2,
                    lv_message3.

                    lv_bal_qty = lv_pr_qty - lv_exis_po_qty.
                    lv_pr_qty_by = lv_curr_po_qty - lv_bal_qty.
*// To use in concatenate for error message
                    lv_bal_qtyc = lv_bal_qty.
*// Since it is cumulating the values - balance should be shown less than current
                    IF lv_pr_qty_by <= lv_cur_ser_qty.
                      lv_pr_qty_byc = lv_pr_qty_by.
                    ELSE.
                      lv_pr_qty_byc = lv_cur_ser_qty.
                    ENDIF.
                    lv_cur_ser_qtyc = lv_cur_ser_qty.


*‘Quantity XXXX (this is entered quantity) exceeds target quantity yyyy (balance quantity available)
*for the Purchase req. by ZZZZ (Difference of XXXX and yyyyy)’

                    CONCATENATE
                    'For line item' lv_ebelps
                    'service line' lv_extrow
                    '/' lv_service '-'
                    INTO
                    lv_message SEPARATED BY space.

                    CONCATENATE
                    'Quantity exceeds by' lv_pr_qty_byc
                    INTO
                    lv_message1 SEPARATED BY space.

                    CONCATENATE
                    'for entered quantity'
                    lv_cur_ser_qtyc
                    INTO
                    lv_message2 SEPARATED BY space.


                    CONCATENATE
                    'as balance PR Qty is'
                    lv_bal_qtyc
                    INTO
                    lv_message3 SEPARATED BY space.

                    mmpur_message_forced 'E' 'ME' '303' lv_message lv_message1 lv_message2 lv_message3.
                  ELSE.
*// do nothing
                  ENDIF.
                ENDIF.
              ENDIF.
            ENDLOOP.
          ENDLOOP.    "LOOP AT lt_items_ser INTO wa_items_final.
        ENDLOOP.    "For unique PR & PR line item

        IF lv_err_flg IS NOT INITIAL.
          ch_failed = 'X'.
        ENDIF.

      ENDIF.        "At least one service line item must exist..

No comments:

Post a Comment