diff --git a/api/composables.api.md b/api/composables.api.md index 5089b7fea..9f46cecdc 100644 --- a/api/composables.api.md +++ b/api/composables.api.md @@ -5,6 +5,7 @@ ```ts import { AddressType } from '@shopware-pwa/commons/interfaces/models/checkout/customer/CustomerAddress'; +import { BillingAddress } from '@shopware-pwa/commons/interfaces/request/GuestOrderParams'; import { Country } from '@shopware-pwa/commons/interfaces/models/system/country/Country'; import { Customer } from '@shopware-pwa/commons/interfaces/models/checkout/customer/Customer'; import { CustomerAddress } from '@shopware-pwa/commons/interfaces/models/checkout/customer/CustomerAddress'; @@ -20,6 +21,7 @@ import { Product } from '@shopware-pwa/commons/interfaces/models/content/product import { Ref } from '@vue/composition-api'; import { Salutation } from '@shopware-pwa/commons/interfaces/models/system/salutation/Salutation'; import { SessionContext } from '@shopware-pwa/commons/interfaces/response/SessionContext'; +import { ShippingAddress } from '@shopware-pwa/commons/interfaces/request/GuestOrderParams'; import { ShippingMethod } from '@shopware-pwa/commons/interfaces/models/checkout/shipping/ShippingMethod'; // @alpha (undocumented) @@ -88,6 +90,8 @@ export const useCategoryFilters: () => any; // @alpha (undocumented) export interface UseCheckout { + // (undocumented) + billingAddress: Readonly>; // (undocumented) createOrder: () => Promise; // (undocumented) @@ -103,6 +107,8 @@ export interface UseCheckout { // (undocumented) isGuestOrder: Readonly>; // (undocumented) + shippingAddress: Readonly>; + // (undocumented) updateGuestOrderParams: (params: Partial) => void; } diff --git a/packages/commons/interfaces/models/system/user/User.ts b/packages/commons/interfaces/models/system/user/User.ts index e918b910d..d8e260ca0 100644 --- a/packages/commons/interfaces/models/system/user/User.ts +++ b/packages/commons/interfaces/models/system/user/User.ts @@ -1 +1,4 @@ -export interface User {} +import { BillingAddress } from "../../../request/GuestOrderParams"; +export interface User { + activeBillingAddress?: BillingAddress; +} diff --git a/packages/commons/interfaces/response/SessionContext.ts b/packages/commons/interfaces/response/SessionContext.ts index 5d3e8cf63..48631c8f1 100644 --- a/packages/commons/interfaces/response/SessionContext.ts +++ b/packages/commons/interfaces/response/SessionContext.ts @@ -2,6 +2,7 @@ import { PaymentMethod } from "../models/checkout/payment/PaymentMethod"; import { ShippingMethod } from "../models/checkout/shipping/ShippingMethod"; import { Country } from "../models/system/country/Country"; import { User } from "../models/system/user/User"; +import { ShippingAddress } from "../request/GuestOrderParams"; export interface ContextTokenResponse { contextToken: string; @@ -33,6 +34,7 @@ export interface SessionContext { paymentMethod: PaymentMethod; shippingMethod: ShippingMethod; shippingLocation: { + address: ShippingAddress; country: Country; }; } diff --git a/packages/composables/__tests__/useCheckout.spec.ts b/packages/composables/__tests__/useCheckout.spec.ts index 29a1fc73b..7549b4d6f 100644 --- a/packages/composables/__tests__/useCheckout.spec.ts +++ b/packages/composables/__tests__/useCheckout.spec.ts @@ -1,5 +1,10 @@ import Vue from "vue"; -import VueCompositionApi, { ref } from "@vue/composition-api"; +import VueCompositionApi, { + ref, + Ref, + reactive, + computed, +} from "@vue/composition-api"; Vue.use(VueCompositionApi); // // Mock API client @@ -10,9 +15,12 @@ const mockedApiClient = shopwareClient as jest.Mocked; const consoleErrorSpy = jest.spyOn(console, "error"); import * as Composables from "@shopware-pwa/composables"; import { useCheckout } from "@shopware-pwa/composables"; +import { SessionContext } from "@shopware-pwa/commons/interfaces/response/SessionContext"; describe("Composables - useCheckout", () => { let isLoggedIn = ref(false); + const stateContext: Ref | null> = ref(null); + const refreshCartMock = jest.fn(async () => {}); beforeEach(() => { jest.resetAllMocks(); @@ -20,6 +28,7 @@ describe("Composables - useCheckout", () => { jest.spyOn(Composables, "useUser").mockImplementation(() => { return { isLoggedIn, + user: ref({ firstName: "Elton" }), } as any; }); jest.spyOn(Composables, "useCart").mockImplementation(() => { @@ -27,6 +36,15 @@ describe("Composables - useCheckout", () => { refreshCart: refreshCartMock, } as any; }); + stateContext.value = null; + Composables.setStore({ + getters: reactive({ + getSessionContext: computed(() => stateContext.value), + }), + commit: (name: string, value: SessionContext) => { + stateContext.value = value; + }, + }); consoleErrorSpy.mockImplementationOnce(() => {}); }); @@ -51,6 +69,109 @@ describe("Composables - useCheckout", () => { expect(guestOrderParams.value).toEqual({}); }); }); + + describe("shippingAddress", () => { + it("should return guest order address if is guest order", () => { + const { shippingAddress, updateGuestOrderParams } = useCheckout(); + updateGuestOrderParams({ + shippingAddress: { + street: "first street", + }, + } as any); + expect(shippingAddress.value).toEqual({ street: "first street" }); + updateGuestOrderParams({ + shippingAddress: undefined, + } as any); + }); + + it("should undefined when guest address is not set", () => { + const { shippingAddress } = useCheckout(); + expect(shippingAddress.value).toBeUndefined(); + }); + + it("should return user address in case of user order", async () => { + isLoggedIn.value = true; + stateContext.value = { + shippingLocation: { + address: { + street: "some street", + }, + }, + } as any; + const { shippingAddress } = useCheckout(); + expect(shippingAddress.value).toEqual({ street: "some street" }); + }); + + it("should return undefined if address is not set", async () => { + isLoggedIn.value = true; + stateContext.value = {} as any; + const { shippingAddress } = useCheckout(); + expect(shippingAddress.value).toBeUndefined(); + }); + + it("should return undefined if no session context", async () => { + isLoggedIn.value = true; + stateContext.value = null as any; + const { shippingAddress } = useCheckout(); + expect(shippingAddress.value).toBeUndefined(); + }); + }); + + describe("billingAddress", () => { + it("should return guest order address if is guest order", () => { + const { billingAddress, updateGuestOrderParams } = useCheckout(); + updateGuestOrderParams({ + billingAddress: { + street: "thrid street", + }, + } as any); + expect(billingAddress.value).toEqual({ street: "thrid street" }); + updateGuestOrderParams({ + billingAddress: undefined, + } as any); + }); + + it("should return undefined when guest billing address is not set", () => { + const { billingAddress, updateGuestOrderParams } = useCheckout(); + updateGuestOrderParams({ + billingAddress: { + street: "thrid street", + }, + } as any); + expect(billingAddress.value).toEqual({ street: "thrid street" }); + updateGuestOrderParams({ + billingAddress: undefined, + } as any); + }); + + it("should return address in case of user order", async () => { + isLoggedIn.value = true; + stateContext.value = { + customer: { + activeBillingAddress: { + street: "some street", + }, + }, + } as any; + const { billingAddress } = useCheckout(); + await Vue.nextTick(); + expect(billingAddress.value).toEqual({ street: "some street" }); + }); + + it("should return undefined if address is not set", async () => { + isLoggedIn.value = true; + stateContext.value = {} as any; + const { billingAddress } = useCheckout(); + expect(billingAddress.value).toBeUndefined(); + }); + + it("should return undefined if no session context", async () => { + isLoggedIn.value = true; + stateContext.value = null as any; + const { billingAddress } = useCheckout(); + expect(billingAddress.value).toBeUndefined(); + }); + }); }); describe("methods", () => { diff --git a/packages/composables/src/logic/useCheckout.ts b/packages/composables/src/logic/useCheckout.ts index 2e4996a15..50d8f7a51 100644 --- a/packages/composables/src/logic/useCheckout.ts +++ b/packages/composables/src/logic/useCheckout.ts @@ -3,7 +3,11 @@ import { Ref, computed, reactive } from "@vue/composition-api"; import { useUser, useCart } from "@shopware-pwa/composables"; import { ShippingMethod } from "@shopware-pwa/commons/interfaces/models/checkout/shipping/ShippingMethod"; import { PaymentMethod } from "@shopware-pwa/commons/interfaces/models/checkout/payment/PaymentMethod"; -import { GuestOrderParams } from "@shopware-pwa/commons/interfaces/request/GuestOrderParams"; +import { + GuestOrderParams, + ShippingAddress, + BillingAddress, +} from "@shopware-pwa/commons/interfaces/request/GuestOrderParams"; import { Order } from "@shopware-pwa/commons/interfaces/models/checkout/order/Order"; import { getAvailableShippingMethods, @@ -11,6 +15,7 @@ import { createGuestOrder, createOrder as createApiOrder, } from "@shopware-pwa/shopware-6-client"; +import { useSessionContext } from "./useSessionContext"; /** * @alpha @@ -26,6 +31,8 @@ export interface UseCheckout { }) => Promise>>; createOrder: () => Promise; updateGuestOrderParams: (params: Partial) => void; + shippingAddress: Readonly>; + billingAddress: Readonly>; } const orderData: { @@ -44,6 +51,8 @@ const orderData: { export const useCheckout = (): UseCheckout => { const { isLoggedIn } = useUser(); const { refreshCart } = useCart(); + const { sessionContext } = useSessionContext(); + const shippingMethods: Readonly> = computed( () => orderData.shippingMethods ); @@ -96,6 +105,17 @@ export const useCheckout = (): UseCheckout => { orderData.guestOrderParams = { ...orderData.guestOrderParams, ...params }; }; + const shippingAddress = computed(() => + isGuestOrder.value + ? guestOrderParams.value.shippingAddress + : sessionContext.value?.shippingLocation?.address + ); + const billingAddress = computed(() => + isGuestOrder.value + ? guestOrderParams.value.billingAddress + : sessionContext.value?.customer?.activeBillingAddress + ); + return { isGuestOrder, getPaymentMethods, @@ -103,5 +123,7 @@ export const useCheckout = (): UseCheckout => { createOrder, guestOrderParams, updateGuestOrderParams, + shippingAddress, + billingAddress, }; }; diff --git a/packages/default-theme/components/checkout/steps/guest/BillingAddressGuestForm.vue b/packages/default-theme/components/checkout/steps/guest/BillingAddressGuestForm.vue index 9dbc5f2bc..e9d444369 100644 --- a/packages/default-theme/components/checkout/steps/guest/BillingAddressGuestForm.vue +++ b/packages/default-theme/components/checkout/steps/guest/BillingAddressGuestForm.vue @@ -73,6 +73,7 @@ required /> - {{ countryOption }} + {{ countryOption.name }} []) + const { getCountries } = useCountries() return { validations, @@ -162,7 +163,7 @@ export default { countryId, phoneNumber, differentThanShipping, - countries + getCountries, } }, watch: { diff --git a/packages/default-theme/components/checkout/summary/BillingAddressSummary.vue b/packages/default-theme/components/checkout/summary/BillingAddressSummary.vue index 13fcb8569..8a61e5bcd 100644 --- a/packages/default-theme/components/checkout/summary/BillingAddressSummary.vue +++ b/packages/default-theme/components/checkout/summary/BillingAddressSummary.vue @@ -1,19 +1,14 @@