SAP Fiori Elements

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

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

Pre-requisites

  • Node >= 14
  • UI5 Tooling – npm install –global @ui5/cli
  • Cloud Foundry CLI
  • VS Code & SAP Fiori tools Extension Pack – Installing Visual Studio Code and Configuring SAP Extensions in Visual Studio Code
  • cds development kit by using cds v in the terminal – npm i -g @sap/cds-dk
  • SAP BTP Trial Account – Get a Free Account on SAP BTP Trial
  • SAP HANA Cloud

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 ovpcap.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.

Leave a Reply

Your email address will not be published.