From 870f8d063d02d750a8adfd7dcbdbc3bd56dc5d8d Mon Sep 17 00:00:00 2001 From: Uwe Klinger Date: Fri, 11 Jun 2021 08:15:27 +0200 Subject: [PATCH] Add Fiori UI for Book administration * Copyied from fiori/app/admin * Added Supplier + Value Help + Texts * Fixed bug in replication handler that caused to return wrong data --- suppliers/app/_i18n/i18n.properties | 31 ++ suppliers/app/_i18n/i18n_de.properties | 18 ++ suppliers/app/admin-fiori.html | 55 ++++ suppliers/app/admin/fiori-service.cds | 121 ++++++++ suppliers/app/admin/webapp/Component.js | 8 + .../app/admin/webapp/i18n/i18n.properties | 11 + suppliers/app/admin/webapp/manifest.json | 128 +++++++++ suppliers/app/common.cds | 264 ++++++++++++++++++ suppliers/app/services.cds | 12 + suppliers/srv/mashup.cds | 1 + suppliers/srv/mashup.js | 6 +- 11 files changed, 653 insertions(+), 2 deletions(-) create mode 100644 suppliers/app/_i18n/i18n.properties create mode 100644 suppliers/app/_i18n/i18n_de.properties create mode 100644 suppliers/app/admin-fiori.html create mode 100644 suppliers/app/admin/fiori-service.cds create mode 100644 suppliers/app/admin/webapp/Component.js create mode 100644 suppliers/app/admin/webapp/i18n/i18n.properties create mode 100644 suppliers/app/admin/webapp/manifest.json create mode 100644 suppliers/app/common.cds create mode 100644 suppliers/app/services.cds diff --git a/suppliers/app/_i18n/i18n.properties b/suppliers/app/_i18n/i18n.properties new file mode 100644 index 00000000..05cd38b5 --- /dev/null +++ b/suppliers/app/_i18n/i18n.properties @@ -0,0 +1,31 @@ +Books = Books +Book = Book +ID = ID +Title = Title +Author = Author +AuthorID = Author ID +Stock = Stock +Name = Name +AuthorName = Author's Name +DateOfBirth = Date of Birth +DateOfDeath = Date of Death +PlaceOfBirth = Place of Birth +PlaceOfDeath = Place of Death +Age = Age +Authors = Authors +Order = Order +Orders = Orders +Price = Price + +Genre = Genre +Genres = Genres +SubGenres = Sub Genres + +NumCode = Numeric Code +MinorUnit = Minor Unit +Exponent = Exponent + +Supplier = Supplier +SupplierName = Supplier Name +Id = Id +Name = Name \ No newline at end of file diff --git a/suppliers/app/_i18n/i18n_de.properties b/suppliers/app/_i18n/i18n_de.properties new file mode 100644 index 00000000..ee3b52f1 --- /dev/null +++ b/suppliers/app/_i18n/i18n_de.properties @@ -0,0 +1,18 @@ +Books = Bücher +Book = Buch +ID = ID +Title = Titel +Authors = Autoren +Author = Autor +AuthorID = ID des Autors +AuthorName = Name des Autors +Age = Alter +Name = Name +Stock = Bestand +Order = Bestellung +Orders = Bestellungen +Price = Preis +Supplier = Lieferant +SupplierName = Lieferantenname +Id = Id +Name = Name \ No newline at end of file diff --git a/suppliers/app/admin-fiori.html b/suppliers/app/admin-fiori.html new file mode 100644 index 00000000..6c229e6b --- /dev/null +++ b/suppliers/app/admin-fiori.html @@ -0,0 +1,55 @@ + + + + + + + + Bookshop + + + + + + + + + + \ No newline at end of file diff --git a/suppliers/app/admin/fiori-service.cds b/suppliers/app/admin/fiori-service.cds new file mode 100644 index 00000000..da70b9b4 --- /dev/null +++ b/suppliers/app/admin/fiori-service.cds @@ -0,0 +1,121 @@ +//using { AdminService } from '../../db/schema'; + +//////////////////////////////////////////////////////////////////////////// +// +// Books Object Page +// + +annotate AdminService.Books with @( + UI: { + Facets: [ + {$Type: 'UI.ReferenceFacet', Label: '{i18n>General}', Target: '@UI.FieldGroup#General'}, + {$Type: 'UI.ReferenceFacet', Label: '{i18n>Translations}', Target: 'texts/@UI.LineItem'}, + {$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'}, + {$Type: 'UI.ReferenceFacet', Label: '{i18n>Supplier}', Target: '@UI.FieldGroup#Supplier'}, + {$Type: 'UI.ReferenceFacet', Label: '{i18n>Admin}', Target: '@UI.FieldGroup#Admin'}, + ], + FieldGroup#General: { + Data: [ + {Value: title}, + {Value: author_ID}, + {Value: genre_ID}, + {Value: descr}, + ] + }, + FieldGroup#Details: { + Data: [ + {Value: stock}, + {Value: price}, + {Value: currency_code, Label: '{i18n>Currency}'}, + ] + }, + FieldGroup#Supplier: { + Data: [ + {Value: supplier_ID, Label: '{i18n>Id}'}, + {Value: supplier.name, Label: '{i18n>Name}'} + ] + }, + FieldGroup#Admin: { + Data: [ + {Value: createdBy}, + {Value: createdAt}, + {Value: modifiedBy}, + {Value: modifiedAt} + ] + } + } +); + +annotate AdminService.Authors with @( + UI: { + HeaderInfo: { + Description: {Value: lifetime} + }, + Facets: [ + {$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'}, + {$Type: 'UI.ReferenceFacet', Label: '{i18n>Books}', Target: 'books/@UI.LineItem'}, + ], + FieldGroup#Details: { + Data: [ + {Value: placeOfBirth}, + {Value: placeOfDeath}, + {Value: dateOfBirth}, + {Value: dateOfDeath}, + {Value: age, Label: '{i18n>Age}'}, + ] + }, + } +); + + + +//////////////////////////////////////////////////////////// +// +// Draft for Localized Data +// + +annotate sap.capire.bookshop.Books with @fiori.draft.enabled; +annotate AdminService.Books with @odata.draft.enabled; + +annotate AdminService.Books.texts with @( + UI: { + Identification: [{Value:title}], + SelectionFields: [ locale, title ], + LineItem: [ + {Value: locale, Label: 'Locale'}, + {Value: title, Label: 'Title'}, + {Value: descr, Label: 'Description'} + ] + } +); + +// Add Value Help for Locales +annotate AdminService.Books.texts { + locale @ValueList:{entity:'Languages',type:#fixed} +} +// In addition we need to expose Languages through AdminService +using { sap } from '@sap/cds/common'; +extend service AdminService { + entity Languages as projection on sap.common.Languages; +} + +annotate AdminService.Books with { + supplier @( + Common: { + Label: '{i18n>Supplier}', + ValueList: { + Label: '{i18n>Supplier}', + CollectionPath: 'Suppliers', + Parameters: [ + { $Type: 'Common.ValueListParameterInOut', + LocalDataProperty: supplier_ID, + ValueListProperty: 'ID' + }, + { $Type: 'Common.ValueListParameterDisplayOnly', + ValueListProperty: 'name' + } + ] + } + } + ); +} diff --git a/suppliers/app/admin/webapp/Component.js b/suppliers/app/admin/webapp/Component.js new file mode 100644 index 00000000..c3137017 --- /dev/null +++ b/suppliers/app/admin/webapp/Component.js @@ -0,0 +1,8 @@ +sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) { + "use strict"; + return AppComponent.extend("admin.Component", { + metadata: { manifest: "json" } + }); +}); + +/* eslint no-undef:0 */ \ No newline at end of file diff --git a/suppliers/app/admin/webapp/i18n/i18n.properties b/suppliers/app/admin/webapp/i18n/i18n.properties new file mode 100644 index 00000000..28b03dff --- /dev/null +++ b/suppliers/app/admin/webapp/i18n/i18n.properties @@ -0,0 +1,11 @@ +# This is the resource bundle of itelo +# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459 + +# JCI app descriptor contains lower case TITLE +appTitle=Bookshop Sample + +# JCI app descriptor contains lower case DESCRIPTION +appSubTitle=CAP Sample Application + +# JCI app descriptor contains lower case DESCRIPTION +appDescription=CDS Sample Service diff --git a/suppliers/app/admin/webapp/manifest.json b/suppliers/app/admin/webapp/manifest.json new file mode 100644 index 00000000..25047c29 --- /dev/null +++ b/suppliers/app/admin/webapp/manifest.json @@ -0,0 +1,128 @@ +{ + "_version": "1.8.0", + "sap.app": { + "id": "admin", + "type": "application", + "title": "Manage Books", + "description": "Sample Application", + "i18n": "i18n/i18n.properties", + "dataSources": { + "AdminService": { + "uri": "/admin/", + "type": "OData", + "settings": { + "odataVersion": "4.0" + } + } + }, + "-sourceTemplate": { + "id": "ui5template.basicSAPUI5ApplicationProject", + "-id": "ui5template.smartTemplate", + "-version": "1.40.12" + } + }, + "sap.ui5": { + "dependencies": { + "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": "BooksList", + "target": "BooksList" + }, + { + "pattern": "Books({key}):?query:", + "name": "BooksDetails", + "target": "BooksDetails" + }, + { + "pattern": "Books({key}/author({key2}):?query:", + "name": "AuthorsDetails", + "target": "AuthorsDetails" + } + ], + "targets": { + "BooksList": { + "type": "Component", + "id": "BooksList", + "name": "sap.fe.templates.ListReport", + "options": { + "settings" : { + "entitySet" : "Books", + "navigation" : { + "Books" : { + "detail" : { + "route" : "BooksDetails" + } + } + } + } + } + }, + "BooksDetails": { + "type": "Component", + "id": "BooksDetailsList", + "name": "sap.fe.templates.ObjectPage", + "options": { + "settings" : { + "entitySet" : "Books", + "navigation" : { + "Authors" : { + "detail" : { + "route" : "AuthorsDetails" + } + } + } + } + } + }, + "AuthorsDetails": { + "type": "Component", + "id": "AuthorsDetailsList", + "name": "sap.fe.templates.ObjectPage", + "options": { + "settings" : { + "entitySet" : "Authors" + } + } + } + } + }, + "contentDensities": { + "compact": true, + "cozy": true + } + }, + "sap.ui": { + "technology": "UI5", + "fullWidth": false + }, + "sap.fiori": { + "registrationIds": [], + "archeType": "transactional" + } +} \ No newline at end of file diff --git a/suppliers/app/common.cds b/suppliers/app/common.cds new file mode 100644 index 00000000..a37b2b05 --- /dev/null +++ b/suppliers/app/common.cds @@ -0,0 +1,264 @@ +/* + Common Annotations shared by all apps +*/ + +using { sap.capire.bookshop as my } from '@capire/bookshop'; +using { sap.common } from '@capire/common'; + +//////////////////////////////////////////////////////////////////////////// +// +// Books Lists +// +annotate my.Books with @( + Common.SemanticKey: [title], + UI: { + Identification: [{Value:title}], + SelectionFields: [ ID, author_ID, price, currency_code, supplier_ID, supplier.name ], + LineItem: [ + {Value: ID}, + {Value: title}, + {Value: author.name, Label:'{i18n>Author}'}, + {Value: genre.name}, + {Value: stock}, + {Value: price}, + {Value: currency.symbol, Label:' '}, + {Value: supplier_ID}, + {Value: supplier.name} + ] + } +) { + author @ValueList.entity:'Authors'; +}; + +//////////////////////////////////////////////////////////////////////////// +// +// Books Details +// +annotate my.Books with @( + UI: { + HeaderInfo: { + TypeName: '{i18n>Book}', + TypeNamePlural: '{i18n>Books}', + Title: {Value: title}, + Description: {Value: author.name} + }, + } +); + + + +//////////////////////////////////////////////////////////////////////////// +// +// Books Elements +// +annotate my.Books with { + ID @title:'{i18n>ID}' @UI.HiddenFilter; + title @title:'{i18n>Title}'; + genre @title:'{i18n>Genre}' @Common: { Text: genre.name, TextArrangement: #TextOnly }; + author @title:'{i18n>Author}' @Common: { Text: author.name, TextArrangement: #TextOnly }; + price @title:'{i18n>Price}' @Measures.ISOCurrency: currency_code; + stock @title:'{i18n>Stock}'; + descr @UI.MultiLineText; + supplier_ID @title:'{i18n>Supplier}'; +} + +annotate my.Suppliers with { + name @title:'{i18n>SupplierName}'; +} + +//////////////////////////////////////////////////////////////////////////// +// +// Genres List +// +annotate my.Genres with @( + Common.SemanticKey: [name], + UI: { + SelectionFields: [ name ], + LineItem:[ + {Value: name}, + {Value: parent.name, Label: 'Main Genre'}, + ], + } +); + +//////////////////////////////////////////////////////////////////////////// +// +// Genre Details +// +annotate my.Genres with @( + UI: { + Identification: [{Value:name}], + HeaderInfo: { + TypeName: '{i18n>Genre}', + TypeNamePlural: '{i18n>Genres}', + Title: {Value: name}, + Description: {Value: ID} + }, + Facets: [ + {$Type: 'UI.ReferenceFacet', Label: '{i18n>SubGenres}', Target: 'children/@UI.LineItem'}, + ], + } +); + +//////////////////////////////////////////////////////////////////////////// +// +// Genres Elements +// +annotate my.Genres with { + ID @title: '{i18n>ID}'; + name @title: '{i18n>Genre}'; +} + +//////////////////////////////////////////////////////////////////////////// +// +// Authors List +// +annotate my.Authors with @( + Common.SemanticKey: [name], + UI: { + Identification: [{Value:name}], + SelectionFields: [ name ], + LineItem:[ + {Value: ID}, + {Value: name}, + {Value: dateOfBirth}, + {Value: dateOfDeath}, + {Value: placeOfBirth}, + {Value: placeOfDeath}, + ], + } +); + +//////////////////////////////////////////////////////////////////////////// +// +// Author Details +// +annotate my.Authors with @( + UI: { + HeaderInfo: { + TypeName: '{i18n>Author}', + TypeNamePlural: '{i18n>Authors}', + Title: {Value: name}, + Description: {Value: dateOfBirth} + }, + Facets: [ + {$Type: 'UI.ReferenceFacet', Target: 'books/@UI.LineItem'}, + ], + } +); + + +//////////////////////////////////////////////////////////////////////////// +// +// Authors Elements +// +annotate my.Authors with { + ID @title:'{i18n>ID}' @UI.HiddenFilter; + name @title:'{i18n>Name}'; + dateOfBirth @title:'{i18n>DateOfBirth}'; + dateOfDeath @title:'{i18n>DateOfDeath}'; + placeOfBirth @title:'{i18n>PlaceOfBirth}'; + placeOfDeath @title:'{i18n>PlaceOfDeath}'; +} + +//////////////////////////////////////////////////////////////////////////// +// +// Languages List +// +annotate common.Languages with @( + Common.SemanticKey: [code], + Identification: [{Value:code}], + UI: { + SelectionFields: [ name, descr ], + LineItem:[ + {Value: code}, + {Value: name}, + ], + } +); + +//////////////////////////////////////////////////////////////////////////// +// +// Language Details +// +annotate common.Languages with @( + UI: { + HeaderInfo: { + TypeName: '{i18n>Language}', + TypeNamePlural: '{i18n>Languages}', + Title: {Value: name}, + Description: {Value: descr} + }, + Facets: [ + {$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'}, + ], + FieldGroup#Details: { + Data: [ + {Value: code}, + {Value: name}, + {Value: descr} + ] + }, + } +); + +//////////////////////////////////////////////////////////////////////////// +// +// Currencies List +// +annotate common.Currencies with @( + Common.SemanticKey: [code], + Identification: [{Value:code}], + UI: { + SelectionFields: [ name, descr ], + LineItem:[ + {Value: descr}, + {Value: symbol}, + {Value: code}, + ], + } +); + +//////////////////////////////////////////////////////////////////////////// +// +// Currency Details +// +annotate common.Currencies with @( + UI: { + HeaderInfo: { + TypeName: '{i18n>Currency}', + TypeNamePlural: '{i18n>Currencies}', + Title: {Value: descr}, + Description: {Value: code} + }, + Facets: [ + {$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'}, + {$Type: 'UI.ReferenceFacet', Label: '{i18n>Extended}', Target: '@UI.FieldGroup#Extended'}, + ], + FieldGroup#Details: { + Data: [ + {Value: name}, + {Value: symbol}, + {Value: code}, + {Value: descr} + ] + }, + FieldGroup#Extended: { + Data: [ + {Value: numcode}, + {Value: minor}, + {Value: exponent} + ] + }, + } +); + +//////////////////////////////////////////////////////////////////////////// +// +// Currencies Elements +// +annotate common.Currencies with { + numcode @title:'{i18n>NumCode}'; + minor @title:'{i18n>MinorUnit}'; + exponent @title:'{i18n>Exponent}'; +} diff --git a/suppliers/app/services.cds b/suppliers/app/services.cds new file mode 100644 index 00000000..595023e9 --- /dev/null +++ b/suppliers/app/services.cds @@ -0,0 +1,12 @@ +/* + This model controls what gets served to Fiori frontends... +*/ + +using from './admin/fiori-service'; +using from './browse/fiori-service'; +using from './common'; + +using from '@capire/common'; + +// only works in case of embedded orders service +using from '@capire/orders/app/orders/fiori-service'; diff --git a/suppliers/srv/mashup.cds b/suppliers/srv/mashup.cds index 3d2e9799..a6e28b0e 100644 --- a/suppliers/srv/mashup.cds +++ b/suppliers/srv/mashup.cds @@ -8,6 +8,7 @@ using { API_BUSINESS_PARTNER as S4 } from './external/API_BUSINESS_PARTNER.csn'; @cds.autoexpose // or expose explicitly in Catalog and AdminService @cds.persistence: {table,skip:false} // add persistency +@readonly entity sap.capire.bookshop.Suppliers as projection on S4.A_BusinessPartner { // TODO: Aliases not supported in Java, yet? key BusinessPartner as ID, diff --git a/suppliers/srv/mashup.js b/suppliers/srv/mashup.js index 0c903d40..981634ac 100644 --- a/suppliers/srv/mashup.js +++ b/suppliers/srv/mashup.js @@ -31,8 +31,10 @@ module.exports = async()=>{ // called by server.js if (!replicated) await replicate (supplierId, 'initial'); }; - if (supplierId) return Promise.all ([ next(), replicateIfNotExists() ]) - else return next() //> don't forget to pass down the interceptor stack + if (supplierId) + return (await Promise.all ([ next(), replicateIfNotExists() ]))[0] + else + return next() //> don't forget to pass down the interceptor stack }) })