@nuskin/cs-product-lib 1.7.3-bundleonly-members.1 → 1.8.0-bundleonly-members.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/csProduct.js +65 -26
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuskin/cs-product-lib",
3
- "version": "1.7.3-bundleonly-members.1",
3
+ "version": "1.8.0-bundleonly-members.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
@@ -42,6 +42,9 @@ const validateConfig = (props) => {
42
42
  /* istanbul ignore next */
43
43
  const csProduct = (props) => {
44
44
  const DEV_ENVIRONMENT = 'public-dev'
45
+ // CS-native sellable gate for the bypassProductStatus path -- mirrors
46
+ // content-validator ValidationSteps (csStatus === 'sellable' || 'bundleOnly').
47
+ const CS_SELLABLE_STATUSES = ['sellable', 'bundleOnly']
45
48
  const VARIANTS = 'variants'
46
49
  const VARIANT = 'variant'
47
50
  const KEY_INGREDIENTS = 'ingredients.key_ingredients'
@@ -108,7 +111,7 @@ const csProduct = (props) => {
108
111
  }
109
112
  }
110
113
 
111
- const removeInvalidVariants = async (products = [], locale, translations, keepBundleOnly = false) => {
114
+ const removeInvalidVariants = async (products = [], locale, translations, keepBundleOnly = false, bypassProductStatus = false) => {
112
115
  const validSkus = []
113
116
  for (const product of products) {
114
117
  for (const variant of product?.variants || []) {
@@ -125,13 +128,35 @@ const csProduct = (props) => {
125
128
  let status = {}
126
129
  let marketData = {}
127
130
  if (validSkus?.length > 0) {
128
- status = await getProductStatus(
129
- validSkus,
130
- locale,
131
- homeUrl,
132
- awsBase,
133
- clientId
134
- )
131
+ if (bypassProductStatus) {
132
+ // Equinox markets: derive status from the CS entry instead of
133
+ // calling product-status. Live availability/quantity is
134
+ // intentionally not available on this path. Temporary until
135
+ // cs-product-graphql is retired.
136
+ status = {}
137
+ for (const product of products) {
138
+ for (const variant of product?.variants || []) {
139
+ const best = helpers?.getBestSkuData(variant)
140
+ if (best?.sku) {
141
+ const csStatus = helpers.mapCsStatus(variant)
142
+ status[best.sku] = {
143
+ status: { retail: csStatus.retail, wholesale: csStatus.wholesale },
144
+ csStatus: csStatus.status,
145
+ isBackordered: false,
146
+ backorderedAvailableDate: null
147
+ }
148
+ }
149
+ }
150
+ }
151
+ } else {
152
+ status = await getProductStatus(
153
+ validSkus,
154
+ locale,
155
+ homeUrl,
156
+ awsBase,
157
+ clientId
158
+ )
159
+ }
135
160
  const localeSplit = locale.split('_')
136
161
  let market =
137
162
  localeSplit?.length > 1 ? localeSplit[1].toUpperCase() : 'XX'
@@ -156,7 +181,12 @@ const csProduct = (props) => {
156
181
  const varStatus = status[bestSkuData.sku]
157
182
  const isBundleOnly =
158
183
  bestSkuData?.status_selections?.status === 'bundleOnly'
159
- if ((varStatus && varStatus.status.retail) || (keepBundleOnly && isBundleOnly)) {
184
+ const passesBypassGate =
185
+ bypassProductStatus &&
186
+ CS_SELLABLE_STATUSES.includes(varStatus?.csStatus)
187
+ const passesStatusGate =
188
+ !bypassProductStatus && varStatus && varStatus.status.retail
189
+ if (passesStatusGate || passesBypassGate || (keepBundleOnly && isBundleOnly)) {
160
190
  variant.sap_data.sku_data = [bestSkuData]
161
191
  validVariants.push({
162
192
  ...variant,
@@ -256,7 +286,7 @@ const csProduct = (props) => {
256
286
  })
257
287
  }
258
288
 
259
- const convertKitToRequiredProducts = async (skuKit, locale) => {
289
+ const convertKitToRequiredProducts = async (skuKit, locale, bypassProductStatus = false) => {
260
290
  let productBundle = {}
261
291
  const variantSkus = []
262
292
  for (let kitItem of skuKit[0].variants) {
@@ -264,8 +294,9 @@ const csProduct = (props) => {
264
294
  variantSkus.push((skuData || {}).sku)
265
295
  kitItem.sku = (skuData || {}).sku
266
296
  }
267
- const products = await getProductsBySku(variantSkus, locale, true)
297
+ const products = await getProductsBySku(variantSkus, locale, true, bypassProductStatus)
268
298
  const requiredProducts = []
299
+ const seenSkuQuantities = new Set()
269
300
  for (let i = 0; i < products?.length; i++) {
270
301
  for (const variantSku of variantSkus) {
271
302
  const productSelect = JSON.parse(JSON.stringify(products[i]))
@@ -276,10 +307,15 @@ const csProduct = (props) => {
276
307
  }
277
308
  }
278
309
  if (!isNullOrEmpty(productSelect?.variants)) {
279
- requiredProducts.push({
280
- product: [productSelect],
281
- quantity: skuKit[0].variants.find(v=>v.sku === variantSku).quantity
282
- })
310
+ const quantity = skuKit[0].variants.find(v=>v.sku === variantSku).quantity
311
+ const dedupKey = `${variantSku}:${quantity}`
312
+ if (!seenSkuQuantities.has(dedupKey)) {
313
+ seenSkuQuantities.add(dedupKey)
314
+ requiredProducts.push({
315
+ product: [productSelect],
316
+ quantity
317
+ })
318
+ }
283
319
  }
284
320
  }
285
321
  }
@@ -316,14 +352,15 @@ const csProduct = (props) => {
316
352
  return [productBundle]
317
353
  }
318
354
 
319
- const formatKit = async (products, locale, translations) => {
355
+ const formatKit = async (products, locale, translations, bypassProductStatus = false) => {
320
356
  for (let p = 0; p < products.length; p++) {
321
357
  let noFormat = false
322
358
  if (products[p]?.sku_kit?.length > 0) {
323
359
  products[p].type = 'kit'
324
360
  products[p].product_bundle = await convertKitToRequiredProducts(
325
361
  products[p].sku_kit,
326
- locale
362
+ locale,
363
+ bypassProductStatus
327
364
  )
328
365
  noFormat = true
329
366
  }
@@ -439,8 +476,8 @@ const csProduct = (props) => {
439
476
  * @deprecated use getProductsByUID instead
440
477
  * @returns
441
478
  */
442
- const getBundle = async (id, locale) => {
443
- const bundles = await getBundles(id, locale)
479
+ const getBundle = async (id, locale, bypassProductStatus = false) => {
480
+ const bundles = await getBundles(id, locale, bypassProductStatus)
444
481
  return bundles && bundles[0] ? bundles[0] : null
445
482
  }
446
483
 
@@ -464,7 +501,7 @@ const csProduct = (props) => {
464
501
  * @deprecated use getProductsByUID instead
465
502
  * @returns
466
503
  */
467
- const getBundles = async (ids, locale) => {
504
+ const getBundles = async (ids, locale, bypassProductStatus = false) => {
468
505
  locale = badLocaleMap(locale)
469
506
  if (!Array.isArray(ids)) {
470
507
  ids = [ids]
@@ -480,9 +517,11 @@ const csProduct = (props) => {
480
517
  const products = await queryProducts(query, locale)
481
518
  if (Array.isArray(products) && products.length > 0) {
482
519
  const translations = await getTranslations(locale)
483
- await formatKit(products, locale, translations)
484
- await removeInvalidVariants(products, locale, translations)
485
- const status = await getProductStatus(getAllBundleIds(products), helpers?.reorderLocale(locale), homeUrl, awsBase, clientId)
520
+ await formatKit(products, locale, translations, bypassProductStatus)
521
+ await removeInvalidVariants(products, locale, translations, false, bypassProductStatus)
522
+ const status = bypassProductStatus
523
+ ? {}
524
+ : await getProductStatus(getAllBundleIds(products), helpers?.reorderLocale(locale), homeUrl, awsBase, clientId)
486
525
  return formatProductWithSchema(products, status)
487
526
  }
488
527
  const errorResponse = products.response || {}
@@ -490,7 +529,7 @@ const csProduct = (props) => {
490
529
  return [{error: {message: products.message, name: products.name, status: errorResponse.status, errors: [`${responseData.error_code}: ${responseData.error_message}`]}}]
491
530
  }
492
531
 
493
- const getProductsBySku = async (skus, locale, keepBundleOnly = false) => {
532
+ const getProductsBySku = async (skus, locale, keepBundleOnly = false, bypassProductStatus = false) => {
494
533
  locale = badLocaleMap(locale)
495
534
  if (!Array.isArray(skus)) {
496
535
  skus = [skus]
@@ -516,8 +555,8 @@ const csProduct = (props) => {
516
555
  return bundleProducts
517
556
  }
518
557
  const translations = await getTranslations(locale)
519
- await formatKit(products, locale, translations)
520
- await removeInvalidVariants(products, locale, translations, keepBundleOnly)
558
+ await formatKit(products, locale, translations, bypassProductStatus)
559
+ await removeInvalidVariants(products, locale, translations, keepBundleOnly, bypassProductStatus)
521
560
  const formattedProducts = formatProductWithSchema(products)
522
561
  for (const product of formattedProducts) {
523
562
  product.skus = skus