@nuskin/cs-product-lib 1.7.2 → 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 +68 -27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuskin/cs-product-lib",
3
- "version": "1.7.2",
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) => {
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'
@@ -154,11 +179,18 @@ const csProduct = (props) => {
154
179
  const bestSkuData = helpers?.getBestSkuData(variant)
155
180
  if (bestSkuData.sku) {
156
181
  const varStatus = status[bestSkuData.sku]
157
- if (varStatus && varStatus.status.retail) {
182
+ const isBundleOnly =
183
+ bestSkuData?.status_selections?.status === 'bundleOnly'
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)) {
158
190
  variant.sap_data.sku_data = [bestSkuData]
159
191
  validVariants.push({
160
192
  ...variant,
161
- status: varStatus,
193
+ status: varStatus || { status: { retail: 'bundleOnly', wholesale: 'bundleOnly' } },
162
194
  backorderQuantity: varStatus?.backordered ? varStatus?.availableQuantity : 0,
163
195
  atpQuantity: varStatus?.backordered ? 0 : varStatus?.availableQuantity,
164
196
  availableQuantity: varStatus?.availableQuantity,
@@ -254,7 +286,7 @@ const csProduct = (props) => {
254
286
  })
255
287
  }
256
288
 
257
- const convertKitToRequiredProducts = async (skuKit, locale) => {
289
+ const convertKitToRequiredProducts = async (skuKit, locale, bypassProductStatus = false) => {
258
290
  let productBundle = {}
259
291
  const variantSkus = []
260
292
  for (let kitItem of skuKit[0].variants) {
@@ -262,8 +294,9 @@ const csProduct = (props) => {
262
294
  variantSkus.push((skuData || {}).sku)
263
295
  kitItem.sku = (skuData || {}).sku
264
296
  }
265
- const products = await getProductsBySku(variantSkus, locale)
297
+ const products = await getProductsBySku(variantSkus, locale, true, bypassProductStatus)
266
298
  const requiredProducts = []
299
+ const seenSkuQuantities = new Set()
267
300
  for (let i = 0; i < products?.length; i++) {
268
301
  for (const variantSku of variantSkus) {
269
302
  const productSelect = JSON.parse(JSON.stringify(products[i]))
@@ -274,10 +307,15 @@ const csProduct = (props) => {
274
307
  }
275
308
  }
276
309
  if (!isNullOrEmpty(productSelect?.variants)) {
277
- requiredProducts.push({
278
- product: [productSelect],
279
- quantity: skuKit[0].variants.find(v=>v.sku === variantSku).quantity
280
- })
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
+ }
281
319
  }
282
320
  }
283
321
  }
@@ -314,14 +352,15 @@ const csProduct = (props) => {
314
352
  return [productBundle]
315
353
  }
316
354
 
317
- const formatKit = async (products, locale, translations) => {
355
+ const formatKit = async (products, locale, translations, bypassProductStatus = false) => {
318
356
  for (let p = 0; p < products.length; p++) {
319
357
  let noFormat = false
320
358
  if (products[p]?.sku_kit?.length > 0) {
321
359
  products[p].type = 'kit'
322
360
  products[p].product_bundle = await convertKitToRequiredProducts(
323
361
  products[p].sku_kit,
324
- locale
362
+ locale,
363
+ bypassProductStatus
325
364
  )
326
365
  noFormat = true
327
366
  }
@@ -437,8 +476,8 @@ const csProduct = (props) => {
437
476
  * @deprecated use getProductsByUID instead
438
477
  * @returns
439
478
  */
440
- const getBundle = async (id, locale) => {
441
- const bundles = await getBundles(id, locale)
479
+ const getBundle = async (id, locale, bypassProductStatus = false) => {
480
+ const bundles = await getBundles(id, locale, bypassProductStatus)
442
481
  return bundles && bundles[0] ? bundles[0] : null
443
482
  }
444
483
 
@@ -462,7 +501,7 @@ const csProduct = (props) => {
462
501
  * @deprecated use getProductsByUID instead
463
502
  * @returns
464
503
  */
465
- const getBundles = async (ids, locale) => {
504
+ const getBundles = async (ids, locale, bypassProductStatus = false) => {
466
505
  locale = badLocaleMap(locale)
467
506
  if (!Array.isArray(ids)) {
468
507
  ids = [ids]
@@ -478,9 +517,11 @@ const csProduct = (props) => {
478
517
  const products = await queryProducts(query, locale)
479
518
  if (Array.isArray(products) && products.length > 0) {
480
519
  const translations = await getTranslations(locale)
481
- await formatKit(products, locale, translations)
482
- await removeInvalidVariants(products, locale, translations)
483
- 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)
484
525
  return formatProductWithSchema(products, status)
485
526
  }
486
527
  const errorResponse = products.response || {}
@@ -488,7 +529,7 @@ const csProduct = (props) => {
488
529
  return [{error: {message: products.message, name: products.name, status: errorResponse.status, errors: [`${responseData.error_code}: ${responseData.error_message}`]}}]
489
530
  }
490
531
 
491
- const getProductsBySku = async (skus, locale) => {
532
+ const getProductsBySku = async (skus, locale, keepBundleOnly = false, bypassProductStatus = false) => {
492
533
  locale = badLocaleMap(locale)
493
534
  if (!Array.isArray(skus)) {
494
535
  skus = [skus]
@@ -514,8 +555,8 @@ const csProduct = (props) => {
514
555
  return bundleProducts
515
556
  }
516
557
  const translations = await getTranslations(locale)
517
- await formatKit(products, locale, translations)
518
- await removeInvalidVariants(products, locale, translations)
558
+ await formatKit(products, locale, translations, bypassProductStatus)
559
+ await removeInvalidVariants(products, locale, translations, keepBundleOnly, bypassProductStatus)
519
560
  const formattedProducts = formatProductWithSchema(products)
520
561
  for (const product of formattedProducts) {
521
562
  product.skus = skus