Skip to Content

In this blog, I would like to share how to do business object update operation via Service Layer API.

As Service Layer follows OData protocol, it uses HTTP verb PATCH or PUT to update entities. The semantic difference is:

  • A PUT request indicates a replacement update. All property values with those specified in the request body will be replaced. Missing properties are set to their default values. Be aware of the potential for data-loss in round-tripping properties that the client may not know about in advance.
  • A PATCH indicates a differential update. Those property values that are specified in the request body will be exactly replaced. Missing properties are not altered. PATCH provides more resiliency between clients and services by directly modifying only those values specified by the client.

Generally, PATCH is strongly recommended while PUT is deprecated.

1. Update a Primitive Property

CardName is a primitive property of the entity BusinessPartners. To update the name of the customer “c1”, send a PATCH request as below:

PATCH /BusinessPartners('c1')

{ "CardName": "Updated customer name" }

On success, HTTP code 204 is returned without content.

HTTP/1.1 204 No Content 
2. Update a Complex Property

Only those properties of the complex type specified in the payload of the PATCH request will be modified. For example, AddressExtension is a complex property of the order entity. The original value of this property is as below:

{

"AddressExtension": {

"ShipToStreet": null,

"ShipToStreetNo": null,

"ShipToBlock": null,

"ShipToBuilding": null,

"ShipToCity": null,

"ShipToZipCode": null,

"ShipToCounty": null,

"ShipToState": null,

"ShipToCountry": null,

"ShipToAddressType": null,

"BillToStreet": null,

"BillToStreetNo": null,

"BillToBlock": null,

"BillToBuilding": "",

"BillToCity": null,

"BillToZipCode": null,

"BillToCounty": null,

"BillToState": null,

"BillToCountry": "US",

"BillToAddressType": null,

"ShipToGlobalLocationNumber": null,

"BillToGlobalLocationNumber": null,

"ShipToAddress2": null,

"ShipToAddress3": null,

"BillToAddress2": null,

"BillToAddress3": null

}

}

Send a PATCH request with the following payload to update the AddressExtension:

PATCH /b1s/v1/Orders(9)

{

"AddressExtension": {

"ShipToStreet": "Chenhui",

"ShipToStreetNo": "1001",

"ShipToBuilding": "pvg02",

"ShipToCity": "Shanghai",

"ShipToCounty": "China"

}

}

On success, HTTP code 204 is returned without content.

HTTP/1.1 204 No Content 

Then send a HTTP GET request to retrieve the changed AddressExtension, Service Layer will return:

{ ...

"AddressExtension": {

"ShipToStreet": "Chenhui",

"ShipToStreetNo": "1001",

"ShipToBlock": null,

"ShipToBuilding": "pvg02",

"ShipToCity": "Shanghai",

"ShipToZipCode": null,

"ShipToCounty": "China",

"ShipToState": null,

"ShipToCountry": null,

"ShipToAddressType": null,

"BillToStreet": null,

"BillToStreetNo": null,

"BillToBlock": null,

"BillToBuilding": "",

"BillToCity": null,

"BillToZipCode": null,

"BillToCounty": null,

"BillToState": null,

"BillToCountry": "US",

"BillToAddressType": null,

"ShipToGlobalLocationNumber": null,

"BillToGlobalLocationNumber": null,

"ShipToAddress2": null,

"ShipToAddress3": null,

"BillToAddress2": null,

"BillToAddress3": null

}

}

3. Update a Collection Property

According to the OData standard, since collection members have no individual identity, PATCH is not supported for collection properties.

However, this rule does not suit the B1 data model well, as the collection members of B1 business objects have individual identity. Considering this fact, Service Layer extends the PATCH to support collection properties.

[Add and Update elements in Collection]

Before 9.1H PL12, Service Layer allows to add and update the elements in a collection property. Although removing element in collection is also possible, it is supported using PUTmethod, which is not user-friendly and not recommended.

Assume there is an order with DocEntry = 9 and having one document line as below:

{

...

"DocEntry": 9,

"DocNum": 4,

"CardCode": "bp004",

"DocDate": "2016-04-23",

"DocDueDate": "2016-04-23",

"DocumentLines": [

{

"LineNum": 0,

"ItemCode": "i001",

"UnitPrice": 10,

"Quantitiy": 1

}

]

...

}

To add a new document line and update the UnitPrice from an existing document line in one round trip, send a request as below:

PATCH /b1s/v1/Orders(9)

{

  "DocumentLines": [

    {

      "LineNum": 0,

      "ItemCode": "i001",

      "UnitPrice": 20,

      "Quantitiy": 1

    },

    {

      "ItemCode": "i002",

      "UnitPrice": 10,

      "Quantitiy": 2

    }

  ]

}

On success, HTTP code 204 is returned without content.

HTTP/1.1 204 No Content 

Then send a HTTP GET request to check the changed line, Service Layer will return:

{

  ...

  "DocumentLines": [

    {

      "LineNum": 0,

      "ItemCode": "i001",

      "UnitPrice": 20,

      "Quantitiy": 1

    },

    {

      "LineNum": 1,

      "ItemCode": "i002",

      "UnitPrice": 10,

      "Quantitiy": 2

    }

  ]

  ...

}

To delete a document line (e.g. the first line), two steps are needed.

1. Retrieve this order by sending below request:

GET /b1s/v1/Orders(9) 

2. Get a copy of the response from step 1 and remove the first document line. Then send the rest content to Service Layer by:

PUT /b1s/v1/Orders(9)

{

...

"DocumentLines": [

{

"LineNum": 1,

"ItemCode": "i002",

"UnitPrice": 10,

"Quantitiy": 2

}

],

...

}

On success, HTTP code 204 is returned without content:

HTTP/1.1 204 No Content 

Then send a HTTP GET request to check the changed lines, Service Layer will return:

{

...

"DocumentLines": [

{

"LineNum": 1,

"ItemCode": "i002",

"UnitPrice": 10,

"Quantitiy": 2

}

],

...

}

[Add, Update and Delete elements in collection]

Since 9.1H PL12, Service Layer provides a new way to support removing an element in the collection property by adding a special header in the request:

B1S-ReplaceCollectionsOnPatch=true

Assume there is one business partner with CardCode = ‘bp004’ and having two addresses (e.g. “addr1” and “addr2”) as below:

{

...

  "BPAddresses": [

    {

      "AddressName": "addr1",

      "Country": "US",

      "AddressType": "bo_BillTo",

      "BPCode": "bp004",

      "RowNum": 0,

....

    },

    {

      "AddressName": "addr2",

      "Country": "US",

      "AddressType": "bo_BillTo",

      "BPCode": "bp004",

      "RowNum": 1,

...

    }

  ],

...

}

To add a new address (e.g. “addr3”), update the existing address from “addr2” to “ddr2_new”, and remove address “addr1” in one round trip, send a request as below:

PATCH /b1s/v1/BusinessPartners('bp004')

B1S-ReplaceCollectionsOnPatch=true

{

  "BPAddresses": [

    {

      "AddressName": "addr2_new",

      "AddressType": "bo_BillTo",

      "BPCode": "bp004",

      "RowNum": 1

    },

    {

      "AddressName": "addr3",

      "AddressType": "bo_BillTo",

      "BPCode": "bp004"

    }

  ]

}

On success, HTTP code 204 is returned without content.

HTTP/1.1 204 No Content 

Then send a HTTP GET request to check the addresses, Service Layer will return

{

  ...

  "BPAddresses": [

    {

      "AddressName": "addr2_new",

      "Country": "US",

      "AddressType": "bo_BillTo",

      "BPCode": "bp004",

      "RowNum": 1,

      ...

    },

    {

      "AddressName": "addr3",

      "Country": "US",

      "AddressType": "bo_BillTo",

      "BPCode": "bp004",

      "RowNum": 2,

      ...

    }

  ],

...

}

[Note]

  • Read only properties (e.g. CardCode) are not possible to be updated. They are ignored silently if assigned in the request.
  • (Version Note: since 9.1 patch 02) The HTTP verb MERGE is also supported and behavior like PATCH. It’s not recommended to use as it’s not a standard HTTP verb. It’s just for compatibility like supporting Microsoft WCF framework.

In sum, Service Layer mainly uses the OData PATCH verb to update the B1 business objects . Actually, Service Layer also supports PUT. As PUT does not conform to the update semantics defined in B1 data model, it is not suggested to use.

To report this post you need to login first.

15 Comments

You must be Logged on to comment or reply to a post.

  1. Chin Chong

    Hi Andy,

       I need your assistance

    1. How to get the BusinessPartner, for each sales order.  I tried  ServiceRoot/Orders?expand=BusinessPartners, but this does not work.

    2. I notice in the metadata, that there is no EntityType Name=”Order”. However there is a EntityType SAPB1.Document Name=”Orders”

      Please help. Thank you.

       Chong Chin

    (0) 
    1. Andy Bai Post author

      hi, Chin:


      1. It is allowed to retrieve the BusinessPartner properties of all orders, just like this:

      GET Orders?$select=*,BusinessPartner&$expand=BusinessPartner

      2. Yes. You are right. Orders is an instance of document, just like Invoices is an instance of document as well. Document is treated as an entity type while Orders is considered to be an entity set.

      thanks

      andy

      (0) 
      1. Chin Chong

        Hi Andy,

             Thank you for your quick response. I tried and it works. On a separate note, we are trying to use SAPB1 service layer (B1SL) from SAP Web IDE. SAP Web IDE is not integrated with B1SL, so we probably have to  use Java Script to call B1SL, convert data to JSON, and use them to create xml views (SAP Web IDE, including its layout editor uses xml views primarily).

        Are you aware of any code examples for accessing B1SL within Web IDE?

           Thank you again.

           Chong Chin

        (0) 
        1. Andy Bai Post author

          Not known much about web IDE.

          It is appreciated if you could provide some knowledge about how to use build up web IDE.

          Thanks

          Andy

          (0) 
          1. Chin Chong

            Hi Andy,

            1.    SAP uses SAP UI5 to develop user interfaces in Friori /UI5 applications. The documentation is here

                  SAPUI5 SDK – Demo Kit.

               It consists of all the visual controls, API and a very useful tutorial “Walk Through”. Unlike the standard SAPB1, developed with DI API and UI API and .NET, SAP UI5 is based on HTML5 / JQuery / Java Script.

            2.   To develop full “native” applications then, we have to use Frori, Hana database, and SAP Business service layer i.e. we should not use .NET or DI / UI API anymore.

            3.   The IDE to develop UI5 /Friori is SAP Web IDE (not Hana  Studio). There are 2 versions, one a cloud version, and the other for local installation. The local installation is here

            http://scn.sap.com/community/developer-center/front-end/blog/2015/04/21/install-sap-web-ide-locally-for-trial-windows-version


            Please let me know if you need help.

            Regards

            Chong Chin

            (0) 
              1. Andy Bai Post author

                Thanks for providing such informative materials.

                I will find some time to evaluation how to integrate service layer with web IDE.

                thanks

                Andy

                (0) 
                1. Chin Chong

                  Hi Andy,

                      SAP Web IDE is also intended to be used for other SAP higher back end products.The ODATA integration of these higher SAP back end ERP are already there in SAP Web IDE. However SAP Web IDE ODATA integration to SAP B1 service layer (B1SL) is not ready yet. Hence my question above.

                      Regards

                      Chong Chin

                  (0) 
  2. Chin Chong

    Hi Andy,

      1.   I am trying to get Orders (only some fields), OrderItems (only some fields), BP (only some fields). I succeed only with Orders and BP. But I am unable to select fields for OrderItems.

      2  The following succeeds  >> Orders?$select=DocEntry,DocNum,DocumentLines,BusinessPartner/CardCode,BusinessPartner/Address&$expand=BusinessPartner

         But I only want a few fields in DocumentLines e.g. ItemCode, ItemDescription. I tried several combinations, but all did not work.

        Please help.

        Thank you

        Chong Chin

    (0) 
    1. Andy Bai Post author

      Select properties from the collections is not allowed currently.

      DocumentLines is just a collection.

      Sorry to tell you this.

      (0) 
      1. Chin Chong

        Hi Andy,

             Thanks. This could affect performance, since I need only a few fields and additionally, we have many user defined fields, which are not need for my app. Have you tried using Java Script or Hana SQL Script to retrieve or update? Some of our queries (e.g. with inner SELECT), and checks for IFNULL condition, are quite complicated. Also our update involves several tables (for UDT) with multiple conditions? Or do you know of any sites that provide information on using JavaScript for complicated queries.

           Thanks.

           Chong Chin

        (0) 
        1. Andy Bai Post author

          1. Yes. It is not good for the performance. We may implement this in some patch of 9.2

          2. How complex is your query? Could you give some typical examples? Service layer is           able to support mutiple BO Join in the future patch.

          3. JavaScript are running on the client side. They may be provided by some odata libraries.

              I do not believe the client javascript can help you to do complex queries. The key is the       server is able to parsing the complex queries.

          Thanks

          andy

          (0) 
          1. Chin Chong

            Hi Andy,

            1.  This is an example, where NCM_APPROVAL is a UDT and the fields U_XX are UDF. NCM_APPROVAL has multiple rows for 1 Sales Order.

            SELECT   T1.*, IFNULL(CAST(YEAR(T1.””U_Updated””) AS VARCHAR) || Right(’00’ || CAST(MONTH(T1.””U_Updated””) AS VARCHAR(2)),2) || Right(’00’ || CAST(EXTRACT(DAY FROM (T1.””U_Updated””)) AS VARCHAR(2)),2),”) AS “”LastUpdated”” ,

                   CASE T1.””U_Status””   WHEN ‘P’ THEN ‘Pending’

                                          WHEN ‘A’ THEN ‘Approved’

                                          WHEN ‘R’ THEN ‘Rejected’ END AS “”Status””

                   CASE T1.””U_Status”” WHEN ‘P’ THEN “”U_SubRmrk””

                                        WHEN ‘A’ THEN “”U_AppRmrk””

                                        WHEN ‘R’ THEN “”U_RejRmrk””  END AS “”Remarks””

                  CASE T1.””U_Status””   WHEN ‘P’ THEN “”U_SubTime””

                                         WHEN ‘A’ THEN “”U_AppTime””

                                         WHEN ‘R’ THEN “”U_RejTime””  END AS “”LastTime””

                    

            FROM  oCompany.CompanyDB & “.””@NCM_APPROVAL”” T1 LEFT OUTER JOIN “ & oCompany.CompanyDB & “ORDR” T2 ON T1.””U_DocEntry”” = T2.””DocEntry””

                        

            WHERE T1.””U_DocEntry”” = ‘” & ORDR“‘ AND T1.””U_DocType”” = ‘” &                  AND T1.””U_TmpltType”” = ‘GP’ AND

                          T1.””U_Batch”” = (SELECT MAX(H1.””U_Batch””) FROM “ & oCompany.CompanyDB & “.””@NCM_APPROVAL”” H1 WHERE H1.””U_DocEntry”” = ‘” & g_iDocEntry & “‘ AND H1.””U_DocType”” = ‘” & g_iObjectType & “‘  AND H1.””U_TmpltType”” = ‘GP’)

               ORDER BY T1.””U_TmpltCode””, T1.””U_StageLvl””

            2. For the case of update, it is even more complicated, several ancillary methods are called, with multiple tables (read and / or write)

            Seq

            Method Name

            Table (R=Read, W= Write)

            Parameters needed

            1

            IsValidDocument

            ORDR – R

            DocEntry

            2

            IsValidToApprove

            NCM_APPROVAL – R

            U_DocEntry, U_StageLvl, U_Status, U_ImpltType, U_DocType

            3

            IsVallidAuthorizer

            1. OUSR – R
            2. NCM_ADS

            UserID, U_Delegate, U_TmplCode, U_StageCode

            4

            IsValidSenderEmail

            OUSR – R

            UserID

            5

            IsValidReceipientEmail

            OUSR – R

            6

            SendApprovedApproval

            7

            UpdateTables(A)

            NCM_APPROVAL – W

            8

            InsertGPLog

            1. NCM_GPA_LOG – W
            2. NCM_GP1_LOG – W
            3. 1.NCM_GPA_LOG – Code, Name, U_DocEntry, U_DocNum, U_CardCode, U_DocDate, U_DocTotal, U_GrossProfit, U_Discount
            1. NCM_GP1_LOG – Code, U_DocEntry, U_LineNum, U_GPACode, U_ItemCode, U_Quantity, U_StockPrice, U_BasePrice, U_SalesPrice, U_LineTotal, U_GrossProfit, U_Discount

            9

            UpdateDocument

            ORDR – W

            3.     I believe that if I hit a constraint on ODATA syntax, then I should try to build the logic in JavaScript on the server, using Hana Studio or Development Web Bench. But UI5 development is typically carried out in Web IDE (client).  How then do I call from Web IDE to a JavaScript function on the server?

              Thank you.

              Chong Chin

            (0) 
            1. Andy Bai Post author

              For the 1st case, service layer cannot provide exactly the same functionality.

              service layer is able to be enhanced to support join but may not be able to support ifnull and property concatenation.

              Actually, service layer is working on the integration with v8, which allows customers to embed java script inside service layer. Maybe you can write the equivalent script.

              (0) 
              1. Chin Chong

                Hi Andy,

                    Thanks. Are there some examples of embedding java script inside the service layer, that I can view in SCN?

                    Chong Chin

                (0) 

Leave a Reply