From 85cda7ce3754e2a8ecb207f29d462a9bcd442ade Mon Sep 17 00:00:00 2001 From: Adrien de Peretti Date: Thu, 7 Dec 2023 17:05:23 +0100 Subject: [PATCH] feat(medusa, core-workflows, product): slightly improve create cart workflow (#5725) --- .changeset/unlucky-snails-drive.md | 6 ++ .../address/find-or-create-addresses.ts | 5 ++ .../src/handlers/cart/create-cart.ts | 6 +- .../customer/find-or-create-customer.ts | 20 ++++--- .../src/handlers/region/find-region.ts | 25 +++++--- packages/medusa/src/services/cart.ts | 14 +++-- packages/medusa/src/types/cart.ts | 2 + .../product-module-service/products.spec.ts | 60 ++++++++++++------- packages/types/src/bundles.ts | 2 + packages/types/src/customer/common.ts | 24 ++++++++ packages/types/src/customer/index.ts | 1 + packages/types/src/index.ts | 2 + packages/types/src/region/common.ts | 11 ++++ packages/types/src/region/index.ts | 1 + 14 files changed, 136 insertions(+), 43 deletions(-) create mode 100644 .changeset/unlucky-snails-drive.md create mode 100644 packages/types/src/customer/common.ts create mode 100644 packages/types/src/customer/index.ts create mode 100644 packages/types/src/region/common.ts create mode 100644 packages/types/src/region/index.ts diff --git a/.changeset/unlucky-snails-drive.md b/.changeset/unlucky-snails-drive.md new file mode 100644 index 0000000000000..e29c4c8aa9368 --- /dev/null +++ b/.changeset/unlucky-snails-drive.md @@ -0,0 +1,6 @@ +--- +"@medusajs/medusa": patch +"@medusajs/core-flows": patch +--- + +slightly improve create cart workflow diff --git a/packages/core-flows/src/handlers/address/find-or-create-addresses.ts b/packages/core-flows/src/handlers/address/find-or-create-addresses.ts index 1b4b7b501df4b..024a557dc9d20 100644 --- a/packages/core-flows/src/handlers/address/find-or-create-addresses.ts +++ b/packages/core-flows/src/handlers/address/find-or-create-addresses.ts @@ -5,7 +5,9 @@ import { WorkflowArguments } from "@medusajs/workflows-sdk" type AddressesDTO = { shipping_address_id?: string + shipping_address?: AddressDTO billing_address_id?: string + billing_address?: AddressDTO } type HandlerInputData = { @@ -48,6 +50,7 @@ export async function findOrCreateAddresses({ country_code: regionCountries[0], }) + addressesDTO.shipping_address = shippingAddress addressesDTO.shipping_address_id = shippingAddress?.id } } else { @@ -75,6 +78,7 @@ export async function findOrCreateAddresses({ ) } + addressesDTO.shipping_address = address addressesDTO.shipping_address_id = address.id } } @@ -103,6 +107,7 @@ export async function findOrCreateAddresses({ ) } + addressesDTO.billing_address = address addressesDTO.billing_address_id = billingAddressId } diff --git a/packages/core-flows/src/handlers/cart/create-cart.ts b/packages/core-flows/src/handlers/cart/create-cart.ts index bee49af29dff8..3325d90bb0d05 100644 --- a/packages/core-flows/src/handlers/cart/create-cart.ts +++ b/packages/core-flows/src/handlers/cart/create-cart.ts @@ -1,4 +1,4 @@ -import { CartDTO } from "@medusajs/types" +import { AddressDTO, CartDTO, CustomerDTO, RegionDTO } from "@medusajs/types" import { WorkflowArguments } from "@medusajs/workflows-sdk" enum Aliases { @@ -14,14 +14,18 @@ type HandlerInputData = { sales_channel_id?: string } addresses: { + shipping_address?: AddressDTO shipping_address_id: string + billing_address?: AddressDTO billing_address_id: string } customer: { + customer?: CustomerDTO customer_id?: string email?: string } region: { + region?: RegionDTO region_id: string } context: { diff --git a/packages/core-flows/src/handlers/customer/find-or-create-customer.ts b/packages/core-flows/src/handlers/customer/find-or-create-customer.ts index ab04c843849f8..b3fcb62ffe254 100644 --- a/packages/core-flows/src/handlers/customer/find-or-create-customer.ts +++ b/packages/core-flows/src/handlers/customer/find-or-create-customer.ts @@ -1,8 +1,10 @@ import { validateEmail } from "@medusajs/utils" import { WorkflowArguments } from "@medusajs/workflows-sdk" +import { CustomerTypes } from "@medusajs/types" -type CustomerDTO = { +type CustomerResultDTO = { + customer?: CustomerTypes.CustomerDTO customer_id?: string email?: string } @@ -22,12 +24,12 @@ export async function findOrCreateCustomer({ container, context, data, -}: WorkflowArguments): Promise { +}: WorkflowArguments): Promise { const { manager } = context const customerService = container.resolve("customerService") - const customerDTO: CustomerDTO = {} + const customerDataDTO: CustomerResultDTO = {} const customerId = data[Aliases.Customer].customer_id const customerServiceTx = customerService.withTransaction(manager) @@ -36,8 +38,9 @@ export async function findOrCreateCustomer({ .retrieve(customerId) .catch(() => undefined) - customerDTO.customer_id = customer?.id - customerDTO.email = customer?.email + customerDataDTO.customer = customer + customerDataDTO.customer_id = customer?.id + customerDataDTO.email = customer?.email } const customerEmail = data[Aliases.Customer].email @@ -53,11 +56,12 @@ export async function findOrCreateCustomer({ customer = await customerServiceTx.create({ email: validatedEmail }) } - customerDTO.customer_id = customer.id - customerDTO.email = customer.email + customerDataDTO.customer = customer + customerDataDTO.customer_id = customer.id + customerDataDTO.email = customer.email } - return customerDTO + return customerDataDTO } findOrCreateCustomer.aliases = Aliases diff --git a/packages/core-flows/src/handlers/region/find-region.ts b/packages/core-flows/src/handlers/region/find-region.ts index c727cefea49e3..71916e8de24ef 100644 --- a/packages/core-flows/src/handlers/region/find-region.ts +++ b/packages/core-flows/src/handlers/region/find-region.ts @@ -1,10 +1,12 @@ import { MedusaError } from "@medusajs/utils" +import { RegionTypes } from "@medusajs/types" import { isDefined } from "medusa-core-utils" import { WorkflowArguments } from "@medusajs/workflows-sdk" -type RegionDTO = { +type RegionResultDTO = { region_id?: string + region?: RegionTypes.RegionDTO } type HandlerInputData = { @@ -20,16 +22,24 @@ enum Aliases { export async function findRegion({ container, data, -}: WorkflowArguments): Promise { +}: WorkflowArguments): Promise { const regionService = container.resolve("regionService") let regionId: string - const regionDTO: RegionDTO = {} + const regionDTO: RegionResultDTO = {} if (isDefined(data[Aliases.Region].region_id)) { - regionId = data[Aliases.Region].region_id + regionDTO.region_id = data[Aliases.Region].region_id + regionDTO.region = await regionService.retrieve(regionDTO.region_id, { + relations: ["countries"], + }) } else { - const regions = await regionService.list({}, {}) + const regions = await regionService.list( + {}, + { + relations: ["countries"], + } + ) if (!regions?.length) { throw new MedusaError( @@ -38,11 +48,10 @@ export async function findRegion({ ) } - regionId = regions[0].id + regionDTO.region_id = regions[0].id + regionDTO.region = regions[0] } - regionDTO.region_id = regionId - return regionDTO } diff --git a/packages/medusa/src/services/cart.ts b/packages/medusa/src/services/cart.ts index add52254945b4..6aadd7b98d081 100644 --- a/packages/medusa/src/services/cart.ts +++ b/packages/medusa/src/services/cart.ts @@ -364,11 +364,15 @@ class CartService extends TransactionBaseService { ).id } - if (data.customer_id) { - const customer = await this.customerService_ - .withTransaction(transactionManager) - .retrieve(data.customer_id) - .catch(() => undefined) + if (data.customer_id || data.customer) { + const customer = + (data.customer ?? + (data.customer_id && + (await this.customerService_ + .withTransaction(transactionManager) + .retrieve(data.customer_id) + .catch(() => undefined)))) as Customer + rawCart.customer = customer rawCart.customer_id = customer?.id rawCart.email = customer?.email diff --git a/packages/medusa/src/types/cart.ts b/packages/medusa/src/types/cart.ts index f79907a076d9d..3a9e069957a7f 100644 --- a/packages/medusa/src/types/cart.ts +++ b/packages/medusa/src/types/cart.ts @@ -8,6 +8,7 @@ import { Cart, CartType } from "../models/cart" import { IsType } from "../utils/validators/is-type" import { Region } from "../models" import { ValidateNested } from "class-validator" +import { CustomerTypes } from "@medusajs/types" // eslint-disable-next-line @typescript-eslint/no-explicit-any export function isCart(object: any): object is Cart { @@ -60,6 +61,7 @@ export type CartCreateProps = { shipping_address?: Partial gift_cards?: GiftCard[] discounts?: Discount[] + customer?: CustomerTypes.CustomerDTO customer_id?: string type?: CartType context?: object diff --git a/packages/product/integration-tests/__tests__/services/product-module-service/products.spec.ts b/packages/product/integration-tests/__tests__/services/product-module-service/products.spec.ts index f2e4f8754b76b..9f1d17c35c6e9 100644 --- a/packages/product/integration-tests/__tests__/services/product-module-service/products.spec.ts +++ b/packages/product/integration-tests/__tests__/services/product-module-service/products.spec.ts @@ -1,5 +1,9 @@ import { MedusaModule } from "@medusajs/modules-sdk" -import { IProductModuleService, ProductTypes } from "@medusajs/types" +import { + IProductModuleService, + ProductTypes, + UpdateProductDTO, +} from "@medusajs/types" import { kebabCase } from "@medusajs/utils" import { Product, @@ -172,16 +176,30 @@ describe("ProductModuleService products", function () { const variantTitle = data.variants[0].title - const updateData = { - ...data, - id: productOne.id, - title: "updated title", - } + const productBefore = (await module.retrieve(productOne.id, { + relations: [ + "images", + "variants", + "options", + "options.values", + "variants.options", + "tags", + "type", + ], + })) as unknown as UpdateProductDTO + + productBefore.title = "updated title" + productBefore.variants = [...productBefore.variants!, ...data.variants] + productBefore.type = { value: "new-type" } + productBefore.options = data.options + productBefore.images = data.images + productBefore.thumbnail = data.thumbnail + productBefore.tags = data.tags - const updatedProducts = await module.update([updateData]) + const updatedProducts = await module.update([productBefore]) expect(updatedProducts).toHaveLength(1) - const product = await module.retrieve(updateData.id, { + const product = await module.retrieve(productBefore.id, { relations: [ "images", "variants", @@ -195,7 +213,7 @@ describe("ProductModuleService products", function () { const createdVariant = product.variants.find( (v) => v.title === variantTitle - ) + )! expect(product.images).toHaveLength(1) expect(createdVariant?.options).toHaveLength(1) @@ -206,12 +224,12 @@ describe("ProductModuleService products", function () { expect.objectContaining({ id: expect.any(String), title: "updated title", - description: updateData.description, - subtitle: updateData.subtitle, - is_giftcard: updateData.is_giftcard, - discountable: updateData.discountable, + description: productBefore.description, + subtitle: productBefore.subtitle, + is_giftcard: productBefore.is_giftcard, + discountable: productBefore.discountable, thumbnail: images[0], - status: updateData.status, + status: productBefore.status, images: expect.arrayContaining([ expect.objectContaining({ id: expect.any(String), @@ -221,11 +239,11 @@ describe("ProductModuleService products", function () { options: expect.arrayContaining([ expect.objectContaining({ id: expect.any(String), - title: updateData.options[0].title, + title: productBefore.options?.[0].title, values: expect.arrayContaining([ expect.objectContaining({ id: expect.any(String), - value: updateData.variants[0].options?.[0].value, + value: createdVariant.options?.[0].value, }), ]), }), @@ -233,18 +251,18 @@ describe("ProductModuleService products", function () { tags: expect.arrayContaining([ expect.objectContaining({ id: expect.any(String), - value: updateData.tags[0].value, + value: productBefore.tags?.[0].value, }), ]), type: expect.objectContaining({ id: expect.any(String), - value: updateData.type.value, + value: productBefore.type!.value, }), variants: expect.arrayContaining([ expect.objectContaining({ id: expect.any(String), - title: updateData.variants[0].title, - sku: updateData.variants[0].sku, + title: createdVariant.title, + sku: createdVariant.sku, allow_backorder: false, manage_inventory: true, inventory_quantity: "100", @@ -252,7 +270,7 @@ describe("ProductModuleService products", function () { options: expect.arrayContaining([ expect.objectContaining({ id: expect.any(String), - value: updateData.variants[0].options?.[0].value, + value: createdVariant.options?.[0].value, }), ]), }), diff --git a/packages/types/src/bundles.ts b/packages/types/src/bundles.ts index 1bacfa8434092..f928d021ccbb0 100644 --- a/packages/types/src/bundles.ts +++ b/packages/types/src/bundles.ts @@ -1,5 +1,6 @@ export * as CacheTypes from "./cache" export * as CommonTypes from "./common" +export * as CustomerTypes from "./customer" export * as DAL from "./dal" export * as EventBusTypes from "./event-bus" export * as FeatureFlagTypes from "./feature-flag" @@ -8,6 +9,7 @@ export * as LoggerTypes from "./logger" export * as ModulesSdkTypes from "./modules-sdk" export * as PricingTypes from "./pricing" export * as ProductTypes from "./product" +export * as RegionTypes from "./region" export * as SalesChannelTypes from "./sales-channel" export * as SearchTypes from "./search" export * as StockLocationTypes from "./stock-location" diff --git a/packages/types/src/customer/common.ts b/packages/types/src/customer/common.ts new file mode 100644 index 0000000000000..1aa6c14f3dbed --- /dev/null +++ b/packages/types/src/customer/common.ts @@ -0,0 +1,24 @@ +import { AddressDTO } from "../address" + +export interface CustomerDTO { + id: string + email: string + billing_address_id?: string | null + shipping_address_id?: string | null + first_name?: string | null + last_name?: string | null + billing_address?: AddressDTO + shipping_address?: AddressDTO + phone?: string | null + has_account: boolean + groups?: { + id: string + }[] + orders: { + id: string + }[] + metadata?: Record + deleted_at?: Date | string + created_at?: Date | string + updated_at?: Date | string +} diff --git a/packages/types/src/customer/index.ts b/packages/types/src/customer/index.ts new file mode 100644 index 0000000000000..488a94fdffa50 --- /dev/null +++ b/packages/types/src/customer/index.ts @@ -0,0 +1 @@ +export * from "./common" diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index bd22231b23b54..85df67d803679 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -3,6 +3,7 @@ export * from "./bundles" export * from "./cache" export * from "./cart" export * from "./common" +export * from "./customer" export * from "./dal" export * from "./event-bus" export * from "./feature-flag" @@ -15,6 +16,7 @@ export * from "./modules-sdk" export * from "./pricing" export * from "./product" export * from "./product-category" +export * from "./region" export * from "./sales-channel" export * from "./search" export * from "./shared-context" diff --git a/packages/types/src/region/common.ts b/packages/types/src/region/common.ts new file mode 100644 index 0000000000000..2353257c2ea52 --- /dev/null +++ b/packages/types/src/region/common.ts @@ -0,0 +1,11 @@ +export type RegionDTO = { + name: string + currency_code: string + tax_rate?: number + tax_code?: string | null + gift_cards_taxable?: boolean + automatic_taxes?: boolean + tax_provider_id?: string | null + metadata?: Record + includes_tax?: boolean +} diff --git a/packages/types/src/region/index.ts b/packages/types/src/region/index.ts new file mode 100644 index 0000000000000..488a94fdffa50 --- /dev/null +++ b/packages/types/src/region/index.ts @@ -0,0 +1 @@ +export * from "./common"