@ordergroove/offers 2.47.2 → 2.48.1-alpha-PR-1357-7.8

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.
@@ -42,16 +42,18 @@ import type {
42
42
  } from '../core/types/reducer';
43
43
 
44
44
  import { sellingPlanAllocationsReducer, getSellingPlans } from './reducers/productPlans';
45
- import config from './reducers/config';
45
+ import config, { getPrepaidSellingPlans } from './reducers/config';
46
46
  import { experimentsReducer } from '../core/experiments';
47
47
  import {
48
48
  getPayAsYouGoSellingPlanGroup,
49
49
  getPayAsYouGoSellingPlanGroups,
50
50
  sellingPlansToEveryPeriod,
51
51
  sellingPlansToFrequencies,
52
- getPrepaidShipments
52
+ getPrepaidShipments,
53
+ getDefaultPrepaidOption
53
54
  } from './utils';
54
55
  import { EmptyObject } from '../core/types/utility';
56
+ import { ELIGIBILITY_GROUPS } from '../core/constants';
55
57
 
56
58
  const overrideLineKey = (state, productId, newValue) => {
57
59
  const keys = Object.keys(state).filter(it => it.startsWith(productId.toString()));
@@ -116,39 +118,76 @@ export const mapExistingOptinsFromOfferResponse = (
116
118
  });
117
119
 
118
120
  export const reduceNewOptinsFromOfferResponse = (
119
- { autoship = {}, autoship_by_default = {}, default_frequencies = {}, in_stock = {} }: ReceiveOfferPayload,
121
+ {
122
+ autoship = {},
123
+ autoship_by_default = {},
124
+ default_frequencies = {},
125
+ in_stock = {},
126
+ eligibility_groups = {}
127
+ }: ReceiveOfferPayload,
120
128
  existingOptins: OptedInState,
121
129
  offerEl: OfferElement | EmptyObject,
122
- frequencyConfig: ReceiveOfferPayload['frequencyConfig']
130
+ frequencyConfig: ReceiveOfferPayload['frequencyConfig'],
131
+ prepaidSellingPlans: ReceiveOfferPayload['prepaidSellingPlans']
123
132
  ) =>
124
133
  Object.keys(autoship).reduce((acc, id) => {
125
- if (!existingOptins.some(it => it.id === id)) {
126
- if (!(autoship[id] && autoship_by_default[id] && in_stock[id])) return acc;
127
- const { frequencies: sellingPlans, frequenciesEveryPeriod } = frequencyConfig;
128
- const { defaultFrequency } = offerEl || {};
129
- const psdf = default_frequencies[id];
130
- let frequency;
131
-
132
- if (default_frequencies[id] && hasShopifySellingPlans(sellingPlans, frequenciesEveryPeriod)) {
133
- frequency =
134
- mapFrequencyToSellingPlan(sellingPlans, frequenciesEveryPeriod, `${psdf.every}_${psdf.every_period}`) ||
135
- getDefaultSellingPlan(sellingPlans, frequenciesEveryPeriod, defaultFrequency) ||
136
- getFirstSellingPlan(sellingPlans);
137
- } else if (default_frequencies[id]) {
138
- frequency = `${psdf.every}_${psdf.every_period}`;
139
- } else {
140
- frequency = getDefaultSellingPlan(sellingPlans, frequenciesEveryPeriod, defaultFrequency) || '_'; // Placeholder to be backfilled in SETUP_PRODUCT reducer
134
+ // if the user is not opted in and the product is set to default to subscription
135
+ if (!existingOptins.some(it => it.id === id) && autoship_by_default[id] && in_stock[id]) {
136
+ if (autoship[id]) {
137
+ return acc.concat({
138
+ id,
139
+ frequency: getOptInDefaultFrequency({
140
+ frequencyConfig,
141
+ offerEl,
142
+ default_frequencies,
143
+ id
144
+ })
145
+ });
146
+ } else if (eligibility_groups[id]?.includes(ELIGIBILITY_GROUPS.PREPAID)) {
147
+ // if the product is prepaid eligible but not subscription eligible, opt them into a prepaid subscription
148
+ const prepaidPlan = prepaidSellingPlans ? getDefaultPrepaidOption(prepaidSellingPlans) : null;
149
+ return acc.concat({
150
+ id,
151
+ // we might not have a prepaid plan yet if the Shopify product request hasn't completed (SETUP_PRODUCT)
152
+ frequency: prepaidPlan?.sellingPlan || PREPAID_PLACEHOLDER,
153
+ prepaidShipments: prepaidPlan?.numberShipments || null
154
+ });
141
155
  }
142
-
143
- return acc.concat({
144
- id,
145
- frequency
146
- });
147
156
  }
148
157
 
149
158
  return acc;
150
159
  }, []);
151
160
 
161
+ const getOptInDefaultFrequency = ({
162
+ frequencyConfig,
163
+ offerEl,
164
+ default_frequencies,
165
+ id
166
+ }: {
167
+ frequencyConfig: ReceiveOfferPayload['frequencyConfig'];
168
+ offerEl: OfferElement | EmptyObject;
169
+ default_frequencies: ReceiveOfferPayload['default_frequencies'];
170
+ id: string;
171
+ }) => {
172
+ const { frequencies: sellingPlans, frequenciesEveryPeriod } = frequencyConfig;
173
+ const { defaultFrequency } = offerEl || {};
174
+ const psdf = default_frequencies[id];
175
+ let frequency;
176
+
177
+ if (default_frequencies[id] && hasShopifySellingPlans(sellingPlans, frequenciesEveryPeriod)) {
178
+ frequency =
179
+ mapFrequencyToSellingPlan(sellingPlans, frequenciesEveryPeriod, `${psdf.every}_${psdf.every_period}`) ||
180
+ getDefaultSellingPlan(sellingPlans, frequenciesEveryPeriod, defaultFrequency) ||
181
+ getFirstSellingPlan(sellingPlans);
182
+ } else if (default_frequencies[id]) {
183
+ frequency = `${psdf.every}_${psdf.every_period}`;
184
+ } else {
185
+ frequency = getDefaultSellingPlan(sellingPlans, frequenciesEveryPeriod, defaultFrequency) || '_'; // Placeholder to be backfilled in SETUP_PRODUCT reducer
186
+ }
187
+
188
+ return frequency;
189
+ };
190
+
152
191
  const productOrVariantInStockReducer = (acc, cur) => ({
153
192
  ...overrideLineKey(acc, cur.id, cur.available),
154
193
  [cur.id]: cur.available
@@ -234,6 +273,7 @@ function getOptedInItem(cartItem) {
234
273
  return item;
235
274
  }
236
275
 
276
+ const PREPAID_PLACEHOLDER = 'prepaid-replace-me';
237
277
  export const optedin = (state: OptedInState = [], action): OptedInState => {
238
278
  if (constants.SETUP_CART === action.type) {
239
279
  const cart = action.payload;
@@ -244,9 +284,15 @@ export const optedin = (state: OptedInState = [], action): OptedInState => {
244
284
 
245
285
  if (constants.RECEIVE_OFFER === action.type) {
246
286
  const payload = action.payload as ReceiveOfferPayload;
247
- const { offer: offerEl = {}, frequencyConfig } = payload;
287
+ const { offer: offerEl = {}, frequencyConfig, prepaidSellingPlans } = payload;
248
288
  const existingOptins = mapExistingOptinsFromOfferResponse(state, offerEl, frequencyConfig);
249
- const newOptins = reduceNewOptinsFromOfferResponse(payload, existingOptins, offerEl, frequencyConfig);
289
+ const newOptins = reduceNewOptinsFromOfferResponse(
290
+ payload,
291
+ existingOptins,
292
+ offerEl,
293
+ frequencyConfig,
294
+ prepaidSellingPlans
295
+ );
250
296
 
251
297
  return [...existingOptins, ...newOptins];
252
298
  }
@@ -254,24 +300,33 @@ export const optedin = (state: OptedInState = [], action): OptedInState => {
254
300
  if (constants.SETUP_PRODUCT === action.type) {
255
301
  const { product } = action.payload;
256
302
  const sellingPlanGroup = getPayAsYouGoSellingPlanGroup(product?.selling_plan_groups);
257
- if (!sellingPlanGroup) {
258
- return state;
259
- }
260
- const frequencies = sellingPlansToFrequencies(sellingPlanGroup);
261
- const frequenciesEveryPeriod = sellingPlansToEveryPeriod(sellingPlanGroup);
262
-
263
- return state.map(curr => {
264
- if (isOgFrequency(curr.frequency)) {
265
- return {
266
- ...curr,
267
- frequency:
268
- mapFrequencyToSellingPlan(frequencies, frequenciesEveryPeriod, curr.frequency) ||
269
- getFirstSellingPlan(frequencies)
270
- };
271
- }
303
+ const prepaidSellingPlans = getPrepaidSellingPlans(product);
304
+ const frequencies = sellingPlanGroup ? sellingPlansToFrequencies(sellingPlanGroup) : [];
305
+ const frequenciesEveryPeriod = sellingPlanGroup ? sellingPlansToEveryPeriod(sellingPlanGroup) : [];
272
306
 
273
- return curr;
274
- });
307
+ return state
308
+ .map(curr => {
309
+ const prepaidSellingPlansForVariant = prepaidSellingPlans[curr.id];
310
+ if (sellingPlanGroup && isOgFrequency(curr.frequency)) {
311
+ return {
312
+ ...curr,
313
+ frequency:
314
+ mapFrequencyToSellingPlan(frequencies, frequenciesEveryPeriod, curr.frequency) ||
315
+ getFirstSellingPlan(frequencies)
316
+ };
317
+ } else if (curr.frequency === PREPAID_PLACEHOLDER && prepaidSellingPlansForVariant?.length > 0) {
318
+ // if we opted the user into a prepaid plan on RECEIVE_OFFER and now have the actual selling plan information, update it
319
+ const { sellingPlan, numberShipments } = getDefaultPrepaidOption(prepaidSellingPlansForVariant);
320
+ return {
321
+ ...curr,
322
+ frequency: sellingPlan,
323
+ prepaidShipments: numberShipments
324
+ };
325
+ }
326
+
327
+ return curr;
328
+ })
329
+ .filter(i => i.frequency !== PREPAID_PLACEHOLDER); // remove any leftover placeholders
275
330
  }
276
331
 
277
332
  if (constants.PRODUCT_CHANGE_PREPAID_SHIPMENTS === action.type) {
@@ -77,3 +77,8 @@ export function getPrepaidShipments(sellingPlan) {
77
77
  const shipments = sellingPlan?.options.find(({ name }) => name === 'Shipment amount')?.value.split(' ')[0];
78
78
  return shipments ? Number(shipments) : undefined;
79
79
  }
80
+
81
+ export function getDefaultPrepaidOption<T>(options: T[]): T {
82
+ // prefer the second plan by default, which has more prepaid shipments
83
+ return options[1] || options[0];
84
+ }