Consuming External API Tax Rate Information in RAP

Estimated read time 19 min read

Introduction 

With the increasing need for real-time data integration, consuming external APIs directly within the ABAP RESTful Application Programming Model (RAP) is becoming a critical requirement. This blog post demonstrates how to integrate external tax rate data into a RAP-based application using unmanaged query implementation. 

Scenario Overview 

The goal is to expose tax rate information retrieved from an external API within an SAP Fiori application, structured using RAP. This example uses two custom entities: 

ZI_TAXRATE_UQ (Root View) ZI_TAXRATE_ITEM_UQ (Item View) 

The backend implementation is handled in an ABAP class ZCL_TAXRATE_UQ that implements the interface IF_RAP_QUERY_PROVIDER.  

Data Model Definition 

The data model consists of: 

Root Entity: ZI_TAXRATE_UQ 

@EndUserText.label: ‘Interface View for Unmanaged scenario’
@Metadata.allowExtensions: true
define root custom entity ZI_TAXRATE_UQ
{
key Id : abap.int1;
key Country : abap.char(2);
key State : abap.char(2);
key City : abap.char(20);
key Postcode : abap.char(5);
key Address1 : abap.char(50);
key Address2 : abap.char(50);
key TaxClass : abap.char(20);
// Return Fields
TaxRateLabel : abap.char(20);
Shipping : abap.char(3);
Rate : abap.dec(16, 4);
Compound : abap.char(3);
CompoundCriticality : abap.int1;
Combined : abap.char(3);
CombinedCriticality : abap.int1;
CombineError : abap.char(3);
CombineErrorCriticality : abap.int1;
// This does not work with Custom Entities: _TaxrateItem : …
_TaxrateItem : composition [1..*] of zi_taxrate_item_uq;
}

 Item Entity: ZI_TAXRATE_ITEM_UQ 

@EndUserText.label: ‘Interface View from item details’
//@ObjectModel.query.implementedBy: ‘ABAP:ZCL_TAXRATE_UQ’
@Metadata.allowExtensions: true
define custom entity ZI_TAXRATE_ITEM_UQ
{
// Filter Fields
key TaxrateId : abap.int1;
key Id : abap.int1;
key Country : abap.char(2);
key State : abap.char(2);
key City : abap.char(20);
key Postcode : abap.char(5);
key Address1 : abap.char(50);
key Address2 : abap.char(50);
key TaxClass : abap.char(20);
// Return Fields
TaxRateLabel : abap.char(20);
Shipping : abap.char(3);
Rate : abap.dec(16, 4);
Compound : abap.char(3);
_TaxRate : association to parent zi_taxrate_uq
on $projection.taxrateid = _TaxRate.id
and $projection.country = _TaxRate.country
and $projection.state = _TaxRate.state
and $projection.city = _TaxRate.city
and $projection.postcode = _TaxRate.postcode
and $projection.address1 = _TaxRate.address1
and $projection.address2 = _TaxRate.address2
and $projection.taxclass = _TaxRate.taxclass;
}

Custom Logic Implementation 

CLASS zcl_taxrate_uq DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_rap_query_provider .
METHODS:
constructor.
PROTECTED SECTION.
METHODS:
build_taxrates_query IMPORTING sql_filter TYPE string,
get_tax_rates RETURNING VALUE(tax_rates_combined) TYPE zif_gtax_rates=>ts_tax_rates_combined,
sanitize_fields_for_insert,
handle_paging IMPORTING io_request TYPE REF TO if_rap_query_request,
get_value_from_sql_string
IMPORTING
VALUE(i_string) TYPE string ” Input string with key-value pairs
VALUE(i_key) TYPE string ” Key to search for
RETURNING
VALUE(e_value) TYPE string. ” Corresponding value

PRIVATE SECTION.
DATA: parameters TYPE if_rap_query_request=>tt_parameters,
sql_filter TYPE string,
offset TYPE int8,
page_size TYPE int8,
max_rows TYPE int8.
DATA: tr_query TYPE zif_gtax_rates=>ts_taxrates_query,

use_mockdata TYPE abap_bool,
tax_rates_combined TYPE zif_gtax_rates=>ts_tax_rates_combined,
tax_rate TYPE zif_gtax_rates=>ts_tax_rates.

DATA: compound_criticality TYPE i,
combined TYPE string,
combined_critically TYPE i,
combine_error TYPE string,
combine_error_critically TYPE i.
ENDCLASS.
CLASS zcl_taxrate_uq IMPLEMENTATION.
METHOD constructor.
use_mockdata = abap_false.
ENDMETHOD.
METHOD if_rap_query_provider~select.
TRY.
CASE io_request->get_entity_id( ).
WHEN ‘ZI_TAXRATE_UQ’.
” Read parameters and filters
parameters = io_request->get_parameters( ). “Parameters not used
sql_filter = io_request->get_filter( )->get_as_sql_string( ).

” Get tax rates from remote API
build_taxrates_query( sql_filter = sql_filter ).
tax_rates_combined = get_tax_rates( ).
sanitize_fields_for_insert( ).

” Append the received tax rates to internal table
DATA lt_taxrate TYPE STANDARD TABLE OF zi_taxrate_uq.
INSERT VALUE #(
Id = 1
Country = tr_query-country
State = tr_query-state
City = tr_query-city
Postcode = tr_query-postcode
Address1 = tr_query-address1
Address2 = tr_query-address2
TaxClass = tr_query-tax_class
TaxRateLabel = tax_rates_combined-tax_rate-label
Shipping = tax_rates_combined-tax_rate-shipping
Rate = tax_rates_combined-tax_rate-rate
Compound = tax_rates_combined-tax_rate-compound
CompoundCriticality = compound_criticality
Combined = combined
CombinedCriticality = combined_criticality
CombineError = combine_error
CombineErrorCriticality = combine_error_criticality
) INTO TABLE lt_taxrate.
” Return requested data
IF io_request->is_data_requested( ).
handle_paging( io_request ).
io_response->set_data( lt_taxrate ).
ENDIF.
” Return total number of records
IF io_request->is_total_numb_of_rec_requested( ).
io_response->set_total_number_of_records( lines( lt_taxrate ) ).
ENDIF.

WHEN ‘ZI_TAXRATE_ITEM_UQ’ .
parameters = io_request->get_parameters( ).
sql_filter = io_request->get_filter( )->get_as_sql_string( ).

” Get tax rates from remote API
build_taxrates_query( sql_filter = sql_filter ).
tax_rates_combined = get_tax_rates( ).
” Append the received tax rates to internal table

DATA lt_taxrate_item TYPE STANDARD TABLE OF zi_taxrate_item_uq.

DATA(lv_id) = 1.
LOOP AT tax_rates_combined-tax_rates INTO tax_rate.
INSERT VALUE #(
TaxrateId = 1
Id = lv_id
Country = tr_query-country
State = tr_query-state
City = tr_query-city
Postcode = tr_query-postcode
Address1 = tr_query-address1
Address2 = tr_query-address2
TaxClass = tr_query-tax_class
TaxRateLabel = tax_rate-label
Shipping = tax_rate-shipping
Rate = tax_rate-rate
Compound = tax_rate-compound
) INTO TABLE lt_taxrate_item.
lv_id += 1.
ENDLOOP.

” Query table with id AND previous query if specific item is requested (by click on an item in the Tax Rate Items – Listview)

DATA(id) = get_value_from_sql_string( i_string = sql_filter i_key = ‘ID’ ).

IF id IS NOT INITIAL.
SELECT *
FROM _taxrate_item AS taxrates
WHERE (sql_filter)

ORDER BY Id
INTO TABLE (lt_result).
lt_taxrate_item = lt_result.

ENDIF.

” Return requested data
IF io_request->is_data_requested( ).
handle_paging( io_request ).
io_response->set_data( lt_taxrate_item ).

ENDIF.
ENDCASE.
CATCH cx_rap_query_provider.
ENDTRY.
ENDMETHOD.

METHOD build_taxrates_query.
IF use_mockdata = abap_false.
tr_query-country = get_value_from_sql_string( i_string = sql_filter i_key = ‘COUNTRY’ ).

tr_query-state = get_value_from_sql_string( i_string = sql_filter i_key = ‘STATE’ ).

tr_query-city = get_value_from_sql_string( i_string = sql_filter i_key = ‘CITY’ ).

tr_query-postcode = get_value_from_sql_string( i_string = sql_filter i_key = ‘POSTCODE’ ).

tr_query-address1 = get_value_from_sql_string( i_string = sql_filter i_key = ‘ADDRESS1’ ).

tr_query-address2 = get_value_from_sql_string( i_string = sql_filter i_key = ‘ADDRESS2’ ).

tr_query-tax_class = get_value_from_sql_string( i_string = sql_filter i_key = ‘TAXCLASS’ ).

ELSE.
tr_query-country = ‘US’.
tr_query-state = ‘CA’.
tr_query-city = ‘Mountain View’.
tr_query-postcode = ‘94043’.
tr_query-address1 = ‘1600 Amphitheatre Parkway’.
tr_query-address2 = ”.
tr_query-tax_class = ”.

ENDIF.

ENDMETHOD.
METHOD get_tax_rates.
DATA: gtax_client TYPE REF TO zcl_gtax_client,
jsonpath TYPE REF TO zcl_jsonpath,
tax_rates_json TYPE string.

gtax_client = NEW zcl_gtax_client( ).
jsonpath = NEW zcl_jsonpath( ).

TRY.
tax_rates_json = gtax_client->get_tax_rates( tr_query = tr_query use_mockdata = use_mockdata ).
jsonpath->set( tax_rates_json ).
jsonpath->get_data( EXPORTING query_string = ‘$’ CHANGING data = tax_rates_combined ).
CATCH cx_http_dest_provider_error cx_web_http_client_error INTO DATA(lx_error).

“out->write( lx_error->get_text( ) ).
“EXIT.

ENDTRY.
ENDMETHOD.

METHOD sanitize_fields_for_insert.
compound_criticality = 3. “Green
IF tax_rates_combined-tax_rate-compound = ‘yes’.
compound_criticality = 2. “Orange
ENDIF.

combined = ‘no ‘.
combined_criticality = 3. “Green
IF tax_rates_combined-combined = abap_true.
combined = ‘yes’.
combined_criticality = 5. “Blue
ENDIF.

combine_error = ‘no ‘.
combine_error_criticality = 3. “Green
IF tax_rates_combined-combine_error = abap_true.
combine_error = ‘yes’.
combine_error_criticality = 2. “Orange
ENDIF.
ENDMETHOD.

METHOD handle_paging.
” Get Paging to prevent “NOT IMPLEMENTED” error
offset = io_request->get_paging( )->get_offset( ).
page_size = io_request->get_paging( )->get_page_size( ).
max_rows = COND #( WHEN page_size = if_rap_query_paging=>page_size_unlimited THEN 0 ELSE page_size ).
ENDMETHOD.

METHOD get_value_from_sql_string.
DATA: lt_parts TYPE TABLE OF string,
lv_field TYPE string,
lv_value TYPE string.

” Replace all occurrences of ‘AND’ in i_string with ‘;’
REPLACE ALL OCCURRENCES OF ‘AND’ IN i_string WITH ‘;’.

” Split the transformed string into individual key-value pairs
SPLIT i_string AT ‘;’ INTO TABLE lt_parts.

LOOP AT lt_parts INTO DATA(lv_part).
” Split each key-value pair into key and value using ‘=’ as the delimiter
SPLIT lv_part AT ‘=’ INTO lv_field lv_value.
” Check if the current key matches the input key (i_key)
IF lv_field = i_key.
e_value = lv_value.
RETURN. ” Exit method once matching key is found
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS.

External API Consumption 
The get_tax_rates method uses a client class ZCL_GTAX_CLIENT to retrieve data from the remote endpoint. The JSON response is parsed using a helper class ZCL_JSONPATH. 

Query Preparation 
build_taxrates_query extracts input parameters from SQL filters and constructs the API request payload. Mock data can be used optionally for testing. 

Result Formatting 
The sanitize_fields_for_insert method enriches the result by computing criticality indicators for compound status, combination, and error handling. 

Paging Support 
Handled by handle_paging, which uses standard RAP query paging mechanisms to limit result sets. 

Utility for SQL Filter Parsing 
get_value_from_sql_string helps extract individual filter values (like Country, State, etc.) from a SQL string. 

Service Definition and Binding 
The first screenshot shows the Service Definition (ZUI_TAXRATE_O4) where both custom entities ZI_TAXRATE_UQ and ZI_TAXRATE_ITEM_UQ are exposed using the OData V4 protocol. The service is later bound in the Service Binding screen where the service endpoint is generated for UI consumption. 

Fiori Preview  

The initial screen before search execution shows an empty table, prompting the user to input filter values and trigger the tax rate retrieval. 

This example shows the tax rate lookup for Austria (AT), where a VAT of 20% is retrieved based on the given input parameters. It highlights that the service is capable of handling international addresses. 

The filter bar allows users to input specific address details like Country, State, City, Postcode, Address1, and Tax Class to trigger the API call and retrieve appropriate tax rate information dynamically. 

The Address tab displays the country, state, city, postcode, and address information entered by the user, which is used to filter and query the correct tax rates from the external API. 

Below image shows the list of Taxrate Items for a given address. Each line item represents a tax type like “CA COUNTY TAX”, “CA STATE TAX”, and “CA SPECIAL TAX”, along with the respective tax rate and compound information. 
Below image presents a detailed view of a single Taxrate Item. It displays the tax label, rate, and compound status retrieved from the external API. 

Conclusion 
This implementation demonstrates how RAP can be extended to consume and expose data from external APIs using unmanaged custom entities and ABAP logic. Key advantages of this design include: 

Full control over API communication and data transformation. Reusability and testability using mock data. Seamless Fiori integration via RAP and CDS. 

 

 

 

​ Introduction With the increasing need for real-time data integration, consuming external APIs directly within the ABAP RESTful Application Programming Model (RAP) is becoming a critical requirement. This blog post demonstrates how to integrate external tax rate data into a RAP-based application using unmanaged query implementation. Scenario Overview The goal is to expose tax rate information retrieved from an external API within an SAP Fiori application, structured using RAP. This example uses two custom entities: ZI_TAXRATE_UQ (Root View) ZI_TAXRATE_ITEM_UQ (Item View) The backend implementation is handled in an ABAP class ZCL_TAXRATE_UQ that implements the interface IF_RAP_QUERY_PROVIDER.  Data Model Definition The data model consists of: Root Entity: ZI_TAXRATE_UQ @EndUserText.label: ‘Interface View for Unmanaged scenario’
@Metadata.allowExtensions: true
define root custom entity ZI_TAXRATE_UQ
{
key Id : abap.int1;
key Country : abap.char(2);
key State : abap.char(2);
key City : abap.char(20);
key Postcode : abap.char(5);
key Address1 : abap.char(50);
key Address2 : abap.char(50);
key TaxClass : abap.char(20);
// Return Fields
TaxRateLabel : abap.char(20);
Shipping : abap.char(3);
Rate : abap.dec(16, 4);
Compound : abap.char(3);
CompoundCriticality : abap.int1;
Combined : abap.char(3);
CombinedCriticality : abap.int1;
CombineError : abap.char(3);
CombineErrorCriticality : abap.int1;
// This does not work with Custom Entities: _TaxrateItem : …
_TaxrateItem : composition [1..*] of zi_taxrate_item_uq;
}  Item Entity: ZI_TAXRATE_ITEM_UQ @EndUserText.label: ‘Interface View from item details’
//@ObjectModel.query.implementedBy: ‘ABAP:ZCL_TAXRATE_UQ’
@Metadata.allowExtensions: true
define custom entity ZI_TAXRATE_ITEM_UQ
{
// Filter Fields
key TaxrateId : abap.int1;
key Id : abap.int1;
key Country : abap.char(2);
key State : abap.char(2);
key City : abap.char(20);
key Postcode : abap.char(5);
key Address1 : abap.char(50);
key Address2 : abap.char(50);
key TaxClass : abap.char(20);
// Return Fields
TaxRateLabel : abap.char(20);
Shipping : abap.char(3);
Rate : abap.dec(16, 4);
Compound : abap.char(3);
_TaxRate : association to parent zi_taxrate_uq
on $projection.taxrateid = _TaxRate.id
and $projection.country = _TaxRate.country
and $projection.state = _TaxRate.state
and $projection.city = _TaxRate.city
and $projection.postcode = _TaxRate.postcode
and $projection.address1 = _TaxRate.address1
and $projection.address2 = _TaxRate.address2
and $projection.taxclass = _TaxRate.taxclass;
} Custom Logic Implementation CLASS zcl_taxrate_uq DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_rap_query_provider .
METHODS:
constructor.
PROTECTED SECTION.
METHODS:
build_taxrates_query IMPORTING sql_filter TYPE string,
get_tax_rates RETURNING VALUE(tax_rates_combined) TYPE zif_gtax_rates=>ts_tax_rates_combined,
sanitize_fields_for_insert,
handle_paging IMPORTING io_request TYPE REF TO if_rap_query_request,
get_value_from_sql_string
IMPORTING
VALUE(i_string) TYPE string ” Input string with key-value pairs
VALUE(i_key) TYPE string ” Key to search for
RETURNING
VALUE(e_value) TYPE string. ” Corresponding value

PRIVATE SECTION.
DATA: parameters TYPE if_rap_query_request=>tt_parameters,
sql_filter TYPE string,
offset TYPE int8,
page_size TYPE int8,
max_rows TYPE int8.
DATA: tr_query TYPE zif_gtax_rates=>ts_taxrates_query,

use_mockdata TYPE abap_bool,
tax_rates_combined TYPE zif_gtax_rates=>ts_tax_rates_combined,
tax_rate TYPE zif_gtax_rates=>ts_tax_rates.

DATA: compound_criticality TYPE i,
combined TYPE string,
combined_critically TYPE i,
combine_error TYPE string,
combine_error_critically TYPE i.
ENDCLASS.
CLASS zcl_taxrate_uq IMPLEMENTATION.
METHOD constructor.
use_mockdata = abap_false.
ENDMETHOD.
METHOD if_rap_query_provider~select.
TRY.
CASE io_request->get_entity_id( ).
WHEN ‘ZI_TAXRATE_UQ’.
” Read parameters and filters
parameters = io_request->get_parameters( ). “Parameters not used
sql_filter = io_request->get_filter( )->get_as_sql_string( ).

” Get tax rates from remote API
build_taxrates_query( sql_filter = sql_filter ).
tax_rates_combined = get_tax_rates( ).
sanitize_fields_for_insert( ).

” Append the received tax rates to internal table
DATA lt_taxrate TYPE STANDARD TABLE OF zi_taxrate_uq.
INSERT VALUE #(
Id = 1
Country = tr_query-country
State = tr_query-state
City = tr_query-city
Postcode = tr_query-postcode
Address1 = tr_query-address1
Address2 = tr_query-address2
TaxClass = tr_query-tax_class
TaxRateLabel = tax_rates_combined-tax_rate-label
Shipping = tax_rates_combined-tax_rate-shipping
Rate = tax_rates_combined-tax_rate-rate
Compound = tax_rates_combined-tax_rate-compound
CompoundCriticality = compound_criticality
Combined = combined
CombinedCriticality = combined_criticality
CombineError = combine_error
CombineErrorCriticality = combine_error_criticality
) INTO TABLE lt_taxrate.
” Return requested data
IF io_request->is_data_requested( ).
handle_paging( io_request ).
io_response->set_data( lt_taxrate ).
ENDIF.
” Return total number of records
IF io_request->is_total_numb_of_rec_requested( ).
io_response->set_total_number_of_records( lines( lt_taxrate ) ).
ENDIF.

WHEN ‘ZI_TAXRATE_ITEM_UQ’ .
parameters = io_request->get_parameters( ).
sql_filter = io_request->get_filter( )->get_as_sql_string( ).

” Get tax rates from remote API
build_taxrates_query( sql_filter = sql_filter ).
tax_rates_combined = get_tax_rates( ).
” Append the received tax rates to internal table

DATA lt_taxrate_item TYPE STANDARD TABLE OF zi_taxrate_item_uq.

DATA(lv_id) = 1.
LOOP AT tax_rates_combined-tax_rates INTO tax_rate.
INSERT VALUE #(
TaxrateId = 1
Id = lv_id
Country = tr_query-country
State = tr_query-state
City = tr_query-city
Postcode = tr_query-postcode
Address1 = tr_query-address1
Address2 = tr_query-address2
TaxClass = tr_query-tax_class
TaxRateLabel = tax_rate-label
Shipping = tax_rate-shipping
Rate = tax_rate-rate
Compound = tax_rate-compound
) INTO TABLE lt_taxrate_item.
lv_id += 1.
ENDLOOP.

” Query table with id AND previous query if specific item is requested (by click on an item in the Tax Rate Items – Listview)

DATA(id) = get_value_from_sql_string( i_string = sql_filter i_key = ‘ID’ ).

IF id IS NOT INITIAL.
SELECT *
FROM _taxrate_item AS taxrates
WHERE (sql_filter)

ORDER BY Id
INTO TABLE (lt_result).
lt_taxrate_item = lt_result.

ENDIF.

” Return requested data
IF io_request->is_data_requested( ).
handle_paging( io_request ).
io_response->set_data( lt_taxrate_item ).

ENDIF.
ENDCASE.
CATCH cx_rap_query_provider.
ENDTRY.
ENDMETHOD.

METHOD build_taxrates_query.
IF use_mockdata = abap_false.
tr_query-country = get_value_from_sql_string( i_string = sql_filter i_key = ‘COUNTRY’ ).

tr_query-state = get_value_from_sql_string( i_string = sql_filter i_key = ‘STATE’ ).

tr_query-city = get_value_from_sql_string( i_string = sql_filter i_key = ‘CITY’ ).

tr_query-postcode = get_value_from_sql_string( i_string = sql_filter i_key = ‘POSTCODE’ ).

tr_query-address1 = get_value_from_sql_string( i_string = sql_filter i_key = ‘ADDRESS1’ ).

tr_query-address2 = get_value_from_sql_string( i_string = sql_filter i_key = ‘ADDRESS2’ ).

tr_query-tax_class = get_value_from_sql_string( i_string = sql_filter i_key = ‘TAXCLASS’ ).

ELSE.
tr_query-country = ‘US’.
tr_query-state = ‘CA’.
tr_query-city = ‘Mountain View’.
tr_query-postcode = ‘94043’.
tr_query-address1 = ‘1600 Amphitheatre Parkway’.
tr_query-address2 = ”.
tr_query-tax_class = ”.

ENDIF.

ENDMETHOD.
METHOD get_tax_rates.
DATA: gtax_client TYPE REF TO zcl_gtax_client,
jsonpath TYPE REF TO zcl_jsonpath,
tax_rates_json TYPE string.

gtax_client = NEW zcl_gtax_client( ).
jsonpath = NEW zcl_jsonpath( ).

TRY.
tax_rates_json = gtax_client->get_tax_rates( tr_query = tr_query use_mockdata = use_mockdata ).
jsonpath->set( tax_rates_json ).
jsonpath->get_data( EXPORTING query_string = ‘$’ CHANGING data = tax_rates_combined ).
CATCH cx_http_dest_provider_error cx_web_http_client_error INTO DATA(lx_error).

“out->write( lx_error->get_text( ) ).
“EXIT.

ENDTRY.
ENDMETHOD.

METHOD sanitize_fields_for_insert.
compound_criticality = 3. “Green
IF tax_rates_combined-tax_rate-compound = ‘yes’.
compound_criticality = 2. “Orange
ENDIF.

combined = ‘no ‘.
combined_criticality = 3. “Green
IF tax_rates_combined-combined = abap_true.
combined = ‘yes’.
combined_criticality = 5. “Blue
ENDIF.

combine_error = ‘no ‘.
combine_error_criticality = 3. “Green
IF tax_rates_combined-combine_error = abap_true.
combine_error = ‘yes’.
combine_error_criticality = 2. “Orange
ENDIF.
ENDMETHOD.

METHOD handle_paging.
” Get Paging to prevent “NOT IMPLEMENTED” error
offset = io_request->get_paging( )->get_offset( ).
page_size = io_request->get_paging( )->get_page_size( ).
max_rows = COND #( WHEN page_size = if_rap_query_paging=>page_size_unlimited THEN 0 ELSE page_size ).
ENDMETHOD.

METHOD get_value_from_sql_string.
DATA: lt_parts TYPE TABLE OF string,
lv_field TYPE string,
lv_value TYPE string.

” Replace all occurrences of ‘AND’ in i_string with ‘;’
REPLACE ALL OCCURRENCES OF ‘AND’ IN i_string WITH ‘;’.

” Split the transformed string into individual key-value pairs
SPLIT i_string AT ‘;’ INTO TABLE lt_parts.

LOOP AT lt_parts INTO DATA(lv_part).
” Split each key-value pair into key and value using ‘=’ as the delimiter
SPLIT lv_part AT ‘=’ INTO lv_field lv_value.
” Check if the current key matches the input key (i_key)
IF lv_field = i_key.
e_value = lv_value.
RETURN. ” Exit method once matching key is found
ENDIF.
ENDLOOP.
ENDMETHOD.
ENDCLASS. External API Consumption The get_tax_rates method uses a client class ZCL_GTAX_CLIENT to retrieve data from the remote endpoint. The JSON response is parsed using a helper class ZCL_JSONPATH. Query Preparation build_taxrates_query extracts input parameters from SQL filters and constructs the API request payload. Mock data can be used optionally for testing. Result Formatting The sanitize_fields_for_insert method enriches the result by computing criticality indicators for compound status, combination, and error handling. Paging Support Handled by handle_paging, which uses standard RAP query paging mechanisms to limit result sets. Utility for SQL Filter Parsing get_value_from_sql_string helps extract individual filter values (like Country, State, etc.) from a SQL string. Service Definition and Binding The first screenshot shows the Service Definition (ZUI_TAXRATE_O4) where both custom entities ZI_TAXRATE_UQ and ZI_TAXRATE_ITEM_UQ are exposed using the OData V4 protocol. The service is later bound in the Service Binding screen where the service endpoint is generated for UI consumption. Fiori Preview  The initial screen before search execution shows an empty table, prompting the user to input filter values and trigger the tax rate retrieval. This example shows the tax rate lookup for Austria (AT), where a VAT of 20% is retrieved based on the given input parameters. It highlights that the service is capable of handling international addresses. The filter bar allows users to input specific address details like Country, State, City, Postcode, Address1, and Tax Class to trigger the API call and retrieve appropriate tax rate information dynamically. The Address tab displays the country, state, city, postcode, and address information entered by the user, which is used to filter and query the correct tax rates from the external API. Below image shows the list of Taxrate Items for a given address. Each line item represents a tax type like “CA COUNTY TAX”, “CA STATE TAX”, and “CA SPECIAL TAX”, along with the respective tax rate and compound information. Below image presents a detailed view of a single Taxrate Item. It displays the tax label, rate, and compound status retrieved from the external API. Conclusion This implementation demonstrates how RAP can be extended to consume and expose data from external APIs using unmanaged custom entities and ABAP logic. Key advantages of this design include: Full control over API communication and data transformation. Reusability and testability using mock data. Seamless Fiori integration via RAP and CDS.      Read More Application Development and Automation Blog Posts articles 

#SAP

You May Also Like

More From Author