@sapui5/sap.suite.ui.generic.template 1.148.0 → 1.148.2
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/controller/ControllerImplementation.js +6 -3
- 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/genericUtilities/controlStateWrapperFactory/SmartFilterBarWrapper.js +20 -1
- package/src/sap/suite/ui/generic/template/lib/AppComponent.js +1 -1
- package/src/sap/suite/ui/generic/template/lib/CRUDManager.js +0 -7
- package/src/sap/suite/ui/generic/template/lib/CommonEventHandlers.js +57 -6
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillAIOrchestrator.js +216 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillApplyHandler.js +325 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillDialogController.js +358 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFieldCollector.js +848 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillFormRenderer.js +292 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillHandler.js +652 -1071
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/EasyFillTableRenderer.js +940 -0
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFill/fragments/EasyFillDialog.fragment.xml +41 -26
- package/src/sap/suite/ui/generic/template/lib/ai/EasyFilterBarHandler.js +90 -73
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n.properties +35 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ar.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_bg.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ca.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cnr.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cs.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_cy.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_da.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_de.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_el.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_GB.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_en_US_saprigi.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_es_MX.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_et.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fi.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr.properties +30 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_fr_CA.properties +31 -9
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hi.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hr.properties +30 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_hu.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_id.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_it.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_iw.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ja.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_kk.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ko.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lt.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_lv.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_mk.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ms.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_nl.properties +30 -8
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_no.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pl.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_pt_PT.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ro.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_ru.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sh.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sk.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sl.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sr.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_sv.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_th.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_tr.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_uk.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_vi.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_CN.properties +29 -7
- package/src/sap/suite/ui/generic/template/lib/i18n/i18n_zh_TW.properties +29 -7
- 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 +30 -4
- 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,786 @@
|
|
|
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
|
-
|
|
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
|
+
], function(
|
|
13
|
+
BaseObject,
|
|
14
|
+
extend,
|
|
15
|
+
Library,
|
|
16
|
+
FeError,
|
|
17
|
+
EasyFillFieldCollector,
|
|
18
|
+
EasyFillAIOrchestrator,
|
|
19
|
+
EasyFillFormRenderer,
|
|
20
|
+
EasyFillTableRenderer,
|
|
21
|
+
EasyFillApplyHandler,
|
|
22
|
+
EasyFillDialogController
|
|
23
|
+
) {
|
|
24
|
+
"use strict";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* EasyFillHandler
|
|
34
28
|
*
|
|
29
|
+
* Thin orchestrator for the EasyFill AI feature on the ObjectPage.
|
|
30
|
+
*
|
|
31
|
+
* This class wires all EasyFill sub-modules together and exposes a single public
|
|
32
|
+
* entry point (onEasyFillButtonClick) to the ObjectPage ControllerImplementation.
|
|
33
|
+
*
|
|
34
|
+
* Sub-module responsibilities:
|
|
35
|
+
* EasyFillFieldCollector — OData metadata field/section extraction
|
|
36
|
+
* EasyFillAIOrchestrator — AI service call and response post-processing
|
|
37
|
+
* EasyFillFormRenderer — SmartForm/GroupElement/SmartField population
|
|
38
|
+
* EasyFillTableRenderer — SmartTable collection preview rendering
|
|
39
|
+
* EasyFillApplyHandler — Confirm/save, validation, reset logic
|
|
40
|
+
* EasyFillDialogController — Dialog lifecycle, formatters, UI event handlers
|
|
41
|
+
*
|
|
42
|
+
* Shared mutable state lives here and is passed by reference into sub-modules.
|
|
43
|
+
* Sub-modules are stateless — they never store state themselves.
|
|
35
44
|
*/
|
|
36
45
|
function getMethods(oState, oController, oTemplateUtils, oObjectPage) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
var EasyFillLayoutMode = Library.EasyFillLayoutMode;
|
|
47
|
+
|
|
48
|
+
// ─── Shared Mutable State ────────────────────────────────────────────────
|
|
49
|
+
// All state is declared here and passed into sub-modules as needed.
|
|
50
|
+
// Sub-modules must not cache these values — always use the latest reference.
|
|
51
|
+
|
|
52
|
+
var oTransientContextForEasyFill = null; // Transient OData context for staged AI scalar values
|
|
53
|
+
var oObjectPageContext = null; // Live ObjectPage binding context
|
|
54
|
+
var oObjectPageModel = null; // OData model of the ObjectPage
|
|
55
|
+
var mUpdatableLookupMap = {}; // { fieldName: value } for updatable fields only
|
|
56
|
+
var oRb = null; // i18n resource bundle
|
|
57
|
+
var oAIPopoverRef = { popover: null }; // Mutable ref for lazy-loaded AINotice popover
|
|
58
|
+
|
|
59
|
+
// Collection state — tracks OData row contexts and property metadata for SmartTable collections
|
|
60
|
+
var mCollectionState = {
|
|
61
|
+
mCollectionRowContexts: Object.create(null),
|
|
62
|
+
mCollectionRowContextsByPath: Object.create(null),
|
|
63
|
+
mCollectionProperties: Object.create(null),
|
|
64
|
+
mCollectionEntityTypes: Object.create(null),
|
|
65
|
+
mCollectionEntitySets: Object.create(null),
|
|
66
|
+
mCollectionAllowsCreation: Object.create(null)
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
var mCollectionUpdates = Object.create(null); // { navProperty: [ rowUpdate ] } from AI response
|
|
70
|
+
var mLastFieldMapping = null; // retained so table sections can be re-rendered on mode switch
|
|
71
|
+
var oTableStagingState = {
|
|
72
|
+
mStagedRows: Object.create(null),
|
|
73
|
+
mPendingNewRowContexts: Object.create(null)
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// ─── Internal Helpers ────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Opens the EasyFill dialog for the given ObjectPage context.
|
|
80
|
+
* Initializes the dialog model on first open, then loads and opens the dialog fragment
|
|
81
|
+
* with all event handlers and formatters bound.
|
|
82
|
+
*
|
|
83
|
+
* @param {sap.ui.base.Event} oEvent - the button click event
|
|
84
|
+
* @param {sap.ui.model.Context} [oContext] - optional context override (used when OP just entered edit mode)
|
|
85
|
+
*/
|
|
86
|
+
async function openEasyFillDialog(oEvent, oContext) {
|
|
54
87
|
oRb = oController.getView().getModel("i18n").getResourceBundle();
|
|
55
|
-
const bIsModelCreated = !!oController.getView().getModel("easyFillDialogModel");
|
|
56
88
|
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
|
-
|
|
63
|
-
//Creating a JSON model for the dialog
|
|
64
|
-
|
|
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
|
-
};
|
|
83
|
-
|
|
84
|
-
let oModel = new JSONModel(oData);
|
|
85
|
-
oController.getView().setModel(oModel, "easyFillDialogModel");
|
|
86
|
-
}
|
|
87
89
|
|
|
88
|
-
|
|
90
|
+
// Initialize dialog model and deferred group once per view lifetime
|
|
91
|
+
EasyFillDialogController.initDialogModel(oController, oObjectPageModel, EasyFillLayoutMode);
|
|
92
|
+
|
|
93
|
+
// Use the provided context override or fall back to the current OP binding context
|
|
94
|
+
oObjectPageContext = oContext || oObjectPage.getBindingContext();
|
|
95
|
+
|
|
96
|
+
// Build the handler/formatter map that the dialog fragment binds to
|
|
97
|
+
var oHandlers = _buildDialogHandlers();
|
|
98
|
+
|
|
99
|
+
await EasyFillDialogController.openDialog(oController, oTemplateUtils, oHandlers);
|
|
100
|
+
}
|
|
89
101
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
102
|
+
/**
|
|
103
|
+
* Builds the event handler and formatter map passed to the EasyFillDialog fragment.
|
|
104
|
+
* Each entry corresponds to a function called from the fragment via event binding or formatter.
|
|
105
|
+
*
|
|
106
|
+
* @returns {object} handler/formatter map
|
|
107
|
+
*/
|
|
108
|
+
function _buildDialogHandlers() {
|
|
109
|
+
return {
|
|
110
|
+
// ── Layout mode toggle (Condensed ↔ ObjectPage view) ──────────────
|
|
111
|
+
// After the sections binding is refreshed, SmartForms for the new mode
|
|
112
|
+
// are repopulated asynchronously (setTimeout in populateSmartFields).
|
|
113
|
+
// _validateFieldsAndUpdateConfirmState is called here so that validation runs
|
|
114
|
+
// immediately on whatever SmartForms are currently in the DOM, and then
|
|
115
|
+
// again via fnOnFieldChange once the new SmartFields are rendered.
|
|
93
116
|
onSelectionChange: function(oEvent) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
117
|
+
// Step 1: clean up table content injected for the CURRENT mode BEFORE the
|
|
118
|
+
// model update. This ensures the OPL aggregation is free of manually-added
|
|
119
|
+
// sections when the sections binding recreates controls for the new mode,
|
|
120
|
+
// so getSections() in renderCollectionPreviews returns only bound sections.
|
|
121
|
+
var oOpl = oController.byId("EasyFillObjectPageLayout");
|
|
122
|
+
if (oOpl && mLastFieldMapping && Object.keys(mCollectionUpdates).length > 0) {
|
|
123
|
+
EasyFillTableRenderer.removeTableSections(oOpl);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Step 2: update model — this triggers the OPL sections binding to recreate
|
|
127
|
+
// bound section controls for the new mode.
|
|
128
|
+
EasyFillDialogController.onSelectionChange(
|
|
129
|
+
oEvent, oController, oTemplateUtils, oRb,
|
|
130
|
+
EasyFillFieldCollector, EasyFillLayoutMode
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// Step 3: re-render table previews for the new mode.
|
|
134
|
+
// _removeTableSections is skipped inside renderCollectionPreviews because
|
|
135
|
+
// bReuseStaging=true, since cleanup was already done in step 1 above.
|
|
136
|
+
if (mLastFieldMapping && Object.keys(mCollectionUpdates).length > 0) {
|
|
137
|
+
EasyFillTableRenderer.renderCollectionPreviews(
|
|
138
|
+
mCollectionUpdates,
|
|
139
|
+
mLastFieldMapping,
|
|
140
|
+
[],
|
|
141
|
+
oController,
|
|
142
|
+
oRb,
|
|
143
|
+
mCollectionState,
|
|
144
|
+
oTableStagingState,
|
|
145
|
+
oObjectPageModel,
|
|
146
|
+
_validateFieldsAndUpdateConfirmState,
|
|
147
|
+
true // bReuseStaging — also skips re-cleanup (already done above)
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
_validateFieldsAndUpdateConfirmState();
|
|
101
151
|
},
|
|
152
|
+
|
|
153
|
+
// ── Feedback thumbs-up/down ───────────────────────────────────────
|
|
102
154
|
onFeedbackPress: function() {
|
|
103
|
-
|
|
104
|
-
easyFillDialogModel.setProperty("/feedbackEnabled",false);
|
|
105
|
-
MessageToast.show(oRb.getText("EASYFILL_RESULT_FEEDBACK"));
|
|
155
|
+
EasyFillDialogController.onFeedbackPress(oController, oRb);
|
|
106
156
|
},
|
|
157
|
+
|
|
158
|
+
// ── Section title formatter (resolves i18n patterns) ──────────────
|
|
107
159
|
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;
|
|
160
|
+
return EasyFillDialogController.formatSectionTitle(sTitle, oController);
|
|
118
161
|
},
|
|
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
|
-
}
|
|
162
|
+
|
|
163
|
+
// ── IllustratedMessage state formatters ───────────────────────────
|
|
164
|
+
formatIllustrationType: function(sState) {
|
|
165
|
+
return EasyFillDialogController.formatIllustrationType(sState);
|
|
130
166
|
},
|
|
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
|
-
}
|
|
167
|
+
formatIllustrationTitle: function(sState) {
|
|
168
|
+
return EasyFillDialogController.formatIllustrationTitle(sState, oRb);
|
|
142
169
|
},
|
|
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
|
-
}
|
|
170
|
+
formatIllustrationDescription: function(sState) {
|
|
171
|
+
return EasyFillDialogController.formatIllustrationDescription(sState, oRb);
|
|
154
172
|
},
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
173
|
+
|
|
174
|
+
// ── Confirm: apply AI values to the live OP context ───────────────
|
|
175
|
+
// The Confirm button is disabled reactively by _validateFieldsAndUpdateConfirmState
|
|
176
|
+
// whenever a SmartField value is invalid — no click-time check needed.
|
|
177
|
+
onSaveFromEasyFillDialog: async function() {
|
|
178
|
+
// Capture staged collection updates BEFORE applyAndSave writes them to the live
|
|
179
|
+
// contexts — after applyAndSave the staged values equal the live values and the map would be empty.
|
|
180
|
+
var mStagedUpdates = EasyFillApplyHandler.buildStagedCollectionUpdates(oTableStagingState, mCollectionState);
|
|
181
|
+
|
|
182
|
+
// Extract reviewed new-row values from staging contexts BEFORE reset discards them.
|
|
183
|
+
var mNewRowValues = EasyFillApplyHandler.extractPendingNewRowValues(oTableStagingState, mCollectionState);
|
|
184
|
+
|
|
185
|
+
EasyFillApplyHandler.applyAndSave(
|
|
186
|
+
oTransientContextForEasyFill,
|
|
187
|
+
oObjectPageContext,
|
|
188
|
+
mUpdatableLookupMap,
|
|
189
|
+
oTableStagingState,
|
|
190
|
+
mCollectionState
|
|
191
|
+
);
|
|
192
|
+
// Trigger side effects on the OP context for all changed SmartForm fields
|
|
193
|
+
// before resetting the dialog (SmartFields must still be present)
|
|
194
|
+
_activateSideEffects();
|
|
195
|
+
// Trigger side effects for all modified table rows
|
|
196
|
+
_activateSideEffectsForTable(mStagedUpdates);
|
|
197
|
+
_resetEasyFill(true);
|
|
198
|
+
EasyFillDialogController.closeDialog(oController);
|
|
199
|
+
|
|
200
|
+
// Create the new rows via the standard CRUDManager (draft-aware, handles side effects)
|
|
201
|
+
// This is done after dialog close so the user sees the OP immediately.
|
|
202
|
+
await _createNewRows(mNewRowValues);
|
|
169
203
|
},
|
|
204
|
+
|
|
205
|
+
// ── Text area live change: enable/disable submit button ───────────
|
|
170
206
|
onLiveChange: function(oEvent) {
|
|
171
|
-
|
|
172
|
-
easyFillDialogModel.setProperty("/isEasyFillButtonEnabled",!!oEvent.getSource().getValue());
|
|
207
|
+
EasyFillDialogController.onLiveChange(oEvent, oController);
|
|
173
208
|
},
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
209
|
+
|
|
210
|
+
// ── Cancel: revert staged values and close dialog ─────────────────
|
|
211
|
+
onCancelEasyFillDialog: function() {
|
|
212
|
+
_resetEasyFill(true);
|
|
213
|
+
EasyFillDialogController.closeDialog(oController);
|
|
177
214
|
},
|
|
178
|
-
|
|
179
|
-
|
|
215
|
+
|
|
216
|
+
// ── Field value formatter (binding expression builder) ────────────
|
|
217
|
+
formatFieldValue: function(sFieldName) {
|
|
218
|
+
return "{" + sFieldName + "}";
|
|
180
219
|
},
|
|
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
|
-
|
|
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);
|
|
217
220
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
} else {
|
|
222
|
-
applyTableValidationState([]);
|
|
223
|
-
easyFillDialogModel.setProperty("/stateType","Error");
|
|
224
|
-
}
|
|
221
|
+
// ── Submit user text to AI ────────────────────────────────────────
|
|
222
|
+
onEasyFillSubmitInputToAI: async function() {
|
|
223
|
+
await _handleAISubmit();
|
|
225
224
|
},
|
|
226
225
|
|
|
226
|
+
// ── Clear all (reset without closing) ────────────────────────────
|
|
227
227
|
onEasyFillClearAll: function() {
|
|
228
|
-
|
|
228
|
+
_resetEasyFill(true);
|
|
229
229
|
},
|
|
230
230
|
|
|
231
|
+
// ── SmartForm group population formatter ──────────────────────────
|
|
231
232
|
populateSmartFields: function(sId, aFields, sEasyFillMode) {
|
|
232
|
-
|
|
233
|
-
|
|
233
|
+
return EasyFillFormRenderer.populateSmartFields(
|
|
234
|
+
sId, aFields, sEasyFillMode,
|
|
235
|
+
oController,
|
|
236
|
+
oTransientContextForEasyFill,
|
|
237
|
+
oObjectPageContext,
|
|
238
|
+
mUpdatableLookupMap,
|
|
239
|
+
oDialogModel().getProperty("/llmData"),
|
|
240
|
+
_validateFieldsAndUpdateConfirmState
|
|
241
|
+
);
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
// ── Section visibility: hide SmartForm sections with no fields ──────────────
|
|
245
|
+
hasSectionFields: function(aFields, aChildren) {
|
|
246
|
+
if (Array.isArray(aFields) && aFields.length > 0) {
|
|
234
247
|
return true;
|
|
235
248
|
}
|
|
236
249
|
|
|
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;
|
|
250
|
+
function hasFieldsInNode(oNode) {
|
|
251
|
+
if (!oNode) {
|
|
252
|
+
return false;
|
|
266
253
|
}
|
|
254
|
+
if (Array.isArray(oNode.fields) && oNode.fields.length > 0) {
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
if (Array.isArray(oNode.children)) {
|
|
258
|
+
return oNode.children.some(hasFieldsInNode);
|
|
259
|
+
}
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
267
262
|
|
|
268
|
-
|
|
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;
|
|
263
|
+
return Array.isArray(aChildren) && aChildren.some(hasFieldsInNode);
|
|
340
264
|
},
|
|
341
265
|
|
|
266
|
+
// ── Fullscreen toggle ─────────────────────────────────────────────
|
|
342
267
|
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
|
-
}
|
|
268
|
+
EasyFillDialogController.onToggleFullScreen(oController);
|
|
354
269
|
},
|
|
355
270
|
|
|
356
|
-
|
|
271
|
+
// ── AI Notice popover ─────────────────────────────────────────────
|
|
357
272
|
onLinkPress: async function(oEvent) {
|
|
358
|
-
|
|
359
|
-
oAIPopover = await getAIPopover();
|
|
360
|
-
}
|
|
361
|
-
oAIPopover.openBy(oEvent.getSource());
|
|
273
|
+
await EasyFillDialogController.onLinkPress(oEvent, oTemplateUtils, oAIPopoverRef);
|
|
362
274
|
}
|
|
363
|
-
|
|
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"];
|
|
275
|
+
};
|
|
445
276
|
}
|
|
446
277
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
278
|
+
/**
|
|
279
|
+
* Handles the full AI submission flow:
|
|
280
|
+
* 1. Build field mapping
|
|
281
|
+
* 2. Call AI service
|
|
282
|
+
* 3. Post-process response (dates, split)
|
|
283
|
+
* 4. Update dialog model
|
|
284
|
+
* 5. Populate SmartForms and table previews
|
|
285
|
+
*/
|
|
286
|
+
async function _handleAISubmit() {
|
|
287
|
+
var oModel = oDialogModel();
|
|
288
|
+
oModel.setProperty("/isBusy", true);
|
|
289
|
+
|
|
290
|
+
// Build complete LLM field mapping (entity fields + collection fields)
|
|
291
|
+
var mFieldMapping = EasyFillAIOrchestrator.buildFieldMapping(
|
|
292
|
+
oTemplateUtils, oController, oObjectPage, oObjectPageModel,
|
|
293
|
+
oObjectPageContext, EasyFillFieldCollector, mCollectionState
|
|
294
|
+
);
|
|
457
295
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
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])
|
|
296
|
+
// Submit to AI
|
|
297
|
+
var aiCallResult = await EasyFillAIOrchestrator.submitToAI(
|
|
298
|
+
oController.byId("EasyFillInputTextArea").getValue(),
|
|
299
|
+
mFieldMapping,
|
|
300
|
+
oTemplateUtils
|
|
487
301
|
);
|
|
488
|
-
}
|
|
489
302
|
|
|
490
|
-
|
|
491
|
-
|
|
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",[]);
|
|
303
|
+
// Reset editable fields tracker for fresh population
|
|
304
|
+
oModel.setProperty("/isBusy", false);
|
|
517
305
|
|
|
518
|
-
|
|
306
|
+
// Convert date strings in AI response to JS Date objects
|
|
307
|
+
EasyFillAIOrchestrator.formatDateValues(aiCallResult, mFieldMapping);
|
|
519
308
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
const updatableFields = {};
|
|
523
|
-
const nonUpdatableFields = {};
|
|
309
|
+
var sSelectedKey = oModel.getProperty("/easyFillMode") || EasyFillLayoutMode.CondensedMode;
|
|
310
|
+
oModel.setProperty("/easyFillMode", sSelectedKey);
|
|
524
311
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
312
|
+
if (aiCallResult.success) {
|
|
313
|
+
// Split AI response into simple fields (SmartForm) and collection rows (SmartTable)
|
|
314
|
+
// Pass mCollectionState so the splitter can check which tables allow creation
|
|
315
|
+
var oSplitResult = EasyFillAIOrchestrator.splitAIResultByCollection(
|
|
316
|
+
aiCallResult.data, mFieldMapping, EasyFillFieldCollector, oObjectPage, mCollectionState
|
|
317
|
+
);
|
|
318
|
+
var simpleFields = oSplitResult.simpleFields;
|
|
319
|
+
var collectionFields = oSplitResult.collectionFields;
|
|
320
|
+
|
|
321
|
+
// Separate updatable from non-updatable fields
|
|
322
|
+
var sEntitySetName = oController.getOwnerComponent().getEntitySet();
|
|
323
|
+
var oEntityType = oTemplateUtils.oCommonUtils.getMetaModelEntityType(sEntitySetName);
|
|
324
|
+
var oMetaModel = oObjectPageModel.getMetaModel();
|
|
325
|
+
var oEntitySet = oMetaModel.getODataEntitySet(sEntitySetName);
|
|
326
|
+
var oSplit = EasyFillFieldCollector.getUpdatableAndNonUpdatableFields(
|
|
327
|
+
simpleFields, oEntityType, oObjectPageContext, oEntitySet
|
|
328
|
+
);
|
|
329
|
+
mUpdatableLookupMap = oSplit.updatableFields;
|
|
330
|
+
var mNonUpdatableFields = oSplit.nonUpdatableFields;
|
|
331
|
+
|
|
332
|
+
// Build section tree for selected view mode
|
|
333
|
+
var aData = (sSelectedKey === EasyFillLayoutMode.CondensedMode)
|
|
334
|
+
? EasyFillFieldCollector.getSectionsForCondensedView(simpleFields, mUpdatableLookupMap, mNonUpdatableFields)
|
|
335
|
+
: EasyFillFieldCollector.getSectionsForObjectPageView(oEntityType, simpleFields, oRb, mNonUpdatableFields);
|
|
336
|
+
|
|
337
|
+
// Reset form (destroy previous SmartForm content + revert table staging)
|
|
338
|
+
// then create a fresh transient context for the new AI result.
|
|
339
|
+
// Restore the user's chosen layout mode afterwards because _resetEasyFill
|
|
340
|
+
// unconditionally resets it to CondensedMode — without this, renderCollectionPreviews
|
|
341
|
+
// would see the wrong mode and embed table sections inside the SmartForm SubSection
|
|
342
|
+
// instead of adding them as standalone OPL sections (OP mode) or vice-versa.
|
|
343
|
+
_resetEasyFill();
|
|
344
|
+
oModel.setProperty("/easyFillMode", sSelectedKey);
|
|
345
|
+
oTransientContextForEasyFill = EasyFillFormRenderer.makeTransientContext(
|
|
346
|
+
null, oObjectPageModel, oController
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
// Update dialog model with section data and AI result.
|
|
350
|
+
// llmData must be set BEFORE sections so that when the sections binding fires
|
|
351
|
+
// and triggers the populateSmartFields formatter synchronously, the AI-suggested
|
|
352
|
+
// values are already available and can be written to the transient context.
|
|
353
|
+
oModel.setProperty("/llmData", simpleFields);
|
|
354
|
+
oModel.setProperty("/updatableFields", mUpdatableLookupMap);
|
|
355
|
+
oModel.setProperty("/nonUpdatableFields", mNonUpdatableFields);
|
|
356
|
+
oModel.setProperty("/sections", aData);
|
|
357
|
+
oModel.setProperty("/hasNonUpdatableFields", Object.keys(mNonUpdatableFields).length > 0);
|
|
358
|
+
|
|
359
|
+
// Show warning if AI attempted to add multiple new rows but only one was accepted
|
|
360
|
+
oModel.setProperty("/attemptedMultipleNewRows", oSplitResult.attemptedMultipleNewRows === true);
|
|
361
|
+
|
|
362
|
+
// Process collection/table updates
|
|
363
|
+
mCollectionUpdates = collectionFields;
|
|
364
|
+
mLastFieldMapping = mFieldMapping;
|
|
365
|
+
var mRejectedNewRows = oSplitResult.collectionsWithRejectedNewRows || {};
|
|
366
|
+
// hasTableUpdates is true when there are accepted updates OR rejected-only tables to show
|
|
367
|
+
var bHasTableUpdates = Object.keys(mCollectionUpdates).length > 0 ||
|
|
368
|
+
Object.keys(mRejectedNewRows).length > 0;
|
|
369
|
+
oModel.setProperty("/hasTableUpdates", bHasTableUpdates);
|
|
370
|
+
|
|
371
|
+
// Render collection previews into the table preview container
|
|
372
|
+
EasyFillTableRenderer.renderCollectionPreviews(
|
|
373
|
+
mCollectionUpdates,
|
|
374
|
+
mFieldMapping,
|
|
375
|
+
[],
|
|
376
|
+
oController,
|
|
377
|
+
oRb,
|
|
378
|
+
mCollectionState,
|
|
379
|
+
oTableStagingState,
|
|
380
|
+
oObjectPageModel,
|
|
381
|
+
_validateFieldsAndUpdateConfirmState,
|
|
382
|
+
false,
|
|
383
|
+
mRejectedNewRows
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
_validateFieldsAndUpdateConfirmState();
|
|
387
|
+
|
|
388
|
+
var iTotalFields = Object.keys(oSplit.updatableFields).length + Object.keys(oSplit.nonUpdatableFields).length;
|
|
389
|
+
if (iTotalFields === 0 && !bHasTableUpdates) {
|
|
390
|
+
oModel.setProperty("/stateType", "NoEntries");
|
|
529
391
|
return;
|
|
530
392
|
}
|
|
531
|
-
if (isEditable(oProperty)) {
|
|
532
|
-
updatableFields[sKey] = oAiCallResult[sKey];
|
|
533
|
-
} else {
|
|
534
|
-
nonUpdatableFields[sKey] = oAiCallResult[sKey];
|
|
535
|
-
}
|
|
536
393
|
|
|
537
|
-
|
|
538
|
-
|
|
394
|
+
oModel.setProperty("/isIllustrationVisible", false);
|
|
395
|
+
oModel.setProperty("/isFormVisible", iTotalFields > 0 || bHasTableUpdates);
|
|
396
|
+
} else {
|
|
397
|
+
// AI call failed — show error illustration
|
|
398
|
+
oModel.setProperty("/stateType", "Error");
|
|
399
|
+
}
|
|
539
400
|
}
|
|
540
401
|
|
|
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
402
|
/**
|
|
548
|
-
*
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
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;
|
|
403
|
+
* Runs SmartForm.check() on every SmartForm in the EasyFill ObjectPageLayout.
|
|
404
|
+
* Returns true if all SmartForms pass validation (or none exist yet).
|
|
405
|
+
* As a side effect, check() visually marks invalid fields in the dialog.
|
|
406
|
+
*
|
|
407
|
+
* SmartForm.check() returns either:
|
|
408
|
+
* - string[] (synchronous validationMode) — field IDs with errors
|
|
409
|
+
* - Promise<Object[]> (asynchronous validationMode) — resolves with field objects with errors
|
|
410
|
+
* Both are normalised via Promise.resolve() so the caller can always await the result.
|
|
411
|
+
*
|
|
412
|
+
* @returns {Promise<boolean>} resolves to true when all SmartForms have zero errors
|
|
413
|
+
*/
|
|
414
|
+
async function _checkSmartFormsValidity() {
|
|
415
|
+
var oObjectPageLayout = oController.byId("EasyFillObjectPageLayout");
|
|
416
|
+
if (!oObjectPageLayout) {
|
|
417
|
+
return true;
|
|
590
418
|
}
|
|
591
419
|
|
|
592
|
-
//
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
420
|
+
// Collect one Promise<errorArray> per SmartForm found in the layout
|
|
421
|
+
var aCheckPromises = [];
|
|
422
|
+
oObjectPageLayout.getSections().forEach(function(oSection) {
|
|
423
|
+
oSection.getSubSections().forEach(function(oSubSection) {
|
|
424
|
+
oSubSection.getBlocks().forEach(function(oBlock) {
|
|
425
|
+
var aSmartFormCandidates = [];
|
|
426
|
+
if (oBlock.isA("sap.ui.comp.smartform.SmartForm")) {
|
|
427
|
+
aSmartFormCandidates.push(oBlock);
|
|
428
|
+
} else if (typeof oBlock.getItems === "function") {
|
|
429
|
+
// In condensed mode the SmartForm is nested inside a wrapper VBox;
|
|
430
|
+
// look one level deeper to find it (mirrors EasyFillFormRenderer).
|
|
431
|
+
oBlock.getItems().forEach(function(oItem) {
|
|
432
|
+
if (oItem.isA("sap.ui.comp.smartform.SmartForm")) {
|
|
433
|
+
aSmartFormCandidates.push(oItem);
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
aSmartFormCandidates.forEach(function(oSmartForm) {
|
|
438
|
+
// .catch(() => []) absorbs internal SmartField metadata-promise rejections
|
|
439
|
+
// that fire when SmartFields are destroyed during a mode switch.
|
|
440
|
+
aCheckPromises.push(
|
|
441
|
+
Promise.resolve(oSmartForm.check()).catch(function() { return []; })
|
|
442
|
+
);
|
|
443
|
+
});
|
|
613
444
|
});
|
|
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
|
-
}
|
|
445
|
+
});
|
|
624
446
|
});
|
|
625
|
-
return oSplitResult;
|
|
626
|
-
}
|
|
627
447
|
|
|
628
|
-
|
|
629
|
-
var
|
|
630
|
-
|
|
631
|
-
return
|
|
448
|
+
// Resolve all checks in parallel; valid only when every SmartForm has no errors
|
|
449
|
+
var aResults = await Promise.all(aCheckPromises);
|
|
450
|
+
return aResults.every(function(aErrors) {
|
|
451
|
+
return Array.isArray(aErrors) && aErrors.length === 0;
|
|
632
452
|
});
|
|
633
|
-
return {
|
|
634
|
-
isValid: !!oSmartTable,
|
|
635
|
-
smartTable: oSmartTable
|
|
636
|
-
};
|
|
637
453
|
}
|
|
638
454
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
455
|
+
/**
|
|
456
|
+
* Re-computes isSaveEnabled from the combined SmartForm and table SmartField validity
|
|
457
|
+
* flag. Called as the single source of truth for the Confirm button state.
|
|
458
|
+
*
|
|
459
|
+
* @param {boolean} bAllFieldsValid - true when both SmartForm and table SmartField checks pass
|
|
460
|
+
*/
|
|
461
|
+
function _computeSaveEnabled(bAllFieldsValid) {
|
|
462
|
+
var bHasScalarChanges = Object.keys(mUpdatableLookupMap || {}).length > 0;
|
|
463
|
+
var bHasTableChanges = EasyFillApplyHandler.hasStagedCollectionChanges(oTableStagingState, mCollectionState);
|
|
464
|
+
var bHasChanges = bHasScalarChanges || bHasTableChanges;
|
|
465
|
+
oDialogModel().setProperty("/isSaveEnabled", bAllFieldsValid && bHasChanges);
|
|
650
466
|
}
|
|
651
467
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
468
|
+
/**
|
|
469
|
+
* Runs SmartField.checkValuesValidity() on every editable table SmartField.
|
|
470
|
+
* Returns true if all fields pass (or no table exists yet).
|
|
471
|
+
*
|
|
472
|
+
* checkValuesValidity() returns a Promise<boolean> (true = valid).
|
|
473
|
+
*
|
|
474
|
+
* @returns {Promise<boolean>} resolves to true when all table SmartFields are valid
|
|
475
|
+
*/
|
|
476
|
+
async function _checkTableSmartFieldsValidity() {
|
|
477
|
+
var sMarker = "easyfillTableSection";
|
|
478
|
+
var oObjectPageLayout = oController.byId("EasyFillObjectPageLayout");
|
|
479
|
+
if (!oObjectPageLayout) {
|
|
480
|
+
return true;
|
|
660
481
|
}
|
|
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
482
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
483
|
+
var aCheckPromises = [];
|
|
484
|
+
|
|
485
|
+
function scanTableForSmartFields(oTable) {
|
|
486
|
+
if (!oTable.isA("sap.m.Table") || !oTable.getVisible()) { return; }
|
|
487
|
+
oTable.getItems().forEach(function(oListItem) {
|
|
488
|
+
if (!oListItem.isA("sap.m.ColumnListItem")) { return; }
|
|
489
|
+
oListItem.getCells().forEach(function(oCell) {
|
|
490
|
+
if (!oCell.isA("sap.ui.comp.smartfield.SmartField") || !oCell.getEditable()) { return; }
|
|
491
|
+
var bValueStateOk = oCell.getValueState() !== "Error";
|
|
492
|
+
aCheckPromises.push(
|
|
493
|
+
Promise.resolve(oCell.checkValuesValidity()).then(function(bValid) {
|
|
494
|
+
return bValueStateOk && bValid !== false;
|
|
495
|
+
})
|
|
496
|
+
);
|
|
497
|
+
});
|
|
697
498
|
});
|
|
499
|
+
}
|
|
698
500
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
501
|
+
function scanVBox(oVBox) {
|
|
502
|
+
oVBox.getItems().forEach(function(oItem) { scanTableForSmartFields(oItem); });
|
|
503
|
+
}
|
|
702
504
|
|
|
703
|
-
|
|
704
|
-
var
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
var vValue = oContext.getProperty(sPropName);
|
|
720
|
-
if (typeof vValue !== "object") {
|
|
721
|
-
oRowData[sPropName] = vValue;
|
|
505
|
+
oObjectPageLayout.getSections().forEach(function(oSection) {
|
|
506
|
+
var bIsTableSection = oSection.getCustomData().some(function(oData) {
|
|
507
|
+
return oData.getKey() === sMarker;
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
oSection.getSubSections().forEach(function(oSubSection) {
|
|
511
|
+
oSubSection.getBlocks().forEach(function(oBlock) {
|
|
512
|
+
if (!oBlock.isA("sap.m.VBox")) { return; }
|
|
513
|
+
var bIsMarkedBlock = oBlock.getCustomData().some(function(oData) {
|
|
514
|
+
return oData.getKey() === sMarker;
|
|
515
|
+
});
|
|
516
|
+
// Scan VBoxes that are either:
|
|
517
|
+
// (a) directly marked (condensed mode: injected into Filled Fields SubSection)
|
|
518
|
+
// (b) inside a marked standalone section (OP mode)
|
|
519
|
+
if (bIsMarkedBlock || bIsTableSection) {
|
|
520
|
+
scanVBox(oBlock);
|
|
722
521
|
}
|
|
723
522
|
});
|
|
724
|
-
aCurrentItems.push(oRowData);
|
|
725
523
|
});
|
|
726
|
-
|
|
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
524
|
});
|
|
741
|
-
}
|
|
742
525
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
return null;
|
|
746
|
-
}
|
|
747
|
-
var aParts = sBindingPath.split("/").filter(Boolean);
|
|
748
|
-
return aParts.length ? aParts[aParts.length - 1] : null;
|
|
526
|
+
var aResults = await Promise.all(aCheckPromises);
|
|
527
|
+
return aResults.every(function(bValid) { return bValid === true; });
|
|
749
528
|
}
|
|
750
529
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
return null;
|
|
766
|
-
}
|
|
767
|
-
|
|
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
|
-
});
|
|
530
|
+
/**
|
|
531
|
+
* Called after every SmartField change event (both form and table) and once after each
|
|
532
|
+
* group/table is populated. Validates all SmartForms and all table SmartFields and
|
|
533
|
+
* updates the Confirm button enabled state.
|
|
534
|
+
*/
|
|
535
|
+
function _validateFieldsAndUpdateConfirmState() {
|
|
536
|
+
setTimeout(function() {
|
|
537
|
+
Promise.all([
|
|
538
|
+
_checkSmartFormsValidity(),
|
|
539
|
+
_checkTableSmartFieldsValidity()
|
|
540
|
+
]).then(function(aResults) {
|
|
541
|
+
_computeSaveEnabled(aResults[0] && aResults[1]);
|
|
542
|
+
}).catch(Function.prototype);
|
|
543
|
+
}, 0);
|
|
786
544
|
}
|
|
787
545
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
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
|
-
}
|
|
869
|
-
}
|
|
546
|
+
/**
|
|
547
|
+
* Resets the EasyFill dialog to its initial state.
|
|
548
|
+
* Delegates to EasyFillApplyHandler.resetEasyFill with the current state references.
|
|
549
|
+
*
|
|
550
|
+
* @param {boolean} [bRemoveTextAreaValue=false] - if true, also clears the text area input
|
|
551
|
+
*/
|
|
552
|
+
function _resetEasyFill(bRemoveTextAreaValue) {
|
|
553
|
+
mLastFieldMapping = null;
|
|
554
|
+
EasyFillApplyHandler.resetEasyFill(
|
|
555
|
+
!!bRemoveTextAreaValue,
|
|
556
|
+
oDialogModel(),
|
|
557
|
+
oController,
|
|
558
|
+
mCollectionUpdates,
|
|
559
|
+
EasyFillLayoutMode,
|
|
560
|
+
function() {
|
|
561
|
+
EasyFillTableRenderer.resetStagedCollectionContexts(oTableStagingState, oObjectPageModel);
|
|
562
|
+
},
|
|
563
|
+
function() {
|
|
564
|
+
EasyFillFormRenderer.resetTransientContext(oTransientContextForEasyFill, oObjectPageModel);
|
|
565
|
+
oTransientContextForEasyFill = null;
|
|
870
566
|
}
|
|
871
|
-
|
|
872
|
-
return aValidationErrors;
|
|
567
|
+
);
|
|
873
568
|
}
|
|
874
569
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
570
|
+
/**
|
|
571
|
+
* Activates side effects for all editable SmartFields in the EasyFill SmartForm.
|
|
572
|
+
* Called after applyAndSave so that side effects are triggered on the original
|
|
573
|
+
* ObjectPage context (not the transient context used while the dialog was open).
|
|
574
|
+
*
|
|
575
|
+
* Collects field group IDs from the inner controls of every editable SmartField,
|
|
576
|
+
* updates the view's CustomData for each field group to reference the OP context,
|
|
577
|
+
* then registers the group change and triggers field-group validation on the view.
|
|
578
|
+
*/
|
|
579
|
+
function _activateSideEffects() {
|
|
580
|
+
var oObjectPageLayout = oController.byId("EasyFillObjectPageLayout");
|
|
581
|
+
if (!oObjectPageLayout) {
|
|
582
|
+
return;
|
|
881
583
|
}
|
|
882
|
-
return String(vValue);
|
|
883
|
-
}
|
|
884
|
-
|
|
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
584
|
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
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);
|
|
585
|
+
// Collect all editable SmartFields from the EasyFill dialog SmartForms
|
|
586
|
+
var aEditableFields = [];
|
|
587
|
+
oObjectPageLayout.getSections().forEach(function(oSection) {
|
|
588
|
+
oSection.getSubSections().forEach(function(oSubSection) {
|
|
589
|
+
oSubSection.getBlocks().forEach(function(oBlock) {
|
|
590
|
+
if (oBlock.isA("sap.ui.comp.smartform.SmartForm")) {
|
|
591
|
+
oBlock.getGroups().forEach(function(oGroup) {
|
|
592
|
+
oGroup.getGroupElements().forEach(function(oGroupElement) {
|
|
593
|
+
oGroupElement.getElements().forEach(function(oElement) {
|
|
594
|
+
if (oElement.isA && oElement.isA("sap.ui.comp.smartfield.SmartField") && oElement.getEditable()) {
|
|
595
|
+
aEditableFields.push(oElement);
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
});
|
|
599
|
+
});
|
|
932
600
|
}
|
|
933
|
-
oContext.setProperty(sPropName, oRowUpdate[sPropName]);
|
|
934
601
|
});
|
|
935
602
|
});
|
|
936
603
|
});
|
|
937
|
-
}
|
|
938
604
|
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
605
|
+
// Making a Set so that duplicate field group IDs are not passed
|
|
606
|
+
var aFieldGroupIdSet = new Set();
|
|
607
|
+
aEditableFields.forEach(function(oSmartField) {
|
|
608
|
+
// SideEffect would be invoked only for the editable controls inside the SmartField
|
|
609
|
+
var aEditableInnerFields = oSmartField.getInnerControls().filter(function(oControl) {
|
|
610
|
+
return oControl.getEditable();
|
|
611
|
+
});
|
|
612
|
+
aEditableInnerFields.forEach(function(oControl) {
|
|
613
|
+
var aControlFieldGroupId = oControl.getFieldGroupIds();
|
|
614
|
+
if (aControlFieldGroupId.length > 0) {
|
|
615
|
+
aControlFieldGroupId.forEach(function(sFieldGroupId) {
|
|
616
|
+
aFieldGroupIdSet.add(sFieldGroupId);
|
|
617
|
+
});
|
|
618
|
+
}
|
|
945
619
|
});
|
|
946
620
|
});
|
|
947
|
-
mStagedRowValues = Object.create(null);
|
|
948
|
-
}
|
|
949
621
|
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
622
|
+
var oView = oController.getView();
|
|
623
|
+
|
|
624
|
+
aFieldGroupIdSet.forEach(function(sFieldGroupId) {
|
|
625
|
+
var oID = oView.data(sFieldGroupId);
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Currently, the SmartFields are created using the TransientContext, which means both
|
|
629
|
+
* "context" and "contextObject" refer to the TransientContext instance.
|
|
630
|
+
*
|
|
631
|
+
* However, we need to use the original OP context so that side effects are triggered
|
|
632
|
+
* on the original OP context when the user saves data from the EasyFill dialog back to OP.
|
|
633
|
+
*
|
|
634
|
+
* Therefore, we update the "context" and "contextObject" to reference the original OP context.
|
|
635
|
+
*/
|
|
636
|
+
if (oID) {
|
|
637
|
+
oID["context"] = oObjectPageContext.getPath();
|
|
638
|
+
oID["contextObject"] = oObjectPageContext;
|
|
639
|
+
oView.data(sFieldGroupId, oID);
|
|
640
|
+
|
|
641
|
+
// To make sure that all the fields side effect would be triggered
|
|
642
|
+
var sSideEffectQualifier = oTemplateUtils.oServices.oApplicationController._getSideEffectsQualifier(oID.name);
|
|
643
|
+
oTemplateUtils.oServices.oApplicationController.registerGroupChange(sSideEffectQualifier);
|
|
963
644
|
}
|
|
964
|
-
oPreviewRow[sPropertyName] = oContext ? oContext.getProperty(sPropertyName) : "";
|
|
965
645
|
});
|
|
966
|
-
|
|
646
|
+
|
|
647
|
+
oView.triggerValidateFieldGroup([...aFieldGroupIdSet]);
|
|
967
648
|
}
|
|
968
649
|
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
650
|
+
/**
|
|
651
|
+
* Activates side effects for all modified table (collection) rows after applyAndSave.
|
|
652
|
+
* For each collection row that had AI-suggested value changes applied, triggers
|
|
653
|
+
* executeSideEffects on the real row context with the list of changed property names.
|
|
654
|
+
*
|
|
655
|
+
* Must be called with mStagedUpdates captured BEFORE applyAndSave, because after
|
|
656
|
+
* applyAndSave the staged values equal the live values so buildStagedCollectionUpdates
|
|
657
|
+
* would return an empty map.
|
|
658
|
+
*
|
|
659
|
+
* @param {object} mStagedUpdates - pre-computed collection updates: { navProperty: [ rowUpdate ] }
|
|
660
|
+
*/
|
|
661
|
+
function _activateSideEffectsForTable(mStagedUpdates) {
|
|
662
|
+
if (!mStagedUpdates || !Object.keys(mStagedUpdates).length) {
|
|
663
|
+
return;
|
|
978
664
|
}
|
|
979
|
-
return "row:" + String(iFallbackIndex);
|
|
980
|
-
}
|
|
981
665
|
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
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]) });
|
|
666
|
+
Object.keys(mStagedUpdates).forEach(function(sCollectionName) {
|
|
667
|
+
var aRowUpdates = mStagedUpdates[sCollectionName] || [];
|
|
668
|
+
aRowUpdates.forEach(function(oRowUpdate) {
|
|
669
|
+
var oResolvedRow = EasyFillApplyHandler._resolveCollectionRowContext(sCollectionName, oRowUpdate, mCollectionState);
|
|
670
|
+
if (!oResolvedRow || !oResolvedRow.context) {
|
|
671
|
+
return;
|
|
995
672
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
var
|
|
999
|
-
|
|
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
|
-
}
|
|
673
|
+
|
|
674
|
+
// Collect the names of changed properties (exclude internal metadata fields)
|
|
675
|
+
var aChangedProperties = Object.keys(oRowUpdate).filter(function(sKey) {
|
|
676
|
+
return sKey !== "_rowIndex" && sKey !== "_contextPath";
|
|
1010
677
|
});
|
|
1011
678
|
|
|
1012
|
-
if (
|
|
1013
|
-
|
|
679
|
+
if (!aChangedProperties.length) {
|
|
680
|
+
return;
|
|
1014
681
|
}
|
|
1015
|
-
|
|
682
|
+
|
|
683
|
+
oTemplateUtils.oServices.oApplicationController.executeSideEffects(
|
|
684
|
+
oResolvedRow.context,
|
|
685
|
+
aChangedProperties
|
|
686
|
+
);
|
|
1016
687
|
});
|
|
1017
|
-
var oItem = new ColumnListItem({ cells: aCells });
|
|
1018
|
-
if (oContext) {
|
|
1019
|
-
oItem.setBindingContext(oContext);
|
|
1020
|
-
}
|
|
1021
|
-
return oItem;
|
|
1022
688
|
});
|
|
1023
|
-
return new Table({
|
|
1024
|
-
columns: aColumns,
|
|
1025
|
-
items: aItems,
|
|
1026
|
-
fixedLayout: false,
|
|
1027
|
-
autoPopinMode: true
|
|
1028
|
-
}).addStyleClass("sapSmartTemplatesObjectPageEasyFillTablePreview");
|
|
1029
689
|
}
|
|
1030
690
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
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
|
-
});
|
|
1111
|
-
|
|
1112
|
-
var oHeader = new HBox({
|
|
1113
|
-
justifyContent: "SpaceBetween",
|
|
1114
|
-
items: [oSectionTitle, oSegmentedButton]
|
|
1115
|
-
}).addStyleClass("sapUiSmallMarginTop");
|
|
1116
|
-
|
|
1117
|
-
var oSection = new VBox({
|
|
1118
|
-
items: [oHeader, oPreviousTable, oNewTable]
|
|
1119
|
-
}).addStyleClass("sapSmartTemplatesObjectPageEasyFillTableSection");
|
|
691
|
+
/**
|
|
692
|
+
* Convenience accessor for the easyFillDialogModel JSONModel.
|
|
693
|
+
* @returns {sap.ui.model.json.JSONModel}
|
|
694
|
+
*/
|
|
695
|
+
function oDialogModel() {
|
|
696
|
+
return oController.getView().getModel("easyFillDialogModel");
|
|
697
|
+
}
|
|
1120
698
|
|
|
1121
|
-
|
|
699
|
+
function _findSmartTableForNavProperty(aSmartTables, sNavProperty) {
|
|
700
|
+
return aSmartTables.find(function(oTable) {
|
|
701
|
+
var oHandler = oTemplateUtils.oServices.oPresentationControlHandlerFactory.getPresentationControlHandler(oTable);
|
|
702
|
+
if (!oHandler) { return false; }
|
|
703
|
+
var sPath = oHandler.getBindingPath && oHandler.getBindingPath();
|
|
704
|
+
return sPath && sPath.split("/").pop() === sNavProperty;
|
|
1122
705
|
});
|
|
1123
706
|
}
|
|
1124
707
|
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
708
|
+
/**
|
|
709
|
+
* Creates new table rows via CommonEventHandlers.addEntry (draft-aware) with navigation
|
|
710
|
+
* suppressed so the user stays on the ObjectPage.
|
|
711
|
+
* Using CommonEventHandlers.addEntry (bSuppressNavigation=true) instead of calling
|
|
712
|
+
* CRUDManager.addEntry directly ensures the SmartTable binding is refreshed and side
|
|
713
|
+
* effects are executed after the row is created — without that refresh the ObjectPage
|
|
714
|
+
* table would not show the newly created row.
|
|
715
|
+
*
|
|
716
|
+
* @param {object} mNewRowValues - map of { navProperty: plainValuesObject }
|
|
717
|
+
* @returns {Promise<void>}
|
|
718
|
+
*/
|
|
719
|
+
async function _createNewRows(mNewRowValues) {
|
|
720
|
+
if (!mNewRowValues || !Object.keys(mNewRowValues).length) {
|
|
721
|
+
return;
|
|
1133
722
|
}
|
|
723
|
+
var aSmartTables = EasyFillFieldCollector.getObjectPageSmartTables(oObjectPage);
|
|
724
|
+
var aNavProperties = Object.keys(mNewRowValues);
|
|
1134
725
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
726
|
+
for (var i = 0; i < aNavProperties.length; i++) {
|
|
727
|
+
var sNavProp = aNavProperties[i];
|
|
728
|
+
var oSmartTable = _findSmartTableForNavProperty(aSmartTables, sNavProp);
|
|
729
|
+
if (!oSmartTable) {
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
try {
|
|
733
|
+
// bSuppressNavigation=true: stay on the OP (don't navigate to the new sub-item).
|
|
734
|
+
// bIgnoreTableRefresh not passed (undefined/falsy): the SmartTable binding is
|
|
735
|
+
// refreshed and side effects are executed so the new row becomes visible.
|
|
736
|
+
await oTemplateUtils.oCommonEventHandlers.addEntry(oSmartTable, true, undefined, mNewRowValues[sNavProp]);
|
|
737
|
+
} catch (e) {
|
|
738
|
+
// addEntry handles its own error messaging via CommonEventHandlers/CRUDManager
|
|
1143
739
|
}
|
|
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
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
|
|
1166
|
-
function makeTransientContext() {
|
|
1167
|
-
if (oTransientContextForEasyFill) {
|
|
1168
|
-
resetTransientContext();
|
|
1169
740
|
}
|
|
1170
|
-
oTransientContextForEasyFill = oObjectPageModel.createEntry("/" + oController.getOwnerComponent().getEntitySet(), {
|
|
1171
|
-
"groupId" : "EasyFillFETransientChanges",
|
|
1172
|
-
"changeSetId":"EasyFillFETransientChanges"
|
|
1173
|
-
});
|
|
1174
741
|
}
|
|
1175
742
|
|
|
1176
|
-
//
|
|
1177
|
-
// function onSmartFieldChange() {
|
|
1178
|
-
// validateSmartForm();
|
|
1179
|
-
// }
|
|
1180
|
-
|
|
743
|
+
// ─── Public API ──────────────────────────────────────────────────────────
|
|
1181
744
|
|
|
1182
745
|
return {
|
|
746
|
+
/**
|
|
747
|
+
* Entry point called by the ObjectPage ControllerImplementation when the
|
|
748
|
+
* EasyFill button is pressed.
|
|
749
|
+
*
|
|
750
|
+
* If the ObjectPage is already in edit mode, the dialog opens immediately.
|
|
751
|
+
* If not, the page is transitioned to edit mode first and the resulting
|
|
752
|
+
* edit context is passed into the dialog.
|
|
753
|
+
*
|
|
754
|
+
* @param {sap.ui.base.Event} oEvent - the button click event
|
|
755
|
+
*/
|
|
1183
756
|
onEasyFillButtonClick: function(oEvent) {
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
const isEditable = oController.getView().getModel("ui").getProperty("/editable");
|
|
1187
|
-
if (isEditable) {
|
|
757
|
+
var bIsEditable = oController.getView().getModel("ui").getProperty("/editable");
|
|
758
|
+
if (bIsEditable) {
|
|
1188
759
|
openEasyFillDialog(oEvent);
|
|
1189
760
|
} else {
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
761
|
+
// Trigger the OP edit flow and wait for the new edit context before opening
|
|
762
|
+
oController._templateEventHandlers.onEditByEasyFill()
|
|
763
|
+
.then(function(oContext) {
|
|
764
|
+
oObjectPageContext = oContext;
|
|
765
|
+
openEasyFillDialog(oEvent, oContext);
|
|
766
|
+
})
|
|
767
|
+
.catch(Function.prototype);
|
|
1194
768
|
}
|
|
1195
769
|
}
|
|
1196
770
|
};
|
|
1197
771
|
}
|
|
1198
772
|
|
|
1199
773
|
return BaseObject.extend("sap.suite.ui.generic.template.lib.ai.EasyFill.EasyFillHandler", {
|
|
1200
|
-
|
|
774
|
+
/**
|
|
775
|
+
* Constructor — wires all helpers and exposes the public API via getMethods.
|
|
776
|
+
*
|
|
777
|
+
* @param {object} oState - shared template state (passed from ControllerImplementation)
|
|
778
|
+
* @param {object} oController - MVC controller instance
|
|
779
|
+
* @param {object} oTemplateUtils - FE template utilities (services, common utils, etc.)
|
|
780
|
+
* @param {object} oObjectPage - the ObjectPage control instance
|
|
781
|
+
*/
|
|
782
|
+
constructor: function(oState, oController, oTemplateUtils, oObjectPage) {
|
|
1201
783
|
extend(this, getMethods(oState, oController, oTemplateUtils, oObjectPage));
|
|
1202
784
|
}
|
|
1203
785
|
});
|
|
1204
|
-
|
|
1205
786
|
});
|