@sapui5/sap.suite.ui.generic.template 1.148.0 → 1.149.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 (132) 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/i18n/i18n_fr_CA.properties +2 -2
  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/controller/IappStateHandler.js +254 -224
  7. package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_de.properties +1 -1
  8. package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_fr.properties +1 -1
  9. package/src/sap/suite/ui/generic/template/ListReport/manifest.json +1 -1
  10. package/src/sap/suite/ui/generic/template/ObjectPage/controller/inlineCreationRows/InlineCreationRowsHelper.js +76 -33
  11. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_ar.properties +4 -0
  12. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_bg.properties +4 -0
  13. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_ca.properties +4 -0
  14. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_cnr.properties +4 -0
  15. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_cs.properties +4 -0
  16. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_cy.properties +4 -0
  17. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_da.properties +4 -0
  18. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_de.properties +6 -2
  19. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_el.properties +4 -0
  20. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_en.properties +4 -0
  21. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_en_GB.properties +4 -0
  22. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_es.properties +4 -0
  23. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_es_MX.properties +4 -0
  24. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_et.properties +4 -0
  25. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_fi.properties +4 -0
  26. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_fr.properties +4 -0
  27. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_fr_CA.properties +4 -0
  28. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_hi.properties +4 -0
  29. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_hr.properties +4 -0
  30. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_hu.properties +4 -0
  31. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_id.properties +5 -1
  32. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_it.properties +4 -0
  33. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_iw.properties +4 -0
  34. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_ja.properties +4 -0
  35. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_kk.properties +4 -0
  36. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_ko.properties +4 -0
  37. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_lt.properties +4 -0
  38. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_lv.properties +4 -0
  39. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_mk.properties +4 -0
  40. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_ms.properties +4 -0
  41. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_nl.properties +4 -0
  42. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_no.properties +4 -0
  43. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_pl.properties +4 -0
  44. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_pt.properties +5 -1
  45. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_pt_PT.properties +4 -0
  46. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_ro.properties +4 -0
  47. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_ru.properties +4 -0
  48. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_sh.properties +4 -0
  49. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_sk.properties +4 -0
  50. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_sl.properties +4 -0
  51. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_sr.properties +4 -0
  52. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_sv.properties +4 -0
  53. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_th.properties +4 -0
  54. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_tr.properties +4 -0
  55. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_uk.properties +5 -1
  56. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_vi.properties +4 -0
  57. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_zh_CN.properties +4 -0
  58. package/src/sap/suite/ui/generic/template/ObjectPage/i18n/i18n_zh_TW.properties +4 -0
  59. package/src/sap/suite/ui/generic/template/ObjectPage/manifest.json +1 -1
  60. package/src/sap/suite/ui/generic/template/ObjectPage/view/Details.view.xml +1 -0
  61. package/src/sap/suite/ui/generic/template/ObjectPage/view/fragments/Actions.fragment.xml +13 -13
  62. package/src/sap/suite/ui/generic/template/QuickCreate/manifest.json +1 -1
  63. package/src/sap/suite/ui/generic/template/QuickView/manifest.json +1 -1
  64. package/src/sap/suite/ui/generic/template/detailTemplates/DiscardEditHandler.js +9 -0
  65. package/src/sap/suite/ui/generic/template/genericUtilities/controlStateWrapperFactory/SmartVariantManagementWrapper.js +43 -35
  66. package/src/sap/suite/ui/generic/template/lib/AppComponent.js +1 -1
  67. package/src/sap/suite/ui/generic/template/lib/CRUDManager.js +9 -7
  68. package/src/sap/suite/ui/generic/template/lib/CommonEventHandlers.js +57 -6
  69. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillAIOrchestrator.js +182 -0
  70. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillApplyHandler.js +476 -0
  71. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController.js +311 -0
  72. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector.js +669 -0
  73. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer.js +217 -0
  74. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillHandler.js +326 -1120
  75. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer.js +491 -0
  76. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillDialog.fragment.xml +14 -10
  77. package/src/sap/suite/ui/generic/template/lib/i18n/i18n.properties +11 -8
  78. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ar.properties +23 -7
  79. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_bg.properties +23 -7
  80. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ca.properties +23 -7
  81. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cnr.properties +23 -7
  82. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cs.properties +23 -7
  83. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cy.properties +23 -7
  84. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_da.properties +23 -7
  85. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_de.properties +23 -7
  86. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_el.properties +23 -7
  87. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en.properties +23 -7
  88. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_GB.properties +23 -7
  89. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saprigi.properties +23 -7
  90. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es.properties +23 -7
  91. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es_MX.properties +23 -7
  92. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_et.properties +23 -7
  93. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fi.properties +23 -7
  94. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr.properties +24 -8
  95. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr_CA.properties +25 -9
  96. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hi.properties +23 -7
  97. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hr.properties +24 -8
  98. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hu.properties +23 -7
  99. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_id.properties +23 -7
  100. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_it.properties +23 -7
  101. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_iw.properties +23 -7
  102. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ja.properties +23 -7
  103. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_kk.properties +23 -7
  104. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ko.properties +23 -7
  105. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lt.properties +23 -7
  106. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lv.properties +23 -7
  107. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_mk.properties +23 -7
  108. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ms.properties +23 -7
  109. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_nl.properties +24 -8
  110. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_no.properties +23 -7
  111. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pl.properties +23 -7
  112. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt.properties +23 -7
  113. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt_PT.properties +23 -7
  114. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ro.properties +23 -7
  115. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ru.properties +23 -7
  116. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sh.properties +23 -7
  117. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sk.properties +23 -7
  118. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sl.properties +23 -7
  119. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sr.properties +23 -7
  120. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sv.properties +23 -7
  121. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_th.properties +23 -7
  122. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_tr.properties +23 -7
  123. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_uk.properties +23 -7
  124. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_vi.properties +23 -7
  125. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_CN.properties +23 -7
  126. package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_TW.properties +23 -7
  127. package/src/sap/suite/ui/generic/template/lib/navigation/NavigationController.js +1 -1
  128. package/src/sap/suite/ui/generic/template/library.js +1 -1
  129. package/src/sap/suite/ui/generic/template/themes/base/ObjectPage.less +8 -3
  130. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/ObjectPageSectionHandler.js +0 -282
  131. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillNonUpdatableSmartForm.fragment.xml +0 -18
  132. package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillUpdatebleSmartForm.fragment.xml +0 -15
@@ -1,1205 +1,411 @@
1
1
  sap.ui.define([
2
2
  "sap/ui/base/Object",
3
3
  "sap/base/util/extend",
4
- "sap/ui/comp/smartform/GroupElement",
5
- "sap/ui/comp/smartfield/SmartField",
6
- "sap/m/MessageToast",
7
- "sap/ui/model/json/JSONModel",
8
- "sap/ui/model/odata/AnnotationHelper",
9
- "sap/suite/ui/generic/template/lib/ai/EasyFill/ObjectPageSectionHandler",
10
- "sap/m/VBox",
11
- "sap/m/Text",
12
4
  "sap/suite/ui/generic/template/library",
13
- "sap/m/HBox",
14
- "sap/ui/layout/form/ColumnElementData",
15
- "sap/suite/ui/generic/template/js/AnnotationHelper",
16
- "sap/suite/ui/generic/template/genericUtilities/controlHelper",
17
- "sap/ui/model/Filter",
18
- "sap/ui/model/FilterOperator",
19
- "sap/m/Title",
20
- "sap/m/SegmentedButton",
21
- "sap/m/SegmentedButtonItem",
22
- "sap/m/Table",
23
- "sap/m/Column",
24
- "sap/m/ColumnListItem"
25
- ], function(BaseObject, extend, GroupElement, SmartField,MessageToast,JSONModel,AnnotationHelperModel,ObjectPageSectionHandler,VBox,Text,Library,HBox,ColumnElementData,GenericAnnotationHelper,controlHelper,Filter,FilterOperator,Title,SegmentedButton,SegmentedButtonItem,Table,Column,ColumnListItem) {
26
- 'use strict';
27
- const EasyFillLayoutMode = Library.EasyFillLayoutMode;
28
- /*
29
- * This is a handler class for EasyFill.
30
- * It gets initialized when the Fiori AI EasyFill is enabled
31
- * Below things are handled in this class:
32
- * 1. Prepare Field metadata
33
- * 2. Prepare EasyFill Dialog
5
+ "sap/suite/ui/generic/template/genericUtilities/FeError",
6
+ "sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector",
7
+ "sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillAIOrchestrator",
8
+ "sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer",
9
+ "sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer",
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"
13
+ ], function(
14
+ BaseObject,
15
+ extend,
16
+ Library,
17
+ FeError,
18
+ EasyFillFieldCollector,
19
+ EasyFillAIOrchestrator,
20
+ EasyFillFormRenderer,
21
+ EasyFillTableRenderer,
22
+ EasyFillApplyHandler,
23
+ EasyFillDialogController,
24
+ GenericAnnotationHelper
25
+ ) {
26
+ "use strict";
27
+
28
+ /**
29
+ * EasyFillHandler
34
30
  *
31
+ * Thin orchestrator for the EasyFill AI feature on the ObjectPage.
32
+ *
33
+ * This class wires all EasyFill sub-modules together and exposes a single public
34
+ * entry point (onEasyFillButtonClick) to the ObjectPage ControllerImplementation.
35
+ *
36
+ * Sub-module responsibilities:
37
+ * EasyFillFieldCollector — OData metadata field/section extraction
38
+ * EasyFillAIOrchestrator — AI service call and response post-processing
39
+ * EasyFillFormRenderer — SmartForm/GroupElement/SmartField population
40
+ * EasyFillTableRenderer — SmartTable collection preview rendering
41
+ * EasyFillApplyHandler — Confirm/save, validation, reset logic
42
+ * EasyFillDialogController — Dialog lifecycle, formatters, UI event handlers
43
+ *
44
+ * Shared mutable state lives here and is passed by reference into sub-modules.
45
+ * Sub-modules are stateless — they never store state themselves.
35
46
  */
36
47
  function getMethods(oState, oController, oTemplateUtils, oObjectPage) {
37
- let oTransientContextForEasyFill;
38
- let oObjectPageContext;
39
- let oObjectPageModel;
40
- let mUpdatableLookupMap;
41
- let oRb;
42
- let oAIPopover;
43
- let aEditableFields = [];
44
- let mCollectionUpdates = Object.create(null);
45
- let mCollectionRowContexts = Object.create(null);
46
- let mCollectionRowContextsByPath = Object.create(null);
47
- let mCollectionProperties = Object.create(null);
48
- let mCollectionEntityTypes = Object.create(null);
49
- let mStagedRowValues = Object.create(null);
50
- const MAX_TABLE_PREVIEW_ROWS = 20;
48
+ var EasyFillLayoutMode = Library.EasyFillLayoutMode;
49
+
50
+ // ─── Shared Mutable State ────────────────────────────────────────────────
51
+ // All state is declared here and passed into sub-modules as needed.
52
+ // Sub-modules must not cache these values — always use the latest reference.
53
+
54
+ var oTransientContextForEasyFill = null; // Transient OData context for staged AI scalar values
55
+ var oObjectPageContext = null; // Live ObjectPage binding context
56
+ var oObjectPageModel = null; // OData model of the ObjectPage
57
+ var mUpdatableLookupMap = {}; // { fieldName: value } for updatable fields only
58
+ var oRb = null; // i18n resource bundle
59
+ var oAIPopoverRef = { popover: null }; // Mutable ref for lazy-loaded AINotice popover
60
+
61
+ // Collection state — tracks OData row contexts and property metadata for SmartTable collections
62
+ var mCollectionState = {
63
+ mCollectionRowContexts: Object.create(null),
64
+ mCollectionRowContextsByPath: Object.create(null),
65
+ mCollectionProperties: Object.create(null),
66
+ mCollectionEntityTypes: Object.create(null)
67
+ };
68
+
69
+ var mCollectionUpdates = Object.create(null); // { navProperty: [ rowUpdate ] } from AI response
70
+ var oTableStagingState = {
71
+ mStagedRows: Object.create(null)
72
+ };
73
+
74
+ // ─── Internal Helpers ────────────────────────────────────────────────────
51
75
 
52
- async function openEasyFillDialog(oEvent,oContext) {
53
- let oEasyFillDialog;
76
+ /**
77
+ * Opens the EasyFill dialog for the given ObjectPage context.
78
+ * Initializes the dialog model on first open, then loads and opens the dialog fragment
79
+ * with all event handlers and formatters bound.
80
+ *
81
+ * @param {sap.ui.base.Event} oEvent - the button click event
82
+ * @param {sap.ui.model.Context} [oContext] - optional context override (used when OP just entered edit mode)
83
+ */
84
+ async function openEasyFillDialog(oEvent, oContext) {
54
85
  oRb = oController.getView().getModel("i18n").getResourceBundle();
55
- const bIsModelCreated = !!oController.getView().getModel("easyFillDialogModel");
56
86
  oObjectPageModel = oObjectPage.getModel();
57
- if (!bIsModelCreated) {
58
- const aDeferredGroups = oObjectPageModel.getDeferredGroups();
59
- //Adding a defferred group will make sure that no batch call would get triggered to backend until submitChanges() is invoked
60
- aDeferredGroups.push("EasyFillFETransientChanges");
61
- oObjectPageModel.setDeferredGroups(aDeferredGroups);
62
87
 
63
- //Creating a JSON model for the dialog
88
+ // Initialize dialog model and deferred group once per view lifetime
89
+ EasyFillDialogController.initDialogModel(oController, oObjectPageModel, EasyFillLayoutMode);
64
90
 
65
- const oData = {
66
- isBusy: false,
67
- isFormVisible: false,
68
- isIllustrationVisible: true,
69
- isSaveEnabled: false,
70
- stateType: "Initial",
71
- feedbackEnabled: true,
72
- feedbackPressedUp: false,
73
- feedbackPressedDown: false,
74
- isEasyFillButtonEnabled: false,
75
- llmData: {},
76
- easyFillMode: EasyFillLayoutMode.CondensedMode,
77
- sections:[],
78
- isFullScreen: true,
79
- hasTableUpdates: false,
80
- hasTableValidationErrors: false,
81
- tableValidationMessage: ""
82
- };
91
+ // Use the provided context override or fall back to the current OP binding context
92
+ oObjectPageContext = oContext || oObjectPage.getBindingContext();
83
93
 
84
- let oModel = new JSONModel(oData);
85
- oController.getView().setModel(oModel, "easyFillDialogModel");
86
- }
94
+ // Build the handler/formatter map that the dialog fragment binds to
95
+ var oHandlers = _buildDialogHandlers();
87
96
 
88
- oObjectPageContext = (oContext) ? oContext : oObjectPage.getBindingContext();
97
+ await EasyFillDialogController.openDialog(oController, oTemplateUtils, oHandlers);
98
+ }
89
99
 
90
- // cannot use OP path to create a list binding as it is a individual entry
91
- var sFragmentname = "sap.suite.ui.generic.template.lib.ai.EasyFill.fragments.EasyFillDialog";
92
- oTemplateUtils.oCommonUtils.getDialogFragmentAsync(sFragmentname, {
100
+ /**
101
+ * Builds the event handler and formatter map passed to the EasyFillDialog fragment.
102
+ * Each entry corresponds to a function called from the fragment via event binding or formatter.
103
+ *
104
+ * @returns {object} handler/formatter map
105
+ */
106
+ function _buildDialogHandlers() {
107
+ return {
108
+ // ── Layout mode toggle (Condensed ↔ ObjectPage view) ──────────────
93
109
  onSelectionChange: function(oEvent) {
94
- const sSelectedKey = oEvent.getSource().getSelectedKey();
95
- const easyFillDialogModel = oController.getView().getModel("easyFillDialogModel");
96
- const oEntityType = oTemplateUtils.oCommonUtils.getMetaModelEntityType(oController.getOwnerComponent().getEntitySet());
97
- easyFillDialogModel.setProperty("/easyFillMode",sSelectedKey);
98
- const oLLMResult = easyFillDialogModel.getProperty("/llmData");
99
- const aData = (sSelectedKey === EasyFillLayoutMode.CondensedMode) ? ObjectPageSectionHandler.getSectionsForCondensedView(oLLMResult) : ObjectPageSectionHandler.getSectionsForObjectPageView(oEntityType,oLLMResult,oRb);
100
- easyFillDialogModel.setProperty("/sections",aData);
110
+ EasyFillDialogController.onSelectionChange(
111
+ oEvent, oController, oTemplateUtils, oRb,
112
+ EasyFillFieldCollector, EasyFillLayoutMode
113
+ );
101
114
  },
115
+
116
+ // ── Feedback thumbs-up/down ───────────────────────────────────────
102
117
  onFeedbackPress: function() {
103
- const easyFillDialogModel = oController.getView().getModel("easyFillDialogModel");
104
- easyFillDialogModel.setProperty("/feedbackEnabled",false);
105
- MessageToast.show(oRb.getText("EASYFILL_RESULT_FEEDBACK"));
118
+ EasyFillDialogController.onFeedbackPress(oController, oRb);
106
119
  },
120
+
121
+ // ── Section title formatter (resolves i18n patterns) ──────────────
107
122
  formatSectionTitle: function(sTitle) {
108
- if (!sTitle) {return "";}
109
- // Matches {@i18n>SomeKey} or {i18n>SomeKey}
110
- const oMatch = sTitle.match(/\{@?i18n>([^}]+)\}/);
111
- if (oMatch) {
112
- const sKey = oMatch[1];
113
- const oResourceBundle = oController.getView().getModel("i18n").getResourceBundle();
114
- return oResourceBundle.getText(sKey);
115
- }
116
- // No i18n pattern → return as-is
117
- return sTitle;
123
+ return EasyFillDialogController.formatSectionTitle(sTitle, oController);
118
124
  },
119
- formatIllustrationType: function (sState) {
120
- switch (sState) {
121
- case "Initial":
122
- return "sapIllus-NoSearchResults";
123
- case "NoEntries":
124
- return "sapIllus-NoEntries";
125
- case "Error":
126
- return "sapIllus-UnableToLoad";
127
- default:
128
- return "sapIllus-NoSearchResults";
129
- }
125
+
126
+ // ── IllustratedMessage state formatters ───────────────────────────
127
+ formatIllustrationType: function(sState) {
128
+ return EasyFillDialogController.formatIllustrationType(sState);
130
129
  },
131
- formatIllustrationTitle: function (sState) {
132
- switch (sState) {
133
- case "Initial":
134
- return oRb.getText("EASY_FILL_ILLUSTRATION_TITLE_INITIAL");
135
- case "NoEntries":
136
- return oRb.getText("EASY_FILL_ILLUSTRATION_TITLE_NO_ENTRIES");
137
- case "Error":
138
- return oRb.getText("EASY_FILL_ILLUSTRATION_TITLE_ERROR");
139
- default:
140
- return oRb.getText("EASY_FILL_ILLUSTRATION_TITLE_INITIAL");
141
- }
130
+ formatIllustrationTitle: function(sState) {
131
+ return EasyFillDialogController.formatIllustrationTitle(sState, oRb);
142
132
  },
143
- formatIllustrationDescription: function (sState) {
144
- switch (sState) {
145
- case "Initial":
146
- return oRb.getText("EASY_FILL_ILLUSTRATION_DESCRIPTION_INITIAL");
147
- case "NoEntries":
148
- return oRb.getText("EASY_FILL_ILLUSTRATION_DESCRIPTION_NO_ENTRIES");
149
- case "Error":
150
- return oRb.getText("EASY_FILL_ILLUSTRATION_DESCRIPTION_ERROR");
151
- default:
152
- return oRb.getText("EASY_FILL_ILLUSTRATION_DESCRIPTION_INITIAL");
153
- }
133
+ formatIllustrationDescription: function(sState) {
134
+ return EasyFillDialogController.formatIllustrationDescription(sState, oRb);
154
135
  },
155
- onSaveFromEasyFillDialog : async function (oEvent) {
156
- var oTransientObject = oTransientContextForEasyFill.getObject();
157
- for (const oProp in oTransientObject) {
158
- if (oProp !== "__metadata" && mUpdatableLookupMap[oProp]) {
159
- oObjectPageContext.setProperty(oProp, oTransientObject[oProp]);
160
- }
161
- }
162
- // Collection row values are already staged onto OData contexts via the editable New table;
163
- // clear staged tracking without reverting, then activate side effects.
164
- mStagedRowValues = Object.create(null);
165
- //Temporarily deactivating, Yet to be added again
166
- // activateSideEffects();
167
- resetEasyFill(true);
168
- oEasyFillDialog?.close();
136
+
137
+ // ── Confirm: apply AI values to the live OP context ───────────────
138
+ onSaveFromEasyFillDialog: async function() {
139
+ EasyFillApplyHandler.applyAndSave(
140
+ oTransientContextForEasyFill,
141
+ oObjectPageContext,
142
+ mUpdatableLookupMap,
143
+ oTableStagingState,
144
+ mCollectionState
145
+ );
146
+ _resetEasyFill(true);
147
+ EasyFillDialogController.closeDialog(oController);
169
148
  },
149
+
150
+ // ── Text area live change: enable/disable submit button ───────────
170
151
  onLiveChange: function(oEvent) {
171
- const easyFillDialogModel = oController.getView().getModel("easyFillDialogModel");
172
- easyFillDialogModel.setProperty("/isEasyFillButtonEnabled",!!oEvent.getSource().getValue());
173
- },
174
- onCancelEasyFillDialog : function () {
175
- resetEasyFill(true);
176
- oEasyFillDialog.close();
152
+ EasyFillDialogController.onLiveChange(oEvent, oController);
177
153
  },
178
- formatFieldValue: function(sFieldName,oData) {
179
- return `{${sFieldName}}`;
154
+
155
+ // ── Cancel: revert staged values and close dialog ─────────────────
156
+ onCancelEasyFillDialog: function() {
157
+ _resetEasyFill(true);
158
+ EasyFillDialogController.closeDialog(oController);
180
159
  },
181
- onEasyFillSubmitInputToAI : async function (oEvent) {
182
- const easyFillDialogModel = oController.getView().getModel("easyFillDialogModel");
183
- easyFillDialogModel.setProperty("/isBusy",true);
184
- let mFieldMapping = getEntitySetMapForLLM();
185
- let aiCallResult = await oTemplateUtils.oServices.oFioriAIHandler.fioriaiLib.EasyFill.extractFieldValuesFromText(oController.byId("EasyFillInputTextArea").getValue(), mFieldMapping);
186
- aEditableFields = [];
187
- easyFillDialogModel.setProperty("/isBusy",false);
188
- formatDateValues(aiCallResult,mFieldMapping);
189
- const oEntityType = oTemplateUtils.oCommonUtils.getMetaModelEntityType(oController.getOwnerComponent().getEntitySet());
190
- const sSelectedKey = easyFillDialogModel.getProperty("/easyFillMode") || EasyFillLayoutMode.CondensedMode;
191
- easyFillDialogModel.setProperty("/easyFillMode",sSelectedKey);
192
- if (aiCallResult.success) {
193
- const {simpleFields,collectionFields} = splitAIResultByCollection(aiCallResult.data, mFieldMapping);
194
- const {updatableFields,nonUpdatableFields} = getUpdatableAndNonUpdatableFields(simpleFields);
195
- mUpdatableLookupMap = updatableFields;
196
- //Setting up Sections for both condensed view and Object page view
197
- const aData = (sSelectedKey === EasyFillLayoutMode.CondensedMode) ? ObjectPageSectionHandler.getSectionsForCondensedView(simpleFields) : ObjectPageSectionHandler.getSectionsForObjectPageView(oEntityType,simpleFields,oRb);
198
- resetEasyFill();
199
- makeTransientContext();
200
- easyFillDialogModel.setProperty("/sections",aData);
201
- easyFillDialogModel.setProperty("/llmData",simpleFields);
202
- //Setting and rendering of SmartTables
203
- mCollectionUpdates = collectionFields;
204
- const bHasTableUpdates = Object.keys(mCollectionUpdates).length > 0;
205
- easyFillDialogModel.setProperty("/hasTableUpdates", bHasTableUpdates);
206
- const aTableValidationErrors = await validateCollectionUpdatesAgainstValueHelp(mFieldMapping);
207
- applyTableValidationState(aTableValidationErrors);
208
- renderCollectionPreviews(mFieldMapping, aTableValidationErrors);
209
160
 
210
- if (Object.keys(updatableFields).length + Object.keys(nonUpdatableFields).length === 0 && !bHasTableUpdates) {
211
- easyFillDialogModel.setProperty("/stateType","NoEntries");
212
- return;
213
- }
214
- easyFillDialogModel.setProperty("/isIllustrationVisible",false);
215
- //Fillable Fields
216
- easyFillDialogModel.setProperty("/isFormVisible",Object.keys(updatableFields).length + Object.keys(nonUpdatableFields).length > 0);
161
+ // ── Field value formatter (binding expression builder) ────────────
162
+ formatFieldValue: function(sFieldName) {
163
+ return "{" + sFieldName + "}";
164
+ },
217
165
 
218
- if (bHasTableUpdates) {
219
- easyFillDialogModel.setProperty("/isSaveEnabled", aTableValidationErrors.length === 0);
220
- }
221
- } else {
222
- applyTableValidationState([]);
223
- easyFillDialogModel.setProperty("/stateType","Error");
224
- }
166
+ // ── Submit user text to AI ────────────────────────────────────────
167
+ onEasyFillSubmitInputToAI: async function() {
168
+ await _handleAISubmit();
225
169
  },
226
170
 
171
+ // ── Clear all (reset without closing) ────────────────────────────
227
172
  onEasyFillClearAll: function() {
228
- resetEasyFill(true);
173
+ _resetEasyFill(true);
229
174
  },
230
175
 
176
+ // ── SmartForm group population formatter ──────────────────────────
231
177
  populateSmartFields: function(sId, aFields, sEasyFillMode) {
232
- const oObjectPageLayout = oController.byId("EasyFillObjectPageLayout");
233
- if (!oObjectPageLayout || !Array.isArray(aFields) || aFields.length === 0) {
234
- return true;
235
- }
236
-
237
- const easyFillDialogModel = oController.getView().getModel("easyFillDialogModel");
238
-
239
- setTimeout(() => {
240
- const oLLMData = easyFillDialogModel.getProperty("/llmData");
241
- let oTargetGroup = null;
242
-
243
- // sections -> ObjectPageSection
244
- oObjectPageLayout.getSections().forEach((oSection) => {
245
- // subSections -> ObjectPageSubSection
246
- oSection.getSubSections().forEach((oSubSection) => {
247
- // blocks -> SmartForm
248
- oSubSection.getBlocks().forEach((oBlock) => {
249
- if (oBlock.isA("sap.ui.comp.smartform.SmartForm")) {
250
- // ✅ Search inside Groups, not SmartForm
251
- oBlock.getGroups().forEach((oGroup) => {
252
- const oMatchedCustomData = oGroup.getCustomData().find(
253
- (oData) => oData.getKey() === "id" && oData.getValue() === sId
254
- );
255
- if (oMatchedCustomData) {
256
- oTargetGroup = oGroup;
257
- }
258
- });
259
- }
260
- });
261
- });
262
- });
263
-
264
- if (!oTargetGroup) {
265
- return;
266
- }
267
-
268
- // Destroy old GroupElements before adding new
269
- oTargetGroup.destroyGroupElements();
270
-
271
- aFields.forEach((sKey) => {
272
- // const bFieldEditable = true;
273
- const oGroupElement = new GroupElement({
274
- useHorizontalLayout: false
275
- });
276
-
277
- const isEditable = mUpdatableLookupMap && !!mUpdatableLookupMap[sKey];
278
-
279
- const oSmartField = new SmartField({
280
- value: "{" + sKey + "}",
281
- editable: isEditable,
282
- enabled: isEditable,
283
- contextEditable: isEditable,
284
- //Yet to be added, Temporarily disabled
285
- // change: onSmartFieldChange.bind(this),
286
- layoutData: new ColumnElementData({
287
- cellsSmall: 12,
288
- cellsLarge: 12
289
- }),
290
- editableChanged: (oEvent) => {
291
- if (oEvent.getParameter("editable") !== isEditable) {
292
- oEvent.getSource().setEditable(isEditable);
293
- }
294
- }
295
- });
296
-
297
- const oSmartFieldPrev = new SmartField({
298
- value: "{" + sKey + "}",
299
- editable: false,
300
- enabled: false,
301
- contextEditable: false,
302
- showLabel: false,
303
- visible: isEditable,
304
- layoutData: new ColumnElementData({
305
- cellsSmall: 9,
306
- cellsLarge: 10
307
- }),
308
- editableChanged: (oEvent) => {
309
- if (oEvent.getParameter("editable") !== false) {
310
- oEvent.getSource().setEditable(false);
311
- }
312
- }
313
- });
314
-
315
-
316
- if (isEditable) {
317
- oSmartField.setBindingContext(oTransientContextForEasyFill);
318
- oSmartFieldPrev.setBindingContext(oObjectPageContext);
319
- // aEditableFields.push(oSmartField);
320
- oTransientContextForEasyFill.setProperty(sKey, oLLMData[sKey]);
321
- } else {
322
- oSmartField.setBindingContext(oObjectPageContext);
323
- }
324
- oGroupElement.addElement(oSmartField); // label source
325
- oGroupElement.addElement( new Text({
326
- text: "Previously:",
327
- visible: isEditable,
328
- layoutData: new ColumnElementData({
329
- cellsSmall: 3,
330
- cellsLarge: 2
331
- })
332
- }));
333
- oGroupElement.addElement(oSmartFieldPrev);
334
- oTargetGroup.addGroupElement(oGroupElement);
335
- });
336
-
337
- }, 0);
338
-
339
- return true;
178
+ return EasyFillFormRenderer.populateSmartFields(
179
+ sId, aFields, sEasyFillMode,
180
+ oController,
181
+ oTransientContextForEasyFill,
182
+ oObjectPageContext,
183
+ mUpdatableLookupMap,
184
+ oDialogModel().getProperty("/llmData")
185
+ );
340
186
  },
341
187
 
188
+ // ── Fullscreen toggle ─────────────────────────────────────────────
342
189
  onToggleFullScreen: function() {
343
- const oEasyFillDialogModel = oController.getView().getModel("easyFillDialogModel");
344
- const bIsFullScreen = oEasyFillDialogModel.getProperty("/isFullScreen");
345
- // ✅ Toggle fullscreen state
346
- oEasyFillDialogModel.setProperty("/isFullScreen", !bIsFullScreen);
347
- if (bIsFullScreen) {
348
- oController.byId("inputAreaSplitPane").getLayoutData().setSize("0%");
349
- oController.byId("reviewAreaSplitPane").getLayoutData().setSize("100%");
350
- } else {
351
- oController.byId("inputAreaSplitPane").getLayoutData().setSize("40%");
352
- oController.byId("reviewAreaSplitPane").getLayoutData().setSize("60%");
353
- }
190
+ EasyFillDialogController.onToggleFullScreen(oController);
354
191
  },
355
192
 
356
-
193
+ // ── AI Notice popover ─────────────────────────────────────────────
357
194
  onLinkPress: async function(oEvent) {
358
- if (!oAIPopover) {
359
- oAIPopover = await getAIPopover();
360
- }
361
- oAIPopover.openBy(oEvent.getSource());
362
- }
363
- }, "easyFillPopup",undefined,true,true).then(function (oPopup) {
364
- oEasyFillDialog = oPopup;
365
- oPopup?.open();
366
- });
367
- }
368
-
369
- //Yet to implement, temporarily disabled
370
- // function activateSideEffects() {
371
- // //Making a set so that , duplicate field group ids are not passed
372
- // const aFieldGroupIdSet = new Set();
373
- // aEditableFields.forEach((oSmartField)=>{
374
- // //SideEffect would be invoked only for the editable controls inside the SmartField
375
- // const aEditableInnerFields = oSmartField.getInnerControls().filter((oControl)=>oControl.getEditable());
376
- // aEditableInnerFields.forEach((oControl)=>{
377
- // const aControlFieldGroupId = oControl.getFieldGroupIds();
378
- // if (aControlFieldGroupId.length > 0) {
379
- // aControlFieldGroupId.forEach((sFieldGroupId)=>{
380
- // aFieldGroupIdSet.add(sFieldGroupId);
381
- // });
382
- // }
383
- // });
384
-
385
- // });
386
-
387
- // const oView = oController.getView();
388
-
389
- // aFieldGroupIdSet.forEach((sFieldGroupId)=>{
390
- // const oID = oView.data(sFieldGroupId);
391
-
392
- // /**
393
- // * Currently, the SmartFields are created using the TransientContext, which means both
394
- // * "context" and "contextObject" refer to the TransientContext instance.
395
- // *
396
- // * However, we need to use the original OP context so that side effects are triggered
397
- // * on the original OP context when the user saves data from the EasyFill dialog back to OP.
398
- // *
399
- // * Therefore, we update the "context" and "contextObject" to reference the original OP context.
400
- // */
401
-
402
- // if (oID) {
403
- // oID["context"] = oObjectPageContext.getPath();
404
- // oID["contextObject"] = oObjectPageContext;
405
- // oView.data(sFieldGroupId,oID);
406
- // }
407
- // //To make sure that all the fields sideeffect would be triggered
408
- // const sSideEffectQualifier = oTemplateUtils.oServices.oApplicationController._getSideEffectsQualifier(oID.name);
409
- // oTemplateUtils.oServices.oApplicationController.registerGroupChange(sSideEffectQualifier);
410
- // });
411
-
412
-
413
- // oView.triggerValidateFieldGroup([...aFieldGroupIdSet]);
414
- // }
415
-
416
- function getAIPopover() {
417
- return new Promise(async(resolve,reject) => {
418
- const sFragmentname = "sap.suite.ui.generic.template.fragments.AINotice";
419
- const oPopover = await oTemplateUtils.oCommonUtils.getDialogFragmentAsync(sFragmentname,{
420
- onCloseAiPopover: function() {
421
- oAIPopover.close();
422
- }
423
- },"aiPopover",undefined,true);
424
- resolve(oPopover);
425
- });
426
- }
427
-
428
- function getEntitySetMapForLLM(oLLMResult) {
429
- const mPropertyMap = {};
430
- const oEntityType = oTemplateUtils.oCommonUtils.getMetaModelEntityType(oController.getOwnerComponent().getEntitySet());
431
- oEntityType.property.forEach((oProperty)=>{
432
- if (isVisible(oProperty)) {
433
- mPropertyMap[oProperty.name] = {
434
- description: getLabel(oProperty),
435
- dataType: oProperty.type
436
- };
437
- }
438
- });
439
- appendCollectionMapForLLM(mPropertyMap);
440
- return mPropertyMap;
441
- }
442
-
443
- function getLabel(oProperty) {
444
- return oProperty["com.sap.vocabularies.Common.v1.Label"] ? AnnotationHelperModel.format(oObjectPageContext,oProperty["com.sap.vocabularies.Common.v1.Label"]) : oProperty["sap:label"];
445
- }
446
-
447
- function formatDateValues(aiCallResult,mFieldMapping) {
448
- if (aiCallResult.success) {
449
- for (const key of Object.keys(aiCallResult.data)) {
450
- const sDataType = mFieldMapping[key].dataType;
451
- if (sDataType === "Edm.DateTimeOffset" || sDataType === "Edm.DateTime") {
452
- aiCallResult.data[key] = new Date(aiCallResult.data[key]);
453
- }
195
+ await EasyFillDialogController.onLinkPress(oEvent, oTemplateUtils, oAIPopoverRef);
454
196
  }
455
- }
456
- }
457
-
458
- //Yet to be implemented, temporarily disabled
459
- // function validateSmartForm() {
460
- // setTimeout(async() => {
461
- // const easyFillDialogModel = oController.getView().getModel("easyFillDialogModel");
462
- // try {
463
- // const oAIResponseReviewRegionSmartFormForUpdatableFields = oController.byId("EasyFillUpdatebleForm");
464
- // const aIds = await oAIResponseReviewRegionSmartFormForUpdatableFields.check();
465
- // const bHasTableValidationErrors = !!easyFillDialogModel.getProperty("/hasTableValidationErrors");
466
- // easyFillDialogModel.setProperty("/isSaveEnabled",aIds.length === 0 && !bHasTableValidationErrors);
467
- // } catch (error) {
468
- // easyFillDialogModel.setProperty("/isSaveEnabled",false);
469
- // }
470
- // }, 0);
471
- // }
472
-
473
- function applyTableValidationState(aValidationErrors) {
474
- const easyFillDialogModel = oController.getView().getModel("easyFillDialogModel");
475
- if (!aValidationErrors || aValidationErrors.length === 0) {
476
- easyFillDialogModel.setProperty("/hasTableValidationErrors", false);
477
- easyFillDialogModel.setProperty("/tableValidationMessage", "");
478
- return;
479
- }
480
- const sSummary = aValidationErrors.slice(0, 3).map(function(oError) {
481
- return oError.collectionLabel + " / " + oError.rowLabel + " / " + oError.propertyLabel;
482
- }).join(", ");
483
- easyFillDialogModel.setProperty("/hasTableValidationErrors", true);
484
- easyFillDialogModel.setProperty(
485
- "/tableValidationMessage",
486
- oRb.getText("EASY_FILL_TABLE_VALIDATION_MESSAGE", [sSummary])
487
- );
488
- }
489
-
490
- function resetEasyFill(bRemoveTextAreaValue) {
491
- const easyFillDialogModel = oController.getView().getModel("easyFillDialogModel");
492
- const oTablePreviewContainer = oController.byId("EasyFillTablePreviewContainer");
493
- if (oTablePreviewContainer) {
494
- oTablePreviewContainer.destroyItems();
495
- }
496
- revertStagedCollectionUpdates();
497
- resetTransientContext();
498
- easyFillDialogModel.setProperty("/isFormVisible",false);
499
- easyFillDialogModel.setProperty("/isIllustrationVisible",true);
500
- easyFillDialogModel.setProperty("/isSaveEnabled",false);
501
- easyFillDialogModel.setProperty("/stateType","Initial");
502
- easyFillDialogModel.setProperty("/feedbackEnabled",true);
503
- easyFillDialogModel.setProperty("/feedbackPressedUp",false);
504
- easyFillDialogModel.setProperty("/feedbackPressedDown",false);
505
- easyFillDialogModel.setProperty("/isFullScreen",true);
506
- easyFillDialogModel.setProperty("/hasTableUpdates",false);
507
- easyFillDialogModel.setProperty("/hasTableValidationErrors",false);
508
- easyFillDialogModel.setProperty("/tableValidationMessage","");
509
- easyFillDialogModel.setProperty("/easyFillMode",EasyFillLayoutMode.CondensedMode);
510
- mCollectionUpdates = Object.create(null);
511
- if (bRemoveTextAreaValue) {
512
- oController.byId("EasyFillInputTextArea").setValue("");
513
- easyFillDialogModel.setProperty("/isEasyFillButtonEnabled",false);
514
- }
515
- easyFillDialogModel.setProperty("/llmData",{});
516
- easyFillDialogModel.setProperty("/sections",[]);
517
-
518
- }
519
-
520
- function getUpdatableAndNonUpdatableFields(oAiCallResult) {
521
- const oEntityType = oTemplateUtils.oCommonUtils.getMetaModelEntityType(oController.getOwnerComponent().getEntitySet());
522
- const updatableFields = {};
523
- const nonUpdatableFields = {};
524
-
525
- Object.keys(oAiCallResult).forEach((sKey)=>{
526
- const oProperty = oEntityType.property.find((oProperty)=>oProperty.name === sKey);
527
- if (!oProperty) {
528
- nonUpdatableFields[sKey] = oAiCallResult[sKey];
529
- return;
530
- }
531
- if (isEditable(oProperty)) {
532
- updatableFields[sKey] = oAiCallResult[sKey];
533
- } else {
534
- nonUpdatableFields[sKey] = oAiCallResult[sKey];
535
- }
536
-
537
- });
538
- return {updatableFields,nonUpdatableFields};
539
- }
540
-
541
- //Checks if the field is editable or not
542
-
543
- // It can be set in 2 ways using field-control, it would be present when sap:updatable attribute is not present
544
- // 1. By attaching to an existing metadata property, sap:field-control-> path for the property
545
- //Below are the values by which the editable state would be defined
546
-
547
- /**
548
- * Value Meaning
549
- -----------------------------------------------
550
- 0 Hidden (field not shown at all)
551
- 1 Read-only (display only)
552
- 3 Editable
553
- 7 Mandatory (Still Editable)
554
- */
555
-
556
- // 2. By Adding to an annotation "com.sap.vocabularies.Common.v1.FieldControl"
557
-
558
- //Since "com.sap.vocabularies.Common.v1.FieldControl" is an V4 annotation, it would be given the first priority
559
-
560
- function isEditable(oProperty) {
561
- return isEditableByContextObject(oProperty, oObjectPageContext && oObjectPageContext.getObject && oObjectPageContext.getObject());
562
- }
563
-
564
- function isEditableByContextObject(oProperty, oContextObject) {
565
- if (!oProperty) {
566
- return false;
567
- }
568
-
569
- //Check for sap:updatable
570
- const sUpdatable = oProperty["sap:updatable"];
571
- if (sUpdatable) {
572
- return sUpdatable === "false" ? false : true;
573
- }
574
-
575
- //Check for "com.sap.vocabularies.Common.v1.FieldControl"
576
- const oAnnotation = oProperty["com.sap.vocabularies.Common.v1.FieldControl"];
577
-
578
- if (oAnnotation && oAnnotation["EnumMember"]) {
579
- const enumNumberType = oAnnotation["EnumMember"];
580
- return enumNumberType === 'com.sap.vocabularies.Common.v1.FieldControlType/ReadOnly' ? false : true;
581
- }
582
-
583
-
584
- //Check for "sap:field-control"
585
- const sFieldControl = oProperty["sap:field-control"];
586
-
587
- if (sFieldControl) {
588
- const iFieldControlValue = oContextObject && oContextObject[sFieldControl];
589
- return iFieldControlValue === 1 ? false : true;
590
- }
591
-
592
- //If no restriction is present then by default all fields are editable
593
- return true;
594
- }
595
-
596
- function splitAIResultByCollection(oAiData, mFieldMapping) {
597
- var oSplitResult = {
598
- simpleFields: {},
599
- collectionFields: {}
600
197
  };
601
- Object.keys(oAiData || {}).forEach(function(sFieldName) {
602
- var oFieldMeta = mFieldMapping[sFieldName];
603
- var vFieldValue = oAiData[sFieldName];
604
- var aCollectionRows = null;
605
- if (Array.isArray(vFieldValue)) {
606
- aCollectionRows = vFieldValue;
607
- } else if (vFieldValue && Array.isArray(vFieldValue.rows)) {
608
- aCollectionRows = vFieldValue.rows;
609
- }
610
- if (oFieldMeta && oFieldMeta.isCollection === true && aCollectionRows) {
611
- var aRowUpdates = aCollectionRows.filter(function(oRow) {
612
- return oRow && typeof oRow === "object" && (typeof oRow._contextPath === "string" || typeof oRow._rowIndex === "number");
613
- });
614
-
615
- // For current development, we are only supporting one SmartTable
616
- // in future if we support multiple SmartTable in the same OP, then we can change the structure of collectionFields to support multiple tables
617
- const {isValid} = isValidSmartTable(oFieldMeta.entitySet);
618
- if (aRowUpdates.length > 0 && Object.keys(oSplitResult.collectionFields).length === 0 && isValid) {
619
- oSplitResult.collectionFields[sFieldName] = aRowUpdates;
620
- }
621
- } else {
622
- oSplitResult.simpleFields[sFieldName] = vFieldValue;
623
- }
624
- });
625
- return oSplitResult;
626
- }
627
-
628
- function isValidSmartTable(sEntitySet) {
629
- var aSmartTables = getObjectPageSmartTables();
630
- const oSmartTable = aSmartTables.find(function(oSmartTable) {
631
- return oSmartTable.getEntitySet() === sEntitySet;
632
- });
633
- return {
634
- isValid: !!oSmartTable,
635
- smartTable: oSmartTable
636
- };
637
- }
638
-
639
- function getObjectPageSmartTables() {
640
- if (!oObjectPage || !oObjectPage.findAggregatedObjects) {
641
- return [];
642
- }
643
- //Currently we are looping all the tables in the ObjectPage, but it might contain custom smarttables
644
- //In future we have to read the table data from annotations/manifest so that we dont confuse with custom smart tables
645
- return oObjectPage.findAggregatedObjects(true, function(oControl) {
646
- const isSmartTable = controlHelper.isSmartTable(oControl);
647
- //Currently restricting it to Responsive Table and UITable, in future we have to extend it for other table types as well
648
- return isSmartTable && (oControl.getTable().isA("sap.m.Table") || oControl.getTable().isA("sap.ui.table.Table"));
649
- }) || [];
650
198
  }
651
199
 
652
- function appendCollectionMapForLLM(mPropertyMap) {
653
- mCollectionRowContexts = Object.create(null);
654
- mCollectionRowContextsByPath = Object.create(null);
655
- mCollectionProperties = Object.create(null);
656
- mCollectionEntityTypes = Object.create(null);
657
- var aSmartTables = getObjectPageSmartTables();
658
- if (!aSmartTables.length) {
659
- return;
660
- }
661
- var oMetaModel = oObjectPageModel && oObjectPageModel.getMetaModel();
662
- aSmartTables.forEach(function(oSmartTable) {
663
- var oHandler = oTemplateUtils.oServices.oPresentationControlHandlerFactory.getPresentationControlHandler(oSmartTable);
664
- if (!oHandler) {
665
- return;
666
- }
667
- var sNavProperty = extractNavigationPropertyName(oHandler.getBindingPath());
668
- if (!sNavProperty) {
669
- return;
670
- }
671
- var aVisibleProps = (oHandler.findVisibleColumnProperties && oHandler.findVisibleColumnProperties()) || [];
672
- if (!aVisibleProps.length) {
673
- return;
674
- }
675
- var aRowContexts = (oHandler.getCurrentContexts && oHandler.getCurrentContexts()) || [];
676
- var sEntitySetName = oSmartTable.getEntitySet();
677
- var oEntitySet = oMetaModel.getODataEntitySet(oSmartTable.getEntitySet());
678
- var oEntityType = oEntitySet && oMetaModel.getODataEntityType(oEntitySet.entityType);
679
- if (!oEntityType || !oEntityType.property) {
680
- return;
681
- }
682
- var mPropertyByName = Object.create(null);
683
- oEntityType.property.forEach(function(oProperty) {
684
- mPropertyByName[oProperty.name] = oProperty;
685
- });
686
-
687
- var oItemProperties = {};
688
- aVisibleProps.forEach(function(sPropName) {
689
- var oProperty = mPropertyByName[sPropName];
690
- if (!oProperty) {
691
- return;
692
- }
693
- oItemProperties[sPropName] = {
694
- description: getLabel(oProperty) || sPropName,
695
- dataType: oProperty.type
696
- };
697
- });
698
-
699
- if (!Object.keys(oItemProperties).length) {
700
- return;
701
- }
702
-
703
- var aCurrentItems = [];
704
- var mRowsByPath = Object.create(null);
705
- aRowContexts.forEach(function(oContext, iIndex) {
706
- if (!oContext || !oContext.getObject) {
707
- return;
708
- }
709
- var sContextPath = oContext.getPath && oContext.getPath();
710
- var oRowData = {_rowIndex: iIndex};
711
- if (typeof sContextPath === "string") {
712
- oRowData._contextPath = sContextPath;
713
- mRowsByPath[sContextPath] = {
714
- context: oContext,
715
- rowIndex: iIndex
716
- };
717
- }
718
- Object.keys(oItemProperties).forEach(function(sPropName) {
719
- var vValue = oContext.getProperty(sPropName);
720
- if (typeof vValue !== "object") {
721
- oRowData[sPropName] = vValue;
722
- }
723
- });
724
- aCurrentItems.push(oRowData);
725
- });
200
+ /**
201
+ * Handles the full AI submission flow:
202
+ * 1. Build field mapping
203
+ * 2. Call AI service
204
+ * 3. Post-process response (dates, split)
205
+ * 4. Update dialog model
206
+ * 5. Populate SmartForms and table previews
207
+ */
208
+ async function _handleAISubmit() {
209
+ var oModel = oDialogModel();
210
+ oModel.setProperty("/isBusy", true);
211
+
212
+ // Build complete LLM field mapping (entity fields + collection fields)
213
+ var mFieldMapping = EasyFillAIOrchestrator.buildFieldMapping(
214
+ oTemplateUtils, oController, oObjectPage, oObjectPageModel,
215
+ oObjectPageContext, EasyFillFieldCollector, mCollectionState
216
+ );
726
217
 
727
- mCollectionRowContexts[sNavProperty] = aRowContexts;
728
- mCollectionRowContextsByPath[sNavProperty] = mRowsByPath;
729
- mCollectionProperties[sNavProperty] = mPropertyByName;
730
- mCollectionEntityTypes[sNavProperty] = oEntityType;
731
- mPropertyMap[sNavProperty] = {
732
- description: typeof oSmartTable.getHeader() === "string" ? oSmartTable.getHeader() : sNavProperty,
733
- dataType: "Collection",
734
- isCollection: true,
735
- itemProperties: oItemProperties,
736
- currentItems: aCurrentItems,
737
- entitySet: sEntitySetName,
738
- section: typeof oSmartTable.getHeader() === "string" ? oSmartTable.getHeader() : sNavProperty
739
- };
740
- });
741
- }
218
+ // Submit to AI
219
+ var aiCallResult = await EasyFillAIOrchestrator.submitToAI(
220
+ oController.byId("EasyFillInputTextArea").getValue(),
221
+ mFieldMapping,
222
+ oTemplateUtils
223
+ );
742
224
 
743
- function extractNavigationPropertyName(sBindingPath) {
744
- if (!sBindingPath || typeof sBindingPath !== "string") {
745
- return null;
746
- }
747
- var aParts = sBindingPath.split("/").filter(Boolean);
748
- return aParts.length ? aParts[aParts.length - 1] : null;
749
- }
225
+ // Reset editable fields tracker for fresh population
226
+ oModel.setProperty("/isBusy", false);
750
227
 
751
- function resolveCollectionRowContext(sCollectionName, oRowUpdate) {
752
- var mByPath = mCollectionRowContextsByPath[sCollectionName] || Object.create(null);
753
- var sContextPath = oRowUpdate && oRowUpdate._contextPath;
754
- if (typeof sContextPath === "string" && mByPath[sContextPath]) {
755
- return mByPath[sContextPath];
756
- }
757
- var iRowIndex = oRowUpdate && oRowUpdate._rowIndex;
758
- var aRowContexts = mCollectionRowContexts[sCollectionName] || [];
759
- if (typeof iRowIndex === "number" && aRowContexts[iRowIndex]) {
760
- return {
761
- context: aRowContexts[iRowIndex],
762
- rowIndex: iRowIndex
763
- };
764
- }
765
- return null;
766
- }
228
+ // Convert date strings in AI response to JS Date objects
229
+ EasyFillAIOrchestrator.formatDateValues(aiCallResult, mFieldMapping);
767
230
 
768
- function readValueListByEquality(sCollectionPath, sValueListProperty, vValue) {
769
- return new Promise(function(resolve) {
770
- oObjectPageModel.read("/" + sCollectionPath, {
771
- filters: [
772
- new Filter({
773
- path: sValueListProperty,
774
- operator: FilterOperator.EQ,
775
- value1: vValue
776
- })
777
- ],
778
- success: function(oResponse) {
779
- resolve(!!(oResponse && Array.isArray(oResponse.results) && oResponse.results.length > 0));
780
- },
781
- error: function() {
782
- resolve(true);
783
- }
784
- });
785
- });
786
- }
231
+ var sSelectedKey = oModel.getProperty("/easyFillMode") || EasyFillLayoutMode.CondensedMode;
232
+ oModel.setProperty("/easyFillMode", sSelectedKey);
787
233
 
788
- async function validateCollectionUpdatesAgainstValueHelp(mFieldMapping) {
789
- const oMetaModel = oObjectPageModel && oObjectPageModel.getMetaModel();
790
- if (!oMetaModel) {
791
- return [];
792
- }
793
- const aValidationErrors = [];
794
- const aCollectionNames = Object.keys(mCollectionUpdates || {});
795
- for (const sCollectionName of aCollectionNames) {
796
- const aRowUpdates = mCollectionUpdates[sCollectionName] || [];
797
- const mCollectionPropertyMap = mCollectionProperties[sCollectionName] || Object.create(null);
798
- const oCollectionEntityType = mCollectionEntityTypes[sCollectionName];
799
- const sCollectionLabel = (mFieldMapping[sCollectionName] && mFieldMapping[sCollectionName].description) || sCollectionName;
800
- if (!oCollectionEntityType) {
801
- continue;
802
- }
803
- for (const oRowUpdate of aRowUpdates) {
804
- const oResolvedRow = resolveCollectionRowContext(sCollectionName, oRowUpdate);
805
- if (!oResolvedRow || !oResolvedRow.context) {
806
- continue;
807
- }
808
- const oRowContext = oResolvedRow.context;
809
- const oRowObject = oRowContext.getObject && oRowContext.getObject();
810
- const sRowLabel = oRb.getText("EASY_FILL_TABLE_ROW_LABEL", [String((oResolvedRow.rowIndex || 0) + 1)]);
811
- for (const sPropertyName of Object.keys(oRowUpdate)) {
812
- if (sPropertyName === "_rowIndex" || sPropertyName === "_contextPath") {
813
- continue;
814
- }
815
- const oPropertyMeta = mCollectionPropertyMap[sPropertyName];
816
- if (!oPropertyMeta || !isEditableByContextObject(oPropertyMeta, oRowObject)) {
817
- continue;
818
- }
819
- if (!GenericAnnotationHelper.isValueHelpTableAvailable(oPropertyMeta)) {
820
- continue;
821
- }
822
- const vUpdatedValue = oRowUpdate[sPropertyName];
823
- if (vUpdatedValue === null || vUpdatedValue === undefined || typeof vUpdatedValue === "object") {
824
- continue;
825
- }
826
- try {
827
- const sPropertyPath = oMetaModel.getODataProperty(oCollectionEntityType, sPropertyName, true);
828
- if (!sPropertyPath) {
829
- continue;
830
- }
831
- const oPropertyContext = oMetaModel.createBindingContext(sPropertyPath);
832
- const oValueLists = await oMetaModel.getODataValueLists(oPropertyContext);
833
- const oDefaultValueList = oValueLists && oValueLists[""];
834
- if (!oDefaultValueList || !oDefaultValueList.CollectionPath || !oDefaultValueList.CollectionPath.String) {
835
- continue;
836
- }
837
- let sValueListProperty = sPropertyName;
838
- if (Array.isArray(oDefaultValueList.Parameters)) {
839
- const oParameter = oDefaultValueList.Parameters.find(function(oParam) {
840
- return (
841
- oParam.RecordType === "com.sap.vocabularies.Common.v1.ValueListParameterInOut" &&
842
- oParam.LocalDataProperty &&
843
- oParam.LocalDataProperty.PropertyPath === sPropertyName
844
- );
845
- });
846
- if (oParameter && oParameter.ValueListProperty && oParameter.ValueListProperty.String) {
847
- sValueListProperty = oParameter.ValueListProperty.String;
848
- }
849
- }
850
- const bValueFound = await readValueListByEquality(
851
- oDefaultValueList.CollectionPath.String,
852
- sValueListProperty,
853
- vUpdatedValue
854
- );
855
- if (!bValueFound) {
856
- aValidationErrors.push({
857
- collectionName: sCollectionName,
858
- collectionLabel: sCollectionLabel,
859
- rowLabel: sRowLabel,
860
- rowIndex: oResolvedRow.rowIndex,
861
- rowPath: oRowContext.getPath && oRowContext.getPath(),
862
- propertyName: sPropertyName,
863
- propertyLabel: getLabel(oPropertyMeta) || sPropertyName
864
- });
865
- }
866
- } catch (e) {
867
- // If metadata/value list cannot be resolved, we keep current behavior and do not block save.
868
- }
234
+ if (aiCallResult.success) {
235
+ // Split AI response into simple fields (SmartForm) and collection rows (SmartTable)
236
+ var oSplitResult = EasyFillAIOrchestrator.splitAIResultByCollection(
237
+ aiCallResult.data, mFieldMapping, EasyFillFieldCollector, oObjectPage
238
+ );
239
+ var simpleFields = oSplitResult.simpleFields;
240
+ var collectionFields = oSplitResult.collectionFields;
241
+
242
+ // Separate updatable from non-updatable fields
243
+ var oEntityType = oTemplateUtils.oCommonUtils.getMetaModelEntityType(
244
+ oController.getOwnerComponent().getEntitySet()
245
+ );
246
+ var oSplit = EasyFillFieldCollector.getUpdatableAndNonUpdatableFields(
247
+ simpleFields, oEntityType, oObjectPageContext
248
+ );
249
+ mUpdatableLookupMap = oSplit.updatableFields;
250
+
251
+ // Build section tree for selected view mode
252
+ var aData = (sSelectedKey === EasyFillLayoutMode.CondensedMode)
253
+ ? EasyFillFieldCollector.getSectionsForCondensedView(simpleFields)
254
+ : EasyFillFieldCollector.getSectionsForObjectPageView(oEntityType, simpleFields, oRb);
255
+
256
+ // Reset form (destroy previous SmartForm content + revert table staging)
257
+ // then create a fresh transient context for the new AI result
258
+ _resetEasyFill();
259
+ oTransientContextForEasyFill = EasyFillFormRenderer.makeTransientContext(
260
+ null, oObjectPageModel, oController
261
+ );
262
+
263
+ // Update dialog model with section data and AI result.
264
+ // llmData must be set BEFORE sections so that when the sections binding fires
265
+ // and triggers the populateSmartFields formatter synchronously, the AI-suggested
266
+ // values are already available and can be written to the transient context.
267
+ oModel.setProperty("/llmData", simpleFields);
268
+ oModel.setProperty("/sections", aData);
269
+
270
+ // Process collection/table updates
271
+ mCollectionUpdates = collectionFields;
272
+ var bHasTableUpdates = Object.keys(mCollectionUpdates).length > 0;
273
+ oModel.setProperty("/hasTableUpdates", bHasTableUpdates);
274
+
275
+ // Render collection previews into the table preview container
276
+ EasyFillTableRenderer.renderCollectionPreviews(
277
+ mCollectionUpdates,
278
+ mFieldMapping,
279
+ [],
280
+ oController,
281
+ oRb,
282
+ mCollectionState,
283
+ oTableStagingState,
284
+ oObjectPageModel,
285
+ function() {
286
+ _refreshTableValidationAndSaveState().catch(Function.prototype);
869
287
  }
870
- }
871
- }
872
- return aValidationErrors;
873
- }
874
-
875
- function stringifyCellValue(vValue) {
876
- if (vValue === null || vValue === undefined) {
877
- return "";
878
- }
879
- if (vValue instanceof Date) {
880
- return vValue.toISOString();
881
- }
882
- return String(vValue);
883
- }
288
+ );
884
289
 
885
- function createPreviewTable(aPropertyNames, oItemProperties, aRows, fnCellFormatter) {
886
- var aColumns = aPropertyNames.map(function(sPropertyName) {
887
- return new Column({
888
- header: new Text({ text: (oItemProperties[sPropertyName] && oItemProperties[sPropertyName].description) || sPropertyName })
889
- });
890
- });
891
-
892
- var aItems = aRows.map(function(oRow) {
893
- return new ColumnListItem({
894
- cells: aPropertyNames.map(function(sPropertyName) {
895
- var sText = fnCellFormatter
896
- ? fnCellFormatter(oRow, sPropertyName, stringifyCellValue(oRow[sPropertyName]))
897
- : stringifyCellValue(oRow[sPropertyName]);
898
- return new Text({ text: sText });
899
- })
900
- });
901
- });
902
-
903
- return new Table({
904
- columns: aColumns,
905
- items: aItems,
906
- fixedLayout: false,
907
- autoPopinMode: true
908
- }).addStyleClass("sapSmartTemplatesObjectPageEasyFillTablePreview");
909
- }
910
-
911
- function stageCollectionUpdatesForPreview() {
912
- mStagedRowValues = Object.create(null);
913
- Object.keys(mCollectionUpdates).forEach(function(sCollectionName) {
914
- var mCollectionPropertyMap = mCollectionProperties[sCollectionName] || Object.create(null);
915
- var aRowUpdates = mCollectionUpdates[sCollectionName] || [];
916
- aRowUpdates.forEach(function(oRowUpdate) {
917
- var oResolved = resolveCollectionRowContext(sCollectionName, oRowUpdate);
918
- if (!oResolved || !oResolved.context) { return; }
919
- var oContext = oResolved.context;
920
- var sPath = oContext.getPath && oContext.getPath();
921
- if (!sPath) { return; }
922
- if (!mStagedRowValues[sPath]) {
923
- mStagedRowValues[sPath] = { context: oContext, originalValues: {} };
924
- }
925
- var oRowObject = oContext.getObject && oContext.getObject();
926
- Object.keys(oRowUpdate).forEach(function(sPropName) {
927
- if (sPropName === "_rowIndex" || sPropName === "_contextPath") { return; }
928
- var oPropertyMeta = mCollectionPropertyMap[sPropName];
929
- if (!isEditableByContextObject(oPropertyMeta, oRowObject)) { return; }
930
- if (!Object.prototype.hasOwnProperty.call(mStagedRowValues[sPath].originalValues, sPropName)) {
931
- mStagedRowValues[sPath].originalValues[sPropName] = oContext.getProperty(sPropName);
932
- }
933
- oContext.setProperty(sPropName, oRowUpdate[sPropName]);
934
- });
935
- });
936
- });
937
- }
290
+ await _refreshTableValidationAndSaveState();
938
291
 
939
- function revertStagedCollectionUpdates() {
940
- Object.keys(mStagedRowValues).forEach(function(sPath) {
941
- var oEntry = mStagedRowValues[sPath];
942
- var oContext = oEntry.context;
943
- Object.keys(oEntry.originalValues).forEach(function(sPropName) {
944
- oContext.setProperty(sPropName, oEntry.originalValues[sPropName]);
945
- });
946
- });
947
- mStagedRowValues = Object.create(null);
948
- }
949
-
950
- function buildCollectionPreviewRow(aPropertyNames, oResolvedRow, oRowUpdate, bUseUpdatedValues) {
951
- var oPreviewRow = {};
952
- var oContext = oResolvedRow && oResolvedRow.context;
953
- var sPath = oContext && oContext.getPath && oContext.getPath();
954
- var oStagedEntry = sPath && mStagedRowValues[sPath];
955
- aPropertyNames.forEach(function(sPropertyName) {
956
- if (bUseUpdatedValues && oRowUpdate && Object.prototype.hasOwnProperty.call(oRowUpdate, sPropertyName)) {
957
- oPreviewRow[sPropertyName] = oRowUpdate[sPropertyName];
958
- return;
959
- }
960
- if (!bUseUpdatedValues && oStagedEntry && Object.prototype.hasOwnProperty.call(oStagedEntry.originalValues, sPropertyName)) {
961
- oPreviewRow[sPropertyName] = oStagedEntry.originalValues[sPropertyName];
292
+ var iTotalFields = Object.keys(oSplit.updatableFields).length + Object.keys(oSplit.nonUpdatableFields).length;
293
+ if (iTotalFields === 0 && !bHasTableUpdates) {
294
+ oModel.setProperty("/stateType", "NoEntries");
962
295
  return;
963
296
  }
964
- oPreviewRow[sPropertyName] = oContext ? oContext.getProperty(sPropertyName) : "";
965
- });
966
- return oPreviewRow;
967
- }
968
297
 
969
- function getCollectionRowIdentity(oResolvedRow, oRowUpdate, iFallbackIndex) {
970
- if (oResolvedRow && oResolvedRow.context && oResolvedRow.context.getPath) {
971
- return oResolvedRow.context.getPath();
972
- }
973
- if (oRowUpdate && typeof oRowUpdate._contextPath === "string") {
974
- return oRowUpdate._contextPath;
975
- }
976
- if (oRowUpdate && typeof oRowUpdate._rowIndex === "number") {
977
- return "index:" + String(oRowUpdate._rowIndex);
298
+ oModel.setProperty("/isIllustrationVisible", false);
299
+ oModel.setProperty("/isFormVisible", iTotalFields > 0);
300
+ } else {
301
+ // AI call failed — show error illustration
302
+ EasyFillApplyHandler.applyTableValidationState([], oModel, oRb);
303
+ oModel.setProperty("/stateType", "Error");
978
304
  }
979
- return "row:" + String(iFallbackIndex);
980
305
  }
981
306
 
982
- function createEditableNewTable(aPropertyNames, oItemProperties, aPreviewRows, sCollectionName) {
983
- var aColumns = aPropertyNames.map(function(sPropName) {
984
- return new Column({
985
- header: new Text({ text: (oItemProperties[sPropName] && oItemProperties[sPropName].description) || sPropName })
986
- });
987
- });
988
- var mCollectionPropertyMap = mCollectionProperties[sCollectionName] || Object.create(null);
989
- var aItems = aPreviewRows.map(function(oPreviewRow) {
990
- var oContext = oPreviewRow.context;
991
- var oRowObject = oContext && oContext.getObject && oContext.getObject();
992
- var aCells = aPropertyNames.map(function(sPropName) {
993
- if (!oContext) {
994
- return new Text({ text: stringifyCellValue(oPreviewRow.newValues[sPropName]) });
995
- }
996
- var oPropertyMeta = mCollectionPropertyMap[sPropName];
997
- var bEditable = isEditableByContextObject(oPropertyMeta, oRowObject);
998
- var oSmartField = new SmartField({
999
- value: "{" + sPropName + "}",
1000
- editable: bEditable,
1001
- enabled: bEditable,
1002
- contextEditable: bEditable,
1003
- // change: onSmartFieldChange.bind(this),
1004
- editableChanged: function(oEvent) {
1005
- // Keep table SmartField editability deterministic in preview mode.
1006
- if (oEvent.getParameter("editable") !== bEditable) {
1007
- oEvent.getSource().setEditable(bEditable);
1008
- }
1009
- }
1010
- });
1011
-
1012
- if (bEditable) {
1013
- aEditableFields.push(oSmartField);
1014
- }
1015
- return oSmartField;
1016
- });
1017
- var oItem = new ColumnListItem({ cells: aCells });
1018
- if (oContext) {
1019
- oItem.setBindingContext(oContext);
1020
- }
1021
- return oItem;
1022
- });
1023
- return new Table({
1024
- columns: aColumns,
1025
- items: aItems,
1026
- fixedLayout: false,
1027
- autoPopinMode: true
1028
- }).addStyleClass("sapSmartTemplatesObjectPageEasyFillTablePreview");
1029
- }
1030
-
1031
- function renderCollectionPreviews(mFieldMapping, aTableValidationErrors) {
1032
- var oTablePreviewContainer = oController.byId("EasyFillTablePreviewContainer");
1033
- if (!oTablePreviewContainer) {
1034
- return;
1035
- }
1036
- oTablePreviewContainer.destroyItems();
1037
- // Stage LLM values onto OData row contexts; saves originals for revert on cancel
1038
- stageCollectionUpdatesForPreview();
1039
- var mInvalidCellByPath = Object.create(null);
1040
- (aTableValidationErrors || []).forEach(function(oError) {
1041
- if (!oError || !oError.rowPath) {
1042
- return;
1043
- }
1044
- mInvalidCellByPath[oError.rowPath + "|" + oError.propertyName] = true;
1045
- });
1046
-
1047
- Object.keys(mCollectionUpdates).forEach(function(sCollectionName) {
1048
- var oCollectionMeta = mFieldMapping[sCollectionName];
1049
- if (!oCollectionMeta || !oCollectionMeta.itemProperties) {
1050
- return;
1051
- }
1052
- var aRowUpdates = mCollectionUpdates[sCollectionName] || [];
1053
- var aPropertyNames = Object.keys(oCollectionMeta.itemProperties || {});
1054
- if (!aPropertyNames.length) {
1055
- return;
1056
- }
1057
- var mUpdatesByIdentity = Object.create(null);
1058
- aRowUpdates.forEach(function(oRowUpdate, iUpdateIndex) {
1059
- var oResolvedUpdate = resolveCollectionRowContext(sCollectionName, oRowUpdate);
1060
- mUpdatesByIdentity[getCollectionRowIdentity(oResolvedUpdate, oRowUpdate, iUpdateIndex)] = oRowUpdate;
1061
- });
1062
- var aPreviewRows = [];
1063
- var aCollectionRowContexts = mCollectionRowContexts[sCollectionName] || [];
1064
- aCollectionRowContexts.forEach(function(oContext, iRowIndex) {
1065
- var oResolved = {
1066
- context: oContext,
1067
- rowIndex: iRowIndex
1068
- };
1069
- var sIdentity = getCollectionRowIdentity(oResolved, null, iRowIndex);
1070
- var oRowUpdate = mUpdatesByIdentity[sIdentity];
1071
- aPreviewRows.push({
1072
- context: oContext,
1073
- rowIndex: iRowIndex,
1074
- update: oRowUpdate,
1075
- previousValues: buildCollectionPreviewRow(aPropertyNames, oResolved, oRowUpdate, false),
1076
- newValues: buildCollectionPreviewRow(aPropertyNames, oResolved, oRowUpdate, true)
1077
- });
1078
- });
1079
- aPreviewRows = aPreviewRows.slice(0, MAX_TABLE_PREVIEW_ROWS);
1080
-
1081
- var aPreviousRows = aPreviewRows.map(function(oPreviewRow) {
1082
- return oPreviewRow.previousValues;
1083
- });
1084
-
1085
- if (!aPreviewRows.length) {
1086
- return;
1087
- }
1088
-
1089
- var oPreviousTable = createPreviewTable(aPropertyNames, oCollectionMeta.itemProperties, aPreviousRows);
1090
- var oNewTable = createEditableNewTable(aPropertyNames, oCollectionMeta.itemProperties, aPreviewRows, sCollectionName);
1091
- oPreviousTable.setVisible(false);
1092
-
1093
- var oSectionTitle = new Title({
1094
- level: "H5",
1095
- text: (oCollectionMeta.description || sCollectionName) + " (" + aPreviewRows.length + ")"
1096
- });
1097
-
1098
- var oSegmentedButton = new SegmentedButton({
1099
- selectedKey: "new",
1100
- width:"auto",
1101
- items: [
1102
- new SegmentedButtonItem({ key: "previous", text: oRb.getText("EASY_FILL_TABLE_VIEW_PREVIOUS"), width:"auto" }),
1103
- new SegmentedButtonItem({ key: "new", text: oRb.getText("EASY_FILL_TABLE_VIEW_NEW"), width:"auto" })
1104
- ],
1105
- selectionChange: function() {
1106
- var sKey = oSegmentedButton.getSelectedKey();
1107
- oPreviousTable.setVisible(sKey === "previous");
1108
- oNewTable.setVisible(sKey === "new");
1109
- }
1110
- });
307
+ async function _refreshTableValidationAndSaveState() {
308
+ var oModel = oDialogModel();
309
+ var mStagedCollectionUpdates = EasyFillApplyHandler.buildStagedCollectionUpdates(
310
+ oTableStagingState,
311
+ mCollectionState
312
+ );
313
+ var aTableValidationErrors = await EasyFillApplyHandler.validateCollectionUpdatesAgainstValueHelp(
314
+ mStagedCollectionUpdates,
315
+ mCollectionState,
316
+ oObjectPageModel,
317
+ oRb,
318
+ EasyFillFieldCollector,
319
+ GenericAnnotationHelper
320
+ );
321
+ EasyFillApplyHandler.applyTableValidationState(aTableValidationErrors, oModel, oRb);
1111
322
 
1112
- var oHeader = new HBox({
1113
- justifyContent: "SpaceBetween",
1114
- items: [oSectionTitle, oSegmentedButton]
1115
- }).addStyleClass("sapUiSmallMarginTop");
323
+ var bHasScalarChanges = Object.keys(mUpdatableLookupMap || {}).length > 0;
324
+ var bHasTableChanges = EasyFillApplyHandler.hasStagedCollectionChanges(
325
+ oTableStagingState,
326
+ mCollectionState
327
+ );
1116
328
 
1117
- var oSection = new VBox({
1118
- items: [oHeader, oPreviousTable, oNewTable]
1119
- }).addStyleClass("sapSmartTemplatesObjectPageEasyFillTableSection");
329
+ oModel.setProperty(
330
+ "/isSaveEnabled",
331
+ bHasScalarChanges || (bHasTableChanges && aTableValidationErrors.length === 0)
332
+ );
1120
333
 
1121
- oTablePreviewContainer.addItem(oSection);
1122
- });
334
+ return aTableValidationErrors;
1123
335
  }
1124
336
 
1125
- function isVisible(oProperty) {
1126
-
1127
- //Check for "com.sap.vocabularies.Common.v1.FieldControl"
1128
- const oAnnotation = oProperty["com.sap.vocabularies.Common.v1.FieldControl"];
1129
-
1130
- if (oAnnotation && oAnnotation["EnumMember"]) {
1131
- const enumNumberType = oAnnotation["EnumMember"];
1132
- return enumNumberType === 'com.sap.vocabularies.Common.v1.FieldControlType/Hidden' ? false : true;
1133
- }
1134
-
1135
- //Check for Hidden annotation i.e com.sap.vocabularies.UI.v1.Hidden
1136
- const oHiddenAnnotation = oProperty["com.sap.vocabularies.UI.v1.Hidden"];
1137
- if (oHiddenAnnotation) {
1138
- let bIsVisible = false; // <Annotation Term="UI.Hidden"/>
1139
- if (oHiddenAnnotation["Bool"]) {
1140
- bIsVisible = (oHiddenAnnotation["Bool"] === "true") ? false : true;
1141
- } else if (oHiddenAnnotation["Path"]) {
1142
- bIsVisible = !oObjectPageContext.getObject()[oHiddenAnnotation["Path"]];
337
+ /**
338
+ * Resets the EasyFill dialog to its initial state.
339
+ * Delegates to EasyFillApplyHandler.resetEasyFill with the current state references.
340
+ *
341
+ * @param {boolean} [bRemoveTextAreaValue=false] - if true, also clears the text area input
342
+ */
343
+ function _resetEasyFill(bRemoveTextAreaValue) {
344
+ EasyFillApplyHandler.resetEasyFill(
345
+ !!bRemoveTextAreaValue,
346
+ oDialogModel(),
347
+ oController,
348
+ mCollectionUpdates,
349
+ EasyFillLayoutMode,
350
+ function() {
351
+ EasyFillTableRenderer.resetStagedCollectionContexts(oTableStagingState, oObjectPageModel);
352
+ },
353
+ function() {
354
+ EasyFillFormRenderer.resetTransientContext(oTransientContextForEasyFill, oObjectPageModel);
355
+ oTransientContextForEasyFill = null;
1143
356
  }
1144
- return bIsVisible;
1145
- }
1146
-
1147
- //Check for "sap:field-control"
1148
- const sFieldControl = oProperty["sap:field-control"];
1149
-
1150
- if (sFieldControl) {
1151
- const iFieldControlValue = oObjectPageContext.getObject()[sFieldControl];
1152
- return iFieldControlValue === 0 ? false : true;
1153
- }
1154
-
1155
- //If no restriction is present then by default all fields are visible
1156
- return true;
1157
- }
1158
-
1159
- function resetTransientContext() {
1160
- if (oTransientContextForEasyFill) {
1161
- oObjectPageModel.resetChanges([oTransientContextForEasyFill.getPath()],false,true);
1162
- oTransientContextForEasyFill = null;
1163
- }
357
+ );
1164
358
  }
1165
359
 
1166
- function makeTransientContext() {
1167
- if (oTransientContextForEasyFill) {
1168
- resetTransientContext();
1169
- }
1170
- oTransientContextForEasyFill = oObjectPageModel.createEntry("/" + oController.getOwnerComponent().getEntitySet(), {
1171
- "groupId" : "EasyFillFETransientChanges",
1172
- "changeSetId":"EasyFillFETransientChanges"
1173
- });
360
+ /**
361
+ * Convenience accessor for the easyFillDialogModel JSONModel.
362
+ * @returns {sap.ui.model.json.JSONModel}
363
+ */
364
+ function oDialogModel() {
365
+ return oController.getView().getModel("easyFillDialogModel");
1174
366
  }
1175
367
 
1176
- //Yet to implement, temporarily disabled
1177
- // function onSmartFieldChange() {
1178
- // validateSmartForm();
1179
- // }
1180
-
368
+ // ─── Public API ──────────────────────────────────────────────────────────
1181
369
 
1182
370
  return {
371
+ /**
372
+ * Entry point called by the ObjectPage ControllerImplementation when the
373
+ * EasyFill button is pressed.
374
+ *
375
+ * If the ObjectPage is already in edit mode, the dialog opens immediately.
376
+ * If not, the page is transitioned to edit mode first and the resulting
377
+ * edit context is passed into the dialog.
378
+ *
379
+ * @param {sap.ui.base.Event} oEvent - the button click event
380
+ */
1183
381
  onEasyFillButtonClick: function(oEvent) {
1184
- // Once you click on the easyFill button the OP should be going to editable state, if its already in editState then directly open the dialog
1185
- //If its in non-editable state we should wait until the context switch, We need that context to finally save the data from the AI into the newly came editable context of the OP
1186
- const isEditable = oController.getView().getModel("ui").getProperty("/editable");
1187
- if (isEditable) {
382
+ var bIsEditable = oController.getView().getModel("ui").getProperty("/editable");
383
+ if (bIsEditable) {
1188
384
  openEasyFillDialog(oEvent);
1189
385
  } else {
1190
- oController._templateEventHandlers.onEditByEasyFill().then((oContext)=>{
1191
- oObjectPageContext = oContext;
1192
- openEasyFillDialog(oEvent,oContext);
1193
- }).catch(Function.prototype);
386
+ // Trigger the OP edit flow and wait for the new edit context before opening
387
+ oController._templateEventHandlers.onEditByEasyFill()
388
+ .then(function(oContext) {
389
+ oObjectPageContext = oContext;
390
+ openEasyFillDialog(oEvent, oContext);
391
+ })
392
+ .catch(Function.prototype);
1194
393
  }
1195
394
  }
1196
395
  };
1197
396
  }
1198
397
 
1199
398
  return BaseObject.extend("sap.suite.ui.generic.template.lib.ai.EasyFill.EasyFillHandler", {
1200
- constructor: function (oState, oController, oTemplateUtils, oObjectPage) {
399
+ /**
400
+ * Constructor — wires all helpers and exposes the public API via getMethods.
401
+ *
402
+ * @param {object} oState - shared template state (passed from ControllerImplementation)
403
+ * @param {object} oController - MVC controller instance
404
+ * @param {object} oTemplateUtils - FE template utilities (services, common utils, etc.)
405
+ * @param {object} oObjectPage - the ObjectPage control instance
406
+ */
407
+ constructor: function(oState, oController, oTemplateUtils, oObjectPage) {
1201
408
  extend(this, getMethods(oState, oController, oTemplateUtils, oObjectPage));
1202
409
  }
1203
410
  });
1204
-
1205
411
  });