@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.
- package/package.json +1 -1
- package/src/sap/suite/ui/generic/template/.library +1 -1
- 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 +6 -3
- package/src/sap/suite/ui/generic/template/ListReport/manifest.json +1 -1
- package/src/sap/suite/ui/generic/template/ObjectPage/manifest.json +1 -1
- 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/genericUtilities/controlStateWrapperFactory/SmartFilterBarWrapper.js +48 -8
- package/src/sap/suite/ui/generic/template/lib/AppComponent.js +1 -1
- package/src/sap/suite/ui/generic/template/lib/CRUDManager.js +7 -8
- package/src/sap/suite/ui/generic/template/lib/CommonEventHandlers.js +8 -1
- package/src/sap/suite/ui/generic/template/lib/CommonUtils.js +10 -2
- package/src/sap/suite/ui/generic/template/lib/MessageUtils.js +4 -3
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillAIOrchestrator.js +43 -12
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillApplyHandler.js +67 -218
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController.js +55 -8
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector.js +214 -35
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer.js +93 -18
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillHandler.js +426 -48
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer.js +512 -63
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillDialog.fragment.xml +33 -23
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFilterBarHandler.js +90 -73
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n.properties +24 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ar.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_bg.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ca.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cnr.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cs.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cy.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_da.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_de.properties +30 -12
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_el.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_GB.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saprigi.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es_MX.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_et.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fi.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr.properties +29 -11
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr_CA.properties +27 -9
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hi.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hr.properties +27 -9
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hu.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_id.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_it.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_iw.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ja.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_kk.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ko.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lt.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lv.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_mk.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ms.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_nl.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_no.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pl.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt_PT.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ro.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ru.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sh.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sk.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sl.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sr.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sv.properties +37 -19
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_th.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_tr.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_uk.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_vi.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_CN.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_TW.properties +26 -8
- package/src/sap/suite/ui/generic/template/lib/navigation/NavigationController.js +1 -1
- package/src/sap/suite/ui/generic/template/library.js +1 -1
- 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
|
|
244
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
283
|
-
|
|
284
|
-
|
|
381
|
+
_validateFieldsAndUpdateConfirmState,
|
|
382
|
+
false,
|
|
383
|
+
mRejectedNewRows
|
|
285
384
|
);
|
|
286
385
|
|
|
287
|
-
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
);
|
|
318
|
-
|
|
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
|
-
|
|
323
|
-
|
|
324
|
-
|
|
463
|
+
var bHasTableChanges = EasyFillApplyHandler.hasStagedCollectionChanges(oTableStagingState, mCollectionState);
|
|
464
|
+
var bHasChanges = bHasScalarChanges || bHasTableChanges;
|
|
465
|
+
oDialogModel().setProperty("/isSaveEnabled", bAllFieldsValid && bHasChanges);
|
|
466
|
+
}
|
|
325
467
|
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
-
|
|
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 {
|