@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
|
@@ -60,7 +60,7 @@ sap.ui.define([
|
|
|
60
60
|
* @param {object} oObjectPage - the ObjectPage control (used to find SmartTables)
|
|
61
61
|
* @param {object} oObjectPageModel - the OData model of the ObjectPage
|
|
62
62
|
* @param {object} oTemplateUtils - FE template utilities (used for PresentationControlHandler)
|
|
63
|
-
* @param {object} mCollectionState - mutable collection state: { mCollectionRowContexts, mCollectionRowContextsByPath, mCollectionProperties, mCollectionEntityTypes }
|
|
63
|
+
* @param {object} mCollectionState - mutable collection state: { mCollectionRowContexts, mCollectionRowContextsByPath, mCollectionProperties, mCollectionEntityTypes, mCollectionEntitySets }
|
|
64
64
|
* @param {function} fnGetLabel - function(oProperty) → string label
|
|
65
65
|
*/
|
|
66
66
|
appendCollectionMap: function(mPropertyMap, oObjectPage, oObjectPageModel, oTemplateUtils, mCollectionState, fnGetLabel) {
|
|
@@ -69,6 +69,8 @@ sap.ui.define([
|
|
|
69
69
|
mCollectionState.mCollectionRowContextsByPath = Object.create(null);
|
|
70
70
|
mCollectionState.mCollectionProperties = Object.create(null);
|
|
71
71
|
mCollectionState.mCollectionEntityTypes = Object.create(null);
|
|
72
|
+
mCollectionState.mCollectionEntitySets = Object.create(null);
|
|
73
|
+
mCollectionState.mCollectionAllowsCreation = Object.create(null);
|
|
72
74
|
|
|
73
75
|
var aSmartTables = this.getObjectPageSmartTables(oObjectPage);
|
|
74
76
|
if (!aSmartTables.length) {
|
|
@@ -154,6 +156,8 @@ sap.ui.define([
|
|
|
154
156
|
mCollectionState.mCollectionRowContextsByPath[sNavProperty] = mRowsByPath;
|
|
155
157
|
mCollectionState.mCollectionProperties[sNavProperty] = mPropertyByName;
|
|
156
158
|
mCollectionState.mCollectionEntityTypes[sNavProperty] = oEntityType;
|
|
159
|
+
mCollectionState.mCollectionEntitySets[sNavProperty] = oEntitySet;
|
|
160
|
+
mCollectionState.mCollectionAllowsCreation[sNavProperty] = this.resolveTableAllowsCreation(oSmartTable);
|
|
157
161
|
|
|
158
162
|
// Add collection entry to the LLM field mapping
|
|
159
163
|
mPropertyMap[sNavProperty] = {
|
|
@@ -177,9 +181,10 @@ sap.ui.define([
|
|
|
177
181
|
* @param {object} oAiCallResult - flat map of { fieldName: aiValue } from the LLM
|
|
178
182
|
* @param {object} oEntityType - OData entity type metadata
|
|
179
183
|
* @param {object} oObjectPageContext - current ObjectPage binding context
|
|
184
|
+
* @param {object} oEntitySet - OData entity set metadata (for entity-set-level sap:updatable checks)
|
|
180
185
|
* @returns {{ updatableFields: object, nonUpdatableFields: object }}
|
|
181
186
|
*/
|
|
182
|
-
getUpdatableAndNonUpdatableFields: function(oAiCallResult, oEntityType, oObjectPageContext) {
|
|
187
|
+
getUpdatableAndNonUpdatableFields: function(oAiCallResult, oEntityType, oObjectPageContext, oEntitySet) {
|
|
183
188
|
var updatableFields = {};
|
|
184
189
|
var nonUpdatableFields = {};
|
|
185
190
|
|
|
@@ -195,7 +200,7 @@ sap.ui.define([
|
|
|
195
200
|
nonUpdatableFields[sKey] = oAiCallResult[sKey];
|
|
196
201
|
return;
|
|
197
202
|
}
|
|
198
|
-
if (this.isEditable(oProperty, oObjectPageContext)) {
|
|
203
|
+
if (this.isEditable(oProperty, oObjectPageContext, oEntitySet)) {
|
|
199
204
|
updatableFields[sKey] = oAiCallResult[sKey];
|
|
200
205
|
} else {
|
|
201
206
|
nonUpdatableFields[sKey] = oAiCallResult[sKey];
|
|
@@ -250,18 +255,19 @@ sap.ui.define([
|
|
|
250
255
|
* Checks whether a property is editable based on OData annotations.
|
|
251
256
|
* Uses the current ObjectPage context object for dynamic field-control checks.
|
|
252
257
|
*
|
|
253
|
-
* Priority: sap:updatable → FieldControl (EnumMember) → sap:field-control (dynamic value)
|
|
258
|
+
* Priority: entity-set-level sap:updatable → property sap:updatable → FieldControl (EnumMember) → sap:field-control (dynamic value)
|
|
254
259
|
*
|
|
255
260
|
* Field control values:
|
|
256
261
|
* 0 = Hidden, 1 = ReadOnly, 3 = Editable, 7 = Mandatory (editable)
|
|
257
262
|
*
|
|
258
263
|
* @param {object} oProperty - OData property metadata
|
|
259
264
|
* @param {object} oObjectPageContext - current ObjectPage binding context
|
|
265
|
+
* @param {object} oEntitySet - optional OData entity set metadata (for entity-set-level checks)
|
|
260
266
|
* @returns {boolean}
|
|
261
267
|
*/
|
|
262
|
-
isEditable: function(oProperty, oObjectPageContext) {
|
|
268
|
+
isEditable: function(oProperty, oObjectPageContext, oEntitySet) {
|
|
263
269
|
var oContextObject = oObjectPageContext && oObjectPageContext.getObject && oObjectPageContext.getObject();
|
|
264
|
-
return this.isEditableByContextObject(oProperty, oContextObject);
|
|
270
|
+
return this.isEditableByContextObject(oProperty, oContextObject, oEntitySet);
|
|
265
271
|
},
|
|
266
272
|
|
|
267
273
|
/**
|
|
@@ -270,14 +276,31 @@ sap.ui.define([
|
|
|
270
276
|
*
|
|
271
277
|
* @param {object} oProperty - OData property metadata
|
|
272
278
|
* @param {object} oContextObject - plain JS object of the current row/entity data
|
|
279
|
+
* @param {object} oEntitySet - optional OData entity set metadata (for entity-set-level checks)
|
|
273
280
|
* @returns {boolean}
|
|
274
281
|
*/
|
|
275
|
-
isEditableByContextObject: function(oProperty, oContextObject) {
|
|
282
|
+
isEditableByContextObject: function(oProperty, oContextObject, oEntitySet) {
|
|
276
283
|
if (!oProperty) {
|
|
277
284
|
return false;
|
|
278
285
|
}
|
|
279
286
|
|
|
280
|
-
// Check sap:updatable
|
|
287
|
+
// Check entity-set-level sap:updatable (entire entity set is read-only)
|
|
288
|
+
if (oEntitySet) {
|
|
289
|
+
var sEntitySetUpdatable = oEntitySet["sap:updatable"];
|
|
290
|
+
if (sEntitySetUpdatable === "false") {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
// sap:updatable-path: resolved against the context object (boolean property)
|
|
294
|
+
var sEntitySetUpdatablePath = oEntitySet["sap:updatable-path"];
|
|
295
|
+
if (sEntitySetUpdatablePath && oContextObject) {
|
|
296
|
+
var bUpdatableFromPath = oContextObject[sEntitySetUpdatablePath];
|
|
297
|
+
if (bUpdatableFromPath === false || bUpdatableFromPath === "false") {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Check sap:updatable — static annotation on the property
|
|
281
304
|
var sUpdatable = oProperty["sap:updatable"];
|
|
282
305
|
if (sUpdatable) {
|
|
283
306
|
return sUpdatable !== "false";
|
|
@@ -304,32 +327,101 @@ sap.ui.define([
|
|
|
304
327
|
|
|
305
328
|
/**
|
|
306
329
|
* Builds a flat section tree for the Condensed view mode.
|
|
307
|
-
*
|
|
330
|
+
* Creates two sections: "Filled Fields" (updatable) and "Unfilled Fields" (non-updatable).
|
|
308
331
|
*
|
|
309
332
|
* @param {object} oLLMResult - flat map of { fieldName: value } from the LLM
|
|
333
|
+
* @param {object} oUpdatableFields - flat map of updatable fields only
|
|
334
|
+
* @param {object} oNonUpdatableFields - flat map of non-updatable fields only
|
|
310
335
|
* @returns {Array} section tree array
|
|
311
336
|
*/
|
|
312
|
-
getSectionsForCondensedView: function(oLLMResult) {
|
|
313
|
-
|
|
337
|
+
getSectionsForCondensedView: function(oLLMResult, oUpdatableFields, oNonUpdatableFields) {
|
|
338
|
+
var aUpdatableKeys = Object.keys(oUpdatableFields || {});
|
|
339
|
+
var aNonUpdatableKeys = Object.keys(oNonUpdatableFields || {});
|
|
340
|
+
var aSections = [];
|
|
341
|
+
// Section 1: Filled Fields (updatable)
|
|
342
|
+
if (aUpdatableKeys.length > 0) {
|
|
343
|
+
aSections.push({
|
|
344
|
+
title: "{@i18n>EASY_FILL_FILLED_FIELDS}",
|
|
345
|
+
fields: [],
|
|
346
|
+
children: [{
|
|
347
|
+
title: "",
|
|
348
|
+
fields: aUpdatableKeys,
|
|
349
|
+
id: this._generateId("filledFields", aUpdatableKeys)
|
|
350
|
+
}]
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Section 2: Unfilled Fields (non-updatable/read-only)
|
|
355
|
+
if (aNonUpdatableKeys.length > 0) {
|
|
356
|
+
aSections.push({
|
|
357
|
+
title: "{@i18n>EASY_FILL_UNFILLED_FIELDS}",
|
|
358
|
+
fields: [],
|
|
359
|
+
children: [{
|
|
360
|
+
title: "",
|
|
361
|
+
fields: aNonUpdatableKeys,
|
|
362
|
+
id: this._generateId("unfilledFields", aNonUpdatableKeys)
|
|
363
|
+
}]
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Fallback: if no split provided, use old behavior
|
|
368
|
+
if (aSections.length === 0) {
|
|
369
|
+
return this._transformToTreeExpanded(oLLMResult);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return aSections;
|
|
314
373
|
},
|
|
315
374
|
|
|
316
375
|
/**
|
|
317
376
|
* Builds a structured section tree for the ObjectPage view mode.
|
|
318
377
|
* Mirrors the ObjectPage section/facet structure and filters to only include
|
|
319
|
-
* fields returned by the LLM.
|
|
378
|
+
* updatable fields returned by the LLM. Non-updatable (read-only) fields are
|
|
379
|
+
* excluded from the annotation sections and shown exclusively in the
|
|
380
|
+
* "Unfilled Fields" section appended at the end.
|
|
320
381
|
*
|
|
321
382
|
* @param {object} oEntityType - OData entity type metadata
|
|
322
383
|
* @param {object} oLLMResult - flat map of { fieldName: value } from the LLM
|
|
323
384
|
* @param {object} oRb - i18n resource bundle
|
|
385
|
+
* @param {object} oNonUpdatableFields - flat map of non-updatable fields only (optional)
|
|
324
386
|
* @returns {Array} filtered section tree array
|
|
325
387
|
*/
|
|
326
|
-
getSectionsForObjectPageView: function(oEntityType, oLLMResult, oRb) {
|
|
388
|
+
getSectionsForObjectPageView: function(oEntityType, oLLMResult, oRb, oNonUpdatableFields) {
|
|
327
389
|
var aSections = [];
|
|
328
390
|
aSections = aSections
|
|
329
391
|
.concat(this._getSectionsForHeader(oEntityType, oRb))
|
|
330
392
|
.concat(this._getSectionsForObjectPageFacets(oEntityType));
|
|
331
393
|
var aTree = this._transformToTree(aSections);
|
|
332
|
-
|
|
394
|
+
|
|
395
|
+
// Annotation sections must only show updatable fields.
|
|
396
|
+
// Derive the updatable-only subset by removing non-updatable keys from oLLMResult
|
|
397
|
+
// so non-updatable fields do not appear in both annotation sections AND Unfilled Fields.
|
|
398
|
+
var aNonUpdatableSet = Object.keys(oNonUpdatableFields || {});
|
|
399
|
+
var oUpdatableResult = Object.keys(oLLMResult).reduce(function(oAcc, sKey) {
|
|
400
|
+
if (aNonUpdatableSet.indexOf(sKey) === -1) {
|
|
401
|
+
oAcc[sKey] = oLLMResult[sKey];
|
|
402
|
+
}
|
|
403
|
+
return oAcc;
|
|
404
|
+
}, {});
|
|
405
|
+
var aFilteredTree = this._filterTreeByLLMResult(aTree, oUpdatableResult);
|
|
406
|
+
aFilteredTree = this._flattenTreeForObjectPageDialog(aFilteredTree);
|
|
407
|
+
|
|
408
|
+
// "Unfilled Fields" contains only non-updatable (read-only) fields.
|
|
409
|
+
// Editable fields that have no annotation section are silently dropped here —
|
|
410
|
+
// they already appear in their respective annotation-matched sections above.
|
|
411
|
+
var aNonUpdatableKeys = Object.keys(oNonUpdatableFields || {});
|
|
412
|
+
if (aNonUpdatableKeys.length > 0) {
|
|
413
|
+
aFilteredTree.push({
|
|
414
|
+
title: "{@i18n>EASY_FILL_UNFILLED_FIELDS}",
|
|
415
|
+
fields: [],
|
|
416
|
+
children: [{
|
|
417
|
+
title: "",
|
|
418
|
+
fields: aNonUpdatableKeys,
|
|
419
|
+
id: this._generateId("unfilledFields", aNonUpdatableKeys)
|
|
420
|
+
}]
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return aFilteredTree;
|
|
333
425
|
},
|
|
334
426
|
|
|
335
427
|
// ─── SmartTable Utilities ─────────────────────────────────────────────────
|
|
@@ -375,6 +467,49 @@ sap.ui.define([
|
|
|
375
467
|
};
|
|
376
468
|
},
|
|
377
469
|
|
|
470
|
+
/**
|
|
471
|
+
* Determines whether a SmartTable supports creation of new rows via inline mode.
|
|
472
|
+
* Supports "inline" createMode (with an enabled Create toolbar button) and
|
|
473
|
+
* "creationRows" / "creationRowsHiddenInEditMode" createMode (InlineCreationRows).
|
|
474
|
+
*
|
|
475
|
+
* @param {object} oSmartTable - sap.ui.comp.smarttable.SmartTable control
|
|
476
|
+
* @returns {boolean} true if new rows can be created for this table
|
|
477
|
+
*/
|
|
478
|
+
resolveTableAllowsCreation: function(oSmartTable) {
|
|
479
|
+
var sCreateMode = oSmartTable.data("creationMode");
|
|
480
|
+
var bIsInlineCreationRows = sCreateMode === "creationRows" || sCreateMode === "creationRowsHiddenInEditMode";
|
|
481
|
+
var bIsInline = sCreateMode === "inline";
|
|
482
|
+
|
|
483
|
+
if (bIsInlineCreationRows) {
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (bIsInline) {
|
|
488
|
+
// Inline create requires the Create toolbar button to be enabled
|
|
489
|
+
var oTable = oSmartTable.getTable && oSmartTable.getTable();
|
|
490
|
+
if (!oTable) {
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
var oToolbar = oSmartTable.getCustomToolbar && oSmartTable.getCustomToolbar();
|
|
494
|
+
if (!oToolbar) {
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
var aButtons = oToolbar.getContent && oToolbar.getContent();
|
|
498
|
+
if (!Array.isArray(aButtons)) {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
var oCreateButton = aButtons.find(function(oControl) {
|
|
502
|
+
return oControl.isA && oControl.isA("sap.m.Button") &&
|
|
503
|
+
typeof oControl.getId === "function" &&
|
|
504
|
+
oControl.getId().indexOf("add") >= 0 &&
|
|
505
|
+
oControl.getEnabled && oControl.getEnabled();
|
|
506
|
+
});
|
|
507
|
+
return !!oCreateButton;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
return false;
|
|
511
|
+
},
|
|
512
|
+
|
|
378
513
|
// ─── Private Helpers ─────────────────────────────────────────────────────
|
|
379
514
|
|
|
380
515
|
/**
|
|
@@ -482,23 +617,23 @@ sap.ui.define([
|
|
|
482
617
|
|
|
483
618
|
/**
|
|
484
619
|
* Filters a section tree to only include nodes that contain at least one LLM-returned field.
|
|
485
|
-
* Fields
|
|
620
|
+
* Fields with no matching annotation section are silently dropped; the caller is responsible
|
|
621
|
+
* for surfacing them elsewhere (e.g. the "unfilledFields" section for non-updatable fields).
|
|
622
|
+
*
|
|
623
|
+
* @param {Array} aTree - section tree
|
|
624
|
+
* @param {object} oLLMResult - flat map of all LLM fields
|
|
625
|
+
* @returns {Array} filtered tree
|
|
486
626
|
* @private
|
|
487
627
|
*/
|
|
488
628
|
_filterTreeByLLMResult: function(aTree, oLLMResult) {
|
|
489
629
|
var aRequestedKeys = Object.keys(oLLMResult);
|
|
490
|
-
var aMatchedKeys = new Set();
|
|
491
630
|
var that = this;
|
|
492
631
|
|
|
493
632
|
function filterNode(oNode, sParentPath) {
|
|
494
633
|
var sTitlePath = sParentPath ? (sParentPath + "_" + oNode.title) : oNode.title;
|
|
495
634
|
|
|
496
635
|
var aFilteredFields = oNode.fields.filter(function(sField) {
|
|
497
|
-
|
|
498
|
-
aMatchedKeys.add(sField);
|
|
499
|
-
return true;
|
|
500
|
-
}
|
|
501
|
-
return false;
|
|
636
|
+
return aRequestedKeys.includes(sField);
|
|
502
637
|
});
|
|
503
638
|
|
|
504
639
|
var aFilteredChildren = oNode.children
|
|
@@ -513,22 +648,58 @@ sap.ui.define([
|
|
|
513
648
|
};
|
|
514
649
|
}
|
|
515
650
|
|
|
516
|
-
|
|
651
|
+
return aTree
|
|
517
652
|
.map(function(oNode) { return filterNode(oNode, ""); })
|
|
518
653
|
.filter(function(oNode) { return oNode.fields.length > 0 || oNode.children.length > 0; });
|
|
654
|
+
},
|
|
519
655
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
656
|
+
/**
|
|
657
|
+
* Flattens nested ObjectPage section trees to one group level expected by the dialog fragment.
|
|
658
|
+
*
|
|
659
|
+
* EasyFill dialog binds SmartForm groups only to section.children (one level deep).
|
|
660
|
+
* If annotation facets produce deeper nesting, section titles can render while fields remain
|
|
661
|
+
* invisible because grandchildren are never bound as groups.
|
|
662
|
+
*
|
|
663
|
+
* @param {Array} aTree - filtered section tree
|
|
664
|
+
* @returns {Array} dialog-compatible tree with one-level children per section
|
|
665
|
+
* @private
|
|
666
|
+
*/
|
|
667
|
+
_flattenTreeForObjectPageDialog: function(aTree) {
|
|
668
|
+
var that = this;
|
|
669
|
+
|
|
670
|
+
function collectRenderableGroups(oNode, sParentPath) {
|
|
671
|
+
var sTitlePath = sParentPath ? (sParentPath + "_" + oNode.title) : oNode.title;
|
|
672
|
+
var aGroups = [];
|
|
673
|
+
|
|
674
|
+
if (Array.isArray(oNode.fields) && oNode.fields.length > 0) {
|
|
675
|
+
aGroups.push({
|
|
676
|
+
title: oNode.title,
|
|
677
|
+
id: that._generateId(sTitlePath, oNode.fields),
|
|
678
|
+
fields: oNode.fields,
|
|
679
|
+
children: []
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (Array.isArray(oNode.children) && oNode.children.length > 0) {
|
|
684
|
+
oNode.children.forEach(function(oChild) {
|
|
685
|
+
aGroups = aGroups.concat(collectRenderableGroups(oChild, sTitlePath));
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return aGroups;
|
|
529
690
|
}
|
|
530
691
|
|
|
531
|
-
return
|
|
692
|
+
return (aTree || []).map(function(oSection) {
|
|
693
|
+
var sSectionPath = oSection.title || "";
|
|
694
|
+
return {
|
|
695
|
+
title: oSection.title,
|
|
696
|
+
id: oSection.id,
|
|
697
|
+
fields: [],
|
|
698
|
+
children: collectRenderableGroups(oSection, sSectionPath)
|
|
699
|
+
};
|
|
700
|
+
}).filter(function(oSection) {
|
|
701
|
+
return Array.isArray(oSection.children) && oSection.children.length > 0;
|
|
702
|
+
});
|
|
532
703
|
},
|
|
533
704
|
|
|
534
705
|
/**
|
|
@@ -638,12 +809,14 @@ sap.ui.define([
|
|
|
638
809
|
// FieldGroup / Form — iterate Data array
|
|
639
810
|
oAnnotation.Data.forEach(function(oData) {
|
|
640
811
|
if (!this._isSupportedRecordType(oData.RecordType)) { return; }
|
|
812
|
+
if (!oData.Value || !oData.Value.Path) { return; }
|
|
641
813
|
aRes.push({ title: aTempText, property: oData.Value.Path });
|
|
642
814
|
}.bind(this));
|
|
643
815
|
} else if (Array.isArray(oAnnotation)) {
|
|
644
816
|
// LineItem — iterate annotation array directly
|
|
645
817
|
oAnnotation.forEach(function(oAnno) {
|
|
646
818
|
if (!this._isSupportedRecordType(oAnno.RecordType)) { return; }
|
|
819
|
+
if (!oAnno.Value || !oAnno.Value.Path) { return; }
|
|
647
820
|
aRes.push({ title: aTempText, property: oAnno.Value.Path });
|
|
648
821
|
}.bind(this));
|
|
649
822
|
} else if (oAnnotation.Value && oAnnotation.Value.Path) {
|
|
@@ -658,12 +831,18 @@ sap.ui.define([
|
|
|
658
831
|
/**
|
|
659
832
|
* Returns false for record types that EasyFill does not support rendering.
|
|
660
833
|
* Currently excludes: IntentBasedNavigation, ForAnnotation, ForAction.
|
|
834
|
+
* Handles both fully-qualified names (com.sap.vocabularies.UI.v1.*) and
|
|
835
|
+
* alias form (UI.*) that OData metadata can return depending on the service.
|
|
661
836
|
* @private
|
|
662
837
|
*/
|
|
663
838
|
_isSupportedRecordType: function(sRecordType) {
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
839
|
+
if (!sRecordType) { return false; }
|
|
840
|
+
var sNormalized = sRecordType.replace("com.sap.vocabularies.UI.v1.", "UI.");
|
|
841
|
+
return sNormalized !== "UI.DataFieldForIntentBasedNavigation" &&
|
|
842
|
+
sNormalized !== "UI.DataFieldForAnnotation" &&
|
|
843
|
+
sNormalized !== "UI.DataFieldForAction" &&
|
|
844
|
+
sNormalized !== "UI.DataFieldWithNavigationPath" &&
|
|
845
|
+
sNormalized !== "UI.DataFieldWithIntentBasedNavigation";
|
|
667
846
|
}
|
|
668
847
|
};
|
|
669
848
|
});
|
|
@@ -47,23 +47,40 @@ sap.ui.define([
|
|
|
47
47
|
* @param {object} oLLMData - flat map of { fieldName: aiValue } from the LLM response
|
|
48
48
|
* @returns {boolean} always returns true (used as a formatter return value in XML)
|
|
49
49
|
*/
|
|
50
|
-
populateSmartFields: function(sId, aFields, sEasyFillMode, oController, oTransientContextForEasyFill, oObjectPageContext, mUpdatableLookupMap, oLLMData) {
|
|
50
|
+
populateSmartFields: function(sId, aFields, sEasyFillMode, oController, oTransientContextForEasyFill, oObjectPageContext, mUpdatableLookupMap, oLLMData, fnOnFieldChange) {
|
|
51
51
|
var oObjectPageLayout = oController.byId("EasyFillObjectPageLayout");
|
|
52
52
|
if (!oObjectPageLayout || !Array.isArray(aFields) || aFields.length === 0) {
|
|
53
53
|
return true;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
var sPreviouslyLabel = oController.getView().getModel("i18n").getResourceBundle().getText("EASY_FILL_PREVIOUSLY_LABEL");
|
|
57
|
+
|
|
56
58
|
// Defer injection until after the SmartForm has rendered into the DOM
|
|
57
59
|
setTimeout(function() {
|
|
58
60
|
var oTargetGroup = null;
|
|
59
61
|
|
|
60
62
|
// Walk the ObjectPageLayout section hierarchy to find the group
|
|
61
|
-
// matching the given CustomData "id" value
|
|
63
|
+
// matching the given CustomData "id" value.
|
|
64
|
+
// In condensed mode the SmartForm may be nested inside a wrapper VBox
|
|
65
|
+
// (used to prevent BlockLayout from placing the table side-by-side with
|
|
66
|
+
// the SmartForm on resize). We therefore check one level deeper when the
|
|
67
|
+
// direct block is not a SmartForm.
|
|
62
68
|
oObjectPageLayout.getSections().forEach(function(oSection) {
|
|
63
69
|
oSection.getSubSections().forEach(function(oSubSection) {
|
|
64
70
|
oSubSection.getBlocks().forEach(function(oBlock) {
|
|
71
|
+
var aSmartFormCandidates = [];
|
|
65
72
|
if (oBlock.isA("sap.ui.comp.smartform.SmartForm")) {
|
|
66
|
-
|
|
73
|
+
aSmartFormCandidates.push(oBlock);
|
|
74
|
+
} else if (typeof oBlock.getItems === "function") {
|
|
75
|
+
// Look one level inside (e.g. condensed-mode wrapper VBox)
|
|
76
|
+
oBlock.getItems().forEach(function(oItem) {
|
|
77
|
+
if (oItem.isA("sap.ui.comp.smartform.SmartForm")) {
|
|
78
|
+
aSmartFormCandidates.push(oItem);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
aSmartFormCandidates.forEach(function(oSmartForm) {
|
|
83
|
+
oSmartForm.getGroups().forEach(function(oGroup) {
|
|
67
84
|
var oMatchedCustomData = oGroup.getCustomData().find(function(oData) {
|
|
68
85
|
return oData.getKey() === "id" && oData.getValue() === sId;
|
|
69
86
|
});
|
|
@@ -71,7 +88,7 @@ sap.ui.define([
|
|
|
71
88
|
oTargetGroup = oGroup;
|
|
72
89
|
}
|
|
73
90
|
});
|
|
74
|
-
}
|
|
91
|
+
});
|
|
75
92
|
});
|
|
76
93
|
});
|
|
77
94
|
});
|
|
@@ -85,7 +102,7 @@ sap.ui.define([
|
|
|
85
102
|
oTargetGroup.destroyGroupElements();
|
|
86
103
|
|
|
87
104
|
aFields.forEach(function(sKey) {
|
|
88
|
-
var bIsEditable = mUpdatableLookupMap
|
|
105
|
+
var bIsEditable = Object.prototype.hasOwnProperty.call(mUpdatableLookupMap || {}, sKey);
|
|
89
106
|
|
|
90
107
|
var oGroupElement = new GroupElement({
|
|
91
108
|
useHorizontalLayout: false
|
|
@@ -114,6 +131,56 @@ sap.ui.define([
|
|
|
114
131
|
}
|
|
115
132
|
});
|
|
116
133
|
|
|
134
|
+
if (!bIsEditable) {
|
|
135
|
+
// Keep read-only behavior, but render like an input field (v4-like visual parity)
|
|
136
|
+
oSmartField.addStyleClass("sapUiEasyFillReadOnlyInputLike sapMInputBaseContentWrapper");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Notify on user edits so SmartForm validity can re-gate the Confirm button.
|
|
140
|
+
if (bIsEditable && typeof fnOnFieldChange === "function") {
|
|
141
|
+
oSmartField.attachChange(fnOnFieldChange);
|
|
142
|
+
|
|
143
|
+
// Validate AI-seeded values on initial load — mirrors ObjectPage behaviour.
|
|
144
|
+
//
|
|
145
|
+
// In the OP, validation triggers because when a user TYPES a value the browser
|
|
146
|
+
// fires a change event which causes UI5 to call setExternalValue() on the inner
|
|
147
|
+
// control's binding. setExternalValue() has no fast-path: it always calls
|
|
148
|
+
// parseValue → validateValue → OData value-list lookup. If the lookup fails the
|
|
149
|
+
// binding calls setInvalidValue() + checkDataState() → ManagedObject fires
|
|
150
|
+
// validationError → inner control gets valueState="Error" (red mark).
|
|
151
|
+
//
|
|
152
|
+
// SmartForm.check() / SmartField.checkValuesValidity() do NOT work for
|
|
153
|
+
// AI-seeded values because SmartField._checkErrors() contains a fast-path:
|
|
154
|
+
// if (!isControlDirty() && formattedModelValue === displayedValue) → skip
|
|
155
|
+
// AI values always satisfy this (model = display = AI value, user never typed),
|
|
156
|
+
// so _checkErrors() short-circuits to "valid" without the OData lookup.
|
|
157
|
+
//
|
|
158
|
+
// Fix: after innerControlsCreated, call setExternalValue() on the inner
|
|
159
|
+
// control's own binding with the currently displayed value. This is exactly
|
|
160
|
+
// the same path the OP uses when the user types. For async types (TextArrangement
|
|
161
|
+
// with ValueList) the call starts the OData lookup:
|
|
162
|
+
// valid value → resolves quietly, no red mark
|
|
163
|
+
// invalid value → rejects → setInvalidValue + checkDataState → red mark
|
|
164
|
+
// Non-async types fall through to a plain fnOnFieldChange call because
|
|
165
|
+
// SmartForm.check() already handles them correctly (no fast-path for sync).
|
|
166
|
+
oSmartField.attachInnerControlsCreated(function() {
|
|
167
|
+
var oInnerControl = oSmartField.getFirstInnerControl();
|
|
168
|
+
if (oInnerControl && typeof oInnerControl.getValue === "function") {
|
|
169
|
+
var oBinding = oInnerControl.getBinding("value");
|
|
170
|
+
var oType = oBinding && typeof oBinding.getType === "function" && oBinding.getType();
|
|
171
|
+
if (oType && oType.async && typeof oBinding.setExternalValue === "function") {
|
|
172
|
+
// setExternalValue replicates the "user typed this value" path:
|
|
173
|
+
// parseValue → validateValue → OData lookup → validationError/Success
|
|
174
|
+
Promise.resolve(oBinding.setExternalValue(oInnerControl.getValue()))
|
|
175
|
+
.catch(Function.prototype)
|
|
176
|
+
.finally(fnOnFieldChange);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
fnOnFieldChange();
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
117
184
|
/**
|
|
118
185
|
* "Previously:" SmartField (read-only):
|
|
119
186
|
* - Only shown for editable fields to display the current OP value for comparison
|
|
@@ -125,10 +192,9 @@ sap.ui.define([
|
|
|
125
192
|
enabled: false,
|
|
126
193
|
contextEditable: false,
|
|
127
194
|
showLabel: false,
|
|
128
|
-
visible: bIsEditable,
|
|
129
195
|
layoutData: new ColumnElementData({
|
|
130
196
|
cellsSmall: 9,
|
|
131
|
-
cellsLarge:
|
|
197
|
+
cellsLarge: 9
|
|
132
198
|
}),
|
|
133
199
|
editableChanged: function(oEvent) {
|
|
134
200
|
// Always keep the "previously" field read-only
|
|
@@ -138,33 +204,42 @@ sap.ui.define([
|
|
|
138
204
|
}
|
|
139
205
|
});
|
|
140
206
|
|
|
141
|
-
if (
|
|
142
|
-
//
|
|
143
|
-
//
|
|
207
|
+
if (oTransientContextForEasyFill) {
|
|
208
|
+
// Primary field always uses transient context to show AI-proposed value.
|
|
209
|
+
// Field editability is controlled by bIsEditable.
|
|
144
210
|
oSmartField.setBindingContext(oTransientContextForEasyFill);
|
|
145
|
-
oSmartFieldPrev.setBindingContext(oObjectPageContext);
|
|
146
211
|
|
|
147
|
-
//
|
|
148
|
-
|
|
212
|
+
// Only seed the transient context with the AI-suggested value on the first
|
|
213
|
+
// population. On subsequent calls (e.g. mode switch), the transient context
|
|
214
|
+
// already holds the current value — overwriting it would discard user edits.
|
|
215
|
+
if (oTransientContextForEasyFill.getProperty(sKey) === undefined) {
|
|
216
|
+
oTransientContextForEasyFill.setProperty(sKey, oLLMData[sKey]);
|
|
217
|
+
}
|
|
149
218
|
} else {
|
|
150
|
-
//
|
|
219
|
+
// Fallback: without transient context, bind to original OP context.
|
|
151
220
|
oSmartField.setBindingContext(oObjectPageContext);
|
|
152
221
|
}
|
|
153
222
|
|
|
223
|
+
// Previous value is always shown from the original ObjectPage context.
|
|
224
|
+
oSmartFieldPrev.setBindingContext(oObjectPageContext);
|
|
225
|
+
|
|
154
226
|
// Build the GroupElement: [label+primary SmartField] [Previously: label] [preview SmartField]
|
|
155
227
|
oGroupElement.addElement(oSmartField); // SmartField provides the label
|
|
156
228
|
oGroupElement.addElement(new Text({
|
|
157
|
-
text:
|
|
158
|
-
visible: bIsEditable,
|
|
229
|
+
text: sPreviouslyLabel,
|
|
159
230
|
layoutData: new ColumnElementData({
|
|
160
231
|
cellsSmall: 3,
|
|
161
|
-
cellsLarge:
|
|
232
|
+
cellsLarge: 3
|
|
162
233
|
})
|
|
163
|
-
}));
|
|
234
|
+
}).addStyleClass("sapEasyFillPreviouslyLabel"));
|
|
164
235
|
oGroupElement.addElement(oSmartFieldPrev);
|
|
165
236
|
oTargetGroup.addGroupElement(oGroupElement);
|
|
166
237
|
});
|
|
167
238
|
|
|
239
|
+
// Trigger initial SmartForm validation now that all SmartFields are rendered
|
|
240
|
+
if (typeof fnOnFieldChange === "function") {
|
|
241
|
+
fnOnFieldChange();
|
|
242
|
+
}
|
|
168
243
|
oLogger.debug("populateSmartFields: populated " + aFields.length + " fields for group '" + sId + "'");
|
|
169
244
|
}, 0);
|
|
170
245
|
|