Rangos de numeros en ABAP: Asignacion automatica de numeros

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

Los rangos de numeros permiten la asignacion automatica y unica de numeros para documentos, comprobantes y otros objetos de negocio. Garantizan unicidad incluso con accesos paralelos.

Conceptos de rangos de numeros

ConceptoDescripcion
Objeto de rango de numerosContenedor para intervalos (SNRO)
IntervaloRango de numeros (desde-hasta)
Asignacion internaEl sistema asigna automaticamente
Asignacion externaEl usuario proporciona el numero

Transacciones importantes

TransaccionDescripcion
SNROMantener objetos de rango de numeros
SNUMMantener estados de rango de numeros
SLG1Protocolo de rango de numeros

Ejemplos basicos

Obtener siguiente numero

DATA: lv_number TYPE char20,
lv_rc TYPE inri-returncode.
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = '01' " Numero de intervalo
object = 'ZORDER' " Objeto de rango de numeros
IMPORTING
number = lv_number
returncode = lv_rc
EXCEPTIONS
interval_not_found = 1
number_range_not_intern = 2
object_not_found = 3
quantity_is_0 = 4
quantity_is_not_1 = 5
interval_overflow = 6
buffer_overflow = 7
OTHERS = 8.
IF sy-subrc = 0.
WRITE: / 'Nuevo numero:', lv_number.
ELSE.
MESSAGE 'Error en asignacion de numero' TYPE 'E'.
ENDIF.

Numero con dependencia de ano

DATA: lv_number TYPE char20,
lv_year TYPE nriv-toyear.
lv_year = sy-datum(4).
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = '01'
object = 'ZINVOICE'
toyession = lv_year " Ejercicio fiscal
IMPORTING
number = lv_number
EXCEPTIONS
OTHERS = 1.
" Resultado ej.: 2025-0000001
DATA(lv_doc_number) = |{ lv_year }-{ lv_number ALPHA = IN }|.

Multiples numeros de una vez

DATA: lv_from_number TYPE char20,
lv_to_number TYPE char20,
lv_quantity TYPE i VALUE 10.
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = '01'
object = 'ZBATCH'
quantity = lv_quantity " 10 numeros
IMPORTING
number = lv_from_number
returncode = lv_rc
EXCEPTIONS
OTHERS = 1.
" lv_from_number = primer numero
" lv_from_number + quantity - 1 = ultimo numero
lv_to_number = lv_from_number + lv_quantity - 1.
WRITE: / 'Rango de numeros:', lv_from_number, 'hasta', lv_to_number.

Numero con subobjeto

" Subobjeto para rangos de numeros cross-mandante
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = '01'
object = 'ZMATERIAL'
subobject = 'PLANT1' " ej. por centro
IMPORTING
number = lv_number
EXCEPTIONS
OTHERS = 1.

Leer estado actual

DATA: ls_nriv TYPE nriv.
CALL FUNCTION 'NUMBER_GET_INFO'
EXPORTING
nr_range_nr = '01'
object = 'ZORDER'
IMPORTING
interval = ls_nriv
EXCEPTIONS
OTHERS = 1.
WRITE: / 'Numero desde:', ls_nriv-fromnumber.
WRITE: / 'Numero hasta:', ls_nriv-tonumber.
WRITE: / 'Estado actual:', ls_nriv-nrlevel.

Verificar intervalo de rango de numeros

DATA: lv_percent TYPE p DECIMALS 2.
CALL FUNCTION 'NUMBER_GET_INFO'
EXPORTING
nr_range_nr = '01'
object = 'ZORDER'
IMPORTING
interval = ls_nriv
EXCEPTIONS
OTHERS = 1.
IF sy-subrc = 0.
" Calcular utilizacion
DATA(lv_total) = ls_nriv-tonumber - ls_nriv-fromnumber.
DATA(lv_used) = ls_nriv-nrlevel - ls_nriv-fromnumber.
IF lv_total > 0.
lv_percent = ( lv_used / lv_total ) * 100.
ENDIF.
WRITE: / 'Utilizacion:', lv_percent, '%'.
IF lv_percent > 90.
MESSAGE 'Rango de numeros casi agotado!' TYPE 'W'.
ENDIF.
ENDIF.

Verificar asignacion externa de numeros

DATA: lv_extern_number TYPE char10 VALUE '1000000001'.
" Verificar si numero esta en rango valido
CALL FUNCTION 'NUMBER_CHECK'
EXPORTING
nr_range_nr = '02' " Intervalo externo
object = 'ZORDER'
number = lv_extern_number
EXCEPTIONS
interval_not_found = 1
number_outside = 2
OTHERS = 3.
CASE sy-subrc.
WHEN 0.
" Numero es valido, ahora reservar
CALL FUNCTION 'NUMBER_MARK_AS_USED'
EXPORTING
nr_range_nr = '02'
object = 'ZORDER'
number = lv_extern_number
EXCEPTIONS
OTHERS = 1.
WHEN 2.
MESSAGE 'Numero esta fuera del rango' TYPE 'E'.
ENDCASE.

Buffer de rango de numeros

" Buffer para mejor rendimiento (en procesos masivos)
CALL FUNCTION 'NUMBER_RANGE_ENQUEUE'
EXPORTING
object = 'ZORDER'
EXCEPTIONS
OTHERS = 1.
" Obtener numeros del buffer (mas rapido)
DO 100 TIMES.
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = '01'
object = 'ZORDER'
IMPORTING
number = lv_number.
" Procesamiento...
ENDDO.
" Escribir buffer de vuelta
CALL FUNCTION 'NUMBER_RANGE_DEQUEUE'
EXPORTING
object = 'ZORDER'.

Resetear estado de rango de numeros (Precaucion!)

" Solo para sistemas de prueba - NUNCA en produccion!
DATA: ls_interval TYPE nriv.
ls_interval-nrrangenr = '01'.
ls_interval-fromnumber = '0000000001'.
ls_interval-tonumber = '9999999999'.
ls_interval-nrlevel = '0000000000'. " Resetear
CALL FUNCTION 'NUMBER_RANGE_INTERVAL_UPDATE'
EXPORTING
object = 'ZORDER'
interval = ls_interval
EXCEPTIONS
OTHERS = 1.

Clase CL_NUMBERRANGE_RUNTIME

" API moderna (desde 7.50)
DATA: lo_nr TYPE REF TO cl_numberrange_runtime,
lv_number TYPE cl_numberrange_runtime=>nr_number.
TRY.
lo_nr = cl_numberrange_runtime=>get_instance(
iv_object = 'ZORDER' ).
lv_number = lo_nr->get_next(
iv_nrrangenr = '01' ).
WRITE: / 'Numero:', lv_number.
CATCH cx_nr_object_not_found.
MESSAGE 'Objeto de rango de numeros no encontrado' TYPE 'E'.
CATCH cx_nr_subobject.
MESSAGE 'Error de subobjeto' TYPE 'E'.
CATCH cx_nr_interval.
MESSAGE 'Error de intervalo' TYPE 'E'.
ENDTRY.

Rango de numeros en Update-Task

" Numero reservado solo en COMMIT
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = '01'
object = 'ZORDER'
IMPORTING
number = lv_number
EXCEPTIONS
OTHERS = 1.
" Guardar documento con numero
ls_order-order_id = lv_number.
INSERT zorders FROM ls_order.
" En ROLLBACK el numero no se consume
" (con asignacion con buffer)
COMMIT WORK.

Crear objeto de rango de numeros propio (SNRO)

" Crear programaticamente (raramente necesario)
DATA: ls_attributes TYPE nrobj.
ls_attributes-object = 'ZORDER'.
ls_attributes-domlen = 'CHAR10'.
ls_attributes-percentage = 10. " Advertencia al 10% restante
ls_attributes-devclass = 'ZDEV'.
CALL FUNCTION 'NUMBER_RANGE_OBJECT_UPDATE'
EXPORTING
object = 'ZORDER'
attributes = ls_attributes
EXCEPTIONS
OTHERS = 1.

Crear intervalo programaticamente

DATA: ls_interval TYPE nriv.
ls_interval-nrrangenr = '01'.
ls_interval-fromnumber = '0000000001'.
ls_interval-tonumber = '9999999999'.
ls_interval-externind = ' '. " Interno
CALL FUNCTION 'NUMBER_RANGE_INTERVAL_CREATE'
EXPORTING
object = 'ZORDER'
interval = ls_interval
EXCEPTIONS
OTHERS = 1.

Transporte de rango de numeros

" Los objetos de rango de numeros se transportan
" Los intervalos deben mantenerse por separado!
" En el sistema destino crear intervalos:
" Transaccion SNUM -> Mantener intervalos
" O via reporte:
REPORT znr_interval_setup.
CALL FUNCTION 'NUMBER_RANGE_INTERVAL_CREATE'
EXPORTING
object = 'ZORDER'
interval = VALUE nriv(
nrrangenr = '01'
fromnumber = '0000000001'
tonumber = '9999999999' )
EXCEPTIONS
interval_already_exist = 1
OTHERS = 2.
IF sy-subrc = 1.
" Intervalo ya existe
ENDIF.

Tipos de rango de numeros

TipoIndicadorDescripcion
Internoexternind = ’ ‘Sistema asigna numero
Externoexternind = ‘X’Usuario proporciona numero
Con bufferbuffer > 0Numeros se precargan
Con rollbackNo consumido en caso de error

Estrategia de intervalo

EscenarioRecomendacion
Dependiente del anoIntervalo por ano (01-2024, 01-2025)
Especifico de mandanteSubobjeto = Mandante
Por centroSubobjeto = Centro
Alto rendimientoActivar buffering

Mejores practicas

  1. Asignacion sin huecos: Solo cuando realmente se requiere
  2. Buffering: Activar para procesos masivos
  3. Monitoreo: Verificar utilizacion regularmente
  4. Datos de prueba: Usar intervalos separados para pruebas
  5. Rollback: No desperdiciar numeros en caso de errores
  6. Cambio de ano: Crear nuevos intervalos a tiempo

Temas relacionados