
ABAP Cloud - Change Documents
Let's take a look at how we can actually create change documents for our tables in the ABAP Cloud environment and what process we need to follow. To do this, we'll extend our RAP application.
Table of contents
In this article, we'll look at how to use change documents in ABAP Cloud and thus log all changes to our RAP object in the system.
Introduction
Change documents are a central component of the standard when it comes to the traceability of changes in the system. Auditors often require this in important applications, especially when it comes to accounting-related content that is relevant for an annual audit. In this article, we'll extend our Sales App and log important content within the application.
Change Object
Let's first create a change document. To do this, right-click on the package in the Project Explorer and select "New -> Other ABAP Repository Object". There, we search for our object to create.
Here, we assign a name and a description and create the object. In the object, we now need to include the tables/entities for which logging should occur. In this case, we use our Sales table and the Sellers, which we want to log later. At the object level, we want to log all field changes, as long as they are not initial values. Once we have configured the object, we activate it, and the class (ZCL_ZBS_CO_SALES_CHDO) is generated for us in the background.
Various settings can be made in the change object if you adopt the individual tables and structures.
- Log Multiple Changes - Parameters are also created when the API is generated to pass a table of changes. Thus, the WRITE method only needs to be called once to write all changes in a change document.
- Database Insertions - If the flags are not set, only a general entry (KEY) is written when inserting, and not the specific data content.
- Database Deletions - If the flags are not set, only a general entry (KEY) is written when deleting, and not the specific data content.
Fields
To log changes to specific fields, we first need to set the settings on these fields. For example, we go to the ZBS_SASALE table and navigate to the ZBS_DEMO_SA_DATE data element. During the initial setup, we focused primarily on data elements, as this is where we find the setting for logging (Change Document Logging). To do this, expand the "Additional Properties" section.
We now activate logging in the following fields of the two tables:
- ZBS_SASALE - Partner (PARTNERNUMBER), Sales Date (SALESDATE), Sales Volume (SALESVOLUME)
- ZBS_SASELLER - Seller (SELLERID), Quota (QUOTA)
With these settings, not all fields of the two tables will be logged, but only fields with the corresponding settings in the data element. It's not always necessary to record the entire table, but only fields defined as important.
Logging
In this chapter, let's look at how we can write changes to the log.
Generated Class
Now that we have prepared our data model, we can try logging the changes. For this, we use the class ZCL_ZBS_CO_SALES_CHDO, which was generated by the system as a standard implementation. Let's take a closer look at the class. We find a static method WRITE, which we can use to persist the changes.
The method provides various settings that we need to populate when writing, such as a key, date and time, and user. Furthermore, we can find the structures defined in the change document. There is an Old (O) and New (N) transfer for each, as well as the actual change indicator (create, modify, or delete).
Data
Now we need appropriate test data to perform a change. In this case, we create a synthetic dataset, transfer the information to the new record, and adjust some of its fields. In real-world scenarios, we receive the changes in our logic or are in the middle of processing them with RAP.
DATA old_sale TYPE zbs_sasale.
DATA new_sale TYPE zbs_sasale.
DATA object_id TYPE if_chdo_object_tools_rel=>ty_cdobjectv.
old_sale = VALUE #( client = sy-mandt
uuid = xco_cp=>uuid( )->value
partnernumber = '1000000000'
SalesDate = '20260101'
SalesVolume = '23000'
SalesCurrency = 'EUR'
SaleComment = `New year, new sales`
DifferenceAmount = '15000'
DifferenceCurrency = 'EUR' ).
new_sale = old_sale.
new_sale-SalesDate = '20260215'.
new_sale-SalesVolume = '22500'.
We also generate an object ID, which we can pass to the method. In many cases, this is the unique key from the database. Since the client is also part of the key, we concatenate all the information.
object_id = old_sale-client && old_sale-uuid.
Save
To complete the processing, we call the WRITE method with our data. We pass the object ID and some current system parameters. We also populate the current structures and set the update flag. In this case, we use Update (U). Insert (I) and Delete (D) are also possible. Unfortunately, we haven't found suitable standard constants to derive the possible flags or to pass them without hardcoding. You can find more information about the constants in the linked documentation at the end of the article.
zcl_zbs_co_sales_chdo=>write(
EXPORTING objectid = object_id
utime = cl_abap_context_info=>get_system_time( )
udate = cl_abap_context_info=>get_system_date( )
username = CONV #( cl_abap_context_info=>get_user_technical_name( ) )
o_zbs_sasale = old_sale
n_zbs_sasale = new_sale
upd_zbs_sasale = 'U'
IMPORTING changenumber = DATA(changenumber) ).
out->write( changenumber ).
COMMIT WORK.
After the call, we perform a COMMIT WORK and persist the result in the database. We also receive our change number from the system.
Reading
To read the changes from the database, we have various standard classes available, which we will use in this chapter.
Logic
We can implement the actual read logic with the class CL_CHDO_READ_TOOLS. We call this with our object ID, pass the data from our previous attempt, and thus want to read all database changes. In principle, further filters such as date, time, or user are also available to appropriately restrict the result set.
cl_chdo_read_tools=>changedocument_read(
EXPORTING i_objectclass = zcl_zbs_co_sales_chdo=>objectclass
it_objectid = VALUE #( ( sign = 'I' option = 'EQ' low = object_id ) )
IMPORTING et_cdredadd_tab = DATA(db_changes) ).
out->write( db_changes ).
Authorization
However, it can happen that we don't have any authorizations. In this case, we are working on the ABAP environment and don't have authorizations to read the change documents for our own object.
In this case, we first have to assign ourselves authorizations, as we already did with the application logs. However, the standard authorization objects such as S_SCD0 and S_SCD0_OBJ are not enabled, even if they are queried in the existing logic. However, there is also an exit that is called beforehand. The system attempts to execute the interface IF_CHDO_ENHANCEMENTS and the method AUTHORITY_CHECK from our generated class. However, these are not implemented. Therefore, we implement the method in the class ZCL_ZBS_CO_SALES_CHDO to obtain the necessary permissions.
METHOD if_chdo_enhancements~authority_check.
rv_is_authorized = abap_true.
ENDMETHOD.
Basically, you can also implement your own objects and permission checks here, which you then map in your permissions.
Changes
If we then execute the logic again, we should now receive the Ächanges from the database. The table is relatively large, but contains the various field changes, as shown in the screenshot.
Complete Example
You can find the complete example in GitHub in the corresponding package for the Sales App. The changes from this article can be found in this commit, allowing you to track the changes, plus the additional information.
Conclusion
In principle, changes can also be logged manually or via the actual table change, however, the standard method should be used first. This ensures that the standard tools can also be used to evaluate documents and changes, including by auditors.





