Copyright 2022 - BV TallVision IT

The internal number range is a beautiful concept, but if the actual documents on your system are in an internal range (typically because of a migration) the system will eventually cause collisions Here is an example of a function module that works like a protection shell around the NUMBER_GET_NEXT. All it does is check an actual database table for the existance of the "next number" - as R/3 does not do this. R/3 assumes (correclty) that an internal number range will never be used for external numbering. The example will repeat "pulling a new number" until the result is an actual new number.

 

This function module should be called instead of (replacing) the NUMBER_GET_NEXT function module to get a new number from a number range.

FUNCTION Z_UM_NUMBER_RANGE_SECURITY.
*"----------------------------------------------------------------------
*"*"Local interface:
*"  IMPORTING
*"     REFERENCE(OBJECT) LIKE  NRIV-OBJECT DEFAULT 'EINKBELEG'
*"     REFERENCE(NRRANGENR) LIKE  NRIV-NRRANGENR DEFAULT '70'
*"     REFERENCE(SUBOBJECT) LIKE  NRIV-SUBOBJECT DEFAULT SPACE
*"  EXCEPTIONS
*"      SECURITY_FAILURE
*"----------------------------------------------------------------------

*----------------------------------------------------------------------*
* This function module checks whether the following number that is     *
* available on a number range object/nrRangeNr/subobject is already    *
* available on the database. If it is, the number is reserved, avoiding*
* an update error. This was done because some purchase orders were     *
* created with numbers within an internal number range.                *
*----------------------------------------------------------------------*
  data: l_nrlevel like nriv-nrlevel,
        l_check_ok,
        l_ekko_ebeln like ekko-ebeln.

  clear: l_check_ok.
*--- as long as the check is not done or a problem NR was found:
  while l_check_ok eq space.

    select single nrlevel from nriv into l_nrlevel
      where object eq object and
            nrrangenr eq nrrangenr and
            SUBOBJECT eq SUBOBJECT.
    if sy-subrc ne 0.
      raise SECURITY_FAILURE.
    endif.

* The number range level holds the last number that was USED, here
* we need to check whether the next number is available for use, hence:
    add 1 to l_nrlevel.

* Check the relevant objects:
    case OBJECT.
* Purchase documents: table EKKO!
      when 'EINKBELEG'.
        write l_nrlevel+10(10) to l_ekko_ebeln.
        select single ebeln from ekko into l_ekko_ebeln
          where ebeln eq l_ekko_ebeln.
        if sy-subrc eq 0. "Alert! PO already available
* Don't set the check_ok flag...
        else.
          l_check_ok = 'X'.
        endif.
      when others.
* Function was called with unknown object... ask a developer to
* add a check like the one above first...
        raise SECURITY_FAILURE.
    endcase.

    if l_check_ok eq space.
      CALL FUNCTION 'NUMBER_GET_NEXT'
        EXPORTING
          NR_RANGE_NR = NRRANGENR
          OBJECT      = OBJECT
          SUBOBJECT   = SUBOBJECT
        EXCEPTIONS
          OTHERS = 1.
      IF SY-SUBRC <> 0.
        raise SECURITY_FAILURE.
      ENDIF.

    endif.

  endwhile.

ENDFUNCTION.