@sapui5/sap.suite.ui.generic.template 1.148.0 → 1.148.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/sap/suite/ui/generic/template/.library +1 -1
- package/src/sap/suite/ui/generic/template/AnalyticalListPage/i18n/i18n_fr_CA.properties +2 -2
- package/src/sap/suite/ui/generic/template/AnalyticalListPage/manifest.json +1 -1
- package/src/sap/suite/ui/generic/template/Canvas/manifest.json +1 -1
- package/src/sap/suite/ui/generic/template/ListReport/i18n/i18n_fr.properties +1 -1
- package/src/sap/suite/ui/generic/template/ListReport/manifest.json +1 -1
- package/src/sap/suite/ui/generic/template/ObjectPage/manifest.json +1 -1
- package/src/sap/suite/ui/generic/template/ObjectPage/view/fragments/Actions.fragment.xml +13 -13
- package/src/sap/suite/ui/generic/template/QuickCreate/manifest.json +1 -1
- package/src/sap/suite/ui/generic/template/QuickView/manifest.json +1 -1
- package/src/sap/suite/ui/generic/template/lib/AppComponent.js +1 -1
- package/src/sap/suite/ui/generic/template/lib/CommonEventHandlers.js +57 -6
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillAIOrchestrator.js +185 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillApplyHandler.js +476 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController.js +311 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector.js +669 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer.js +217 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillHandler.js +323 -1120
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer.js +491 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillDialog.fragment.xml +14 -10
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n.properties +11 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ar.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_bg.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ca.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cnr.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cs.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cy.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_da.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_de.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_el.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_GB.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saprigi.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es_MX.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_et.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fi.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr.properties +15 -1
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr_CA.properties +15 -1
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hi.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hr.properties +15 -1
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hu.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_id.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_it.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_iw.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ja.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_kk.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ko.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lt.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lv.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_mk.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ms.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_nl.properties +15 -1
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_no.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pl.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt_PT.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ro.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ru.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sh.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sk.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sl.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sr.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sv.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_th.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_tr.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_uk.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_vi.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_CN.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_TW.properties +14 -0
- package/src/sap/suite/ui/generic/template/lib/navigation/NavigationController.js +1 -1
- package/src/sap/suite/ui/generic/template/library.js +1 -1
- package/src/sap/suite/ui/generic/template/themes/base/ObjectPage.less +8 -3
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/ObjectPageSectionHandler.js +0 -282
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillNonUpdatableSmartForm.fragment.xml +0 -18
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillUpdatebleSmartForm.fragment.xml +0 -15
|
@@ -1,1205 +1,408 @@
|
|
|
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/
|
|
14
|
-
"sap/ui/
|
|
15
|
-
"sap/suite/ui/generic/template/
|
|
16
|
-
|
|
17
|
-
"sap/ui/
|
|
18
|
-
"sap/ui/
|
|
19
|
-
"sap/
|
|
20
|
-
"sap/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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
|
-
|
|
88
|
+
// Initialize dialog model and deferred group once per view lifetime
|
|
89
|
+
EasyFillDialogController.initDialogModel(oController, oObjectPageModel, EasyFillLayoutMode);
|
|
64
90
|
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
94
|
+
// Build the handler/formatter map that the dialog fragment binds to
|
|
95
|
+
var oHandlers = _buildDialogHandlers();
|
|
87
96
|
|
|
88
|
-
|
|
97
|
+
await EasyFillDialogController.openDialog(oController, oTemplateUtils, oHandlers);
|
|
98
|
+
}
|
|
89
99
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
|
132
|
-
|
|
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
|
|
144
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
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
|
-
|
|
179
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
219
|
-
|
|
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
|
-
|
|
173
|
+
_resetEasyFill(true);
|
|
229
174
|
},
|
|
230
175
|
|
|
176
|
+
// ── SmartForm group population formatter ──────────────────────────
|
|
231
177
|
populateSmartFields: function(sId, aFields, sEasyFillMode) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
var
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
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
|
-
|
|
744
|
-
|
|
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
|
-
|
|
752
|
-
|
|
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
|
-
|
|
769
|
-
|
|
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
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
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
|
+
oModel.setProperty("/sections", aData);
|
|
265
|
+
oModel.setProperty("/llmData", simpleFields);
|
|
266
|
+
|
|
267
|
+
// Process collection/table updates
|
|
268
|
+
mCollectionUpdates = collectionFields;
|
|
269
|
+
var bHasTableUpdates = Object.keys(mCollectionUpdates).length > 0;
|
|
270
|
+
oModel.setProperty("/hasTableUpdates", bHasTableUpdates);
|
|
271
|
+
|
|
272
|
+
// Render collection previews into the table preview container
|
|
273
|
+
EasyFillTableRenderer.renderCollectionPreviews(
|
|
274
|
+
mCollectionUpdates,
|
|
275
|
+
mFieldMapping,
|
|
276
|
+
[],
|
|
277
|
+
oController,
|
|
278
|
+
oRb,
|
|
279
|
+
mCollectionState,
|
|
280
|
+
oTableStagingState,
|
|
281
|
+
oObjectPageModel,
|
|
282
|
+
function() {
|
|
283
|
+
_refreshTableValidationAndSaveState().catch(Function.prototype);
|
|
869
284
|
}
|
|
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
|
-
}
|
|
285
|
+
);
|
|
884
286
|
|
|
885
|
-
|
|
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
|
-
}
|
|
287
|
+
await _refreshTableValidationAndSaveState();
|
|
938
288
|
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
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];
|
|
289
|
+
var iTotalFields = Object.keys(oSplit.updatableFields).length + Object.keys(oSplit.nonUpdatableFields).length;
|
|
290
|
+
if (iTotalFields === 0 && !bHasTableUpdates) {
|
|
291
|
+
oModel.setProperty("/stateType", "NoEntries");
|
|
962
292
|
return;
|
|
963
293
|
}
|
|
964
|
-
oPreviewRow[sPropertyName] = oContext ? oContext.getProperty(sPropertyName) : "";
|
|
965
|
-
});
|
|
966
|
-
return oPreviewRow;
|
|
967
|
-
}
|
|
968
294
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
}
|
|
976
|
-
if (oRowUpdate && typeof oRowUpdate._rowIndex === "number") {
|
|
977
|
-
return "index:" + String(oRowUpdate._rowIndex);
|
|
295
|
+
oModel.setProperty("/isIllustrationVisible", false);
|
|
296
|
+
oModel.setProperty("/isFormVisible", iTotalFields > 0);
|
|
297
|
+
} else {
|
|
298
|
+
// AI call failed — show error illustration
|
|
299
|
+
EasyFillApplyHandler.applyTableValidationState([], oModel, oRb);
|
|
300
|
+
oModel.setProperty("/stateType", "Error");
|
|
978
301
|
}
|
|
979
|
-
return "row:" + String(iFallbackIndex);
|
|
980
302
|
}
|
|
981
303
|
|
|
982
|
-
function
|
|
983
|
-
var
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
var
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
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
|
-
});
|
|
304
|
+
async function _refreshTableValidationAndSaveState() {
|
|
305
|
+
var oModel = oDialogModel();
|
|
306
|
+
var mStagedCollectionUpdates = EasyFillApplyHandler.buildStagedCollectionUpdates(
|
|
307
|
+
oTableStagingState,
|
|
308
|
+
mCollectionState
|
|
309
|
+
);
|
|
310
|
+
var aTableValidationErrors = await EasyFillApplyHandler.validateCollectionUpdatesAgainstValueHelp(
|
|
311
|
+
mStagedCollectionUpdates,
|
|
312
|
+
mCollectionState,
|
|
313
|
+
oObjectPageModel,
|
|
314
|
+
oRb,
|
|
315
|
+
EasyFillFieldCollector,
|
|
316
|
+
GenericAnnotationHelper
|
|
317
|
+
);
|
|
318
|
+
EasyFillApplyHandler.applyTableValidationState(aTableValidationErrors, oModel, oRb);
|
|
1111
319
|
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
320
|
+
var bHasScalarChanges = Object.keys(mUpdatableLookupMap || {}).length > 0;
|
|
321
|
+
var bHasTableChanges = EasyFillApplyHandler.hasStagedCollectionChanges(
|
|
322
|
+
oTableStagingState,
|
|
323
|
+
mCollectionState
|
|
324
|
+
);
|
|
1116
325
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
326
|
+
oModel.setProperty(
|
|
327
|
+
"/isSaveEnabled",
|
|
328
|
+
bHasScalarChanges || (bHasTableChanges && aTableValidationErrors.length === 0)
|
|
329
|
+
);
|
|
1120
330
|
|
|
1121
|
-
|
|
1122
|
-
});
|
|
331
|
+
return aTableValidationErrors;
|
|
1123
332
|
}
|
|
1124
333
|
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
334
|
+
/**
|
|
335
|
+
* Resets the EasyFill dialog to its initial state.
|
|
336
|
+
* Delegates to EasyFillApplyHandler.resetEasyFill with the current state references.
|
|
337
|
+
*
|
|
338
|
+
* @param {boolean} [bRemoveTextAreaValue=false] - if true, also clears the text area input
|
|
339
|
+
*/
|
|
340
|
+
function _resetEasyFill(bRemoveTextAreaValue) {
|
|
341
|
+
EasyFillApplyHandler.resetEasyFill(
|
|
342
|
+
!!bRemoveTextAreaValue,
|
|
343
|
+
oDialogModel(),
|
|
344
|
+
oController,
|
|
345
|
+
mCollectionUpdates,
|
|
346
|
+
EasyFillLayoutMode,
|
|
347
|
+
function() {
|
|
348
|
+
EasyFillTableRenderer.resetStagedCollectionContexts(oTableStagingState, oObjectPageModel);
|
|
349
|
+
},
|
|
350
|
+
function() {
|
|
351
|
+
EasyFillFormRenderer.resetTransientContext(oTransientContextForEasyFill, oObjectPageModel);
|
|
352
|
+
oTransientContextForEasyFill = null;
|
|
1143
353
|
}
|
|
1144
|
-
|
|
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
|
-
}
|
|
354
|
+
);
|
|
1164
355
|
}
|
|
1165
356
|
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
"changeSetId":"EasyFillFETransientChanges"
|
|
1173
|
-
});
|
|
357
|
+
/**
|
|
358
|
+
* Convenience accessor for the easyFillDialogModel JSONModel.
|
|
359
|
+
* @returns {sap.ui.model.json.JSONModel}
|
|
360
|
+
*/
|
|
361
|
+
function oDialogModel() {
|
|
362
|
+
return oController.getView().getModel("easyFillDialogModel");
|
|
1174
363
|
}
|
|
1175
364
|
|
|
1176
|
-
//
|
|
1177
|
-
// function onSmartFieldChange() {
|
|
1178
|
-
// validateSmartForm();
|
|
1179
|
-
// }
|
|
1180
|
-
|
|
365
|
+
// ─── Public API ──────────────────────────────────────────────────────────
|
|
1181
366
|
|
|
1182
367
|
return {
|
|
368
|
+
/**
|
|
369
|
+
* Entry point called by the ObjectPage ControllerImplementation when the
|
|
370
|
+
* EasyFill button is pressed.
|
|
371
|
+
*
|
|
372
|
+
* If the ObjectPage is already in edit mode, the dialog opens immediately.
|
|
373
|
+
* If not, the page is transitioned to edit mode first and the resulting
|
|
374
|
+
* edit context is passed into the dialog.
|
|
375
|
+
*
|
|
376
|
+
* @param {sap.ui.base.Event} oEvent - the button click event
|
|
377
|
+
*/
|
|
1183
378
|
onEasyFillButtonClick: function(oEvent) {
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
const isEditable = oController.getView().getModel("ui").getProperty("/editable");
|
|
1187
|
-
if (isEditable) {
|
|
379
|
+
var bIsEditable = oController.getView().getModel("ui").getProperty("/editable");
|
|
380
|
+
if (bIsEditable) {
|
|
1188
381
|
openEasyFillDialog(oEvent);
|
|
1189
382
|
} else {
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
383
|
+
// Trigger the OP edit flow and wait for the new edit context before opening
|
|
384
|
+
oController._templateEventHandlers.onEditByEasyFill()
|
|
385
|
+
.then(function(oContext) {
|
|
386
|
+
oObjectPageContext = oContext;
|
|
387
|
+
openEasyFillDialog(oEvent, oContext);
|
|
388
|
+
})
|
|
389
|
+
.catch(Function.prototype);
|
|
1194
390
|
}
|
|
1195
391
|
}
|
|
1196
392
|
};
|
|
1197
393
|
}
|
|
1198
394
|
|
|
1199
395
|
return BaseObject.extend("sap.suite.ui.generic.template.lib.ai.EasyFill.EasyFillHandler", {
|
|
1200
|
-
|
|
396
|
+
/**
|
|
397
|
+
* Constructor — wires all helpers and exposes the public API via getMethods.
|
|
398
|
+
*
|
|
399
|
+
* @param {object} oState - shared template state (passed from ControllerImplementation)
|
|
400
|
+
* @param {object} oController - MVC controller instance
|
|
401
|
+
* @param {object} oTemplateUtils - FE template utilities (services, common utils, etc.)
|
|
402
|
+
* @param {object} oObjectPage - the ObjectPage control instance
|
|
403
|
+
*/
|
|
404
|
+
constructor: function(oState, oController, oTemplateUtils, oObjectPage) {
|
|
1201
405
|
extend(this, getMethods(oState, oController, oTemplateUtils, oObjectPage));
|
|
1202
406
|
}
|
|
1203
407
|
});
|
|
1204
|
-
|
|
1205
408
|
});
|