@nuskin/ns-product-lib 2.6.1-cx24-3608.4 → 2.6.1-cx24-3607.1

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -1,32 +1,10 @@
1
- ## [2.6.1-cx24-3608.4](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/compare/v2.6.1-cx24-3608.3...v2.6.1-cx24-3608.4) (2023-03-15)
1
+ ## [2.6.1-cx24-3607.1](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/compare/v2.6.0...v2.6.1-cx24-3607.1) (2023-03-15)
2
2
 
3
3
 
4
4
  ### Fix
5
5
 
6
- * imageURL issue when product is kit ([0bde1fb](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/commit/0bde1fb0ce99186a30859c2f3509d32c1861cf7b))
7
- * imageURL issue when product is kit ([e302726](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/commit/e30272648ad6214c0dca08f40a6a3f5511c661cd))
8
-
9
- ## [2.6.1-cx24-3608.3](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/compare/v2.6.1-cx24-3608.2...v2.6.1-cx24-3608.3) (2023-03-15)
10
-
11
-
12
- ### Fix
13
-
14
- * imageURL issue when product is kit ([0e4281d](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/commit/0e4281de4435d1a5749ff9b6d52a7c4a45d75d6d))
15
-
16
- ## [2.6.1-cx24-3608.2](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/compare/v2.6.1-cx24-3608.1...v2.6.1-cx24-3608.2) (2023-03-15)
17
-
18
-
19
- ### Fix
20
-
21
- * imageURL issue when product is kit ([3172244](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/commit/3172244e53c328e90d663a0989c25c25eba967d3))
22
-
23
- ## [2.6.1-cx24-3608.1](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/compare/v2.6.0...v2.6.1-cx24-3608.1) (2023-03-15)
24
-
25
-
26
- ### Fix
27
-
28
- * imageURL issue when product is kit ([2a68af6](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/commit/2a68af668a2ca0835a4b0d0995117ea8ab019bed))
29
- * imageURL issue when product is kit ([656505a](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/commit/656505a32421ae4e5d8e29dd451b68b24e72f30d))
6
+ * (equinox-enabled) skip product mapping when an error occurs in PLP #CX24-3607 ([950c165](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/commit/950c1650115f92badc5b898b5df722568b034afe)), closes [#CX24-3607](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/issues/CX24-3607)
7
+ * Cannot read properties of undefined (reading 'qty') in PLP #CX24-3607 ([8cde88f](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/commit/8cde88f2f61279d20794a0e43696187c4326ca28)), closes [#CX24-3607](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/issues/CX24-3607)
30
8
 
31
9
  # [2.6.0](https://code.tls.nuskin.io/ns-am/product/js-libs/ns-product-lib/compare/v2.5.2...v2.6.0) (2023-03-13)
32
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuskin/ns-product-lib",
3
- "version": "2.6.1-cx24-3608.4",
3
+ "version": "2.6.1-cx24-3607.1",
4
4
  "description": "This project contains shared Product models and code between the backend and frontend.",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
@@ -0,0 +1,25 @@
1
+ /**
2
+ * childSkus creates an array of object that contains
3
+ * the list of SKUs of a kit.
4
+ *
5
+ * @param {import('./').KitSKUQuantity[]} kits
6
+ * @param {import('./').EquinoxProduct} product
7
+ */
8
+ function childSkus(kits, product) {
9
+ const sku = product.sku[0];
10
+ const kit = kits.filter(k => k.sku === sku.identifier)[0];
11
+
12
+ return {
13
+ productId: product.identifier,
14
+ skuId: sku.identifier,
15
+ type: 'MANDATORY',
16
+ skuQuantity: kit.qty,
17
+ availableChannels: sku.properties.availableChannels,
18
+ inventory: {
19
+ atpQty: sku.inventoryProperties.atpQty,
20
+ backOrdered: sku.inventoryProperties.backOrdered
21
+ }
22
+ }
23
+ }
24
+
25
+ module.exports = childSkus;
@@ -1,10 +1,11 @@
1
1
  /**
2
+ * @description This model represents the mapped values of a product
2
3
  * @typedef Product
3
4
  * @type {object}
4
- * @property {ChildSku[]} childSkus
5
+ * @property {ProductChildSku[]} childSkus
5
6
  * @property {Inventory} [inventoryProperties]
6
- *
7
- * @typedef ChildSku
7
+ *
8
+ * @typedef ProductChildSku
8
9
  * @type {object}
9
10
  * @property {string} productId
10
11
  * @property {string} skuId
@@ -12,14 +13,48 @@
12
13
  * @property {number} skuQuantity
13
14
  * @property {string} availableChannels
14
15
  * @property {Inventory} [inventory]
16
+ *
17
+ * ---
18
+ *
19
+ * @description This model represents a product within an Equinox response.
20
+ * @typedef EquinoxProduct
21
+ * @type {object}
22
+ * @property {EquinoxProduct[]} sku
23
+ * @property {"kit"|"bundle"} [type]
24
+ *
25
+ * @typedef EquinoxProductSKU
26
+ * @type {object}
27
+ * @property {string} identifier
28
+ * @property {EquinoxProductProperties} properties
29
+ * @property {Inventory} inventoryProperties
30
+ *
31
+ * @typedef EquinoxProductProperties
32
+ * @type {object}
33
+ * @property {string} availableChannels
34
+ *
35
+ * ---
36
+ *
37
+ * Types common to Product model and Equinox Product model
15
38
  *
16
39
  * @typedef Inventory
17
40
  * @type {object}
18
41
  * @property {number} atpQty
19
42
  * @property {boolean} backOrdered
43
+ *
44
+ * ---
45
+ *
46
+ * Standalone types. These are types not related to Product
47
+ * and Equinox Product models.
48
+ *
49
+ * @typedef KitSKUQuantity
50
+ * @type {object}
51
+ * @property {string} sku
52
+ * @property {number} number
20
53
  */
21
54
  const availableQuantity = require('./availableQuantity');
55
+ const childSkus = require('./childSkus');
22
56
 
23
57
  module.exports = {
24
- mapAvailableQuantity: availableQuantity
58
+ mapAvailableQuantity: availableQuantity,
59
+ mapChildSkus: childSkus
25
60
  };
@@ -5,7 +5,7 @@ const contentstack = require('./contentstack/contentstack');
5
5
  const Product = require("./product");
6
6
  const CustomerTypes = require('./models/customerTypes');
7
7
  const ProductStatus = require("./models/productStatus");
8
- const { mapAvailableQuantity } = require('./equinox-helpers');
8
+ const { mapAvailableQuantity, mapChildSkus } = require('./equinox-helpers');
9
9
 
10
10
  const productTypes = {
11
11
  kit: 'kit'
@@ -129,16 +129,14 @@ const ProductData = {
129
129
  */
130
130
  eqProductVariantMapper: function (eqVariant) {
131
131
 
132
- let imageURL = eqVariant.properties.imageURL ? eqVariant.properties.imageURL : '';
132
+ let imageURL = eqVariant.properties.imageURL;
133
133
  const regex = /\d+\.\d+/
134
134
  let thumbnailImage = ''
135
135
 
136
- if (imageURL !== "") {
137
- if (imageURL.includes('contentstack')) {
138
- thumbnailImage = imageURL + '?width=40'
139
- } else {
140
- thumbnailImage = imageURL.replace(regex, '40.40')
141
- }
136
+ if (imageURL.includes('contentstack')) {
137
+ thumbnailImage = imageURL + '?width=40'
138
+ } else {
139
+ thumbnailImage = imageURL.replace(regex, '40.40')
142
140
  }
143
141
 
144
142
  const { eventName, eventLabels, computedPrice, defaultProductPrice } = this.getEqProductPromotions(eqVariant);
@@ -346,139 +344,142 @@ const ProductData = {
346
344
  },
347
345
 
348
346
  eqProductMapper: async function (productDataResponse, skus) {
349
- // console.log(productDataResponse, skus);
350
347
  let count = 0;
351
348
  let variants = {};
352
349
  const products = [];
353
350
 
354
351
  for (const productData of productDataResponse) {
355
- let product = (productData.type && productData.type === "kit") ? productData : productData.sku[count];
356
- let imageURL = product.properties.imageURL ? product.properties.imageURL : '';
357
- const regex = /\d+\.\d+/
358
- let thumbnailImage = ''
352
+ try {
353
+ let product = (productData.type && productData.type === "kit") ? productData : productData.sku[count];
354
+ let imageURL = product.properties.imageURL;
355
+ const regex = /\d+\.\d+/
356
+ let thumbnailImage = ''
359
357
 
360
- if (imageURL !== "") {
361
358
  if (imageURL.includes('contentstack')) {
362
359
  thumbnailImage = imageURL + '?width=40'
363
360
  } else {
364
361
  thumbnailImage = imageURL.replace(regex, '40.40')
365
362
  }
366
- }
367
-
368
- if (productData.sku && productData.sku.length > 1) {
369
- // exclude base product from variants
370
- productData.sku.filter(v => v.identifier.substring(2, 4) !== "55").map(variant => {
371
- variants[variant.identifier] = this.eqProductVariantMapper(variant);
372
- return variant;
373
- });
374
- }
375
363
 
376
- const {
377
- eventName,
378
- eventLabels,
379
- computedPrice,
380
- defaultProductPrice
381
- } = this.getEqProductPromotions(product);
382
- const productPrice = eventName ? defaultProductPrice : product.priceFacets["Regular Price"];
383
- const discountedPrice = eventName ? computedPrice : product.priceFacets["Regular Price"];
384
-
385
- product.childSkus = await this.fetchChildSkus(product);
386
- product.availableQuantity = mapAvailableQuantity(product);
387
- product = {
388
- ...product,
389
- "sku": product.identifier,
390
- "globalProductID": productData.identifier,
391
- "title": productData.properties.name,
392
- "country": product.properties.market,
393
- "language": "en",
394
- "shortDescr": productData.properties.description,
395
- "longDescr": productData.properties.productDetailsDescription,
396
- "fullImage": imageURL,
397
- "imageAltText": productData.properties.name,
398
- "productCarouselImages": [
399
- {
400
- "contentType": "img",
401
- "src": "/content/products/24/00/35/24003529/jcr:content/fullImage.imgw.1280.1280.png",
402
- "alt": ""
403
- }
404
- ],
405
- "thumbnail": thumbnailImage,
406
- "ingredients": productData.properties.ingredients,
407
- "benefits": productData.properties.benefits,
408
- "usage": productData.properties.usage,
409
- "resources": product.properties.resources,
410
- "videos": "",
411
- "movie": "",
412
- "youtube": "",
413
- "salesEventText": "",
414
- "contentSection": [
415
- {
416
- "sectionContent": "<div class=\"contentSections\"></div>\n"
417
- }
418
- ],
419
- "scanQualified": productData.properties.scanQualifiedCount,
420
- "maxQuantity": 999,
421
- "points": "",
422
- "cv": (product.priceFacets.CV) ? product.priceFacets.CV : '',
423
- "pv": product.priceFacets.PV,
424
- "priceType": "WRTL",
425
- "price": discountedPrice,
426
- "priceMap": {
427
- "WRTL": productPrice,
428
- "WADW-WRTL": product.priceFacets["Regular Price"],
429
- "WADR": product.priceFacets["Regular Price"],
430
- "RTL": product.priceFacets["Regular Price"],
431
- "WADW": product.priceFacets["Wholesale Price"],
432
- "WHL": product.priceFacets["Wholesale Price"],
433
- "WWHL": product.priceFacets["Wholesale Price"]
434
- },
435
- "cvMap": {
436
- "WWHL": (product.priceFacets.CV) ? product.priceFacets.CV : '',
437
- "WADW": (product.priceFacets.CV) ? product.priceFacets.CV : '',
438
- "WHL": (product.priceFacets.CV) ? product.priceFacets.CV : ''
439
- },
440
- "pvMap": {
441
- "WWHL": product.priceFacets.PV,
442
- "WADW": product.priceFacets.PV,
443
- "WHL": product.priceFacets.PV
444
- },
445
- "orderTypes": this._setOrderType(product.properties),
446
- "custTypes": this.switchCustType(product.properties.customerTypes),
447
- "division": productData.properties.division,
448
- "backOrderDate": null,
449
- "locallyProduced": false,
450
- "agelocme": null,
451
- "count": "",
452
- "flavor": "",
453
- "size": product.properties.size,
454
- "shade": "",
455
- "status": this.switchStatusFromEquinox(product.properties.productStatus),
456
- "variantType": "Other",
457
- "variantDropdownLabel": product.properties.variantLabel || "",
458
- "variantDropdownPlaceholder": "Select Type",
459
- "variantsLabel": product.properties.variantLabel || "",
460
- "groupOffer": false,
461
- "personalOffer": false,
462
- "savedEventName": eventName,
463
- "salesLabel": eventLabels,
464
- "eventName": eventName,
465
- "sizeWeight": '',
466
- "nettoWeight": "",
467
- "variants": variants,
468
- "searchScore": 0,
469
- "isExclusive": product.properties.isExclusive || false,
470
- "marketAttributes": {
471
- "discount": true,
472
- "redeem": true,
473
- "earn": true
474
- },
475
- "restrictedMarkets": [],
476
- "addOns": [],
477
- "inventory": product.inventory || "", //inventory label
478
- "equinoxProductId": productData.identifier
479
- };
364
+ if (productData.sku && productData.sku.length > 1) {
365
+ // exclude base product from variants
366
+ productData.sku.filter(v => v.identifier.substring(2, 4) !== "55").map(variant => {
367
+ variants[variant.identifier] = this.eqProductVariantMapper(variant);
368
+ return variant;
369
+ });
370
+ }
480
371
 
481
- products.push(new Product(product));
372
+ const {
373
+ eventName,
374
+ eventLabels,
375
+ computedPrice,
376
+ defaultProductPrice
377
+ } = this.getEqProductPromotions(product);
378
+ const productPrice = eventName ? defaultProductPrice : product.priceFacets["Regular Price"];
379
+ const discountedPrice = eventName ? computedPrice : product.priceFacets["Regular Price"];
380
+
381
+ product.childSkus = await this.fetchChildSkus(product);
382
+ product.availableQuantity = mapAvailableQuantity(product);
383
+ product = {
384
+ ...product,
385
+ "sku": product.identifier,
386
+ "globalProductID": productData.identifier,
387
+ "title": productData.properties.name,
388
+ "country": product.properties.market,
389
+ "language": "en",
390
+ "shortDescr": productData.properties.description,
391
+ "longDescr": productData.properties.productDetailsDescription,
392
+ "fullImage": imageURL,
393
+ "imageAltText": productData.properties.name,
394
+ "productCarouselImages": [
395
+ {
396
+ "contentType": "img",
397
+ "src": "/content/products/24/00/35/24003529/jcr:content/fullImage.imgw.1280.1280.png",
398
+ "alt": ""
399
+ }
400
+ ],
401
+ "thumbnail": thumbnailImage,
402
+ "ingredients": productData.properties.ingredients,
403
+ "benefits": productData.properties.benefits,
404
+ "usage": productData.properties.usage,
405
+ "resources": product.properties.resources,
406
+ "videos": "",
407
+ "movie": "",
408
+ "youtube": "",
409
+ "salesEventText": "",
410
+ "contentSection": [
411
+ {
412
+ "sectionContent": "<div class=\"contentSections\"></div>\n"
413
+ }
414
+ ],
415
+ "scanQualified": productData.properties.scanQualifiedCount,
416
+ "maxQuantity": 999,
417
+ "points": "",
418
+ "cv": (product.priceFacets.CV) ? product.priceFacets.CV : '',
419
+ "pv": product.priceFacets.PV,
420
+ "priceType": "WRTL",
421
+ "price": discountedPrice,
422
+ "priceMap": {
423
+ "WRTL": productPrice,
424
+ "WADW-WRTL": product.priceFacets["Regular Price"],
425
+ "WADR": product.priceFacets["Regular Price"],
426
+ "RTL": product.priceFacets["Regular Price"],
427
+ "WADW": product.priceFacets["Wholesale Price"],
428
+ "WHL": product.priceFacets["Wholesale Price"],
429
+ "WWHL": product.priceFacets["Wholesale Price"]
430
+ },
431
+ "cvMap": {
432
+ "WWHL": (product.priceFacets.CV) ? product.priceFacets.CV : '',
433
+ "WADW": (product.priceFacets.CV) ? product.priceFacets.CV : '',
434
+ "WHL": (product.priceFacets.CV) ? product.priceFacets.CV : ''
435
+ },
436
+ "pvMap": {
437
+ "WWHL": product.priceFacets.PV,
438
+ "WADW": product.priceFacets.PV,
439
+ "WHL": product.priceFacets.PV
440
+ },
441
+ "orderTypes": this._setOrderType(product.properties),
442
+ "custTypes": this.switchCustType(product.properties.customerTypes),
443
+ "division": productData.properties.division,
444
+ "backOrderDate": null,
445
+ "locallyProduced": false,
446
+ "agelocme": null,
447
+ "count": "",
448
+ "flavor": "",
449
+ "size": product.properties.size,
450
+ "shade": "",
451
+ "status": this.switchStatusFromEquinox(product.properties.productStatus),
452
+ "variantType": "Other",
453
+ "variantDropdownLabel": product.properties.variantLabel || "",
454
+ "variantDropdownPlaceholder": "Select Type",
455
+ "variantsLabel": product.properties.variantLabel || "",
456
+ "groupOffer": false,
457
+ "personalOffer": false,
458
+ "savedEventName": eventName,
459
+ "salesLabel": eventLabels,
460
+ "eventName": eventName,
461
+ "sizeWeight": '',
462
+ "nettoWeight": "",
463
+ "variants": variants,
464
+ "searchScore": 0,
465
+ "isExclusive": product.properties.isExclusive || false,
466
+ "marketAttributes": {
467
+ "discount": true,
468
+ "redeem": true,
469
+ "earn": true
470
+ },
471
+ "restrictedMarkets": [],
472
+ "addOns": [],
473
+ "inventory": product.inventory || "", //inventory label
474
+ "equinoxProductId": productData.identifier
475
+ };
476
+
477
+ products.push(new Product(product));
478
+ } catch (err) {
479
+ console.error('Unable to map the product', productData.identifier, ':', err);
480
+ console.error('Product info:', productDataResponse, skus);
481
+ continue;
482
+ }
482
483
  }
483
484
 
484
485
  let data = {
@@ -521,7 +522,7 @@ const ProductData = {
521
522
  type: 'SKUKIT',
522
523
  availableChannels: product.properties.availableChannels
523
524
  },
524
- ...res.data.product.map(p => this.buildChildSkus(kits, p))
525
+ ...res.data.product.map(p => mapChildSkus(kits, p))
525
526
  ];
526
527
 
527
528
  return childSkus;
@@ -530,23 +531,6 @@ const ProductData = {
530
531
  return [];
531
532
  },
532
533
 
533
- buildChildSkus: function (kits, product) {
534
- const sku = product.sku[0];
535
- const kit = kits.filter(k => k.sku === sku.identifier)[0];
536
-
537
- return {
538
- productId: product.identifier,
539
- skuId: sku.identifier,
540
- type: 'MANDATORY',
541
- skuQuantity: kit.qty,
542
- availableChannels: sku.properties.availableChannels,
543
- inventory: {
544
- atpQty: sku.inventoryProperties.atpQty,
545
- backOrdered: sku.inventoryProperties.backOrdered
546
- }
547
- }
548
- },
549
-
550
534
  /**
551
535
  *
552
536
  * Map status from equinox to legacy