@sapui5/sap.suite.ui.generic.template 1.147.2 → 1.148.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 (98) 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/controller/ControllerImplementation.js +5 -3
  4. package/src/sap/suite/ui/generic/template/AnalyticalListPage/i18n/i18n_bg.properties +3 -3
  5. package/src/sap/suite/ui/generic/template/AnalyticalListPage/i18n/i18n_da.properties +3 -3
  6. package/src/sap/suite/ui/generic/template/AnalyticalListPage/i18n/i18n_fr_CA.properties +2 -2
  7. package/src/sap/suite/ui/generic/template/AnalyticalListPage/i18n/i18n_id.properties +2 -2
  8. package/src/sap/suite/ui/generic/template/AnalyticalListPage/manifest.json +1 -1
  9. package/src/sap/suite/ui/generic/template/Canvas/manifest.json +1 -1
  10. package/src/sap/suite/ui/generic/template/ListReport/controller/ControllerImplementation.js +32 -3
  11. package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_fr.properties +1 -1
  12. package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_id.properties +1 -1
  13. package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_uk.properties +1 -1
  14. package/src/sap/suite/ui/generic/template/ListReport/manifest.json +1 -1
  15. package/src/sap/suite/ui/generic/template/ObjectPage/Component.js +4 -0
  16. package/src/sap/suite/ui/generic/template/ObjectPage/controller/ControllerImplementation.js +57 -25
  17. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n.properties +6 -0
  18. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_en_US_saprigi.properties +4 -0
  19. package/src/sap/suite/ui/generic/template/ObjectPage/manifest.json +6 -1
  20. package/src/sap/suite/ui/generic/template/ObjectPage/templateSpecificPreparationHelper.js +35 -19
  21. package/src/sap/suite/ui/generic/template/ObjectPage/view/fragments/Actions.fragment.xml +13 -13
  22. package/src/sap/suite/ui/generic/template/ObjectPage/view/fragments/Footer.fragment.xml +103 -42
  23. package/src/sap/suite/ui/generic/template/QuickCreate/manifest.json +1 -1
  24. package/src/sap/suite/ui/generic/template/QuickView/manifest.json +1 -1
  25. package/src/sap/suite/ui/generic/template/fragments/EasyFilter.fragment.xml +3 -0
  26. package/src/sap/suite/ui/generic/template/genericUtilities/controlHelper.js +30 -29
  27. package/src/sap/suite/ui/generic/template/genericUtilities/controlStateWrapperFactory/SmartFilterBarWrapper.js +15 -0
  28. package/src/sap/suite/ui/generic/template/lib/AppComponent.js +1 -1
  29. package/src/sap/suite/ui/generic/template/lib/CRUDHelper.js +93 -55
  30. package/src/sap/suite/ui/generic/template/lib/CRUDManager.js +7 -0
  31. package/src/sap/suite/ui/generic/template/lib/CommonEventHandlers.js +76 -19
  32. package/src/sap/suite/ui/generic/template/lib/CommonUtils.js +4 -1
  33. package/src/sap/suite/ui/generic/template/lib/ComponentUtils.js +17 -7
  34. package/src/sap/suite/ui/generic/template/lib/StableIdDefinition.js +35 -34
  35. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillAIOrchestrator.js +185 -0
  36. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillApplyHandler.js +476 -0
  37. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController.js +311 -0
  38. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector.js +669 -0
  39. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer.js +217 -0
  40. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillHandler.js +348 -422
  41. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer.js +491 -0
  42. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillDialog.fragment.xml +142 -72
  43. package/src/sap/suite/ui/generic/template/lib/ai/EasyFilterBarHandler.js +409 -22
  44. package/src/sap/suite/ui/generic/template/lib/i18n/i18n.properties +32 -8
  45. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ar.properties +14 -0
  46. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_bg.properties +14 -0
  47. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ca.properties +14 -0
  48. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cnr.properties +14 -0
  49. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cs.properties +14 -0
  50. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cy.properties +14 -0
  51. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_da.properties +14 -0
  52. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_de.properties +14 -0
  53. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_el.properties +14 -0
  54. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en.properties +14 -0
  55. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_GB.properties +14 -0
  56. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saprigi.properties +14 -0
  57. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es.properties +14 -0
  58. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es_MX.properties +14 -0
  59. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_et.properties +14 -0
  60. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fi.properties +14 -0
  61. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr.properties +15 -1
  62. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr_CA.properties +15 -1
  63. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hi.properties +14 -0
  64. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hr.properties +15 -1
  65. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hu.properties +14 -0
  66. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_id.properties +14 -0
  67. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_it.properties +14 -0
  68. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_iw.properties +14 -0
  69. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ja.properties +20 -6
  70. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_kk.properties +14 -0
  71. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ko.properties +14 -0
  72. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lt.properties +14 -0
  73. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lv.properties +14 -0
  74. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_mk.properties +14 -0
  75. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ms.properties +14 -0
  76. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_nl.properties +15 -1
  77. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_no.properties +14 -0
  78. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pl.properties +14 -0
  79. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt.properties +14 -0
  80. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt_PT.properties +14 -0
  81. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ro.properties +14 -0
  82. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ru.properties +14 -0
  83. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sh.properties +14 -0
  84. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sk.properties +14 -0
  85. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sl.properties +14 -0
  86. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sr.properties +14 -0
  87. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sv.properties +14 -0
  88. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_th.properties +14 -0
  89. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_tr.properties +14 -0
  90. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_uk.properties +17 -3
  91. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_vi.properties +14 -0
  92. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_CN.properties +20 -6
  93. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_TW.properties +20 -6
  94. package/src/sap/suite/ui/generic/template/lib/navigation/NavigationController.js +1 -1
  95. package/src/sap/suite/ui/generic/template/library.js +25 -2
  96. package/src/sap/suite/ui/generic/template/themes/base/ObjectPage.less +22 -3
  97. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillNonUpdatableSmartForm.fragment.xml +0 -18
  98. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillUpdatebleSmartForm.fragment.xml +0 -15
@@ -0,0 +1,476 @@
1
+ sap.ui.define([
2
+ "sap/suite/ui/generic/template/genericUtilities/FeLogger",
3
+ "sap/suite/ui/generic/template/genericUtilities/FeError",
4
+ "sap/ui/model/Filter",
5
+ "sap/ui/model/FilterOperator"
6
+ ], function(FeLogger, FeError, Filter, FilterOperator) {
7
+ "use strict";
8
+
9
+ var sClassName = "lib.ai.EasyFill.EasyFillApplyHandler";
10
+ var oLogger = new FeLogger(sClassName).getLogger();
11
+
12
+ /**
13
+ * Returns the first ValueListParameterInOut parameter matching the given local property name.
14
+ * Extracted as a module-level helper to avoid no-loop-func ESLint violations.
15
+ *
16
+ * @param {Array} aParameters - value-list Parameters array from OData annotations
17
+ * @param {string} sProperty - local property name to match
18
+ * @returns {object|undefined}
19
+ */
20
+ function fnFindValueListParameter(aParameters, sProperty) {
21
+ return aParameters.find(function(oParam) {
22
+ return oParam.RecordType === "com.sap.vocabularies.Common.v1.ValueListParameterInOut" &&
23
+ oParam.LocalDataProperty &&
24
+ oParam.LocalDataProperty.PropertyPath === sProperty;
25
+ });
26
+ }
27
+
28
+ /**
29
+ * EasyFillApplyHandler
30
+ *
31
+ * Responsible for:
32
+ * 1. Applying AI-suggested field values from transient contexts back to the live ObjectPage context on Confirm.
33
+ * 2. Building and validating staged collection (table) row updates before allowing save.
34
+ * 3. Resetting the EasyFill dialog state (model, staged values, transient context) on cancel or clear.
35
+ */
36
+ return {
37
+
38
+ /**
39
+ * Applies all AI-suggested scalar field values from the transient context to the live ObjectPage context.
40
+ * Called when the user clicks "Confirm" in the EasyFill dialog.
41
+ *
42
+ *
43
+ * Only properties present in the updatable lookup map are written back.
44
+ * Collection (table) row values are copied from transient staging contexts to the
45
+ * live row contexts only here, so the ObjectPage table updates only after confirm.
46
+ *
47
+ * @param {object} oTransientContextForEasyFill - the transient OData context holding AI-suggested values
48
+ * @param {object} oObjectPageContext - the live ObjectPage binding context to write values into
49
+ * @param {object} mUpdatableLookupMap - map of { fieldName: value } for fields that are updatable
50
+ * @param {object} oTableStagingState - table staging state { mStagedRows }
51
+ * @param {object} mCollectionState - collection tracking state
52
+ */
53
+ applyAndSave: function(oTransientContextForEasyFill, oObjectPageContext, mUpdatableLookupMap, oTableStagingState, mCollectionState) {
54
+ if (!oTransientContextForEasyFill || !oObjectPageContext) {
55
+ oLogger.warning("applyAndSave: missing transient or ObjectPage context — skipping apply");
56
+ return;
57
+ }
58
+
59
+ var oTransientObject = oTransientContextForEasyFill.getObject();
60
+ var iApplied = 0;
61
+
62
+ // Copy each AI-suggested property value from the transient context to the live OP context
63
+ for (var sProp in oTransientObject) {
64
+ if (sProp !== "__metadata" && mUpdatableLookupMap[sProp]) {
65
+ oObjectPageContext.setProperty(sProp, oTransientObject[sProp]);
66
+ iApplied++;
67
+ }
68
+ }
69
+
70
+ oLogger.debug("applyAndSave: applied " + iApplied + " field(s) to ObjectPage context");
71
+
72
+ var mCollectionUpdates = this.buildStagedCollectionUpdates(oTableStagingState, mCollectionState);
73
+ var iCollectionApplied = 0;
74
+
75
+ Object.keys(mCollectionUpdates).forEach(function(sCollectionName) {
76
+ var aRowUpdates = mCollectionUpdates[sCollectionName] || [];
77
+ aRowUpdates.forEach(function(oRowUpdate) {
78
+ var oResolvedRow = this._resolveCollectionRowContext(sCollectionName, oRowUpdate, mCollectionState);
79
+ if (!oResolvedRow || !oResolvedRow.context) {
80
+ return;
81
+ }
82
+
83
+ Object.keys(oRowUpdate).forEach(function(sPropertyName) {
84
+ if (sPropertyName === "_rowIndex" || sPropertyName === "_contextPath") {
85
+ return;
86
+ }
87
+ oResolvedRow.context.setProperty(sPropertyName, oRowUpdate[sPropertyName]);
88
+ iCollectionApplied++;
89
+ });
90
+ }, this);
91
+ }, this);
92
+
93
+ oLogger.debug("applyAndSave: applied " + iCollectionApplied + " staged table value(s) to ObjectPage context");
94
+ },
95
+
96
+ /**
97
+ * Builds a collection updates map from the staged transient row contexts.
98
+ * Only editable scalar properties whose staged value differs from the live row value are included.
99
+ *
100
+ * @param {object} oTableStagingState - table staging state { mStagedRows }
101
+ * @param {object} mCollectionState - collection tracking state
102
+ * @returns {object} map of { navProperty: [ rowUpdate ] }
103
+ */
104
+ buildStagedCollectionUpdates: function(oTableStagingState, mCollectionState) {
105
+ var mCollectionUpdates = Object.create(null);
106
+ var mStagedRowsByCollection = oTableStagingState && oTableStagingState.mStagedRows;
107
+
108
+ Object.keys(mStagedRowsByCollection || {}).forEach(function(sCollectionName) {
109
+ var aStagedRows = mStagedRowsByCollection[sCollectionName] || [];
110
+ var mCollectionPropertyMap = mCollectionState.mCollectionProperties[sCollectionName] || Object.create(null);
111
+
112
+ aStagedRows.forEach(function(oStagedRow) {
113
+ var oRealContext = oStagedRow.realContext;
114
+ var oTransientContext = oStagedRow.transientContext;
115
+ if (!oRealContext || !oTransientContext) {
116
+ return;
117
+ }
118
+
119
+ var oRealRowObject = oRealContext.getObject && oRealContext.getObject();
120
+ var oRowUpdate = {
121
+ _rowIndex: oStagedRow.rowIndex,
122
+ _contextPath: oRealContext.getPath && oRealContext.getPath()
123
+ };
124
+ var bHasChanges = false;
125
+
126
+ (oStagedRow.propertyNames || []).forEach(function(sPropertyName) {
127
+ var oPropertyMeta = mCollectionPropertyMap[sPropertyName];
128
+ if (!oPropertyMeta || !this._isEditableProperty(oPropertyMeta, oRealRowObject)) {
129
+ return;
130
+ }
131
+
132
+ var vLiveValue = oRealContext.getProperty(sPropertyName);
133
+ var vStagedValue = oTransientContext.getProperty(sPropertyName);
134
+ if (!this._isSupportedStagedValue(vStagedValue)) {
135
+ return;
136
+ }
137
+
138
+ if (!this._areValuesEqual(vLiveValue, vStagedValue)) {
139
+ oRowUpdate[sPropertyName] = vStagedValue;
140
+ bHasChanges = true;
141
+ }
142
+ }, this);
143
+
144
+ if (bHasChanges) {
145
+ if (!mCollectionUpdates[sCollectionName]) {
146
+ mCollectionUpdates[sCollectionName] = [];
147
+ }
148
+ mCollectionUpdates[sCollectionName].push(oRowUpdate);
149
+ }
150
+ }, this);
151
+ }, this);
152
+
153
+ return mCollectionUpdates;
154
+ },
155
+
156
+ /**
157
+ * Returns whether any staged collection row currently differs from the live ObjectPage rows.
158
+ *
159
+ * @param {object} oTableStagingState - table staging state { mStagedRows }
160
+ * @param {object} mCollectionState - collection tracking state
161
+ * @returns {boolean}
162
+ */
163
+ hasStagedCollectionChanges: function(oTableStagingState, mCollectionState) {
164
+ var mCollectionUpdates = this.buildStagedCollectionUpdates(oTableStagingState, mCollectionState);
165
+ return Object.keys(mCollectionUpdates).some(function(sCollectionName) {
166
+ return (mCollectionUpdates[sCollectionName] || []).length > 0;
167
+ });
168
+ },
169
+
170
+ /**
171
+ * Validates each collection (table) row update by checking that value-help constrained
172
+ * properties contain values that exist in the corresponding value-help collection.
173
+ *
174
+ * Returns an array of validation error objects for any values that fail the check.
175
+ * If validation cannot be performed (e.g. missing metadata), the property is skipped
176
+ * and save is not blocked.
177
+ *
178
+ * @param {object} mCollectionUpdates - map of { navProperty: [ rowUpdate ] }
179
+ * @param {object} mCollectionState - collection tracking state: { mCollectionProperties, mCollectionEntityTypes, mCollectionRowContextsByPath, mCollectionRowContexts }
180
+ * @param {object} oObjectPageModel - the OData model (used to read value-help collections)
181
+ * @param {object} oRb - i18n resource bundle
182
+ * @param {object} oFieldCollector - EasyFillFieldCollector (used for isEditableByContextObject)
183
+ * @param {object} oGenericAnnotationHelper - GenericAnnotationHelper (used for isValueHelpTableAvailable)
184
+ * @returns {Promise<Array>} array of { collectionName, collectionLabel, rowLabel, rowIndex, rowPath, propertyName, propertyLabel }
185
+ */
186
+ validateCollectionUpdatesAgainstValueHelp: async function(mCollectionUpdates, mCollectionState, oObjectPageModel, oRb, oFieldCollector, oGenericAnnotationHelper) {
187
+ var oMetaModel = oObjectPageModel && oObjectPageModel.getMetaModel();
188
+ if (!oMetaModel) {
189
+ oLogger.warning("validateCollectionUpdatesAgainstValueHelp: no MetaModel available");
190
+ return [];
191
+ }
192
+
193
+ var aValidationErrors = [];
194
+ var aCollectionNames = Object.keys(mCollectionUpdates || {});
195
+
196
+ for (var i = 0; i < aCollectionNames.length; i++) {
197
+ var sCollectionName = aCollectionNames[i];
198
+ var aRowUpdates = mCollectionUpdates[sCollectionName] || [];
199
+ var mCollectionPropertyMap = mCollectionState.mCollectionProperties[sCollectionName] || Object.create(null);
200
+ var oCollectionEntityType = mCollectionState.mCollectionEntityTypes[sCollectionName];
201
+
202
+ if (!oCollectionEntityType) { continue; }
203
+
204
+ // Use the field mapping description as the collection label in error messages
205
+ var sCollectionLabel = sCollectionName;
206
+
207
+ for (var j = 0; j < aRowUpdates.length; j++) {
208
+ var oRowUpdate = aRowUpdates[j];
209
+ var oResolvedRow = this._resolveCollectionRowContext(sCollectionName, oRowUpdate, mCollectionState);
210
+ if (!oResolvedRow || !oResolvedRow.context) { continue; }
211
+
212
+ var oRowContext = oResolvedRow.context;
213
+ var oRowObject = oRowContext.getObject && oRowContext.getObject();
214
+ var sRowLabel = oRb.getText("EASY_FILL_TABLE_ROW_LABEL", [String((oResolvedRow.rowIndex || 0) + 1)]);
215
+
216
+ var aPropNames = Object.keys(oRowUpdate);
217
+ for (var k = 0; k < aPropNames.length; k++) {
218
+ var sPropertyName = aPropNames[k];
219
+ if (sPropertyName === "_rowIndex" || sPropertyName === "_contextPath") { continue; }
220
+
221
+ var oPropertyMeta = mCollectionPropertyMap[sPropertyName];
222
+ // Skip non-editable properties — they cannot be written back
223
+ if (!oPropertyMeta || !oFieldCollector.isEditableByContextObject(oPropertyMeta, oRowObject)) { continue; }
224
+
225
+ // Only validate properties that have an associated value-help table
226
+ if (!oGenericAnnotationHelper.isValueHelpTableAvailable(oPropertyMeta)) { continue; }
227
+
228
+ var vUpdatedValue = oRowUpdate[sPropertyName];
229
+ if (vUpdatedValue === null || vUpdatedValue === undefined || typeof vUpdatedValue === "object") { continue; }
230
+
231
+ try {
232
+ var sPropertyPath = oMetaModel.getODataProperty(oCollectionEntityType, sPropertyName, true);
233
+ if (!sPropertyPath) { continue; }
234
+
235
+ var oPropertyContext = oMetaModel.createBindingContext(sPropertyPath);
236
+ var oValueLists = await oMetaModel.getODataValueLists(oPropertyContext);
237
+ var oDefaultValueList = oValueLists && oValueLists[""];
238
+
239
+ if (!oDefaultValueList || !oDefaultValueList.CollectionPath || !oDefaultValueList.CollectionPath.String) { continue; }
240
+
241
+ // Resolve which value-list property maps to this local property
242
+ var sValueListProperty = sPropertyName;
243
+ if (Array.isArray(oDefaultValueList.Parameters)) {
244
+ var oParameter = fnFindValueListParameter(oDefaultValueList.Parameters, sPropertyName);
245
+ if (oParameter && oParameter.ValueListProperty && oParameter.ValueListProperty.String) {
246
+ sValueListProperty = oParameter.ValueListProperty.String;
247
+ }
248
+ }
249
+
250
+ // Check that the AI-provided value exists in the value-help collection
251
+ var bValueFound = await this._readValueListByEquality(
252
+ oDefaultValueList.CollectionPath.String,
253
+ sValueListProperty,
254
+ vUpdatedValue,
255
+ oObjectPageModel
256
+ );
257
+
258
+ if (!bValueFound) {
259
+ aValidationErrors.push({
260
+ collectionName: sCollectionName,
261
+ collectionLabel: sCollectionLabel,
262
+ rowLabel: sRowLabel,
263
+ rowIndex: oResolvedRow.rowIndex,
264
+ rowPath: oRowContext.getPath && oRowContext.getPath(),
265
+ propertyName: sPropertyName,
266
+ propertyLabel: this._getPropertyLabel(oPropertyMeta) || sPropertyName
267
+ });
268
+ oLogger.warning("validateCollectionUpdatesAgainstValueHelp: invalid value for '" + sPropertyName + "' in row " + oResolvedRow.rowIndex);
269
+ }
270
+ } catch (e) {
271
+ // If metadata or value list cannot be resolved, skip the check and do not block save
272
+ oLogger.warning("validateCollectionUpdatesAgainstValueHelp: could not validate '" + sPropertyName + "': " + (e && e.message));
273
+ }
274
+ }
275
+ }
276
+ }
277
+
278
+ return aValidationErrors;
279
+ },
280
+
281
+ /**
282
+ * Updates the EasyFill dialog model to reflect the current table validation state.
283
+ * Sets hasTableValidationErrors and a human-readable summary message (up to 3 errors shown).
284
+ *
285
+ * @param {Array} aValidationErrors - array of validation error objects
286
+ * @param {object} oDialogModel - the easyFillDialogModel JSONModel
287
+ * @param {object} oRb - i18n resource bundle
288
+ */
289
+ applyTableValidationState: function(aValidationErrors, oDialogModel, oRb) {
290
+ if (!aValidationErrors || aValidationErrors.length === 0) {
291
+ oDialogModel.setProperty("/hasTableValidationErrors", false);
292
+ oDialogModel.setProperty("/tableValidationMessage", "");
293
+ return;
294
+ }
295
+
296
+ // Show up to 3 errors in the summary message to avoid overwhelming the user
297
+ var sSummary = aValidationErrors.slice(0, 3).map(function(oError) {
298
+ return oError.collectionLabel + " / " + oError.rowLabel + " / " + oError.propertyLabel;
299
+ }).join(", ");
300
+
301
+ oDialogModel.setProperty("/hasTableValidationErrors", true);
302
+ oDialogModel.setProperty(
303
+ "/tableValidationMessage",
304
+ oRb.getText("EASY_FILL_TABLE_VALIDATION_MESSAGE", [sSummary])
305
+ );
306
+
307
+ oLogger.warning("applyTableValidationState: " + aValidationErrors.length + " validation error(s) found");
308
+ },
309
+
310
+ /**
311
+ * Resets the EasyFill dialog to its initial empty state.
312
+ * Clears the dialog model properties, destroys table preview items,
313
+ * and optionally clears the text area value.
314
+ *
315
+ * @param {boolean} bRemoveTextAreaValue - if true, also clears the user input text area
316
+ * @param {object} oDialogModel - the easyFillDialogModel JSONModel
317
+ * @param {object} oController - MVC controller (used to byId controls)
318
+ * @param {object} mCollectionUpdates - mutable collection updates map (cleared in place)
319
+ * @param {object} oEasyFillLayoutMode - EasyFillLayoutMode enum
320
+ * @param {function} fnRevertStagedUpdates - EasyFillTableRenderer.revertStagedCollectionUpdates
321
+ * @param {function} fnResetTransientContext - EasyFillFormRenderer.resetTransientContext
322
+ */
323
+ resetEasyFill: function(bRemoveTextAreaValue, oDialogModel, oController, mCollectionUpdates, oEasyFillLayoutMode, fnRevertStagedUpdates, fnResetTransientContext) {
324
+ // Destroy all rendered table preview sections
325
+ var oTablePreviewContainer = oController.byId("EasyFillTablePreviewContainer");
326
+ if (oTablePreviewContainer) {
327
+ oTablePreviewContainer.destroyItems();
328
+ }
329
+
330
+ // Revert any staged table row values back to original
331
+ fnRevertStagedUpdates();
332
+
333
+ // Reset the transient context (discard all staged scalar AI values)
334
+ fnResetTransientContext();
335
+
336
+ // Reset all dialog model properties to their initial state
337
+ oDialogModel.setProperty("/isFormVisible", false);
338
+ oDialogModel.setProperty("/isIllustrationVisible", true);
339
+ oDialogModel.setProperty("/isSaveEnabled", false);
340
+ oDialogModel.setProperty("/stateType", "Initial");
341
+ oDialogModel.setProperty("/feedbackEnabled", true);
342
+ oDialogModel.setProperty("/feedbackPressedUp", false);
343
+ oDialogModel.setProperty("/feedbackPressedDown", false);
344
+ oDialogModel.setProperty("/isFullScreen", true);
345
+ oDialogModel.setProperty("/hasTableUpdates", false);
346
+ oDialogModel.setProperty("/hasTableValidationErrors", false);
347
+ oDialogModel.setProperty("/tableValidationMessage", "");
348
+ oDialogModel.setProperty("/easyFillMode", oEasyFillLayoutMode.CondensedMode);
349
+ oDialogModel.setProperty("/llmData", {});
350
+ oDialogModel.setProperty("/sections", []);
351
+
352
+ // Clear the mutable collection updates map in place
353
+ Object.keys(mCollectionUpdates).forEach(function(sKey) {
354
+ delete mCollectionUpdates[sKey];
355
+ });
356
+
357
+ if (bRemoveTextAreaValue) {
358
+ var oTextArea = oController.byId("EasyFillInputTextArea");
359
+ if (oTextArea) {
360
+ oTextArea.setValue("");
361
+ }
362
+ oDialogModel.setProperty("/isEasyFillButtonEnabled", false);
363
+ }
364
+
365
+ oLogger.debug("resetEasyFill: dialog state reset" + (bRemoveTextAreaValue ? " (including text area)" : ""));
366
+ },
367
+
368
+ // ─── Private Helpers ─────────────────────────────────────────────────────
369
+
370
+ /**
371
+ * Resolves an OData row context for a collection row update.
372
+ * Prefers context path; falls back to row index.
373
+ * (Mirrors EasyFillTableRenderer._resolveCollectionRowContext to keep this module independent.)
374
+ *
375
+ * @param {string} sCollectionName
376
+ * @param {object} oRowUpdate
377
+ * @param {object} mCollectionState
378
+ * @returns {{ context, rowIndex }|null}
379
+ */
380
+ _resolveCollectionRowContext: function(sCollectionName, oRowUpdate, mCollectionState) {
381
+ var mByPath = mCollectionState.mCollectionRowContextsByPath[sCollectionName] || Object.create(null);
382
+ var sContextPath = oRowUpdate && oRowUpdate._contextPath;
383
+ if (typeof sContextPath === "string" && mByPath[sContextPath]) {
384
+ return mByPath[sContextPath];
385
+ }
386
+ var iRowIndex = oRowUpdate && oRowUpdate._rowIndex;
387
+ var aRowContexts = mCollectionState.mCollectionRowContexts[sCollectionName] || [];
388
+ if (typeof iRowIndex === "number" && aRowContexts[iRowIndex]) {
389
+ return { context: aRowContexts[iRowIndex], rowIndex: iRowIndex };
390
+ }
391
+ return null;
392
+ },
393
+
394
+ /**
395
+ * Checks whether a given value exists in a value-help collection via an OData $filter read.
396
+ * Returns true if the value is found or if the read fails (fail-open to not block save unexpectedly).
397
+ *
398
+ * @param {string} sCollectionPath - value-help entity set name
399
+ * @param {string} sValueListProperty - property name in the value-help to filter on
400
+ * @param {*} vValue - the value to look up
401
+ * @param {object} oObjectPageModel - the OData model
402
+ * @returns {Promise<boolean>}
403
+ */
404
+ _readValueListByEquality: function(sCollectionPath, sValueListProperty, vValue, oObjectPageModel) {
405
+ return new Promise(function(resolve) {
406
+ oObjectPageModel.read("/" + sCollectionPath, {
407
+ filters: [
408
+ new Filter({
409
+ path: sValueListProperty,
410
+ operator: FilterOperator.EQ,
411
+ value1: vValue
412
+ })
413
+ ],
414
+ success: function(oResponse) {
415
+ // Value found if the filtered result set is non-empty
416
+ resolve(!!(oResponse && Array.isArray(oResponse.results) && oResponse.results.length > 0));
417
+ },
418
+ error: function() {
419
+ // On error, resolve true to not block save (fail-open)
420
+ resolve(true);
421
+ }
422
+ });
423
+ });
424
+ },
425
+
426
+ /**
427
+ * Extracts the display label for an OData property.
428
+ * Prefers Common.Label annotation, falls back to sap:label.
429
+ *
430
+ * @param {object} oPropertyMeta - OData property metadata
431
+ * @returns {string|null}
432
+ */
433
+ _getPropertyLabel: function(oPropertyMeta) {
434
+ if (!oPropertyMeta) { return null; }
435
+ var oLabel = oPropertyMeta["com.sap.vocabularies.Common.v1.Label"];
436
+ if (oLabel && oLabel.String) {
437
+ return oLabel.String;
438
+ }
439
+ return oPropertyMeta["sap:label"] || null;
440
+ },
441
+
442
+ _isEditableProperty: function(oPropertyMeta, oRowObject) {
443
+ if (!oPropertyMeta) {
444
+ return false;
445
+ }
446
+ var sUpdatable = oPropertyMeta["sap:updatable"];
447
+ if (sUpdatable) {
448
+ return sUpdatable !== "false";
449
+ }
450
+ var oAnnotation = oPropertyMeta["com.sap.vocabularies.Common.v1.FieldControl"];
451
+ if (oAnnotation && oAnnotation["EnumMember"]) {
452
+ return oAnnotation["EnumMember"] !== "com.sap.vocabularies.Common.v1.FieldControlType/ReadOnly";
453
+ }
454
+ var sFieldControl = oPropertyMeta["sap:field-control"];
455
+ if (sFieldControl) {
456
+ var iFieldControlValue = oRowObject && oRowObject[sFieldControl];
457
+ return iFieldControlValue !== 1;
458
+ }
459
+ return true;
460
+ },
461
+
462
+ _isSupportedStagedValue: function(vValue) {
463
+ return vValue === null ||
464
+ vValue === undefined ||
465
+ typeof vValue !== "object" ||
466
+ vValue instanceof Date;
467
+ },
468
+
469
+ _areValuesEqual: function(vLeft, vRight) {
470
+ if (vLeft instanceof Date && vRight instanceof Date) {
471
+ return vLeft.getTime() === vRight.getTime();
472
+ }
473
+ return vLeft === vRight;
474
+ }
475
+ };
476
+ });