@configura/web-api 3.0.0-alpha.1 → 3.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CatalogueAPI.d.ts +68 -1
- package/dist/CatalogueAPI.js +184 -219
- package/dist/CfgMeasure.js +1 -2
- package/dist/CfgProduct.d.ts +22 -3
- package/dist/CfgProduct.js +172 -162
- package/dist/io/CfgIOManager.js +2 -11
- package/dist/io/CfgIOProdConfConnector.js +14 -25
- package/dist/io/CfgObservableStateManager.js +1 -1
- package/dist/productConfiguration/CfgFeature.d.ts +5 -0
- package/dist/productConfiguration/CfgFeature.js +68 -53
- package/dist/productConfiguration/CfgOption.d.ts +8 -0
- package/dist/productConfiguration/CfgOption.js +68 -73
- package/dist/productConfiguration/CfgProductConfiguration.js +22 -34
- package/dist/productConfiguration/productParamsGenerator.js +43 -57
- package/dist/productLoader.js +2 -13
- package/dist/syncGroups/SyncGroupsHandler.js +60 -83
- package/dist/syncGroups/SyncGroupsPathHelper.js +5 -5
- package/dist/syncGroups/SyncGroupsState.js +4 -5
- package/dist/syncGroups/SyncGroupsTransaction.d.ts +19 -1
- package/dist/syncGroups/SyncGroupsTransaction.js +352 -303
- package/dist/tasks/TaskHandler.js +53 -64
- package/dist/tests/testData/dummyProductForTest.js +4 -1
- package/dist/tests/testData/testDataAdditionalProductInAdditionalProductInProductForTest.js +18 -21
- package/dist/tests/testData/testDataCachedGetProduct.js +20 -20
- package/dist/tests/testData/testDataCachedPostValidate.js +6 -15
- package/dist/tests/testData/testDataProductAggregatedPrice.js +21 -21
- package/dist/tests/testData/testDataUpcharge.js +31 -22
- package/dist/utilitiesCatalogueData.js +21 -9
- package/dist/utilitiesCataloguePermission.js +5 -2
- package/dist/utilitiesConfiguration.js +21 -23
- package/package.json +3 -3
package/dist/CfgProduct.js
CHANGED
|
@@ -1,28 +1,19 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
var _a;
|
|
11
2
|
import { AggregatedLoadingObservable, assert, assertDefined, augmentErrorMessage, compareArrays, count, Observable, toLengthUnit, } from "@configura/web-utilities";
|
|
12
3
|
import { CfgMeasureDefinition } from "./CfgMeasure.js";
|
|
13
|
-
import { ProductConfigurationBubbleMode } from "./productConfiguration/CfgOption.js";
|
|
4
|
+
import { ProductConfigurationBubbleMode, } from "./productConfiguration/CfgOption.js";
|
|
14
5
|
import { CfgProductConfiguration } from "./productConfiguration/CfgProductConfiguration.js";
|
|
15
6
|
import { collectAdditionalProductRefs } from "./productConfiguration/utilitiesProductConfiguration.js";
|
|
16
7
|
import { wrapWithCache } from "./productLoader.js";
|
|
17
8
|
import { SyncGroupsHandler } from "./syncGroups/SyncGroupsHandler.js";
|
|
9
|
+
import { generateSelectionDiff } from "./syncGroups/SyncGroupsTransaction.js";
|
|
18
10
|
import { compareCfgProductData, correctDefaultsOnCatalogueParams, isSameCatalogueParams, isSameProductRef, makeProductKey, } from "./utilitiesCatalogueData.js";
|
|
19
11
|
import { CfgProdConfParts, convertDtoProductConfToV1, isAdditionalProductConfiguration, isProductConf, } from "./utilitiesConfiguration.js";
|
|
20
12
|
function completeSettings(incompleteSettings) {
|
|
21
|
-
var _b, _c;
|
|
22
13
|
return {
|
|
23
|
-
strictSelectOneSelectionCount:
|
|
24
|
-
syncGroupsApplyMode: incompleteSettings
|
|
25
|
-
disableMatchOnDescription:
|
|
14
|
+
strictSelectOneSelectionCount: incompleteSettings?.strictSelectOneSelectionCount ?? false,
|
|
15
|
+
syncGroupsApplyMode: incompleteSettings?.syncGroupsApplyMode,
|
|
16
|
+
disableMatchOnDescription: incompleteSettings?.disableMatchOnDescription ?? false,
|
|
26
17
|
};
|
|
27
18
|
}
|
|
28
19
|
/**
|
|
@@ -55,7 +46,6 @@ function isDescriptionMatch(l, r) {
|
|
|
55
46
|
*/
|
|
56
47
|
export class _CfgProductInternal {
|
|
57
48
|
constructor(initSuccess, initFail, _productLoaderRaw, prodParams, settings, optional, selected, rootFeatureRefs, rawFeatures, notes, uuid, _rawUnit, _rawProductData, apiSelection, apiConstraints, loadingObservable, parent, root, _additionalProductRef, _syncGroupHandler) {
|
|
58
|
-
var _b;
|
|
59
49
|
this._productLoaderRaw = _productLoaderRaw;
|
|
60
50
|
this.prodParams = prodParams;
|
|
61
51
|
this.settings = settings;
|
|
@@ -70,25 +60,31 @@ export class _CfgProductInternal {
|
|
|
70
60
|
this.additionalProducts = [];
|
|
71
61
|
this._notes = new Map();
|
|
72
62
|
this.changeObservable = new Observable();
|
|
63
|
+
/**
|
|
64
|
+
* The last diff that was generated during the last validate call.
|
|
65
|
+
*
|
|
66
|
+
* This is used to keep track of the changes that were made to the
|
|
67
|
+
* configuration as a result of apply constraints.
|
|
68
|
+
*/
|
|
69
|
+
this._lastDiff = undefined;
|
|
73
70
|
/** Mark this and its descendants as destroyed and remove all listeners */
|
|
74
71
|
this.destroy = () => {
|
|
75
|
-
var _b;
|
|
76
72
|
this._destroyed = true;
|
|
77
73
|
this.changeObservable.stopAllListen();
|
|
78
74
|
this.configuration.stopAllListenForChange();
|
|
79
|
-
for (const additionalProduct of
|
|
75
|
+
for (const additionalProduct of this.additionalProducts ?? []) {
|
|
80
76
|
additionalProduct.destroy();
|
|
81
77
|
}
|
|
82
78
|
};
|
|
83
79
|
/**
|
|
84
80
|
* Reset will reset the product to its initial state
|
|
85
81
|
*/
|
|
86
|
-
this.reset = () =>
|
|
82
|
+
this.reset = async () => {
|
|
87
83
|
if (this._initialClone !== undefined) {
|
|
88
|
-
|
|
84
|
+
await this.copyFrom(this._initialClone, true);
|
|
89
85
|
}
|
|
90
|
-
}
|
|
91
|
-
this._notifyAllOfChange = (bubbleMode, committed) =>
|
|
86
|
+
};
|
|
87
|
+
this._notifyAllOfChange = async (bubbleMode, committed) => {
|
|
92
88
|
if (bubbleMode === CfgProductBubbleMode.Stop) {
|
|
93
89
|
return;
|
|
94
90
|
}
|
|
@@ -96,23 +92,23 @@ export class _CfgProductInternal {
|
|
|
96
92
|
const freshRef = CfgProduct._makeNewRefFrom(this);
|
|
97
93
|
this.changeObservable.notifyAll({ freshRef, committed });
|
|
98
94
|
if (parent !== undefined) {
|
|
99
|
-
|
|
95
|
+
await parent._additionalProductHasChanged(freshRef, bubbleMode === CfgProductBubbleMode.OneLevel
|
|
100
96
|
? CfgProductBubbleMode.Stop
|
|
101
97
|
: bubbleMode, committed);
|
|
102
98
|
}
|
|
103
|
-
}
|
|
99
|
+
};
|
|
104
100
|
/** Called when a child (additional product or the configuration) has changed. */
|
|
105
|
-
this._childHasChanged = (bubbleMode, committed) =>
|
|
101
|
+
this._childHasChanged = async (bubbleMode, committed) => {
|
|
106
102
|
if (bubbleMode === CfgProductBubbleMode.ToRootAndBubbleSelected &&
|
|
107
103
|
this.optional &&
|
|
108
104
|
!this.selected) {
|
|
109
|
-
|
|
105
|
+
await this.setSelected(true, bubbleMode, false);
|
|
110
106
|
return;
|
|
111
107
|
}
|
|
112
|
-
|
|
113
|
-
}
|
|
108
|
+
await this._notifyAllOfChange(bubbleMode, committed);
|
|
109
|
+
};
|
|
114
110
|
/** Called by child to tell its parent that it has changed. */
|
|
115
|
-
this._additionalProductHasChanged = (freshRef, bubbleMode, committed) =>
|
|
111
|
+
this._additionalProductHasChanged = async (freshRef, bubbleMode, committed) => {
|
|
116
112
|
const i = this.additionalProducts.findIndex((a) => a.isBackedBySame(freshRef));
|
|
117
113
|
if (i !== -1) {
|
|
118
114
|
// Child additional product might not be found. This probably means that propagate
|
|
@@ -122,39 +118,38 @@ export class _CfgProductInternal {
|
|
|
122
118
|
// C no longer is part of the tree. Odd, but fully permitted.
|
|
123
119
|
this.additionalProducts[i] = freshRef;
|
|
124
120
|
}
|
|
125
|
-
|
|
126
|
-
}
|
|
121
|
+
await this._childHasChanged(bubbleMode, committed);
|
|
122
|
+
};
|
|
127
123
|
/** Called by the configuration to tell its parent that it has changed. */
|
|
128
|
-
this._configurationHasChanged = (freshRef, bubbleMode, committed) =>
|
|
124
|
+
this._configurationHasChanged = async (freshRef, bubbleMode, committed) => {
|
|
129
125
|
this._configuration = freshRef;
|
|
130
126
|
switch (bubbleMode) {
|
|
131
127
|
case ProductConfigurationBubbleMode.ValidateAndBubbleSelected:
|
|
132
128
|
// The revalidate call will continue the bubble
|
|
133
|
-
|
|
129
|
+
await this._revalidate(CfgProductBubbleMode.ToRootAndBubbleSelected, this._productLoaderRaw, committed);
|
|
134
130
|
return;
|
|
135
131
|
case ProductConfigurationBubbleMode.BubbleSelected:
|
|
136
|
-
|
|
132
|
+
await this._childHasChanged(CfgProductBubbleMode.ToRootAndBubbleSelected, committed);
|
|
137
133
|
return;
|
|
138
134
|
case ProductConfigurationBubbleMode.Validate:
|
|
139
135
|
// The revalidate call will continue the bubble
|
|
140
|
-
|
|
136
|
+
await this._revalidate(CfgProductBubbleMode.ToRoot, this._productLoaderRaw, committed);
|
|
141
137
|
return;
|
|
142
138
|
case ProductConfigurationBubbleMode.ToParentProduct:
|
|
143
139
|
// Do not continue bubble as we have reached the parent CfgProduct
|
|
144
140
|
return;
|
|
145
141
|
case ProductConfigurationBubbleMode.OneLevel:
|
|
146
|
-
|
|
142
|
+
await this._childHasChanged(CfgProductBubbleMode.OneLevel, committed);
|
|
147
143
|
return;
|
|
148
144
|
case ProductConfigurationBubbleMode.Stop:
|
|
149
|
-
|
|
145
|
+
await this._childHasChanged(CfgProductBubbleMode.Stop, committed);
|
|
150
146
|
return;
|
|
151
147
|
case ProductConfigurationBubbleMode.ToRoot:
|
|
152
|
-
|
|
148
|
+
await this._childHasChanged(CfgProductBubbleMode.ToRoot, committed);
|
|
153
149
|
return;
|
|
154
150
|
}
|
|
155
|
-
}
|
|
151
|
+
};
|
|
156
152
|
this.getDtoConf = (include) => {
|
|
157
|
-
var _b;
|
|
158
153
|
const conf = {};
|
|
159
154
|
const features = this.configuration._internal.getDtoConf((include & CfgProdConfParts.ExtendedData) === CfgProdConfParts.ExtendedData);
|
|
160
155
|
if (0 < features.length) {
|
|
@@ -168,7 +163,7 @@ export class _CfgProductInternal {
|
|
|
168
163
|
// so no need to store it for additional products.
|
|
169
164
|
if ((include & CfgProdConfParts.SyncGroupState) === CfgProdConfParts.SyncGroupState &&
|
|
170
165
|
this.parent === undefined) {
|
|
171
|
-
conf.syncGroupState =
|
|
166
|
+
conf.syncGroupState = this.syncGroupHandler?.getCompactSyncGroupState();
|
|
172
167
|
}
|
|
173
168
|
const additionalProducts = this.additionalProducts;
|
|
174
169
|
if (0 < additionalProducts.length) {
|
|
@@ -185,7 +180,7 @@ export class _CfgProductInternal {
|
|
|
185
180
|
}
|
|
186
181
|
return conf;
|
|
187
182
|
};
|
|
188
|
-
this.setDtoConf = (s, doValidate, productLoaderForGroupedLoad) =>
|
|
183
|
+
this.setDtoConf = async (s, doValidate, productLoaderForGroupedLoad) => {
|
|
189
184
|
// The DtoProdConf format can contain a sync group state, but
|
|
190
185
|
// not the DtoAdditionalProductConfiguration format. So we apply
|
|
191
186
|
// any passed state here.
|
|
@@ -196,15 +191,11 @@ export class _CfgProductInternal {
|
|
|
196
191
|
syncGroupHandler.setCompactSyncGroupState(newSyncGroupState);
|
|
197
192
|
}
|
|
198
193
|
}
|
|
199
|
-
return
|
|
200
|
-
}
|
|
201
|
-
this.setApiSelection = (s, doValidate, productLoaderForGroupedLoad) =>
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
this.copyFrom = (source, doValidate, productLoaderForGroupedLoad) => __awaiter(this, void 0, void 0, function* () {
|
|
205
|
-
return yield this._setApiSelectionWithOtherProduct(convertDtoProductConfToV1(source.getDtoConf(CfgProdConfParts.NoExtra)), doValidate, productLoaderForGroupedLoad, source);
|
|
206
|
-
});
|
|
207
|
-
this._setApiSelectionWithOtherProduct = (productConfiguration, doValidate, productLoaderForGroupedLoad, sourceProduct) => __awaiter(this, void 0, void 0, function* () {
|
|
194
|
+
return await this.setApiSelection(convertDtoProductConfToV1(s), doValidate, productLoaderForGroupedLoad);
|
|
195
|
+
};
|
|
196
|
+
this.setApiSelection = async (s, doValidate, productLoaderForGroupedLoad) => await this._setApiSelectionWithOtherProduct(s, doValidate, productLoaderForGroupedLoad, undefined);
|
|
197
|
+
this.copyFrom = async (source, doValidate, productLoaderForGroupedLoad) => await this._setApiSelectionWithOtherProduct(convertDtoProductConfToV1(source.getDtoConf(CfgProdConfParts.NoExtra)), doValidate, productLoaderForGroupedLoad, source);
|
|
198
|
+
this._setApiSelectionWithOtherProduct = async (productConfiguration, doValidate, productLoaderForGroupedLoad, sourceProduct) => {
|
|
208
199
|
// Wrap with cache will make getProduct for this function call use the same server call
|
|
209
200
|
// for the same product with the same params. Used for getProduct (when a new additional
|
|
210
201
|
// product is loaded) and postValidate.
|
|
@@ -237,16 +228,20 @@ export class _CfgProductInternal {
|
|
|
237
228
|
}
|
|
238
229
|
}
|
|
239
230
|
}
|
|
240
|
-
const configurationChange =
|
|
231
|
+
const configurationChange = await this.configuration._internal.setApiSelection(productConfiguration.selOptions, sourceProduct?._rawProductData.partsData.constrOptions, false);
|
|
241
232
|
if (configurationChange) {
|
|
242
233
|
change = true;
|
|
243
234
|
}
|
|
244
235
|
if (this.optional) {
|
|
245
|
-
if (
|
|
236
|
+
if (await this.setSelected(productConfiguration.selected !== false, CfgProductBubbleMode.Stop, false)) {
|
|
246
237
|
change = true;
|
|
247
238
|
}
|
|
248
239
|
}
|
|
249
|
-
|
|
240
|
+
if (sourceProduct?._lastDiff) {
|
|
241
|
+
this._lastDiff = sourceProduct._lastDiff;
|
|
242
|
+
change = true;
|
|
243
|
+
}
|
|
244
|
+
await this._syncAndLoadAdditionalProducts(productLoaderForGroupedLoad, productConfiguration);
|
|
250
245
|
const apiSelectionAdditionalProducts = productConfiguration.additionalProducts || [];
|
|
251
246
|
const additionalProducts = this.additionalProducts.slice();
|
|
252
247
|
const additionalProductsCount = additionalProducts.length;
|
|
@@ -254,11 +249,10 @@ export class _CfgProductInternal {
|
|
|
254
249
|
if (apiSelectionAdditionalProductsCount !== additionalProductsCount) {
|
|
255
250
|
throw new Error(`Additional products are not same length. This product: "${this.key}". This product additional products count: ${additionalProductsCount}. Passed apiSelection additional products count: ${apiSelectionAdditionalProductsCount}`);
|
|
256
251
|
}
|
|
257
|
-
const sourceProductAdditionalProducts = sourceProduct
|
|
252
|
+
const sourceProductAdditionalProducts = sourceProduct?.additionalProducts;
|
|
258
253
|
assert(!sourceProductAdditionalProducts ||
|
|
259
254
|
additionalProductsCount === sourceProductAdditionalProducts.length, `Passed sourceProduct does not have the same number of additional products as this.`);
|
|
260
|
-
if ((
|
|
261
|
-
var _c;
|
|
255
|
+
if ((await Promise.all(apiSelectionAdditionalProducts.map(async (apiSelectionAdditionalProduct) => {
|
|
262
256
|
const refKey = apiSelectionAdditionalProduct.refKey;
|
|
263
257
|
assertDefined(refKey, "Additional product api configurations must have refKey.");
|
|
264
258
|
const i = additionalProducts.findIndex((a) => refKey === a.refKey);
|
|
@@ -266,25 +260,25 @@ export class _CfgProductInternal {
|
|
|
266
260
|
let sourceProductAdditionalProduct = undefined;
|
|
267
261
|
if (sourceProductAdditionalProducts !== undefined) {
|
|
268
262
|
sourceProductAdditionalProduct =
|
|
269
|
-
|
|
263
|
+
sourceProductAdditionalProducts.find((a) => refKey === a.refKey)?._internal;
|
|
270
264
|
assertDefined(sourceProductAdditionalProduct, "Additional product not found in sourceProduct");
|
|
271
265
|
}
|
|
272
266
|
const additionalProduct = additionalProducts.splice(i, 1)[0]; // Splicing like this is okay because this is done synchronous. The setCon. is what is async.
|
|
273
|
-
return
|
|
274
|
-
})))
|
|
267
|
+
return await additionalProduct._internal._setApiSelectionWithOtherProduct(apiSelectionAdditionalProduct, doValidate, productLoaderForGroupedLoad, sourceProductAdditionalProduct);
|
|
268
|
+
}))).some((b) => b)) {
|
|
275
269
|
change = true;
|
|
276
270
|
}
|
|
277
271
|
if (doValidate && configurationChange) {
|
|
278
|
-
|
|
272
|
+
await this._revalidate(CfgProductBubbleMode.ToRoot, productLoaderForGroupedLoad, true);
|
|
279
273
|
}
|
|
280
274
|
else if (change) {
|
|
281
275
|
// As setApiSelection is done recursively each level takes care of its own notifications
|
|
282
276
|
// so we only need to bubble one level to notify this and swap out the reference in the
|
|
283
277
|
// parent.
|
|
284
|
-
|
|
278
|
+
await this._notifyAllOfChange(CfgProductBubbleMode.OneLevel, true);
|
|
285
279
|
}
|
|
286
280
|
return change;
|
|
287
|
-
}
|
|
281
|
+
};
|
|
288
282
|
this.structureCompare = (other, strictOrder = true, descriptionMatch = false) => {
|
|
289
283
|
if (!this.configuration.structureCompare(other.configuration, strictOrder, descriptionMatch)) {
|
|
290
284
|
return false;
|
|
@@ -292,25 +286,25 @@ export class _CfgProductInternal {
|
|
|
292
286
|
return compareArrays(this.additionalProducts, other.additionalProducts, (l, r) => (!descriptionMatch || (descriptionMatch && isDescriptionMatch(l, r))) &&
|
|
293
287
|
l.structureCompare(r, strictOrder, descriptionMatch), !descriptionMatch);
|
|
294
288
|
};
|
|
295
|
-
this.tryMatchSelection = (other, descriptionMatch = false, // Match on case insensitive description, not code
|
|
296
|
-
productLoaderForGroupedLoad) =>
|
|
289
|
+
this.tryMatchSelection = async (other, descriptionMatch = false, // Match on case insensitive description, not code
|
|
290
|
+
productLoaderForGroupedLoad) => {
|
|
297
291
|
// wrap with cache will make getProduct for this function call use the same server call
|
|
298
292
|
// for the same product with the same params
|
|
299
293
|
productLoaderForGroupedLoad =
|
|
300
294
|
productLoaderForGroupedLoad || wrapWithCache(this._productLoaderRaw);
|
|
301
295
|
let change = false;
|
|
302
296
|
if (this.optional && other.optional) {
|
|
303
|
-
if (
|
|
297
|
+
if (await this.setSelected(other.selected, CfgProductBubbleMode.Stop, false)) {
|
|
304
298
|
change = true;
|
|
305
299
|
}
|
|
306
300
|
}
|
|
307
|
-
const configurationChange =
|
|
301
|
+
const configurationChange = await this.configuration._internal.tryMatchSelection(other.configuration._internal, descriptionMatch, false);
|
|
308
302
|
if (configurationChange) {
|
|
309
303
|
change = true;
|
|
310
|
-
|
|
304
|
+
await this._revalidate(CfgProductBubbleMode.ToRootAndBubbleSelected, productLoaderForGroupedLoad, true);
|
|
311
305
|
}
|
|
312
306
|
else if (change) {
|
|
313
|
-
|
|
307
|
+
await this._notifyAllOfChange(CfgProductBubbleMode.ToRootAndBubbleSelected, true);
|
|
314
308
|
}
|
|
315
309
|
const thisAdditionalProducts = this.additionalProducts;
|
|
316
310
|
const otherAdditionalProducts = other.additionalProducts;
|
|
@@ -342,11 +336,11 @@ export class _CfgProductInternal {
|
|
|
342
336
|
promises.push(thisAdditionalProducts[i]._internal.tryMatchSelection(otherAdditionalProducts[i]._internal, descriptionMatch, productLoaderForGroupedLoad));
|
|
343
337
|
}
|
|
344
338
|
}
|
|
345
|
-
if ((
|
|
339
|
+
if ((await Promise.all(promises)).some((b) => b)) {
|
|
346
340
|
change = true;
|
|
347
341
|
}
|
|
348
342
|
return change;
|
|
349
|
-
}
|
|
343
|
+
};
|
|
350
344
|
/** Only features in selected options and selected additional products. */
|
|
351
345
|
this._getDescendantFeaturesWithCode = (code) => this.additionalProducts.reduce((agg, additionalProduct) => {
|
|
352
346
|
if (additionalProduct.selected) {
|
|
@@ -355,16 +349,21 @@ export class _CfgProductInternal {
|
|
|
355
349
|
return agg;
|
|
356
350
|
}, this._configuration._internal._getFeaturesWithCode(code));
|
|
357
351
|
/**
|
|
358
|
-
* Do a validate call for this product.
|
|
359
|
-
*
|
|
352
|
+
* Do a validate call for this product.
|
|
353
|
+
*
|
|
354
|
+
* Does not validate additional products, unless application of sync groups
|
|
355
|
+
* on the result of the validate call results in changes in the additional
|
|
356
|
+
* product.
|
|
357
|
+
*
|
|
358
|
+
* The validation result is applied on the configuration. Then additional
|
|
360
359
|
* products are synced (unloaded, loaded etc.) Finally the changes bubble up the tree.
|
|
361
360
|
*/
|
|
362
|
-
this._revalidate = (bubbleMode, productLoader, committed) =>
|
|
361
|
+
this._revalidate = async (bubbleMode, productLoader, committed) => {
|
|
363
362
|
const { _configuration: configuration } = this;
|
|
364
363
|
const token = this.loadingObservable.startChildLoading();
|
|
365
364
|
this._revalidateInProgressToken = token;
|
|
366
365
|
try {
|
|
367
|
-
const response =
|
|
366
|
+
const response = await productLoader.postValidate(correctDefaultsOnCatalogueParams(this.prodParams), {
|
|
368
367
|
selOptions: configuration._internal.getApiSelection(),
|
|
369
368
|
knownFeatureCodes: configuration.rawFeatures.map((f) => f.code),
|
|
370
369
|
});
|
|
@@ -395,12 +394,22 @@ export class _CfgProductInternal {
|
|
|
395
394
|
if (rootFeatureRefs !== undefined) {
|
|
396
395
|
configuration._internal.populateFeatures(rootFeatureRefs);
|
|
397
396
|
}
|
|
398
|
-
|
|
399
|
-
|
|
397
|
+
const originalProduct = await this.clone();
|
|
398
|
+
await configuration._internal.setApiSelection(productData.partsData.selOptions || [], productData.partsData.constrOptions, false);
|
|
399
|
+
if (this.parent === undefined) {
|
|
400
|
+
const diff = generateSelectionDiff(originalProduct, this);
|
|
401
|
+
if (this._lastDiff && diff) {
|
|
402
|
+
this._lastDiff.push(...diff);
|
|
403
|
+
}
|
|
404
|
+
else if (diff) {
|
|
405
|
+
this._lastDiff = diff;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
await this._syncAndLoadAdditionalProducts(productLoader, undefined);
|
|
400
409
|
if (this._destroyed) {
|
|
401
410
|
return false;
|
|
402
411
|
}
|
|
403
|
-
|
|
412
|
+
await this._notifyAllOfChange(bubbleMode, committed);
|
|
404
413
|
return true;
|
|
405
414
|
}
|
|
406
415
|
catch (e) {
|
|
@@ -409,12 +418,12 @@ export class _CfgProductInternal {
|
|
|
409
418
|
finally {
|
|
410
419
|
this.loadingObservable.stopChildLoading(token);
|
|
411
420
|
}
|
|
412
|
-
}
|
|
421
|
+
};
|
|
413
422
|
/**
|
|
414
423
|
* Based on this configuration find what additional products should be shown and not, unload
|
|
415
424
|
* (i.e. destroy) those that should no longer be shown, load the new ones.
|
|
416
425
|
*/
|
|
417
|
-
this._syncAndLoadAdditionalProducts = (productLoaderForGroupedLoad, initialProductConfiguration) =>
|
|
426
|
+
this._syncAndLoadAdditionalProducts = async (productLoaderForGroupedLoad, initialProductConfiguration) => {
|
|
418
427
|
const { _productLoaderRaw: productLoaderRaw, rawProductData: productData, configuration, additionalProducts: currentAdditionalProducts, } = this;
|
|
419
428
|
const additionalProductRefs = [
|
|
420
429
|
...(productData.additionalProductRefs || []),
|
|
@@ -455,20 +464,23 @@ export class _CfgProductInternal {
|
|
|
455
464
|
if (additionalProductRefs.length !== 0) {
|
|
456
465
|
change = true;
|
|
457
466
|
}
|
|
458
|
-
const newAdditionalProducts =
|
|
459
|
-
var _d;
|
|
467
|
+
const newAdditionalProducts = await Promise.all(additionalProductRefs.map((p) => (async () => {
|
|
460
468
|
const additionalProductRef = p.prodRef;
|
|
461
469
|
const refKey = additionalProductRef.refKey;
|
|
462
|
-
const additionalProductInitialProductConfiguration =
|
|
470
|
+
const additionalProductInitialProductConfiguration = initialProductConfiguration?.additionalProducts?.find((aP) => refKey === aP.refKey);
|
|
463
471
|
if (initialProductConfiguration !== undefined &&
|
|
464
472
|
additionalProductInitialProductConfiguration === undefined) {
|
|
465
473
|
console.warn(`Did not find initial product configuration for additional product with refKey ${refKey}`);
|
|
466
474
|
}
|
|
467
475
|
return {
|
|
468
476
|
originalIndex: p.originalIndex,
|
|
469
|
-
product: CfgProduct._makeNewRefFrom(
|
|
477
|
+
product: CfgProduct._makeNewRefFrom(await _CfgProductInternal.make(productLoaderRaw, productLoaderForGroupedLoad, {
|
|
478
|
+
...additionalProductRef.catId,
|
|
479
|
+
lang: this.prodParams.lang,
|
|
480
|
+
partNumber: additionalProductRef.partNumber,
|
|
481
|
+
}, this.settings, additionalProductRef.optional === true, this.loadingObservable, this, this.root, additionalProductRef, additionalProductInitialProductConfiguration)),
|
|
470
482
|
};
|
|
471
|
-
})
|
|
483
|
+
})()));
|
|
472
484
|
if (this._destroyed) {
|
|
473
485
|
return change;
|
|
474
486
|
}
|
|
@@ -480,13 +492,16 @@ export class _CfgProductInternal {
|
|
|
480
492
|
finally {
|
|
481
493
|
this.loadingObservable.stopChildLoading(token);
|
|
482
494
|
}
|
|
483
|
-
}
|
|
495
|
+
};
|
|
484
496
|
this._setRawProductData = (productData) => {
|
|
485
497
|
this._rawProductData = productData;
|
|
486
498
|
this._clearStretchMeasurements();
|
|
487
499
|
};
|
|
488
|
-
this.root = root
|
|
489
|
-
this.key = makeProductKey(
|
|
500
|
+
this.root = root ?? this;
|
|
501
|
+
this.key = makeProductKey({
|
|
502
|
+
...prodParams,
|
|
503
|
+
partNumber: _additionalProductRef?.refKey ?? prodParams.partNumber,
|
|
504
|
+
});
|
|
490
505
|
this._selected = optional ? selected : undefined;
|
|
491
506
|
this.isAdditionalProduct = _additionalProductRef !== undefined;
|
|
492
507
|
if (notes !== undefined) {
|
|
@@ -503,19 +518,16 @@ export class _CfgProductInternal {
|
|
|
503
518
|
* Providing the parent and root of what you clone as arguments is unwise as it will
|
|
504
519
|
* make changes you do on the clone be propagated up to the original non-clone root product.
|
|
505
520
|
*/
|
|
506
|
-
clone(parent, root) {
|
|
507
|
-
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
initSuccess(p);
|
|
512
|
-
}, initFail, this._productLoaderRaw, this.prodParams, this.settings, this.optional, this.selected, this._configuration.rootFeatureRefs, this._configuration.rawFeatures, this._notes.values(), this.uuid, this._rawUnit, this._rawProductData, this.configuration._internal.getApiSelection(), this.configuration._internal.getApiConstrained(), new AggregatedLoadingObservable(), parent, root, this._additionalProductRef, (_b = this._syncGroupHandler) === null || _b === void 0 ? void 0 : _b.clone());
|
|
513
|
-
});
|
|
514
|
-
for (const additionalProduct of this.additionalProducts) {
|
|
515
|
-
product.additionalProducts.push(CfgProduct._makeNewRefFrom(yield additionalProduct._internal.clone(product, root || product)));
|
|
516
|
-
}
|
|
517
|
-
return product;
|
|
521
|
+
async clone(parent, root) {
|
|
522
|
+
const product = await new Promise((initSuccess, initFail) => {
|
|
523
|
+
const p = new _CfgProductInternal(() => {
|
|
524
|
+
initSuccess(p);
|
|
525
|
+
}, initFail, this._productLoaderRaw, this.prodParams, this.settings, this.optional, this.selected, this._configuration.rootFeatureRefs, this._configuration.rawFeatures, this._notes.values(), this.uuid, this._rawUnit, this._rawProductData, this.configuration._internal.getApiSelection(), this.configuration._internal.getApiConstrained(), new AggregatedLoadingObservable(), parent, root, this._additionalProductRef, this._syncGroupHandler?.clone());
|
|
518
526
|
});
|
|
527
|
+
for (const additionalProduct of this.additionalProducts) {
|
|
528
|
+
product.additionalProducts.push(CfgProduct._makeNewRefFrom(await additionalProduct._internal.clone(product, root || product)));
|
|
529
|
+
}
|
|
530
|
+
return product;
|
|
519
531
|
}
|
|
520
532
|
/**
|
|
521
533
|
* Internal use. Used when this product is an additional product, and
|
|
@@ -556,20 +568,17 @@ export class _CfgProductInternal {
|
|
|
556
568
|
}
|
|
557
569
|
}
|
|
558
570
|
get notes() {
|
|
559
|
-
|
|
560
|
-
return this.getNotes((_b = this.rawProductData.noteRefs) !== null && _b !== void 0 ? _b : []);
|
|
571
|
+
return this.getNotes(this.rawProductData.noteRefs ?? []);
|
|
561
572
|
}
|
|
562
573
|
get miscFiles() {
|
|
563
|
-
|
|
564
|
-
return (_b = this._rawProductData.miscFiles) !== null && _b !== void 0 ? _b : [];
|
|
574
|
+
return this._rawProductData.miscFiles ?? [];
|
|
565
575
|
}
|
|
566
576
|
get hasRootFeaturesChanged() {
|
|
567
577
|
return (this._configuration._internal.hasRootFeaturesChanged ||
|
|
568
578
|
this.additionalProducts.some((p) => p._internal.hasRootFeaturesChanged));
|
|
569
579
|
}
|
|
570
580
|
get description() {
|
|
571
|
-
|
|
572
|
-
return (_c = (_b = this._additionalProductRef) === null || _b === void 0 ? void 0 : _b.refDescription) !== null && _c !== void 0 ? _c : this._rawProductData.description;
|
|
581
|
+
return this._additionalProductRef?.refDescription ?? this._rawProductData.description;
|
|
573
582
|
}
|
|
574
583
|
get rootNodeSources() {
|
|
575
584
|
return this._rawProductData.models;
|
|
@@ -587,25 +596,21 @@ export class _CfgProductInternal {
|
|
|
587
596
|
return this._rawProductData.partsData.prices;
|
|
588
597
|
}
|
|
589
598
|
get measureDefinitions() {
|
|
590
|
-
var _b;
|
|
591
599
|
if (this._measureDefinitions === undefined) {
|
|
592
|
-
this._measureDefinitions = (
|
|
600
|
+
this._measureDefinitions = (this._rawProductData.measurements ?? [])
|
|
593
601
|
.map((m) => CfgMeasureDefinition.make(m))
|
|
594
602
|
.filter((m) => m !== undefined);
|
|
595
603
|
}
|
|
596
604
|
return this._measureDefinitions;
|
|
597
605
|
}
|
|
598
606
|
get refKey() {
|
|
599
|
-
|
|
600
|
-
return (_b = this._additionalProductRef) === null || _b === void 0 ? void 0 : _b.refKey;
|
|
607
|
+
return this._additionalProductRef?.refKey;
|
|
601
608
|
}
|
|
602
609
|
get transform() {
|
|
603
|
-
|
|
604
|
-
return (_b = this._additionalProductRef) === null || _b === void 0 ? void 0 : _b.transform;
|
|
610
|
+
return this._additionalProductRef?.transform;
|
|
605
611
|
}
|
|
606
612
|
get anchor() {
|
|
607
|
-
|
|
608
|
-
return (_b = this._additionalProductRef) === null || _b === void 0 ? void 0 : _b.anchor;
|
|
613
|
+
return this._additionalProductRef?.anchor;
|
|
609
614
|
}
|
|
610
615
|
/** @throws an error if the actual unit sent by the server was not a LengthUnit */
|
|
611
616
|
get unit() {
|
|
@@ -641,34 +646,32 @@ export class _CfgProductInternal {
|
|
|
641
646
|
get optional() {
|
|
642
647
|
return this._selected !== undefined;
|
|
643
648
|
}
|
|
644
|
-
setSelected(selected, bubbleMode, interactive) {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
if (
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
yield syncGroupHandler.init(this.root, wrapWithCache(this._productLoaderRaw));
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
else {
|
|
664
|
-
yield this.reset();
|
|
665
|
-
// In case a passed initial configuration has made it reset to selected
|
|
666
|
-
this._selected = selected;
|
|
649
|
+
async setSelected(selected, bubbleMode, interactive) {
|
|
650
|
+
if (!this.optional) {
|
|
651
|
+
console.warn("This product is not optional. Nothing will happen");
|
|
652
|
+
return false;
|
|
653
|
+
}
|
|
654
|
+
if (this._selected === selected) {
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
// Vitally important that this happens before the call to reset,
|
|
658
|
+
// so that the reset won't cause infinite loops
|
|
659
|
+
this._selected = selected;
|
|
660
|
+
if (interactive) {
|
|
661
|
+
if (selected) {
|
|
662
|
+
const syncGroupHandler = this.syncGroupHandler;
|
|
663
|
+
if (syncGroupHandler !== undefined) {
|
|
664
|
+
await syncGroupHandler.init(this.root, wrapWithCache(this._productLoaderRaw));
|
|
667
665
|
}
|
|
668
666
|
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
667
|
+
else {
|
|
668
|
+
await this.reset();
|
|
669
|
+
// In case a passed initial configuration has made it reset to selected
|
|
670
|
+
this._selected = selected;
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
await this._notifyAllOfChange(bubbleMode, true);
|
|
674
|
+
return true;
|
|
672
675
|
}
|
|
673
676
|
get configuration() {
|
|
674
677
|
return this._configuration;
|
|
@@ -711,8 +714,7 @@ export class _CfgProductInternal {
|
|
|
711
714
|
return this.root._syncGroupHandler;
|
|
712
715
|
}
|
|
713
716
|
get syncGroupsVerboseLogging() {
|
|
714
|
-
|
|
715
|
-
return (_c = (_b = this.syncGroupHandler) === null || _b === void 0 ? void 0 : _b.verboseLogging) !== null && _c !== void 0 ? _c : false;
|
|
717
|
+
return this.syncGroupHandler?.verboseLogging ?? false;
|
|
716
718
|
}
|
|
717
719
|
/**
|
|
718
720
|
* Set to true to get verbose sync state changes logged to the console.
|
|
@@ -724,10 +726,21 @@ export class _CfgProductInternal {
|
|
|
724
726
|
}
|
|
725
727
|
syncGroupHandler.verboseLogging = v;
|
|
726
728
|
}
|
|
729
|
+
/**
|
|
730
|
+
* Obtains the differences found in the last validate call.
|
|
731
|
+
* Used to update the sync group state.
|
|
732
|
+
*
|
|
733
|
+
* Removes the diff from the product.
|
|
734
|
+
*/
|
|
735
|
+
takeDiff() {
|
|
736
|
+
const diff = this._lastDiff?.filter((option) => option.selected);
|
|
737
|
+
this._lastDiff = undefined;
|
|
738
|
+
return diff;
|
|
739
|
+
}
|
|
727
740
|
}
|
|
728
741
|
_a = _CfgProductInternal;
|
|
729
|
-
_CfgProductInternal.make = (productLoaderRaw, productLoaderForGroupedLoad, // Used when instantiating the current product
|
|
730
|
-
prodParams, settings, optional, loadingObservable, parent, root, additionalProductRef, initialProductConfiguration) =>
|
|
742
|
+
_CfgProductInternal.make = async (productLoaderRaw, productLoaderForGroupedLoad, // Used when instantiating the current product
|
|
743
|
+
prodParams, settings, optional, loadingObservable, parent, root, additionalProductRef, initialProductConfiguration) => {
|
|
731
744
|
// Wrap with cache will make getProduct for this function call use the same server call
|
|
732
745
|
// for the same product with the same params. Not retained for future calls, only used
|
|
733
746
|
// at this initial load.
|
|
@@ -756,39 +769,38 @@ prodParams, settings, optional, loadingObservable, parent, root, additionalProdu
|
|
|
756
769
|
initialProductConfigurationV1 = initialProductConfiguration;
|
|
757
770
|
}
|
|
758
771
|
}
|
|
759
|
-
const productResponse =
|
|
772
|
+
const productResponse = await (initialProductConfigurationV1
|
|
760
773
|
? productLoaderForGroupedLoad.postValidate(defaultCorrectedProdParams, {
|
|
761
774
|
knownFeatureCodes: [],
|
|
762
775
|
selOptions: initialProductConfigurationV1.selOptions,
|
|
763
776
|
})
|
|
764
777
|
: productLoaderForGroupedLoad.getProduct(defaultCorrectedProdParams));
|
|
765
778
|
const { productData, rootFeatureRefs, features: rawFeatures, notes, uuid, unit, } = productResponse;
|
|
766
|
-
const initiallySelected = !optional ||
|
|
767
|
-
const product =
|
|
768
|
-
var _b;
|
|
779
|
+
const initiallySelected = !optional || initialProductConfigurationV1?.selected === true;
|
|
780
|
+
const product = await new Promise((initSuccess, initFail) => {
|
|
769
781
|
const p = new _CfgProductInternal(() => {
|
|
770
782
|
// We absolutely do not want anyone to assign to this._configuration. So we want that field private.
|
|
771
783
|
// But we can not set the api selection synchronously. And the product configuration needs "this". So we use this callback.
|
|
772
784
|
// Feel free to find a nicer more readable solution :)
|
|
773
785
|
initSuccess(p);
|
|
774
|
-
}, initFail, productLoaderRaw, prodParams, settings, optional, initiallySelected, rootFeatureRefs
|
|
786
|
+
}, initFail, productLoaderRaw, prodParams, settings, optional, initiallySelected, rootFeatureRefs ?? [], rawFeatures, notes?.values(), uuid, unit, productData, productData.partsData.selOptions ?? [], productData.partsData.constrOptions, loadingObservable, parent, root, additionalProductRef, syncGroupHandler);
|
|
775
787
|
});
|
|
776
|
-
|
|
777
|
-
product._initialClone =
|
|
788
|
+
await product._syncAndLoadAdditionalProducts(productLoaderForGroupedLoad, initialProductConfigurationV1);
|
|
789
|
+
product._initialClone = await product.clone();
|
|
778
790
|
if (syncGroupHandler !== undefined && !initialProductConfiguration) {
|
|
779
791
|
// As syncGroupHandler is only set for root product we know that we will init with root.
|
|
780
792
|
// If we have an initialProductConfiguration we do not run init for the syncGroups.
|
|
781
793
|
// The idea with an initial configuration is that the product should look as it did when
|
|
782
794
|
// the configuration was saved. Not running init does that. Also, the DtoProductConf
|
|
783
795
|
// object can contain a sync state which is then used to populate the sync state.
|
|
784
|
-
|
|
796
|
+
await syncGroupHandler.init(product, productLoaderForGroupedLoad);
|
|
785
797
|
}
|
|
786
798
|
return product;
|
|
787
799
|
}
|
|
788
800
|
catch (e) {
|
|
789
801
|
throw augmentErrorMessage(e, `Load product request (${prodParams.partNumber}) failure`);
|
|
790
802
|
}
|
|
791
|
-
}
|
|
803
|
+
};
|
|
792
804
|
export class CfgProduct {
|
|
793
805
|
constructor(_internal) {
|
|
794
806
|
this._internal = _internal;
|
|
@@ -799,13 +811,13 @@ export class CfgProduct {
|
|
|
799
811
|
*/
|
|
800
812
|
this.destroy = () => this._internal.destroy();
|
|
801
813
|
/** Makes a clone of this. It is disconnected from the original. */
|
|
802
|
-
this.clone = () =>
|
|
814
|
+
this.clone = async () => CfgProduct._makeNewRefFrom(await this._internal.clone());
|
|
803
815
|
/**
|
|
804
816
|
* Only applicable when this product is optional.
|
|
805
817
|
* Setting this does not cause a validation call as toggling an optional additional product is
|
|
806
818
|
* assumed to always be legal.
|
|
807
819
|
*/
|
|
808
|
-
this.setSelected = (v) =>
|
|
820
|
+
this.setSelected = async (v) => await this._internal.setSelected(v, CfgProductBubbleMode.ToRootAndBubbleSelected, true);
|
|
809
821
|
/**
|
|
810
822
|
* Experimental. Additional products lacks descriptions or keys that are suitably for structure
|
|
811
823
|
* compare, so we use strict-order when trying to match the additional products. This makes
|
|
@@ -819,8 +831,8 @@ export class CfgProduct {
|
|
|
819
831
|
* This method does not propagate its selections.
|
|
820
832
|
* This method will cause validation calls if something change.
|
|
821
833
|
*/
|
|
822
|
-
this.tryMatchSelection = (other, descriptionMatch = false // Match on case insensitive description, not code
|
|
823
|
-
) =>
|
|
834
|
+
this.tryMatchSelection = async (other, descriptionMatch = false // Match on case insensitive description, not code
|
|
835
|
+
) => await this._internal.tryMatchSelection(other._internal, descriptionMatch);
|
|
824
836
|
/**
|
|
825
837
|
* Gets what selections has been made on the product, recursively including product
|
|
826
838
|
* configuration, optional products and additional products. Used when a full view of all
|
|
@@ -836,7 +848,7 @@ export class CfgProduct {
|
|
|
836
848
|
* the right models are loaded.
|
|
837
849
|
* @deprecated setDtoConf uses a newer format.
|
|
838
850
|
*/
|
|
839
|
-
this.setApiSelection = (configuration, doValidate = true) =>
|
|
851
|
+
this.setApiSelection = async (configuration, doValidate = true) => await this._internal.setApiSelection(configuration, doValidate);
|
|
840
852
|
/**
|
|
841
853
|
* A newer alternative version of getApiSelection. This returns the configuration (selections)
|
|
842
854
|
* on the product, recursively including product configuration, optional products and additional
|
|
@@ -858,7 +870,7 @@ export class CfgProduct {
|
|
|
858
870
|
* @param doValidate Makes a server side validation call. These are necessary to ensure that
|
|
859
871
|
* the right models are loaded.
|
|
860
872
|
*/
|
|
861
|
-
this.setDtoConf = (configuration, doValidate = true) =>
|
|
873
|
+
this.setDtoConf = async (configuration, doValidate = true) => await this._internal.setDtoConf(configuration, doValidate);
|
|
862
874
|
this.listenForChange = (l) => this._internal.changeObservable.listen(l);
|
|
863
875
|
this.stopListenForChange = (l) => this._internal.changeObservable.stopListen(l);
|
|
864
876
|
this.stopAllListenForChange = () => this._internal.changeObservable.stopAllListen();
|
|
@@ -866,10 +878,8 @@ export class CfgProduct {
|
|
|
866
878
|
this.stopListenForLoading = (l) => this._internal.loadingObservable.stopListen(l);
|
|
867
879
|
this.stopAllListenForLoading = () => this._internal.loadingObservable.stopAllListen();
|
|
868
880
|
}
|
|
869
|
-
static make(productLoader, prodParams, settings, initialProductConfiguration) {
|
|
870
|
-
return
|
|
871
|
-
return this._makeNewRefFrom(yield _CfgProductInternal.make(productLoader, undefined, prodParams, completeSettings(settings), false, new AggregatedLoadingObservable(), undefined, undefined, undefined, initialProductConfiguration));
|
|
872
|
-
});
|
|
881
|
+
static async make(productLoader, prodParams, settings, initialProductConfiguration) {
|
|
882
|
+
return this._makeNewRefFrom(await _CfgProductInternal.make(productLoader, undefined, prodParams, completeSettings(settings), false, new AggregatedLoadingObservable(), undefined, undefined, undefined, initialProductConfiguration));
|
|
873
883
|
}
|
|
874
884
|
/**
|
|
875
885
|
* Makes an object wrapping the passed object. This is not a clone method, it is a method to
|