@nuskin/cs-product-lib 1.0.25-cx12-8656.3 → 1.0.25

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.25-cx12-8656.3",
3
+ "version": "1.0.25",
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
@@ -132,9 +132,6 @@ const csProduct = (props) => {
132
132
  validVariants.push({
133
133
  ...variant,
134
134
  status: varStatus,
135
- backorderQuantity: varStatus?.backordered ? varStatus?.availableQuantity : 0,
136
- availableQuantity: varStatus?.backordered ? 0 : varStatus?.availableQuantity,
137
- backorderedAvailableDate: varStatus?.backorderedAvailableDate || null,
138
135
  marketData
139
136
  })
140
137
  }
@@ -294,12 +291,14 @@ const csProduct = (props) => {
294
291
  .length;
295
292
  i++
296
293
  ) {
297
- await removeInvalidVariants(
298
- products[p].product_bundle[b].required_products[i]
299
- .product,
300
- locale,
301
- translations
302
- )
294
+ if (!noFormat) {
295
+ await removeInvalidVariants(
296
+ products[p].product_bundle[b].required_products[i]
297
+ .product,
298
+ locale,
299
+ translations
300
+ )
301
+ }
303
302
  }
304
303
  for (
305
304
  let i = 0;
package/src/formatKit.js CHANGED
@@ -16,40 +16,52 @@ const parseStatus = (variant) => {
16
16
  let backorderedAvailableDate = null
17
17
  if (variant?.type === 'kit') {
18
18
  try {
19
- let dates = [];
20
- variant?.requiredProducts?.flatMap(a => a?.product?.variants?.map(s => {
21
- const backorderDate = s?.status?.backorderedAvailableDate || null;
22
- dates.push(moment(backorderDate))
23
- }))
24
- dates = dates.filter(date => date.isValid());
25
- const latestBODate = dates && dates.length > 0 ? moment.max(dates).format(dateFormat) : null;
19
+ /* eslint-disable no-unsafe-optional-chaining */
20
+ const latestBODate = Math.max(
21
+ ...variant?.requiredProducts?.flatMap((a) =>
22
+ a?.product?.variants?.map(
23
+ (s) =>
24
+ Number(
25
+ new Date(s?.status?.backorderedAvailableDate)
26
+ ) || 0
27
+ )
28
+ )
29
+ )
26
30
  if (latestBODate) {
27
31
  isBackordered = true
28
- backorderedAvailableDate = latestBODate
32
+ backorderedAvailableDate = moment(
33
+ new Date(latestBODate)
34
+ ).format(dateFormat)
29
35
  }
30
36
  } catch {
31
37
  backorderedAvailableDate = null
32
38
  }
33
39
  } else if (variant?.type === 'bundle') {
34
40
  try {
35
- let dates = [];
36
- variant?.requiredProducts?.flatMap(a => a?.product?.variants?.map(s => {
37
- const backorderDate = s?.status?.backorderedAvailableDate || null;
38
- dates.push(moment(backorderDate))
39
- }))
40
- dates = dates.filter(date => date.isValid());
41
- const latestBODate = dates && dates.length > 0 ? moment.max(dates).format(dateFormat) : null;
41
+ const latestBODate = Math.max(
42
+ ...variant?.requiredProducts?.flatMap(
43
+ (a) =>
44
+ Number(
45
+ new Date(
46
+ a?.product?.variants[0]?.status?.backorderedAvailableDate
47
+ )
48
+ ) || 0
49
+ )
50
+ )
42
51
  if (latestBODate) {
43
52
  isBackordered = true
44
- backorderedAvailableDate = latestBODate
53
+ backorderedAvailableDate = moment(
54
+ new Date(latestBODate)
55
+ ).format(dateFormat)
45
56
  }
46
57
  } catch {
47
58
  backorderedAvailableDate = null
48
59
  }
49
60
  }
50
61
  return {
51
- status: variant?.status?.status,
52
- isBackordered: isBackordered,
62
+ // changed
63
+ status: variant?.status?.status,
64
+ isBackordered: isBackordered,
53
65
  backorderedAvailableDate: backorderedAvailableDate
54
66
  }
55
67
  }
@@ -142,72 +154,36 @@ const parsePrice = (product = []) => {
142
154
  return sumOfPriceAndPoints
143
155
  }
144
156
 
145
- const getBundleQuantities = (properties) => {
146
- let maxQty = null;
147
- let backOrderedQty = null;
148
- let availableQty = null;
149
- let quantities = [];
150
- let availableQuantities = [];
151
- let prodQty = 0;
157
+ const parseKitAvailableQuantity = (kit) => {
152
158
  try {
153
- properties?.requiredProducts?.flatMap(a => {
154
- prodQty = Number(a?.quantity || 1);
155
- a?.product?.variants?.map(s => {
156
- const backorderQty = s?.backorderQuantity || 0;
157
- const availableQty = s?.availableQuantity || 0;
158
- quantities.push({maxPurchasable: (backorderQty + availableQty)/prodQty});
159
- availableQuantities.push({availableQty});
160
- })
161
- })
162
- const maxQtyPurchasable = quantities && quantities.length > 0 ? Math.min(...quantities.map(obj=>obj.maxPurchasable)) : 0;
163
- const maxAvailableQty = availableQuantities && availableQuantities.length > 0 ? Math.min(...availableQuantities.map(obj=>obj.availableQty)) : 0;
164
- maxQty = Math.floor(maxQtyPurchasable);
165
- backOrderedQty = maxQty - maxAvailableQty;
166
- availableQty = maxAvailableQty;
167
- } catch (error) {
168
- console.error('back order qty, available qty, or max qty error', error);
169
- maxQty = 0;
170
- backOrderedQty = 0;
171
- availableQty = 0;
159
+ return (
160
+ Math.min(
161
+ ...kit.flatMap((a) =>
162
+ a?.sku?.map((s) => s?.inventoryProperties?.atpQty)
163
+ )
164
+ ) || 0
165
+ )
166
+ } catch {
167
+ return 0
172
168
  }
173
- return {maxQty, backOrderedQty, availableQty}
174
169
  }
175
170
 
176
- const getKitQuantities = (properties) => {
177
- let maxQty = null;
178
- let backOrderedQty = null;
179
- let availableQty = null;
180
- let quantities = [];
181
- let availableQuantities = [];
182
- let prodQty = 0;
171
+ const parseBundleAvailableQuantity = (product) => {
183
172
  try {
184
- properties?.flatMap(a => {
185
- prodQty = Number(a?.properties?.quantity || 1);
186
- a.sku.map(s => {
187
- const backorderQty = s?.inventoryProperties?.backOrderedQty|| 0;
188
- const availableQty = s?.inventoryProperties?.atpQty || 0;
189
- quantities.push({maxPurchasable: (backorderQty + availableQty)/prodQty});
190
- availableQuantities.push({availableQty});
191
- })
192
- })
193
- const maxQtyPurchasable = quantities && quantities.length > 0 ? Math.min(...quantities.map(obj=>obj.maxPurchasable)) : 0;
194
- const maxAvailableQty = availableQuantities && availableQuantities.length > 0 ? Math.min(...availableQuantities.map(obj=>obj.availableQty)) : 0;
195
- maxQty = Math.floor(maxQtyPurchasable);
196
- backOrderedQty = maxQty - maxAvailableQty;
197
- availableQty = maxAvailableQty;
198
- } catch (error) {
199
- console.error('back order qty, available qty, or max qty error', error);
200
- maxQty = 0;
201
- backOrderedQty = 0;
202
- availableQty = 0;
173
+ const { requiredProducts = [] } = !isNullOrEmpty(product) ? product : {}
174
+ const quantities = requiredProducts?.map((p) =>
175
+ p?.product?.variants?.map((a) => a?.availableQuantity)
176
+ )
177
+ return Math.min(...quantities) || 0
178
+ } catch {
179
+ console.log('some error in availableQuantity')
180
+ return 0
203
181
  }
204
- return {maxQty, backOrderedQty, availableQty}
205
182
  }
206
183
 
207
184
  const parseKit = (product, market) => {
208
185
  const kits = {}
209
186
  const [properties] = !isNullOrEmpty(product) ? product : []
210
- const status = parseStatus(properties)
211
187
  kits.id = properties?.id
212
188
  kits.uid = properties?.uid
213
189
  kits.title = properties?.title
@@ -216,9 +192,11 @@ const parseKit = (product, market) => {
216
192
  kits.chargeShipping = properties?.chargeShipping === 'true'
217
193
  kits.customerTypes = properties?.customerTypes
218
194
  kits.dangerousGoods = properties?.dangerousGoods === 'true'
195
+ // missing
219
196
  kits.excludeFromSearch = properties?.excludeFromSearch === 'true'
220
197
  kits.isExclusive = properties?.isExclusive === 'true'
221
198
  kits.marketAttributes = properties?.marketAttributes
199
+ kits.maxQuantity = (product?.inventoryProperties || {}).atpQty
222
200
  const priceData = parsePrice(product)
223
201
  kits.points = priceData?.points
224
202
  kits.price = priceData?.price
@@ -234,16 +212,14 @@ const parseKit = (product, market) => {
234
212
  kits.grpDiscount = properties?.grpDiscount
235
213
  kits.scanQualifiedCount = properties?.scanQualifiedCount
236
214
  kits.taxClass = properties?.taxClass
237
- kits.status = status
238
- if ((kits?.status || {})?.status === 'active') kits.status.status = 'sellable'
215
+ // newVariant.slug = undefined
216
+ kits.status = parseStatus(properties)
217
+ if ((kits?.status || {})?.status === 'active')
218
+ kits.status.status = 'sellable'
239
219
  const kitProducts = []
240
220
  if (properties?.type === 'kit' && product?.kit) {
241
221
  kits.type = 'kit'
242
- const quantities = getKitQuantities(properties)
243
- kits.maxQuantity = quantities.maxQty
244
- kits.availableQuantity = quantities.availableQty
245
- kits.backorderQuantity = quantities.backOrderedQty
246
- kits.backorderedAvailableDate = status?.backorderedAvailableDate
222
+ kits.availableQuantity = parseKitAvailableQuantity(product?.kit)
247
223
  for (const kit of product?.kit || []) {
248
224
  const kitProduct = formatKit(kit, market)
249
225
  kitProduct.id = kit?.identifier
@@ -258,11 +234,7 @@ const parseKit = (product, market) => {
258
234
  (properties?.type === 'kit' && properties?.requiredProducts)
259
235
  ) {
260
236
  kits.type = properties?.type
261
- const quantities = getBundleQuantities(properties)
262
- kits.maxQuantity = quantities.maxQty
263
- kits.availableQuantity = quantities.availableQty
264
- kits.backorderQuantity = quantities.backOrderedQty
265
- kits.backorderedAvailableDate = status?.backorderedAvailableDate
237
+ kits.availableQuantity = parseBundleAvailableQuantity(properties)
266
238
  for (const kit of properties?.requiredProducts || []) {
267
239
  if (kit?.product) {
268
240
  const kitProduct = formatKit(kit?.product, market)
@@ -308,8 +280,8 @@ const formatKit = (product, market) => {
308
280
  module.exports = {
309
281
  formatKit,
310
282
  parseKit,
311
- getKitQuantities,
312
- getBundleQuantities,
283
+ parseBundleAvailableQuantity,
284
+ parseKitAvailableQuantity,
313
285
  parsePrice,
314
286
  parseStatus
315
287
  }
@@ -76,7 +76,7 @@ const formatProduct = (parent, homeUrl) => {
76
76
  upSellProductIds: helpers?.mapUpSellProductIds(parent),
77
77
  crossProductIds: helpers?.mapCrossProductIds(parent)
78
78
  }
79
- if (parent.bundle || parent.sku_kit || parent.product_bundle) {
79
+ if (parent.bundle || (parent.sku_kit && parent.sku_kit.length > 0) || (parent.product_bundle && parent.product_bundle.length > 0)) {
80
80
  return formatKit(formattedProduct)
81
81
  } else {
82
82
  return formattedProduct
@@ -2,117 +2,111 @@ const helpers = require('./helpers')
2
2
  const _ = require('lodash')
3
3
  const entities = require('entities')
4
4
 
5
- const formatVariant = (parent) => {
6
- const isBackOrdered = _.get(parent, 'defaults.status.backordered', null);
7
- const backOrderedDate = _.get(parent, 'defaults.status.backorderedAvailableDate', null);
8
- const preliminaryAvailableQty = _.get(parent, 'defaults.status.availableQuantity', 0);
9
- const availableQty = isBackOrdered ? 0 : preliminaryAvailableQty;
10
- const backOrderedQty = isBackOrdered ? availableQty : 0;
11
-
5
+ const formatVariant = (parent) => ({
12
6
  // Each value is a function with a parameter 'parent' that is the current instance of a Variant
13
7
  // ContentStack references need to be queried separately
14
- return {
15
- id: parent?.uid,
16
- globalId: parent?.sap_data?.global_id,
17
- sku: helpers?.getBestSkuData(parent)?.sku,
18
- slug: parent?.slug,
19
- size: parent?.size,
20
- variantLabel: parent?.select_label,
21
- variantColor: parent?.select_color,
22
- title: parent?.name || parent?.defaults?.name,
23
- salesLabel: parent?.sales_label,
24
- salesText: parent?.sales_text,
25
- description: entities?.decodeHTML(
26
- parent?.short_description_override || parent?.defaults?.description || ''
27
- ),
28
- salesDisclaimer: helpers?.mapSalesDisclaimer(parent?.sales_disclaimer),
29
- nettoWeight: parent?.netto_weight,
30
- productImages: helpers?.mapProductImages(parent),
31
- isExclusive: parent?.is_exclusive,
32
- pricingJson: JSON.stringify(parent?.defaults?.status?.pricing) || null,
33
- price: helpers?.mapPrice(parent),
34
- points: helpers?.mapPoints(parent),
35
- backorderedAvailableDate: backOrderedDate,
36
- backorderQuantity: backOrderedQty,
37
- availableQuantity: availableQty,
38
- maxQuantity: _.get(parent, 'defaults.status.maxQuantity', 0), // will be product status overrideMaxQty else its availableQty
39
- status: {
40
- status: _.get(parent, 'defaults.status.status.retail', null),
41
- retail: _.get(parent, 'defaults.status.status.retail', null),
42
- wholesale: _.get(parent, 'defaults.status.status.wholesale', null),
43
- isBackordered: isBackOrdered,
44
- backorderedAvailableDate: backOrderedDate
45
- },
46
- csStatus: helpers?.mapCsStatus(parent),
47
- purchaseTypes: helpers?.mapPurchaseTypes(parent),
48
- features: helpers?.mapFeatures(parent),
49
- productDetails: helpers?.mapProductDetails(parent),
50
- ingredients: helpers?.mapIngredients(
51
- parent.ingredients_override,
52
- parent.legacy_ingredients
8
+ id: parent?.uid,
9
+ globalId: parent?.sap_data?.global_id,
10
+ sku: helpers?.getBestSkuData(parent)?.sku,
11
+ slug: parent?.slug,
12
+ size: parent?.size,
13
+ variantLabel: parent?.select_label,
14
+ variantColor: parent?.select_color,
15
+ title: parent?.name || parent?.defaults?.name,
16
+ salesLabel: parent?.sales_label,
17
+ salesText: parent?.sales_text,
18
+ description: entities?.decodeHTML(
19
+ parent?.short_description_override || parent?.defaults?.description || ''
20
+ ),
21
+ salesDisclaimer: helpers?.mapSalesDisclaimer(parent?.sales_disclaimer),
22
+ nettoWeight: parent?.netto_weight,
23
+ productImages: helpers?.mapProductImages(parent),
24
+ isExclusive: parent?.is_exclusive,
25
+ pricingJson: JSON.stringify(parent?.defaults?.status?.pricing) || null,
26
+ price: helpers?.mapPrice(parent),
27
+ points: helpers?.mapPoints(parent),
28
+ availableQuantity: _.get(parent, 'defaults.status.availableQuantity', 0),
29
+ maxQuantity: _.get(parent, 'defaults.status.maxQuantity', 0),
30
+ status: {
31
+ status: _.get(parent, 'defaults.status.status.retail', null),
32
+ retail: _.get(parent, 'defaults.status.status.retail', null),
33
+ wholesale: _.get(parent, 'defaults.status.status.wholesale', null),
34
+ isBackordered: _.get(parent, 'defaults.status.backordered', null),
35
+ backorderedAvailableDate: _.get(
36
+ parent,
37
+ 'defaults.status.backorderedAvailableDate',
38
+ ''
39
+ )
40
+ },
41
+ csStatus: helpers?.mapCsStatus(parent),
42
+ purchaseTypes: helpers?.mapPurchaseTypes(parent),
43
+ features: helpers?.mapFeatures(parent),
44
+ productDetails: helpers?.mapProductDetails(parent),
45
+ ingredients: helpers?.mapIngredients(
46
+ parent.ingredients_override,
47
+ parent.legacy_ingredients
48
+ ),
49
+ benefitsAndFeatures: helpers?.mapBenefitsAndFeatures(parent),
50
+ results: helpers?.mapResults(parent),
51
+ usage: helpers?.mapUsage(parent, parent.legacy_usage),
52
+ resources: helpers?.mapResources(parent),
53
+ faqs: helpers?.mapFaqs(parent),
54
+ disclaimers: parent?.disclaimers || [],
55
+ disclaimerWarnings: helpers?.mapDisclaimerWarnings(
56
+ parent.disclaimer_warnings
57
+ ),
58
+ matchingVariant:
59
+ parent.sap_data?.sku_data?.findIndex((s) =>
60
+ parent.searchedSkus.includes(s.sku)
61
+ ) > -1,
62
+ shadeable: parent?.shadeable || false,
63
+ restrictedMarkets: parent?.restricted_markets || [],
64
+ productType: parent?.sap_data?.product_type,
65
+ primaryBrand: parent.sap_data?.primary_brand,
66
+ brandFamily: parent?.sap_data?.brand_family,
67
+ marketAttributes: {
68
+ discount: _.get(
69
+ parent,
70
+ 'defaults.status.marketAttributes.discount',
71
+ null
53
72
  ),
54
- benefitsAndFeatures: helpers?.mapBenefitsAndFeatures(parent),
55
- results: helpers?.mapResults(parent),
56
- usage: helpers?.mapUsage(parent, parent.legacy_usage),
57
- resources: helpers?.mapResources(parent),
58
- faqs: helpers?.mapFaqs(parent),
59
- disclaimers: parent?.disclaimers || [],
60
- disclaimerWarnings: helpers?.mapDisclaimerWarnings(
61
- parent.disclaimer_warnings
62
- ),
63
- matchingVariant:
64
- parent.sap_data?.sku_data?.findIndex((s) =>
65
- parent.searchedSkus.includes(s.sku)
66
- ) > -1,
67
- shadeable: parent?.shadeable || false,
68
- restrictedMarkets: parent?.restricted_markets || [],
69
- productType: parent?.sap_data?.product_type,
70
- primaryBrand: parent.sap_data?.primary_brand,
71
- brandFamily: parent?.sap_data?.brand_family,
72
- marketAttributes: {
73
- discount: _.get(
74
- parent,
75
- 'defaults.status.marketAttributes.discount',
76
- null
77
- ),
78
- redeem: _.get(parent, 'defaults.status.marketAttributes.redeem', null),
79
- earn: _.get(parent, 'defaults.status.marketAttributes.earn', null)
80
- },
81
- legacyBenefits: parent.legacy_benefits,
82
- legacyLongDescription: parent.legacy_long_description,
83
- legacyResources: parent.legacy_resources,
84
- legacyUsage: parent.legacy_usage,
85
- legacyIngredients: parent.legacy_ingredients,
86
- legacyImages: parent.legacy_images || [],
87
- legacyYouTube: parent.legacy_youtube_video,
88
- csAtts: {
89
- version: parent._version || 0,
90
- updatedAt: parent.updated_at || '',
91
- updatedBy: parent.updated_by || '',
92
- createdAt: parent.created_at || '',
93
- createdBy: parent.created_by || '',
94
- locale: parent.locale || ''
95
- },
96
- searchKeywords: parent?.search_keywords,
97
- excludeFromSearch: parent?.exclude_from_search || helpers?.mapCsStatus(parent).status === 'discontinued' || _.get(parent, 'defaults.status.status.retail', null) === 'DISCONTINUED',
98
- customerTypes: helpers?.getBestSkuData(parent)?.status_selections
99
- ?.customer_types,
100
- availableChannels: helpers?.getBestSkuData(parent)?.status_selections
101
- ?.display_channels,
102
- displayChannels: helpers?.getBestSkuData(parent)?.status_selections
103
- ?.display_channels,
104
- scanQualifiedCount: helpers?.getBestSkuData(parent)?.status_selections
105
- ?.scan_qualify_count,
106
- scanQualifyCount: helpers?.getBestSkuData(parent)?.status_selections
107
- ?.scan_qualify_count,
108
- chargeShipping: helpers?.getBestSkuData(parent)?.charge_shipping,
109
- dangerousGoods: helpers?.getBestSkuData(parent)?.dangerous_goods,
110
- channels: helpers?.getBestSkuData(parent)?.channels,
111
- foreignLimit: helpers?.getBestSkuData(parent)?.foreign_limit,
112
- foreignOrderLimit: helpers?.getBestSkuData(parent)?.foreign_limit,
113
- orderType: parent.sap_data?.order_type
114
- }
115
- }
73
+ redeem: _.get(parent, 'defaults.status.marketAttributes.redeem', null),
74
+ earn: _.get(parent, 'defaults.status.marketAttributes.earn', null)
75
+ },
76
+ legacyBenefits: parent.legacy_benefits,
77
+ legacyLongDescription: parent.legacy_long_description,
78
+ legacyResources: parent.legacy_resources,
79
+ legacyUsage: parent.legacy_usage,
80
+ legacyIngredients: parent.legacy_ingredients,
81
+ legacyImages: parent.legacy_images || [],
82
+ legacyYouTube: parent.legacy_youtube_video,
83
+ csAtts: {
84
+ version: parent._version || 0,
85
+ updatedAt: parent.updated_at || '',
86
+ updatedBy: parent.updated_by || '',
87
+ createdAt: parent.created_at || '',
88
+ createdBy: parent.created_by || '',
89
+ locale: parent.locale || ''
90
+ },
91
+ searchKeywords: parent?.search_keywords,
92
+ excludeFromSearch: parent?.exclude_from_search || helpers?.mapCsStatus(parent).status === 'discontinued' || _.get(parent, 'defaults.status.status.retail', null) === 'DISCONTINUED',
93
+ customerTypes: helpers?.getBestSkuData(parent)?.status_selections
94
+ ?.customer_types,
95
+ availableChannels: helpers?.getBestSkuData(parent)?.status_selections
96
+ ?.display_channels,
97
+ displayChannels: helpers?.getBestSkuData(parent)?.status_selections
98
+ ?.display_channels,
99
+ scanQualifiedCount: helpers?.getBestSkuData(parent)?.status_selections
100
+ ?.scan_qualify_count,
101
+ scanQualifyCount: helpers?.getBestSkuData(parent)?.status_selections
102
+ ?.scan_qualify_count,
103
+ chargeShipping: helpers?.getBestSkuData(parent)?.charge_shipping,
104
+ dangerousGoods: helpers?.getBestSkuData(parent)?.dangerous_goods,
105
+ channels: helpers?.getBestSkuData(parent)?.channels,
106
+ foreignLimit: helpers?.getBestSkuData(parent)?.foreign_limit,
107
+ foreignOrderLimit: helpers?.getBestSkuData(parent)?.foreign_limit,
108
+ orderType: parent.sap_data?.order_type
109
+ })
116
110
 
117
111
  module.exports = {
118
112
  formatVariant
@@ -45,7 +45,6 @@ async function getProductStatus(skus, locale, homeUrl, awsBase, clientId) {
45
45
  const overrideMaxQuantity = marketConfig.skuMaxQty
46
46
  ? marketConfig.skuMaxQty[skus[i]]
47
47
  : undefined
48
- // @TODO - Update this code when we update our config for minimum availableQuantity in CS
49
48
  const availableQuantity =
50
49
  productStatus.availableQuantity < 999
51
50
  ? productStatus.availableQuantity