Blog

Klasse zum Kreieren der XML-Datei

Klasse Z_CM_CL_BERK_XML_MANAGE

Methoden:

CONVERT_DOCUMENT2XML Instance Method Public    von BIN lesen und in XML Format convertieren
CONSTRUCTOR Instance Method Public  CONSTRUCTOR

INSERT_TAB2DOCUMENT Instance Method Public                                 eine Tabelle an das bestehende XML-Documnet anhängen

RENDER_2_TABLE Instance Method Public                                 XML-Document in die Int- Tabelle

DISPLAY Instance Method Public                                 Display

INSERT_TAB_AS_ACHILD_2NODE Instance Method Public                                 Tabelle an das best.Tabelle  XML-Documnet anhängen als Node

 

Attribute:

GR_XML_DOC Instance Attribute Public Type Ref To CL_XML_DOCUMENT XML-Dokument für WF- WEB-Aktivität
GR_NODE_INP Instance Attribute Private Type Ref To IF_IXML_NODE Knoten eines DOM

 

METHOD convert_document2xml.
DATAlr_convin                 TYPE REF TO cl_abap_conv_in_ce,
lt_table                  TYPE swxmlcont,
lv_text_line              TYPE char1024,
lv_text_line_1            TYPE char255,
lv_text_line_buffer       TYPE char255,
lv_search_parser          TYPE char255,
lv_search_parser_gruppe   TYPE char255,
lv_search_parser_gruppe_2 TYPE char255,
lv_search_parser_gruppe_3 TYPE char255,
lv_search_parser_gruppe_4 TYPE char255,
lv_search_parser_gruppe_5 TYPE char255,
lv_search_parser_gruppe_6 TYPE char255,
lv_string_out             TYPE char50,
lv_line                   TYPE i,
lv_line_1                 TYPE i,
lv_line_2                 TYPE i,
lv_line_3                 TYPE i,
lv_line_4                 TYPE i,
lv_line_g1                TYPE i,
lv_line_g2                TYPE i,
lv_rc                     TYPE sysubrc.

TRY.
lt_table =  me->render_2_table).
CATCH cx_sy_ref_is_initial.
RAISE EXCEPTION TYPE zcx_berk_exception.
ENDTRY.

lv_rc   lineslt_table ).

*    TRANSLATE   lv_string_in TO LOWER CASE.

LOOP AT lt_table ASSIGNING FIELDSYMBOL(<ls_xstring>).

CALL METHOD cl_abap_conv_in_ce=>create
EXPORTING
encoding    ‚UTF-8‘
endian      ‚L‘
ignore_cerr ‚X‘
replacement ‚#‘
input       <ls_xstring> “ Xstring
RECEIVING
conv        lr_convin.              “ Xstring

CALL METHOD lr_convin->read
IMPORTING
data lv_text_line_1.

lv_text_line  =  lv_text_line_1.

IF sytabix 1.

IF  iv_string_in IS NOT INITIAL.
FIND FIRST OCCURRENCE OF  ‚<?xml version=“1.0″?>‘  IN lv_text_line  MATCH OFFSET lv_line_1 IN CHARACTER MODE.
IF sysubrc 0.
lv_text_line_buffer  ‚<?xml version=“1.0″ ‚ && ‚ ‚ && iv_string_in  &&  ‚“?>‘ .
SHIFT lv_text_line_buffer+19 RIGHT .
*****************************************************
TRANSLATE lv_text_line_buffer TO LOWER CASE.
*************************************************

APPEND  lv_text_line_buffer TO rt_secundary_tab.

CLEARlv_text_line(21),
lv_text_line_buffer,
lv_line_1.
SHIFT lv_text_line LEFT DELETING LEADING space.
ENDIF.
ENDIF.

FIND FIRST OCCURRENCE OF ‚<XML>‘ IN lv_text_line  MATCH OFFSET lv_line_1 IN CHARACTER MODE.
IF sysubrc 0.
lv_text_line_buffer  ‚<xml>‘.
APPEND  lv_text_line_buffer TO rt_secundary_tab.

CLEARlv_text_line+lv_line_1(5),
lv_text_line_buffer.
SHIFT lv_text_line+lv_line_1 LEFT DELETING LEADING space.
ENDIF.
ENDIF.

IF sytabix lv_rc.

FIND FIRST OCCURRENCE OF ‚</XML>‘ IN lv_text_line  MATCH OFFSET lv_line_1 IN CHARACTER MODE.
IF sysubrc 0.
CLEAR lv_text_line+lv_line_1(5).
ENDIF.
ENDIF.

IF  it_string_out[] IS NOT INITIAL.
LOOP AT  it_string_out INTO lv_string_out.
CHECK lv_string_out IS NOT INITIAL.
*       TRANSLATE   lv_string_out TO LOWER CASE.
REPLACE ALL OCCURRENCES OF lv_string_out IN lv_text_line  WITH space.

ENDLOOP.
ENDIF.

* Main-Part
IF lv_text_line_buffer IS NOT INITIAL.
lv_text_line lv_text_line_buffer && lv_text_line.
CLEAR lv_text_line_buffer.
ENDIF.
lv_line 0.
DO.

****************************************************** FeldGruppe -Ende

IF lv_search_parser_gruppe_6 IS NOT INITIAL.
FIND FIRST OCCURRENCE OF  lv_search_parser_gruppe_6 IN  lv_text_line+lv_line MATCH OFFSET lv_line_1 MATCH LENGTH lv_line_2.
IF sysubrc AND lv_line_1 0.
lv_line lv_line + lv_line_2.
TRANSLATE lv_search_parser_gruppe_6  TO LOWER CASE.
APPEND lv_search_parser_gruppe_6 TO rt_secundary_tab.
CLEAR lv_search_parser_gruppe_6.
ENDIF.
ENDIF.
IF lv_search_parser_gruppe_5 IS NOT INITIAL.
FIND FIRST OCCURRENCE OF  lv_search_parser_gruppe_5 IN  lv_text_line+lv_line MATCH OFFSET lv_line_1 MATCH LENGTH lv_line_2.
IF sysubrc AND lv_line_1 0.
lv_line lv_line + lv_line_2.
TRANSLATE lv_search_parser_gruppe_5  TO LOWER CASE.
APPEND lv_search_parser_gruppe_5 TO rt_secundary_tab.
CLEAR lv_search_parser_gruppe_5.
ENDIF.
ENDIF.
IF lv_search_parser_gruppe_4 IS NOT INITIAL.
FIND FIRST OCCURRENCE OF  lv_search_parser_gruppe_4 IN  lv_text_line+lv_line MATCH OFFSET lv_line_1 MATCH LENGTH lv_line_2.
IF sysubrc AND lv_line_1 0.
lv_line lv_line + lv_line_2.
TRANSLATE lv_search_parser_gruppe_4  TO LOWER CASE.
APPEND lv_search_parser_gruppe_4 TO rt_secundary_tab.
CLEAR lv_search_parser_gruppe_4.
ENDIF.
ENDIF.

IF lv_search_parser_gruppe_3 IS NOT INITIAL.
FIND FIRST OCCURRENCE OF  lv_search_parser_gruppe_3 IN  lv_text_line+lv_line MATCH OFFSET lv_line_1 MATCH LENGTH lv_line_2.
IF sysubrc AND lv_line_1 0.
lv_line lv_line + lv_line_2.
TRANSLATE lv_search_parser_gruppe_3  TO LOWER CASE.
APPEND lv_search_parser_gruppe_3 TO rt_secundary_tab.
CLEAR lv_search_parser_gruppe_3.
ENDIF.
ENDIF.

IF lv_search_parser_gruppe_2 IS NOT INITIAL.
FIND FIRST OCCURRENCE OF  lv_search_parser_gruppe_2 IN  lv_text_line+lv_line MATCH OFFSET lv_line_1 MATCH LENGTH lv_line_2.
IF sysubrc AND lv_line_1 0.
lv_line lv_line + lv_line_2.
TRANSLATE lv_search_parser_gruppe_2  TO LOWER CASE.
APPEND lv_search_parser_gruppe_2 TO rt_secundary_tab.
CLEAR lv_search_parser_gruppe_2.
ENDIF.
ENDIF.

IF lv_search_parser_gruppe IS NOT INITIAL.
FIND FIRST OCCURRENCE OF  lv_search_parser_gruppe IN  lv_text_line+lv_line MATCH OFFSET lv_line_1 MATCH LENGTH lv_line_2.
IF sysubrc AND lv_line_1 0.
lv_line lv_line + lv_line_2.
TRANSLATE lv_search_parser_gruppe  TO LOWER CASE.
APPEND lv_search_parser_gruppe TO rt_secundary_tab.
CLEAR lv_search_parser_gruppe.
ENDIF.
ENDIF.

*************************************************************************************************************
FIND FIRST OCCURRENCE OF REGEX ‚[<]{1}[\w]{1,}[>]{1}‘ IN lv_text_line+lv_line MATCH OFFSET lv_line_1 MATCH LENGTH lv_line_2.

IF sysubrc 0.

lv_line_g1 lv_line_1 + + lv_line.
lv_line_g2   lv_line_2 – 2.
lv_search_parser lv_text_line+lv_line_g1(lv_line_g2.
****************************************************** FeldGruppe
FIND REGEX ‚[a-z]{1,}‘ IN lv_search_parser.
IF sysubrc .
lv_line_1 lv_line_1 + lv_line.
lv_search_parser lv_text_line+lv_line_1(lv_line_2.
IF lv_search_parser_gruppe IS INITIAL.
lv_search_parser_gruppe lv_search_parser.
SHIFT lv_search_parser_gruppe+1 RIGHT BY PLACES.
lv_search_parser_gruppe+1(1=   ‚/‘.
ELSEIF lv_search_parser_gruppe_2 IS INITIAL.
lv_search_parser_gruppe_2 lv_search_parser.
SHIFT lv_search_parser_gruppe_2+1 RIGHT BY PLACES.
lv_search_parser_gruppe_2+1(1=   ‚/‘.
ELSEIF lv_search_parser_gruppe_3 IS INITIAL.
lv_search_parser_gruppe_3 lv_search_parser.
SHIFT lv_search_parser_gruppe_3+1 RIGHT BY PLACES.
lv_search_parser_gruppe_3+1(1=   ‚/‘.
ELSEIF lv_search_parser_gruppe_4 IS INITIAL.
lv_search_parser_gruppe_4 lv_search_parser.
SHIFT lv_search_parser_gruppe_4+1 RIGHT BY PLACES.
lv_search_parser_gruppe_4+1(1=   ‚/‘.
ELSEIF lv_search_parser_gruppe_5 IS INITIAL.
lv_search_parser_gruppe_5 lv_search_parser.
SHIFT lv_search_parser_gruppe_5+1 RIGHT BY PLACES.
lv_search_parser_gruppe_5+1(1=   ‚/‘.
ELSEIF lv_search_parser_gruppe_6 IS INITIAL.
lv_search_parser_gruppe_6 lv_search_parser.
SHIFT lv_search_parser_gruppe_6+1 RIGHT BY PLACES.
lv_search_parser_gruppe_6+1(1=   ‚/‘.
ENDIF.
lv_search_parser lv_text_line+lv_line_1(lv_line_2.
TRANSLATE lv_search_parser  TO LOWER CASE.
APPEND  lv_search_parser TO rt_secundary_tab.
CLEAR  lv_search_parser.
lv_line lv_line_1 + lv_line_2.
CONTINUE.
ENDIF.
*******************************************************
lv_line_1 lv_line_1 + lv_line.
lv_search_parser lv_text_line+lv_line_1(lv_line_2.
SHIFT lv_search_parser+1 RIGHT BY PLACES.
lv_search_parser+1(1=   ‚/‘.
FIND FIRST OCCURRENCE OF lv_search_parser   IN lv_text_line+lv_line  MATCH OFFSET lv_line_3 MATCH LENGTH lv_line_4.
IF sysubrc 0.
TRANSLATE lv_text_line+lv_line_1(lv_line_2)  TO LOWER CASE.
lv_line_3 lv_line_3 + lv_line.
TRANSLATE lv_text_line+lv_line_3(lv_line_4)  TO LOWER CASE.
lv_line lv_line_3 + lv_line_4.
lv_line_2 lv_line_3 + lv_line_4 – lv_line_1.
lv_text_line_buffer lv_text_line+lv_line_1(lv_line_2).
APPEND lv_text_line_buffer TO rt_secundary_tab.
CLEAR lv_text_line_buffer.
ELSE.
* Fehlt second part
lv_text_line_buffer  lv_text_line+lv_line.
EXIT.
ENDIF.

ELSE.
* Fehlt first part
lv_text_line_buffer  lv_text_line+lv_line.
EXIT.
ENDIF.
IF    lv_line GE 1024.
CLEAR lv_text_line_buffer.
EXIT.
ENDIF.
ENDDO.

* the final step

lv_rc =  lv_rc – 1.
IF  lv_rc 0.

lv_text_line_buffer  ‚</xml>‘.
APPEND  lv_text_line_buffer TO rt_secundary_tab.
ENDIF.

ENDLOOP.

ENDMETHOD.

 METHOD constructor.
DATAlv_rval     TYPE sysubrc,
lr_node_new TYPE REF TO if_ixml_node,
lr_node     TYPE REF TO if_ixml_node.
CREATE OBJECT gr_xml_doc.

CALL METHOD gr_xml_doc->create_with_datadataobject it_main_data[] ).

TRY.
lr_node gr_xml_doc->m_document->get_root).
CATCH cx_sy_ref_is_initial.“ INTO DATA(lr_cx) .
RAISE EXCEPTION TYPE zcx_berk_exception.
ENDTRY.

TRY.
gr_node_inp =  lr_node->get_first_child).
CATCH cx_sy_ref_is_initial.
RAISE EXCEPTION TYPE zcx_berk_exception.
ENDTRY.

CALL METHOD gr_node_inp->set_name
EXPORTING
name iv_header
RECEIVING
rval lv_rval.
IF lv_rval NE 0.
RAISE EXCEPTION TYPE zcx_berk_exception.
ENDIF.
TRY.
lr_node_new gr_node_inp->get_first_child).
CATCH cx_sy_ref_is_initial.
RAISE EXCEPTION TYPE zcx_berk_exception.
ENDTRY.
CHECK  lr_node_new IS BOUND.
CALL METHOD lr_node_new->set_name
EXPORTING
name iv_header_line
RECEIVING
rval lv_rval.
CHECK lv_rval NE 0.
RAISE EXCEPTION TYPE zcx_berk_exception.
ENDMETHOD.

 METHOD insert_tab2document.
DATA:
lr_xml_buffer   TYPE REF TO cl_xml_document,
lv_str          TYPE  string,
lr_node_new_1   TYPE REF TO if_ixml_node,
lr_node_item    TYPE REF TO if_ixml_node,
lv_line         TYPE syindex  VALUE 0,
lr_subnode_list TYPE REF TO if_ixml_node_list,
lv_rc           TYPE sysubrc.

CREATE OBJECT lr_xml_buffer.

CALL METHOD lr_xml_buffer->create_with_datadataobject it_secundary_tab[] ).
CALL METHOD gr_xml_doc->insert_document_as_child
EXPORTING
node     gr_node_inp
document lr_xml_buffer
RECEIVING
retcode  lv_rc.

TRY.
lr_node_new_1 gr_node_inp->get_last_child).
CATCH cx_sy_ref_is_initial.“ INTO DATA(lr_cx) .
RAISE EXCEPTION TYPE zcx_berk_exception.
ENDTRY.

CALL METHOD lr_node_new_1->set_name
EXPORTING
name iv_label_header
RECEIVING
rval lv_rc.

IF lv_rc NE 0.
RAISE EXCEPTION TYPE zcx_berk_exception.
ENDIF.

lr_subnode_list =  lr_node_new_1->get_children).

LOOP AT it_secundary_tab ASSIGNING FIELDSYMBOL(<ls_sub_line>).
lr_node_item lr_subnode_list->get_itemindex  =   lv_line  ).

CALL METHOD lr_node_item->set_name
EXPORTING
name iv_label_item
RECEIVING
rval lv_rc.

IF lv_rc NE 0.
RAISE EXCEPTION TYPE zcx_berk_exception.
ENDIF.
ADD TO   lv_line.
ENDLOOP.

FREE  lr_xml_buffer.
ENDMETHOD.

  METHOD render_2_table.
DATAlv_size            TYPE i.
CALL METHOD gr_xml_doc->render_2_table
IMPORTING
table rt_xml_tab
size  lv_size.

ENDMETHOD.

  METHOD display.
CALL METHOD gr_xml_doc->display).
ENDMETHOD.

 METHOD insert_tab_as_achild_2node.
DATA:
lr_xml_buffer        TYPE REF TO cl_xml_document,
lr_node_new_1        TYPE REF TO if_ixml_node,
lr_node_new_2        TYPE REF TO if_ixml_node,
lr_node_item         TYPE REF TO if_ixml_node,
lv_line              TYPE syindex  VALUE 0,
lr_subnode_list      TYPE REF TO if_ixml_node_list,
lr_subnode_list_last TYPE REF TO if_ixml_node_list,
lv_node_name         TYPE string,
lv_rc                TYPE sysubrc.

CREATE OBJECT lr_xml_buffer.

CALL METHOD lr_xml_buffer->create_with_datadataobject it_secundary_tab[] ).

TRY.
lr_node_new_1 gr_node_inp->get_last_child).
CATCH cx_sy_ref_is_initial.“ INTO DATA(lr_cx) .
RAISE EXCEPTION TYPE zcx_berk_exception.
ENDTRY.

* for later remove
CALL METHOD lr_node_new_1->get_name
RECEIVING
rval lv_node_name.

lr_subnode_list =  lr_node_new_1->get_children).

TRY.
lr_node_item lr_subnode_list->get_itemindex  =   iv_line_idx  ).
CATCH cx_sy_ref_is_initial.“ INTO lr_cx .
RAISE EXCEPTION TYPE zcx_berk_exception.
ENDTRY.

CALL METHOD gr_xml_doc->insert_document_as_child
EXPORTING
node     lr_node_item
document lr_xml_buffer
RECEIVING
retcode  lv_rc.

TRY.
lr_node_new_2 lr_node_item->get_last_child).
CATCH cx_sy_ref_is_initial„INTO lr_cx .
RAISE EXCEPTION TYPE zcx_berk_exception.
ENDTRY.

CHECK   lr_node_new_2 IS BOUND.
*  for later remove
CALL METHOD lr_node_new_2->get_name
RECEIVING
rval lv_node_name.

CALL METHOD lr_node_new_2->set_name
EXPORTING
name iv_label_header
RECEIVING
rval lv_rc.

lr_subnode_list_last =  lr_node_new_2->get_children).
LOOP AT it_secundary_tab ASSIGNING FIELDSYMBOL(<ls_sub_line>).
lr_node_item lr_subnode_list_last->get_itemindex  =   lv_line  ).
CALL METHOD lr_node_item->set_name
EXPORTING
name iv_label_item
RECEIVING
rval lv_rc.
ADD TO   lv_line.
ENDLOOP.
IF lv_rc NE 0.
RAISE EXCEPTION TYPE zcx_berk_exception.
ENDIF.
FREE  lr_xml_buffer.
ENDMETHOD.

Feldkatalog aus einem internen Typ lesen und leere Felder mit dem definierten Zeichen vorgelegen.

  METHOD prapare_table4xml.
DATA:
lv_abap_typekind TYPE   abap_typekind,
lr_type          TYPE REF TO cl_abap_typedescr,
lr_struct        TYPE REF TO cl_abap_structdescr,
lt_comp          TYPE abap_component_view_tab.

FIELD-SYMBOLS <fs> TYPE any.

*
**   Get fields of structure
lr_type cl_abap_structdescr=>describe_by_namep_name =   iv_typname ).

TRY.
lr_struct ?= lr_type.
lt_comp lr_struct->get_included_view).
CATCH cx_sy_move_cast_error.
RETURN.
ENDTRY.

LOOP AT ct_tab ASSIGNING FIELDSYMBOL(<fs_line>).
LOOP AT lt_comp ASSIGNING FIELDSYMBOL(<fs_field>).
ASSIGN COMPONENT <fs_field>name OF STRUCTURE <fs_line> TO <fs>.
CHECK sysubrc AND  <fs> IS INITIAL.
lv_abap_typekind cl_abap_elemdescr=>get_data_type_kindp_data <fs> ).
CHECK sysubrc AND lv_abap_typekind NE ‚N‘ AND lv_abap_typekind NE ‚I‘ AND lv_abap_typekind NE ‚P‘  .
<fs> iv_overlay.
ENDLOOP.
ENDLOOP.

ENDMETHOD.

Definition:

   CLASS-METHODS  prapare_table4xml
IMPORTING iv_overlay TYPE char2
iv_typname TYPE  slis_tabname
CHANGING  ct_tab     TYPE ANY TABLE.

Aufruf:

     CALL METHOD prapare_table4xml
EXPORTING
iv_overlay ‚~‘
iv_typname ‚TYS_DAMAGE_OBJECT‘
CHANGING
ct_tab     gt_demage_obj.

Constructor Call BRF+ Function

Verwendungszweck-Parser mit regulären Ausdrücken

Alexander Tcherniak, Freitag, 7. Februar 2014, 0 Kommentare, Tags: FS-CD | RegEx

Zur Verarbeitung von Zahlungseingängen und zur korrekten Zuordnung zu offenen Posten, Versicherungsobjekten, Vertragskonten bzw. Geschäftspartnern ist es erforderlich, die von der Bank per Kontoauszug übermittelten Verwendungszwecke zu parsen. FS-CD bietet hier standardmäßig nur die in SAP übliche Mustererkennung mit den Wildcards + (genau ein beliebiges Zeichen) oder * (beliebig viele Zeichen) an. In den meisten Fällen wird dies sicherlich ausreichen, insbesondere wenn Einzahlern standardisierte Verwendungszwecke vorgegeben werden können. Es kann jedoch Szenarios geben, in denen die Verwendungszwecke nicht standardisiert werden können, z. B. im B2B-Bereich, wenn von den Einzahlern verschiedene Abrechnungssysteme mit unterschiedlich aufgebauten Verwendungszwecken eingesetzt werden. In einem solchen Szenario stößt man schnell an die Grenzen des SAP-Standards, und eine hartcodierte ABAP-Lösung ist von vorneherein zum Scheitern verurteilt, da sie viel zu unflexibel ist. In einem solchen Szenario bieten sich reguläre Ausdrücke zur Mustererkennung an. Eine Einführung in reguläre Ausdrücke bietet der verlinkte Wikipedia-Artikel.

Lösungsansatz

Der Lösungsansatz, den ich hier beschreiben werde, basiert darauf, dass die Struktur des Verwendungszwecks (welche Elemente sind in welcher Reihenfolge enthalten), mit einem regulären Ausdruck erkannt wird. In einem zweiten Schritt werden diese Elemente dann mit Hilfe der Submatches aus dem Verwendungszweck extrahiert.

Beispiele für Verwendungszwecke:
0212647563/274635
KONTO 021.1426342 RE 685634

Die Kontonummern haben für dieses Beispiel stets den Präfix 021, gefolgt von sieben Stellen. Die Rechnungsnummern sind sechsstellig. Ein regulärer Ausdruck könnte so aussehen:

(021\.?)(\d{7})(\D*)(\d{6})

Bedeutet: Ein Treffer beginnt mit 021, optional gefolgt von einem Punkt. Danach eine siebenstellige Nummer. Als nächstes ein beliebig langer Abschnitt (auch leer), der keine Ziffern enthalten darf. Danach eine sechsstellige Nummer. Die vier in Klammern gesetzten Blöcke bewirken, dass bei einem Treffer diese vier Blöcke als Submatches zurückgegeben werden (ABAP-Befehl find first occurrence of regex … in … results …). Aus diesen vier Submatches lassen sich nun die Selektionswerte für die Zahlstapelposition bilden:

Beispiele:
Versicherungsobjekt-ID (Selektionstyp V): ABC-(2)
Referenzbelegnummer (Selektionstyp X): (4)

Die in Klammern gesetzten Ziffern werden dabei durch das jeweilige Submatch ersetzt und die Ergebnisse in die Zahlstapelposition geschrieben. Das ganze passiert im Zeitpunkt 0950 (Zahlungsstapelübernahme: Selektion ergänzen).

Für die beiden o. g. Verwendungszwecke ergeben sich damit folgende Selektionswerte:
V = ABC-2647563, X = 274635
V = ABC-1426342, X = 685634

Welche Elemente werden für die Umsetzung benötigt? Offensichtlich einen Funktionsbaustein für den Zeitpunkt 0950, und weiterhin eine Customizing-Tabelle, in der die Regeln abgelegt werden. Um den Funktionsbaustein nicht zu umfangreich werden zu lassen, lagere ich den eigentlichen Parser in eine separate Klasse aus.

Die Customizing-Tabelle ZCDRULES

Der Schlüssel dieser Tabelle wird neben dem Mandant die Regel-ID. Jede Regel bekommt somit einen eindeutigen Schlüssel, der beliebig vergeben werden kann. Weiterhin bekommt jede Regel eine Priorität, die die Reihenfolge der Ausführung festlegt.

Da wir von FS-CD im Zeitpunkt 0950 auch den Buchungskreis und das Bankverrechnungskonto übergeben bekommen, bietet es sich für Spezialfälle an, eine Möglichkeit vorzusehen, um die Ausführung bestimmter Regeln auf einzelne Buchungskreise bzw. Bankverrechnungskonten einzuschränken.

Weitere Attribute der Tabelle sind der reguläre Ausdruck, der das Muster des Verwendungszwecks beschreibt, sowie für jeden zu findenden Selektionstyp einen Ausdruck, der beschreibt, wie der Wert aus den Submatches zusammengesetzt wird.

Zusätzlich bekommt die Tabelle noch drei Flags, „Akonto buchen“, „Klärfall erzeugen“ und „Aktiv“. Die ersten beiden Flags werden direkt in die Zahlstapelposition geschrieben, wenn sie gesetzt sind, und erzeugen somit sofort eine Akontobuchung oder einen Klärfall, ohne dass die Position zuvor in die Verrechnungssteuerung geschickt wird. Mit dem dritten Flag kann eine Regel einfach deaktiviert werden, ohne die Regel gleich löschen zu müssen.

Zur Dokumentation der Regel gibt es noch ein Attribut, in dem Anmerkungen erfasst werden können.

Hier der komplette Aufbau der Tabelle:

Attribut Key Datentyp Beschreibung
CLIENT X CLNT Mandant
RULE_ID X CHAR 10 Regel-ID
PATTERN CHAR 200 Muster für Verwendungszweck (RegExp)
PRIORITY NUMC 3 Priorität der Regel
COMPANY_CODE CHAR 4 Buchungskreis (auch * möglich)
BANK_ACCOUNT CHAR 10 Bankverrechnungskonto (auch * möglich)
INSOBJ_ID CHAR 30 Muster für Versicherungsobjekt (mit (n)-Parametern)
INVOICE_ID CHAR 30 Muster für Referenzbelegnummer (mit (n)-Parametern)
ON_ACCOUNT CHAR 1 Akonto-Flag setzen
CLEARING CHAR 1 Klärfall-Flag setzen
ACTIVE CHAR 1 Kennzeichen, ob die Regel aktiv ist
REMARK CHAR 50 Anmerkung

Die Parser-Klasse ZCL_CD_PARSER

Die Klasse besteht im Wesentlichen aus der Methode PARSE, die für einen übergebenen Verwendungszweck die anzuwendende Regel bestimmt und die gewünschten Elemente zurückliefert. Dazu verwendet die Methode die lokale Hilfsklasse CL_RULE, die eine einzelne Regel repräsentiert. Die Parser-Klasse erzeugt in ihrem Konstruktor für jede Regel ein Objekt von CL_RULE und übergibt ihm den zugehörigen Eintrag aus ZCDRULES.

Die Hilfsklasse hat neben dem Konstruktor die beiden funktionalen Methode APPLIES_TO und GET_RESULT.

Die Methode APPLIES_TO prüft zunächst, ob die Regel angewendet werden darf (Buchungskreis und Bankverrechnungskonto) und wertet dann den regulären Ausdruck aus. Falls erfolgreich, sichert sie noch die Submatches und gibt ABAP_TRUE zurück, ansonsten ABAP_FALSE.

Die Methode GET_RESULT legt eine Kopie des Eintrags aus ZCDRULES an und ersetzt die (n)-Parameter mit den konkreten Submatches.

Hier zunächst der vollständige Quelltext der Hilfsklasse CL_RULE:

*"* use this source file for the definition and implementation of
*"* local helper classes, interface definitions and type
*"* declarations
*----------------------------------------------------------------------*
*       CLASS CL_RULE DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class CL_RULE definition final.
  public section.
    methods CONSTRUCTOR
      importing
        RULE_DATA type ZCDRULES.
    methods APPLIES_TO
      importing
        COMPANY_CODE type ZCDRULES-COMPANY_CODE
        BANK_ACCOUNT type ZCDRULES-BANK_ACCOUNT
        REFERENCE type CLIKE
      returning
        VALUE(RESULT) type ABAP_BOOL.
    methods GET_RESULT
      returning
        VALUE(RESULT) type ref to ZCDRULES.
  private section.
    data:
      RULE_DATA  type          ZCDRULES,
      SUBMATCHES type table of STRING.
endclass.                    "CL_RULE DEFINITION
*----------------------------------------------------------------------*
*       CLASS CL_RULE IMPLEMENTATION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class CL_RULE implementation.
  method CONSTRUCTOR.
    ME->RULE_DATA = RULE_DATA.
  endmethod.                    "CONSTRUCTOR
  method APPLIES_TO.
    data: MATCH_RESULT    type        MATCH_RESULT,
          SUBMATCH_RESULT type ref to SUBMATCH_RESULT,
          SUBMATCH        type        STRING.
    clear SUBMATCHES.
    if RULE_DATA-COMPANY_CODE <> '*' and RULE_DATA-COMPANY_CODE <> COMPANY_CODE.
      return.
    endif.
    if RULE_DATA-BANK_ACCOUNT <> '*' and RULE_DATA-BANK_ACCOUNT <> BANK_ACCOUNT.
      return.
    endif.
    find first occurrence of regex RULE_DATA-PATTERN
         in REFERENCE
         results MATCH_RESULT.
    if SY-SUBRC <> 0.
      return.
    endif.
    loop at MATCH_RESULT-SUBMATCHES reference into SUBMATCH_RESULT.
      if SUBMATCH_RESULT->LENGTH > 0.
        SUBMATCH = REFERENCE+SUBMATCH_RESULT->OFFSET(SUBMATCH_RESULT->LENGTH).
        insert SUBMATCH into table SUBMATCHES.
      else.
        insert initial line into table SUBMATCHES.
      endif.
    endloop.
    RESULT = ABAP_TRUE.
  endmethod.                    "APPLIES_TO
  method GET_RESULT.
    data SUBMATCH type ref to STRING.
    create data RESULT.
    RESULT->* = RULE_DATA.
    loop at SUBMATCHES reference into SUBMATCH.
      replace all occurrences of |({ SY-TABIX })| in RESULT->INSOBJ_ID    with SUBMATCH->*.
      replace all occurrences of |({ SY-TABIX })| in RESULT->INVOICE_ID   with SUBMATCH->*.
    endloop.
  endmethod.                    "GET_RESULT
endclass.                    "CL_RULE IMPLEMENTATION

In der Methode PARSE der Parser-Klasse ZCL_CD_PARSER passiert dann eigentlich nicht mehr viel. Die Methode führt eine Schleife über alle Regel-Objekte durch und ruft für jedes Regel-Objekt die Methode APPLIES_TO auf. Falls diese ABAP_TRUE zurückliefert, ruft die Methode PARSE die Methode GET_RESULT auf und gibt das Ergebnis (die angewendete Regel mit den aufgelösten Elementen) an den Aufrufer zurück. Falls keine Regel angewendet werden konnte, gibt die Methode eine leere Regel mit gesetztem Klärfall-Flag zurück. Wichtig ist noch, dass der Konstruktur der Parser-Klasse die Regeln nach Priorität und Regel-ID vorsortiert, damit später die Regeln in der richtigen und immer gleichen Reihenfolge durchlaufen werden.

Hier nun der vollständige Quelltext der Parser-Klasse:

class ZCL_CD_PARSER definition 
  public
  final .
public section.
*"* public components of class ZCL_CD_PARSER
*"* do not include other source files here!!!
  types:
    RULES_TAB type table of ZCDRULES with default key .
  methods CONSTRUCTOR .
  methods PARSE
    importing
      !COMPANY_CODE type ZCDRULES-COMPANY_CODE
      !BANK_ACCOUNT type ZCDRULES-BANK_ACCOUNT
      !REFERENCE type CLIKE
    returning
      value(RESULT) type ref to ZCDRULES
    raising
      CX_SY_REGEX .
  protected section.
*"* protected components of class ZCL_CD_PARSER
*"* do not include other source files here!!!
private section.
*"* private components of class ZCL_CD_PARSER
*"* do not include other source files here!!!
  data:
    RULES type table of ref to CL_RULE
            with non-unique default key .
ENDCLASS.
CLASS ZCL_CD_PARSER IMPLEMENTATION.
* <signature>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_CD_PARSER->CONSTRUCTOR
* +-------------------------------------------------------------------------------------------------+
* +--------------------------------------------------------------------------------------</signature>
  method CONSTRUCTOR.
    data: RULE_DATA_TAB type table of ZCDRULES,
          RULE_DATA     type ref to   ZCDRULES,
          RULE          type ref to   CL_RULE.
    select *
      from ZCDRULES
      into table RULE_DATA_TAB
      where ACTIVE = ABAP_TRUE.
    sort RULE_DATA_TAB by PRIORITY RULE_ID.
    loop at RULE_DATA_TAB reference into RULE_DATA .
      create object RULE
        exporting
          RULE_DATA = RULE_DATA->*.
      insert RULE into table RULES.
    endloop.
  endmethod.                    "CONSTRUCTOR
* <signature>---------------------------------------------------------------------------------------+
* | Instance Public Method ZCL_CD_PARSER->PARSE
* +-------------------------------------------------------------------------------------------------+
* | [--->] COMPANY_CODE                   TYPE        ZCDRULES-COMPANY_CODE
* | [--->] BANK_ACCOUNT                   TYPE        ZCDRULES-BANK_ACCOUNT
* | [--->] REFERENCE                      TYPE        CLIKE
* | [<-()] RESULT                         TYPE REF TO ZCDRULES
* | [!CX!] CX_SY_REGEX
* +--------------------------------------------------------------------------------------</SIGNATURE>
  method PARSE.
    data RULE type ref to CL_RULE.
    loop at RULES into RULE.
      check RULE->APPLIES_TO( COMPANY_CODE = COMPANY_CODE
                              BANK_ACCOUNT = BANK_ACCOUNT
                              REFERENCE    = REFERENCE ) = ABAP_TRUE.
      RESULT = RULE->GET_RESULT( ).
*** hier ggf. weitere Prüfungen einbauen ***
      return.
    endloop.
    create data RESULT.
    RESULT->CLEARING = ABAP_TRUE.
  endmethod.                    "PARSE
ENDCLASS.

Der Funktionsbaustein Z_CD_EVENT_0950

Dieser Funktionsbaustein, der in der Transaktion FQEVENTS im Zeitpunkt 0950 eingetragen wird, stellt das Bindeglied zwischen FS-CD und dem Parser dar. Ein wichtiges Detail ist noch, dass der Funktionsbaustein die Regel-ID der angewendeten Regel im Attribut INFOF in der Zahlstapelposition einträgt. Dies hilft ungemein bei der Analyse von Klärfällen, da sofort erkannt werden kann, ob und welche Regel angewendet wurde. Hier eine mögliche Implementierung des Funktionsbausteins:

function Z_CD_EVENT_0950.
*"----------------------------------------------------------------------
*"*"Lokale Schnittstelle:
*"  IMPORTING
*"     REFERENCE(I_FKKZK) LIKE  DFKKZK STRUCTURE  DFKKZK
*"  TABLES
*"      T_FKKZP STRUCTURE  DFKKZP
*"      T_FKKZS STRUCTURE  DFKKZS
*"      T_FKKZV STRUCTURE  DFKKZV OPTIONAL
*"----------------------------------------------------------------------
  data: PARSER    type ref to ZCL_CD_PARSER,
        POSITION  type ref to DFKKZP,
        REFERENCE type        STRING,
        EXT_REF   type ref to DFKKZV,
        RESULT    type ref to ZCDRULES.
  create object PARSER.
  loop at T_FKKZP reference into POSITION.
    REFERENCE = TO_UPPER( POSITION->TXTVW ).
    loop at T_FKKZV reference into EXT_REF where KEYZ1 = POSITION->KEYZ1 and UPOSV = POSITION->UPOSV. "#EC CI_NESTED
      REFERENCE = REFERENCE && TO_UPPER( EXT_REF->TXTVW ).
    endloop.
    RESULT = PARSER->PARSE(
        COMPANY_CODE = POSITION->BUKRS
        BANK_ACCOUNT = POSITION->BVRKO
        REFERENCE    = REFERENCE ).
    if not RESULT->INSOBJ_ID is initial.
      POSITION->SELW2 = RESULT->INSOBJ_ID.
      POSITION->SELT2 = 'V'.
    endif.
    if not RESULT->INVOICE_ID is initial.
      POSITION->SELW3 = RESULT->INVOICE_ID.
      POSITION->SELT3 = 'X'.
    endif.
    POSITION->XAKON = RESULT->ON_ACCOUNT.
    POSITION->XKLAE = RESULT->CLEARING.
    POSITION->INFOF = RESULT->RULE_ID.
  endloop.
endfunction.

Zusammenfassung

Der hier vorgestellte Lösungsansatz stellt ein Grundgerüst dar, das für die unterschiedlichsten Anforderungen erweiterbar ist. Sofern in den im Verwendungszweck übermittelten Nummern Prüfziffern enthalten sind, könnten diese überprüft werden, und falls die Prüfziffer ungültig ist, gleich das Klärfall-Flag gesetzt werden (siehe markierte Stelle in der Methode PARSE).