@configura/web-api 1.6.1 → 1.7.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.
- package/dist/CatalogueAPI.d.ts +32 -2
- package/dist/CatalogueAPI.js +37 -2
- package/dist/CfgProduct.d.ts +11 -0
- package/dist/CfgProduct.js +55 -14
- package/dist/productConfiguration/CfgFeature.d.ts +6 -5
- package/dist/productConfiguration/CfgFeature.js +13 -7
- package/dist/productConfiguration/CfgOption.d.ts +3 -3
- package/dist/productConfiguration/CfgOption.js +5 -5
- package/dist/productConfiguration/CfgProductConfiguration.d.ts +16 -7
- package/dist/productConfiguration/CfgProductConfiguration.js +40 -12
- package/dist/productConfiguration/utilitiesProductConfiguration.d.ts +1 -1
- package/dist/productConfiguration/utilitiesProductConfiguration.js +11 -4
- package/dist/syncGroups/SyncGroupsHandler.d.ts +4 -1
- package/dist/syncGroups/SyncGroupsHandler.js +6 -2
- package/dist/syncGroups/SyncGroupsTransaction.js +34 -21
- package/dist/tests/testData/testDataAdditionalProductInAdditionalProductInProductForTest.js +1 -0
- package/dist/tests/testData/testDataCachedGetProduct.js +1 -0
- package/dist/tests/testData/testDataCachedPostValidate.js +1 -0
- package/dist/tests/testData/testDataProductAggregatedPrice.js +1 -0
- package/dist/tests/testData/testDataUpcharge.js +1 -0
- package/dist/utilitiesCatalogueData.d.ts +5 -0
- package/dist/utilitiesCatalogueData.js +7 -0
- package/package.json +3 -3
package/dist/CatalogueAPI.d.ts
CHANGED
|
@@ -143,6 +143,16 @@ export interface GetPriceListsParams {
|
|
|
143
143
|
vendor: string;
|
|
144
144
|
priceList: string;
|
|
145
145
|
}
|
|
146
|
+
/** GetProductLegacyV2Params represents the URL parameters of getProductLegacyV2 */
|
|
147
|
+
export interface GetProductLegacyV2Params {
|
|
148
|
+
lang: string;
|
|
149
|
+
enterprise: string;
|
|
150
|
+
prdCat: string;
|
|
151
|
+
prdCatVersion: string;
|
|
152
|
+
vendor: string;
|
|
153
|
+
priceList: string;
|
|
154
|
+
partNumber: string;
|
|
155
|
+
}
|
|
146
156
|
/** GetProductParams represents the URL parameters of getProduct */
|
|
147
157
|
export interface GetProductParams {
|
|
148
158
|
lang: string;
|
|
@@ -287,6 +297,16 @@ export interface PostRenderParams {
|
|
|
287
297
|
priceList: string;
|
|
288
298
|
partNumber: string;
|
|
289
299
|
}
|
|
300
|
+
/** PostValidateLegacyV1Params represents the URL parameters of postValidateLegacyV1 */
|
|
301
|
+
export interface PostValidateLegacyV1Params {
|
|
302
|
+
lang: string;
|
|
303
|
+
enterprise: string;
|
|
304
|
+
prdCat: string;
|
|
305
|
+
prdCatVersion: string;
|
|
306
|
+
vendor: string;
|
|
307
|
+
priceList: string;
|
|
308
|
+
partNumber: string;
|
|
309
|
+
}
|
|
290
310
|
/** PostValidateParams represents the URL parameters of postValidate */
|
|
291
311
|
export interface PostValidateParams {
|
|
292
312
|
lang: string;
|
|
@@ -448,11 +468,19 @@ export interface Transform {
|
|
|
448
468
|
rot: Orientation;
|
|
449
469
|
}
|
|
450
470
|
/** ValidateRequest */
|
|
451
|
-
export interface ValidateRequest {
|
|
471
|
+
export interface ValidateRequest extends ValidateRequestLegacyV1 {
|
|
472
|
+
knownFeatureCodes: Array<string>;
|
|
473
|
+
}
|
|
474
|
+
/** ValidateRequestLegacyV1 */
|
|
475
|
+
export interface ValidateRequestLegacyV1 {
|
|
452
476
|
selOptions: Array<SelectedOption>;
|
|
453
477
|
}
|
|
454
478
|
/** ValidateResponse */
|
|
455
|
-
export interface ValidateResponse {
|
|
479
|
+
export interface ValidateResponse extends ValidateResponseLegacyV1 {
|
|
480
|
+
features: Array<Feature>;
|
|
481
|
+
}
|
|
482
|
+
/** ValidateResponseLegacyV1 */
|
|
483
|
+
export interface ValidateResponseLegacyV1 {
|
|
456
484
|
productData: ProductData;
|
|
457
485
|
uuid: string;
|
|
458
486
|
validated: boolean;
|
|
@@ -495,8 +523,10 @@ export declare class CatalogueAPI {
|
|
|
495
523
|
getApplicationAreas(params: GetApplicationAreasParams): Promise<ApplicationAreasResponse>;
|
|
496
524
|
postExport(params: PostExportParams, body: ExportRequest): Promise<ExportResponse>;
|
|
497
525
|
getPriceLists(params: GetPriceListsParams): Promise<PriceListsResponse>;
|
|
526
|
+
getProductLegacyV2(params: GetProductLegacyV2Params): Promise<ProductResponse>;
|
|
498
527
|
getProduct(params: GetProductParams): Promise<ProductResponse>;
|
|
499
528
|
postRender(params: PostRenderParams, body: RenderRequest): Promise<RenderResponse>;
|
|
529
|
+
postValidateLegacyV1(params: PostValidateLegacyV1Params, body: ValidateRequestLegacyV1): Promise<ValidateResponseLegacyV1>;
|
|
500
530
|
postValidate(params: PostValidateParams, body: ValidateRequest): Promise<ValidateResponse>;
|
|
501
531
|
getTocTree(params: GetTocTreeParams): Promise<TOCResponse>;
|
|
502
532
|
getTocFlat(params: GetTocFlatParams): Promise<TOCResponse>;
|
package/dist/CatalogueAPI.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// TODO: Fix the linter issue in Tygen and regenerate this file.
|
|
2
2
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
|
3
|
+
// WARNING: This file was auto generated by the code in web-rnd/tygen.
|
|
4
|
+
// Do not commit manual changes to this file.
|
|
3
5
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
4
6
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
5
7
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -147,7 +149,7 @@ export class CatalogueAPI {
|
|
|
147
149
|
return this.fetch(this.auth.endpoint + url, options);
|
|
148
150
|
});
|
|
149
151
|
}
|
|
150
|
-
|
|
152
|
+
getProductLegacyV2(params) {
|
|
151
153
|
return __awaiter(this, void 0, void 0, function* () {
|
|
152
154
|
if (this.auth === undefined) {
|
|
153
155
|
throw new Error("missing auth");
|
|
@@ -163,6 +165,22 @@ export class CatalogueAPI {
|
|
|
163
165
|
return this.fetch(this.auth.endpoint + url, options);
|
|
164
166
|
});
|
|
165
167
|
}
|
|
168
|
+
getProduct(params) {
|
|
169
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
170
|
+
if (this.auth === undefined) {
|
|
171
|
+
throw new Error("missing auth");
|
|
172
|
+
}
|
|
173
|
+
const url = `/v1/catalogue/${encodeURIComponent(params.lang)}/${encodeURIComponent(params.enterprise)}/${encodeURIComponent(params.prdCat)}/${encodeURIComponent(params.prdCatVersion)}/${encodeURIComponent(params.vendor)}/${encodeURIComponent(params.priceList)}/product-v3/${encodeURIComponent(params.partNumber)}`;
|
|
174
|
+
const options = {
|
|
175
|
+
method: "GET",
|
|
176
|
+
headers: { "X-API-Key": this.auth.secretToken || "" },
|
|
177
|
+
};
|
|
178
|
+
if (this._alternativeReferer) {
|
|
179
|
+
options.headers["Alternative-Referer"] = this._alternativeReferer;
|
|
180
|
+
}
|
|
181
|
+
return this.fetch(this.auth.endpoint + url, options);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
166
184
|
postRender(params, body) {
|
|
167
185
|
return __awaiter(this, void 0, void 0, function* () {
|
|
168
186
|
if (this.auth === undefined) {
|
|
@@ -180,7 +198,7 @@ export class CatalogueAPI {
|
|
|
180
198
|
return this.fetch(this.auth.endpoint + url, options);
|
|
181
199
|
});
|
|
182
200
|
}
|
|
183
|
-
|
|
201
|
+
postValidateLegacyV1(params, body) {
|
|
184
202
|
return __awaiter(this, void 0, void 0, function* () {
|
|
185
203
|
if (this.auth === undefined) {
|
|
186
204
|
throw new Error("missing auth");
|
|
@@ -197,6 +215,23 @@ export class CatalogueAPI {
|
|
|
197
215
|
return this.fetch(this.auth.endpoint + url, options);
|
|
198
216
|
});
|
|
199
217
|
}
|
|
218
|
+
postValidate(params, body) {
|
|
219
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
220
|
+
if (this.auth === undefined) {
|
|
221
|
+
throw new Error("missing auth");
|
|
222
|
+
}
|
|
223
|
+
const url = `/v1/catalogue/${encodeURIComponent(params.lang)}/${encodeURIComponent(params.enterprise)}/${encodeURIComponent(params.prdCat)}/${encodeURIComponent(params.prdCatVersion)}/${encodeURIComponent(params.vendor)}/${encodeURIComponent(params.priceList)}/product/${encodeURIComponent(params.partNumber)}/validate-v2`;
|
|
224
|
+
const options = {
|
|
225
|
+
method: "POST",
|
|
226
|
+
headers: { "X-API-Key": this.auth.secretToken || "" },
|
|
227
|
+
body: JSON.stringify(body),
|
|
228
|
+
};
|
|
229
|
+
if (this._alternativeReferer) {
|
|
230
|
+
options.headers["Alternative-Referer"] = this._alternativeReferer;
|
|
231
|
+
}
|
|
232
|
+
return this.fetch(this.auth.endpoint + url, options);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
200
235
|
getTocTree(params) {
|
|
201
236
|
return __awaiter(this, void 0, void 0, function* () {
|
|
202
237
|
if (this.auth === undefined) {
|
package/dist/CfgProduct.d.ts
CHANGED
|
@@ -76,6 +76,7 @@ export declare class _CfgProductInternal {
|
|
|
76
76
|
private _additionalProductRef;
|
|
77
77
|
private readonly _syncGroupHandler;
|
|
78
78
|
static make: (productLoaderRaw: ProductLoader, productLoaderForGroupedLoad: ProductLoader | undefined, lang: string, catId: CatalogueParams, partNumber: string, settings: CfgProductSettings, optional: boolean, loadingObservable: AggregatedLoadingObservable, parent: _CfgProductInternal | undefined, root: _CfgProductInternal | undefined, additionalProductRef: AdditionalProductRef | undefined) => Promise<_CfgProductInternal>;
|
|
79
|
+
_initialClone: _CfgProductInternal | undefined;
|
|
79
80
|
private constructor();
|
|
80
81
|
readonly root: _CfgProductInternal;
|
|
81
82
|
private _destroyed;
|
|
@@ -86,8 +87,18 @@ export declare class _CfgProductInternal {
|
|
|
86
87
|
readonly changeObservable: Observable<CfgProductChangeNotification>;
|
|
87
88
|
get selected(): boolean;
|
|
88
89
|
readonly isAdditionalProduct: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Please note that cloning an additional product will make the clone believe is is
|
|
92
|
+
* an additional product, even if it has no parent and root.
|
|
93
|
+
* Providing the parent and root of what you clone as arguments is unwise as it will
|
|
94
|
+
* make changes you do on the clone be propagated up to the original non-clone root product.
|
|
95
|
+
*/
|
|
89
96
|
clone(parent?: _CfgProductInternal, root?: _CfgProductInternal): Promise<_CfgProductInternal>;
|
|
90
97
|
destroy: () => void;
|
|
98
|
+
/**
|
|
99
|
+
* Reset will reset the product to its initial state
|
|
100
|
+
*/
|
|
101
|
+
reset: () => Promise<void>;
|
|
91
102
|
_updateAdditionalProdRef(p: AdditionalProductRef): void;
|
|
92
103
|
get description(): string | undefined;
|
|
93
104
|
get rootNodeSources(): RootNodeSource[] | undefined;
|
package/dist/CfgProduct.js
CHANGED
|
@@ -14,7 +14,7 @@ import { CfgProductConfiguration } from "./productConfiguration/CfgProductConfig
|
|
|
14
14
|
import { collectAdditionalProductRefs } from "./productConfiguration/utilitiesProductConfiguration.js";
|
|
15
15
|
import { wrapWithCache } from "./productLoader.js";
|
|
16
16
|
import { SyncGroupsHandler } from "./syncGroups/SyncGroupsHandler.js";
|
|
17
|
-
import { comparePricesObjects, correctDefaultsOnCatalogueParams, isSameCatalogueParams, isSameProductRef, makeProductKey, } from "./utilitiesCatalogueData.js";
|
|
17
|
+
import { compareCfgProductData, comparePricesObjects, correctDefaultsOnCatalogueParams, isSameCatalogueParams, isSameProductRef, makeProductKey, } from "./utilitiesCatalogueData.js";
|
|
18
18
|
function completeSettings(incompleteSettings) {
|
|
19
19
|
var _a;
|
|
20
20
|
return {
|
|
@@ -51,7 +51,7 @@ function isDescriptionMatch(l, r) {
|
|
|
51
51
|
* the class that should be used and interacted with.
|
|
52
52
|
*/
|
|
53
53
|
export class _CfgProductInternal {
|
|
54
|
-
constructor(initSuccess, initFail, _productLoaderRaw, lang, catId, partNumber, settings, optional, selected, rootFeatureRefs,
|
|
54
|
+
constructor(initSuccess, initFail, _productLoaderRaw, lang, catId, partNumber, settings, optional, selected, rootFeatureRefs, rawFeatures, uuid, _rawUnit, _rawProductData, apiSelection, loadingObservable, parent, root, _additionalProductRef, _syncGroupHandler) {
|
|
55
55
|
var _a;
|
|
56
56
|
this._productLoaderRaw = _productLoaderRaw;
|
|
57
57
|
this.lang = lang;
|
|
@@ -76,6 +76,14 @@ export class _CfgProductInternal {
|
|
|
76
76
|
additionalProduct.destroy();
|
|
77
77
|
}
|
|
78
78
|
};
|
|
79
|
+
/**
|
|
80
|
+
* Reset will reset the product to its initial state
|
|
81
|
+
*/
|
|
82
|
+
this.reset = () => __awaiter(this, void 0, void 0, function* () {
|
|
83
|
+
if (this._initialClone !== undefined) {
|
|
84
|
+
yield this.copyFrom(this._initialClone, true);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
79
87
|
this._notifyAllOfChange = (bubbleMode) => __awaiter(this, void 0, void 0, function* () {
|
|
80
88
|
if (bubbleMode === CfgProductBubbleMode.Stop) {
|
|
81
89
|
return;
|
|
@@ -161,9 +169,16 @@ export class _CfgProductInternal {
|
|
|
161
169
|
productLoaderForGroupedLoad || wrapWithCache(this._productLoaderRaw);
|
|
162
170
|
let change = false;
|
|
163
171
|
if (sourceProduct !== undefined) {
|
|
164
|
-
this._rawProductData
|
|
165
|
-
|
|
166
|
-
|
|
172
|
+
if (!compareCfgProductData(this._rawProductData, sourceProduct.rawProductData)) {
|
|
173
|
+
this._rawProductData = sourceProduct.rawProductData;
|
|
174
|
+
change = true;
|
|
175
|
+
}
|
|
176
|
+
if (this.configuration._internal.addRawFeatures(sourceProduct.configuration.rawFeatures, false)) {
|
|
177
|
+
change = true;
|
|
178
|
+
}
|
|
179
|
+
if (this.configuration._internal.populateFeatures(sourceProduct.configuration.rootFeatureRefs)) {
|
|
180
|
+
change = true;
|
|
181
|
+
}
|
|
167
182
|
}
|
|
168
183
|
const configurationChange = yield this.configuration._internal.setApiSelection(s.selOptions, false);
|
|
169
184
|
if (configurationChange) {
|
|
@@ -292,7 +307,10 @@ export class _CfgProductInternal {
|
|
|
292
307
|
const token = this.loadingObservable.startChildLoading();
|
|
293
308
|
this._revalidateInProgressToken = token;
|
|
294
309
|
try {
|
|
295
|
-
const response = yield productLoader.postValidate(Object.assign(Object.assign({ lang: this.lang }, correctDefaultsOnCatalogueParams(this.catId)), { partNumber: this.partNumber }), {
|
|
310
|
+
const response = yield productLoader.postValidate(Object.assign(Object.assign({ lang: this.lang }, correctDefaultsOnCatalogueParams(this.catId)), { partNumber: this.partNumber }), {
|
|
311
|
+
selOptions: configuration.getApiSelection(),
|
|
312
|
+
knownFeatureCodes: configuration.rawFeatures.map((f) => f.code),
|
|
313
|
+
});
|
|
296
314
|
// The revalidateInProgressToken is used to know if some other revalidate
|
|
297
315
|
// call has happened after this call, thereby making this call obsolete.
|
|
298
316
|
// This is a bit crude in that it does not actually cancel previous validate
|
|
@@ -311,9 +329,10 @@ export class _CfgProductInternal {
|
|
|
311
329
|
if (this._destroyed) {
|
|
312
330
|
return false;
|
|
313
331
|
}
|
|
314
|
-
const { productData, rootFeatureRefs } = response;
|
|
332
|
+
const { productData, rootFeatureRefs, features } = response;
|
|
315
333
|
const pricesUpdated = !comparePricesObjects(this.prices, productData.partsData.prices);
|
|
316
334
|
this._rawProductData = productData;
|
|
335
|
+
configuration._internal.addRawFeatures(features, true);
|
|
317
336
|
if (rootFeatureRefs !== undefined) {
|
|
318
337
|
configuration._internal.populateFeatures(rootFeatureRefs);
|
|
319
338
|
}
|
|
@@ -403,19 +422,25 @@ export class _CfgProductInternal {
|
|
|
403
422
|
this.root = root !== null && root !== void 0 ? root : this;
|
|
404
423
|
this.key = makeProductKey(catId, (_a = _additionalProductRef === null || _additionalProductRef === void 0 ? void 0 : _additionalProductRef.refKey) !== null && _a !== void 0 ? _a : partNumber);
|
|
405
424
|
this._selected = optional ? selected : undefined;
|
|
406
|
-
this.isAdditionalProduct =
|
|
407
|
-
this._configuration = CfgProductConfiguration.make(initSuccess, initFail, rootFeatureRefs,
|
|
425
|
+
this.isAdditionalProduct = _additionalProductRef !== undefined;
|
|
426
|
+
this._configuration = CfgProductConfiguration.make(initSuccess, initFail, rootFeatureRefs, rawFeatures, apiSelection, this, this.root);
|
|
408
427
|
}
|
|
409
428
|
get selected() {
|
|
410
429
|
return this._selected !== false;
|
|
411
430
|
}
|
|
431
|
+
/**
|
|
432
|
+
* Please note that cloning an additional product will make the clone believe is is
|
|
433
|
+
* an additional product, even if it has no parent and root.
|
|
434
|
+
* Providing the parent and root of what you clone as arguments is unwise as it will
|
|
435
|
+
* make changes you do on the clone be propagated up to the original non-clone root product.
|
|
436
|
+
*/
|
|
412
437
|
clone(parent, root) {
|
|
413
438
|
return __awaiter(this, void 0, void 0, function* () {
|
|
414
439
|
const product = yield new Promise((initSuccess, initFail) => {
|
|
415
440
|
var _a;
|
|
416
441
|
const p = new _CfgProductInternal(() => {
|
|
417
442
|
initSuccess(p);
|
|
418
|
-
}, initFail, this._productLoaderRaw, this.lang, this.catId, this.partNumber, this.settings, this.optional, this.selected, this._configuration.rootFeatureRefs, this._configuration.
|
|
443
|
+
}, initFail, this._productLoaderRaw, this.lang, this.catId, this.partNumber, this.settings, this.optional, this.selected, this._configuration.rootFeatureRefs, this._configuration.rawFeatures, 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
444
|
});
|
|
420
445
|
for (const additionalProduct of this.additionalProducts) {
|
|
421
446
|
product.additionalProducts.push(CfgProduct._makeNewRefFrom(yield additionalProduct._internal.clone(product, root || product)));
|
|
@@ -512,7 +537,19 @@ export class _CfgProductInternal {
|
|
|
512
537
|
if (this._selected === v) {
|
|
513
538
|
return false;
|
|
514
539
|
}
|
|
540
|
+
// Vitally important that this happens before the call to reset. An optional
|
|
541
|
+
// additional product is always deselected at start, so this way the reset won't cause
|
|
542
|
+
// infinite loops
|
|
515
543
|
this._selected = v;
|
|
544
|
+
if (!v) {
|
|
545
|
+
yield this.reset();
|
|
546
|
+
}
|
|
547
|
+
if (v) {
|
|
548
|
+
const syncGroupHandler = this.syncGroupHandler;
|
|
549
|
+
if (syncGroupHandler !== undefined) {
|
|
550
|
+
yield syncGroupHandler.init(this.root, wrapWithCache(this._productLoaderRaw));
|
|
551
|
+
}
|
|
552
|
+
}
|
|
516
553
|
yield this._notifyAllOfChange(bubbleMode);
|
|
517
554
|
return true;
|
|
518
555
|
});
|
|
@@ -576,18 +613,22 @@ lang, catId, partNumber, settings, optional, loadingObservable, parent, root, ad
|
|
|
576
613
|
: undefined;
|
|
577
614
|
try {
|
|
578
615
|
const productResponse = yield productLoaderForGroupedLoad.getProduct(Object.assign(Object.assign({ lang }, correctDefaultsOnCatalogueParams(catId)), { partNumber }));
|
|
579
|
-
const { productData, rootFeatureRefs, features:
|
|
616
|
+
const { productData, rootFeatureRefs, features: rawFeatures, uuid, unit, } = productResponse;
|
|
580
617
|
const product = yield new Promise((initSuccess, initFail) => {
|
|
618
|
+
var _a;
|
|
581
619
|
const p = new _CfgProductInternal(() => {
|
|
582
620
|
// We absolutely do not want anyone to assign to this._configuration. So we want that field private.
|
|
583
621
|
// But we can not set the api selection synchronously. And the product configuration needs "this". So we use this callback.
|
|
584
622
|
// Feel free to find a nicer more readable solution :)
|
|
585
623
|
initSuccess(p);
|
|
586
|
-
}, initFail, productLoaderRaw, lang, catId, partNumber, settings, optional, !optional, rootFeatureRefs,
|
|
624
|
+
}, initFail, productLoaderRaw, lang, catId, partNumber, settings, optional, !optional, rootFeatureRefs, rawFeatures, uuid, unit, productData, (_a = productData.partsData.selOptions) !== null && _a !== void 0 ? _a : [], loadingObservable, parent, root, additionalProductRef, syncGroupHandler);
|
|
587
625
|
});
|
|
588
626
|
yield product._syncAndLoadAdditionalProducts(productLoaderForGroupedLoad);
|
|
589
|
-
|
|
590
|
-
|
|
627
|
+
product._initialClone = yield product.clone();
|
|
628
|
+
if (syncGroupHandler !== undefined) {
|
|
629
|
+
// As syncGroupHandler is only set for root product we know that we will init with root
|
|
630
|
+
yield syncGroupHandler.init(product, productLoaderForGroupedLoad);
|
|
631
|
+
}
|
|
591
632
|
return product;
|
|
592
633
|
}
|
|
593
634
|
catch (e) {
|
|
@@ -31,14 +31,13 @@ export declare type FeatureChangeNotification = {
|
|
|
31
31
|
*/
|
|
32
32
|
export declare class _CfgFeatureInternal {
|
|
33
33
|
readonly rawFeature: Feature;
|
|
34
|
-
private readonly
|
|
35
|
-
|
|
34
|
+
private readonly rawFeatures;
|
|
35
|
+
private _key;
|
|
36
36
|
readonly parent: _CfgProductConfigurationInternal | _CfgOptionInternal;
|
|
37
37
|
readonly parentConfiguration: _CfgProductConfigurationInternal;
|
|
38
38
|
readonly parentProduct: _CfgProductInternal;
|
|
39
39
|
readonly rootProduct: _CfgProductInternal;
|
|
40
|
-
constructor(rawFeature: Feature,
|
|
41
|
-
parent: _CfgProductConfigurationInternal | _CfgOptionInternal, parentConfiguration: _CfgProductConfigurationInternal, parentProduct: _CfgProductInternal, rootProduct: _CfgProductInternal);
|
|
40
|
+
constructor(rawFeature: Feature, rawFeatures: Feature[], _key: string, parent: _CfgProductConfigurationInternal | _CfgOptionInternal, parentConfiguration: _CfgProductConfigurationInternal, parentProduct: _CfgProductInternal, rootProduct: _CfgProductInternal);
|
|
42
41
|
readonly selectionType: SelectionType;
|
|
43
42
|
private _options;
|
|
44
43
|
private readonly _selectedOptions;
|
|
@@ -47,6 +46,8 @@ export declare class _CfgFeatureInternal {
|
|
|
47
46
|
readonly changeObservable: Observable<FeatureChangeNotification>;
|
|
48
47
|
get code(): string;
|
|
49
48
|
get groupCode(): string | undefined;
|
|
49
|
+
get key(): string;
|
|
50
|
+
set key(k: string);
|
|
50
51
|
get isUseNumericValue(): boolean;
|
|
51
52
|
get numericValue(): number | undefined;
|
|
52
53
|
setNumericValue: (val: number) => Promise<boolean>;
|
|
@@ -129,7 +130,7 @@ export declare class _CfgFeatureInternal {
|
|
|
129
130
|
}
|
|
130
131
|
export declare class CfgFeature {
|
|
131
132
|
readonly _internal: _CfgFeatureInternal;
|
|
132
|
-
static make(rawFeature: Feature,
|
|
133
|
+
static make(rawFeature: Feature, rawFeatures: Feature[], key: string, parent: _CfgProductConfigurationInternal | _CfgOptionInternal, parentConfiguration: _CfgProductConfigurationInternal, parentProduct: _CfgProductInternal, rootProduct: _CfgProductInternal): CfgFeature;
|
|
133
134
|
/**
|
|
134
135
|
* Makes an object wrapping the passed object. This is not a clone method,
|
|
135
136
|
* it is a method to make a new outer reference. Like a shallow copy.
|
|
@@ -62,11 +62,10 @@ function doFreshRefOption(options, optionInternal, beforeNotify) {
|
|
|
62
62
|
* should be used and interacted with.
|
|
63
63
|
*/
|
|
64
64
|
export class _CfgFeatureInternal {
|
|
65
|
-
constructor(rawFeature,
|
|
66
|
-
parent, parentConfiguration, parentProduct, rootProduct) {
|
|
65
|
+
constructor(rawFeature, rawFeatures, _key, parent, parentConfiguration, parentProduct, rootProduct) {
|
|
67
66
|
this.rawFeature = rawFeature;
|
|
68
|
-
this.
|
|
69
|
-
this.
|
|
67
|
+
this.rawFeatures = rawFeatures;
|
|
68
|
+
this._key = _key;
|
|
70
69
|
this.parent = parent;
|
|
71
70
|
this.parentConfiguration = parentConfiguration;
|
|
72
71
|
this.parentProduct = parentProduct;
|
|
@@ -398,6 +397,12 @@ export class _CfgFeatureInternal {
|
|
|
398
397
|
get groupCode() {
|
|
399
398
|
return this.rawFeature.groupCode;
|
|
400
399
|
}
|
|
400
|
+
get key() {
|
|
401
|
+
return this._key;
|
|
402
|
+
}
|
|
403
|
+
set key(k) {
|
|
404
|
+
this._key = k;
|
|
405
|
+
}
|
|
401
406
|
get isUseNumericValue() {
|
|
402
407
|
return this.rawFeature.numericOrder;
|
|
403
408
|
}
|
|
@@ -503,7 +508,7 @@ export class _CfgFeatureInternal {
|
|
|
503
508
|
const hasDuplicateDescription = someMatch(this.rawFeature.options, (l, r) => {
|
|
504
509
|
return l.description.toLowerCase() === r.description.toLowerCase();
|
|
505
510
|
});
|
|
506
|
-
this._options = this.rawFeature.options.map((o) => CfgOption.make(o, this.
|
|
511
|
+
this._options = this.rawFeature.options.map((o) => CfgOption.make(o, this.rawFeatures, hasDuplicateDescription, this, this.parentConfiguration, this.parentProduct, this.rootProduct));
|
|
507
512
|
}
|
|
508
513
|
return this._options;
|
|
509
514
|
}
|
|
@@ -562,8 +567,8 @@ export class CfgFeature {
|
|
|
562
567
|
this.listenForChange = (l) => this._internal.changeObservable.listen(l);
|
|
563
568
|
this.stopListenForChange = (l) => this._internal.changeObservable.stopListen(l);
|
|
564
569
|
}
|
|
565
|
-
static make(rawFeature,
|
|
566
|
-
return new this(new _CfgFeatureInternal(rawFeature,
|
|
570
|
+
static make(rawFeature, rawFeatures, key, parent, parentConfiguration, parentProduct, rootProduct) {
|
|
571
|
+
return new this(new _CfgFeatureInternal(rawFeature, rawFeatures, key, parent, parentConfiguration, parentProduct, rootProduct));
|
|
567
572
|
}
|
|
568
573
|
/**
|
|
569
574
|
* Makes an object wrapping the passed object. This is not a clone method,
|
|
@@ -582,6 +587,7 @@ export class CfgFeature {
|
|
|
582
587
|
get selectionType() {
|
|
583
588
|
return this._internal.selectionType;
|
|
584
589
|
}
|
|
590
|
+
// Unique amongst siblings. Can change. Only use for presentation layer i.e. React.
|
|
585
591
|
get key() {
|
|
586
592
|
return this._internal.key;
|
|
587
593
|
}
|
|
@@ -59,12 +59,12 @@ export declare enum ProductConfigurationBubbleMode {
|
|
|
59
59
|
*/
|
|
60
60
|
export declare class _CfgOptionInternal {
|
|
61
61
|
readonly rawOption: Option;
|
|
62
|
-
private readonly
|
|
62
|
+
private readonly rawFeatures;
|
|
63
63
|
readonly parent: _CfgFeatureInternal;
|
|
64
64
|
readonly parentConfiguration: _CfgProductConfigurationInternal;
|
|
65
65
|
readonly parentProduct: _CfgProductInternal;
|
|
66
66
|
readonly rootProduct: _CfgProductInternal;
|
|
67
|
-
constructor(rawOption: Option,
|
|
67
|
+
constructor(rawOption: Option, rawFeatures: Feature[], siblingHasDuplicateDescription: boolean, parent: _CfgFeatureInternal, parentConfiguration: _CfgProductConfigurationInternal, parentProduct: _CfgProductInternal, rootProduct: _CfgProductInternal);
|
|
68
68
|
private _features;
|
|
69
69
|
private _mtrlApplications;
|
|
70
70
|
readonly key: string;
|
|
@@ -98,7 +98,7 @@ export declare class _CfgOptionInternal {
|
|
|
98
98
|
}
|
|
99
99
|
export declare class CfgOption {
|
|
100
100
|
readonly _internal: _CfgOptionInternal;
|
|
101
|
-
static make(rawOption: Option,
|
|
101
|
+
static make(rawOption: Option, rawFeatures: Feature[], siblingHasDuplicateDescription: boolean, parent: _CfgFeatureInternal, parentConfiguration: _CfgProductConfigurationInternal, parentProduct: _CfgProductInternal, rootProduct: _CfgProductInternal): CfgOption;
|
|
102
102
|
/**
|
|
103
103
|
* Makes an object wrapping the passed object. This is not a clone method,
|
|
104
104
|
* it is a method to make a new outer reference. Like a shallow copy.
|
|
@@ -80,9 +80,9 @@ function doesChildrenShareOptionsCode(features) {
|
|
|
80
80
|
* the class that should be used and interacted with.
|
|
81
81
|
*/
|
|
82
82
|
export class _CfgOptionInternal {
|
|
83
|
-
constructor(rawOption,
|
|
83
|
+
constructor(rawOption, rawFeatures, siblingHasDuplicateDescription, parent, parentConfiguration, parentProduct, rootProduct) {
|
|
84
84
|
this.rawOption = rawOption;
|
|
85
|
-
this.
|
|
85
|
+
this.rawFeatures = rawFeatures;
|
|
86
86
|
this.parent = parent;
|
|
87
87
|
this.parentConfiguration = parentConfiguration;
|
|
88
88
|
this.parentProduct = parentProduct;
|
|
@@ -314,7 +314,7 @@ export class _CfgOptionInternal {
|
|
|
314
314
|
get features() {
|
|
315
315
|
if (this._features === undefined) {
|
|
316
316
|
const allRefs = this.rawOption.featureRefs || [];
|
|
317
|
-
const features = syncCfgFeatures(allRefs, [], this.
|
|
317
|
+
const features = syncCfgFeatures(allRefs, [], this.rawFeatures, this, this.parentConfiguration, this.parentProduct, this.rootProduct);
|
|
318
318
|
if (doesChildrenShareOptionsCode(features)) {
|
|
319
319
|
throw new Error("Stage does not yet properly support Options that has multiple sub-features with overlapping option codes.");
|
|
320
320
|
}
|
|
@@ -353,8 +353,8 @@ export class CfgOption {
|
|
|
353
353
|
this.listenForChange = (l) => this._internal.changeObservable.listen(l);
|
|
354
354
|
this.stopListenForChange = (l) => this._internal.changeObservable.stopListen(l);
|
|
355
355
|
}
|
|
356
|
-
static make(rawOption,
|
|
357
|
-
return new this(new _CfgOptionInternal(rawOption,
|
|
356
|
+
static make(rawOption, rawFeatures, siblingHasDuplicateDescription, parent, parentConfiguration, parentProduct, rootProduct) {
|
|
357
|
+
return new this(new _CfgOptionInternal(rawOption, rawFeatures, siblingHasDuplicateDescription, parent, parentConfiguration, parentProduct, rootProduct));
|
|
358
358
|
}
|
|
359
359
|
/**
|
|
360
360
|
* Makes an object wrapping the passed object. This is not a clone method,
|
|
@@ -20,14 +20,14 @@ export declare type StretchMap = Map<string, {
|
|
|
20
20
|
* modified. CfgProductConfiguration is the class that should be used and interacted with.
|
|
21
21
|
*/
|
|
22
22
|
export declare class _CfgProductConfigurationInternal {
|
|
23
|
-
readonly allRawFeatures: Feature[];
|
|
24
23
|
readonly parentProduct: _CfgProductInternal;
|
|
25
24
|
readonly rootProduct: _CfgProductInternal;
|
|
26
|
-
static _makeUninitialized(rootFeatureRefs: FeatureRef[],
|
|
25
|
+
static _makeUninitialized(rootFeatureRefs: FeatureRef[], rawFeatures: Feature[], // Flat packed. All the features that can currently appear anyplace in the selection tree.
|
|
27
26
|
parentProduct: _CfgProductInternal, rootProduct: _CfgProductInternal): _CfgProductConfigurationInternal;
|
|
28
27
|
private constructor();
|
|
29
28
|
readonly key = "~";
|
|
30
29
|
private _rootFeatureRefs;
|
|
30
|
+
readonly accumulatedRawFeatures: Feature[];
|
|
31
31
|
private _features;
|
|
32
32
|
readonly stretchReferenceLengthsByMeasureParamCode: StretchMap;
|
|
33
33
|
readonly changeObservable: Observable<ProductConfigurationChangeNotification>;
|
|
@@ -51,7 +51,16 @@ export declare class _CfgProductConfigurationInternal {
|
|
|
51
51
|
tryMatchSelection: (other: _CfgProductConfigurationInternal, descriptionMatch: boolean | undefined, validate: boolean) => Promise<boolean>;
|
|
52
52
|
/** Only selected features. */
|
|
53
53
|
_getFeaturesWithCode: (code: string) => _CfgFeatureInternal[];
|
|
54
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Extends the list of loaded potentially used features. Will warn for but ignore duplicates.
|
|
56
|
+
* Returns true if a change happened.
|
|
57
|
+
*/
|
|
58
|
+
addRawFeatures: (rawFeatures: Feature[], warnForDuplicates: boolean) => boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Populates _features based on the passed @param rootFeatureRefs .
|
|
61
|
+
* @return true if a change happened.
|
|
62
|
+
*/
|
|
63
|
+
populateFeatures: (rootFeatureRefs: FeatureRef[]) => boolean;
|
|
55
64
|
setStretchReferenceLength: (measureParamCode: string, referenceLength: number | undefined, unit: LengthUnit, doNotify?: boolean) => Promise<boolean>;
|
|
56
65
|
}
|
|
57
66
|
export declare class CfgProductConfiguration {
|
|
@@ -61,7 +70,7 @@ export declare class CfgProductConfiguration {
|
|
|
61
70
|
* CfgProductConfiguration, but is not properly initialized until the initDone-callback
|
|
62
71
|
* has been called.
|
|
63
72
|
*/
|
|
64
|
-
static make(initSuccess: (c: CfgProductConfiguration) => void, initFail: (error: Error) => void, rootFeatureRefs: FeatureRef[],
|
|
73
|
+
static make(initSuccess: (c: CfgProductConfiguration) => void, initFail: (error: Error) => void, rootFeatureRefs: FeatureRef[], rawFeatures: Feature[], // Flat packed. All the features that can appear anyplace in the selection tree.
|
|
65
74
|
apiSelection: SelectedOption[], parentProduct: _CfgProductInternal, rootProduct: _CfgProductInternal): CfgProductConfiguration;
|
|
66
75
|
/**
|
|
67
76
|
* Makes an object wrapping the passed object. This is not a clone method, it is a method to
|
|
@@ -81,11 +90,11 @@ export declare class CfgProductConfiguration {
|
|
|
81
90
|
get rootProduct(): CfgProduct;
|
|
82
91
|
get key(): string;
|
|
83
92
|
/**
|
|
84
|
-
* Every (unprocessed) feature
|
|
85
|
-
*
|
|
93
|
+
* Every (unprocessed) feature that is currently loaded for this product. This can be extended
|
|
94
|
+
* by validation calls. What features are actually used is controlled by rootFeatureRefs and what
|
|
86
95
|
* options are selected.
|
|
87
96
|
*/
|
|
88
|
-
get
|
|
97
|
+
get rawFeatures(): Feature[];
|
|
89
98
|
/** What features are used in the root of this. This can change with new validate calls. */
|
|
90
99
|
get rootFeatureRefs(): FeatureRef[];
|
|
91
100
|
/** The root features at the root of the selection tree. */
|
|
@@ -18,13 +18,12 @@ import { syncCfgFeatures } from "./utilitiesProductConfiguration.js";
|
|
|
18
18
|
* modified. CfgProductConfiguration is the class that should be used and interacted with.
|
|
19
19
|
*/
|
|
20
20
|
export class _CfgProductConfigurationInternal {
|
|
21
|
-
constructor(
|
|
22
|
-
parentProduct, rootProduct) {
|
|
23
|
-
this.allRawFeatures = allRawFeatures;
|
|
21
|
+
constructor(rawFeatures, parentProduct, rootProduct) {
|
|
24
22
|
this.parentProduct = parentProduct;
|
|
25
23
|
this.rootProduct = rootProduct;
|
|
26
24
|
this.key = "~";
|
|
27
25
|
this._rootFeatureRefs = [];
|
|
26
|
+
this.accumulatedRawFeatures = []; // Flat packed. May be extended in validate calls.
|
|
28
27
|
this._features = [];
|
|
29
28
|
this.changeObservable = new Observable();
|
|
30
29
|
this._notifyAllOfChange = (bubbleMode) => __awaiter(this, void 0, void 0, function* () {
|
|
@@ -111,9 +110,37 @@ export class _CfgProductConfigurationInternal {
|
|
|
111
110
|
agg.push(...feature._internal._getFeaturesWithCode(code));
|
|
112
111
|
return agg;
|
|
113
112
|
}, []);
|
|
113
|
+
/**
|
|
114
|
+
* Extends the list of loaded potentially used features. Will warn for but ignore duplicates.
|
|
115
|
+
* Returns true if a change happened.
|
|
116
|
+
*/
|
|
117
|
+
this.addRawFeatures = (rawFeatures, warnForDuplicates) => {
|
|
118
|
+
let change = false;
|
|
119
|
+
const accumulatedRawFeatures = this.accumulatedRawFeatures;
|
|
120
|
+
for (const rawFeature of rawFeatures) {
|
|
121
|
+
const code = rawFeature.code;
|
|
122
|
+
if (accumulatedRawFeatures.find((f) => code === f.code)) {
|
|
123
|
+
if (warnForDuplicates) {
|
|
124
|
+
console.warn(`Feature ${code} was already loaded. Will ignore it.`);
|
|
125
|
+
}
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
accumulatedRawFeatures.push(rawFeature);
|
|
129
|
+
change = true;
|
|
130
|
+
}
|
|
131
|
+
return change;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Populates _features based on the passed @param rootFeatureRefs .
|
|
135
|
+
* @return true if a change happened.
|
|
136
|
+
*/
|
|
114
137
|
this.populateFeatures = (rootFeatureRefs) => {
|
|
138
|
+
if (compareArrays(this._rootFeatureRefs, rootFeatureRefs, (l, r) => l.code === r.code, true)) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
115
141
|
this._rootFeatureRefs = rootFeatureRefs;
|
|
116
|
-
this._features = syncCfgFeatures(rootFeatureRefs, this._features, this.
|
|
142
|
+
this._features = syncCfgFeatures(rootFeatureRefs, this._features, this.accumulatedRawFeatures, this, this, this.parentProduct, this.rootProduct);
|
|
143
|
+
return true;
|
|
117
144
|
};
|
|
118
145
|
this.setStretchReferenceLength = (measureParamCode, referenceLength, unit, doNotify = true) => __awaiter(this, void 0, void 0, function* () {
|
|
119
146
|
if (measureParamCode === "") {
|
|
@@ -160,6 +187,7 @@ export class _CfgProductConfigurationInternal {
|
|
|
160
187
|
console.log(`Use "window['${dbgName}']" or window.conf to access conf.`);
|
|
161
188
|
}
|
|
162
189
|
/* eslint-enable */
|
|
190
|
+
this.addRawFeatures(rawFeatures, true);
|
|
163
191
|
/**
|
|
164
192
|
* Although the measurement-datas are also passed in validate-calls they are only used at
|
|
165
193
|
* initial product creation. The data is assumed to always be the same for a product, so
|
|
@@ -196,9 +224,9 @@ export class _CfgProductConfigurationInternal {
|
|
|
196
224
|
}
|
|
197
225
|
this.stretchReferenceLengthsByMeasureParamCode = stretchReferenceLengthsByMeasureParamCode;
|
|
198
226
|
}
|
|
199
|
-
static _makeUninitialized(rootFeatureRefs,
|
|
227
|
+
static _makeUninitialized(rootFeatureRefs, rawFeatures, // Flat packed. All the features that can currently appear anyplace in the selection tree.
|
|
200
228
|
parentProduct, rootProduct) {
|
|
201
|
-
const configuration = new this(
|
|
229
|
+
const configuration = new this(rawFeatures, parentProduct, rootProduct);
|
|
202
230
|
configuration.populateFeatures(rootFeatureRefs);
|
|
203
231
|
return configuration;
|
|
204
232
|
}
|
|
@@ -261,9 +289,9 @@ export class CfgProductConfiguration {
|
|
|
261
289
|
* CfgProductConfiguration, but is not properly initialized until the initDone-callback
|
|
262
290
|
* has been called.
|
|
263
291
|
*/
|
|
264
|
-
static make(initSuccess, initFail, rootFeatureRefs,
|
|
292
|
+
static make(initSuccess, initFail, rootFeatureRefs, rawFeatures, // Flat packed. All the features that can appear anyplace in the selection tree.
|
|
265
293
|
apiSelection, parentProduct, rootProduct) {
|
|
266
|
-
const internal = _CfgProductConfigurationInternal._makeUninitialized(rootFeatureRefs,
|
|
294
|
+
const internal = _CfgProductConfigurationInternal._makeUninitialized(rootFeatureRefs, rawFeatures, parentProduct, rootProduct);
|
|
267
295
|
const external = new this(internal);
|
|
268
296
|
// Note the async-callback at the end of the following line. The call is async.
|
|
269
297
|
internal
|
|
@@ -291,12 +319,12 @@ export class CfgProductConfiguration {
|
|
|
291
319
|
return this._internal.key;
|
|
292
320
|
}
|
|
293
321
|
/**
|
|
294
|
-
* Every (unprocessed) feature
|
|
295
|
-
*
|
|
322
|
+
* Every (unprocessed) feature that is currently loaded for this product. This can be extended
|
|
323
|
+
* by validation calls. What features are actually used is controlled by rootFeatureRefs and what
|
|
296
324
|
* options are selected.
|
|
297
325
|
*/
|
|
298
|
-
get
|
|
299
|
-
return this._internal.
|
|
326
|
+
get rawFeatures() {
|
|
327
|
+
return this._internal.accumulatedRawFeatures;
|
|
300
328
|
}
|
|
301
329
|
/** What features are used in the root of this. This can change with new validate calls. */
|
|
302
330
|
get rootFeatureRefs() {
|
|
@@ -8,7 +8,7 @@ import { CfgProductConfiguration, _CfgProductConfigurationInternal } from "./Cfg
|
|
|
8
8
|
* Returns a new array of CfgFeatures that maps to the newFeatureRefs array. Uses CfgFeatures from
|
|
9
9
|
* currentFeatures if they can be found, otherwise makes new.
|
|
10
10
|
*/
|
|
11
|
-
export declare function syncCfgFeatures(newFeatureRefs: FeatureRef[], currentFeatures: CfgFeature[],
|
|
11
|
+
export declare function syncCfgFeatures(newFeatureRefs: FeatureRef[], currentFeatures: CfgFeature[], rawFeatures: Feature[], parent: _CfgProductConfigurationInternal | _CfgOptionInternal, parentConfiguration: _CfgProductConfigurationInternal, parentProduct: _CfgProductInternal, rootProduct: _CfgProductInternal): CfgFeature[];
|
|
12
12
|
export declare function getMtrlPreview(mtrlApplications: CfgMtrlApplication[] | undefined): string | undefined;
|
|
13
13
|
/**
|
|
14
14
|
* Recursively find all additional product references given a product configuration.
|
|
@@ -6,11 +6,11 @@ import { CfgProductConfiguration, } from "./CfgProductConfiguration.js";
|
|
|
6
6
|
* Returns a new array of CfgFeatures that maps to the newFeatureRefs array. Uses CfgFeatures from
|
|
7
7
|
* currentFeatures if they can be found, otherwise makes new.
|
|
8
8
|
*/
|
|
9
|
-
export function syncCfgFeatures(newFeatureRefs, currentFeatures,
|
|
9
|
+
export function syncCfgFeatures(newFeatureRefs, currentFeatures, rawFeatures, parent, parentConfiguration, parentProduct, rootProduct) {
|
|
10
10
|
const usedRawFeatures = newFeatureRefs
|
|
11
11
|
.map((r) => r.code)
|
|
12
12
|
.map((c) => {
|
|
13
|
-
const rawFeature =
|
|
13
|
+
const rawFeature = rawFeatures.find((f) => c === f.code);
|
|
14
14
|
if (rawFeature === undefined) {
|
|
15
15
|
throw new Error(`Feature not found. Requested feature code: "${c}".`);
|
|
16
16
|
}
|
|
@@ -38,12 +38,19 @@ export function syncCfgFeatures(newFeatureRefs, currentFeatures, allRawFeatures,
|
|
|
38
38
|
// products with similar feature-options tree and trying
|
|
39
39
|
// to retain made selections
|
|
40
40
|
const key = fDescription + (fDescription === "" || hasDuplicateDescription ? fCode : "");
|
|
41
|
-
const existingFeature = currentFeatures.find((cfgF) => cfgF.code === fCode
|
|
41
|
+
const existingFeature = currentFeatures.find((cfgF) => cfgF.code === fCode);
|
|
42
42
|
if (existingFeature !== undefined) {
|
|
43
|
+
if (hasDuplicateDescription) {
|
|
44
|
+
// HasDuplicateDescription could mean the key need to be more specific compared
|
|
45
|
+
// to the one we already have, so then we set it. An old duplicate description
|
|
46
|
+
// key will cause no harm as it is guaranteed to be at least as specific as this.
|
|
47
|
+
// Not changing the key will make React not rerender, so we don't change.
|
|
48
|
+
existingFeature._internal.key = key;
|
|
49
|
+
}
|
|
43
50
|
newFeatures.push(existingFeature);
|
|
44
51
|
continue;
|
|
45
52
|
}
|
|
46
|
-
newFeatures.push(CfgFeature.make(f,
|
|
53
|
+
newFeatures.push(CfgFeature.make(f, rawFeatures, key, parent, parentConfiguration, parentProduct, rootProduct));
|
|
47
54
|
}
|
|
48
55
|
return newFeatures;
|
|
49
56
|
}
|
|
@@ -24,7 +24,10 @@ export declare class SyncGroupsHandler {
|
|
|
24
24
|
clone(): SyncGroupsHandler;
|
|
25
25
|
get verboseLogging(): boolean;
|
|
26
26
|
set verboseLogging(v: boolean);
|
|
27
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Used to initially apply the sync state onto a new product so that it is "in sync"
|
|
29
|
+
* and to reapply the sync state when an optional additional product is selected.
|
|
30
|
+
*/
|
|
28
31
|
init(product: _CfgProductInternal, productLoader: ProductLoader): Promise<void>;
|
|
29
32
|
/**
|
|
30
33
|
* Used when an Option is selected or deselected to apply all consequences of the sync groups.
|
|
@@ -123,7 +123,8 @@ import { SyncGroupsTransaction } from "./SyncGroupsTransaction.js";
|
|
|
123
123
|
* C) The SyncState has an option code for this SyncGroup.
|
|
124
124
|
* D) The option code in the SyncState for the SyncGroup is not the Option selected.
|
|
125
125
|
* E) The Feature has an Option with the right option code.
|
|
126
|
-
* F) The Feature has not previously been affected in this transaction
|
|
126
|
+
* F) The Feature has not previously been affected in this transaction, unless it was affected
|
|
127
|
+
* at Feature initialization.
|
|
127
128
|
*
|
|
128
129
|
* ...for SelectMany (done for every Option):
|
|
129
130
|
* C) The SyncState has a value (on or off) for this SyncGroup and option code.
|
|
@@ -274,7 +275,10 @@ export class SyncGroupsHandler {
|
|
|
274
275
|
set verboseLogging(v) {
|
|
275
276
|
this._syncState.verboseLogging = v;
|
|
276
277
|
}
|
|
277
|
-
/**
|
|
278
|
+
/**
|
|
279
|
+
* Used to initially apply the sync state onto a new product so that it is "in sync"
|
|
280
|
+
* and to reapply the sync state when an optional additional product is selected.
|
|
281
|
+
*/
|
|
278
282
|
init(product, productLoader) {
|
|
279
283
|
return __awaiter(this, void 0, void 0, function* () {
|
|
280
284
|
const transaction = yield this.newTransaction(product, productLoader, true);
|
|
@@ -13,6 +13,12 @@ import { SelectionType, } from "../productConfiguration/CfgFeature.js";
|
|
|
13
13
|
import { ProductConfigurationBubbleMode, } from "../productConfiguration/CfgOption.js";
|
|
14
14
|
import { SyncGroupsApplyMode } from "./SyncGroupsApplyMode.js";
|
|
15
15
|
import { SyncGroupsPathHelper } from "./SyncGroupsPathHelper.js";
|
|
16
|
+
var AffectedLevel;
|
|
17
|
+
(function (AffectedLevel) {
|
|
18
|
+
AffectedLevel[AffectedLevel["None"] = 0] = "None";
|
|
19
|
+
AffectedLevel[AffectedLevel["Initialized"] = 1] = "Initialized";
|
|
20
|
+
AffectedLevel[AffectedLevel["Set"] = 2] = "Set";
|
|
21
|
+
})(AffectedLevel || (AffectedLevel = {}));
|
|
16
22
|
/**
|
|
17
23
|
* The Transaction is a transient object used to track all the changes made to the supplied
|
|
18
24
|
* SyncGroupState as a result of a (normally) single initial event, like when opening a product
|
|
@@ -39,7 +45,7 @@ import { SyncGroupsPathHelper } from "./SyncGroupsPathHelper.js";
|
|
|
39
45
|
export class SyncGroupsTransaction {
|
|
40
46
|
constructor(syncState, updateMode, productLoader, original, target, initial) {
|
|
41
47
|
this._closed = false;
|
|
42
|
-
this.affectedSelectOneFeatures = new
|
|
48
|
+
this.affectedSelectOneFeatures = new Map();
|
|
43
49
|
this.affectedSelectManyOptions = new Set();
|
|
44
50
|
this.affectedSelectOneSyncGroups = new Set();
|
|
45
51
|
this.affectedSelectManySyncGroupsAndOptions = new Map();
|
|
@@ -104,7 +110,7 @@ export class SyncGroupsTransaction {
|
|
|
104
110
|
change = true;
|
|
105
111
|
switch (feature.selectionType) {
|
|
106
112
|
case SelectionType.SelectOne:
|
|
107
|
-
this.affectedSelectOneFeatures.
|
|
113
|
+
this.affectedSelectOneFeatures.set(feature, AffectedLevel.Set);
|
|
108
114
|
break;
|
|
109
115
|
case SelectionType.SelectMany:
|
|
110
116
|
this.affectedSelectManyOptions.add(option);
|
|
@@ -266,14 +272,19 @@ export class SyncGroupsTransaction {
|
|
|
266
272
|
return __awaiter(this, void 0, void 0, function* () {
|
|
267
273
|
const feature = featureWithInitial.target;
|
|
268
274
|
const featureDidJustComeIntoScope = featureWithInitial.initial === undefined;
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
//
|
|
275
|
+
const hasBeenAffectedLevel = this.affectedSelectOneFeatures.get(feature);
|
|
276
|
+
const syncGroupHasBeenUpdated = this.affectedSelectOneSyncGroups.has(syncCode);
|
|
277
|
+
if (hasBeenAffectedLevel === AffectedLevel.Set ||
|
|
278
|
+
(!syncGroupHasBeenUpdated && hasBeenAffectedLevel === AffectedLevel.Initialized)) {
|
|
279
|
+
// A feature can change value for two reasons:
|
|
280
|
+
// 1. The feature did just come into scope and loads from the sync group to set its "defaults"
|
|
281
|
+
// 2. The sync group has updated with a new value
|
|
282
|
+
// The rule is that in one sync group transaction the feature value is allowed to be updated
|
|
283
|
+
// once or twice. Once for initialization and once for new sync group value. But they have to
|
|
284
|
+
// happen in the right order.
|
|
274
285
|
return "recurseDown";
|
|
275
286
|
}
|
|
276
|
-
if (!featureDidJustComeIntoScope && !
|
|
287
|
+
if (!featureDidJustComeIntoScope && !syncGroupHasBeenUpdated) {
|
|
277
288
|
return "recurseDown";
|
|
278
289
|
}
|
|
279
290
|
const currentSyncGroupValue = this.syncState.getForSelectOne(syncCode);
|
|
@@ -295,7 +306,7 @@ export class SyncGroupsTransaction {
|
|
|
295
306
|
// Do not recurse further as we will change the state and so what is selected now won't be
|
|
296
307
|
// selected then.
|
|
297
308
|
yield feature.selectOption(optionToSelect._internal, true, ProductConfigurationBubbleMode.ToRoot);
|
|
298
|
-
this.affectedSelectOneFeatures.
|
|
309
|
+
this.affectedSelectOneFeatures.set(feature, syncGroupHasBeenUpdated ? AffectedLevel.Set : AffectedLevel.Initialized);
|
|
299
310
|
productsToValidate.add(feature.parentProduct);
|
|
300
311
|
return "stop";
|
|
301
312
|
});
|
|
@@ -435,7 +446,7 @@ export class SyncGroupsTransaction {
|
|
|
435
446
|
return change;
|
|
436
447
|
});
|
|
437
448
|
}
|
|
438
|
-
applySelectOneFeature(feature,
|
|
449
|
+
applySelectOneFeature(feature, userInitiated, featureDidJustComeIntoScope) {
|
|
439
450
|
const selectionType = feature.selectionType;
|
|
440
451
|
if (selectionType !== SelectionType.SelectOne) {
|
|
441
452
|
throw new Error("can only be used for selectOne");
|
|
@@ -453,7 +464,7 @@ export class SyncGroupsTransaction {
|
|
|
453
464
|
// Options with no default are never written
|
|
454
465
|
return false;
|
|
455
466
|
}
|
|
456
|
-
if (
|
|
467
|
+
if (userInitiated) {
|
|
457
468
|
// To make re-apply happen, even if it actually does not update the sync group
|
|
458
469
|
this.affectedSelectOneSyncGroups.add(syncCode);
|
|
459
470
|
}
|
|
@@ -464,7 +475,7 @@ export class SyncGroupsTransaction {
|
|
|
464
475
|
// can not be set to the current sync group value, then it will set in the opposite
|
|
465
476
|
// direction. Like if the sync group was empty. To avoid bouncing back and forth we will
|
|
466
477
|
// need to enforce that a sync group can only be updated once per transaction
|
|
467
|
-
if (!(
|
|
478
|
+
if (!(userInitiated ||
|
|
468
479
|
currentSyncGroupOptionCode === undefined ||
|
|
469
480
|
(featureDidJustComeIntoScope &&
|
|
470
481
|
feature.options.every((o) => currentSyncGroupOptionCode !== o.code)))) {
|
|
@@ -486,7 +497,7 @@ export class SyncGroupsTransaction {
|
|
|
486
497
|
}
|
|
487
498
|
return change;
|
|
488
499
|
}
|
|
489
|
-
applySelectManyOption(option,
|
|
500
|
+
applySelectManyOption(option, userInitiated) {
|
|
490
501
|
const feature = option.parent;
|
|
491
502
|
if (feature.selectionType !== SelectionType.SelectMany) {
|
|
492
503
|
throw new Error("can only be used for selectMany");
|
|
@@ -500,14 +511,14 @@ export class SyncGroupsTransaction {
|
|
|
500
511
|
}
|
|
501
512
|
const optionSelected = option.selected;
|
|
502
513
|
const currentSyncGroupValue = this.syncState.getForSelectMany(syncCode, option.code);
|
|
503
|
-
if (
|
|
514
|
+
if (userInitiated) {
|
|
504
515
|
// To make re-apply happen, even if it actually does not update the sync group
|
|
505
516
|
this.addSyncGroupAffectedForSelectMany(syncCode, option);
|
|
506
517
|
}
|
|
507
|
-
// We only initialize if the option
|
|
508
|
-
//
|
|
509
|
-
if (
|
|
510
|
-
!(
|
|
518
|
+
// We only initialize if the option has not been initiated or if it is userInitiated.
|
|
519
|
+
// userInitiated = active selection by the user.
|
|
520
|
+
if (currentSyncGroupValue !== undefined &&
|
|
521
|
+
!(userInitiated && currentSyncGroupValue !== optionSelected)) {
|
|
511
522
|
return false;
|
|
512
523
|
}
|
|
513
524
|
this.addSyncGroupAffectedForSelectMany(syncCode, option);
|
|
@@ -529,7 +540,9 @@ export class SyncGroupsTransaction {
|
|
|
529
540
|
}
|
|
530
541
|
function getAdditionalProducts(product) {
|
|
531
542
|
const initial = product.initial;
|
|
532
|
-
return product.target.additionalProducts
|
|
543
|
+
return product.target.additionalProducts
|
|
544
|
+
.filter((p) => p.selected)
|
|
545
|
+
.map((childTarget) => {
|
|
533
546
|
const refKey = childTarget.refKey;
|
|
534
547
|
const childInitial = initial === null || initial === void 0 ? void 0 : initial.additionalProducts.find((p) => refKey === p.refKey);
|
|
535
548
|
return {
|
|
@@ -558,8 +571,8 @@ function getOptions(feature) {
|
|
|
558
571
|
}
|
|
559
572
|
function pairFeatures(targets, initials) {
|
|
560
573
|
return targets.map((childTarget) => {
|
|
561
|
-
const
|
|
562
|
-
const childInitial = initials === null || initials === void 0 ? void 0 : initials.find((f) =>
|
|
574
|
+
const code = childTarget.code;
|
|
575
|
+
const childInitial = initials === null || initials === void 0 ? void 0 : initials.find((f) => code === f.code);
|
|
563
576
|
return {
|
|
564
577
|
target: childTarget._internal,
|
|
565
578
|
initial: childInitial === null || childInitial === void 0 ? void 0 : childInitial._internal,
|
|
@@ -254,6 +254,7 @@ export const cfgProductTest = (testFunc, prepFunc) => __awaiter(void 0, void 0,
|
|
|
254
254
|
uuid: "",
|
|
255
255
|
validated: true,
|
|
256
256
|
productData,
|
|
257
|
+
features: []
|
|
257
258
|
};
|
|
258
259
|
collect.pushNotification(`Validate ${params.enterprise} ${params.prdCat} ${params.prdCatVersion} ${params.priceList} ${params.vendor} ${params.partNumber}`);
|
|
259
260
|
return validateResponse;
|
|
@@ -9,6 +9,11 @@ export declare type CfgProductData = Omit<Omit<ProductData, "models">, "partsDat
|
|
|
9
9
|
selOptions: SelectedOption[];
|
|
10
10
|
};
|
|
11
11
|
};
|
|
12
|
+
/**
|
|
13
|
+
* Deep compare. Uses the JSON.stringify method for comparison
|
|
14
|
+
* @returns true if equal. Key and array order is respected.
|
|
15
|
+
*/
|
|
16
|
+
export declare function compareCfgProductData(pd1: CfgProductData, pd2: CfgProductData): boolean;
|
|
12
17
|
export declare type CfgProductResponse = Omit<ProductResponse, "productData"> & {
|
|
13
18
|
productData: CfgProductData;
|
|
14
19
|
};
|
|
@@ -16,6 +16,13 @@ export const makeSelOptionsKey = (options) => options.reduce((p, option) => {
|
|
|
16
16
|
p += "_}";
|
|
17
17
|
return p;
|
|
18
18
|
}, "");
|
|
19
|
+
/**
|
|
20
|
+
* Deep compare. Uses the JSON.stringify method for comparison
|
|
21
|
+
* @returns true if equal. Key and array order is respected.
|
|
22
|
+
*/
|
|
23
|
+
export function compareCfgProductData(pd1, pd2) {
|
|
24
|
+
return JSON.stringify(pd1) === JSON.stringify(pd2);
|
|
25
|
+
}
|
|
19
26
|
export function isModel(arg) {
|
|
20
27
|
return typeof arg === "object" && arg !== null && "cid" in arg && "uri" in arg;
|
|
21
28
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@configura/web-api",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@configura/web-utilities": "1.
|
|
26
|
+
"@configura/web-utilities": "1.7.0"
|
|
27
27
|
},
|
|
28
|
-
"gitHead": "
|
|
28
|
+
"gitHead": "f6e1e62d91488535ef5b65eb93bbd69bdf1aabe7"
|
|
29
29
|
}
|