feat(medusa, core-workflows, product): slightly improve create cart workflow (#5725)
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/core-flows": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
slightly improve create cart workflow
|
||||||
@@ -5,7 +5,9 @@ import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
|||||||
|
|
||||||
type AddressesDTO = {
|
type AddressesDTO = {
|
||||||
shipping_address_id?: string
|
shipping_address_id?: string
|
||||||
|
shipping_address?: AddressDTO
|
||||||
billing_address_id?: string
|
billing_address_id?: string
|
||||||
|
billing_address?: AddressDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
type HandlerInputData = {
|
type HandlerInputData = {
|
||||||
@@ -48,6 +50,7 @@ export async function findOrCreateAddresses({
|
|||||||
country_code: regionCountries[0],
|
country_code: regionCountries[0],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
addressesDTO.shipping_address = shippingAddress
|
||||||
addressesDTO.shipping_address_id = shippingAddress?.id
|
addressesDTO.shipping_address_id = shippingAddress?.id
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -75,6 +78,7 @@ export async function findOrCreateAddresses({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addressesDTO.shipping_address = address
|
||||||
addressesDTO.shipping_address_id = address.id
|
addressesDTO.shipping_address_id = address.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,6 +107,7 @@ export async function findOrCreateAddresses({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addressesDTO.billing_address = address
|
||||||
addressesDTO.billing_address_id = billingAddressId
|
addressesDTO.billing_address_id = billingAddressId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CartDTO } from "@medusajs/types"
|
import { AddressDTO, CartDTO, CustomerDTO, RegionDTO } from "@medusajs/types"
|
||||||
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
enum Aliases {
|
enum Aliases {
|
||||||
@@ -14,14 +14,18 @@ type HandlerInputData = {
|
|||||||
sales_channel_id?: string
|
sales_channel_id?: string
|
||||||
}
|
}
|
||||||
addresses: {
|
addresses: {
|
||||||
|
shipping_address?: AddressDTO
|
||||||
shipping_address_id: string
|
shipping_address_id: string
|
||||||
|
billing_address?: AddressDTO
|
||||||
billing_address_id: string
|
billing_address_id: string
|
||||||
}
|
}
|
||||||
customer: {
|
customer: {
|
||||||
|
customer?: CustomerDTO
|
||||||
customer_id?: string
|
customer_id?: string
|
||||||
email?: string
|
email?: string
|
||||||
}
|
}
|
||||||
region: {
|
region: {
|
||||||
|
region?: RegionDTO
|
||||||
region_id: string
|
region_id: string
|
||||||
}
|
}
|
||||||
context: {
|
context: {
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { validateEmail } from "@medusajs/utils"
|
import { validateEmail } from "@medusajs/utils"
|
||||||
|
|
||||||
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
||||||
|
import { CustomerTypes } from "@medusajs/types"
|
||||||
|
|
||||||
type CustomerDTO = {
|
type CustomerResultDTO = {
|
||||||
|
customer?: CustomerTypes.CustomerDTO
|
||||||
customer_id?: string
|
customer_id?: string
|
||||||
email?: string
|
email?: string
|
||||||
}
|
}
|
||||||
@@ -22,12 +24,12 @@ export async function findOrCreateCustomer({
|
|||||||
container,
|
container,
|
||||||
context,
|
context,
|
||||||
data,
|
data,
|
||||||
}: WorkflowArguments<HandlerInputData>): Promise<CustomerDTO> {
|
}: WorkflowArguments<HandlerInputData>): Promise<CustomerResultDTO> {
|
||||||
const { manager } = context
|
const { manager } = context
|
||||||
|
|
||||||
const customerService = container.resolve("customerService")
|
const customerService = container.resolve("customerService")
|
||||||
|
|
||||||
const customerDTO: CustomerDTO = {}
|
const customerDataDTO: CustomerResultDTO = {}
|
||||||
const customerId = data[Aliases.Customer].customer_id
|
const customerId = data[Aliases.Customer].customer_id
|
||||||
const customerServiceTx = customerService.withTransaction(manager)
|
const customerServiceTx = customerService.withTransaction(manager)
|
||||||
|
|
||||||
@@ -36,8 +38,9 @@ export async function findOrCreateCustomer({
|
|||||||
.retrieve(customerId)
|
.retrieve(customerId)
|
||||||
.catch(() => undefined)
|
.catch(() => undefined)
|
||||||
|
|
||||||
customerDTO.customer_id = customer?.id
|
customerDataDTO.customer = customer
|
||||||
customerDTO.email = customer?.email
|
customerDataDTO.customer_id = customer?.id
|
||||||
|
customerDataDTO.email = customer?.email
|
||||||
}
|
}
|
||||||
|
|
||||||
const customerEmail = data[Aliases.Customer].email
|
const customerEmail = data[Aliases.Customer].email
|
||||||
@@ -53,11 +56,12 @@ export async function findOrCreateCustomer({
|
|||||||
customer = await customerServiceTx.create({ email: validatedEmail })
|
customer = await customerServiceTx.create({ email: validatedEmail })
|
||||||
}
|
}
|
||||||
|
|
||||||
customerDTO.customer_id = customer.id
|
customerDataDTO.customer = customer
|
||||||
customerDTO.email = customer.email
|
customerDataDTO.customer_id = customer.id
|
||||||
|
customerDataDTO.email = customer.email
|
||||||
}
|
}
|
||||||
|
|
||||||
return customerDTO
|
return customerDataDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
findOrCreateCustomer.aliases = Aliases
|
findOrCreateCustomer.aliases = Aliases
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import { MedusaError } from "@medusajs/utils"
|
import { MedusaError } from "@medusajs/utils"
|
||||||
|
import { RegionTypes } from "@medusajs/types"
|
||||||
import { isDefined } from "medusa-core-utils"
|
import { isDefined } from "medusa-core-utils"
|
||||||
|
|
||||||
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
import { WorkflowArguments } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
type RegionDTO = {
|
type RegionResultDTO = {
|
||||||
region_id?: string
|
region_id?: string
|
||||||
|
region?: RegionTypes.RegionDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
type HandlerInputData = {
|
type HandlerInputData = {
|
||||||
@@ -20,16 +22,24 @@ enum Aliases {
|
|||||||
export async function findRegion({
|
export async function findRegion({
|
||||||
container,
|
container,
|
||||||
data,
|
data,
|
||||||
}: WorkflowArguments<HandlerInputData>): Promise<RegionDTO> {
|
}: WorkflowArguments<HandlerInputData>): Promise<RegionResultDTO> {
|
||||||
const regionService = container.resolve("regionService")
|
const regionService = container.resolve("regionService")
|
||||||
|
|
||||||
let regionId: string
|
let regionId: string
|
||||||
const regionDTO: RegionDTO = {}
|
const regionDTO: RegionResultDTO = {}
|
||||||
|
|
||||||
if (isDefined(data[Aliases.Region].region_id)) {
|
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 {
|
} else {
|
||||||
const regions = await regionService.list({}, {})
|
const regions = await regionService.list(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
relations: ["countries"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
if (!regions?.length) {
|
if (!regions?.length) {
|
||||||
throw new MedusaError(
|
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
|
return regionDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -364,11 +364,15 @@ class CartService extends TransactionBaseService {
|
|||||||
).id
|
).id
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.customer_id) {
|
if (data.customer_id || data.customer) {
|
||||||
const customer = await this.customerService_
|
const customer =
|
||||||
.withTransaction(transactionManager)
|
(data.customer ??
|
||||||
.retrieve(data.customer_id)
|
(data.customer_id &&
|
||||||
.catch(() => undefined)
|
(await this.customerService_
|
||||||
|
.withTransaction(transactionManager)
|
||||||
|
.retrieve(data.customer_id)
|
||||||
|
.catch(() => undefined)))) as Customer
|
||||||
|
|
||||||
rawCart.customer = customer
|
rawCart.customer = customer
|
||||||
rawCart.customer_id = customer?.id
|
rawCart.customer_id = customer?.id
|
||||||
rawCart.email = customer?.email
|
rawCart.email = customer?.email
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Cart, CartType } from "../models/cart"
|
|||||||
import { IsType } from "../utils/validators/is-type"
|
import { IsType } from "../utils/validators/is-type"
|
||||||
import { Region } from "../models"
|
import { Region } from "../models"
|
||||||
import { ValidateNested } from "class-validator"
|
import { ValidateNested } from "class-validator"
|
||||||
|
import { CustomerTypes } from "@medusajs/types"
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
export function isCart(object: any): object is Cart {
|
export function isCart(object: any): object is Cart {
|
||||||
@@ -60,6 +61,7 @@ export type CartCreateProps = {
|
|||||||
shipping_address?: Partial<AddressPayload>
|
shipping_address?: Partial<AddressPayload>
|
||||||
gift_cards?: GiftCard[]
|
gift_cards?: GiftCard[]
|
||||||
discounts?: Discount[]
|
discounts?: Discount[]
|
||||||
|
customer?: CustomerTypes.CustomerDTO
|
||||||
customer_id?: string
|
customer_id?: string
|
||||||
type?: CartType
|
type?: CartType
|
||||||
context?: object
|
context?: object
|
||||||
|
|||||||
+39
-21
@@ -1,5 +1,9 @@
|
|||||||
import { MedusaModule } from "@medusajs/modules-sdk"
|
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 { kebabCase } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
Product,
|
Product,
|
||||||
@@ -172,16 +176,30 @@ describe("ProductModuleService products", function () {
|
|||||||
|
|
||||||
const variantTitle = data.variants[0].title
|
const variantTitle = data.variants[0].title
|
||||||
|
|
||||||
const updateData = {
|
const productBefore = (await module.retrieve(productOne.id, {
|
||||||
...data,
|
relations: [
|
||||||
id: productOne.id,
|
"images",
|
||||||
title: "updated title",
|
"variants",
|
||||||
}
|
"options",
|
||||||
|
"options.values",
|
||||||
|
"variants.options",
|
||||||
|
"tags",
|
||||||
|
"type",
|
||||||
|
],
|
||||||
|
})) as unknown as UpdateProductDTO
|
||||||
|
|
||||||
const updatedProducts = await module.update([updateData])
|
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([productBefore])
|
||||||
expect(updatedProducts).toHaveLength(1)
|
expect(updatedProducts).toHaveLength(1)
|
||||||
|
|
||||||
const product = await module.retrieve(updateData.id, {
|
const product = await module.retrieve(productBefore.id, {
|
||||||
relations: [
|
relations: [
|
||||||
"images",
|
"images",
|
||||||
"variants",
|
"variants",
|
||||||
@@ -195,7 +213,7 @@ describe("ProductModuleService products", function () {
|
|||||||
|
|
||||||
const createdVariant = product.variants.find(
|
const createdVariant = product.variants.find(
|
||||||
(v) => v.title === variantTitle
|
(v) => v.title === variantTitle
|
||||||
)
|
)!
|
||||||
|
|
||||||
expect(product.images).toHaveLength(1)
|
expect(product.images).toHaveLength(1)
|
||||||
expect(createdVariant?.options).toHaveLength(1)
|
expect(createdVariant?.options).toHaveLength(1)
|
||||||
@@ -206,12 +224,12 @@ describe("ProductModuleService products", function () {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
title: "updated title",
|
title: "updated title",
|
||||||
description: updateData.description,
|
description: productBefore.description,
|
||||||
subtitle: updateData.subtitle,
|
subtitle: productBefore.subtitle,
|
||||||
is_giftcard: updateData.is_giftcard,
|
is_giftcard: productBefore.is_giftcard,
|
||||||
discountable: updateData.discountable,
|
discountable: productBefore.discountable,
|
||||||
thumbnail: images[0],
|
thumbnail: images[0],
|
||||||
status: updateData.status,
|
status: productBefore.status,
|
||||||
images: expect.arrayContaining([
|
images: expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
@@ -221,11 +239,11 @@ describe("ProductModuleService products", function () {
|
|||||||
options: expect.arrayContaining([
|
options: expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
title: updateData.options[0].title,
|
title: productBefore.options?.[0].title,
|
||||||
values: expect.arrayContaining([
|
values: expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
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([
|
tags: expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
value: updateData.tags[0].value,
|
value: productBefore.tags?.[0].value,
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
type: expect.objectContaining({
|
type: expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
value: updateData.type.value,
|
value: productBefore.type!.value,
|
||||||
}),
|
}),
|
||||||
variants: expect.arrayContaining([
|
variants: expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
title: updateData.variants[0].title,
|
title: createdVariant.title,
|
||||||
sku: updateData.variants[0].sku,
|
sku: createdVariant.sku,
|
||||||
allow_backorder: false,
|
allow_backorder: false,
|
||||||
manage_inventory: true,
|
manage_inventory: true,
|
||||||
inventory_quantity: "100",
|
inventory_quantity: "100",
|
||||||
@@ -252,7 +270,7 @@ describe("ProductModuleService products", function () {
|
|||||||
options: expect.arrayContaining([
|
options: expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
value: updateData.variants[0].options?.[0].value,
|
value: createdVariant.options?.[0].value,
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export * as CacheTypes from "./cache"
|
export * as CacheTypes from "./cache"
|
||||||
export * as CommonTypes from "./common"
|
export * as CommonTypes from "./common"
|
||||||
|
export * as CustomerTypes from "./customer"
|
||||||
export * as DAL from "./dal"
|
export * as DAL from "./dal"
|
||||||
export * as EventBusTypes from "./event-bus"
|
export * as EventBusTypes from "./event-bus"
|
||||||
export * as FeatureFlagTypes from "./feature-flag"
|
export * as FeatureFlagTypes from "./feature-flag"
|
||||||
@@ -8,6 +9,7 @@ export * as LoggerTypes from "./logger"
|
|||||||
export * as ModulesSdkTypes from "./modules-sdk"
|
export * as ModulesSdkTypes from "./modules-sdk"
|
||||||
export * as PricingTypes from "./pricing"
|
export * as PricingTypes from "./pricing"
|
||||||
export * as ProductTypes from "./product"
|
export * as ProductTypes from "./product"
|
||||||
|
export * as RegionTypes from "./region"
|
||||||
export * as SalesChannelTypes from "./sales-channel"
|
export * as SalesChannelTypes from "./sales-channel"
|
||||||
export * as SearchTypes from "./search"
|
export * as SearchTypes from "./search"
|
||||||
export * as StockLocationTypes from "./stock-location"
|
export * as StockLocationTypes from "./stock-location"
|
||||||
|
|||||||
@@ -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<string, unknown>
|
||||||
|
deleted_at?: Date | string
|
||||||
|
created_at?: Date | string
|
||||||
|
updated_at?: Date | string
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./common"
|
||||||
@@ -3,6 +3,7 @@ export * from "./bundles"
|
|||||||
export * from "./cache"
|
export * from "./cache"
|
||||||
export * from "./cart"
|
export * from "./cart"
|
||||||
export * from "./common"
|
export * from "./common"
|
||||||
|
export * from "./customer"
|
||||||
export * from "./dal"
|
export * from "./dal"
|
||||||
export * from "./event-bus"
|
export * from "./event-bus"
|
||||||
export * from "./feature-flag"
|
export * from "./feature-flag"
|
||||||
@@ -15,6 +16,7 @@ export * from "./modules-sdk"
|
|||||||
export * from "./pricing"
|
export * from "./pricing"
|
||||||
export * from "./product"
|
export * from "./product"
|
||||||
export * from "./product-category"
|
export * from "./product-category"
|
||||||
|
export * from "./region"
|
||||||
export * from "./sales-channel"
|
export * from "./sales-channel"
|
||||||
export * from "./search"
|
export * from "./search"
|
||||||
export * from "./shared-context"
|
export * from "./shared-context"
|
||||||
|
|||||||
@@ -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<string, unknown>
|
||||||
|
includes_tax?: boolean
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./common"
|
||||||
Reference in New Issue
Block a user