@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.
- package/package.json +1 -1
- package/src/sap/suite/ui/generic/template/.library +1 -1
- package/src/sap/suite/ui/generic/template/AnalyticalListPage/controller/ControllerImplementation.js +5 -3
- package/src/sap/suite/ui/generic/template/AnalyticalListPage/i18n/i18n_bg.properties +3 -3
- package/src/sap/suite/ui/generic/template/AnalyticalListPage/i18n/i18n_da.properties +3 -3
- package/src/sap/suite/ui/generic/template/AnalyticalListPage/i18n/i18n_fr_CA.properties +2 -2
- package/src/sap/suite/ui/generic/template/AnalyticalListPage/i18n/i18n_id.properties +2 -2
- package/src/sap/suite/ui/generic/template/AnalyticalListPage/manifest.json +1 -1
- package/src/sap/suite/ui/generic/template/Canvas/manifest.json +1 -1
- package/src/sap/suite/ui/generic/template/ListReport/controller/ControllerImplementation.js +32 -3
- package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_fr.properties +1 -1
- package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_id.properties +1 -1
- package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_uk.properties +1 -1
- package/src/sap/suite/ui/generic/template/ListReport/manifest.json +1 -1
- package/src/sap/suite/ui/generic/template/ObjectPage/Component.js +4 -0
- package/src/sap/suite/ui/generic/template/ObjectPage/controller/ControllerImplementation.js +57 -25
- package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n.properties +6 -0
- package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_en_US_saprigi.properties +4 -0
- package/src/sap/suite/ui/generic/template/ObjectPage/manifest.json +6 -1
- package/src/sap/suite/ui/generic/template/ObjectPage/templateSpecificPreparationHelper.js +35 -19
- package/src/sap/suite/ui/generic/template/ObjectPage/view/fragments/Actions.fragment.xml +13 -13
- package/src/sap/suite/ui/generic/template/ObjectPage/view/fragments/Footer.fragment.xml +103 -42
- package/src/sap/suite/ui/generic/template/QuickCreate/manifest.json +1 -1
- package/src/sap/suite/ui/generic/template/QuickView/manifest.json +1 -1
- package/src/sap/suite/ui/generic/template/fragments/EasyFilter.fragment.xml +3 -0
- package/src/sap/suite/ui/generic/template/genericUtilities/controlHelper.js +30 -29
- package/src/sap/suite/ui/generic/template/genericUtilities/controlStateWrapperFactory/SmartFilterBarWrapper.js +15 -0
- package/src/sap/suite/ui/generic/template/lib/AppComponent.js +1 -1
- package/src/sap/suite/ui/generic/template/lib/CRUDHelper.js +93 -55
- package/src/sap/suite/ui/generic/template/lib/CRUDManager.js +7 -0
- package/src/sap/suite/ui/generic/template/lib/CommonEventHandlers.js +76 -19
- package/src/sap/suite/ui/generic/template/lib/CommonUtils.js +4 -1
- package/src/sap/suite/ui/generic/template/lib/ComponentUtils.js +17 -7
- package/src/sap/suite/ui/generic/template/lib/StableIdDefinition.js +35 -34
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillAIOrchestrator.js +185 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillApplyHandler.js +476 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController.js +311 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector.js +669 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer.js +217 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillHandler.js +348 -422
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer.js +491 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillDialog.fragment.xml +142 -72
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFilterBarHandler.js +409 -22
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n.properties +32 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ar.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_bg.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ca.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cnr.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cs.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cy.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_da.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_de.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_el.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_GB.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saprigi.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es_MX.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_et.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fi.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr.properties +15 -1
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr_CA.properties +15 -1
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hi.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hr.properties +15 -1
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hu.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_id.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_it.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_iw.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ja.properties +20 -6
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_kk.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ko.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lt.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lv.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_mk.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ms.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_nl.properties +15 -1
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_no.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pl.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt_PT.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ro.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ru.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sh.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sk.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sl.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sr.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sv.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_th.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_tr.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_uk.properties +17 -3
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_vi.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_CN.properties +20 -6
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_TW.properties +20 -6
- package/src/sap/suite/ui/generic/template/lib/navigation/NavigationController.js +1 -1
- package/src/sap/suite/ui/generic/template/library.js +25 -2
- package/src/sap/suite/ui/generic/template/themes/base/ObjectPage.less +22 -3
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillNonUpdatableSmartForm.fragment.xml +0 -18
- 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
|
+
});
|