Sebastian Wey,

FS-CD RegEx

To process incoming payments and correctly assign them to open items, insurance objects, contract accounts or business partners, you need to parse the payment notes transmitted by the bank by bank statement. FS-CD only offers the standard SAP pattern recognition with the wildcards + (exactly one character) or * (any number of characters). In most cases this will certainly suffice, especially if depositors can be given standardized payment notes. However, there may be scenarios in which the payment notes cannot be standardized, for example, in the B2B area, if the payers use different accounting systems with different payment notes. In such a scenario, you quickly reach the limits of the SAP standard, and a hard-coded ABAP solution is doomed to failure from the outset because it is far too inflexible. In such a scenario, regular expressions are useful for pattern recognition. An introduction to regular expressions can be found in the linked Wikipedia article.

Solution approach

The solution I will describe here is based on the fact that the structure of the payment note (which elements are contained in which order) is recognized with a regular expression. In a second step, these elements are then extracted from the note to payee using the submatches.

Examples for payment notes:
0212647563/274635
KONTO 021.1426342 RE 685634

The account numbers in this example always have the prefix 021, followed by seven digits. The invoice numbers have six digits. A regular expression could look like this:

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

Means: A hit starts with 021, optionally followed by a period. Then a seven-digit number. Next, a section of any length (also empty), which must not contain any digits. Then a six-digit number. The four blocks in parentheses mean that these four blocks are returned as submatches when a hit occurs (ABAP command find first occurrence of regex... in ... results ...). The selection values for the number stack position can now be formed from these four submatches:

Examples:
Insurance Object ID (selection category V): ABC-(2)
Reference Document Number(selection category X): (4)

The digits in brackets are replaced by the respective submatch and the results are written to the payment lot position. This all takes place in event 0950 (Payment lot transfer: Add selection).

This results in the following selection values for the two above-mentioned payment notes:
V = ABC-2647563, X = 274635
V = ABC-1426342, X = 685634

Which elements are required for the implementation? Obviously a function module for event 0950, and still a Customizing table in which the rules are stored. In order not to let the function module become too extensive, I lay out the actual parser in a separate class.

The Customizing table ZCDRULES

The key of this table is the rule ID in addition to the client. This gives each rule a unique key that can be defined as required. Furthermore, each rule has a priority that determines the sequence of execution.

Since FS-CD also transfers the company code and the bank clearing account in event 0950, it is useful for special cases to provide a way of restricting the execution of certain rules to individual company codes or bank clearing accounts.

Other attributes of the table are the regular expression that describes the pattern of the note to payee, and an expression for each selection type to be found that describes how the value is composed of the submatches.

The table also has three additional flags, "Post on account", "Create clarification case" and "Active". The first two flags are written directly to the payment lot item if they are set, and thus immediately generate a posting on account or a clarification case without the item being sent to clearing control beforehand. With the third flag, a rule can be simply deactivated without having to delete the rule immediately.

There is also an attribute for documenting the rule, in which comments can be entered.

Here the complete structure of the table:

AttributeKeyData TypeDescription
CLIENTXCLNTClient
RULE_IDXCHAR 10Rule ID
PATTERNCHAR 200Pattern of payment note (RegExp)
PRIORITYNUMC 3Priority of the rule
COMPANY_CODECHAR 4Company Code (also * possible)
BANK_ACCOUNTCHAR 10Bank Clearing account (also * possible)
INSOBJ_IDCHAR 30Template of Insurance Object ID (with (n) parameters)
INVOICE_IDCHAR 30Template for Reference Document Number (with (n) parameters)
ON_ACCOUNTCHAR 1Set On Account Flag
CLEARINGCHAR 1Set clarification case flag
ACTIVECHAR 1Indicator as to whether the rule is active
REMARKCHAR 50Remark

The Parser class ZCL_CD_PARSER

The class essentially consists of the method PARSE, which determines the rule to be used for a transferred purpose and returns the required elements. The method uses the local auxiliary class CL_RULE, which represents a single rule. The parser class creates an object of CL_RULE in its constructor for each rule and passes the corresponding entry from ZCDRULES to it.

In addition to the constructor, the auxiliary class has the two functional methods APPLIES_TO and GET_RESULT.

The method APPLIES_TO first checks whether the rule can be applied (company code and bank clearing account) and then evaluates the regular expression. If successful, it saves the submatches and returns ABAP_TRUE, otherwise ABAP_FALSE.

The method GET_RESULT creates a copy of the entry from ZCDRULES and replaces the (n) parameters with the concrete submatches.

The complete source code of the auxiliary class 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 the method PARSE of the parser class ZCL_CD_PARSER not much happens anymore. The method loops all rule objects and calls the method APPLIES_TO for each rule object. If this ABAP_TRUE returns, the method PARSE calls the method GET_RESULT and returns the result (the applied rule with the resolved elements) to the caller. If no rule could be applied, the method returns an empty rule with the clarification case flag set. It is also important that the rules are pre-sorted according to priority and rule ID so that the rules are run through later in the correct and always the same order.

Here is the complete source code of the parser class:

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( ).
*** Include further checks here if necessary ***
      return.
    endloop.
    create data RESULT.
    RESULT->CLEARING = ABAP_TRUE.
  endmethod.                    "PARSE
ENDCLASS.

The Function Module Z_CD_EVENT_0950

This function module, which is defined in transaction FQEVENTS in event 0950, represents the link between FS-CD and the parser. Another important detail is that the function module enters the rule ID of the applied rule in the attribute INFOF in the payment lot position. This helps tremendously in the analysis of clarification cases, since it can be recognized immediately whether and which rule was applied. Here is a possible implementation of the function module:

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.

Recap

The solution approach presented here represents a basic framework that can be extended for the most diverse requirements. If the numbers transmitted in the note to payee contain check digits, these could be checked, and if the check digit is invalid, the clarification case flag could be set immediately (see selected position in method PARSE).

Translated with DeepL


Back to list < Fire Protection Tax Dependency Injection with ABAP Objects >