Application Development and Automation Blog Posts articles Application Development and Automation Blog Posts articles
-
Internal Table and Flow Control in AMDP
by Nihal__Raj on October 29, 2025 at 8:37 am
Introduction: AMDP or ABAP Managed Database Procedure, is a SAP technology that allows database specific procedure (like those written in SQL Script for SAP HANA) to be managed and executed directly from ABAP code. The primary aim of AMDP is to optimize performance by pushing complex data operations to the database layer.Important Points:While adding interface IF_AMDP_MARKER_HDB. Remember, interfaces can only be added in PUBLIC SECTION.Only Variables and Tables are allowed as parameters. We cannot use structures or nested tables.Generic types cannot be used for parameters. For example, Type Any cannot be used. Only elementary data types and table types with a structured row type can be used.The table type components must be elementary data types and it cannot have elements which are table types.Only pass by value can be used. Pass by reference is not permitted. Using VALUE keyword for all parameters is required.RETURNING parameters are not allowed. We can use EXPORTING or CHANGING to receive the values.Only input parameters can be flagged as optional with a DEFAULT value (literals/constants)Every AMDP method will have below addition. READ-ONLY is only the optional addition and is used for methods that only read the data.o BY DATABASE PROCEDUREo FOR HDBo LANGUAGE SQLSCRIPTo OPTIONS READ-ONLYo USING table/view namesIt is also mandatory to specify all the database objects and other AMDP methods that are used within the SQLSCRIPT code.No ABAP statements can be written in the method code.AMDP methods do not have any implicit enhancement options.Internal Table and Flow Control in AMDPIn AMDP (ABAP Managed Database Procedures), you can declare internal tables using SQL Script. These internal tables are used to store intermediate results and can be manipulated within your AMDP methods.CLASS zrj_amdp_01 DEFINITION PUBLIC FINAL CREATE PUBLIC . PUBLIC SECTION. TYPES: BEGIN OF ty_spfli, mandt TYPE mandt, carrid TYPE s_carr_id, connid TYPE s_conn_id, countryfr TYPE land1, countryto TYPE land1, END OF ty_spfli. TYPES: tt_spfli TYPE TABLE of ty_spfli. INTERFACES: if_amdp_marker_hdb. METHODS: get_spfli_details IMPORTING VALUE(iv_mandt) TYPE mandt EXPORTING VALUE(et_spfli) TYPE tt_spfli. PROTECTED SECTION. PRIVATE SECTION. ENDCLASS. CLASS zrj_amdp_01 IMPLEMENTATION. METHOD get_spfli_details BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT OPTIONS READ-ONLY. DECLARE lt_spfli TABLE ( mandt "$ABAP.type( sy-mandt )", carrid "$ABAP.type( S_CARR_ID )", connid "$ABAP.type( S_CONN_ID )", countryfr "$ABAP.type( LAND1 )", countryto "$ABAP.type( LAND1 )" ); lt_spfli.mandt[1] := '100'; lt_spfli.carrid[1] := 'AA'; lt_spfli.connid[1] := '1000'; lt_spfli.countryfr[1] := 'DE'; lt_spfli.countryto[1] := 'US'; et_spfli = select * from :lt_spfli; ENDMETHOD. ENDCLASS.Some Useful Internal Table with Operations:RECORD_COUNT( ) Operator is used to get to know the no of lines in the internal table. Soit is likes LINES( ) / DESCRIBE table in ABAP.SEARCH() can be applied on an internal table and it receives two arguments, the columnname and the value, if it finds then it returns the index of that record within the internaltable.EXISTS() tells whether the record exists or not.IS_EMPTY( ) can be used and we can pass the Internal table name as an argument to it andthen it checks the emptiness of the table , it is similar to IS INITIAL check in ABAP.APPLY_FILTER( ) function can be used on internal table or DB tables to get all the items thatmatch the filter condition.EXCEPT use is quite simple. It’s like a minus operation. The result of EXCEPT operation is therecord set that present in first and not present in the second.Flow Control with IF and LoopsUsing IF and ELSEIf syntax is similar to ABAP but uses additional THEN keyword.Condition can have followingEXISTSComparison between variablesIS [NOT] NULLIS_EMPTYINThe conditions can be negated with NOT, combined with keywords like AND, OR and can benested.CLASS zrj_amdp_02 IMPLEMENTATION. METHOD if_statement BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT USING sflight. DECLARE lv_result INT; DECLARE lv_carrier CONSTANT NVARCHAR(2) := 'AA'; DECLARE lv_carrier1 CONSTANT NVARCHAR(2) := 'BB'; ***Example for EXISTS if EXISTS ( select carrid FROM sflight WHERE carrid = '1' ) THEN lv_result = 1; else lv_result = 2; END if; ***comparision between variables if :lv_carrier = :lv_carrier1 THEN lv_result = 1; end if; *** IN if :lv_carrier1 IN( 'AA', 'AB', 'AZ' ) THEN lv_result = 1; end if; ENDMETHOD. ENDCLASS.For LoopFOR <variable> IN [REVERSE] <initial_value>..<final_value> DO <block> END FOR; The variable is assigned an initial value for the first iteration, incremented or, if youspecified REVERSE, decremented with every iteration till final value is reached. Variablesdeclared inside the FOR loop are not visible on the outside.While LoopWHILE <condition> DO <block> END WHILE; Condition block is same as IF statement. BREAK statement can be used to abort the loop.METHOD loop_statement BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT. DECLARE lv_sum INT := 0; DECLARE lv_index INT := 0; *** for loop for lv_index IN 1..10 DO DECLARE lv_tmp int := lv_sum; lv_tmp = lv_tmp + lv_index; lv_sum = lv_tmp; END for; *** while loop lv_index = 0; WHILE lv_index <= 10 do DECLARE lv_tmp INT := lv_sum; lv_tmp = lv_tmp + lv_index; lv_sum = lv_tmp; lv_index = lv_index + 1; END WHILE;While Loop With Break :*** while loop with break lv_index = 0; WHILE lv_index <= 1000 DO lv_index = lv_index + 1; if lv_index = 10 THEN BREAK ; END IF; END WHILE; ENDMETHOD. ENDCLASS.Conclusion: Internal tables in AMDP mirror ABAP's data handling but are managed using SQL Script constructs. Flow control allows procedural operations inside the database to optimize logic execution. Together, these enable high-performance, code-pushed data processing-minimizing data transfer between ABAP and database layer and making AMDP ideal for complex analytical logic.
-
Upload a Document (LOB) to Application using ABAP Restful programming.
by Swasthik3012 on October 21, 2025 at 12:01 pm
IntroductionUploading large files like Excel sheets or images is a frequent requirement in many business applications. Previously, in RAP-based applications, the only way to handle such uploads was by extending the application and using UI5 tooling to support file uploads manually.However, with the release of SAP BTP ABAP environment version 2208, the RAP framework has introduced support for OData streams. This enhancement now allows developers to natively manage and process Large Objects (LOBs) directly within RAP applications. It enables end users to upload various external file types — including PDFs, XLSX files, binary formats, and other media — without custom extensions or additional tooling.In this blog, we’ll walk through how to implement file upload functionality for large objects (like PDFs or binary files) within a standard RAP application, making full use of the new stream handling capabilities — and all without extending the UI manually.
-
New Developer- and SAP-Technology Forums/Groups
by AnnePetteroe on October 3, 2025 at 3:48 pm
Dear "Application Development and Automation" group members/subscribers, In case you haven't seen the general announcement post that went out earlier this week, this blog post is to inform you that we launched a pilot of new developer- and SAP-technology related forums/groups this week.For you, this means that while the pilot runs, the "Application Development and Automation" group is set to read-only. We would like to encourage you to go to our new developer forums/groups, if you would like to ask or answer questions, or publish blog posts, on development/technology-related topics. There are a number of different reasons why we would like to test these new forums/groups: Since our platform migration last year, we have received feedback that you didn't know where to go to find topics like ABAP, Integration or UI5. With these new developer forums/groups, we hope we have made it easier for you to find the technology topics you are most interested in. We noticed that the discussion board in this group was used to ask development related-questions. Generally, everyone has been very helpful answering questions asked here. However, our interest groups are for having conversations with, and connecting to, like-minded people around shared interests. This is why these groups have discussion boards (= for conversations) and blog posts, instead of Q&As/forums. Therefore, when someone asks a question in the "Application Development and Automation" discussion board, it is not possible for the author to accept the right answer as a solution. This again means, that if someone with the same problem comes to the community to get help later on, they will not be able to see what the solution to the problem was.Which brings us to no. 3 below: Because answers to questions in discussion (conversation) boards won't have accepted solutions, we are also not able to give credit to, or celebrate, the members who took the time to help answer questions. With the new developer forums, authors can accept any answer as a correct solution, which means we will be able to bring back our topic expert leaderboards, and give proper credit to our members who answer questions. A new denser forum feed, with no preview of content, making it easier to scroll down the feed to look for the questions you can help answer: New filter for "Questions with no answers". This long-requested filter has now been added to all new Developers forums, so that you can find all questions with. no answers on a specific topic with one click: We can now highlight important product announcements, or other valuable content, you shouldn't miss, around your favourite development topics by using the "Featured Topics" section for the individual topics. Better subscription management. You will now be able to subscribe to either all developer/SAP-technology related content, or drill down to and only subscribe to the specific parts that interest you the most: Developers https://community.sap.com/t5/developers/ct-p/developers - here you can subscribe to the whole category, this subscription sends you notifications on the platform and email notifications according to your account settings. The individual groups, with both the Forums and Blog posts - here taking ABAP Cloud as an example: https://community.sap.com/t5/abap-cloud/gh-p/abap-cloud - here you have the same subscribe option as above. If you subscribe to content here, it will send you notifications on the platform and email notifications according to your account settings. The individual group boards, ABAP Cloud Forum: https://community.sap.com/t5/abap-cloud-forum/bd-p/abap-cloudforum-board - here you have both the option to Subscribe and Subscribe to RSS Feed. The Subscribe option is the same as the two options above, and the RSS Feed is the one you are looking for. You will find the same options for the blog posts boards as well: https://community.sap.com/t5/abap-cloud-blog-posts/bg-p/abap-cloudblog-board If you missed the main announcement post, you can find more information in this What's New blog post. We hope you hop over to the new Developer groups to try them out 🙂 If you have any feedback to us on the forums, you can provide it in the comments section on the What's New post linked above.
-
Enhancing Standard BAPIs: When and How to Do It
by Shantalinga on September 29, 2025 at 7:08 am
Business Application Programming Interfaces (BAPIs) are one of the most powerful tools in SAP, designed to enable seamless integration between SAP and external systems. In most cases, the standard BAPIs provided by SAP are sufficient to cover typical business scenarios.However, real-world requirements often demand more. Maybe you need an extra field that isn’t delivered, or you have to add custom checks before the data is posted. That’s when enhancing a standard BAPI becomes necessary.
-
Foreign entity in RAP
by Shyam4U on September 29, 2025 at 7:05 am
Description: A RAP BO entity which is not part of the BO composition tree for which it is defined. Defining a foreign entity in the behavior definition enables cross-BO message mapping between the two BOs.
-
How to Track Data Changes in RAP with a Change Logs in RAP.
by Gireesh_pg1 on September 29, 2025 at 7:04 am
In the RAP model, a change log table provides an efficient way to track modifications without maintaining full historical data snapshots. Instead of duplicating entire records, the main table continues to store only the current business data, while the change log table captures key details about each modification, such as record identifiers, type of change (insert, update, delete), user information, and optional reasons for the update. These entries are written during the RAP save sequence through determinations in the behavior implementation. This lightweight mechanism creates a reliable audit trail that helps with compliance checks, reporting, and troubleshooting, while significantly reducing storage overhead compared to a shadow table that preserves complete data history.
-
AUnits steps for RAP
by Nihal__Raj on September 26, 2025 at 6:51 am
Introduction:AUnits are unit tests written by ABAP developers to test each functionality of their code. In RAP, AUnits are used to test functionalities such as validations, determinations, actions, and CRUD operations etc. Your AUnits should aim for maximum coverage of your code. It is recommended to follow TDD(Test Driven Development), where you write the test cases first and then implement the logic. This ensures that the test cases are based on the actual requirements and are not influenced by the implementation.Reference link for TDD: https://help.sap.com/docs/abap-cloud/abap-development-tools-user-guide/test-driven-development-with-abap-unitBasics of AUnits:Test classes can be either local or global. Unit tests should typically be implemented as local classes within the program object that is being tested. However, you can use global test classes to host reusable logic for unit tests across different programs. In Eclipse ADT, there is a separate environment for managing local test classes.Reference link: https://learning.sap.com/learning-journeys/acquire-core-abap-skills/implementing-code-tests-with-abap-unit_b23c7a00-c2e8-406d-8969-b00db3f1fd87Structure of test class:*"* use this source file for your ABAP unit test classes class ltc_zi_lfa1_2 DEFINITION FINAL for TESTING RISK LEVEL HARMLESS DURATION SHORT. PRIVATE SECTION. CLASS-DATA environment TYPE REF TO if_cds_test_environment. DATA td_zlfa TYPE STANDARD TABLE OF zlfa1 with empty key. DATA act_results type STANDARD TABLE OF zi_lfa1_2 WITH EMPTY KEY. CLASS-METHODS class_setup RAISING cx_static_check. CLASS-METHODS class_teardown. METHODS setup RAISING cx_static_check. METHODS prepare_testdata. METHODS aunit_for_cds_method FOR TESTING RAISING cx_static_check. ENDCLASS. For Testing: The addition FOR TESTING is used to identify a test class. These are the classes that will be executed when you run ABAPUnit tests. I will explain further how AUnits are triggered and how to execute them.Risk Level: CRITICAL: The test changes system settings or customizing data.DANGEROUS: The test changes persistent data.HARMLESS: The test does not change system settings or persistent data.Duration:SHORT: Execution time is imperceptible, expected to take only a few seconds.MEDIUM: Execution time is noticeable, around a minute.LONG: Execution time is very noticeable, taking more than a minute. Methods in Test Classes:Helper Methods: Helper methods are ordinary methods of the test class. They are not called by the ABAP Unit framework. You can use helper methods to structure the code of your test methods. For example, prepare_testdata is a helper method.Test Methods: Test methods are defined with the addition FORTESTING after the method name. Each test method represents one test. The ABAP Unit framework performs this test by calling the corresponding test method. Test methods must not have any parameters. In the above example, aunit_for_cds_method is your test method.Good Practices:It’s a good practice to have a separate method for each functionality, with no dependencies inside the method. Each method should be treated as a single test case.Always include both positive and negative test cases.Flow of test case:CLASS_SETUPThis static method is executed once before the first test of the test class. Use this method for fixtures that are time-consuming to create and for which you are sure the settings will not be changed by any of the test methods. Example: Setting up a test double in the test environment.SETUPThis instance method is called before each test of the test class. Use this method for fixtures that you want to create fresh for every test case. Example: Creating an instance of the CUT (Class Under Test).TEARDOWNThis instance method is called after each test of the test class. Use it to undo changes made in the SETUP method. It is especially important if SETUP modifies persistent data (such as system configuration, customizing, or master data). Example: Clearing test doubles so that each test starts with fresh data.CLASS_TEARDOWNThis static method is executed once after the last test of the test class. Use it to clean up or destroy the test environment set up in CLASS_SETUP, once all tests are completed. Example: Tearing down the overall test environment.To run ABAP units execute ABAP Unit Test. This will trigger the test class. If you run ABAP application it will not trigger test class.Test double framework:As I mentioned earlier, your unit tests should check only the functionality(CUT – Code Under Test) that you have written the test case for. However, in real-world scenarios, each component is usually interlinked and not standalone.What is the issue?To explain this, let’s take an example. Suppose you are testing an instance authorization method. This method is dependent on an authorization object. If the AUTH-CHECK of the authorization object fails, the instance authorization method will also fail. This means that even if your instance authorization method is working correctly, the test could still fail due to its dependency. Ideally, the failure of a dependency should not cause your unit test to fail when you are testing the functionality of the CUT itself.Examples of dependencies in RAPInstance and global authorizations: Dependency on authorization checks.SAVE / SAVE_MODIFIED methods: Dependency on function modules or BAPIs.Determinations: You might use EML of another BO, making your BO dependent.Validations: Your CDS view could be a dependency; if the required record is not present in the CDS view, the validation might fail. And there are many more such cases.Solution — Test Double FrameworkSo, how do we overcome this? Instead of relying on dependencies that are beyond our control, we mock or stub these dependencies. This allows us to influence how they behave and interact during testing, thereby avoiding test case failures due to external dependencies. This approach is implemented using the Test Double Framework.The main purpose of both mock and stub is to not to test from actual data but rather a fake data that we can configure. Because our test class will fail if its dependent on real time data as it will be different in differentenvironment.STUB: Stubs just provide predefined response.MOCK: You design how the interaction should be. Like if you provide this value this is how the output would be and also verify it.I will now discuss in detail how we write ABAP Unit tests with a simple example. Requirement:Write a positive test case to check the validation check_country, which should throw an exception if we pass a country code with more than2characters. Also, remove the dependency on the CDS view.Steps:Identify CUT and dependenciesHere, I have taken a validation method as an example for testing. The CUT(Code Under Test) is the validation method, and the dependency is the CDS view. So, I will use the STUB methodology to configure the response of the CDS view.Creation of test classOnce you create the test class, you need to add it as a friend in the local handler class (since we are testing a validation). This is necessary because all the methods of the local handler class are inside the private section and cannot be accessed directly.Inside the test classI have already explained the use of SETUP, CLASS_SETUP, TEARDOWN, andCLASS_TEARDOWN earlier, so I will not repeat that here.In this example, validate_check_country is our test method. I have added a test double for the CDS view, because the validation should be tested using the mock data we provide, not the actual data.I create the mock data and insert it, so that the CDS view returns this data. Now, when we pass parameters to the validation, we can check if we get a value in reported, since we have provided a country code with 3 characters.Note: This is just an example program. Typically, mocking, adding mock data, and different functionalities would be implemented in separate methods. It is not a good practice to combine everything into a single method. Each method should have only one responsibility. Here, I have combined them solely to showcase the concept.class ltc_lfa1_validation IMPLEMENTATION. METHOD class_setup. cds_environment = cl_cds_test_environment=>create( i_for_entity = 'zi_lfa1_2' ). ENDMETHOD. METHOD setup. CREATE object cut for testing. cds_encironment->clear_doubles( ). ENDMETHOD. method validate_check_country. data: lfa1_mock_data type STANDARD TABLE OF zlfa1. data(system_uuid) = cl_uuid_factory=>create_system_uuid( ). DATA(uuid_x16) = system_uuid->create_uuid_x16( ). select * FROM zi_lfa1_2 into TABLE @DATA(lt_result). lfa1_ock_data = VALUE#( ( Lifnr = uuid_x16 Name1 = 'Raj' Land1 = 'Ind' ) ) cds_environment->insert_test_data( i_data = lfa1_mock_data ). select * from zi_lfa1_2 into TABLE @DATA(lt_result). i_keys = value #( ( Lifnr = uuid_x16 ) ). cut-> check_country( exporting key = i_keys changing failed = c_failed reported = c_reported ). cl_abap_unit_assert=>assert_not_initial( act = c_reported msg = 'failed' ). ENDMETHOD. method teardown. cds_environment->destroy( ). ENDMETHOD. METHOD class_teardown. cds_environment->clear_doubles( ). ENDMETHOD. ENDCLASS.Conclusion: Writing ABAP unit test in RAP ensures that the underling business logic is robust and reliable. Structured unit test not only improve code quality and reliability but also facilitate easier maintenance and refactoring.
-
How to Track Data Changes in RAP and Change Log Mechanism in RAP.
by Gireesh_pg1 on September 26, 2025 at 6:48 am
hi, A change log table offers a lightweight and efficient way to track data modifications in RAP without maintaining full historical snapshots. Instead of duplicating business data in shadow tables, only essential metadata—such as record keys, change type, user, and timestamp—is logged. This ensures transparency for compliance, reporting, and troubleshooting while keeping storage overhead minimal and system performance optimized.
-
SAP Developer News September 25th, 2025
by Eberenwaobiora on September 25, 2025 at 7:10 pm
This week's episode contains segments on Devtoberfest Begins, TechEd Promo from the TechEd Recording in NSQ on Wednesday, Devtoberfest Fun Activities, Favorites and Recent in SAP BTP cockpit.
-
How to Track Data Changes and Change Log Mechanism in RAP.
by Gireesh_pg1 on September 25, 2025 at 4:41 am
Hi,A change log table is a simple yet effective way to track data modifications without storing the complete historical snapshots of records. While the main table continues to hold only the active business data, the change log table captures essential metadata such as the record identifiers, type of change, user, and reason for the update. In a RAP scenario, determinations during the save sequence record these details before the final commit, ensuring a reliable audit trail. This approach not only supports compliance monitoring, reporting, and troubleshooting but also minimizes storage and performance overhead compared to maintaining full shadow tables.Here we see some major key Advantages. Simplified Design – Only one additional table is needed, making the data model and maintenance simpler. Faster Reporting on Changes – The log contains concise, relevant details, making it easier to query and generate audit reports. Regulatory Compliance – Still fulfills many compliance and audit requirements by showing who changed what and when. Steps to achieving the Track Change Log Mechanism in RAPI have create 2 database tables For basic details Capturing the changes record table @EndUserText.label : 'log chanes' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zgiri_t_emp_det { key id : abap.int8 not null; name : abap.char(30); department : abap.char(30); curr_key : abap.cuky; @Semantics.amount.currencyCode : ' zgiri_t_emp_det.curr_key' salary : abap.curr(15,2); created_by : syuname; created_at : timestampl; changed_by : syuname; changed_at :timestampl; } Table 2. @EndUserText.label : 'log chanes' @AbapCatalog.enhancement.category : #NOT_EXTENSIBLE @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #RESTRICTED define table zgiri_t_ch_log1 { key id : abap.int8 not null; name : abap.char(30); old_value : abap.string(0); new_value : abap.string(0); changed_by : syuname; changed_at : timestampl; } Step2. We have to create root view on top of the database table. @AbapCatalog.viewEnhancementCategory: [#NONE] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'employee details' @Metadata.ignorePropagatedAnnotations: true @ObjectModel.usageType:{ serviceQuality: #X, sizeCategory: #S, dataClass: #MIXED } define view entity zgiri_i_empl1_details as select from ZGIRI_T_emp_det association [0..*] to zgiri_i_ch_log1 as _change on _change.id = $projection.id { key id as Id, name as Name, department as Department, curr_key as CurrKey, @semantics.amount.currencyCode: 'CurrKey' salary as Salary, created_by as CreatedBy, created_at as CreatedAt, changed_by as ChangedBy, changed_at as ChangedAt, _change } Then we have to create data definition for change log also. @AbapCatalog.viewEnhancementCategory: [#NONE] @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'employee details' @Metadata.ignorePropagatedAnnotations: true @ObjectModel.usageType:{ serviceQuality: #X, sizeCategory: #S, dataClass: #MIXED } define view entity zgiri_i_empl1_details as select from zgiri_i_ch_log1 association [1..1] to zgiri_i_emp_det as _emp on _change.id = $projection.id { key id as Id, field_name as Field_name, old_value as OldValue, new_value as NewValue, created_by as CreatedBy, _emp } Step 3. for the basic data definition we have create on metadata extension for front end display. @Metadata.layer:#CORE annotate entity zgiri_i_empl_details with { @UI.facet: [ { id: 'object', label: 'Basic Data', type: #IDENTIFICATION_REFERENCE, purpose: #STANDARD, position: 10 }] @UI.lineItem: [{ label: 'Id' , position: 10 }] @UI.identification: [{ label: 'Id' , position: 10 }] Id; @UI.lineItem: [{ label: 'Name' , position: 20 }] @UI.identification: [{ label: 'Name' , position: 20 }] Name; @UI.lineItem: [{ label: 'Department' , position: 30 }] @UI.identification : [{ label: 'Department' , position: 30 }] Department; @UI.lineItem: [{ label: 'CurrKey' , position: 40 }] @UI.identification : [{ label: 'CurrKey' , position: 40 }] CurrKey; @UI.lineItem: [{ label: 'Salary' , position: 50 }] @UI.identification : [{ label: 'Salary' , position: 50 }] Salary; @UI.lineItem: [{ label: 'CreatedBy' , position: 60 }] CreatedBy; @UI.lineItem: [{ label: 'CreatedAt' , position: 70 }] CreatedAt; @UI.lineItem: [{ label: 'ChangedBy' , position: 80 }] ChangedBy; @UI.lineItem: [{ label: 'ChangedAt' , position: 90 }] ChangedAt; } Step 4. On top of the basic view we have to behavior definition. managed implementation in class zbp_giri_i_empl_details unique; strict ( 2 ); define behavior for zgiri_i_empl_details //alias <alias_name> persistent table zgiri_t_emp_det lock master with additional save authorization master ( instance ) early numbering //etag master <field_name> { create ( authorization : global ); update; delete; field ( readonly ) Id; mapping for zgiri_t_emp_det { ChangedAt = changed_at; ChangedBy = changed_by; CreatedAt = created_at; CreatedBy = created_by; CurrKey = curr_key; Department = department; Id = id; Name = name; Salary = salary; } } On top the consumption view i have create behavior definition for consumption view. projection; strict ( 2 ); define behavior for zgiri_c_empl //alias <alias_name> { use create; use update; use delete; } I'm implementing logic in this class. CLASS lsc_zgiri_i_empl_details DEFINITION INHERITING FROM cl_abap_behavior_saver. PROTECTED SECTION. METHODS save_modified REDEFINITION. ENDCLASS. CLASS lsc_zgiri_i_empl_details IMPLEMENTATION. METHOD save_modified. DATA lt_log TYPE STANDARD TABLE OF zgiri_t_ch_logs1. DATA lt_log1 TYPE STANDARD TABLE OF zgiri_t_ch_logs1. IF update-zgiri_i_empl_details iS NOT INITIAL. lt_log = CORRESPONDING #( update-zgiri_i_empl_details ). LOOP AT update-zgiri_i_empl_details ASSIGNING FIELD-SYMBOL(<ls_log_update>). ASSIGN lt_log[ id = <ls_log_update>-Id ] TO FIELD-SYMBOL(<ls_log_u>) . * / NAME UPDATE......................................... if <ls_log_update>-%control-Name = if_abap_behv=>mk-on. <ls_log_u>-name = <ls_log_update>-Name. TRY. <ls_log_u>-id = cl_system_uuid=>create_uuid_x16_static( ). CATCH cx_uuid_error. "handle exception ENDTRY. APPEND <ls_log_u> TO lt_log1. ENDIF. ENDLOOP. INSERT zgiri_t_ch_logs1 FROM TABLE _log1. ENDIF. ENDMETHOD. ENDCLASS. CLASS lhc_zgiri_i_empl_details DEFINITION INHERITING FROM cl_abap_behavior_handler. PUBLIC SECTION. PRIVATE SECTION. METHODS get_instance_authorizations FOR INSTANCE AUTHORIZATION IMPORTING keys REQUEST requested_authorizations FOR zgiri_i_empl_details RESULT result. METHODS get_global_authorizations FOR GLOBAL AUTHORIZATION IMPORTING REQUEST requested_authorizations FOR zgiri_i_empl_details RESULT result. METHODS earlynumbering_create FOR NUMBERING IMPORTING entities FOR CREATE zgiri_i_empl_details. ENDCLASS. CLASS lhc_zgiri_i_empl_details IMPLEMENTATION. METHOD get_instance_authorizations. ENDMETHOD. METHOD get_global_authorizations. ENDMETHOD. METHOD earlynumbering_create. DATA(lt_entities) = entities. DELETE lt_entities WHERE Id IS NOT INITIAL. TRY. cl_numberrange_runtime=>number_get( EXPORTING nr_range_nr = '01' object = '/DMO/TRV_M' quantity = CONV #( lines( lt_entities ) ) IMPORTING number = DATA(lv_latest_num) returncode = DATA(lv_code) returned_quantity = DATA(lv_qty) ). CATCH cx_nr_object_not_found. CATCH cx_number_ranges INTO DATA(lo_error). LOOP AT lt_entities INTO DATA(ls_entities). APPEND VALUE #( %cid = ls_entities-%cid %key = ls_entities-%key ) TO failed-zgiri_i_empl_details. APPEND VALUE #( %cid = ls_entities-%cid %key = ls_entities-%key %msg = lo_error ) TO reported-zgiri_i_empl_details. ENDLOOP. EXIT. ENDTRY. ASSERT lv_qty = lines( lt_entities ). * DATA: lt_travel_tech_m TYPE TABLE FOR MAPPED EARLY yi_travel_tech_m, * ls_travel_tech_m LIKE LINE OF lt_travel_tech_m. DATA(lv_curr_num) = lv_latest_num - lv_qty. LOOP AT lt_entities INTO ls_entities. lv_curr_num = lv_curr_num + 1. APPEND VALUE #( %cid = ls_entities-%cid ID = lv_curr_num ) TO mapped-zgiri_i_empl_details. ENDLOOP. ENDMETHOD. ENDCLASS. On top the behavior definition we have to create service definition. @EndUserText.label: 'service definition' define service Zgiri_rap_scn_ser { expose zgiri_i_empl_details; } On top the service definition we have to create service binding. after that we can preview our application.Im taking name as example im going to update the name sham signiwis this value. see we can update shyam prasad.And we see in the chang log table. thank you if u have any query reach out me
-
BRF+ Object Creation using ABAP Z-Report, Part 2
by manoj_sm on September 25, 2025 at 4:39 am
To create BRF+ Elements programmatically using ABAP Refer Previous blogBRF+ Object Creation using ABAP Z-ReportIn this blog, we will learn how to create BRF+ Structures and Tables programmatically using ABAP. This provides a scalable way to automate the creation of data objects and integrate them into larger rule frameworks.
-
BRF+ Object Creation using ABAP Z-Report
by manoj_sm on September 24, 2025 at 5:11 am
IntroductionWhen working with SAP BRF+ (Business Rule Framework Plus), Elements are the most basic building blocks. They represent data fields such as numbers, text, or dates, and act as reusable components for structures, tables, functions, and rules. Without elements, we cannot define or process rules effectively in BRF+.In this blog, we will learn how to create BRF+ Elements programmatically using ABAP. This provides a scalable way to automate the creation of data objects and integrate them into larger rule frameworks.
-
Working with Internal Tables, Parallel cursor, Control Break Statements, Messages
by mandanaveenofficial on September 24, 2025 at 5:10 am
Working with internal TablesBasic concepts of Internal Table usage in Classical Reports.Here We will understand the various types of internal tables and creation of each internal table and memory management as wellBelow topics we are going to discuss in detail1.Types of Internal Tables2.Creating Internal Tables3.Populating and Processing Internal Tables4.Usage of work area and Field-symbol5.Data manipulation in Internal table6.Parallel Cursor usage7.Control Break Statements8.Messages
-
Step-by-Step Guide to Creating a Custom Business Object in BOPF
by Naveen_n on September 24, 2025 at 5:03 am
Introduction The Business Object Processing Framework (BOPF) is an SAP framework that simplifies the development of business objects by providing a standardized way to handle data persistence, business logic, validations, determinations, and actions. Instead of writing custom code for every use case, developers can rely on BOPF’s reusable building blocks, ensuring consistency and reducing development effort. In this guide, we will walk through the step-by-step process of creating a custom Business Object in BOPF. Starting with the creation of dictionary elements, moving through defining the root node and generating dictionary artifacts in transaction BOBX, and finally testing our object in BOBT, you’ll gain a clear understanding of how to set up and validate your first custom BO.
-
Validations in BOPF and Consistency Validation in BOPF
by Krishna_karale on September 24, 2025 at 5:00 am
Hello Everyone,In this blog we will be going to learn about the Validations in BOPF.What is Validation in BOPF? In BOPF, a validation is a consistency check defined on a node of the business object that ensures the data fulfills specific business rules before it is saved or processed. In BOPF, keeping the data clean and consistent is very important. Validations help achieve this by checking the data against specific rules. Just like determinations, validations are created on the node of the data model where they are needed. Today, we’ll walk you through how to create a validation and write the logic for it in the implementation class. Procedure Here we are defining a custom Travel table (zkk_dt_travel). @EndUserText.label : 'Data base table for Travel Details' @AbapCatalog.enhancement.category : #EXTENSIBLE_ANY @AbapCatalog.tableCategory : #TRANSPARENT @AbapCatalog.deliveryClass : #A @AbapCatalog.dataMaintenance : #ALLOWED define table zkk_dt_travel { key client : abap.clnt not null; key travel_id : zkk_de_travel_id not null; key user_email : zkk_de_email not null; start_date : dats; destination : zkk_de_destination; status : zkk_de_status; expense_type : zkk_de_item_type; @Semantics.amount.currencyCode : 'zkk_travel_hdr.curr_key' total_cost : zkk_de_cost; curr_key : zkk_de_cost_key; remarks : zkk_de_remarks; created_by : zkk_de_createdby; created_on : datum; }The above table has the below shown data.For the Database table we are defining the Basic view @AbapCatalog.sqlViewName: 'ZKK_DBV_TRAVEL' @AbapCatalog.compiler.compareFilter: true @AccessControl.authorizationCheck: #NOT_REQUIRED @EndUserText.label: 'Interface view for Travel details' @Metadata.ignorePropagatedAnnotations: true @Metadata.allowExtensions: true @ObjectModel:{ modelCategory: #BUSINESS_OBJECT, compositionRoot: true, transactionalProcessingEnabled: true, writeActivePersistence: 'ZKK_dt_travel', createEnabled: true, updateEnabled: true, deleteEnabled: true } @OData.publish: true define view zi_kk_travel as select from zkk_dt_travel { key travel_id, @ObjectModel.mandatory: true key user_email, start_date, destination, status, @ObjectModel.mandatory: true expense_type, @ObjectModel.mandatory: true total_cost, curr_key, remarks, created_by, created_on }For creating the Business object directly from the Eclipse editor, we have to make use of annotations which I have given with their functionality Here’s the explanation of each annotation in your snippet: @ObjectModel: { modelCategory: #BUSINESS_OBJECT, compositionRoot: true, transactionalProcessingEnabled: true, writeActivePersistence: 'ZKK_dt_travel', createEnabled: true, updateEnabled: true, deleteEnabled: true } Explanation modelCategory: #BUSINESS_OBJECT Declares that the CDS view represents a Business Object (BO). This means it can be used in BOPF to handle business logic and transactional operations. compositionRoot: true Defines this entity as the root node of the Business Object. Other nodes (child entities) can be linked to it via composition relationships. transactionalProcessingEnabled: true Enables transactional behavior (create, update, delete, save, rollback) for the entity, making it suitable for business object processing. writeActivePersistence: 'ZKK_dt_travel' Specifies the database table (ZKK_DT_TRAVEL) where the active (persisted) data of this entity is stored. createEnabled: true Allows the creation of new records in the entity. updateEnabled: true Allows modification of existing records. deleteEnabled: true Allows deletion of records. @odata.publish: true turns your CDS view into an OData service provider, making its data available for consumption via OData protocols. In short: This annotation block is used to define a CDS entity as a Business Object root node with full CRUD (Create, Read, Update, Delete) support and persistence in the custom table ZKK_DT_TRAVEL. Here I am creating the Metadata extension for the Basic view (ZI_KK_TRAVEL) for consuming it in the front end. @Metadata.layer: #CORE @UI.headerInfo: { typeName: 'Travel', typeNamePlural: 'Travel Details' } annotate entity zi_kk_travel with { @UI.facet: [{ id: 'Travel', purpose: #STANDARD, position: 10, label: 'Travel Details ', type: #IDENTIFICATION_REFERENCE } ] @UI.lineItem: [{ position: 10, label: 'Travel Id' }] @UI.identification: [{ position: 10 }] travel_id; @UI.lineItem: [{ position: 20, label: 'Email' }] @UI.identification: [{ position: 20 }] user_email; @UI.lineItem: [{ position: 30, label: 'Travel Date' }] @UI.identification: [{ position: 30 }] start_date; @UI.lineItem: [{ position: 40, label: 'Destination' }] @UI.identification: [{ position: 40 }] destination; @UI.lineItem: [{ position: 50, label: 'Status' }] @UI.identification: [{ position: 50 }] status; @UI.lineItem: [{ position: 60, label: 'Expense Type' }] @UI.identification: [{ position: 60 }] expense_type; @UI.lineItem: [{ position: 65, label: 'Amount' }] @UI.identification: [{ position: 65 }] total_cost; @UI.lineItem: [{ position: 70, label: 'Currency' }] @UI.identification: [{ position: 70 }] curr_key; @UI.lineItem: [{ position: 80, label: 'Remarks for Expense' }] @UI.identification: [{ position: 80 }] remarks; @UI.lineItem: [{ position: 85, label: 'Created By' }] @UI.identification: [{ position: 10 }] created_by; @UI.lineItem: [{ position: 90, label: 'Created Date' }] @UI.identification: [{ position: 90 }] created_on; } Now activate the Basic view and the meta data extension, when it gets activated the Business Object get created implicitly. Now open the BOBX Transaction. We can search the business object in Home business objects or click on the below marked option.Provide the BO name which is our Basic View name (ZI_KK_TRAVEL) Now to Implement the validations expand the Node Elements Right Click on Validations will see the option to Create Validations Provide the Validation Name and Description and Class for implementing the validation logic. Double click on Class Name, and create a class which will be having the super class implicitly Now we have to provide Triggering conditions for the validations, so select the Node instance and tick on Create, Update, Delete and Check Save it and check and generate it. After this we have to go to eclipse and create a service definition on basic view (ZI_KK_TRAVEL) Now create a Service binding on top of Service definition and publish it When we preview it It will be navigated to the frontend while navigating it will ask for your SAP GUI Credentials to login.Now go to SE91 Transaction and create a message class Here, I am doing Validation for a Single status field only so i have passed only two messages Now I want to provide the Validations for Status field, so go to our class which we have created in Validations Implement our Custom login in this method. METHOD /bobf/if_frw_validation~execute. CLEAR : et_failed_key, eo_message. eo_message = /bobf/cl_frw_factory=>get_message( ). DATA : lt_data TYPE ztikk_travel, " Table type for header node ls_msg TYPE symsg. io_read->retrieve( EXPORTING iv_node = is_ctx-node_key it_key = it_key iv_fill_data = abap_true it_requested_attributes = VALUE #( ( zif_i_kk_travel_c=>sc_node_attribute-zi_kk_travel-status ) ) ) IMPORTING et_data = lt_data ). LOOP AT lt_data REFERENCE INTO DATA(ls_data). IF ls_data->status IS INITIAL . ls_msg-msgid = 'ZKK_MSG_BOPF'. ls_msg-msgno = '000'. ls_msg-msgty = 'E'. eo_message->add_message( is_msg = ls_msg " Message that is to be added to the message object iv_node = is_ctx-node_key " Node to be used in the origin location iv_key = ls_data->key " Instance key to be used in the origin location iv_attribute = zif_i_kk_travel_c=>sc_node_attribute-zi_kk_travel-status " Attribute to be used in the origin location ). INSERT VALUE #( key = ls_data->key ) INTO TABLE et_failed_key. ENDIF. DATA(lv_status) = to_upper( condense( ls_data->status ) ). IF lv_status NA 'ACCEPT' OR lv_status NA 'REJECT'. ls_msg-msgid = 'ZKK_MSG_BOPF'. ls_msg-msgno = '001'. ls_msg-msgty = 'E'. eo_message->add_message( is_msg = ls_msg iv_node = is_ctx-node_key iv_key = ls_data->key iv_attribute = zif_i_kk_travel_c=>sc_node_attribute-zi_kk_travel-status ). INSERT VALUE #( key = ls_data->key ) INTO TABLE et_failed_key. ENDIF. ENDLOOP. ENDMETHOD.Now go to service binding and preview it. Try to create a new record. Here I am keeping Status field as empty so it will throw error. Now Status should accept only ‘ACCEPT’ or ‘REJECT’. If I try to give any other value it will throw error again. If you give the correct value ‘Accept’ or ‘Reject’. ConclusionValidations in BOPF play a vital role in safeguarding data quality and enforcing business rules at the framework level. By defining validations directly on the business object nodes, we ensure that only consistent and compliant data enters the system, regardless of whether it originates from an SAP UI, an external interface, or a background process. This not only strengthens the reliability of the application but also reduces the need for redundant checks across different layers. In short, validations serve as a powerful and centralized mechanism to guarantee data integrity within the BOPF model.
-
Creation of Value Range in Business Rule Framework Plus(BRF+)
by manoharreddy478 on September 24, 2025 at 4:59 am
In Business Rule Framework plus (BRF+), a Value Range refers to an expression used to check if a specific value falls within a defined range or set of values. The outcome of a value range expression is always a Boolean (true or false), indicating whether the value meets the specified range condition.
-
SAP Developer News September 18th, 2025
by Eberenwaobiora on September 18, 2025 at 7:10 pm
This week's episode contains segments on Avoiding Data Loss When Loading Initial Data with CSV Files in CAP, 50k Subscribers, Devtoberfest, UI5con Bengaluru 2025, and eslint-plugin-better-cap-config
-
Migration Guide From BOPF to RAP Business Objects
by SRSATAPATHY on September 17, 2025 at 5:37 am
Business Object Processing Framework (BOPF) is gradually being replaced by the RESTful ABAP Programming Model (RAP), which provides a more modern, cloud-ready, and Fiori-optimized approach for building business applications. To help developers transition, SAP provides a guided migration procedure that converts CDS-based BOPF Business Objects into RAP Business Objects.
-
OData Deep Insert: Implementation and Real-Life Examples
by SRSATAPATHY on September 15, 2025 at 6:37 am
OData Deep Insert is used when we need to create a main record along with its related records in one go. For example, if we're creating a sales order and want to include multiple items in that order, instead of calling the service multiple times (once for the header and once for each item), we can just send everything together in one request. This helps reduce the number of API calls and keeps things more efficient.
-
Steps to Connect Eclipse ADT with GITHUB
by Nihal__Raj on September 15, 2025 at 6:25 am
In this blog, I'll walk you through the simple steps to seamlessly connect Eclipse ADT with GitHub - right from configuring your workspace to pushing your first commit. Whether you're new to version control or looking to streamline your development workflow, this quick guide will get you started in on time.