@sapui5/sap.suite.ui.generic.template 1.151.1 → 1.153.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 (87) 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/controller/ControllerImplementation.js +0 -6
  8. package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_sv.properties +1 -1
  9. package/src/sap/suite/ui/generic/template/ListReport/manifest.json +1 -1
  10. package/src/sap/suite/ui/generic/template/ListReport/view/fragments/FullscreenPage.fragment.xml +3 -0
  11. package/src/sap/suite/ui/generic/template/ObjectPage/Component.js +5 -0
  12. package/src/sap/suite/ui/generic/template/ObjectPage/controller/ControllerImplementation.js +31 -0
  13. package/src/sap/suite/ui/generic/template/ObjectPage/manifest.json +1 -1
  14. package/src/sap/suite/ui/generic/template/ObjectPage/view/fragments/Actions.fragment.xml +1 -1
  15. package/src/sap/suite/ui/generic/template/QuickCreate/manifest.json +1 -1
  16. package/src/sap/suite/ui/generic/template/QuickView/manifest.json +1 -1
  17. package/src/sap/suite/ui/generic/template/genericUtilities/controlStateWrapperFactory/SmartFilterBarWrapper.js +13 -7
  18. package/src/sap/suite/ui/generic/template/genericUtilities/designtime/messagebundle.properties +0 -0
  19. package/src/sap/suite/ui/generic/template/js/AnnotationHelper.js +21 -17
  20. package/src/sap/suite/ui/generic/template/lib/AppComponent.js +19 -3
  21. package/src/sap/suite/ui/generic/template/lib/ShareUtils.js +1 -1
  22. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillAIOrchestrator.js +16 -6
  23. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillApplyHandler.js +35 -47
  24. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController.js +80 -64
  25. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector.js +442 -259
  26. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer.js +186 -161
  27. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillHandler.js +137 -161
  28. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer.js +157 -191
  29. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillDialog.fragment.xml +67 -85
  30. package/src/sap/suite/ui/generic/template/lib/ai/EasyFilterBarHandler.js +56 -4
  31. package/src/sap/suite/ui/generic/template/lib/i18n/i18n.properties +11 -2
  32. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ar.properties +7 -1
  33. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_bg.properties +7 -1
  34. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ca.properties +7 -1
  35. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cnr.properties +7 -1
  36. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cs.properties +7 -1
  37. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cy.properties +7 -1
  38. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_da.properties +7 -1
  39. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_de.properties +7 -1
  40. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_el.properties +7 -1
  41. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en.properties +7 -1
  42. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_GB.properties +7 -1
  43. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_sappsd.properties +7 -1
  44. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saprigi.properties +7 -1
  45. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saptrc.properties +13 -7
  46. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es.properties +7 -1
  47. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es_MX.properties +7 -1
  48. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_et.properties +7 -1
  49. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fi.properties +11 -5
  50. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr.properties +10 -4
  51. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr_CA.properties +7 -1
  52. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hi.properties +7 -1
  53. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hr.properties +7 -1
  54. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hu.properties +7 -1
  55. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_id.properties +7 -1
  56. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_it.properties +7 -1
  57. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_iw.properties +7 -1
  58. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ja.properties +7 -1
  59. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_kk.properties +7 -1
  60. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ko.properties +7 -1
  61. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lt.properties +7 -1
  62. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lv.properties +7 -1
  63. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_mk.properties +7 -1
  64. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ms.properties +7 -1
  65. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_nl.properties +7 -1
  66. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_no.properties +7 -1
  67. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pl.properties +7 -1
  68. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt.properties +7 -1
  69. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt_PT.properties +7 -1
  70. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ro.properties +7 -1
  71. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ru.properties +7 -1
  72. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sh.properties +7 -1
  73. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sk.properties +7 -1
  74. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sl.properties +8 -2
  75. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sr.properties +7 -1
  76. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sv.properties +7 -1
  77. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_th.properties +7 -1
  78. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_tr.properties +7 -1
  79. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_uk.properties +7 -1
  80. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_vi.properties +7 -1
  81. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_CN.properties +7 -1
  82. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_TW.properties +7 -1
  83. package/src/sap/suite/ui/generic/template/lib/insights/InsightsCardHelper.js +40 -8
  84. package/src/sap/suite/ui/generic/template/lib/navigation/NavigationController.js +3 -2
  85. package/src/sap/suite/ui/generic/template/lib/presentationControl/SmartTableHandler.js +4 -0
  86. package/src/sap/suite/ui/generic/template/library.js +1 -1
  87. package/src/sap/suite/ui/generic/template/themes/base/ObjectPage.less +7 -1
@@ -1,5 +1,6 @@
1
1
  sap.ui.define([
2
2
  "sap/ui/comp/smartfield/SmartField",
3
+ "sap/m/Button",
3
4
  "sap/m/GenericTag",
4
5
  "sap/m/MessageStrip",
5
6
  "sap/m/Text",
@@ -16,8 +17,8 @@ sap.ui.define([
16
17
  "sap/uxap/ObjectPageSubSection",
17
18
  "sap/ui/core/CustomData",
18
19
  "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) {
20
+ "sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector"
21
+ ], function(SmartField, Button, GenericTag, MessageStrip, Text, Title, Table, Column, ColumnListItem, VBox, HBox, ScrollContainer, SegmentedButton, SegmentedButtonItem, ObjectPageSection, ObjectPageSubSection, CustomData, FeLogger, EasyFillFieldCollector) {
21
22
  "use strict";
22
23
 
23
24
  var sClassName = "lib.ai.EasyFill.EasyFillTableRenderer";
@@ -39,14 +40,8 @@ sap.ui.define([
39
40
  * 1. Creates transient OData row contexts for preview rendering.
40
41
  * 2. Creates a "Previous Values" read-only table and a "New Values" editable SmartField table.
41
42
  * 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.
43
+ * 4. Injects each collection as an ObjectPageSection into the shared EasyFillObjectPageLayout.
44
44
  * 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
45
  * - On confirm, EasyFillApplyHandler activates these contexts via submitChanges.
51
46
  * - Only one new row per table is created (extra new rows are suppressed upstream).
52
47
  */
@@ -78,10 +73,6 @@ sap.ui.define([
78
73
  return;
79
74
  }
80
75
 
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
76
  if (!bReuseStaging) {
86
77
  this._removeTableSections(oObjectPageLayout);
87
78
  }
@@ -108,7 +99,7 @@ sap.ui.define([
108
99
  // Bound sections always have a "easyFillDialogModel" binding context; manually-added
109
100
  // ones never do, so there is no ambiguity.
110
101
  var oFilledFieldsSubSection = null; // condensed mode: add table as block inside here
111
- var oUnfilledFieldsSection = null; // OP mode: insert standalone section before this
102
+ var oUnfilledFieldsSection = null; // OP mode: insert standalone section before this
112
103
  oObjectPageLayout.getSections().forEach(function(oSection) {
113
104
  var oCtx = oSection.getBindingContext("easyFillDialogModel");
114
105
  if (!oCtx) { return; }
@@ -127,32 +118,26 @@ sap.ui.define([
127
118
 
128
119
  // iInsertOffset keeps multiple consecutive tables in the correct order when
129
120
  // inserting standalone sections before the Unfilled Fields section (OP mode).
121
+ // iBaseInsertIndex is captured ONCE before any insertions so it does not shift
122
+ // as tables are added — each table is inserted at base + offset, not re-queried.
123
+ var iBaseInsertIndex = oUnfilledFieldsSection
124
+ ? oObjectPageLayout.indexOfSection(oUnfilledFieldsSection) : -1;
130
125
  var iInsertOffset = 0;
131
-
132
126
  var that = this;
133
127
  var oRejected = mCollectionsWithRejectedNewRows || {};
134
128
 
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
129
  Object.keys(oRejected).forEach(function(sCollectionName) {
138
- if (oRejected[sCollectionName] !== "all") {
139
- return; // "mixed" is handled below alongside the diff table
140
- }
130
+ if (oRejected[sCollectionName] !== "all") { return; }
141
131
  var oCollectionMeta = mFieldMapping[sCollectionName];
142
132
  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
- });
133
+ var oStrip = new MessageStrip({ text: oRb.getText("EASY_FILL_TABLE_NEW_ROW_NOT_ALLOWED"), type: "Warning", showIcon: true });
148
134
  var oNotifVBox = new VBox({ items: [oStrip] });
149
135
  oNotifVBox.addCustomData(new CustomData({ key: sTableSectionMarker, value: "true" }));
150
136
  var oNotifSubSection = new ObjectPageSubSection({ title: sTitle, titleUppercase: false, blocks: [oNotifVBox] });
151
137
  var oNotifSection = new ObjectPageSection({ title: sTitle, titleUppercase: false, subSections: [oNotifSubSection] });
152
138
  oNotifSection.addCustomData(new CustomData({ key: sTableSectionMarker, value: "true" }));
153
- if (oUnfilledFieldsSection) {
154
- var iRejectedAt = oObjectPageLayout.indexOfSection(oUnfilledFieldsSection);
155
- oObjectPageLayout.insertSection(oNotifSection, iRejectedAt + iInsertOffset);
139
+ if (iBaseInsertIndex >= 0) {
140
+ oObjectPageLayout.insertSection(oNotifSection, iBaseInsertIndex + iInsertOffset);
156
141
  iInsertOffset++;
157
142
  } else {
158
143
  oObjectPageLayout.addSection(oNotifSection);
@@ -162,100 +147,136 @@ sap.ui.define([
162
147
 
163
148
  Object.keys(mCollectionUpdates).forEach(function(sCollectionName) {
164
149
  var oCollectionMeta = mFieldMapping[sCollectionName];
165
- if (!oCollectionMeta || !oCollectionMeta.itemProperties) {
166
- return;
167
- }
168
-
150
+ if (!oCollectionMeta || !oCollectionMeta.itemProperties) { return; }
169
151
  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
- });
152
+ if (!aPropertyNames.length) { return; }
153
+
154
+ var aPreviewRows = (oTableStagingState.mStagedRows[sCollectionName] || []).filter(function(r) { return r.isPreviewVisible; });
176
155
  var aNewRows = oTableStagingState.mPendingNewRowContexts[sCollectionName] || [];
177
156
  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);
157
+ return oPreviewRow.update && that._hasUneditableCollectionChanges(oPreviewRow.update, sCollectionName, { context: oPreviewRow.realContext, rowIndex: oPreviewRow.rowIndex }, mCollectionState);
182
158
  });
183
- if (!aPreviewRows.length && !aNewRows.length) {
184
- return;
185
- }
159
+ if (!aPreviewRows.length && !aNewRows.length) { return; }
186
160
 
187
161
  var iExistingRowCount = aPreviewRows.length;
188
162
  var iNewRowCount = aNewRows.length;
189
163
  var iTotalCount = iExistingRowCount + iNewRowCount;
190
164
 
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
- });
165
+ var aPreviousRows = aPreviewRows.map(function(r) { return that._buildCollectionPreviewRowFromContext(aPropertyNames, r.realContext); });
195
166
  var oPreviousTable = that._createPreviewTable(aPropertyNames, oCollectionMeta.itemProperties, aPreviousRows);
196
167
  oPreviousTable.setVisible(false);
197
168
 
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
- );
169
+ var aItemMetaOut = [];
170
+ var oNewTable = that._createEditableNewTable(aPropertyNames, oCollectionMeta.itemProperties, aPreviewRows, aNewRows, sCollectionName, mCollectionState, fnOnTableFieldChange, aItemMetaOut);
171
+
172
+ // "Exclude Changes" button — disabled until at least 1 active (non-excluded) row is checked
173
+ var oExcludeButton = new Button({
174
+ text: oRb.getText("EASY_FILL_EXCLUDE_ROW_BUTTON"),
175
+ type: "Ghost",
176
+ enabled: false
177
+ });
178
+
179
+ // Re-evaluate button enabled state whenever a checkbox changes
180
+ function refreshExcludeButtonState() {
181
+ var bAnyActiveChecked = aItemMetaOut.some(function(oMeta) {
182
+ if (!oMeta.item.isSelected()) { return false; }
183
+ var oExcludedSet = oMeta.isNew
184
+ ? oTableStagingState.mExcludedNewRows[sCollectionName]
185
+ : oTableStagingState.mExcludedRows[sCollectionName];
186
+ return !oExcludedSet || !oExcludedSet[oMeta.index];
187
+ });
188
+ oExcludeButton.setEnabled(bAnyActiveChecked);
189
+ }
209
190
 
210
- var oReadonlyTag = bHasUneditableChanges
211
- ? new GenericTag({
212
- text: oRb.getText("EASYFILL_TABLE_READONLY_TAG"),
213
- status: "Warning"
214
- }).addStyleClass("sapUiTinyMarginBegin")
215
- : null;
191
+ // Re-apply excluded row visual state after re-render (e.g. Condensed ↔ OP mode switch).
192
+ // mExcludedRows/mExcludedNewRows survive the mode switch but new controls are created fresh.
193
+ aItemMetaOut.forEach(function(oMeta) {
194
+ var mTarget = oMeta.isNew ? oTableStagingState.mExcludedNewRows : oTableStagingState.mExcludedRows;
195
+ var oExcludedSet = mTarget && mTarget[sCollectionName];
196
+ if (!oExcludedSet || !oExcludedSet[oMeta.index]) { return; }
197
+
198
+ oMeta.item.addStyleClass("sapEasyFillExcludedRow");
199
+ oMeta.item.getCells().forEach(function(oCell) {
200
+ if (oCell.isA && oCell.isA("sap.ui.comp.smartfield.SmartField")) {
201
+ oCell.setEditable(false);
202
+ oCell.setEnabled(false);
203
+ }
204
+ });
205
+ });
216
206
 
217
- var sCollectionName_label = oCollectionMeta.description || sCollectionName;
218
- var oTitleControl = new Title({ text: sCollectionName_label + " (" + iTotalCount + ")", titleStyle: "H4" });
207
+ var oActualTable = oNewTable.getContent && oNewTable.getContent()[0];
208
+ if (oActualTable) {
209
+ oActualTable.attachSelectionChange(function(oEvent) {
210
+ if (oEvent.getParameter("selectAll")) {
211
+ aItemMetaOut.forEach(function(oMeta) {
212
+ var oExcludedSet = oMeta.isNew
213
+ ? oTableStagingState.mExcludedNewRows[sCollectionName]
214
+ : oTableStagingState.mExcludedRows[sCollectionName];
215
+ if (oExcludedSet && oExcludedSet[oMeta.index]) {
216
+ oMeta.item.setSelected(false);
217
+ }
218
+ });
219
+ }
220
+ refreshExcludeButtonState();
221
+ });
222
+ }
223
+
224
+ oExcludeButton.attachPress(function() {
225
+ aItemMetaOut.forEach(function(oMeta) {
226
+ if (!oMeta.item.isSelected()) { return; }
227
+ var mTarget = oMeta.isNew ? oTableStagingState.mExcludedNewRows : oTableStagingState.mExcludedRows;
228
+ var oExcludedSet = mTarget[sCollectionName];
229
+ if (oExcludedSet && oExcludedSet[oMeta.index]) { return; } // already excluded
230
+
231
+ if (!mTarget[sCollectionName]) { mTarget[sCollectionName] = Object.create(null); }
232
+ mTarget[sCollectionName][oMeta.index] = true;
233
+
234
+ oMeta.item.addStyleClass("sapEasyFillExcludedRow");
235
+ oMeta.item.getCells().forEach(function(oCell) {
236
+ if (oCell.isA && oCell.isA("sap.ui.comp.smartfield.SmartField")) {
237
+ oCell.setEditable(false);
238
+ oCell.setEnabled(false);
239
+ }
240
+ });
241
+ oMeta.item.setSelected(false);
242
+ });
243
+
244
+ oExcludeButton.setEnabled(false);
245
+ if (typeof fnOnTableFieldChange === "function") { fnOnTableFieldChange(); }
246
+ });
247
+
248
+ var oReadonlyTag = bHasUneditableChanges ? new GenericTag({ text: oRb.getText("EASYFILL_TABLE_READONLY_TAG"), status: "Warning" }).addStyleClass("sapUiTinyMarginBegin") : null;
249
+ var sLabel = oCollectionMeta.description || sCollectionName;
250
+ var oTitleControl = new Title({ text: sLabel + " (" + iTotalCount + ")", titleStyle: "H4" });
219
251
 
220
- // Toggle button between "Previous" and "New" views
221
252
  var oSegmentedButton = new SegmentedButton({
222
- selectedKey: "new",
223
- width: "auto",
253
+ selectedKey: "new", width: "auto",
224
254
  items: [
225
255
  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" })
256
+ new SegmentedButtonItem({ key: "new", text: oRb.getText("EASY_FILL_TABLE_VIEW_NEW"), width: "auto" })
227
257
  ],
228
258
  selectionChange: function() {
229
259
  var sKey = oSegmentedButton.getSelectedKey();
230
260
  oPreviousTable.setVisible(sKey === "previous");
231
261
  oNewTable.setVisible(sKey === "new");
232
- if (oReadonlyTag) {
233
- oReadonlyTag.setVisible(sKey === "new");
234
- }
235
- oTitleControl.setText(sCollectionName_label + " (" + (sKey === "previous" ? iExistingRowCount : iTotalCount) + ")");
262
+ oExcludeButton.setVisible(sKey === "new");
263
+ if (oReadonlyTag) { oReadonlyTag.setVisible(sKey === "new"); }
264
+ oTitleControl.setText(sLabel + " (" + (sKey === "previous" ? iExistingRowCount : iTotalCount) + ")");
236
265
  }
237
266
  });
238
- var aRightItems = oReadonlyTag ? [oReadonlyTag, oSegmentedButton] : [oSegmentedButton];
267
+
268
+ var oLeftGroup = new HBox({ alignItems: "Center", items: oReadonlyTag ? [oTitleControl, oReadonlyTag] : [oTitleControl] });
269
+ var oRightGroup = new HBox({ alignItems: "Center", items: [oExcludeButton, oSegmentedButton] });
239
270
  var oHeader = new HBox({
240
- justifyContent: "SpaceBetween",
241
- alignItems: "Center",
242
- wrap: "Wrap",
243
- items: [oTitleControl].concat(aRightItems)
271
+ justifyContent: "SpaceBetween", alignItems: "Center", wrap: "Wrap",
272
+ items: [oLeftGroup, oRightGroup]
244
273
  }).addStyleClass("sapUiSmallMarginTop sapUiSmallMarginBottom");
245
274
 
246
- var oTableVBox = new VBox({
247
- items: [oHeader, oPreviousTable, oNewTable]
248
- }).addStyleClass("sapSmartTemplatesObjectPageEasyFillTableSection");
275
+ var oTableVBox = new VBox({ items: [oHeader, oPreviousTable, oNewTable] })
276
+ .addStyleClass("sapSmartTemplatesObjectPageEasyFillTableSection");
249
277
 
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
278
  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
279
+ oTableVBox.insertItem(new MessageStrip({ text: oRb.getText("EASY_FILL_TABLE_NEW_ROW_NOT_ALLOWED"), type: "Warning", showIcon: true }), 1);
259
280
  }
260
281
 
261
282
  if (bIsCondensedMode && oFilledFieldsSubSection) {
@@ -269,12 +290,9 @@ sap.ui.define([
269
290
 
270
291
  // Check if a wrapper VBox already exists (multiple collections: re-use it)
271
292
  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
- });
293
+ var oExistingWrapper = aExistingBlocks.find(function(b) {
294
+ return b.getCustomData && b.getCustomData().some(function(d) { return d.getKey() === sCondensedWrapperMarker; });
276
295
  });
277
-
278
296
  if (oExistingWrapper) {
279
297
  // Append the new table VBox to the already-created wrapper
280
298
  oExistingWrapper.addItem(oTableVBox);
@@ -283,13 +301,8 @@ sap.ui.define([
283
301
  // place them inside a new wrapper VBox alongside the table VBox,
284
302
  // and add the wrapper as the sole block.
285
303
  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
- });
304
+ aOriginalBlocks.forEach(function(b) { oFilledFieldsSubSection.removeBlock(b); });
305
+ var oWrapper = new VBox({ width: "100%", items: aOriginalBlocks.concat([oTableVBox]) });
293
306
  oWrapper.addCustomData(new CustomData({ key: sCondensedWrapperMarker, value: "true" }));
294
307
  oFilledFieldsSubSection.addBlock(oWrapper);
295
308
  }
@@ -298,24 +311,14 @@ sap.ui.define([
298
311
  // Wrap in a standalone ObjectPageSection so the anchor bar shows the table
299
312
  // name. Insert just before the Unfilled Fields section (if present) so the
300
313
  // 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
- });
314
+ var oSubSection = new ObjectPageSubSection({ title: sLabel, titleUppercase: false, showTitle: false, blocks: [oTableVBox] });
315
+ var oOpSection = new ObjectPageSection({ title: sLabel, titleUppercase: false, subSections: [oSubSection] });
312
316
  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);
317
+ if (iBaseInsertIndex >= 0) {
318
+ // Use the base index captured before any insertions — not re-queried.
319
+ // This prevents the second+ table from overshooting past Unfilled Fields
320
+ // because indexOfSection(oUnfilledFieldsSection) shifts as tables are added.
321
+ oObjectPageLayout.insertSection(oOpSection, iBaseInsertIndex + iInsertOffset);
319
322
  iInsertOffset++;
320
323
  } else {
321
324
  oObjectPageLayout.addSection(oOpSection);
@@ -374,17 +377,16 @@ sap.ui.define([
374
377
  // ─── Private Helpers ─────────────────────────────────────────────────────
375
378
 
376
379
  /**
377
- * Removes all ObjectPageSections that were dynamically added by renderCollectionPreviews.
378
- * Identified by the custom data marker key set during creation.
380
+ * Removes all ObjectPageSections and condensed-mode wrapper VBoxes that were
381
+ * dynamically added by renderCollectionPreviews. Identified by the custom data
382
+ * marker keys set during creation.
379
383
  *
380
384
  * @param {sap.uxap.ObjectPageLayout} oObjectPageLayout
381
385
  */
382
386
  _removeTableSections: function(oObjectPageLayout) {
383
387
  // OP mode: remove marked standalone ObjectPageSections.
384
388
  oObjectPageLayout.getSections().filter(function(oSection) {
385
- return oSection.getCustomData().some(function(oData) {
386
- return oData.getKey() === sTableSectionMarker;
387
- });
389
+ return oSection.getCustomData().some(function(oData) { return oData.getKey() === sTableSectionMarker; });
388
390
  }).forEach(function(oSection) {
389
391
  oObjectPageLayout.removeSection(oSection);
390
392
  oSection.destroy();
@@ -395,22 +397,14 @@ sap.ui.define([
395
397
  oObjectPageLayout.getSections().forEach(function(oSection) {
396
398
  oSection.getSubSections().forEach(function(oSubSec) {
397
399
  oSubSec.getBlocks().filter(function(oBlock) {
398
- return oBlock.getCustomData && oBlock.getCustomData().some(function(oData) {
399
- return oData.getKey() === sCondensedWrapperMarker;
400
- });
400
+ return oBlock.getCustomData && oBlock.getCustomData().some(function(d) { return d.getKey() === sCondensedWrapperMarker; });
401
401
  }).forEach(function(oWrapper) {
402
402
  // Separate original (non-table) items from table VBox items
403
403
  var aItems = oWrapper.getItems().slice();
404
404
  aItems.forEach(function(oItem) {
405
- var bIsTableVBox = oItem.getCustomData && oItem.getCustomData().some(function(oData) {
406
- return oData.getKey() === sTableSectionMarker;
407
- });
405
+ var bIsTableVBox = oItem.getCustomData && oItem.getCustomData().some(function(d) { return d.getKey() === sTableSectionMarker; });
408
406
  oWrapper.removeItem(oItem);
409
- if (bIsTableVBox) {
410
- oItem.destroy();
411
- } else {
412
- oSubSec.addBlock(oItem); // restore SmartForm
413
- }
407
+ if (bIsTableVBox) { oItem.destroy(); } else { oSubSec.addBlock(oItem); } // restore SmartForm
414
408
  });
415
409
  oSubSec.removeBlock(oWrapper);
416
410
  oWrapper.destroy();
@@ -571,9 +565,10 @@ sap.ui.define([
571
565
  * @param {string} sCollectionName - navigation property name (used to look up property metadata)
572
566
  * @param {object} mCollectionState - collection tracking state (used to get property metadata)
573
567
  * @param {function} fnOnTableFieldChange - callback when a cell value changes
568
+ * @param {Array} [aItemMetaOut] - optional collector: receives { item, isNew, index } per row
574
569
  * @returns {sap.m.ScrollContainer} ScrollContainer wrapping the table with horizontal scrolling enabled
575
570
  */
576
- _createEditableNewTable: function(aPropertyNames, oItemProperties, aPreviewRows, aNewRowContexts, sCollectionName, mCollectionState, fnOnTableFieldChange) {
571
+ _createEditableNewTable: function(aPropertyNames, oItemProperties, aPreviewRows, aNewRowContexts, sCollectionName, mCollectionState, fnOnTableFieldChange, aItemMetaOut) {
577
572
  var aColumns = aPropertyNames.map(function(sPropName) {
578
573
  return new Column({
579
574
  header: new Text({
@@ -587,7 +582,7 @@ sap.ui.define([
587
582
  var that = this;
588
583
 
589
584
  // Rows for existing rows with AI-proposed updates
590
- var aExistingItems = aPreviewRows.map(function(oPreviewRow) {
585
+ var aExistingItems = aPreviewRows.map(function(oPreviewRow, iIndex) {
591
586
  var oContext = oPreviewRow.transientContext;
592
587
  var oRowObject = oPreviewRow.realContext && oPreviewRow.realContext.getObject && oPreviewRow.realContext.getObject();
593
588
 
@@ -631,6 +626,11 @@ sap.ui.define([
631
626
  if (oContext) {
632
627
  oItem.setBindingContext(oContext);
633
628
  }
629
+
630
+ if (Array.isArray(aItemMetaOut)) {
631
+ aItemMetaOut.push({ item: oItem, isNew: false, index: iIndex });
632
+ }
633
+
634
634
  return oItem;
635
635
  });
636
636
 
@@ -638,7 +638,7 @@ sap.ui.define([
638
638
  // sEntitySetName is set explicitly on SmartField so it can resolve OData metadata
639
639
  // even though it lives inside a plain sap.m.Table (not a SmartTable).
640
640
  var sEntitySetName = (oEntitySet && oEntitySet.name) || sCollectionName;
641
- var aNewItems = (aNewRowContexts || []).map(function(oNewContext) {
641
+ var aNewItems = (aNewRowContexts || []).map(function(oNewContext, iIndex) {
642
642
  var aCells = aPropertyNames.map(function(sPropName) {
643
643
  if (!oNewContext) {
644
644
  return new Text({ text: "" });
@@ -668,17 +668,24 @@ sap.ui.define([
668
668
  });
669
669
  return oSmartField;
670
670
  });
671
+
671
672
  var oItem = new ColumnListItem({
672
673
  cells: aCells,
673
674
  highlight: "Information"
674
675
  });
675
676
  oItem.setBindingContext(oNewContext);
677
+
678
+ if (Array.isArray(aItemMetaOut)) {
679
+ aItemMetaOut.push({ item: oItem, isNew: true, index: iIndex });
680
+ }
681
+
676
682
  return oItem;
677
683
  });
678
684
 
679
685
  var oTable = new Table({
680
686
  columns: aColumns,
681
687
  items: aExistingItems.concat(aNewItems),
688
+ mode: "MultiSelect",
682
689
  fixedLayout: false,
683
690
  autoPopinMode: false
684
691
  }).addStyleClass("sapSmartTemplatesObjectPageEasyFillTablePreview");
@@ -751,6 +758,12 @@ sap.ui.define([
751
758
  Object.keys((oTableStagingState && oTableStagingState.mPendingNewRowContexts) || {}).forEach(function(sCollectionName) {
752
759
  delete oTableStagingState.mPendingNewRowContexts[sCollectionName];
753
760
  });
761
+ Object.keys((oTableStagingState && oTableStagingState.mExcludedRows) || {}).forEach(function(sCollectionName) {
762
+ delete oTableStagingState.mExcludedRows[sCollectionName];
763
+ });
764
+ Object.keys((oTableStagingState && oTableStagingState.mExcludedNewRows) || {}).forEach(function(sCollectionName) {
765
+ delete oTableStagingState.mExcludedNewRows[sCollectionName];
766
+ });
754
767
  },
755
768
 
756
769
  /**
@@ -860,63 +873,16 @@ sap.ui.define([
860
873
  },
861
874
 
862
875
  /**
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).
876
+ * Delegates property editability check to EasyFillFieldCollector.isEditableByContextObject —
877
+ * the single authoritative implementation shared across all EasyFill modules.
866
878
  *
867
879
  * @param {object} oPropertyMeta - OData property metadata
868
880
  * @param {object} oRowObject - plain row data object
881
+ * @param {object} oEntitySet - OData entity set metadata
869
882
  * @returns {boolean}
870
883
  */
871
884
  _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;
885
+ return EasyFillFieldCollector.isEditableByContextObject(oPropertyMeta, oRowObject, oEntitySet);
920
886
  },
921
887
 
922
888
  /**