@sapui5/sap.fe.core 1.95.0 → 1.98.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/package.json +5 -5
  2. package/src/sap/fe/core/.library +1 -1
  3. package/src/sap/fe/core/AnnotationHelper.js +3 -3
  4. package/src/sap/fe/core/AppComponent.js +9 -44
  5. package/src/sap/fe/core/AppStateHandler.js +2 -2
  6. package/src/sap/fe/core/BaseController.js +1 -1
  7. package/src/sap/fe/core/BusyLocker.js +13 -1
  8. package/src/sap/fe/core/CommonUtils.js +129 -219
  9. package/src/sap/fe/core/ExtensionAPI.js +14 -6
  10. package/src/sap/fe/core/PageController.js +9 -35
  11. package/src/sap/fe/core/RouterProxy.js +79 -75
  12. package/src/sap/fe/core/Synchronization.js +1 -1
  13. package/src/sap/fe/core/TemplateComponent.js +16 -7
  14. package/src/sap/fe/core/TemplateModel.js +1 -1
  15. package/src/sap/fe/core/TransactionHelper.js +183 -65
  16. package/src/sap/fe/core/actions/draft.js +50 -23
  17. package/src/sap/fe/core/actions/messageHandling.js +86 -7
  18. package/src/sap/fe/core/actions/nonDraft.js +1 -1
  19. package/src/sap/fe/core/actions/operations.js +75 -33
  20. package/src/sap/fe/core/actions/sticky.js +1 -1
  21. package/src/sap/fe/core/controllerextensions/ControllerExtensionMetadata.js +1 -1
  22. package/src/sap/fe/core/controllerextensions/EditFlow.js +276 -122
  23. package/src/sap/fe/core/controllerextensions/IntentBasedNavigation.js +1 -1
  24. package/src/sap/fe/core/controllerextensions/InternalEditFlow.js +14 -6
  25. package/src/sap/fe/core/controllerextensions/InternalIntentBasedNavigation.js +90 -46
  26. package/src/sap/fe/core/controllerextensions/InternalRouting.js +234 -232
  27. package/src/sap/fe/core/controllerextensions/KPIManagement.js +539 -209
  28. package/src/sap/fe/core/controllerextensions/KPIManagement.ts +550 -213
  29. package/src/sap/fe/core/controllerextensions/MassEdit.js +79 -0
  30. package/src/sap/fe/core/controllerextensions/MessageHandler.js +227 -103
  31. package/src/sap/fe/core/controllerextensions/PageReady.js +2 -2
  32. package/src/sap/fe/core/controllerextensions/PageReady.ts +3 -7
  33. package/src/sap/fe/core/controllerextensions/Paginator.js +38 -12
  34. package/src/sap/fe/core/controllerextensions/Placeholder.js +16 -19
  35. package/src/sap/fe/core/controllerextensions/Routing.js +17 -2
  36. package/src/sap/fe/core/controllerextensions/RoutingListener.js +1 -1
  37. package/src/sap/fe/core/controllerextensions/Share.js +20 -7
  38. package/src/sap/fe/core/controllerextensions/SideEffects.js +8 -9
  39. package/src/sap/fe/core/controllerextensions/SideEffects.ts +19 -17
  40. package/src/sap/fe/core/controllerextensions/ViewState.js +54 -9
  41. package/src/sap/fe/core/controls/ActionParameterDialog.fragment.xml +40 -34
  42. package/src/sap/fe/core/controls/ActionParameterDialogValueHelp.fragment.xml +35 -0
  43. package/src/sap/fe/core/controls/CommandExecution.js +1 -1
  44. package/src/sap/fe/core/controls/ConditionalWrapper.js +1 -1
  45. package/src/sap/fe/core/controls/CustomQuickViewPage.js +110 -109
  46. package/src/sap/fe/core/controls/DataLossOrDraftDiscard/DataLossOrDraftDiscardHandler.js +7 -4
  47. package/src/sap/fe/core/controls/FieldWrapper.js +10 -4
  48. package/src/sap/fe/core/controls/FilterBar.js +1 -1
  49. package/src/sap/fe/core/controls/FormElementWrapper.js +1 -6
  50. package/src/sap/fe/core/controls/MultiValueParameterDelegate.js +45 -0
  51. package/src/sap/fe/core/controls/NonComputedVisibleKeyFieldsDialog.fragment.xml +1 -1
  52. package/src/sap/fe/core/controls/filterbar/FilterContainer.js +1 -1
  53. package/src/sap/fe/core/controls/filterbar/VisualFilter.js +1 -1
  54. package/src/sap/fe/core/controls/filterbar/VisualFilterContainer.js +1 -1
  55. package/src/sap/fe/core/controls/filterbar/utils/VisualFilterUtils.js +1 -1
  56. package/src/sap/fe/core/controls/massEdit/MassEditDialog.fragment.xml +106 -0
  57. package/src/sap/fe/core/controls/massEdit/MassEditHandlers.js +78 -0
  58. package/src/sap/fe/core/converters/ConverterContext.js +13 -1
  59. package/src/sap/fe/core/converters/ConverterContext.ts +10 -0
  60. package/src/sap/fe/core/converters/ManifestSettings.js +2 -1
  61. package/src/sap/fe/core/converters/ManifestSettings.ts +7 -1
  62. package/src/sap/fe/core/converters/ManifestWrapper.js +18 -7
  63. package/src/sap/fe/core/converters/ManifestWrapper.ts +15 -6
  64. package/src/sap/fe/core/converters/MetaModelConverter.js +920 -882
  65. package/src/sap/fe/core/converters/MetaModelConverter.ts +829 -853
  66. package/src/sap/fe/core/converters/annotations/DataField.js +54 -4
  67. package/src/sap/fe/core/converters/annotations/DataField.ts +53 -3
  68. package/src/sap/fe/core/converters/common/AnnotationConverter.js +83 -48
  69. package/src/sap/fe/core/converters/controls/Common/Action.js +15 -3
  70. package/src/sap/fe/core/converters/controls/Common/Action.ts +12 -3
  71. package/src/sap/fe/core/converters/controls/Common/Chart.js +1 -3
  72. package/src/sap/fe/core/converters/controls/Common/Chart.ts +0 -2
  73. package/src/sap/fe/core/converters/controls/Common/Form.js +68 -21
  74. package/src/sap/fe/core/converters/controls/Common/Form.ts +60 -16
  75. package/src/sap/fe/core/converters/controls/Common/KPI.js +331 -248
  76. package/src/sap/fe/core/converters/controls/Common/KPI.ts +355 -287
  77. package/src/sap/fe/core/converters/controls/Common/Table.js +426 -332
  78. package/src/sap/fe/core/converters/controls/Common/Table.ts +516 -383
  79. package/src/sap/fe/core/converters/controls/Common/table/StandardActions.js +468 -0
  80. package/src/sap/fe/core/converters/controls/Common/table/StandardActions.ts +609 -0
  81. package/src/sap/fe/core/converters/controls/ListReport/FilterBar.js +30 -13
  82. package/src/sap/fe/core/converters/controls/ListReport/FilterBar.ts +31 -12
  83. package/src/sap/fe/core/converters/controls/ListReport/VisualFilters.js +2 -2
  84. package/src/sap/fe/core/converters/controls/ListReport/VisualFilters.ts +1 -1
  85. package/src/sap/fe/core/converters/controls/ObjectPage/SubSection.js +15 -7
  86. package/src/sap/fe/core/converters/controls/ObjectPage/SubSection.ts +27 -6
  87. package/src/sap/fe/core/converters/helpers/Aggregation.js +8 -1
  88. package/src/sap/fe/core/converters/helpers/Aggregation.ts +9 -1
  89. package/src/sap/fe/core/converters/helpers/BindingHelper.js +3 -2
  90. package/src/sap/fe/core/converters/helpers/BindingHelper.ts +2 -2
  91. package/src/sap/fe/core/converters/helpers/ID.js +9 -1
  92. package/src/sap/fe/core/converters/helpers/ID.ts +6 -2
  93. package/src/sap/fe/core/converters/helpers/IssueManager.js +7 -5
  94. package/src/sap/fe/core/converters/helpers/IssueManager.ts +6 -4
  95. package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.js +2 -2
  96. package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.ts +1 -1
  97. package/src/sap/fe/core/converters/templates/ListReportConverter.js +34 -4
  98. package/src/sap/fe/core/converters/templates/ListReportConverter.ts +36 -7
  99. package/src/sap/fe/core/converters/templates/ObjectPageConverter.js +2 -5
  100. package/src/sap/fe/core/converters/templates/ObjectPageConverter.ts +1 -2
  101. package/src/sap/fe/core/designtime/AppComponent.designtime.js +11 -2
  102. package/src/sap/fe/core/formatters/FPMFormatter.js +1 -1
  103. package/src/sap/fe/core/formatters/FPMFormatter.ts +8 -3
  104. package/src/sap/fe/core/formatters/TableFormatter.js +27 -4
  105. package/src/sap/fe/core/formatters/TableFormatter.ts +20 -3
  106. package/src/sap/fe/core/formatters/ValueFormatter.js +30 -5
  107. package/src/sap/fe/core/formatters/ValueFormatter.ts +25 -3
  108. package/src/sap/fe/core/fpm/Component.js +2 -2
  109. package/src/sap/fe/core/helpers/AppStartupHelper.js +359 -0
  110. package/src/sap/fe/core/helpers/AppStartupHelper.ts +388 -0
  111. package/src/sap/fe/core/helpers/BindingExpression.js +199 -111
  112. package/src/sap/fe/core/helpers/BindingExpression.ts +193 -102
  113. package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.js +1 -1
  114. package/src/sap/fe/core/helpers/EditState.js +1 -1
  115. package/src/sap/fe/core/helpers/ExcelFormatHelper.js +1 -3
  116. package/src/sap/fe/core/helpers/FPMHelper.js +1 -1
  117. package/src/sap/fe/core/helpers/KeepAliveHelper.js +211 -0
  118. package/src/sap/fe/core/helpers/KeepAliveHelper.ts +202 -0
  119. package/src/sap/fe/core/helpers/KeepAliveRefreshTypes.js +32 -0
  120. package/src/sap/fe/core/helpers/KeepAliveRefreshTypes.ts +36 -0
  121. package/src/sap/fe/core/helpers/MassEditHelper.js +678 -0
  122. package/src/sap/fe/core/helpers/ModelHelper.js +64 -1
  123. package/src/sap/fe/core/helpers/PasteHelper.js +7 -1
  124. package/src/sap/fe/core/helpers/SemanticDateOperators.js +1 -1
  125. package/src/sap/fe/core/helpers/SemanticKeyHelper.js +1 -1
  126. package/src/sap/fe/core/helpers/StableIdHelper.js +2 -2
  127. package/src/sap/fe/core/helpers/StableIdHelper.ts +1 -1
  128. package/src/sap/fe/core/library.js +28 -2
  129. package/src/sap/fe/core/library.support.js +10 -4
  130. package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.js +62 -0
  131. package/src/sap/fe/core/manifestMerger/ChangePageConfiguration.ts +66 -0
  132. package/src/sap/fe/core/messagebundle.properties +67 -3
  133. package/src/sap/fe/core/messagebundle_ar.properties +40 -3
  134. package/src/sap/fe/core/messagebundle_bg.properties +40 -3
  135. package/src/sap/fe/core/messagebundle_ca.properties +41 -4
  136. package/src/sap/fe/core/messagebundle_cs.properties +41 -4
  137. package/src/sap/fe/core/messagebundle_cy.properties +40 -3
  138. package/src/sap/fe/core/messagebundle_da.properties +40 -3
  139. package/src/sap/fe/core/messagebundle_de.properties +40 -3
  140. package/src/sap/fe/core/messagebundle_el.properties +40 -3
  141. package/src/sap/fe/core/messagebundle_en.properties +37 -0
  142. package/src/sap/fe/core/messagebundle_en_GB.properties +37 -0
  143. package/src/sap/fe/core/messagebundle_en_US_sappsd.properties +37 -0
  144. package/src/sap/fe/core/messagebundle_en_US_saprigi.properties +46 -2
  145. package/src/sap/fe/core/messagebundle_en_US_saptrc.properties +44 -0
  146. package/src/sap/fe/core/messagebundle_es.properties +40 -3
  147. package/src/sap/fe/core/messagebundle_es_MX.properties +41 -4
  148. package/src/sap/fe/core/messagebundle_et.properties +40 -3
  149. package/src/sap/fe/core/messagebundle_fi.properties +40 -3
  150. package/src/sap/fe/core/messagebundle_fr.properties +41 -4
  151. package/src/sap/fe/core/messagebundle_fr_CA.properties +40 -3
  152. package/src/sap/fe/core/messagebundle_hi.properties +40 -3
  153. package/src/sap/fe/core/messagebundle_hr.properties +40 -3
  154. package/src/sap/fe/core/messagebundle_hu.properties +41 -4
  155. package/src/sap/fe/core/messagebundle_id.properties +42 -5
  156. package/src/sap/fe/core/messagebundle_it.properties +40 -3
  157. package/src/sap/fe/core/messagebundle_iw.properties +40 -3
  158. package/src/sap/fe/core/messagebundle_ja.properties +49 -12
  159. package/src/sap/fe/core/messagebundle_kk.properties +40 -3
  160. package/src/sap/fe/core/messagebundle_ko.properties +40 -3
  161. package/src/sap/fe/core/messagebundle_lt.properties +40 -3
  162. package/src/sap/fe/core/messagebundle_lv.properties +40 -3
  163. package/src/sap/fe/core/messagebundle_ms.properties +40 -3
  164. package/src/sap/fe/core/messagebundle_nl.properties +40 -3
  165. package/src/sap/fe/core/messagebundle_no.properties +40 -3
  166. package/src/sap/fe/core/messagebundle_pl.properties +40 -3
  167. package/src/sap/fe/core/messagebundle_pt.properties +42 -5
  168. package/src/sap/fe/core/messagebundle_pt_PT.properties +40 -3
  169. package/src/sap/fe/core/messagebundle_ro.properties +40 -3
  170. package/src/sap/fe/core/messagebundle_ru.properties +40 -3
  171. package/src/sap/fe/core/messagebundle_sh.properties +40 -3
  172. package/src/sap/fe/core/messagebundle_sk.properties +40 -3
  173. package/src/sap/fe/core/messagebundle_sl.properties +40 -3
  174. package/src/sap/fe/core/messagebundle_sv.properties +41 -4
  175. package/src/sap/fe/core/messagebundle_th.properties +40 -3
  176. package/src/sap/fe/core/messagebundle_tr.properties +40 -3
  177. package/src/sap/fe/core/messagebundle_uk.properties +40 -3
  178. package/src/sap/fe/core/messagebundle_vi.properties +40 -3
  179. package/src/sap/fe/core/messagebundle_zh_CN.properties +40 -3
  180. package/src/sap/fe/core/messagebundle_zh_TW.properties +40 -3
  181. package/src/sap/fe/core/services/AsyncComponentServiceFactory.js +1 -1
  182. package/src/sap/fe/core/services/CacheHandlerServiceFactory.js +1 -1
  183. package/src/sap/fe/core/services/EnvironmentServiceFactory.js +4 -3
  184. package/src/sap/fe/core/services/EnvironmentServiceFactory.ts +3 -0
  185. package/src/sap/fe/core/services/NavigationServiceFactory.js +1 -1
  186. package/src/sap/fe/core/services/ResourceModelServiceFactory.js +1 -1
  187. package/src/sap/fe/core/services/RoutingServiceFactory.js +98 -328
  188. package/src/sap/fe/core/services/ShellServicesFactory.js +31 -2
  189. package/src/sap/fe/core/services/ShellServicesFactory.ts +30 -1
  190. package/src/sap/fe/core/services/SideEffectsServiceFactory.js +8 -5
  191. package/src/sap/fe/core/services/SideEffectsServiceFactory.ts +11 -6
  192. package/src/sap/fe/core/services/TemplatedViewServiceFactory.js +14 -5
  193. package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.js +1 -1
  194. package/src/sap/fe/core/support/InvalidAnnotationColumnKey.support.js +38 -0
  195. package/src/sap/fe/core/support/InvalidAnnotationColumnKey.support.ts +18 -0
  196. package/src/sap/fe/core/templating/DataModelPathHelper.js +8 -37
  197. package/src/sap/fe/core/templating/DataModelPathHelper.ts +13 -33
  198. package/src/sap/fe/core/templating/DisplayModeFormatter.js +114 -0
  199. package/src/sap/fe/core/templating/DisplayModeFormatter.ts +86 -0
  200. package/src/sap/fe/core/templating/EntitySetHelper.js +86 -3
  201. package/src/sap/fe/core/templating/EntitySetHelper.ts +44 -0
  202. package/src/sap/fe/core/templating/FilterHelper.js +10 -7
  203. package/src/sap/fe/core/templating/FilterHelper.ts +8 -6
  204. package/src/sap/fe/core/templating/PropertyHelper.js +2 -2
  205. package/src/sap/fe/core/templating/PropertyHelper.ts +1 -1
  206. package/src/sap/fe/core/templating/UIFormatters.js +47 -107
  207. package/src/sap/fe/core/templating/UIFormatters.ts +76 -72
@@ -2,53 +2,34 @@ import { ControllerExtension } from "sap/ui/core/mvc";
2
2
  import { ControllerExtensionMetadata } from "sap/fe/core/controllerextensions";
3
3
  import { UI5Class, Override, Public } from "../helpers/ClassSupport";
4
4
  import { JSONModel } from "sap/ui/model/json";
5
+ import { ODataModel, Context } from "sap/ui/model/odata/v4";
5
6
  import { Log } from "sap/base";
6
- import { KPIDefinition } from "sap/fe/core/converters/controls/Common/KPI";
7
- import { CommonUtils } from "sap/fe/core";
8
- import { Context } from "sap/ui/model/odata/v4";
7
+ import { KPIChartDefinition, KPIDefinition, NavigationInfo } from "sap/fe/core/converters/controls/Common/KPI";
8
+ import { BaseController } from "sap/fe/core";
9
9
  import { MessageType } from "sap/fe/core/formatters/TableFormatterTypes";
10
10
  import { FilterDefinition, RangeDefinition } from "sap/fe/core/converters/helpers/SelectionVariantHelper";
11
11
  import { NumberFormat, DateFormat } from "sap/ui/core/format";
12
12
  import { Locale } from "sap/ui/core";
13
- import { Filter, FilterOperator } from "sap/ui/model";
13
+ import { Filter, FilterOperator, Sorter } from "sap/ui/model";
14
14
  import { GenericTag, Popover } from "sap/m";
15
- import { Card } from "sap/ui/integration/widgets";
16
15
 
17
- /**
18
- * Function to calculate a message state from a criticality value.
19
- *
20
- * @param {number|string} criticalityValue The criticality values.
21
- * @returns {MessageType} Returns the corresponding MessageType
22
- */
23
- function messageTypeFromCriticality(criticalityValue: number | string): MessageType {
24
- let criticalityProperty: MessageType;
25
-
26
- switch (criticalityValue) {
27
- case 1:
28
- case "1":
29
- criticalityProperty = MessageType.Error;
30
- break;
31
- case 2:
32
- case "2":
33
- criticalityProperty = MessageType.Warning;
34
- break;
35
- case 3:
36
- case "3":
37
- criticalityProperty = MessageType.Success;
38
- break;
39
- case 5:
40
- case "5":
41
- criticalityProperty = MessageType.Information;
42
- break;
43
- default:
44
- criticalityProperty = MessageType.None;
45
- }
46
-
47
- return criticalityProperty;
48
- }
16
+ const MessageTypeFromCriticality: Record<string, MessageType> = {
17
+ "1": MessageType.Error,
18
+ "2": MessageType.Warning,
19
+ "3": MessageType.Success,
20
+ "5": MessageType.Information
21
+ };
22
+
23
+ const ValueColorFromMessageType: Record<MessageType, string> = {
24
+ Error: "Error",
25
+ Warning: "Critical",
26
+ Success: "Good",
27
+ Information: "None",
28
+ None: "None"
29
+ };
49
30
 
50
31
  /**
51
- * Function to calculate the message state for a criticality calculation of type 'Target'.
32
+ * Function to get a message state from a calculated criticality of type 'Target'.
52
33
  *
53
34
  * @param {number} kpiValue The value of the KPI to be tested against.
54
35
  * @param {number[]} aThresholds Thresholds to be used [DeviationRangeLowValue,ToleranceRangeLowValue,AcceptanceRangeLowValue,AcceptanceRangeHighValue,ToleranceRangeHighValue,DeviationRangeHighValue].
@@ -77,7 +58,7 @@ function messageTypeFromTargetCalculation(kpiValue: number, aThresholds: (number
77
58
  }
78
59
 
79
60
  /**
80
- * Function to calculate the message state for a criticality calculation of type 'Minimize'.
61
+ * Function to get a message state from a calculated criticality of type 'Minimize'.
81
62
  *
82
63
  * @param {number} kpiValue The value of the KPI to be tested against.
83
64
  * @param {number[]} aThresholds Thresholds to be used [AcceptanceRangeHighValue,ToleranceRangeHighValue,DeviationRangeHighValue].
@@ -100,29 +81,7 @@ function messageTypeFromMinimizeCalculation(kpiValue: number, aThresholds: (numb
100
81
  }
101
82
 
102
83
  /**
103
- * Function to transform a MessageType into a ValueColor.
104
- *
105
- * @param messageType The message type
106
- * @returns The ValueColor
107
- */
108
- function valueColorFromMessageType(messageType: MessageType): string {
109
- switch (messageType) {
110
- case MessageType.Error:
111
- return "Error";
112
-
113
- case MessageType.Warning:
114
- return "Critical";
115
-
116
- case MessageType.Success:
117
- return "Good";
118
-
119
- default:
120
- return "None";
121
- }
122
- }
123
-
124
- /**
125
- * Function to calculate the message state for a criticality calculation of type 'Maximize'.
84
+ * Function to get a message state from a calculated criticality of type 'Maximize'.
126
85
  *
127
86
  * @param {number} kpiValue The value of the KPI to be tested against.
128
87
  * @param {number[]} aThresholds Thresholds to be used [DeviationRangeLowValue,ToleranceRangeLowValue,AcceptanceRangeLowValue].
@@ -303,8 +262,299 @@ function getFilterStringFromDefinition(filterDefinition: FilterDefinition): stri
303
262
  }
304
263
  }
305
264
 
265
+ function formatChartTitle(kpiDef: KPIDefinition): string {
266
+ const resBundle = sap.ui.getCore().getLibraryResourceBundle("sap.fe.core");
267
+
268
+ function formatList(items: { name: string; label: string }[]) {
269
+ if (items.length === 0) {
270
+ return "";
271
+ } else if (items.length === 1) {
272
+ return items[0].label;
273
+ } else {
274
+ let res = items[0].label;
275
+ for (let I = 1; I < items.length - 1; I++) {
276
+ res += ", " + items[I].label;
277
+ }
278
+
279
+ return resBundle.getText("C_KPICARD_ITEMSLIST", [res, items[items.length - 1].label]);
280
+ }
281
+ }
282
+
283
+ return resBundle.getText("C_KPICARD_CHARTTITLE", [formatList(kpiDef.chart.measures), formatList(kpiDef.chart.dimensions)]);
284
+ }
285
+
286
+ function updateChartLabelSettings(chartDefinition: KPIChartDefinition, oChartProperties: any): void {
287
+ switch (chartDefinition.chartType) {
288
+ case "Donut":
289
+ // Show data labels, do not show axis titles
290
+ oChartProperties.categoryAxis = {
291
+ title: {
292
+ visible: false
293
+ }
294
+ };
295
+ oChartProperties.valueAxis = {
296
+ title: {
297
+ visible: false
298
+ },
299
+ label: {
300
+ formatString: "ShortFloat"
301
+ }
302
+ };
303
+ oChartProperties.plotArea.dataLabel = {
304
+ visible: true,
305
+ type: "value",
306
+ formatString: "ShortFloat_MFD2"
307
+ };
308
+ break;
309
+
310
+ case "bubble":
311
+ // Show axis title, bubble size legend, do not show data labels
312
+ oChartProperties.valueAxis = {
313
+ title: {
314
+ visible: true
315
+ },
316
+ label: {
317
+ formatString: "ShortFloat"
318
+ }
319
+ };
320
+ oChartProperties.valueAxis2 = {
321
+ title: {
322
+ visible: true
323
+ },
324
+ label: {
325
+ formatString: "ShortFloat"
326
+ }
327
+ };
328
+ oChartProperties.legendGroup = {
329
+ layout: {
330
+ position: "bottom",
331
+ alignment: "topLeft"
332
+ }
333
+ };
334
+ oChartProperties.sizeLegend = {
335
+ visible: true
336
+ };
337
+ oChartProperties.plotArea.dataLabel = { visible: false };
338
+ break;
339
+
340
+ case "scatter":
341
+ // Do not show data labels and axis titles
342
+ oChartProperties.valueAxis = {
343
+ title: {
344
+ visible: false
345
+ },
346
+ label: {
347
+ formatString: "ShortFloat"
348
+ }
349
+ };
350
+ oChartProperties.valueAxis2 = {
351
+ title: {
352
+ visible: false
353
+ },
354
+ label: {
355
+ formatString: "ShortFloat"
356
+ }
357
+ };
358
+ oChartProperties.plotArea.dataLabel = { visible: false };
359
+ break;
360
+
361
+ default:
362
+ // Do not show data labels and axis titles
363
+ oChartProperties.categoryAxis = {
364
+ title: {
365
+ visible: false
366
+ }
367
+ };
368
+ oChartProperties.valueAxis = {
369
+ title: {
370
+ visible: false
371
+ },
372
+ label: {
373
+ formatString: "ShortFloat"
374
+ }
375
+ };
376
+ oChartProperties.plotArea.dataLabel = { visible: false };
377
+ }
378
+ }
379
+ function filterMap(aObjects: { name: string; label: string; role?: string }[], aRoles?: (string | undefined)[]): string[] {
380
+ if (aRoles && aRoles.length) {
381
+ return aObjects
382
+ .filter(dimension => {
383
+ return aRoles.indexOf(dimension.role) >= 0;
384
+ })
385
+ .map(dimension => {
386
+ return dimension.label;
387
+ });
388
+ } else {
389
+ return aObjects.map(dimension => {
390
+ return dimension.label;
391
+ });
392
+ }
393
+ }
394
+
395
+ function getScatterBubbleChartFeeds(chartDefinition: KPIChartDefinition): { uid: string; type: string; values: string[] }[] {
396
+ const axis1Measures = filterMap(chartDefinition.measures, ["Axis1"]);
397
+ const axis2Measures = filterMap(chartDefinition.measures, ["Axis2"]);
398
+ const axis3Measures = filterMap(chartDefinition.measures, ["Axis3"]);
399
+ const otherMeasures = filterMap(chartDefinition.measures, [undefined]);
400
+ const seriesDimensions = filterMap(chartDefinition.dimensions, ["Series"]);
401
+
402
+ // Get the first dimension with role "Category" for the shape
403
+ const shapeDimension = chartDefinition.dimensions.find(dimension => {
404
+ return dimension.role === "Category";
405
+ });
406
+
407
+ // Measure for the x-Axis : first measure for Axis1, or for Axis2 if not found, or for Axis3 if not found
408
+ const xMeasure = axis1Measures.shift() || axis2Measures.shift() || axis3Measures.shift() || otherMeasures.shift() || "";
409
+ // Measure for the y-Axis : first measure for Axis2, or second measure for Axis1 if not found, or first measure for Axis3 if not found
410
+ const yMeasure = axis2Measures.shift() || axis1Measures.shift() || axis3Measures.shift() || otherMeasures.shift() || "";
411
+ const res = [
412
+ {
413
+ "uid": "valueAxis",
414
+ "type": "Measure",
415
+ "values": [xMeasure]
416
+ },
417
+ {
418
+ "uid": "valueAxis2",
419
+ "type": "Measure",
420
+ "values": [yMeasure]
421
+ }
422
+ ];
423
+
424
+ if (chartDefinition.chartType === "bubble") {
425
+ // Measure for the size of the bubble: first measure for Axis3, or remaining measure for Axis1/Axis2 if not found
426
+ const sizeMeasure = axis3Measures.shift() || axis1Measures.shift() || axis2Measures.shift() || otherMeasures.shift() || "";
427
+ res.push({
428
+ "uid": "bubbleWidth",
429
+ "type": "Measure",
430
+ "values": [sizeMeasure]
431
+ });
432
+ }
433
+
434
+ // Color (optional)
435
+ if (seriesDimensions.length) {
436
+ res.push({
437
+ "uid": "color",
438
+ "type": "Dimension",
439
+ "values": seriesDimensions
440
+ });
441
+ }
442
+ // Shape (optional)
443
+ if (shapeDimension) {
444
+ res.push({
445
+ "uid": "shape",
446
+ "type": "Dimension",
447
+ "values": [shapeDimension.label]
448
+ });
449
+ }
450
+ return res;
451
+ }
452
+
453
+ function getChartFeeds(chartDefinition: KPIChartDefinition): { uid: string; type: string; values: string[] }[] {
454
+ let res: { uid: string; type: string; values: string[] }[];
455
+
456
+ switch (chartDefinition.chartType) {
457
+ case "Donut":
458
+ res = [
459
+ {
460
+ "uid": "size",
461
+ "type": "Measure",
462
+ "values": filterMap(chartDefinition.measures)
463
+ },
464
+ {
465
+ "uid": "color",
466
+ "type": "Dimension",
467
+ "values": filterMap(chartDefinition.dimensions)
468
+ }
469
+ ];
470
+ break;
471
+
472
+ case "bubble":
473
+ case "scatter":
474
+ res = getScatterBubbleChartFeeds(chartDefinition);
475
+ break;
476
+
477
+ case "vertical_bullet":
478
+ res = [
479
+ {
480
+ "uid": "actualValues",
481
+ "type": "Measure",
482
+ "values": filterMap(chartDefinition.measures, [undefined, "Axis1"])
483
+ },
484
+ {
485
+ "uid": "targetValues",
486
+ "type": "Measure",
487
+ "values": filterMap(chartDefinition.measures, ["Axis2"])
488
+ },
489
+ {
490
+ "uid": "categoryAxis",
491
+ "type": "Dimension",
492
+ "values": filterMap(chartDefinition.dimensions, [undefined, "Category"])
493
+ },
494
+ {
495
+ "uid": "color",
496
+ "type": "Dimension",
497
+ "values": filterMap(chartDefinition.dimensions, ["Series"])
498
+ }
499
+ ];
500
+ break;
501
+
502
+ default:
503
+ res = [
504
+ {
505
+ "uid": "valueAxis",
506
+ "type": "Measure",
507
+ "values": filterMap(chartDefinition.measures)
508
+ },
509
+ {
510
+ "uid": "categoryAxis",
511
+ "type": "Dimension",
512
+ "values": filterMap(chartDefinition.dimensions, [undefined, "Category"])
513
+ },
514
+ {
515
+ "uid": "color",
516
+ "type": "Dimension",
517
+ "values": filterMap(chartDefinition.dimensions, ["Series"])
518
+ }
519
+ ];
520
+ }
521
+
522
+ return res;
523
+ }
524
+
525
+ function getNavigationParameters(
526
+ navInfo: NavigationInfo,
527
+ oShellService: any
528
+ ): Promise<{ semanticObject?: string; action?: string; outbound?: string } | undefined> {
529
+ if (navInfo.semanticObject) {
530
+ if (navInfo.action) {
531
+ // Action is already specified: check if it's available in the shell
532
+ return oShellService.getLinks({ semanticObject: navInfo.semanticObject, action: navInfo.action }).then((aLinks: any[]) => {
533
+ return aLinks.length ? { semanticObject: navInfo.semanticObject, action: navInfo.action } : undefined;
534
+ });
535
+ } else {
536
+ // We get the primary intent from the shell
537
+ return oShellService.getPrimaryIntent(navInfo.semanticObject).then((oLink: any) => {
538
+ if (!oLink) {
539
+ // No primary intent...
540
+ return undefined;
541
+ }
542
+
543
+ // Check that the primary intent is not part of the unavailable actions
544
+ const oInfo = oShellService.parseShellHash(oLink.intent);
545
+ return navInfo.unavailableActions && navInfo.unavailableActions.indexOf(oInfo.action) >= 0
546
+ ? undefined
547
+ : { semanticObject: oInfo.semanticObject, action: oInfo.action };
548
+ });
549
+ }
550
+ } else {
551
+ // Outbound navigation specified in the manifest
552
+ return navInfo.outboundNavigation ? Promise.resolve({ outbound: navInfo.outboundNavigation }) : Promise.resolve(undefined);
553
+ }
554
+ }
555
+
306
556
  /**
307
- * @class A controller extension for managing KPI in an AnalyticalListPage
557
+ * @class A controller extension for managing the KPIs in an analytical list page
308
558
  *
309
559
  * @name sap.fe.core.controllerextensions.KPIManagement
310
560
  * @hideconstructor
@@ -315,17 +565,131 @@ function getFilterStringFromDefinition(filterDefinition: FilterDefinition): stri
315
565
  @UI5Class("sap.fe.core.controllerextensions.KPIManagement", ControllerExtensionMetadata)
316
566
  class KPIManagementControllerExtension extends ControllerExtension {
317
567
  protected aKPIDefinitions?: KPIDefinition[];
318
- protected oAppComponent!: any;
319
- protected oCard!: Card;
568
+ protected oCard: any;
320
569
  protected oPopover!: Popover;
321
570
 
571
+ /**
572
+ * Creates the card manifest for a KPI definition and stores it in a JSON model.
573
+ *
574
+ * @param kpiDefinition The KPI definition
575
+ * @param oKPIModel The JSON model in which the manifest will be stored
576
+ */
577
+ protected initCardManifest(kpiDefinition: KPIDefinition, oKPIModel: JSONModel<any>): void {
578
+ const oCardManifest: any = {
579
+ "sap.app": {
580
+ id: "sap.fe",
581
+ type: "card"
582
+ },
583
+ "sap.ui": {
584
+ technology: "UI5"
585
+ },
586
+ "sap.card": {
587
+ type: "Analytical",
588
+ data: {
589
+ json: {}
590
+ },
591
+ header: {
592
+ type: "Numeric",
593
+ title: kpiDefinition.datapoint.title,
594
+ subTitle: kpiDefinition.datapoint.description,
595
+ unitOfMeasurement: "{mainUnit}",
596
+ mainIndicator: {
597
+ number: "{mainValueNoScale}",
598
+ unit: "{mainValueScale}",
599
+ state: "{mainState}",
600
+ trend: "{trend}"
601
+ }
602
+ },
603
+ content: {
604
+ minHeight: "25rem",
605
+ chartProperties: {
606
+ plotArea: {},
607
+ title: {
608
+ visible: true,
609
+ alignment: "left"
610
+ }
611
+ },
612
+ data: {
613
+ path: "/chartData"
614
+ }
615
+ }
616
+ }
617
+ };
618
+
619
+ // Add side indicators in the card header if a target is defined for the KPI
620
+ if (kpiDefinition.datapoint.targetPath || kpiDefinition.datapoint.targetValue !== undefined) {
621
+ const resBundle = sap.ui.getCore().getLibraryResourceBundle("sap.fe.core");
622
+ oCardManifest["sap.card"].header.sideIndicators = [
623
+ {
624
+ title: resBundle.getText("C_KPICARD_INDICATOR_TARGET"),
625
+ number: "{targetNumber}",
626
+ unit: "{targetUnit}"
627
+ },
628
+ {
629
+ title: resBundle.getText("C_KPICARD_INDICATOR_DEVIATION"),
630
+ number: "{deviationNumber}",
631
+ unit: "%"
632
+ }
633
+ ];
634
+ }
635
+
636
+ // Details of the card: filter descriptions
637
+ if (kpiDefinition.selectionVariantFilterDefinitions?.length) {
638
+ const aDescriptions: string[] = [];
639
+ kpiDefinition.selectionVariantFilterDefinitions.forEach(filterDefinition => {
640
+ const desc = getFilterStringFromDefinition(filterDefinition);
641
+ if (desc) {
642
+ aDescriptions.push(desc);
643
+ }
644
+ });
645
+
646
+ if (aDescriptions.length) {
647
+ oCardManifest["sap.card"].header.details = aDescriptions.join(", ");
648
+ }
649
+ }
650
+
651
+ // Chart settings: type, title, dimensions and measures in the manifest
652
+ oCardManifest["sap.card"].content.chartType = kpiDefinition.chart.chartType;
653
+ updateChartLabelSettings(kpiDefinition.chart, oCardManifest["sap.card"].content.chartProperties);
654
+ oCardManifest["sap.card"].content.chartProperties.title.text = formatChartTitle(kpiDefinition);
655
+ oCardManifest["sap.card"].content.dimensions = kpiDefinition.chart.dimensions.map(dimension => {
656
+ return { label: dimension.label, value: "{" + dimension.name + "}" };
657
+ });
658
+ oCardManifest["sap.card"].content.measures = kpiDefinition.chart.measures.map(measure => {
659
+ return { label: measure.label, value: "{" + measure.name + "}" };
660
+ });
661
+ oCardManifest["sap.card"].content.feeds = getChartFeeds(kpiDefinition.chart);
662
+
663
+ oKPIModel.setProperty("/" + kpiDefinition.id, {
664
+ manifest: oCardManifest
665
+ });
666
+ }
667
+
668
+ protected initNavigationInfo(kpiDefinition: KPIDefinition, oKPIModel: JSONModel<any>, oShellService: any): Promise<void> {
669
+ // Add navigation
670
+ if (kpiDefinition.navigation) {
671
+ return getNavigationParameters(kpiDefinition.navigation, oShellService).then(oNavInfo => {
672
+ if (oNavInfo) {
673
+ oKPIModel.setProperty("/" + kpiDefinition.id + "/manifest/sap.card/header/actions", [
674
+ {
675
+ type: "Navigation",
676
+ parameters: oNavInfo
677
+ }
678
+ ]);
679
+ }
680
+ });
681
+ } else {
682
+ return Promise.resolve();
683
+ }
684
+ }
685
+
322
686
  @Override()
323
687
  public onInit(): void {
324
688
  this.aKPIDefinitions = this.getKPIData();
325
689
 
326
690
  if (this.aKPIDefinitions && this.aKPIDefinitions.length) {
327
691
  const oView = this.getView();
328
- this.oAppComponent = CommonUtils.getAppComponent(oView);
692
+ const oAppComponent = (oView.getController() as BaseController).getAppComponent() as any;
329
693
 
330
694
  // Create a JSON model to store KPI data
331
695
  const oKPIModel = new JSONModel();
@@ -333,101 +697,15 @@ class KPIManagementControllerExtension extends ControllerExtension {
333
697
 
334
698
  this.aKPIDefinitions.forEach(kpiDefinition => {
335
699
  // Create the manifest for the KPI card and store it in the KPI model
336
- const oCardManifest: any = {
337
- "sap.app": {
338
- id: "sap.fe",
339
- type: "card"
340
- },
341
- "sap.ui": {
342
- technology: "UI5"
343
- },
344
- "sap.card": {
345
- type: "Analytical",
346
- data: {
347
- json: {}
348
- },
349
- header: {
350
- type: "Numeric",
351
- title: kpiDefinition.datapoint.title,
352
- subTitle: kpiDefinition.datapoint.description,
353
- unitOfMeasurement: "{mainUnit}",
354
- mainIndicator: {
355
- number: "{mainValueNoScale}",
356
- unit: "{mainValueScale}",
357
- state: "{mainState}",
358
- trend: "{trend}"
359
- }
360
- },
361
- content: {
362
- plotArea: {
363
- "dataLabel": {
364
- "visible": false
365
- },
366
- "categoryAxisText": {
367
- "visible": false
368
- },
369
- "valueAxisText": {
370
- "visible": false
371
- }
372
- },
373
- title: {
374
- text: "{chartTitle}",
375
- visible: true,
376
- alignment: "Left"
377
- },
378
- measureAxis: "valueAxis",
379
- dimensionAxis: "categoryAxis",
380
- data: {
381
- path: "/chartData"
382
- }
383
- }
384
- }
385
- };
386
-
387
- // Add side indicators in the card header if a target is defined for the KPI
388
- if (kpiDefinition.datapoint.targetPath || kpiDefinition.datapoint.targetValue !== undefined) {
389
- const resBundle = sap.ui.getCore().getLibraryResourceBundle("sap.fe.core");
390
- oCardManifest["sap.card"].header.sideIndicators = [
391
- {
392
- title: resBundle.getText("C_KPICARD_INDICATOR_TARGET"),
393
- number: "{targetNumber}",
394
- unit: "{targetUnit}"
395
- },
396
- {
397
- title: resBundle.getText("C_KPICARD_INDICATOR_DEVIATION"),
398
- number: "{deviationNumber}",
399
- unit: "%"
400
- }
401
- ];
402
- }
403
-
404
- // Details of the card: filter descriptions
405
- if (kpiDefinition.selectionVariantFilterDefinitions?.length) {
406
- const aDescriptions: string[] = [];
407
- kpiDefinition.selectionVariantFilterDefinitions.forEach(filterDefinition => {
408
- const desc = getFilterStringFromDefinition(filterDefinition);
409
- if (desc) {
410
- aDescriptions.push(desc);
411
- }
412
- });
700
+ this.initCardManifest(kpiDefinition, oKPIModel);
413
701
 
414
- if (aDescriptions.length) {
415
- oCardManifest["sap.card"].header.details = aDescriptions.join(", ");
416
- }
417
- }
418
-
419
- // Update card content in the manifest
420
- oCardManifest["sap.card"].content = Object.assign(
421
- oCardManifest["sap.card"].content,
422
- kpiDefinition.card.cardContentManifest
423
- );
424
-
425
- oKPIModel.setProperty("/" + kpiDefinition.id, {
426
- manifest: oCardManifest
702
+ // Set the navigation information in the manifest
703
+ this.initNavigationInfo(kpiDefinition, oKPIModel, oAppComponent.getShellServices()).catch(function(err: any) {
704
+ Log.error(err);
427
705
  });
428
706
 
429
707
  // Load tag data for the KPI
430
- this.loadKPITagData(kpiDefinition).catch(function(err: any) {
708
+ this.loadKPITagData(kpiDefinition, oAppComponent.getModel() as ODataModel, oKPIModel).catch(function(err: any) {
431
709
  Log.error(err);
432
710
  });
433
711
  });
@@ -436,7 +714,7 @@ class KPIManagementControllerExtension extends ControllerExtension {
436
714
 
437
715
  @Override()
438
716
  public onExit(): void {
439
- const oKPIModel = this.getView().getModel("kpiModel") as JSONModel;
717
+ const oKPIModel = this.getView().getModel("kpiModel") as JSONModel<any>;
440
718
 
441
719
  if (oKPIModel) {
442
720
  oKPIModel.destroy();
@@ -459,7 +737,7 @@ class KPIManagementControllerExtension extends ControllerExtension {
459
737
  }
460
738
  }
461
739
 
462
- private updateDatapointValueAndCurrency(kpiDefinition: KPIDefinition, kpiContext: Context) {
740
+ private updateDatapointValueAndCurrency(kpiDefinition: KPIDefinition, kpiContext: Context, oKPIModel: JSONModel<any>) {
463
741
  const currentLocale = new Locale(
464
742
  sap.ui
465
743
  .getCore()
@@ -471,7 +749,6 @@ class KPIManagementControllerExtension extends ControllerExtension {
471
749
  : kpiDefinition.datapoint.unit?.value;
472
750
 
473
751
  const isPercentage = kpiDefinition.datapoint.unit?.isCurrency === false && rawUnit === "%";
474
- const oKPIModel = this.getView().getModel("kpiModel") as JSONModel;
475
752
 
476
753
  // /////////////////////
477
754
  // Main KPI value
@@ -537,9 +814,8 @@ class KPIManagementControllerExtension extends ControllerExtension {
537
814
  }
538
815
  }
539
816
 
540
- private updateDatapointCriticality(kpiDefinition: KPIDefinition, kpiContext: Context) {
817
+ private updateDatapointCriticality(kpiDefinition: KPIDefinition, kpiContext: Context, oKPIModel: JSONModel<any>) {
541
818
  const rawValue = Number.parseFloat(kpiContext.getProperty(kpiDefinition.datapoint.propertyPath));
542
- const oKPIModel = this.getView().getModel("kpiModel") as JSONModel;
543
819
 
544
820
  let criticalityValue = MessageType.None;
545
821
  if (kpiDefinition.datapoint.criticalityValue) {
@@ -547,7 +823,8 @@ class KPIManagementControllerExtension extends ControllerExtension {
547
823
  criticalityValue = kpiDefinition.datapoint.criticalityValue;
548
824
  } else if (kpiDefinition.datapoint.criticalityPath) {
549
825
  // Criticality comes from another property (via a path)
550
- criticalityValue = messageTypeFromCriticality(kpiContext.getProperty(kpiDefinition.datapoint.criticalityPath));
826
+ criticalityValue =
827
+ MessageTypeFromCriticality[kpiContext.getProperty(kpiDefinition.datapoint.criticalityPath)] || MessageType.None;
551
828
  } else if (kpiDefinition.datapoint.criticalityCalculationThresholds && kpiDefinition.datapoint.criticalityCalculationMode) {
552
829
  // Criticality calculation
553
830
  switch (kpiDefinition.datapoint.criticalityCalculationMode) {
@@ -575,13 +852,12 @@ class KPIManagementControllerExtension extends ControllerExtension {
575
852
  oKPIModel.setProperty("/" + kpiDefinition.id + "/manifest/sap.card/data/json/mainCriticality", criticalityValue);
576
853
  oKPIModel.setProperty(
577
854
  "/" + kpiDefinition.id + "/manifest/sap.card/data/json/mainState",
578
- valueColorFromMessageType(criticalityValue)
855
+ ValueColorFromMessageType[criticalityValue] || "None"
579
856
  );
580
857
  }
581
858
 
582
- private updateDatapointTrend(kpiDefinition: KPIDefinition, kpiContext: Context) {
859
+ private updateDatapointTrend(kpiDefinition: KPIDefinition, kpiContext: Context, oKPIModel: JSONModel<any>) {
583
860
  const rawValue = Number.parseFloat(kpiContext.getProperty(kpiDefinition.datapoint.propertyPath));
584
- const oKPIModel = this.getView().getModel("kpiModel") as JSONModel;
585
861
 
586
862
  let trendValue = "None";
587
863
 
@@ -615,12 +891,11 @@ class KPIManagementControllerExtension extends ControllerExtension {
615
891
  oKPIModel.setProperty("/" + kpiDefinition.id + "/manifest/sap.card/data/json/trend", trendValue);
616
892
  }
617
893
 
618
- private updateTargetValue(kpiDefinition: KPIDefinition, kpiContext: Context) {
894
+ private updateTargetValue(kpiDefinition: KPIDefinition, kpiContext: Context, oKPIModel: JSONModel<any>) {
619
895
  if (kpiDefinition.datapoint.targetValue === undefined && kpiDefinition.datapoint.targetPath === undefined) {
620
896
  return; // No target set for the KPI
621
897
  }
622
898
  const rawValue = Number.parseFloat(kpiContext.getProperty(kpiDefinition.datapoint.propertyPath));
623
- const oKPIModel = this.getView().getModel("kpiModel") as JSONModel;
624
899
  const currentLocale = new Locale(
625
900
  sap.ui
626
901
  .getCore()
@@ -678,13 +953,17 @@ class KPIManagementControllerExtension extends ControllerExtension {
678
953
  * Loads tag data for a KPI, and stores it in the JSON KPI model.
679
954
  *
680
955
  * @param {KPIDefinition} kpiDefinition The definition of the KPI.
956
+ * @param {ODataModel} oMainModel The model used to load the data.
957
+ * @param {JSONModel} oKPIModel The JSON model where the data will be stored
681
958
  * @param loadFull If not true, loads only data for the KPI tag
682
- * @returns {Promise} Returns the Promise that is resolved when data is loaded.
959
+ * @returns {Promise} Returns the promise that is resolved when data is loaded.
683
960
  */
684
- protected loadKPITagData(kpiDefinition: KPIDefinition, loadFull?: boolean): any {
685
- const oModel = this.oAppComponent.getModel();
686
- const oKPIModel = this.getView().getModel("kpiModel") as JSONModel;
687
- const oListBinding = oModel.bindList("/" + kpiDefinition.entitySet);
961
+ protected loadKPITagData(kpiDefinition: KPIDefinition, oMainModel: ODataModel, oKPIModel: JSONModel<any>, loadFull?: boolean): any {
962
+ // If loadFull=false, then we're just loading data for the tag and we use the "$auto.LongRunners" groupID
963
+ // If loadFull=true, we're loading data for the whole KPI (tag + card) and we use the default ("$auto") groupID
964
+ const oListBinding = loadFull
965
+ ? oMainModel.bindList("/" + kpiDefinition.entitySet)
966
+ : oMainModel.bindList("/" + kpiDefinition.entitySet, undefined, undefined, undefined, { $$groupId: "$auto.LongRunners" });
688
967
  const oAggregate: Record<string, { unit?: string }> = {};
689
968
 
690
969
  // Main value + currency/unit
@@ -716,7 +995,9 @@ class KPIManagementControllerExtension extends ControllerExtension {
716
995
 
717
996
  // Manage SelectionVariant filters
718
997
  if (kpiDefinition.selectionVariantFilterDefinitions?.length) {
719
- const aFilters = kpiDefinition.selectionVariantFilterDefinitions.map(createFilterFromDefinition);
998
+ const aFilters = kpiDefinition.selectionVariantFilterDefinitions.map(createFilterFromDefinition).filter(filter => {
999
+ return filter !== undefined;
1000
+ }) as Filter[];
720
1001
  oListBinding.filter(aFilters);
721
1002
  }
722
1003
 
@@ -740,30 +1021,36 @@ class KPIManagementControllerExtension extends ControllerExtension {
740
1021
  oKPIModel.setProperty("/" + kpiDefinition.id + "/manifest/sap.card/data/json/targetUnit", undefined);
741
1022
  oKPIModel.setProperty("/" + kpiDefinition.id + "/manifest/sap.card/data/json/deviationNumber", undefined);
742
1023
  } else {
743
- this.updateDatapointValueAndCurrency(kpiDefinition, aContexts[0]);
744
- this.updateDatapointCriticality(kpiDefinition, aContexts[0]);
1024
+ this.updateDatapointValueAndCurrency(kpiDefinition, aContexts[0], oKPIModel);
1025
+ this.updateDatapointCriticality(kpiDefinition, aContexts[0], oKPIModel);
745
1026
 
746
1027
  if (loadFull) {
747
- this.updateDatapointTrend(kpiDefinition, aContexts[0]);
748
- this.updateTargetValue(kpiDefinition, aContexts[0]);
1028
+ this.updateDatapointTrend(kpiDefinition, aContexts[0], oKPIModel);
1029
+ this.updateTargetValue(kpiDefinition, aContexts[0], oKPIModel);
749
1030
  }
750
1031
  }
751
1032
  }
752
1033
  });
753
1034
  }
754
1035
 
755
- protected loadKPICardData(kpiDefinition: KPIDefinition): any {
756
- const oModel = this.oAppComponent.getModel();
757
- const oListBinding = oModel.bindList("/" + kpiDefinition.entitySet);
1036
+ /**
1037
+ * Loads card data for a KPI, and stores it in the JSON KPI model.
1038
+ *
1039
+ * @param {KPIDefinition} kpiDefinition The definition of the KPI.
1040
+ * @param {ODataModel} oMainModel The model used to load the data.
1041
+ * @param {JSONModel} oKPIModel The JSON model where the data will be stored
1042
+ * @returns {Promise} Returns the promise that is resolved when data is loaded.
1043
+ */
1044
+ protected loadKPICardData(kpiDefinition: KPIDefinition, oMainModel: ODataModel, oKPIModel: JSONModel<any>): any {
1045
+ const oListBinding = oMainModel.bindList("/" + kpiDefinition.entitySet);
758
1046
  const oGroup: Record<string, Object> = {};
759
1047
  const oAggregate: Record<string, Object> = {};
760
- const oKPIModel = this.getView().getModel("kpiModel") as JSONModel;
761
1048
 
762
- kpiDefinition.card.dimensions.forEach(function(sDimensionName) {
763
- oGroup[sDimensionName] = {};
1049
+ kpiDefinition.chart.dimensions.forEach(dimension => {
1050
+ oGroup[dimension.name] = {};
764
1051
  });
765
- kpiDefinition.card.measures.forEach(function(sMeasureName) {
766
- oAggregate[sMeasureName] = {};
1052
+ kpiDefinition.chart.measures.forEach(measure => {
1053
+ oAggregate[measure.name] = {};
767
1054
  });
768
1055
  oListBinding.setAggregation({
769
1056
  group: oGroup,
@@ -772,18 +1059,29 @@ class KPIManagementControllerExtension extends ControllerExtension {
772
1059
 
773
1060
  // Manage SelectionVariant filters
774
1061
  if (kpiDefinition.selectionVariantFilterDefinitions?.length) {
775
- const aFilters = kpiDefinition.selectionVariantFilterDefinitions.map(createFilterFromDefinition);
1062
+ const aFilters = kpiDefinition.selectionVariantFilterDefinitions.map(createFilterFromDefinition).filter(filter => {
1063
+ return filter !== undefined;
1064
+ }) as Filter[];
776
1065
  oListBinding.filter(aFilters);
777
1066
  }
778
1067
 
779
- return oListBinding.requestContexts(0).then((aContexts: Context[]) => {
1068
+ // Sorting
1069
+ if (kpiDefinition.chart.sortOrder) {
1070
+ oListBinding.sort(
1071
+ kpiDefinition.chart.sortOrder.map(sortInfo => {
1072
+ return new Sorter(sortInfo.name, sortInfo.descending);
1073
+ })
1074
+ );
1075
+ }
1076
+
1077
+ return oListBinding.requestContexts(0, kpiDefinition.chart.maxItems).then((aContexts: Context[]) => {
780
1078
  const chartData = aContexts.map(function(oContext) {
781
1079
  const oData: Record<string, any> = {};
782
- kpiDefinition.card.dimensions.forEach(function(sDimensionName) {
783
- oData[sDimensionName] = oContext.getProperty(sDimensionName);
1080
+ kpiDefinition.chart.dimensions.forEach(dimension => {
1081
+ oData[dimension.name] = oContext.getProperty(dimension.name);
784
1082
  });
785
- kpiDefinition.card.measures.forEach(function(sMeasureName) {
786
- oData[sMeasureName] = oContext.getProperty(sMeasureName);
1083
+ kpiDefinition.chart.measures.forEach(measure => {
1084
+ oData[measure.name] = oContext.getProperty(measure.name);
787
1085
  });
788
1086
 
789
1087
  return oData;
@@ -796,34 +1094,67 @@ class KPIManagementControllerExtension extends ControllerExtension {
796
1094
  /**
797
1095
  * Gets the popover to display the KPI card
798
1096
  * The popover and the contained card for the KPIs are created if necessary.
799
- * The popover is shared between all KPIs, so we create it only once.
1097
+ * The popover is shared between all KPIs, so it's created only once.
800
1098
  *
801
- * @param oKPITag The Tag that triggered the popover opening.
802
- * @returns The shared popover.
1099
+ * @param oKPITag The tag that triggered the popover opening.
1100
+ * @returns The shared popover as a promise.
803
1101
  */
804
- protected getPopover(oKPITag: GenericTag): Popover {
1102
+ protected getPopover(oKPITag: GenericTag): Promise<Popover> {
805
1103
  if (!this.oPopover) {
806
- this.oCard = new Card({
807
- width: "25rem",
808
- height: "auto"
809
- });
1104
+ return new Promise((resolve, reject) => {
1105
+ sap.ui
1106
+ .getCore()
1107
+ .loadLibrary("sap/ui/integration", { async: true })
1108
+ .then(() => {
1109
+ sap.ui.require(["sap/ui/integration/widgets/Card", "sap/ui/integration/Host"], (Card: any, Host: any) => {
1110
+ const oHost = new Host();
1111
+
1112
+ oHost.attachAction((oEvent: any) => {
1113
+ const sType = oEvent.getParameter("type");
1114
+ const oParams = oEvent.getParameter("parameters");
1115
+
1116
+ if (sType === "Navigation") {
1117
+ if (oParams.semanticObject) {
1118
+ (this.getView().getController() as any)._intentBasedNavigation.navigate(
1119
+ oParams.semanticObject,
1120
+ oParams.action
1121
+ );
1122
+ } else {
1123
+ (this.getView().getController() as any)._intentBasedNavigation.navigateOutbound(oParams.outbound);
1124
+ }
1125
+ }
1126
+ });
810
1127
 
811
- this.oPopover = new Popover({
812
- id: "kpi-Popover",
813
- showHeader: false,
814
- placement: "Auto",
815
- content: [this.oCard]
816
- });
1128
+ this.oCard = new Card({
1129
+ width: "25rem",
1130
+ height: "auto"
1131
+ });
1132
+ this.oCard.setHost(oHost as any);
817
1133
 
818
- oKPITag.addDependent(this.oPopover); // The first clicked tag gets the popover as dependent
819
- }
1134
+ this.oPopover = new Popover({
1135
+ id: "kpi-Popover",
1136
+ showHeader: false,
1137
+ placement: "Auto",
1138
+ content: [this.oCard]
1139
+ });
1140
+
1141
+ oKPITag.addDependent(this.oPopover); // The first clicked tag gets the popover as dependent
820
1142
 
821
- return this.oPopover;
1143
+ resolve(this.oPopover);
1144
+ });
1145
+ })
1146
+ .catch(function() {
1147
+ reject();
1148
+ });
1149
+ });
1150
+ } else {
1151
+ return Promise.resolve(this.oPopover);
1152
+ }
822
1153
  }
823
1154
 
824
1155
  @Public
825
1156
  public onKPIPressed(oKPITag: any, kpiID: string): void {
826
- const oKPIModel = oKPITag.getModel("kpiModel") as JSONModel;
1157
+ const oKPIModel = oKPITag.getModel("kpiModel") as JSONModel<any>;
827
1158
 
828
1159
  if (this.aKPIDefinitions && this.aKPIDefinitions.length) {
829
1160
  const kpiDefinition = this.aKPIDefinitions.find(function(oDef) {
@@ -831,13 +1162,19 @@ class KPIManagementControllerExtension extends ControllerExtension {
831
1162
  });
832
1163
 
833
1164
  if (kpiDefinition) {
834
- const aPromises = [this.loadKPITagData(kpiDefinition, true), this.loadKPICardData(kpiDefinition)];
835
- const oPopover = this.getPopover(oKPITag);
1165
+ const oModel = oKPITag.getModel();
1166
+ const aPromises = [
1167
+ this.loadKPITagData(kpiDefinition, oModel, oKPIModel, true),
1168
+ this.loadKPICardData(kpiDefinition, oModel, oKPIModel),
1169
+ this.getPopover(oKPITag)
1170
+ ];
836
1171
 
837
1172
  Promise.all(aPromises)
838
- .then(() => {
1173
+ .then(aResults => {
839
1174
  this.oCard.setManifest(oKPIModel.getProperty("/" + kpiID + "/manifest"));
840
1175
  this.oCard.refresh();
1176
+
1177
+ const oPopover = aResults[2];
841
1178
  oPopover.openBy(oKPITag, false);
842
1179
  })
843
1180
  .catch(err => {