diff --git a/src/helpers/item.ts b/src/helpers/item.ts index 5d7d9a1..f399846 100644 --- a/src/helpers/item.ts +++ b/src/helpers/item.ts @@ -174,8 +174,18 @@ export class Item { let selectedValueCount = (selectedModifier)?.textEntry?.length || 0; if ((selectedModifier)?.choiceSelections?.length) { // Invalid or sold out choices - const invalidChoice = (selectedModifier).choiceSelections.find(s => !modifierList.modifiers?.find(m => m.id === s)); - const soldOutChoice = (selectedModifier).choiceSelections.find(s => modifierList.modifiers?.find(m => m.id === s)?.sold_out); + const invalidChoice = (selectedModifier).choiceSelections.find(s => !modifierList.modifiers?.find(m => { + if (typeof s === 'object') { + return m.id === s.id; + } + return m.id === s; + })); + const soldOutChoice = (selectedModifier).choiceSelections.find(s => modifierList.modifiers?.find(m => { + if (typeof s === 'object') { + return m.id === s.id; + } + return m.id === s; + })?.sold_out); if (invalidChoice || soldOutChoice) { return false; } @@ -322,9 +332,18 @@ export class Item { const matchingModifierList = item.modifier_lists?.find(l => l.id === selectedModifier.id); if (matchingModifierList) { matchingModifierList.modifiers?.forEach(m => { - if (selectedModifier.choiceSelections.includes(m.id) && m.price_money) { - regularPrice += m.price_money.amount; - salePrice += m.price_money.amount; + let quantity = 1; + if (selectedModifier.choiceSelections.find(s => { + if (typeof s === 'object') { + if (m.id === s.id) { + quantity = s.quantity; + return true; + } + } + return m.id === s; + }) && m.price_money) { + regularPrice += m.price_money.amount * quantity; + salePrice += m.price_money.amount * quantity; } }); } diff --git a/src/types/api/cart/index.ts b/src/types/api/cart/index.ts index f7f5237..26acad0 100644 --- a/src/types/api/cart/index.ts +++ b/src/types/api/cart/index.ts @@ -92,7 +92,7 @@ export interface ChoiceModifier extends BaseModifier { /** The modifier type. */ type: 'CHOICE'; /** Choice selections for modifier. */ - choiceSelections: string[]; + choiceSelections: Choice[]; } export interface TextModifier extends BaseModifier { @@ -106,7 +106,14 @@ export interface GiftWrapModifier extends BaseModifier { /** The modifier type. */ type: 'GIFT_WRAP'; /** Choice selections for modifier. */ - choiceSelections: string[]; + choiceSelections: Choice[]; +} + +export type Choice = string | ChoiceSelection; + +export interface ChoiceSelection { + id: string; + quantity: number; } export interface GiftMessageModifier extends BaseModifier { @@ -387,4 +394,4 @@ export interface CartValidation { export interface CartResponse extends CartBaseResponse { /** The response from the Fetch API. */ response: Response; -} +} \ No newline at end of file diff --git a/test/helpers.item.test.ts b/test/helpers.item.test.ts index 5badf26..44c3d1b 100644 --- a/test/helpers.item.test.ts +++ b/test/helpers.item.test.ts @@ -420,7 +420,9 @@ function createModifierSelection({ excludeText = false, choiceSelectionsCount = 2, textSelectionCount = 2, - invalidChoice = false + invalidChoice = false, + useChoiceSelectionObject = false, + useMultipleChoiceSelectionQuantity = false, }: { excludeGiftWrap?: boolean; excludeChoice?: boolean; @@ -429,13 +431,17 @@ function createModifierSelection({ choiceSelectionsCount?: number; textSelectionCount?: number; invalidChoice?: boolean; + useChoiceSelectionObject?: boolean; + useMultipleChoiceSelectionQuantity?: boolean; }) { const selectedModifiers: AddItemModifier[] = []; if (!excludeGiftWrap) { selectedModifiers.push({ id: 'modifierListGiftWrapId', type: ModifierType.GIFT_WRAP, - choiceSelections: [ + choiceSelections: useChoiceSelectionObject ? [ + { id: 'modifierGiftWrapId1', quantity: 1 } + ] : [ 'modifierGiftWrapId1' ] }); @@ -444,7 +450,10 @@ function createModifierSelection({ const choiceModifier = { id: 'modifierListChoiceId', type: ModifierType.CHOICE, - choiceSelections: [ + choiceSelections: useChoiceSelectionObject ? [ + invalidChoice ? { id: 'invalidChoiceId1', quantity: 1 } : { id: 'modifierChoiceId1', quantity: useMultipleChoiceSelectionQuantity ? 2 : 1 }, + { id: 'modifierChoiceId2', quantity: 1 }, + ]: [ invalidChoice ? 'invalidChoiceId1' : 'modifierChoiceId1', 'modifierChoiceId2', ] @@ -454,7 +463,8 @@ function createModifierSelection({ } else if (choiceSelectionsCount === 0) { choiceModifier.choiceSelections = []; } else if (choiceSelectionsCount === 3) { - choiceModifier.choiceSelections.push('modifierChoiceId3'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any + (choiceModifier.choiceSelections).push(useChoiceSelectionObject ? { id: 'modifierChoiceId3', quantity: 1 } : 'modifierChoiceId3'); } selectedModifiers.push(choiceModifier); } @@ -772,6 +782,19 @@ describe('Modifier list valid', () => { expect(result4).toStrictEqual(true); }); + it('should return valid for all with valid modifiers selections using ChoiceSelection', () => { + const modifierSelections = createModifierSelection({useChoiceSelectionObject: true}); + + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![0], modifierSelections); + expect(result).toStrictEqual(true); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![1], modifierSelections); + expect(result2).toStrictEqual(true); + const result3 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![2], modifierSelections); + expect(result3).toStrictEqual(true); + const result4 = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![3], modifierSelections); + expect(result4).toStrictEqual(true); + }); + it('should return false for invalid choice modifier selections', () => { const modifierSelections = createModifierSelection({ invalidChoice: true }); const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![1], modifierSelections); @@ -782,6 +805,16 @@ describe('Modifier list valid', () => { expect(result2).toStrictEqual(false); }); + it('should return false for invalid choice modifier selections using ChoiceSelection', () => { + const modifierSelections = createModifierSelection({ invalidChoice: true, useChoiceSelectionObject: true }); + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(defaultItem.modifier_lists![1], modifierSelections); + expect(result).toStrictEqual(false); + const modifierSelections2 = createModifierSelection({ choiceSelectionsCount: 1, useChoiceSelectionObject: true }); + const zeroChoiceModifiersItem = createTestItem({ zeroChoiceModifiers: true }); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(zeroChoiceModifiersItem.modifier_lists![1], modifierSelections2); + expect(result2).toStrictEqual(false); + }); + it('should return false for sold out choice modifier selections', () => { const soldOutChoiceModifiersItem = createTestItem({ soldOutChoiceModifier: true }); const modifierSelections = createModifierSelection({ choiceSelectionsCount: 1 }); @@ -792,6 +825,16 @@ describe('Modifier list valid', () => { expect(result2).toStrictEqual(false); }); + it('should return false for sold out choice modifier selections using ChoiceSelection', () => { + const soldOutChoiceModifiersItem = createTestItem({ soldOutChoiceModifier: true}); + const modifierSelections = createModifierSelection({ choiceSelectionsCount: 1, useChoiceSelectionObject: true }); + const result = sdk.helpers.item.isModifierListForSelectedModifiersValid(soldOutChoiceModifiersItem.modifier_lists![1], modifierSelections); + expect(result).toStrictEqual(true); + const modifierSelections2 = createModifierSelection({ useChoiceSelectionObject: true }); + const result2 = sdk.helpers.item.isModifierListForSelectedModifiersValid(soldOutChoiceModifiersItem.modifier_lists![1], modifierSelections2); + expect(result2).toStrictEqual(false); + }); + it('should verify choice modifier with same min/max 0', () => { const sameChoiceMinMaxItem = createTestItem({ choiceModifierMin: 0, choiceModifierMax: 0 }); @@ -1406,6 +1449,25 @@ describe('Validate item', () => { ]); } }); + + it('should throw invalid or missing modifiers using ChoiceSelection', () => { + const singleVariationItem = createTestItem({ variationType: 'single' }); + const invalidChoiceModifiers = createModifierSelection({ choiceSelectionsCount: 3, useChoiceSelectionObject: true }); + const validateItemFn = () => sdk.helpers.item.validateItem({ item: singleVariationItem, selectedModifiers: invalidChoiceModifiers }); + expect(validateItemFn).toThrowError(); + try { + validateItemFn(); + } catch (ex) { + expect((ex).itemOptionIds).toBeUndefined(); + expect((ex).flatVariationSelectionMissing).toBeUndefined(); + expect((ex).variationId).toBeUndefined(); + expect((ex).modifierListIds).toStrictEqual([singleVariationItem.modifier_lists![1].id]); + } + const singleVariationItem2 = createTestItem({ variationType: 'single', choiceModifierMin: 1, choiceModifierMax: 1 }); + const missingChoiceModifiers = createModifierSelection({ choiceSelectionsCount: 0, useChoiceSelectionObject: true }); + const validateItemFn2 = () => sdk.helpers.item.validateItem({ item: singleVariationItem2, selectedModifiers: missingChoiceModifiers }); + expect(validateItemFn2).toThrowError(); + }); }); describe('Get item price', () => { @@ -1482,6 +1544,27 @@ describe('Get item price', () => { }); }); + it('should return price on modifiers using ChoiceSelection', () => { + const singleVariationItem = createTestItem({ variationType: 'single' }); + const modifierSelections = createModifierSelection({ useChoiceSelectionObject: true, useMultipleChoiceSelectionQuantity: true }); + const modifierCost = singleVariationItem.modifier_lists![0].modifiers![0].price_money.amount + + (singleVariationItem.modifier_lists![1].modifiers![0].price_money.amount * 2) + + singleVariationItem.modifier_lists![1].modifiers![1].price_money.amount; + const result = sdk.helpers.item.getItemPrice({ item: singleVariationItem, selectedModifiers: modifierSelections }); + expect(result).toStrictEqual({ + regular: { + amount: singleVariationItem.variations[0].price.regular.amount + modifierCost, + formatted: '', + currency: singleVariationItem.variations[0].price.regular.currency + }, + sale: { + amount: singleVariationItem.variations[0].price.sale.amount + modifierCost, + formatted: '', + currency: singleVariationItem.variations[0].price.sale.currency + } + }); + }); + it('should return price on flat variation', () => { const flatItem = createTestItem({ variationType: 'flat' }); const selectedVariationId = flatItem.variations[3].id; diff --git a/typedocs/interfaces/types_api_cart.ChoiceModifier.md b/typedocs/interfaces/types_api_cart.ChoiceModifier.md index df25a1e..507db96 100644 --- a/typedocs/interfaces/types_api_cart.ChoiceModifier.md +++ b/typedocs/interfaces/types_api_cart.ChoiceModifier.md @@ -42,6 +42,6 @@ ___ ### choiceSelections -• **choiceSelections**: `string`[] +• **choiceSelections**: [`Choice`](../modules/types_api_cart.md#choice)[] Choice selections for modifier. diff --git a/typedocs/interfaces/types_api_cart.ChoiceSelection.md b/typedocs/interfaces/types_api_cart.ChoiceSelection.md new file mode 100644 index 0000000..80e441e --- /dev/null +++ b/typedocs/interfaces/types_api_cart.ChoiceSelection.md @@ -0,0 +1,24 @@ +[@square/site-theme-sdk](../GettingStarted.md) / [Modules](../modules.md) / [types/api/cart](../modules/types_api_cart.md) / ChoiceSelection + +# Interface: ChoiceSelection + +[types/api/cart](../modules/types_api_cart.md).ChoiceSelection + +## Table of contents + +### Properties + +- [id](types_api_cart.ChoiceSelection.md#id) +- [quantity](types_api_cart.ChoiceSelection.md#quantity) + +## Properties + +### id + +• **id**: `string` + +___ + +### quantity + +• **quantity**: `number` diff --git a/typedocs/interfaces/types_api_cart.GiftWrapModifier.md b/typedocs/interfaces/types_api_cart.GiftWrapModifier.md index 324300d..f15c64d 100644 --- a/typedocs/interfaces/types_api_cart.GiftWrapModifier.md +++ b/typedocs/interfaces/types_api_cart.GiftWrapModifier.md @@ -42,6 +42,6 @@ ___ ### choiceSelections -• **choiceSelections**: `string`[] +• **choiceSelections**: [`Choice`](../modules/types_api_cart.md#choice)[] Choice selections for modifier. diff --git a/typedocs/modules/types_api_cart.md b/typedocs/modules/types_api_cart.md index 52438a3..68450af 100644 --- a/typedocs/modules/types_api_cart.md +++ b/typedocs/modules/types_api_cart.md @@ -12,6 +12,7 @@ - [ChoiceModifier](../interfaces/types_api_cart.ChoiceModifier.md) - [TextModifier](../interfaces/types_api_cart.TextModifier.md) - [GiftWrapModifier](../interfaces/types_api_cart.GiftWrapModifier.md) +- [ChoiceSelection](../interfaces/types_api_cart.ChoiceSelection.md) - [GiftMessageModifier](../interfaces/types_api_cart.GiftMessageModifier.md) - [AddLineItem](../interfaces/types_api_cart.AddLineItem.md) - [BuyNowSubscriptionLineItem](../interfaces/types_api_cart.BuyNowSubscriptionLineItem.md) @@ -34,6 +35,7 @@ - [ScheduleTypeEnum](types_api_cart.md#scheduletypeenum) - [ModifierTypeEnum](types_api_cart.md#modifiertypeenum) - [CurrencyTypeEnum](types_api_cart.md#currencytypeenum) +- [Choice](types_api_cart.md#choice) - [AddItemModifier](types_api_cart.md#additemmodifier) - [BuyNowSubscriptionItemModifier](types_api_cart.md#buynowsubscriptionitemmodifier) @@ -70,6 +72,12 @@ ___ ___ +### Choice + +Ƭ **Choice**: `string` \| [`ChoiceSelection`](../interfaces/types_api_cart.ChoiceSelection.md) + +___ + ### AddItemModifier Ƭ **AddItemModifier**: [`ChoiceModifier`](../interfaces/types_api_cart.ChoiceModifier.md) \| [`TextModifier`](../interfaces/types_api_cart.TextModifier.md) \| [`GiftWrapModifier`](../interfaces/types_api_cart.GiftWrapModifier.md) \| [`GiftMessageModifier`](../interfaces/types_api_cart.GiftMessageModifier.md)