ABAP Nummernkreise: Automatische Nummernvergabe

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

Nummernkreise ermöglichen die automatische, eindeutige Vergabe von Nummern für Belege, Dokumente und andere Geschäftsobjekte. Sie garantieren Eindeutigkeit auch bei parallelen Zugriffen.

Nummernkreis-Konzepte

KonzeptBeschreibung
NummernkreisobjektContainer für Intervalle (SNRO)
IntervallNummernbereich (von-bis)
Interne VergabeSystem vergibt automatisch
Externe VergabeBenutzer gibt Nummer vor

Wichtige Transaktionen

TransaktionBeschreibung
SNRONummernkreisobjekte pflegen
SNUMNummernkreisstände pflegen
SLG1Nummernkreis-Protokoll

Grundlegende Beispiele

Nächste Nummer holen

DATA: lv_number TYPE char20,
lv_rc TYPE inri-returncode.
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = '01' " Intervallnummer
object = 'ZORDER' " Nummernkreisobjekt
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: / 'Neue Nummer:', lv_number.
ELSE.
MESSAGE 'Fehler bei Nummernvergabe' TYPE 'E'.
ENDIF.

Nummer mit Jahr-Abhängigkeit

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 " Geschäftsjahr
IMPORTING
number = lv_number
EXCEPTIONS
OTHERS = 1.
" Ergebnis z.B.: 2025-0000001
DATA(lv_doc_number) = |{ lv_year }-{ lv_number ALPHA = IN }|.

Mehrere Nummern auf einmal

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 Nummern
IMPORTING
number = lv_from_number
returncode = lv_rc
EXCEPTIONS
OTHERS = 1.
" lv_from_number = erste Nummer
" lv_from_number + quantity - 1 = letzte Nummer
lv_to_number = lv_from_number + lv_quantity - 1.
WRITE: / 'Nummernbereich:', lv_from_number, 'bis', lv_to_number.

Nummer mit Subobjekt

" Subobjekt für mandantenübergreifende Nummernkreise
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = '01'
object = 'ZMATERIAL'
subobject = 'WERK1' " z.B. pro Werk
IMPORTING
number = lv_number
EXCEPTIONS
OTHERS = 1.

Aktuellen Stand lesen

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: / 'Von-Nummer:', ls_nriv-fromnumber.
WRITE: / 'Bis-Nummer:', ls_nriv-tonumber.
WRITE: / 'Aktueller Stand:', ls_nriv-nrlevel.

Nummernkreis-Intervall prüfen

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.
" Auslastung berechnen
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: / 'Auslastung:', lv_percent, '%'.
IF lv_percent > 90.
MESSAGE 'Nummernkreis fast erschöpft!' TYPE 'W'.
ENDIF.
ENDIF.

Externe Nummernvergabe prüfen

DATA: lv_extern_number TYPE char10 VALUE '1000000001'.
" Prüfen ob Nummer im gültigen Bereich
CALL FUNCTION 'NUMBER_CHECK'
EXPORTING
nr_range_nr = '02' " Externes Intervall
object = 'ZORDER'
number = lv_extern_number
EXCEPTIONS
interval_not_found = 1
number_outside = 2
OTHERS = 3.
CASE sy-subrc.
WHEN 0.
" Nummer ist gültig, jetzt reservieren
CALL FUNCTION 'NUMBER_MARK_AS_USED'
EXPORTING
nr_range_nr = '02'
object = 'ZORDER'
number = lv_extern_number
EXCEPTIONS
OTHERS = 1.
WHEN 2.
MESSAGE 'Nummer liegt außerhalb des Bereichs' TYPE 'E'.
ENDCASE.

Nummernkreis-Puffer

" Puffer für bessere Performance (in Massenprozessen)
CALL FUNCTION 'NUMBER_RANGE_ENQUEUE'
EXPORTING
object = 'ZORDER'
EXCEPTIONS
OTHERS = 1.
" Nummern aus Puffer holen (schneller)
DO 100 TIMES.
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = '01'
object = 'ZORDER'
IMPORTING
number = lv_number.
" Verarbeitung...
ENDDO.
" Puffer zurückschreiben
CALL FUNCTION 'NUMBER_RANGE_DEQUEUE'
EXPORTING
object = 'ZORDER'.

Nummernkreisstand zurücksetzen (Vorsicht!)

" Nur für Testsysteme - NIE in Produktion!
DATA: ls_interval TYPE nriv.
ls_interval-nrrangenr = '01'.
ls_interval-fromnumber = '0000000001'.
ls_interval-tonumber = '9999999999'.
ls_interval-nrlevel = '0000000000'. " Zurücksetzen
CALL FUNCTION 'NUMBER_RANGE_INTERVAL_UPDATE'
EXPORTING
object = 'ZORDER'
interval = ls_interval
EXCEPTIONS
OTHERS = 1.

Klasse CL_NUMBERRANGE_RUNTIME

" Moderne API (ab 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: / 'Nummer:', lv_number.
CATCH cx_nr_object_not_found.
MESSAGE 'Nummernkreisobjekt nicht gefunden' TYPE 'E'.
CATCH cx_nr_subobject.
MESSAGE 'Subobjekt-Fehler' TYPE 'E'.
CATCH cx_nr_interval.
MESSAGE 'Intervallfehler' TYPE 'E'.
ENDTRY.

Nummernkreis im Update-Task

" Nummer erst bei COMMIT reservieren
CALL FUNCTION 'NUMBER_GET_NEXT'
EXPORTING
nr_range_nr = '01'
object = 'ZORDER'
IMPORTING
number = lv_number
EXCEPTIONS
OTHERS = 1.
" Beleg mit Nummer speichern
ls_order-order_id = lv_number.
INSERT zorders FROM ls_order.
" Bei ROLLBACK wird Nummer nicht verbraucht
" (bei gepufferter Vergabe)
COMMIT WORK.

Eigenes Nummernkreisobjekt anlegen (SNRO)

" Programmatisch anlegen (selten benötigt)
DATA: ls_attributes TYPE nrobj.
ls_attributes-object = 'ZORDER'.
ls_attributes-domlen = 'CHAR10'.
ls_attributes-percentage = 10. " Warnung bei 10% Rest
ls_attributes-devclass = 'ZDEV'.
CALL FUNCTION 'NUMBER_RANGE_OBJECT_UPDATE'
EXPORTING
object = 'ZORDER'
attributes = ls_attributes
EXCEPTIONS
OTHERS = 1.

Intervall programmatisch anlegen

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

Nummernkreis-Transport

" Nummernkreis-Objekte werden transportiert
" Intervalle müssen separat gepflegt werden!
" Im Zielsystem Intervalle anlegen:
" Transaktion SNUM -> Intervalle pflegen
" Oder per Report:
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.
" Intervall existiert bereits
ENDIF.

Nummernkreis-Typen

TypKennzeichenBeschreibung
Internexternind = ’ ‘System vergibt Nummer
Externexternind = ‘X’Benutzer gibt Nummer
Gepuffertbuffer > 0Nummern werden vorgeladen
Rollback-fähigBei Fehler nicht verbraucht

Intervall-Strategie

SzenarioEmpfehlung
JahresabhängigIntervall pro Jahr (01-2024, 01-2025)
MandantenspezifischSubobjekt = Mandant
WerksbezogenSubobjekt = Werk
Hoher DurchsatzPufferung aktivieren

Best Practices

  1. Lückenlose Vergabe: Nur wenn wirklich erforderlich
  2. Pufferung: Für Massenprozesse aktivieren
  3. Monitoring: Auslastung regelmäßig prüfen
  4. Testdaten: Separate Intervalle für Tests verwenden
  5. Rollback: Bei Fehlern keine Nummern verschwenden
  6. Jahreswechsel: Neue Intervalle rechtzeitig anlegen

Verwandte Themen