@sapui5/sap.fe.core 1.95.0 → 1.96.4

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 (160) hide show
  1. package/package.json +1 -1
  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 +7 -43
  5. package/src/sap/fe/core/AppStateHandler.js +18 -4
  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 +136 -19
  9. package/src/sap/fe/core/ExtensionAPI.js +1 -1
  10. package/src/sap/fe/core/PageController.js +1 -33
  11. package/src/sap/fe/core/RouterProxy.js +59 -69
  12. package/src/sap/fe/core/Synchronization.js +1 -1
  13. package/src/sap/fe/core/TemplateComponent.js +3 -3
  14. package/src/sap/fe/core/TemplateModel.js +1 -1
  15. package/src/sap/fe/core/TransactionHelper.js +87 -6
  16. package/src/sap/fe/core/actions/draft.js +1 -1
  17. package/src/sap/fe/core/actions/messageHandling.js +1 -1
  18. package/src/sap/fe/core/actions/nonDraft.js +1 -1
  19. package/src/sap/fe/core/actions/operations.js +22 -3
  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 +74 -48
  23. package/src/sap/fe/core/controllerextensions/IntentBasedNavigation.js +1 -1
  24. package/src/sap/fe/core/controllerextensions/InternalEditFlow.js +5 -4
  25. package/src/sap/fe/core/controllerextensions/InternalIntentBasedNavigation.js +64 -28
  26. package/src/sap/fe/core/controllerextensions/InternalRouting.js +194 -223
  27. package/src/sap/fe/core/controllerextensions/KPIManagement.js +460 -207
  28. package/src/sap/fe/core/controllerextensions/KPIManagement.ts +475 -213
  29. package/src/sap/fe/core/controllerextensions/MessageHandler.js +1 -1
  30. package/src/sap/fe/core/controllerextensions/PageReady.js +1 -1
  31. package/src/sap/fe/core/controllerextensions/PageReady.ts +3 -7
  32. package/src/sap/fe/core/controllerextensions/Paginator.js +4 -5
  33. package/src/sap/fe/core/controllerextensions/Placeholder.js +11 -17
  34. package/src/sap/fe/core/controllerextensions/Routing.js +1 -1
  35. package/src/sap/fe/core/controllerextensions/RoutingListener.js +1 -1
  36. package/src/sap/fe/core/controllerextensions/Share.js +20 -7
  37. package/src/sap/fe/core/controllerextensions/SideEffects.js +2 -3
  38. package/src/sap/fe/core/controllerextensions/SideEffects.ts +13 -12
  39. package/src/sap/fe/core/controllerextensions/ViewState.js +21 -3
  40. package/src/sap/fe/core/controls/ActionParameterDialog.fragment.xml +39 -34
  41. package/src/sap/fe/core/controls/ActionParameterDialogValueHelp.fragment.xml +35 -0
  42. package/src/sap/fe/core/controls/CommandExecution.js +1 -1
  43. package/src/sap/fe/core/controls/ConditionalWrapper.js +1 -1
  44. package/src/sap/fe/core/controls/CustomQuickViewPage.js +4 -1
  45. package/src/sap/fe/core/controls/DataLossOrDraftDiscard/DataLossOrDraftDiscardHandler.js +1 -1
  46. package/src/sap/fe/core/controls/FieldWrapper.js +1 -1
  47. package/src/sap/fe/core/controls/FilterBar.js +1 -1
  48. package/src/sap/fe/core/controls/FormElementWrapper.js +1 -1
  49. package/src/sap/fe/core/controls/MultiValueParameterDelegate.js +45 -0
  50. package/src/sap/fe/core/controls/filterbar/FilterContainer.js +1 -1
  51. package/src/sap/fe/core/controls/filterbar/VisualFilter.js +1 -1
  52. package/src/sap/fe/core/controls/filterbar/VisualFilterContainer.js +1 -1
  53. package/src/sap/fe/core/controls/filterbar/utils/VisualFilterUtils.js +1 -1
  54. package/src/sap/fe/core/converters/MetaModelConverter.js +852 -881
  55. package/src/sap/fe/core/converters/MetaModelConverter.ts +761 -849
  56. package/src/sap/fe/core/converters/annotations/DataField.js +52 -2
  57. package/src/sap/fe/core/converters/annotations/DataField.ts +52 -2
  58. package/src/sap/fe/core/converters/controls/Common/Chart.js +1 -3
  59. package/src/sap/fe/core/converters/controls/Common/Chart.ts +0 -2
  60. package/src/sap/fe/core/converters/controls/Common/Form.js +16 -8
  61. package/src/sap/fe/core/converters/controls/Common/Form.ts +9 -3
  62. package/src/sap/fe/core/converters/controls/Common/KPI.js +271 -253
  63. package/src/sap/fe/core/converters/controls/Common/KPI.ts +287 -291
  64. package/src/sap/fe/core/converters/controls/Common/Table.js +196 -102
  65. package/src/sap/fe/core/converters/controls/Common/Table.ts +240 -109
  66. package/src/sap/fe/core/converters/controls/ObjectPage/SubSection.js +15 -7
  67. package/src/sap/fe/core/converters/controls/ObjectPage/SubSection.ts +27 -6
  68. package/src/sap/fe/core/converters/helpers/BindingHelper.js +3 -2
  69. package/src/sap/fe/core/converters/helpers/BindingHelper.ts +2 -2
  70. package/src/sap/fe/core/converters/helpers/ID.js +1 -1
  71. package/src/sap/fe/core/converters/helpers/ID.ts +2 -2
  72. package/src/sap/fe/core/converters/helpers/IssueManager.js +4 -5
  73. package/src/sap/fe/core/converters/helpers/IssueManager.ts +3 -4
  74. package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.js +2 -2
  75. package/src/sap/fe/core/converters/objectPage/HeaderAndFooterAction.ts +1 -1
  76. package/src/sap/fe/core/converters/templates/ListReportConverter.js +20 -1
  77. package/src/sap/fe/core/converters/templates/ListReportConverter.ts +25 -1
  78. package/src/sap/fe/core/converters/templates/ObjectPageConverter.js +2 -5
  79. package/src/sap/fe/core/converters/templates/ObjectPageConverter.ts +1 -2
  80. package/src/sap/fe/core/designtime/AppComponent.designtime.js +1 -1
  81. package/src/sap/fe/core/formatters/FPMFormatter.js +1 -1
  82. package/src/sap/fe/core/formatters/FPMFormatter.ts +8 -3
  83. package/src/sap/fe/core/fpm/Component.js +2 -2
  84. package/src/sap/fe/core/helpers/BindingExpression.js +34 -2
  85. package/src/sap/fe/core/helpers/BindingExpression.ts +24 -1
  86. package/src/sap/fe/core/helpers/DynamicAnnotationPathHelper.js +1 -1
  87. package/src/sap/fe/core/helpers/EditState.js +1 -1
  88. package/src/sap/fe/core/helpers/ExcelFormatHelper.js +1 -3
  89. package/src/sap/fe/core/helpers/FPMHelper.js +1 -1
  90. package/src/sap/fe/core/helpers/ModelHelper.js +13 -1
  91. package/src/sap/fe/core/helpers/PasteHelper.js +7 -1
  92. package/src/sap/fe/core/helpers/SemanticDateOperators.js +1 -1
  93. package/src/sap/fe/core/helpers/SemanticKeyHelper.js +1 -1
  94. package/src/sap/fe/core/helpers/StableIdHelper.js +2 -2
  95. package/src/sap/fe/core/helpers/StableIdHelper.ts +1 -1
  96. package/src/sap/fe/core/library.js +2 -2
  97. package/src/sap/fe/core/library.support.js +1 -1
  98. package/src/sap/fe/core/messagebundle.properties +18 -3
  99. package/src/sap/fe/core/messagebundle_ar.properties +8 -3
  100. package/src/sap/fe/core/messagebundle_bg.properties +8 -3
  101. package/src/sap/fe/core/messagebundle_ca.properties +9 -4
  102. package/src/sap/fe/core/messagebundle_cs.properties +8 -3
  103. package/src/sap/fe/core/messagebundle_cy.properties +8 -3
  104. package/src/sap/fe/core/messagebundle_da.properties +8 -3
  105. package/src/sap/fe/core/messagebundle_de.properties +8 -3
  106. package/src/sap/fe/core/messagebundle_el.properties +8 -3
  107. package/src/sap/fe/core/messagebundle_en.properties +5 -0
  108. package/src/sap/fe/core/messagebundle_en_GB.properties +5 -0
  109. package/src/sap/fe/core/messagebundle_en_US_sappsd.properties +5 -0
  110. package/src/sap/fe/core/messagebundle_en_US_saprigi.properties +20 -2
  111. package/src/sap/fe/core/messagebundle_en_US_saptrc.properties +12 -0
  112. package/src/sap/fe/core/messagebundle_es.properties +8 -3
  113. package/src/sap/fe/core/messagebundle_es_MX.properties +9 -4
  114. package/src/sap/fe/core/messagebundle_et.properties +8 -3
  115. package/src/sap/fe/core/messagebundle_fi.properties +8 -3
  116. package/src/sap/fe/core/messagebundle_fr.properties +8 -3
  117. package/src/sap/fe/core/messagebundle_fr_CA.properties +8 -3
  118. package/src/sap/fe/core/messagebundle_hi.properties +8 -3
  119. package/src/sap/fe/core/messagebundle_hr.properties +8 -3
  120. package/src/sap/fe/core/messagebundle_hu.properties +8 -3
  121. package/src/sap/fe/core/messagebundle_id.properties +7 -2
  122. package/src/sap/fe/core/messagebundle_it.properties +8 -3
  123. package/src/sap/fe/core/messagebundle_iw.properties +8 -3
  124. package/src/sap/fe/core/messagebundle_ja.properties +8 -3
  125. package/src/sap/fe/core/messagebundle_kk.properties +8 -3
  126. package/src/sap/fe/core/messagebundle_ko.properties +8 -3
  127. package/src/sap/fe/core/messagebundle_lt.properties +8 -3
  128. package/src/sap/fe/core/messagebundle_lv.properties +8 -3
  129. package/src/sap/fe/core/messagebundle_ms.properties +8 -3
  130. package/src/sap/fe/core/messagebundle_nl.properties +8 -3
  131. package/src/sap/fe/core/messagebundle_no.properties +8 -3
  132. package/src/sap/fe/core/messagebundle_pl.properties +8 -3
  133. package/src/sap/fe/core/messagebundle_pt.properties +8 -3
  134. package/src/sap/fe/core/messagebundle_pt_PT.properties +8 -3
  135. package/src/sap/fe/core/messagebundle_ro.properties +8 -3
  136. package/src/sap/fe/core/messagebundle_ru.properties +8 -3
  137. package/src/sap/fe/core/messagebundle_sh.properties +8 -3
  138. package/src/sap/fe/core/messagebundle_sk.properties +8 -3
  139. package/src/sap/fe/core/messagebundle_sl.properties +8 -3
  140. package/src/sap/fe/core/messagebundle_sv.properties +8 -3
  141. package/src/sap/fe/core/messagebundle_th.properties +8 -3
  142. package/src/sap/fe/core/messagebundle_tr.properties +8 -3
  143. package/src/sap/fe/core/messagebundle_uk.properties +8 -3
  144. package/src/sap/fe/core/messagebundle_vi.properties +8 -3
  145. package/src/sap/fe/core/messagebundle_zh_CN.properties +8 -3
  146. package/src/sap/fe/core/messagebundle_zh_TW.properties +8 -3
  147. package/src/sap/fe/core/services/CacheHandlerServiceFactory.js +1 -1
  148. package/src/sap/fe/core/services/NavigationServiceFactory.js +1 -1
  149. package/src/sap/fe/core/services/ResourceModelServiceFactory.js +1 -1
  150. package/src/sap/fe/core/services/RoutingServiceFactory.js +70 -33
  151. package/src/sap/fe/core/services/SideEffectsServiceFactory.js +2 -2
  152. package/src/sap/fe/core/services/SideEffectsServiceFactory.ts +1 -1
  153. package/src/sap/fe/core/services/TemplatedViewServiceFactory.js +1 -1
  154. package/src/sap/fe/core/services/view/TemplatingErrorPage.controller.js +1 -1
  155. package/src/sap/fe/core/templating/DataModelPathHelper.js +3 -14
  156. package/src/sap/fe/core/templating/DataModelPathHelper.ts +3 -10
  157. package/src/sap/fe/core/templating/EntitySetHelper.js +13 -3
  158. package/src/sap/fe/core/templating/EntitySetHelper.ts +4 -0
  159. package/src/sap/fe/core/templating/UIFormatters.js +39 -1
  160. package/src/sap/fe/core/templating/UIFormatters.ts +73 -1
@@ -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";
@@ -18,14 +27,12 @@ import { IssueCategory, IssueSeverity, IssueType } from "sap/fe/core/converters/
18
27
  import { getMessageTypeFromCriticalityType } from "./Criticality";
19
28
  import { getFilterDefinitionsFromSelectionVariant, FilterDefinition } from "sap/fe/core/converters/helpers/SelectionVariantHelper";
20
29
 
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
- };
30
+ export type KPIChartDefinition = {
31
+ chartType: string;
32
+ dimensions: { name: string; label: string; role?: string }[];
33
+ measures: { name: string; label: string; role?: string }[];
34
+ sortOrder?: { name: string; descending: boolean }[];
35
+ maxItems?: number;
29
36
  };
30
37
 
31
38
  export type KPIDefinition = {
@@ -54,328 +61,317 @@ export type KPIDefinition = {
54
61
  targetValue?: number;
55
62
  targetPath?: string;
56
63
  };
57
- card: KPICardDefinition;
64
+ chart: KPIChartDefinition;
58
65
  selectionVariantFilterDefinitions?: FilterDefinition[];
59
66
  };
60
67
 
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";
80
- }
81
- return deviationIndicator;
82
- }
68
+ const DeviationIndicatorFromTrendType: Record<string, string> = {
69
+ "UI.TrendType/StrongUp": "Up",
70
+ "UI.TrendType/Up": "Up",
71
+ "UI.TrendType/StrongDown": "Down",
72
+ "UI.TrendType/Down": "Down",
73
+ "UI.TrendType/Sideways": "None"
74
+ };
83
75
 
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";
91
- });
76
+ const KPIChartTypeFromUI: Record<string, string> = {
77
+ "UI.ChartType/ColumnStacked": "StackedColumn",
78
+ "UI.ChartType/BarStacked": "StackedBar",
79
+ "UI.ChartType/Donut": "Donut",
80
+ "UI.ChartType/Line": "Line",
81
+ "UI.ChartType/Bubble": "bubble",
82
+ "UI.ChartType/Column": "column",
83
+ "UI.ChartType/Bar": "bar",
84
+ "UI.ChartType/VerticalBullet": "vertical_bullet",
85
+ "UI.ChartType/Combination": "combination",
86
+ "UI.ChartType/Scatter": "scatter"
87
+ };
92
88
 
93
- if (chart) {
94
- const chartAnnotation = chart.$target as ChartDefinitionTypeTypes;
89
+ function convertKPIChart(chartAnnotation: Chart, presentationVariantAnnotation: PresentationVariant): KPIChartDefinition | undefined {
90
+ if (chartAnnotation.Measures === undefined) {
91
+ // We need at least 1 measure (but no dimension is allowed, e.g. for bubble chart)
92
+ return undefined;
93
+ }
95
94
 
96
- let chartType: string;
97
- switch (chartAnnotation.ChartType) {
98
- case "UI.ChartType/ColumnStacked":
99
- chartType = "StackedColumn";
100
- break;
95
+ const charDimensions = chartAnnotation.Dimensions
96
+ ? (chartAnnotation.Dimensions as PropertyPath[]).map(propertyPath => {
97
+ const dimAttribute = chartAnnotation.DimensionAttributes?.find(attribute => {
98
+ return attribute.Dimension?.value === propertyPath.value;
99
+ });
100
+ return {
101
+ name: propertyPath.value,
102
+ label: propertyPath.$target.annotations.Common?.Label?.toString() || propertyPath.value,
103
+ role: dimAttribute?.Role?.replace("UI.ChartDimensionRoleType/", "")
104
+ };
105
+ })
106
+ : [];
101
107
 
102
- case "UI.ChartType/BarStacked":
103
- chartType = "StackedBar";
104
- break;
108
+ const chartMeasures = (chartAnnotation.Measures as PropertyPath[]).map(propertyPath => {
109
+ const measureAttribute = chartAnnotation.MeasureAttributes?.find(attribute => {
110
+ return attribute.Measure?.value === propertyPath.value;
111
+ });
112
+ return {
113
+ name: propertyPath.value,
114
+ label: propertyPath.$target.annotations.Common?.Label?.toString() || propertyPath.value,
115
+ role: measureAttribute?.Role?.replace("UI.ChartMeasureRoleType/", "")
116
+ };
117
+ });
105
118
 
106
- case "UI.ChartType/Donut":
107
- chartType = "Donut";
108
- break;
119
+ return {
120
+ chartType: KPIChartTypeFromUI[chartAnnotation.ChartType] || "Line",
121
+ dimensions: charDimensions,
122
+ measures: chartMeasures,
123
+ sortOrder: presentationVariantAnnotation?.SortOrder?.map(sortOrder => {
124
+ return { name: sortOrder.Property?.value || "", descending: !!sortOrder.Descending };
125
+ }),
126
+ maxItems: presentationVariantAnnotation?.MaxItems?.valueOf() as number
127
+ };
128
+ }
109
129
 
110
- default:
111
- chartType = "Line";
130
+ function updateCurrency(datapointAnnotation: DataPoint, kpiDef: KPIDefinition): void {
131
+ const targetValueProperty = datapointAnnotation.Value.$target as Property;
132
+ if (targetValueProperty.annotations.Measures?.ISOCurrency) {
133
+ const currency = targetValueProperty.annotations.Measures?.ISOCurrency;
134
+ if (isPathExpression(currency)) {
135
+ kpiDef.datapoint.unit = {
136
+ value: ((currency.$target as unknown) as Property).name,
137
+ isCurrency: true,
138
+ isPath: true
139
+ };
140
+ } else {
141
+ kpiDef.datapoint.unit = {
142
+ value: currency.toString(),
143
+ isCurrency: true,
144
+ isPath: false
145
+ };
112
146
  }
113
-
114
- if (!chartAnnotation.Dimensions || !chartAnnotation.Measures) {
115
- return undefined;
147
+ } else if (targetValueProperty.annotations.Measures?.Unit) {
148
+ const unit = targetValueProperty.annotations.Measures?.Unit;
149
+ if (isPathExpression(unit)) {
150
+ kpiDef.datapoint.unit = {
151
+ value: ((unit.$target as unknown) as Property).name,
152
+ isCurrency: false,
153
+ isPath: true
154
+ };
116
155
  } 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
- }
156
+ kpiDef.datapoint.unit = {
157
+ value: unit.toString(),
158
+ isCurrency: false,
159
+ isPath: false
135
160
  };
136
161
  }
137
- } else {
138
- return undefined;
139
162
  }
140
163
  }
141
164
 
142
- function createKPIDefinition(kpiName: string, kpiConfig: KPIConfiguration, converterContext: ConverterContext): KPIDefinition | undefined {
143
- const kpiConverterContext = converterContext.getConverterContextFor("/" + kpiConfig.entitySet);
144
- const aKPIAnnotations = kpiConverterContext.getAnnotationsByTerm("UI", UIAnnotationTerms.KPI) as AnnotationTerm<KPIType>[];
145
- const targetKPI = aKPIAnnotations.find(kpi => {
146
- return kpi.qualifier === kpiConfig.qualifier;
147
- });
148
- const aggregationHelper = new AggregationHelper(kpiConverterContext.getEntityType(), kpiConverterContext);
149
-
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;
166
- }
167
-
168
- // Chart
169
- const cardDef = convertKPICardContent(datapointAnnotation, targetKPI.Detail.DefaultPresentationVariant);
170
- if (!cardDef) {
171
- // Can't find info for the chart
172
- converterContext
173
- .getDiagnostics()
174
- .addIssue(
175
- IssueCategory.Annotation,
176
- IssueSeverity.Medium,
177
- IssueType.KPI_ISSUES.NO_CHART_VISUALIZATION + kpiConfig.qualifier
178
- );
179
- return undefined;
180
- }
181
-
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
- };
196
-
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
- };
165
+ function updateCriticality(datapointAnnotation: DataPoint, aggregationHelper: AggregationHelper, kpiDef: KPIDefinition): void {
166
+ if (datapointAnnotation.Criticality) {
167
+ if (typeof datapointAnnotation.Criticality === "object") {
168
+ // Criticality is a path --> check if the corresponding property is aggregatable
169
+ const criticalityProperty = (datapointAnnotation.Criticality as PathAnnotationExpression<CriticalityType>).$target as Property;
170
+ if (aggregationHelper.isPropertyAggregatable(criticalityProperty)) {
171
+ kpiDef.datapoint.criticalityPath = (datapointAnnotation.Criticality as PathAnnotationExpression<CriticalityType>).path;
207
172
  } 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
- };
173
+ // The property isn't aggregatable --> we ignore it
174
+ kpiDef.datapoint.criticalityValue = MessageType.None;
228
175
  }
176
+ } else {
177
+ // Criticality is an enum Value --> get the corresponding static value
178
+ kpiDef.datapoint.criticalityValue = getMessageTypeFromCriticalityType(datapointAnnotation.Criticality);
229
179
  }
180
+ } else if (datapointAnnotation.CriticalityCalculation) {
181
+ kpiDef.datapoint.criticalityCalculationMode = datapointAnnotation.CriticalityCalculation.ImprovementDirection;
182
+ kpiDef.datapoint.criticalityCalculationThresholds = [];
183
+ switch (kpiDef.datapoint.criticalityCalculationMode) {
184
+ case "UI.ImprovementDirectionType/Target":
185
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.DeviationRangeLowValue);
186
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.ToleranceRangeLowValue);
187
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.AcceptanceRangeLowValue);
188
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.AcceptanceRangeHighValue);
189
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.ToleranceRangeHighValue);
190
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.DeviationRangeHighValue);
191
+ break;
230
192
 
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;
193
+ case "UI.ImprovementDirectionType/Minimize":
194
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.AcceptanceRangeHighValue);
195
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.ToleranceRangeHighValue);
196
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.DeviationRangeHighValue);
197
+ break;
283
198
 
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;
199
+ case "UI.ImprovementDirectionType/Maximize":
200
+ default:
201
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.DeviationRangeLowValue);
202
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.ToleranceRangeLowValue);
203
+ kpiDef.datapoint.criticalityCalculationThresholds.push(datapointAnnotation.CriticalityCalculation.AcceptanceRangeLowValue);
298
204
  }
205
+ } else {
206
+ kpiDef.datapoint.criticalityValue = MessageType.None;
207
+ }
208
+ }
299
209
 
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
- }
210
+ function updateTrend(datapointAnnotation: DataPoint, aggregationHelper: AggregationHelper, kpiDef: KPIDefinition): void {
211
+ if (datapointAnnotation.Trend) {
212
+ if (typeof datapointAnnotation.Trend === "object") {
213
+ // Trend is a path --> check if the corresponding property is aggregatable
214
+ const trendProperty = (datapointAnnotation.Trend as PathAnnotationExpression<TrendType>).$target as Property;
215
+ if (aggregationHelper.isPropertyAggregatable(trendProperty)) {
216
+ kpiDef.datapoint.trendPath = (datapointAnnotation.Trend as PathAnnotationExpression<TrendType>).path;
311
217
  } else {
312
- // Trend is an enum Value --> get the corresponding static value
313
- kpiDef.datapoint.trendValue = getDeviationIndicatorFromTrendType(datapointAnnotation.Trend);
218
+ // The property isn't aggregatable --> we ignore it
219
+ kpiDef.datapoint.trendValue = "None";
314
220
  }
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
- }
221
+ } else {
222
+ // Trend is an enum Value --> get the corresponding static value
223
+ kpiDef.datapoint.trendValue = DeviationIndicatorFromTrendType[datapointAnnotation.Trend] || "None";
224
+ }
225
+ } else if (datapointAnnotation.TrendCalculation) {
226
+ kpiDef.datapoint.trendCalculationIsRelative = datapointAnnotation.TrendCalculation.IsRelativeDifference ? true : false;
227
+ if (datapointAnnotation.TrendCalculation.ReferenceValue.$target) {
228
+ // Reference value is a path --> check if the corresponding property is aggregatable
229
+ const referenceProperty = datapointAnnotation.TrendCalculation.ReferenceValue.$target as Property;
230
+ if (aggregationHelper.isPropertyAggregatable(referenceProperty)) {
231
+ kpiDef.datapoint.trendCalculationReferencePath = datapointAnnotation.TrendCalculation.ReferenceValue.path;
326
232
  } 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
- ];
233
+ // The property isn't aggregatable --> we ignore it and switch back to trend 'None'
234
+ kpiDef.datapoint.trendValue = "None";
340
235
  }
341
236
  } else {
342
- kpiDef.datapoint.trendValue = "None";
237
+ // Reference value is a static value
238
+ kpiDef.datapoint.trendCalculationReferenceValue = datapointAnnotation.TrendCalculation.ReferenceValue;
343
239
  }
240
+ if (kpiDef.datapoint.trendCalculationReferencePath !== undefined || kpiDef.datapoint.trendCalculationReferenceValue !== undefined) {
241
+ kpiDef.datapoint.trendCalculationTresholds = [
242
+ datapointAnnotation.TrendCalculation.StrongDownDifference.valueOf() as number,
243
+ datapointAnnotation.TrendCalculation.DownDifference.valueOf() as number,
244
+ datapointAnnotation.TrendCalculation.UpDifference.valueOf() as number,
245
+ datapointAnnotation.TrendCalculation.StrongUpDifference.valueOf() as number
246
+ ];
247
+ }
248
+ } else {
249
+ kpiDef.datapoint.trendValue = "None";
250
+ }
251
+ }
344
252
 
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;
253
+ function updateTarget(datapointAnnotation: DataPoint, aggregationHelper: AggregationHelper, kpiDef: KPIDefinition): void {
254
+ if (datapointAnnotation.TargetValue) {
255
+ if (datapointAnnotation.TargetValue.$target) {
256
+ // Target value is a path --> check if the corresponding property is aggregatable (otherwise ignore)
257
+ const targetProperty = datapointAnnotation.TargetValue.$target as Property;
258
+ if (aggregationHelper.isPropertyAggregatable(targetProperty)) {
259
+ kpiDef.datapoint.targetPath = datapointAnnotation.TargetValue.path;
356
260
  }
261
+ } else {
262
+ // Target value is a static value
263
+ kpiDef.datapoint.targetValue = datapointAnnotation.TargetValue;
357
264
  }
265
+ }
266
+ }
267
+
268
+ function createKPIDefinition(kpiName: string, kpiConfig: KPIConfiguration, converterContext: ConverterContext): KPIDefinition | undefined {
269
+ const kpiConverterContext = converterContext.getConverterContextFor("/" + kpiConfig.entitySet);
270
+ const aggregationHelper = new AggregationHelper(kpiConverterContext.getEntityType(), kpiConverterContext);
271
+
272
+ if (!aggregationHelper.isAnalyticsSupported()) {
273
+ // The entity doesn't support analytical queries
274
+ converterContext
275
+ .getDiagnostics()
276
+ .addIssue(IssueCategory.Annotation, IssueSeverity.Medium, IssueType.KPI_ISSUES.NO_ANALYTICS + kpiConfig.entitySet);
277
+
278
+ return undefined;
279
+ }
358
280
 
359
- return kpiDef;
281
+ let selectionVariantAnnotation: SelectionVariant | undefined;
282
+ let datapointAnnotation: DataPoint | undefined;
283
+ let presentationVariantAnnotation: PresentationVariant | undefined;
284
+ let chartAnnotation: Chart | undefined;
285
+
286
+ // Search for a KPI with the qualifier frmo the manifest
287
+ const aKPIAnnotations = kpiConverterContext.getAnnotationsByTerm("UI", UIAnnotationTerms.KPI) as AnnotationTerm<KPIType>[];
288
+ const targetKPI = aKPIAnnotations.find(kpi => {
289
+ return kpi.qualifier === kpiConfig.qualifier;
290
+ });
291
+ if (targetKPI) {
292
+ datapointAnnotation = targetKPI.DataPoint;
293
+ selectionVariantAnnotation = targetKPI.SelectionVariant;
294
+ presentationVariantAnnotation = targetKPI.Detail?.DefaultPresentationVariant;
295
+ chartAnnotation = presentationVariantAnnotation?.Visualizations?.find((viz: any) => {
296
+ return viz.$target.$Type === UIAnnotationTypes.ChartDefinitionType;
297
+ })?.$target as Chart;
360
298
  } 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);
299
+ // Fallback: try to find a SPV with the same qualifier
300
+ const aSPVAnnotations = kpiConverterContext.getAnnotationsByTerm(
301
+ "UI",
302
+ UIAnnotationTerms.SelectionPresentationVariant
303
+ ) as AnnotationTerm<SelectionPresentationVariantType>[];
304
+ const targetSPV = aSPVAnnotations.find(spv => {
305
+ return spv.qualifier === kpiConfig.qualifier;
306
+ });
307
+ if (targetSPV) {
308
+ selectionVariantAnnotation = targetSPV.SelectionVariant;
309
+ presentationVariantAnnotation = targetSPV.PresentationVariant;
310
+ datapointAnnotation = presentationVariantAnnotation?.Visualizations?.find((viz: any) => {
311
+ return viz.$target.$Type === UIAnnotationTypes.DataPointType;
312
+ })?.$target as DataPoint;
313
+ chartAnnotation = presentationVariantAnnotation?.Visualizations?.find((viz: any) => {
314
+ return viz.$target.$Type === UIAnnotationTypes.ChartDefinitionType;
315
+ })?.$target as Chart;
371
316
  } else {
372
- // Entity doesn't support analytics
317
+ // Couldn't find a KPI or a SPV annotation with the qualifier from the manifest
373
318
  converterContext
374
319
  .getDiagnostics()
375
- .addIssue(IssueCategory.Annotation, IssueSeverity.Medium, IssueType.KPI_ISSUES.NO_ANALYTICS + kpiConfig.entitySet);
320
+ .addIssue(IssueCategory.Annotation, IssueSeverity.Medium, IssueType.KPI_ISSUES.KPI_NOT_FOUND + kpiConfig.qualifier);
321
+
322
+ return undefined;
376
323
  }
324
+ }
325
+
326
+ if (!presentationVariantAnnotation || !datapointAnnotation || !chartAnnotation) {
327
+ // Couldn't find a chart or datapoint definition
328
+ converterContext
329
+ .getDiagnostics()
330
+ .addIssue(IssueCategory.Annotation, IssueSeverity.Medium, IssueType.KPI_ISSUES.KPI_DETAIL_NOT_FOUND + kpiConfig.qualifier);
331
+
332
+ return undefined;
333
+ }
334
+
335
+ const datapointProperty = datapointAnnotation.Value.$target as Property;
336
+ if (!aggregationHelper.isPropertyAggregatable(datapointProperty)) {
337
+ // The main property of the KPI is not aggregatable --> We can't calculate its value so we ignore the KPI
338
+ converterContext
339
+ .getDiagnostics()
340
+ .addIssue(
341
+ IssueCategory.Annotation,
342
+ IssueSeverity.Medium,
343
+ IssueType.KPI_ISSUES.MAIN_PROPERTY_NOT_AGGREGATABLE + kpiConfig.qualifier
344
+ );
377
345
  return undefined;
378
346
  }
347
+
348
+ // Chart definition
349
+ const chartDef = convertKPIChart(chartAnnotation, presentationVariantAnnotation);
350
+ if (!chartDef) {
351
+ return undefined;
352
+ }
353
+
354
+ const kpiDef: KPIDefinition = {
355
+ id: KPIID(kpiName),
356
+ entitySet: kpiConfig.entitySet,
357
+ datapoint: {
358
+ propertyPath: datapointAnnotation.Value.path,
359
+ annotationPath: kpiConverterContext.getEntitySetBasedAnnotationPath(datapointAnnotation.fullyQualifiedName),
360
+ title: datapointAnnotation.Title?.toString(),
361
+ description: datapointAnnotation.Description?.toString()
362
+ },
363
+ selectionVariantFilterDefinitions: selectionVariantAnnotation
364
+ ? getFilterDefinitionsFromSelectionVariant(selectionVariantAnnotation)
365
+ : undefined,
366
+ chart: chartDef
367
+ };
368
+
369
+ updateCurrency(datapointAnnotation, kpiDef);
370
+ updateCriticality(datapointAnnotation, aggregationHelper, kpiDef);
371
+ updateTrend(datapointAnnotation, aggregationHelper, kpiDef);
372
+ updateTarget(datapointAnnotation, aggregationHelper, kpiDef);
373
+
374
+ return kpiDef;
379
375
  }
380
376
 
381
377
  /**