@nuskin/ns-shop 7.1.0-cx24-6991.1 → 7.1.0

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/ns-shop",
3
- "version": "7.1.0-cx24-6991.1",
3
+ "version": "7.1.0",
4
4
  "description": "The description that will amaze and astound your audience when they read it",
5
5
  "main": "src/shop.js",
6
6
  "scripts": {
@@ -26,8 +26,10 @@
26
26
  "@nuskin/ns-common-lib": "1.4.7",
27
27
  "@nuskin/ns-feature-flags": "1.4.7",
28
28
  "@nuskin/ns-loyalty-web": "1.5.6",
29
- "@nuskin/ns-product-lib": "2.17.0",
29
+ "@nuskin/ns-product-lib": "2.19.1",
30
30
  "@nuskin/nuskinjquery": "2.3.1",
31
+ "@nuskin/order-model": "3.1.3",
32
+ "@nuskin/product-lib": "2.2.1",
31
33
  "axios": "1.6.5",
32
34
  "decimal.js": "10.4.3",
33
35
  "jp-conversion": "0.0.7",
@@ -40,8 +42,8 @@
40
42
  "@nuskin/exclusive-offer-sdk": "1.2.4",
41
43
  "@nuskin/ns-account": "5.9.2",
42
44
  "@nuskin/ns-jsanalyzer": "1.0.1",
43
- "@nuskin/ns-product": "3.50.0",
44
- "@nuskin/ns-util": "4.5.4",
45
+ "@nuskin/ns-product": "3.50.5",
46
+ "@nuskin/ns-util": "4.6.1",
45
47
  "axios-mock-adapter": "1.22.0",
46
48
  "babel-cli": "6.26.0",
47
49
  "babel-core": "6.26.3",
package/src/cart/cart.js CHANGED
@@ -832,9 +832,12 @@ export default function Cart(cartData) {
832
832
  };
833
833
 
834
834
  this.updateProductEventPrice = function(item, activeEvents) {
835
+ let eventNameAdded = false;
835
836
  const user = UserService.getUser();
836
837
  const priceType = ShopContext.getPriceType(item.isAdr(), user);
838
+ const eventName = item.product.eventName;
837
839
  if (item.product.setPriceAndPvFromType(priceType, activeEvents)) {
840
+ if (!eventName && item.product.eventName) eventNameAdded = true;
838
841
  SalesEventService.logAnalyticInfo(
839
842
  'product-price-to-event-price',
840
843
  item.product.eventName,
@@ -848,15 +851,20 @@ export default function Cart(cartData) {
848
851
  }
849
852
  });
850
853
  }
854
+ return eventNameAdded;
851
855
  };
852
856
 
853
857
  this.updateEventPrices = function(activeEvents) {
858
+ let eventNameAdded = false;
854
859
  getCartItems({cartItems: true}).forEach(item => {
855
- this.updateProductEventPrice(item, activeEvents);
860
+ if (this.updateProductEventPrice(item, activeEvents))
861
+ eventNameAdded = true;
856
862
  });
863
+ return eventNameAdded;
857
864
  };
858
865
 
859
866
  this.setMissingEventNames = async (order) => {
867
+ let cartUpdated = false;
860
868
  const orderItems = this.getItems({cartOrderItems: true});
861
869
  let nameSet = new Set();
862
870
  let itemsChecked = [];
@@ -875,6 +883,7 @@ export default function Cart(cartData) {
875
883
  if (itemEvents.includes(ien)) {
876
884
  // set this items eventName if another item already has it set.
877
885
  item.product.eventName = ien;
886
+ cartUpdated = true;
878
887
  }
879
888
  return !!item.product.eventName; // causes loop to stop if true
880
889
  });
@@ -896,6 +905,7 @@ export default function Cart(cartData) {
896
905
  itemEvents.some((ie) => {
897
906
  if (eventStatuses[ie]) {
898
907
  // set the status if event is active.
908
+ cartUpdated = true;
899
909
  item.product.eventName == ie;
900
910
  SalesEventService.logAnalyticInfo(
901
911
  'cart-eventname-fixed', ie,
@@ -906,6 +916,7 @@ export default function Cart(cartData) {
906
916
  })
907
917
  });
908
918
  }
919
+ return cartUpdated;
909
920
  }
910
921
 
911
922
  this.getCartInfo = function(options) {
@@ -34,6 +34,7 @@ export default {
34
34
  hasItems,
35
35
  hasEventItems,
36
36
  hasPreviewItems,
37
+ hasDangerousGoods,
37
38
  getItemCnt,
38
39
  addProductToCart,
39
40
  addSkuToCart,
@@ -229,10 +230,23 @@ function updateItemPrices() {
229
230
  }
230
231
 
231
232
  async function checkForMissingEventNames(order) {
233
+ let outOfSync = false;
232
234
  await awaitForConfig();
233
235
  const cart = _getCart();
234
- await cart.setMissingEventNames(order);
236
+
237
+ // check to see if cart item has eventName but sapItem does not.
238
+ const orderItems = cart.getItems({cartOrderItems: true});
239
+ orderItems.map((item) => {
240
+ const sapItem = order.sapItems.find((sapItem) => (
241
+ sapItem.sku === item.product.sku && sapItem.isAdr === item.adr
242
+ ));
243
+ if (sapItem && item.product.eventName && !sapItem.eventName)
244
+ outOfSync = true;
245
+ });
246
+
247
+ let updated = await cart.setMissingEventNames(order);
235
248
  setCart(cart);
249
+ return updated || outOfSync;
236
250
  }
237
251
 
238
252
  function getCartProperty(key) {
@@ -285,6 +299,14 @@ function hasPreviewItems() {
285
299
  return _getCart().hasPreviewItems();
286
300
  }
287
301
 
302
+ function hasDangerousGoods(options = {cartOrderItems: true}) {
303
+ let retVal = false;
304
+ _getCart().getItems(options).forEach((item) => {
305
+ retVal = retVal || item.product.dangerousGoods;
306
+ });
307
+ return retVal;
308
+ }
309
+
288
310
  function getItemCnt(options) {
289
311
  return _getCart().getItemCnt(options);
290
312
  }
@@ -354,10 +376,11 @@ async function addProductToCart(options) {
354
376
  }
355
377
 
356
378
  options.product.isBackOrdered = (options.product.inventory === "BACKORDER") ? true : false
357
-
379
+
358
380
  const language = options.product.lang || options.product.language;
359
381
  options.product.lang = language;
360
382
  options.product.language = language;
383
+ const eventName = options.product.eventName;
361
384
 
362
385
  let itemKey = cart.addProduct({
363
386
  id: options.id,
@@ -377,6 +400,8 @@ async function addProductToCart(options) {
377
400
  });
378
401
 
379
402
  const item = itemKey ? cart.getItemByKey(itemKey) : null;
403
+ // eventName would get nulled out if sapItem
404
+ item.product.eventName = eventName;
380
405
  setCart(cart, item ? !item.isSapLineItem() : true);
381
406
  return itemKey;
382
407
  }
@@ -489,7 +514,10 @@ async function getAddToCartQty(product) {
489
514
  retVal = product.maxQuantity - cartQty;
490
515
  if (product.isExclusive) {
491
516
  try {
492
- const qual = await QualificationService.getQualification(product.sku)
517
+ const qual = await QualificationService.getQualification(product.sku);
518
+ if (!qual) {
519
+ return 0;
520
+ }
493
521
  const productIdentifier = qual.productId || qual.sku;
494
522
  cartQty = await getBaseSkuCartQuantity(productIdentifier)
495
523
  retVal = product.maxQuantity - cartQty;
@@ -1407,8 +1435,10 @@ const cartServiceConfigCallback = async (configs) => {
1407
1435
  if (_activeEvents != undefined) {
1408
1436
  activeEvents = _activeEvents;
1409
1437
  const cart = _getCart();
1410
- cart.updateEventPrices(activeEvents)
1411
- setCart(cart);
1438
+ if (cart.updateEventPrices(activeEvents)) {
1439
+ events.setValue(events.shop.CART_EVENT_PRICE_UPDATED, [activeEvents]);
1440
+ setCart(cart);
1441
+ }
1412
1442
  }
1413
1443
  }, true);
1414
1444
  events.publish(events.salesevent.POLL_MARKET_CONFIG);
@@ -0,0 +1,274 @@
1
+ import {productLib} from '@nuskin/product-lib';
2
+ import {RunConfigService, events, UrlService, BrowserDetection} from '@nuskin/ns-util';
3
+ import { getConfiguration } from '@nuskin/configuration-sdk';
4
+ import {AccountManager} from '@nuskin/ns-account';
5
+ import {PriceType, Product} from '@nuskin/ns-product-lib';
6
+
7
+ const extractProductData = (data, sku) => {
8
+ let retVal;
9
+
10
+ if (Array.isArray(data.variants) && data.variants.length > 0) {
11
+ retVal = data.variants.find((variant) => variant.sku === sku) || data.variants[0];
12
+ retVal.variantSelectLabel = data.variantSelectLabel;
13
+ } else if (data.bundle) {
14
+ retVal = data.bundle;
15
+ retVal.title = data.title;
16
+ retVal.productImages = data.productImages;
17
+ }
18
+
19
+ return retVal;
20
+ }
21
+
22
+ const addPrice = (productData, priceTypeKey, subKey, priceValue) => {
23
+ if (!['Points', 'cv', 'price', 'pv'].includes(priceTypeKey)) {
24
+ // if the priceTypeKey is not one of these, then we don't want it to show.
25
+ return;
26
+ }
27
+ if (priceTypeKey === 'pv') {
28
+ priceTypeKey = 'psv';
29
+ }
30
+ if (priceTypeKey === 'cv') {
31
+ priceTypeKey = 'csv';
32
+ }
33
+
34
+ if (productData[priceTypeKey] === undefined) {
35
+ if (priceTypeKey === 'Points') {
36
+ if (!productData.price) {
37
+ productData.price = {};
38
+ }
39
+ productData.price.PTS = priceValue;
40
+ return;
41
+ } else {
42
+ productData[priceTypeKey] = {};
43
+ }
44
+ }
45
+ productData[priceTypeKey][subKey] = priceValue;
46
+ }
47
+
48
+ const convertPricing = (pricing, productData) => {
49
+ if (pricing) {
50
+ Object.keys(pricing).forEach((channelKey) => {
51
+ Object.keys(pricing[channelKey]).forEach((contextKey) => {
52
+ Object.keys(pricing[channelKey][contextKey]).forEach((priceTypeKey) => {
53
+ const priceValue = pricing[channelKey][contextKey][priceTypeKey];
54
+ switch (channelKey) {
55
+ case 'wholesale':
56
+ switch (contextKey) {
57
+ case 'webOrder':
58
+ addPrice(productData, priceTypeKey, 'WWHL', priceValue);
59
+ break;
60
+ case 'webAdr':
61
+ case 'WADW':
62
+ case 'ADR5':
63
+ addPrice(productData, priceTypeKey, 'WADW', priceValue);
64
+ break;
65
+ case 'order':
66
+ case 'WHL':
67
+ addPrice(productData, priceTypeKey, 'WHL', priceValue);
68
+ break;
69
+ case 'adr':
70
+ addPrice(productData, priceTypeKey, 'ADW', priceValue);
71
+ break;
72
+ case 'ADR10':
73
+ case 'WADWD':
74
+ addPrice(productData, priceTypeKey, 'WADWD', priceValue);
75
+ break;
76
+ default:
77
+ addPrice(
78
+ productData,
79
+ priceTypeKey,
80
+ `${contextKey}-WWHL`,
81
+ priceValue
82
+ );
83
+ break;
84
+ }
85
+ break;
86
+ case 'retail':
87
+ switch (contextKey) {
88
+ case 'webOrder':
89
+ addPrice(productData, priceTypeKey, 'WRTL', priceValue);
90
+ break;
91
+ case 'webAdr':
92
+ addPrice(productData, priceTypeKey, 'WADR', priceValue);
93
+ break;
94
+ case 'order':
95
+ case 'RTL':
96
+ addPrice(productData, priceTypeKey, 'RTL', priceValue);
97
+ break;
98
+ case 'adr':
99
+ addPrice(productData, priceTypeKey, 'ADR', priceValue);
100
+ break;
101
+ default:
102
+ addPrice(
103
+ productData,
104
+ priceTypeKey,
105
+ `${contextKey}-WRTL`,
106
+ priceValue
107
+ );
108
+ break;
109
+ }
110
+ break;
111
+ }
112
+ });
113
+ });
114
+ });
115
+ }
116
+ }
117
+
118
+ const createDataFromSkuSearch = (searchData, options) => {
119
+ if (!searchData) {
120
+ return new Product();
121
+ }
122
+ let product = new Product();
123
+
124
+ if (searchData) {
125
+ product.sku = options.sku;
126
+ product.country = options.country;
127
+ product.lang = options.language;
128
+ product.isExclusive = searchData.isExclusive;
129
+ if (Array.isArray(searchData.productImages) && searchData.productImages.length > 0) {
130
+ product.setFullImage(searchData.productImages[0].url);
131
+ product.setThumbnail(searchData.productImages[0].thumbnail);
132
+ product.setImageAltText(searchData.productImages[0].altText);
133
+ // product.setProductCarouselImages(???searchData.contents.imageCarousel);
134
+ }
135
+ // product.productLabels = ???
136
+ product.title = searchData.title;
137
+ product.shortDescr = searchData.description;
138
+ // product.longDescr = langObj['longDescription']; looks like it is deprecated
139
+ product.dangerousGoods = searchData.dangerousGoods === true;
140
+ product.ingredients = searchData.ingredients;
141
+ product.benefits = searchData.benefits;
142
+ product.usage = searchData.usage;
143
+ product.resources = searchData.resources;
144
+ // product.videos = searchData.???;
145
+ // product.contentSection =searchData.???;
146
+ // product.sizeWeight = ???;
147
+ product.size = searchData.size;
148
+ product.nettoWeight = searchData.nettoWeight;
149
+ product.salesLabel = searchData.salesLabel;
150
+ // product.movie = searchData.???;
151
+ // product.youtube = searchData.???;
152
+ product.salesEventText = searchData.salesText;
153
+ product.scanQualified = searchData.scanQualifiedCount;
154
+ // product.division = searchData.???
155
+ product.custTypes = Array.isArray(searchData.customerTypes) && searchData.customerTypes.map((ct) => {
156
+ if (ct === 'Preferred') return '30';
157
+ else if (ct === 'Retail') return '20';
158
+ else if (ct === '') return '10';
159
+ });
160
+ product.restrictedMarkets = searchData.restrictedMarkets;
161
+ // if (addOns) {
162
+ // product.addOns = addOns;
163
+ // } else {
164
+ // product.addOns = [];
165
+ // }
166
+ if (options.isGroupOffer) product.isGroupOffer = options.isGroupOffer;
167
+ if (options.isPersonalOffer) product.isPersonalOffer = options.isPersonalOffer;
168
+
169
+ product.variantsLabel = searchData.variantLabel;
170
+ product.variantDropdownLabel = searchData.variantSelectLabel;
171
+ product.shade = searchData.variantColor;
172
+
173
+ const pricing = JSON.parse(searchData.pricingJson);
174
+ const orderTypes = [];
175
+ if (searchData.purchaseTypes.buyOnce) orderTypes.push('order');
176
+ if (searchData.purchaseTypes.subscription) orderTypes.push('adr');
177
+ let status = {
178
+ sku: options.sku,
179
+ globalProductId: searchData.globalId,
180
+ status: searchData.status.status === 'sellable' ? 'RELEASED_FOR_SALE' : 'NOT_RELEASED_FOR_SALE',
181
+ availableQuantity: searchData.availableQuantity,
182
+ maxQuantity: searchData.maxQuantity,
183
+ locallyProduced: searchData.locallyProduced, // ???
184
+ backorderedAvailableDate: searchData.status.isBackordered > 0 ? searchData.status.backorderedAvailableDate : null,
185
+ orderType: orderTypes, // searchData.channels.map((channel) => channel.toLowerCase()),
186
+ childSkus: null // ???
187
+ };
188
+ convertPricing(pricing, status);
189
+ product.addPricingFromStatus(status);
190
+ }
191
+
192
+ // Note: this mapping was originally created for the product data in AEM. This product
193
+ // data comes from content stack and is not identical to what is in AEM. There may
194
+ // need to be some tweaks in here. ESPECIALLY when the data gets moved to the
195
+ // bundle field (vs variants) in the product data.
196
+
197
+ return product;
198
+ }
199
+
200
+ const handleDetailResponse = async (responseData, options) => {
201
+ let retVal = {success: true},
202
+ dataForProduct = createDataFromSkuSearch(responseData, options);
203
+
204
+ if (dataForProduct.sku) {
205
+ retVal.product = new Product(dataForProduct);
206
+
207
+ const siteUrl = UrlService.getSiteUrl();
208
+ const isEdge = BrowserDetection.isEdge();
209
+ const isFirefox = BrowserDetection.isFirefox();
210
+
211
+ // set the base url for images, etc
212
+ retVal.product.setBaseUrl(siteUrl);
213
+
214
+ // fix image paths for non-edge/firefox
215
+ if (!isEdge && !isFirefox) {
216
+ retVal.product.fullImage = retVal.product.fullImage
217
+ ? `${retVal.product.fullImage.split("?")[0]}?format=pjpg`
218
+ : undefined;
219
+ retVal.product.thumbnail = retVal.product.thumbnail
220
+ ? `${retVal.product.thumbnail.split("?")[0]}?format=pjpg`
221
+ : undefined;
222
+ }
223
+ } else {
224
+ retVal.success = false;
225
+ retVal.failedSku = options.sku;
226
+ }
227
+
228
+ if (retVal.product && options.priceType) {
229
+ const {Cart: cartCfg} = await getConfiguration(['Cart']);
230
+ const priceType = cartCfg.showWholeSalePricing && !AccountManager.isLoggedIn()
231
+ ? PriceType.WWHL
232
+ : options.priceType;
233
+ retVal.product.setPriceAndPvFromType(priceType);
234
+ }
235
+
236
+ return retVal;
237
+ }
238
+
239
+ const getProductDetail = async (options) => {
240
+ let response;
241
+
242
+ // Hit the PMD and get all the data for the product including the price information.
243
+ try {
244
+ response = await productLib.getProduct({
245
+ fromId: options.sku,
246
+ env: RunConfigService.getEnvironmentCode(),
247
+ market: options.country,
248
+ language: options.language
249
+ })
250
+ } catch (e) {
251
+ throw {error: e, failedSku: options.sku, type: events.errors.PRODUCT_LOOKUP};
252
+ }
253
+
254
+ return extractProductData(response.data, options.sku);
255
+ }
256
+
257
+ const getProductBySku = async (options) => {
258
+ const data = await getProductDetail(options);
259
+
260
+ let result = await handleDetailResponse(data, options);
261
+
262
+ return result;
263
+ }
264
+
265
+ const getProductData = async (options) => {
266
+ const data = await getProductDetail(options);
267
+
268
+ return createDataFromSkuSearch(data, options);
269
+ }
270
+
271
+ export {
272
+ getProductBySku,
273
+ getProductData
274
+ }
@@ -20,7 +20,8 @@ const shoppingContextCartTypes = {
20
20
 
21
21
  const DEFAULT_CART_TYPE = 'USER';
22
22
 
23
- let cartContents = null;
23
+ let requestStatus = 'idle';
24
+ let cartPromise = null;
24
25
 
25
26
  export default {
26
27
  addProductToEquinoxCart,
@@ -223,22 +224,25 @@ function _getCartShoppingContext() {
223
224
  async function getEquinoxCart() {
224
225
 
225
226
  const cartType = _getCartShoppingContext();
226
-
227
+
227
228
  const equinoxRequestOptions = {
228
229
  method: 'GET',
229
230
  endpoint: `carts/${cartType}`
230
231
  }
231
- if (!cartContents){
232
- cartContents = _equinoxRequest(equinoxRequestOptions).then(result => {
233
- cartContents = result;
234
- });
232
+ if(requestStatus === 'pending'){
233
+ return await resolveCartPromise();
235
234
  }
235
+ if (requestStatus === 'idle'){
236
+ requestStatus = 'pending';
237
+ cartPromise = _equinoxRequest(equinoxRequestOptions);
238
+ return await resolveCartPromise();
239
+ }
240
+ }
236
241
 
237
- await cartContents
238
-
239
- // if this is a promise, this will resolve it
240
- // otherwise, it won't do anything
241
- return cartContents;
242
+ async function resolveCartPromise () {
243
+ const response = await cartPromise;
244
+ requestStatus = 'idle';
245
+ return response;
242
246
  }
243
247
 
244
248
  /**
@@ -269,7 +273,6 @@ async function getAddToCartQty(product) {
269
273
  const qualification = await QualificationService.getQualification(product.sku)
270
274
  qualifiedQuantity = qualification.isConsumable ? qualification.quantity : 0
271
275
  } catch (err) {
272
- console.error(err)
273
276
  qualifiedQuantity = 0;
274
277
  }
275
278
  }
@@ -401,6 +404,7 @@ function _assembleChildSkus(requestData, options) {
401
404
  if(personalOffer != null && productWithVariant.length > 0 && productWithVariant[0].variantSelected){
402
405
  selectedVariants = productWithVariant[0].variantSelected;
403
406
  }
407
+ if (personalOffer != null && selectedVariants && cs.skuId != selectedVariants[cs.productId] && (cs.type !== "BUNDLE" && cs.type !== "OPTIONAL")) return null
404
408
  const childSku =
405
409
  (personalOffer != null && selectedVariants && selectedVariants[cs.productId] && config.MySite_graphql_active)
406
410
  ? {
@@ -421,6 +425,7 @@ function _assembleChildSkus(requestData, options) {
421
425
 
422
426
  return childSku;
423
427
  });
428
+ requestData.skus = requestData.skus.filter(n => n)
424
429
  }
425
430
 
426
431
  return requestData;
@@ -466,7 +471,6 @@ const runConfigCallback = async (runConfig) => {
466
471
  const publishEquinoxCartCount = (options) => {
467
472
  getEquinoxCart().then((response) => {
468
473
  const cart = response.data;
469
- cartContents = null;
470
474
  events.publish(events.shop.CART_UPDATED, { cartInfo: { qty: cart.value.count }, ...options });
471
475
  });
472
476
  }
@@ -1,10 +1,10 @@
1
- import $ from '@nuskin/nuskinjquery';
2
1
  import {PriceType, Product} from '@nuskin/ns-product-lib';
3
- import {RunConfigService, util, events, BrowserDetection, UrlService} from '@nuskin/ns-util';
2
+ import {RunConfigService, events, util, BrowserDetection, UrlService} from '@nuskin/ns-util';
4
3
  import {UserService, AccountManager} from '@nuskin/ns-account';
5
4
  import ProductStatusService from '../product/ProductStatusService.js';
6
5
  import _ from 'lodash';
7
6
  import { getConfiguration } from '@nuskin/configuration-sdk';
7
+ import { getProductBySku as getCsProductBySku, getProductData as getCsProductData } from './csProductHelper.js';
8
8
  import axios from 'axios';
9
9
 
10
10
  let ProductService = function() {
@@ -21,24 +21,13 @@ let ProductService = function() {
21
21
  * @returns {null}
22
22
  */
23
23
  getProductBySku: async function(_options) {
24
- let options = verifyLocaleFields(_options),
25
- promises = [getProductDetail(options)];
26
-
27
- promises.push(getProductStatus(options));
28
-
29
- let values = await Promise.all(promises);
30
-
31
- let result = await handleDetailResponse(values[0], options);
32
-
33
- if (values[1] && values[1][0]) {
34
- handleStatusResponse(result.product, values[1][0], options);
24
+ const options = verifyLocaleFields(_options)
25
+ const {Cart: cartCfg} = await getConfiguration(['Cart']);
26
+ if (cartCfg.useContentStackProductData) {
27
+ return await getCsProductBySku(options)
35
28
  } else {
36
- // Fallback for product quantity on a failed product status call.
37
- // This is done to prevent the product detail component from locking up unnecessarily.
38
- result.product.availableQuantity = 10000;
29
+ return await legacyGetProductBySku(options);
39
30
  }
40
-
41
- return result;
42
31
  },
43
32
 
44
33
  /**
@@ -47,35 +36,34 @@ let ProductService = function() {
47
36
  * @param {string} sku - ex. 01111155
48
37
  * @returns {Promise}
49
38
  */
50
- getProductData: function(sku, country, language) {
51
- if(!sku) return;
52
- let optionsData = country ? {country} : {};
53
- if(language)
54
- optionsData.language = language;
55
-
56
- let tokenizedSku = tokenizeSku(sku),
57
- url,
58
- options = verifyLocaleFields(optionsData);
59
-
60
- url = UrlService.getSiteUrl() + "/content/products/" + tokenizedSku + "/" +
61
- options.language + ".service." + options.country + ".json";
62
-
63
- return new Promise((resolve, reject) => {
64
- $.ajax({
65
- type: 'GET',
66
- url: url,
67
- success: function(response) {
68
- resolve(createDataFromSkuSearch(response, options));
69
- },
70
- error: function(response) {
71
- console.error("There was a problem retrieving product data:", response);
72
- reject(response);
39
+ getProductData: async function(sku, country, language) {
40
+ let retVal;
41
+
42
+ if (sku) {
43
+ const optionsData = {
44
+ country: country ? country : undefined,
45
+ language: language ? language : undefined,
46
+ sku
47
+ };
48
+ const options = verifyLocaleFields(optionsData);
49
+ const {Cart: cartCfg} = await getConfiguration(['Cart']);
50
+
51
+ try {
52
+
53
+ if (cartCfg.useContentStackProductData) {
54
+ retVal = await getCsProductData(options);
55
+ } else {
56
+ const tokenizedSku = tokenizeSku(sku);
57
+ const url = UrlService.getSiteUrl() + "/content/products/" + tokenizedSku + "/" +
58
+ options.language + ".service." + options.country + ".json";
59
+ const response = await axios.get(url);
60
+ retVal = createDataFromSkuSearch(response, options);
73
61
  }
74
- });
75
- });
76
-
77
-
78
-
62
+ } catch (err) {
63
+ console.error("There was a problem retrieving product data", err);
64
+ }
65
+ }
66
+ return retVal;
79
67
  }
80
68
  };
81
69
 
@@ -84,6 +72,26 @@ let ProductService = function() {
84
72
  // Private Methods
85
73
  //
86
74
  // ---------------------------------------------
75
+ async function legacyGetProductBySku(options) {
76
+ const promises = [getProductDetail(options)];
77
+
78
+ promises.push(getProductStatus(options));
79
+
80
+ let values = await Promise.all(promises);
81
+
82
+ let result = await handleDetailResponse(values[0], options);
83
+
84
+ if (values[1] && values[1][0]) {
85
+ handleStatusResponse(result.product, values[1][0], options);
86
+ } else {
87
+ // Fallback for product quantity on a failed product status call.
88
+ // This is done to prevent the product detail component from locking up unnecessarily.
89
+ result.product.availableQuantity = 10000;
90
+ }
91
+
92
+ return result;
93
+ }
94
+
87
95
  async function getProductDetail(_options) {
88
96
  let options = _options;
89
97
  let url = await getSkuSearchUrl(options);
@@ -5,6 +5,7 @@ import {RunConfigService, events, util, PersonalOfferStorageService, BrowserDete
5
5
  import {getCachedConfigField} from '@nuskin/configuration-sdk';
6
6
  import {UserService} from "@nuskin/ns-account";
7
7
  import PaymentAdapter from '../payment/PaymentAdapter';
8
+ import pickupUtil from '../shipping/pickupUtil';
8
9
 
9
10
  let splitLineItems = [];
10
11
 
@@ -600,6 +601,7 @@ const syncProductItems = async (order, sapProducts, adr, includeSapItems) => {
600
601
  newItem.priceType = ShopContextService.getPriceType(isAdrItem);
601
602
  newItem.pv = sapItem.PSV;
602
603
  newItem.backOrderDate = sapItem.BackorderAvailableDate;
604
+ newItem.eventName = sapItem.LineItem.Event;
603
605
 
604
606
  if (sapItem.LineItem.Custom) {
605
607
  let code = null, name = null, label = null;
@@ -634,7 +636,7 @@ const syncProductItems = async (order, sapProducts, adr, includeSapItems) => {
634
636
  newItem.isBusinessPortfolio = matchedItem.isBusinessPortfolio;
635
637
  newItem.redeem = matchedItem.redeem;
636
638
  newItem.qtyRedeemWithPoints = matchedItem.qtyRedeemWithPoints;
637
- newItem.eventName = matchedItem.eventName;
639
+ newItem.eventName = !newItem.eventName ? matchedItem.eventName : newItem.eventName;
638
640
  }
639
641
  if (existingItem) {
640
642
  newItem.thumbnail = existingItem.thumbnail;
@@ -864,13 +866,14 @@ let setSelectedShipMethod = function(order, matchMethod = {}) {
864
866
 
865
867
  /**
866
868
  * Parses pickup points from SAP response, in case shipping method is set to pickup points!
867
- * @param {Object[]} custom The Custom array on the salesorderrequest
869
+ * @param {Object[]} oldPup The Custom array on the salesorderrequest
870
+ * @param {Object[]} newPup The Custom array on the salesorderrequest
868
871
  * @param {Object} order
869
872
  */
870
- function setPickupPoints(custom, order) {
871
- const pupLocations = custom || [];
873
+ function setPickupPoints(oldPup, newPup, order) {
874
+ const pupLocations = oldPup || [];
872
875
  if (order.selectedShippingMethod && PickupUtil.supports(order.selectedShippingMethod.Code)) {
873
- order.pickupPoints = PickupUtil.parse(pupLocations);
876
+ order.pickupPoints = newPup ? newPup : PickupUtil.parse(pupLocations);
874
877
  if (order.selectedPickupPoint) {
875
878
  order.selectedPickupPoint = order.pickupPoints.find(pickupPoint => order.selectedPickupPoint.id === pickupPoint.id);
876
879
  }
@@ -1089,13 +1092,19 @@ const toOrder = async (salesOrderDetail, adr, createResponse) => {
1089
1092
  }
1090
1093
 
1091
1094
  if (salesOrderDetail.ShippingAvailableMethods && salesOrderDetail.ShippingAvailableMethods.length > 0) {
1092
- order.shippingAvailableMethods = sortShippingAvailableMethods(salesOrderDetail.ShippingAvailableMethods);
1095
+ const useShipMethodsApi = getCachedConfigField('useShipMethodsApi');
1096
+ const shipMethods = useShipMethodsApi ? await pickupUtil.getShipMethods() : salesOrderDetail.ShippingAvailableMethods;
1097
+ order.shippingAvailableMethods = sortShippingAvailableMethods(shipMethods);
1093
1098
  setShippingAvailableMethodsPickupInfo(order.shippingAvailableMethods);
1094
1099
  setSelectedShipMethod(order, salesOrderDetail.Shipping.ShippingMethod);
1095
1100
  // When an order or ADR is created. The response from the create call is missing the list of pickup locations
1096
1101
  // The list isn't needed in this case since the user has already created the order so we don't need to update the pickup points
1097
1102
  if (!createResponse) {
1098
- setPickupPoints(salesOrderDetail.Custom, order);
1103
+ let oldPup = null;
1104
+ if (!useShipMethodsApi) oldPup = salesOrderDetail.Custom;
1105
+ let newPup = null;
1106
+ if (useShipMethodsApi) newPup = await pickupUtil.getPickupPoints();
1107
+ setPickupPoints(oldPup, newPup, order);
1099
1108
  }
1100
1109
  }
1101
1110
  shippingHack(order, salesOrderDetail.OrderTotals.Shipping, salesOrderDetail.Currency);
@@ -1107,6 +1116,7 @@ const toOrder = async (salesOrderDetail, adr, createResponse) => {
1107
1116
  await syncProductItems(order, salesOrderDetail.LineItemDetails, order.adr ? adr : null);
1108
1117
  syncOrderTotals(salesOrderDetail.OrderTotals, order.orderTotals);
1109
1118
  syncPayment(salesOrderDetail.Payment, order.selectedPayment);
1119
+ OrderManager.saveOrders();
1110
1120
  return order;
1111
1121
  };
1112
1122
 
@@ -142,15 +142,21 @@ const getAdrOverrideId = () => {
142
142
  const _doNewExternalPaymentProcess = (order) => {
143
143
  // We only redirect if there is a redirect Url.
144
144
  if (order.redirectUrl) {
145
+ const redirectUrl = new URL(order.redirectUrl);
145
146
  // The landing page should be the checkout page.
146
147
  const protocol = window.location.href.split('://')[0];
147
148
  const domain = UrlService.extractDomain();
148
149
  const path = encodeURIComponent(CartService.getCheckoutPage({route: 'orderSummary'}));
149
150
 
151
+ // If we are on mynuskin.com we want to keep external payment on mynuskin.com. For developing localhost we don't want to stay on localhost
152
+ if (!domain.includes('localhost')) {
153
+ redirectUrl.hostname = window.location.hostname;
154
+ }
155
+
150
156
  // Append the landing page URL
151
157
  // Example of order.redirectUrl: "redirectUrl":
152
158
  // "https://test.nuskin.com/external-payment/api/v1/redirect?orderId=NS002761827&verify=null&ofsOrderId=0149707745"
153
- window.location = `${order.redirectUrl}&landingUrl=${protocol}://${domain}${path}`;
159
+ window.location = `${redirectUrl.toString()}&landingUrl=${protocol}://${domain}${path}`;
154
160
  }
155
161
  };
156
162
 
@@ -139,69 +139,32 @@ function getActiveTicketEvents() {
139
139
  return events;
140
140
  }
141
141
 
142
- async function getEventStatus(eventsToCheck = [], externalId = 'none') {
142
+ async function getEventStatus(eventsToCheck = []) {
143
143
  const eventStatus = {};
144
144
  const promises = [];
145
145
 
146
146
  eventsToCheck.forEach((toCheck) => {
147
- promises.push(_getTempTicket(toCheck, externalId));
147
+ promises.push(_checkForMissingTicket(toCheck));
148
148
  });
149
149
 
150
- const responses = await Promise.allSettled(promises);
150
+ await Promise.allSettled(promises);
151
151
 
152
- responses.forEach((response) => {
152
+ eventsToCheck.forEach((eventName) => {
153
153
  let activeStatus = false;
154
- if (response.status === 'fulfilled' && response.value) {
155
- const status = _decodeStatus(response.value.status);
154
+ const ticketInfo = eventTicketInfoMap[eventName];
155
+ if (ticketInfo && ticketInfo.ticket) {
156
+ const status = _decodeStatus(ticketInfo.ticket.status);
156
157
  if ((status.eventStatus === PRE_EVENT && status.preWaitTime <= 30000) ||
157
158
  status.eventStatus === IN_EVENT) {
158
159
  activeStatus = true;
159
160
  }
160
- eventStatus[response.value.eventName] = activeStatus;
161
161
  }
162
+ eventStatus[eventName] = activeStatus;
162
163
  });
163
164
 
164
165
  return eventStatus;
165
166
  }
166
167
 
167
- /**
168
- * Either requests a new ticket or an updated event ticket from the AWS service
169
- *
170
- * @param eventTicketInfo - contains informatino about the ticket.
171
- *
172
- * @returns {Promise<*>} a new or updated ticket for the specified event
173
- * @private
174
- */
175
- async function _getTempTicket(eventName, externalId) {
176
- let ticket;
177
-
178
- try {
179
- let method = 'POST',
180
- seParams = _getSeParams(eventName, false),
181
- response = await axios({
182
- method: method,
183
- url: `${_getAwsUrl(eventName, null)}?orderEventCheck=${externalId}${seParams}`,
184
- headers: _getHeaders(),
185
- timeout: 7000
186
- });
187
-
188
- ticket = response.data.salesEventResponse;
189
- } catch (err) {
190
- if (err.response && err.response.data.status === 404) {
191
- console.error(`getTempTicket - unable to get a temporary ticket for ${eventName}`);
192
- throw err;
193
- } else {
194
- console.error(`getTempTicket error or timeout - waiting to try again for ${eventName}`);
195
- // Timed out or some other error
196
- // Wait and then call again.
197
- await _wait(3000);
198
- ticket = await _getTempTicket(eventName);
199
- }
200
- }
201
-
202
- return ticket;
203
- }
204
-
205
168
  function logAnalyticInfo(tag, eventName, data = {}) {
206
169
  const user = UserService.getUser();
207
170
  const eventTicketInfo = eventTicketInfoMap[eventName];
@@ -297,7 +260,7 @@ function _checkStatus(eventTicketInfo, useServerWaitTime) {
297
260
  [ticket.eventName, eventTicketInfo.paused, {fromServer: useServerWaitTime, waitTime: actualWait}]
298
261
  );
299
262
 
300
- eventInfo.timerId = setTimeout(() => _timerCallback(ticket.eventName), timerWait);
263
+ eventInfo.timerId = setTimeout(_timerCallback(ticket.eventName), timerWait);
301
264
  if (useServerWaitTime && timerWait < 30000) {
302
265
  // if the wait time is base off of what the server passed back then
303
266
  // don't call the server to update the ticket at the end of this timer
@@ -317,7 +280,7 @@ function _checkStatus(eventTicketInfo, useServerWaitTime) {
317
280
  [ticket.eventName, eventTicketInfo.paused, {fromServer: useServerWaitTime, waitTime: actualWait}]
318
281
  );
319
282
  logAnalyticInfo('in-event', ticket.eventName);
320
- eventInfo.timerId = setTimeout(() => _timerCallback(ticket.eventName), timerWait);
283
+ eventInfo.timerId = setTimeout(_timerCallback(ticket.eventName), timerWait);
321
284
  } else {
322
285
  _changeStatus(eventTicketInfo, POST_EVENT);
323
286
  _checkStatus(eventTicketInfo, false); // to send post event and clear ticket.
@@ -563,7 +526,7 @@ async function _getRemoteTicket(eventTicketInfo) {
563
526
  response = await axios({
564
527
  method: method,
565
528
  url: `${_getAwsUrl(eventName, ticketId)}${seParams}`,
566
- headers: _getHeaders(),
529
+ headers: _getHeaders(method === 'GET' ? true : false),
567
530
  timeout: 7000
568
531
  });
569
532
 
@@ -678,14 +641,26 @@ function _getAwsUrl(eventName, ticketId) {
678
641
  * @returns {{Accept: string, "Content-Type": string, client_id: *}}
679
642
  * @private
680
643
  */
681
- function _getHeaders() {
644
+ function _getHeaders(noCache = false) {
682
645
  return {
683
646
  'Accept': 'application/json',
684
647
  'Content-Type': 'application/json',
685
- 'client_id': RunConfigService.getRunConfig().clientId
648
+ 'client_id': RunConfigService.getRunConfig().clientId,
649
+ ...(noCache && {'Cache-Control': 'no-cache'}),
650
+ ...(noCache && {'Pragma': 'no-cache'}),
651
+ ...(noCache && {'Expires': '0'})
686
652
  };
687
653
  }
688
654
 
655
+ async function _getActiveEvents() {
656
+ let response = await axios({
657
+ method: 'GET',
658
+ url: `${_getAwsUrl()}?activeWithin=${ACTIVE_EVENT_WITHIN}`,
659
+ headers: _getHeaders(true)
660
+ });
661
+ return response.data.salesEventResponse
662
+ }
663
+
689
664
  //=========================================================================================================//
690
665
  //
691
666
  // Misc functions
@@ -762,7 +737,7 @@ async function _loadMarketEventConfig(force, country=undefined) {
762
737
  const response = await axios({
763
738
  method: 'GET',
764
739
  url: `${_getAwsUrl()}`,
765
- headers: _getHeaders()
740
+ headers: _getHeaders(true)
766
741
  });
767
742
  const salesEvents = response.data.salesEventResponse || [];
768
743
  mktEventConfigMap = {};
@@ -804,6 +779,12 @@ function getStorageItem(key) {
804
779
  return item;
805
780
  }
806
781
 
782
+ async function _checkForMissingTicket(eventName) {
783
+ if (mktEventConfigMap[eventName] && !eventTicketInfoMap[eventName]) {
784
+ await _initEventTicketInfo(eventName);
785
+ }
786
+ }
787
+
807
788
  /**
808
789
  * This initializes an interval timer which checks to see if the browser (computer)
809
790
  * went to sleep. It's intention is to update the wait timer if it has.
@@ -815,17 +796,18 @@ async function _initializePolling() {
815
796
  intervalCnt = 1;
816
797
 
817
798
  intervalId = setInterval(async () => {
818
- let now = Date.now();
799
+ const now = Date.now();
800
+ const slept = now > (lastTime + TIMEOUT + 3000)
819
801
 
820
802
  // if polling market config, only reload it every 30 seconds
821
- if (pollMarketConfig && intervalCnt % 3 === 0) {
803
+ if (slept || pollMarketConfig && intervalCnt % 6 === 0) {
822
804
  await _loadMarketEventConfig(true);
823
805
  _marketStatusCheck();
824
806
  }
825
807
  intervalCnt++;
826
808
 
827
809
  // if been to sleep for more then 3 seconds then check all ticket statuses.
828
- if (now > (lastTime + TIMEOUT + 3000)) {
810
+ if (slept) {
829
811
  // Went to sleep for 3 seconds or more
830
812
  Object.values(eventTicketInfoMap).forEach(eventTicketInfo => {
831
813
  // A new timer needs to be set, clear current one if exists
@@ -838,6 +820,17 @@ async function _initializePolling() {
838
820
  _checkStatus(eventTicketInfo, false);
839
821
  });
840
822
 
823
+ if (window.location.pathname === '/static/checkout/checkout.html' ||
824
+ window.location.pathname === '/static/cart/cart.html'
825
+ ) {
826
+ // check for missing tickets. Only perform on checkout screen
827
+ const activeEvents = await _getActiveEvents();
828
+
829
+ for (const eventInfo of activeEvents) {
830
+ await _checkForMissingTicket(eventInfo.eventName);
831
+ }
832
+ }
833
+
841
834
  lastTime = now;
842
835
  }, TIMEOUT);
843
836
  }
@@ -963,7 +956,7 @@ function _removeWaitCheck(value) {
963
956
  */
964
957
  async function _loadAwsActiveEvents(country) {
965
958
  let activeEventsName = `${ACTIVE_EVENTS}-${country? country: RunConfigService.getRunConfig().country}`,
966
- storageObj = getStorageItem(activeEventsName),
959
+ storageObj = null,
967
960
  ttl = storageObj ? storageObj.ttl - Date.now() : ACTIVE_EVENT_TTL,
968
961
  eventInfos = {},
969
962
  ok = true;
@@ -989,12 +982,7 @@ async function _loadAwsActiveEvents(country) {
989
982
  storageObj = {events: []};
990
983
  storage.setItem(activeEventsName, storageObj, {ttl: 250}); // to prevent multiple calls.
991
984
  try {
992
- let response = await axios({
993
- method: 'GET',
994
- url: `${_getAwsUrl()}?activeWithin=${ACTIVE_EVENT_WITHIN}`,
995
- headers: _getHeaders()
996
- });
997
- addEvents(response.data.salesEventResponse);
985
+ addEvents(await _getActiveEvents());
998
986
  } catch (e) {
999
987
  // an error occurred with the call, clear active events
1000
988
  // so it won't wait 30 minutes to try again
@@ -1027,6 +1015,14 @@ async function initActiveEvents(country = undefined) {
1027
1015
  if (Object.values(mktEventConfigMap).find(mktEvent => {return mktEvent.status !== 'off'})) {
1028
1016
  let awsEvents = await _loadAwsActiveEvents(country);
1029
1017
 
1018
+ // if a ticket exists in localStorage for an event the is not active, remove it.
1019
+ Object.keys(eventTicketInfoMap).forEach((key) => {
1020
+ if (!awsEvents[key]) {
1021
+ storage.removeItem(_getTicketName(key));
1022
+ delete eventTicketInfoMap[key];
1023
+ }
1024
+ })
1025
+
1030
1026
  eventTicketInfoMap = Object.assign({}, awsEvents, eventTicketInfoMap);
1031
1027
  Object.keys(eventTicketInfoMap).forEach(eventName => {
1032
1028
  if (mktEventConfigMap[eventName] && eventTicketInfoMap[eventName].eventName) {
@@ -1036,12 +1032,10 @@ async function initActiveEvents(country = undefined) {
1036
1032
  }
1037
1033
 
1038
1034
  return Promise.all(promises).then(async () => {
1035
+
1039
1036
  events.setValue(events.salesevent.INITIAL_ACTIVE_EVENTS, [Object.keys(activeEventMap)]);
1040
1037
 
1041
- // If there are event tickets then initialize polling
1042
- if (Object.keys(eventTicketInfoMap).length > 0 && !country) {
1043
- await _initializePolling();
1044
- }
1038
+ await _initializePolling();
1045
1039
  _removeWaitCheck(INITIALIZING);
1046
1040
  });
1047
1041
  }
@@ -1097,13 +1091,7 @@ async function _init(force = true) {
1097
1091
  * If there are, make sure polling is happening, otherwise, stop polling.
1098
1092
  */
1099
1093
  events.subscribe(events.salesevent.ACTIVE_EVENTS, (/*activeEvents*/) => {
1100
- if (eventTicketInfoMap) {
1101
- if (Object.keys(eventTicketInfoMap).length > 0) {
1102
- _initializePolling().then(() => console.log('Polling initialized'));
1103
- } else {
1104
- _clearPollingInterval();
1105
- }
1106
- }
1094
+ _initializePolling().then(() => console.log('Polling initialized'));
1107
1095
  });
1108
1096
 
1109
1097
  // persistentCartService needs to wait until the following configMaps are loaded
@@ -1,3 +1,9 @@
1
+ import {getCachedConfiguration} from '@nuskin/configuration-sdk';
2
+ import {Order} from '@nuskin/order-model';
3
+ import {RunConfigService} from '@nuskin/ns-util';
4
+ import {OrderManager, CartService, OrderType, OrderAdapter} from '../shop';
5
+ import axios from 'axios';
6
+
1
7
  let PickupUtil = function() {
2
8
  'use strict';
3
9
  const SERVICE_GROUP = 'SERVICE_GROUP',
@@ -15,6 +21,10 @@ let PickupUtil = function() {
15
21
  UNIT = 'UNIT',
16
22
  CARRIER_CODE = 'CARRIER_CODE';
17
23
 
24
+ let promise = Promise.resolve();
25
+ let shipMethods = [];
26
+ let pickupPoints = [];
27
+
18
28
  /**
19
29
  * Pickup Point shipping methods.
20
30
  * Values are shipping method codes.
@@ -69,7 +79,7 @@ let PickupUtil = function() {
69
79
  distance: distances && distances[i] ? distances[i].Value : undefined,
70
80
  locker: lockers && lockers[i] ? lockers[i].Value : undefined,
71
81
  unit: units && units[i] ? units[i].Value : undefined,
72
- openingHours: openHours && openHours[i] ? openHours[i].Value.split(/\s*;\s*/) : undefined,
82
+ openingHours: openHours && openHours[i] ? openHours[i].Value : undefined,
73
83
  latitude: latitudes && latitudes[i] ? parseFloat(latitudes[i].Value) : undefined,
74
84
  longitude: longitudes && longitudes[i] ? parseFloat(longitudes[i].Value) : undefined,
75
85
  carrierCode: carrierCodes && carrierCodes[i] ? carrierCodes[i].Value : undefined
@@ -130,12 +140,96 @@ let PickupUtil = function() {
130
140
  return markers;
131
141
  }
132
142
 
143
+ function getOrderValue() {
144
+ let value = 0;
145
+ const orderType = OrderManager.getOrder().orderType;
146
+ const cartInfo = CartService.getCartInfo();
147
+
148
+ if (orderType === OrderType.SINGLE_ORDER) {
149
+ value = cartInfo.totalOrderPrice;
150
+ } else {
151
+ value = cartInfo.totalOrderPrice + cartInfo.totalAdrPrice;
152
+ }
153
+
154
+ return value;
155
+ }
156
+
157
+ function getUrl() {
158
+ switch (RunConfigService.getEnvironmentCode()) {
159
+ case 'dev':
160
+ return 'https://ship-methods.api.dev.nuskin.com/v1';
161
+ case 'test':
162
+ return 'https://ship-methods.api.test.nuskin.com/v1';
163
+ case 'stage':
164
+ return 'https://ship-methods.api.dev.nuskin.com/v1';
165
+ case 'prod':
166
+ return 'https://ship-methods.api.nuskin.com/v1';
167
+ default:
168
+ return 'https://ship-methods.api.test.nuskin.com/v1';
169
+ }
170
+ }
171
+
172
+ function loadShipMethods() {
173
+ const order = Order.fromSalesOrderRequest(OrderAdapter.populateSalesOrder('SIMULATE'));
174
+
175
+ const postalCode = order.shippingPostalCode;
176
+ const {metapackMarket, metapackZipExclusions} = getCachedConfiguration('Checkout');
177
+
178
+ if (metapackMarket) {
179
+ let includePickupPoints = true;
180
+ for (const zipRegEx of metapackZipExclusions || []) {
181
+ const regEx = new RegExp(zipRegEx);
182
+ includePickupPoints = includePickupPoints && !postalCode.match(regEx);
183
+ }
184
+
185
+ const runConfig = RunConfigService.getRunConfig();
186
+ promise = axios.post(
187
+ getUrl(),
188
+ {
189
+ order,
190
+ pudo: includePickupPoints,
191
+ orderValue: getOrderValue(),
192
+ dangerousGoods: CartService.hasDangerousGoods(),
193
+ language: runConfig.language,
194
+ country: runConfig.country
195
+ },
196
+ {
197
+ headers: {
198
+ 'Content-Type': 'application/json; charset=UTF-8'
199
+ }
200
+ }
201
+ ).then((results) => {
202
+ shipMethods = results.data.shipMethods;
203
+ pickupPoints = results.data.pickupPoints;
204
+ }).catch((err) => {
205
+ console.log(err);
206
+ shipMethods = [];
207
+ pickupPoints = [];
208
+ });
209
+ }
210
+ }
211
+
212
+ async function getShipMethods() {
213
+ await promise;
214
+
215
+ return shipMethods;
216
+ }
217
+
218
+ async function getPickupPoints() {
219
+ await promise;
220
+
221
+ return pickupPoints;
222
+ }
223
+
133
224
  return {
134
225
  allMethods: shippingMethods,
135
226
  supports: isPickupPointShippingMethod,
136
227
  parse: parsePickupPoints,
137
228
  bounds: getBounds,
138
- markers: generateMarkers
229
+ markers: generateMarkers,
230
+ loadShipMethods,
231
+ getShipMethods,
232
+ getPickupPoints
139
233
  }
140
234
  }();
141
235