Full Code of SAP-samples/cap-sflight for AI

main 5a4377d7c540 cached
148 files
4.5 MB
1.2M tokens
88 symbols
1 requests
Download .txt
Showing preview only (4,713K chars total). Download the full file or copy to clipboard to get everything.
Repository: SAP-samples/cap-sflight
Branch: main
Commit: 5a4377d7c540
Files: 148
Total size: 4.5 MB

Directory structure:
gitextract_co2fgw5z/

├── .cdsrc.json
├── .devcontainer/
│   ├── cds-dk/
│   │   ├── devcontainer-feature.json
│   │   └── install.sh
│   ├── cf-deploy/
│   │   ├── devcontainer-feature.json
│   │   └── install.sh
│   └── devcontainer.json
├── .github/
│   ├── actions/
│   │   ├── btp/
│   │   │   ├── Dockerfile
│   │   │   ├── action.yml
│   │   │   └── entrypoint.sh
│   │   └── cf-deploy/
│   │       ├── Dockerfile
│   │       ├── action.yml
│   │       └── entrypoint.sh
│   ├── dependabot.yml
│   ├── deployment/
│   │   └── kyma/
│   │       └── scripts/
│   │           ├── build-ui-image.sh
│   │           ├── create-container-registry-secret.sh
│   │           ├── create-db-secret.sh
│   │           ├── format-kyma-secret.js
│   │           ├── prepareUiFiles.js
│   │           ├── value.js
│   │           └── values.sh
│   └── workflows/
│       ├── deploy-btp.yml
│       ├── maven.yml
│       └── node.js.yml
├── .gitignore
├── .vscode/
│   └── launch.json
├── LICENSE
├── LICENSES/
│   └── Apache-2.0.txt
├── README-Kyma.md
├── README.md
├── REUSE.toml
├── _i18n/
│   ├── i18n.properties
│   ├── i18n_de.properties
│   ├── i18n_en.properties
│   └── i18n_fr.properties
├── app/
│   ├── .karma/
│   │   ├── karma-cap-middleware.js
│   │   └── karma.conf.js
│   ├── common.cds
│   ├── labels.cds
│   ├── services.cds
│   ├── travel_analytics/
│   │   ├── annotations.cds
│   │   ├── karma.conf.js
│   │   ├── package.json
│   │   ├── tsconfig.json
│   │   ├── ui5.yaml
│   │   ├── webapp/
│   │   │   ├── Component.ts
│   │   │   ├── i18n/
│   │   │   │   ├── i18n.properties
│   │   │   │   ├── i18n_de.properties
│   │   │   │   ├── i18n_en.properties
│   │   │   │   └── i18n_fr.properties
│   │   │   ├── index.html
│   │   │   ├── manifest.json
│   │   │   └── test/
│   │   │       ├── flpSandbox.html
│   │   │       ├── integration/
│   │   │       │   ├── Opa.qunit.html
│   │   │       │   ├── Opa.qunit.js
│   │   │       │   ├── OpaJourney.js
│   │   │       │   └── pages/
│   │   │       │       ├── BookingsList.js
│   │   │       │       └── BookingsObjectPage.js
│   │   │       ├── testsuite.qunit.html
│   │   │       └── testsuite.qunit.js
│   │   ├── xs-app.json
│   │   └── xs-security.json
│   ├── travel_processor/
│   │   ├── capabilities.cds
│   │   ├── field-control.cds
│   │   ├── karma.conf.js
│   │   ├── layouts.cds
│   │   ├── package.json
│   │   ├── tsconfig.json
│   │   ├── ui5.yaml
│   │   ├── webapp/
│   │   │   ├── Component.ts
│   │   │   ├── changes/
│   │   │   │   ├── changes-bundle.json
│   │   │   │   └── flexibility-bundle.json
│   │   │   ├── ext/
│   │   │   │   ├── controller/
│   │   │   │   │   ├── ControllerExtension.d.ts
│   │   │   │   │   └── ObjectPageExtension.controller.ts
│   │   │   │   └── fragment/
│   │   │   │       ├── CustomSection.fragment.xml
│   │   │   │       ├── CustomSection.ts
│   │   │   │       └── Trees4Tickets.fragment.xml
│   │   │   ├── i18n/
│   │   │   │   ├── i18n.properties
│   │   │   │   ├── i18n_de.properties
│   │   │   │   ├── i18n_en.properties
│   │   │   │   └── i18n_fr.properties
│   │   │   ├── index.html
│   │   │   ├── manifest.json
│   │   │   └── test/
│   │   │       ├── integration/
│   │   │       │   ├── Opa.qunit.html
│   │   │       │   ├── Opa.qunit.js
│   │   │       │   ├── OpaJourney.js
│   │   │       │   └── pages/
│   │   │       │       ├── ItemObjectPage.js
│   │   │       │       ├── MainListReport.js
│   │   │       │       └── MainObjectPage.js
│   │   │       ├── testsuite.qunit.html
│   │   │       └── testsuite.qunit.js
│   │   ├── xs-app.json
│   │   └── xs-security.json
│   └── value-helps.cds
├── db/
│   ├── common.cds
│   ├── data/
│   │   ├── sap.common-Countries.csv
│   │   ├── sap.common-Countries.texts.csv
│   │   ├── sap.common-Currencies.csv
│   │   ├── sap.common-Currencies.texts.csv
│   │   ├── sap.fe.cap.travel-Airline.csv
│   │   ├── sap.fe.cap.travel-Airport.csv
│   │   ├── sap.fe.cap.travel-Booking.csv
│   │   ├── sap.fe.cap.travel-BookingStatus.csv
│   │   ├── sap.fe.cap.travel-BookingStatus.texts.csv
│   │   ├── sap.fe.cap.travel-BookingSupplement.csv
│   │   ├── sap.fe.cap.travel-Flight.csv
│   │   ├── sap.fe.cap.travel-FlightConnection.csv
│   │   ├── sap.fe.cap.travel-Passenger.csv
│   │   ├── sap.fe.cap.travel-Supplement.csv
│   │   ├── sap.fe.cap.travel-Supplement.texts.csv
│   │   ├── sap.fe.cap.travel-SupplementType.csv
│   │   ├── sap.fe.cap.travel-Travel.csv
│   │   ├── sap.fe.cap.travel-TravelAgency.csv
│   │   ├── sap.fe.cap.travel-TravelStatus.csv
│   │   └── sap.fe.cap.travel-TravelStatus.texts.csv
│   ├── master-data.cds
│   ├── package.json
│   └── schema.cds
├── eslint.config.js
├── mta-java.yaml
├── mta.yaml
├── native-build-env.json
├── package.json
├── pom.xml
├── srv/
│   ├── analytics-service.cds
│   ├── pom.xml
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/
│   │   │   │   └── com/
│   │   │   │       └── sap/
│   │   │   │           └── cap/
│   │   │   │               └── sflight/
│   │   │   │                   ├── SFlightApplication.java
│   │   │   │                   ├── processor/
│   │   │   │                   │   ├── AcceptRejectHandler.java
│   │   │   │                   │   ├── CreationHandler.java
│   │   │   │                   │   ├── DeductDiscountHandler.java
│   │   │   │                   │   ├── IllegalTravelDateException.java
│   │   │   │                   │   ├── IllegalTravelStatusException.java
│   │   │   │                   │   ├── RecalculatePriceHandler.java
│   │   │   │                   │   └── UpdateFlightSeatsHandler.java
│   │   │   │                   ├── security/
│   │   │   │                   │   └── WebSecurityConfig.java
│   │   │   │                   └── ui/
│   │   │   │                       └── RedirectFilter.java
│   │   │   └── resources/
│   │   │       ├── META-INF/
│   │   │       │   └── native-image/
│   │   │       │       └── resource-config.json
│   │   │       ├── application.yml
│   │   │       └── messages.properties
│   │   └── test/
│   │       ├── java/
│   │       │   └── com/
│   │       │       └── sap/
│   │       │           └── cap/
│   │       │               └── sflight/
│   │       │                   ├── SFlightApplicationTest.java
│   │       │                   └── processor/
│   │       │                       ├── TravelSmokeTest.java
│   │       │                       └── UpdateFlightSeatsHandlerServiceIntegrationTest.java
│   │       └── resources/
│   │           └── META-INF/
│   │               └── native-image/
│   │                   └── reflect-config.json
│   ├── travel-service.cds
│   └── travel-service.ts
├── test/
│   ├── odata.test.ts
│   ├── requests.http
│   └── setup.ts
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .cdsrc.json
================================================
{}


================================================
FILE: .devcontainer/cds-dk/devcontainer-feature.json
================================================
{
    "id": "cds-dk",
    "version": "1.0.0",
    "name": "cds-dk",
    "description": "Install @sap/cds-dk (via npm)",

    "options": {

        "version": {
            "type": "string",
            "proposals": [
                "^9",
                "^8"
            ],
            "default": "latest",
            "description": "Select the @sap/cds-dk version"
        }
    },

    "dependsOn": {
        "ghcr.io/devcontainers/features/node:1": { }
    }
}

================================================
FILE: .devcontainer/cds-dk/install.sh
================================================
#!/bin/sh
set -e

export VERSION_SPECIFIER="${VERSION:+@${VERSION}}"

. ${NVM_DIR}/nvm.sh

npm i -g @sap/cds-dk${VERSION_SPECIFIER}


================================================
FILE: .devcontainer/cf-deploy/devcontainer-feature.json
================================================
{
    "id": "cf-deploy",
    "version": "1.0.1",
    "name": "cf-deploy",
    "description": "Install tooling for deploying to Cloud Foundry",

    "dependsOn": {
        "ghcr.io/devcontainers/features/node:1": { }
    }
}

================================================
FILE: .devcontainer/cf-deploy/install.sh
================================================
#!/bin/sh
set -e

# Homebrew
if ! type brew >/dev/null 2>&1; then
    NONINTERACTIVE=1 su vscode -s /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

    echo >> /home/vscode/.bashrc
    echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> /home/vscode/.bashrc
    eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
fi

su vscode -s /bin/bash -c "brew install cloudfoundry/tap/cf-cli@8"
su vscode -s /bin/bash -c "cf install-plugin -f multiapps"

npm i -g mbt


================================================
FILE: .devcontainer/devcontainer.json
================================================
// For format details, see https://aka.ms/devcontainer.json
{
	"name": "CAP Node.js & TypeScript & Java",
	"image": "mcr.microsoft.com/devcontainers/base:debian",

	// Features to add to the dev container. More info: https://containers.dev/features.
	"features": {
		"ghcr.io/devcontainers/features/node:1": {},
		"ghcr.io/devcontainers-extra/features/typescript:2": {},
		"ghcr.io/devcontainers/features/java:1": {
			"version": "21",
			"jdkDistro": "sapmchn",
			"installMaven": "true"
		},

		"./cds-dk": {},

		"./cf-deploy": {}
	},

	// Use 'postCreateCommand' to run commands after the container is created.
	"postCreateCommand": "npm install"

}

================================================
FILE: .github/actions/btp/Dockerfile
================================================
FROM buildpack-deps:stretch-curl

# https://blogs.sap.com/2021/09/01/sap-tech-bytes-btp-cli-installation/
RUN curl -s -O --location --url "https://raw.githubusercontent.com/SAP-samples/sap-tech-bytes/2021-09-01-btp-cli/getbtpcli" && \
    chmod +x getbtpcli && \
    echo | ./getbtpcli && \
    mv $HOME/bin/btp* /usr/local/bin/

COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]


================================================
FILE: .github/actions/btp/action.yml
================================================
name: 'BTP'
description: Performs BTP operations
inputs:
  cli_url:
    description: The API endpoint for the CLI
    required: false
    default: https://cpcli.cf.eu10.hana.ondemand.com
  subdomain:
    description: Name of BTP subdomain
    required: true
  subaccount_id:
    description: ID of BTP subaccount
    required: false
  role_collection:
    description: A role collection to assign to $CF_USER
    required: false
  command:
    description: The command to execute
    required: false

runs:
  using: docker
  image: Dockerfile


================================================
FILE: .github/actions/btp/entrypoint.sh
================================================
#!/bin/bash
set -e

INPUT_USERNAME=${INPUT_USERNAME:-$CF_USERNAME}
INPUT_PASSWORD=${INPUT_PASSWORD:-$CF_PASSWORD}
btp login --url "${INPUT_CLI_URL}" --user "${INPUT_USERNAME}" --password "${INPUT_PASSWORD}" --subdomain "${INPUT_SUBDOMAIN}"

if [ ! -z "${INPUT_SUBACCOUNT_ID}" ]; then
  btp target --subaccount "${INPUT_SUBACCOUNT_ID}"
fi

if [ ! -z "${INPUT_ROLE_COLLECTION}" ]; then
  btp assign security/role-collection ${INPUT_ROLE_COLLECTION} --to-user ${INPUT_USERNAME} > /dev/null

  if [ ! -z "${GRANT_USERS}" ]; then
    for user in ${GRANT_USERS//\\n/ }  # newline separated
    do
      btp assign security/role-collection ${INPUT_ROLE_COLLECTION} --to-user ${user} --create-user-if-missing
    done
  fi

fi

if [ ! -z "${INPUT_COMMAND}" ]; then
  ${INPUT_COMMAND}
fi


================================================
FILE: .github/actions/cf-deploy/Dockerfile
================================================
FROM ppiper/cf-cli:v11

# needed for cf to find its config
# (GH resets HOME to /github/workspace)
ENV CF_HOME=$HOME

COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]


================================================
FILE: .github/actions/cf-deploy/action.yml
================================================
name: 'CF Deploy MTA'
description: Deploys an MTA file to Cloud Foundry
inputs:
  mtafile:
    description: Path to your MTA file, relative to the workspace
    required: true
  api:
    description: Cloud Foundry API endpoint. Default is $CF_API
    required: false
  username:
    description: Cloud Foundry username. Default is $CF_USERNAME
    required: false
  password:
    description: Cloud Foundry password. Default is $CF_PASSWORD
    required: false
  org:
    description: Cloud Foundry organization. Default is $CF_ORG
    required: false
  space:
    description: Cloud Foundry space to deploy to.  Default is $CF_SPACE
    required: false
  createspace:
    description: Whether to create the given space
    required: false
    default: 'false'
  findurl_command:
    description: Bash command that determines the application's main URL
    required: false
  findurl_regex:
    description: Regex that scans the result of 'findurl_command'
    required: false
    default: 'https.*'
outputs:
  url:
    description: The applications's main URL (only if 'findurl_command' is set and successful)

runs:
  using: docker
  image: Dockerfile


================================================
FILE: .github/actions/cf-deploy/entrypoint.sh
================================================
#!/bin/bash
set -e

cf_opts=
cf api ${INPUT_API:-$CF_API} ${cf_opts}

INPUT_USERNAME=${INPUT_USERNAME:-$CF_USERNAME}
INPUT_PASSWORD=${INPUT_PASSWORD:-$CF_PASSWORD}
cf auth

if [ "x${INPUT_CREATESPACE}" = "xtrue" ]; then
  cf create-space ${INPUT_SPACE:-$CF_SPACE} -o ${INPUT_ORG:-$CF_ORG}
fi

cf target -o ${INPUT_ORG:-$CF_ORG} -s ${INPUT_SPACE:-$CF_SPACE}

cf deploy ${INPUT_MTAFILE} -f

if [ ! -z "${INPUT_FINDURL_COMMAND}" ]; then
  # echo "Find URL command: ${INPUT_FINDURL_COMMAND}, regex: ${INPUT_FINDURL_REGEX}"
  res=`${INPUT_FINDURL_COMMAND}`
  url=`echo "${res}" | grep -o "${INPUT_FINDURL_REGEX}"`
  echo "URL: $url"
  echo "::set-output name=url::$url"
fi


================================================
FILE: .github/dependabot.yml
================================================
version: 2

updates:

- package-ecosystem: npm
  directory: /
  versioning-strategy: increase-if-necessary
  schedule:
    interval: weekly

- package-ecosystem: npm
  directory: /app/travel_processor
  versioning-strategy: increase-if-necessary
  schedule:
    interval: weekly

- package-ecosystem: npm
  directory: /app/travel_analytics
  versioning-strategy: increase-if-necessary
  schedule:
    interval: weekly

- package-ecosystem: maven
  directory: /
  schedule:
    interval: weekly


================================================
FILE: .github/deployment/kyma/scripts/build-ui-image.sh
================================================
#!/bin/bash

set -e
cd "$(dirname "$(npm root)")"
DIR="$(pwd)/.github"

npm install --no-save yaml

function value() {
    node "$DIR/deployment/kyma/scripts/value.js" "$1"
}

function image() {
    local REPOSITORY="$(value "$1.image.repository")"
    local TAG="$(value "$1.image.tag")"
    if [ "$TAG" != "" ]; then
        echo "$REPOSITORY:$TAG"
    else
        echo "$REPOSITORY"
    fi
}

rm -rf gen/ui
mkdir -p gen/ui/resources

CLOUD_SERVICE="$(value html5-apps-deployer.env.SAP_CLOUD_SERVICE)"
DESTINATIONS="$(value backendDestinations)"

IMAGE="$(image html5-apps-deployer)"

for APP in app/*; do
    if [ -f "$APP/webapp/manifest.json" ]; then
        echo "Build $APP..."
        echo

        rm -rf "gen/$APP"
        mkdir -p "gen/app"
        cp -r "$APP" gen/app
        pushd >/dev/null "gen/$APP"

        node "$DIR/deployment/kyma/scripts/prepareUiFiles.js" $CLOUD_SERVICE $DESTINATIONS
        npm install
        npx ui5 build preload --clean-dest --config ui5-deploy.yaml --include-task=generateManifestBundle generateCachebusterInfo
        cd dist
        rm manifest-bundle.zip
        mv *.zip "$DIR/gen/ui/resources"

        popd >/dev/null
    fi
done

cd gen/ui

echo
echo "HTML5 Apps:"
ls -l resources
echo

cat >package.json <<EOF
{
    "name": "ui-deployer",
    "scripts": { "start": "node node_modules/@sap/html5-app-deployer/index.js" }
}
EOF

npm install @sap/html5-app-deployer
pack build $IMAGE --path . --buildpack gcr.io/paketo-buildpacks/nodejs --builder paketobuildpacks/builder-jammy-base


================================================
FILE: .github/deployment/kyma/scripts/create-container-registry-secret.sh
================================================
#!/bin/bash

read -p "Docker Server: " DOCKER_SERVER

read -p "User ($USER): " DOCKER_USER
if [ "$DOCKER_USER" == "" ]; then
    DOCKER_USER="$USER"
fi

if [ "$EMAIL" == "" ]; then
    read -p "EMail: " DOCKER_EMAIL
else
    read -p "EMail ($EMAIL): " DOCKER_EMAIL
    if [ "$DOCKER_EMAIL" == "" ]; then
        DOCKER_EMAIL="$EMAIL"
    fi
fi

read -sp "API Key: " API_KEY

echo
echo

kubectl create secret docker-registry container-registry \
    "--docker-server=$DOCKER_SERVER" \
    "--docker-username=$DOCKER_USER" \
    "--docker-email=$DOCKER_EMAIL" \
    "--docker-password=$API_KEY"


================================================
FILE: .github/deployment/kyma/scripts/create-db-secret.sh
================================================
#!/bin/bash

set -e
cd "$(dirname "$(dirname "$0")")"
. ./scripts/values.sh

if true-value 2>/dev/null .saas_registry.enabled; then
  echo >&2 "[ERROR] DB secret only required for single tenancy apps"
fi

NAME="$1"
if [ "$NAME" == "" ]; then
  if [ ! -f "chart/values.yaml" ]; then
    echo >&2 "[ERROR] Please either specify the name for the DB secret or maintain it in the Helm chart"
    exit 1
  fi
  NAME="$(value .srv.bindings.db.fromSecret)"
  if [ "$NAME" == "" -o "$NAME" == "<nil>" ]; then
    echo >&2 "[ERROR] Please either specify the name for the DB secret or maintain it in the Helm chart"
    exit 1
  fi
fi

SECRET_HEADER="$(cat <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: $NAME
type: Opaque
stringData:
  .metadata: |
    {
      "credentialProperties":
        [
          { "name": "certificate", "format": "text"},
          { "name": "database_id", "format": "text"},
          { "name": "driver", "format": "text"},
          { "name": "hdi_password", "format": "text"},
          { "name": "hdi_user", "format": "text"},
          { "name": "host", "format": "text"},
          { "name": "password", "format": "text"},
          { "name": "port", "format": "text"},
          { "name": "schema", "format": "text"},
          { "name": "url", "format": "text"},
          { "name": "user", "format": "text"}
        ],
      "metaDataProperties":
        [
          { "name": "plan", "format": "text" },
          { "name": "label", "format": "text" },
          { "name": "type", "format": "text" },
          { "name": "tags", "format": "json" }
        ]
    }
  type: hana
  label: hana
  plan: hdi-shared
  tags: '[ "hana", "database", "relational" ]'
EOF
)"

cf 2>/dev/null >/dev/null service $NAME || cf create-service hana hdi-shared $NAME
while true; do
    STATUS="$(cf 2>/dev/null service $NAME | grep status: | head -n 1)"
    echo $STATUS
    if [[ "$STATUS" = *succeeded* ]]; then
        break
    fi
    sleep 1
done

cf create-service-key $NAME $NAME-key

node "$(dirname "$1")/scripts/format-kyma-secret.js" -- "$(echo "$SECRET_HEADER")" "$(cf service-key $NAME $NAME-key)" | kubectl apply -f -
echo
echo "HANA DB Kubernetes secret '$NAME' created."
echo
echo "You can view it using:"
echo
echo "kubectl get secret $NAME -o yaml"
exit 0

================================================
FILE: .github/deployment/kyma/scripts/format-kyma-secret.js
================================================
const key=JSON.parse(process.argv[4].replace(/^.*/, ""));
const credentials=key.credentials /* new cfcli? */ || key;
console.log(process.argv[3]);
console.log(Object.keys(credentials).map(k => {
    if (credentials[k].match(/\n/s))
        return (`  ${k}: |\n${credentials[k]}`).replace(/\n/gs,"\n    ")
    else
        return `  ${k}: "${credentials[k]}"`
}).join("\n"))

================================================
FILE: .github/deployment/kyma/scripts/prepareUiFiles.js
================================================

const Path = require('path');
const posixJoin = Path.posix.join;

function prepareUiFiles(path, options) {
    const { readFileSync, writeFileSync, existsSync } = require('fs');
    const Path = require('path');

    const srvDestination = getSrvDestination(options.destinations);

    const packageJsonInclude = getPackageJsonInclude();
    const ui5DeployTemplate = getUI5DeployTemplateYaml();
    const xsAppJsonTemplate = getXsAppTemplateJson();

    const packageJsonPath = Path.join(path, 'package.json');
    const manifestJsonPath = Path.join(path, 'webapp/manifest.json');
    const packageJson = JSON.parse(readFileSync(packageJsonPath));
    const manifestJson = JSON.parse(readFileSync(manifestJsonPath));

    // Adjust package.json
    packageJson.devDependencies = packageJsonInclude.devDependencies;
    packageJson.ui5 = packageJsonInclude.ui5;
    writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 4));

    // Adjust manifest.json
    console.log('Adjust webapp/manifest.json');
    manifestJson["sap.cloud"] = {
        "public": true,
        "service": options.cloudService || throwError(`Missing cloudService name`)
    };

    if (!existsSync(Path.join(path, 'xs-app.json'))) {
      const dataSources = manifestJson['sap.app'].dataSources || {};
      for (const dataSource of Object.values(dataSources)) {
          dataSource.uri = posixJoin('service', dataSource.uri);
      }
    }

    writeFileSync(manifestJsonPath, JSON.stringify(manifestJson, null, 4));

    // Add xs-app.json
    if (!existsSync(Path.join(path, 'xs-app.json'))) {
        console.log('Add xs-app.json');
        const xsAppJson = JSON.parse(JSON.stringify(xsAppJsonTemplate));
        xsAppJson.routes[0].destination = srvDestination || throwError('Expect srv destination');
        writeFileSync(Path.join(path, 'xs-app.json'), JSON.stringify(xsAppJson, null, 4));
    }

    // Add ui5-deploy.yaml
    if (!existsSync(Path.join(path, 'ui5-deploy.yaml'))) {
        console.log('Add ui5-deploy.yaml');
        const appId = manifestJson["sap.app"].id;
        const replacements = {
            "ID": appId,
            "ARCHIVENAME": appId.replace(/\./g, "")
        };

        const ui5Deploy = ui5DeployTemplate.replace(/\$([A-Z]+|)/g, (_, v) => replacements[v]);
        writeFileSync(Path.join(path, "ui5-deploy.yaml"), ui5Deploy);
    }
}

function throwError(msg) {
    throw new Error(msg);
}

function getPackageJsonInclude() {
    return {
        "name": "ui5-builde-root",
        "devDependencies": {
            "@ui5/cli": "^2.11.1",
            "@ui5/fs": "^2.0.6",
            "@ui5/logger": "^2.0.1",
            "@sap/ux-ui5-tooling": "1",
            "rimraf": "3.0.2",
            "@sap/ui5-builder-webide-extension": "1.0.x",
            "ui5-task-zipper": "^0.3.1",
            "mbt": "^1.0.15"
        },
        "ui5": {
            "dependencies": [
                "@sap/ui5-builder-webide-extension",
                "ui5-task-zipper",
                "mbt"
            ]
        }
    }
}

function getUI5DeployTemplateYaml() {
    return `specVersion: '2.4'
metadata:
  name: $ID
type: application
resources:
  configuration:
    propertiesFileSourceEncoding: UTF-8
builder:
  resources:
    excludes:
      - "/test/**"
      - "/localService/**"
  customTasks:
  - name: webide-extension-task-updateManifestJson
    beforeTask: generateManifestBundle
    configuration:
      appFolder: webapp
      destDir: dist
  - name: ui5-task-zipper
    afterTask: generateCachebusterInfo
    configuration:
      archiveName: $ARCHIVENAME
      additionalFiles:
      - xs-app.json`
}

function getXsAppTemplateJson() {
    return {
        "welcomeFile": "/index.html",
        "authenticationMethod": "route",
        "routes": [
          {
            "source": "^/service/(.*)$",
            "target": "$1",
            "destination": "overwrite-me",
            "authenticationType": "xsuaa",
            "csrfProtection": false
          },
          {
            "source": "^/resources/(.*)$",
            "target": "/resources/$1",
            "authenticationType": "none",
            "destination": "ui5"
          },
          {
            "source": "^/test-resources/(.*)$",
            "target": "/test-resources/$1",
            "authenticationType": "none",
            "destination": "ui5"
          },
          {
            "source": "^(.*)$",
            "target": "$1",
            "service": "html5-apps-repo-rt",
            "authenticationType": "xsuaa"
          }
        ]
      }

}

function getSrvDestination(destinations) {
  let destinationsJSON = JSON.parse(destinations);
  for (let key in destinationsJSON) {
    if(destinationsJSON[key].service === "srv") {
      return key;
    }
  }
}

if (process.argv[1].endsWith('prepareUiFiles.js')) {
    // Run in standalone mode
    prepareUiFiles('.', { cloudService: process.argv[2], destinations: process.argv[3] });
} else {
    module.exports = prepareUiFiles;
}

================================================
FILE: .github/deployment/kyma/scripts/value.js
================================================
#!/usr/bin/env node
/*eslint no-unused-vars: ["error", { "caughtErrors": "none" }]*/

const yaml = require('yaml');
const fs = require('fs');

const path = process.argv[2];
if (!path) {
    console.error('missing path');
    process.exit(1);
}

const segments = path.split('.');

function printProperty(file) {
    let valuesYaml;
    try {
        valuesYaml = fs.readFileSync(file, 'utf-8');
    } catch(error) {
        return false;
    }
    const values = yaml.parse(valuesYaml);

    let o = values;
    for (let segment of segments) {
        o = o[segment];
        if (typeof o === "undefined" || o === null) return false;
    }

    if (typeof o === "object") {
        console.log(JSON.stringify(o));
    } else {
        console.log(o);
    }

    return true;
}

printProperty('.values.yaml') || printProperty('deployment/kyma/values.yaml') || printProperty('chart/values.yaml') || process.exit(1);

================================================
FILE: .github/deployment/kyma/scripts/values.sh
================================================

function read-value() {
    if [ -f .values.yaml ]; then
        local VALUE="$(yaml2json <.values.yaml | jq -er "$1" || echo "")"
        if [ "$VALUE" != "" -a "$VALUE" != "null" ]; then
            echo "$VALUE"
            return
        fi
    fi

    local VALUE="$(helm inspect values ./chart --jsonpath="{$1}")"
    echo "$VALUE"
}

function true-value() {
    local VALUE="$(read-value "$1")"
    if [ "$VALUE" == "" -o "$VALUE" == "false" ]; then
        return 1
    else
        return 0
    fi
}

function value() {
    local VALUE="$(read-value "$1")"
    if [ "$VALUE" == "" ]; then
        echo >&2 "ERROR: Expect value for $1 in chart/values.yaml"
        return 1
    fi

    echo "$VALUE"
}

function image() {
    local REPOSITORY="$(value "$1.image.repository")"
    local TAG="$(read-value "$1.image.tag")"
    if [ "$TAG" != "" ]; then
        echo "$REPOSITORY:$TAG"
    else
        echo "$REPOSITORY"
    fi
}

================================================
FILE: .github/workflows/deploy-btp.yml
================================================
on:
  workflow_call:
    inputs:
      btp-subdomain:
        type: string
        required: false
        default: 284dfa06-4fb6-445e-809c-263b132a0840
      btp-subaccount-id:
        type: string
        required: false
        default: 1e4c6bcd-71c5-42c6-88ab-636e3a6dc12e
      btp-cli-url:
        type: string
        required: false
        default: https://cpcli.cf.sap.hana.ondemand.com
      cf-api:
        type: string
        required: false
        default: 'https://api.cf.eu12.hana.ondemand.com'
      cf-org:
        type: string
        required: false
        default: cap-sflight
      cf-space:
        type: string
        required: true
      role-collection:
        type: string
        required: true
      mta-source-dir:
        description: relative path to dir with mta.yaml
        type: string
        required: false
        default: '.'
      mtar-dir:
        description: path to dir with mtar
        type: string
        required: false
        default: gen
      mtar-file:
        description: file name of mtar
        type: string
        required: false
        default: sflight.mtar
    secrets:
      username:
        required: true
      password:
        required: true
      grant-users:
        description: List of users to assign the role role-collection to
        required: false
    outputs:
      ui_app_url:
        value: ${{ jobs.deployment.outputs.ui_app_url }}

jobs:

  deployment:
    name: To BTP
    runs-on: ubuntu-latest
    environment:
      name: staging
      url: ${{ steps.deploy.outputs.url }}
    concurrency: staging
    env:
      CF_API: ${{ inputs.cf-api }}
      CF_ORG: ${{ inputs.cf-org }}
      CF_SPACE: ${{ inputs.cf-space }}
      CF_USERNAME: ${{ secrets.username }}
      CF_PASSWORD: ${{ secrets.password }}
      GRANT_USERS: ${{ secrets.grant-users }}
    outputs:
      ui_app_url: ${{ steps.deploy.outputs.url }}

    steps:
    - uses: actions/checkout@v4
    - uses: actions/setup-node@v4
      with:
        node-version: 16
    - uses: actions/cache@v4
      id: cache
      with:
        path: ${{ inputs.mtar-dir }}/${{ inputs.mtar-file }}
        key: ${{ runner.os }}-${{ github.head_ref || github.ref_name }}-${{ hashFiles('_i18n/**','app/**','db/**','srv/**','*.json','**/mta.yaml','pom.xml') }}

    - name: MTA build
      if: steps.cache.outputs.cache-hit != 'true'
      run: npx mbt build -s ${{ inputs.mta-source-dir }} -t ${{ inputs.mtar-dir }} --mtar ${{ inputs.mtar-file }}

    - name: CF Deploy
      id: deploy
      uses: ./.github/actions/cf-deploy
      with:
        createspace: true
        mtafile: ${{ inputs.mtar-dir }}/${{ inputs.mtar-file }}
        findurl_command: 'cf html5-list -di sflight-destination-service -u'

    - name: Assign Roles
      uses: ./.github/actions/btp
      with:
        cli_url: ${{ inputs.btp-cli-url }}
        subdomain: ${{ inputs.btp-subdomain }}
        subaccount_id: ${{ inputs.btp-subaccount-id }}
        role_collection: ${{ inputs.role-collection }}


================================================
FILE: .github/workflows/maven.yml
================================================
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven

name: Java CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  schedule:
    - cron: 30 4 * * *
jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        java-version: [21]

    steps:
    - uses: actions/checkout@v4

    - name: Set up Java ${{ matrix.java-version }}
      uses: actions/setup-java@v4
      with:
        java-version: ${{ matrix.java-version }}
        distribution: 'sapmachine'

    - run: npm ci

    - name: Build with Maven
      run: mvn -B clean verify

    - name: Run integration tests - Manage Travels
      working-directory: ./app/travel_processor
      run: |
        npm run test:java -- --ci

    - name: Run integration tests - Analyze Bookings
      working-directory: ./app/travel_analytics
      run: |
        npm run test:java -- --ci


================================================
FILE: .github/workflows/node.js.yml
================================================
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions

name: Node.js CI

on:
  workflow_dispatch:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
    types: [ labeled, opened, synchronize, reopened ]
  schedule:
    - cron: 20 4 * * *
jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        node-version: [22.x, 20.x]

    steps:
    - uses: actions/checkout@v4
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
    - run: npm i -g @sap/cds-dk typescript tsx
    - run: npm ci
    - run: npx cds-typer "*"
    - run: npm run lint
    - run: npm run build --if-present
    - run: npm run test
    - run: npm run test:mocha

    - name: Run integration tests - Manage Travels
      working-directory: ./app/travel_processor
      run: |
        npm run test:node -- --ci

    - name: Run integration tests - Analyze Bookings
      working-directory: ./app/travel_analytics
      run: |
        npm run test:node -- --ci

#
### temporarily switch off deployment as it consistently fails
#
#  deployment:
#    name: Deploy
#    if: ${{ github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'deploy') }}  # run only if label is set
#    uses: ./.github/workflows/deploy-btp.yml
#    with:
#      cf-space: staging-${{ github.head_ref || github.ref_name }}
#      role-collection: sflight-admin-staging-${{ github.head_ref || github.ref_name }}  # must match to mta.yaml
#    secrets:
#      username: ${{ secrets.CF_USERNAME }}
#      password: ${{ secrets.CF_PASSWORD }}
#      grant-users: ${{ secrets.GRANT_USERS }}
#
#  e2etest:
#    name: E2E Tests
#    runs-on: ubuntu-latest
#    needs: deployment
#    steps:
#    - name: More UI tests
#      run: |
#        echo ${{ needs.deployment.outputs.ui_app_url }}


================================================
FILE: .gitignore
================================================
# CAP sflight
_out
*.db
connection.properties
default-*.json
*.sql
gen/
node_modules/
target/
.reloadtrigger
@cds-models/

# Web IDE, App Studio
.che/
.gen/

# MTA
*_mta_build_tmp
*.mtar
mta_archives/
/Makefile_*.mta

# Other
.DS_Store
.npmrc
*.orig
*.log

*.flattened-pom.xml

srv/src/main/resources/edmx/**
dist/
/app/resources/
/app/router/package-lock.json


# IDEs
.idea
*.iml
.project
.settings
.classpath
# .vscode/*
# IMPORTANT: Do not exclude .vscode please!

# Local dev
.env
.values.yaml
.cdsrc-private.json

# Exclude Helm chart for this example
/chart

# Experimental
xxx-*
xxx_*
xxx/*

@cds-models

================================================
FILE: .vscode/launch.json
================================================
{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "type": "java",
      "name": "Launch SFlightApplication",
      "request": "launch",
      "mainClass": "com.sap.cap.sflight.SFlightApplication",
      "projectName": "srv"
    },
    {
      "name": "cds watch",
      "command": "npx cds watch",
      "env": {
        "DEBUG": "sqlite"
      },
      "type": "node-terminal",
      "request": "launch",
      "skipFiles": [
        "<node_internals>/**",
        "**/node_modules/**",
        "**/cds/lib/lazy.js",
        "**/cds/lib/req/cls.js",
        "**/odata-v4/okra/**"
      ]
    }
  ]
}


================================================
FILE: LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.


================================================
FILE: LICENSES/Apache-2.0.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.

"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.

"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.

"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.

"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.

"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.

"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).

"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.

"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."

"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.

2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.

3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.

4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:

     (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and

     (b) You must cause any modified files to carry prominent notices stating that You changed the files; and

     (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and

     (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.

     You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.

5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.

6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.

8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!)  The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


================================================
FILE: README-Kyma.md
================================================
# Deployment to SAP Business Technology Platform - Kyma Runtime

- [Deployment to SAP Business Technology Platform - Kyma Runtime](#deployment-to-sap-business-technology-platform---kyma-runtime)
  - [Preconditions](#preconditions)
  - [Add Deployment Files](#add-deployment-files)
  - [Configuration](#configuration)
  - [Prepare Kubernetes Namespace](#prepare-kubernetes-namespace)
    - [Create container registry secret](#create-container-registry-secret)
    - [Create a secret for your HDI container](#create-a-secret-for-your-hdi-container)
  - [Build - Node.js](#build---nodejs)
  - [Build - Java](#build---java)
  - [Build HTML5 application deployer image](#build-html5-application-deployer-image)
  - [Push docker images](#push-docker-images)
  - [Deployment](#deployment)
  - [Access the UI](#access-the-ui)

**TIP:** You can find more information in the [Deploy Your CAP Application on SAP BTP Kyma Runtime](https://developers.sap.com/mission.btp-deploy-cap-kyma.html) tutorial and in the [Deploy to Kyma/K8s](https://cap.cloud.sap/docs/guides/deployment/deploy-to-kyma) guide of the CAP documentation.

## Preconditions

- BTP Subaccount with Kyma Runtime
- BTP Subaccount with Cloud Foundry Space
- HANA Cloud instance available for your Cloud Foundry space
- BTP Entitlements for: *HANA HDI Services & Container* plan *hdi-shared*, *Build Work Zone* plan *standard*
- Container Registry
- Command Line Tools: `kubectl`, `kubectl-oidc_login`, `pack`, `docker`, `helm`, `cf`
- Logged into Kyma Runtime (with `kubectl` CLI), Cloud Foundry space (with `cf` CLI) and Container Registry (with `docker login`)
- `@sap/cds-dk` >= 6.6.0

## Add Deployment Files

CAP tooling provides your a Helm chart for deployment to Kyma.

Add the CAP Helm chart with the required features to this project:

```bash
cds add helm
cds add html5-repo
```

## Prepare Kubernetes Namespace

1. Export the kubeconfig.yaml

    ```
    set KUBECONFIG=~/.kube/cap-kyma-app-config
    ```

2. Setting the namespace

    ```
    kubectl config set-context --current --namespace=<<NAMESPACE>>
    ```

### Create container registry secret

Create a secret `container-registry` with credentials to access the container registry:

```
bash .github/deployment/kyma/scripts/create-container-registry-secret.sh
```

The *Docker Server* is the full-qualified hostname of your container registry.

### Create a secret for your HDI container

This step is only required if you're using a BTP Trial account. If you're using a production or a free tier account then you can create HDI Container from Kyma directly by adding a [mapping to your Kyma namespace in your HANA Cloud Instance](https://blogs.sap.com/2022/12/15/consuming-sap-hana-cloud-from-the-kyma-environment/) and skip this step.

```
bash .github/deployment/kyma/scripts/create-db-secret.sh sflight-db
```

It will create a HDI container `sflight-db` instance on your currently targeted Cloud Foundry space and creates a secret `sflight-db` with the credentials in your current Kubernetes namespace.

Make the following changes to your _`chart/values.yaml`_.

```diff
srv:
  bindings:
    db:
-     serviceInstanceName: hana
+     fromSecret: sflight-db
...

hana-deployer:
  bindings:
    hana:
-     serviceInstanceName: hana
+     fromSecret: sflight-db

...
- hana:
-   serviceOfferingName: hana
-   servicePlanName: hdi-shared
```

## Configuration

Make the following changes to your _`chart/values.yaml`_ file:

1. Change value of `global.domain` key to your cluster domain.

2. Replace `<your-container-registry>` with your container registry.

3. Set the value of `SAP_CLOUD_SERVICE` key.

```diff
html5-apps-deployer:
  env:
-    SAP_CLOUD_SERVICE: null
+    SAP_CLOUD_SERVICE: sap.fe.cap.sflight
```

4. Add backend destinations required by HTML5 Apps Deployer.

```diff
-  backendDestinations: {}
+  backendDestinations:
+     sflight-srv:
+       service: srv
```

5. Add your image registry secret created in [Create container registry secret](#create-container-registry-secret) step.

```diff
global:
  domain: null
-  imagePullSecret: {}
+  imagePullSecret:
+    name: container-registry
```

## Build - Node.js

Do the following steps if you want to deploy the **Node.js** application.

The `CDS_ENV=node` env variable needs to be provided to build for Node.js. The application will be built for Java by default.

```
CDS_ENV=node cds build --production
```
**Build data base deployer image:**

```bash
pack build $YOUR_CONTAINER_REGISTRY/sflight-hana-deployer \
     --path gen/db \
     --buildpack gcr.io/paketo-buildpacks/nodejs \
     --builder paketobuildpacks/builder-jammy-base \
     --env BP_NODE_RUN_SCRIPTS=""
```
(Replace `$YOUR_CONTAINER_REGISTRY` with the full-qualified hostname of your container registry)

**Build image for CAP service:**

```bash
pack build $YOUR_CONTAINER_REGISTRY/sflight-srv \
     --path "gen/srv" \
     --buildpack gcr.io/paketo-buildpacks/nodejs \
     --builder paketobuildpacks/builder-jammy-base \
     --env BP_NODE_RUN_SCRIPTS=""
```

## Build - Java

Do the following steps if you want to deploy the **Java** application.

**Build data base deployer image:**

```bash
cds build --production
```

```bash
pack build $YOUR_CONTAINER_REGISTRY/sflight-hana-deployer \
     --path gen/db \
     --buildpack gcr.io/paketo-buildpacks/nodejs \
     --builder paketobuildpacks/builder-jammy-base \
     --env BP_NODE_RUN_SCRIPTS=""
```

(Replace `$YOUR_CONTAINER_REGISTRY` with the full-qualified hostname of your container registry)

**Build image for CAP service:**

```bash
mvn package
```

```bash
pack build $YOUR_CONTAINER_REGISTRY/sflight-srv \
     --path srv/target/*-exec.jar \
     --buildpack gcr.io/paketo-buildpacks/sap-machine \
     --buildpack gcr.io/paketo-buildpacks/java \
     --builder paketobuildpacks/builder-jammy-base \
     --env SPRING_PROFILES_ACTIVE=cloud \
     --env BP_JVM_VERSION=17
```

## Build HTML5 application deployer image

```
bash .github/deployment/kyma/scripts/build-ui-image.sh
```

## Push docker images

You can push all the docker images to your docker registry, using:

```bash
docker push $YOUR_CONTAINER_REGISTRY/sflight-hana-deployer
docker push $YOUR_CONTAINER_REGISTRY/sflight-srv
docker push $YOUR_CONTAINER_REGISTRY/sflight-html5-deployer
```

## Deployment

```bash
helm install sflight ./chart --set-file xsuaa.jsonParameters=xs-security.json
```

## Access the UI

1. Create Build Work Zone subscription in the BTP Cockpit
2. Create a role collection `sflight`
3. Add role `admin` of `sflight.tXYZ` application to role collection
4. Add your user to the role collection
5. Goto **HTML5 Applications**
6. Start HTML5 application `sapfecaptravel`

Additionally, you can add the UI to a Build Work Zone site like it is described in in the last two steps of [this tutorial](https://developers.sap.com/tutorials/btp-app-kyma-launchpad-service.html#9aab2dd0-18ea-4ccd-bc44-24e87c845740).


================================================
FILE: README.md
================================================


# Welcome to the CAP SFLIGHT App

This is a sample app for the travel reference scenario, built with the [SAP Cloud Application Programming Model (CAP)](https://cap.cloud.sap) and [SAP Fiori Elements](https://experience.sap.com/fiori-design-web/smart-templates).

The purpose of this sample app is to:
* Demonstrate SAP Fiori annotations
* Demonstrate and compare SAP Fiori features on various stacks (CAP Node.js, CAP Java SDK, ABAP)
* Run UI test suites on various stacks

![Process Travels Page](.github/assets/img.png)

The app still contains some workarounds that are going to be addressed over time.
In some cases, the model and the handlers can be improved or simplified once further planned CAP features become available.
In other cases, the app itself could be improved. For example, calculation of the total price for a travel
currently simply sums up the single prices ignoring the currencies.

> For enabling all features of the Analytical List Page (ALP) in the Node.js runtime, we have switched on the new OData parser
(`odata_new_parser: true` in `package.json`), which is still in an **experimental state**.
Early adopters may use this feature in own projects on their own risk.
You can also use the ALP with the standard OData parser, but then some features like grouping in the table are not available.

![](https://github.com/SAP-samples/cap-sflight/workflows/CI/badge.svg)
[![REUSE status](https://api.reuse.software/badge/github.com/SAP-samples/cap-sflight)](https://api.reuse.software/info/github.com/SAP-samples/cap-sflight)


## Run locally

### Build and Run - Node.js Backend

Prerequisite:
```
npm i -g @sap/cds-dk
```

In the root folder of your project, run
```
npm ci
cds watch
```

#### Accessing the SAP Fiori Apps

Open these links in your browser:
* http://localhost:4004/sap.fe.cap.travel/index.html for processing the travel data
* http://localhost:4004/sap.fe.cap.travel_analytics/index.html for the [Analytical List Page](https://ui5.sap.com/#/topic/3d33684b08ca4490b26a844b6ce19b83) (ALP)


### Build and Run - Java Backend

In the root folder of your project, run
```
npm ci
npm run build:ui
mvn spring-boot:run
```

> At the moment, there is no watch mode for Fiori UI changes.  Run `npm run build:ui` after each change there.

#### Accessing the SAP Fiori Apps

Open these links in your browser:
* http://localhost:4004/travel_processor/dist/index.html for processing the travel data
* http://localhost:4004/travel_analytics/dist/index.html for the [Analytical List Page](https://ui5.sap.com/#/topic/3d33684b08ca4490b26a844b6ce19b83) (ALP)

Log in with user `amy` and empty password.

### Integration Tests

To start OPA tests, open this link in your browser:
http://localhost:4004/travel_processor/webapp/test/integration/Opa.qunit.html

Test documentation is available at:
https://ui5.sap.com/#/api/sap.fe.test

## Deployment to SAP Business Technology Platform

The project contains a configuration for deploying the CAP services and the SAP Fiori app to the SAP Business Technology Platform (SAP BTP) using a managed application router. The app then becomes visible in the content manager of the SAP Build Work Zone.

The configuration file `mta.yaml` is for the Node.js backend of the app. If you want to deploy the Java backend, copy `mta-java.yaml` to `mta.yaml`.

### Prerequisites
#### SAP Business Technology Platform

- Create a [trial account on SAP BTP](https://www.sap.com/products/business-technology-platform/trial.html). See this [tutorial](https://developers.sap.com/tutorials/hcp-create-trial-account.html) for more information. Alternatively, you can use a sub-account in a productive environment.
- Get a [SAP Cloud Identity Services tenant](https://help.sap.com/docs/cloud-identity-services/cloud-identity-services/get-your-tenant?version=Cloud#get-trial-tenant) and [establish trust](https://help.sap.com/docs/btp/sap-business-technology-platform/establish-trust-and-federation-between-uaa-and-identity-authentication?version=Cloud)
- Subscribe to the [SAP Build Work Zone](https://developers.sap.com/tutorials/cp-portal-cloud-foundry-getting-started.html).
- Create an [SAP HANA Cloud Service instance](https://developers.sap.com/tutorials/hana-cloud-mission-trial-2.html) or use an existing one. Then, [provision an instance](https://developers.sap.com/tutorials/hana-cloud-mission-trial-3.html) - make sure to choose the Cloud Foundry Runtime environment.

#### Local Machine

- Install the Cloud Foundry command line interface (CLI). See this [tutorial](https://developers.sap.com/tutorials/cp-cf-download-cli.html) for more details.
- Install the [MultiApps CF CLI Plugin](https://github.com/cloudfoundry-incubator/multiapps-cli-plugin):

  ```shell
  cf add-plugin-repo CF-Community https://plugins.cloudfoundry.org
  cf install-plugin multiapps
  ```

- Install the [Cloud MTA Build Tool](https://github.com/SAP/cloud-mta-build-tool) globally:

  ```shell
  npm install -g mbt
   ```

### Build the Project

Build the project from the command line:

```shell
mbt build
```

The build results will be stored in the directory `mta_archives`.

### Deploy

1. Log in to the target space.
2. Deploy the MTA archive using the CF CLI: `cf deploy mta_archives/capire.sflight_1.0.0.mtar`

### Assign Role Collection

Any authorized user has read access to the app. For further authorization, assign a role collection to your user in the SAP BTP Cockpit:
* `sflight-reviewer-{spacename}` for executing actions *Accept Travel*, *Reject Travel*, and *Deduct Discount*
* `sflight-processor-{spacename}` for full write access

### Integrate SFlight with SAP Build Work Zone, Standard Edition

CAP SFlight uses the managed AppRouter, which in case of a trial account, is provided by the Navigation Service in SAP Build Work Zone, Standard Edition. Please consult [this tutorial](https://developers.sap.com/tutorials/integrate-with-work-zone.html) to make sure that your Build Work Zone is configured correctly to serve the CAP SFlight Frontend.

### Local Development with a HANA Cloud Instance

You need to have access to a HANA Cloud instance and SAP BTP.

1. Deploy the HDI content to a HANA HDI container (which is newly created on first call): `cds deploy --to hana`.
2. Start the application with the Spring Profile `cloud`.
   1. From Maven: `mvn spring-boot:run -Dspring-boot.run.profiles=cloud`
   2. From your IDE with the JVM argument `-Dspring.profiles.active=cloud` or env variable `spring.profiles.active=cloud`

The running application is now connected to its own HDI container/schema. Please keep in mind that the credentials for
that HDI container are stored locally on your filesystem (default-env.json).

## Deployment to SAP Business Technology Platform - Kyma Runtime

The deployment to Kyma Runtime is explained in file [README-Kyma.md](./README-Kyma.md).

## Creating an SAP Fiori App from Scratch

If you want to implement an SAP Fiori app, follow these tutorials:

* [Create a List Report Object Page App with SAP Fiori Tools](https://developers.sap.com/group.fiori-tools-lrop.html)
* [Developing SAP Fiori applications with SAP Fiori Tools](https://help.sap.com/viewer/17d50220bcd848aa854c9c182d65b699/Latest/en-US)

## Get Support

In case you've a question, find a bug, or otherwise need support, use the [SAP Community](https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce) to get more visibility.

## License

Copyright (c) 2021 SAP SE or an SAP affiliate company and cap-sflight contributors. This project is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](LICENSES/Apache-2.0.txt) file.


================================================
FILE: REUSE.toml
================================================
version = 1
SPDX-PackageName = "cap-sflight"
SPDX-PackageSupplier = "<Steffen Weinstock (steffen.weinstock@sap.com)>"
SPDX-PackageDownloadLocation = "https://github.com/sap-samples/cap-sflight"
SPDX-PackageComment = "The code in this project may include calls to APIs (“API Calls”) of\n SAP or third-party products or services developed outside of this project\n (“External Products”).\n “APIs” means application programming interfaces, as well as their respective\n specifications and implementing code that allows software to communicate with\n other software.\n API Calls to External Products are not licensed under the open source license\n that governs this project. The use of such API Calls and related External\n Products are subject to applicable additional agreements with the relevant\n provider of the External Products. In no event shall the open source license\n that governs this project grant any rights in or to any External Products,or\n alter, expand or supersede any terms of the applicable additional agreements.\n If you have a valid license agreement with SAP for the use of a particular SAP\n External Product, then you may make use of any API Calls included in this\n project’s code for that SAP External Product, subject to the terms of such\n license agreement. If you do not have a valid license agreement for the use of\n a particular SAP External Product, then you may only make use of any API Calls\n in this project for that SAP External Product for your internal, non-productive\n and non-commercial test and evaluation of such API Calls. Nothing herein grants\n you any rights to use or access any SAP External Product, or provide any third\n parties the right to use of access any SAP External Product, through API Calls."

[[annotations]]
path = "**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2021 SAP SE or an SAP affiliate company and cap-sflight contributors"
SPDX-License-Identifier = "Apache-2.0"


================================================
FILE: _i18n/i18n.properties
================================================
# This is the resource bundle for travel_processor

Travel=Travel
Travels=Travels
TravelUUID=Travel UUID
TravelID=Travel
AgencyID=Agency
AgencyName=Agency Name
TravelAgency=Travel Agency
BeginDate=Starting Date
EndDate=End Date
Dates=Dates
TotalPrice=Total Price
TravelStatus=Travel Status
CustomerID=Customer
CustomerName=Customer Name
BookingFee=Booking Fee
Description=Description
Price=Price
Prices=Prices
ProductPrice=Product Price
ProductID=Product
OverallStatus=Overall Status
GeneralInformation=General Information
GoGreen=Trees-4-Tickets
GreenFee=Green Flight Fee
TreesPlanted=Trees Planted
Sustainability=Sustainability

Booking=Booking
Bookings=Bookings
BookingDate=Booking Date
BookingID=Booking ID
BookingNumber=Booking Number
FlightPrice=Flight Price
ConnectionID=Flight Number
FlightDate=Flight Date
FlightNumber=Flight Number
BookingStatus=Booking Status
Airline=Airline
AirlineID=Airline
Name=Name
AirlineNumber=Airline Number
Status=Status
DepartureAirport=Departure Airport
ArrivalAirport=Arrival Airport
Distance=Distance

BookingSupplement=Booking Supplement
BookingSupplements=Booking Supplements
BookingSupplementID=Booking. Supp. Number
SupplementDescription=Supplement Description
SupplementID=Supplement
Supplement=Supplement
SupplementType=Supplement Type

Passenger=Passenger
LastName=Last Name
PlaneType=Plane Type
MaximumSeats=Maximum Seats
OccupiedSeats=Occupied Seats

FirstName=First name
Title=Title
Street=Street
PostalCode=Postal Code
City=City
CountryCode=Country Code
CurrencyCode=Currency Code
PhoneNumber=Phone Number
EMailAddress=EMail Address
WebAddress=Web Address

Flight=Flight

CopyTravel=Copy Travel
AcceptTravel=Accept Travel
RejectTravel=Reject Travel
DeductDiscount=Deduct Discount

MinPrice=Minimal Price
AvgPrice=Average Price
MaxPrice=Maximum Price
TotalPrice=Total Price

BookingDetails=Booking Details


================================================
FILE: _i18n/i18n_de.properties
================================================
# This is the resource bundle for travel_processor

Travel=Reise
Travels=Reisen
TravelUUID=Reise-UUID
TravelID=Reise
AgencyID=Agentur
AgencyName=Agenturname
TravelAgency=Veranstalter
BeginDate=Startdatum
EndDate=Enddatum
Dates=Termine
TotalPrice=Gesamtpreis
TravelStatus=Reisestatus
CustomerID=Kunde
CustomerName=Name des Kunden
BookingFee=Reservierungsgebühr
Description=Beschreibung
Price=Preis
Prices=Preise
ProductPrice=Produktpreis
ProductID=Produkt
OverallStatus=Gesamtstatus
GeneralInformation=Allgemeine Informationen

Booking=Buchung
Bookings=Buchungen
BookingDate=Buchungsdatum
BookingID=Buchungsnummer
BookingNumber=Buchungsnummer
FlightPrice=Flugpreis
ConnectionID=Flugnummer
FlightDate=Flugdatum
FlightNumber=Flugnummer
BookingStatus=Buchungsstatus
Airline=Fluggesellschaft
AirlineID=Fluggesellschaft
Name=Name
AirlineNumber=Fluggesellschaftsnummer
Status=Status
DepartureAirport=Abflughafen
ArrivalAirport=Zielflughafen
Distance=Distanz

BookingSupplement=Buchungszusatz
BookingSupplements=Buchungszusätze
BookingSupplementID=Buchungszusatz
SupplementDescription=Zusatzbeschreibung
SupplementID=Zusatz-ID
Supplement=Zusatz
SupplementType=Zusatzart

Passenger=Passagier
LastName=Nachname
PlaneType=Flugzeugtyp
MaximumSeats=Maximale Sitzplätze
OccupiedSeats=Belegte Plätze

FirstName=Vorname
Title=Titel
Street=Straße
PostalCode=Postleitzahl
City=Ort
CountryCode=Ländercode
CurrencyCode=Währungscode
PhoneNumber=Telefonnummer
EMailAddress=E-Mail-Addresse
WebAddress=Web-Adresse

Flight=Flug

CopyTravel=Reise kopieren
AcceptTravel=Reise akzeptieren
RejectTravel=Reise ablehnen
DeductDiscount=Rabatt abziehen

MinPrice=Niedrigster Preis
AvgPrice=Durchschnittspreis
MaxPrice=Höchster Preis
TotalPrice=Gesamtpreis

BookingDetails=Buchungsdetails


================================================
FILE: _i18n/i18n_en.properties
================================================
# This is the resource bundle for travel_processor

Travel=Travel
Travels=Travels
TravelUUID=Travel UUID
TravelID=Travel
AgencyID=Agency
AgencyName=Agency Name
TravelAgency=Travel Agency
BeginDate=Starting Date
EndDate=End Date
Dates=Dates
TotalPrice=Total Price
TravelStatus=Travel Status
CustomerID=Customer
CustomerName=Customer Name
BookingFee=Booking Fee
Description=Description
Price=Price
Prices=Prices
ProductPrice=Product Price
ProductID=Product
OverallStatus=Overall Status
GeneralInformation=General Information

Booking=Booking
Bookings=Bookings
BookingDate=Booking Date
BookingID=Booking ID
BookingNumber=Booking Number
FlightPrice=Flight Price 
ConnectionID=Flight Number
FlightDate=Flight Date
FlightNumber=Flight Number
BookingStatus=Booking Status
Airline=Airline
AirlineID=Airline
Name=Name
AirlineNumber=Airline Number
Status=Status
DepartureAirport=Departure Airport
ArrivalAirport=Arrival Airport
Distance=Distance

BookingSupplement=Booking Supplement
BookingSupplements=Booking Supplements
BookingSupplementID=Booking. Supp. Number
SupplementDescription=Supplement Description
SupplementID=Supplement
Supplement=Supplement
SupplementType=Supplement Type

Passenger=Passenger
LastName=Last Name
PlaneType=Plane Type
MaximumSeats=Maximum Seats
OccupiedSeats=Occupied Seats

FirstName=First name
Title=Title
Street=Street
PostalCode=Postal Code
City=City
CountryCode=Country Code
CurrencyCode=Currency Code
PhoneNumber=Phone Number
EMailAddress=EMail Address
WebAddress=Web Address

Flight=Flight

CopyTravel=Copy Travel
AcceptTravel=Accept Travel
RejectTravel=Reject Travel
DeductDiscount=Deduct Discount

MinPrice=Minimal Price
AvgPrice=Average Price
MaxPrice=Maximum Price
TotalPrice=Total Price

BookingDetails=Booking Details


================================================
FILE: _i18n/i18n_fr.properties
================================================
# This is the resource bundle for travel_processor

Travel=Voyage
Travels=Voyages
TravelUUID=UUID voyage
TravelID=Voyage
AgencyID=Agence
AgencyName=Nom de l'agence
TravelAgency=Travel Agency
BeginDate=Date de début
EndDate=Date de fin
Dates=Dates
TotalPrice=Prix total
TravelStatus=Statut du voyage
CustomerID=Client
CustomerName=Nom du client
BookingFee=Frais de réservation
Description=Description
Price=Prix
Prices=Prix
ProductPrice=Prix du produit
ProductID=Produit
OverallStatus=Statut global
GeneralInformation=Informations générales

Booking=Réservation
Bookings=Réservations
BookingDate=Date d'inscription
BookingID=ID réservation
BookingNumber=Numéro de réservation
FlightPrice=Flight Price 
ConnectionID=Flight Number
FlightDate=Date vol
FlightNumber=Nº vol
BookingStatus=Booking Status
Airline=Airline
AirlineID=Airline
Name=Nom
AirlineNumber=Numéro de compagnie aérienne
Status=Statut
DepartureAirport=Aéroport de départ
ArrivalAirport=Aéroport de destination
Distance=Distance

BookingSupplement=Supplément de réservation
BookingSupplements=Réservation de suppléments
BookingSupplementID=Nº Supplément de réservation
SupplementDescription=Description du supplément
SupplementID=Supplément.
Supplement=Supplément
SupplementType=Type de supplément

Passenger=Passager
LastName=Last Name
PlaneType=Type d'avion
MaximumSeats=Nombre maximal de places
OccupiedSeats=Places occup.

FirstName=Prénom
Title=Titre
Street=Rue
PostalCode=Code postal
City=Ville
CountryCode=Code pays
CurrencyCode=Currency Code
PhoneNumber=Phone Number
EMailAddress=Numéro de téléphone
WebAddress=Adresse e-mail

Flight=Vol

CopyTravel=Copier le voyage
AcceptTravel=Accepter le voyage
RejectTravel=Refuser le voyage
DeductDiscount=Déduire remise

MinPrice=Prix minimum
AvgPrice=Prix moyen
MaxPrice=Prix maximum
TotalPrice=Prix total

BookingDetails=Les détails de réservation


================================================
FILE: app/.karma/karma-cap-middleware.js
================================================
const spawn = require("cross-spawn"),
  HttpProxy = require("http-proxy");

function spawnServer(cmd, args, cwd, fnIsReady) {
  return new Promise((resolve, reject) => {
    const proc = spawn(cmd, args, {
      cwd,
      env: Object.assign({ PORT: 0 }, process.env),
      stdio: ["ignore", "pipe", "inherit"],
    });

    const checkServerReady = (data) => {
      const targetUrl = fnIsReady(data.toString());
      if (targetUrl) {
        proc.stdout.removeListener("data", checkServerReady);
        resolve(targetUrl);
      }
    };

    proc.on("close", reject);
    proc.stdout.on("data", (data) => {
      process.stdout.write(data.toString())
      checkServerReady(data)
    });

    // clean up sub process
    process.on("exit", () => {
      if (proc) proc.kill("SIGKILL");
    });
  });
}

function createKarmaMiddleware(serverUrl, auth) {
  const proxyOptions = {
    target: serverUrl,
    auth: auth ? `${auth.user}:${auth.password}` : undefined,
  };

  const middleware = (logFactory) => {
    const log = logFactory.create("cap-server");

    const proxy = new HttpProxy(proxyOptions);
    proxy.on("error", (data) => log.error(data.toString()));

    return (req, res) => proxy.web(req, res);
  };

  middleware.$inject = ["logger"];
  return { "middleware:cap-proxy": ["factory", middleware] };
}

async function java() {
  const isReady = (data) => {
    const started = data.match(/started on port (?<port>\d+)/);
    if (started) return new URL(`http://localhost:${started.groups.port}`);
  };
  const serverUrl = await spawnServer(
    "mvn", ["spring-boot:run", "-B", "-Dspring-boot.run.jvmArguments=-Dserver.port=0"],
    "../..",
    isReady
  );

  return createKarmaMiddleware(serverUrl, { user: "admin", password: "admin" });
}

async function node() {
  const isReady = (data) => {
    const started = data.match(/server listening on {.*url:.*'(?<url>.+)'.*}/);
    if (started) {
      return new URL(started.groups.url);
    }
  };

  const serverUrl = await spawnServer("cds", ["serve"], "../..", isReady);

  return createKarmaMiddleware(serverUrl, { user: "admin", password: "admin" });
}

module.exports = { java, node };


================================================
FILE: app/.karma/karma.conf.js
================================================
const puppeteer = require("puppeteer"),
  cap = require("./karma-cap-middleware");

process.env.CHROME_BIN = puppeteer.executablePath();

module.exports = async (config) => {
  // start the CAP server (either specify CLI arg --server=node or --server=java)
  let capMiddleware;
  if (config.server === "node") {
    capMiddleware = cap.node();
  } else if (config.server === "java") {
    capMiddleware = cap.java();
  } else {
    throw new Error(`Unknown server type: ${config.server}`);
  }

  config.set({
    frameworks: ["ui5"],
    preprocessors: {
      "webapp/**/*.ts": ["ui5-transpile"],
    },
    logLevel: "INFO", // log errors only. Change to "DEBUG" for more verbosity
    proxies: {
      "/base/webapp/": "/",
    },
    ui5: {
      failOnEmptyTestPage: true,
    },
    plugins: [...config.plugins, await capMiddleware],
    middleware: ["cap-proxy"],
    browsers: config.ci ? ["ChromeHeadlessNoSandbox"] : ["Chrome"],
    customLaunchers: {
      ChromeHeadlessNoSandbox: {
        base: 'ChromeHeadless',
        flags: ['--no-sandbox']
      }
    },
    singleRun: config.ci || config.singleRun || false
  });
};


================================================
FILE: app/common.cds
================================================
using { Currency } from '../db/common';


// Workarounds for overly strict OData libs and clients
annotate cds.UUID with @UI.Hidden  @odata.Type : 'Edm.String';

annotate Currency with @Common.UnitSpecificScale : 'Decimals';


================================================
FILE: app/labels.cds
================================================
using { sap.fe.cap.travel as schema } from '../db/schema';

//
// annotations that control rendering of fields and labels
//

annotate schema.Travel with @title: '{i18n>Travel}' {
  TravelUUID   @UI.Hidden;
  TravelID     @title: '{i18n>TravelID}'      @Common.Text: Description;
  BeginDate    @title: '{i18n>BeginDate}';
  EndDate      @title: '{i18n>EndDate}';
  Description  @title: '{i18n>Description}';
  BookingFee   @title: '{i18n>BookingFee}'    @Measures.ISOCurrency: (CurrencyCode.code);
  TotalPrice   @title: '{i18n>TotalPrice}'    @Measures.ISOCurrency: (CurrencyCode.code);
  GoGreen      @title: '{i18n>GoGreen}';  
  GreenFee     @title: '{i18n>GreenFee}'      @Measures.ISOCurrency: (CurrencyCode.code);
  TreesPlanted @title: '{i18n>TreesPlanted}'; 
  TravelStatus @title: '{i18n>TravelStatus}'  @Common.Text: TravelStatus.name     @Common.TextArrangement: #TextOnly;
  to_Customer  @title: '{i18n>CustomerID}'    @Common.Text: to_Customer.LastName;
  to_Agency    @title: '{i18n>AgencyID}'      @Common.Text: to_Agency.Name;
}

annotate schema.TravelStatus with {
  code @Common.Text: name @Common.TextArrangement: #TextOnly
}

annotate schema.Booking with @title: '{i18n>Booking}' {
  BookingUUID   @UI.Hidden;
  to_Travel     @UI.Hidden;
  BookingID     @title: '{i18n>BookingID}';
  BookingDate   @title: '{i18n>BookingDate}';
  ConnectionID  @title: '{i18n>ConnectionID}';
  CurrencyCode  @title: '{i18n>CurrencyCode}';
  FlightDate    @title: '{i18n>FlightDate}';
  FlightPrice   @title: '{i18n>FlightPrice}'    @Measures.ISOCurrency: (CurrencyCode.code);
  BookingStatus @title: '{i18n>BookingStatus}'  @Common.Text: BookingStatus.name    @Common.TextArrangement: #TextOnly;
  to_Carrier    @title: '{i18n>AirlineID}'      @Common.Text: to_Carrier.Name;
  to_Customer   @title: '{i18n>CustomerID}'     @Common.Text: to_Customer.LastName;
}

annotate schema.BookingStatus with {
  code @Common.Text : name @Common.TextArrangement: #TextOnly
}

annotate schema.BookingSupplement with @title: '{i18n>BookingSupplement}' {
  BookSupplUUID        @UI.Hidden;
  to_Booking           @UI.Hidden;
  to_Travel            @UI.Hidden;
  to_Supplement        @title: '{i18n>SupplementID}'  @Common.Text: to_Supplement.Description;
  Price                @title: '{i18n>Price}'         @Measures.ISOCurrency: (CurrencyCode.code);
  BookingSupplementID  @title: '{i18n>BookingSupplementID}';
  CurrencyCode         @title: '{i18n>CurrencyCode}';
}

annotate schema.TravelAgency with @title: '{i18n>TravelAgency}' {
  AgencyID     @title: '{i18n>AgencyID}'      @Common.Text: Name;
  Name         @title: '{i18n>AgencyName}';
  Street       @title: '{i18n>Street}';
  PostalCode   @title: '{i18n>PostalCode}';
  City         @title: '{i18n>City}';
  CountryCode  @title: '{i18n>CountryCode}';
  PhoneNumber  @title: '{i18n>PhoneNumber}';
  EMailAddress @title: '{i18n>EMailAddress}';
  WebAddress   @title: '{i18n>WebAddress}';
}

annotate schema.Passenger with @title: '{i18n>Passenger}' {
  CustomerID   @title: '{i18n>CustomerID}'    @Common.Text: LastName;
  FirstName    @title: '{i18n>FirstName}';
  LastName     @title: '{i18n>LastName}';
  Title        @title: '{i18n>Title}';
  Street       @title: '{i18n>Street}';
  PostalCode   @title: '{i18n>PostalCode}';
  City         @title: '{i18n>City}';
  CountryCode  @title: '{i18n>CountryCode}';
  PhoneNumber  @title: '{i18n>PhoneNumber}';
  EMailAddress @title: '{i18n>EMailAddress}';
}

annotate schema.Airline with @title: '{i18n>Airline}' {
  AirlineID    @title: '{i18n>AirlineID}'     @Common.Text: Name;
  Name         @title: '{i18n>Name}';
  CurrencyCode @title: '{i18n>CurrencyCode}';
}

annotate schema.Flight with @title: '{i18n>Flight}' {
  AirlineID     @title: '{i18n>AirlineID}';
  FlightDate    @title: '{i18n>FlightDate}';
  ConnectionID  @title: '{i18n>ConnectionID}';
  CurrencyCode  @title: '{i18n>CurrencyCode}';
  Price         @title: '{i18n>Price}'        @Measures.ISOCurrency: (CurrencyCode.code);
  PlaneType     @title: '{i18n>PlaneType}';
  MaximumSeats  @title: '{i18n>MaximumSeats}';
  OccupiedSeats @title: '{i18n>OccupiedSeats}';
}

annotate schema.Supplement with @title: '{i18n>Supplement}' {
  SupplementID @title: '{i18n>SupplementID}'  @Common.Text: Description;
  Price        @title: '{i18n>Price}'         @Measures.ISOCurrency: (CurrencyCode.code);
  CurrencyCode @title: '{i18n>CurrencyCode}';
  Description  @title: '{i18n>Description}';
}


================================================
FILE: app/services.cds
================================================
using from './travel_processor/capabilities';
using from './travel_processor/field-control';
using from './travel_processor/layouts';

using from './travel_analytics/annotations';


================================================
FILE: app/travel_analytics/annotations.cds
================================================
using AnalyticsService as service from '../../srv/analytics-service';

annotate service.Bookings with @(
  Aggregation.CustomAggregate #FlightPrice : 'Edm.Decimal',
  Aggregation.CustomAggregate #CurrencyCode_code : 'Edm.String',
  Common.SemanticKey : [ID],
) {
  ID                @ID : 'ID';
  FlightPrice       @Aggregation.default: #SUM;
  CurrencyCode_code @Aggregation.default: #MAX;
};

annotate service.Bookings with @Aggregation.ApplySupported : {
  Transformations : [
    'aggregate',
    'topcount',
    'bottomcount',
    'identity',
    'concat',
    'groupby',
    'filter',
    'search'
  ],
  GroupableProperties  : [
    TravelID,
    BookingID,
    CombinedID,
    ConnectionID,
    FlightDate,
    CurrencyCode_code,
    status,
    airline,
  ],
  AggregatableProperties : [
    {Property : status      },
    {Property : FlightPrice },
    {Property : ID          },
  ],
};

annotate service.Bookings with @(
  Analytics.AggregatedProperty #countBookings :
  {
    Name                 : 'countBookings',
    AggregationMethod    : 'countdistinct',
    AggregatableProperty : ID,
    @Common.Label        : '{i18n>Bookings}'
  },
  Analytics.AggregatedProperty #minPrice :
  {
    Name                 : 'minPrice',
    AggregationMethod    : 'min',
    AggregatableProperty : FlightPrice,
    @Common.Label        : '{i18n>MinPrice}'
  },
  Analytics.AggregatedProperty #maxPrice :
  {
    Name                 : 'maxPrice',
    AggregationMethod    : 'max',
    AggregatableProperty : FlightPrice,
    @Common.Label        : '{i18n>MaxPrice}'
  },
  Analytics.AggregatedProperty #avgPrice :
  {
    Name                 : 'avgPrice',
    AggregationMethod    : 'average',
    AggregatableProperty : FlightPrice,
    @Common.Label        : '{i18n>AvgPrice}'
  },
  // measure "sum of prices" is available by default (but name/label doesn't indicate summing -> ?)
  // Analytics.AggregatedProperty #sumPrice :
  //  {
  //   Name                 : 'sumPrice',
  //   AggregationMethod    : 'sum',
  //   AggregatableProperty : FlightPrice,
  //   @Common.Label        : '{i18n>TotalPrice}'
  // }
);


annotate service.Bookings with @UI.LineItem : [
  {
    Value          : TravelID,
    @UI.Importance : #High,
    @HTML5.CssDefaults: {width:'8em'},
  }, {
    Value          : BookingID,
    Label          : '{i18n>Booking}',
    @UI.Importance : #High,
    @HTML5.CssDefaults: {width:'8em'},
  }, {
    Value          : airline,
    @UI.Importance : #High,
    @HTML5.CssDefaults: {width:'14em'},
  }, {
    Value          : ConnectionID,
    @UI.Importance : #High,
    @HTML5.CssDefaults: {width:'8em'},
  }, {
    Value          : FlightDate,
    @UI.Importance : #High,
  }, {
    Value          : FlightPrice,
    @UI.Importance : #High,
    @HTML5.CssDefaults: {width:'12em'},
  }, {
    Value          : status,
    @UI.Importance : #High,
    @HTML5.CssDefaults: {width:'8em'},
  }
];


annotate service.Bookings with @UI.Chart : {
  Title               : '{i18n>Bookings}',
  ChartType           : #Column,
  DynamicMeasures : [
    '@Analytics.AggregatedProperty#minPrice',
    '@Analytics.AggregatedProperty#maxPrice',
    '@Analytics.AggregatedProperty#avgPrice'
  ],
  Dimensions          : [airline],
  MeasureAttributes   : [{
    DynamicMeasure : '@Analytics.AggregatedProperty#minPrice',
    Role    : #Axis1
  }],
  DimensionAttributes : [{
    Dimension : airline,
    Role      : #Category
  }],
};
annotate service.Bookings with @UI.PresentationVariant : {
  GroupBy : [  // default grouping in table
    airline,
    status
  ],
  Total : [    // default aggregation in table
    FlightPrice
  ],
  Visualizations : [
    '@UI.Chart',
    '@UI.LineItem',
  ]
};


//
// Visual Filters
//
annotate service.Bookings with @(
  UI.PresentationVariant #pvAirline : {
    Visualizations : ['@UI.Chart#chartAirline']
  },
  UI.Chart #chartAirline : {
    ChartType           : #Bar,
    DynamicMeasures : [
      '@Analytics.AggregatedProperty#countBookings',
    ],
    Dimensions          : [airline],
    MeasureAttributes   : [{
      DynamicMeasure : '@Analytics.AggregatedProperty#countBookings',
      Role      : #Axis1,
    }],
    DimensionAttributes : [{
      Dimension : airline,
      Role      : #Category
    }],
  }
) {
  airline @(
    Common.ValueList #vlAirline : {
      CollectionPath               : 'Bookings',
      PresentationVariantQualifier : 'pvAirline',
      Parameters                   : [{
        $Type             : 'Common.ValueListParameterInOut',
        LocalDataProperty : airline,
        ValueListProperty : 'airline'
      }]
    },
    Common.ValueList : {
      CollectionPath : 'Airline',
      Parameters : [{
        $Type             : 'Common.ValueListParameterInOut',
        LocalDataProperty : airline,
        ValueListProperty : 'AirlineID',
      }, {
          $Type : 'Common.ValueListParameterDisplayOnly',
          ValueListProperty : 'Name',
      }]
    }
  )
};


annotate service.Bookings with @(
  UI.PresentationVariant #pvStatus : {
    Visualizations : ['@UI.Chart#chartStatus']
  },
  UI.Chart #chartStatus : {
    ChartType           : #Bar,
    DynamicMeasures : [
      '@Analytics.AggregatedProperty#countBookings',
    ],
    Dimensions          : [status],
    MeasureAttributes   : [{
      DynamicMeasure : '@Analytics.AggregatedProperty#countBookings',
      Role      : #Axis1,
    }],
    DimensionAttributes : [{
      Dimension : status,
      Role      : #Category
    }]
  }
) {
  status @(
    Common.ValueList #vlStatus : {
      CollectionPath               : 'Bookings',
      PresentationVariantQualifier : 'pvStatus',
      Parameters                   : [{
        $Type             : 'Common.ValueListParameterInOut',
        LocalDataProperty : status,
        ValueListProperty : 'status'
      }]
    },
    Common.ValueList : {
      CollectionPath : 'BookingStatus',
      Parameters : [{
        $Type : 'Common.ValueListParameterInOut',
        LocalDataProperty : status,
        ValueListProperty : 'code',
      }]
    },
    //Common.ValueListWithFixedValues : true
  )
};


annotate service.Bookings with @(
  UI.PresentationVariant #pvFlightDate : {
    SortOrder      : [{
      Property   : FlightDate,
      Descending : true
    }],
    Visualizations : ['@UI.Chart#chartFlightDate']
  },
  UI.Chart #chartFlightDate            : {
    Title               : 'Bookings over FlightDate',
    ChartType           : #Line,
    DynamicMeasures : [
      '@Analytics.AggregatedProperty#countBookings',
    ],
    Dimensions          : [FlightDate],
    MeasureAttributes   : [{
      DynamicMeasure : '@Analytics.AggregatedProperty#countBookings',
      Role      : #Axis1,
    }],
    DimensionAttributes : [{
      Dimension : FlightDate,
      Role      : #Category
    }]
  }
) {
  FlightDate @Common.ValueList #vlFlightDate : {
    CollectionPath               : 'Bookings',
    PresentationVariantQualifier : 'pvFlightDate',
    Parameters                   : [{
      $Type             : 'Common.ValueListParameterInOut',
      LocalDataProperty : FlightDate,
      ValueListProperty : 'FlightDate'
    }]
  };
};



//
// KPI
//
annotate service.Bookings with @(
  UI.KPI #myKPI1 : {
    DataPoint : {
      Value            : FlightPrice,
      Title            : 'TOT',
      Description      : '{i18n>Total Price}',
      CriticalityCalculation : {
        ImprovementDirection: #Maximize,
        AcceptanceRangeLowValue: 26000000,
        ToleranceRangeLowValue:  24000000,
        DeviationRangeLowValue:  22000000
      }
    },
    Detail : {
      DefaultPresentationVariant : {
        Visualizations : [
          '@UI.Chart#kpi1'
        ],
      },
    },
    SelectionVariant : {
      SelectOptions : [{
        PropertyName : FlightPrice,
        Ranges       : [{
          Sign   : #E,
          Option : #EQ,
          Low    : 0,
        }, ],
      }],
    }
  },
  UI.Chart #kpi1 : {
    ChartType         : #Line,
    Measures          : [FlightPrice],
    Dimensions        : [FlightDate],
    MeasureAttributes : [{
      Measure : FlightPrice,
      Role    : #Axis1
    }],
    DimensionAttributes : [{
      Dimension : FlightDate,
      Role      : #Category
    }]
  },
);



//
// Detail page
//
annotate service.Bookings with @UI : {
  Identification : [
    { Value : CombinedID },
  ],
  HeaderInfo : {
    TypeName       : '{i18n>Bookings}',
    TypeNamePlural : '{i18n>Bookings}',
    Title          : { Value : CombinedID },
    Description    : { Value : CombinedID }
  },
  Facets : [{
    $Type  : 'UI.CollectionFacet',
    Label  : '{i18n>BookingDetails}',
    ID     : 'Booking',
    Facets : [{
      $Type  : 'UI.ReferenceFacet',
      ID     : 'TravelData',
      Target : '@UI.FieldGroup#TravelInformation',
      Label  : '{i18n>Travel}'
    }, {
      $Type  : 'UI.ReferenceFacet',
      ID     : 'BookingData',
      Target : '@UI.FieldGroup#BookingInformation',
      Label  : '{i18n>Booking}'
    }, {
      $Type  : 'UI.ReferenceFacet',
      ID     : 'FlightData',
      Target : '@UI.FieldGroup#FlightInformation',
      Label  : '{i18n>Flight}'
    }]
  }],
  FieldGroup #TravelInformation : { Data : [
    { Value : to_Travel.TravelID,
      Label : '{i18n>TravelID}'            },
    { Value : to_Travel.Description        },
    { Value : to_Travel.to_Agency.Name,    },
    { Value : to_Travel.CustomerName,      },
    { Value : to_Travel.TravelStatus.code,
      Label : '{i18n>Status}'              },    // why does the label not come from below?
  ]},
  FieldGroup #BookingInformation : { Data : [
    { Value : BookingID,
      Label : '{i18n>BookingID}'        },
    { Value : BookingDate               },
    { Value : FlightDate,               },
    { Value : FlightPrice               },
    { Value : status,
      Label : '{i18n>Status}'           },
  ]},
  FieldGroup #FlightInformation : { Data : [
    { Value : airline,                  },
    { Value : to_Carrier.AirlinePicURL, },
    { Value : ConnectionID              },
    // Java doesn't work with these association paths
    // { Value : to_Flight.PlaneType       },
    // { Value : to_Flight.to_Connection.DepartureAirport.AirportID,
    //   Label: '{i18n>DepartureAirport}'          },
    // { Value : to_Flight.to_Connection.DestinationAirport.AirportID,
    //   Label: '{i18n>ArrivalAirport}'            },
    // { Value : to_Flight.to_Connection.Distance, },

    // Workaround:
    { Value : PlaneType       },
    { Value : DepAirport,     },
    { Value : DestAirport     },
    { Value : Distance,       },
  ]},
};

// determines the order of visual filters
annotate service.Bookings with @UI.SelectionFields : [
  FlightDate,
  status,
  airline
];


================================================
FILE: app/travel_analytics/karma.conf.js
================================================
module.exports = require("../.karma/karma.conf.js")

================================================
FILE: app/travel_analytics/package.json
================================================
{
  "name": "travel-analytics",
  "version": "1.0.0",
  "private": true,
  "description": "SFlight ALP",
  "main": "webapp/index.html",
  "engines": {
    "node": ">=18"
  },
  "scripts": {
    "build": "ui5 build preload --clean-dest --include-task=generateCachebusterInfo",
    "start": "ui5 serve",
    "test": "npm run test:node && npm run test:java",
    "test:java": "karma start --server=java --single-run",
    "test:node": "karma start --server=node --single-run",
    "ts-typecheck": "tsc --noEmit",
    "prestart": "npm run ts-typecheck",
    "prebuild": "npm run ts-typecheck",
    "deploy-config": "npx -p @sap/ux-ui5-tooling fiori add deploy-config cf"
  },
  "keywords": [
    "ui5",
    "openui5",
    "sapui5"
  ],
  "devDependencies": {
    "@sap/ux-ui5-tooling": "1",
    "@sapui5/types": "^1.139.0",
    "@typescript-eslint/eslint-plugin": "^8",
    "@typescript-eslint/parser": "^8",
    "@ui5/cli": "^4.0.0",
    "karma": "^6.4.3",
    "karma-chrome-launcher": "^3.2.0",
    "karma-ui5": "^4.0.1",
    "karma-ui5-transpile": "^3.5.1",
    "puppeteer": "^24",
    "typescript": "^5.1.6",
    "ui5-middleware-simpleproxy": "^3.2.16",
    "ui5-task-zipper": "^3.1.4",
    "ui5-tooling-transpile": "^3.3.7"
  }
}


================================================
FILE: app/travel_analytics/tsconfig.json
================================================
{
    "compilerOptions": {
        "target": "es2022",
        "module": "es2022",
        "skipLibCheck": true,
        "allowJs": true,
        "strict": true,
        "strictPropertyInitialization": false,
        "moduleResolution": "node",
        "rootDir": "./webapp",
        "outDir": "./dist",
        "baseUrl": "./",
        "paths": {
            "sap/fe/cap/travel_analytics/*": [
                "./webapp/*"
            ]
        },
        "typeRoots": [
            "./node_modules/@types",
            "./node_modules/@sapui5/types",
            "../../node_modules/@types",
            "../../node_modules/@sapui5/types"
          ]
    },
    "include": [
        "./webapp/**/*"
    ]
}

================================================
FILE: app/travel_analytics/ui5.yaml
================================================
# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json
specVersion: "3.0"
metadata:
  name: sap.fe.cap.travel_analytics
type: application

resources:
  configuration:
    propertiesFileSourceEncoding: UTF-8

builder:
  resources:
    excludes:
      - "/test/**"
      - "/localService/**"
  customTasks:
    - name: ui5-task-zipper
      afterTask: generateCachebusterInfo
      configuration:
        archiveName: travel-analytics
        additionalFiles:
          - xs-app.json
    - name: ui5-tooling-transpile-task
      afterTask: replaceVersion
      # configuration:
        # debug: true

server:
  customMiddleware:
    - name: ui5-middleware-simpleproxy
      mountPath: /analytics
      afterMiddleware: compression
      configuration:
        baseUri: http://localhost:4004/analytics
        username: admin # dummy credentials for local testing
        password: admin # dummy credentials for local testing
    - name: fiori-tools-appreload
      afterMiddleware: compression
    - name: ui5-tooling-transpile-middleware
      afterMiddleware: compression
      configuration:
        # debug: true
        excludePatterns:
          - /Component-preload.js


================================================
FILE: app/travel_analytics/webapp/Component.ts
================================================
import BaseComponent from "sap/fe/core/AppComponent";

/**
 * @namespace sap.fe.cap.travel_analytics
 */
export default class Component extends BaseComponent {

  public static metadata = {
    manifest: "json"
  };

  /**
   * The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
   * @public
   * @override
   */
  //public init() : void {
  //    super.init();
  //}
}

================================================
FILE: app/travel_analytics/webapp/i18n/i18n.properties
================================================
# This is the resource bundle for travel_processor

#Texts for manifest.json

#XTIT: Application name
title=Analyze Bookings

#YDES: Application description
description=Analyze Bookings


================================================
FILE: app/travel_analytics/webapp/i18n/i18n_de.properties
================================================
# This is the resource bundle for travel_processor

#Texts for manifest.json

#XTIT: Application name
title=Buchungen analysieren

#YDES: Application description
description=Buchungen analysieren


================================================
FILE: app/travel_analytics/webapp/i18n/i18n_en.properties
================================================
# This is the resource bundle for travel_processor

#Texts for manifest.json

#XTIT: Application name
title=Analyze Bookings

#YDES: Application description
description=Analyze Bookings


================================================
FILE: app/travel_analytics/webapp/i18n/i18n_fr.properties
================================================
# This is the resource bundle for travel_processor

#Texts for manifest.json

#XTIT: Application name
title=Analyser les réservations

#YDES: Application description
description=Analyser les réservations


================================================
FILE: app/travel_analytics/webapp/index.html
================================================
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title></title>
    <style>
      html,
      body,
      body > div,
      #container,
      #container-uiarea {
        height: 100%;
      }
    </style>
    <script
      id="sap-ui-bootstrap"
      src="https://ui5.sap.com/1.139.0/resources/sap-ui-core.js"
      data-sap-ui-theme="sap_horizon"
      data-sap-ui-oninit="module:sap/ui/core/ComponentSupport"
      data-sap-ui-resourceroots='{ "sap.fe.cap.travel_analytics": "./" }'
      data-sap-ui-compatVersion="edge"
      data-sap-ui-async="true"
      data-sap-ui-preload="async"
      data-sap-ui-flexibilityServices='[{"connector": "SessionStorageConnector"}]'
      data-sap-ui-frameOptions="trusted"
      data-sap-ui-libs="sap.m, sap.fe.core"
    ></script>
  </head>
  <body class="sapUiBody sapUiSizeCompact" id="content">
    <div
      data-sap-ui-component
      data-name="sap.fe.cap.travel_analytics"
      data-id="container"
      data-settings='{"id" : "sap.fe.cap.travel_analytics"}'
      data-handle-validation="true"
    ></div>
  </body>
</html>


================================================
FILE: app/travel_analytics/webapp/manifest.json
================================================
{
    "_version": "1.42.0",
    "sap.app": {
        "id": "sap.fe.cap.travel_analytics",
        "type": "application",
        "title": "{{title}}",
        "description": "{{description}}",
        "i18n": {
            "bundleName": "sap.fe.cap.travel_analytics.i18n.i18n",
            "supportedLocales": [
                "en",
                "de",
                "fr"
            ],
            "fallbackLocale": "en"
        },
        "applicationVersion": {
            "version": "1.0.0"
        },
        "dataSources": {
            "mainService": {
                "uri": "analytics/",
                "type": "OData",
                "settings": {
                    "odataVersion": "4.0"
                }
            }
        },
        "resources": "resources.json",
        "sourceTemplate": {
            "id": "@sap/generator-fiori:lrop",
            "version": "1.7.5",
            "toolsId": "14e5ba92-7ef6-40ce-9b6b-9eff19d0c9a2"
        },
        "crossNavigation": {
            "inbounds": {
                "travel-alp-inbound": {
                    "signature": {
                        "parameters": {},
                        "additionalParameters": "allowed"
                    },
                    "semanticObject": "booking",
                    "action": "process",
                    "title": "Analyze Travels",
                    "subTitle": "Analyze travels",
                    "icon": "sap-icon://flight"
                }
            }
        }
    },
    "sap.ui": {
        "technology": "UI5",
        "icons": {
            "icon": "sap-icon://flight",
            "favIcon": "sap-icon://flight",
            "phone": "sap-icon://flight",
            "phone@2": "sap-icon://flight",
            "tablet": "sap-icon://flight",
            "tablet@2": "sap-icon://flight"
        },
        "deviceTypes": {
            "desktop": true,
            "tablet": true,
            "phone": true
        }
    },
    "sap.ui5": {
        "flexEnabled": false,
        "dependencies": {
            "minUI5Version": "1.139.0",
            "libs": {
                "sap.m": {},
                "sap.ui.core": {},
                "sap.ushell": {},
                "sap.fe.templates": {}
            }
        },
        "contentDensities": {
            "compact": true,
            "cozy": true
        },
        "models": {
            "i18n": {
                "type": "sap.ui.model.resource.ResourceModel",
                "settings": {
                    "bundleName": "sap.fe.cap.travel_analytics.i18n.i18n",
                    "supportedLocales": [
                        "en",
                        "de",
                        "fr"
                    ],
                    "fallbackLocale": "en"
                }
            },
            "": {
                "dataSource": "mainService",
                "preload": true,
                "settings": {
                    "synchronizationMode": "None",
                    "operationMode": "Server",
                    "autoExpandSelect": true,
                    "earlyRequests": true
                }
            },
            "@i18n": {
                "type": "sap.ui.model.resource.ResourceModel",
                "uri": "i18n/i18n.properties"
            }
        },
        "resources": {
            "css": []
        },
        "routing": {
            "config": {},
            "routes": [
                {
                    "pattern": ":?query:",
                    "name": "BookingsList",
                    "target": "BookingsList"
                },
                {
                    "pattern": "Bookings({key}):?query:",
                    "name": "BookingsObjectPage",
                    "target": "BookingsObjectPage"
                }
            ],
            "targets": {
                "BookingsList": {
                    "type": "Component",
                    "id": "BookingsList",
                    "name": "sap.fe.templates.ListReport",
                    "options": {
                        "settings": {
                            "entitySet": "Bookings",
                            "variantManagement": "Page",
                            "initialLoad": "Enabled",
                            "navigation": {
                                "Bookings": {
                                    "detail": {
                                        "route": "BookingsObjectPage"
                                    }
                                }
                            },
                            "views": {
                                "paths": [
                                    {
                                        "primary": [
                                            {
                                                "annotationPath": "@com.sap.vocabularies.UI.v1.PresentationVariant"
                                            }
                                        ],
                                        "secondary": [
                                            {
                                                "annotationPath": "@com.sap.vocabularies.UI.v1.PresentationVariant"
                                            }
                                        ],
                                        "defaultPath": "both"
                                    }
                                ]
                            },
                            "controlConfiguration": {
                                "@com.sap.vocabularies.UI.v1.LineItem": {
                                    "tableSettings": {
                                        "type": "AnalyticalTable",
                                        "enableExport": true
                                    }
                                },
                                "@com.sap.vocabularies.UI.v1.SelectionFields": {
                                    "layout": "CompactVisual",
                                    "initialLayout": "Visual",
                                    "filterFields": {
                                        "status": {
                                            "label": "Status",
                                            "availability": "Default",
                                            "visualFilter": {
                                                "valueList": "com.sap.vocabularies.Common.v1.ValueList#vlStatus"
                                            }
                                        },
                                        "airline": {
                                            "label": "Status",
                                            "availability": "Default",
                                            "visualFilter": {
                                                "valueList": "com.sap.vocabularies.Common.v1.ValueList#vlAirline"
                                            }
                                        },
                                        "FlightDate": {
                                            "label": "Flight Date",
                                            "availability": "Default",
                                            "visualFilter": {
                                                "valueList": "com.sap.vocabularies.Common.v1.ValueList#vlFlightDate"
                                            }
                                        }
                                    }
                                }
                            },
                            "keyPerformanceIndicators": {
                                "KPI1": {
                                    "entitySet": "Bookings",
                                    "qualifier": "myKPI1"
                                }
                            }
                        }
                    }
                },
                "BookingsObjectPage": {
                    "type": "Component",
                    "id": "BookingsObjectPage",
                    "name": "sap.fe.templates.ObjectPage",
                    "options": {
                        "settings": {
                            "entitySet": "Bookings",
                            "editableHeaderContent": false
                        }
                    }
                }
            }
        }
    },
    "sap.fiori": {
        "registrationIds": [],
        "archeType": "transactional"
    },
    "sap.cloud": {
        "public": true,
        "service": "sap.fe.cap.sflight"
    }
}


================================================
FILE: app/travel_analytics/webapp/test/flpSandbox.html
================================================
<!DOCTYPE html>
<html lang="en">
  <!-- Copyright (c) 2015 SAP AG, All Rights Reserved -->
  <head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{{appTitle}}</title>

    <!-- Bootstrap the unified shell in sandbox mode for standalone usage.

         The renderer is specified in the global Unified Shell configuration object "sap-ushell-config".

         The fiori2 renderer will render the shell header allowing, for instance,
         testing of additional application setting buttons.

         The navigation target resolution service is configured in a way that the empty URL hash is
         resolved to our own application.

         This example uses relative path references for the SAPUI5 resources and test-resources;
         it might be necessary to adapt them depending on the target runtime platform.
         The sandbox platform is restricted to development or demo use cases and must NOT be used
         for productive scenarios.
    -->
    <script type="text/javascript">
      window["sap-ushell-config"] = {
        defaultRenderer: "fiori2",
        bootstrapPlugins: {
          RuntimeAuthoringPlugin: {
            component: "sap.ushell.plugins.rta",
            config: {
              validateAppVersion: false,
            },
          },
        },
        renderers: {
          fiori2: {
            componentData: {
              config: {
                search: "hidden",
              },
            },
          },
        },
        applications: {
          "sapfecapsflightsflightanalytics-tile": {
            title: "SFlight Analytics",
            description: "SFlight ALP",
            additionalInformation:
              "SAPUI5.Component=sap.fe.cap.travel_analytics",
            applicationType: "URL",
            url: "../",
          },
        },
      };
    </script>

    <script
      src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"
      id="sap-ushell-bootstrap"
    ></script>
    <!-- Bootstrap the UI5 core library -->
    <script
      id="sap-ui-bootstrap"
      src="https://ui5.sap.com/1.139.0/resources/sap-ui-core.js"
      data-sap-ui-libs=""
      data-sap-ui-async="true"
      data-sap-ui-preload="async"
      data-sap-ui-theme="sap_horizon"
      data-sap-ui-compatVersion="edge"
      data-sap-ui-language="en"
      data-sap-ui-resourceroots='{"sap.fe.cap.travel_analytics": "../"}'
      data-sap-ui-frameOptions="allow"
    >
      // NON-SECURE setting for testing environment
    </script>

    <script>
      sap.ui.getCore().attachInit(async () => {
        const renderer = await sap.ushell.Container.createRenderer(true);
        renderer.placeAt('content');
      });
    </script>
  </head>

  <!-- UI Content -->

  <body class="sapUiBody" id="content"></body>
</html>


================================================
FILE: app/travel_analytics/webapp/test/integration/Opa.qunit.html
================================================
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Integration tests</title>
    <meta charset="UTF-8" content="IE=edge" http-equiv="X-UA-Compatible" />
    <meta content="text/html;charset=UTF-8" http-equiv="Content-Type" />
    <meta content="width=device-width, initial-scale=1.0" name="viewport" />

    <script
      id="sap-ui-bootstrap"
      src="https://ui5.sap.com/1.139.0/resources/sap-ui-core.js"
      data-sap-ui-animation="false"
      data-sap-ui-compatVersion="edge"
      data-sap-ui-resourceroots='{
                "sap.fe.cap.travel_analytics": "../../"
            }'
      data-sap-ui-theme="sap_horizon"
      data-sap-ui-libs="sap.m, sap.fe.core"
    ></script>

    <link
      rel="stylesheet"
      type="text/css"
      href="https://ui5.sap.com/1.139.0/resources/sap/ui/thirdparty/qunit-2.css"
    />

    <script src="https://ui5.sap.com/1.139.0/resources/sap/ui/thirdparty/qunit-2.js"></script>
    <script src="https://ui5.sap.com/1.139.0/resources/sap/ui/qunit/qunit-junit.js"></script>

    <script src="Opa.qunit.js"></script>
  </head>
  <body>
    <div id="qunit"></div>
    <div id="qunit-fixture"></div>
  </body>
</html>


================================================
FILE: app/travel_analytics/webapp/test/integration/Opa.qunit.js
================================================
sap.ui.require(
  [
    "sap/fe/test/JourneyRunner",
    "sap/fe/cap/travel_analytics/test/integration/OpaJourney",
    "sap/fe/cap/travel_analytics/test/integration/pages/BookingsList",
    "sap/fe/cap/travel_analytics/test/integration/pages/BookingsObjectPage",
  ],
  function (JourneyRunner, opaJourney, BookingsList, BookingsObjectPage) {
    "use strict";

    const runner = new JourneyRunner({
      // start index.html in web folder
      launchUrl:
        sap.ui.require.toUrl("sap/fe/cap/travel_analytics") + "/index.html",
    });

    runner.run(
      {
        pages: {
          onTheBookingsList: BookingsList,
          onTheBookingsObjectPage: BookingsObjectPage,
        },
      },
      opaJourney.run
    );
  }
);


================================================
FILE: app/travel_analytics/webapp/test/integration/OpaJourney.js
================================================
/* global QUnit */
sap.ui.define(["sap/ui/test/opaQunit"], function (opaTest) {
  "use strict";

  return {
    run: function () {
      QUnit.module("Travel Analytics Tests");

      opaTest("Start application", function (Given, When, Then) {
        Given.iStartMyApp();

        Then.onTheBookingsList.iSeeThisPage();
      });

      opaTest("Teardown", function (Given /*, When, Then*/) {
        // Cleanup
        Given.iTearDownMyApp();
      });
    },
  };
});


================================================
FILE: app/travel_analytics/webapp/test/integration/pages/BookingsList.js
================================================
sap.ui.define(["sap/fe/test/ListReport"], function (ListReport) {
  "use strict";

  const CustomPageDefinitions = {
    actions: {},
    assertions: {},
  };

  return new ListReport(
    {
      appId: "sap.fe.cap.travel_analytics",
      componentId: "BookingsList",
      entitySet: "Bookings",
    },
    CustomPageDefinitions
  );
});


================================================
FILE: app/travel_analytics/webapp/test/integration/pages/BookingsObjectPage.js
================================================
sap.ui.define(["sap/fe/test/ObjectPage"], function (ObjectPage) {
  "use strict";

  const CustomPageDefinitions = {
    actions: {},
    assertions: {},
  };

  return new ObjectPage(
    {
      appId: "sap.fe.cap.travel_analytics",
      componentId: "BookingsObjectPage",
      entitySet: "Bookings",
    },
    CustomPageDefinitions
  );
});


================================================
FILE: app/travel_analytics/webapp/test/testsuite.qunit.html
================================================
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>QUnit test suite</title>
    <script src="../resources/sap/ui/qunit/qunit-redirect.js"></script>
    <script src="testsuite.qunit.js" data-sap-ui-testsuite></script>
  </head>
  <body></body>
</html>


================================================
FILE: app/travel_analytics/webapp/test/testsuite.qunit.js
================================================
/* global window, parent, location */
window.suite = function () {
  "use strict";

  const oSuite = new parent.jsUnitTestSuite(),
    sContextPath = location.pathname.substring(
      0,
      location.pathname.lastIndexOf("/") + 1
    );
  oSuite.addTestPage(sContextPath + "integration/Opa.qunit.html");

  return oSuite;
};


================================================
FILE: app/travel_analytics/xs-app.json
================================================
{
  "welcomeFile": "/index.html",
  "authenticationMethod": "route",
  "routes": [
    {
      "source": "^/analytics/(.*)$",
      "target": "/analytics/$1",
      "destination": "sflight-srv",
      "authenticationType": "xsuaa",
      "csrfProtection": false
    },
    {
      "source": "^(.*)$",
      "target": "$1",
      "service": "html5-apps-repo-rt",
      "authenticationType": "xsuaa"
    }
  ]
}


================================================
FILE: app/travel_analytics/xs-security.json
================================================
{
  "scopes": [
    {
      "name": "$XSAPPNAME.reviewer",
      "description": "reviewer"
    },
    {
      "name": "$XSAPPNAME.processor",
      "description": "processor"
    },
    {
      "name": "$XSAPPNAME.admin",
      "description": "admin"
    }
  ],
  "attributes": [],
  "role-templates": [
    {
      "name": "reviewer",
      "description": "generated",
      "scope-references": [
        "$XSAPPNAME.reviewer"
      ],
      "attribute-references": []
    },
    {
      "name": "processor",
      "description": "generated",
      "scope-references": [
        "$XSAPPNAME.processor"
      ],
      "attribute-references": []
    },
    {
      "name": "admin",
      "description": "generated",
      "scope-references": [
        "$XSAPPNAME.admin"
      ],
      "attribute-references": []
    }
  ]
}


================================================
FILE: app/travel_processor/capabilities.cds
================================================
using TravelService from '../../srv/travel-service';

annotate TravelService.Travel with @odata.draft.enabled;
annotate TravelService.Travel with @Common.SemanticKey: [TravelID];
annotate TravelService.Booking with @Common.SemanticKey: [BookingID];
annotate TravelService.BookingSupplement with @Common.SemanticKey: [BookingSupplementID];


================================================
FILE: app/travel_processor/field-control.cds
================================================
using TravelService from '../../srv/travel-service';

//
// annotations that control the behavior of fields and actions
//


using { sap.fe.cap.travel.TravelStatus } from '../../db/schema';

// As we use field control based on travel status for many elements, we do the computation in a calculated element
// right here instead of repeating the same expression in multiple annotations
extend TravelStatus with {  
  // can't use UInt8 (which would automatically be mapped to Edm.Byte) because it's not supported on H2
  fieldControl: Int16 @odata.Type:'Edm.Byte' enum {Inapplicable = 0; ReadOnly = 1; Optional = 3; Mandatory = 7;}
    = (code = #Accepted ? #ReadOnly : #Mandatory );
}


annotate TravelService.Travel with @(Common : {
  SideEffects: {
    SourceProperties: [BookingFee],
    TargetProperties: ['TotalPrice', 'GreenFee', 'TreesPlanted']
  },
  SideEffects #GoGreen:{
    SourceProperties: [GoGreen],
    TargetProperties: ['TotalPrice', 'GreenFee', 'TreesPlanted']
  }
}){
  BookingFee  @Common.FieldControl  : TravelStatus.fieldControl;
  BeginDate   @Common.FieldControl  : TravelStatus.fieldControl;
  EndDate     @Common.FieldControl  : TravelStatus.fieldControl;
  to_Agency   @Common.FieldControl  : TravelStatus.fieldControl;
  to_Customer @Common.FieldControl  : TravelStatus.fieldControl;

} actions {
  rejectTravel @(
    Core.OperationAvailable : ($self.TravelStatus.code != #Canceled),
    Common.SideEffects.TargetProperties : ['in/TravelStatus_code'],
  );
  acceptTravel @(
    Core.OperationAvailable : ($self.TravelStatus.code != #Accepted),
    Common.SideEffects.TargetProperties : ['in/TravelStatus_code'],
  );
  deductDiscount @(
    Core.OperationAvailable : ($self.TravelStatus.code = #Open),
    Common.SideEffects.TargetProperties : ['in/TotalPrice', 'in/BookingFee'],
  );
}

annotate TravelService.Travel @(
    Common.SideEffects#ReactonItemCreationOrDeletion : {
        SourceEntities : [
            to_Booking
        ],
       TargetProperties : ['TotalPrice'
       ]
    }
);

annotate TravelService.Booking with @UI.CreateHidden : (to_Travel.TravelStatus.code != #Open);
annotate TravelService.Booking with @UI.DeleteHidden : (to_Travel.TravelStatus.code != #Open);

annotate TravelService.Booking {
  BookingDate   @Core.Computed;
  ConnectionID  @Common.FieldControl  : to_Travel.TravelStatus.fieldControl;
  FlightDate    @Common.FieldControl  : to_Travel.TravelStatus.fieldControl;
  FlightPrice   @Common.FieldControl  : to_Travel.TravelStatus.fieldControl;
  BookingStatus @Common.FieldControl  : to_Travel.TravelStatus.fieldControl;
  to_Carrier    @Common.FieldControl  : to_Travel.TravelStatus.fieldControl;
  to_Customer   @Common.FieldControl  : to_Travel.TravelStatus.fieldControl;
};

annotate TravelService.Booking with @(
  Capabilities.NavigationRestrictions : {
    RestrictedProperties : [
      {
        NavigationProperty : to_BookSupplement,
        InsertRestrictions : {
          Insertable : (to_Travel.TravelStatus.code = #Open)
        },
        DeleteRestrictions : {
          Deletable : (to_Travel.TravelStatus.code = #Open)
        }
      }
    ]
  }
);


annotate TravelService.BookingSupplement {
  Price         @Common.FieldControl  : to_Travel.TravelStatus.fieldControl;
  to_Supplement @Common.FieldControl  : to_Travel.TravelStatus.fieldControl;
  to_Booking    @Common.FieldControl  : to_Travel.TravelStatus.fieldControl;
  to_Travel     @Common.FieldControl  : to_Travel.TravelStatus.fieldControl;
};


================================================
FILE: app/travel_processor/karma.conf.js
================================================
module.exports = require("../.karma/karma.conf.js")

================================================
FILE: app/travel_processor/layouts.cds
================================================
using TravelService from '../../srv/travel-service';

//
// annotations that control the Fiori layout
//

annotate TravelService.Travel with @UI : {

  Identification : [
    { $Type  : 'UI.DataFieldForAction', Action : 'TravelService.acceptTravel',   Label  : '{i18n>AcceptTravel}'   },
    { $Type  : 'UI.DataFieldForAction', Action : 'TravelService.rejectTravel',   Label  : '{i18n>RejectTravel}'   },
    { $Type  : 'UI.DataFieldForAction', Action : 'TravelService.deductDiscount', Label  : '{i18n>DeductDiscount}' }
  ],
  HeaderInfo : {
    TypeName       : '{i18n>Travel}',
    TypeNamePlural : '{i18n>Travels}',
    Title          : {
      $Type : 'UI.DataField',
      Value : Description
    },
    Description    : {
      $Type : 'UI.DataField',
      Value : TravelID
    }
  },
  PresentationVariant : {
    Text           : 'Default',
    Visualizations : ['@UI.LineItem'],
    SortOrder      : [{
      $Type      : 'Common.SortOrderType',
      Property   : TravelID,
      Descending : true
    }]
  },
  SelectionFields : [
    to_Agency_AgencyID,
    to_Customer_CustomerID,
    TravelStatus_code
  ],
  LineItem : [
    { $Type  : 'UI.DataFieldForAction', Action : 'TravelService.acceptTravel',   Label  : '{i18n>AcceptTravel}'   },
    { $Type  : 'UI.DataFieldForAction', Action : 'TravelService.rejectTravel',   Label  : '{i18n>RejectTravel}'   },
    { $Type  : 'UI.DataFieldForAction', Action : 'TravelService.deductDiscount', Label  : '{i18n>DeductDiscount}' },
    {
      Value : TravelID,
      @UI.Importance : #High
    },
    {
      Value : (to_Agency.AgencyID),
      @HTML5.CssDefaults: {width:'16em'}
    },
    {
      Value : (to_Customer.CustomerID),
      @UI.Importance : #High,
      @HTML5.CssDefaults: {width:'14em'}
    },
    { Value : BeginDate,  @HTML5.CssDefaults: {width:'9em'} },
    { Value : EndDate,    @HTML5.CssDefaults: {width:'9em'} },
    { Value : BookingFee, @HTML5.CssDefaults: {width:'10em'} },
    { Value : TotalPrice, @HTML5.CssDefaults: {width:'12em'} },
    {
      Value : (TravelStatus.code),
      Criticality : (TravelStatus.code = #Open ? 2 : (TravelStatus.code = #Accepted ? 3 : 0)),
      @UI.Importance : #High,
      @HTML5.CssDefaults: {width:'10em'}
    }
  ],
  Facets : [{
    $Type  : 'UI.CollectionFacet',
    Label  : '{i18n>GeneralInformation}',
    ID     : 'Travel',
    Facets : [
      {  // travel details
        $Type  : 'UI.ReferenceFacet',
        ID     : 'TravelData',
        Target : '@UI.FieldGroup#TravelData',
        Label  : '{i18n>GeneralInformation}'
      },
      {  // price information
        $Type  : 'UI.ReferenceFacet',
        ID     : 'PriceData',
        Target : '@UI.FieldGroup#PriceData',
        Label  : '{i18n>Prices}'
      },
      {  // date information
        $Type  : 'UI.ReferenceFacet',
        ID     : 'DateData',
        Target : '@UI.FieldGroup#DateData',
        Label  : '{i18n>Dates}'
      },
      {
        $Type : 'UI.ReferenceFacet',
        Label : '{i18n>Sustainability}',
        ID    : 'i18nSustainability',
        Target: '@UI.FieldGroup#i18nSustainability',
      }
    ]
  }],
  FieldGroup#TravelData : { Data : [
    { Value : TravelID               },
    { Value : (to_Agency.AgencyID)     },
    { Value : (to_Customer.CustomerID) },
    { Value : Description            },
    {
      $Type       : 'UI.DataField',
      Value       : (TravelStatus.code),
      Criticality : (TravelStatus.code = #Open ? 2 : (TravelStatus.code = #Accepted ? 3 : 0)),
      Label : '{i18n>Status}' // label only necessary if differs from title of element
    }
  ]},
  FieldGroup #DateData : {Data : [
    { $Type : 'UI.DataField', Value : BeginDate },
    { $Type : 'UI.DataField', Value : EndDate }
  ]},
  FieldGroup #PriceData : {Data : [
    { $Type : 'UI.DataField', Value : BookingFee },
    { $Type : 'UI.DataField', Value : TotalPrice },
    { $Type : 'UI.DataField', Value : (CurrencyCode.code) }
  ]},
  FieldGroup #i18nSustainability: {
    $Type: 'UI.FieldGroupType',
    Data : [
      {
        $Type: 'UI.DataField',
        Value: GoGreen,
      },
      {
        $Type: 'UI.DataField',
        Value: GreenFee,
      },
      {
        $Type: 'UI.DataField',
        Value: TreesPlanted,
      },
    ],
  }
};

annotate TravelService.Booking with @UI : {
  Identification : [
    { Value : BookingID },
  ],
  HeaderInfo : {
    TypeName       : '{i18n>Bookings}',
    TypeNamePlural : '{i18n>Bookings}',
    Title          : { Value : to_Customer.LastName },
    Description    : { Value : BookingID }
  },
  PresentationVariant : {
    Visualizations : ['@UI.LineItem'],
    SortOrder      : [{
      $Type      : 'Common.SortOrderType',
      Property   : BookingID,
      Descending : false
    }]
  },
  SelectionFields : [],
  LineItem : [
    { Value : to_Carrier.AirlinePicURL,  Label : '  '},
    { Value : BookingID              },
    { Value : BookingDate            },
    { Value : (to_Customer.CustomerID) },
    { Value : (to_Carrier.AirlineID )  },
    { Value : ConnectionID,          Label : '{i18n>FlightNumber}' },
    { Value : FlightDate             },
    { Value : FlightPrice            },
    { Value : (BookingStatus.code),
      Criticality : (BookingStatus.code = #New ? 2 : (BookingStatus.code = #Booked ? 3 : 0)),
    }
  ],
  Facets : [{
    $Type  : 'UI.CollectionFacet',
    Label  : '{i18n>GeneralInformation}',
    ID     : 'Booking',
    Facets : [{  // booking details
      $Type  : 'UI.ReferenceFacet',
      ID     : 'BookingData',
      Target : '@UI.FieldGroup#GeneralInformation',
      Label  : '{i18n>Booking}'
    }, {  // flight details
      $Type  : 'UI.ReferenceFacet',
      ID     : 'FlightData',
      Target : '@UI.FieldGroup#Flight',
      Label  : '{i18n>Flight}'
    }]
  }, {  // supplements list
    $Type  : 'UI.ReferenceFacet',
    ID     : 'SupplementsList',
    Target : 'to_BookSupplement/@UI.PresentationVariant',
    Label  : '{i18n>BookingSupplements}'
  }],
  FieldGroup #GeneralInformation : { Data : [
    { Value : BookingID              },
    { Value : BookingDate,           },
    { Value : (to_Customer.CustomerID) },
    { Value : BookingDate,           },
    { Value : (BookingStatus.code),
      Criticality : (BookingStatus.code = #New ? 2 : (BookingStatus.code = #Booked ? 3 : 0)),
     }
  ]},
  FieldGroup #Flight : { Data : [
    { Value : (to_Carrier.AirlineID) },
    { Value : ConnectionID           },
    { Value : FlightDate             },
    { Value : FlightPrice            }
  ]},
};

annotate TravelService.BookingSupplement with @UI : {
  Identification : [
    { Value : BookingSupplementID }
  ],
  HeaderInfo : {
    TypeName       : '{i18n>BookingSupplement}',
    TypeNamePlural : '{i18n>BookingSupplements}',
    Title          : { Value : BookingSupplementID },
    Description    : { Value : BookingSupplementID }
  },
  PresentationVariant : {
    Text           : 'Default',
    Visualizations : ['@UI.LineItem'],
    SortOrder      : [{
      $Type      : 'Common.SortOrderType',
      Property   : BookingSupplementID,
      Descending : false
    }]
  },
  LineItem : [
    { Value : BookingSupplementID                                       },
    { Value : (to_Supplement.SupplementID), Label : '{i18n>ProductID}'    },
    { Value : Price,                        Label : '{i18n>ProductPrice}' }
  ],
};


================================================
FILE: app/travel_processor/package.json
================================================
{
  "name": "travel-processor",
  "version": "1.0.0",
  "private": true,
  "main": "webapp/index.html",
  "engines": {
    "node": ">=18"
  },
  "scripts": {
    "build": "ui5 build preload --clean-dest --include-task=generateCachebusterInfo",
    "start": "ui5 serve",
    "test": "npm run test:node && npm run test:java",
    "test:java": "karma start --server=java --single-run",
    "test:node": "karma start --server=node --single-run",
    "ts-typecheck": "tsc --noEmit",
    "prestart": "npm run ts-typecheck",
    "prebuild": "npm run ts-typecheck",
    "deploy-config": "npx -p @sap/ux-ui5-tooling fiori add deploy-config cf"
  },
  "keywords": [
    "ui5",
    "openui5",
    "sapui5"
  ],
  "devDependencies": {
    "@sap/ux-ui5-tooling": "1",
    "@sapui5/types": "^1.139.0",
    "@typescript-eslint/eslint-plugin": "^8",
    "@typescript-eslint/parser": "^8",
    "@ui5/cli": "^4.0.0",
    "karma": "^6.4.3",
    "karma-chrome-launcher": "^3.2.0",
    "karma-ui5": "^4.0.1",
    "karma-ui5-transpile": "^3.5.1",
    "puppeteer": "^24",
    "typescript": "^5.1.6",
    "ui5-middleware-simpleproxy": "^3.2.16",
    "ui5-task-zipper": "^3.1.4",
    "ui5-tooling-transpile": "^3.3.7"
  }
}


================================================
FILE: app/travel_processor/tsconfig.json
================================================
{
    "compilerOptions": {
        "target": "es2022",
        "module": "es2022",
        "skipLibCheck": true,
        "allowJs": true,
        "strict": true,
        "strictPropertyInitialization": false,
        "moduleResolution": "node",
        "rootDir": "./webapp",
        "outDir": "./dist",
        "baseUrl": "./",
        "paths": {
            "sap/fe/cap/travel_processor/*": [
                "./webapp/*"
            ]
        },
        "typeRoots": [
            "./node_modules/@types",
            "./node_modules/@sapui5/types",
            "../../node_modules/@types",
            "../../node_modules/@sapui5/types"
          ]
    },
    "include": [
        "./webapp/**/*"
    ]
}

================================================
FILE: app/travel_processor/ui5.yaml
================================================
# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json
specVersion: "3.0"
metadata:
  name: sap.fe.cap.travel
type: application

resources:
  configuration:
    propertiesFileSourceEncoding: UTF-8

builder:
  resources:
    excludes:
      - "/test/**"
      - "/localService/**"
  customTasks:
    - name: ui5-task-zipper
      afterTask: generateCachebusterInfo
      configuration:
        archiveName: travel-processor
        additionalFiles:
          - xs-app.json
    - name: ui5-tooling-transpile-task
      afterTask: replaceVersion
      # configuration:
        # debug: true

server:
  customMiddleware:
    - name: ui5-middleware-simpleproxy
      mountPath: /processor
      afterMiddleware: compression
      configuration:
        baseUri: http://localhost:4004/processor
        username: admin # dummy credentials for local testing
        password: admin # dummy credentials for local testing
    - name: fiori-tools-appreload
      afterMiddleware: compression
    - name: ui5-tooling-transpile-middleware
      afterMiddleware: compression
      configuration:
        # debug: true
        excludePatterns:
          - /Component-preload.js


================================================
FILE: app/travel_processor/webapp/Component.ts
================================================
import BaseComponent from "sap/fe/core/AppComponent";

/**
 * @namespace sap.fe.cap.travel
 */
export default class Component extends BaseComponent {

	public static metadata = {
		manifest: "json"
	};

    /**
     * The component is initialized by UI5 automatically during the startup of the app and calls the init method once.
     * @public
     * @override
     */
	//public init() : void {
    //    super.init();
	//}
}


================================================
FILE: app/travel_processor/webapp/changes/changes-bundle.json
================================================
{}


================================================
FILE: app/travel_processor/webapp/changes/flexibility-bundle.json
================================================
{
  "compVariants": [],
  "changes": []
}


================================================
FILE: app/travel_processor/webapp/ext/controller/ControllerExtension.d.ts
================================================
/**
 * Helper to be able to define how to get the page specific extension API when writing a controller extension.
 */
declare module 'sap/ui/core/mvc/ControllerExtension' {
    export default class ControllerExtension<API> {
        static overrides: unknown;
        base: {
            getExtensionAPI(): API;
        }
    }
}


================================================
FILE: app/travel_processor/webapp/ext/controller/ObjectPageExtension.controller.ts
================================================
import ControllerExtension from "sap/ui/core/mvc/ControllerExtension";
import ExtensionAPI from "sap/fe/templates/ObjectPage/ExtensionAPI";
import Context from "sap/ui/model/odata/v4/Context";
import Dialog from "sap/m/Dialog";

/**
 * @namespace sap.fe.cap.travel.ext.controller
 * @controller
 */
export default class ObjectPageExtension extends ControllerExtension<ExtensionAPI> {
	async openDialog(resolve: (value: PromiseLike<null> | null) => void, reject: (reason?: any) => void, context: Context) {
		//try catch ensures errors in floating promises are handled properly
		try {
			let approveDialog = (await this.base.getExtensionAPI().loadFragment({
				id: "myFragment",
				initialBindingContext: context,
				name: "sap.fe.cap.travel.ext.fragment.Trees4Tickets"
			})) as Dialog;
			//Dialog Save button
			approveDialog.getBeginButton().attachPress(function () {
				approveDialog.close().destroy();
				resolve(null);
			});
			//Dialog Cancel button
			approveDialog.getEndButton().attachPress(function () {
				approveDialog.close().destroy();
				reject(null);
			});
			//consider dialog closing with ESC
			approveDialog.attachAfterClose(function () {
				approveDialog.destroy();
				reject(null);
			});
			approveDialog.open();
		} catch (error) {
			reject(null);
		}
	}
	static overrides = {

		/**
		 * Called when a controller is instantiated and its View controls (if available) are already created.
		 * Can be used to modify the View before it is displayed, to bind event handlers and do other one-time initialization.
		 * @memberOf sap.fe.cap.travel.ext.controller.ObjectPageExtension
		 */
		onInit(this: ObjectPageExtension) {
			// you can access the Fiori elements extensionAPI via this.base.getExtensionAPI
			//const model = this.base.getExtensionAPI().getModel();
		},
		editFlow: {
			onBeforeSave(this: ObjectPageExtension) {
				const context = this.base
					.getExtensionAPI()
					.getBindingContext() as Context;
				if (!context.getProperty("GoGreen")) {
					//void intentionally discards returned floating promise
					return new Promise<null>((resolve, reject) => { void this.openDialog(resolve, reject, context); });
				}
				return undefined;
			}
		}
	};
}

================================================
FILE: app/travel_processor/webapp/ext/fragment/CustomSection.fragment.xml
================================================
<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:l="sap.ui.layout" xmlns:macros="sap.fe.macros">
	<VBox core:require="{ handler: 'sap/fe/cap/travel/ext/fragment/CustomSection'}">
        <MessageStrip text="All bookings for travel {TravelID} got confirmed by the agency." showIcon="true" class="sapUiSmallMarginBottom">
        </MessageStrip>
        <l:Grid hSpacing="1" containerQuery="true" defaultSpan="L12 M12 S12">      
            <l:content>
                <macros:Table metaPath="to_Booking/@com.sap.vocabularies.UI.v1.LineItem" id="bookingTable"/>
            </l:content>
        </l:Grid>
	</VBox>
</core:FragmentDefinition>

================================================
FILE: app/travel_processor/webapp/ext/fragment/CustomSection.ts
================================================
import ExtensionAPI from 'sap/fe/core/ExtensionAPI';
import UI5Event from 'sap/ui/base/Event';
import MessageToast from 'sap/m/MessageToast';

/**
 * Generated event handler.
 *
 * @param this reference to the 'this' that the event handler is bound to.
 * @param event the event object provided by the event provider
 */
export function onPress(this: ExtensionAPI, event: UI5Event) {
    MessageToast.show("Custom handler invoked.");
}

================================================
FILE: app/travel_processor/webapp/ext/fragment/Trees4Tickets.fragment.xml
================================================
<core:FragmentDefinition
	xmlns="sap.m"
	xmlns:macros="sap.fe.macros"
	xmlns:l="sap.ui.layout"
	xmlns:f="sap.ui.layout.form"
	xmlns:core="sap.ui.core">
    <Dialog title="Opt for Trees-4-Tickets" icon="sap-icon://flight"
        id="Trees4Tickets" contentWidth="38rem">
        <content>             
                <MessageStrip
                    text="Plant some trees with an additional 10 % on your booking fee"
                    type="Success"
                    showIcon='{GoGreen}'
                    class="sapUiSmallMarginTop sapUiSmallMarginBottom sapUiSmallMarginBeginEnd">
                </MessageStrip>             
            <f:Form>
                <f:layout>
				<f:ColumnLayout
					columnsXL="2"
					columnsL="2"
					columnsM="2"/>
                </f:layout>
                <f:formContainers>
                    <f:FormContainer>
                        <f:formElements>
                            <macros:FormElement metaPath="TotalPrice" id="formElementMacroID1" />
                            <macros:FormElement metaPath="GoGreen" id="formElementMacroID2" />                                                                           
                        </f:formElements>
                    </f:FormContainer>
                    <f:FormContainer>
                        <f:formElements>                                         
                            <macros:FormElement metaPath="GreenFee" visible="{GoGreen}" id="formElementMacroID3" />      
                            <macros:FormElement metaPath="TreesPlanted" visible="{GoGreen}" id="formElementMacroID4" />                              
                        </f:formElements>
                    </f:FormContainer>                    
                </f:formContainers>
            </f:Form>      
        </content>
        <beginButton>
            <Button id="Save" text="Save" type="Emphasized"/>
        </beginButton>
        <endButton>
            <Button id="Cancel" text="Cancel"/>
        </endButton>        
    </Dialog>
</core:FragmentDefinition>


================================================
FILE: app/travel_processor/webapp/i18n/i18n.properties
================================================
# This is the resource bundle for travel_processor

#Texts for manifest.json

#XTIT: Application name
title=Manage Travels

#YDES: Application description
description=Manage Travels


================================================
FILE: app/travel_processor/webapp/i18n/i18n_de.properties
================================================
# This is the resource bundle for travel_processor

#Texts for manifest.json

#XTIT: Application name
title=Reisen verwalten

#YDES: Application description
description=Reisen verwalten


================================================
FILE: app/travel_processor/webapp/i18n/i18n_en.properties
================================================
# This is the resource bundle for travel_processor

#Texts for manifest.json

#XTIT: Application name
title=Manage Travels

#YDES: Application description
description=Manage Travels


================================================
FILE: app/travel_processor/webapp/i18n/i18n_fr.properties
================================================
# This is the resource bundle for travel_processor

#Texts for manifest.json

#XTIT: Application name
title=Gérer les voyages

#YDES: Application description
description=Gérer les voyages


================================================
FILE: app/travel_processor/webapp/index.html
================================================
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title></title>
    <style>
      html,
      body,
      body > div,
      #container,
      #container-uiarea {
        height: 100%;
      }
    </style>
    <script
      id="sap-ui-bootstrap"
      src="https://ui5.sap.com/1.139.0/resources/sap-ui-core.js"
      data-sap-ui-theme="sap_horizon"
      data-sap-ui-oninit="module:sap/ui/core/ComponentSupport"
      data-sap-ui-resourceroots='{ "sap.fe.cap.travel": "." }'
      data-sap-ui-compatVersion="edge"
      data-sap-ui-async="true"
      data-sap-ui-preload="async"
      data-sap-ui-flexibilityServices='[{"connector": "SessionStorageConnector"}]'
      data-sap-ui-xx-componentPreload="off"
      data-sap-ui-libs="sap.m, sap.fe.core"
    ></script>
  </head>
  <body class="sapUiBody sapUiSizeCompact" id="content">
    <div
      data-sap-ui-component
      data-name="sap.fe.cap.travel"
      data-id="container"
      data-settings='{"id" : "sap.fe.cap.travel"}'
      data-handle-validation="true"
    ></div>
  </body>
</html>


================================================
FILE: app/travel_processor/webapp/manifest.json
================================================
{
    "_version": "1.32.0",
    "sap.app": {
        "id": "sap.fe.cap.travel",
        "type": "application",
        "title": "{{title}}",
        "description": "{{description}}",
        "i18n": {
            "bundleName": "sap.fe.cap.travel.i18n.i18n",
            "supportedLocales": [
                "en",
                "de",
                "fr"
            ],
            "fallbackLocale": "en"
        },
        "applicationVersion": {
            "version": "1.0.0"
        },
        "dataSources": {
            "mainService": {
                "uri": "processor/",
                "type": "OData",
                "settings": {
                    "odataVersion": "4.0"
                }
            }
        },
        "offline": false,
        "resources": "resources.json",
        "sourceTemplate": {
            "id": "ui5template.fiorielements.v4.lrop",
            "version": "1.0.0"
        },
        "crossNavigation": {
            "inbounds": {
                "travel-inbound": {
                    "signature": {
                        "parameters": {},
                        "additionalParameters": "allowed"
                    },
                    "semanticObject": "travel",
                    "action": "process",
                    "title": "Process Travels",
                    "subTitle": "Process travels",
                    "icon": "sap-icon://flight"
                }
            }
        }
    },
    "sap.ui": {
        "technology": "UI5",
        "icons": {
            "icon": "sap-icon://flight",
            "favIcon": "sap-icon://flight",
            "phone": "sap-icon://flight",
            "phone@2": "sap-icon://flight",
            "tablet": "sap-icon://flight",
            "tablet@2": "sap-icon://flight"
        },
        "deviceTypes": {
            "desktop": true,
            "tablet": true,
            "phone": true
        }
    },
    "sap.ui5": {
        "flexEnabled": false,
        "resources": {
            "js": [],
            "css": []
        },
        "dependencies": {
            "minUI5Version": "1.139.0",
            "libs": {
                "sap.ui.core": {},
                "sap.fe.templates": {}
            }
        },
        "models": {
            "i18n": {
                "type": "sap.ui.model.resource.ResourceModel",
                "settings": {
                    "bundleName": "sap.fe.cap.travel.i18n.i18n",
                    "supportedLocales": [
                        "en",
                        "de",
                        "fr"
                    ],
                    "fallbackLocale": "en"
                }
            },
            "": {
                "dataSource": "mainService",
                "preload": true,
                "settings": {
                    "synchronizationMode": "None",
                    "operationMode": "Server",
                    "autoExpandSelect": true,
                    "earlyRequests": true
                }
            },
            "@i18n": {
                "type": "sap.ui.model.resource.ResourceModel",
                "uri": "i18n/i18n.properties"
            }
        },
        "routing": {
            "routes": [
                {
                    "pattern": ":?query:",
                    "name": "TravelList",
                    "target": "TravelList"
                },
                {
                    "pattern": "Travel({key}):?query:",
                    "name": "TravelObjectPage",
                    "target": "TravelObjectPage"
                },
                {
                    "pattern": "Travel({key})/to_Booking({key2}):?query:",
                    "name": "BookingObjectPage",
                    "target": "BookingObjectPage"
                }
            ],
            "targets": {
                "TravelList": {
                    "type": "Component",
                    "id": "TravelList",
                    "name": "sap.fe.templates.ListReport",
                    "options": {
                        "settings": {
                            "entitySet": "Travel",
                            "controlConfiguration": {
                                "@com.sap.vocabularies.UI.v1.SelectionFields": {
                                    "useSemanticDateRange": true
                                }
                            },
                            "variantManagement": "Page",
                            "initialLoad": "Enabled",
                            "navigation": {
                                "Travel": {
                                    "detail": {
                                        "route": "TravelObjectPage"
                                    }
                                }
                            }
                        }
                    }
                },
                "TravelObjectPage": {
                    "type": "Component",
                    "id": "TravelObjectPage",
                    "name": "sap.fe.templates.ObjectPage",
                    "options": {
                        "settings": {
                            "entitySet": "Travel",
                            "navigation": {
                                "to_Booking": {
                                    "detail": {
                                        "route": "BookingObjectPage"
                                    }
                                }
                            },
                            "controlConfiguration": {
                                "to_Booking/@com.sap.vocabularies.UI.v1.LineItem": {
                                    "tableSettings": {
                                        "type": "ResponsiveTable",
                                        "personalization": {
                                            "column": true,
                                            "sort": false
                                        },
                                        "creationMode": {
                                            "name": "Inline",
                                            "createAtEnd": true
                                        }
                                    }
                                }
                            },
                            "editableHeaderContent": false,
                            "content": {
                                "body": {
                                    "sections": {
                                        "CustomSection": {
                                            "template": "sap.fe.cap.travel.ext.fragment.CustomSection",
                                            "position": {
                                                "placement": "After",
                                                "anchor": "Travel"
                                            },
                                            "title": "My Itinerary"
                                        }
                                    }
                                }
                            }
                        }
                    }
                },
                "BookingObjectPage": {
                    "type": "Component",
                    "id": "BookingObjectPage",
                    "name": "sap.fe.templates.ObjectPage",
                    "options": {
                        "settings": {
                            "entitySet": "Booking",
                            "editableHeaderContent": false
                        }
                    }
                }
            },
            "config": {}
        },
        "contentDensities": {
            "compact": true,
            "cozy": true
        },
        "extends": {
            "extensions": {
                "sap.ui.controllerExtensions": {
                    "sap.fe.templates.ObjectPage.ObjectPageController": {
                        "controllerName": "sap.fe.cap.travel.ext.controller.ObjectPageExtension"
                    }
                }
            }
        }
    },
    "sap.platform.abap": {
        "_version": "1.1.0",
        "uri": ""
    },
    "sap.platform.hcp": {
        "_version": "1.1.0",
        "uri": ""
    },
    "sap.fiori": {
        "_version": "1.1.0",
        "registrationIds": [],
        "archeType": "transactional"
    },
    "sap.cloud": {
        "public": true,
        "service": "sap.fe.cap.sflight"
    }
}


================================================
FILE: app/travel_processor/webapp/test/integration/Opa.qunit.html
================================================
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Integration tests</title>
    <meta charset="UTF-8" content="IE=edge" http-equiv="X-UA-Compatible" />
    <meta content="text/html;charset=UTF-8" http-equiv="Content-Type" />
    <meta content="width=device-width, initial-scale=1.0" name="viewport" />

    <script
      id="sap-ui-bootstrap"
      src="https://ui5.sap.com/1.139.0/resources/sap-ui-core.js"
      data-sap-ui-animation="false"
      data-sap-ui-compatVersion="edge"
      data-sap-ui-resourceroots='{
        "sap.fe.cap.travel": "../../"
      }'
      data-sap-ui-theme="sap_horizon"
      data-sap-ui-libs="sap.m, sap.fe.core"
    ></script>

    <link
      rel="stylesheet"
      type="text/css"
      href="https://ui5.sap.com/1.139.0/resources/sap/ui/thirdparty/qunit-2.css"
    />

    <script src="https://ui5.sap.com/1.139.0/resources/sap/ui/thirdparty/qunit-2.js"></script>
    <script src="https://ui5.sap.com/1.139.0/resources/sap/ui/qunit/qunit-junit.js"></script>

    <script src="Opa.qunit.js"></script>
  </head>
  <body>
    <div id="qunit"></div>
    <div id="qunit-fixture"></div>
  </body>
</html>


================================================
FILE: app/travel_processor/webapp/test/integration/Opa.qunit.js
================================================
sap.ui.require(
  [
    "sap/fe/test/JourneyRunner",
    "sap/fe/cap/travel/test/integration/pages/MainListReport",
    "sap/fe/cap/travel/test/integration/pages/MainObjectPage",
    "sap/fe/cap/travel/test/integration/pages/ItemObjectPage",
    "sap/fe/cap/travel/test/integration/OpaJourney",
  ],
  function (
    JourneyRunner,
    MainListReport,
    MainObjectPage,
    ItemObjectPage,
    Journey
  ) {
    "use strict";

    const runner = new JourneyRunner({
      // start index.html in web folder
      launchUrl: sap.ui.require.toUrl("sap/fe/cap/travel") + "/index.html",
      opaConfig: { timeout: 30 },
    });

    runner.run(
      {
        pages: {
          onTheMainPage: MainListReport,
          onTheDetailPage: MainObjectPage,
          onTheDetailItemPage: ItemObjectPage,
        },
      },
      Journey.run
    );
  }
);


================================================
FILE: app/travel_processor/webapp/test/integration/OpaJourney.js
================================================
/* global QUnit */
sap.ui.define(["sap/ui/test/opaQunit"], function (opaTest) {
  "use strict";

  const Journey = {
    start: function () {
      QUnit.module("Travel Processor Tests");
      opaTest("#000: Start", function (Given) {
        Given.iResetTestData().and.iStartMyApp("", { "sap-language": "EN" });
      });
      return Journey;
    },

    test: function () {
      opaTest(
        "#1: ListReport: Check List Report Page loads",
        function (Given, When, Then) {
          Then.onTheMainPage.iSeeThisPage();
        }
      );

      opaTest(
        "#2: Object Page: Check Object Page loads",
        function (Given, When, Then) {
          When.onTheMainPage.onTable().iPressRow({ Travel: "4,133" });
          Then.onTheDetailPage.iSeeThisPage();

          When.iNavigateBack();
          Then.onTheMainPage.iSeeThisPage();
        }
      );

      opaTest("#3: List report: Create travel", function (Given, When, Then) {
        Then.onTheMainPage.iSeeThisPage();
        Then.onTheMainPage.onTable().iCheckAction("Create", { enabled: true });

        // Click on Create button
        When.onTheMainPage.onTable().iExecuteAction("Create");
        Then.onTheDetailPage.iSeeObjectPageInEditMode();
        When.onTheDetailPage.iGoToSection("General Information");

        // Value help Agency ID
        When.onTheDetailPage
          .onForm({ section: "Travel", fieldGroup: "TravelData" })
          .iOpenValueHelp({ property: "to_Agency_AgencyID" });
        When.onTheDetailPage.onValueHelpDialog().iSelectRows({ 0: "070006" });

        // Value help Customer ID
        When.onTheDetailPage
          .onForm({ section: "Travel", fieldGroup: "TravelData" })
          .iOpenValueHelp({ property: "to_Customer_CustomerID" });
        When.onTheDetailPage.onValueHelpDialog().iSelectRows({ 0: "000001" });

        // Starting date
        const dateFormat = new Intl.DateTimeFormat("en", {
          month: "short",
          day: "numeric",
          year: "numeric",
        });
        const startDate = new Date();
        startDate.setUTCDate(startDate.getUTCDate() + 1);
        When.onTheDetailPage
          .onForm({ section: "Travel", fieldGroup: "DateData" })
          .iChangeField(
            { property: "BeginDate" },
            dateFormat.format(startDate)
          );

        // End date
        const endDate = new Date(startDate);
        endDate.setUTCDate(endDate.getUTCDate() + 7);
        When.onTheDetailPage
          .onForm({ section: "Travel", fieldGroup: "DateData" })
          .iChangeField({ property: "EndDate" }, dateFormat.format(endDate));

        // Booking fee
        When.onTheDetailPage
          .onForm({ section: "Travel", fieldGroup: "PriceData" })
          .iChangeField({ property: "BookingFee" }, "50.00");

        // Currency
        When.onTheDetailPage
          .onForm({ section: "Travel", fieldGroup: "PriceData" })
          .iChangeField({ property: "CurrencyCode_code" }, "EUR");

        // Description
        When.onTheDetailPage
          .onForm({ section: "Travel", fieldGroup: "TravelData" })
          .iChangeField({ property: "Description" }, "Travel for deletion");

        // Save all
        Then.onTheDetailPage.onFooter().iCheckDraftStateSaved();
        When.onTheDetailPage.onFooter().iExecuteSave();
        When.onTheDetailPage.onActionDialog().iConfirm();
        Then.onTheDetailPage.iSeeThisPage().and.iSeeObjectPageInDisplayMode();
        When.iNavigateBack();
      });

      opaTest("#4: List report: Delete travel", function (Given, When, Then) {
        Then.onTheMainPage.iSeeThisPage();

        Then.onTheMainPage
          .onTable()
          .iCheckDelete({ visible: true, enabled: false });

        // select row to be deleted
        Given.onTheMainPage
          .onTable()
          .iSelectRows({ Travel: "Travel for deletion" });

        Then.onTheMainPage
          .onTable()
          .iCheckDelete({ visible: true, enabled: true });
        When.onTheMainPage.onTable().iExecuteDelete();
        When.onTheMainPage.onDialog().iConfirm();
        Then.onTheMainPage
          .onTable()
          .iCheckDelete({ visible: true, enabled: false });
      });

      opaTest(
        "#5: List report: Check actions (Accept, Reject)",
        function (Given, When, Then) {
          Then.onTheMainPage.iSeeThisPage();

          // Check that bound action is inactive without selection
          Then.onTheMainPage
            .onTable()
            .iCheckAction(
              { service: "TravelService", action: "acceptTravel" },
              { visible: true, enabled: false }
            );

          // select first row
          Given.onTheMainPage.onTable().iSelectRows({ Travel: "4,132" });

          // Check that bound action is now active after selection
          Then.onTheMainPage
            .onTable()
            .iCheckAction(
              { service: "TravelService", action: "acceptTravel" },
              { visible: true, enabled: true }
            );

          // check that "Travel status" is Open
          Then.onTheMainPage
            .onTable()
            .iCheckRows({ Travel: "4,132", "Travel Status": "Open" }, 1);

          // trigger action
          When.onTheMainPage.onTable().iExecuteAction({
            service: "TravelService",
            action: "acceptTravel",
          });

          // check that "Travel status" is now Accepted
          Then.onTheMainPage
            .onTable()
            .iCheckRows({ Travel: "4,132", "Travel Status": "Accepted" }, 1);

          // unselect first row
          Given.onTheMainPage.onTable().iSelectRows({ Travel: "4,132" });

          // select 2nd row
          Given.onTheMainPage.onTable().iSelectRows({ Travel: "4,131" });

          // check that "Travel status" is Open
          Then.onTheMainPage
            .onTable()
            .iCheckRows({ Travel: "4,131", "Travel Status": "Open" }, 1);

          // trigger action
          When.onTheMainPage.onTable().iExecuteAction({
            service: "TravelService",
            action: "rejectTravel",
          });

          // check that "Travel status" is Open
          Then.onTheMainPage
            .onTable()
            .iCheckRows({ Travel: "4,131", "Travel Status": "Canceled" }, 1);

          // unselect 2nd row
          Given.onTheMainPage.onTable().iSelectRows({ Travel: "4,131" });

          Then.onTheMainPage.iSeeThisPage();
        }
      );

      opaTest(
        "#6: Object Page: Check details for accepted travel",
        function (Given, When, Then) {
          // Open travelk with travel status = "Accepted"
          When.onTheMainPage.onTable().iPressRow({ Travel: "4,133" });
          Then.onTheDetailPage.iSeeThisPage();

          When.onTheDetailPage.onHeader().iExecuteAction("Edit");
          Then.onTheDetailPage.iSeeObjectPageInEditMode();

          When.onTheDetailPage.iGoToSection("My Itinerary");
          // Check buttons for bookings
          Then.onTheDetailPage
            .onTable({
              id: "sap.fe.cap.travel::TravelObjectPage--fe::CustomSubSection::CustomSection--bookingTable-content",
            })
            .iCheckDelete({ visible: false, enabled: false })
            .and.iCheckCreate({ visible: false, enabled: false });

          // Check fields
          When.onTheDetailPage.iGoToSection("Travel");
          // Starting date
          Then.onTheDetailPage
            .onForm({ section: "Travel", fieldGroup: "DateData" })
            .iCheckField(
              { property: "BeginDate" },
              { value: "Dec 11, 2025" },
              { editable: false }
            );

          // Booking fee
          Then.onTheDetailPage
            .onForm({ section: "Travel", fieldGroup: "PriceData" })
            .iCheckField({ property: "BookingFee" }, { editable: false });

          When.onTheDetailPage.iGoToSection("General Information");
          When.onTheDetailPage
            .onTable({
              id: "sap.fe.cap.travel::TravelObjectPage--fe::CustomSubSection::CustomSection--bookingTable-content",
            })
            .iPressRow({ BookingID: "1" });

          Then.onTheDetailItemPage.iSeeThisPage();
          // Check fields
          When.onTheDetailItemPage.iGoToSection("General Information");

          // Flight Price
          Then.onTheDetailItemPage
            .onForm({ section: "Booking", fieldGroup: "FlightData" })
            .iCheckField({ property: "FlightPrice" }, { editable: false });

          // Flight Number
          Then.onTheDetailItemPage
            .onForm({ section: "Booking", fieldGroup: "FlightData" })
            .iCheckField(
              { property: "ConnectionID" },
              { value: "0018" },
              { editable: false }
            );

          // Check buttons for booking supplements
          When.onTheDetailItemPage.iGoToSection("Booking Supplement");
          When.onTheDetailItemPage
            .onTable({ property: "to_BookSupplement" })
            .iSelectRows({ BookingSupplementID: "1" });
          Then.onTheDetailItemPage
            .onTable({ property: "to_BookSupplement" })
            .iCheckDelete({ visible: true, enabled: false })
            .and.iCheckCreate({ visible: true, enabled: false });

          When.iNavigateBack();
          Then.onTheDetailPage.iSeeThisPage();

          When.onTheDetailPage.onFooter().iExecuteCancel();
          Then.onTheDetailPage.iSeeObjectPageInDisplayMode();

          When.iNavigateBack();
          Then.onTheMainPage.iSeeThisPage();
        }
      );

      opaTest(
        "#7: Object Page: Check details for open travel",
        function (Given, When, Then) {
          // Open travel with travel status = "open"
          When.onTheMainPage.onTable().iPressRow({ Travel: "4,129" });
          Then.onTheDetailPage.iSeeThisPage();

          When.onTheDetailPage.onHeader().iExecuteAction("Edit");
          Then.onTheDetailPage.iSeeObjectPageInEditMode();

          When.onTheDetailPage.iGoToSection("My Itinerary");
          // Check buttons
          When.onTheDetailPage
            .onTable({
              id: "sap.fe.cap.travel::TravelObjectPage--fe::CustomSubSection::CustomSection--bookingTable-content",
            })
            .iSelectRows({ BookingID: "2" });
          Then.onTheDetailPage
            .onTable({
              id: "sap.fe.cap.travel::TravelObjectPage--fe::CustomSubSection::CustomSection--bookingTable-content",
            })
            .iCheckDelete({ visible: true, enabled: true })
            .and.iCheckCreate({ visible: true, enabled: true });

          // Check fields
          When.onTheDetailPage.iGoToSection("Travel");
          // Starting date
          Then.onTheDetailPage
            .onForm({ section: "Travel", fieldGroup: "DateData" })
            .iCheckField(
              { property: "BeginDate" },
              { value: "Dec 11, 2025" },
              { editable: true }
            );

          // Booking fee
          Then.onTheDetailPage
            .onForm({ section: "Travel", fieldGroup: "PriceData" })
            .iCheckField({ property: "BookingFee" }, { editable: true });

          When.onTheDetailPage.iGoToSection("General Information");
          When.onTheDetailPage
            .onTable({
              id: "sap.fe.cap.travel::TravelObjectPage--fe::CustomSubSection::CustomSection--bookingTable-content",
            })
            .iPressRow({ BookingID: "2" });

          Then.onTheDetailItemPage.iSeeThisPage();
          // Check fields
          When.onTheDetailItemPage.iGoToSection("General Information");
          // Flight Price
          Then.onTheDetailItemPage
            .onForm({ section: "Booking", fieldGroup: "FlightData" })
            .iCheckField({ property: "FlightPrice" }, { editable: true });

          // Flight Number
          Then.onTheDetailItemPage
            .onForm({ section: "Booking", fieldGroup: "FlightData" })
            .iCheckField(
              { property: "ConnectionID" },
              { value: "0018" },
              { editable: true }
            );

          // Check buttons for booking supplements
          When.onTheDetailItemPage.iGoToSection("Booking Supplements");
          When.onTheDetailItemPage
            .onTable({ property: "to_BookSupplement" })
            .iSelectRows({ BookingSupplementID: "1" });
          Then.onTheDetailItemPage
            .onTable({ property: "to_BookSupplement" })
            .iCheckDelete({ visible: true, enabled: true })
            .and.iCheckCreate({ visible: true, enabled: true });

          When.iNavigateBack();
          Then.onTheDetailPage.iSeeThisPage();

          When.onTheDetailPage.onFooter().iExecuteCancel();
          Then.onTheDetailPage.iSeeObjectPageInDisplayMode();

          When.iNavigateBack();
          Then.onTheMainPage.iSeeThisPage();
        }
      );

      return Journey;
    },
    end: function () {
      opaTest("#999: Tear down", function (Given) {
        Given.iTearDownMyApp();
      });
      return Journey;
    },
  };
  Journey.run = function () {
    Journey.start().test().end();
  };

  return Journey;
});


================================================
FILE: app/travel_processor/webapp/test/integration/pages/ItemObjectPage.js
================================================
sap.ui.define(["sap/fe/test/ObjectPage"], function (ObjectPage) {
  "use strict";

  // OPTIONAL
  const AdditionalCustomObjectPageDefinition = {
    actions: {},
    assertions: {},
  };

  return new ObjectPage(
    {
      appId: "sap.fe.cap.travel",
      componentId: "BookingObjectPage",
      entitySet: "Booking",
    },
    AdditionalCustomObjectPageDefinition
  );
});


================================================
FILE: app/travel_processor/webapp/test/integration/pages/MainListReport.js
================================================
sap.ui.define(["sap/fe/test/ListReport"], function (ListReport) {
  "use strict";

  // OPTIONAL
  const AdditionalCustomListReportDefinition = {
    actions: {},
    assertions: {},
  };

  return new ListReport(
    {
      appId: "sap.fe.cap.travel",
      componentId: "TravelList",
      entitySet: "Travel",
    },
    AdditionalCustomListReportDefinition
  );
});


================================================
FILE: app/travel_processor/webapp/test/integration/pages/MainObjectPage.js
================================================
sap.ui.define(["sap/fe/test/ObjectPage"], function (ObjectPage) {
  "use strict";

  // OPTIONAL
  const AdditionalCustomObjectPageDefinition = {
    actions: {},
    assertions: {},
  };

  return new ObjectPage(
    {
      appId: "sap.fe.cap.travel",
      componentId: "TravelObjectPage",
      entitySet: "Travel",
    },
    AdditionalCustomObjectPageDefinition
  );
});


================================================
FILE: app/travel_processor/webapp/test/testsuite.qunit.html
================================================
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Testsuite</title>
    <script src="/resources/sap/ui/qunit/qunit-redirect.js"></script>
    <script src="./testsuite.qunit.js" data-sap-ui-testsuite></script>
  </head>
  <body></body>
</html>


================================================
FILE: app/travel_processor/webapp/test/testsuite.qunit.js
================================================
/* global window, parent, location */
window.suite = function () {
  "use strict";

  const oSuite = new parent.jsUnitTestSuite();
  const sContextPath = location.pathname.substring(
    0,
    location.pathname.lastIndexOf("/")
  );

  oSuite.addTestPage(`${sContextPath}/integration/Opa.qunit.html`);

  return oSuite;
};


================================================
FILE: app/travel_processor/xs-app.json
================================================
{
  "welcomeFile": "/index.html",
  "authenticationMethod": "route",
  "routes": [
    {
      "source": "^/processor/(.*)$",
      "target": "/processor/$1",
      "destination": "sflight-srv",
      "authenticationType": "xsuaa",
      "csrfProtection": false
    },
    {
      "source": "^(.*)$",
      "target": "$1",
      "service": "html5-apps-repo-rt",
      "authenticationType": "xsuaa"
    }
  ]
}


================================================
FILE: app/travel_processor/xs-security.json
================================================
{
  "scopes": [
    {
      "name": "$XSAPPNAME.reviewer",
      "description": "reviewer"
    },
    {
      "name": "$XSAPPNAME.processor",
      "description": "processor"
    },
    {
      "name": "$XSAPPNAME.admin",
      "description": "admin"
    }
  ],
  "attributes": [],
  "role-templates": [
    {
      "name": "reviewer",
      "description": "generated",
      "scope-references": [
        "$XSAPPNAME.reviewer"
      ],
      "attribute-references": []
    },
    {
      "name": "processor",
      "description": "generated",
      "scope-references": [
        "$XSAPPNAME.processor"
      ],
      "attribute-references": []
    },
    {
      "name": "admin",
      "description": "generated",
      "scope-references": [
        "$XSAPPNAME.admin"
      ],
      "attribute-references": []
    }
  ]
}


================================================
FILE: app/value-helps.cds
================================================
using { sap.fe.cap.travel as schema } from '../db/schema';

//
// annotations for value helps
//

annotate schema.Travel {

  TravelStatus @Common.ValueListWithFixedValues;

  to_Agency @Common.ValueList: {
    CollectionPath : 'TravelAgency',
    Label : '',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: to_Agency_AgencyID, ValueListProperty: 'AgencyID'},  // local data property is the foreign key
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Name'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Street'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PostalCode'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'City'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CountryCode_code'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PhoneNumber'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'EMailAddress'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'WebAddress'}
    ]
  };

  to_Customer @Common.ValueList: {
    CollectionPath : 'Passenger',
    Label : 'Customer ID',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: to_Customer_CustomerID, ValueListProperty: 'CustomerID'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'FirstName'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'LastName'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Title'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Street'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PostalCode'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'City'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CountryCode_code'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PhoneNumber'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'EMailAddress'}
    ]
  };

  CurrencyCode @Common.ValueList: {
    CollectionPath : 'Currencies',
    Label : '',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: CurrencyCode_code, ValueListProperty: 'code'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'name'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'descr'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'symbol'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'minor'}
    ]
  };

}


annotate schema.Booking {

  BookingStatus @Common.ValueListWithFixedValues;

  to_Customer @Common.ValueList: {
    CollectionPath : 'Passenger',
    Label : '',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: to_Customer_CustomerID, ValueListProperty: 'CustomerID'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'FirstName'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'LastName'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Title'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Street'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PostalCode'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'City'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CountryCode_code'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PhoneNumber'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'EMailAddress'}
    ]
  };

  to_Carrier @Common.ValueList: {
    CollectionPath : 'Airline',
    Label : '',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: to_Carrier_AirlineID, ValueListProperty: 'AirlineID'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Name'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CurrencyCode_code'}
    ]
  };

  ConnectionID @Common.ValueList: {
    CollectionPath : 'Flight',
    Label : '',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: to_Carrier_AirlineID,    ValueListProperty: 'AirlineID'},
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: ConnectionID, ValueListProperty: 'ConnectionID'},
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: FlightDate,   ValueListProperty: 'FlightDate'},
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: FlightPrice,  ValueListProperty: 'Price'},
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: CurrencyCode_code, ValueListProperty: 'CurrencyCode_code'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'to_Airline/Name'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PlaneType'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'MaximumSeats'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'OccupiedSeats'}
    ],
    PresentationVariantQualifier: 'SortOrderPV'  // use presentation variant to sort by FlightDate desc
  };

  FlightDate @Common.ValueList: {
    CollectionPath : 'Flight',
    Label : '',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: to_Carrier_AirlineID,    ValueListProperty: 'AirlineID'},
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: ConnectionID, ValueListProperty: 'ConnectionID'},
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: FlightDate,   ValueListProperty: 'FlightDate'},
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: FlightPrice,  ValueListProperty: 'Price'},
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: CurrencyCode_code, ValueListProperty: 'CurrencyCode_code'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'to_Airline/Name'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'PlaneType'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'MaximumSeats'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'OccupiedSeats'}
    ]
  };

  CurrencyCode @Common.ValueList: {
    CollectionPath : 'Currencies',
    Label : '',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: CurrencyCode_code, ValueListProperty: 'code'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'name'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'descr'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'symbol'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'minor'}
    ]
  };

}


annotate schema.BookingSupplement {

  to_Supplement @Common.ValueList: {
    CollectionPath : 'Supplement',
    Label : '',
    Parameters : [
    {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: to_Supplement_SupplementID, ValueListProperty: 'SupplementID'},
    {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: Price,        ValueListProperty: 'Price'},
    {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: CurrencyCode_code, ValueListProperty: 'CurrencyCode_code'},
    {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Description'}
    ]
  };

  CurrencyCode @Common.ValueList: {
    CollectionPath : 'Currencies',
    Label : '',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: CurrencyCode_code, ValueListProperty: 'code'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'name'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'descr'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'symbol'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'minor'}
    ]
  };
}


annotate schema.Flight with @UI.PresentationVariant#SortOrderPV : {    // used in ValueList for Bookings:ConnectionId above
    SortOrder      : [{
      Property   : FlightDate,
      Descending : true
    }]
  }
{
  AirlineID @Common.ValueList: {
    CollectionPath : 'Airline',
    Label : '',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: AirlineID, ValueListProperty: 'AirlineID'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Name'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CurrencyCode'}
    ]
  };

  ConnectionID @Common.ValueList: {
    CollectionPath : 'FlightConnection',
    Label : '',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: AirlineID, ValueListProperty: 'AirlineID'},
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: ConnectionID, ValueListProperty: 'ConnectionID'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'AirlineID_Text'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'DepartureAirport'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'DestinationAirport'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'DepartureTime'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'ArrivalTime'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Distance'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'DistanceUnit'}
    ]
  };
};



annotate schema.FlightConnection {

  AirlineID @Common.ValueList: {
    CollectionPath : 'Airline',
    Label : '',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: AirlineID, ValueListProperty: 'CarrierID'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'AirlineID'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Name'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CurrencyCode'}
    ]
  };

  DepartureAirport @Common.ValueList: {
    CollectionPath : 'Airport',
    Label : '',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut', LocalDataProperty: DepartureAirport_AirportID, ValueListProperty: 'Airport_ID'},  // here FK is required
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'AirportID'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Name'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'City'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CountryCode'}
    ]
  };

  DestinationAirport @Common.ValueList: {
    CollectionPath : 'Airport',
    Label : '',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut',       LocalDataProperty: DestinationAirport_AirportID, ValueListProperty: 'Airport_ID'},  // here FK is required
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'AirportID'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'Name'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'City'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty: 'CountryCode'}
    ]
  };

}


annotate schema.Passenger {

  CountryCode @Common.ValueList : {
    CollectionPath  : 'Countries',
    Label : '',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut',       LocalDataProperty : CountryCode_code, ValueListProperty : 'code'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty : 'name'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty : 'descr'}
    ]
  };

}


annotate schema.TravelAgency {

  CountryCode @Common.ValueList: {
    CollectionPath : 'Countries',
    Label : '',
    Parameters : [
      {$Type: 'Common.ValueListParameterInOut',       LocalDataProperty: CountryCode_code, ValueListProperty: 'code'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty : 'name'},
      {$Type: 'Common.ValueListParameterDisplayOnly', ValueListProperty : 'descr'}
    ]
  };

}


================================================
FILE: db/common.cds
================================================
using { sap, managed } from '@sap/cds/common';

extend sap.common.Currencies with {
  // Currencies.code = ISO 4217 alphabetic three-letter code
  // with the first two letters being equal to ISO 3166 alphabetic country codes
  // See also:
  // [1] https://www.iso.org/iso-4217-currency-codes.html
  // [2] https://www.currency-iso.org/en/home/tables/table-a1.html
  // [3] https://www.ibm.com/support/knowledgecenter/en/SSZLC2_7.0.0/com.ibm.commerce.payments.developer.doc/refs/rpylerl2mst97.htm
  numcode  : Integer;
  exponent : Integer; //> e.g. 2 --> 1 Dollar = 10^2 Cent
  minor    : String; //> e.g. 'Cent'
}


aspect custom.managed {
  createdAt     : managed:createdAt;
  createdBy     : managed:createdBy;
  LastChangedAt : managed:modifiedAt;
  LastChangedBy : managed:modifiedBy;
}


================================================
FILE: db/data/sap.common-Countries.csv
================================================
code;name;descr
AU;Australia;Commonwealth of Australia
CA;Canada;Canada
CN;China;People's Republic of China (PRC)
FR;France;French Republic
DE;Germany;Federal Republic of Germany
IN;India;Republic of India
IL;Israel;State of Israel
MM;Myanmar;Republic of the Union of Myanmar
GB;United Kingdom;United Kingdom of Great Britain and Northern Ireland
US;United States;United States of America (USA)
EU;European Union;European Union
IT;Italy;Italy
SG;Singapore;Singapore
AT;Austria;Austria
SE;Sweden;Sweden
CH;Switzerland;Switzerland
RU;Russia;Russia
JP;Japan;Japan
ZA;South Africa;South Africa
ES;Spain;Spain
BE;Belgium;Belgium
SI;Slovenia;Slovenia
NL;Netherlands;Netherlands
MX;Mexico;Mexico
BR;Brazil;Brazil
CU;Cuba;Cuba
ZW;Zimbabwe;Zimbabwe
MY;Malaysia;Malaysia
TH;Thailand;Thailand

================================================
FILE: db/data/sap.common-Countries.texts.csv
================================================
code;locale;name;descr
AU;de;Australien;Commonwealth Australien
CA;de;Kanada;Canada
CN;de;China;Volksrepublik China
FR;de;Frankreich;Republik Frankreich
DE;de;Deutschland;Bundesrepublik Deutschland
IN;de;Indien;Republik Indien
IL;de;Israel;Staat Israel
MM;de;Myanmar;Republik der Union Myanmar
GB;de;Vereinigtes Königreich;Vereinigtes Königreich Großbritannien und Nordirland
US;de;Vereinigte Staaten;Vereinigte Staaten von Amerika
EU;de;Europäische Union;Europäische Union
IT;de;Italien;Italien
SG;de;Singapur;Singapur
AT;de;Österreich;Österreich
SE;de;Schweden;Schweden
CH;de;Schweiz;Schweiz
RU;de;Russland;Russland
JP;de;Japan;Japan
ZA;de;Südafrika;Südafrika
ES;de;Spanien;Spanien
BE;de;Belgien;Belgien
SI;de;Slowenien;Slowenien
NL;de;Niederlande;Niederlande
MX;de;Mexiko;Mexiko
BR;de;Brasilien;Brasilien
CU;de;Kuba;Kuba
ZW;de;Simbabwe;Simbabwe
MY;de;Malaysia;Malaysia
TH;de;Thailand;Thailand
AU;fr;Australie;Commonwealth d'Australie
CA;fr;Canada;Canada
CN;fr;Chine;République populaire de Chine (RPC)
FR;fr;France;République française
DE;fr;Allemagne;République fédérale d'Allemagne
IN;fr;Inde;République de l'Inde
IL;fr;Israël;État d'Israël
MM;fr;Birmanie;République de l'Union du Myanmar
GB;fr;Royaume-Uni;Royaume-Uni et Irlande du Nord
US;fr;États-Unis;États-Unis d'Amérique (États-Unis)
EU;fr;Union Européenne;Union Européenne
IT;fr;Italie;Italie
SG;fr;Singapour;Singapour
AT;fr;Autriche;Autriche
SE;fr;Suède;Suède
CH;fr;Suisse;Suisse
RU;fr;Russie;Russie
JP;fr;Japon;Japon
ZA;fr;Afrique du Sud;Afrique du Sud
ES;fr;Espagne;Espagne
BE;fr;Belgique;Belgique
SI;fr;Slovénie;Slovénie
NL;fr;Pays-Bas;Pays-Bas
MX;fr;Mexique;Mexique
BR;fr;Brésil;Brésil
CU;fr;Cuba;Cuba
ZW;fr;Zimbabwe;Zimbabwe
MY;fr;Malaisie;Malaisie
TH;fr;Thaïlande;Thaïlande
AU;en;Australie;Commonwealth d'Australie
CA;en;Canada;Canada
CN;en;China;People's Republic of China (PRC)
FR;en;France;French Republic
DE;en;Germany;Federal Republic of Germany
IN;en;India;Republic of India
IL;en;Israel;State of Israel
MM;en;Myanmar;Republic of the Union of Myanmar
GB;en;United Kingdom;United Kingdom of Great Britain and Northern Ireland
US;en;United States;United States of America (USA)
EU;en;European Union;European Union
IT;en;Italy;Italy
SG;en;Singapore;Singapore
AT;en;Austria;Austria
SE;en;Sweden;Sweden
CH;en;Switzerland;Switzerland
RU;en;Russia;Russia
JP;en;Japan;Japan
ZA;en;South Africa;South Africa
ES;en;Spain;Spain
BE;en;Belgium;Belgium
SI;en;Slovenia;Slovenia
NL;en;Netherlands;Netherlands
MX;en;Mexico;Mexico
BR;en;Brazil;Brazil
CU;en;Cuba;Cuba
ZW;en;Zimbabwe;Zimbabwe
MY;en;Malaysia;Malaysia
TH;en;Thailand;Thailand

================================================
FILE: db/data/sap.common-Currencies.csv
================================================
code;symbol;name;descr;numcode;minor;exponent
EUR;€;Euro;European Euro;978;Cent;2
USD;$;US Dollar;United States Dollar;840;Cent;2
CAD;$;Canadian Dollar;Canadian Dollar;124;Cent;2
AUD;$;Australian Dollar;Australian Dollar;036;Cent;2
GBP;£;British Pound;Great Britain Pound;826;Penny;2
ILS;₪;Shekel;Israeli New Shekel;376;Agorat;2
INR;₹;Rupee;Indian Rupee;356;Paise;2
QAR;﷼;Riyal;Katar Riyal;356;Dirham;2
SAR;﷼;Riyal;Saudi Riyal;682;Halala;2
JPY;¥;Yen;Japanese Yen;392;Sen;2
CNY;¥;Yuan;Chinese Yuan Renminbi;156;Jiao;1
SGD;S$;Singapore Dollar;Singapore Dollar;702;Cent;2
ZAR;R;Rand;South African Rand;710;Cent;2

================================================
FILE: db/data/sap.common-Currencies.texts.csv
================================================
code;locale;name;descr
EUR;de;Euro;European Euro
USD;de;US-Dollar;United States Dollar
CAD;de;Kanadischer Dollar;Kanadischer Dollar
AUD;de;Australischer Dollar;Australischer Dollar
GBP;de;Pfund;Britische Pfund
ILS;de;Schekel;Israelische Schekel
INR;de;Rupie;Indische Rupie
QAR;de;RiYal;Riyal (Katar)
SAR;de;RiYal;Riyal (Saudi Arabien)
JPY;de;Yen;Japanische Yen
CNY;de;Renminbi Yuan;Chinesische Renminbi Yuan
SGD;de;Singapurischer Dollar;Singapurischer Dollar
ZAR;de;Rand;Südafrikanische Rand
EUR;fr;euro;de la Zone euro
USD;fr;dollar;dollar des États-Unis
CAD;fr;dollar canadien;dollar canadien
AUD;fr;dollar australien;dollar australien
GBP;fr;livre sterling;pound sterling
ILS;fr;Shekel;shekel israelien
INR;fr;Roupie;Roupie
QAR;fr;Rial;Rial
SAR;fr;Riyal;Riyal
JPY;fr;Yen;Yen
CNY;fr;yuan renminbi;yuan renminbi
SGD;fr;Singapour Dollar;Singapour Dollar
ZAR;fr;Rand;Rand
EUR;en;euro;euro
USD;en;dollar;US dollar
CAD;en;canadien dollar;canadien dollar 
AUD;en;australien dollar;australien dollar 
GBP;en;pound sterling;pound sterling
ILS;en;Shekel;shekel
INR;en;Rupie;indian rupie
QAR;en;Riyal;qatar riyal
SAR;en;Riyal;saudi arabian Riyal
JPY;en;Yen;japanese yen
CNY;en;Yuan;chinese yuan
SGD;en;Singapore dollar;Singapore dollar
ZAR;en;Rand;south african rand

================================================
FILE: db/data/sap.fe.cap.travel-Airline.csv
================================================
AirlineID;Name;CurrencyCode_code;AirlinePicURL
GA;Green Albatros;CAD;https://raw.githubusercontent.com/SAP-samples/fiori-elements-opensap/main/week1/images/airlines/Green-Albatross-logo.png
FA;Fly Africa;ZAR;https://raw.githubusercontent.com/SAP-samples/fiori-elements-opensap/main/week1/images/airlines/Fly-Africa-logo.png
EA;European Airlines;EUR;https://raw.githubusercontent.com/SAP-samples/fiori-elements-opensap/main/week1/images/airlines/European-Airlines-logo.png
OC;Oceania;USD;https://raw.githubusercontent.com/SAP-samples/fiori-elements-opensap/main/week1/images/airlines/Oceania-logo.png
SW;Sunset Wings;USD;https://raw.githubusercontent.com/SAP-samples/fiori-elements-opensap/main/week1/images/airlines/Sunset-Wings-logo.png

================================================
FILE: db/data/sap.fe.cap.travel-Airport.csv
================================================
AirportID;Name;City;CountryCode_code
FRA;Frankfurt Airport;Frankfurt/Main;DE
HAM;Hamburg Airport;Hamburg;DE
MUC;Munich Airport;Munich;DE
SXF;Berlin Schönefeld Airport;Berlin;DE
THF;Berlin Tempelhof Airport;Berlin;DE
TXL;Berlin Tegel Airport;Berlin;DE
CDG;Charles de Gaulle Airport;Paris;FR
ORY;Orly Airport;Paris;FR
VIE;Vienna International Airport;Vienna;AT
ZRH;Zürich Airport;Zurich;CH
RTM;Rotterdam The Hague Airport;Rotterdam;NL
FCO;Leonardo da Vinci–Fiumicino Airport;Rome;IT
VCE;Venice Marco Polo Airport;Venice;IT
LCY;London City Airport;London;GB
LGW;Gatwick Airport;London;GB
LHR;Heathrow Airport;London;GB
MAD;Adolfo Suárez Madrid–Barajas Airport;Madrid;ES
VKO;Vnukovo International Airport;Moscow;RU
SVO;Sheremetyevo International Airport;Moscow;RU
JFK;John F. Kennedy International Airport;New York City, New York;US
BNA;Nashville International Airport;Nashville, Tennessee;US
BOS;Logan International Airport;Boston, Massachusetts;US
ELP;El Paso International Airport;El Paso, Texas;US
DEN;Denver International Airport;Denver, Colorado;US
HOU;William P. Hobby Airport;Houston, Texas;US
LAS;McCarran International Airport;Las Vegas, Nevada;US
LAX;Los Angeles International Airport;Los Angeles, California;US
MCI;Kansas City International Airport;Kansas City, Missouri;US
MIA;Miami International Airport;Miami, Florida;US
SFO;San Francisco International Airport;San Francisco, California;US
EWR;Newark Liberty International Airport;Newark, New Jersey;US
YOW;Ottawa Macdonald–Cartier Int. Airport;Ottawa, Ontario;CA
ACA;General Juan N. Álvarez Int. Airport;Acapulco, Guerrero;MX
GIG;Rio de Janeiro–Galeão Int. Airport;Rio de Janeiro;BR
HAV;José Martí International Airport;Havana;CU
ASP;Alice Springs Airport;Alice Springs, Northern Territory;AU
ACE;Lanzarote Airport;Lanzarote, Canary Islands;ES
HRE;Harare International Airport;Harare;ZW
GCJ;Grand Central Airport;Johannesburg;ZA
NRT;Narita International Airport;Tokyo, Honshu;JP
ITM;Osaka International Airport;Osaka, Honshu;JP
KIX;Kansai International Airport;Osaka, Honshu;JP
HIJ;Hiroshima Airport;Hiroshima, Honshu;JP
SIN;Singapore Changi Airport;Singapore;SG
KUL;Kuala Lumpur International Airport;Kuala Lumpur;MY
HKG;Hong Kong International Airport;Hongkong;CN
BKK;Suvarnabhumi Airport;Bangkok;TH

================================================
FILE: db/data/sap.fe.cap.travel-Booking.csv
================================================
BookingUUID;to_Travel_TravelUUID;BookingID;BookingDate;to_Customer_CustomerID;to_Carrier_AirlineID;ConnectionID;FlightDate;FlightPrice;CurrencyCode_code;BookingStatus_code;LastChangedAt
7A757221A8E4645C17002DF03754AB66;52657221A8E4645C17002DF03754AB66;0001;2025-02-12;000099;SW;1537;2025-02-14;438.00;USD;N;2025-01-31T19:48:08Z
7B757221A8E4645C17002DF03754AB66;52657221A8E4645C17002DF03754AB66;0002;2025-02-12;000660;SW;1537;2025-02-14;438.00;USD;N;2025-01-31T19:48:08Z
7C757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0001;2025-01-25;000093;SW;1537;2025-02-14;438.00;USD;N;2025-01-31T21:07:58Z
7D757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0002;2025-01-25;000706;SW;1537;2025-02-14;438.00;USD;N;2025-01-31T21:07:58Z
7E757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0003;2025-01-28;000093;GA;0322;2025-02-16;438.00;USD;X;2025-01-31T21:07:58Z
7F757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0004;2025-01-28;000706;GA;0322;2025-02-16;438.00;USD;N;2025-01-31T21:07:58Z
80757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0005;2025-12-08;000093;SW;1537;2025-12-12;438.00;USD;N;2025-01-31T21:07:58Z
81757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0006;2025-12-08;000706;SW;1537;2025-12-12;438.00;USD;N;2025-01-31T21:07:58Z
82757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0007;2025-12-01;000093;GA;0322;2025-12-14;438.00;USD;N;2025-01-31T21:07:58Z
83757221A8E4645C17002DF03754AB66;53657221A8E4645C17002DF03754AB66;0008;2025-12-01;000706;GA;0322;2025-12-14;438.00;USD;N;2025-01-31T21:07:58Z
84757221A8E4645C17002DF03754AB66;54657221A8E4645C17002DF03754AB66;0001;2025-02-08;000665;SW;1537;2025-02-14;438.00;USD;N;2025-01-30T03:37:18Z
85757221A8E4645C17002DF03754AB66;54657221A8E4645C17002DF03754AB66;0002;2025-02-14;000665;GA;0322;2025-02-16;438.00;USD;N;2025-01-30T03:37:18Z
86757221A8E4645C17002DF03754AB66;54657221A8E4645C17002DF03754AB66;0003;2025-12-01;000665;SW;1537;2025-12-12;438.00;USD;N;2025-01-30T03:37:18Z
87757221A8E4645C17002DF03754AB66;54657221A8E4645C17002DF03754AB66;0004;2025-12-09;000665;GA;0322;2025-12-14;438.00;USD;N;2025-01-30T03:37:18Z
88757221A8E4645C17002DF03754AB66;55657221A8E4645C17002DF03754AB66;0001;2025-02-11;000161;SW;1537;2025-02-14;438.00;USD;X;2025-02-11T21:16:11Z
89757221A8E4645C17002DF03754AB66;55657221A8E4645C17002DF03754AB66;0002;2025-02-11;000461;SW;1537;2025-02-14;438.00;USD;N;2025-02-11T21:16:11Z
8A757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0001;2025-02-03;000072;SW;1537;2025-02-14;438.00;USD;N;2025-01-30T15:57:59Z
8B757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0002;2025-02-03;000399;SW;1537;2025-02-14;438.00;USD;N;2025-01-30T15:57:59Z
8C757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0003;2025-02-03;000203;SW;1537;2025-02-14;438.00;USD;N;2025-01-30T15:57:59Z
8D757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0004;2025-01-27;000072;GA;0322;2025-02-16;438.00;USD;N;2025-01-30T15:57:59Z
8E757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0005;2025-01-27;000399;GA;0322;2025-02-16;438.00;USD;N;2025-01-30T15:57:59Z
8F757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0006;2025-01-27;000203;GA;0322;2025-02-16;438.00;USD;N;2025-01-30T15:57:59Z
90757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0007;2025-11-22;000072;SW;1537;2025-12-12;438.00;USD;N;2025-01-30T15:57:59Z
91757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0008;2025-11-22;000399;SW;1537;2025-12-12;438.00;USD;N;2025-01-30T15:57:59Z
92757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0009;2025-11-22;000203;SW;1537;2025-12-12;438.00;USD;X;2025-01-30T15:57:59Z
93757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0010;2025-11-24;000072;GA;0322;2025-12-14;438.00;USD;N;2025-01-30T15:57:59Z
94757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0011;2025-11-24;000399;GA;0322;2025-12-14;438.00;USD;N;2025-01-30T15:57:59Z
95757221A8E4645C17002DF03754AB66;56657221A8E4645C17002DF03754AB66;0012;2025-11-24;000203;GA;0322;2025-12-
Download .txt
gitextract_co2fgw5z/

├── .cdsrc.json
├── .devcontainer/
│   ├── cds-dk/
│   │   ├── devcontainer-feature.json
│   │   └── install.sh
│   ├── cf-deploy/
│   │   ├── devcontainer-feature.json
│   │   └── install.sh
│   └── devcontainer.json
├── .github/
│   ├── actions/
│   │   ├── btp/
│   │   │   ├── Dockerfile
│   │   │   ├── action.yml
│   │   │   └── entrypoint.sh
│   │   └── cf-deploy/
│   │       ├── Dockerfile
│   │       ├── action.yml
│   │       └── entrypoint.sh
│   ├── dependabot.yml
│   ├── deployment/
│   │   └── kyma/
│   │       └── scripts/
│   │           ├── build-ui-image.sh
│   │           ├── create-container-registry-secret.sh
│   │           ├── create-db-secret.sh
│   │           ├── format-kyma-secret.js
│   │           ├── prepareUiFiles.js
│   │           ├── value.js
│   │           └── values.sh
│   └── workflows/
│       ├── deploy-btp.yml
│       ├── maven.yml
│       └── node.js.yml
├── .gitignore
├── .vscode/
│   └── launch.json
├── LICENSE
├── LICENSES/
│   └── Apache-2.0.txt
├── README-Kyma.md
├── README.md
├── REUSE.toml
├── _i18n/
│   ├── i18n.properties
│   ├── i18n_de.properties
│   ├── i18n_en.properties
│   └── i18n_fr.properties
├── app/
│   ├── .karma/
│   │   ├── karma-cap-middleware.js
│   │   └── karma.conf.js
│   ├── common.cds
│   ├── labels.cds
│   ├── services.cds
│   ├── travel_analytics/
│   │   ├── annotations.cds
│   │   ├── karma.conf.js
│   │   ├── package.json
│   │   ├── tsconfig.json
│   │   ├── ui5.yaml
│   │   ├── webapp/
│   │   │   ├── Component.ts
│   │   │   ├── i18n/
│   │   │   │   ├── i18n.properties
│   │   │   │   ├── i18n_de.properties
│   │   │   │   ├── i18n_en.properties
│   │   │   │   └── i18n_fr.properties
│   │   │   ├── index.html
│   │   │   ├── manifest.json
│   │   │   └── test/
│   │   │       ├── flpSandbox.html
│   │   │       ├── integration/
│   │   │       │   ├── Opa.qunit.html
│   │   │       │   ├── Opa.qunit.js
│   │   │       │   ├── OpaJourney.js
│   │   │       │   └── pages/
│   │   │       │       ├── BookingsList.js
│   │   │       │       └── BookingsObjectPage.js
│   │   │       ├── testsuite.qunit.html
│   │   │       └── testsuite.qunit.js
│   │   ├── xs-app.json
│   │   └── xs-security.json
│   ├── travel_processor/
│   │   ├── capabilities.cds
│   │   ├── field-control.cds
│   │   ├── karma.conf.js
│   │   ├── layouts.cds
│   │   ├── package.json
│   │   ├── tsconfig.json
│   │   ├── ui5.yaml
│   │   ├── webapp/
│   │   │   ├── Component.ts
│   │   │   ├── changes/
│   │   │   │   ├── changes-bundle.json
│   │   │   │   └── flexibility-bundle.json
│   │   │   ├── ext/
│   │   │   │   ├── controller/
│   │   │   │   │   ├── ControllerExtension.d.ts
│   │   │   │   │   └── ObjectPageExtension.controller.ts
│   │   │   │   └── fragment/
│   │   │   │       ├── CustomSection.fragment.xml
│   │   │   │       ├── CustomSection.ts
│   │   │   │       └── Trees4Tickets.fragment.xml
│   │   │   ├── i18n/
│   │   │   │   ├── i18n.properties
│   │   │   │   ├── i18n_de.properties
│   │   │   │   ├── i18n_en.properties
│   │   │   │   └── i18n_fr.properties
│   │   │   ├── index.html
│   │   │   ├── manifest.json
│   │   │   └── test/
│   │   │       ├── integration/
│   │   │       │   ├── Opa.qunit.html
│   │   │       │   ├── Opa.qunit.js
│   │   │       │   ├── OpaJourney.js
│   │   │       │   └── pages/
│   │   │       │       ├── ItemObjectPage.js
│   │   │       │       ├── MainListReport.js
│   │   │       │       └── MainObjectPage.js
│   │   │       ├── testsuite.qunit.html
│   │   │       └── testsuite.qunit.js
│   │   ├── xs-app.json
│   │   └── xs-security.json
│   └── value-helps.cds
├── db/
│   ├── common.cds
│   ├── data/
│   │   ├── sap.common-Countries.csv
│   │   ├── sap.common-Countries.texts.csv
│   │   ├── sap.common-Currencies.csv
│   │   ├── sap.common-Currencies.texts.csv
│   │   ├── sap.fe.cap.travel-Airline.csv
│   │   ├── sap.fe.cap.travel-Airport.csv
│   │   ├── sap.fe.cap.travel-Booking.csv
│   │   ├── sap.fe.cap.travel-BookingStatus.csv
│   │   ├── sap.fe.cap.travel-BookingStatus.texts.csv
│   │   ├── sap.fe.cap.travel-BookingSupplement.csv
│   │   ├── sap.fe.cap.travel-Flight.csv
│   │   ├── sap.fe.cap.travel-FlightConnection.csv
│   │   ├── sap.fe.cap.travel-Passenger.csv
│   │   ├── sap.fe.cap.travel-Supplement.csv
│   │   ├── sap.fe.cap.travel-Supplement.texts.csv
│   │   ├── sap.fe.cap.travel-SupplementType.csv
│   │   ├── sap.fe.cap.travel-Travel.csv
│   │   ├── sap.fe.cap.travel-TravelAgency.csv
│   │   ├── sap.fe.cap.travel-TravelStatus.csv
│   │   └── sap.fe.cap.travel-TravelStatus.texts.csv
│   ├── master-data.cds
│   ├── package.json
│   └── schema.cds
├── eslint.config.js
├── mta-java.yaml
├── mta.yaml
├── native-build-env.json
├── package.json
├── pom.xml
├── srv/
│   ├── analytics-service.cds
│   ├── pom.xml
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/
│   │   │   │   └── com/
│   │   │   │       └── sap/
│   │   │   │           └── cap/
│   │   │   │               └── sflight/
│   │   │   │                   ├── SFlightApplication.java
│   │   │   │                   ├── processor/
│   │   │   │                   │   ├── AcceptRejectHandler.java
│   │   │   │                   │   ├── CreationHandler.java
│   │   │   │                   │   ├── DeductDiscountHandler.java
│   │   │   │                   │   ├── IllegalTravelDateException.java
│   │   │   │                   │   ├── IllegalTravelStatusException.java
│   │   │   │                   │   ├── RecalculatePriceHandler.java
│   │   │   │                   │   └── UpdateFlightSeatsHandler.java
│   │   │   │                   ├── security/
│   │   │   │                   │   └── WebSecurityConfig.java
│   │   │   │                   └── ui/
│   │   │   │                       └── RedirectFilter.java
│   │   │   └── resources/
│   │   │       ├── META-INF/
│   │   │       │   └── native-image/
│   │   │       │       └── resource-config.json
│   │   │       ├── application.yml
│   │   │       └── messages.properties
│   │   └── test/
│   │       ├── java/
│   │       │   └── com/
│   │       │       └── sap/
│   │       │           └── cap/
│   │       │               └── sflight/
│   │       │                   ├── SFlightApplicationTest.java
│   │       │                   └── processor/
│   │       │                       ├── TravelSmokeTest.java
│   │       │                       └── UpdateFlightSeatsHandlerServiceIntegrationTest.java
│   │       └── resources/
│   │           └── META-INF/
│   │               └── native-image/
│   │                   └── reflect-config.json
│   ├── travel-service.cds
│   └── travel-service.ts
├── test/
│   ├── odata.test.ts
│   ├── requests.http
│   └── setup.ts
└── tsconfig.json
Download .txt
SYMBOL INDEX (88 symbols across 23 files)

FILE: .github/deployment/kyma/scripts/prepareUiFiles.js
  function prepareUiFiles (line 5) | function prepareUiFiles(path, options) {
  function throwError (line 63) | function throwError(msg) {
  function getPackageJsonInclude (line 67) | function getPackageJsonInclude() {
  function getUI5DeployTemplateYaml (line 90) | function getUI5DeployTemplateYaml() {
  function getXsAppTemplateJson (line 117) | function getXsAppTemplateJson() {
  function getSrvDestination (line 152) | function getSrvDestination(destinations) {

FILE: .github/deployment/kyma/scripts/value.js
  function printProperty (line 15) | function printProperty(file) {

FILE: app/.karma/karma-cap-middleware.js
  function spawnServer (line 4) | function spawnServer(cmd, args, cwd, fnIsReady) {
  function createKarmaMiddleware (line 33) | function createKarmaMiddleware(serverUrl, auth) {
  function java (line 52) | async function java() {
  function node (line 66) | async function node() {

FILE: app/travel_analytics/webapp/Component.ts
  class Component (line 6) | class Component extends BaseComponent {

FILE: app/travel_processor/webapp/Component.ts
  class Component (line 6) | class Component extends BaseComponent {

FILE: app/travel_processor/webapp/ext/controller/ControllerExtension.d.ts
  class ControllerExtension (line 5) | class ControllerExtension<API> {

FILE: app/travel_processor/webapp/ext/controller/ObjectPageExtension.controller.ts
  class ObjectPageExtension (line 10) | class ObjectPageExtension extends ControllerExtension<ExtensionAPI> {
    method openDialog (line 11) | async openDialog(resolve: (value: PromiseLike<null> | null) => void, r...
    method onInit (line 46) | onInit(this: ObjectPageExtension) {
    method onBeforeSave (line 51) | onBeforeSave(this: ObjectPageExtension) {

FILE: app/travel_processor/webapp/ext/fragment/CustomSection.ts
  function onPress (line 11) | function onPress(this: ExtensionAPI, event: UI5Event) {

FILE: srv/src/main/java/com/sap/cap/sflight/SFlightApplication.java
  class SFlightApplication (line 6) | @SpringBootApplication
    method main (line 9) | public static void main(String[] args) {

FILE: srv/src/main/java/com/sap/cap/sflight/processor/AcceptRejectHandler.java
  class AcceptRejectHandler (line 29) | @Component
    method AcceptRejectHandler (line 40) | public AcceptRejectHandler(DraftService draftService, PersistenceServi...
    method beforeAcceptTravel (line 45) | @Before(entity = Travel_.CDS_NAME)
    method beforeRejectTravel (line 50) | @Before(entity = Travel_.CDS_NAME)
    method beforeAcceptOrRejectTravel (line 55) | private void beforeAcceptOrRejectTravel(CqnSelect select, UserInfo use...
    method onRejectTravel (line 72) | @On(entity = Travel_.CDS_NAME)
    method onAcceptTravel (line 81) | @On(entity = Travel_.CDS_NAME)
    method updateStatusForTravelId (line 90) | private void updateStatusForTravelId(String travelUUID, String newStat...
    method checkIfTravelHasExceptedStatus (line 103) | private void checkIfTravelHasExceptedStatus(Travel travel) {
    method checkIfTravelIsLockedByAnotherUser (line 111) | private void checkIfTravelIsLockedByAnotherUser(Travel travel, UserInf...

FILE: srv/src/main/java/com/sap/cap/sflight/processor/CreationHandler.java
  class CreationHandler (line 31) | @Component
    method CreationHandler (line 39) | public CreationHandler(PersistenceService persistenceService, DraftSer...
    method setBookingDateIfNotProvided (line 44) | @Before(event = { CqnService.EVENT_CREATE, CqnService.EVENT_UPDATE, Dr...
    method saveComputedValues (line 55) | @Before(event = DraftService.EVENT_DRAFT_SAVE, entity = Travel_.CDS_NAME)
    method checkTravelEndDateIsAfterBeginDate (line 75) | @Before(event = { CqnService.EVENT_CREATE, CqnService.EVENT_UPDATE }, ...
    method calculateTravelIdBeforeCreation (line 90) | @Before(event = CqnService.EVENT_CREATE, entity = Travel_.CDS_NAME)
    method fillBookingIdsBeforeCreationAndUpdate (line 100) | @Before(event = { CqnService.EVENT_CREATE, CqnService.EVENT_UPDATE, },...
    method addBookingSupplementIds (line 108) | private void addBookingSupplementIds(Travel travel) {
    method addBookingIds (line 131) | private void addBookingIds(Travel travel) {
    method initialTravelId (line 145) | @Before(event = DraftService.EVENT_DRAFT_NEW, entity = Travel_.CDS_NAME)
    method initialBookingSupplementId (line 150) | @Before(event = DraftService.EVENT_DRAFT_NEW, entity = BookingSuppleme...

FILE: srv/src/main/java/com/sap/cap/sflight/processor/DeductDiscountHandler.java
  class DeductDiscountHandler (line 24) | @Component
    method DeductDiscountHandler (line 31) | public DeductDiscountHandler(DraftService draftService, PersistenceSer...
    method deductDiscount (line 36) | @On(entity = Travel_.CDS_NAME)

FILE: srv/src/main/java/com/sap/cap/sflight/processor/IllegalTravelDateException.java
  class IllegalTravelDateException (line 6) | public class IllegalTravelDateException extends ServiceException {
    method IllegalTravelDateException (line 7) | public IllegalTravelDateException(String message, Object... parameters) {

FILE: srv/src/main/java/com/sap/cap/sflight/processor/IllegalTravelStatusException.java
  class IllegalTravelStatusException (line 6) | public class IllegalTravelStatusException extends ServiceException {
    method IllegalTravelStatusException (line 7) | public IllegalTravelStatusException(String message, Object... paramete...

FILE: srv/src/main/java/com/sap/cap/sflight/processor/RecalculatePriceHandler.java
  class RecalculatePriceHandler (line 40) | @Component
    method RecalculatePriceHandler (line 47) | public RecalculatePriceHandler(DraftService draftService, PersistenceS...
    method disableUpdateAndCreateForBookingAndBookingSupplement (line 52) | @Before(event = {CqnService.EVENT_CREATE, CqnService.EVENT_UPDATE}, en...
    method calculateTotalPriceForTravel (line 57) | private static BigDecimal calculateTotalPriceForTravel(CqnService db, ...
    method calculateNewTotalPriceForActiveTravel (line 97) | @After(event = {CqnService.EVENT_UPDATE, CqnService.EVENT_CREATE}, ent...
    method recalculateTravelPriceIfTravelWasUpdated (line 121) | @After(event = { DraftService.EVENT_DRAFT_PATCH }, entity = Travel_.CD...
    method recalculateTravelPriceIfFlightPriceWasUpdated (line 129) | @After(event = { DraftService.EVENT_DRAFT_PATCH, DraftService.EVENT_DR...
    method recalculateTravelPriceIfPriceWasUpdated (line 138) | @After(event = { DraftService.EVENT_DRAFT_NEW, DraftService.EVENT_DRAF...
    method calculateAndPatchNewTotalPriceForDraft (line 149) | private BigDecimal calculateAndPatchNewTotalPriceForDraft(final String...

FILE: srv/src/main/java/com/sap/cap/sflight/processor/UpdateFlightSeatsHandler.java
  class UpdateFlightSeatsHandler (line 41) | @Component
    method UpdateFlightSeatsHandler (line 51) | public UpdateFlightSeatsHandler(PersistenceService persistenceService) {
    type Status (line 55) | enum Status {
    method updateSeatsDiffProc (line 60) | @Before(event = { "CREATE", "UPDATE", "DELETE" }, entity = Travel_.CDS...
    method handleUpdatedTravelWithDiffProcessor (line 96) | private void handleUpdatedTravelWithDiffProcessor(EventContext context...
    method getOldStateTravel (line 126) | private Travel getOldStateTravel(String travelUUID) {
    method getTravelUuidFromDeleteCqn (line 135) | private String getTravelUuidFromDeleteCqn(EventContext context) {
    method updateSeatsOnFlights (line 141) | void updateSeatsOnFlights(Map<Status, List<Flight>> flightsStatus) {
    method getFlights (line 166) | Map<Status, List<Flight>> getFlights(Map<Status, List<Booking>> bookin...
    class BookingDiffVisitor (line 200) | private static class BookingDiffVisitor implements DiffVisitor {
      method BookingDiffVisitor (line 205) | public BookingDiffVisitor(Map<Status, List<Booking>> modifications, ...
      method changed (line 211) | @Override
      method added (line 228) | @Override
      method removed (line 241) | @Override

FILE: srv/src/main/java/com/sap/cap/sflight/security/WebSecurityConfig.java
  class WebSecurityConfig (line 12) | @Configuration
    method configure (line 17) | @Bean

FILE: srv/src/main/java/com/sap/cap/sflight/ui/RedirectFilter.java
  class RedirectFilter (line 20) | @Component
    method doFilter (line 24) | @Override

FILE: srv/src/test/java/com/sap/cap/sflight/SFlightApplicationTest.java
  class SFlightApplicationTest (line 6) | @SpringBootTest
    method contextLoads (line 9) | @Test

FILE: srv/src/test/java/com/sap/cap/sflight/processor/TravelSmokeTest.java
  class TravelSmokeTest (line 16) | @SpringBootTest
    method testReadTravels (line 23) | @Test

FILE: srv/src/test/java/com/sap/cap/sflight/processor/UpdateFlightSeatsHandlerServiceIntegrationTest.java
  class UpdateFlightSeatsHandlerServiceIntegrationTest (line 30) | @SpringBootTest
    method testUpdateFlightHandlerForAddedBooking (line 42) | @Test
    method testUpdateFlightHandlerForDeletedBooking (line 81) | @Test
    method testUpdateFlightHandlerForUpdatedBooking (line 116) | @Test
    method addRequiredDataToTravel (line 167) | private static void addRequiredDataToTravel(Travel travel) {

FILE: srv/travel-service.ts
  class TravelService (line 6) | class TravelService extends cds.ApplicationService { init() {
    method init (line 6) | init() {

FILE: test/odata.test.ts
  function expect_totals (line 166) | async function expect_totals (expected, _active = 'IsActiveEntity=false') {
Condensed preview — 148 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (4,736K chars).
[
  {
    "path": ".cdsrc.json",
    "chars": 3,
    "preview": "{}\n"
  },
  {
    "path": ".devcontainer/cds-dk/devcontainer-feature.json",
    "chars": 465,
    "preview": "{\n    \"id\": \"cds-dk\",\n    \"version\": \"1.0.0\",\n    \"name\": \"cds-dk\",\n    \"description\": \"Install @sap/cds-dk (via npm)\",\n"
  },
  {
    "path": ".devcontainer/cds-dk/install.sh",
    "chars": 132,
    "preview": "#!/bin/sh\nset -e\n\nexport VERSION_SPECIFIER=\"${VERSION:+@${VERSION}}\"\n\n. ${NVM_DIR}/nvm.sh\n\nnpm i -g @sap/cds-dk${VERSION"
  },
  {
    "path": ".devcontainer/cf-deploy/devcontainer-feature.json",
    "chars": 223,
    "preview": "{\n    \"id\": \"cf-deploy\",\n    \"version\": \"1.0.1\",\n    \"name\": \"cf-deploy\",\n    \"description\": \"Install tooling for deploy"
  },
  {
    "path": ".devcontainer/cf-deploy/install.sh",
    "chars": 523,
    "preview": "#!/bin/sh\nset -e\n\n# Homebrew\nif ! type brew >/dev/null 2>&1; then\n    NONINTERACTIVE=1 su vscode -s /bin/bash -c \"$(curl"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "chars": 653,
    "preview": "// For format details, see https://aka.ms/devcontainer.json\n{\n\t\"name\": \"CAP Node.js & TypeScript & Java\",\n\t\"image\": \"mcr"
  },
  {
    "path": ".github/actions/btp/Dockerfile",
    "chars": 394,
    "preview": "FROM buildpack-deps:stretch-curl\n\n# https://blogs.sap.com/2021/09/01/sap-tech-bytes-btp-cli-installation/\nRUN curl -s -O"
  },
  {
    "path": ".github/actions/btp/action.yml",
    "chars": 543,
    "preview": "name: 'BTP'\ndescription: Performs BTP operations\ninputs:\n  cli_url:\n    description: The API endpoint for the CLI\n    re"
  },
  {
    "path": ".github/actions/btp/entrypoint.sh",
    "chars": 779,
    "preview": "#!/bin/bash\nset -e\n\nINPUT_USERNAME=${INPUT_USERNAME:-$CF_USERNAME}\nINPUT_PASSWORD=${INPUT_PASSWORD:-$CF_PASSWORD}\nbtp lo"
  },
  {
    "path": ".github/actions/cf-deploy/Dockerfile",
    "chars": 182,
    "preview": "FROM ppiper/cf-cli:v11\n\n# needed for cf to find its config\n# (GH resets HOME to /github/workspace)\nENV CF_HOME=$HOME\n\nCO"
  },
  {
    "path": ".github/actions/cf-deploy/action.yml",
    "chars": 1153,
    "preview": "name: 'CF Deploy MTA'\ndescription: Deploys an MTA file to Cloud Foundry\ninputs:\n  mtafile:\n    description: Path to your"
  },
  {
    "path": ".github/actions/cf-deploy/entrypoint.sh",
    "chars": 668,
    "preview": "#!/bin/bash\nset -e\n\ncf_opts=\ncf api ${INPUT_API:-$CF_API} ${cf_opts}\n\nINPUT_USERNAME=${INPUT_USERNAME:-$CF_USERNAME}\nINP"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 494,
    "preview": "version: 2\n\nupdates:\n\n- package-ecosystem: npm\n  directory: /\n  versioning-strategy: increase-if-necessary\n  schedule:\n "
  },
  {
    "path": ".github/deployment/kyma/scripts/build-ui-image.sh",
    "chars": 1537,
    "preview": "#!/bin/bash\n\nset -e\ncd \"$(dirname \"$(npm root)\")\"\nDIR=\"$(pwd)/.github\"\n\nnpm install --no-save yaml\n\nfunction value() {\n "
  },
  {
    "path": ".github/deployment/kyma/scripts/create-container-registry-secret.sh",
    "chars": 593,
    "preview": "#!/bin/bash\n\nread -p \"Docker Server: \" DOCKER_SERVER\n\nread -p \"User ($USER): \" DOCKER_USER\nif [ \"$DOCKER_USER\" == \"\" ]; "
  },
  {
    "path": ".github/deployment/kyma/scripts/create-db-secret.sh",
    "chars": 2286,
    "preview": "#!/bin/bash\n\nset -e\ncd \"$(dirname \"$(dirname \"$0\")\")\"\n. ./scripts/values.sh\n\nif true-value 2>/dev/null .saas_registry.en"
  },
  {
    "path": ".github/deployment/kyma/scripts/format-kyma-secret.js",
    "chars": 373,
    "preview": "const key=JSON.parse(process.argv[4].replace(/^.*/, \"\"));\nconst credentials=key.credentials /* new cfcli? */ || key;\ncon"
  },
  {
    "path": ".github/deployment/kyma/scripts/prepareUiFiles.js",
    "chars": 4993,
    "preview": "\nconst Path = require('path');\nconst posixJoin = Path.posix.join;\n\nfunction prepareUiFiles(path, options) {\n    const { "
  },
  {
    "path": ".github/deployment/kyma/scripts/value.js",
    "chars": 912,
    "preview": "#!/usr/bin/env node\n/*eslint no-unused-vars: [\"error\", { \"caughtErrors\": \"none\" }]*/\n\nconst yaml = require('yaml');\ncons"
  },
  {
    "path": ".github/deployment/kyma/scripts/values.sh",
    "chars": 936,
    "preview": "\nfunction read-value() {\n    if [ -f .values.yaml ]; then\n        local VALUE=\"$(yaml2json <.values.yaml | jq -er \"$1\" |"
  },
  {
    "path": ".github/workflows/deploy-btp.yml",
    "chars": 3017,
    "preview": "on:\n  workflow_call:\n    inputs:\n      btp-subdomain:\n        type: string\n        required: false\n        default: 284d"
  },
  {
    "path": ".github/workflows/maven.yml",
    "chars": 1006,
    "preview": "# This workflow will build a Java project with Maven\n# For more information see: https://help.github.com/actions/languag"
  },
  {
    "path": ".github/workflows/node.js.yml",
    "chars": 2101,
    "preview": "# This workflow will do a clean install of node dependencies, build the source code and run tests across different versi"
  },
  {
    "path": ".gitignore",
    "chars": 611,
    "preview": "# CAP sflight\n_out\n*.db\nconnection.properties\ndefault-*.json\n*.sql\ngen/\nnode_modules/\ntarget/\n.reloadtrigger\n@cds-models"
  },
  {
    "path": ".vscode/launch.json",
    "chars": 804,
    "preview": "{\n  // Use IntelliSense to learn about possible attributes.\n  // Hover to view descriptions of existing attributes.\n  //"
  },
  {
    "path": "LICENSE",
    "chars": 10142,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "LICENSES/Apache-2.0.txt",
    "chars": 10280,
    "preview": "Apache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AN"
  },
  {
    "path": "README-Kyma.md",
    "chars": 6934,
    "preview": "# Deployment to SAP Business Technology Platform - Kyma Runtime\n\n- [Deployment to SAP Business Technology Platform - Kym"
  },
  {
    "path": "README.md",
    "chars": 7642,
    "preview": "\n\n# Welcome to the CAP SFLIGHT App\n\nThis is a sample app for the travel reference scenario, built with the [SAP Cloud Ap"
  },
  {
    "path": "REUSE.toml",
    "chars": 1947,
    "preview": "version = 1\nSPDX-PackageName = \"cap-sflight\"\nSPDX-PackageSupplier = \"<Steffen Weinstock (steffen.weinstock@sap.com)>\"\nSP"
  },
  {
    "path": "_i18n/i18n.properties",
    "chars": 1857,
    "preview": "# This is the resource bundle for travel_processor\n\nTravel=Travel\nTravels=Travels\nTravelUUID=Travel UUID\nTravelID=Travel"
  },
  {
    "path": "_i18n/i18n_de.properties",
    "chars": 1755,
    "preview": "# This is the resource bundle for travel_processor\n\nTravel=Reise\nTravels=Reisen\nTravelUUID=Reise-UUID\nTravelID=Reise\nAge"
  },
  {
    "path": "_i18n/i18n_en.properties",
    "chars": 1751,
    "preview": "# This is the resource bundle for travel_processor\n\nTravel=Travel\nTravels=Travels\nTravelUUID=Travel UUID\nTravelID=Travel"
  },
  {
    "path": "_i18n/i18n_fr.properties",
    "chars": 1859,
    "preview": "# This is the resource bundle for travel_processor\n\nTravel=Voyage\nTravels=Voyages\nTravelUUID=UUID voyage\nTravelID=Voyage"
  },
  {
    "path": "app/.karma/karma-cap-middleware.js",
    "chars": 2166,
    "preview": "const spawn = require(\"cross-spawn\"),\n  HttpProxy = require(\"http-proxy\");\n\nfunction spawnServer(cmd, args, cwd, fnIsRea"
  },
  {
    "path": "app/.karma/karma.conf.js",
    "chars": 1138,
    "preview": "const puppeteer = require(\"puppeteer\"),\n  cap = require(\"./karma-cap-middleware\");\n\nprocess.env.CHROME_BIN = puppeteer.e"
  },
  {
    "path": "app/common.cds",
    "chars": 225,
    "preview": "using { Currency } from '../db/common';\n\n\n// Workarounds for overly strict OData libs and clients\nannotate cds.UUID with"
  },
  {
    "path": "app/labels.cds",
    "chars": 4453,
    "preview": "using { sap.fe.cap.travel as schema } from '../db/schema';\n\n//\n// annotations that control rendering of fields and label"
  },
  {
    "path": "app/services.cds",
    "chars": 180,
    "preview": "using from './travel_processor/capabilities';\nusing from './travel_processor/field-control';\nusing from './travel_proces"
  },
  {
    "path": "app/travel_analytics/annotations.cds",
    "chars": 10761,
    "preview": "using AnalyticsService as service from '../../srv/analytics-service';\n\nannotate service.Bookings with @(\n  Aggregation.C"
  },
  {
    "path": "app/travel_analytics/karma.conf.js",
    "chars": 51,
    "preview": "module.exports = require(\"../.karma/karma.conf.js\")"
  },
  {
    "path": "app/travel_analytics/package.json",
    "chars": 1231,
    "preview": "{\n  \"name\": \"travel-analytics\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"description\": \"SFlight ALP\",\n  \"main\": \"weba"
  },
  {
    "path": "app/travel_analytics/tsconfig.json",
    "chars": 708,
    "preview": "{\n    \"compilerOptions\": {\n        \"target\": \"es2022\",\n        \"module\": \"es2022\",\n        \"skipLibCheck\": true,\n       "
  },
  {
    "path": "app/travel_analytics/ui5.yaml",
    "chars": 1206,
    "preview": "# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json\nspecVersion: \"3.0\"\nmetadata:\n  na"
  },
  {
    "path": "app/travel_analytics/webapp/Component.ts",
    "chars": 430,
    "preview": "import BaseComponent from \"sap/fe/core/AppComponent\";\n\n/**\n * @namespace sap.fe.cap.travel_analytics\n */\nexport default "
  },
  {
    "path": "app/travel_analytics/webapp/i18n/i18n.properties",
    "chars": 186,
    "preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Analyze Book"
  },
  {
    "path": "app/travel_analytics/webapp/i18n/i18n_de.properties",
    "chars": 196,
    "preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Buchungen an"
  },
  {
    "path": "app/travel_analytics/webapp/i18n/i18n_en.properties",
    "chars": 186,
    "preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Analyze Book"
  },
  {
    "path": "app/travel_analytics/webapp/i18n/i18n_fr.properties",
    "chars": 204,
    "preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Analyser les"
  },
  {
    "path": "app/travel_analytics/webapp/index.html",
    "chars": 1239,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n    <meta charset="
  },
  {
    "path": "app/travel_analytics/webapp/manifest.json",
    "chars": 8564,
    "preview": "{\n    \"_version\": \"1.42.0\",\n    \"sap.app\": {\n        \"id\": \"sap.fe.cap.travel_analytics\",\n        \"type\": \"application\","
  },
  {
    "path": "app/travel_analytics/webapp/test/flpSandbox.html",
    "chars": 2931,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <!-- Copyright (c) 2015 SAP AG, All Rights Reserved -->\n  <head>\n    <meta http-equiv"
  },
  {
    "path": "app/travel_analytics/webapp/test/integration/Opa.qunit.html",
    "chars": 1164,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <title>Integration tests</title>\n    <meta charset=\"UTF-8\" content=\"IE=edg"
  },
  {
    "path": "app/travel_analytics/webapp/test/integration/Opa.qunit.js",
    "chars": 739,
    "preview": "sap.ui.require(\n  [\n    \"sap/fe/test/JourneyRunner\",\n    \"sap/fe/cap/travel_analytics/test/integration/OpaJourney\",\n    "
  },
  {
    "path": "app/travel_analytics/webapp/test/integration/OpaJourney.js",
    "chars": 471,
    "preview": "/* global QUnit */\nsap.ui.define([\"sap/ui/test/opaQunit\"], function (opaTest) {\n  \"use strict\";\n\n  return {\n    run: fun"
  },
  {
    "path": "app/travel_analytics/webapp/test/integration/pages/BookingsList.js",
    "chars": 341,
    "preview": "sap.ui.define([\"sap/fe/test/ListReport\"], function (ListReport) {\n  \"use strict\";\n\n  const CustomPageDefinitions = {\n   "
  },
  {
    "path": "app/travel_analytics/webapp/test/integration/pages/BookingsObjectPage.js",
    "chars": 347,
    "preview": "sap.ui.define([\"sap/fe/test/ObjectPage\"], function (ObjectPage) {\n  \"use strict\";\n\n  const CustomPageDefinitions = {\n   "
  },
  {
    "path": "app/travel_analytics/webapp/test/testsuite.qunit.html",
    "chars": 253,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <title>QUnit test suite</title>\n    <script src=\"../resources/sap/ui/qunit"
  },
  {
    "path": "app/travel_analytics/webapp/test/testsuite.qunit.js",
    "chars": 328,
    "preview": "/* global window, parent, location */\nwindow.suite = function () {\n  \"use strict\";\n\n  const oSuite = new parent.jsUnitTe"
  },
  {
    "path": "app/travel_analytics/xs-app.json",
    "chars": 410,
    "preview": "{\n  \"welcomeFile\": \"/index.html\",\n  \"authenticationMethod\": \"route\",\n  \"routes\": [\n    {\n      \"source\": \"^/analytics/(."
  },
  {
    "path": "app/travel_analytics/xs-security.json",
    "chars": 824,
    "preview": "{\n  \"scopes\": [\n    {\n      \"name\": \"$XSAPPNAME.reviewer\",\n      \"description\": \"reviewer\"\n    },\n    {\n      \"name\": \"$"
  },
  {
    "path": "app/travel_processor/capabilities.cds",
    "chars": 339,
    "preview": "using TravelService from '../../srv/travel-service';\n\nannotate TravelService.Travel with @odata.draft.enabled;\nannotate "
  },
  {
    "path": "app/travel_processor/field-control.cds",
    "chars": 3499,
    "preview": "using TravelService from '../../srv/travel-service';\n\n//\n// annotations that control the behavior of fields and actions\n"
  },
  {
    "path": "app/travel_processor/karma.conf.js",
    "chars": 51,
    "preview": "module.exports = require(\"../.karma/karma.conf.js\")"
  },
  {
    "path": "app/travel_processor/layouts.cds",
    "chars": 7378,
    "preview": "using TravelService from '../../srv/travel-service';\n\n//\n// annotations that control the Fiori layout\n//\n\nannotate Trave"
  },
  {
    "path": "app/travel_processor/package.json",
    "chars": 1199,
    "preview": "{\n  \"name\": \"travel-processor\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"main\": \"webapp/index.html\",\n  \"engines\": {\n "
  },
  {
    "path": "app/travel_processor/tsconfig.json",
    "chars": 708,
    "preview": "{\n    \"compilerOptions\": {\n        \"target\": \"es2022\",\n        \"module\": \"es2022\",\n        \"skipLibCheck\": true,\n       "
  },
  {
    "path": "app/travel_processor/ui5.yaml",
    "chars": 1196,
    "preview": "# yaml-language-server: $schema=https://sap.github.io/ui5-tooling/schema/ui5.yaml.json\nspecVersion: \"3.0\"\nmetadata:\n  na"
  },
  {
    "path": "app/travel_processor/webapp/Component.ts",
    "chars": 427,
    "preview": "import BaseComponent from \"sap/fe/core/AppComponent\";\n\n/**\n * @namespace sap.fe.cap.travel\n */\nexport default class Comp"
  },
  {
    "path": "app/travel_processor/webapp/changes/changes-bundle.json",
    "chars": 3,
    "preview": "{}\n"
  },
  {
    "path": "app/travel_processor/webapp/changes/flexibility-bundle.json",
    "chars": 42,
    "preview": "{\n  \"compVariants\": [],\n  \"changes\": []\n}\n"
  },
  {
    "path": "app/travel_processor/webapp/ext/controller/ControllerExtension.d.ts",
    "chars": 331,
    "preview": "/**\n * Helper to be able to define how to get the page specific extension API when writing a controller extension.\n */\nd"
  },
  {
    "path": "app/travel_processor/webapp/ext/controller/ObjectPageExtension.controller.ts",
    "chars": 2208,
    "preview": "import ControllerExtension from \"sap/ui/core/mvc/ControllerExtension\";\nimport ExtensionAPI from \"sap/fe/templates/Object"
  },
  {
    "path": "app/travel_processor/webapp/ext/fragment/CustomSection.fragment.xml",
    "chars": 660,
    "preview": "<core:FragmentDefinition xmlns:core=\"sap.ui.core\" xmlns=\"sap.m\" xmlns:l=\"sap.ui.layout\" xmlns:macros=\"sap.fe.macros\">\n\t<"
  },
  {
    "path": "app/travel_processor/webapp/ext/fragment/CustomSection.ts",
    "chars": 435,
    "preview": "import ExtensionAPI from 'sap/fe/core/ExtensionAPI';\nimport UI5Event from 'sap/ui/base/Event';\nimport MessageToast from "
  },
  {
    "path": "app/travel_processor/webapp/ext/fragment/Trees4Tickets.fragment.xml",
    "chars": 2074,
    "preview": "<core:FragmentDefinition\n\txmlns=\"sap.m\"\n\txmlns:macros=\"sap.fe.macros\"\n\txmlns:l=\"sap.ui.layout\"\n\txmlns:f=\"sap.ui.layout.f"
  },
  {
    "path": "app/travel_processor/webapp/i18n/i18n.properties",
    "chars": 182,
    "preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Manage Trave"
  },
  {
    "path": "app/travel_processor/webapp/i18n/i18n_de.properties",
    "chars": 186,
    "preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Reisen verwa"
  },
  {
    "path": "app/travel_processor/webapp/i18n/i18n_en.properties",
    "chars": 182,
    "preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Manage Trave"
  },
  {
    "path": "app/travel_processor/webapp/i18n/i18n_fr.properties",
    "chars": 188,
    "preview": "# This is the resource bundle for travel_processor\n\n#Texts for manifest.json\n\n#XTIT: Application name\ntitle=Gérer les vo"
  },
  {
    "path": "app/travel_processor/webapp/index.html",
    "chars": 1211,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n    <meta charset="
  },
  {
    "path": "app/travel_processor/webapp/manifest.json",
    "chars": 8452,
    "preview": "{\n    \"_version\": \"1.32.0\",\n    \"sap.app\": {\n        \"id\": \"sap.fe.cap.travel\",\n        \"type\": \"application\",\n        \""
  },
  {
    "path": "app/travel_processor/webapp/test/integration/Opa.qunit.html",
    "chars": 1140,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <title>Integration tests</title>\n    <meta charset=\"UTF-8\" content=\"IE=edg"
  },
  {
    "path": "app/travel_processor/webapp/test/integration/Opa.qunit.js",
    "chars": 851,
    "preview": "sap.ui.require(\n  [\n    \"sap/fe/test/JourneyRunner\",\n    \"sap/fe/cap/travel/test/integration/pages/MainListReport\",\n    "
  },
  {
    "path": "app/travel_processor/webapp/test/integration/OpaJourney.js",
    "chars": 13199,
    "preview": "/* global QUnit */\nsap.ui.define([\"sap/ui/test/opaQunit\"], function (opaTest) {\n  \"use strict\";\n\n  const Journey = {\n   "
  },
  {
    "path": "app/travel_processor/webapp/test/integration/pages/ItemObjectPage.js",
    "chars": 379,
    "preview": "sap.ui.define([\"sap/fe/test/ObjectPage\"], function (ObjectPage) {\n  \"use strict\";\n\n  // OPTIONAL\n  const AdditionalCusto"
  },
  {
    "path": "app/travel_processor/webapp/test/integration/pages/MainListReport.js",
    "chars": 371,
    "preview": "sap.ui.define([\"sap/fe/test/ListReport\"], function (ListReport) {\n  \"use strict\";\n\n  // OPTIONAL\n  const AdditionalCusto"
  },
  {
    "path": "app/travel_processor/webapp/test/integration/pages/MainObjectPage.js",
    "chars": 377,
    "preview": "sap.ui.define([\"sap/fe/test/ObjectPage\"], function (ObjectPage) {\n  \"use strict\";\n\n  // OPTIONAL\n  const AdditionalCusto"
  },
  {
    "path": "app/travel_processor/webapp/test/testsuite.qunit.html",
    "chars": 246,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <title>Testsuite</title>\n    <script src=\"/resources/sap/ui/qunit/qunit-re"
  },
  {
    "path": "app/travel_processor/webapp/test/testsuite.qunit.js",
    "chars": 324,
    "preview": "/* global window, parent, location */\nwindow.suite = function () {\n  \"use strict\";\n\n  const oSuite = new parent.jsUnitTe"
  },
  {
    "path": "app/travel_processor/xs-app.json",
    "chars": 410,
    "preview": "{\n  \"welcomeFile\": \"/index.html\",\n  \"authenticationMethod\": \"route\",\n  \"routes\": [\n    {\n      \"source\": \"^/processor/(."
  },
  {
    "path": "app/travel_processor/xs-security.json",
    "chars": 824,
    "preview": "{\n  \"scopes\": [\n    {\n      \"name\": \"$XSAPPNAME.reviewer\",\n      \"description\": \"reviewer\"\n    },\n    {\n      \"name\": \"$"
  },
  {
    "path": "app/value-helps.cds",
    "chars": 12468,
    "preview": "using { sap.fe.cap.travel as schema } from '../db/schema';\n\n//\n// annotations for value helps\n//\n\nannotate schema.Travel"
  },
  {
    "path": "db/common.cds",
    "chars": 795,
    "preview": "using { sap, managed } from '@sap/cds/common';\n\nextend sap.common.Currencies with {\n  // Currencies.code = ISO 4217 alph"
  },
  {
    "path": "db/data/sap.common-Countries.csv",
    "chars": 781,
    "preview": "code;name;descr\nAU;Australia;Commonwealth of Australia\nCA;Canada;Canada\nCN;China;People's Republic of China (PRC)\nFR;Fra"
  },
  {
    "path": "db/data/sap.common-Countries.texts.csv",
    "chars": 2594,
    "preview": "code;locale;name;descr\nAU;de;Australien;Commonwealth Australien\nCA;de;Kanada;Canada\nCN;de;China;Volksrepublik China\nFR;d"
  },
  {
    "path": "db/data/sap.common-Currencies.csv",
    "chars": 609,
    "preview": "code;symbol;name;descr;numcode;minor;exponent\nEUR;€;Euro;European Euro;978;Cent;2\nUSD;$;US Dollar;United States Dollar;8"
  },
  {
    "path": "db/data/sap.common-Currencies.texts.csv",
    "chars": 1258,
    "preview": "code;locale;name;descr\nEUR;de;Euro;European Euro\nUSD;de;US-Dollar;United States Dollar\nCAD;de;Kanadischer Dollar;Kanadis"
  },
  {
    "path": "db/data/sap.fe.cap.travel-Airline.csv",
    "chars": 737,
    "preview": "AirlineID;Name;CurrencyCode_code;AirlinePicURL\nGA;Green Albatros;CAD;https://raw.githubusercontent.com/SAP-samples/fiori"
  },
  {
    "path": "db/data/sap.fe.cap.travel-Airport.csv",
    "chars": 2263,
    "preview": "AirportID;Name;City;CountryCode_code\nFRA;Frankfurt Airport;Frankfurt/Main;DE\nHAM;Hamburg Airport;Hamburg;DE\nMUC;Munich A"
  },
  {
    "path": "db/data/sap.fe.cap.travel-Booking.csv",
    "chars": 1308261,
    "preview": "BookingUUID;to_Travel_TravelUUID;BookingID;BookingDate;to_Customer_CustomerID;to_Carrier_AirlineID;ConnectionID;FlightDa"
  },
  {
    "path": "db/data/sap.fe.cap.travel-BookingStatus.csv",
    "chars": 35,
    "preview": "code;name\nN;New\nX;Canceled\nB;Booked"
  },
  {
    "path": "db/data/sap.fe.cap.travel-BookingStatus.texts.csv",
    "chars": 173,
    "preview": "code;locale;name\nN;de;Neu\nB;de;Bestätigt\nX;de;Storniert\nN;fr;Nouveau\nB;fr;Réservé\nX;fr;Annulé\nN;it;Nuova\nB;it;Prenotato\n"
  },
  {
    "path": "db/data/sap.fe.cap.travel-BookingSupplement.csv",
    "chars": 2274389,
    "preview": "BookSupplUUID;to_Travel_TravelUUID;to_Booking_BookingUUID;BookingSupplementID;to_Supplement_SupplementID;Price;CurrencyC"
  },
  {
    "path": "db/data/sap.fe.cap.travel-Flight.csv",
    "chars": 2036,
    "preview": "AirlineID;ConnectionID;FlightDate;Price;CurrencyCode_code;PlaneType;MaximumSeats;OccupiedSeats\nSW;0001;2025-12-10;10818."
  },
  {
    "path": "db/data/sap.fe.cap.travel-FlightConnection.csv",
    "chars": 966,
    "preview": "AirlineID;ConnectionID;DepartureAirport_AirportID;DestinationAirport_AirportID;DepartureTime;ArrivalTime;Distance;Distan"
  },
  {
    "path": "db/data/sap.fe.cap.travel-Passenger.csv",
    "chars": 80371,
    "preview": "CustomerID;FirstName;LastName;Title;Street;PostalCode;City;CountryCode_code;PhoneNumber;EMailAddress\n000001;Theresia;Buc"
  },
  {
    "path": "db/data/sap.fe.cap.travel-Supplement.csv",
    "chars": 1956,
    "preview": "SupplementID;Price;Type_code;Description;CurrencyCode_code\nBV-0001;2.30;BV;Hot Chocolate;EUR\nBV-0002;7.50;BV;Alcohol fre"
  },
  {
    "path": "db/data/sap.fe.cap.travel-Supplement.texts.csv",
    "chars": 5018,
    "preview": "SupplementID;locale;Description\nBV-0001;de;Heiße Schokolade\nBV-0002;de;Alkoholfreier Champagner\nBV-0003;de;Cola\nBV-0004;"
  },
  {
    "path": "db/data/sap.fe.cap.travel-SupplementType.csv",
    "chars": 49,
    "preview": "code;name\nBV;Beverage\nML;Meal\nLU;Luggage\nEX;Extra"
  },
  {
    "path": "db/data/sap.fe.cap.travel-Travel.csv",
    "chars": 722959,
    "preview": "TravelUUID;TravelID;to_Agency_AgencyID;to_Customer_CustomerID;BeginDate;EndDate;BookingFee;TotalPrice;CurrencyCode_code;"
  },
  {
    "path": "db/data/sap.fe.cap.travel-TravelAgency.csv",
    "chars": 5901,
    "preview": "AgencyID;Name;Street;PostalCode;City;CountryCode_code;PhoneNumber;EMailAddress;WebAddress\n070001;Sunshine Travel;134 Wes"
  },
  {
    "path": "db/data/sap.fe.cap.travel-TravelStatus.csv",
    "chars": 39,
    "preview": "code;name\nO;Open\nA;Accepted\nX;Canceled\n"
  },
  {
    "path": "db/data/sap.fe.cap.travel-TravelStatus.texts.csv",
    "chars": 175,
    "preview": "code;locale;name\nO;en;Open\nA;en;Accepted\nX;en;Canceled\nO;de;Offen\nA;de;Genehmigt\nX;de;Abgelehnt\nO;fr;Ouverte\nA;fr;Accept"
  },
  {
    "path": "db/master-data.cds",
    "chars": 3106,
    "preview": "using { Currency, Country, custom.managed, sap } from './common';\nnamespace sap.fe.cap.travel;\n\n// ensure all masterdata"
  },
  {
    "path": "db/package.json",
    "chars": 154,
    "preview": "{\n  \"name\": \"deploy\",\n  \"dependencies\": {\n    \"@sap/hdi-deploy\": \"^4\"\n  },\n  \"scripts\": {\n    \"start\": \"node node_module"
  },
  {
    "path": "db/schema.cds",
    "chars": 2911,
    "preview": "using { Currency, custom.managed, sap.common.CodeList } from './common';\nusing {\n  sap.fe.cap.travel.Airline,\n  sap.fe.c"
  },
  {
    "path": "eslint.config.js",
    "chars": 943,
    "preview": "'use strict'\n\nconst eslint_js = require('@eslint/js')\nconst tseslint = require('typescript-eslint');\nconst globals = req"
  },
  {
    "path": "mta-java.yaml",
    "chars": 6690,
    "preview": "_schema-version: \"3.2\"\nID: capire.sflight\nversion: 1.0.0\ndescription: \"CAP sflight demo\"\n\nparameters:\n  enable-parallel-"
  },
  {
    "path": "mta.yaml",
    "chars": 6392,
    "preview": "_schema-version: \"3.2\"\nID: capire.sflight\nversion: 1.0.0\ndescription: \"CAP sflight demo\"\n\nparameters:\n  enable-parallel-"
  },
  {
    "path": "native-build-env.json",
    "chars": 89,
    "preview": "{\n    \"hana\": [ { \"name\": \"sflight-db\" } ],\n    \"xsuaa\": [ { \"name\": \"sflight-uaa\" } ]\n}\n"
  },
  {
    "path": "package.json",
    "chars": 3276,
    "preview": "{\n  \"name\": \"@capire/sflight\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"description\": \"CAP flight demo scenario\",\n  \""
  },
  {
    "path": "pom.xml",
    "chars": 3923,
    "preview": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLoc"
  },
  {
    "path": "srv/analytics-service.cds",
    "chars": 2215,
    "preview": "using { sap.fe.cap.travel as my } from '../db/schema';\n\nservice AnalyticsService @(path:'/analytics') {\n\n  // @(restrict"
  },
  {
    "path": "srv/pom.xml",
    "chars": 6091,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"htt"
  },
  {
    "path": "srv/src/main/java/com/sap/cap/sflight/SFlightApplication.java",
    "chars": 325,
    "preview": "package com.sap.cap.sflight;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autocon"
  },
  {
    "path": "srv/src/main/java/com/sap/cap/sflight/processor/AcceptRejectHandler.java",
    "chars": 4818,
    "preview": "package com.sap.cap.sflight.processor;\n\nimport static cds.gen.travelservice.TravelService_.TRAVEL;\n\nimport java.util.Opt"
  },
  {
    "path": "srv/src/main/java/com/sap/cap/sflight/processor/CreationHandler.java",
    "chars": 6137,
    "preview": "package com.sap.cap.sflight.processor;\n\n\nimport java.time.LocalDate;\nimport java.util.Comparator;\nimport java.util.HashM"
  },
  {
    "path": "srv/src/main/java/com/sap/cap/sflight/processor/DeductDiscountHandler.java",
    "chars": 2534,
    "preview": "package com.sap.cap.sflight.processor;\n\nimport static cds.gen.travelservice.TravelService_.TRAVEL;\nimport static java.la"
  },
  {
    "path": "srv/src/main/java/com/sap/cap/sflight/processor/IllegalTravelDateException.java",
    "chars": 346,
    "preview": "package com.sap.cap.sflight.processor;\n\nimport com.sap.cds.services.ErrorStatuses;\nimport com.sap.cds.services.ServiceEx"
  },
  {
    "path": "srv/src/main/java/com/sap/cap/sflight/processor/IllegalTravelStatusException.java",
    "chars": 350,
    "preview": "package com.sap.cap.sflight.processor;\n\nimport com.sap.cds.services.ErrorStatuses;\nimport com.sap.cds.services.ServiceEx"
  },
  {
    "path": "srv/src/main/java/com/sap/cap/sflight/processor/RecalculatePriceHandler.java",
    "chars": 6791,
    "preview": "package com.sap.cap.sflight.processor;\n\nimport static cds.gen.travelservice.TravelService_.BOOKING;\nimport static cds.ge"
  },
  {
    "path": "srv/src/main/java/com/sap/cap/sflight/processor/UpdateFlightSeatsHandler.java",
    "chars": 10305,
    "preview": "package com.sap.cap.sflight.processor;\n\nimport cds.gen.travelservice.Booking;\nimport cds.gen.travelservice.Booking_;\nimp"
  },
  {
    "path": "srv/src/main/java/com/sap/cap/sflight/security/WebSecurityConfig.java",
    "chars": 1034,
    "preview": "package com.sap.cap.sflight.security;\n\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplicati"
  },
  {
    "path": "srv/src/main/java/com/sap/cap/sflight/ui/RedirectFilter.java",
    "chars": 1757,
    "preview": "package com.sap.cap.sflight.ui;\n\nimport java.io.IOException;\n\nimport jakarta.servlet.FilterChain;\nimport jakarta.servlet"
  },
  {
    "path": "srv/src/main/resources/META-INF/native-image/resource-config.json",
    "chars": 112,
    "preview": "{\n  \"resources\": {\n    \"includes\": [\n      {\n        \"pattern\": \"\\\\Qmessages.properties\\\\E\"\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "srv/src/main/resources/application.yml",
    "chars": 588,
    "preview": "management:\n    endpoints:\n        web:\n            exposure:\n                include: \"*\"\n\ncds:\n    sql.hana.search:\n  "
  },
  {
    "path": "srv/src/main/resources/messages.properties",
    "chars": 441,
    "preview": "error.travel.status.unexpected=Travel with Id {0} does not have expected status {1} but already has status {2}.\nerror.tr"
  },
  {
    "path": "srv/src/test/java/com/sap/cap/sflight/SFlightApplicationTest.java",
    "chars": 296,
    "preview": "package com.sap.cap.sflight;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.spri"
  },
  {
    "path": "srv/src/test/java/com/sap/cap/sflight/processor/TravelSmokeTest.java",
    "chars": 1615,
    "preview": "package com.sap.cap.sflight.processor;\n\nimport static org.hamcrest.Matchers.containsString;\nimport static org.hamcrest.M"
  },
  {
    "path": "srv/src/test/java/com/sap/cap/sflight/processor/UpdateFlightSeatsHandlerServiceIntegrationTest.java",
    "chars": 7434,
    "preview": "package com.sap.cap.sflight.processor;\n\nimport static cds.gen.travelservice.TravelService_.FLIGHT;\nimport static cds.gen"
  },
  {
    "path": "srv/src/test/resources/META-INF/native-image/reflect-config.json",
    "chars": 88,
    "preview": "[\n{\n    \"name\": \"org.hamcrest.TypeSafeMatcher\",\n    \"queryAllDeclaredMethods\": true\n}\n]\n"
  },
  {
    "path": "srv/travel-service.cds",
    "chars": 614,
    "preview": "using { sap.fe.cap.travel as my } from '../db/schema';\n\nservice TravelService @(path:'/processor') {\n\n  @(restrict: [\n  "
  },
  {
    "path": "srv/travel-service.ts",
    "chars": 5960,
    "preview": "import cds from '@sap/cds'\nimport { Booking, BookingSupplement as Supplements, Travel } from '#cds-models/TravelService'"
  },
  {
    "path": "test/odata.test.ts",
    "chars": 9212,
    "preview": "import cds from '@sap/cds'\nimport { Travel } from '#cds-models/TravelService'\nconst { GET, POST, PATCH, axios, expect } "
  },
  {
    "path": "test/requests.http",
    "chars": 433,
    "preview": "@server = http://localhost:4004\n@alice = Authorization: Basic alice\n\nGET {{server}}/processor/Travel?$filter=TravelID eq"
  },
  {
    "path": "test/setup.ts",
    "chars": 215,
    "preview": "// for mocha\nprocess.env.CDS_TYPESCRIPT = \"true\"\n// disables cds-ui5-plugin as otherwise it would not let mocha tests te"
  },
  {
    "path": "tsconfig.json",
    "chars": 562,
    "preview": "{\n    \"compilerOptions\": {\n        \"target\": \"ES2022\",\n        \"module\": \"nodenext\",\n        \"rootDir\": \"./\",\n        \"b"
  }
]

About this extraction

This page contains the full source code of the SAP-samples/cap-sflight GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 148 files (4.5 MB), approximately 1.2M tokens, and a symbol index with 88 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!