diff --git a/app/admin/fiori-service.cds b/app/admin/fiori-service.cds
index 2480f4cc..8acab159 100644
--- a/app/admin/fiori-service.cds
+++ b/app/admin/fiori-service.cds
@@ -54,6 +54,33 @@ annotate AdminService.Books with @(UI : {
]}
});
+// Add Value Help for Tree Table
+annotate AdminService.Books with {
+ genre @(Common: {
+ Label : 'Genre',
+ ValueList: {
+ CollectionPath : 'GenreHierarchy',
+ Parameters : [
+ {
+ $Type : 'Common.ValueListParameterDisplayOnly',
+ ValueListProperty: 'name',
+ }],
+ PresentationVariantQualifier: 'VH',
+ }
+ });
+}
+
+annotate AdminService.GenreHierarchy with @UI: {
+ PresentationVariant #VH: {
+ $Type : 'UI.PresentationVariantType',
+ Visualizations : ['@UI.LineItem'],
+ RecursiveHierarchyQualifier: 'GenreHierarchy'
+ },
+ LineItem : [{
+ $Type: 'UI.DataField',
+ Value: name,
+ }]
+};
////////////////////////////////////////////////////////////
//
diff --git a/app/appconfig/fioriSandboxConfig.json b/app/appconfig/fioriSandboxConfig.json
index 1dc45ef8..48b2ef6a 100644
--- a/app/appconfig/fioriSandboxConfig.json
+++ b/app/appconfig/fioriSandboxConfig.json
@@ -20,6 +20,14 @@
"title": "Browse Books",
"description": "Find your favorite book"
}
+ }, {
+ "id": "browse-genres",
+ "tileType": "sap.ushell.ui.tile.StaticTile",
+ "properties": {
+ "targetURL": "#Genres-display",
+ "title": "Browse Genres",
+ "description": "Find your favorite genre"
+ }
}
]
},
@@ -112,6 +120,19 @@
"url": "/browse/webapp"
}
},
+ "browse-genres": {
+ "semanticObject": "Genres",
+ "action": "display",
+ "signature": {
+ "parameters": {},
+ "additionalParameters": "allowed"
+ },
+ "resolutionResult": {
+ "applicationType": "SAPUI5",
+ "additionalInformation": "SAPUI5.Component=genres",
+ "url": "/genres/webapp"
+ }
+ },
"manage-books": {
"semanticObject": "Books",
"action": "manage",
diff --git a/app/common.cds b/app/common.cds
index a8607057..ba638052 100644
--- a/app/common.cds
+++ b/app/common.cds
@@ -177,10 +177,8 @@ annotate my.Genres with
UI : {
SelectionFields : [name],
LineItem : [
- {Value : name},
- {
- Value : parent.name,
- Label : 'Main Genre'
+ {Value : name,
+ Label : '{i18n>Name}',
},
],
}
@@ -199,12 +197,7 @@ annotate my.Genres with
TypeNamePlural : '{i18n>Genres}',
Title : {Value : name},
Description : {Value : ID}
- },
- Facets : [{
- $Type : 'UI.ReferenceFacet',
- Label : '{i18n>SubGenres}',
- Target : 'children/@UI.LineItem'
- }, ],
+ }
});
diff --git a/app/genres/fiori-service.cds b/app/genres/fiori-service.cds
new file mode 100644
index 00000000..d195d935
--- /dev/null
+++ b/app/genres/fiori-service.cds
@@ -0,0 +1,23 @@
+/*
+ UI annotations for the Browse GenreHierarchy App
+*/
+
+using AdminService from '../../srv/admin-service';
+
+
+annotate AdminService.GenreHierarchy with @Aggregation.RecursiveHierarchy#GenreHierarchy: {
+ $Type: 'Aggregation.RecursiveHierarchyType',
+ NodeProperty: node_id, // identifies a node
+ ParentNavigationProperty: parent // navigates to a node's parent
+ };
+
+ annotate AdminService.GenreHierarchy with @Hierarchy.RecursiveHierarchy#GenreHierarchy: {
+ $Type: 'Hierarchy.RecursiveHierarchyType',
+ // ExternalKey : null,
+ LimitedDescendantCount: LimitedDescendantCount,
+ DistanceFromRoot: DistanceFromRoot,
+ DrillState: DrillState,
+ Matched: Matched,
+ MatchedDescendantCount: MatchedDescendantCount,
+ LimitedRank: LimitedRank
+};
\ No newline at end of file
diff --git a/app/genres/package.json b/app/genres/package.json
new file mode 100644
index 00000000..9b81674d
--- /dev/null
+++ b/app/genres/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "genres",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC"
+}
diff --git a/app/genres/webapp/Component.js b/app/genres/webapp/Component.js
new file mode 100644
index 00000000..6b352417
--- /dev/null
+++ b/app/genres/webapp/Component.js
@@ -0,0 +1,3 @@
+sap.ui.define(["sap/fe/core/AppComponent"], ac => ac.extend("genres.Component", {
+ metadata:{ manifest:'json' }
+}))
diff --git a/app/genres/webapp/i18n/i18n.properties b/app/genres/webapp/i18n/i18n.properties
new file mode 100644
index 00000000..d2792ee9
--- /dev/null
+++ b/app/genres/webapp/i18n/i18n.properties
@@ -0,0 +1,2 @@
+appTitle=Browse Genres
+appDescription=Genres as Tree View
diff --git a/app/genres/webapp/i18n/i18n_de.properties b/app/genres/webapp/i18n/i18n_de.properties
new file mode 100644
index 00000000..e8714e92
--- /dev/null
+++ b/app/genres/webapp/i18n/i18n_de.properties
@@ -0,0 +1,2 @@
+appTitle=Zeige Genres
+appDescription=Genres als Baumansicht
diff --git a/app/genres/webapp/index.html b/app/genres/webapp/index.html
new file mode 100644
index 00000000..d2192773
--- /dev/null
+++ b/app/genres/webapp/index.html
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+ Browse Genres
+
+
+
+
+
+
+
diff --git a/app/genres/webapp/manifest.json b/app/genres/webapp/manifest.json
new file mode 100644
index 00000000..ab4a7d46
--- /dev/null
+++ b/app/genres/webapp/manifest.json
@@ -0,0 +1,129 @@
+{
+ "_version": "1.8.0",
+ "sap.app": {
+ "id": "genres",
+ "type": "application",
+ "title": "{{appTitle}}",
+ "description": "{{appDescription}}",
+ "applicationVersion": {
+ "version": "1.0.0"
+ },
+ "dataSources": {
+ "AdminService": {
+ "uri": "/api/admin/",
+ "type": "OData",
+ "settings": {
+ "odataVersion": "4.0"
+ }
+ }
+ },
+ "-sourceTemplate": {
+ "id": "ui5template.basicSAPUI5ApplicationProject",
+ "-id": "ui5template.smartTemplate",
+ "-version": "1.40.12"
+ },
+ "crossNavigation": {
+ "inbounds": {
+ "Genres-show": {
+ "signature": {
+ "parameters": {},
+ "additionalParameters": "allowed"
+ },
+ "semanticObject": "GenreHierarchy",
+ "action": "show"
+ }
+ }
+ }
+ },
+ "sap.ui5": {
+ "dependencies": {
+ "minUI5Version": "1.122.0",
+ "libs": {
+ "sap.fe.templates": {}
+ }
+ },
+ "models": {
+ "i18n": {
+ "type": "sap.ui.model.resource.ResourceModel",
+ "uri": "i18n/i18n.properties"
+ },
+ "": {
+ "dataSource": "AdminService",
+ "settings": {
+ "synchronizationMode": "None",
+ "operationMode": "Server",
+ "autoExpandSelect" : true,
+ "earlyRequests": true,
+ "groupProperties": {
+ "default": {
+ "submit": "Auto"
+ }
+ }
+ }
+ }
+ },
+ "routing": {
+ "routes": [
+ {
+ "pattern": ":?query:",
+ "name": "GenreHierarchyList",
+ "target": "GenreHierarchyList"
+ },
+ {
+ "pattern": "GenreHierarchy({key}):?query:",
+ "name": "GenreHierarchyDetails",
+ "target": "GenreHierarchyDetails"
+ }
+ ],
+ "targets": {
+ "GenreHierarchyList": {
+ "type": "Component",
+ "id": "GenreHierarchyList",
+ "name": "sap.fe.templates.ListReport",
+ "options": {
+ "settings" : {
+ "entitySet" : "GenreHierarchy",
+ "navigation" : {
+ "GenreHierarchy" : {
+ "detail" : {
+ "route" : "GenreHierarchyDetails"
+ }
+ }
+ },
+ "controlConfiguration": {
+ "@com.sap.vocabularies.UI.v1.LineItem": {
+ "tableSettings": {
+ "hierarchyQualifier": "GenreHierarchy",
+ "type": "TreeTable"
+ }
+ }
+ }
+ }
+ }
+ },
+ "GenreHierarchyDetails": {
+ "type": "Component",
+ "id": "GenreHierarchyDetails",
+ "name": "sap.fe.templates.ObjectPage",
+ "options": {
+ "settings" : {
+ "entitySet": "GenreHierarchy"
+ }
+ }
+ }
+ }
+ },
+ "contentDensities": {
+ "compact": true,
+ "cozy": true
+ }
+ },
+ "sap.ui": {
+ "technology": "UI5",
+ "fullWidth": false
+ },
+ "sap.fiori": {
+ "registrationIds": [],
+ "archeType": "transactional"
+ }
+}
diff --git a/app/index.cds b/app/index.cds
index dcbf40e5..0c07c888 100644
--- a/app/index.cds
+++ b/app/index.cds
@@ -8,4 +8,5 @@ using from './orders/fiori-service';
using from './reviews/fiori-service';
using from './notes/fiori-service';
using from './addresses/fiori-service';
+using from './genres/fiori-service';
using from './common';
diff --git a/app/xs-app.json b/app/xs-app.json
index f6adf5a4..4facc67d 100644
--- a/app/xs-app.json
+++ b/app/xs-app.json
@@ -2,77 +2,82 @@
"welcomeFile": "/app/fiori.html",
"authenticationMethod": "route",
"routes": [
- {
- "source": "^/app/(.*)$",
- "cacheControl": "no-cache, no-store, must-revalidate",
- "target": "$1",
- "localDir": "./",
- "authenticationType": "xsuaa"
- },
- {
- "source": "^/appconfig/(.*)$",
- "localDir": "./",
- "authenticationType": "xsuaa"
- },
- {
- "source": "^/browse/webapp/(.*)$",
- "localDir": "./",
- "authenticationType": "xsuaa"
- },
- {
- "source": "^/admin/webapp/(.*)$",
- "localDir": "./",
- "authenticationType": "xsuaa"
- },
- {
- "source": "^/orders/webapp/(.*)$",
- "localDir": "./",
- "authenticationType": "xsuaa"
- },
- {
- "source": "^/reviews/webapp/(.*)$",
- "localDir": "./",
- "authenticationType": "xsuaa"
- },
- {
- "source": "^/notes/webapp/(.*)$",
- "localDir": "./",
- "authenticationType": "xsuaa"
- },
- {
- "source": "^/addresses/webapp/(.*)$",
- "localDir": "./",
- "authenticationType": "xsuaa"
- },
- {
- "source": "^/vue/(.*)$",
- "localDir": "./",
- "authenticationType": "xsuaa"
- },
- {
- "source": "^/api/admin/(.*)",
- "authenticationType": "xsuaa",
- "destination": "backend"
- },
- {
- "source": "^/api/browse/(.*)",
- "authenticationType": "xsuaa",
- "destination": "backend"
- },
- {
- "source": "^/api/review/(.*)",
- "authenticationType": "xsuaa",
- "destination": "backend"
- },
- {
- "source": "^/api/notes/(.*)",
- "authenticationType": "xsuaa",
- "destination": "backend"
- },
- {
- "source": "^/api/(.*)$",
- "authenticationType": "none",
- "destination": "backend"
- }
+ {
+ "source": "^/app/(.*)$",
+ "cacheControl": "no-cache, no-store, must-revalidate",
+ "target": "$1",
+ "localDir": "./",
+ "authenticationType": "xsuaa"
+ },
+ {
+ "source": "^/appconfig/(.*)$",
+ "localDir": "./",
+ "authenticationType": "xsuaa"
+ },
+ {
+ "source": "^/browse/webapp/(.*)$",
+ "localDir": "./",
+ "authenticationType": "xsuaa"
+ },
+ {
+ "source": "^/admin/webapp/(.*)$",
+ "localDir": "./",
+ "authenticationType": "xsuaa"
+ },
+ {
+ "source": "^/orders/webapp/(.*)$",
+ "localDir": "./",
+ "authenticationType": "xsuaa"
+ },
+ {
+ "source": "^/genres/webapp/(.*)$",
+ "localDir": "./",
+ "authenticationType": "xsuaa"
+ },
+ {
+ "source": "^/reviews/webapp/(.*)$",
+ "localDir": "./",
+ "authenticationType": "xsuaa"
+ },
+ {
+ "source": "^/notes/webapp/(.*)$",
+ "localDir": "./",
+ "authenticationType": "xsuaa"
+ },
+ {
+ "source": "^/addresses/webapp/(.*)$",
+ "localDir": "./",
+ "authenticationType": "xsuaa"
+ },
+ {
+ "source": "^/vue/(.*)$",
+ "localDir": "./",
+ "authenticationType": "xsuaa"
+ },
+ {
+ "source": "^/api/admin/(.*)",
+ "authenticationType": "xsuaa",
+ "destination": "backend"
+ },
+ {
+ "source": "^/api/browse/(.*)",
+ "authenticationType": "xsuaa",
+ "destination": "backend"
+ },
+ {
+ "source": "^/api/review/(.*)",
+ "authenticationType": "xsuaa",
+ "destination": "backend"
+ },
+ {
+ "source": "^/api/notes/(.*)",
+ "authenticationType": "xsuaa",
+ "destination": "backend"
+ },
+ {
+ "source": "^/api/(.*)$",
+ "authenticationType": "none",
+ "destination": "backend"
+ }
]
}
diff --git a/db/books.cds b/db/books.cds
index fe2e5e0a..a4d8ba44 100644
--- a/db/books.cds
+++ b/db/books.cds
@@ -2,7 +2,6 @@ namespace my.bookshop;
using {
Currency,
- sap,
managed,
cuid
} from '@sap/cds/common';
@@ -46,9 +45,11 @@ annotate Authors with
/**
* Hierarchically organized Code List for Genres
*/
-entity Genres : sap.common.CodeList {
- key ID : Integer;
- parent : Association to Genres;
- children : Composition of many Genres
- on children.parent = $self;
+entity Genres {
+ key ID : Integer;
+ node : Integer not null;
+ parent_node : Integer default 0;
+ name : localized String(255);
+ descr : localized String(1000);
+ parent : Association to one Genres on parent.node = parent_node;
}
diff --git a/db/data/my.bookshop-Genres.csv b/db/data/my.bookshop-Genres.csv
index 88e73bdd..044b2b47 100644
--- a/db/data/my.bookshop-Genres.csv
+++ b/db/data/my.bookshop-Genres.csv
@@ -1,16 +1,16 @@
-ID;parent_ID;name
-10;;Fiction
-11;10;Drama
-12;10;Poetry
-13;10;Fantasy
-14;10;Science Fiction
-15;10;Romance
-16;10;Mystery
-17;10;Thriller
-18;10;Dystopia
-19;10;Fairy Tale
-20;;Non-Fiction
-21;20;Biography
-22;21;Autobiography
-23;20;Essay
-24;20;Speech
\ No newline at end of file
+ID;parent_node;name;node
+10;;Fiction;10
+11;10;Drama;11
+12;10;Poetry;12
+13;10;Fantasy;13
+14;10;Science Fiction;14
+15;10;Romance;15
+16;10;Mystery;16
+17;10;Thriller;17
+18;10;Dystopia;18
+19;10;Fairy Tale;19
+20;;Non-Fiction;20
+21;20;Biography;21
+22;21;Autobiography;22
+23;20;Essay;23
+24;20;Speech;24
\ No newline at end of file
diff --git a/srv/admin-service.cds b/srv/admin-service.cds
index 0f0e1a9b..7709ebd0 100644
--- a/srv/admin-service.cds
+++ b/srv/admin-service.cds
@@ -1,10 +1,12 @@
using {sap.common.Languages as CommonLanguages} from '@sap/cds/common';
using {my.bookshop as my} from '../db/index';
using {sap.changelog as changelog} from 'com.sap.cds/change-tracking';
+using {my.common.Hierarchy as Hierarchy} from './hierarchy';
extend my.Orders with changelog.changeTracked;
@path : 'admin'
+@odata.apply.transformations
service AdminService @(requires : 'admin') {
entity Books as projection on my.Books excluding { reviews } actions {
action addToOrder(order_ID : UUID, quantity : Integer) returns Orders;
@@ -12,6 +14,13 @@ service AdminService @(requires : 'admin') {
entity Authors as projection on my.Authors;
entity Orders as select from my.Orders;
+ extend my.Genres with Hierarchy;
+
+ entity GenreHierarchy as projection on my.Genres {
+ node as node_id,
+ parent_node as parent_id,
+ *
+ } excluding { node, parent_node }
@cds.persistence.skip
entity Upload @odata.singleton {
@@ -60,6 +69,6 @@ annotate AdminService.OrderItems {
// Assign identifiers to the tracked entities
annotate AdminService.Orders with @changelog: [OrderNo];
annotate AdminService.OrderItems with @changelog: [
- parent.OrderNo,
- book.title,
- ];
\ No newline at end of file
+ parent.OrderNo,
+ book.title,
+];
diff --git a/srv/cat-service.cds b/srv/cat-service.cds
index e887271f..c83eb432 100644
--- a/srv/cat-service.cds
+++ b/srv/cat-service.cds
@@ -13,6 +13,9 @@ service CatalogService @(requires: 'any') {
@readonly
entity Authors as projection on my.Authors;
+ @readonly
+ entity Genres as projection on my.Genres;
+
@readonly
entity Reviews as projection on my.Reviews;
diff --git a/srv/hierarchy.cds b/srv/hierarchy.cds
new file mode 100644
index 00000000..f6619d98
--- /dev/null
+++ b/srv/hierarchy.cds
@@ -0,0 +1,29 @@
+namespace my.common;
+
+aspect Hierarchy {
+ virtual LimitedDescendantCount : Integer64;
+ virtual DistanceFromRoot : Integer64;
+ virtual DrillState : String;
+ virtual Matched : Boolean;
+ virtual MatchedDescendantCount : Integer64;
+ virtual LimitedRank : Integer64;
+}
+
+
+annotate Hierarchy with @Capabilities.FilterRestrictions.NonFilterableProperties: [
+ 'LimitedDescendantCount',
+ 'DistanceFromRoot',
+ 'DrillState',
+ 'Matched',
+ 'MatchedDescendantCount',
+ 'LimitedRank'
+];
+
+annotate Hierarchy with @Capabilities.SortRestrictions.NonSortableProperties: [
+ 'LimitedDescendantCount',
+ 'DistanceFromRoot',
+ 'DrillState',
+ 'Matched',
+ 'MatchedDescendantCount',
+ 'LimitedRank'
+];
diff --git a/srv/review-service.cds b/srv/review-service.cds
index d87950f9..599a2d79 100644
--- a/srv/review-service.cds
+++ b/srv/review-service.cds
@@ -13,6 +13,9 @@ service ReviewService {
@readonly
entity Authors as projection on my.Authors;
+ @readonly
+ entity Genres as projection on my.Genres;
+
// access control restrictions
annotate Reviews with @restrict : [
{