@sapui5/sap.suite.ui.generic.template 1.151.1 → 1.153.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/package.json +1 -1
  2. package/src/sap/suite/ui/generic/template/.library +1 -1
  3. package/src/sap/suite/ui/generic/template/AnalyticalListPage/control/visualfilterbar/SmartVisualFilterBar.js +4 -3
  4. package/src/sap/suite/ui/generic/template/AnalyticalListPage/manifest.json +1 -1
  5. package/src/sap/suite/ui/generic/template/Canvas/manifest.json +1 -1
  6. package/src/sap/suite/ui/generic/template/ListReport/Component.js +4 -2
  7. package/src/sap/suite/ui/generic/template/ListReport/controller/ControllerImplementation.js +0 -6
  8. package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_sv.properties +1 -1
  9. package/src/sap/suite/ui/generic/template/ListReport/manifest.json +1 -1
  10. package/src/sap/suite/ui/generic/template/ListReport/view/fragments/FullscreenPage.fragment.xml +3 -0
  11. package/src/sap/suite/ui/generic/template/ObjectPage/Component.js +5 -0
  12. package/src/sap/suite/ui/generic/template/ObjectPage/controller/ControllerImplementation.js +31 -0
  13. package/src/sap/suite/ui/generic/template/ObjectPage/manifest.json +1 -1
  14. package/src/sap/suite/ui/generic/template/ObjectPage/view/fragments/Actions.fragment.xml +1 -1
  15. package/src/sap/suite/ui/generic/template/QuickCreate/manifest.json +1 -1
  16. package/src/sap/suite/ui/generic/template/QuickView/manifest.json +1 -1
  17. package/src/sap/suite/ui/generic/template/genericUtilities/controlStateWrapperFactory/SmartFilterBarWrapper.js +13 -7
  18. package/src/sap/suite/ui/generic/template/genericUtilities/designtime/messagebundle.properties +0 -0
  19. package/src/sap/suite/ui/generic/template/js/AnnotationHelper.js +21 -17
  20. package/src/sap/suite/ui/generic/template/lib/AppComponent.js +19 -3
  21. package/src/sap/suite/ui/generic/template/lib/ShareUtils.js +1 -1
  22. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillAIOrchestrator.js +16 -6
  23. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillApplyHandler.js +35 -47
  24. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController.js +80 -64
  25. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector.js +442 -259
  26. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer.js +186 -161
  27. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillHandler.js +137 -161
  28. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer.js +157 -191
  29. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillDialog.fragment.xml +67 -85
  30. package/src/sap/suite/ui/generic/template/lib/ai/EasyFilterBarHandler.js +56 -4
  31. package/src/sap/suite/ui/generic/template/lib/i18n/i18n.properties +11 -2
  32. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ar.properties +7 -1
  33. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_bg.properties +7 -1
  34. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ca.properties +7 -1
  35. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cnr.properties +7 -1
  36. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cs.properties +7 -1
  37. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cy.properties +7 -1
  38. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_da.properties +7 -1
  39. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_de.properties +7 -1
  40. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_el.properties +7 -1
  41. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en.properties +7 -1
  42. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_GB.properties +7 -1
  43. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_sappsd.properties +7 -1
  44. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saprigi.properties +7 -1
  45. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saptrc.properties +13 -7
  46. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es.properties +7 -1
  47. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es_MX.properties +7 -1
  48. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_et.properties +7 -1
  49. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fi.properties +11 -5
  50. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr.properties +10 -4
  51. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr_CA.properties +7 -1
  52. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hi.properties +7 -1
  53. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hr.properties +7 -1
  54. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hu.properties +7 -1
  55. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_id.properties +7 -1
  56. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_it.properties +7 -1
  57. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_iw.properties +7 -1
  58. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ja.properties +7 -1
  59. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_kk.properties +7 -1
  60. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ko.properties +7 -1
  61. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lt.properties +7 -1
  62. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lv.properties +7 -1
  63. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_mk.properties +7 -1
  64. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ms.properties +7 -1
  65. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_nl.properties +7 -1
  66. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_no.properties +7 -1
  67. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pl.properties +7 -1
  68. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt.properties +7 -1
  69. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt_PT.properties +7 -1
  70. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ro.properties +7 -1
  71. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ru.properties +7 -1
  72. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sh.properties +7 -1
  73. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sk.properties +7 -1
  74. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sl.properties +8 -2
  75. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sr.properties +7 -1
  76. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sv.properties +7 -1
  77. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_th.properties +7 -1
  78. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_tr.properties +7 -1
  79. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_uk.properties +7 -1
  80. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_vi.properties +7 -1
  81. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_CN.properties +7 -1
  82. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_TW.properties +7 -1
  83. package/src/sap/suite/ui/generic/template/lib/insights/InsightsCardHelper.js +40 -8
  84. package/src/sap/suite/ui/generic/template/lib/navigation/NavigationController.js +3 -2
  85. package/src/sap/suite/ui/generic/template/lib/presentationControl/SmartTableHandler.js +4 -0
  86. package/src/sap/suite/ui/generic/template/library.js +1 -1
  87. package/src/sap/suite/ui/generic/template/themes/base/ObjectPage.less +7 -1
@@ -25,6 +25,58 @@ sap.ui.define([
25
25
 
26
26
  // ─── Field Mapping for LLM ───────────────────────────────────────────────
27
27
 
28
+ /**
29
+ * Builds a live editability map by scanning all SmartFields on the ObjectPage
30
+ * and checking their actual runtime editable state.
31
+ *
32
+ * This is the authoritative source for whether a field is editable — it reflects
33
+ * hardcoded editable="false" in extension fragments, dynamic field-control values,
34
+ * key fields, and any other UI-level restriction, without needing to re-implement
35
+ * each rule from metadata. Mirrors the FE V4 approach (EasyFillButton._getEditableFields).
36
+ *
37
+ * If the same property appears in multiple SmartFields, it is treated as editable
38
+ * if at least one instance is editable (OR logic — consistent with V4).
39
+ *
40
+ * @param {object} oObjectPage - the ObjectPage control
41
+ * @returns {object} map of { propertyName: boolean } — true means editable in live UI
42
+ */
43
+ buildLiveEditabilityMap: function(oObjectPage) {
44
+ // This function is kept for backward compatibility but the full
45
+ // rendered walk is now done inside buildObjectPageStructureMap which
46
+ // returns both sections/fieldMap AND liveEditable in one pass.
47
+ var mLiveEditable = Object.create(null);
48
+ if (!oObjectPage || !oObjectPage.findAggregatedObjects) {
49
+ return mLiveEditable;
50
+ }
51
+ oObjectPage.findAggregatedObjects(true, function(oControl) {
52
+ if (!oControl.isA("sap.ui.comp.smartfield.SmartField")) {
53
+ return false;
54
+ }
55
+ var oBindingInfo = oControl.getBindingInfo("value");
56
+ var sPropertyName = oBindingInfo && oBindingInfo.path;
57
+ if (!sPropertyName || sPropertyName.indexOf("/") !== -1) {
58
+ return false;
59
+ }
60
+ var bEditable = oControl.getEditable();
61
+ if (mLiveEditable[sPropertyName] === undefined) {
62
+ mLiveEditable[sPropertyName] = bEditable;
63
+ } else {
64
+ // Hardcoded editable="false" (no binding) is non-negotiable — overrides everything.
65
+ // OR logic only applies when the existing stored value is not already a settled hardcoded false,
66
+ // preventing a later dynamic-true instance from overwriting it.
67
+ var oEditableBinding = oControl.getBindingInfo("editable");
68
+ if (!bEditable && !oEditableBinding) {
69
+ mLiveEditable[sPropertyName] = false;
70
+ } else if (mLiveEditable[sPropertyName] !== false) {
71
+ mLiveEditable[sPropertyName] = mLiveEditable[sPropertyName] || bEditable;
72
+ }
73
+ }
74
+ return false;
75
+ });
76
+ oLogger.debug("buildLiveEditabilityMap: scanned " + Object.keys(mLiveEditable).length + " SmartField(s)");
77
+ return mLiveEditable;
78
+ },
79
+
28
80
  /**
29
81
  * Builds the flat property map for the main entity type, used as the LLM input schema.
30
82
  * Each entry contains the field label and OData data type.
@@ -175,16 +227,26 @@ sap.ui.define([
175
227
  // ─── Updatable / Non-Updatable Classification ────────────────────────────
176
228
 
177
229
  /**
178
- * Splits LLM-returned simple fields into updatable and non-updatable buckets
179
- * based on OData metadata annotations (sap:updatable, FieldControl, etc.).
230
+ * Splits LLM-returned simple fields into updatable and non-updatable buckets.
180
231
  *
181
- * @param {object} oAiCallResult - flat map of { fieldName: aiValue } from the LLM
182
- * @param {object} oEntityType - OData entity type metadata
232
+ * Uses mLiveEditable (scanned from live SmartFields at dialog open) as the primary
233
+ * source when available — it captures all runtime restrictions: hardcoded editable="false"
234
+ * in extension fragments, key fields, dynamic field-control values, etc.
235
+ * Falls back to OData metadata annotation checks for fields not present in the live map.
236
+ *
237
+ * Decision per field:
238
+ * 1. mLiveEditable[field] === false → non-updatable (UI definitively non-editable)
239
+ * 2. mLiveEditable[field] === true → updatable (UI definitively editable)
240
+ * 3. mLiveEditable[field] === undefined → fall back to metadata annotation check
241
+ *
242
+ * @param {object} oAiCallResult - flat map of { fieldName: aiValue } from the LLM
243
+ * @param {object} oEntityType - OData entity type metadata
183
244
  * @param {object} oObjectPageContext - current ObjectPage binding context
184
- * @param {object} oEntitySet - OData entity set metadata (for entity-set-level sap:updatable checks)
245
+ * @param {object} oEntitySet - OData entity set metadata
246
+ * @param {object} [mLiveEditable] - live editability map from buildLiveEditabilityMap
185
247
  * @returns {{ updatableFields: object, nonUpdatableFields: object }}
186
248
  */
187
- getUpdatableAndNonUpdatableFields: function(oAiCallResult, oEntityType, oObjectPageContext, oEntitySet) {
249
+ getUpdatableAndNonUpdatableFields: function(oAiCallResult, oEntityType, oObjectPageContext, oEntitySet, mLiveEditable) {
188
250
  var updatableFields = {};
189
251
  var nonUpdatableFields = {};
190
252
 
@@ -194,9 +256,22 @@ sap.ui.define([
194
256
  }
195
257
 
196
258
  Object.keys(oAiCallResult).forEach(function(sKey) {
259
+ // Live map is the primary source when available
260
+ if (mLiveEditable) {
261
+ if (mLiveEditable[sKey] === false) {
262
+ nonUpdatableFields[sKey] = oAiCallResult[sKey];
263
+ return;
264
+ }
265
+ if (mLiveEditable[sKey] === true) {
266
+ updatableFields[sKey] = oAiCallResult[sKey];
267
+ return;
268
+ }
269
+ // undefined → field not present in live UI (e.g. table-only property)
270
+ // fall through to metadata check
271
+ }
272
+
197
273
  var oProperty = oEntityType.property.find(function(oP) { return oP.name === sKey; });
198
274
  if (!oProperty) {
199
- // Property not found in metadata → treat as non-updatable
200
275
  nonUpdatableFields[sKey] = oAiCallResult[sKey];
201
276
  return;
202
277
  }
@@ -234,7 +309,8 @@ sap.ui.define([
234
309
  return oHiddenAnnotation["Bool"] !== "true";
235
310
  }
236
311
  if (oHiddenAnnotation["Path"] && oObjectPageContext) {
237
- return !oObjectPageContext.getObject()[oHiddenAnnotation["Path"]];
312
+ var oContextObj = oObjectPageContext.getObject();
313
+ return oContextObj ? !oContextObj[oHiddenAnnotation["Path"]] : false;
238
314
  }
239
315
  // <Annotation Term="UI.Hidden"/> with no value → hidden
240
316
  return false;
@@ -243,7 +319,8 @@ sap.ui.define([
243
319
  // Check "sap:field-control" attribute (value 0 = hidden)
244
320
  var sFieldControl = oProperty["sap:field-control"];
245
321
  if (sFieldControl && oObjectPageContext) {
246
- var iFieldControlValue = oObjectPageContext.getObject()[sFieldControl];
322
+ var oCtxObj = oObjectPageContext.getObject();
323
+ var iFieldControlValue = oCtxObj ? oCtxObj[sFieldControl] : undefined;
247
324
  return iFieldControlValue !== 0;
248
325
  }
249
326
 
@@ -306,122 +383,338 @@ sap.ui.define([
306
383
  return sUpdatable !== "false";
307
384
  }
308
385
 
309
- // Check "com.sap.vocabularies.Common.v1.FieldControl" enum member
386
+ // Check "com.sap.vocabularies.Common.v1.FieldControl" — static enum or dynamic path
387
+ // FieldControl values: 0=Hidden, 1=ReadOnly, 3=Optional, 7=Mandatory
310
388
  var oAnnotation = oProperty["com.sap.vocabularies.Common.v1.FieldControl"];
311
389
  if (oAnnotation && oAnnotation["EnumMember"]) {
312
390
  return oAnnotation["EnumMember"] !== "com.sap.vocabularies.Common.v1.FieldControlType/ReadOnly";
313
391
  }
392
+ if (oAnnotation && oAnnotation["Path"]) {
393
+ var iDynamicFieldControl = oContextObject && oContextObject[oAnnotation["Path"]];
394
+ if (iDynamicFieldControl !== undefined && iDynamicFieldControl !== null) {
395
+ return iDynamicFieldControl !== 0 && iDynamicFieldControl !== 1;
396
+ }
397
+ }
314
398
 
315
- // Check "sap:field-control" dynamic path (value 1 = read-only)
399
+ // Check "sap:field-control" dynamic path (values: 0=Hidden, 1=ReadOnly)
400
+ // If the _fc property is defined in metadata but absent from the context object
401
+ // (not fetched / not in $select), treat as non-editable — safe fallback.
316
402
  var sFieldControl = oProperty["sap:field-control"];
317
403
  if (sFieldControl) {
318
- var iFieldControlValue = oContextObject && oContextObject[sFieldControl];
319
- return iFieldControlValue !== 1;
404
+ if (!oContextObject || !(sFieldControl in oContextObject)) {
405
+ return false;
406
+ }
407
+ var iFieldControlValue = oContextObject[sFieldControl];
408
+ return iFieldControlValue !== 0 && iFieldControlValue !== 1;
320
409
  }
321
410
 
322
411
  // No restriction → editable by default
323
412
  return true;
324
413
  },
325
414
 
326
- // ─── Section Tree Building (from ObjectPageSectionHandler) ───────────────
415
+ // ─── ObjectPage Structure Map & Section Building ─────────────────────────
327
416
 
328
417
  /**
329
- * Builds a flat section tree for the Condensed view mode.
330
- * Creates two sections: "Filled Fields" (updatable) and "Unfilled Fields" (non-updatable).
418
+ * Walks the RENDERED ObjectPage using findAggregatedObjects — the same proven
419
+ * approach used by buildLiveEditabilityMap — and produces the section/group/field
420
+ * structure map AND the live editability map in a SINGLE pass.
421
+ *
422
+ * IMPORTANT: Must be called when the page is in EDIT MODE with the OData entity
423
+ * context fully resolved (i.e. inside _handleAISubmit, not at dialog open).
424
+ * At that point getBindingInfo("value").path is reliably populated for all SmartFields.
425
+ *
426
+ * This approach is correct because:
427
+ * ✓ Extension fragment SmartFields are included automatically
428
+ * ✓ Editability reflects actual UI state (editable="false" in extension = non-editable)
429
+ * ✓ Section/group titles come from the rendered page — same as what the user sees
430
+ * ✓ Hidden sections are naturally excluded by checking the parent Section's visible state
431
+ * ✓ One walk replaces two separate scans
331
432
  *
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
335
- * @returns {Array} section tree array
433
+ * For each SmartField found, we walk UP the parent chain to get section/group context:
434
+ * SmartField → GroupElement → SmartForm.Group → SmartForm → block → SubSection → Section
435
+ *
436
+ * Falls back to annotation-based structure if the rendered walk finds no fields
437
+ * (edge case: entity context not yet resolved).
438
+ *
439
+ * @param {object} oObjectPage - the REAL rendered FLP ObjectPage (not EasyFill dialog OPL)
440
+ * @param {object} [oEntityTypeFallback] - OData entity type for annotation fallback
441
+ * @param {object} [oRbFallback] - i18n resource bundle for annotation fallback
442
+ * @returns {{ sections: Array, fieldMap: Map, liveEditable: object }}
336
443
  */
337
- getSectionsForCondensedView: function(oLLMResult, oUpdatableFields, oNonUpdatableFields) {
338
- var aUpdatableKeys = Object.keys(oUpdatableFields || {});
339
- var aNonUpdatableKeys = Object.keys(oNonUpdatableFields || {});
444
+ buildObjectPageStructureMap: function(oObjectPage, oEntityTypeFallback, oRbFallback) {
445
+ if (!oObjectPage || !oObjectPage.findAggregatedObjects) {
446
+ return { sections: [], fieldMap: new Map(), liveEditable: Object.create(null) };
447
+ }
448
+
449
+ var aSections = [];
450
+ var mFieldMap = new Map();
451
+ var mLiveEditable = Object.create(null);
452
+ var mSectionByKey = {};
453
+ var mGroupByKey = {};
454
+ var that = this;
455
+
456
+ oObjectPage.findAggregatedObjects(true, function(oControl) {
457
+ if (!oControl.isA("sap.ui.comp.smartfield.SmartField")) { return false; }
458
+
459
+ // Get property name from value binding
460
+ var oBindingInfo = oControl.getBindingInfo("value");
461
+ var sPropertyName = oBindingInfo && oBindingInfo.path;
462
+ if (!sPropertyName || sPropertyName.indexOf("/") !== -1) { return false; }
463
+
464
+ // Walk UP parent chain to get section + group context
465
+ var sSectionTitle = "";
466
+ var sGroupTitle = "";
467
+ var oParent = oControl.getParent();
468
+ while (oParent) {
469
+ if (oParent.isA && oParent.isA("sap.ui.comp.smartform.Group")) {
470
+ var oTitleObj = oParent.getTitle && oParent.getTitle();
471
+ sGroupTitle = (oTitleObj && typeof oTitleObj.getText === "function")
472
+ ? (oTitleObj.getText() || "") : "";
473
+ }
474
+ if (oParent.isA && oParent.isA("sap.uxap.ObjectPageSection")) {
475
+ // Skip hidden sections
476
+ if (oParent.getVisible && !oParent.getVisible()) { return false; }
477
+ sSectionTitle = oParent.getTitle() || "";
478
+ break;
479
+ }
480
+ oParent = oParent.getParent && oParent.getParent();
481
+ }
482
+
483
+ // Skip fields not in any visible section (e.g. header display fields)
484
+ if (!sSectionTitle) { return false; }
485
+ if (!sGroupTitle) { sGroupTitle = sSectionTitle; }
486
+
487
+ var sGroupKey = sSectionTitle + "|" + sGroupTitle;
488
+
489
+ // Ensure section + group exist in our structures
490
+ if (!Object.prototype.hasOwnProperty.call(mSectionByKey, sSectionTitle)) {
491
+ mSectionByKey[sSectionTitle] = aSections.length;
492
+ aSections.push({
493
+ id: that._generateId(sSectionTitle, []),
494
+ title: sSectionTitle,
495
+ groups: []
496
+ });
497
+ }
498
+ var iSectionIdx = mSectionByKey[sSectionTitle];
499
+ var oSectionEntry = aSections[iSectionIdx];
500
+
501
+ if (!Object.prototype.hasOwnProperty.call(mGroupByKey, sGroupKey)) {
502
+ var sGroupId = that._generateId(sGroupKey, []);
503
+ mGroupByKey[sGroupKey] = { sectionId: oSectionEntry.id, groupId: sGroupId };
504
+ oSectionEntry.groups.push({ id: sGroupId, title: sGroupTitle });
505
+ }
506
+
507
+ // Register in fieldMap (first occurrence wins)
508
+ if (!mFieldMap.has(sPropertyName)) {
509
+ mFieldMap.set(sPropertyName, {
510
+ sectionId: mGroupByKey[sGroupKey].sectionId,
511
+ groupId: mGroupByKey[sGroupKey].groupId
512
+ });
513
+ }
514
+
515
+ // Live editability — hardcoded false overrides dynamic instances regardless of order
516
+ var bEditable = oControl.getEditable();
517
+ var oEditableBinding = oControl.getBindingInfo("editable");
518
+ var bIsHardcodedFalse = !bEditable && !oEditableBinding;
519
+
520
+ if (mLiveEditable[sPropertyName] === undefined) {
521
+ mLiveEditable[sPropertyName] = bEditable;
522
+ } else if (bIsHardcodedFalse) {
523
+ // Extension fragment has explicit editable="false" — non-negotiable
524
+ mLiveEditable[sPropertyName] = false;
525
+ } else if (mLiveEditable[sPropertyName] !== false) {
526
+ // Only OR-in the new value if a hardcoded false hasn't already settled this field
527
+ mLiveEditable[sPropertyName] = mLiveEditable[sPropertyName] || bEditable;
528
+ }
529
+
530
+ return false;
531
+ });
532
+
533
+ oLogger.debug("buildObjectPageStructureMap: rendered walk found "
534
+ + mFieldMap.size + " fields in " + aSections.length + " sections");
535
+
536
+ // Fallback: if rendered walk found nothing (entity context not yet resolved),
537
+ // fall back to annotation walk so at least section structure is available
538
+ if (mFieldMap.size === 0 && oEntityTypeFallback) {
539
+ oLogger.debug("buildObjectPageStructureMap: falling back to annotation walk");
540
+ var oAnnotationResult = this._buildFromAnnotations(oEntityTypeFallback, oRbFallback);
541
+ return {
542
+ sections: oAnnotationResult.sections,
543
+ fieldMap: oAnnotationResult.fieldMap,
544
+ liveEditable: this.buildLiveEditabilityMap(oObjectPage)
545
+ };
546
+ }
547
+
548
+ return { sections: aSections, fieldMap: mFieldMap, liveEditable: mLiveEditable };
549
+ },
550
+
551
+ /**
552
+ * Annotation-based fallback for buildObjectPageStructureMap.
553
+ * Used when the rendered walk finds no fields (entity context not yet resolved).
554
+ * @private
555
+ */
556
+ _buildFromAnnotations: function(oEntityType, oRb) {
557
+ var aRaw = this._buildRawAnnotationTree(oEntityType, oRb);
340
558
  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
- }]
559
+ var mSectionByKey = {};
560
+ var mGroupByKey = {};
561
+ var mFieldMap = new Map();
562
+
563
+ aRaw.forEach(function(oEntry) {
564
+ var aTitle = oEntry.title;
565
+ var sField = oEntry.property;
566
+ var sSectionTitle = aTitle[0] || "";
567
+ var sGroupTitle = aTitle.length > 1 ? aTitle[aTitle.length - 1] : "";
568
+ var sGroupKey = sSectionTitle + "|" + sGroupTitle;
569
+
570
+ if (!Object.prototype.hasOwnProperty.call(mSectionByKey, sSectionTitle)) {
571
+ mSectionByKey[sSectionTitle] = aSections.length;
572
+ aSections.push({ id: this._generateId(sSectionTitle, []), title: sSectionTitle, groups: [] });
573
+ }
574
+ var oSection = aSections[mSectionByKey[sSectionTitle]];
575
+
576
+ if (!Object.prototype.hasOwnProperty.call(mGroupByKey, sGroupKey)) {
577
+ var sGroupId = this._generateId(sGroupKey, []);
578
+ mGroupByKey[sGroupKey] = { sectionId: oSection.id, groupId: sGroupId };
579
+ oSection.groups.push({ id: sGroupId, title: sGroupTitle });
580
+ }
581
+
582
+ if (!mFieldMap.has(sField)) {
583
+ mFieldMap.set(sField, {
584
+ sectionId: mGroupByKey[sGroupKey].sectionId,
585
+ groupId: mGroupByKey[sGroupKey].groupId
586
+ });
587
+ }
588
+ }.bind(this));
589
+
590
+ return { sections: aSections, fieldMap: mFieldMap };
591
+ },
592
+
593
+ /**
594
+ * Applies LLM results to the pre-built structure map and returns a sections array
595
+ * ready to be written to easyFillDialogModel>/sections.
596
+ *
597
+ * Each section node shape: { type, title, id, visible, fields, children }
598
+ * Each child group shape: { id, title, visible, aActiveFields, fields }
599
+ *
600
+ * O(fields returned by LLM) — no annotation walk, no DOM traversal.
601
+ *
602
+ * @param {Map} mFieldMap - from buildObjectPageStructureMap
603
+ * @param {Array} aOpSections - sections from buildObjectPageStructureMap
604
+ * @param {object} oUpdatableFields - { fieldName: value }
605
+ * @param {object} oNonUpdatableFields - { fieldName: value }
606
+ * @returns {Array} sections array for easyFillDialogModel>/sections
607
+ */
608
+ applyLLMResultToStructureMap: function(mFieldMap, aOpSections, oUpdatableFields, oNonUpdatableFields) {
609
+ var mSectionById = {};
610
+ var mGroupById = {};
611
+ (aOpSections || []).forEach(function(oSec, iSec) {
612
+ mSectionById[oSec.id] = iSec;
613
+ (oSec.groups || []).forEach(function(oGrp, iGrp) {
614
+ mGroupById[oGrp.id] = { sectionIdx: iSec, groupIdx: iGrp };
351
615
  });
352
- }
616
+ });
617
+
618
+ // Start with all sections hidden, no active fields
619
+ var aSections = (aOpSections || []).map(function(oSec) {
620
+ return {
621
+ title: oSec.title,
622
+ id: oSec.id,
623
+ visible: false,
624
+ fields: [],
625
+ children: (oSec.groups || []).map(function(oGrp) {
626
+ return {
627
+ id: oGrp.id,
628
+ title: oGrp.title,
629
+ visible: false,
630
+ aActiveFields: [],
631
+ fields: []
632
+ };
633
+ })
634
+ };
635
+ });
636
+
637
+ // Flip sections/groups that have LLM-matched updatable fields
638
+ Object.keys(oUpdatableFields || {}).forEach(function(sField) {
639
+ if (!mFieldMap.has(sField)) { return; }
640
+ var oLoc = mFieldMap.get(sField);
641
+ var iSec = mSectionById[oLoc.sectionId];
642
+ var oGrpLoc = mGroupById[oLoc.groupId];
643
+ if (iSec === undefined || !oGrpLoc) { return; }
644
+ aSections[iSec].visible = true;
645
+ var oGrp = aSections[oGrpLoc.sectionIdx].children[oGrpLoc.groupIdx];
646
+ oGrp.visible = true;
647
+ if (oGrp.aActiveFields.indexOf(sField) === -1) {
648
+ oGrp.aActiveFields.push(sField);
649
+ }
650
+ });
353
651
 
354
- // Section 2: Unfilled Fields (non-updatable/read-only)
652
+ // Non-updatable fields → "Unfilled Fields" section appended at the end
653
+ var aNonUpdatableKeys = Object.keys(oNonUpdatableFields || {});
355
654
  if (aNonUpdatableKeys.length > 0) {
655
+ var sUnfilledGrpId = this._generateId("unfilledFields", []);
356
656
  aSections.push({
357
- title: "{@i18n>EASY_FILL_UNFILLED_FIELDS}",
358
- fields: [],
657
+ title: "{@i18n>EASY_FILL_UNFILLED_FIELDS}",
658
+ id: this._generateId("unfilledFieldsSection", []),
659
+ visible: true,
660
+ fields: [],
359
661
  children: [{
360
- title: "",
361
- fields: aNonUpdatableKeys,
362
- id: this._generateId("unfilledFields", aNonUpdatableKeys)
662
+ id: sUnfilledGrpId,
663
+ title: "",
664
+ visible: true,
665
+ aActiveFields: aNonUpdatableKeys,
666
+ fields: aNonUpdatableKeys
363
667
  }]
364
668
  });
365
669
  }
366
670
 
367
- // Fallback: if no split provided, use old behavior
368
- if (aSections.length === 0) {
369
- return this._transformToTreeExpanded(oLLMResult);
370
- }
371
-
372
671
  return aSections;
373
672
  },
374
673
 
375
674
  /**
376
- * Builds a structured section tree for the ObjectPage view mode.
377
- * Mirrors the ObjectPage section/facet structure and filters to only include
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.
675
+ * Builds a sections array for Condensed mode ready for easyFillDialogModel>/sections.
676
+ * Two flat groups: "Filled Fields" (updatable) and "Unfilled Fields" (non-updatable).
381
677
  *
382
- * @param {object} oEntityType - OData entity type metadata
383
- * @param {object} oLLMResult - flat map of { fieldName: value } from the LLM
384
- * @param {object} oRb - i18n resource bundle
385
- * @param {object} oNonUpdatableFields - flat map of non-updatable fields only (optional)
386
- * @returns {Array} filtered section tree array
678
+ * @param {object} oUpdatableFields - { fieldName: value }
679
+ * @param {object} oNonUpdatableFields - { fieldName: value }
680
+ * @returns {Array} sections array
387
681
  */
388
- getSectionsForObjectPageView: function(oEntityType, oLLMResult, oRb, oNonUpdatableFields) {
389
- var aSections = [];
390
- aSections = aSections
391
- .concat(this._getSectionsForHeader(oEntityType, oRb))
392
- .concat(this._getSectionsForObjectPageFacets(oEntityType));
393
- var aTree = this._transformToTree(aSections);
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.
682
+ getSectionInfoForCondensedMode: function(oUpdatableFields, oNonUpdatableFields) {
683
+ var aUpdatableKeys = Object.keys(oUpdatableFields || {});
411
684
  var aNonUpdatableKeys = Object.keys(oNonUpdatableFields || {});
685
+ var aSections = [];
686
+
687
+ if (aUpdatableKeys.length > 0) {
688
+ aSections.push({
689
+ title: "{@i18n>EASY_FILL_FILLED_FIELDS}",
690
+ visible: true,
691
+ fields: [],
692
+ children: [{
693
+ title: "",
694
+ visible: true,
695
+ aActiveFields: aUpdatableKeys,
696
+ fields: aUpdatableKeys,
697
+ id: this._generateId("filledFields", aUpdatableKeys)
698
+ }]
699
+ });
700
+ }
701
+
412
702
  if (aNonUpdatableKeys.length > 0) {
413
- aFilteredTree.push({
414
- title: "{@i18n>EASY_FILL_UNFILLED_FIELDS}",
415
- fields: [],
703
+ aSections.push({
704
+ title: "{@i18n>EASY_FILL_UNFILLED_FIELDS}",
705
+ visible: true,
706
+ fields: [],
416
707
  children: [{
417
- title: "",
418
- fields: aNonUpdatableKeys,
419
- id: this._generateId("unfilledFields", aNonUpdatableKeys)
708
+ title: "",
709
+ visible: true,
710
+ aActiveFields: aNonUpdatableKeys,
711
+ fields: aNonUpdatableKeys,
712
+ id: this._generateId("unfilledFields", aNonUpdatableKeys)
420
713
  }]
421
714
  });
422
715
  }
423
716
 
424
- return aFilteredTree;
717
+ return aSections;
425
718
  },
426
719
 
427
720
  // ─── SmartTable Utilities ─────────────────────────────────────────────────
@@ -513,102 +806,15 @@ sap.ui.define([
513
806
  // ─── Private Helpers ─────────────────────────────────────────────────────
514
807
 
515
808
  /**
516
- * Extracts the navigation property name from an OData binding path.
517
- * E.g. "/to_Items" → "to_Items", "/SalesOrderSet/to_Items" → "to_Items"
518
- *
519
- * @param {string} sBindingPath
520
- * @returns {string|null}
521
- */
522
- _extractNavigationPropertyName: function(sBindingPath) {
523
- if (!sBindingPath || typeof sBindingPath !== "string") {
524
- return null;
525
- }
526
- var aParts = sBindingPath.split("/").filter(Boolean);
527
- return aParts.length ? aParts[aParts.length - 1] : null;
528
- },
529
-
530
- /**
531
- * Builds the "condensed" tree: all LLM fields in a single flat group.
532
- * @private
533
- */
534
- _transformToTreeExpanded: function(oLLMRes) {
535
- return [
536
- {
537
- title: "",
538
- fields: [],
539
- children: [{
540
- title: "",
541
- fields: Object.keys(oLLMRes),
542
- id: this._generateId("", Object.keys(oLLMRes))
543
- }]
544
- }
545
- ];
546
- },
547
-
548
- /**
549
- * Builds the "ObjectPage" section tree from a flat array of { title[], property } entries.
550
- * Groups entries by their title path hierarchy.
551
- * @private
552
- */
553
- _transformToTree: function(data) {
554
- var rootMap = {};
555
- var seen = new Set();
556
- var that = this;
557
-
558
- data.forEach(function(entry) {
559
- var title = entry.title;
560
- var property = entry.property;
561
- if (seen.has(property)) { return; }
562
- seen.add(property);
563
-
564
- var currentMap = rootMap;
565
- var sTitlePath = "";
566
-
567
- title.forEach(function(level, index) {
568
- var isLast = index === title.length - 1;
569
- sTitlePath = sTitlePath ? (sTitlePath + "_" + level) : level;
570
-
571
- if (!currentMap[level]) {
572
- currentMap[level] = {
573
- title: level,
574
- titlePath: sTitlePath,
575
- fields: [],
576
- _childrenMap: {}
577
- };
578
- }
579
-
580
- if (isLast) {
581
- currentMap[level].fields.push(property);
582
- } else {
583
- currentMap = currentMap[level]._childrenMap;
584
- }
585
- });
586
- });
587
-
588
- function convertToArray(oMap) {
589
- return Object.values(oMap).map(function(oNode) {
590
- return {
591
- title: oNode.title,
592
- id: that._generateId(oNode.titlePath, oNode.fields),
593
- fields: oNode.fields,
594
- children: oNode._childrenMap ? convertToArray(oNode._childrenMap) : []
595
- };
596
- });
597
- }
598
-
599
- return convertToArray(rootMap);
600
- },
601
-
602
- /**
603
- * Generates a stable DOM-safe ID for a SmartForm section based on its title path and fields.
809
+ * Generates a stable DOM-safe ID string from a title path and optional field list.
604
810
  * @private
605
811
  */
606
812
  _generateId: function(sTitlePath, aFields) {
607
- var sSanitizedTitle = sTitlePath
813
+ var sSanitizedTitle = (sTitlePath || "")
608
814
  .replace(/\s+/g, "_")
609
815
  .replace(/[^a-zA-Z0-9_]/g, "")
610
816
  .toLowerCase();
611
- var sFieldsPart = aFields
817
+ var sFieldsPart = (aFields || [])
612
818
  .join("_")
613
819
  .replace(/[^a-zA-Z0-9_]/g, "")
614
820
  .toLowerCase();
@@ -616,90 +822,29 @@ sap.ui.define([
616
822
  },
617
823
 
618
824
  /**
619
- * Filters a section tree to only include nodes that contain at least one LLM-returned field.
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
825
+ * Extracts the navigation property name from an OData binding path.
826
+ * E.g. "/to_Items" → "to_Items", "/SalesOrderSet/to_Items" → "to_Items"
626
827
  * @private
627
828
  */
628
- _filterTreeByLLMResult: function(aTree, oLLMResult) {
629
- var aRequestedKeys = Object.keys(oLLMResult);
630
- var that = this;
631
-
632
- function filterNode(oNode, sParentPath) {
633
- var sTitlePath = sParentPath ? (sParentPath + "_" + oNode.title) : oNode.title;
634
-
635
- var aFilteredFields = oNode.fields.filter(function(sField) {
636
- return aRequestedKeys.includes(sField);
637
- });
638
-
639
- var aFilteredChildren = oNode.children
640
- .map(function(oChild) { return filterNode(oChild, sTitlePath); })
641
- .filter(function(oChild) { return oChild.fields.length > 0 || oChild.children.length > 0; });
642
-
643
- return {
644
- title: oNode.title,
645
- id: that._generateId(sTitlePath, aFilteredFields),
646
- fields: aFilteredFields,
647
- children: aFilteredChildren
648
- };
829
+ _extractNavigationPropertyName: function(sBindingPath) {
830
+ if (!sBindingPath || typeof sBindingPath !== "string") {
831
+ return null;
649
832
  }
650
-
651
- return aTree
652
- .map(function(oNode) { return filterNode(oNode, ""); })
653
- .filter(function(oNode) { return oNode.fields.length > 0 || oNode.children.length > 0; });
833
+ var aParts = sBindingPath.split("/").filter(Boolean);
834
+ return aParts.length ? aParts[aParts.length - 1] : null;
654
835
  },
655
836
 
656
837
  /**
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
838
+ * Walks all ObjectPage annotations (HeaderInfo + HeaderFacets + UI.Facets) and returns
839
+ * a flat array of { title[], property } entries. This is the single source of truth for
840
+ * the raw annotation structure and is shared by both the map builder and the un-cached path.
665
841
  * @private
666
842
  */
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;
690
- }
691
-
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
- });
843
+ _buildRawAnnotationTree: function(oEntityType, oRb) {
844
+ return [].concat(
845
+ this._getSectionsForHeader(oEntityType, oRb),
846
+ this._getSectionsForObjectPageFacets(oEntityType)
847
+ );
703
848
  },
704
849
 
705
850
  /**
@@ -725,8 +870,12 @@ sap.ui.define([
725
870
 
726
871
  if (aHeaderFacets && aHeaderFacets.length > 0) {
727
872
  var aSections = this._extractSectionInfoFromReferenceFacet(oEntityType, aHeaderFacets);
873
+ var sHeaderTitle = oRb.getText("EASY_FILL_SECTION_HEADER");
728
874
  aSections.forEach(function(oSection) {
729
- oSection.title = [oRb.getText("EASY_FILL_SECTION_HEADER"), oRb.getText("EASY_FILL_SECTION_HEADER")];
875
+ // Prepend the "Header" section title while keeping the facet's own label as the
876
+ // second hierarchy level. Previously both levels were set to the same headerText,
877
+ // which collapsed all header facets into one group and lost their individual labels.
878
+ oSection.title = [sHeaderTitle].concat(oSection.title);
730
879
  });
731
880
  aRes.push.apply(aRes, aSections);
732
881
  }
@@ -772,14 +921,49 @@ sap.ui.define([
772
921
 
773
922
  /**
774
923
  * Extracts { title[], property } entries from a set of ReferenceFacets.
775
- * Skips IntentBasedNavigation, ForAction, and ForAnnotation record types (not supported).
924
+ * Handles UI.DataFieldForAnnotation by following its Target to the referenced FieldGroup —
925
+ * this is the primary pattern used in ObjectPage apps to embed FieldGroups inside UI.Facets.
926
+ * Skips IntentBasedNavigation, ForAction, and other non-renderable record types.
776
927
  * @private
777
928
  */
778
929
  _extractSectionInfoFromReferenceFacet: function(oEntityType, aFacets, aText) {
779
930
  aText = aText || [];
780
931
  var aRes = [];
781
932
  aFacets.forEach(function(oFacet) {
782
- // Skip unsupported annotation types
933
+ var sNormalized = oFacet.RecordType
934
+ ? oFacet.RecordType.replace("com.sap.vocabularies.UI.v1.", "UI.")
935
+ : null;
936
+
937
+ // DataFieldForAnnotation: the record itself is not a field — it is a pointer to a
938
+ // FieldGroup (or other annotation). Follow the Target.AnnotationPath to get its Data.
939
+ if (sNormalized === "UI.DataFieldForAnnotation") {
940
+ if (!oFacet.Target || !oFacet.Target.AnnotationPath) { return; }
941
+ var sInnerPath = oFacet.Target.AnnotationPath.substring(1);
942
+ var oInnerAnnotation = oEntityType[sInnerPath];
943
+ if (!oInnerAnnotation) { return; }
944
+
945
+ // Resolve the label: DataFieldForAnnotation.Label → FieldGroup.Label → fallback
946
+ var sInnerLabel = null;
947
+ if (oFacet.Label && oFacet.Label.String) {
948
+ sInnerLabel = oFacet.Label.String;
949
+ } else if (oInnerAnnotation.Label && oInnerAnnotation.Label.String) {
950
+ sInnerLabel = oInnerAnnotation.Label.String;
951
+ }
952
+
953
+ var aTempText = aText.slice();
954
+ if (sInnerLabel) { aTempText.push(sInnerLabel); }
955
+
956
+ if (Array.isArray(oInnerAnnotation.Data)) {
957
+ oInnerAnnotation.Data.forEach(function(oData) {
958
+ if (!this._isSupportedRecordType(oData.RecordType)) { return; }
959
+ if (!oData.Value || !oData.Value.Path) { return; }
960
+ aRes.push({ title: aTempText, property: oData.Value.Path });
961
+ }.bind(this));
962
+ }
963
+ return;
964
+ }
965
+
966
+ // Skip remaining unsupported types
783
967
  if (!this._isSupportedRecordType(oFacet.RecordType)) {
784
968
  return;
785
969
  }
@@ -829,17 +1013,16 @@ sap.ui.define([
829
1013
  },
830
1014
 
831
1015
  /**
832
- * Returns false for record types that EasyFill does not support rendering.
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.
1016
+ * Returns false for record types that represent navigation/action triggers rather than
1017
+ * editable data fields. DataFieldForAnnotation is NOT in this list — it is handled
1018
+ * explicitly in _extractSectionInfoFromReferenceFacet by following its AnnotationPath.
1019
+ * Handles both fully-qualified names (com.sap.vocabularies.UI.v1.*) and alias form (UI.*).
836
1020
  * @private
837
1021
  */
838
1022
  _isSupportedRecordType: function(sRecordType) {
839
1023
  if (!sRecordType) { return false; }
840
1024
  var sNormalized = sRecordType.replace("com.sap.vocabularies.UI.v1.", "UI.");
841
1025
  return sNormalized !== "UI.DataFieldForIntentBasedNavigation" &&
842
- sNormalized !== "UI.DataFieldForAnnotation" &&
843
1026
  sNormalized !== "UI.DataFieldForAction" &&
844
1027
  sNormalized !== "UI.DataFieldWithNavigationPath" &&
845
1028
  sNormalized !== "UI.DataFieldWithIntentBasedNavigation";