@sapui5/sap.suite.ui.generic.template 1.148.1 → 1.148.3

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 (77) 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/manifest.json +1 -1
  4. package/src/sap/suite/ui/generic/template/Canvas/manifest.json +1 -1
  5. package/src/sap/suite/ui/generic/template/ListReport/controller/ControllerImplementation.js +6 -3
  6. package/src/sap/suite/ui/generic/template/ListReport/manifest.json +1 -1
  7. package/src/sap/suite/ui/generic/template/ObjectPage/manifest.json +1 -1
  8. package/src/sap/suite/ui/generic/template/QuickCreate/manifest.json +1 -1
  9. package/src/sap/suite/ui/generic/template/QuickView/manifest.json +1 -1
  10. package/src/sap/suite/ui/generic/template/genericUtilities/controlStateWrapperFactory/SmartFilterBarWrapper.js +48 -8
  11. package/src/sap/suite/ui/generic/template/lib/AppComponent.js +1 -1
  12. package/src/sap/suite/ui/generic/template/lib/CRUDManager.js +7 -8
  13. package/src/sap/suite/ui/generic/template/lib/CommonEventHandlers.js +8 -1
  14. package/src/sap/suite/ui/generic/template/lib/CommonUtils.js +10 -2
  15. package/src/sap/suite/ui/generic/template/lib/MessageUtils.js +4 -3
  16. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillAIOrchestrator.js +43 -12
  17. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillApplyHandler.js +67 -218
  18. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController.js +55 -8
  19. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector.js +214 -35
  20. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer.js +93 -18
  21. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillHandler.js +426 -48
  22. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer.js +512 -63
  23. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillDialog.fragment.xml +33 -23
  24. package/src/sap/suite/ui/generic/template/lib/ai/EasyFilterBarHandler.js +90 -73
  25. package/src/sap/suite/ui/generic/template/lib/i18n/i18n.properties +24 -0
  26. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ar.properties +26 -8
  27. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_bg.properties +26 -8
  28. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ca.properties +26 -8
  29. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cnr.properties +26 -8
  30. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cs.properties +26 -8
  31. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cy.properties +26 -8
  32. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_da.properties +26 -8
  33. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_de.properties +30 -12
  34. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_el.properties +26 -8
  35. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en.properties +26 -8
  36. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_GB.properties +26 -8
  37. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saprigi.properties +26 -8
  38. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es.properties +26 -8
  39. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es_MX.properties +26 -8
  40. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_et.properties +26 -8
  41. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fi.properties +26 -8
  42. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr.properties +29 -11
  43. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr_CA.properties +27 -9
  44. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hi.properties +26 -8
  45. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hr.properties +27 -9
  46. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hu.properties +26 -8
  47. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_id.properties +26 -8
  48. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_it.properties +26 -8
  49. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_iw.properties +26 -8
  50. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ja.properties +26 -8
  51. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_kk.properties +26 -8
  52. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ko.properties +26 -8
  53. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lt.properties +26 -8
  54. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lv.properties +26 -8
  55. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_mk.properties +26 -8
  56. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ms.properties +26 -8
  57. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_nl.properties +26 -8
  58. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_no.properties +26 -8
  59. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pl.properties +26 -8
  60. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt.properties +26 -8
  61. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt_PT.properties +26 -8
  62. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ro.properties +26 -8
  63. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ru.properties +26 -8
  64. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sh.properties +26 -8
  65. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sk.properties +26 -8
  66. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sl.properties +26 -8
  67. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sr.properties +26 -8
  68. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sv.properties +37 -19
  69. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_th.properties +26 -8
  70. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_tr.properties +26 -8
  71. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_uk.properties +26 -8
  72. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_vi.properties +26 -8
  73. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_CN.properties +26 -8
  74. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_TW.properties +26 -8
  75. package/src/sap/suite/ui/generic/template/lib/navigation/NavigationController.js +1 -1
  76. package/src/sap/suite/ui/generic/template/library.js +1 -1
  77. package/src/sap/suite/ui/generic/template/themes/base/ObjectPage.less +22 -1
@@ -8,8 +8,7 @@ sap.ui.define([
8
8
  "sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer",
9
9
  "sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer",
10
10
  "sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillApplyHandler",
11
- "sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController",
12
- "sap/suite/ui/generic/template/js/AnnotationHelper"
11
+ "sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController"
13
12
  ], function(
14
13
  BaseObject,
15
14
  extend,
@@ -20,8 +19,7 @@ sap.ui.define([
20
19
  EasyFillFormRenderer,
21
20
  EasyFillTableRenderer,
22
21
  EasyFillApplyHandler,
23
- EasyFillDialogController,
24
- GenericAnnotationHelper
22
+ EasyFillDialogController
25
23
  ) {
26
24
  "use strict";
27
25
 
@@ -63,12 +61,16 @@ sap.ui.define([
63
61
  mCollectionRowContexts: Object.create(null),
64
62
  mCollectionRowContextsByPath: Object.create(null),
65
63
  mCollectionProperties: Object.create(null),
66
- mCollectionEntityTypes: Object.create(null)
64
+ mCollectionEntityTypes: Object.create(null),
65
+ mCollectionEntitySets: Object.create(null),
66
+ mCollectionAllowsCreation: Object.create(null)
67
67
  };
68
68
 
69
69
  var mCollectionUpdates = Object.create(null); // { navProperty: [ rowUpdate ] } from AI response
70
+ var mLastFieldMapping = null; // retained so table sections can be re-rendered on mode switch
70
71
  var oTableStagingState = {
71
- mStagedRows: Object.create(null)
72
+ mStagedRows: Object.create(null),
73
+ mPendingNewRowContexts: Object.create(null)
72
74
  };
73
75
 
74
76
  // ─── Internal Helpers ────────────────────────────────────────────────────
@@ -106,11 +108,46 @@ sap.ui.define([
106
108
  function _buildDialogHandlers() {
107
109
  return {
108
110
  // ── Layout mode toggle (Condensed ↔ ObjectPage view) ──────────────
111
+ // After the sections binding is refreshed, SmartForms for the new mode
112
+ // are repopulated asynchronously (setTimeout in populateSmartFields).
113
+ // _validateFieldsAndUpdateConfirmState is called here so that validation runs
114
+ // immediately on whatever SmartForms are currently in the DOM, and then
115
+ // again via fnOnFieldChange once the new SmartFields are rendered.
109
116
  onSelectionChange: function(oEvent) {
117
+ // Step 1: clean up table content injected for the CURRENT mode BEFORE the
118
+ // model update. This ensures the OPL aggregation is free of manually-added
119
+ // sections when the sections binding recreates controls for the new mode,
120
+ // so getSections() in renderCollectionPreviews returns only bound sections.
121
+ var oOpl = oController.byId("EasyFillObjectPageLayout");
122
+ if (oOpl && mLastFieldMapping && Object.keys(mCollectionUpdates).length > 0) {
123
+ EasyFillTableRenderer.removeTableSections(oOpl);
124
+ }
125
+
126
+ // Step 2: update model — this triggers the OPL sections binding to recreate
127
+ // bound section controls for the new mode.
110
128
  EasyFillDialogController.onSelectionChange(
111
129
  oEvent, oController, oTemplateUtils, oRb,
112
130
  EasyFillFieldCollector, EasyFillLayoutMode
113
131
  );
132
+
133
+ // Step 3: re-render table previews for the new mode.
134
+ // _removeTableSections is skipped inside renderCollectionPreviews because
135
+ // bReuseStaging=true, since cleanup was already done in step 1 above.
136
+ if (mLastFieldMapping && Object.keys(mCollectionUpdates).length > 0) {
137
+ EasyFillTableRenderer.renderCollectionPreviews(
138
+ mCollectionUpdates,
139
+ mLastFieldMapping,
140
+ [],
141
+ oController,
142
+ oRb,
143
+ mCollectionState,
144
+ oTableStagingState,
145
+ oObjectPageModel,
146
+ _validateFieldsAndUpdateConfirmState,
147
+ true // bReuseStaging — also skips re-cleanup (already done above)
148
+ );
149
+ }
150
+ _validateFieldsAndUpdateConfirmState();
114
151
  },
115
152
 
116
153
  // ── Feedback thumbs-up/down ───────────────────────────────────────
@@ -135,7 +172,16 @@ sap.ui.define([
135
172
  },
136
173
 
137
174
  // ── Confirm: apply AI values to the live OP context ───────────────
175
+ // The Confirm button is disabled reactively by _validateFieldsAndUpdateConfirmState
176
+ // whenever a SmartField value is invalid — no click-time check needed.
138
177
  onSaveFromEasyFillDialog: async function() {
178
+ // Capture staged collection updates BEFORE applyAndSave writes them to the live
179
+ // contexts — after applyAndSave the staged values equal the live values and the map would be empty.
180
+ var mStagedUpdates = EasyFillApplyHandler.buildStagedCollectionUpdates(oTableStagingState, mCollectionState);
181
+
182
+ // Extract reviewed new-row values from staging contexts BEFORE reset discards them.
183
+ var mNewRowValues = EasyFillApplyHandler.extractPendingNewRowValues(oTableStagingState, mCollectionState);
184
+
139
185
  EasyFillApplyHandler.applyAndSave(
140
186
  oTransientContextForEasyFill,
141
187
  oObjectPageContext,
@@ -143,8 +189,17 @@ sap.ui.define([
143
189
  oTableStagingState,
144
190
  mCollectionState
145
191
  );
192
+ // Trigger side effects on the OP context for all changed SmartForm fields
193
+ // before resetting the dialog (SmartFields must still be present)
194
+ _activateSideEffects();
195
+ // Trigger side effects for all modified table rows
196
+ _activateSideEffectsForTable(mStagedUpdates);
146
197
  _resetEasyFill(true);
147
198
  EasyFillDialogController.closeDialog(oController);
199
+
200
+ // Create the new rows via the standard CRUDManager (draft-aware, handles side effects)
201
+ // This is done after dialog close so the user sees the OP immediately.
202
+ await _createNewRows(mNewRowValues);
148
203
  },
149
204
 
150
205
  // ── Text area live change: enable/disable submit button ───────────
@@ -181,10 +236,33 @@ sap.ui.define([
181
236
  oTransientContextForEasyFill,
182
237
  oObjectPageContext,
183
238
  mUpdatableLookupMap,
184
- oDialogModel().getProperty("/llmData")
239
+ oDialogModel().getProperty("/llmData"),
240
+ _validateFieldsAndUpdateConfirmState
185
241
  );
186
242
  },
187
243
 
244
+ // ── Section visibility: hide SmartForm sections with no fields ──────────────
245
+ hasSectionFields: function(aFields, aChildren) {
246
+ if (Array.isArray(aFields) && aFields.length > 0) {
247
+ return true;
248
+ }
249
+
250
+ function hasFieldsInNode(oNode) {
251
+ if (!oNode) {
252
+ return false;
253
+ }
254
+ if (Array.isArray(oNode.fields) && oNode.fields.length > 0) {
255
+ return true;
256
+ }
257
+ if (Array.isArray(oNode.children)) {
258
+ return oNode.children.some(hasFieldsInNode);
259
+ }
260
+ return false;
261
+ }
262
+
263
+ return Array.isArray(aChildren) && aChildren.some(hasFieldsInNode);
264
+ },
265
+
188
266
  // ── Fullscreen toggle ─────────────────────────────────────────────
189
267
  onToggleFullScreen: function() {
190
268
  EasyFillDialogController.onToggleFullScreen(oController);
@@ -233,40 +311,61 @@ sap.ui.define([
233
311
 
234
312
  if (aiCallResult.success) {
235
313
  // Split AI response into simple fields (SmartForm) and collection rows (SmartTable)
314
+ // Pass mCollectionState so the splitter can check which tables allow creation
236
315
  var oSplitResult = EasyFillAIOrchestrator.splitAIResultByCollection(
237
- aiCallResult.data, mFieldMapping, EasyFillFieldCollector, oObjectPage
316
+ aiCallResult.data, mFieldMapping, EasyFillFieldCollector, oObjectPage, mCollectionState
238
317
  );
239
318
  var simpleFields = oSplitResult.simpleFields;
240
319
  var collectionFields = oSplitResult.collectionFields;
241
320
 
242
321
  // Separate updatable from non-updatable fields
243
- var oEntityType = oTemplateUtils.oCommonUtils.getMetaModelEntityType(
244
- oController.getOwnerComponent().getEntitySet()
245
- );
322
+ var sEntitySetName = oController.getOwnerComponent().getEntitySet();
323
+ var oEntityType = oTemplateUtils.oCommonUtils.getMetaModelEntityType(sEntitySetName);
324
+ var oMetaModel = oObjectPageModel.getMetaModel();
325
+ var oEntitySet = oMetaModel.getODataEntitySet(sEntitySetName);
246
326
  var oSplit = EasyFillFieldCollector.getUpdatableAndNonUpdatableFields(
247
- simpleFields, oEntityType, oObjectPageContext
327
+ simpleFields, oEntityType, oObjectPageContext, oEntitySet
248
328
  );
249
329
  mUpdatableLookupMap = oSplit.updatableFields;
330
+ var mNonUpdatableFields = oSplit.nonUpdatableFields;
250
331
 
251
332
  // Build section tree for selected view mode
252
333
  var aData = (sSelectedKey === EasyFillLayoutMode.CondensedMode)
253
- ? EasyFillFieldCollector.getSectionsForCondensedView(simpleFields)
254
- : EasyFillFieldCollector.getSectionsForObjectPageView(oEntityType, simpleFields, oRb);
334
+ ? EasyFillFieldCollector.getSectionsForCondensedView(simpleFields, mUpdatableLookupMap, mNonUpdatableFields)
335
+ : EasyFillFieldCollector.getSectionsForObjectPageView(oEntityType, simpleFields, oRb, mNonUpdatableFields);
255
336
 
256
337
  // Reset form (destroy previous SmartForm content + revert table staging)
257
- // then create a fresh transient context for the new AI result
338
+ // then create a fresh transient context for the new AI result.
339
+ // Restore the user's chosen layout mode afterwards because _resetEasyFill
340
+ // unconditionally resets it to CondensedMode — without this, renderCollectionPreviews
341
+ // would see the wrong mode and embed table sections inside the SmartForm SubSection
342
+ // instead of adding them as standalone OPL sections (OP mode) or vice-versa.
258
343
  _resetEasyFill();
344
+ oModel.setProperty("/easyFillMode", sSelectedKey);
259
345
  oTransientContextForEasyFill = EasyFillFormRenderer.makeTransientContext(
260
346
  null, oObjectPageModel, oController
261
347
  );
262
348
 
263
- // Update dialog model with section data and AI result
264
- oModel.setProperty("/sections", aData);
349
+ // Update dialog model with section data and AI result.
350
+ // llmData must be set BEFORE sections so that when the sections binding fires
351
+ // and triggers the populateSmartFields formatter synchronously, the AI-suggested
352
+ // values are already available and can be written to the transient context.
265
353
  oModel.setProperty("/llmData", simpleFields);
354
+ oModel.setProperty("/updatableFields", mUpdatableLookupMap);
355
+ oModel.setProperty("/nonUpdatableFields", mNonUpdatableFields);
356
+ oModel.setProperty("/sections", aData);
357
+ oModel.setProperty("/hasNonUpdatableFields", Object.keys(mNonUpdatableFields).length > 0);
358
+
359
+ // Show warning if AI attempted to add multiple new rows but only one was accepted
360
+ oModel.setProperty("/attemptedMultipleNewRows", oSplitResult.attemptedMultipleNewRows === true);
266
361
 
267
362
  // Process collection/table updates
268
363
  mCollectionUpdates = collectionFields;
269
- var bHasTableUpdates = Object.keys(mCollectionUpdates).length > 0;
364
+ mLastFieldMapping = mFieldMapping;
365
+ var mRejectedNewRows = oSplitResult.collectionsWithRejectedNewRows || {};
366
+ // hasTableUpdates is true when there are accepted updates OR rejected-only tables to show
367
+ var bHasTableUpdates = Object.keys(mCollectionUpdates).length > 0 ||
368
+ Object.keys(mRejectedNewRows).length > 0;
270
369
  oModel.setProperty("/hasTableUpdates", bHasTableUpdates);
271
370
 
272
371
  // Render collection previews into the table preview container
@@ -279,12 +378,12 @@ sap.ui.define([
279
378
  mCollectionState,
280
379
  oTableStagingState,
281
380
  oObjectPageModel,
282
- function() {
283
- _refreshTableValidationAndSaveState().catch(Function.prototype);
284
- }
381
+ _validateFieldsAndUpdateConfirmState,
382
+ false,
383
+ mRejectedNewRows
285
384
  );
286
385
 
287
- await _refreshTableValidationAndSaveState();
386
+ _validateFieldsAndUpdateConfirmState();
288
387
 
289
388
  var iTotalFields = Object.keys(oSplit.updatableFields).length + Object.keys(oSplit.nonUpdatableFields).length;
290
389
  if (iTotalFields === 0 && !bHasTableUpdates) {
@@ -293,42 +392,155 @@ sap.ui.define([
293
392
  }
294
393
 
295
394
  oModel.setProperty("/isIllustrationVisible", false);
296
- oModel.setProperty("/isFormVisible", iTotalFields > 0);
395
+ oModel.setProperty("/isFormVisible", iTotalFields > 0 || bHasTableUpdates);
297
396
  } else {
298
397
  // AI call failed — show error illustration
299
- EasyFillApplyHandler.applyTableValidationState([], oModel, oRb);
300
398
  oModel.setProperty("/stateType", "Error");
301
399
  }
302
400
  }
303
401
 
304
- async function _refreshTableValidationAndSaveState() {
305
- var oModel = oDialogModel();
306
- var mStagedCollectionUpdates = EasyFillApplyHandler.buildStagedCollectionUpdates(
307
- oTableStagingState,
308
- mCollectionState
309
- );
310
- var aTableValidationErrors = await EasyFillApplyHandler.validateCollectionUpdatesAgainstValueHelp(
311
- mStagedCollectionUpdates,
312
- mCollectionState,
313
- oObjectPageModel,
314
- oRb,
315
- EasyFillFieldCollector,
316
- GenericAnnotationHelper
317
- );
318
- EasyFillApplyHandler.applyTableValidationState(aTableValidationErrors, oModel, oRb);
402
+ /**
403
+ * Runs SmartForm.check() on every SmartForm in the EasyFill ObjectPageLayout.
404
+ * Returns true if all SmartForms pass validation (or none exist yet).
405
+ * As a side effect, check() visually marks invalid fields in the dialog.
406
+ *
407
+ * SmartForm.check() returns either:
408
+ * - string[] (synchronous validationMode) field IDs with errors
409
+ * - Promise<Object[]> (asynchronous validationMode) — resolves with field objects with errors
410
+ * Both are normalised via Promise.resolve() so the caller can always await the result.
411
+ *
412
+ * @returns {Promise<boolean>} resolves to true when all SmartForms have zero errors
413
+ */
414
+ async function _checkSmartFormsValidity() {
415
+ var oObjectPageLayout = oController.byId("EasyFillObjectPageLayout");
416
+ if (!oObjectPageLayout) {
417
+ return true;
418
+ }
319
419
 
420
+ // Collect one Promise<errorArray> per SmartForm found in the layout
421
+ var aCheckPromises = [];
422
+ oObjectPageLayout.getSections().forEach(function(oSection) {
423
+ oSection.getSubSections().forEach(function(oSubSection) {
424
+ oSubSection.getBlocks().forEach(function(oBlock) {
425
+ var aSmartFormCandidates = [];
426
+ if (oBlock.isA("sap.ui.comp.smartform.SmartForm")) {
427
+ aSmartFormCandidates.push(oBlock);
428
+ } else if (typeof oBlock.getItems === "function") {
429
+ // In condensed mode the SmartForm is nested inside a wrapper VBox;
430
+ // look one level deeper to find it (mirrors EasyFillFormRenderer).
431
+ oBlock.getItems().forEach(function(oItem) {
432
+ if (oItem.isA("sap.ui.comp.smartform.SmartForm")) {
433
+ aSmartFormCandidates.push(oItem);
434
+ }
435
+ });
436
+ }
437
+ aSmartFormCandidates.forEach(function(oSmartForm) {
438
+ // .catch(() => []) absorbs internal SmartField metadata-promise rejections
439
+ // that fire when SmartFields are destroyed during a mode switch.
440
+ aCheckPromises.push(
441
+ Promise.resolve(oSmartForm.check()).catch(function() { return []; })
442
+ );
443
+ });
444
+ });
445
+ });
446
+ });
447
+
448
+ // Resolve all checks in parallel; valid only when every SmartForm has no errors
449
+ var aResults = await Promise.all(aCheckPromises);
450
+ return aResults.every(function(aErrors) {
451
+ return Array.isArray(aErrors) && aErrors.length === 0;
452
+ });
453
+ }
454
+
455
+ /**
456
+ * Re-computes isSaveEnabled from the combined SmartForm and table SmartField validity
457
+ * flag. Called as the single source of truth for the Confirm button state.
458
+ *
459
+ * @param {boolean} bAllFieldsValid - true when both SmartForm and table SmartField checks pass
460
+ */
461
+ function _computeSaveEnabled(bAllFieldsValid) {
320
462
  var bHasScalarChanges = Object.keys(mUpdatableLookupMap || {}).length > 0;
321
- var bHasTableChanges = EasyFillApplyHandler.hasStagedCollectionChanges(
322
- oTableStagingState,
323
- mCollectionState
324
- );
463
+ var bHasTableChanges = EasyFillApplyHandler.hasStagedCollectionChanges(oTableStagingState, mCollectionState);
464
+ var bHasChanges = bHasScalarChanges || bHasTableChanges;
465
+ oDialogModel().setProperty("/isSaveEnabled", bAllFieldsValid && bHasChanges);
466
+ }
325
467
 
326
- oModel.setProperty(
327
- "/isSaveEnabled",
328
- bHasScalarChanges || (bHasTableChanges && aTableValidationErrors.length === 0)
329
- );
468
+ /**
469
+ * Runs SmartField.checkValuesValidity() on every editable table SmartField.
470
+ * Returns true if all fields pass (or no table exists yet).
471
+ *
472
+ * checkValuesValidity() returns a Promise<boolean> (true = valid).
473
+ *
474
+ * @returns {Promise<boolean>} resolves to true when all table SmartFields are valid
475
+ */
476
+ async function _checkTableSmartFieldsValidity() {
477
+ var sMarker = "easyfillTableSection";
478
+ var oObjectPageLayout = oController.byId("EasyFillObjectPageLayout");
479
+ if (!oObjectPageLayout) {
480
+ return true;
481
+ }
482
+
483
+ var aCheckPromises = [];
484
+
485
+ function scanTableForSmartFields(oTable) {
486
+ if (!oTable.isA("sap.m.Table") || !oTable.getVisible()) { return; }
487
+ oTable.getItems().forEach(function(oListItem) {
488
+ if (!oListItem.isA("sap.m.ColumnListItem")) { return; }
489
+ oListItem.getCells().forEach(function(oCell) {
490
+ if (!oCell.isA("sap.ui.comp.smartfield.SmartField") || !oCell.getEditable()) { return; }
491
+ var bValueStateOk = oCell.getValueState() !== "Error";
492
+ aCheckPromises.push(
493
+ Promise.resolve(oCell.checkValuesValidity()).then(function(bValid) {
494
+ return bValueStateOk && bValid !== false;
495
+ })
496
+ );
497
+ });
498
+ });
499
+ }
330
500
 
331
- return aTableValidationErrors;
501
+ function scanVBox(oVBox) {
502
+ oVBox.getItems().forEach(function(oItem) { scanTableForSmartFields(oItem); });
503
+ }
504
+
505
+ oObjectPageLayout.getSections().forEach(function(oSection) {
506
+ var bIsTableSection = oSection.getCustomData().some(function(oData) {
507
+ return oData.getKey() === sMarker;
508
+ });
509
+
510
+ oSection.getSubSections().forEach(function(oSubSection) {
511
+ oSubSection.getBlocks().forEach(function(oBlock) {
512
+ if (!oBlock.isA("sap.m.VBox")) { return; }
513
+ var bIsMarkedBlock = oBlock.getCustomData().some(function(oData) {
514
+ return oData.getKey() === sMarker;
515
+ });
516
+ // Scan VBoxes that are either:
517
+ // (a) directly marked (condensed mode: injected into Filled Fields SubSection)
518
+ // (b) inside a marked standalone section (OP mode)
519
+ if (bIsMarkedBlock || bIsTableSection) {
520
+ scanVBox(oBlock);
521
+ }
522
+ });
523
+ });
524
+ });
525
+
526
+ var aResults = await Promise.all(aCheckPromises);
527
+ return aResults.every(function(bValid) { return bValid === true; });
528
+ }
529
+
530
+ /**
531
+ * Called after every SmartField change event (both form and table) and once after each
532
+ * group/table is populated. Validates all SmartForms and all table SmartFields and
533
+ * updates the Confirm button enabled state.
534
+ */
535
+ function _validateFieldsAndUpdateConfirmState() {
536
+ setTimeout(function() {
537
+ Promise.all([
538
+ _checkSmartFormsValidity(),
539
+ _checkTableSmartFieldsValidity()
540
+ ]).then(function(aResults) {
541
+ _computeSaveEnabled(aResults[0] && aResults[1]);
542
+ }).catch(Function.prototype);
543
+ }, 0);
332
544
  }
333
545
 
334
546
  /**
@@ -338,6 +550,7 @@ sap.ui.define([
338
550
  * @param {boolean} [bRemoveTextAreaValue=false] - if true, also clears the text area input
339
551
  */
340
552
  function _resetEasyFill(bRemoveTextAreaValue) {
553
+ mLastFieldMapping = null;
341
554
  EasyFillApplyHandler.resetEasyFill(
342
555
  !!bRemoveTextAreaValue,
343
556
  oDialogModel(),
@@ -354,6 +567,127 @@ sap.ui.define([
354
567
  );
355
568
  }
356
569
 
570
+ /**
571
+ * Activates side effects for all editable SmartFields in the EasyFill SmartForm.
572
+ * Called after applyAndSave so that side effects are triggered on the original
573
+ * ObjectPage context (not the transient context used while the dialog was open).
574
+ *
575
+ * Collects field group IDs from the inner controls of every editable SmartField,
576
+ * updates the view's CustomData for each field group to reference the OP context,
577
+ * then registers the group change and triggers field-group validation on the view.
578
+ */
579
+ function _activateSideEffects() {
580
+ var oObjectPageLayout = oController.byId("EasyFillObjectPageLayout");
581
+ if (!oObjectPageLayout) {
582
+ return;
583
+ }
584
+
585
+ // Collect all editable SmartFields from the EasyFill dialog SmartForms
586
+ var aEditableFields = [];
587
+ oObjectPageLayout.getSections().forEach(function(oSection) {
588
+ oSection.getSubSections().forEach(function(oSubSection) {
589
+ oSubSection.getBlocks().forEach(function(oBlock) {
590
+ if (oBlock.isA("sap.ui.comp.smartform.SmartForm")) {
591
+ oBlock.getGroups().forEach(function(oGroup) {
592
+ oGroup.getGroupElements().forEach(function(oGroupElement) {
593
+ oGroupElement.getElements().forEach(function(oElement) {
594
+ if (oElement.isA && oElement.isA("sap.ui.comp.smartfield.SmartField") && oElement.getEditable()) {
595
+ aEditableFields.push(oElement);
596
+ }
597
+ });
598
+ });
599
+ });
600
+ }
601
+ });
602
+ });
603
+ });
604
+
605
+ // Making a Set so that duplicate field group IDs are not passed
606
+ var aFieldGroupIdSet = new Set();
607
+ aEditableFields.forEach(function(oSmartField) {
608
+ // SideEffect would be invoked only for the editable controls inside the SmartField
609
+ var aEditableInnerFields = oSmartField.getInnerControls().filter(function(oControl) {
610
+ return oControl.getEditable();
611
+ });
612
+ aEditableInnerFields.forEach(function(oControl) {
613
+ var aControlFieldGroupId = oControl.getFieldGroupIds();
614
+ if (aControlFieldGroupId.length > 0) {
615
+ aControlFieldGroupId.forEach(function(sFieldGroupId) {
616
+ aFieldGroupIdSet.add(sFieldGroupId);
617
+ });
618
+ }
619
+ });
620
+ });
621
+
622
+ var oView = oController.getView();
623
+
624
+ aFieldGroupIdSet.forEach(function(sFieldGroupId) {
625
+ var oID = oView.data(sFieldGroupId);
626
+
627
+ /**
628
+ * Currently, the SmartFields are created using the TransientContext, which means both
629
+ * "context" and "contextObject" refer to the TransientContext instance.
630
+ *
631
+ * However, we need to use the original OP context so that side effects are triggered
632
+ * on the original OP context when the user saves data from the EasyFill dialog back to OP.
633
+ *
634
+ * Therefore, we update the "context" and "contextObject" to reference the original OP context.
635
+ */
636
+ if (oID) {
637
+ oID["context"] = oObjectPageContext.getPath();
638
+ oID["contextObject"] = oObjectPageContext;
639
+ oView.data(sFieldGroupId, oID);
640
+
641
+ // To make sure that all the fields side effect would be triggered
642
+ var sSideEffectQualifier = oTemplateUtils.oServices.oApplicationController._getSideEffectsQualifier(oID.name);
643
+ oTemplateUtils.oServices.oApplicationController.registerGroupChange(sSideEffectQualifier);
644
+ }
645
+ });
646
+
647
+ oView.triggerValidateFieldGroup([...aFieldGroupIdSet]);
648
+ }
649
+
650
+ /**
651
+ * Activates side effects for all modified table (collection) rows after applyAndSave.
652
+ * For each collection row that had AI-suggested value changes applied, triggers
653
+ * executeSideEffects on the real row context with the list of changed property names.
654
+ *
655
+ * Must be called with mStagedUpdates captured BEFORE applyAndSave, because after
656
+ * applyAndSave the staged values equal the live values so buildStagedCollectionUpdates
657
+ * would return an empty map.
658
+ *
659
+ * @param {object} mStagedUpdates - pre-computed collection updates: { navProperty: [ rowUpdate ] }
660
+ */
661
+ function _activateSideEffectsForTable(mStagedUpdates) {
662
+ if (!mStagedUpdates || !Object.keys(mStagedUpdates).length) {
663
+ return;
664
+ }
665
+
666
+ Object.keys(mStagedUpdates).forEach(function(sCollectionName) {
667
+ var aRowUpdates = mStagedUpdates[sCollectionName] || [];
668
+ aRowUpdates.forEach(function(oRowUpdate) {
669
+ var oResolvedRow = EasyFillApplyHandler._resolveCollectionRowContext(sCollectionName, oRowUpdate, mCollectionState);
670
+ if (!oResolvedRow || !oResolvedRow.context) {
671
+ return;
672
+ }
673
+
674
+ // Collect the names of changed properties (exclude internal metadata fields)
675
+ var aChangedProperties = Object.keys(oRowUpdate).filter(function(sKey) {
676
+ return sKey !== "_rowIndex" && sKey !== "_contextPath";
677
+ });
678
+
679
+ if (!aChangedProperties.length) {
680
+ return;
681
+ }
682
+
683
+ oTemplateUtils.oServices.oApplicationController.executeSideEffects(
684
+ oResolvedRow.context,
685
+ aChangedProperties
686
+ );
687
+ });
688
+ });
689
+ }
690
+
357
691
  /**
358
692
  * Convenience accessor for the easyFillDialogModel JSONModel.
359
693
  * @returns {sap.ui.model.json.JSONModel}
@@ -362,6 +696,50 @@ sap.ui.define([
362
696
  return oController.getView().getModel("easyFillDialogModel");
363
697
  }
364
698
 
699
+ function _findSmartTableForNavProperty(aSmartTables, sNavProperty) {
700
+ return aSmartTables.find(function(oTable) {
701
+ var oHandler = oTemplateUtils.oServices.oPresentationControlHandlerFactory.getPresentationControlHandler(oTable);
702
+ if (!oHandler) { return false; }
703
+ var sPath = oHandler.getBindingPath && oHandler.getBindingPath();
704
+ return sPath && sPath.split("/").pop() === sNavProperty;
705
+ });
706
+ }
707
+
708
+ /**
709
+ * Creates new table rows via CommonEventHandlers.addEntry (draft-aware) with navigation
710
+ * suppressed so the user stays on the ObjectPage.
711
+ * Using CommonEventHandlers.addEntry (bSuppressNavigation=true) instead of calling
712
+ * CRUDManager.addEntry directly ensures the SmartTable binding is refreshed and side
713
+ * effects are executed after the row is created — without that refresh the ObjectPage
714
+ * table would not show the newly created row.
715
+ *
716
+ * @param {object} mNewRowValues - map of { navProperty: plainValuesObject }
717
+ * @returns {Promise<void>}
718
+ */
719
+ async function _createNewRows(mNewRowValues) {
720
+ if (!mNewRowValues || !Object.keys(mNewRowValues).length) {
721
+ return;
722
+ }
723
+ var aSmartTables = EasyFillFieldCollector.getObjectPageSmartTables(oObjectPage);
724
+ var aNavProperties = Object.keys(mNewRowValues);
725
+
726
+ for (var i = 0; i < aNavProperties.length; i++) {
727
+ var sNavProp = aNavProperties[i];
728
+ var oSmartTable = _findSmartTableForNavProperty(aSmartTables, sNavProp);
729
+ if (!oSmartTable) {
730
+ continue;
731
+ }
732
+ try {
733
+ // bSuppressNavigation=true: stay on the OP (don't navigate to the new sub-item).
734
+ // bIgnoreTableRefresh not passed (undefined/falsy): the SmartTable binding is
735
+ // refreshed and side effects are executed so the new row becomes visible.
736
+ await oTemplateUtils.oCommonEventHandlers.addEntry(oSmartTable, true, undefined, mNewRowValues[sNavProp]);
737
+ } catch (e) {
738
+ // addEntry handles its own error messaging via CommonEventHandlers/CRUDManager
739
+ }
740
+ }
741
+ }
742
+
365
743
  // ─── Public API ──────────────────────────────────────────────────────────
366
744
 
367
745
  return {