@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
@@ -3,11 +3,20 @@ import { Property } from "@sap-ux/annotation-converter";
3
3
  import {
4
4
  AnnotationTerm,
5
5
  UIAnnotationTerms,
6
+ UIAnnotationTypes,
6
7
  PathAnnotationExpression,
7
8
  CriticalityType,
8
9
  ImprovementDirectionType
9
10
  } from "@sap-ux/vocabularies-types";
10
- import { KPIType, PresentationVariant, ChartDefinitionTypeTypes, DataPoint, TrendType } from "@sap-ux/vocabularies-types/dist/generated/UI";
11
+ import {
12
+ KPIType,
13
+ SelectionPresentationVariantType,
14
+ PresentationVariant,
15
+ SelectionVariant,
16
+ DataPoint,
17
+ Chart,
18
+ TrendType
19
+ } from "@sap-ux/vocabularies-types/dist/generated/UI";
11
20
  import { PropertyPath } from "@sap-ux/vocabularies-types/dist/Edm";
12
21
  import ConverterContext from "sap/fe/core/converters/ConverterContext";
13
22
  import { KPIID } from "../../helpers/ID";
@@ -17,15 +26,21 @@ import { AggregationHelper } from "../../helpers/Aggregation";
17
26
  import { IssueCategory, IssueSeverity, IssueType } from "sap/fe/core/converters/helpers/IssueManager";
18
27
  import { getMessageTypeFromCriticalityType } from "./Criticality";
19
28
  import { getFilterDefinitionsFromSelectionVariant, FilterDefinition } from "sap/fe/core/converters/helpers/SelectionVariantHelper";
29
+ import { CommonAnnotationTerms, SemanticObject } from "@sap-ux/vocabularies-types/dist/generated/Common";
20
30
 
21
- type KPICardDefinition = {
22
- dimensions: string[];
23
- measures: string[];
24
- cardContentManifest: {
25
- chartType: string;
26
- dimensions: { label: string; value: string }[];
27
- measures: { label: string; value: string }[];
28
- };
31
+ export type NavigationInfo = {
32
+ semanticObject?: string;
33
+ action?: string;
34
+ unavailableActions?: string[];
35
+ outboundNavigation?: string;
36
+ };
37
+
38
+ export type KPIChartDefinition = {
39
+ chartType: string;
40
+ dimensions: { name: string; label: string; role?: string }[];
41
+ measures: { name: string; label: string; role?: string }[];
42
+ sortOrder?: { name: string; descending: boolean }[];
43
+ maxItems?: number;
29
44
  };
30
45
 
31
46
  export type KPIDefinition = {
@@ -54,86 +69,244 @@ export type KPIDefinition = {
54
69
  targetValue?: number;
55
70
  targetPath?: string;
56
71
  };
57
- card: KPICardDefinition;
72
+ chart: KPIChartDefinition;
58
73
  selectionVariantFilterDefinitions?: FilterDefinition[];
74
+ navigation?: NavigationInfo;
59
75
  };
60
76
 
61
- /**
62
- * Gets a DeviationIndicator enum value from a TrendType enum value.
63
- *
64
- * @param {TrendType} trendEnum The trend enum value
65
- * @returns {DeviationIndicator} Returns the DeviationIndicator enum value
66
- */
67
- function getDeviationIndicatorFromTrendType(trendEnum: TrendType): string {
68
- let deviationIndicator: string;
69
- switch (trendEnum) {
70
- case "UI.TrendType/StrongUp":
71
- case "UI.TrendType/Up":
72
- deviationIndicator = "Up";
73
- break;
74
- case "UI.TrendType/StrongDown":
75
- case "UI.TrendType/Down":
76
- deviationIndicator = "Down";
77
- break;
78
- default:
79
- deviationIndicator = "None";
77
+ const DeviationIndicatorFromTrendType: Record<string, string> = {
78
+ "UI.TrendType/StrongUp": "Up",
79
+ "UI.TrendType/Up": "Up",
80
+ "UI.TrendType/StrongDown": "Down",
81
+ "UI.TrendType/Down": "Down",
82
+ "UI.TrendType/Sideways": "None"
83
+ };
84
+
85
+ const KPIChartTypeFromUI: Record<string, string> = {
86
+ "UI.ChartType/ColumnStacked": "StackedColumn",
87
+ "UI.ChartType/BarStacked": "StackedBar",
88
+ "UI.ChartType/Donut": "Donut",
89
+ "UI.ChartType/Line": "Line",
90
+ "UI.ChartType/Bubble": "bubble",
91
+ "UI.ChartType/Column": "column",
92
+ "UI.ChartType/Bar": "bar",
93
+ "UI.ChartType/VerticalBullet": "vertical_bullet",
94
+ "UI.ChartType/Combination": "combination",
95
+ "UI.ChartType/Scatter": "scatter"
96
+ };
97
+
98
+ function convertKPIChart(chartAnnotation: Chart, presentationVariantAnnotation: PresentationVariant): KPIChartDefinition | undefined {
99
+ if (chartAnnotation.Measures === undefined) {
100
+ // We need at least 1 measure (but no dimension is allowed, e.g. for bubble chart)
101
+ return undefined;
80
102
  }
81
- return deviationIndicator;
82
- }
83
103
 
84
- function convertKPICardContent(
85
- datapointAnnotation: DataPoint,
86
- presentationVariantAnnotation?: PresentationVariant
87
- ): KPICardDefinition | undefined {
88
- // Find the chart visualization
89
- const chart = presentationVariantAnnotation?.Visualizations?.find((viz: any) => {
90
- return viz.$target.$Type === "com.sap.vocabularies.UI.v1.ChartDefinitionType";
104
+ const charDimensions = chartAnnotation.Dimensions
105
+ ? (chartAnnotation.Dimensions as PropertyPath[]).map(propertyPath => {
106
+ const dimAttribute = chartAnnotation.DimensionAttributes?.find(attribute => {
107
+ return attribute.Dimension?.value === propertyPath.value;
108
+ });
109
+ return {
110
+ name: propertyPath.value,
111
+ label: propertyPath.$target.annotations.Common?.Label?.toString() || propertyPath.value,
112
+ role: dimAttribute?.Role?.replace("UI.ChartDimensionRoleType/", "")
113
+ };
114
+ })
115
+ : [];
116
+
117
+ const chartMeasures = (chartAnnotation.Measures as PropertyPath[]).map(propertyPath => {
118
+ const measureAttribute = chartAnnotation.MeasureAttributes?.find(attribute => {
119
+ return attribute.Measure?.value === propertyPath.value;
120
+ });
121
+ return {
122
+ name: propertyPath.value,
123
+ label: propertyPath.$target.annotations.Common?.Label?.toString() || propertyPath.value,
124
+ role: measureAttribute?.Role?.replace("UI.ChartMeasureRoleType/", "")
125
+ };
91
126
  });
92
127
 
93
- if (chart) {
94
- const chartAnnotation = chart.$target as ChartDefinitionTypeTypes;
128
+ return {
129
+ chartType: KPIChartTypeFromUI[chartAnnotation.ChartType] || "Line",
130
+ dimensions: charDimensions,
131
+ measures: chartMeasures,
132
+ sortOrder: presentationVariantAnnotation?.SortOrder?.map(sortOrder => {
133
+ return { name: sortOrder.Property?.value || "", descending: !!sortOrder.Descending };
134
+ }),
135
+ maxItems: presentationVariantAnnotation?.MaxItems?.valueOf() as number
136
+ };
137
+ }
95
138
 
96
- let chartType: string;
97
- switch (chartAnnotation.ChartType) {
98
- case "UI.ChartType/ColumnStacked":
99
- chartType = "StackedColumn";
100
- break;
139
+ function updateCurrency(datapointAnnotation: DataPoint, kpiDef: KPIDefinition): void {
140
+ const targetValueProperty = datapointAnnotation.Value.$target as Property;
141
+ if (targetValueProperty.annotations.Measures?.ISOCurrency) {
142
+ const currency = targetValueProperty.annotations.Measures?.ISOCurrency;
143
+ if (isPathExpression(currency)) {
144
+ kpiDef.datapoint.unit = {
145
+ value: ((currency.$target as unknown) as Property).name,
146
+ isCurrency: true,
147
+ isPath: true
148
+ };
149
+ } else {
150
+ kpiDef.datapoint.unit = {
151
+ value: currency.toString(),
152
+ isCurrency: true,
153
+ isPath: false
154
+ };
155
+ }
156
+ } else if (targetValueProperty.annotations.Measures?.Unit) {
157
+ const unit = targetValueProperty.annotations.Measures?.Unit;
158
+ if (isPathExpression(unit)) {
159
+ kpiDef.datapoint.unit = {
160
+ value: ((unit.$target as unknown) as Property).name,
161
+ isCurrency: false,
162
+ isPath: true
163
+ };
164
+ } else {
165
+ kpiDef.datapoint.unit = {
166
+ value: unit.toString(),
167
+ isCurrency: false,
168
+ isPath: false
169
+ };
170
+ }
171
+ }
172
+ }
101
173
 
102
- case "UI.ChartType/BarStacked":
103
- chartType = "StackedBar";
174
+ function updateCriticality(datapointAnnotation: DataPoint, aggregationHelper: AggregationHelper, kpiDef: KPIDefinition): void {
175
+ if (datapointAnnotation.Criticality) {
176
+ if (typeof datapointAnnotation.Criticality === "object") {
177
+ // Criticality is a path --> check if the corresponding property is aggregatable
178
+ const criticalityProperty = (datapointAnnotation.Criticality as PathAnnotationExpression<CriticalityType>).$target as Property;
179
+ if (aggregationHelper.isPropertyAggregatable(criticalityProperty)) {
180
+ kpiDef.datapoint.criticalityPath = (datapointAnnotation.Criticality as PathAnnotationExpression<CriticalityType>).path;
181
+ } else {
182
+ // The property isn't aggregatable --> we ignore it
183
+ kpiDef.datapoint.criticalityValue = MessageType.None;
184
+ }
185
+ } else {
186
+ // Criticality is an enum Value --> get the corresponding static value
187
+ kpiDef.datapoint.criticalityValue = getMessageTypeFromCriticalityType(datapointAnnotation.Criticality);
188
+ }
189
+ } else if (datapointAnnotation.CriticalityCalculation) {
190
+ kpiDef.datapoint.criticalityCalculationMode = datapointAnnotation.CriticalityCalculation.ImprovementDirection;
191
+ kpiDef.datapoint.criticalityCalculationThresholds = [];
192
+ switch (kpiDef.datapoint.criticalityCalculationMode) {
193
+ case "UI.ImprovementDirectionType/Target":
194
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.DeviationRangeLowValue);
195
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.ToleranceRangeLowValue);
196
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.AcceptanceRangeLowValue);
197
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.AcceptanceRangeHighValue);
198
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.ToleranceRangeHighValue);
199
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.DeviationRangeHighValue);
104
200
  break;
105
201
 
106
- case "UI.ChartType/Donut":
107
- chartType = "Donut";
202
+ case "UI.ImprovementDirectionType/Minimize":
203
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.AcceptanceRangeHighValue);
204
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.ToleranceRangeHighValue);
205
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.DeviationRangeHighValue);
108
206
  break;
109
207
 
208
+ case "UI.ImprovementDirectionType/Maximize":
110
209
  default:
111
- chartType = "Line";
210
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.DeviationRangeLowValue);
211
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.ToleranceRangeLowValue);
212
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.AcceptanceRangeLowValue);
112
213
  }
214
+ } else {
215
+ kpiDef.datapoint.criticalityValue = MessageType.None;
216
+ }
217
+ }
113
218
 
114
- if (!chartAnnotation.Dimensions || !chartAnnotation.Measures) {
115
- return undefined;
219
+ function updateTrend(datapointAnnotation: DataPoint, aggregationHelper: AggregationHelper, kpiDef: KPIDefinition): void {
220
+ if (datapointAnnotation.Trend) {
221
+ if (typeof datapointAnnotation.Trend === "object") {
222
+ // Trend is a path --> check if the corresponding property is aggregatable
223
+ const trendProperty = (datapointAnnotation.Trend as PathAnnotationExpression<TrendType>).$target as Property;
224
+ if (aggregationHelper.isPropertyAggregatable(trendProperty)) {
225
+ kpiDef.datapoint.trendPath = (datapointAnnotation.Trend as PathAnnotationExpression<TrendType>).path;
226
+ } else {
227
+ // The property isn't aggregatable --> we ignore it
228
+ kpiDef.datapoint.trendValue = "None";
229
+ }
116
230
  } else {
117
- return {
118
- dimensions: (chartAnnotation.Dimensions as PropertyPath[]).map(propertyPath => propertyPath.value),
119
- measures: (chartAnnotation.Measures as PropertyPath[]).map(propertyPath => propertyPath.value),
120
- cardContentManifest: {
121
- chartType: chartType,
122
- dimensions: (chartAnnotation.Dimensions as PropertyPath[]).map(propertyPath => {
123
- return {
124
- label: propertyPath.$target.annotations.Common?.Label?.toString() || propertyPath.value,
125
- value: "{" + propertyPath.value + "}"
126
- };
127
- }),
128
- measures: (chartAnnotation.Measures as PropertyPath[]).map(propertyPath => {
129
- return {
130
- label: propertyPath.$target.annotations.Common?.Label?.toString() || propertyPath.value,
131
- value: "{" + propertyPath.value + "}"
132
- };
133
- })
134
- }
135
- };
231
+ // Trend is an enum Value --> get the corresponding static value
232
+ kpiDef.datapoint.trendValue = DeviationIndicatorFromTrendType[datapointAnnotation.Trend] || "None";
233
+ }
234
+ } else if (datapointAnnotation.TrendCalculation) {
235
+ kpiDef.datapoint.trendCalculationIsRelative = datapointAnnotation.TrendCalculation.IsRelativeDifference ? true : false;
236
+ if (datapointAnnotation.TrendCalculation.ReferenceValue.$target) {
237
+ // Reference value is a path --> check if the corresponding property is aggregatable
238
+ const referenceProperty = datapointAnnotation.TrendCalculation.ReferenceValue.$target as Property;
239
+ if (aggregationHelper.isPropertyAggregatable(referenceProperty)) {
240
+ kpiDef.datapoint.trendCalculationReferencePath = datapointAnnotation.TrendCalculation.ReferenceValue.path;
241
+ } else {
242
+ // The property isn't aggregatable --> we ignore it and switch back to trend 'None'
243
+ kpiDef.datapoint.trendValue = "None";
244
+ }
245
+ } else {
246
+ // Reference value is a static value
247
+ kpiDef.datapoint.trendCalculationReferenceValue = datapointAnnotation.TrendCalculation.ReferenceValue;
248
+ }
249
+ if (kpiDef.datapoint.trendCalculationReferencePath !== undefined || kpiDef.datapoint.trendCalculationReferenceValue !== undefined) {
250
+ kpiDef.datapoint.trendCalculationTresholds = [
251
+ datapointAnnotation.TrendCalculation.StrongDownDifference.valueOf() as number,
252
+ datapointAnnotation.TrendCalculation.DownDifference.valueOf() as number,
253
+ datapointAnnotation.TrendCalculation.UpDifference.valueOf() as number,
254
+ datapointAnnotation.TrendCalculation.StrongUpDifference.valueOf() as number
255
+ ];
256
+ }
257
+ } else {
258
+ kpiDef.datapoint.trendValue = "None";
259
+ }
260
+ }
261
+
262
+ function updateTarget(datapointAnnotation: DataPoint, aggregationHelper: AggregationHelper, kpiDef: KPIDefinition): void {
263
+ if (datapointAnnotation.TargetValue) {
264
+ if (datapointAnnotation.TargetValue.$target) {
265
+ // Target value is a path --> check if the corresponding property is aggregatable (otherwise ignore)
266
+ const targetProperty = datapointAnnotation.TargetValue.$target as Property;
267
+ if (aggregationHelper.isPropertyAggregatable(targetProperty)) {
268
+ kpiDef.datapoint.targetPath = datapointAnnotation.TargetValue.path;
269
+ }
270
+ } else {
271
+ // Target value is a static value
272
+ kpiDef.datapoint.targetValue = datapointAnnotation.TargetValue;
273
+ }
274
+ }
275
+ }
276
+
277
+ function getNavigationInfoFromProperty(property: Property): NavigationInfo | undefined {
278
+ const annotations: Record<string, AnnotationTerm<any>> = property.annotations["Common"] || {};
279
+
280
+ // Look for the semanticObject annotation (if any)
281
+ let semanticObjectAnnotation: AnnotationTerm<SemanticObject> | undefined;
282
+ Object.keys(annotations).forEach(annotationKey => {
283
+ const annotation = annotations[annotationKey];
284
+ if (annotation.term === CommonAnnotationTerms.SemanticObject) {
285
+ if (!annotation.qualifier || !semanticObjectAnnotation) {
286
+ // We always take the annotation without qualifier if there's one, otherwise we take the first one
287
+ semanticObjectAnnotation = annotation;
288
+ }
289
+ }
290
+ });
291
+
292
+ if (semanticObjectAnnotation) {
293
+ const result = {
294
+ semanticObject: semanticObjectAnnotation.toString(),
295
+ unavailableActions: []
296
+ };
297
+
298
+ // Look for the unavailable actions (if any)
299
+ const annotationKey = Object.keys(annotations).find(key => {
300
+ return (
301
+ annotations[key].term === CommonAnnotationTerms.SemanticObjectUnavailableActions &&
302
+ annotations[key].qualifier === semanticObjectAnnotation?.qualifier
303
+ );
304
+ });
305
+ if (annotationKey) {
306
+ result.unavailableActions = annotations[annotationKey];
136
307
  }
308
+
309
+ return result;
137
310
  } else {
138
311
  return undefined;
139
312
  }
@@ -141,241 +314,136 @@ function convertKPICardContent(
141
314
 
142
315
  function createKPIDefinition(kpiName: string, kpiConfig: KPIConfiguration, converterContext: ConverterContext): KPIDefinition | undefined {
143
316
  const kpiConverterContext = converterContext.getConverterContextFor("/" + kpiConfig.entitySet);
317
+ const aggregationHelper = new AggregationHelper(kpiConverterContext.getEntityType(), kpiConverterContext);
318
+
319
+ if (!aggregationHelper.isAnalyticsSupported()) {
320
+ // The entity doesn't support analytical queries
321
+ converterContext
322
+ .getDiagnostics()
323
+ .addIssue(IssueCategory.Annotation, IssueSeverity.Medium, IssueType.KPI_ISSUES.NO_ANALYTICS + kpiConfig.entitySet);
324
+
325
+ return undefined;
326
+ }
327
+
328
+ let selectionVariantAnnotation: SelectionVariant | undefined;
329
+ let datapointAnnotation: DataPoint | undefined;
330
+ let presentationVariantAnnotation: PresentationVariant | undefined;
331
+ let chartAnnotation: Chart | undefined;
332
+ let navigationInfo: NavigationInfo | undefined;
333
+
334
+ // Search for a KPI with the qualifier frmo the manifest
144
335
  const aKPIAnnotations = kpiConverterContext.getAnnotationsByTerm("UI", UIAnnotationTerms.KPI) as AnnotationTerm<KPIType>[];
145
336
  const targetKPI = aKPIAnnotations.find(kpi => {
146
337
  return kpi.qualifier === kpiConfig.qualifier;
147
338
  });
148
- const aggregationHelper = new AggregationHelper(kpiConverterContext.getEntityType(), kpiConverterContext);
339
+ if (targetKPI) {
340
+ datapointAnnotation = targetKPI.DataPoint;
341
+ selectionVariantAnnotation = targetKPI.SelectionVariant;
342
+ presentationVariantAnnotation = targetKPI.Detail?.DefaultPresentationVariant;
343
+ chartAnnotation = presentationVariantAnnotation?.Visualizations?.find((viz: any) => {
344
+ return viz.$target.$Type === UIAnnotationTypes.ChartDefinitionType;
345
+ })?.$target as Chart;
149
346
 
150
- if (targetKPI && targetKPI.Detail?.DefaultPresentationVariant && aggregationHelper.isAnalyticsSupported()) {
151
- const kpiID = KPIID(kpiName);
152
-
153
- // Datapoint
154
- const datapointAnnotation = targetKPI.DataPoint;
155
- const datapointProperty = datapointAnnotation.Value.$target as Property;
156
- if (!aggregationHelper.isPropertyAggregatable(datapointProperty)) {
157
- // The main property of the KPI is not aggregatable --> We can't calculate its value so we ignore the KPI
158
- converterContext
159
- .getDiagnostics()
160
- .addIssue(
161
- IssueCategory.Annotation,
162
- IssueSeverity.Medium,
163
- IssueType.KPI_ISSUES.MAIN_PROPERTY_NOT_AGGREGATABLE + kpiConfig.qualifier
164
- );
165
- return undefined;
347
+ if (targetKPI.Detail?.SemanticObject) {
348
+ navigationInfo = {
349
+ semanticObject: targetKPI.Detail.SemanticObject.toString(),
350
+ action: targetKPI.Detail.Action?.toString(),
351
+ unavailableActions: []
352
+ };
166
353
  }
167
-
168
- // Chart
169
- const cardDef = convertKPICardContent(datapointAnnotation, targetKPI.Detail.DefaultPresentationVariant);
170
- if (!cardDef) {
171
- // Can't find info for the chart
354
+ } else {
355
+ // Fallback: try to find a SPV with the same qualifier
356
+ const aSPVAnnotations = kpiConverterContext.getAnnotationsByTerm(
357
+ "UI",
358
+ UIAnnotationTerms.SelectionPresentationVariant
359
+ ) as AnnotationTerm<SelectionPresentationVariantType>[];
360
+ const targetSPV = aSPVAnnotations.find(spv => {
361
+ return spv.qualifier === kpiConfig.qualifier;
362
+ });
363
+ if (targetSPV) {
364
+ selectionVariantAnnotation = targetSPV.SelectionVariant;
365
+ presentationVariantAnnotation = targetSPV.PresentationVariant;
366
+ datapointAnnotation = presentationVariantAnnotation?.Visualizations?.find((viz: any) => {
367
+ return viz.$target.$Type === UIAnnotationTypes.DataPointType;
368
+ })?.$target as DataPoint;
369
+ chartAnnotation = presentationVariantAnnotation?.Visualizations?.find((viz: any) => {
370
+ return viz.$target.$Type === UIAnnotationTypes.ChartDefinitionType;
371
+ })?.$target as Chart;
372
+ } else {
373
+ // Couldn't find a KPI or a SPV annotation with the qualifier from the manifest
172
374
  converterContext
173
375
  .getDiagnostics()
174
- .addIssue(
175
- IssueCategory.Annotation,
176
- IssueSeverity.Medium,
177
- IssueType.KPI_ISSUES.NO_CHART_VISUALIZATION + kpiConfig.qualifier
178
- );
376
+ .addIssue(IssueCategory.Annotation, IssueSeverity.Medium, IssueType.KPI_ISSUES.KPI_NOT_FOUND + kpiConfig.qualifier);
377
+
179
378
  return undefined;
180
379
  }
380
+ }
181
381
 
182
- const kpiDef: KPIDefinition = {
183
- id: kpiID,
184
- entitySet: kpiConfig.entitySet,
185
- datapoint: {
186
- propertyPath: datapointAnnotation.Value.path,
187
- annotationPath: kpiConverterContext.getEntitySetBasedAnnotationPath(datapointAnnotation.fullyQualifiedName),
188
- title: datapointAnnotation.Title?.toString(),
189
- description: datapointAnnotation.Description?.toString()
190
- },
191
- selectionVariantFilterDefinitions: targetKPI.SelectionVariant
192
- ? getFilterDefinitionsFromSelectionVariant(targetKPI.SelectionVariant)
193
- : undefined,
194
- card: cardDef
195
- };
382
+ if (!presentationVariantAnnotation || !datapointAnnotation || !chartAnnotation) {
383
+ // Couldn't find a chart or datapoint definition
384
+ converterContext
385
+ .getDiagnostics()
386
+ .addIssue(IssueCategory.Annotation, IssueSeverity.Medium, IssueType.KPI_ISSUES.KPI_DETAIL_NOT_FOUND + kpiConfig.qualifier);
196
387
 
197
- // Unit or currency
198
- const targetValueProperty = datapointAnnotation.Value.$target as Property;
199
- if (targetValueProperty.annotations.Measures?.ISOCurrency) {
200
- const currency = targetValueProperty.annotations.Measures?.ISOCurrency;
201
- if (isPathExpression(currency)) {
202
- kpiDef.datapoint.unit = {
203
- value: ((currency.$target as unknown) as Property).name,
204
- isCurrency: true,
205
- isPath: true
206
- };
207
- } else {
208
- kpiDef.datapoint.unit = {
209
- value: currency.toString(),
210
- isCurrency: true,
211
- isPath: false
212
- };
213
- }
214
- } else if (targetValueProperty.annotations.Measures?.Unit) {
215
- const unit = targetValueProperty.annotations.Measures?.Unit;
216
- if (isPathExpression(unit)) {
217
- kpiDef.datapoint.unit = {
218
- value: ((unit.$target as unknown) as Property).name,
219
- isCurrency: false,
220
- isPath: true
221
- };
222
- } else {
223
- kpiDef.datapoint.unit = {
224
- value: unit.toString(),
225
- isCurrency: false,
226
- isPath: false
227
- };
228
- }
229
- }
388
+ return undefined;
389
+ }
230
390
 
231
- // Criticality
232
- if (datapointAnnotation.Criticality) {
233
- if (typeof datapointAnnotation.Criticality === "object") {
234
- // Criticality is a path --> check if the corresponding property is aggregatable
235
- const criticalityProperty = (datapointAnnotation.Criticality as PathAnnotationExpression<CriticalityType>)
236
- .$target as Property;
237
- if (aggregationHelper.isPropertyAggregatable(criticalityProperty)) {
238
- kpiDef.datapoint.criticalityPath = (datapointAnnotation.Criticality as PathAnnotationExpression<CriticalityType>).path;
239
- } else {
240
- // The property isn't aggregatable --> we ignore it
241
- kpiDef.datapoint.criticalityValue = MessageType.None;
242
- }
243
- } else {
244
- // Criticality is an enum Value --> get the corresponding static value
245
- kpiDef.datapoint.criticalityValue = getMessageTypeFromCriticalityType(datapointAnnotation.Criticality);
246
- }
247
- } else if (datapointAnnotation.CriticalityCalculation) {
248
- kpiDef.datapoint.criticalityCalculationMode = datapointAnnotation.CriticalityCalculation.ImprovementDirection;
249
- kpiDef.datapoint.criticalityCalculationThresholds = [];
250
- switch (kpiDef.datapoint.criticalityCalculationMode) {
251
- case "UI.ImprovementDirectionType/Target":
252
- kpiDef.datapoint.criticalityCalculationThresholds.push(
253
- datapointAnnotation.CriticalityCalculation.DeviationRangeLowValue
254
- );
255
- kpiDef.datapoint.criticalityCalculationThresholds.push(
256
- datapointAnnotation.CriticalityCalculation.ToleranceRangeLowValue
257
- );
258
- kpiDef.datapoint.criticalityCalculationThresholds.push(
259
- datapointAnnotation.CriticalityCalculation.AcceptanceRangeLowValue
260
- );
261
- kpiDef.datapoint.criticalityCalculationThresholds.push(
262
- datapointAnnotation.CriticalityCalculation.AcceptanceRangeHighValue
263
- );
264
- kpiDef.datapoint.criticalityCalculationThresholds.push(
265
- datapointAnnotation.CriticalityCalculation.ToleranceRangeHighValue
266
- );
267
- kpiDef.datapoint.criticalityCalculationThresholds.push(
268
- datapointAnnotation.CriticalityCalculation.DeviationRangeHighValue
269
- );
270
- break;
271
-
272
- case "UI.ImprovementDirectionType/Minimize":
273
- kpiDef.datapoint.criticalityCalculationThresholds.push(
274
- datapointAnnotation.CriticalityCalculation.AcceptanceRangeHighValue
275
- );
276
- kpiDef.datapoint.criticalityCalculationThresholds.push(
277
- datapointAnnotation.CriticalityCalculation.ToleranceRangeHighValue
278
- );
279
- kpiDef.datapoint.criticalityCalculationThresholds.push(
280
- datapointAnnotation.CriticalityCalculation.DeviationRangeHighValue
281
- );
282
- break;
283
-
284
- case "UI.ImprovementDirectionType/Maximize":
285
- default:
286
- kpiDef.datapoint.criticalityCalculationThresholds.push(
287
- datapointAnnotation.CriticalityCalculation.DeviationRangeLowValue
288
- );
289
- kpiDef.datapoint.criticalityCalculationThresholds.push(
290
- datapointAnnotation.CriticalityCalculation.ToleranceRangeLowValue
291
- );
292
- kpiDef.datapoint.criticalityCalculationThresholds.push(
293
- datapointAnnotation.CriticalityCalculation.AcceptanceRangeLowValue
294
- );
295
- }
296
- } else {
297
- kpiDef.datapoint.criticalityValue = MessageType.None;
298
- }
391
+ const datapointProperty = datapointAnnotation.Value.$target as Property;
392
+ if (!aggregationHelper.isPropertyAggregatable(datapointProperty)) {
393
+ // The main property of the KPI is not aggregatable --> We can't calculate its value so we ignore the KPI
394
+ converterContext
395
+ .getDiagnostics()
396
+ .addIssue(
397
+ IssueCategory.Annotation,
398
+ IssueSeverity.Medium,
399
+ IssueType.KPI_ISSUES.MAIN_PROPERTY_NOT_AGGREGATABLE + kpiConfig.qualifier
400
+ );
401
+ return undefined;
402
+ }
299
403
 
300
- // Trend
301
- if (datapointAnnotation.Trend) {
302
- if (typeof datapointAnnotation.Trend === "object") {
303
- // Trend is a path --> check if the corresponding property is aggregatable
304
- const trendProperty = (datapointAnnotation.Trend as PathAnnotationExpression<TrendType>).$target as Property;
305
- if (aggregationHelper.isPropertyAggregatable(trendProperty)) {
306
- kpiDef.datapoint.trendPath = (datapointAnnotation.Trend as PathAnnotationExpression<TrendType>).path;
307
- } else {
308
- // The property isn't aggregatable --> we ignore it
309
- kpiDef.datapoint.trendValue = "None";
310
- }
311
- } else {
312
- // Trend is an enum Value --> get the corresponding static value
313
- kpiDef.datapoint.trendValue = getDeviationIndicatorFromTrendType(datapointAnnotation.Trend);
314
- }
315
- } else if (datapointAnnotation.TrendCalculation) {
316
- kpiDef.datapoint.trendCalculationIsRelative = datapointAnnotation.TrendCalculation.IsRelativeDifference ? true : false;
317
- if (datapointAnnotation.TrendCalculation.ReferenceValue.$target) {
318
- // Reference value is a path --> check if the corresponding property is aggregatable
319
- const referenceProperty = datapointAnnotation.TrendCalculation.ReferenceValue.$target as Property;
320
- if (aggregationHelper.isPropertyAggregatable(referenceProperty)) {
321
- kpiDef.datapoint.trendCalculationReferencePath = datapointAnnotation.TrendCalculation.ReferenceValue.path;
322
- } else {
323
- // The property isn't aggregatable --> we ignore it and switch back to trend 'None'
324
- kpiDef.datapoint.trendValue = "None";
325
- }
326
- } else {
327
- // Reference value is a static value
328
- kpiDef.datapoint.trendCalculationReferenceValue = datapointAnnotation.TrendCalculation.ReferenceValue;
329
- }
330
- if (
331
- kpiDef.datapoint.trendCalculationReferencePath !== undefined ||
332
- kpiDef.datapoint.trendCalculationReferenceValue !== undefined
333
- ) {
334
- kpiDef.datapoint.trendCalculationTresholds = [
335
- datapointAnnotation.TrendCalculation.StrongDownDifference.valueOf() as number,
336
- datapointAnnotation.TrendCalculation.DownDifference.valueOf() as number,
337
- datapointAnnotation.TrendCalculation.UpDifference.valueOf() as number,
338
- datapointAnnotation.TrendCalculation.StrongUpDifference.valueOf() as number
339
- ];
340
- }
341
- } else {
342
- kpiDef.datapoint.trendValue = "None";
343
- }
404
+ // Chart definition
405
+ const chartDef = convertKPIChart(chartAnnotation, presentationVariantAnnotation);
406
+ if (!chartDef) {
407
+ return undefined;
408
+ }
344
409
 
345
- // Target value
346
- if (datapointAnnotation.TargetValue) {
347
- if (datapointAnnotation.TargetValue.$target) {
348
- // Target value is a path --> check if the corresponding property is aggregatable (otherwise ignore)
349
- const targetProperty = datapointAnnotation.TargetValue.$target as Property;
350
- if (aggregationHelper.isPropertyAggregatable(targetProperty)) {
351
- kpiDef.datapoint.targetPath = datapointAnnotation.TargetValue.path;
352
- }
353
- } else {
354
- // Target value is a static value
355
- kpiDef.datapoint.targetValue = datapointAnnotation.TargetValue;
356
- }
357
- }
410
+ const kpiDef: KPIDefinition = {
411
+ id: KPIID(kpiName),
412
+ entitySet: kpiConfig.entitySet,
413
+ datapoint: {
414
+ propertyPath: datapointAnnotation.Value.path,
415
+ annotationPath: kpiConverterContext.getEntitySetBasedAnnotationPath(datapointAnnotation.fullyQualifiedName),
416
+ title: datapointAnnotation.Title?.toString(),
417
+ description: datapointAnnotation.Description?.toString()
418
+ },
419
+ selectionVariantFilterDefinitions: selectionVariantAnnotation
420
+ ? getFilterDefinitionsFromSelectionVariant(selectionVariantAnnotation)
421
+ : undefined,
422
+ chart: chartDef
423
+ };
358
424
 
359
- return kpiDef;
360
- } else {
361
- if (!targetKPI) {
362
- // Couldn't find a KPI with the qualifier specified in the manifest
363
- converterContext
364
- .getDiagnostics()
365
- .addIssue(IssueCategory.Annotation, IssueSeverity.Medium, IssueType.KPI_ISSUES.KPI_NOT_FOUND + kpiConfig.qualifier);
366
- } else if (!targetKPI.Detail?.DefaultPresentationVariant) {
367
- // No KPI detail/default presentation variant
368
- converterContext
369
- .getDiagnostics()
370
- .addIssue(IssueCategory.Annotation, IssueSeverity.Medium, IssueType.KPI_ISSUES.KPI_DETAIL_NOT_FOUND + kpiConfig.qualifier);
425
+ // Navigation
426
+ if (!navigationInfo) {
427
+ // No navigationInfo was found in the KPI annotation --> try the outbound navigation from the manifest
428
+ if (kpiConfig.detailNavigation) {
429
+ navigationInfo = {
430
+ outboundNavigation: kpiConfig.detailNavigation
431
+ };
371
432
  } else {
372
- // Entity doesn't support analytics
373
- converterContext
374
- .getDiagnostics()
375
- .addIssue(IssueCategory.Annotation, IssueSeverity.Medium, IssueType.KPI_ISSUES.NO_ANALYTICS + kpiConfig.entitySet);
433
+ // No outbound navigation in the manifest --> try the semantic object on the Datapoint value
434
+ navigationInfo = getNavigationInfoFromProperty(datapointProperty);
376
435
  }
377
- return undefined;
378
436
  }
437
+ if (navigationInfo) {
438
+ kpiDef.navigation = navigationInfo;
439
+ }
440
+
441
+ updateCurrency(datapointAnnotation, kpiDef);
442
+ updateCriticality(datapointAnnotation, aggregationHelper, kpiDef);
443
+ updateTrend(datapointAnnotation, aggregationHelper, kpiDef);
444
+ updateTarget(datapointAnnotation, aggregationHelper, kpiDef);
445
+
446
+ return kpiDef;
379
447
  }
380
448
 
381
449
  /**