@nuskin/cs-product-lib 1.0.7 → 1.0.9-cx12-6632.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuskin/cs-product-lib",
3
- "version": "1.0.7",
3
+ "version": "1.0.9-cx12-6632.1",
4
4
  "description": "Library for pulling and parsing product content (and applying business logic) retrieved from Contentstack",
5
5
  "main": "src/index.js",
6
6
  "scripts": {
package/src/csProduct.js CHANGED
@@ -35,6 +35,7 @@ const validateConfig = (props) => {
35
35
  const csProduct = (props) => {
36
36
  const DEV_ENVIRONMENT = 'public-dev'
37
37
  const VARIANTS = 'variants'
38
+ const VARIANT = 'variant'
38
39
  const KEY_INGREDIENTS = 'ingredients.key_ingredients'
39
40
  const PRODUCT_HIGHLIGHTS = 'product_details.highlights'
40
41
  const VARIANT_HIGHLIGHTS = 'variants.product_details.highlights'
@@ -54,13 +55,16 @@ const csProduct = (props) => {
54
55
  const OPTIONAL = 'optional_products.product.'
55
56
  const BUNDLE_TYPE = 'bundle'
56
57
  const PRODUCT_BUNDLE = 'product_bundle'
58
+ const PRODUCT_SKU_KIT = 'sku_kit'
57
59
 
58
60
  // eslint-disable-next-line max-len
59
- const productIncludes = `&include[]=${VARIANTS}&include[]=${PRODUCT_FAQS}&include[]=${KEY_INGREDIENTS}&include[]=${PRODUCT_HIGHLIGHTS}&include[]=${PRODUCT_RESOURCES}&include[]=${VARIANT_KEY_INGREDIENTS}&include[]=${VARIANT_HIGHLIGHTS}&include[]=${VARIANT_FAQS}&include[]=${VARIANT_RESOURCES}&include[]=${PRODUCT_CATEGORY}&include[]=${PRODUCT_SUBCATEGORIES}&include[]=${PRODUCT_BRAND_STYLE}&include[]=${PRODUCT_BUNDLE}`
61
+ const productIncludes = `&include[]=${VARIANTS}&include[]=${PRODUCT_FAQS}&include[]=${KEY_INGREDIENTS}&include[]=${PRODUCT_HIGHLIGHTS}&include[]=${PRODUCT_RESOURCES}&include[]=${VARIANT_KEY_INGREDIENTS}&include[]=${VARIANT_HIGHLIGHTS}&include[]=${VARIANT_FAQS}&include[]=${VARIANT_RESOURCES}&include[]=${PRODUCT_CATEGORY}&include[]=${PRODUCT_SUBCATEGORIES}&include[]=${PRODUCT_BRAND_STYLE}&include[]=${PRODUCT_BUNDLE}&include[]=${PRODUCT_SKU_KIT}`
60
62
  const requiredIncludes = `&include[]=${REQUIRED}${VARIANTS}&include[]=${REQUIRED}${PRODUCT_FAQS}&include[]=${REQUIRED}${KEY_INGREDIENTS}&include[]=${REQUIRED}${PRODUCT_HIGHLIGHTS}&include[]=${REQUIRED}${PRODUCT_RESOURCES}&include[]=${REQUIRED}${VARIANT_KEY_INGREDIENTS}&include[]=${REQUIRED}${VARIANT_HIGHLIGHTS}&include[]=${REQUIRED}${VARIANT_FAQS}&include[]=${REQUIRED}${VARIANT_RESOURCES}&include[]=${REQUIRED}${PRODUCT_CATEGORY}&include[]=${REQUIRED}${PRODUCT_SUBCATEGORIES}`
61
63
  const optionalIncludes = `&include[]=${OPTIONAL}${VARIANTS}&include[]=${OPTIONAL}${PRODUCT_FAQS}&include[]=${OPTIONAL}${KEY_INGREDIENTS}&include[]=${OPTIONAL}${PRODUCT_HIGHLIGHTS}&include[]=${OPTIONAL}${PRODUCT_RESOURCES}&include[]=${OPTIONAL}${VARIANT_KEY_INGREDIENTS}&include[]=${OPTIONAL}${VARIANT_HIGHLIGHTS}&include[]=${OPTIONAL}${VARIANT_FAQS}&include[]=${OPTIONAL}${VARIANT_RESOURCES}&include[]=${OPTIONAL}${PRODUCT_CATEGORY}&include[]=${OPTIONAL}${PRODUCT_SUBCATEGORIES}`
62
64
  const bundleRequiredIncludes = `&include[]=${PRODUCT_BUNDLE}.${REQUIRED}${VARIANTS}&include[]=${PRODUCT_BUNDLE}.${REQUIRED}${PRODUCT_FAQS}&include[]=${PRODUCT_BUNDLE}.${REQUIRED}${KEY_INGREDIENTS}&include[]=${PRODUCT_BUNDLE}.${REQUIRED}${PRODUCT_HIGHLIGHTS}&include[]=${PRODUCT_BUNDLE}.${REQUIRED}${PRODUCT_RESOURCES}&include[]=${PRODUCT_BUNDLE}.${REQUIRED}${VARIANT_KEY_INGREDIENTS}&include[]=${PRODUCT_BUNDLE}.${REQUIRED}${VARIANT_HIGHLIGHTS}&include[]=${PRODUCT_BUNDLE}.${REQUIRED}${VARIANT_FAQS}&include[]=${PRODUCT_BUNDLE}.${REQUIRED}${VARIANT_RESOURCES}&include[]=${PRODUCT_BUNDLE}.${REQUIRED}${PRODUCT_CATEGORY}&include[]=${PRODUCT_BUNDLE}.${REQUIRED}${PRODUCT_SUBCATEGORIES}`
63
65
  const bundlOptionalIncludes = `&include[]=${PRODUCT_BUNDLE}.${OPTIONAL}${VARIANTS}&include[]=${PRODUCT_BUNDLE}.${OPTIONAL}${PRODUCT_FAQS}&include[]=${PRODUCT_BUNDLE}.${OPTIONAL}${KEY_INGREDIENTS}&include[]=${PRODUCT_BUNDLE}.${OPTIONAL}${PRODUCT_HIGHLIGHTS}&include[]=${PRODUCT_BUNDLE}.${OPTIONAL}${PRODUCT_RESOURCES}&include[]=${PRODUCT_BUNDLE}.${OPTIONAL}${VARIANT_KEY_INGREDIENTS}&include[]=${PRODUCT_BUNDLE}.${OPTIONAL}${VARIANT_HIGHLIGHTS}&include[]=${PRODUCT_BUNDLE}.${OPTIONAL}${VARIANT_FAQS}&include[]=${PRODUCT_BUNDLE}.${OPTIONAL}${VARIANT_RESOURCES}&include[]=${PRODUCT_BUNDLE}.${OPTIONAL}${PRODUCT_CATEGORY}&include[]=${PRODUCT_BUNDLE}.${OPTIONAL}${PRODUCT_SUBCATEGORIES}`
66
+ const kit = `&include[]=${PRODUCT_SKU_KIT}.${VARIANTS}&include[]=${PRODUCT_SKU_KIT}.${VARIANTS}.${VARIANT}`
67
+
64
68
 
65
69
  validateConfig(props)
66
70
  const { apiKey, environment, deliveryToken, homeUrl, awsBase, clientId } = props
@@ -164,14 +168,18 @@ const csProduct = (props) => {
164
168
  })
165
169
  }
166
170
 
167
- const formatBundleWithSchema = (bundles = []) => {
171
+ const formatKitWithSchema = (bundles = []) => {
168
172
  return bundles?.map((bundle) => {
169
173
  const formattedBundle = transformBundle(bundle, homeUrl)
170
174
 
171
175
  formattedBundle.requiredProducts = formattedBundle?.requiredProducts?.map(
172
176
  ({product, quantity}) => {
173
- const [formattedProduct] = formatProductWithSchema([product])
174
- return {product : formattedProduct, quantity}
177
+ if (product) {
178
+ const [formattedProduct] = formatProductWithSchema([product])
179
+ return {product : formattedProduct, quantity}
180
+ } else {
181
+ return {}
182
+ }
175
183
  }
176
184
  )
177
185
 
@@ -186,42 +194,83 @@ const csProduct = (props) => {
186
194
  })
187
195
  }
188
196
 
189
- const formatBundleKit = async (products, locale, translations) => {
197
+ const convertKitToRequiredProducts = async (skuKit, locale) => {
198
+ let productBundle = {}
199
+ const variantSkus = []
200
+ for (const kitItem of skuKit[0].variants) {
201
+ const skuData = helpers.getBestSkuData((kitItem.variant || [])[0])
202
+ variantSkus.push((skuData || {}).sku)
203
+ }
204
+ const products = await getProductsBySku(variantSkus, locale)
205
+ const requiredProducts = []
206
+ for (let i = 0; i < products.length; i++) {
207
+ requiredProducts.push({
208
+ product: [products[i]],
209
+ quantity: skuKit[0].variants[i].quantity
210
+ })
211
+ }
212
+ productBundle.required_products = requiredProducts
213
+ productBundle.charge_shipping = skuKit[0].charge_shipping
214
+ productBundle.cvdis = skuKit[0].cvdis
215
+ productBundle.foreign_limit = skuKit[0].foreign_limit
216
+ productBundle.grpdis = skuKit[0].grpdis
217
+ productBundle.is_exclusive = skuKit[0].is_exclusive
218
+ productBundle.bundle_id = skuKit[0].kit_id
219
+ productBundle.locale = skuKit[0].locale
220
+ productBundle.point_discount = skuKit[0].point_discount
221
+ productBundle.point_earn = skuKit[0].point_earn
222
+ productBundle.point_redeem = skuKit[0].point_redeem
223
+ productBundle.product_status = skuKit[0].product_status
224
+ productBundle.pvdis = skuKit[0].pvdis
225
+ productBundle.rtlpricedis = skuKit[0].rtlpricedis
226
+ productBundle.sbdis = skuKit[0].sbdis
227
+ productBundle.scan_qualified_count = skuKit[0].scan_qualified_count
228
+ productBundle.tax_class = skuKit[0].tax_class
229
+ productBundle.title = skuKit[0].title
230
+ productBundle.uid = skuKit[0].uid
231
+ productBundle.whlpricedis = skuKit[0].whlpricedis
232
+ return [productBundle]
233
+ }
234
+
235
+ const formatKit = async (products, locale, translations) => {
190
236
  for (let p = 0; p < products.length; p++) {
191
- for (let b = 0; b < products[p]?.product_bundle?.length || 0; b++) {
192
-
193
- for (
194
- let i = 0;
195
- i < (products[p].product_bundle[b].required_products || []).length;
196
- i++
197
- ) {
198
- await removeInvalidVariants(
199
- products[p].product_bundle[b].required_products[i].product,
200
- locale,
201
- translations
202
- )
237
+ if (products[p]?.sku_kit?.length > 0) {
238
+ products[p].type = "kit"
239
+ products[p].product_bundle = await convertKitToRequiredProducts(products[p].sku_kit, locale)
240
+ }
241
+ if (products[p]?.product_bundle?.length > 0) {
242
+ for (let b = 0; b < products[p]?.product_bundle?.length || 0; b++) {
243
+ for (
244
+ let i = 0;
245
+ i < (products[p].product_bundle[b].required_products || []).length;
246
+ i++
247
+ ) {
248
+ await removeInvalidVariants(
249
+ products[p].product_bundle[b].required_products[i].product,
250
+ locale,
251
+ translations
252
+ )
253
+ }
254
+ for (
255
+ let i = 0;
256
+ i < (products[p].product_bundle[b].optional_products || []).length;
257
+ i++
258
+ ) {
259
+ await removeInvalidVariants(
260
+ products[p].product_bundle[b].optional_products[i].product,
261
+ locale,
262
+ translations
263
+ )
264
+ }
203
265
  }
204
- for (
205
- let i = 0;
206
- i < (products[p].product_bundle[b].optional_products || []).length;
207
- i++
208
- ) {
209
- await removeInvalidVariants(
210
- products[p].product_bundle[b].optional_products[i].product,
211
- locale,
212
- translations
213
- )
266
+ if(products[p].product_bundle?.length > 0){
267
+ products[p].bundle = formatKitWithSchema(products[p].product_bundle)
268
+ products[p].variants = []
269
+ products[p].type = products[p].type || "bundle"
270
+ products[p].bundle[0].type = products[p].type
271
+ delete products[p].product_bundle
214
272
  }
215
- products[p].product_bundle[b].type = "bundle"
216
273
  }
217
- products[p].bundle = formatBundleWithSchema(products[p].product_bundle)
218
- if(products[p].product_bundle?.length > 0){
219
- products[p].variants = []
220
- }
221
- products[p].type = (products[p].product_bundle?.length || 0) > 0 ? "bundle" : null
222
- delete products[p].product_bundle
223
-
224
-
225
274
  }
226
275
  }
227
276
 
@@ -242,6 +291,7 @@ const csProduct = (props) => {
242
291
  url += productIncludes
243
292
  url += bundleRequiredIncludes
244
293
  url += bundlOptionalIncludes
294
+ url += kit
245
295
  }
246
296
  if (contentType === BUNDLE_TYPE) {
247
297
  url += requiredIncludes
@@ -308,7 +358,7 @@ const csProduct = (props) => {
308
358
  )
309
359
  }
310
360
  }
311
- return formatBundleWithSchema(bundles)?.[0]
361
+ return formatKitWithSchema(bundles)?.[0]
312
362
  }
313
363
  const getBundles = async (ids, locale) => {
314
364
  const query = `{ "$or": [{ "bundle_id": { "$in": ${helpers?.arrayString(ids)} }}, { "uid": { "$in": ${helpers?.arrayString(ids)} }}] }`
@@ -343,7 +393,7 @@ const csProduct = (props) => {
343
393
  )
344
394
  }
345
395
  }
346
- return formatBundleWithSchema(bundles)
396
+ return formatKitWithSchema(bundles)
347
397
  }
348
398
 
349
399
  const getProductsBySku = async (skus, locale) => {
@@ -356,7 +406,7 @@ const csProduct = (props) => {
356
406
 
357
407
  let products = await queryProducts(query, locale)
358
408
  const translations = await getTranslations(locale)
359
- await formatBundleKit(products, locale, translations)
409
+ await formatKit(products, locale, translations)
360
410
  await removeInvalidVariants(products, locale, translations)
361
411
  for (const product of products) {
362
412
  product.skus = skus
@@ -375,7 +425,7 @@ const csProduct = (props) => {
375
425
 
376
426
  let products = await queryProducts(query, locale)
377
427
  const translations = await getTranslations(locale)
378
- await formatBundleKit(products, locale, translations)
428
+ await formatKit(products, locale, translations)
379
429
  await removeInvalidVariants(products, locale, translations)
380
430
  return formatProductWithSchema(products)
381
431
  }
@@ -390,7 +440,7 @@ const csProduct = (props) => {
390
440
 
391
441
  const products = await queryProducts(query, locale)
392
442
  const translations = await getTranslations(locale)
393
- await formatBundleKit(products, locale, translations)
443
+ await formatKit(products, locale, translations)
394
444
  await removeInvalidVariants(products, locale, translations)
395
445
  return formatProductWithSchema(products)
396
446
  }
@@ -402,7 +452,7 @@ const csProduct = (props) => {
402
452
  const query = `{"uid": {"$in": ${helpers?.arrayString(ids)}}}`
403
453
  let products = await queryProducts(query, locale)
404
454
  const translations = await getTranslations(locale)
405
- await formatBundleKit(products, locale, translations)
455
+ await formatKit(products, locale, translations)
406
456
  await removeInvalidVariants(products, locale, translations)
407
457
  return formatProductWithSchema(products)
408
458
  }
package/src/formatKit.js CHANGED
@@ -17,9 +17,9 @@ const parseStatus = (variant) => {
17
17
  } else if(properties.productStatus) {
18
18
  let isBackordered = false
19
19
  let backorderedAvailableDate = null
20
- if (variant.skuKit) {
20
+ if (variant.kit) {
21
21
  try {
22
- const latestBODate = Math.min(...variant.skuKit.flatMap(a=>a.sku.map(s=>s.inventoryProperties.expectedBackOrderAvailabilityDate))) || 0
22
+ const latestBODate = Math.min(...variant.kit.flatMap(a=>a.sku.map(s=>s.inventoryProperties.expectedBackOrderAvailabilityDate))) || 0
23
23
  if (latestBODate) {
24
24
  isBackordered = true
25
25
  backorderedAvailableDate = moment(Date(latestBODate)).format(dateFormat)
@@ -54,24 +54,64 @@ const parseStatus = (variant) => {
54
54
  }
55
55
  };
56
56
 
57
- const parsePrice = (variant) => {
58
- const price = {}
59
- const points = {}
60
- price.currencyCode = (variant.prices || [])?.[0]?.currencyCode || (variant?.prices || [])?.[0]?.currencySymbol
61
- const priceFacets = variant?.priceFacets || {}
62
- price.retail = priceFacets['Regular Price'] || ((variant?.prices || [])[0] || {}).price
63
- price.wholesale = priceFacets['Wholesale Price'] || ((variant?.prices || [])[0] || {}).price
64
- price.retailSubscription = priceFacets['Regular Price'] || ((variant?.prices || [])[0] || {}).price
65
- price.wholesaleSubscription = priceFacets['Wholesale Price'] || ((variant?.prices || [])[0] || {}).price
66
- points.wholesale = {cv: priceFacets.CV, pv: priceFacets.PV, sb: priceFacets.SB}
67
- // Where do we pull subscription price/points from?
68
- points.subscription = {cv: priceFacets.CV, pv: priceFacets.PV, sb: priceFacets.SB}
69
- return {price, points}
57
+ const parsePrice = (product = []) => {
58
+ const requiredProducts = product.map(p => p.requiredProducts) .flat()
59
+
60
+ const variants = requiredProducts?.map(p => {
61
+ const [firstVariant] = p?.product?.variants || []
62
+ return firstVariant
63
+ })
64
+
65
+ const defaultValues = {
66
+ price : {
67
+ retail: 0,
68
+ retailSubscription : 0,
69
+ wholesale : 0,
70
+ wholesaleSubscription : 0
71
+ },
72
+ points : {
73
+ subscription : {
74
+ cv: 0,
75
+ pv: 0,
76
+ sb: 0
77
+ },
78
+ wholesale: {
79
+ cv: 0,
80
+ pv: 0,
81
+ sb: 0
82
+ }
83
+ }
84
+ }
85
+
86
+ // sum of price and points from 1st variant of product bundle
87
+ const sunOfPriceAndPoints = variants.reduce((acc, curr) => {
88
+ const price = {}
89
+ const points = {
90
+ subscription : {},
91
+ wholesale : {}
92
+ }
93
+
94
+ price.retail = Number(acc?.price?.retail || 0) + Number(curr?.price?.retail || 0)
95
+ price.retailSubscription = Number(acc?.price?.retailSubscription || 0) + Number(curr?.price?.retailSubscription || 0)
96
+ price.wholesale = Number(acc?.price?.wholesale || 0) + Number(curr?.price?.wholesale || 0)
97
+ price.wholesaleSubscription = Number(acc?.price?.wholesaleSubscription || 0) + Number(curr?.price?.wholesaleSubscription || 0)
98
+ price.currencyCode = curr?.price.currencyCode
99
+
100
+ points.subscription.cv = Number(acc?.points?.subscription?.cv || 0) + Number(curr?.points?.subscription?.cv || 0)
101
+ points.subscription.pv = Number(acc?.points?.subscription?.pv || 0) + Number(curr?.points?.subscription?.pv || 0)
102
+ points.subscription.sb = Number(acc?.points?.subscription?.sb || 0) + Number(curr?.points?.subscription?.sb || 0)
103
+ points.wholesale.cv = Number(acc?.points?.wholesale?.cv || 0) + Number(curr?.points?.wholesale?.cv || 0)
104
+ points.wholesale.pv = Number(acc?.points?.wholesale?.pv || 0) + Number(curr?.points?.wholesale?.pv || 0)
105
+ points.wholesale.sb = Number(acc?.points?.wholesale?.sb || 0) + Number(curr?.points?.wholesale?.sb || 0)
106
+
107
+ return { price, points}
108
+ }, defaultValues)
109
+ return sunOfPriceAndPoints
70
110
  }
71
111
 
72
- const parseKitAvailableQuantity = (skuKit) => {
112
+ const parseKitAvailableQuantity = (kit) => {
73
113
  try {
74
- return Math.min(...skuKit.flatMap(a=>a.sku.map(s=>s.inventoryProperties.atpQty))) || 0
114
+ return Math.min(...kit.flatMap(a=>a.sku.map(s=>s.inventoryProperties.atpQty))) || 0
75
115
  } catch {
76
116
  return 0
77
117
  }
@@ -122,26 +162,30 @@ const parseKit = (product, market) => {
122
162
  kits.status = parseStatus(product)
123
163
  if ((kits.status || {}).status === 'active') kits.status.status = 'sellable'
124
164
  const kitProducts = []
125
- if (properties.type === 'kit') {
126
- kits.type = 'skuKit'
127
- kits.availableQuantity = parseKitAvailableQuantity(product.skuKit)
128
- for (const kit of product.skuKit) {
165
+ if (properties.type === 'kit' && product.kit) {
166
+ kits.type = 'kit'
167
+ kits.availableQuantity = parseKitAvailableQuantity(product.kit)
168
+ for (const kit of product.kit || []) {
129
169
  const kitProduct = formatKit(kit, market)
130
170
  kitProduct.id = kit.identifier
131
171
  kitProducts.push({product: kitProduct, quantity: (kit.properties || {}).quantity || 0, isMandatory:true})
132
172
  }
133
- } else if (properties.type === 'bundle') {
134
- kits.type = 'bundle'
173
+ } else if (properties.type === 'bundle' || (properties.type === 'kit' && properties.requiredProducts)) {
174
+ kits.type = properties.type
135
175
  kits.availableQuantity = parseBundleAvailableQuantity(properties)
136
176
  for (const kit of properties?.requiredProducts || []) {
137
- const kitProduct = formatKit(kit?.product, market)
138
- kitProduct.id = kit.product.id
139
- kitProducts.push({product: kitProduct, quantity: kit?.quantity || 0, isMandatory:true})
177
+ if (kit.product) {
178
+ const kitProduct = formatKit(kit?.product, market)
179
+ kitProduct.id = kit.product.id
180
+ kitProducts.push({product: kitProduct, quantity: kit?.quantity || 0, isMandatory:true})
181
+ }
140
182
  }
141
183
  for (const kit of properties?.optionalProducts || []) {
142
- const kitProduct = formatKit(kit?.product, market)
143
- kitProduct.id = kit.product.id
144
- kitProducts.push({product: kitProduct, quantity: kit?.quantity || 0, isMandatory:false})
184
+ if (kit.product) {
185
+ const kitProduct = formatKit(kit?.product, market)
186
+ kitProduct.id = kit.product.id
187
+ kitProducts.push({product: kitProduct, quantity: kit?.quantity || 0, isMandatory:false})
188
+ }
145
189
  }
146
190
  }
147
191
  kits.kitProducts = kitProducts
@@ -4,35 +4,35 @@ const { formatKit } = require('./formatKit')
4
4
 
5
5
  /* istanbul ignore next */
6
6
  const formatProduct = (parent, homeUrl) => {
7
- const formattedProduct = {
7
+ const formatedProduct = {
8
8
  // Each value is a function with a parameter 'parent' that is the current instance of a Product
9
9
  // ContentStack references need to be queried separately (ie: variants)
10
- id: parent.uid,
10
+ id: parent?.uid,
11
11
  slug: parent?.seo_information?.slug,
12
- variantSelectLabel: parent.variant_select_label,
13
- secondaryTitle: parent.secondary_title,
14
- title: parent.name,
12
+ variantSelectLabel: parent?.variant_select_label,
13
+ secondaryTitle: parent?.secondary_title,
14
+ title: parent?.name,
15
15
  productImages: helpers?.getProductImages(parent),
16
- salesLabel: parent.sales_label,
17
- salesText: parent.sales_text,
18
- description: entities.decodeHTML(parent.short_description || ''),
19
- salesDisclaimer: helpers?.mapSalesDisclaimer(parent.sales_disclaimer),
16
+ salesLabel: parent?.sales_label,
17
+ salesText: parent?.sales_text,
18
+ description: entities.decodeHTML(parent?.short_description || ''),
19
+ salesDisclaimer: helpers?.mapSalesDisclaimer(parent?.sales_disclaimer),
20
20
  features: helpers?.mapFeatures(parent),
21
21
  productDetails: helpers?.mapProductDetails(parent),
22
22
  benefitsMedia: {
23
- image: helpers?.mapImage(parent.benefits_media?.image),
24
- youTubeVideoId: parent.benefits_media?.youtube_video_id
23
+ image: helpers?.mapImage(parent?.benefits_media?.image),
24
+ youTubeVideoId: parent?.benefits_media?.youtube_video_id
25
25
  },
26
26
  benefits: helpers?.mapBenefits(parent),
27
27
  benefitsAndFeatures: helpers?.mapBenefitsAndFeatures(parent),
28
- bundle: parent?.bundle,
28
+ bundle: parent?.bundle || parent?.kit,
29
29
  type: parent?.type,
30
30
  results: helpers?.mapResults(parent),
31
31
  sustainability: helpers?.mapSustainability(parent),
32
- usage: helpers?.mapUsage(parent, parent.legacy_usage),
32
+ usage: helpers?.mapUsage(parent, parent?.legacy_usage),
33
33
  resources: helpers?.mapResources(parent),
34
34
  faqs: helpers?.mapFaqs(parent),
35
- categories: parent.categories?.map((category) => ({
35
+ categories: parent?.categories?.map((category) => ({
36
36
  displayName: category.display_name,
37
37
  id: category.category_id,
38
38
  subcategories: category.subcategories?.map((subcategory) => ({
@@ -41,42 +41,42 @@ const formatProduct = (parent, homeUrl) => {
41
41
  }))
42
42
  })),
43
43
  ingredients: helpers?.mapIngredients(
44
- parent.ingredients,
45
- parent.legacy_ingredients
44
+ parent?.ingredients,
45
+ parent?.legacy_ingredients
46
46
  ),
47
- legacyBenefits: parent.legacy_benefits,
48
- legacyLongDescription: parent.legacy_long_description,
49
- legacyResources: parent.legacy_resources,
50
- legacyUsage: parent.legacy_usage,
51
- legacyIngredients: parent.legacy_ingredients,
52
- legacyImages: parent.legacy_images || [],
53
- legacyYouTube: parent.legacy_youtube_video,
54
- disclaimers: parent.disclaimers || [],
47
+ legacyBenefits: parent?.legacy_benefits,
48
+ legacyLongDescription: parent?.legacy_long_description,
49
+ legacyResources: parent?.legacy_resources,
50
+ legacyUsage: parent?.legacy_usage,
51
+ legacyIngredients: parent?.legacy_ingredients,
52
+ legacyImages: parent?.legacy_images || [],
53
+ legacyYouTube: parent?.legacy_youtube_video,
54
+ disclaimers: parent?.disclaimers || [],
55
55
  disclaimerWarnings: helpers?.mapDisclaimerWarnings(
56
- parent.disclaimer_warnings
56
+ parent?.disclaimer_warnings
57
57
  ),
58
58
  seoInformation: helpers?.mapSeoInformation(parent, homeUrl),
59
59
  variants: helpers?.mapVariants(parent),
60
60
  refund: helpers?.mapRefund(parent),
61
61
  shipping: helpers?.mapShipping(parent),
62
- generalDisclaimer: parent.translations?.general_disclaimer_text,
63
- thirdPartyScripts: parent.third_party_scripts
62
+ generalDisclaimer: parent?.translations?.general_disclaimer_text,
63
+ thirdPartyScripts: parent?.third_party_scripts
64
64
  ?.filter((tps) => tps.enabled && !!tps.script)
65
65
  ?.map((tps) => tps.script),
66
66
  csAtts: {
67
- version: parent._version || 0,
68
- updatedAt: parent.updated_at || '',
69
- updatedBy: parent.updated_by || '',
70
- createdAt: parent.created_at || '',
71
- createdBy: parent.created_by || '',
72
- locale: parent.locale || ''
67
+ version: parent?._version || 0,
68
+ updatedAt: parent?.updated_at || '',
69
+ updatedBy: parent?.updated_by || '',
70
+ createdAt: parent?.created_at || '',
71
+ createdBy: parent?.created_by || '',
72
+ locale: parent?.locale || ''
73
73
  },
74
- relevancyRank: parent.relevancy_rank || null,
74
+ relevancyRank: parent?.relevancy_rank || null,
75
75
  warranty: helpers?.mapWarranty(parent),
76
76
  upSellProductIds: helpers?.mapUpSellProductIds(parent),
77
77
  crossProductIds: helpers?.mapCrossProductIds(parent)
78
78
  }
79
- return formatKit(formattedProduct)
79
+ return formatKit(formatedProduct)
80
80
  }
81
81
 
82
82
  module.exports = {
package/src/helpers.js CHANGED
@@ -20,7 +20,7 @@ function mapBrandStyle(feature, type) {
20
20
  // otherwise make text white, and use feature.background_color if it exists or default the background color to be #4f4d4d if not
21
21
  const isBrandStyle = feature.brand_style && Array.isArray(feature.brand_style) && feature.brand_style.length > 0;
22
22
  // eslint-disable-next-line no-nested-ternary
23
- const backgroundColor = isBrandStyle ? feature.brand_style[0].background_color : feature.background_color.length > 1 ? feature.background_color : '#4f4d4d';
23
+ const backgroundColor = isBrandStyle ? feature.brand_style[0].background_color : feature.background_color?.length > 1 ? feature.background_color : '#4f4d4d';
24
24
  const textColor = isBrandStyle && feature.brand_style[0].background_color?.length > 0 ? '#000000' : '#ffffff';
25
25
 
26
26
  switch (type) {
@@ -120,21 +120,21 @@ function mapSubscriptions(subscriptions) {
120
120
 
121
121
  /* istanbul ignore next */
122
122
  function mapProductImages(parent) {
123
- if ((parent.product_images_bynder || []).length) {
124
- return parent.product_images_bynder.map((img) => mapImage(img))
123
+ if ((parent?.product_images_bynder || []).length) {
124
+ return parent?.product_images_bynder.map((img) => mapImage(img))
125
125
  }
126
- if ((parent.product_images || []).length) {
127
- return parent.product_images.map((img) => mapImage(img))
128
- } else if ((parent.baseImages || []).length) {
129
- return parent.baseImages
126
+ if ((parent?.product_images || []).length) {
127
+ return parent?.product_images.map((img) => mapImage(img))
128
+ } else if ((parent?.baseImages || []).length) {
129
+ return parent?.baseImages
130
130
  }
131
- return (parent.legacy_images || []).length
132
- ? parent.legacy_images.map((img) => ({
131
+ return (parent?.legacy_images || []).length
132
+ ? parent?.legacy_images.map((img) => ({
133
133
  url: img,
134
134
  alt: '',
135
135
  thumbnail: img
136
136
  }))
137
- : parent.legacyImages || []
137
+ : parent?.legacyImages || []
138
138
  }
139
139
 
140
140
  /* istanbul ignore next */
@@ -251,32 +251,32 @@ const getProductImages = (parent = {}) => {
251
251
 
252
252
  function mapRefund(parent) {
253
253
  return {
254
- message: parent.translations
255
- ? (parent.translations.common_strings_overrides.refund || {}).message ||
256
- (parent.translations.common_strings.product || {}).refundMessage: '',
257
- linkText: parent.translations
258
- ? (parent.translations.common_strings_overrides.refund || {})
254
+ message: parent?.translations
255
+ ? (parent?.translations.common_strings_overrides.refund || {}).message ||
256
+ (parent?.translations.common_strings.product || {}).refundMessage: '',
257
+ linkText: parent?.translations
258
+ ? (parent?.translations.common_strings_overrides.refund || {})
259
259
  .link_text ||
260
- (parent.translations.common_strings.product || {}).refundLinkText: '',
261
- linkUrl: parent.translations
262
- ? (parent.translations.common_strings_overrides.refund || {})
260
+ (parent?.translations.common_strings.product || {}).refundLinkText: '',
261
+ linkUrl: parent?.translations
262
+ ? (parent?.translations.common_strings_overrides.refund || {})
263
263
  .link_url ||
264
- (parent.translations.common_strings.product || {}).refundLinkUrl
264
+ (parent?.translations.common_strings.product || {}).refundLinkUrl
265
265
  : ''
266
266
  }
267
267
  }
268
268
 
269
269
  function mapShipping(parent) {
270
270
  return {
271
- message: parent.translations
272
- ? (parent.translations.common_strings_overrides.shipping || {})
271
+ message: parent?.translations
272
+ ? (parent?.translations.common_strings_overrides.shipping || {})
273
273
  .message ||
274
- (parent.translations.common_strings.product || {}).shippingMessage
274
+ (parent?.translations.common_strings.product || {}).shippingMessage
275
275
  : '',
276
- boldText: parent.translations
277
- ? (parent.translations.common_strings_overrides.shipping || {})
276
+ boldText: parent?.translations
277
+ ? (parent?.translations.common_strings_overrides.shipping || {})
278
278
  .bold_text ||
279
- (parent.translations.common_strings.product || {})
279
+ (parent?.translations.common_strings.product || {})
280
280
  .shippingBoldText
281
281
  : ''
282
282
  }
@@ -284,27 +284,27 @@ function mapShipping(parent) {
284
284
 
285
285
  /* istanbul ignore next */
286
286
  function mapVariants(parent) {
287
- const _variants = (parent.variants || []).map((variant) => ({
287
+ const _variants = (parent?.variants || []).map((variant) => ({
288
288
  ...variant,
289
- country: parent.country,
290
- locale: parent.locale,
291
- searchedSkus: Array.isArray(parent.skus) ? parent.skus : [parent.skus],
289
+ country: parent?.country,
290
+ locale: parent?.locale,
291
+ searchedSkus: Array.isArray(parent?.skus) ? parent?.skus : [parent?.skus],
292
292
  baseImages: getProductImages(parent),
293
- legacyImages: (parent.legacy_images || []).map((img) => ({
293
+ legacyImages: (parent?.legacy_images || []).map((img) => ({
294
294
  url: img,
295
295
  alt: '',
296
296
  thumbnail: img
297
297
  })),
298
298
  defaults: {
299
- name: `${parent.name} ${
299
+ name: `${parent?.name} ${
300
300
  variant.select_label ? variant.select_label : ''
301
301
  }`,
302
- description: parent.short_description,
302
+ description: parent?.short_description,
303
303
  productDetailsDescription: Array.isArray(
304
- (parent.product_details || {}).long_description
304
+ (parent?.product_details || {}).long_description
305
305
  )
306
- ? (parent.product_details || {}).long_description
307
- : [(parent.product_details || {}).long_description],
306
+ ? (parent?.product_details || {}).long_description
307
+ : [(parent?.product_details || {}).long_description],
308
308
  status: variant.status || {},
309
309
  marketData: variant.marketData
310
310
  }
@@ -315,14 +315,14 @@ function mapVariants(parent) {
315
315
  function mapSeoInformation(parent, homeUrl) {
316
316
  const { ogImage } = parent?.seo_information || {}
317
317
  const seoData = {
318
- metaTitle: parent?.seo_information?.meta_title || parent.name,
318
+ metaTitle: parent?.seo_information?.meta_title || parent?.name,
319
319
  metaDescription:
320
- parent.seo_information?.meta_description ||
320
+ parent?.seo_information?.meta_description ||
321
321
  parent?.short_description,
322
322
  canonicalURL: `${homeUrl}/catalog/${
323
- parent.localeData?.country
324
- }/${parent.localeData?.language}/product/${
325
- parent?.seo_information?.slug || parent.sku
323
+ parent?.localeData?.country
324
+ }/${parent?.localeData?.language}/product/${
325
+ parent?.seo_information?.slug || parent?.sku
326
326
  }`,
327
327
  ogImage: { url: '', alt: '' }
328
328
  }
@@ -352,6 +352,7 @@ function mapPoints(parent) {
352
352
  return {
353
353
  wholesale: {
354
354
  cv:
355
+ _.get(parent, 'points.wholesale.cv', null) ||
355
356
  _.get(
356
357
  parent,
357
358
  'defaults.status.pricing.wholesale.webOrder.cv',
@@ -363,6 +364,7 @@ function mapPoints(parent) {
363
364
  null
364
365
  ),
365
366
  pv:
367
+ _.get(parent, 'points.wholesale.pv', null) ||
366
368
  _.get(
367
369
  parent,
368
370
  'defaults.status.pricing.wholesale.webOrder.pv',
@@ -376,6 +378,7 @@ function mapPoints(parent) {
376
378
  },
377
379
  subscription: {
378
380
  cv:
381
+ _.get(parent, 'points.subscription.cv', null) ||
379
382
  _.get(
380
383
  parent,
381
384
  'defaults.status.pricing.wholesale.WADW.cv',
@@ -387,6 +390,7 @@ function mapPoints(parent) {
387
390
  null
388
391
  ),
389
392
  pv:
393
+ _.get(parent, 'points.subscription.pv', null) ||
390
394
  _.get(
391
395
  parent,
392
396
  'defaults.status.pricing.wholesale.WADW.pv',
@@ -400,18 +404,22 @@ function mapPoints(parent) {
400
404
  function mapPrice(parent) {
401
405
  return {
402
406
  retail:
407
+ _.get(parent, 'price.retail', null) ||
403
408
  _.get(
404
409
  parent,
405
410
  'defaults.status.pricing.retail.webOrder.price',
406
411
  null
407
412
  ) ||
408
413
  _.get(parent, 'defaults.status.pricing.retail.order.price', null),
409
- retailSubscription: _.get(
410
- parent,
411
- 'defaults.status.pricing.retail.webAdr.price',
412
- null
413
- ),
414
+ retailSubscription:
415
+ _.get(parent, 'price.retailSubscription', null) ||
416
+ _.get(
417
+ parent,
418
+ 'defaults.status.pricing.retail.webAdr.price',
419
+ null
420
+ ),
414
421
  wholesale:
422
+ _.get(parent, 'price.wholesale', null) ||
415
423
  _.get(
416
424
  parent,
417
425
  'defaults.status.pricing.wholesale.webOrder.price',
@@ -423,6 +431,7 @@ function mapPrice(parent) {
423
431
  null
424
432
  ),
425
433
  wholesaleSubscription:
434
+ _.get(parent, 'price.wholesaleSubscription', null) ||
426
435
  _.get(
427
436
  parent,
428
437
  'defaults.status.pricing.wholesale.ADR5.price',
@@ -491,12 +500,12 @@ function mapLink(link) {
491
500
 
492
501
  function mapProductDetails(productOrVariant) {
493
502
  const productDetails = (productOrVariant || {}).product_details || {}
494
- if ((productOrVariant.long_description_override || []).length > 0) {
503
+ if ((productOrVariant?.long_description_override || []).length > 0) {
495
504
  productDetails.long_description =
496
- productOrVariant.long_description_override
505
+ productOrVariant?.long_description_override
497
506
  }
498
- if ((productOrVariant.included_items_override || []).length > 0) {
499
- productDetails.included_items = productOrVariant.included_items_override
507
+ if ((productOrVariant?.included_items_override || []).length > 0) {
508
+ productDetails.included_items = productOrVariant?.included_items_override
500
509
  }
501
510
  let description = productDetails.long_description
502
511
  if (!Array.isArray(description)) {
@@ -585,7 +594,7 @@ function benefitsToMarkdown(legacyBenefits, jsonBenefits) {
585
594
 
586
595
  function mapBenefits(parent) {
587
596
  if (parent?.benefits?.length > 0) {
588
- return parent.benefits.map((benefit) => ({
597
+ return parent?.benefits.map((benefit) => ({
589
598
  title: benefit.header,
590
599
  benefits: benefit.benefits || []
591
600
  }))
@@ -597,48 +606,48 @@ function mapBenefits(parent) {
597
606
  function mapBenefitsAndFeatures(productOrVariant) {
598
607
  let benefitsMedia = (productOrVariant || {}).benefits_media || {}
599
608
  const legacyBenefits = Array.isArray((productOrVariant || {}).benefits)
600
- ? productOrVariant.benefits
609
+ ? productOrVariant?.benefits
601
610
  : []
602
611
  if (
603
612
  (productOrVariant || {}).benefits &&
604
- (productOrVariant.benefits.image ||
605
- productOrVariant.benefits.youtube_video_id)
613
+ (productOrVariant?.benefits.image ||
614
+ productOrVariant?.benefits.youtube_video_id)
606
615
  ) {
607
616
  benefitsMedia = {
608
- image: Array.isArray(productOrVariant.benefits.image)
609
- ? productOrVariant.benefits.image[0]
610
- : productOrVariant.benefits.image,
611
- youTubeVideoId: productOrVariant.benefits.youtube_video_id
617
+ image: Array.isArray(productOrVariant?.benefits.image)
618
+ ? productOrVariant?.benefits.image[0]
619
+ : productOrVariant?.benefits.image,
620
+ youTubeVideoId: productOrVariant?.benefits.youtube_video_id
612
621
  }
613
622
  }
614
623
  // Account for new section in Product
615
624
  if (
616
625
  (productOrVariant || {}).benefits_and_key_features &&
617
626
  rteHasContent((productOrVariant || {}).benefits_and_key_features) &&
618
- (productOrVariant.benefits_and_key_features.image ||
619
- productOrVariant.benefits_and_key_features.youtube_video_id)
627
+ (productOrVariant?.benefits_and_key_features.image ||
628
+ productOrVariant?.benefits_and_key_features.youtube_video_id)
620
629
  ) {
621
630
  benefitsMedia = {
622
631
  image: Array.isArray(
623
- productOrVariant.benefits_and_key_features.image
632
+ productOrVariant?.benefits_and_key_features.image
624
633
  )
625
- ? productOrVariant.benefits_and_key_features.image[0]
626
- : productOrVariant.benefits_and_key_features.image,
634
+ ? productOrVariant?.benefits_and_key_features.image[0]
635
+ : productOrVariant?.benefits_and_key_features.image,
627
636
  youTubeVideoId:
628
- productOrVariant.benefits_and_key_features.youtube_video_id
637
+ productOrVariant?.benefits_and_key_features.youtube_video_id
629
638
  }
630
639
  }
631
640
  let jsonBenefits =
632
641
  (productOrVariant || {}).benefits &&
633
- productOrVariant.benefits.benefits &&
634
- productOrVariant.benefits.benefits.type
635
- ? productOrVariant.benefits.benefits
642
+ productOrVariant?.benefits.benefits &&
643
+ productOrVariant?.benefits.benefits.type
644
+ ? productOrVariant?.benefits.benefits
636
645
  : null
637
646
  // Account for new section in Product
638
647
  jsonBenefits =
639
648
  (productOrVariant || {}).benefits_and_key_features &&
640
- productOrVariant.benefits_and_key_features.benefits
641
- ? productOrVariant.benefits_and_key_features.benefits
649
+ productOrVariant?.benefits_and_key_features.benefits
650
+ ? productOrVariant?.benefits_and_key_features.benefits
642
651
  : jsonBenefits
643
652
 
644
653
  return {
@@ -750,17 +759,17 @@ function mapResource(resource) {
750
759
  /* istanbul ignore next */
751
760
  function mapResources(productOrVariant) {
752
761
  if (
753
- productOrVariant.resources &&
754
- Array.isArray(productOrVariant.resources) &&
755
- productOrVariant.resources.length > 0
762
+ productOrVariant?.resources &&
763
+ Array.isArray(productOrVariant?.resources) &&
764
+ productOrVariant?.resources.length > 0
756
765
  ) {
757
- return productOrVariant.resources.map((resource) =>
766
+ return productOrVariant?.resources.map((resource) =>
758
767
  mapResource(resource)
759
768
  )
760
769
  }
761
770
  try {
762
771
  const legacyResources = JSON.parse(
763
- productOrVariant.legacy_resources || '[]'
772
+ productOrVariant?.legacy_resources || '[]'
764
773
  )
765
774
  return (legacyResources || []).map((resource) => ({
766
775
  title: resource.title,
@@ -2,37 +2,37 @@ const helpers = require('./helpers')
2
2
 
3
3
  /* istanbul ignore next */
4
4
  const transformBundle = (parent, homeUrl) => ({
5
- id: parent.bundle_id,
6
- uid: parent.uid,
7
- title: parent.title,
8
- type: parent.type,
9
- requiredProducts: parent.required_products?.map((r) => ({
5
+ id: parent?.bundle_id || parent?.kit_id,
6
+ uid: parent?.uid,
7
+ title: parent?.name,
8
+ type: parent?.type,
9
+ requiredProducts: parent?.required_products?.map((r) => ({
10
10
  product: r?.product?.[0],
11
11
  quantity: r.quantity
12
12
  })),
13
- optionalProducts: parent.optional_products?.map((o) => ({
13
+ optionalProducts: parent?.optional_products?.map((o) => ({
14
14
  product: o?.product?.[0],
15
15
  quantity: o.quantity
16
16
  })),
17
- retailDiscount: parent.rtlpricedis || 0,
18
- wholesaleDiscount: parent.whlpricedis || 0,
19
- pvDiscount: parent.pvdis || 0,
20
- cvDiscount: parent.cvdis || 0,
21
- sbDiscount: parent.sbdis || 0,
22
- grpDiscount: parent.grpdis || 0,
23
- shortDescription: parent.short_description,
24
- description: helpers?.createMarkdown(parent.description),
25
- salesLabel: parent.sales_label,
26
- salesText: parent.sales_text,
17
+ retailDiscount: parent?.rtlpricedis || 0,
18
+ wholesaleDiscount: parent?.whlpricedis || 0,
19
+ pvDiscount: parent?.pvdis || 0,
20
+ cvDiscount: parent?.cvdis || 0,
21
+ sbDiscount: parent?.sbdis || 0,
22
+ grpDiscount: parent?.grpdis || 0,
23
+ shortDescription: parent?.short_description,
24
+ description: helpers?.createMarkdown(parent?.description),
25
+ salesLabel: parent?.sales_label,
26
+ salesText: parent?.sales_text,
27
27
  features: {
28
28
  image: helpers?.mapImage(parent?.features?.image),
29
29
  subtitle: parent?.features?.header,
30
30
  features: parent?.features?.features,
31
31
  backgroundColor: parent?.features?.background_color
32
32
  },
33
- disclaimers: parent.disclaimers || [],
33
+ disclaimers: parent?.disclaimers || [],
34
34
  disclaimerWarnings: helpers?.mapDisclaimerWarnings(
35
- parent.disclaimer_warnings
35
+ parent?.disclaimer_warnings
36
36
  ),
37
37
  seoInformation: helpers?.mapSeoInformation(parent, homeUrl),
38
38
  resources: helpers?.mapResources(parent),
@@ -44,22 +44,22 @@ const transformBundle = (parent, homeUrl) => ({
44
44
  id: subcategory.category_id
45
45
  }))
46
46
  })),
47
- availableChannels: parent.product_status.display_channels,
48
- customerTypes: parent.product_status.customer_types,
47
+ availableChannels: parent?.product_status.display_channels,
48
+ customerTypes: parent?.product_status.customer_types,
49
49
  status: {
50
- status: parent.product_status.status,
51
- wholesale: parent.product_status.status,
52
- retail: parent.product_status.status
50
+ status: parent?.product_status.status,
51
+ wholesale: parent?.product_status.status,
52
+ retail: parent?.product_status.status
53
53
  },
54
- taxClass: parent.tax_class,
55
- foreignOrderLimit: parent.foreign_limit,
56
- scanQualifiedCount: parent.scan_qualified_count,
57
- chargeShipping: parent.charge_shipping,
58
- dangerousGoods: parent.dangerous_goods,
54
+ taxClass: parent?.tax_class,
55
+ foreignOrderLimit: parent?.foreign_limit,
56
+ scanQualifiedCount: parent?.scan_qualified_count,
57
+ chargeShipping: parent?.charge_shipping,
58
+ dangerousGoods: parent?.dangerous_goods,
59
59
  marketAttributes: {
60
- discount: parent.point_discount,
61
- redeem: parent.point_redeem,
62
- earn: parent.point_earn
60
+ discount: parent?.point_discount,
61
+ redeem: parent?.point_redeem,
62
+ earn: parent?.point_earn
63
63
  }
64
64
  })
65
65