Skip to Content
Technical Articles
Author's profile photo chethan santhahalli ningegowda

SAP oDATA V4 API – Using ABAP Restful Programming

Challenge :

Create a ABAP V4 – API with Parent and Child Relationship using existing Standard CDS Views Provided by SAP without Writing an ABAP Code

Note : Some of the code are not pasted so that readers try it with hands on.

Business Scenario : Get All Business Partner Details – Supplier – Customer – Expanding from Parent Child

Inspired By : Every Step of this article is inspired by the SAP Provided documentation in the link below

https://github.com/SAP-samples/abap-platform-rap-opensap

 

Steps Followed :

 

Step 1 :

We need to 2 Views , one acts as a parent ( Header ) and another as a child ( Item level)

In this case Business Partner is a header Info and Supplier is a Item detail ( Child ) . We will also see in the next steps that by using standard CDS Views the associations of the child view ( Supplier ) and associations of parent view ( Business Partner Header ) ,  parent child relationship will automatically pop up in the V4 API without any ABAP code involved.

Create two simple Custom CDS Views ZI_BP_PARENT and ZI_BP_SUPPLIER in your Package as shown below :

 

 

ZI_BP_PARENT

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'View for Consuming View as a oDATA V4'
define root view entity ZI_BP_PARENT as select from I_BusinessPartner as BPartner
composition [1..1] of ZI_BP_SUPPLIER as _Supplier 
association [0..1] to bp001 as _BusinessPartner on $projection.BusinessPartner = _BusinessPartner.partner

association [0..1] to I_CustomerToBusinessPartner as _CustomerToBusinessPartner on BPartner.BusinessPartnerUUID = _CustomerToBusinessPartner.BusinessPartnerUUID

association [0..1] to I_SupplierToBusinessPartner as _SupplierToBusinessPartner on BPartner.BusinessPartnerUUID = _SupplierToBusinessPartner.BusinessPartnerUUID

association [0..1] to A_Customer as _Customer on $projection.customer = _Customer.Customer

//association [0..1] to A_Supplier as _Supplier on $projection.supplier = _Supplier.Supplier

association [0..*] to A_BusinessPartnerAddress as _BusinessPartnerAddress on $projection.BusinessPartner = _BusinessPartnerAddress.BusinessPartner

association [0..*] to A_BusinessPartnerRole as _BusinessPartnerRole on $projection.BusinessPartner =  _BusinessPartnerRole.BusinessPartner

association [0..*] to A_BusinessPartnerTaxNumber as _BusinessPartnerTax on $projection.BusinessPartner =  _BusinessPartnerTax.BusinessPartner

association [0..*] to A_BusinessPartnerBank as _BusinessPartnerBank on $projection.BusinessPartner =  _BusinessPartnerBank.BusinessPartner

association [0..*] to A_BuPaIdentification as _BuPaIdentification on $projection.BusinessPartner =  _BuPaIdentification.BusinessPartner

association [0..*] to A_BusinessPartnerContact as _BusinessPartnerContact on $projection.BusinessPartner = _BusinessPartnerContact.BusinessPartnerCompany

association [0..*] to A_BuPaIndustry as _BuPaIndustry on $projection.BusinessPartner = _BuPaIndustry.BusinessPartner
{

key BusinessPartner,
  @ObjectModel.readOnly: true
  _CustomerToBusinessPartner.Customer,
  @ObjectModel.readOnly: true
  _SupplierToBusinessPartner.Supplier,
  AcademicTitle,
  AuthorizationGroup,
  BusinessPartnerCategory,
  @ObjectModel.readOnly: true
  BusinessPartnerFullName,
  BusinessPartnerGrouping,
  @ObjectModel.readOnly: true
  BusinessPartnerName,
  @ObjectModel.readOnly: true
  BusinessPartnerUUID,
  CorrespondenceLanguage,
  @ObjectModel.readOnly: true
  CreatedByUser,
  @ObjectModel.readOnly: true
  CreationDate,
  @ObjectModel.readOnly: true
  CreationTime,
  FirstName,
  FormOfAddress,
  Industry,
  InternationalLocationNumber1,
  InternationalLocationNumber2,
//  @API.element.releaseState:#DEPRECATED
  IsFemale,
//  @API.element.releaseState:#DEPRECATED
  IsMale,
  IsNaturalPerson,
//  @API.element.releaseState:#DEPRECATED
  IsSexUnknown,
//  @API.element.successor
  GenderCodeName,
  Language,
  @ObjectModel.readOnly: true
  LastChangeDate,
  @ObjectModel.readOnly: true
  LastChangeTime,
  @ObjectModel.readOnly: true
  LastChangedByUser,
  LastName,
  LegalForm,
  OrganizationBPName1,
  OrganizationBPName2,
  OrganizationBPName3,
  OrganizationBPName4,
  OrganizationFoundationDate,
  OrganizationLiquidationDate,
  SearchTerm1,
  SearchTerm2,
  AdditionalLastName,
  BirthDate,
  BusinessPartnerBirthDateStatus,
  BusinessPartnerBirthplaceName,
  BusinessPartnerIsBlocked,
  BusinessPartnerType,
  @ObjectModel.readOnly: true
  ETag,
  GroupBusinessPartnerName1,
  GroupBusinessPartnerName2,
  @ObjectModel.readOnly: true
  IndependentAddressID,
  InternationalLocationNumber3,
  MiddleName,
  NameCountry,
  NameFormat,
  PersonFullName,
  @ObjectModel.readOnly: true
  PersonNumber,
  IsMarkedForArchiving,
  @Consumption.hidden: true
  IsBusinessPurposeCompleted,
  BusinessPartnerIDByExtSystem,
  _BusinessPartner.vbund as TradingPartner,

  _Customer,
  _Supplier,
  _BusinessPartnerAddress,
  _BusinessPartnerRole,
  _BusinessPartnerTax,
  _BusinessPartnerBank,
  _BuPaIdentification,
  _BusinessPartnerContact,
  _BuPaIndustry
    
}

 

ZI_BP_SUPPLIER

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'CDS View for Supplier View'
define view entity ZI_BP_SUPPLIER as select from I_Supplier as BpSupplier

association to parent ZI_BP_PARENT as _BpParent on $projection.Supplier = _BpParent.BusinessPartner

association [0..*] to A_SupplierCompany as _SupplierCompany on $projection.Supplier =  _SupplierCompany.Supplier

association [0..*] to A_SupplierPurchasingOrg as _SupplierPurchasingOrg on $projection.Supplier =  _SupplierPurchasingOrg.Supplier

association [0..*] to A_SupplierText as _SupplierText on $projection.Supplier = _SupplierText.Supplier {
    key Supplier,
AlternativePayeeAccountNumber, 
AuthorizationGroup, 
@ObjectModel.readOnly: true
CreatedByUser,
@ObjectModel.readOnly: true 
CreationDate, 
Customer, 
PaymentIsBlockedForSupplier, 
PostingIsBlocked, 
PurchasingIsBlocked,  
SupplierAccountGroup, 
@ObjectModel.readOnly: true 
SupplierFullName,  
@ObjectModel.readOnly: true
SupplierName, 
VATRegistration,
BirthDate,  
@ObjectModel.readOnly: true
ConcatenatedInternationalLocNo, 
DeletionIndicator, 
FiscalAddress, 
@ObjectModel.readOnly: true
Industry, 
InternationalLocationNumber1, 
InternationalLocationNumber2, 
InternationalLocationNumber3, 
IsNaturalPerson,  
ResponsibleType, 
SuplrQltyInProcmtCertfnValidTo, 
SuplrQualityManagementSystem, 
SupplierCorporateGroup, 
SupplierProcurementBlock, 
@ObjectModel.readOnly: true
TaxNumber1, 
@ObjectModel.readOnly: true
TaxNumber2, 
@ObjectModel.readOnly: true
TaxNumber3, 
@ObjectModel.readOnly: true
TaxNumber4, 
@ObjectModel.readOnly: true
TaxNumber5, 
TaxNumberResponsible, 
TaxNumberType, 
SuplrProofOfDelivRlvtCode,
 @Consumption.hidden: true
IsBusinessPurposeCompleted,
BR_TaxIsSplit,

/*associations*/
_SupplierCompany,
_SupplierPurchasingOrg,
_SupplierText,
_BpParent
}

 

Step 2 : Activate both the views together – Obligatory Step

 

Step 3 : Create two corresponding Projection Views for the CDS Views for previously created Views

Projection View : ZC_BP_SUPPLIER on ZI_BP_PARENT

and ZC_BP_SUPPLIER on ZI_BP_SUPPLIER

@EndUserText.label: 'Projection View for ZI_BP_PARENT'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@Search.searchable: true
@Metadata.allowExtensions: true
define root view entity ZC_BP_PARENT 
as projection on ZI_BP_PARENT as BPartner {
    key BusinessPartner,
    @Search.defaultSearchElement: true
    Customer,
    @Search.defaultSearchElement: true
    Supplier,
    @Search.defaultSearchElement: true
    AcademicTitle,
    AuthorizationGroup,
    BusinessPartnerCategory,
    BusinessPartnerFullName,
    BusinessPartnerGrouping,
    BusinessPartnerName,
    BusinessPartnerUUID,
    CorrespondenceLanguage,
    CreatedByUser,
    CreationDate,
    CreationTime,
    FirstName,
    FormOfAddress,
    Industry,
    InternationalLocationNumber1,
    InternationalLocationNumber2,
    IsFemale,
    IsMale,
    IsNaturalPerson,
    IsSexUnknown,
    GenderCodeName,
    Language,
    LastChangeDate,
    LastChangeTime,
    LastChangedByUser,
    LastName,
    LegalForm,
    OrganizationBPName1,
    OrganizationBPName2,
    OrganizationBPName3,
    OrganizationBPName4,
    OrganizationFoundationDate,
    OrganizationLiquidationDate,
    SearchTerm1,
    SearchTerm2,
    AdditionalLastName,
    BirthDate,
    BusinessPartnerBirthDateStatus,
    BusinessPartnerBirthplaceName,
    BusinessPartnerIsBlocked,
    BusinessPartnerType,
    ETag,
    GroupBusinessPartnerName1,
    GroupBusinessPartnerName2,
    IndependentAddressID,
    InternationalLocationNumber3,
    MiddleName,
    NameCountry,
    NameFormat,
    PersonFullName,
    PersonNumber,
    IsMarkedForArchiving,
    IsBusinessPurposeCompleted,
    BusinessPartnerIDByExtSystem,
    TradingPartner,
    /* Associations */
    _BuPaIdentification,
    _BuPaIndustry,
    _BusinessPartnerAddress,
    _BusinessPartnerBank,
    _BusinessPartnerContact,
    _BusinessPartnerRole,
    _BusinessPartnerTax,
    _Customer,
    _Supplier : redirected to composition child ZC_BP_SUPPLIER
}

 

@EndUserText.label: 'Projection View for ZI_BP_SUPPLIER'
@AccessControl.authorizationCheck: #NOT_REQUIRED
@Search.searchable: true
@Metadata.allowExtensions: true
define view entity ZC_BP_SUPPLIER 
as projection on ZI_BP_SUPPLIER {
    key Supplier,
    @Search.defaultSearchElement: true
    AlternativePayeeAccountNumber,
    @Search.defaultSearchElement: true
    AuthorizationGroup,
    CreatedByUser,
    CreationDate,
    Customer,
    PaymentIsBlockedForSupplier,
    PostingIsBlocked,
    PurchasingIsBlocked,
        @Search.defaultSearchElement: true
    SupplierAccountGroup,
        @Search.defaultSearchElement: true
    SupplierFullName,
    SupplierName,
    VATRegistration,
    BirthDate,
    ConcatenatedInternationalLocNo,
    DeletionIndicator,
    FiscalAddress,
    Industry,
    InternationalLocationNumber1,
    InternationalLocationNumber2,
    InternationalLocationNumber3,
    IsNaturalPerson,
    ResponsibleType,
    SuplrQltyInProcmtCertfnValidTo,
    SuplrQualityManagementSystem,
    SupplierCorporateGroup,
    SupplierProcurementBlock,
    TaxNumber1,
    TaxNumber2,
    TaxNumber3,
    TaxNumber4,
    TaxNumber5,
    TaxNumberResponsible,
    TaxNumberType,
    SuplrProofOfDelivRlvtCode,
    IsBusinessPurposeCompleted,
    BR_TaxIsSplit,
    /* Associations */
    _BpParent : redirected to parent ZC_BP_PARENT,
    _SupplierCompany,
    _SupplierPurchasingOrg,
    _SupplierText
}

 

Step 4 : Create a Business Services

 

Create%20Business%20Service%20Definition

Create Business Service Definition

 

@EndUserText.label: 'Business Partner Service Definitions'
define service ZAPI_BUSINESS_PARTNER {
  expose ZC_BP_PARENT as BPartner;
  expose ZC_BP_SUPPLIER as Supplier;
  expose A_Customer;
  expose A_SupplierCompany;
  expose A_SupplierDunning;
  expose A_BuPaIdentification;
  expose A_BusinessPartnerBank;
  expose A_BusinessPartnerContact;
  expose A_BPContactToFuncAndDept;
  expose A_BusinessPartnerAddress;
  expose A_AddressEmailAddress;
  expose A_AddressFaxNumber;
  expose A_AddressPhoneNumber;
  expose I_CustomerToBusinessPartner;
  expose I_SupplierToBusinessPartner;
  expose A_SupplierPurchasingOrg;
  expose A_SupplierPurchasingOrgText;
}

 

Step 5 :

Create a Business Service Binding

 

Create%20a%20Business%20Service%20Binding

Create a Business Service Binding

 

Choose V4 as Binding Type

Add%20the%20Service%20Definition%20to%20the%20Binding

Add the Service Definition to the Binding

 

Now let us publish the service : Click on Publish Button

Publish%20oDATA%20V4

Publish oDATA V4

 

After Publishing check the child nodes :

 

CDS%20Associations%20are%20automatically%20visible%20in%20the%20service

CDS Associations are automatically visible in the service

 

As you can see there are Parent and Child relationship visible. The associations of the CDS View can be used for multiple $expand and $filter works perfectly with Parent and Child and further child relationship

You can publish the service in Gateway Using below SAP Note instruction

2948977 – How to Register OData V4 Service in SAP Gateway System?

Link :  https://launchpad.support.sap.com/#/notes/2948977

Test Results:

Test URL in your GATEWAY CLIENT will be something like this :

Case 1 : 

 

The  service is going to search the Business Partner details who has an email noidea@gmail.com

/sap/opu/odata4/sap/zapi_business_partner_srv/srvd_a2x/sap/zapi_business_partner/0001/BPartner?$top=10&$filter=_BusinessPartnerAddress/any(a: a/_EmailAddress/any(e: e/EmailAddress eq ‘noidea@gmail.com’))&$expand=_BusinessPartnerAddress

{
    "@odata.context": "$metadata#BPartner(_BusinessPartnerAddress())",
    "@odata.metadataEtag": "W/\"20210408204812\"",
    "value": [
        {
            "BusinessPartner": "12345",
            "Customer": "12345",
            "Supplier": "12345",
            "AcademicTitle": "",
            "AuthorizationGroup": "",
            "BusinessPartnerCategory": "2",
            "BusinessPartnerFullName": "NO IDEA Unlimited",
            "BusinessPartnerGrouping": "ZIDEA",
            "BusinessPartnerName": "NO IDEA",
            "BusinessPartnerUUID": "rewtret435435435435345",
            "CorrespondenceLanguage": "",
            "CreatedByUser": "ABCD",
            "CreationDate": "2019-04-18",
            "CreationTime": "12:53:28",
            "FirstName": "",
            "FormOfAddress": "",
            "Industry": "",
            "InternationalLocationNumber1": "0",
            "InternationalLocationNumber2": "0",
            "IsFemale": false,
            "IsMale": false,
            "IsNaturalPerson": "",
            "IsSexUnknown": false,
            "GenderCodeName": "",
            "Language": "",
            "LastChangeDate": "2021-02-22",
            "LastChangeTime": "18:53:53",
            "LastChangedByUser": "ABCDE",
            "LastName": "",
            "LegalForm": "",
            "OrganizationBPName1": "NO IDEA Unlimited",
            "OrganizationBPName2": "NO IDEA Unlimited",
            "OrganizationBPName3": "",
            "OrganizationBPName4": "",
            "OrganizationFoundationDate": null,
            "OrganizationLiquidationDate": null,
            "SearchTerm1": "NO",
            "SearchTerm2": "NO IDEA",
            "AdditionalLastName": "",
            "BirthDate": null,
            "BusinessPartnerBirthDateStatus": "",
            "BusinessPartnerBirthplaceName": "",
            "BusinessPartnerIsBlocked": false,
            "BusinessPartnerType": "",
            "ETag": "12345678",
            "GroupBusinessPartnerName1": "",
            "GroupBusinessPartnerName2": "",
            "IndependentAddressID": "12345",
            "InternationalLocationNumber3": "0",
            "MiddleName": "",
            "NameCountry": "",
            "NameFormat": "",
            "PersonFullName": "",
            "PersonNumber": "",
            "IsMarkedForArchiving": false,
            "BusinessPartnerIDByExtSystem": "",
            "TradingPartner": "",
            "_BusinessPartnerAddress": [
                {
                    "BusinessPartner": "12345",
                    "AddressID": "12345",
                    "ValidityStartDate": "9999-12-31T23:59:59Z",
                    "ValidityEndDate": "9999-12-31T23:59:59Z",
                    "AuthorizationGroup": "",
                    "AddressUUID": "3232-ferewrew-erwerew",
                    "AdditionalStreetPrefixName": "",
                    "AdditionalStreetSuffixName": "",
                    "AddressTimeZone": "CST",
                    "CareOfName": "",
                    "CityCode": "",
                    "CityName": "NOT SURE",
                    "CompanyPostalCode": "",
                    "Country": "US",
                    "County": "",
                    "DeliveryServiceNumber": "",
                    "DeliveryServiceTypeCode": "",
                    "District": "",
                    "FormOfAddress": "",
                    "FullName": "NO IDEA FULL NAME",
                    "HomeCityName": "",
                    "HouseNumber": "",
                    "HouseNumberSupplementText": "",
                    "Language": "EN",
                    "POBox": "",
                    "POBoxDeviatingCityName": "",
                    "POBoxDeviatingCountry": "",
                    "POBoxDeviatingRegion": "",
                    "POBoxIsWithoutNumber": false,
                    "POBoxLobbyName": "",
                    "POBoxPostalCode": "",
                    "Person": "",
                    "PostalCode": "12345",
                    "PrfrdCommMediumType": "",
                    "Region": "",
                    "StreetName": "XYZ",
                    "StreetPrefixName": "",
                    "StreetSuffixName": "",
                    "TaxJurisdiction": "12345678",
                    "TransportZone": "",
                    "AddressIDByExternalSystem": "",
                    "CountyCode": "",
                    "TownshipCode": "",
                    "TownshipName": "",
                    "SAP__Messages": [],
                    "_EmailAddress": [
                        {
                            "AddressID": "12345",
                            "Person": "",
                            "OrdinalNumber": "1",
                            "IsDefaultEmailAddress": true,
                            "EmailAddress": "NOIDEA@GMAIL.COM",
                            "SearchEmailAddress": "NOIDEA@GMAIL.COM",
                            "AddressCommunicationRemarkText": "",
                            "SAP__Messages": []
                        }
                    ]
                }
            ]
        }
    ]
}

Case 2 :

We can $expand from BusinesssPartner(BPartner) Header to Child _Supplier and its following child _SupplierCompany

Service is going to expand all the child nodes of the parent

/sap/opu/odata4/sap/zapi_business_partner_srv/srvd_a2x/sap/zapi_business_partner/0001/BPartner?$expand=_Supplier($expand=_SupplierCompany),_BusinessPartnerAddress($expand=_EmailAddress)

 

 

Summary :

  1. We have learnt that we can expose CDS Views directly as services by using Service Definition and Service Binding
  2. We can create View Entities and Projection of View Entries and expose them as service
  3. We can create a composite child relationship with Parent view entity so that we can get a parent – child JSON structure
  4. By Exposing the additional CDS Views used in the associations of the entity view , we can automatically inherit the parent and child relationship tied to the main parent – Case 2 – Expand example should be able to  illustrate that since we are able to expand associations which were not part of composition child.

 

Please share your thoughts on the service creation and collaborate with easy and faster coding in ABAP API Development

 

Assigned Tags

      10 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Shailaja Ainala
      Shailaja Ainala

      Nice blog.

      Author's profile photo Vaibhav Goel
      Vaibhav Goel

      Great Blog Chethan !

      Can you please let me know what version of SAP S4 you are on ?

      I am not getting some options in my system.

      We are on SAP_GWFND 754 0003 SAPK-75403INSAPGWFND SAP Gateway Foundation

      Author's profile photo chethan santhahalli ningegowda
      chethan santhahalli ningegowda
      Blog Post Author

      You should be on S/4 HANA 2020 to avail this feature

      Author's profile photo Tjarliman Rusadi
      Tjarliman Rusadi

      Hi Chethan,

       

      Really a nice blog.

      I have the same question.

      I am using SAP HANA 2020, but still I cannot see that options in my Eclipse (ADT). Dont have option to generate OData V4.

      Any idea what could be the reason ?

      Author's profile photo Prakash Varun
      Prakash Varun

      Can you help explaining the SAP Message Tag, how to update it.

       

      "SAP__Messages": []

       

      Thanks

      Prakash Varun

      Author's profile photo Mantri Shekar
      Mantri Shekar

      Hi Chetan,

      In teh same way i also created an App and Created Behaviour Definition to enable Transactional Capabilities. But after that same Consumption view i created one service Binding for Odata V2 and another for Odata v4. Now when i execute for OData V2 am getting radio buttons but for Odata V4 am getting Checkboxes.

      May i know any reason for this dual behaviour of the same Behaviour definition with different versions of Odata Binding.

      Please find the attached screenshot

       

       

      Author's profile photo chethan ningegowda
      chethan ningegowda

      Hi Mantri... I have designed this for webservice operations and not reporting purpose.

      Author's profile photo Torsten Manhardt
      Torsten Manhardt

      Hi,

      i am building a RAP Service in a S/4HANA 2020 System.

      If i publish the service as oData v2, i can call actions/functions, which i defined in the behaviour definition.

      If i publish the service as oData v4, the server responds with a 'Resource not found for segment ' error.

      Perhaps there is an issue in S/4HANA 2020, or perhaps i used a wrong URL.

      Could anyone describe me, how to build the correct URL for an action, please?

      Thanks in advance,
      Torsten

       

      Author's profile photo Torsten Manhardt
      Torsten Manhardt

      I solved the problem by my own, with help of following blog:

      https://blogs.sap.com/2021/08/21/cheat-sheet-for-uri-patterns-for-calling-odata-actions-and-functions/

      To call bound functions or bound actions, you have to prefix them with the service name, including the namespace.

       

      Author's profile photo Achintya Gupta
      Achintya Gupta

      What about POST , UPDATE and DELETE operations in Behaviour Definition and Implementation ?