Skip to content

Commit

Permalink
feat(core): Implement Shipping promotion actions
Browse files Browse the repository at this point in the history
Relates to #580. This commit introduces the new PromotionShippingAction, allowing the creation
of promotions which discount the shipping price specifically.
  • Loading branch information
michaelbromley committed Dec 8, 2020
1 parent d1f7192 commit 69b12e3
Show file tree
Hide file tree
Showing 23 changed files with 474 additions and 101 deletions.
28 changes: 15 additions & 13 deletions packages/admin-ui/src/lib/core/src/common/generated-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,19 @@ export type UpdateFacetValueInput = {
customFields?: Maybe<Scalars['JSON']>;
};

export type Fulfillment = Node & {
__typename?: 'Fulfillment';
nextStates: Array<Scalars['String']>;
id: Scalars['ID'];
createdAt: Scalars['DateTime'];
updatedAt: Scalars['DateTime'];
orderItems: Array<OrderItem>;
state: Scalars['String'];
method: Scalars['String'];
trackingCode?: Maybe<Scalars['String']>;
customFields?: Maybe<Scalars['JSON']>;
};

export type UpdateGlobalSettingsInput = {
availableLanguages?: Maybe<Array<LanguageCode>>;
trackInventory?: Maybe<Scalars['Boolean']>;
Expand Down Expand Up @@ -1462,19 +1475,6 @@ export type OrderHistoryArgs = {
options?: Maybe<HistoryEntryListOptions>;
};

export type Fulfillment = Node & {
__typename?: 'Fulfillment';
nextStates: Array<Scalars['String']>;
id: Scalars['ID'];
createdAt: Scalars['DateTime'];
updatedAt: Scalars['DateTime'];
orderItems: Array<OrderItem>;
state: Scalars['String'];
method: Scalars['String'];
trackingCode?: Maybe<Scalars['String']>;
customFields?: Maybe<Scalars['JSON']>;
};

export type UpdateOrderInput = {
id: Scalars['ID'];
customFields?: Maybe<Scalars['JSON']>;
Expand Down Expand Up @@ -3567,6 +3567,8 @@ export type ShippingLine = {
shippingMethod: ShippingMethod;
price: Scalars['Int'];
priceWithTax: Scalars['Int'];
discountedPrice: Scalars['Int'];
discountedPriceWithTax: Scalars['Int'];
discounts: Array<Adjustment>;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ const result: PossibleTypesResultData = {
'Collection',
'Customer',
'Facet',
'Fulfillment',
'HistoryEntry',
'Job',
'Order',
'Fulfillment',
'PaymentMethod',
'Product',
'ProductVariant',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,18 @@ export type UpdateFacetValueInput = {
customFields?: Maybe<Scalars['JSON']>;
};

export type Fulfillment = Node & {
nextStates: Array<Scalars['String']>;
id: Scalars['ID'];
createdAt: Scalars['DateTime'];
updatedAt: Scalars['DateTime'];
orderItems: Array<OrderItem>;
state: Scalars['String'];
method: Scalars['String'];
trackingCode?: Maybe<Scalars['String']>;
customFields?: Maybe<Scalars['JSON']>;
};

export type UpdateGlobalSettingsInput = {
availableLanguages?: Maybe<Array<LanguageCode>>;
trackInventory?: Maybe<Scalars['Boolean']>;
Expand Down Expand Up @@ -1274,18 +1286,6 @@ export type OrderHistoryArgs = {
options?: Maybe<HistoryEntryListOptions>;
};

export type Fulfillment = Node & {
nextStates: Array<Scalars['String']>;
id: Scalars['ID'];
createdAt: Scalars['DateTime'];
updatedAt: Scalars['DateTime'];
orderItems: Array<OrderItem>;
state: Scalars['String'];
method: Scalars['String'];
trackingCode?: Maybe<Scalars['String']>;
customFields?: Maybe<Scalars['JSON']>;
};

export type UpdateOrderInput = {
id: Scalars['ID'];
customFields?: Maybe<Scalars['JSON']>;
Expand Down Expand Up @@ -3329,6 +3329,8 @@ export type ShippingLine = {
shippingMethod: ShippingMethod;
price: Scalars['Int'];
priceWithTax: Scalars['Int'];
discountedPrice: Scalars['Int'];
discountedPriceWithTax: Scalars['Int'];
discounts: Array<Adjustment>;
};

Expand Down
2 changes: 2 additions & 0 deletions packages/common/src/generated-shop-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1808,6 +1808,8 @@ export type ShippingLine = {
shippingMethod: ShippingMethod;
price: Scalars['Int'];
priceWithTax: Scalars['Int'];
discountedPrice: Scalars['Int'];
discountedPriceWithTax: Scalars['Int'];
discounts: Array<Adjustment>;
};

Expand Down
28 changes: 15 additions & 13 deletions packages/common/src/generated-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1256,6 +1256,19 @@ export type UpdateFacetValueInput = {
customFields?: Maybe<Scalars['JSON']>;
};

export type Fulfillment = Node & {
__typename?: 'Fulfillment';
nextStates: Array<Scalars['String']>;
id: Scalars['ID'];
createdAt: Scalars['DateTime'];
updatedAt: Scalars['DateTime'];
orderItems: Array<OrderItem>;
state: Scalars['String'];
method: Scalars['String'];
trackingCode?: Maybe<Scalars['String']>;
customFields?: Maybe<Scalars['JSON']>;
};

export type UpdateGlobalSettingsInput = {
availableLanguages?: Maybe<Array<LanguageCode>>;
trackInventory?: Maybe<Scalars['Boolean']>;
Expand Down Expand Up @@ -1431,19 +1444,6 @@ export type OrderHistoryArgs = {
options?: Maybe<HistoryEntryListOptions>;
};

export type Fulfillment = Node & {
__typename?: 'Fulfillment';
nextStates: Array<Scalars['String']>;
id: Scalars['ID'];
createdAt: Scalars['DateTime'];
updatedAt: Scalars['DateTime'];
orderItems: Array<OrderItem>;
state: Scalars['String'];
method: Scalars['String'];
trackingCode?: Maybe<Scalars['String']>;
customFields?: Maybe<Scalars['JSON']>;
};

export type UpdateOrderInput = {
id: Scalars['ID'];
customFields?: Maybe<Scalars['JSON']>;
Expand Down Expand Up @@ -3535,6 +3535,8 @@ export type ShippingLine = {
shippingMethod: ShippingMethod;
price: Scalars['Int'];
priceWithTax: Scalars['Int'];
discountedPrice: Scalars['Int'];
discountedPriceWithTax: Scalars['Int'];
discounts: Array<Adjustment>;
};

Expand Down
26 changes: 14 additions & 12 deletions packages/core/e2e/graphql/generated-e2e-admin-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,18 @@ export type UpdateFacetValueInput = {
customFields?: Maybe<Scalars['JSON']>;
};

export type Fulfillment = Node & {
nextStates: Array<Scalars['String']>;
id: Scalars['ID'];
createdAt: Scalars['DateTime'];
updatedAt: Scalars['DateTime'];
orderItems: Array<OrderItem>;
state: Scalars['String'];
method: Scalars['String'];
trackingCode?: Maybe<Scalars['String']>;
customFields?: Maybe<Scalars['JSON']>;
};

export type UpdateGlobalSettingsInput = {
availableLanguages?: Maybe<Array<LanguageCode>>;
trackInventory?: Maybe<Scalars['Boolean']>;
Expand Down Expand Up @@ -1274,18 +1286,6 @@ export type OrderHistoryArgs = {
options?: Maybe<HistoryEntryListOptions>;
};

export type Fulfillment = Node & {
nextStates: Array<Scalars['String']>;
id: Scalars['ID'];
createdAt: Scalars['DateTime'];
updatedAt: Scalars['DateTime'];
orderItems: Array<OrderItem>;
state: Scalars['String'];
method: Scalars['String'];
trackingCode?: Maybe<Scalars['String']>;
customFields?: Maybe<Scalars['JSON']>;
};

export type UpdateOrderInput = {
id: Scalars['ID'];
customFields?: Maybe<Scalars['JSON']>;
Expand Down Expand Up @@ -3329,6 +3329,8 @@ export type ShippingLine = {
shippingMethod: ShippingMethod;
price: Scalars['Int'];
priceWithTax: Scalars['Int'];
discountedPrice: Scalars['Int'];
discountedPriceWithTax: Scalars['Int'];
discounts: Array<Adjustment>;
};

Expand Down
14 changes: 13 additions & 1 deletion packages/core/e2e/graphql/generated-e2e-shop-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1752,6 +1752,8 @@ export type ShippingLine = {
shippingMethod: ShippingMethod;
price: Scalars['Int'];
priceWithTax: Scalars['Int'];
discountedPrice: Scalars['Int'];
discountedPriceWithTax: Scalars['Int'];
discounts: Array<Adjustment>;
};

Expand Down Expand Up @@ -2594,7 +2596,17 @@ export type NativeAuthInput = {

export type TestOrderFragmentFragment = Pick<
Order,
'id' | 'code' | 'state' | 'active' | 'total' | 'totalWithTax' | 'couponCodes' | 'shipping'
| 'id'
| 'code'
| 'state'
| 'active'
| 'subTotal'
| 'subTotalWithTax'
| 'shipping'
| 'shippingWithTax'
| 'total'
| 'totalWithTax'
| 'couponCodes'
> & {
discounts: Array<Pick<Adjustment, 'adjustmentSource' | 'amount' | 'description' | 'type'>>;
lines: Array<
Expand Down
5 changes: 4 additions & 1 deletion packages/core/e2e/graphql/shop-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export const TEST_ORDER_FRAGMENT = gql`
code
state
active
subTotal
subTotalWithTax
shipping
shippingWithTax
total
totalWithTax
couponCodes
Expand All @@ -30,7 +34,6 @@ export const TEST_ORDER_FRAGMENT = gql`
type
}
}
shipping
shippingLines {
shippingMethod {
id
Expand Down
65 changes: 65 additions & 0 deletions packages/core/e2e/order-promotion.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import path from 'path';

import { initialData } from '../../../e2e-common/e2e-initial-data';
import { testConfig, TEST_SETUP_TIMEOUT_MS } from '../../../e2e-common/test-config';
import { freeShipping } from '../src/config/promotion/actions/free-shipping-action';
import { orderFixedDiscount } from '../src/config/promotion/actions/order-fixed-discount-action';

import { testSuccessfulPaymentMethod } from './fixtures/test-payment-methods';
Expand Down Expand Up @@ -49,6 +50,7 @@ import {
GetOrderPromotionsByCode,
RemoveCouponCode,
SetCustomerForOrder,
SetShippingMethod,
TestOrderFragmentFragment,
TestOrderWithPaymentsFragment,
UpdatedOrderFragment,
Expand All @@ -70,6 +72,7 @@ import {
GET_ORDER_PROMOTIONS_BY_CODE,
REMOVE_COUPON_CODE,
SET_CUSTOMER,
SET_SHIPPING_METHOD,
} from './graphql/shop-definitions';
import { addPaymentToOrder, proceedToArrangingPayment } from './utils/test-order-utils';

Expand Down Expand Up @@ -904,6 +907,68 @@ describe('Promotions applied to Orders', () => {
});
});

describe('freeShipping', () => {
const couponCode = 'FREE_SHIPPING';
let promotion: PromotionFragment;

beforeAll(async () => {
promotion = await createPromotion({
enabled: true,
name: 'Free shipping',
couponCode,
conditions: [],
actions: [
{
code: freeShipping.code,
arguments: [],
},
],
});
});

afterAll(async () => {
await deletePromotion(promotion.id);
shopClient.setChannelToken(E2E_DEFAULT_CHANNEL_TOKEN);
});

it('prices exclude tax', async () => {
const { addItemToOrder } = await shopClient.query<
AddItemToOrder.Mutation,
AddItemToOrder.Variables
>(ADD_ITEM_TO_ORDER, {
productVariantId: getVariantBySlug('item-5000').id,
quantity: 1,
});
const { setOrderShippingMethod } = await shopClient.query<
SetShippingMethod.Mutation,
SetShippingMethod.Variables
>(SET_SHIPPING_METHOD, {
id: 'T_1',
});
orderResultGuard.assertSuccess(setOrderShippingMethod);
expect(setOrderShippingMethod.discounts).toEqual([]);
expect(setOrderShippingMethod.shipping).toBe(500);
expect(setOrderShippingMethod.shippingWithTax).toBe(500);
expect(setOrderShippingMethod.total).toBe(5500);
expect(setOrderShippingMethod.totalWithTax).toBe(6500);

const { applyCouponCode } = await shopClient.query<
ApplyCouponCode.Mutation,
ApplyCouponCode.Variables
>(APPLY_COUPON_CODE, {
couponCode,
});
orderResultGuard.assertSuccess(applyCouponCode);

expect(applyCouponCode.discounts.length).toBe(1);
expect(applyCouponCode.discounts[0].description).toBe('Free shipping');
expect(applyCouponCode.shipping).toBe(0);
expect(applyCouponCode.shippingWithTax).toBe(0);
expect(applyCouponCode.total).toBe(5000);
expect(applyCouponCode.totalWithTax).toBe(6000);
});
});

describe('multiple promotions simultaneously', () => {
const saleItem50pcOffCoupon = 'CODE1';
const order15pcOffCoupon = 'CODE2';
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/api/schema/common/order.type.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ type ShippingLine {
shippingMethod: ShippingMethod!
price: Int!
priceWithTax: Int!
discountedPrice: Int!
discountedPriceWithTax: Int!
discounts: [Adjustment!]!
}

Expand Down
12 changes: 12 additions & 0 deletions packages/core/src/config/promotion/actions/free-shipping-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { LanguageCode } from '@vendure/common/lib/generated-types';

import { PromotionShippingAction } from '../promotion-action';

export const freeShipping = new PromotionShippingAction({
code: 'free_shipping',
args: {},
execute(ctx, shippingLine, order, args) {
return -shippingLine.priceWithTax;
},
description: [{ languageCode: LanguageCode.en, value: 'Free shipping' }],
});
Loading

0 comments on commit 69b12e3

Please sign in to comment.