@sapui5/sap.suite.ui.generic.template 1.151.1 → 1.152.1

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 (35) 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/control/visualfilterbar/SmartVisualFilterBar.js +4 -3
  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/Component.js +4 -2
  7. package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_sv.properties +1 -1
  8. package/src/sap/suite/ui/generic/template/ListReport/manifest.json +1 -1
  9. package/src/sap/suite/ui/generic/template/ListReport/view/fragments/FullscreenPage.fragment.xml +3 -0
  10. package/src/sap/suite/ui/generic/template/ObjectPage/manifest.json +1 -1
  11. package/src/sap/suite/ui/generic/template/QuickCreate/manifest.json +1 -1
  12. package/src/sap/suite/ui/generic/template/QuickView/manifest.json +1 -1
  13. package/src/sap/suite/ui/generic/template/genericUtilities/controlStateWrapperFactory/SmartFilterBarWrapper.js +5 -4
  14. package/src/sap/suite/ui/generic/template/js/AnnotationHelper.js +21 -17
  15. package/src/sap/suite/ui/generic/template/lib/AppComponent.js +1 -1
  16. package/src/sap/suite/ui/generic/template/lib/Application.js +0 -13
  17. package/src/sap/suite/ui/generic/template/lib/CommonEventHandlers.js +4 -19
  18. package/src/sap/suite/ui/generic/template/lib/CommonUtils.js +4 -4
  19. package/src/sap/suite/ui/generic/template/lib/ShareUtils.js +1 -1
  20. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillAIOrchestrator.js +1 -6
  21. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillApplyHandler.js +25 -44
  22. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController.js +80 -64
  23. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector.js +442 -259
  24. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer.js +125 -161
  25. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillHandler.js +134 -160
  26. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer.js +55 -189
  27. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillDialog.fragment.xml +67 -85
  28. package/src/sap/suite/ui/generic/template/lib/ai/EasyFilterBarHandler.js +27 -3
  29. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saptrc.properties +6 -6
  30. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fi.properties +4 -4
  31. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sl.properties +1 -1
  32. package/src/sap/suite/ui/generic/template/lib/insights/InsightsCardHelper.js +16 -4
  33. package/src/sap/suite/ui/generic/template/lib/navigation/NavigationController.js +3 -2
  34. package/src/sap/suite/ui/generic/template/library.js +1 -1
  35. package/src/sap/suite/ui/generic/template/themes/base/ObjectPage.less +1 -1
@@ -16,8 +16,8 @@ sap.ui.define([
16
16
  "sap/uxap/ObjectPageSubSection",
17
17
  "sap/ui/core/CustomData",
18
18
  "sap/suite/ui/generic/template/genericUtilities/FeLogger",
19
- "sap/suite/ui/generic/template/genericUtilities/FeError"
20
- ], function(SmartField, GenericTag, MessageStrip, Text, Title, Table, Column, ColumnListItem, VBox, HBox, ScrollContainer, SegmentedButton, SegmentedButtonItem, ObjectPageSection, ObjectPageSubSection, CustomData, FeLogger, FeError) {
19
+ "sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector"
20
+ ], function(SmartField, GenericTag, MessageStrip, Text, Title, Table, Column, ColumnListItem, VBox, HBox, ScrollContainer, SegmentedButton, SegmentedButtonItem, ObjectPageSection, ObjectPageSubSection, CustomData, FeLogger, EasyFillFieldCollector) {
21
21
  "use strict";
22
22
 
23
23
  var sClassName = "lib.ai.EasyFill.EasyFillTableRenderer";
@@ -39,14 +39,8 @@ sap.ui.define([
39
39
  * 1. Creates transient OData row contexts for preview rendering.
40
40
  * 2. Creates a "Previous Values" read-only table and a "New Values" editable SmartField table.
41
41
  * 3. Renders a SegmentedButton toggle between the two views.
42
- * 4. Injects each collection as an ObjectPageSection into the shared EasyFillObjectPageLayout
43
- * so the OPL header and anchor bar remain visible while scrolling through table content.
42
+ * 4. Injects each collection as an ObjectPageSection into the shared EasyFillObjectPageLayout.
44
43
  * 5. Provides reset logic to discard staged values if the user cancels.
45
- *
46
- * Create mode:
47
- * - Rows without _rowIndex or _contextPath are treated as new rows.
48
- * - New rows are rendered as editable SmartField rows highlighted with "Information" row highlight.
49
- * - Transient contexts for new rows are stored in oTableStagingState.mPendingNewRowContexts.
50
44
  * - On confirm, EasyFillApplyHandler activates these contexts via submitChanges.
51
45
  * - Only one new row per table is created (extra new rows are suppressed upstream).
52
46
  */
@@ -78,10 +72,6 @@ sap.ui.define([
78
72
  return;
79
73
  }
80
74
 
81
- // Remove previously injected table content before re-rendering.
82
- // On mode switch (bReuseStaging=true) cleanup is done BEFORE the model update
83
- // in onSelectionChange, so we skip it here to avoid operating on the already-fresh OPL.
84
- // On first render (bReuseStaging=false) we do the cleanup here.
85
75
  if (!bReuseStaging) {
86
76
  this._removeTableSections(oObjectPageLayout);
87
77
  }
@@ -108,7 +98,7 @@ sap.ui.define([
108
98
  // Bound sections always have a "easyFillDialogModel" binding context; manually-added
109
99
  // ones never do, so there is no ambiguity.
110
100
  var oFilledFieldsSubSection = null; // condensed mode: add table as block inside here
111
- var oUnfilledFieldsSection = null; // OP mode: insert standalone section before this
101
+ var oUnfilledFieldsSection = null; // OP mode: insert standalone section before this
112
102
  oObjectPageLayout.getSections().forEach(function(oSection) {
113
103
  var oCtx = oSection.getBindingContext("easyFillDialogModel");
114
104
  if (!oCtx) { return; }
@@ -127,32 +117,26 @@ sap.ui.define([
127
117
 
128
118
  // iInsertOffset keeps multiple consecutive tables in the correct order when
129
119
  // inserting standalone sections before the Unfilled Fields section (OP mode).
120
+ // iBaseInsertIndex is captured ONCE before any insertions so it does not shift
121
+ // as tables are added — each table is inserted at base + offset, not re-queried.
122
+ var iBaseInsertIndex = oUnfilledFieldsSection
123
+ ? oObjectPageLayout.indexOfSection(oUnfilledFieldsSection) : -1;
130
124
  var iInsertOffset = 0;
131
-
132
125
  var that = this;
133
126
  var oRejected = mCollectionsWithRejectedNewRows || {};
134
127
 
135
- // Render "all rejected" tables: AI only proposed new rows, creation not allowed.
136
- // These collections have no entry in mCollectionUpdates so they need their own loop.
137
128
  Object.keys(oRejected).forEach(function(sCollectionName) {
138
- if (oRejected[sCollectionName] !== "all") {
139
- return; // "mixed" is handled below alongside the diff table
140
- }
129
+ if (oRejected[sCollectionName] !== "all") { return; }
141
130
  var oCollectionMeta = mFieldMapping[sCollectionName];
142
131
  var sTitle = (oCollectionMeta && oCollectionMeta.description) || sCollectionName;
143
- var oStrip = new MessageStrip({
144
- text: oRb.getText("EASY_FILL_TABLE_NEW_ROW_NOT_ALLOWED"),
145
- type: "Warning",
146
- showIcon: true
147
- });
132
+ var oStrip = new MessageStrip({ text: oRb.getText("EASY_FILL_TABLE_NEW_ROW_NOT_ALLOWED"), type: "Warning", showIcon: true });
148
133
  var oNotifVBox = new VBox({ items: [oStrip] });
149
134
  oNotifVBox.addCustomData(new CustomData({ key: sTableSectionMarker, value: "true" }));
150
135
  var oNotifSubSection = new ObjectPageSubSection({ title: sTitle, titleUppercase: false, blocks: [oNotifVBox] });
151
136
  var oNotifSection = new ObjectPageSection({ title: sTitle, titleUppercase: false, subSections: [oNotifSubSection] });
152
137
  oNotifSection.addCustomData(new CustomData({ key: sTableSectionMarker, value: "true" }));
153
- if (oUnfilledFieldsSection) {
154
- var iRejectedAt = oObjectPageLayout.indexOfSection(oUnfilledFieldsSection);
155
- oObjectPageLayout.insertSection(oNotifSection, iRejectedAt + iInsertOffset);
138
+ if (iBaseInsertIndex >= 0) {
139
+ oObjectPageLayout.insertSection(oNotifSection, iBaseInsertIndex + iInsertOffset);
156
140
  iInsertOffset++;
157
141
  } else {
158
142
  oObjectPageLayout.addSection(oNotifSection);
@@ -162,100 +146,56 @@ sap.ui.define([
162
146
 
163
147
  Object.keys(mCollectionUpdates).forEach(function(sCollectionName) {
164
148
  var oCollectionMeta = mFieldMapping[sCollectionName];
165
- if (!oCollectionMeta || !oCollectionMeta.itemProperties) {
166
- return;
167
- }
168
-
149
+ if (!oCollectionMeta || !oCollectionMeta.itemProperties) { return; }
169
150
  var aPropertyNames = Object.keys(oCollectionMeta.itemProperties || {});
170
- if (!aPropertyNames.length) {
171
- return;
172
- }
173
- var aPreviewRows = (oTableStagingState.mStagedRows[sCollectionName] || []).filter(function(oStagedRow) {
174
- return oStagedRow.isPreviewVisible;
175
- });
151
+ if (!aPropertyNames.length) { return; }
152
+
153
+ var aPreviewRows = (oTableStagingState.mStagedRows[sCollectionName] || []).filter(function(r) { return r.isPreviewVisible; });
176
154
  var aNewRows = oTableStagingState.mPendingNewRowContexts[sCollectionName] || [];
177
155
  var bHasUneditableChanges = aPreviewRows.some(function(oPreviewRow) {
178
- return oPreviewRow.update && that._hasUneditableCollectionChanges(oPreviewRow.update, sCollectionName, {
179
- context: oPreviewRow.realContext,
180
- rowIndex: oPreviewRow.rowIndex
181
- }, mCollectionState);
156
+ return oPreviewRow.update && that._hasUneditableCollectionChanges(oPreviewRow.update, sCollectionName, { context: oPreviewRow.realContext, rowIndex: oPreviewRow.rowIndex }, mCollectionState);
182
157
  });
183
- if (!aPreviewRows.length && !aNewRows.length) {
184
- return;
185
- }
158
+ if (!aPreviewRows.length && !aNewRows.length) { return; }
186
159
 
187
160
  var iExistingRowCount = aPreviewRows.length;
188
161
  var iNewRowCount = aNewRows.length;
189
162
  var iTotalCount = iExistingRowCount + iNewRowCount;
190
163
 
191
- // Previous values table (read-only, initially hidden) — only existing rows
192
- var aPreviousRows = aPreviewRows.map(function(oPreviewRow) {
193
- return that._buildCollectionPreviewRowFromContext(aPropertyNames, oPreviewRow.realContext);
194
- });
164
+ var aPreviousRows = aPreviewRows.map(function(r) { return that._buildCollectionPreviewRowFromContext(aPropertyNames, r.realContext); });
195
165
  var oPreviousTable = that._createPreviewTable(aPropertyNames, oCollectionMeta.itemProperties, aPreviousRows);
196
166
  oPreviousTable.setVisible(false);
197
167
 
198
- // New values table (editable SmartFields, initially visible)
199
- // Includes both updated existing rows and new rows
200
- var oNewTable = that._createEditableNewTable(
201
- aPropertyNames,
202
- oCollectionMeta.itemProperties,
203
- aPreviewRows,
204
- aNewRows,
205
- sCollectionName,
206
- mCollectionState,
207
- fnOnTableFieldChange
208
- );
209
-
210
- var oReadonlyTag = bHasUneditableChanges
211
- ? new GenericTag({
212
- text: oRb.getText("EASYFILL_TABLE_READONLY_TAG"),
213
- status: "Warning"
214
- }).addStyleClass("sapUiTinyMarginBegin")
215
- : null;
168
+ var oNewTable = that._createEditableNewTable(aPropertyNames, oCollectionMeta.itemProperties, aPreviewRows, aNewRows, sCollectionName, mCollectionState, fnOnTableFieldChange);
216
169
 
217
- var sCollectionName_label = oCollectionMeta.description || sCollectionName;
218
- var oTitleControl = new Title({ text: sCollectionName_label + " (" + iTotalCount + ")", titleStyle: "H4" });
170
+ var oReadonlyTag = bHasUneditableChanges ? new GenericTag({ text: oRb.getText("EASYFILL_TABLE_READONLY_TAG"), status: "Warning" }).addStyleClass("sapUiTinyMarginBegin") : null;
171
+ var sLabel = oCollectionMeta.description || sCollectionName;
172
+ var oTitleControl = new Title({ text: sLabel + " (" + iTotalCount + ")", titleStyle: "H4" });
219
173
 
220
- // Toggle button between "Previous" and "New" views
221
174
  var oSegmentedButton = new SegmentedButton({
222
- selectedKey: "new",
223
- width: "auto",
175
+ selectedKey: "new", width: "auto",
224
176
  items: [
225
177
  new SegmentedButtonItem({ key: "previous", text: oRb.getText("EASY_FILL_TABLE_VIEW_PREVIOUS"), width: "auto" }),
226
- new SegmentedButtonItem({ key: "new", text: oRb.getText("EASY_FILL_TABLE_VIEW_NEW"), width: "auto" })
178
+ new SegmentedButtonItem({ key: "new", text: oRb.getText("EASY_FILL_TABLE_VIEW_NEW"), width: "auto" })
227
179
  ],
228
180
  selectionChange: function() {
229
181
  var sKey = oSegmentedButton.getSelectedKey();
230
182
  oPreviousTable.setVisible(sKey === "previous");
231
183
  oNewTable.setVisible(sKey === "new");
232
- if (oReadonlyTag) {
233
- oReadonlyTag.setVisible(sKey === "new");
234
- }
235
- oTitleControl.setText(sCollectionName_label + " (" + (sKey === "previous" ? iExistingRowCount : iTotalCount) + ")");
184
+ if (oReadonlyTag) { oReadonlyTag.setVisible(sKey === "new"); }
185
+ oTitleControl.setText(sLabel + " (" + (sKey === "previous" ? iExistingRowCount : iTotalCount) + ")");
236
186
  }
237
187
  });
238
- var aRightItems = oReadonlyTag ? [oReadonlyTag, oSegmentedButton] : [oSegmentedButton];
188
+
239
189
  var oHeader = new HBox({
240
- justifyContent: "SpaceBetween",
241
- alignItems: "Center",
242
- wrap: "Wrap",
243
- items: [oTitleControl].concat(aRightItems)
190
+ justifyContent: "SpaceBetween", alignItems: "Center", wrap: "Wrap",
191
+ items: [oTitleControl].concat(oReadonlyTag ? [oReadonlyTag, oSegmentedButton] : [oSegmentedButton])
244
192
  }).addStyleClass("sapUiSmallMarginTop sapUiSmallMarginBottom");
245
193
 
246
- var oTableVBox = new VBox({
247
- items: [oHeader, oPreviousTable, oNewTable]
248
- }).addStyleClass("sapSmartTemplatesObjectPageEasyFillTableSection");
194
+ var oTableVBox = new VBox({ items: [oHeader, oPreviousTable, oNewTable] })
195
+ .addStyleClass("sapSmartTemplatesObjectPageEasyFillTableSection");
249
196
 
250
- // "mixed" rejection: AI also proposed new rows but creation is not allowed.
251
- // Show a warning strip after the toggle header, before the tables.
252
197
  if (oRejected[sCollectionName] === "mixed") {
253
- var oRejectionStrip = new MessageStrip({
254
- text: oRb.getText("EASY_FILL_TABLE_NEW_ROW_NOT_ALLOWED"),
255
- type: "Warning",
256
- showIcon: true
257
- });
258
- oTableVBox.insertItem(oRejectionStrip, 1); // after toggle header, before tables
198
+ oTableVBox.insertItem(new MessageStrip({ text: oRb.getText("EASY_FILL_TABLE_NEW_ROW_NOT_ALLOWED"), type: "Warning", showIcon: true }), 1);
259
199
  }
260
200
 
261
201
  if (bIsCondensedMode && oFilledFieldsSubSection) {
@@ -269,12 +209,9 @@ sap.ui.define([
269
209
 
270
210
  // Check if a wrapper VBox already exists (multiple collections: re-use it)
271
211
  var aExistingBlocks = oFilledFieldsSubSection.getBlocks();
272
- var oExistingWrapper = aExistingBlocks.find(function(oBlock) {
273
- return oBlock.getCustomData && oBlock.getCustomData().some(function(oData) {
274
- return oData.getKey() === sCondensedWrapperMarker;
275
- });
212
+ var oExistingWrapper = aExistingBlocks.find(function(b) {
213
+ return b.getCustomData && b.getCustomData().some(function(d) { return d.getKey() === sCondensedWrapperMarker; });
276
214
  });
277
-
278
215
  if (oExistingWrapper) {
279
216
  // Append the new table VBox to the already-created wrapper
280
217
  oExistingWrapper.addItem(oTableVBox);
@@ -283,13 +220,8 @@ sap.ui.define([
283
220
  // place them inside a new wrapper VBox alongside the table VBox,
284
221
  // and add the wrapper as the sole block.
285
222
  var aOriginalBlocks = aExistingBlocks.slice();
286
- aOriginalBlocks.forEach(function(oBlock) {
287
- oFilledFieldsSubSection.removeBlock(oBlock);
288
- });
289
- var oWrapper = new VBox({
290
- width: "100%",
291
- items: aOriginalBlocks.concat([oTableVBox])
292
- });
223
+ aOriginalBlocks.forEach(function(b) { oFilledFieldsSubSection.removeBlock(b); });
224
+ var oWrapper = new VBox({ width: "100%", items: aOriginalBlocks.concat([oTableVBox]) });
293
225
  oWrapper.addCustomData(new CustomData({ key: sCondensedWrapperMarker, value: "true" }));
294
226
  oFilledFieldsSubSection.addBlock(oWrapper);
295
227
  }
@@ -298,24 +230,14 @@ sap.ui.define([
298
230
  // Wrap in a standalone ObjectPageSection so the anchor bar shows the table
299
231
  // name. Insert just before the Unfilled Fields section (if present) so the
300
232
  // order is: annotation sections → table sections → Unfilled Fields.
301
- var oSubSection = new ObjectPageSubSection({
302
- title: sCollectionName_label,
303
- titleUppercase: false,
304
- showTitle: false,
305
- blocks: [oTableVBox]
306
- });
307
- var oOpSection = new ObjectPageSection({
308
- title: sCollectionName_label,
309
- titleUppercase: false,
310
- subSections: [oSubSection]
311
- });
233
+ var oSubSection = new ObjectPageSubSection({ title: sLabel, titleUppercase: false, showTitle: false, blocks: [oTableVBox] });
234
+ var oOpSection = new ObjectPageSection({ title: sLabel, titleUppercase: false, subSections: [oSubSection] });
312
235
  oOpSection.addCustomData(new CustomData({ key: sTableSectionMarker, value: "true" }));
313
-
314
- if (oUnfilledFieldsSection) {
315
- // Use indexOfSection on the live control — this is the actual aggregation
316
- // index regardless of how many manually-added sections are mixed in.
317
- var iInsertAt = oObjectPageLayout.indexOfSection(oUnfilledFieldsSection);
318
- oObjectPageLayout.insertSection(oOpSection, iInsertAt + iInsertOffset);
236
+ if (iBaseInsertIndex >= 0) {
237
+ // Use the base index captured before any insertions — not re-queried.
238
+ // This prevents the second+ table from overshooting past Unfilled Fields
239
+ // because indexOfSection(oUnfilledFieldsSection) shifts as tables are added.
240
+ oObjectPageLayout.insertSection(oOpSection, iBaseInsertIndex + iInsertOffset);
319
241
  iInsertOffset++;
320
242
  } else {
321
243
  oObjectPageLayout.addSection(oOpSection);
@@ -374,17 +296,16 @@ sap.ui.define([
374
296
  // ─── Private Helpers ─────────────────────────────────────────────────────
375
297
 
376
298
  /**
377
- * Removes all ObjectPageSections that were dynamically added by renderCollectionPreviews.
378
- * Identified by the custom data marker key set during creation.
299
+ * Removes all ObjectPageSections and condensed-mode wrapper VBoxes that were
300
+ * dynamically added by renderCollectionPreviews. Identified by the custom data
301
+ * marker keys set during creation.
379
302
  *
380
303
  * @param {sap.uxap.ObjectPageLayout} oObjectPageLayout
381
304
  */
382
305
  _removeTableSections: function(oObjectPageLayout) {
383
306
  // OP mode: remove marked standalone ObjectPageSections.
384
307
  oObjectPageLayout.getSections().filter(function(oSection) {
385
- return oSection.getCustomData().some(function(oData) {
386
- return oData.getKey() === sTableSectionMarker;
387
- });
308
+ return oSection.getCustomData().some(function(oData) { return oData.getKey() === sTableSectionMarker; });
388
309
  }).forEach(function(oSection) {
389
310
  oObjectPageLayout.removeSection(oSection);
390
311
  oSection.destroy();
@@ -395,22 +316,14 @@ sap.ui.define([
395
316
  oObjectPageLayout.getSections().forEach(function(oSection) {
396
317
  oSection.getSubSections().forEach(function(oSubSec) {
397
318
  oSubSec.getBlocks().filter(function(oBlock) {
398
- return oBlock.getCustomData && oBlock.getCustomData().some(function(oData) {
399
- return oData.getKey() === sCondensedWrapperMarker;
400
- });
319
+ return oBlock.getCustomData && oBlock.getCustomData().some(function(d) { return d.getKey() === sCondensedWrapperMarker; });
401
320
  }).forEach(function(oWrapper) {
402
321
  // Separate original (non-table) items from table VBox items
403
322
  var aItems = oWrapper.getItems().slice();
404
323
  aItems.forEach(function(oItem) {
405
- var bIsTableVBox = oItem.getCustomData && oItem.getCustomData().some(function(oData) {
406
- return oData.getKey() === sTableSectionMarker;
407
- });
324
+ var bIsTableVBox = oItem.getCustomData && oItem.getCustomData().some(function(d) { return d.getKey() === sTableSectionMarker; });
408
325
  oWrapper.removeItem(oItem);
409
- if (bIsTableVBox) {
410
- oItem.destroy();
411
- } else {
412
- oSubSec.addBlock(oItem); // restore SmartForm
413
- }
326
+ if (bIsTableVBox) { oItem.destroy(); } else { oSubSec.addBlock(oItem); } // restore SmartForm
414
327
  });
415
328
  oSubSec.removeBlock(oWrapper);
416
329
  oWrapper.destroy();
@@ -860,63 +773,16 @@ sap.ui.define([
860
773
  },
861
774
 
862
775
  /**
863
- * Checks whether a collection property is editable given the current row data object.
864
- * Delegates to the same annotation checks used in EasyFillFieldCollector.isEditableByContextObject.
865
- * Duplicated here to keep TableRenderer self-contained (no circular dependency).
776
+ * Delegates property editability check to EasyFillFieldCollector.isEditableByContextObject —
777
+ * the single authoritative implementation shared across all EasyFill modules.
866
778
  *
867
779
  * @param {object} oPropertyMeta - OData property metadata
868
780
  * @param {object} oRowObject - plain row data object
781
+ * @param {object} oEntitySet - OData entity set metadata
869
782
  * @returns {boolean}
870
783
  */
871
784
  _isEditableProperty: function(oPropertyMeta, oRowObject, oEntitySet) {
872
- if (!oPropertyMeta) {
873
- return false;
874
- }
875
-
876
- // Check entity-set-level sap:updatable (entire entity set is read-only)
877
- if (oEntitySet) {
878
- var sEntitySetUpdatable = oEntitySet["sap:updatable"];
879
- if (sEntitySetUpdatable === "false") {
880
- return false;
881
- }
882
- // sap:updatable-path: resolved against the row context object (boolean property)
883
- var sEntitySetUpdatablePath = oEntitySet["sap:updatable-path"];
884
- if (sEntitySetUpdatablePath && oRowObject) {
885
- var bUpdatableFromPath = oRowObject[sEntitySetUpdatablePath];
886
- if (bUpdatableFromPath === false || bUpdatableFromPath === "false") {
887
- return false;
888
- }
889
- }
890
- }
891
-
892
- var sUpdatable = oPropertyMeta["sap:updatable"];
893
- if (sUpdatable) {
894
- return sUpdatable !== "false";
895
- }
896
- var oAnnotation = oPropertyMeta["com.sap.vocabularies.Common.v1.FieldControl"];
897
- if (oAnnotation && oAnnotation["EnumMember"]) {
898
- return oAnnotation["EnumMember"] !== "com.sap.vocabularies.Common.v1.FieldControlType/ReadOnly";
899
- }
900
- // Dynamic FieldControl via Path (e.g. { "Path": "SomeField_fc" })
901
- // FieldControl values: 0=Hidden, 1=ReadOnly, 3=Optional, 7=Mandatory
902
- if (oAnnotation && oAnnotation["Path"]) {
903
- var iDynamicFieldControl = oRowObject && oRowObject[oAnnotation["Path"]];
904
- if (iDynamicFieldControl !== undefined && iDynamicFieldControl !== null) {
905
- return iDynamicFieldControl !== 0 && iDynamicFieldControl !== 1;
906
- }
907
- }
908
- // Check "sap:field-control" dynamic path (value 1 = read-only)
909
- // If the _fc property is defined in metadata but its runtime value is absent from
910
- // the row object (not fetched / not in $select), treat as non-editable (safe fallback).
911
- var sFieldControl = oPropertyMeta["sap:field-control"];
912
- if (sFieldControl) {
913
- if (!oRowObject || !(sFieldControl in oRowObject)) {
914
- return false;
915
- }
916
- var iFieldControlValue = oRowObject[sFieldControl];
917
- return iFieldControlValue !== 0 && iFieldControlValue !== 1;
918
- }
919
- return true;
785
+ return EasyFillFieldCollector.isEditableByContextObject(oPropertyMeta, oRowObject, oEntitySet);
920
786
  },
921
787
 
922
788
  /**
@@ -1,11 +1,7 @@
1
1
  <core:FragmentDefinition xmlns="sap.m" xmlns:core="sap.ui.core"
2
2
  xmlns:smartForm="sap.ui.comp.smartform"
3
- xmlns:smartField="sap.ui.comp.smartfield"
4
3
  xmlns:uxap="sap.uxap"
5
4
  xmlns:layout="sap.ui.layout"
6
- xmlns:template="http://schemas.sap.com/sapui5/extension/sap.ui.core.template/1"
7
- template:require="{AH: 'sap/suite/ui/generic/template/js/AnnotationHelper',
8
- AHModel: 'sap/ui/model/odata/AnnotationHelper'}"
9
5
  xmlns:fesr="http://schemas.sap.com/sapui5/extension/sap.ui.core.FESR/1">
10
6
 
11
7
  <Dialog id="EasyFillDialog" contentWidth="72.25rem" class="sapSmartTemplatesObjectPageEasyFill" contentHeight="49.3125rem" title="{i18n>EASY_FILL_TITLE}" titleAlignment="Start" horizontalScrolling="false" verticalScrolling="false" draggable="true" resizable="true">
@@ -66,92 +62,78 @@ xmlns:fesr="http://schemas.sap.com/sapui5/extension/sap.ui.core.FESR/1">
66
62
  visible="{easyFillDialogModel>/isIllustrationVisible}"
67
63
  title="{path: 'easyFillDialogModel>/stateType', formatter: '.formatIllustrationTitle'}"
68
64
  description="{path: 'easyFillDialogModel>/stateType', formatter: '.formatIllustrationDescription'}"/>
69
- <!-- <ScrollContainer height="100%" horizontal = "true"> -->
70
- <uxap:ObjectPageLayout visible="{easyFillDialogModel>/isFormVisible}" id="EasyFillObjectPageLayout" sections="{path: 'easyFillDialogModel>/sections', templateShareable: false}" alwaysShowContentHeader="true" upperCaseAnchorBar="false">
71
- <uxap:headerTitle>
72
- <uxap:ObjectPageDynamicHeaderTitle>
73
- <uxap:heading>
74
- <Title text="{i18n>EASY_FILL_FILLED_FIELDS}" wrapping="true" level="H2"/>
75
- </uxap:heading>
76
- <uxap:actions>
77
- <SegmentedButton selectedKey="{easyFillDialogModel>/easyFillMode}" selectionChange=".onSelectionChange">
78
- <items>
79
- <SegmentedButtonItem icon="sap-icon://increase-line-height" key="ObjectPageMode" tooltip="{i18n>EASY_FILL_DETAILED_VIEW_TOOLTIP}"/>
80
- <SegmentedButtonItem icon="sap-icon://decrease-line-height" key="CondensedMode" tooltip="{i18n>EASY_FILL_CONDENSED_VIEW_TOOLTIP}"/>
81
- </items>
82
- </SegmentedButton>
83
- <Button
84
- icon="{= ${easyFillDialogModel>/isFullScreen} ? 'sap-icon://full-screen' : 'sap-icon://exit-full-screen'}"
85
- tooltip="{= ${easyFillDialogModel>/isFullScreen} ? ${i18n>EASY_FILL_ENTER_FULL_SCREEN_TOOLTIP} : ${i18n>EASY_FILL_EXIT_FULL_SCREEN_TOOLTIP}}"
86
- type="Transparent"
87
- visible="{= !${easyFillDialogModel>/isSplitterPageMode} }"
88
- press=".onToggleFullScreen"/>
89
- </uxap:actions>
90
- </uxap:ObjectPageDynamicHeaderTitle>
91
- </uxap:headerTitle>
92
- <uxap:headerContent>
93
- <VBox width="100%">
94
- <HBox gap="0.5rem" justifyContent="Start" alignItems="Center" renderType="Bare" class="sapSmartTemplatesObjectPageEasyFillReviewAreaBanner">
95
- <Link text="{i18n>AI_LINK_NOTICE_TITLE}" press=".onLinkPress"/>
96
- <ToggleButton icon = "sap-icon://thumb-up" type = "Transparent" fesr:press="fe2:ef:thumbsup" pressed="{easyFillDialogModel>/feedbackPressedUp}" enabled="{easyFillDialogModel>/feedbackEnabled}" press=".onFeedbackPress"/>
97
- <ToggleButton icon = "sap-icon://thumb-down" type = "Transparent" fesr:press="fe2:ef:thumbsdown" pressed="{easyFillDialogModel>/feedbackPressedDown}" enabled="{easyFillDialogModel>/feedbackEnabled}" press=".onFeedbackPress"/>
98
- </HBox>
99
- <MessageStrip
100
- visible="{easyFillDialogModel>/hasNonUpdatableFields}"
101
- text="{i18n>EASY_FILL_READ_ONLY_WARNING}"
102
- showIcon="true"
103
- type="Warning"
104
- class="sapUiSmallMarginBottom"/>
105
- </VBox>
106
- </uxap:headerContent>
107
- <uxap:ObjectPageSection title="{path: 'easyFillDialogModel>title', formatter: '.formatSectionTitle'}" titleUppercase="false"
108
- visible="{
109
- parts: [
110
- {path: 'easyFillDialogModel>fields'},
111
- {path: 'easyFillDialogModel>children'}
112
- ],
113
- formatter: '.hasSectionFields'
114
- }">
115
- <uxap:ObjectPageSubSection>
116
- <smartForm:SmartForm editable="true" id="EasyFillSmartForm" validationMode="Async" groups="{path: 'easyFillDialogModel>children', templateShareable: false}">
65
+ <uxap:ObjectPageLayout
66
+ visible="{easyFillDialogModel>/isFormVisible}"
67
+ id="EasyFillObjectPageLayout"
68
+ sections="{path: 'easyFillDialogModel>/sections', templateShareable: false}"
69
+ alwaysShowContentHeader="true"
70
+ upperCaseAnchorBar="false">
71
+ <uxap:headerTitle>
72
+ <uxap:ObjectPageDynamicHeaderTitle>
73
+ <uxap:heading>
74
+ <Title text="{i18n>EASY_FILL_FILLED_FIELDS}" wrapping="true" level="H2"/>
75
+ </uxap:heading>
76
+ <uxap:actions>
77
+ <SegmentedButton selectedKey="{easyFillDialogModel>/easyFillMode}" selectionChange=".onSelectionChange">
78
+ <items>
79
+ <SegmentedButtonItem icon="sap-icon://increase-line-height" key="ObjectPageMode" tooltip="{i18n>EASY_FILL_DETAILED_VIEW_TOOLTIP}"/>
80
+ <SegmentedButtonItem icon="sap-icon://decrease-line-height" key="CondensedMode" tooltip="{i18n>EASY_FILL_CONDENSED_VIEW_TOOLTIP}"/>
81
+ </items>
82
+ </SegmentedButton>
83
+ <Button
84
+ icon="{= ${easyFillDialogModel>/isFullScreen} ? 'sap-icon://full-screen' : 'sap-icon://exit-full-screen'}"
85
+ tooltip="{= ${easyFillDialogModel>/isFullScreen} ? ${i18n>EASY_FILL_ENTER_FULL_SCREEN_TOOLTIP} : ${i18n>EASY_FILL_EXIT_FULL_SCREEN_TOOLTIP}}"
86
+ type="Transparent"
87
+ visible="{= !${easyFillDialogModel>/isSplitterPageMode} }"
88
+ press=".onToggleFullScreen"/>
89
+ </uxap:actions>
90
+ </uxap:ObjectPageDynamicHeaderTitle>
91
+ </uxap:headerTitle>
92
+ <uxap:headerContent>
93
+ <VBox width="100%">
94
+ <HBox gap="0.5rem" justifyContent="Start" alignItems="Center" renderType="Bare" class="sapSmartTemplatesObjectPageEasyFillReviewAreaBanner">
95
+ <Link text="{i18n>AI_LINK_NOTICE_TITLE}" press=".onLinkPress"/>
96
+ <ToggleButton icon="sap-icon://thumb-up" type="Transparent" fesr:press="fe2:ef:thumbsup" pressed="{easyFillDialogModel>/feedbackPressedUp}" enabled="{easyFillDialogModel>/feedbackEnabled}" press=".onFeedbackPress"/>
97
+ <ToggleButton icon="sap-icon://thumb-down" type="Transparent" fesr:press="fe2:ef:thumbsdown" pressed="{easyFillDialogModel>/feedbackPressedDown}" enabled="{easyFillDialogModel>/feedbackEnabled}" press=".onFeedbackPress"/>
98
+ </HBox>
99
+ <MessageStrip
100
+ visible="{easyFillDialogModel>/hasNonUpdatableFields}"
101
+ text="{i18n>EASY_FILL_READ_ONLY_WARNING}"
102
+ showIcon="true"
103
+ type="Warning"
104
+ class="sapUiSmallMarginBottom"/>
105
+ </VBox>
106
+ </uxap:headerContent>
107
+ <uxap:ObjectPageSection
108
+ title="{path: 'easyFillDialogModel>title', formatter: '.formatSectionTitle'}"
109
+ titleUppercase="false"
110
+ visible="{easyFillDialogModel>visible}">
111
+ <uxap:ObjectPageSubSection>
112
+ <smartForm:SmartForm editable="true" validationMode="Async" groups="{path: 'easyFillDialogModel>children', templateShareable: false}">
113
+ <smartForm:customData>
114
+ <core:CustomData key="defaultInputFieldDisplayBehaviour" value="descriptionAndId"/>
115
+ <core:CustomData key="defaultDropDownDisplayBehaviour" value="descriptionOnly"/>
116
+ <core:CustomData key="defaultTextInEditModeSource" value="ValueList"/>
117
+ <core:CustomData key="dateFormatSettings" value='\{"UTC":true,"style":"medium"\}'/>
118
+ </smartForm:customData>
119
+ <smartForm:layout>
120
+ <smartForm:ColumnLayout columnsXL="3" columnsL="3" columnsM="2" labelCellsLarge="2"/>
121
+ </smartForm:layout>
122
+ <smartForm:Group
123
+ title="{path: 'easyFillDialogModel>title', formatter: '.formatSectionTitle'}"
124
+ visible="{easyFillDialogModel>visible}">
117
125
  <smartForm:customData>
118
- <core:CustomData key="defaultInputFieldDisplayBehaviour" value="descriptionAndId"/>
119
- <core:CustomData key="defaultDropDownDisplayBehaviour" value="descriptionOnly"/>
120
- <core:CustomData key="defaultTextInEditModeSource" value="ValueList"/>
121
- <core:CustomData key="dateFormatSettings" value='\{"UTC":true,"style":"medium"\}'/>
126
+ <core:CustomData key="id" value="{easyFillDialogModel>id}"/>
122
127
  </smartForm:customData>
123
- <smartForm:layout>
124
- <smartForm:ColumnLayout
125
- columnsXL="3"
126
- columnsL="3"
127
- columnsM="2"
128
- labelCellsLarge="2"/>
129
- </smartForm:layout>
130
- <smartForm:Group
131
- title="{path: 'easyFillDialogModel>title', formatter: '.formatSectionTitle'}"
132
- visible="{
133
- parts: [
134
- {path: 'easyFillDialogModel>id'},
135
- {path: 'easyFillDialogModel>fields'},
136
- {path: 'easyFillDialogModel>/easyFillMode'}
137
- ],
138
- formatter: '.populateSmartFields'
139
- }">
140
- <smartForm:customData>
141
- <core:CustomData key="id" value="{easyFillDialogModel>id}"/>
142
- </smartForm:customData>
143
- </smartForm:Group>
144
- </smartForm:SmartForm>
145
- </uxap:ObjectPageSubSection>
146
- </uxap:ObjectPageSection>
147
- </uxap:ObjectPageLayout>
128
+ </smartForm:Group>
129
+ </smartForm:SmartForm>
130
+ </uxap:ObjectPageSubSection>
131
+ </uxap:ObjectPageSection>
132
+ </uxap:ObjectPageLayout>
148
133
  </FlexBox>
149
134
  </layout:SplitPane>
150
-
151
135
  </layout:PaneContainer>
152
-
153
136
  </layout:PaneContainer>
154
-
155
137
  </layout:ResponsiveSplitter>
156
138
  </FlexBox>
157
139
  </content>
@@ -162,4 +144,4 @@ xmlns:fesr="http://schemas.sap.com/sapui5/extension/sap.ui.core.FESR/1">
162
144
  <Button text="{i18n>CANCEL}" press=".onCancelEasyFillDialog" fesr:press="fe2:ef:cancel"/>
163
145
  </endButton>
164
146
  </Dialog>
165
- </core:FragmentDefinition>
147
+ </core:FragmentDefinition>