@configura/web-api 1.6.0-iotest.4 → 1.6.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/.eslintrc.json +18 -0
  2. package/LICENSE +201 -201
  3. package/README.md +1 -1
  4. package/dist/CatalogueAPI.d.ts +507 -511
  5. package/dist/CatalogueAPI.js +280 -273
  6. package/dist/CfgMeasure.d.ts +32 -32
  7. package/dist/CfgMeasure.js +30 -30
  8. package/dist/CfgProduct.d.ts +253 -240
  9. package/dist/CfgProduct.js +733 -668
  10. package/dist/index.d.ts +20 -24
  11. package/dist/index.js +20 -24
  12. package/dist/material/CfgMaterialMapping.d.ts +7 -7
  13. package/dist/material/CfgMaterialMapping.js +181 -181
  14. package/dist/material/CfgMtrlApplication.d.ts +18 -18
  15. package/dist/material/CfgMtrlApplication.js +43 -43
  16. package/dist/material/CfgMtrlApplicationSource.d.ts +7 -7
  17. package/dist/material/CfgMtrlApplicationSource.js +8 -8
  18. package/dist/material/CfgMtrlSource.d.ts +19 -19
  19. package/dist/material/CfgMtrlSource.js +40 -40
  20. package/dist/material/CfgMtrlSourceWithMetaData.d.ts +7 -7
  21. package/dist/material/CfgMtrlSourceWithMetaData.js +1 -1
  22. package/dist/productConfiguration/CfgFeature.d.ts +188 -178
  23. package/dist/productConfiguration/CfgFeature.js +636 -611
  24. package/dist/productConfiguration/CfgOption.d.ts +150 -128
  25. package/dist/productConfiguration/CfgOption.js +426 -394
  26. package/dist/productConfiguration/CfgProductConfiguration.d.ts +120 -121
  27. package/dist/productConfiguration/CfgProductConfiguration.js +309 -306
  28. package/dist/productConfiguration/filters.d.ts +15 -15
  29. package/dist/productConfiguration/filters.js +70 -67
  30. package/dist/productConfiguration/productParamsGenerator.d.ts +15 -15
  31. package/dist/productConfiguration/productParamsGenerator.js +51 -51
  32. package/dist/productConfiguration/utilitiesProductConfiguration.d.ts +17 -17
  33. package/dist/productConfiguration/utilitiesProductConfiguration.js +80 -80
  34. package/dist/productLoader.d.ts +33 -33
  35. package/dist/productLoader.js +49 -49
  36. package/dist/syncGroups/SyncGroupsApplyMode.d.ts +21 -0
  37. package/dist/syncGroups/SyncGroupsApplyMode.js +21 -0
  38. package/dist/syncGroups/SyncGroupsHandler.d.ts +36 -0
  39. package/dist/syncGroups/SyncGroupsHandler.js +349 -0
  40. package/dist/syncGroups/SyncGroupsPathHelper.d.ts +27 -0
  41. package/dist/syncGroups/SyncGroupsPathHelper.js +90 -0
  42. package/dist/syncGroups/SyncGroupsState.d.ts +26 -0
  43. package/dist/syncGroups/SyncGroupsState.js +113 -0
  44. package/dist/syncGroups/SyncGroupsTransaction.d.ts +155 -0
  45. package/dist/syncGroups/SyncGroupsTransaction.js +576 -0
  46. package/dist/tasks/TaskHandler.d.ts +78 -78
  47. package/dist/tasks/TaskHandler.js +276 -265
  48. package/dist/tasks/formats.d.ts +4 -4
  49. package/dist/tasks/formats.js +7 -7
  50. package/dist/tests/testData/collectorForTest.d.ts +73 -73
  51. package/dist/tests/testData/collectorForTest.js +194 -195
  52. package/dist/tests/testData/dummyProductForTest.d.ts +4 -4
  53. package/dist/tests/testData/dummyProductForTest.js +36 -36
  54. package/dist/tests/testData/testDataAdditionalProductInAdditionalProductInProductForTest.d.ts +11 -32
  55. package/dist/tests/testData/testDataAdditionalProductInAdditionalProductInProductForTest.js +277 -348
  56. package/dist/tests/testData/testDataCachedGetProduct.d.ts +5 -5
  57. package/dist/tests/testData/testDataCachedGetProduct.js +185 -196
  58. package/dist/tests/testData/testDataCachedPostValidate.d.ts +7 -7
  59. package/dist/tests/testData/testDataCachedPostValidate.js +183 -183
  60. package/dist/tests/testData/testDataNoAdditionalProductNoPropagateForTest.d.ts +3 -3
  61. package/dist/tests/testData/testDataNoAdditionalProductNoPropagateForTest.js +1099 -1099
  62. package/dist/tests/testData/testDataOptions.d.ts +13 -0
  63. package/dist/tests/testData/testDataOptions.js +60 -0
  64. package/dist/tests/testData/testDataProductAggregatedPrice.d.ts +6 -6
  65. package/dist/tests/testData/testDataProductAggregatedPrice.js +187 -198
  66. package/dist/tests/testData/testDataUpcharge.d.ts +8 -29
  67. package/dist/tests/testData/testDataUpcharge.js +119 -151
  68. package/dist/utilitiesCatalogueData.d.ts +31 -25
  69. package/dist/utilitiesCatalogueData.js +162 -65
  70. package/dist/utilitiesCataloguePermission.d.ts +37 -39
  71. package/dist/utilitiesCataloguePermission.js +80 -84
  72. package/dist/utilitiesNumericValues.d.ts +24 -24
  73. package/dist/utilitiesNumericValues.js +109 -109
  74. package/package.json +3 -3
  75. package/dist/io/CfgHistoryManager.d.ts +0 -26
  76. package/dist/io/CfgHistoryManager.js +0 -58
  77. package/dist/io/CfgHistoryToCameraConfConnector.d.ts +0 -15
  78. package/dist/io/CfgHistoryToCameraConfConnector.js +0 -58
  79. package/dist/io/CfgHistoryToProdConfConnector.d.ts +0 -14
  80. package/dist/io/CfgHistoryToProdConfConnector.js +0 -24
  81. package/dist/io/CfgIOCameraConfConnector.d.ts +0 -31
  82. package/dist/io/CfgIOCameraConfConnector.js +0 -72
  83. package/dist/io/CfgIOManager.d.ts +0 -29
  84. package/dist/io/CfgIOManager.js +0 -89
  85. package/dist/io/CfgIOProdConfConnector.d.ts +0 -36
  86. package/dist/io/CfgIOProdConfConnector.js +0 -102
  87. package/dist/io/CfgWindowMessageManager.d.ts +0 -13
  88. package/dist/io/CfgWindowMessageManager.js +0 -33
  89. package/dist/io/CfgWindowMessageToCameraConfConnector.d.ts +0 -6
  90. package/dist/io/CfgWindowMessageToCameraConfConnector.js +0 -6
  91. package/dist/io/CfgWindowMessageToProdConfConnector.d.ts +0 -14
  92. package/dist/io/CfgWindowMessageToProdConfConnector.js +0 -17
@@ -1,668 +1,733 @@
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
- import { AggregatedLoadingObservable, compareArrays, count, Observable, toLengthUnit, } from "@configura/web-utilities";
11
- import { CfgMeasureDefinition } from "./CfgMeasure.js";
12
- import { ProductConfigurationBubbleMode } from "./productConfiguration/CfgOption.js";
13
- import { CfgProductConfiguration } from "./productConfiguration/CfgProductConfiguration.js";
14
- import { collectAdditionalProductRefs } from "./productConfiguration/utilitiesProductConfiguration.js";
15
- import { wrapWithCache } from "./productLoader.js";
16
- import { comparePricesObjects, correctDefaultsOnCatalogueParams, makeProductKey, } from "./utilitiesCatalogueData.js";
17
- function completeSettings(incompleteSettings) {
18
- var _a, _b;
19
- return {
20
- strictSelectOneSelectionCount: (_a = incompleteSettings === null || incompleteSettings === void 0 ? void 0 : incompleteSettings.strictSelectOneSelectionCount) !== null && _a !== void 0 ? _a : false,
21
- strictSetApiSelectionMatch: (_b = incompleteSettings === null || incompleteSettings === void 0 ? void 0 : incompleteSettings.strictSetApiSelectionMatch) !== null && _b !== void 0 ? _b : false,
22
- };
23
- }
24
- /**
25
- * This enum is used internally in the SDK and is not expected by be used directly by integrators.
26
- */
27
- export var CfgProductBubbleMode;
28
- (function (CfgProductBubbleMode) {
29
- /** Stop bubbling. */
30
- CfgProductBubbleMode["Stop"] = "Stop";
31
- /**
32
- * Bubble to the parent CfgProduct up the tree.
33
- * This makes the CfgProduct we we call from notify that it has changed, and the CfgProduct
34
- * above switch out the reference to this.
35
- */
36
- CfgProductBubbleMode["OneLevel"] = "OneLevel";
37
- /** Bubble to the root CfgProduct. */
38
- CfgProductBubbleMode["ToRoot"] = "ToRoot";
39
- /** Bubble to the root CfgProduct and turn on all optional CfgProducts on the way up. */
40
- CfgProductBubbleMode["ToRootAndBubbleSelected"] = "ToRootAndBubbleSelected";
41
- })(CfgProductBubbleMode || (CfgProductBubbleMode = {}));
42
- function isDescriptionMatch(l, r) {
43
- const ld = l.description;
44
- const rd = r.description;
45
- return ld !== undefined && rd !== undefined && ld.toLowerCase() === rd.toLowerCase();
46
- }
47
- /**
48
- * This class is meant to only be used through CfgProduct. It should never be instantiated on its
49
- * own. Normally the internal state of this class should never be directly modified. CfgProduct is
50
- * the class that should be used and interacted with.
51
- */
52
- export class _CfgProductInternal {
53
- constructor(initSuccess, initFail, _productLoaderRaw, lang, catId, partNumber, settings, optional, selected, rootFeatureRefs, allRawFeatures, uuid, _rawUnit, _rawProductData, apiSelection, loadingObservable, refKey, refDescription, parent, root, transform, anchor) {
54
- this._productLoaderRaw = _productLoaderRaw;
55
- this.lang = lang;
56
- this.catId = catId;
57
- this.partNumber = partNumber;
58
- this.settings = settings;
59
- this.uuid = uuid;
60
- this._rawUnit = _rawUnit;
61
- this._rawProductData = _rawProductData;
62
- this.loadingObservable = loadingObservable;
63
- this.refKey = refKey;
64
- this.refDescription = refDescription;
65
- this.parent = parent;
66
- this.transform = transform;
67
- this.anchor = anchor;
68
- this._destroyed = false;
69
- this.additionalProducts = [];
70
- this.changeObservable = new Observable();
71
- this.destroy = () => {
72
- this._destroyed = true;
73
- this.changeObservable.stopAllListen();
74
- this.configuration.stopAllListenForChange();
75
- for (const additionalProduct of this.additionalProducts || []) {
76
- additionalProduct.destroy();
77
- }
78
- };
79
- this._notifyAllOfChange = (bubbleMode, committed) => __awaiter(this, void 0, void 0, function* () {
80
- if (bubbleMode === CfgProductBubbleMode.Stop) {
81
- return;
82
- }
83
- const parent = this.parent;
84
- const freshRef = CfgProduct._makeNewRefFrom(this);
85
- this.changeObservable.notifyAll({ freshRef, committed });
86
- if (parent !== undefined) {
87
- yield parent._additionalProductHasChanged(freshRef, bubbleMode === CfgProductBubbleMode.OneLevel
88
- ? CfgProductBubbleMode.Stop
89
- : bubbleMode, committed);
90
- }
91
- });
92
- /** Called when a child (additional product or the configuration) has changed. */
93
- this._childHasChanged = (bubbleMode, committed) => __awaiter(this, void 0, void 0, function* () {
94
- if (bubbleMode === CfgProductBubbleMode.ToRootAndBubbleSelected &&
95
- this.optional &&
96
- !this.selected) {
97
- yield this.setSelected(true, bubbleMode);
98
- return;
99
- }
100
- yield this._notifyAllOfChange(bubbleMode, committed);
101
- });
102
- /** Called by child to tell its parent that it has changed. */
103
- this._additionalProductHasChanged = (freshRef, bubbleMode, committed) => __awaiter(this, void 0, void 0, function* () {
104
- const i = this.additionalProducts.findIndex((a) => a.isBackedBySame(freshRef));
105
- if (i !== -1) {
106
- // Child additional product might not be found. This probably means that propagate
107
- // has chopped the branch your on. Let's say we have products A -> B -> C.
108
- // We change an option on C. This is propagated to product A. Product A now
109
- // changes its additional product so that B is no longer a child of A. Hence
110
- // C no longer is part of the tree. Odd, but fully permitted.
111
- this.additionalProducts[i] = freshRef;
112
- }
113
- yield this._childHasChanged(bubbleMode, committed);
114
- });
115
- /** Called by the configuration to tell its parent that it has changed. */
116
- this._configurationHasChanged = (freshRef, bubbleMode, committed) => __awaiter(this, void 0, void 0, function* () {
117
- this._configuration = freshRef;
118
- switch (bubbleMode) {
119
- case ProductConfigurationBubbleMode.ValidateAndBubbleSelected:
120
- // The revalidate call will continue the bubble
121
- yield this._revalidate(CfgProductBubbleMode.ToRootAndBubbleSelected, this._productLoaderRaw, committed);
122
- return;
123
- case ProductConfigurationBubbleMode.Validate:
124
- // The revalidate call will continue the bubble
125
- yield this._revalidate(CfgProductBubbleMode.ToRoot, this._productLoaderRaw, committed);
126
- return;
127
- case ProductConfigurationBubbleMode.ToParentProduct:
128
- // Do not continue bubble as we have reached the parent CfgProduct
129
- return;
130
- case ProductConfigurationBubbleMode.OneLevel:
131
- yield this._childHasChanged(CfgProductBubbleMode.OneLevel, committed);
132
- return;
133
- case ProductConfigurationBubbleMode.Stop:
134
- yield this._childHasChanged(CfgProductBubbleMode.Stop, committed);
135
- return;
136
- case ProductConfigurationBubbleMode.ToRoot:
137
- yield this._childHasChanged(CfgProductBubbleMode.ToRoot, committed);
138
- return;
139
- }
140
- });
141
- this.getApiSelection = () => ({
142
- refKey: this.refKey,
143
- selected: this.selected,
144
- selOptions: this.configuration.getApiSelection(),
145
- additionalProducts: this.additionalProducts.map((additionalProduct) => additionalProduct.getApiSelection()),
146
- });
147
- this.setApiSelection = (s, doValidate, productLoaderForGroupedLoad) => __awaiter(this, void 0, void 0, function* () {
148
- // Wrap with cache will make getProduct for this function call use the same server call
149
- // for the same product with the same params. Used for getProduct (when a new additional
150
- // product is loaded) and postValidate.
151
- productLoaderForGroupedLoad =
152
- productLoaderForGroupedLoad || wrapWithCache(this._productLoaderRaw);
153
- const configurationChange = yield this.configuration._internal.setApiSelection(s.selOptions, false);
154
- let change = configurationChange;
155
- if (this.optional) {
156
- if (yield this.setSelected(s.selected !== false, CfgProductBubbleMode.Stop)) {
157
- change = true;
158
- }
159
- }
160
- yield this._syncAndLoadAdditionalProducts(productLoaderForGroupedLoad);
161
- const apiSelectionAdditionalProducts = s.additionalProducts || [];
162
- const additionalProducts = this.additionalProducts.slice();
163
- const additionalProductsCount = additionalProducts.length;
164
- const apiSelectionAdditionalProductsCount = apiSelectionAdditionalProducts.length;
165
- if (apiSelectionAdditionalProductsCount !== additionalProductsCount) {
166
- 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}`);
167
- }
168
- if ((yield Promise.all(apiSelectionAdditionalProducts.map((apiSelectionChild) => {
169
- const refKey = apiSelectionChild.refKey;
170
- if (refKey === undefined) {
171
- throw new Error("Additional product api configurations must have refKey.");
172
- }
173
- const i = additionalProducts.findIndex((a) => refKey === a.refKey);
174
- if (i === -1) {
175
- throw new Error(`Additional product not found. This product: "${this.key}". refKey of not found additional product: "${refKey}"`);
176
- }
177
- const additionalProduct = additionalProducts.splice(i, 1)[0]; // Splicing like this is okay because this is done synchronous. The setCon. is what is async.
178
- return additionalProduct._internal.setApiSelection(apiSelectionChild, doValidate, productLoaderForGroupedLoad);
179
- }))).some((b) => b)) {
180
- change = true;
181
- }
182
- if (doValidate && configurationChange) {
183
- yield this._revalidate(CfgProductBubbleMode.ToRoot, productLoaderForGroupedLoad, true);
184
- }
185
- else if (change) {
186
- // As setApiSelection is done recursively each level takes care of its own notifications
187
- // so we only need to bubble one level to notify this and swap out the reference in the
188
- // parent.
189
- yield this._notifyAllOfChange(CfgProductBubbleMode.OneLevel, true);
190
- }
191
- return change;
192
- });
193
- this.structureCompare = (other, strictOrder = true, descriptionMatch = false) => {
194
- if (!this.configuration.structureCompare(other.configuration, strictOrder, descriptionMatch)) {
195
- return false;
196
- }
197
- return compareArrays(this.additionalProducts, other.additionalProducts, (l, r) => (!descriptionMatch || (descriptionMatch && isDescriptionMatch(l, r))) &&
198
- l.structureCompare(r, strictOrder, descriptionMatch), !descriptionMatch);
199
- };
200
- this.tryMatchSelection = (other, descriptionMatch = false, // Match on case insensitive description, not code
201
- productLoaderForGroupedLoad) => __awaiter(this, void 0, void 0, function* () {
202
- // wrap with cache will make getProduct for this function call use the same server call
203
- // for the same product with the same params
204
- productLoaderForGroupedLoad =
205
- productLoaderForGroupedLoad || wrapWithCache(this._productLoaderRaw);
206
- let change = false;
207
- if (this.optional && other.optional) {
208
- if (yield this.setSelected(other.selected, CfgProductBubbleMode.Stop)) {
209
- change = true;
210
- }
211
- }
212
- const configurationChange = yield this.configuration._internal.tryMatchSelection(other.configuration._internal, descriptionMatch, false);
213
- if (configurationChange) {
214
- change = true;
215
- yield this._revalidate(CfgProductBubbleMode.ToRootAndBubbleSelected, productLoaderForGroupedLoad, true);
216
- }
217
- else if (change) {
218
- yield this._notifyAllOfChange(CfgProductBubbleMode.ToRootAndBubbleSelected, true);
219
- }
220
- const thisAdditionalProducts = this.additionalProducts;
221
- const otherAdditionalProducts = other.additionalProducts;
222
- const promises = [];
223
- if (descriptionMatch) {
224
- for (const otherAdditionalProduct of otherAdditionalProducts) {
225
- if (1 <
226
- count(otherAdditionalProducts, (product) => isDescriptionMatch(product, otherAdditionalProduct))) {
227
- console.warn("tryMatchSelection will ignore products that have the same description");
228
- continue;
229
- }
230
- const toTryMatchProducts = thisAdditionalProducts.filter((product) => isDescriptionMatch(product, otherAdditionalProduct));
231
- if (1 < toTryMatchProducts.length) {
232
- console.warn("tryMatchSelection will ignore products that have the same description");
233
- continue;
234
- }
235
- if (toTryMatchProducts.length === 0) {
236
- continue;
237
- }
238
- promises.push(toTryMatchProducts[0]._internal.tryMatchSelection(otherAdditionalProduct._internal, descriptionMatch, productLoaderForGroupedLoad));
239
- }
240
- }
241
- else {
242
- if (thisAdditionalProducts.length !== otherAdditionalProducts.length) {
243
- console.warn("tryMatchSelection not same count, will not try to match further.");
244
- return change;
245
- }
246
- for (let i = 0; i < thisAdditionalProducts.length; i++) {
247
- promises.push(thisAdditionalProducts[i]._internal.tryMatchSelection(otherAdditionalProducts[i]._internal, descriptionMatch, productLoaderForGroupedLoad));
248
- }
249
- }
250
- if ((yield Promise.all(promises)).some((b) => b)) {
251
- change = true;
252
- }
253
- return change;
254
- });
255
- /** Only features in selected options and selected additional products. */
256
- this._getDescendantFeaturesWithCode = (code) => this.additionalProducts.reduce((agg, additionalProduct) => {
257
- if (additionalProduct.selected) {
258
- agg.push(...additionalProduct._internal._getDescendantFeaturesWithCode(code));
259
- }
260
- return agg;
261
- }, this._configuration._internal._getFeaturesWithCode(code));
262
- /**
263
- * Do a validate call for this product. It does not validate additional products, only this
264
- * product in isolation. The validation result is applied on the configuration. Then additional
265
- * products are synced (unloaded, loaded etc.) Finally the changes bubble up the tree.
266
- */
267
- this._revalidate = (bubbleMode, productLoader, committed) => __awaiter(this, void 0, void 0, function* () {
268
- const { _configuration: configuration } = this;
269
- const token = this.loadingObservable.startChildLoading();
270
- this._revalidateInProgressToken = token;
271
- try {
272
- const response = yield productLoader.postValidate(Object.assign(Object.assign({ lang: this.lang }, correctDefaultsOnCatalogueParams(this.catId)), { partNumber: this.partNumber }), { selOptions: configuration.getApiSelection() });
273
- // The revalidateInProgressToken is used to know if some other revalidate
274
- // call has happened after this call, thereby making this call obsolete.
275
- // This is a bit crude in that it does not actually cancel previous validate
276
- // calls, but this is fine because 1. it is hard to cancel a call, especially
277
- // if you wanna have clear code 2. the actual calls are small anyhow 3. most
278
- // of all, the heavy work happens on the server, and that work will not be
279
- // cancelled even if we would cancel the call.
280
- if (this._revalidateInProgressToken !== token) {
281
- return false;
282
- }
283
- // After a successful validate-call we will always assume there
284
- // is a change. It would be possible to compare relevant parts
285
- // of productData, consider the result of setApiSelection and
286
- // syndAndLoad, however the code comparing productData would be fragile
287
- // and likely to break if new data-fields were added.
288
- if (this._destroyed) {
289
- return false;
290
- }
291
- const { productData, rootFeatureRefs } = response;
292
- const pricesUpdated = !comparePricesObjects(this.prices, productData.partsData.prices);
293
- this._rawProductData = productData;
294
- if (rootFeatureRefs !== undefined) {
295
- configuration._internal.populateFeatures(rootFeatureRefs);
296
- }
297
- if (pricesUpdated) {
298
- this._configuration._internal._freshRefDescendants();
299
- this._configuration = CfgProductConfiguration._makeNewRefFrom(this._configuration._internal);
300
- }
301
- yield configuration._internal.setApiSelection(productData.partsData.selOptions || [], false);
302
- yield this._syncAndLoadAdditionalProducts(productLoader);
303
- if (this._destroyed) {
304
- return false;
305
- }
306
- yield this._notifyAllOfChange(bubbleMode, committed);
307
- return true;
308
- }
309
- catch (e) {
310
- throw e;
311
- }
312
- finally {
313
- this.loadingObservable.stopChildLoading(token);
314
- }
315
- });
316
- /**
317
- * Based on this configuration find what additional products should be shown and not, unload
318
- * (i.e. destroy) those that should no longer be shown, load the new ones.
319
- */
320
- this._syncAndLoadAdditionalProducts = (productLoaderForGroupedLoad) => __awaiter(this, void 0, void 0, function* () {
321
- const { lang, _productLoaderRaw: productLoaderRaw, rawProductData: productData, configuration, additionalProducts: currentAdditionalProducts, } = this;
322
- const additionalProductRefs = [
323
- ...(productData.additionalProductRefs || []),
324
- ...collectAdditionalProductRefs(configuration),
325
- ].map((prodRef, originalIndex) => ({
326
- prodRef,
327
- originalIndex,
328
- }));
329
- let change = false;
330
- let i = currentAdditionalProducts.length;
331
- while (i--) {
332
- const currentAdditionalProduct = currentAdditionalProducts[i];
333
- const refKey = currentAdditionalProduct.refKey;
334
- const j = additionalProductRefs.findIndex((p) => p.prodRef.refKey === refKey);
335
- if (j === -1) {
336
- currentAdditionalProduct.destroy();
337
- currentAdditionalProducts.splice(i, 1);
338
- change = true;
339
- }
340
- else {
341
- additionalProductRefs.splice(j, 1);
342
- }
343
- }
344
- const token = this.loadingObservable.startChildLoading();
345
- try {
346
- if (additionalProductRefs.length !== 0) {
347
- change = true;
348
- }
349
- const newAdditionalProducts = yield Promise.all(additionalProductRefs.map((p) => (() => __awaiter(this, void 0, void 0, function* () {
350
- const additionalProductRef = p.prodRef;
351
- return {
352
- originalIndex: p.originalIndex,
353
- product: CfgProduct._makeNewRefFrom(yield _CfgProductInternal.make(productLoaderRaw, productLoaderForGroupedLoad, lang, additionalProductRef.catId, additionalProductRef.partNumber, this.settings, additionalProductRef.optional === true, this.loadingObservable, additionalProductRef.refKey, additionalProductRef.refDescription, this, this.root, additionalProductRef.transform, additionalProductRef.anchor)),
354
- };
355
- }))()));
356
- if (this._destroyed) {
357
- return change;
358
- }
359
- for (const newAdditionalProduct of newAdditionalProducts) {
360
- currentAdditionalProducts.splice(newAdditionalProduct.originalIndex, 0, newAdditionalProduct.product);
361
- }
362
- return change;
363
- }
364
- catch (e) {
365
- throw e;
366
- }
367
- finally {
368
- this.loadingObservable.stopChildLoading(token);
369
- }
370
- });
371
- this.root = root || this;
372
- this.key = makeProductKey(catId, refKey || partNumber);
373
- this._selected = optional ? selected : undefined;
374
- this.isAdditionalProduct = parent !== undefined;
375
- this._configuration = CfgProductConfiguration.make(initSuccess, initFail, rootFeatureRefs, allRawFeatures, apiSelection, this, this.root);
376
- }
377
- get selected() {
378
- return this._selected !== false;
379
- }
380
- clone(parent, root) {
381
- return __awaiter(this, void 0, void 0, function* () {
382
- const product = yield new Promise((initSuccess, initFail) => {
383
- const p = new _CfgProductInternal(() => {
384
- initSuccess(p);
385
- }, initFail, this._productLoaderRaw, this.lang, this.catId, this.partNumber, this.settings, this.optional, this.selected, this._configuration.rootFeatureRefs, this._configuration.allRawFeatures, this.uuid, this._rawUnit, this._rawProductData, this.configuration.getApiSelection(), new AggregatedLoadingObservable(), this.refKey, this.refDescription, parent, root, this.transform, this.anchor);
386
- });
387
- for (const additionalProduct of this.additionalProducts) {
388
- product.additionalProducts.push(CfgProduct._makeNewRefFrom(yield additionalProduct._internal.clone(product, root || product)));
389
- }
390
- return product;
391
- });
392
- }
393
- get description() {
394
- return this.refDescription || this._rawProductData.description;
395
- }
396
- get rootNodeSources() {
397
- return this._rawProductData.models;
398
- }
399
- get mtrlApplications() {
400
- return this._rawProductData.mtrlApplications;
401
- }
402
- get currency() {
403
- return this._rawProductData.partsData.currency;
404
- }
405
- get fractionDigits() {
406
- return this._rawProductData.partsData.rounding || 0;
407
- }
408
- get prices() {
409
- return this._rawProductData.partsData.prices;
410
- }
411
- get measureDefinitions() {
412
- var _a;
413
- if (this._measureDefinitions === undefined) {
414
- this._measureDefinitions = ((_a = this._rawProductData.measurements) !== null && _a !== void 0 ? _a : [])
415
- .map((m) => CfgMeasureDefinition.make(m))
416
- .filter((m) => m !== undefined);
417
- }
418
- return this._measureDefinitions;
419
- }
420
- /** @throws an error if the actual unit sent by the server was not a LengthUnit */
421
- get unit() {
422
- if (this._unit === undefined) {
423
- this._unit = toLengthUnit(this._rawUnit);
424
- }
425
- return this._unit;
426
- }
427
- get aggregatedPrice() {
428
- const { currency, fractionDigits, rawProductData } = this;
429
- const { partsData } = rawProductData;
430
- let { listPrice, basePrice } = partsData;
431
- if (this._selected === false) {
432
- return { basePrice: 0, listPrice: 0, currency, fractionDigits };
433
- }
434
- for (const additionalProduct of this.additionalProducts || []) {
435
- const { basePrice: additionalBasePrice, listPrice: additionalListPrice, currency: additionalCurrency, fractionDigits: additionalFractionDigits, } = additionalProduct.aggregatedPrice;
436
- basePrice += additionalBasePrice;
437
- listPrice += additionalListPrice;
438
- if (currency !== additionalCurrency) {
439
- // This should not be possible
440
- // The server shouldn't return additional products with different currency from their parent
441
- throw new Error(`Currency mismatch between parent product and additional product. Parent product: "${this.key}" Additional product: "${additionalProduct.key}". Currency on parent product: ${currency}. Currency on additional product: ${additionalCurrency}`);
442
- }
443
- if (fractionDigits !== additionalFractionDigits) {
444
- // This should not be possible
445
- // The server shouldn't return additional products with different fraction digits from their parent
446
- throw new Error(`Fraction digits mismatch between parent product and additional product. Parent product: "${this.key}" Additional product: "${additionalProduct.key}". Fraction digits on parent product: ${fractionDigits}. Fraction digits on additional product: ${additionalFractionDigits}`);
447
- }
448
- }
449
- return { basePrice, listPrice, currency, fractionDigits };
450
- }
451
- get optional() {
452
- return this._selected !== undefined;
453
- }
454
- setSelected(v, bubbleMode) {
455
- return __awaiter(this, void 0, void 0, function* () {
456
- if (!this.optional) {
457
- console.warn("This product is not optional. Nothing will happen");
458
- return false;
459
- }
460
- if (this._selected === v) {
461
- return false;
462
- }
463
- this._selected = v;
464
- yield this._notifyAllOfChange(bubbleMode, true);
465
- return true;
466
- });
467
- }
468
- get configuration() {
469
- return this._configuration;
470
- }
471
- get rawProductData() {
472
- return this._rawProductData;
473
- }
474
- /**
475
- * Please note that this relates to the visibility in the Configuration tree.
476
- * It does not affect the visibility of anything in the 3D view at all.
477
- */
478
- get visibleIfAdditionalProduct() {
479
- return this.rawProductData.hideIfAdditionalProduct !== true;
480
- }
481
- /**
482
- * Please note that this relates to the visibility in the Configuration tree.
483
- * It does not affect the visibility of anything in the 3D view at all.
484
- */
485
- get visibleIfMainProduct() {
486
- return this.rawProductData.hideIfMainProduct !== true;
487
- }
488
- /**
489
- * Please note that this relates to the visibility in the Configuration tree.
490
- * It does not affect the visibility of anything in the 3D view at all.
491
- */
492
- get visible() {
493
- return this.isAdditionalProduct
494
- ? this.visibleIfAdditionalProduct
495
- : this.visibleIfMainProduct;
496
- }
497
- }
498
- _CfgProductInternal.make = (productLoaderRaw, productLoaderForGroupedLoad, // Used when instantiating the current product
499
- lang, catId, partNumber, settings, optional, loadingObservable, refKey, refDescription, parent, root, transform, anchor) => __awaiter(void 0, void 0, void 0, function* () {
500
- // Wrap with cache will make getProduct for this function call use the same server call
501
- // for the same product with the same params. Not retained for future calls, only used
502
- // at this initial load.
503
- productLoaderForGroupedLoad =
504
- productLoaderForGroupedLoad || wrapWithCache(productLoaderRaw);
505
- const productResponse = yield productLoaderForGroupedLoad.getProduct(Object.assign(Object.assign({ lang }, correctDefaultsOnCatalogueParams(catId)), { partNumber }));
506
- const { productData, rootFeatureRefs, features: allRawFeatures, uuid, unit, } = productResponse;
507
- const product = yield new Promise((initSuccess, initFail) => {
508
- const p = new _CfgProductInternal(() => {
509
- // We absolutely do not want anyone to assign to this._configuration. So we want that field private.
510
- // But we can not set the api selection synchronously. And the product configuration needs "this". So we use this callback.
511
- // Feel free to find a nicer more readable solution :)
512
- initSuccess(p);
513
- }, initFail, productLoaderRaw, lang, catId, partNumber, settings, optional, !optional, rootFeatureRefs, allRawFeatures, uuid, unit, productData, productData.partsData.selOptions || [], loadingObservable, refKey, refDescription, parent, root, transform, anchor);
514
- });
515
- yield product._syncAndLoadAdditionalProducts(productLoaderForGroupedLoad);
516
- return product;
517
- });
518
- export class CfgProduct {
519
- constructor(_internal) {
520
- this._internal = _internal;
521
- this.isBackedBySame = (other) => this._internal === other._internal;
522
- /**
523
- * Recursively marks this and descendants as destroyed so that late events are ignored
524
- * correctly. If you destroy one shallow copy of this you destroy all.
525
- */
526
- this.destroy = () => this._internal.destroy();
527
- /** Makes a clone of this. It is disconnected from the original. */
528
- this.clone = () => __awaiter(this, void 0, void 0, function* () { return CfgProduct._makeNewRefFrom(yield this._internal.clone()); });
529
- /**
530
- * Only applicable when this product is optional.
531
- * Setting this does not cause a validation call as toggling an optional additional product is
532
- * assumed to always be legal.
533
- */
534
- this.setSelected = (v) => __awaiter(this, void 0, void 0, function* () { return yield this._internal.setSelected(v, CfgProductBubbleMode.ToRootAndBubbleSelected); });
535
- /**
536
- * Experimental. Additional products lacks descriptions or keys that are suitably for structure
537
- * compare, so we use strict-order when trying to match the additional products. This makes
538
- * this method work nicely for different products having pretty much the same child products.
539
- */
540
- this.structureCompare = (other, strictOrder = true, descriptionMatch = false) => this._internal.structureCompare(other._internal, strictOrder, descriptionMatch);
541
- /**
542
- * Experimental. Additional products lacks descriptions or keys that are suitably for try
543
- * match, so we use strict-order when trying to match the additional products. This makes
544
- * this method work nicely for different products having pretty much the same child products.
545
- * This method does not propagate its selections.
546
- * This method will cause validation calls if something change.
547
- */
548
- this.tryMatchSelection = (other, descriptionMatch = false // Match on case insensitive description, not code
549
- ) => __awaiter(this, void 0, void 0, function* () { return yield this._internal.tryMatchSelection(other._internal, descriptionMatch); });
550
- /**
551
- * Gets what selections has been made on the product, recursively including product
552
- * configuration, optional products and additional products. Used when a full view of all
553
- * selections on a product is needed, such as when doing Render or Export.
554
- */
555
- this.getApiSelection = () => this._internal.getApiSelection();
556
- this.setApiSelection = (s, doValidate = false) => __awaiter(this, void 0, void 0, function* () { return yield this._internal.setApiSelection(s, doValidate); });
557
- this.listenForChange = (l) => this._internal.changeObservable.listen(l);
558
- this.stopListenForChange = (l) => this._internal.changeObservable.stopListen(l);
559
- this.stopAllListenForChange = () => this._internal.changeObservable.stopAllListen();
560
- this.listenForLoading = (l) => this._internal.loadingObservable.listen(l);
561
- this.stopListenForLoading = (l) => this._internal.loadingObservable.stopListen(l);
562
- this.stopAllListenForLoading = () => this._internal.loadingObservable.stopAllListen();
563
- }
564
- static make(productLoader, lang, catId, partNumber, settings) {
565
- return __awaiter(this, void 0, void 0, function* () {
566
- return this._makeNewRefFrom(yield _CfgProductInternal.make(productLoader, undefined, lang, catId, partNumber, completeSettings(settings), false, new AggregatedLoadingObservable(), undefined, undefined, undefined, undefined, undefined, undefined));
567
- });
568
- }
569
- /**
570
- * Makes an object wrapping the passed object. This is not a clone method, it is a method to
571
- * make a new outer reference. Like a shallow copy./ We use this to help frameworks that are
572
- * build around using equals to detect change.
573
- */
574
- static _makeNewRefFrom(source) {
575
- return new this(source);
576
- }
577
- /**
578
- * A client side only key that should uniquely identify this product amongst other additional
579
- * products.
580
- */
581
- get key() {
582
- return this._internal.key;
583
- }
584
- /**
585
- * Only used when this product is in additional product.
586
- * As a product can have multiple instances of the same additional product this key exists.
587
- * It will be unique amongst child products, but not globally unique.
588
- */
589
- get refKey() {
590
- return this._internal.refKey;
591
- }
592
- get lang() {
593
- return this._internal.lang;
594
- }
595
- get catId() {
596
- return this._internal.catId;
597
- }
598
- get partNumber() {
599
- return this._internal.partNumber;
600
- }
601
- get isAdditionalProduct() {
602
- return this._internal.isAdditionalProduct;
603
- }
604
- /** Only used when this product is an additional product. Root products are never optional. */
605
- get optional() {
606
- return this._internal.optional;
607
- }
608
- /**
609
- * Only applicable when this product is optional. If this product is not optional this is
610
- * always true.
611
- */
612
- get selected() {
613
- return this._internal.selected;
614
- }
615
- /**
616
- * Please note that this relates to the visibility in the Configuration tree.
617
- * It does not affect the visibility of anything in the 3D view at all.
618
- * Visibility is affects the Configuration for this Product, but any Additional Products
619
- * will not be affected.
620
- */
621
- get visible() {
622
- return this._internal.visible;
623
- }
624
- get rawProductData() {
625
- return this._internal.rawProductData;
626
- }
627
- get uuid() {
628
- return this._internal.uuid;
629
- }
630
- get unit() {
631
- return this._internal.unit;
632
- }
633
- get sku() {
634
- return this._internal.rawProductData.sku;
635
- }
636
- get styleNr() {
637
- return this._internal.rawProductData.partsData.styleNr;
638
- }
639
- /** An URL. */
640
- get preview() {
641
- return this._internal.rawProductData.navImage;
642
- }
643
- get description() {
644
- return this._internal.description;
645
- }
646
- get additionalProducts() {
647
- return this._internal.additionalProducts;
648
- }
649
- get configuration() {
650
- return this._internal.configuration;
651
- }
652
- get transform() {
653
- return this._internal.transform;
654
- }
655
- get currency() {
656
- return this._internal.currency;
657
- }
658
- /**
659
- * If positive the number of fraction digits.
660
- * If negative rounding (essentially the number of zeros to the right)
661
- */
662
- get fractionDigits() {
663
- return this._internal.fractionDigits;
664
- }
665
- get aggregatedPrice() {
666
- return this._internal.aggregatedPrice;
667
- }
668
- }
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
+ import { AggregatedLoadingObservable, assert, assertDefined, augmentErrorMessage, compareArrays, count, Observable, toLengthUnit, } from "@configura/web-utilities";
11
+ import { CfgMeasureDefinition } from "./CfgMeasure.js";
12
+ import { ProductConfigurationBubbleMode } from "./productConfiguration/CfgOption.js";
13
+ import { CfgProductConfiguration } from "./productConfiguration/CfgProductConfiguration.js";
14
+ import { collectAdditionalProductRefs } from "./productConfiguration/utilitiesProductConfiguration.js";
15
+ import { wrapWithCache } from "./productLoader.js";
16
+ import { SyncGroupsHandler } from "./syncGroups/SyncGroupsHandler.js";
17
+ import { comparePricesObjects, correctDefaultsOnCatalogueParams, isSameCatalogueParams, isSameProductRef, makeProductKey, } from "./utilitiesCatalogueData.js";
18
+ function completeSettings(incompleteSettings) {
19
+ var _a;
20
+ return {
21
+ strictSelectOneSelectionCount: (_a = incompleteSettings === null || incompleteSettings === void 0 ? void 0 : incompleteSettings.strictSelectOneSelectionCount) !== null && _a !== void 0 ? _a : false,
22
+ syncGroupsApplyMode: incompleteSettings === null || incompleteSettings === void 0 ? void 0 : incompleteSettings.syncGroupsApplyMode,
23
+ };
24
+ }
25
+ /**
26
+ * This enum is used internally in the SDK and is not expected by be used directly by integrators.
27
+ */
28
+ export var CfgProductBubbleMode;
29
+ (function (CfgProductBubbleMode) {
30
+ /** Stop bubbling. */
31
+ CfgProductBubbleMode["Stop"] = "Stop";
32
+ /**
33
+ * Bubble to the parent CfgProduct up the tree.
34
+ * This makes the CfgProduct we we call from notify that it has changed, and the CfgProduct
35
+ * above switch out the reference to this.
36
+ */
37
+ CfgProductBubbleMode["OneLevel"] = "OneLevel";
38
+ /** Bubble to the root CfgProduct. */
39
+ CfgProductBubbleMode["ToRoot"] = "ToRoot";
40
+ /** Bubble to the root CfgProduct and turn on all optional CfgProducts on the way up. */
41
+ CfgProductBubbleMode["ToRootAndBubbleSelected"] = "ToRootAndBubbleSelected";
42
+ })(CfgProductBubbleMode || (CfgProductBubbleMode = {}));
43
+ function isDescriptionMatch(l, r) {
44
+ const ld = l.description;
45
+ const rd = r.description;
46
+ return ld !== undefined && rd !== undefined && ld.toLowerCase() === rd.toLowerCase();
47
+ }
48
+ /**
49
+ * This class is meant to only be used through CfgProduct. It should never be instantiated on its
50
+ * own. Normally the internal state of this class should never be directly modified. CfgProduct is
51
+ * the class that should be used and interacted with.
52
+ */
53
+ export class _CfgProductInternal {
54
+ constructor(initSuccess, initFail, _productLoaderRaw, lang, catId, partNumber, settings, optional, selected, rootFeatureRefs, allRawFeatures, uuid, _rawUnit, _rawProductData, apiSelection, loadingObservable, parent, root, _additionalProductRef, _syncGroupHandler) {
55
+ var _a;
56
+ this._productLoaderRaw = _productLoaderRaw;
57
+ this.lang = lang;
58
+ this.catId = catId;
59
+ this.partNumber = partNumber;
60
+ this.settings = settings;
61
+ this.uuid = uuid;
62
+ this._rawUnit = _rawUnit;
63
+ this._rawProductData = _rawProductData;
64
+ this.loadingObservable = loadingObservable;
65
+ this.parent = parent;
66
+ this._additionalProductRef = _additionalProductRef;
67
+ this._syncGroupHandler = _syncGroupHandler;
68
+ this._destroyed = false;
69
+ this.additionalProducts = [];
70
+ this.changeObservable = new Observable();
71
+ this.destroy = () => {
72
+ this._destroyed = true;
73
+ this.changeObservable.stopAllListen();
74
+ this.configuration.stopAllListenForChange();
75
+ for (const additionalProduct of this.additionalProducts || []) {
76
+ additionalProduct.destroy();
77
+ }
78
+ };
79
+ this._notifyAllOfChange = (bubbleMode) => __awaiter(this, void 0, void 0, function* () {
80
+ if (bubbleMode === CfgProductBubbleMode.Stop) {
81
+ return;
82
+ }
83
+ const parent = this.parent;
84
+ const freshRef = CfgProduct._makeNewRefFrom(this);
85
+ this.changeObservable.notifyAll({ freshRef });
86
+ if (parent !== undefined) {
87
+ yield parent._additionalProductHasChanged(freshRef, bubbleMode === CfgProductBubbleMode.OneLevel
88
+ ? CfgProductBubbleMode.Stop
89
+ : bubbleMode);
90
+ }
91
+ });
92
+ /** Called when a child (additional product or the configuration) has changed. */
93
+ this._childHasChanged = (bubbleMode) => __awaiter(this, void 0, void 0, function* () {
94
+ if (bubbleMode === CfgProductBubbleMode.ToRootAndBubbleSelected &&
95
+ this.optional &&
96
+ !this.selected) {
97
+ yield this.setSelected(true, bubbleMode);
98
+ return;
99
+ }
100
+ yield this._notifyAllOfChange(bubbleMode);
101
+ });
102
+ /** Called by child to tell its parent that it has changed. */
103
+ this._additionalProductHasChanged = (freshRef, bubbleMode) => __awaiter(this, void 0, void 0, function* () {
104
+ const i = this.additionalProducts.findIndex((a) => a.isBackedBySame(freshRef));
105
+ if (i !== -1) {
106
+ // Child additional product might not be found. This probably means that propagate
107
+ // has chopped the branch your on. Let's say we have products A -> B -> C.
108
+ // We change an option on C. This is propagated to product A. Product A now
109
+ // changes its additional product so that B is no longer a child of A. Hence
110
+ // C no longer is part of the tree. Odd, but fully permitted.
111
+ this.additionalProducts[i] = freshRef;
112
+ }
113
+ yield this._childHasChanged(bubbleMode);
114
+ });
115
+ /** Called by the configuration to tell its parent that it has changed. */
116
+ this._configurationHasChanged = (freshRef, bubbleMode) => __awaiter(this, void 0, void 0, function* () {
117
+ this._configuration = freshRef;
118
+ switch (bubbleMode) {
119
+ case ProductConfigurationBubbleMode.ValidateAndBubbleSelected:
120
+ // The revalidate call will continue the bubble
121
+ yield this._revalidate(CfgProductBubbleMode.ToRootAndBubbleSelected, this._productLoaderRaw);
122
+ return;
123
+ case ProductConfigurationBubbleMode.BubbleSelected:
124
+ yield this._childHasChanged(CfgProductBubbleMode.ToRootAndBubbleSelected);
125
+ return;
126
+ case ProductConfigurationBubbleMode.Validate:
127
+ // The revalidate call will continue the bubble
128
+ yield this._revalidate(CfgProductBubbleMode.ToRoot, this._productLoaderRaw);
129
+ return;
130
+ case ProductConfigurationBubbleMode.ToParentProduct:
131
+ // Do not continue bubble as we have reached the parent CfgProduct
132
+ return;
133
+ case ProductConfigurationBubbleMode.OneLevel:
134
+ yield this._childHasChanged(CfgProductBubbleMode.OneLevel);
135
+ return;
136
+ case ProductConfigurationBubbleMode.Stop:
137
+ yield this._childHasChanged(CfgProductBubbleMode.Stop);
138
+ return;
139
+ case ProductConfigurationBubbleMode.ToRoot:
140
+ yield this._childHasChanged(CfgProductBubbleMode.ToRoot);
141
+ return;
142
+ }
143
+ });
144
+ this.getApiSelection = () => ({
145
+ refKey: this.refKey,
146
+ selected: this.selected,
147
+ selOptions: this.configuration.getApiSelection(),
148
+ additionalProducts: this.additionalProducts.map((additionalProduct) => additionalProduct.getApiSelection()),
149
+ });
150
+ this.setApiSelection = (s, doValidate, productLoaderForGroupedLoad) => __awaiter(this, void 0, void 0, function* () {
151
+ return this._setApiSelectionWithOtherProduct(s, doValidate, productLoaderForGroupedLoad, undefined);
152
+ });
153
+ this.copyFrom = (source, doValidate, productLoaderForGroupedLoad) => __awaiter(this, void 0, void 0, function* () {
154
+ return yield this._setApiSelectionWithOtherProduct(source.getApiSelection(), doValidate, productLoaderForGroupedLoad, source);
155
+ });
156
+ this._setApiSelectionWithOtherProduct = (s, doValidate, productLoaderForGroupedLoad, sourceProduct) => __awaiter(this, void 0, void 0, function* () {
157
+ // Wrap with cache will make getProduct for this function call use the same server call
158
+ // for the same product with the same params. Used for getProduct (when a new additional
159
+ // product is loaded) and postValidate.
160
+ productLoaderForGroupedLoad =
161
+ productLoaderForGroupedLoad || wrapWithCache(this._productLoaderRaw);
162
+ let change = false;
163
+ if (sourceProduct !== undefined) {
164
+ this._rawProductData = sourceProduct.rawProductData;
165
+ this.configuration._internal.populateFeatures(sourceProduct.configuration.rootFeatureRefs);
166
+ change = true; // We can not know if this is an actual change, so we assume it is
167
+ }
168
+ const configurationChange = yield this.configuration._internal.setApiSelection(s.selOptions, false);
169
+ if (configurationChange) {
170
+ change = true;
171
+ }
172
+ if (this.optional) {
173
+ if (yield this.setSelected(s.selected !== false, CfgProductBubbleMode.Stop)) {
174
+ change = true;
175
+ }
176
+ }
177
+ yield this._syncAndLoadAdditionalProducts(productLoaderForGroupedLoad);
178
+ const apiSelectionAdditionalProducts = s.additionalProducts || [];
179
+ const additionalProducts = this.additionalProducts.slice();
180
+ const additionalProductsCount = additionalProducts.length;
181
+ const apiSelectionAdditionalProductsCount = apiSelectionAdditionalProducts.length;
182
+ if (apiSelectionAdditionalProductsCount !== additionalProductsCount) {
183
+ 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}`);
184
+ }
185
+ const sourceProductAdditionalProducts = sourceProduct === null || sourceProduct === void 0 ? void 0 : sourceProduct.additionalProducts;
186
+ assert(!sourceProductAdditionalProducts ||
187
+ additionalProductsCount === sourceProductAdditionalProducts.length, `Passed sourceProduct does not have the same number of additional products as this.`);
188
+ if ((yield Promise.all(apiSelectionAdditionalProducts.map((apiSelectionAdditionalProduct, index) => {
189
+ var _a;
190
+ const refKey = apiSelectionAdditionalProduct.refKey;
191
+ assertDefined(refKey, "Additional product api configurations must have refKey.");
192
+ const i = additionalProducts.findIndex((a) => refKey === a.refKey);
193
+ assert(i !== -1, `Additional product not found. This product: "${this.key}". refKey of not found additional product: "${refKey}"`);
194
+ let sourceProductAdditionalProduct = undefined;
195
+ if (sourceProductAdditionalProducts !== undefined) {
196
+ sourceProductAdditionalProduct =
197
+ (_a = sourceProductAdditionalProducts.find((a) => refKey === a.refKey)) === null || _a === void 0 ? void 0 : _a._internal;
198
+ assertDefined(sourceProductAdditionalProduct, "Additional product not found in sourceProduct");
199
+ }
200
+ const additionalProduct = additionalProducts.splice(i, 1)[0]; // Splicing like this is okay because this is done synchronous. The setCon. is what is async.
201
+ return additionalProduct._internal._setApiSelectionWithOtherProduct(apiSelectionAdditionalProduct, doValidate, productLoaderForGroupedLoad, sourceProductAdditionalProduct);
202
+ }))).some((b) => b)) {
203
+ change = true;
204
+ }
205
+ if (doValidate && configurationChange) {
206
+ yield this._revalidate(CfgProductBubbleMode.ToRoot, productLoaderForGroupedLoad);
207
+ }
208
+ else if (change) {
209
+ // As setApiSelection is done recursively each level takes care of its own notifications
210
+ // so we only need to bubble one level to notify this and swap out the reference in the
211
+ // parent.
212
+ yield this._notifyAllOfChange(CfgProductBubbleMode.OneLevel);
213
+ }
214
+ return change;
215
+ });
216
+ this.structureCompare = (other, strictOrder = true, descriptionMatch = false) => {
217
+ if (!this.configuration.structureCompare(other.configuration, strictOrder, descriptionMatch)) {
218
+ return false;
219
+ }
220
+ return compareArrays(this.additionalProducts, other.additionalProducts, (l, r) => (!descriptionMatch || (descriptionMatch && isDescriptionMatch(l, r))) &&
221
+ l.structureCompare(r, strictOrder, descriptionMatch), !descriptionMatch);
222
+ };
223
+ this.tryMatchSelection = (other, descriptionMatch = false, // Match on case insensitive description, not code
224
+ productLoaderForGroupedLoad) => __awaiter(this, void 0, void 0, function* () {
225
+ // wrap with cache will make getProduct for this function call use the same server call
226
+ // for the same product with the same params
227
+ productLoaderForGroupedLoad =
228
+ productLoaderForGroupedLoad || wrapWithCache(this._productLoaderRaw);
229
+ let change = false;
230
+ if (this.optional && other.optional) {
231
+ if (yield this.setSelected(other.selected, CfgProductBubbleMode.Stop)) {
232
+ change = true;
233
+ }
234
+ }
235
+ const configurationChange = yield this.configuration._internal.tryMatchSelection(other.configuration._internal, descriptionMatch, false);
236
+ if (configurationChange) {
237
+ change = true;
238
+ yield this._revalidate(CfgProductBubbleMode.ToRootAndBubbleSelected, productLoaderForGroupedLoad);
239
+ }
240
+ else if (change) {
241
+ yield this._notifyAllOfChange(CfgProductBubbleMode.ToRootAndBubbleSelected);
242
+ }
243
+ const thisAdditionalProducts = this.additionalProducts;
244
+ const otherAdditionalProducts = other.additionalProducts;
245
+ const promises = [];
246
+ if (descriptionMatch) {
247
+ for (const otherAdditionalProduct of otherAdditionalProducts) {
248
+ if (1 <
249
+ count(otherAdditionalProducts, (product) => isDescriptionMatch(product, otherAdditionalProduct))) {
250
+ console.warn("tryMatchSelection will ignore products that have the same description");
251
+ continue;
252
+ }
253
+ const toTryMatchProducts = thisAdditionalProducts.filter((product) => isDescriptionMatch(product, otherAdditionalProduct));
254
+ if (1 < toTryMatchProducts.length) {
255
+ console.warn("tryMatchSelection will ignore products that have the same description");
256
+ continue;
257
+ }
258
+ if (toTryMatchProducts.length === 0) {
259
+ continue;
260
+ }
261
+ promises.push(toTryMatchProducts[0]._internal.tryMatchSelection(otherAdditionalProduct._internal, descriptionMatch, productLoaderForGroupedLoad));
262
+ }
263
+ }
264
+ else {
265
+ if (thisAdditionalProducts.length !== otherAdditionalProducts.length) {
266
+ console.warn("tryMatchSelection not same count, will not try to match further.");
267
+ return change;
268
+ }
269
+ for (let i = 0; i < thisAdditionalProducts.length; i++) {
270
+ promises.push(thisAdditionalProducts[i]._internal.tryMatchSelection(otherAdditionalProducts[i]._internal, descriptionMatch, productLoaderForGroupedLoad));
271
+ }
272
+ }
273
+ if ((yield Promise.all(promises)).some((b) => b)) {
274
+ change = true;
275
+ }
276
+ return change;
277
+ });
278
+ /** Only features in selected options and selected additional products. */
279
+ this._getDescendantFeaturesWithCode = (code) => this.additionalProducts.reduce((agg, additionalProduct) => {
280
+ if (additionalProduct.selected) {
281
+ agg.push(...additionalProduct._internal._getDescendantFeaturesWithCode(code));
282
+ }
283
+ return agg;
284
+ }, this._configuration._internal._getFeaturesWithCode(code));
285
+ /**
286
+ * Do a validate call for this product. It does not validate additional products, only this
287
+ * product in isolation. The validation result is applied on the configuration. Then additional
288
+ * products are synced (unloaded, loaded etc.) Finally the changes bubble up the tree.
289
+ */
290
+ this._revalidate = (bubbleMode, productLoader) => __awaiter(this, void 0, void 0, function* () {
291
+ const { _configuration: configuration } = this;
292
+ const token = this.loadingObservable.startChildLoading();
293
+ this._revalidateInProgressToken = token;
294
+ try {
295
+ const response = yield productLoader.postValidate(Object.assign(Object.assign({ lang: this.lang }, correctDefaultsOnCatalogueParams(this.catId)), { partNumber: this.partNumber }), { selOptions: configuration.getApiSelection() });
296
+ // The revalidateInProgressToken is used to know if some other revalidate
297
+ // call has happened after this call, thereby making this call obsolete.
298
+ // This is a bit crude in that it does not actually cancel previous validate
299
+ // calls, but this is fine because 1. it is hard to cancel a call, especially
300
+ // if you wanna have clear code 2. the actual calls are small anyhow 3. most
301
+ // of all, the heavy work happens on the server, and that work will not be
302
+ // cancelled even if we would cancel the call.
303
+ if (this._revalidateInProgressToken !== token) {
304
+ return false;
305
+ }
306
+ // After a successful validate-call we will always assume there
307
+ // is a change. It would be possible to compare relevant parts
308
+ // of productData, consider the result of setApiSelection and
309
+ // syndAndLoad, however the code comparing productData would be fragile
310
+ // and likely to break if new data-fields were added.
311
+ if (this._destroyed) {
312
+ return false;
313
+ }
314
+ const { productData, rootFeatureRefs } = response;
315
+ const pricesUpdated = !comparePricesObjects(this.prices, productData.partsData.prices);
316
+ this._rawProductData = productData;
317
+ if (rootFeatureRefs !== undefined) {
318
+ configuration._internal.populateFeatures(rootFeatureRefs);
319
+ }
320
+ if (pricesUpdated) {
321
+ this._configuration._internal._freshRefDescendants();
322
+ this._configuration = CfgProductConfiguration._makeNewRefFrom(this._configuration._internal);
323
+ }
324
+ yield configuration._internal.setApiSelection(productData.partsData.selOptions || [], false);
325
+ yield this._syncAndLoadAdditionalProducts(productLoader);
326
+ if (this._destroyed) {
327
+ return false;
328
+ }
329
+ yield this._notifyAllOfChange(bubbleMode);
330
+ return true;
331
+ }
332
+ catch (e) {
333
+ throw augmentErrorMessage(e, "Validate product configuration request failure");
334
+ }
335
+ finally {
336
+ this.loadingObservable.stopChildLoading(token);
337
+ }
338
+ });
339
+ /**
340
+ * Based on this configuration find what additional products should be shown and not, unload
341
+ * (i.e. destroy) those that should no longer be shown, load the new ones.
342
+ */
343
+ this._syncAndLoadAdditionalProducts = (productLoaderForGroupedLoad) => __awaiter(this, void 0, void 0, function* () {
344
+ const { lang, _productLoaderRaw: productLoaderRaw, rawProductData: productData, configuration, additionalProducts: currentAdditionalProducts, } = this;
345
+ const additionalProductRefs = [
346
+ ...(productData.additionalProductRefs || []),
347
+ ...collectAdditionalProductRefs(configuration),
348
+ ]
349
+ .reduce((a, c) => {
350
+ if (a.every((p) => !isSameProductRef(p, c))) {
351
+ a.push(c);
352
+ }
353
+ return a;
354
+ }, [])
355
+ .map((prodRef, originalIndex) => ({
356
+ prodRef,
357
+ originalIndex,
358
+ }));
359
+ let change = false;
360
+ let i = currentAdditionalProducts.length;
361
+ while (i--) {
362
+ const currentAdditionalProduct = currentAdditionalProducts[i];
363
+ const j = additionalProductRefs.findIndex((p) => {
364
+ const prodRef = p.prodRef;
365
+ return (prodRef.refKey === currentAdditionalProduct.refKey &&
366
+ prodRef.partNumber === currentAdditionalProduct.partNumber &&
367
+ isSameCatalogueParams(prodRef.catId, currentAdditionalProduct.catId));
368
+ });
369
+ if (j === -1) {
370
+ currentAdditionalProduct.destroy();
371
+ currentAdditionalProducts.splice(i, 1);
372
+ change = true;
373
+ }
374
+ else {
375
+ currentAdditionalProduct._internal._updateAdditionalProdRef(additionalProductRefs[j].prodRef);
376
+ additionalProductRefs.splice(j, 1);
377
+ }
378
+ }
379
+ const token = this.loadingObservable.startChildLoading();
380
+ try {
381
+ if (additionalProductRefs.length !== 0) {
382
+ change = true;
383
+ }
384
+ const newAdditionalProducts = yield Promise.all(additionalProductRefs.map((p) => (() => __awaiter(this, void 0, void 0, function* () {
385
+ const additionalProductRef = p.prodRef;
386
+ return {
387
+ originalIndex: p.originalIndex,
388
+ product: CfgProduct._makeNewRefFrom(yield _CfgProductInternal.make(productLoaderRaw, productLoaderForGroupedLoad, lang, additionalProductRef.catId, additionalProductRef.partNumber, this.settings, additionalProductRef.optional === true, this.loadingObservable, this, this.root, additionalProductRef)),
389
+ };
390
+ }))()));
391
+ if (this._destroyed) {
392
+ return change;
393
+ }
394
+ for (const newAdditionalProduct of newAdditionalProducts) {
395
+ currentAdditionalProducts.splice(newAdditionalProduct.originalIndex, 0, newAdditionalProduct.product);
396
+ }
397
+ return change;
398
+ }
399
+ finally {
400
+ this.loadingObservable.stopChildLoading(token);
401
+ }
402
+ });
403
+ this.root = root !== null && root !== void 0 ? root : this;
404
+ this.key = makeProductKey(catId, (_a = _additionalProductRef === null || _additionalProductRef === void 0 ? void 0 : _additionalProductRef.refKey) !== null && _a !== void 0 ? _a : partNumber);
405
+ this._selected = optional ? selected : undefined;
406
+ this.isAdditionalProduct = parent !== undefined;
407
+ this._configuration = CfgProductConfiguration.make(initSuccess, initFail, rootFeatureRefs, allRawFeatures, apiSelection, this, this.root);
408
+ }
409
+ get selected() {
410
+ return this._selected !== false;
411
+ }
412
+ clone(parent, root) {
413
+ return __awaiter(this, void 0, void 0, function* () {
414
+ const product = yield new Promise((initSuccess, initFail) => {
415
+ var _a;
416
+ const p = new _CfgProductInternal(() => {
417
+ initSuccess(p);
418
+ }, initFail, this._productLoaderRaw, this.lang, this.catId, this.partNumber, this.settings, this.optional, this.selected, this._configuration.rootFeatureRefs, this._configuration.allRawFeatures, this.uuid, this._rawUnit, this._rawProductData, this.configuration.getApiSelection(), new AggregatedLoadingObservable(), parent, root, this._additionalProductRef, (_a = this._syncGroupHandler) === null || _a === void 0 ? void 0 : _a.clone());
419
+ });
420
+ for (const additionalProduct of this.additionalProducts) {
421
+ product.additionalProducts.push(CfgProduct._makeNewRefFrom(yield additionalProduct._internal.clone(product, root || product)));
422
+ }
423
+ return product;
424
+ });
425
+ }
426
+ _updateAdditionalProdRef(p) {
427
+ this._additionalProductRef = p;
428
+ if (p.optional !== this.optional) {
429
+ this._selected = p.optional ? false : undefined;
430
+ }
431
+ }
432
+ get description() {
433
+ var _a, _b;
434
+ return (_b = (_a = this._additionalProductRef) === null || _a === void 0 ? void 0 : _a.refDescription) !== null && _b !== void 0 ? _b : this._rawProductData.description;
435
+ }
436
+ get rootNodeSources() {
437
+ return this._rawProductData.models;
438
+ }
439
+ get mtrlApplications() {
440
+ return this._rawProductData.mtrlApplications;
441
+ }
442
+ get currency() {
443
+ return this._rawProductData.partsData.currency;
444
+ }
445
+ get fractionDigits() {
446
+ return this._rawProductData.partsData.rounding || 0;
447
+ }
448
+ get prices() {
449
+ return this._rawProductData.partsData.prices;
450
+ }
451
+ get measureDefinitions() {
452
+ var _a;
453
+ if (this._measureDefinitions === undefined) {
454
+ this._measureDefinitions = ((_a = this._rawProductData.measurements) !== null && _a !== void 0 ? _a : [])
455
+ .map((m) => CfgMeasureDefinition.make(m))
456
+ .filter((m) => m !== undefined);
457
+ }
458
+ return this._measureDefinitions;
459
+ }
460
+ get refKey() {
461
+ var _a;
462
+ return (_a = this._additionalProductRef) === null || _a === void 0 ? void 0 : _a.refKey;
463
+ }
464
+ get transform() {
465
+ var _a;
466
+ return (_a = this._additionalProductRef) === null || _a === void 0 ? void 0 : _a.transform;
467
+ }
468
+ get anchor() {
469
+ var _a;
470
+ return (_a = this._additionalProductRef) === null || _a === void 0 ? void 0 : _a.anchor;
471
+ }
472
+ /** @throws an error if the actual unit sent by the server was not a LengthUnit */
473
+ get unit() {
474
+ if (this._unit === undefined) {
475
+ this._unit = toLengthUnit(this._rawUnit);
476
+ }
477
+ return this._unit;
478
+ }
479
+ get aggregatedPrice() {
480
+ const { currency, fractionDigits, rawProductData } = this;
481
+ const { partsData } = rawProductData;
482
+ let { listPrice, basePrice } = partsData;
483
+ if (this._selected === false) {
484
+ return { basePrice: 0, listPrice: 0, currency, fractionDigits };
485
+ }
486
+ for (const additionalProduct of this.additionalProducts || []) {
487
+ const { basePrice: additionalBasePrice, listPrice: additionalListPrice, currency: additionalCurrency, fractionDigits: additionalFractionDigits, } = additionalProduct.aggregatedPrice;
488
+ basePrice += additionalBasePrice;
489
+ listPrice += additionalListPrice;
490
+ if (currency !== additionalCurrency) {
491
+ // This should not be possible
492
+ // The server shouldn't return additional products with different currency from their parent
493
+ throw new Error(`Currency mismatch between parent product and additional product. Parent product: "${this.key}" Additional product: "${additionalProduct.key}". Currency on parent product: ${currency}. Currency on additional product: ${additionalCurrency}`);
494
+ }
495
+ if (fractionDigits !== additionalFractionDigits) {
496
+ // This should not be possible
497
+ // The server shouldn't return additional products with different fraction digits from their parent
498
+ throw new Error(`Fraction digits mismatch between parent product and additional product. Parent product: "${this.key}" Additional product: "${additionalProduct.key}". Fraction digits on parent product: ${fractionDigits}. Fraction digits on additional product: ${additionalFractionDigits}`);
499
+ }
500
+ }
501
+ return { basePrice, listPrice, currency, fractionDigits };
502
+ }
503
+ get optional() {
504
+ return this._selected !== undefined;
505
+ }
506
+ setSelected(v, bubbleMode) {
507
+ return __awaiter(this, void 0, void 0, function* () {
508
+ if (!this.optional) {
509
+ console.warn("This product is not optional. Nothing will happen");
510
+ return false;
511
+ }
512
+ if (this._selected === v) {
513
+ return false;
514
+ }
515
+ this._selected = v;
516
+ yield this._notifyAllOfChange(bubbleMode);
517
+ return true;
518
+ });
519
+ }
520
+ get configuration() {
521
+ return this._configuration;
522
+ }
523
+ get rawProductData() {
524
+ return this._rawProductData;
525
+ }
526
+ /**
527
+ * Please note that this relates to the visibility in the Configuration tree.
528
+ * It does not affect the visibility of anything in the 3D view at all.
529
+ */
530
+ get visibleIfAdditionalProduct() {
531
+ return this.rawProductData.hideIfAdditionalProduct !== true;
532
+ }
533
+ /**
534
+ * Please note that this relates to the visibility in the Configuration tree.
535
+ * It does not affect the visibility of anything in the 3D view at all.
536
+ */
537
+ get visibleIfMainProduct() {
538
+ return this.rawProductData.hideIfMainProduct !== true;
539
+ }
540
+ /**
541
+ * Please note that this relates to the visibility in the Configuration tree.
542
+ * It does not affect the visibility of anything in the 3D view at all.
543
+ */
544
+ get visible() {
545
+ return this.isAdditionalProduct
546
+ ? this.visibleIfAdditionalProduct
547
+ : this.visibleIfMainProduct;
548
+ }
549
+ get syncGroupHandler() {
550
+ return this.root._syncGroupHandler;
551
+ }
552
+ }
553
+ _CfgProductInternal.make = (productLoaderRaw, productLoaderForGroupedLoad, // Used when instantiating the current product
554
+ lang, catId, partNumber, settings, optional, loadingObservable, parent, root, additionalProductRef) => __awaiter(void 0, void 0, void 0, function* () {
555
+ // Wrap with cache will make getProduct for this function call use the same server call
556
+ // for the same product with the same params. Not retained for future calls, only used
557
+ // at this initial load.
558
+ productLoaderForGroupedLoad =
559
+ productLoaderForGroupedLoad || wrapWithCache(productLoaderRaw);
560
+ const syncGroupHandler = root === undefined
561
+ ? SyncGroupsHandler.make(settings.syncGroupsApplyMode, loadingObservable)
562
+ : undefined;
563
+ try {
564
+ const productResponse = yield productLoaderForGroupedLoad.getProduct(Object.assign(Object.assign({ lang }, correctDefaultsOnCatalogueParams(catId)), { partNumber }));
565
+ const { productData, rootFeatureRefs, features: allRawFeatures, uuid, unit, } = productResponse;
566
+ const product = yield new Promise((initSuccess, initFail) => {
567
+ const p = new _CfgProductInternal(() => {
568
+ // We absolutely do not want anyone to assign to this._configuration. So we want that field private.
569
+ // But we can not set the api selection synchronously. And the product configuration needs "this". So we use this callback.
570
+ // Feel free to find a nicer more readable solution :)
571
+ initSuccess(p);
572
+ }, initFail, productLoaderRaw, lang, catId, partNumber, settings, optional, !optional, rootFeatureRefs, allRawFeatures, uuid, unit, productData, productData.partsData.selOptions || [], loadingObservable, parent, root, additionalProductRef, syncGroupHandler);
573
+ });
574
+ yield product._syncAndLoadAdditionalProducts(productLoaderForGroupedLoad);
575
+ // Product is guaranteed to be root
576
+ yield (syncGroupHandler === null || syncGroupHandler === void 0 ? void 0 : syncGroupHandler.init(product, productLoaderForGroupedLoad));
577
+ return product;
578
+ }
579
+ catch (e) {
580
+ throw augmentErrorMessage(e, "Load product request failure");
581
+ }
582
+ });
583
+ export class CfgProduct {
584
+ constructor(_internal) {
585
+ this._internal = _internal;
586
+ this.isBackedBySame = (other) => this._internal === other._internal;
587
+ /**
588
+ * Recursively marks this and descendants as destroyed so that late events are ignored
589
+ * correctly. If you destroy one shallow copy of this you destroy all.
590
+ */
591
+ this.destroy = () => this._internal.destroy();
592
+ /** Makes a clone of this. It is disconnected from the original. */
593
+ this.clone = () => __awaiter(this, void 0, void 0, function* () { return CfgProduct._makeNewRefFrom(yield this._internal.clone()); });
594
+ /**
595
+ * Only applicable when this product is optional.
596
+ * Setting this does not cause a validation call as toggling an optional additional product is
597
+ * assumed to always be legal.
598
+ */
599
+ this.setSelected = (v) => __awaiter(this, void 0, void 0, function* () { return yield this._internal.setSelected(v, CfgProductBubbleMode.ToRootAndBubbleSelected); });
600
+ /**
601
+ * Experimental. Additional products lacks descriptions or keys that are suitably for structure
602
+ * compare, so we use strict-order when trying to match the additional products. This makes
603
+ * this method work nicely for different products having pretty much the same child products.
604
+ */
605
+ this.structureCompare = (other, strictOrder = true, descriptionMatch = false) => this._internal.structureCompare(other._internal, strictOrder, descriptionMatch);
606
+ /**
607
+ * Experimental. Additional products lacks descriptions or keys that are suitably for try
608
+ * match, so we use strict-order when trying to match the additional products. This makes
609
+ * this method work nicely for different products having pretty much the same child products.
610
+ * This method does not propagate its selections.
611
+ * This method will cause validation calls if something change.
612
+ */
613
+ this.tryMatchSelection = (other, descriptionMatch = false // Match on case insensitive description, not code
614
+ ) => __awaiter(this, void 0, void 0, function* () { return yield this._internal.tryMatchSelection(other._internal, descriptionMatch); });
615
+ /**
616
+ * Gets what selections has been made on the product, recursively including product
617
+ * configuration, optional products and additional products. Used when a full view of all
618
+ * selections on a product is needed, such as when doing Render or Export.
619
+ */
620
+ this.getApiSelection = () => this._internal.getApiSelection();
621
+ this.setApiSelection = (s, doValidate = false) => __awaiter(this, void 0, void 0, function* () { return yield this._internal.setApiSelection(s, doValidate); });
622
+ this.listenForChange = (l) => this._internal.changeObservable.listen(l);
623
+ this.stopListenForChange = (l) => this._internal.changeObservable.stopListen(l);
624
+ this.stopAllListenForChange = () => this._internal.changeObservable.stopAllListen();
625
+ this.listenForLoading = (l) => this._internal.loadingObservable.listen(l);
626
+ this.stopListenForLoading = (l) => this._internal.loadingObservable.stopListen(l);
627
+ this.stopAllListenForLoading = () => this._internal.loadingObservable.stopAllListen();
628
+ }
629
+ static make(productLoader, lang, catId, partNumber, settings) {
630
+ return __awaiter(this, void 0, void 0, function* () {
631
+ return this._makeNewRefFrom(yield _CfgProductInternal.make(productLoader, undefined, lang, catId, partNumber, completeSettings(settings), false, new AggregatedLoadingObservable(), undefined, undefined, undefined));
632
+ });
633
+ }
634
+ /**
635
+ * Makes an object wrapping the passed object. This is not a clone method, it is a method to
636
+ * make a new outer reference. Like a shallow copy. We use this to help frameworks that are
637
+ * build around using equals to detect change.
638
+ */
639
+ static _makeNewRefFrom(source) {
640
+ return new this(source);
641
+ }
642
+ /**
643
+ * A client side only key that should uniquely identify this product amongst other additional
644
+ * products.
645
+ */
646
+ get key() {
647
+ return this._internal.key;
648
+ }
649
+ /**
650
+ * Only used when this product is in additional product.
651
+ * As a product can have multiple instances of the same additional product this key exists.
652
+ * It will be unique amongst child products, but not globally unique.
653
+ */
654
+ get refKey() {
655
+ return this._internal.refKey;
656
+ }
657
+ get lang() {
658
+ return this._internal.lang;
659
+ }
660
+ get catId() {
661
+ return this._internal.catId;
662
+ }
663
+ get partNumber() {
664
+ return this._internal.partNumber;
665
+ }
666
+ get isAdditionalProduct() {
667
+ return this._internal.isAdditionalProduct;
668
+ }
669
+ /** Only used when this product is an additional product. Root products are never optional. */
670
+ get optional() {
671
+ return this._internal.optional;
672
+ }
673
+ /**
674
+ * Only applicable when this product is optional. If this product is not optional this is
675
+ * always true.
676
+ */
677
+ get selected() {
678
+ return this._internal.selected;
679
+ }
680
+ /**
681
+ * Please note that this relates to the visibility in the Configuration tree.
682
+ * It does not affect the visibility of anything in the 3D view at all.
683
+ * Visibility is affects the Configuration for this Product, but any Additional Products
684
+ * will not be affected.
685
+ */
686
+ get visible() {
687
+ return this._internal.visible;
688
+ }
689
+ get rawProductData() {
690
+ return this._internal.rawProductData;
691
+ }
692
+ get uuid() {
693
+ return this._internal.uuid;
694
+ }
695
+ get unit() {
696
+ return this._internal.unit;
697
+ }
698
+ get sku() {
699
+ return this._internal.rawProductData.sku;
700
+ }
701
+ get styleNr() {
702
+ return this._internal.rawProductData.partsData.styleNr;
703
+ }
704
+ /** An URL. */
705
+ get preview() {
706
+ return this._internal.rawProductData.navImage;
707
+ }
708
+ get description() {
709
+ return this._internal.description;
710
+ }
711
+ get additionalProducts() {
712
+ return this._internal.additionalProducts;
713
+ }
714
+ get configuration() {
715
+ return this._internal.configuration;
716
+ }
717
+ get transform() {
718
+ return this._internal.transform;
719
+ }
720
+ get currency() {
721
+ return this._internal.currency;
722
+ }
723
+ /**
724
+ * If positive the number of fraction digits.
725
+ * If negative rounding (essentially the number of zeros to the right)
726
+ */
727
+ get fractionDigits() {
728
+ return this._internal.fractionDigits;
729
+ }
730
+ get aggregatedPrice() {
731
+ return this._internal.aggregatedPrice;
732
+ }
733
+ }