@sapui5/sap.suite.ui.generic.template 1.148.0 → 1.149.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 (132) hide show
  1. package/package.json +1 -1
  2. package/src/sap/suite/ui/generic/template/.library +1 -1
  3. package/src/sap/suite/ui/generic/template/AnalyticalListPage/i18n/i18n_fr_CA.properties +2 -2
  4. package/src/sap/suite/ui/generic/template/AnalyticalListPage/manifest.json +1 -1
  5. package/src/sap/suite/ui/generic/template/Canvas/manifest.json +1 -1
  6. package/src/sap/suite/ui/generic/template/ListReport/controller/IappStateHandler.js +254 -224
  7. package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_de.properties +1 -1
  8. package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_fr.properties +1 -1
  9. package/src/sap/suite/ui/generic/template/ListReport/manifest.json +1 -1
  10. package/src/sap/suite/ui/generic/template/ObjectPage/controller/inlineCreationRows/InlineCreationRowsHelper.js +76 -33
  11. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_ar.properties +4 -0
  12. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_bg.properties +4 -0
  13. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_ca.properties +4 -0
  14. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_cnr.properties +4 -0
  15. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_cs.properties +4 -0
  16. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_cy.properties +4 -0
  17. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_da.properties +4 -0
  18. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_de.properties +6 -2
  19. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_el.properties +4 -0
  20. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_en.properties +4 -0
  21. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_en_GB.properties +4 -0
  22. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_es.properties +4 -0
  23. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_es_MX.properties +4 -0
  24. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_et.properties +4 -0
  25. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_fi.properties +4 -0
  26. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_fr.properties +4 -0
  27. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_fr_CA.properties +4 -0
  28. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_hi.properties +4 -0
  29. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_hr.properties +4 -0
  30. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_hu.properties +4 -0
  31. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_id.properties +5 -1
  32. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_it.properties +4 -0
  33. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_iw.properties +4 -0
  34. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_ja.properties +4 -0
  35. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_kk.properties +4 -0
  36. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_ko.properties +4 -0
  37. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_lt.properties +4 -0
  38. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_lv.properties +4 -0
  39. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_mk.properties +4 -0
  40. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_ms.properties +4 -0
  41. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_nl.properties +4 -0
  42. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_no.properties +4 -0
  43. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_pl.properties +4 -0
  44. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_pt.properties +5 -1
  45. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_pt_PT.properties +4 -0
  46. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_ro.properties +4 -0
  47. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_ru.properties +4 -0
  48. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_sh.properties +4 -0
  49. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_sk.properties +4 -0
  50. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_sl.properties +4 -0
  51. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_sr.properties +4 -0
  52. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_sv.properties +4 -0
  53. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_th.properties +4 -0
  54. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_tr.properties +4 -0
  55. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_uk.properties +5 -1
  56. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_vi.properties +4 -0
  57. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_zh_CN.properties +4 -0
  58. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_zh_TW.properties +4 -0
  59. package/src/sap/suite/ui/generic/template/ObjectPage/manifest.json +1 -1
  60. package/src/sap/suite/ui/generic/template/ObjectPage/view/Details.view.xml +1 -0
  61. package/src/sap/suite/ui/generic/template/ObjectPage/view/fragments/Actions.fragment.xml +13 -13
  62. package/src/sap/suite/ui/generic/template/QuickCreate/manifest.json +1 -1
  63. package/src/sap/suite/ui/generic/template/QuickView/manifest.json +1 -1
  64. package/src/sap/suite/ui/generic/template/detailTemplates/DiscardEditHandler.js +9 -0
  65. package/src/sap/suite/ui/generic/template/genericUtilities/controlStateWrapperFactory/SmartVariantManagementWrapper.js +43 -35
  66. package/src/sap/suite/ui/generic/template/lib/AppComponent.js +1 -1
  67. package/src/sap/suite/ui/generic/template/lib/CRUDManager.js +9 -7
  68. package/src/sap/suite/ui/generic/template/lib/CommonEventHandlers.js +57 -6
  69. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillAIOrchestrator.js +182 -0
  70. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillApplyHandler.js +476 -0
  71. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController.js +311 -0
  72. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector.js +669 -0
  73. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer.js +217 -0
  74. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillHandler.js +326 -1120
  75. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer.js +491 -0
  76. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillDialog.fragment.xml +14 -10
  77. package/src/sap/suite/ui/generic/template/lib/i18n/i18n.properties +11 -8
  78. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ar.properties +23 -7
  79. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_bg.properties +23 -7
  80. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ca.properties +23 -7
  81. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cnr.properties +23 -7
  82. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cs.properties +23 -7
  83. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cy.properties +23 -7
  84. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_da.properties +23 -7
  85. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_de.properties +23 -7
  86. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_el.properties +23 -7
  87. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en.properties +23 -7
  88. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_GB.properties +23 -7
  89. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saprigi.properties +23 -7
  90. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es.properties +23 -7
  91. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es_MX.properties +23 -7
  92. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_et.properties +23 -7
  93. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fi.properties +23 -7
  94. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr.properties +24 -8
  95. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr_CA.properties +25 -9
  96. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hi.properties +23 -7
  97. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hr.properties +24 -8
  98. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hu.properties +23 -7
  99. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_id.properties +23 -7
  100. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_it.properties +23 -7
  101. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_iw.properties +23 -7
  102. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ja.properties +23 -7
  103. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_kk.properties +23 -7
  104. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ko.properties +23 -7
  105. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lt.properties +23 -7
  106. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lv.properties +23 -7
  107. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_mk.properties +23 -7
  108. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ms.properties +23 -7
  109. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_nl.properties +24 -8
  110. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_no.properties +23 -7
  111. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pl.properties +23 -7
  112. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt.properties +23 -7
  113. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt_PT.properties +23 -7
  114. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ro.properties +23 -7
  115. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ru.properties +23 -7
  116. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sh.properties +23 -7
  117. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sk.properties +23 -7
  118. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sl.properties +23 -7
  119. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sr.properties +23 -7
  120. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sv.properties +23 -7
  121. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_th.properties +23 -7
  122. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_tr.properties +23 -7
  123. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_uk.properties +23 -7
  124. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_vi.properties +23 -7
  125. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_CN.properties +23 -7
  126. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_TW.properties +23 -7
  127. package/src/sap/suite/ui/generic/template/lib/navigation/NavigationController.js +1 -1
  128. package/src/sap/suite/ui/generic/template/library.js +1 -1
  129. package/src/sap/suite/ui/generic/template/themes/base/ObjectPage.less +8 -3
  130. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/ObjectPageSectionHandler.js +0 -282
  131. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillNonUpdatableSmartForm.fragment.xml +0 -18
  132. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillUpdatebleSmartForm.fragment.xml +0 -15
@@ -0,0 +1,669 @@
1
+ sap.ui.define([
2
+ "sap/suite/ui/generic/template/genericUtilities/FeLogger",
3
+ "sap/suite/ui/generic/template/genericUtilities/FeError",
4
+ "sap/suite/ui/generic/template/genericUtilities/controlHelper"
5
+ ], function(FeLogger, FeError, controlHelper) {
6
+ "use strict";
7
+
8
+ var sClassName = "lib.ai.EasyFill.EasyFillFieldCollector";
9
+ var oLogger = new FeLogger(sClassName).getLogger();
10
+
11
+ /**
12
+ * EasyFillFieldCollector
13
+ *
14
+ * Responsible for:
15
+ * 1. Building the field mapping payload sent to the LLM (entity set properties + collection/table properties).
16
+ * 2. Determining which LLM-returned fields are updatable vs. non-updatable based on OData metadata annotations.
17
+ * 3. Building the section tree structures (Condensed and ObjectPage views) that drive the dialog's SmartForm layout.
18
+ *
19
+ * This module absorbs the logic previously split between EasyFillHandler.js (field collection functions)
20
+ * and ObjectPageSectionHandler.js (section tree building), which has been deleted.
21
+ *
22
+ * All functions are stateless and receive required context via parameters.
23
+ */
24
+ return {
25
+
26
+ // ─── Field Mapping for LLM ───────────────────────────────────────────────
27
+
28
+ /**
29
+ * Builds the flat property map for the main entity type, used as the LLM input schema.
30
+ * Each entry contains the field label and OData data type.
31
+ *
32
+ * @param {object} oEntityType - OData entity type metadata object
33
+ * @param {object} oObjectPageContext - current ObjectPage binding context (used for visibility checks)
34
+ * @param {function} fnGetLabel - function(oProperty) → string label
35
+ * @returns {object} mPropertyMap keyed by property name
36
+ */
37
+ getEntitySetMap: function(oEntityType, oObjectPageContext, fnGetLabel) {
38
+ var mPropertyMap = {};
39
+ if (!oEntityType || !Array.isArray(oEntityType.property)) {
40
+ oLogger.warning("getEntitySetMap: no entity type or properties found");
41
+ return mPropertyMap;
42
+ }
43
+ oEntityType.property.forEach(function(oProperty) {
44
+ if (this.isVisible(oProperty, oObjectPageContext)) {
45
+ mPropertyMap[oProperty.name] = {
46
+ description: fnGetLabel(oProperty),
47
+ dataType: oProperty.type
48
+ };
49
+ }
50
+ }.bind(this));
51
+ return mPropertyMap;
52
+ },
53
+
54
+ /**
55
+ * Appends collection (SmartTable) field descriptors to an existing property map.
56
+ * Resets the collection context tracking maps before re-populating.
57
+ * Currently supports a single SmartTable (multi-table is a future enhancement).
58
+ *
59
+ * @param {object} mPropertyMap - the map to append to (mutated in place)
60
+ * @param {object} oObjectPage - the ObjectPage control (used to find SmartTables)
61
+ * @param {object} oObjectPageModel - the OData model of the ObjectPage
62
+ * @param {object} oTemplateUtils - FE template utilities (used for PresentationControlHandler)
63
+ * @param {object} mCollectionState - mutable collection state: { mCollectionRowContexts, mCollectionRowContextsByPath, mCollectionProperties, mCollectionEntityTypes }
64
+ * @param {function} fnGetLabel - function(oProperty) → string label
65
+ */
66
+ appendCollectionMap: function(mPropertyMap, oObjectPage, oObjectPageModel, oTemplateUtils, mCollectionState, fnGetLabel) {
67
+ // Reset collection tracking before re-populating
68
+ mCollectionState.mCollectionRowContexts = Object.create(null);
69
+ mCollectionState.mCollectionRowContextsByPath = Object.create(null);
70
+ mCollectionState.mCollectionProperties = Object.create(null);
71
+ mCollectionState.mCollectionEntityTypes = Object.create(null);
72
+
73
+ var aSmartTables = this.getObjectPageSmartTables(oObjectPage);
74
+ if (!aSmartTables.length) {
75
+ oLogger.debug("appendCollectionMap: no SmartTables found on ObjectPage");
76
+ return;
77
+ }
78
+
79
+ var oMetaModel = oObjectPageModel && oObjectPageModel.getMetaModel();
80
+ if (!oMetaModel) {
81
+ oLogger.warning("appendCollectionMap: no MetaModel available");
82
+ return;
83
+ }
84
+
85
+ aSmartTables.forEach(function(oSmartTable) {
86
+ var oHandler = oTemplateUtils.oServices.oPresentationControlHandlerFactory.getPresentationControlHandler(oSmartTable);
87
+ if (!oHandler) {
88
+ return;
89
+ }
90
+
91
+ // Navigation property name is derived from the binding path (e.g. "/to_Items" → "to_Items")
92
+ var sNavProperty = this._extractNavigationPropertyName(oHandler.getBindingPath());
93
+ if (!sNavProperty) {
94
+ oLogger.warning("appendCollectionMap: could not extract nav property from binding path");
95
+ return;
96
+ }
97
+
98
+ var aVisibleProps = (oHandler.findVisibleColumnProperties && oHandler.findVisibleColumnProperties()) || [];
99
+ if (!aVisibleProps.length) {
100
+ return;
101
+ }
102
+
103
+ var aRowContexts = (oHandler.getCurrentContexts && oHandler.getCurrentContexts()) || [];
104
+ var sEntitySetName = oSmartTable.getEntitySet();
105
+ var oEntitySet = oMetaModel.getODataEntitySet(sEntitySetName);
106
+ var oEntityType = oEntitySet && oMetaModel.getODataEntityType(oEntitySet.entityType);
107
+ if (!oEntityType || !oEntityType.property) {
108
+ return;
109
+ }
110
+
111
+ // Build a name→property lookup for this collection entity type
112
+ var mPropertyByName = Object.create(null);
113
+ oEntityType.property.forEach(function(oProperty) {
114
+ mPropertyByName[oProperty.name] = oProperty;
115
+ });
116
+
117
+ // Build item-properties map for visible columns only
118
+ var oItemProperties = {};
119
+ aVisibleProps.forEach(function(sPropName) {
120
+ var oProperty = mPropertyByName[sPropName];
121
+ if (!oProperty) { return; }
122
+ oItemProperties[sPropName] = {
123
+ description: fnGetLabel(oProperty) || sPropName,
124
+ dataType: oProperty.type
125
+ };
126
+ });
127
+
128
+ if (!Object.keys(oItemProperties).length) {
129
+ return;
130
+ }
131
+
132
+ // Build row data array for LLM context (current table rows with their values)
133
+ var aCurrentItems = [];
134
+ var mRowsByPath = Object.create(null);
135
+ aRowContexts.forEach(function(oContext, iIndex) {
136
+ if (!oContext || !oContext.getObject) { return; }
137
+ var sContextPath = oContext.getPath && oContext.getPath();
138
+ var oRowData = { _rowIndex: iIndex };
139
+ if (typeof sContextPath === "string") {
140
+ oRowData._contextPath = sContextPath;
141
+ mRowsByPath[sContextPath] = { context: oContext, rowIndex: iIndex };
142
+ }
143
+ Object.keys(oItemProperties).forEach(function(sPropName) {
144
+ var vValue = oContext.getProperty(sPropName);
145
+ if (typeof vValue !== "object") {
146
+ oRowData[sPropName] = vValue;
147
+ }
148
+ });
149
+ aCurrentItems.push(oRowData);
150
+ });
151
+
152
+ // Populate collection state tracking maps
153
+ mCollectionState.mCollectionRowContexts[sNavProperty] = aRowContexts;
154
+ mCollectionState.mCollectionRowContextsByPath[sNavProperty] = mRowsByPath;
155
+ mCollectionState.mCollectionProperties[sNavProperty] = mPropertyByName;
156
+ mCollectionState.mCollectionEntityTypes[sNavProperty] = oEntityType;
157
+
158
+ // Add collection entry to the LLM field mapping
159
+ mPropertyMap[sNavProperty] = {
160
+ description: typeof oSmartTable.getHeader() === "string" ? oSmartTable.getHeader() : sNavProperty,
161
+ dataType: "Collection",
162
+ isCollection: true,
163
+ itemProperties: oItemProperties,
164
+ currentItems: aCurrentItems,
165
+ entitySet: sEntitySetName,
166
+ section: typeof oSmartTable.getHeader() === "string" ? oSmartTable.getHeader() : sNavProperty
167
+ };
168
+ }.bind(this));
169
+ },
170
+
171
+ // ─── Updatable / Non-Updatable Classification ────────────────────────────
172
+
173
+ /**
174
+ * Splits LLM-returned simple fields into updatable and non-updatable buckets
175
+ * based on OData metadata annotations (sap:updatable, FieldControl, etc.).
176
+ *
177
+ * @param {object} oAiCallResult - flat map of { fieldName: aiValue } from the LLM
178
+ * @param {object} oEntityType - OData entity type metadata
179
+ * @param {object} oObjectPageContext - current ObjectPage binding context
180
+ * @returns {{ updatableFields: object, nonUpdatableFields: object }}
181
+ */
182
+ getUpdatableAndNonUpdatableFields: function(oAiCallResult, oEntityType, oObjectPageContext) {
183
+ var updatableFields = {};
184
+ var nonUpdatableFields = {};
185
+
186
+ if (!oEntityType || !Array.isArray(oEntityType.property)) {
187
+ oLogger.warning("getUpdatableAndNonUpdatableFields: no entity type available");
188
+ return { updatableFields: updatableFields, nonUpdatableFields: nonUpdatableFields };
189
+ }
190
+
191
+ Object.keys(oAiCallResult).forEach(function(sKey) {
192
+ var oProperty = oEntityType.property.find(function(oP) { return oP.name === sKey; });
193
+ if (!oProperty) {
194
+ // Property not found in metadata → treat as non-updatable
195
+ nonUpdatableFields[sKey] = oAiCallResult[sKey];
196
+ return;
197
+ }
198
+ if (this.isEditable(oProperty, oObjectPageContext)) {
199
+ updatableFields[sKey] = oAiCallResult[sKey];
200
+ } else {
201
+ nonUpdatableFields[sKey] = oAiCallResult[sKey];
202
+ }
203
+ }.bind(this));
204
+
205
+ return { updatableFields: updatableFields, nonUpdatableFields: nonUpdatableFields };
206
+ },
207
+
208
+ // ─── Visibility & Editability Checks ─────────────────────────────────────
209
+
210
+ /**
211
+ * Checks whether a property should be included in the LLM field mapping.
212
+ * Evaluates UI.Hidden, FieldControl (Hidden), and sap:field-control annotations.
213
+ *
214
+ * @param {object} oProperty - OData property metadata
215
+ * @param {object} oObjectPageContext - current ObjectPage binding context
216
+ * @returns {boolean}
217
+ */
218
+ isVisible: function(oProperty, oObjectPageContext) {
219
+ // Check "com.sap.vocabularies.Common.v1.FieldControl" for Hidden enum
220
+ var oAnnotation = oProperty["com.sap.vocabularies.Common.v1.FieldControl"];
221
+ if (oAnnotation && oAnnotation["EnumMember"]) {
222
+ return oAnnotation["EnumMember"] !== "com.sap.vocabularies.Common.v1.FieldControlType/Hidden";
223
+ }
224
+
225
+ // Check "com.sap.vocabularies.UI.v1.Hidden" annotation
226
+ var oHiddenAnnotation = oProperty["com.sap.vocabularies.UI.v1.Hidden"];
227
+ if (oHiddenAnnotation) {
228
+ if (oHiddenAnnotation["Bool"]) {
229
+ return oHiddenAnnotation["Bool"] !== "true";
230
+ }
231
+ if (oHiddenAnnotation["Path"] && oObjectPageContext) {
232
+ return !oObjectPageContext.getObject()[oHiddenAnnotation["Path"]];
233
+ }
234
+ // <Annotation Term="UI.Hidden"/> with no value → hidden
235
+ return false;
236
+ }
237
+
238
+ // Check "sap:field-control" attribute (value 0 = hidden)
239
+ var sFieldControl = oProperty["sap:field-control"];
240
+ if (sFieldControl && oObjectPageContext) {
241
+ var iFieldControlValue = oObjectPageContext.getObject()[sFieldControl];
242
+ return iFieldControlValue !== 0;
243
+ }
244
+
245
+ // No restriction → visible by default
246
+ return true;
247
+ },
248
+
249
+ /**
250
+ * Checks whether a property is editable based on OData annotations.
251
+ * Uses the current ObjectPage context object for dynamic field-control checks.
252
+ *
253
+ * Priority: sap:updatable → FieldControl (EnumMember) → sap:field-control (dynamic value)
254
+ *
255
+ * Field control values:
256
+ * 0 = Hidden, 1 = ReadOnly, 3 = Editable, 7 = Mandatory (editable)
257
+ *
258
+ * @param {object} oProperty - OData property metadata
259
+ * @param {object} oObjectPageContext - current ObjectPage binding context
260
+ * @returns {boolean}
261
+ */
262
+ isEditable: function(oProperty, oObjectPageContext) {
263
+ var oContextObject = oObjectPageContext && oObjectPageContext.getObject && oObjectPageContext.getObject();
264
+ return this.isEditableByContextObject(oProperty, oContextObject);
265
+ },
266
+
267
+ /**
268
+ * Checks editability given a plain context data object (not a binding context).
269
+ * Used for both entity-level and collection-row-level editability checks.
270
+ *
271
+ * @param {object} oProperty - OData property metadata
272
+ * @param {object} oContextObject - plain JS object of the current row/entity data
273
+ * @returns {boolean}
274
+ */
275
+ isEditableByContextObject: function(oProperty, oContextObject) {
276
+ if (!oProperty) {
277
+ return false;
278
+ }
279
+
280
+ // Check sap:updatable — static annotation, highest priority
281
+ var sUpdatable = oProperty["sap:updatable"];
282
+ if (sUpdatable) {
283
+ return sUpdatable !== "false";
284
+ }
285
+
286
+ // Check "com.sap.vocabularies.Common.v1.FieldControl" enum member
287
+ var oAnnotation = oProperty["com.sap.vocabularies.Common.v1.FieldControl"];
288
+ if (oAnnotation && oAnnotation["EnumMember"]) {
289
+ return oAnnotation["EnumMember"] !== "com.sap.vocabularies.Common.v1.FieldControlType/ReadOnly";
290
+ }
291
+
292
+ // Check "sap:field-control" dynamic path (value 1 = read-only)
293
+ var sFieldControl = oProperty["sap:field-control"];
294
+ if (sFieldControl) {
295
+ var iFieldControlValue = oContextObject && oContextObject[sFieldControl];
296
+ return iFieldControlValue !== 1;
297
+ }
298
+
299
+ // No restriction → editable by default
300
+ return true;
301
+ },
302
+
303
+ // ─── Section Tree Building (from ObjectPageSectionHandler) ───────────────
304
+
305
+ /**
306
+ * Builds a flat section tree for the Condensed view mode.
307
+ * All LLM result fields are grouped into a single unnamed section.
308
+ *
309
+ * @param {object} oLLMResult - flat map of { fieldName: value } from the LLM
310
+ * @returns {Array} section tree array
311
+ */
312
+ getSectionsForCondensedView: function(oLLMResult) {
313
+ return this._transformToTreeExpanded(oLLMResult);
314
+ },
315
+
316
+ /**
317
+ * Builds a structured section tree for the ObjectPage view mode.
318
+ * Mirrors the ObjectPage section/facet structure and filters to only include
319
+ * fields returned by the LLM.
320
+ *
321
+ * @param {object} oEntityType - OData entity type metadata
322
+ * @param {object} oLLMResult - flat map of { fieldName: value } from the LLM
323
+ * @param {object} oRb - i18n resource bundle
324
+ * @returns {Array} filtered section tree array
325
+ */
326
+ getSectionsForObjectPageView: function(oEntityType, oLLMResult, oRb) {
327
+ var aSections = [];
328
+ aSections = aSections
329
+ .concat(this._getSectionsForHeader(oEntityType, oRb))
330
+ .concat(this._getSectionsForObjectPageFacets(oEntityType));
331
+ var aTree = this._transformToTree(aSections);
332
+ return this._filterTreeByLLMResult(aTree, oLLMResult);
333
+ },
334
+
335
+ // ─── SmartTable Utilities ─────────────────────────────────────────────────
336
+
337
+ /**
338
+ * Finds all SmartTable instances on the ObjectPage that are of a supported type
339
+ * (Responsive Table or UI5 Grid Table).
340
+ *
341
+ * @param {object} oObjectPage - the ObjectPage control
342
+ * @returns {Array} array of sap.ui.comp.smarttable.SmartTable controls
343
+ */
344
+ getObjectPageSmartTables: function(oObjectPage) {
345
+ if (!oObjectPage || !oObjectPage.findAggregatedObjects) {
346
+ return [];
347
+ }
348
+ // Note: Currently restricted to Responsive and Grid tables.
349
+ // Custom SmartTables are also included — future work should filter
350
+ // to only annotation-defined tables to avoid false positives.
351
+ return oObjectPage.findAggregatedObjects(true, function(oControl) {
352
+ var bIsSmartTable = controlHelper.isSmartTable(oControl);
353
+ return bIsSmartTable && (
354
+ oControl.getTable().isA("sap.m.Table") ||
355
+ oControl.getTable().isA("sap.ui.table.Table")
356
+ );
357
+ }) || [];
358
+ },
359
+
360
+ /**
361
+ * Validates whether a given entity set name corresponds to a known SmartTable on the page.
362
+ *
363
+ * @param {string} sEntitySet - entity set name to validate
364
+ * @param {object} oObjectPage - the ObjectPage control
365
+ * @returns {{ isValid: boolean, smartTable: object|undefined }}
366
+ */
367
+ isValidSmartTable: function(sEntitySet, oObjectPage) {
368
+ var aSmartTables = this.getObjectPageSmartTables(oObjectPage);
369
+ var oSmartTable = aSmartTables.find(function(oTable) {
370
+ return oTable.getEntitySet() === sEntitySet;
371
+ });
372
+ return {
373
+ isValid: !!oSmartTable,
374
+ smartTable: oSmartTable
375
+ };
376
+ },
377
+
378
+ // ─── Private Helpers ─────────────────────────────────────────────────────
379
+
380
+ /**
381
+ * Extracts the navigation property name from an OData binding path.
382
+ * E.g. "/to_Items" → "to_Items", "/SalesOrderSet/to_Items" → "to_Items"
383
+ *
384
+ * @param {string} sBindingPath
385
+ * @returns {string|null}
386
+ */
387
+ _extractNavigationPropertyName: function(sBindingPath) {
388
+ if (!sBindingPath || typeof sBindingPath !== "string") {
389
+ return null;
390
+ }
391
+ var aParts = sBindingPath.split("/").filter(Boolean);
392
+ return aParts.length ? aParts[aParts.length - 1] : null;
393
+ },
394
+
395
+ /**
396
+ * Builds the "condensed" tree: all LLM fields in a single flat group.
397
+ * @private
398
+ */
399
+ _transformToTreeExpanded: function(oLLMRes) {
400
+ return [
401
+ {
402
+ title: "",
403
+ fields: [],
404
+ children: [{
405
+ title: "",
406
+ fields: Object.keys(oLLMRes),
407
+ id: this._generateId("", Object.keys(oLLMRes))
408
+ }]
409
+ }
410
+ ];
411
+ },
412
+
413
+ /**
414
+ * Builds the "ObjectPage" section tree from a flat array of { title[], property } entries.
415
+ * Groups entries by their title path hierarchy.
416
+ * @private
417
+ */
418
+ _transformToTree: function(data) {
419
+ var rootMap = {};
420
+ var seen = new Set();
421
+ var that = this;
422
+
423
+ data.forEach(function(entry) {
424
+ var title = entry.title;
425
+ var property = entry.property;
426
+ if (seen.has(property)) { return; }
427
+ seen.add(property);
428
+
429
+ var currentMap = rootMap;
430
+ var sTitlePath = "";
431
+
432
+ title.forEach(function(level, index) {
433
+ var isLast = index === title.length - 1;
434
+ sTitlePath = sTitlePath ? (sTitlePath + "_" + level) : level;
435
+
436
+ if (!currentMap[level]) {
437
+ currentMap[level] = {
438
+ title: level,
439
+ titlePath: sTitlePath,
440
+ fields: [],
441
+ _childrenMap: {}
442
+ };
443
+ }
444
+
445
+ if (isLast) {
446
+ currentMap[level].fields.push(property);
447
+ } else {
448
+ currentMap = currentMap[level]._childrenMap;
449
+ }
450
+ });
451
+ });
452
+
453
+ function convertToArray(oMap) {
454
+ return Object.values(oMap).map(function(oNode) {
455
+ return {
456
+ title: oNode.title,
457
+ id: that._generateId(oNode.titlePath, oNode.fields),
458
+ fields: oNode.fields,
459
+ children: oNode._childrenMap ? convertToArray(oNode._childrenMap) : []
460
+ };
461
+ });
462
+ }
463
+
464
+ return convertToArray(rootMap);
465
+ },
466
+
467
+ /**
468
+ * Generates a stable DOM-safe ID for a SmartForm section based on its title path and fields.
469
+ * @private
470
+ */
471
+ _generateId: function(sTitlePath, aFields) {
472
+ var sSanitizedTitle = sTitlePath
473
+ .replace(/\s+/g, "_")
474
+ .replace(/[^a-zA-Z0-9_]/g, "")
475
+ .toLowerCase();
476
+ var sFieldsPart = aFields
477
+ .join("_")
478
+ .replace(/[^a-zA-Z0-9_]/g, "")
479
+ .toLowerCase();
480
+ return "EasyFillSmartForm_" + sSanitizedTitle + (sFieldsPart ? "_" + sFieldsPart : "");
481
+ },
482
+
483
+ /**
484
+ * Filters a section tree to only include nodes that contain at least one LLM-returned field.
485
+ * Fields not matched to any section are grouped into an "unableToFill" catch-all node.
486
+ * @private
487
+ */
488
+ _filterTreeByLLMResult: function(aTree, oLLMResult) {
489
+ var aRequestedKeys = Object.keys(oLLMResult);
490
+ var aMatchedKeys = new Set();
491
+ var that = this;
492
+
493
+ function filterNode(oNode, sParentPath) {
494
+ var sTitlePath = sParentPath ? (sParentPath + "_" + oNode.title) : oNode.title;
495
+
496
+ var aFilteredFields = oNode.fields.filter(function(sField) {
497
+ if (aRequestedKeys.includes(sField)) {
498
+ aMatchedKeys.add(sField);
499
+ return true;
500
+ }
501
+ return false;
502
+ });
503
+
504
+ var aFilteredChildren = oNode.children
505
+ .map(function(oChild) { return filterNode(oChild, sTitlePath); })
506
+ .filter(function(oChild) { return oChild.fields.length > 0 || oChild.children.length > 0; });
507
+
508
+ return {
509
+ title: oNode.title,
510
+ id: that._generateId(sTitlePath, aFilteredFields),
511
+ fields: aFilteredFields,
512
+ children: aFilteredChildren
513
+ };
514
+ }
515
+
516
+ var aFilteredTree = aTree
517
+ .map(function(oNode) { return filterNode(oNode, ""); })
518
+ .filter(function(oNode) { return oNode.fields.length > 0 || oNode.children.length > 0; });
519
+
520
+ // Any LLM fields that didn't map to a known section go into a special "unableToFill" node
521
+ var aUnmatchedKeys = aRequestedKeys.filter(function(sKey) { return !aMatchedKeys.has(sKey); });
522
+ if (aUnmatchedKeys.length > 0) {
523
+ aFilteredTree.push({
524
+ title: "unableToFill",
525
+ id: this._generateId("unableToFill", aUnmatchedKeys),
526
+ fields: aUnmatchedKeys,
527
+ children: []
528
+ });
529
+ }
530
+
531
+ return aFilteredTree;
532
+ },
533
+
534
+ /**
535
+ * Extracts section entries from ObjectPage header annotations (HeaderInfo + HeaderFacets).
536
+ * @private
537
+ */
538
+ _getSectionsForHeader: function(oEntityType, oRb) {
539
+ var aRes = [];
540
+ var oHeaderInfo = oEntityType["com.sap.vocabularies.UI.v1.HeaderInfo"];
541
+ var aHeaderFacets = oEntityType["com.sap.vocabularies.UI.v1.HeaderFacets"];
542
+
543
+ if (oHeaderInfo && Object.keys(oHeaderInfo).length > 0) {
544
+ var aKeys = Object.keys(oHeaderInfo).filter(function(sKey) {
545
+ return oHeaderInfo[sKey].RecordType === "com.sap.vocabularies.UI.v1.DataField";
546
+ });
547
+ aKeys.forEach(function(sKey) {
548
+ aRes.push({
549
+ title: [oRb.getText("EASY_FILL_SECTION_HEADER"), oRb.getText("EASY_FILL_SECTION_HEADER")],
550
+ property: oHeaderInfo[sKey].Value.Path
551
+ });
552
+ });
553
+ }
554
+
555
+ if (aHeaderFacets && aHeaderFacets.length > 0) {
556
+ var aSections = this._extractSectionInfoFromReferenceFacet(oEntityType, aHeaderFacets);
557
+ aSections.forEach(function(oSection) {
558
+ oSection.title = [oRb.getText("EASY_FILL_SECTION_HEADER"), oRb.getText("EASY_FILL_SECTION_HEADER")];
559
+ });
560
+ aRes.push.apply(aRes, aSections);
561
+ }
562
+
563
+ return aRes;
564
+ },
565
+
566
+ /**
567
+ * Extracts section entries from the main ObjectPage facet annotations (UI.Facets).
568
+ * @private
569
+ */
570
+ _getSectionsForObjectPageFacets: function(oEntityType) {
571
+ var aSectionFacets = oEntityType["com.sap.vocabularies.UI.v1.Facets"];
572
+ if (aSectionFacets && aSectionFacets.length > 0) {
573
+ return this._extractFacetInfoRecursively(oEntityType, aSectionFacets, []);
574
+ }
575
+ return [];
576
+ },
577
+
578
+ /**
579
+ * Recursively walks CollectionFacets and ReferenceFacets to extract { title, property } pairs.
580
+ * @private
581
+ */
582
+ _extractFacetInfoRecursively: function(oEntityType, aFacets, aTitle) {
583
+ var aRes = [];
584
+ aFacets.forEach(function(oFacet) {
585
+ if (oFacet.RecordType === "com.sap.vocabularies.UI.v1.CollectionFacet") {
586
+ var aInnerFacets = oFacet.Facets;
587
+ if (aInnerFacets && aInnerFacets.length > 0) {
588
+ var sLabel = oFacet.Label && oFacet.Label.String ? oFacet.Label.String : null;
589
+ aRes = aRes.concat(this._extractFacetInfoRecursively(
590
+ oEntityType,
591
+ aInnerFacets,
592
+ sLabel ? aTitle.concat(sLabel) : aTitle
593
+ ));
594
+ }
595
+ } else {
596
+ aRes = aRes.concat(this._extractSectionInfoFromReferenceFacet(oEntityType, [oFacet], aTitle));
597
+ }
598
+ }.bind(this));
599
+ return aRes;
600
+ },
601
+
602
+ /**
603
+ * Extracts { title[], property } entries from a set of ReferenceFacets.
604
+ * Skips IntentBasedNavigation, ForAction, and ForAnnotation record types (not supported).
605
+ * @private
606
+ */
607
+ _extractSectionInfoFromReferenceFacet: function(oEntityType, aFacets, aText) {
608
+ aText = aText || [];
609
+ var aRes = [];
610
+ aFacets.forEach(function(oFacet) {
611
+ // Skip unsupported annotation types
612
+ if (!this._isSupportedRecordType(oFacet.RecordType)) {
613
+ return;
614
+ }
615
+
616
+ var aTempText = aText.slice();
617
+ var sAnnotationPath = oFacet.Target.AnnotationPath.substring(1);
618
+ var oAnnotation = oEntityType[sAnnotationPath];
619
+ var sTitle = null;
620
+
621
+ // Title resolution priority: Facet.Label → Annotation.Label → Facet.Title → Annotation.Title
622
+ if (oFacet && oFacet.Label) {
623
+ sTitle = oFacet.Label.String;
624
+ } else if (oAnnotation && oAnnotation.Label) {
625
+ sTitle = oAnnotation.Label.String;
626
+ } else if (oFacet && oFacet.Title) {
627
+ sTitle = oFacet.Title.String;
628
+ } else if (oAnnotation && oAnnotation.Title) {
629
+ sTitle = oAnnotation.Title.String;
630
+ }
631
+
632
+ if (sTitle) {
633
+ aTempText.push(sTitle);
634
+ }
635
+
636
+ if (oAnnotation) {
637
+ if (oAnnotation.Data) {
638
+ // FieldGroup / Form — iterate Data array
639
+ oAnnotation.Data.forEach(function(oData) {
640
+ if (!this._isSupportedRecordType(oData.RecordType)) { return; }
641
+ aRes.push({ title: aTempText, property: oData.Value.Path });
642
+ }.bind(this));
643
+ } else if (Array.isArray(oAnnotation)) {
644
+ // LineItem — iterate annotation array directly
645
+ oAnnotation.forEach(function(oAnno) {
646
+ if (!this._isSupportedRecordType(oAnno.RecordType)) { return; }
647
+ aRes.push({ title: aTempText, property: oAnno.Value.Path });
648
+ }.bind(this));
649
+ } else if (oAnnotation.Value && oAnnotation.Value.Path) {
650
+ // Single-value annotation (e.g. DataPoint)
651
+ aRes.push({ title: aTempText, property: oAnnotation.Value.Path });
652
+ }
653
+ }
654
+ }.bind(this));
655
+ return aRes;
656
+ },
657
+
658
+ /**
659
+ * Returns false for record types that EasyFill does not support rendering.
660
+ * Currently excludes: IntentBasedNavigation, ForAnnotation, ForAction.
661
+ * @private
662
+ */
663
+ _isSupportedRecordType: function(sRecordType) {
664
+ return sRecordType !== "com.sap.vocabularies.UI.v1.DataFieldForIntentBasedNavigation" &&
665
+ sRecordType !== "com.sap.vocabularies.UI.v1.DataFieldForAnnotation" &&
666
+ sRecordType !== "com.sap.vocabularies.UI.v1.DataFieldForAction";
667
+ }
668
+ };
669
+ });