Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
ThomasSchneider
Product and Topic Expert
Product and Topic Expert

With this post I want to continue the blog series “A Programming Model for Business Applications”. If you havn´t read the first part in the series, I recommend that you read the first part go back to A Programming Model for Business Applications (1): Assumptions, Building Blocks, and Example App and read it first.

In this part, I will discuss the implementation of the entity model introduced in Figure 2 of part 1 with HANA Core Data Services (HANA CDS).


Note: In HANA SPS8 the implementation suffers from a couple of limitations in HANA CDS, in particular:



  • Missing support for references between CDS files

  • Missing support for “unmanaged associations” (= ability to define an association on existing elements using “on”)

  • Missing support for “Boolean” data type

  • Missing support for calculated elements in entities


According to my knowledge, these limitations will be removed in SPS9, so it is worth checking the SPS9 documentation as soon as it is available!






I implement the model in one CDS file (due to a restriction in HANA SPS8) in four sub-contexts. So, here is the stub of my CDS file:

namespace XYZ;

@Schema: 'XYZ'

context bo {

  context common {

   (...)

  };

  context address {

    (...)

  }; // end of context address

  context businesspartner {

   (...)

}; // end of context businesspartner

  context salesterritorymgmt {

   (...)

  }; // end of context salesterritorymgmt

  context salesorder {

   (...)

}; // end of context salesorder

}; // end of context bo

Context Common

In the common context, I implement common data types.

Code lists are implemented as entities, for example:

  entity Currency {

    key Code          : String(5000);

    DescriptionText   : String(5000);

  };

Code contains the code representation, for example “EN” for English, DescriptionText contains the description in default language.

A code list table may not only contain the code and the default description, but also additional elements (attributes), for example:

  entity IncotermsClassification {

    key Code                      : String(5000);

  DescriptionText               : String(5000);

  LocationIsMandatoryIndicator  : bo.common.Indicator; // workaround for "Boolean"

      //            IncotermsClassificationCode - Examples

      //                   CFR    Cost and freight

      //                   EXW    Ex works

      //                   FCA    Free carrier

     };

As CDS does not support yet the data type “Boolean” as a native data type, I have defined an Indicator data type; I use this data type whenever a Boolean is required:

type Indicator : Integer; // WORKAROUND: Boolean is not supported

Structured re-use data types are implemented as follows:

  type Amount {

    Currency : association [0..1] to common.Currency;

    Value       : DecimalFloat;

    };

  type Incoterms {

    Classification          : association to bo.common.IncotermsClassification;

    TransferLocationName    : String(5000);

  };

Sales Order

The header (or main, root) entity of the Sales Order business object is implemented as follows:

  entity SimpleOrder {

        key ID_                       : Integer64;

    SystemAdministrativeData      : bo.common.SystemAdministrativeData;      

    ID                            : String(35);   

    Name                          : String(256);

        //@EndUserText : { label: 'Posting Date'}

    DateTime                      : UTCDateTime;

    Currency                      : association [0..1] to bo.common.Currency;

    FulfilmentBlockingReason      : association [0..1] to bo.common.FulfilmentBlockingReason;

    DeliveryPriorityCode          : association [0..1] to bo.common.Priority;

    Incoterms                     : bo.common.Incoterms;

    RequestedFulfilmentDateTime   : UTCDateTime;

    PaymentFormCode               : association [0..1] to bo.common.PaymentForm;

    Status                        : bo.simpleorder.Status;

  };

Discussion:

1) For each BO entity, we introduce a technical key of type big integer (Integer64 in CDS), which is named ID_ (which dangling “_”) and which should not be mixed up with the semantic key, which is in this case represented by the element “ID”. Exposing a unified simple key makes the work much easier in the
business logic and the UI implementation (instead of dealing with the “semantic” key, which may differ from entity to entity (for example many entities have semantic keys that consist of multiple elements).

2) Code lists are represented by associations to code list entities. Please note that the resulting database field has the name FulfilmentBlockingReason.Code.

3) CDS does not allow (in SPS8) defining inline structured elements (so-called Anonymous structure types) within an entity, for example:

entity SimpleOrder {

  (...)

  Status {

   LifeCycleStatus : association [0..1] to salesorder.LifeCycleStatus;

   InvoiceProcessingStatus : association [0..1] to ref1.salesorder.InvoiceProcessingStatus;

  };

As a workaround, I had to define all structured data types using a type statement and refer it in the entity.

4) CDS does not allow (in SPS8) defining calculated fields directly in the entity (limitation for HANA SPS8). We will come to this point later in part 3 of this series.

The Item entity is modelled as follows:

  entity Item {

      key ID_                     : Integer64;

   Parent_ID_                  : Integer64;

   SystemAdministrativeData    : bo.common.SystemAdministrativeData;

   Product                     : association [0..1] to bo.product.Product;

   Quantity                    : common.Quantity;

   RequestedFulfilmentDateTime : UTCDateTime;

   ListPriceAmount             : bo.common.Amount;

   DiscountPercentValue        : Decimal(5,2);

  };

Discussion

The relation to the Sales Order header entity is stored in the element Parent_ID_ (I use here again a dangling “_” to mark this element as “technical”). CDS does not know the construct of a “composition association”, which would be the optimal way of defining the relation between header and item. So I decided to
introduce the Parent_ID_ element. The associations between header and item could be modelled as so-called unmanaged associations in the following way:

In the Sales Order entity:

Item : association [0..*] to salesorder.Item on Item.Parent_ID_ = ID_;

In the Item entity:

_Parent : association [0..1] to salesorder.SalesOrder on _Parent.ID_ = _Parent_ID;

Unfortunately unmanaged associations are not yet available in CDS in HANA SPS8.

The Party is the third entity of the Sales Order business object:

  entity Party {

      key ID_                    : Integer64;

   Parent_ID_                 : Integer64;

   MainIndicator              : bo.common.Indicator;

   PartyRole                  : association [0..1] to bo.common.PartyRole;

      PartyID_                   : Integer64;

   Party                      : association [0..1] to bo.businesspartner.BusinessPartner;

   AddressReference           : association [0..1] to bo.address.Address;

  };

Discussion:

1) Associations from Sales Order to Party and vice versa should be added as unmanaged associations (as discussed for Item).

2) Unmanaged association should be also added to allow direct navigation the Customer, Employee, and other business partners:

      Customer                  : association [0..1] to bo.businesspartner.Customer on Customer.ID_ = PartyID_;

Address

In the address context, we implement the Address entity. This entity shall store addresses for re-use in different business objects.

  entity Address {

    key ID_                       : Integer64;

    AddressType                   : Integer; // TODO code

    PreferredCommunicationMediumType    : association [0..1] to bo.common.CommunicationMediumType;

    GeographicalLocation          : address.GeographicalLocation;       

             // TODO-> [0..*] sub nodes

    DefaultName                   : Name;

    DefaultPostalAddress          : PostalAddress;

    DefaultFacsimile              : Telephone;           

    DefaultConventionalPhone      : Telephone;

    DefaultMobilePhone            : Telephone;

    DefaultEmailURI               : String(5000);    

    DefaultWebURI                 : String(5000);

    DefaultWebCode                : String(1); // TODO Homepage, Facebook, Twitter, LinkedIn, Google+

    DefaultInstantMessagingAccountID    : String(5000);

    DefaultInstantMessagingCode   : String(5000); // TODO: Skype, WhatsApp

    Workplace                     : address.Workplace;

  };

Some of the Elements are structured elements, which are defined in the address context, for example:

  type PostalAddress {

   ScriptCode                : String(1); //TODO C = Chinese, I = Latin, K = Japanese, ...

   DeliveryServiceTypeCode   : String(1); //TODO   Street= '1'; POBox = '2'; Company = '3';

   Country                   : association [0..1] to bo.common.Country;

   Region                    : association [0..1] to bo.common.Region {Code};        

   CountyName                : String(40);

   CityName                  : String(40);

   DistrictName              : String(40);

   PostalCode                : String(10);             

   Street                    : StreetPostalAddress;

   POBox                     : POBoxPostalAddress;

  };

Business Partner

Let’s continue with the Business Partner. For our simplified model we implement the following entities:

  • Business Partner: We ignore the fact that the business partners name and other common elements may change over time and model them in the BusinessPartner “header” entity.
  • Address Information: Sub-Entity with cardinality [0..*] that holds the time- and role-dependent address information of a business partner
  • Customer: customer-specific data in the business partner
  • Employee, similar to the Customer, not shown

  entity BusinessPartner {    

      key ID_                       : Integer64;

   ID                            : String(20);

   CategoryCode                  : businesspartner.CategoryCode;

   Person                        : businesspartner.Person;

   Organization                  : businesspartner.Organization;

   UserID                        : String(255);  

   Status                        : association [0..1] to businesspartner.LifeCycleStatus;

  };

Discussion

1) Associations to sub-entities should be implemented as discussed for the Sales Order:

Customer : association[0..1] to businesspartner.Customer on Customer.Parent_ID_ = ID_;

AddressInformation : association [0..*] to businesspartner.AddressInformation on
AddressInformation
.Parent_ID_ = ID_;

2) A filtered association that points to the current default address should be modelled as unmanaged, filtered association. Filtered associations are also not yet supported by CDS:

CurrentDefaultAddressInformation1 : association [0..*] to businesspartner.AddressInformation on
AddressInformation
.Parent_ID_ = ID_ where where ValidityPeriod.StartDate < now() and where ValidityPeriod.EndDate > now() and DefaultIndicator = 1;

The Customer entity is implemented as:

  entity Customer {

      key ID_                       : Integer64;

   Parent_ID_                    : Integer64;

   Industry                      : association [0..1] to bo.common.Industry;

   ProspectIndicator             : bo.common.Indicator;

   BlockingReasons               : businesspartner.BlockingReasons;

  };

The Address Information entity is implemented as:

  entity AddressInformation{

      key ID_                : Integer64;

   Parent_ID_             : Integer64;

   ValidityPeriod         : businesspartner.ValidityPeriod;

   AddressType            : association [0..1] to bo.common.AddressType;     

   DefaultIndicator       : bo.common.Indicator;

   AddressUsage           : association [0..1] to bo.common.AddressUsage;

      //  Examples:

      //    BILL_TO      Bill-to party

   //    SHIP_TO      Deliv. address

      //    EMPP Employee private address

   Address                : association [0..1] to bo.address.Address;

  };

Sales Territory

The Sales Territory is implemented as follows:

  entity SalesTerritory {

      key ID_                   : Integer64;

   Name                      : String(256);

};

  entity Customer {

      key ID_                   : Integer64;

   Parent_ID_                : Integer64;

   TerritoryAssignmentManualOverrideAllowedIndicator   : bo.common.Indicator;

   Customer                  : association [0..1] to bo.businesspartner.Customer;

  };

Discussion

As mentioned for the previous discussions, unmanaged associations should be added for navigation from parent to child and vice versa.

The last entity that we implement for our example application is used to store the access rights (or in other words, the restrictions) for a user:

  entity RoleAssigmentRestriction {

        key ID_                  : Integer64;

    UserID                   : String(255);

    AccessContextCode        : String(5000);

    Object_ID                : Integer64;

  };

A discussion for this entity will follow in the next part of this series.

In the next blog of this series, I will discuss my implementation of the business object read services (calculated fields and properties): A Programming Model for Business Applications (3): Calculated Elements and Properties