@configura/web-api 3.0.0-alpha.0 → 3.0.0-alpha.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/dist/CatalogueAPI.d.ts +68 -1
- package/dist/CatalogueAPI.js +184 -219
- package/dist/CfgMeasure.js +1 -2
- package/dist/CfgProduct.js +131 -159
- 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 +64 -54
- package/dist/productConfiguration/CfgOption.d.ts +44 -0
- package/dist/productConfiguration/CfgOption.js +107 -71
- 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.js +259 -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,12 +1,3 @@
|
|
|
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";
|
|
@@ -18,11 +9,10 @@ import { SyncGroupsHandler } from "./syncGroups/SyncGroupsHandler.js";
|
|
|
18
9
|
import { compareCfgProductData, correctDefaultsOnCatalogueParams, isSameCatalogueParams, isSameProductRef, makeProductKey, } from "./utilitiesCatalogueData.js";
|
|
19
10
|
import { CfgProdConfParts, convertDtoProductConfToV1, isAdditionalProductConfiguration, isProductConf, } from "./utilitiesConfiguration.js";
|
|
20
11
|
function completeSettings(incompleteSettings) {
|
|
21
|
-
var _b, _c;
|
|
22
12
|
return {
|
|
23
|
-
strictSelectOneSelectionCount:
|
|
24
|
-
syncGroupsApplyMode: incompleteSettings
|
|
25
|
-
disableMatchOnDescription:
|
|
13
|
+
strictSelectOneSelectionCount: incompleteSettings?.strictSelectOneSelectionCount ?? false,
|
|
14
|
+
syncGroupsApplyMode: incompleteSettings?.syncGroupsApplyMode,
|
|
15
|
+
disableMatchOnDescription: incompleteSettings?.disableMatchOnDescription ?? false,
|
|
26
16
|
};
|
|
27
17
|
}
|
|
28
18
|
/**
|
|
@@ -55,7 +45,6 @@ function isDescriptionMatch(l, r) {
|
|
|
55
45
|
*/
|
|
56
46
|
export class _CfgProductInternal {
|
|
57
47
|
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
48
|
this._productLoaderRaw = _productLoaderRaw;
|
|
60
49
|
this.prodParams = prodParams;
|
|
61
50
|
this.settings = settings;
|
|
@@ -72,23 +61,22 @@ export class _CfgProductInternal {
|
|
|
72
61
|
this.changeObservable = new Observable();
|
|
73
62
|
/** Mark this and its descendants as destroyed and remove all listeners */
|
|
74
63
|
this.destroy = () => {
|
|
75
|
-
var _b;
|
|
76
64
|
this._destroyed = true;
|
|
77
65
|
this.changeObservable.stopAllListen();
|
|
78
66
|
this.configuration.stopAllListenForChange();
|
|
79
|
-
for (const additionalProduct of
|
|
67
|
+
for (const additionalProduct of this.additionalProducts ?? []) {
|
|
80
68
|
additionalProduct.destroy();
|
|
81
69
|
}
|
|
82
70
|
};
|
|
83
71
|
/**
|
|
84
72
|
* Reset will reset the product to its initial state
|
|
85
73
|
*/
|
|
86
|
-
this.reset = () =>
|
|
74
|
+
this.reset = async () => {
|
|
87
75
|
if (this._initialClone !== undefined) {
|
|
88
|
-
|
|
76
|
+
await this.copyFrom(this._initialClone, true);
|
|
89
77
|
}
|
|
90
|
-
}
|
|
91
|
-
this._notifyAllOfChange = (bubbleMode, committed) =>
|
|
78
|
+
};
|
|
79
|
+
this._notifyAllOfChange = async (bubbleMode, committed) => {
|
|
92
80
|
if (bubbleMode === CfgProductBubbleMode.Stop) {
|
|
93
81
|
return;
|
|
94
82
|
}
|
|
@@ -96,23 +84,23 @@ export class _CfgProductInternal {
|
|
|
96
84
|
const freshRef = CfgProduct._makeNewRefFrom(this);
|
|
97
85
|
this.changeObservable.notifyAll({ freshRef, committed });
|
|
98
86
|
if (parent !== undefined) {
|
|
99
|
-
|
|
87
|
+
await parent._additionalProductHasChanged(freshRef, bubbleMode === CfgProductBubbleMode.OneLevel
|
|
100
88
|
? CfgProductBubbleMode.Stop
|
|
101
89
|
: bubbleMode, committed);
|
|
102
90
|
}
|
|
103
|
-
}
|
|
91
|
+
};
|
|
104
92
|
/** Called when a child (additional product or the configuration) has changed. */
|
|
105
|
-
this._childHasChanged = (bubbleMode, committed) =>
|
|
93
|
+
this._childHasChanged = async (bubbleMode, committed) => {
|
|
106
94
|
if (bubbleMode === CfgProductBubbleMode.ToRootAndBubbleSelected &&
|
|
107
95
|
this.optional &&
|
|
108
96
|
!this.selected) {
|
|
109
|
-
|
|
97
|
+
await this.setSelected(true, bubbleMode, false);
|
|
110
98
|
return;
|
|
111
99
|
}
|
|
112
|
-
|
|
113
|
-
}
|
|
100
|
+
await this._notifyAllOfChange(bubbleMode, committed);
|
|
101
|
+
};
|
|
114
102
|
/** Called by child to tell its parent that it has changed. */
|
|
115
|
-
this._additionalProductHasChanged = (freshRef, bubbleMode, committed) =>
|
|
103
|
+
this._additionalProductHasChanged = async (freshRef, bubbleMode, committed) => {
|
|
116
104
|
const i = this.additionalProducts.findIndex((a) => a.isBackedBySame(freshRef));
|
|
117
105
|
if (i !== -1) {
|
|
118
106
|
// Child additional product might not be found. This probably means that propagate
|
|
@@ -122,39 +110,38 @@ export class _CfgProductInternal {
|
|
|
122
110
|
// C no longer is part of the tree. Odd, but fully permitted.
|
|
123
111
|
this.additionalProducts[i] = freshRef;
|
|
124
112
|
}
|
|
125
|
-
|
|
126
|
-
}
|
|
113
|
+
await this._childHasChanged(bubbleMode, committed);
|
|
114
|
+
};
|
|
127
115
|
/** Called by the configuration to tell its parent that it has changed. */
|
|
128
|
-
this._configurationHasChanged = (freshRef, bubbleMode, committed) =>
|
|
116
|
+
this._configurationHasChanged = async (freshRef, bubbleMode, committed) => {
|
|
129
117
|
this._configuration = freshRef;
|
|
130
118
|
switch (bubbleMode) {
|
|
131
119
|
case ProductConfigurationBubbleMode.ValidateAndBubbleSelected:
|
|
132
120
|
// The revalidate call will continue the bubble
|
|
133
|
-
|
|
121
|
+
await this._revalidate(CfgProductBubbleMode.ToRootAndBubbleSelected, this._productLoaderRaw, committed);
|
|
134
122
|
return;
|
|
135
123
|
case ProductConfigurationBubbleMode.BubbleSelected:
|
|
136
|
-
|
|
124
|
+
await this._childHasChanged(CfgProductBubbleMode.ToRootAndBubbleSelected, committed);
|
|
137
125
|
return;
|
|
138
126
|
case ProductConfigurationBubbleMode.Validate:
|
|
139
127
|
// The revalidate call will continue the bubble
|
|
140
|
-
|
|
128
|
+
await this._revalidate(CfgProductBubbleMode.ToRoot, this._productLoaderRaw, committed);
|
|
141
129
|
return;
|
|
142
130
|
case ProductConfigurationBubbleMode.ToParentProduct:
|
|
143
131
|
// Do not continue bubble as we have reached the parent CfgProduct
|
|
144
132
|
return;
|
|
145
133
|
case ProductConfigurationBubbleMode.OneLevel:
|
|
146
|
-
|
|
134
|
+
await this._childHasChanged(CfgProductBubbleMode.OneLevel, committed);
|
|
147
135
|
return;
|
|
148
136
|
case ProductConfigurationBubbleMode.Stop:
|
|
149
|
-
|
|
137
|
+
await this._childHasChanged(CfgProductBubbleMode.Stop, committed);
|
|
150
138
|
return;
|
|
151
139
|
case ProductConfigurationBubbleMode.ToRoot:
|
|
152
|
-
|
|
140
|
+
await this._childHasChanged(CfgProductBubbleMode.ToRoot, committed);
|
|
153
141
|
return;
|
|
154
142
|
}
|
|
155
|
-
}
|
|
143
|
+
};
|
|
156
144
|
this.getDtoConf = (include) => {
|
|
157
|
-
var _b;
|
|
158
145
|
const conf = {};
|
|
159
146
|
const features = this.configuration._internal.getDtoConf((include & CfgProdConfParts.ExtendedData) === CfgProdConfParts.ExtendedData);
|
|
160
147
|
if (0 < features.length) {
|
|
@@ -168,7 +155,7 @@ export class _CfgProductInternal {
|
|
|
168
155
|
// so no need to store it for additional products.
|
|
169
156
|
if ((include & CfgProdConfParts.SyncGroupState) === CfgProdConfParts.SyncGroupState &&
|
|
170
157
|
this.parent === undefined) {
|
|
171
|
-
conf.syncGroupState =
|
|
158
|
+
conf.syncGroupState = this.syncGroupHandler?.getCompactSyncGroupState();
|
|
172
159
|
}
|
|
173
160
|
const additionalProducts = this.additionalProducts;
|
|
174
161
|
if (0 < additionalProducts.length) {
|
|
@@ -185,7 +172,7 @@ export class _CfgProductInternal {
|
|
|
185
172
|
}
|
|
186
173
|
return conf;
|
|
187
174
|
};
|
|
188
|
-
this.setDtoConf = (s, doValidate, productLoaderForGroupedLoad) =>
|
|
175
|
+
this.setDtoConf = async (s, doValidate, productLoaderForGroupedLoad) => {
|
|
189
176
|
// The DtoProdConf format can contain a sync group state, but
|
|
190
177
|
// not the DtoAdditionalProductConfiguration format. So we apply
|
|
191
178
|
// any passed state here.
|
|
@@ -196,15 +183,11 @@ export class _CfgProductInternal {
|
|
|
196
183
|
syncGroupHandler.setCompactSyncGroupState(newSyncGroupState);
|
|
197
184
|
}
|
|
198
185
|
}
|
|
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* () {
|
|
186
|
+
return await this.setApiSelection(convertDtoProductConfToV1(s), doValidate, productLoaderForGroupedLoad);
|
|
187
|
+
};
|
|
188
|
+
this.setApiSelection = async (s, doValidate, productLoaderForGroupedLoad) => await this._setApiSelectionWithOtherProduct(s, doValidate, productLoaderForGroupedLoad, undefined);
|
|
189
|
+
this.copyFrom = async (source, doValidate, productLoaderForGroupedLoad) => await this._setApiSelectionWithOtherProduct(convertDtoProductConfToV1(source.getDtoConf(CfgProdConfParts.NoExtra)), doValidate, productLoaderForGroupedLoad, source);
|
|
190
|
+
this._setApiSelectionWithOtherProduct = async (productConfiguration, doValidate, productLoaderForGroupedLoad, sourceProduct) => {
|
|
208
191
|
// Wrap with cache will make getProduct for this function call use the same server call
|
|
209
192
|
// for the same product with the same params. Used for getProduct (when a new additional
|
|
210
193
|
// product is loaded) and postValidate.
|
|
@@ -237,16 +220,16 @@ export class _CfgProductInternal {
|
|
|
237
220
|
}
|
|
238
221
|
}
|
|
239
222
|
}
|
|
240
|
-
const configurationChange =
|
|
223
|
+
const configurationChange = await this.configuration._internal.setApiSelection(productConfiguration.selOptions, sourceProduct?._rawProductData.partsData.constrOptions, false);
|
|
241
224
|
if (configurationChange) {
|
|
242
225
|
change = true;
|
|
243
226
|
}
|
|
244
227
|
if (this.optional) {
|
|
245
|
-
if (
|
|
228
|
+
if (await this.setSelected(productConfiguration.selected !== false, CfgProductBubbleMode.Stop, false)) {
|
|
246
229
|
change = true;
|
|
247
230
|
}
|
|
248
231
|
}
|
|
249
|
-
|
|
232
|
+
await this._syncAndLoadAdditionalProducts(productLoaderForGroupedLoad, productConfiguration);
|
|
250
233
|
const apiSelectionAdditionalProducts = productConfiguration.additionalProducts || [];
|
|
251
234
|
const additionalProducts = this.additionalProducts.slice();
|
|
252
235
|
const additionalProductsCount = additionalProducts.length;
|
|
@@ -254,11 +237,10 @@ export class _CfgProductInternal {
|
|
|
254
237
|
if (apiSelectionAdditionalProductsCount !== additionalProductsCount) {
|
|
255
238
|
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
239
|
}
|
|
257
|
-
const sourceProductAdditionalProducts = sourceProduct
|
|
240
|
+
const sourceProductAdditionalProducts = sourceProduct?.additionalProducts;
|
|
258
241
|
assert(!sourceProductAdditionalProducts ||
|
|
259
242
|
additionalProductsCount === sourceProductAdditionalProducts.length, `Passed sourceProduct does not have the same number of additional products as this.`);
|
|
260
|
-
if ((
|
|
261
|
-
var _c;
|
|
243
|
+
if ((await Promise.all(apiSelectionAdditionalProducts.map(async (apiSelectionAdditionalProduct) => {
|
|
262
244
|
const refKey = apiSelectionAdditionalProduct.refKey;
|
|
263
245
|
assertDefined(refKey, "Additional product api configurations must have refKey.");
|
|
264
246
|
const i = additionalProducts.findIndex((a) => refKey === a.refKey);
|
|
@@ -266,25 +248,25 @@ export class _CfgProductInternal {
|
|
|
266
248
|
let sourceProductAdditionalProduct = undefined;
|
|
267
249
|
if (sourceProductAdditionalProducts !== undefined) {
|
|
268
250
|
sourceProductAdditionalProduct =
|
|
269
|
-
|
|
251
|
+
sourceProductAdditionalProducts.find((a) => refKey === a.refKey)?._internal;
|
|
270
252
|
assertDefined(sourceProductAdditionalProduct, "Additional product not found in sourceProduct");
|
|
271
253
|
}
|
|
272
254
|
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
|
-
})))
|
|
255
|
+
return await additionalProduct._internal._setApiSelectionWithOtherProduct(apiSelectionAdditionalProduct, doValidate, productLoaderForGroupedLoad, sourceProductAdditionalProduct);
|
|
256
|
+
}))).some((b) => b)) {
|
|
275
257
|
change = true;
|
|
276
258
|
}
|
|
277
259
|
if (doValidate && configurationChange) {
|
|
278
|
-
|
|
260
|
+
await this._revalidate(CfgProductBubbleMode.ToRoot, productLoaderForGroupedLoad, true);
|
|
279
261
|
}
|
|
280
262
|
else if (change) {
|
|
281
263
|
// As setApiSelection is done recursively each level takes care of its own notifications
|
|
282
264
|
// so we only need to bubble one level to notify this and swap out the reference in the
|
|
283
265
|
// parent.
|
|
284
|
-
|
|
266
|
+
await this._notifyAllOfChange(CfgProductBubbleMode.OneLevel, true);
|
|
285
267
|
}
|
|
286
268
|
return change;
|
|
287
|
-
}
|
|
269
|
+
};
|
|
288
270
|
this.structureCompare = (other, strictOrder = true, descriptionMatch = false) => {
|
|
289
271
|
if (!this.configuration.structureCompare(other.configuration, strictOrder, descriptionMatch)) {
|
|
290
272
|
return false;
|
|
@@ -292,25 +274,25 @@ export class _CfgProductInternal {
|
|
|
292
274
|
return compareArrays(this.additionalProducts, other.additionalProducts, (l, r) => (!descriptionMatch || (descriptionMatch && isDescriptionMatch(l, r))) &&
|
|
293
275
|
l.structureCompare(r, strictOrder, descriptionMatch), !descriptionMatch);
|
|
294
276
|
};
|
|
295
|
-
this.tryMatchSelection = (other, descriptionMatch = false, // Match on case insensitive description, not code
|
|
296
|
-
productLoaderForGroupedLoad) =>
|
|
277
|
+
this.tryMatchSelection = async (other, descriptionMatch = false, // Match on case insensitive description, not code
|
|
278
|
+
productLoaderForGroupedLoad) => {
|
|
297
279
|
// wrap with cache will make getProduct for this function call use the same server call
|
|
298
280
|
// for the same product with the same params
|
|
299
281
|
productLoaderForGroupedLoad =
|
|
300
282
|
productLoaderForGroupedLoad || wrapWithCache(this._productLoaderRaw);
|
|
301
283
|
let change = false;
|
|
302
284
|
if (this.optional && other.optional) {
|
|
303
|
-
if (
|
|
285
|
+
if (await this.setSelected(other.selected, CfgProductBubbleMode.Stop, false)) {
|
|
304
286
|
change = true;
|
|
305
287
|
}
|
|
306
288
|
}
|
|
307
|
-
const configurationChange =
|
|
289
|
+
const configurationChange = await this.configuration._internal.tryMatchSelection(other.configuration._internal, descriptionMatch, false);
|
|
308
290
|
if (configurationChange) {
|
|
309
291
|
change = true;
|
|
310
|
-
|
|
292
|
+
await this._revalidate(CfgProductBubbleMode.ToRootAndBubbleSelected, productLoaderForGroupedLoad, true);
|
|
311
293
|
}
|
|
312
294
|
else if (change) {
|
|
313
|
-
|
|
295
|
+
await this._notifyAllOfChange(CfgProductBubbleMode.ToRootAndBubbleSelected, true);
|
|
314
296
|
}
|
|
315
297
|
const thisAdditionalProducts = this.additionalProducts;
|
|
316
298
|
const otherAdditionalProducts = other.additionalProducts;
|
|
@@ -342,11 +324,11 @@ export class _CfgProductInternal {
|
|
|
342
324
|
promises.push(thisAdditionalProducts[i]._internal.tryMatchSelection(otherAdditionalProducts[i]._internal, descriptionMatch, productLoaderForGroupedLoad));
|
|
343
325
|
}
|
|
344
326
|
}
|
|
345
|
-
if ((
|
|
327
|
+
if ((await Promise.all(promises)).some((b) => b)) {
|
|
346
328
|
change = true;
|
|
347
329
|
}
|
|
348
330
|
return change;
|
|
349
|
-
}
|
|
331
|
+
};
|
|
350
332
|
/** Only features in selected options and selected additional products. */
|
|
351
333
|
this._getDescendantFeaturesWithCode = (code) => this.additionalProducts.reduce((agg, additionalProduct) => {
|
|
352
334
|
if (additionalProduct.selected) {
|
|
@@ -359,12 +341,12 @@ export class _CfgProductInternal {
|
|
|
359
341
|
* product in isolation. The validation result is applied on the configuration. Then additional
|
|
360
342
|
* products are synced (unloaded, loaded etc.) Finally the changes bubble up the tree.
|
|
361
343
|
*/
|
|
362
|
-
this._revalidate = (bubbleMode, productLoader, committed) =>
|
|
344
|
+
this._revalidate = async (bubbleMode, productLoader, committed) => {
|
|
363
345
|
const { _configuration: configuration } = this;
|
|
364
346
|
const token = this.loadingObservable.startChildLoading();
|
|
365
347
|
this._revalidateInProgressToken = token;
|
|
366
348
|
try {
|
|
367
|
-
const response =
|
|
349
|
+
const response = await productLoader.postValidate(correctDefaultsOnCatalogueParams(this.prodParams), {
|
|
368
350
|
selOptions: configuration._internal.getApiSelection(),
|
|
369
351
|
knownFeatureCodes: configuration.rawFeatures.map((f) => f.code),
|
|
370
352
|
});
|
|
@@ -395,12 +377,12 @@ export class _CfgProductInternal {
|
|
|
395
377
|
if (rootFeatureRefs !== undefined) {
|
|
396
378
|
configuration._internal.populateFeatures(rootFeatureRefs);
|
|
397
379
|
}
|
|
398
|
-
|
|
399
|
-
|
|
380
|
+
await configuration._internal.setApiSelection(productData.partsData.selOptions || [], productData.partsData.constrOptions, false);
|
|
381
|
+
await this._syncAndLoadAdditionalProducts(productLoader, undefined);
|
|
400
382
|
if (this._destroyed) {
|
|
401
383
|
return false;
|
|
402
384
|
}
|
|
403
|
-
|
|
385
|
+
await this._notifyAllOfChange(bubbleMode, committed);
|
|
404
386
|
return true;
|
|
405
387
|
}
|
|
406
388
|
catch (e) {
|
|
@@ -409,12 +391,12 @@ export class _CfgProductInternal {
|
|
|
409
391
|
finally {
|
|
410
392
|
this.loadingObservable.stopChildLoading(token);
|
|
411
393
|
}
|
|
412
|
-
}
|
|
394
|
+
};
|
|
413
395
|
/**
|
|
414
396
|
* Based on this configuration find what additional products should be shown and not, unload
|
|
415
397
|
* (i.e. destroy) those that should no longer be shown, load the new ones.
|
|
416
398
|
*/
|
|
417
|
-
this._syncAndLoadAdditionalProducts = (productLoaderForGroupedLoad, initialProductConfiguration) =>
|
|
399
|
+
this._syncAndLoadAdditionalProducts = async (productLoaderForGroupedLoad, initialProductConfiguration) => {
|
|
418
400
|
const { _productLoaderRaw: productLoaderRaw, rawProductData: productData, configuration, additionalProducts: currentAdditionalProducts, } = this;
|
|
419
401
|
const additionalProductRefs = [
|
|
420
402
|
...(productData.additionalProductRefs || []),
|
|
@@ -455,20 +437,23 @@ export class _CfgProductInternal {
|
|
|
455
437
|
if (additionalProductRefs.length !== 0) {
|
|
456
438
|
change = true;
|
|
457
439
|
}
|
|
458
|
-
const newAdditionalProducts =
|
|
459
|
-
var _d;
|
|
440
|
+
const newAdditionalProducts = await Promise.all(additionalProductRefs.map((p) => (async () => {
|
|
460
441
|
const additionalProductRef = p.prodRef;
|
|
461
442
|
const refKey = additionalProductRef.refKey;
|
|
462
|
-
const additionalProductInitialProductConfiguration =
|
|
443
|
+
const additionalProductInitialProductConfiguration = initialProductConfiguration?.additionalProducts?.find((aP) => refKey === aP.refKey);
|
|
463
444
|
if (initialProductConfiguration !== undefined &&
|
|
464
445
|
additionalProductInitialProductConfiguration === undefined) {
|
|
465
446
|
console.warn(`Did not find initial product configuration for additional product with refKey ${refKey}`);
|
|
466
447
|
}
|
|
467
448
|
return {
|
|
468
449
|
originalIndex: p.originalIndex,
|
|
469
|
-
product: CfgProduct._makeNewRefFrom(
|
|
450
|
+
product: CfgProduct._makeNewRefFrom(await _CfgProductInternal.make(productLoaderRaw, productLoaderForGroupedLoad, {
|
|
451
|
+
...additionalProductRef.catId,
|
|
452
|
+
lang: this.prodParams.lang,
|
|
453
|
+
partNumber: additionalProductRef.partNumber,
|
|
454
|
+
}, this.settings, additionalProductRef.optional === true, this.loadingObservable, this, this.root, additionalProductRef, additionalProductInitialProductConfiguration)),
|
|
470
455
|
};
|
|
471
|
-
})
|
|
456
|
+
})()));
|
|
472
457
|
if (this._destroyed) {
|
|
473
458
|
return change;
|
|
474
459
|
}
|
|
@@ -480,13 +465,16 @@ export class _CfgProductInternal {
|
|
|
480
465
|
finally {
|
|
481
466
|
this.loadingObservable.stopChildLoading(token);
|
|
482
467
|
}
|
|
483
|
-
}
|
|
468
|
+
};
|
|
484
469
|
this._setRawProductData = (productData) => {
|
|
485
470
|
this._rawProductData = productData;
|
|
486
471
|
this._clearStretchMeasurements();
|
|
487
472
|
};
|
|
488
|
-
this.root = root
|
|
489
|
-
this.key = makeProductKey(
|
|
473
|
+
this.root = root ?? this;
|
|
474
|
+
this.key = makeProductKey({
|
|
475
|
+
...prodParams,
|
|
476
|
+
partNumber: _additionalProductRef?.refKey ?? prodParams.partNumber,
|
|
477
|
+
});
|
|
490
478
|
this._selected = optional ? selected : undefined;
|
|
491
479
|
this.isAdditionalProduct = _additionalProductRef !== undefined;
|
|
492
480
|
if (notes !== undefined) {
|
|
@@ -503,19 +491,16 @@ export class _CfgProductInternal {
|
|
|
503
491
|
* Providing the parent and root of what you clone as arguments is unwise as it will
|
|
504
492
|
* make changes you do on the clone be propagated up to the original non-clone root product.
|
|
505
493
|
*/
|
|
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;
|
|
494
|
+
async clone(parent, root) {
|
|
495
|
+
const product = await new Promise((initSuccess, initFail) => {
|
|
496
|
+
const p = new _CfgProductInternal(() => {
|
|
497
|
+
initSuccess(p);
|
|
498
|
+
}, 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
499
|
});
|
|
500
|
+
for (const additionalProduct of this.additionalProducts) {
|
|
501
|
+
product.additionalProducts.push(CfgProduct._makeNewRefFrom(await additionalProduct._internal.clone(product, root || product)));
|
|
502
|
+
}
|
|
503
|
+
return product;
|
|
519
504
|
}
|
|
520
505
|
/**
|
|
521
506
|
* Internal use. Used when this product is an additional product, and
|
|
@@ -556,20 +541,17 @@ export class _CfgProductInternal {
|
|
|
556
541
|
}
|
|
557
542
|
}
|
|
558
543
|
get notes() {
|
|
559
|
-
|
|
560
|
-
return this.getNotes((_b = this.rawProductData.noteRefs) !== null && _b !== void 0 ? _b : []);
|
|
544
|
+
return this.getNotes(this.rawProductData.noteRefs ?? []);
|
|
561
545
|
}
|
|
562
546
|
get miscFiles() {
|
|
563
|
-
|
|
564
|
-
return (_b = this._rawProductData.miscFiles) !== null && _b !== void 0 ? _b : [];
|
|
547
|
+
return this._rawProductData.miscFiles ?? [];
|
|
565
548
|
}
|
|
566
549
|
get hasRootFeaturesChanged() {
|
|
567
550
|
return (this._configuration._internal.hasRootFeaturesChanged ||
|
|
568
551
|
this.additionalProducts.some((p) => p._internal.hasRootFeaturesChanged));
|
|
569
552
|
}
|
|
570
553
|
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;
|
|
554
|
+
return this._additionalProductRef?.refDescription ?? this._rawProductData.description;
|
|
573
555
|
}
|
|
574
556
|
get rootNodeSources() {
|
|
575
557
|
return this._rawProductData.models;
|
|
@@ -587,25 +569,21 @@ export class _CfgProductInternal {
|
|
|
587
569
|
return this._rawProductData.partsData.prices;
|
|
588
570
|
}
|
|
589
571
|
get measureDefinitions() {
|
|
590
|
-
var _b;
|
|
591
572
|
if (this._measureDefinitions === undefined) {
|
|
592
|
-
this._measureDefinitions = (
|
|
573
|
+
this._measureDefinitions = (this._rawProductData.measurements ?? [])
|
|
593
574
|
.map((m) => CfgMeasureDefinition.make(m))
|
|
594
575
|
.filter((m) => m !== undefined);
|
|
595
576
|
}
|
|
596
577
|
return this._measureDefinitions;
|
|
597
578
|
}
|
|
598
579
|
get refKey() {
|
|
599
|
-
|
|
600
|
-
return (_b = this._additionalProductRef) === null || _b === void 0 ? void 0 : _b.refKey;
|
|
580
|
+
return this._additionalProductRef?.refKey;
|
|
601
581
|
}
|
|
602
582
|
get transform() {
|
|
603
|
-
|
|
604
|
-
return (_b = this._additionalProductRef) === null || _b === void 0 ? void 0 : _b.transform;
|
|
583
|
+
return this._additionalProductRef?.transform;
|
|
605
584
|
}
|
|
606
585
|
get anchor() {
|
|
607
|
-
|
|
608
|
-
return (_b = this._additionalProductRef) === null || _b === void 0 ? void 0 : _b.anchor;
|
|
586
|
+
return this._additionalProductRef?.anchor;
|
|
609
587
|
}
|
|
610
588
|
/** @throws an error if the actual unit sent by the server was not a LengthUnit */
|
|
611
589
|
get unit() {
|
|
@@ -641,34 +619,32 @@ export class _CfgProductInternal {
|
|
|
641
619
|
get optional() {
|
|
642
620
|
return this._selected !== undefined;
|
|
643
621
|
}
|
|
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;
|
|
622
|
+
async setSelected(selected, bubbleMode, interactive) {
|
|
623
|
+
if (!this.optional) {
|
|
624
|
+
console.warn("This product is not optional. Nothing will happen");
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
if (this._selected === selected) {
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
630
|
+
// Vitally important that this happens before the call to reset,
|
|
631
|
+
// so that the reset won't cause infinite loops
|
|
632
|
+
this._selected = selected;
|
|
633
|
+
if (interactive) {
|
|
634
|
+
if (selected) {
|
|
635
|
+
const syncGroupHandler = this.syncGroupHandler;
|
|
636
|
+
if (syncGroupHandler !== undefined) {
|
|
637
|
+
await syncGroupHandler.init(this.root, wrapWithCache(this._productLoaderRaw));
|
|
667
638
|
}
|
|
668
639
|
}
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
640
|
+
else {
|
|
641
|
+
await this.reset();
|
|
642
|
+
// In case a passed initial configuration has made it reset to selected
|
|
643
|
+
this._selected = selected;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
await this._notifyAllOfChange(bubbleMode, true);
|
|
647
|
+
return true;
|
|
672
648
|
}
|
|
673
649
|
get configuration() {
|
|
674
650
|
return this._configuration;
|
|
@@ -711,8 +687,7 @@ export class _CfgProductInternal {
|
|
|
711
687
|
return this.root._syncGroupHandler;
|
|
712
688
|
}
|
|
713
689
|
get syncGroupsVerboseLogging() {
|
|
714
|
-
|
|
715
|
-
return (_c = (_b = this.syncGroupHandler) === null || _b === void 0 ? void 0 : _b.verboseLogging) !== null && _c !== void 0 ? _c : false;
|
|
690
|
+
return this.syncGroupHandler?.verboseLogging ?? false;
|
|
716
691
|
}
|
|
717
692
|
/**
|
|
718
693
|
* Set to true to get verbose sync state changes logged to the console.
|
|
@@ -726,8 +701,8 @@ export class _CfgProductInternal {
|
|
|
726
701
|
}
|
|
727
702
|
}
|
|
728
703
|
_a = _CfgProductInternal;
|
|
729
|
-
_CfgProductInternal.make = (productLoaderRaw, productLoaderForGroupedLoad, // Used when instantiating the current product
|
|
730
|
-
prodParams, settings, optional, loadingObservable, parent, root, additionalProductRef, initialProductConfiguration) =>
|
|
704
|
+
_CfgProductInternal.make = async (productLoaderRaw, productLoaderForGroupedLoad, // Used when instantiating the current product
|
|
705
|
+
prodParams, settings, optional, loadingObservable, parent, root, additionalProductRef, initialProductConfiguration) => {
|
|
731
706
|
// Wrap with cache will make getProduct for this function call use the same server call
|
|
732
707
|
// for the same product with the same params. Not retained for future calls, only used
|
|
733
708
|
// at this initial load.
|
|
@@ -756,39 +731,38 @@ prodParams, settings, optional, loadingObservable, parent, root, additionalProdu
|
|
|
756
731
|
initialProductConfigurationV1 = initialProductConfiguration;
|
|
757
732
|
}
|
|
758
733
|
}
|
|
759
|
-
const productResponse =
|
|
734
|
+
const productResponse = await (initialProductConfigurationV1
|
|
760
735
|
? productLoaderForGroupedLoad.postValidate(defaultCorrectedProdParams, {
|
|
761
736
|
knownFeatureCodes: [],
|
|
762
737
|
selOptions: initialProductConfigurationV1.selOptions,
|
|
763
738
|
})
|
|
764
739
|
: productLoaderForGroupedLoad.getProduct(defaultCorrectedProdParams));
|
|
765
740
|
const { productData, rootFeatureRefs, features: rawFeatures, notes, uuid, unit, } = productResponse;
|
|
766
|
-
const initiallySelected = !optional ||
|
|
767
|
-
const product =
|
|
768
|
-
var _b;
|
|
741
|
+
const initiallySelected = !optional || initialProductConfigurationV1?.selected === true;
|
|
742
|
+
const product = await new Promise((initSuccess, initFail) => {
|
|
769
743
|
const p = new _CfgProductInternal(() => {
|
|
770
744
|
// We absolutely do not want anyone to assign to this._configuration. So we want that field private.
|
|
771
745
|
// But we can not set the api selection synchronously. And the product configuration needs "this". So we use this callback.
|
|
772
746
|
// Feel free to find a nicer more readable solution :)
|
|
773
747
|
initSuccess(p);
|
|
774
|
-
}, initFail, productLoaderRaw, prodParams, settings, optional, initiallySelected, rootFeatureRefs
|
|
748
|
+
}, 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
749
|
});
|
|
776
|
-
|
|
777
|
-
product._initialClone =
|
|
750
|
+
await product._syncAndLoadAdditionalProducts(productLoaderForGroupedLoad, initialProductConfigurationV1);
|
|
751
|
+
product._initialClone = await product.clone();
|
|
778
752
|
if (syncGroupHandler !== undefined && !initialProductConfiguration) {
|
|
779
753
|
// As syncGroupHandler is only set for root product we know that we will init with root.
|
|
780
754
|
// If we have an initialProductConfiguration we do not run init for the syncGroups.
|
|
781
755
|
// The idea with an initial configuration is that the product should look as it did when
|
|
782
756
|
// the configuration was saved. Not running init does that. Also, the DtoProductConf
|
|
783
757
|
// object can contain a sync state which is then used to populate the sync state.
|
|
784
|
-
|
|
758
|
+
await syncGroupHandler.init(product, productLoaderForGroupedLoad);
|
|
785
759
|
}
|
|
786
760
|
return product;
|
|
787
761
|
}
|
|
788
762
|
catch (e) {
|
|
789
763
|
throw augmentErrorMessage(e, `Load product request (${prodParams.partNumber}) failure`);
|
|
790
764
|
}
|
|
791
|
-
}
|
|
765
|
+
};
|
|
792
766
|
export class CfgProduct {
|
|
793
767
|
constructor(_internal) {
|
|
794
768
|
this._internal = _internal;
|
|
@@ -799,13 +773,13 @@ export class CfgProduct {
|
|
|
799
773
|
*/
|
|
800
774
|
this.destroy = () => this._internal.destroy();
|
|
801
775
|
/** Makes a clone of this. It is disconnected from the original. */
|
|
802
|
-
this.clone = () =>
|
|
776
|
+
this.clone = async () => CfgProduct._makeNewRefFrom(await this._internal.clone());
|
|
803
777
|
/**
|
|
804
778
|
* Only applicable when this product is optional.
|
|
805
779
|
* Setting this does not cause a validation call as toggling an optional additional product is
|
|
806
780
|
* assumed to always be legal.
|
|
807
781
|
*/
|
|
808
|
-
this.setSelected = (v) =>
|
|
782
|
+
this.setSelected = async (v) => await this._internal.setSelected(v, CfgProductBubbleMode.ToRootAndBubbleSelected, true);
|
|
809
783
|
/**
|
|
810
784
|
* Experimental. Additional products lacks descriptions or keys that are suitably for structure
|
|
811
785
|
* compare, so we use strict-order when trying to match the additional products. This makes
|
|
@@ -819,8 +793,8 @@ export class CfgProduct {
|
|
|
819
793
|
* This method does not propagate its selections.
|
|
820
794
|
* This method will cause validation calls if something change.
|
|
821
795
|
*/
|
|
822
|
-
this.tryMatchSelection = (other, descriptionMatch = false // Match on case insensitive description, not code
|
|
823
|
-
) =>
|
|
796
|
+
this.tryMatchSelection = async (other, descriptionMatch = false // Match on case insensitive description, not code
|
|
797
|
+
) => await this._internal.tryMatchSelection(other._internal, descriptionMatch);
|
|
824
798
|
/**
|
|
825
799
|
* Gets what selections has been made on the product, recursively including product
|
|
826
800
|
* configuration, optional products and additional products. Used when a full view of all
|
|
@@ -836,7 +810,7 @@ export class CfgProduct {
|
|
|
836
810
|
* the right models are loaded.
|
|
837
811
|
* @deprecated setDtoConf uses a newer format.
|
|
838
812
|
*/
|
|
839
|
-
this.setApiSelection = (configuration, doValidate = true) =>
|
|
813
|
+
this.setApiSelection = async (configuration, doValidate = true) => await this._internal.setApiSelection(configuration, doValidate);
|
|
840
814
|
/**
|
|
841
815
|
* A newer alternative version of getApiSelection. This returns the configuration (selections)
|
|
842
816
|
* on the product, recursively including product configuration, optional products and additional
|
|
@@ -858,7 +832,7 @@ export class CfgProduct {
|
|
|
858
832
|
* @param doValidate Makes a server side validation call. These are necessary to ensure that
|
|
859
833
|
* the right models are loaded.
|
|
860
834
|
*/
|
|
861
|
-
this.setDtoConf = (configuration, doValidate = true) =>
|
|
835
|
+
this.setDtoConf = async (configuration, doValidate = true) => await this._internal.setDtoConf(configuration, doValidate);
|
|
862
836
|
this.listenForChange = (l) => this._internal.changeObservable.listen(l);
|
|
863
837
|
this.stopListenForChange = (l) => this._internal.changeObservable.stopListen(l);
|
|
864
838
|
this.stopAllListenForChange = () => this._internal.changeObservable.stopAllListen();
|
|
@@ -866,10 +840,8 @@ export class CfgProduct {
|
|
|
866
840
|
this.stopListenForLoading = (l) => this._internal.loadingObservable.stopListen(l);
|
|
867
841
|
this.stopAllListenForLoading = () => this._internal.loadingObservable.stopAllListen();
|
|
868
842
|
}
|
|
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
|
-
});
|
|
843
|
+
static async make(productLoader, prodParams, settings, initialProductConfiguration) {
|
|
844
|
+
return this._makeNewRefFrom(await _CfgProductInternal.make(productLoader, undefined, prodParams, completeSettings(settings), false, new AggregatedLoadingObservable(), undefined, undefined, undefined, initialProductConfiguration));
|
|
873
845
|
}
|
|
874
846
|
/**
|
|
875
847
|
* Makes an object wrapping the passed object. This is not a clone method, it is a method to
|