Skip to Content
Technical Articles
Author's profile photo SaiNithesh Gajula

Build SAP Overview Page using SAP HANA Cloud & SAP CAP service annotations in Visual (VS) Code

Hi Everyone,

This blog will help you to create SAP Overview Page (SAP Fiori Elements) using SAP CAP service in Visual Studio Code.

Pre-requisites

Project development

Step 1 : Make sure you have installed CAP generators in VS Code. If not install them.

Step 2 : Open Template Wizard by pressing Ctrl+Shft+P and start typing and select

Step 3 : Select CAP Project and click on Next.

Step 4: Provide the project details. As we are creating only CAP service using Nodejs & HANA Cloud, I am choosing only “Configuration for SAP HANA Deployment” and click on

Step 5: After initializing the project, you should see the following empty folders:

  • app: for UI artifacts
  • db: for the database level schema model
  • srv: for the service definition layer

Step 6: Install required dependencies using npm install.

Step 7: Install HANA Client using – npm install -g hana-cli also execute the command hana-cli createModule

Step 8: Create the data model cds under db layer.

namespace capsrv;

using
{
    Currency,
    managed
}
from '@sap/cds/common';

entity SalesOrderType : managed
{
    key salesOrder : String(5) @title: 'Sales Order ID';
    customerCompanyName : String(40) @title : 'Company Name';
    revenueInLocalCurrency : String(30) @title : 'Gross Amount';
    localCurrency : String(30) @title : 'Currency Code';
    numberOfItems : String(60) @title : 'Number of Items';
}

entity SalesPerSupplierType : managed
{
    key ID : UUID @(Core.Computed:true) @title: 'Supplier';
    supplier : String(10) @title : 'Business Partner ID';
    supplierName : String(80) @title : 'Supplier';
    grossAmountInCompanyCurrency : Decimal(16, 3) @title : 'Revenue';
    netUnitPriceInCompanyCurrency : Decimal(16, 3) @title : 'Average Item Price';
    quantity : Decimal(13, 3) @title : 'Number of Sold Items';
    companyCurrency : String(5) @Common.Label : 'ISO Currency Code' @Common.IsUpperCase: true;
    quantityUnit : String(3) @Common.Label : 'Unit of Measure';
    companyCurrencyShortName : String(15) @Common.Label : 'Short text';
    quantityUnitName : String(10) @Common.Label : 'Measuremt unit text' @Common.QuickInfo : 'Unit of Measurement Text (Maximum 10 Characters)'
}

entity SalesHistoryType : managed
{
    key ID : UUID @(Core.Computed:true);
    creationMonthAsDate : DateTime;
    creationMonth : String(2);
    creationMonth_Text : String(10);
    grossAmountInCompanyCurrency : Decimal(16, 3) @title : 'Revenue';
    companyCurrency : String(5);
    companyCurrency_Text : String(40) @Common.Label : 'Long text';
    referenceAmount : Integer;
}

Step 9: Create a CDS service capfesrv.cds in srv.

using capsrv as cp from '../db/schema';

@path : 'service/cap'
service capService {
    entity SalesOrder       as select from cp.SalesOrderType;
    @readonly
    entity SalesHistory     as select from cp.SalesHistoryType excluding {
        createdAt,
        createdBy,
        modifiedAt,
        modifiedBy
    };
    entity SalesPerSupplier as select from cp.SalesPerSupplierType;
}

Step 10: Now create annotation file under srv layer named as ovpcapfeannotation.cds.

using {capsrv} from './capfesrv';

//Filters
annotate capService.SalesOrder with @(UI : {
    SelectionFields  : [
        salesOrder,
        customerCompanyName,
        numberOfItems
    ],
    SelectionVariant : {
        $Type         : 'UI.SelectionVariantType',
        SelectOptions : [{
            $Type        : 'UI.SelectOptionType',
            PropertyName : localCurrency,
        }],
    },
});

annotate  capService.SalesOrder with {
 modifiedAt @UI.Hidden;
 modifiedBy @UI.Hidden;
 createdAt @UI.Hidden;
 createdBy @UI.Hidden ;
 };

//Donut Chart
annotate capService.SalesPerSupplier with @(UI : {
    Chart #donut                            : {
        $Type               : 'UI.ChartDefinitionType',
        ChartType           : #Donut,
        Description         : 'Donut Chart',
        Measures            : [grossAmountInCompanyCurrency],
        MeasureAttributes   : [{
            $Type     : 'UI.ChartMeasureAttributeType',
            Measure   : grossAmountInCompanyCurrency,
            Role      : #Axis1,
            DataPoint : '@UI.DataPoint#GrossAmountInCompanyCurrency'
        }],
        Dimensions          : [supplier],
        DimensionAttributes : [{
            $Type     : 'UI.ChartDimensionAttributeType',
            Dimension : supplier,
            Role      : #Category
        }]
    },
    PresentationVariant #donutPreVar        : {
        $Type             : 'UI.PresentationVariantType',
        Visualizations    : ['@UI.Chart#donut'],
        MaxItems          : 3,
        IncludeGrandTotal : true,
        SortOrder         : [{
            $Type      : 'Common.SortOrderType',
            Descending : true,
            Property   : grossAmountInCompanyCurrency
        }]
    },
    DataPoint #GrossAmountInCompanyCurrency : {
        $Type                  : 'UI.DataPointType',
        Value                  : grossAmountInCompanyCurrency,
        Title                  : 'Revenue',
        CriticalityCalculation : {
            $Type                   : 'UI.CriticalityCalculationType',
            ImprovementDirection    : #Maximize,
            DeviationRangeHighValue : 1000000,
            DeviationRangeLowValue  : 3000000
        },
        TrendCalculation       : {
            $Type                : 'UI.TrendCalculationType',
            ReferenceValue       : 1000,
            UpDifference         : 10,
            StrongUpDifference   : 100,
            DownDifference       : -10,
            StrongDownDifference : -100
        },
    },
    Identification                          : [{
        $Type : 'UI.DataField',
        Value : grossAmountInCompanyCurrency
    }]
});


annotate  capService.SalesHistory with {
 modifiedAt @UI.Hidden;
 modifiedBy @UI.Hidden;
 createdAt @UI.Hidden;
 createdBy @UI.Hidden;
 companyCurrency @title : 'Currency' @Measures.ISOCurrency : 'Currency';
 companyCurrency_Text @title : '';
 creationMonthAsDate @title : 'Creation Date';
 creationMonth @title : 'Month';
 creationMonth_Text @title : 'Month' @Common.QuickInfo : 'Month Long text';
 referenceAmount @title : 'Amount' ; 
 };
//Line Chart
annotate capService.SalesHistory with @(
    UI.Chart #Line                             : {
        $Type               : 'UI.ChartDefinitionType',
        ChartType           : #Line,
        Description         : 'Line Chart',
        Measures            : [grossAmountInCompanyCurrency],
        MeasureAttributes   : [{
            $Type     : 'UI.ChartMeasureAttributeType',
            Measure   : grossAmountInCompanyCurrency,
            Role      : #Axis1,
            DataPoint : '@UI.DataPoint#GrossAmountInCompanyCurrency'
        }],
        Dimensions          : [creationMonth],
        DimensionAttributes : [{
            $Type     : 'UI.ChartDimensionAttributeType',
            Dimension : creationMonth,
            Role      : #Category
        }]
    },
    UI.PresentationVariant #Line               : {
        $Type             : 'UI.PresentationVariantType',
        Visualizations    : ['@UI.Chart#Line'],
        MaxItems          : 3,
        IncludeGrandTotal : true,
        SortOrder         : [{
            $Type      : 'Common.SortOrderType',
            Descending : true,
            Property   : creationMonthAsDate
        }]
    },
    UI.DataPoint #GrossAmountInCompanyCurrency : {
        $Type                  : 'UI.DataPointType',
        Value                  : grossAmountInCompanyCurrency,
        Title                  : 'Revenue',
        CriticalityCalculation : {
            $Type                   : 'UI.CriticalityCalculationType',
            ImprovementDirection    : #Maximize,
            DeviationRangeHighValue : 1000000,
            DeviationRangeLowValue  : 3000000
        },
        TrendCalculation       : {
            $Type                : 'UI.TrendCalculationType',
            ReferenceValue       : referenceAmount,
            UpDifference         : 10,
            StrongUpDifference   : 100,
            DownDifference       : -10,
            StrongDownDifference : -100
        }
    }
);

Step 11: Using cds watch we can run the server and can the observe the logs in the terminal according to the development.

Step 12: Let’s us check with metadata based on the service and annotations.

Step 13: Add csv files under db/csv naming as namespace-entity.csv

Step 14: Update the package.json with hana configurations & devDependencies.

Step 15: CAP by default create V4 service. To create V2 will add cds odata v2 proxy using npm i @sap/cds-odata-v2-adapter-proxy

Also add server.js under srv layer to enable v2 version of the service & run cds watch.

Step 16: As we are developing the CAP app using Node.js, we need to mention the version in the package.json before deploying to SAP BTP.

Step 17: Now generate and mta file using cds add mta. By executing the command, mta.yaml will be created which has all the resources, modules and other service details which are required to deploy to SAP BTP.

Step 18: Execute cds build

Step 19: Now let us login to SAP Cloud Foundry.

Step 20: Now install Cloud MTA Build Tool (mbt) plugins using npm install -g mbt which helps in building the mtar file using mbt build -t ./

Step 21: Deploy the app using the command cf deploy ovpcapfe

Step 22: Now we can see the cap service is available to use both in v4 & v2 version

V4 version:

V2 version:

Step 23: Now let us create SAP Fiori Elements. Press Ctrl+Shft+P and start typing and select >Fiori: Open Application Generator. Choose Overview Page and click Next.

Step 24: Choose Connect to an OData Service under Data Source & Service Selection

Step 25: Provide the oData V2 CAP service and click Next.

Step 26: Select the SalesOrder for Filter entity and click on Next.

Step 27: Now let’s run the application, right click on the project, and choose Preview Application.

Step 28: You can see empty screen with filters.

Step 29: Lets add cards now in manifest.json and change enableLiveFilter to false. So that Go action will get enabled.

"enableLiveFilter": false,
{
            "donutCard": {
                "model": "mainService",
                "template": "sap.ovp.cards.charts.analytical",
                "settings": {
                    "title": "Donut Chart",
                    "entitySet": "SalesPerSupplier",
                    "chartAnnotationPath": "com.sap.vocabularies.UI.v1.Chart#donut",
                    "presentationAnnotationPath": "com.sap.vocabularies.UI.v1.PresentationVariant",
                    "dataPointAnnotationPath": "com.sap.vocabularies.UI.v1.DataPoint#GrossAmountInCompanyCurrency",
                    "idenfiticationAnnotationPath": "com.sap.vocabularies.UI.v1.Identification",
                    "chartProperties": {
                        "plotArea": {
                            "dataLabel": {
                                "type": "percentage"
                            }
                        }
                    }
                }
            },
            "lineCard": {
                "model": "mainService",
                "template": "sap.ovp.cards.charts.analytical",
                "settings": {
                    "title": "Line Chart",
                    "entitySet": "SalesHistory",
                    "chartAnnotationPath": "com.sap.vocabularies.UI.v1.Chart#Line",
                    "presentationAnnotationPath": "com.sap.vocabularies.UI.v1.PresentationVariant",
                    "dataPointAnnotationPath": "com.sap.vocabularies.UI.v1.DataPoint#GrossAmountInCompanyCurrency",
                    "idenfiticationAnnotationPath": "com.sap.vocabularies.UI.v1.Identification"
                }
            }
        }

Step 30: Reload the app, we can the charts get rendered based on the annotations which we created earlier.

Hope this blog is helpful.

Git Repo: https://github.com/SaiNithesh/ovpcapfe 

Feel free to comment

 

Regards,

Sai Nithesh Gajula

Assigned Tags

      14 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Yogananda Muthaiah
      Yogananda Muthaiah

      Great work SaiNithesh Gajula ! It looks good

      Keep sharing more lot of different innovative ways to do..

      Author's profile photo SaiNithesh Gajula
      SaiNithesh Gajula
      Blog Post Author

      Thanks @Yogananda Muthaiah.

      Author's profile photo Mohit Bansal
      Mohit Bansal

      Great Job @Sainithesh, Please keep it up.

       

      Author's profile photo SaiNithesh Gajula
      SaiNithesh Gajula
      Blog Post Author

      Thanks Mohit 🙂

      Author's profile photo Mohit Bansal
      Mohit Bansal

      Hi Sainithesh, It would be really great if you can add the git repository / csv files to hands on quickly.

      Author's profile photo SaiNithesh Gajula
      SaiNithesh Gajula
      Blog Post Author

      I have updated the Git Repo as well.

       

      Author's profile photo Vsevolod Obolenskiy
      Vsevolod Obolenskiy

      Hi Sainithesh, the app folder is missing in the repository, can you please add it as well?

      Author's profile photo Nikola Lukovic
      Nikola Lukovic

      Hi, @Sainithesh there is a mistake in step 9 in naming the CDS! It is not "ovpcap.cds"  it should be "capfesrv.cds".

      Great job, keep it up!

      Author's profile photo SaiNithesh Gajula
      SaiNithesh Gajula
      Blog Post Author

      Thanks Nikola. I have corrected it.

      Author's profile photo Chikke Surendra Naidu
      Chikke Surendra Naidu

      Nice Blog, SaiNithesh

       

      Author's profile photo Rufat Gadirov
      Rufat Gadirov

      HI @SaiNithesh Gajula,

       

      thank you for this great tutorial! The overview page should also run with OData V4. Is that correct? When trying to run it with V4 after generating the app in BAS, I cannot see the cards. Only the filters appear. I also adapted the manifest.json accordingly. Can you possibly import the app itself in git as well?

      Thank you.

      BR

      Rufat

      Author's profile photo SaiNithesh Gajula
      SaiNithesh Gajula
      Blog Post Author

      Hi Rufat Gadirov,

      The Overview Page should work with OData V4 service as well. Hope you have added cards in the manifest.json file as explained in Step 29 of this tutorial.

       

      Regards,

      Sai Nithesh

      Author's profile photo Rufat Gadirov
      Rufat Gadirov

      Hi SaiNithesh Gajula,

       

      thank you for your quick response. Ok, I am using OData V4 and I also tried out with V2. Yes, I did add those cards in manifest.json but they are not being rendered. Is there no other adaptation necessary, like in component.js - like adding jQuery.sap.require("sap.ovp.app.Component") for example? I guess something is missing and since that the git repo does not contain your web app I am not sure on that behalf. Maybe you could upload the other relevant files as well? In console the flex settings are marked as error...but in code I don´t see anything wrong...the code matches your example.

       

      Btw, with OData V4, I am getting the same error as described here:

      https://answers.sap.com/questions/13753276/sap-fiori-elements-overview-page.html

       

      BR & Thanks
      Rufat

       

      Author's profile photo Glauco Kubrusly
      Glauco Kubrusly

      Hi. Mine has an error inPromise with model unassigned.
      I'm also trying.