diff --git a/.changeset/shiny-avocados-give.md b/.changeset/shiny-avocados-give.md new file mode 100644 index 0000000000..50baebfb52 --- /dev/null +++ b/.changeset/shiny-avocados-give.md @@ -0,0 +1,8 @@ +--- +"@medusajs/core-flows": patch +"@medusajs/medusa": patch +"@medusajs/types": patch +"@medusajs/utils": patch +--- + +feat(core-flows,medusa,types,utils): adds update cart API with promotions diff --git a/integration-tests/plugins/__tests__/cart/store/carts.spec.ts b/integration-tests/plugins/__tests__/cart/store/carts.spec.ts index eb56343cdd..fe077a33ff 100644 --- a/integration-tests/plugins/__tests__/cart/store/carts.spec.ts +++ b/integration-tests/plugins/__tests__/cart/store/carts.spec.ts @@ -4,9 +4,11 @@ import { ICustomerModuleService, IPricingModuleService, IProductModuleService, + IPromotionModuleService, IRegionModuleService, ISalesChannelModuleService, } from "@medusajs/types" +import { PromotionRuleOperator, PromotionType } from "@medusajs/utils" import path from "path" import { startBootstrapApp } from "../../../../environment-helpers/bootstrap-app" import { useApi } from "../../../../environment-helpers/use-api" @@ -30,6 +32,7 @@ describe("Store Carts API", () => { let productModule: IProductModuleService let pricingModule: IPricingModuleService let remoteLink + let promotionModule: IPromotionModuleService let defaultRegion @@ -45,6 +48,7 @@ describe("Store Carts API", () => { productModule = appContainer.resolve(ModuleRegistrationName.PRODUCT) pricingModule = appContainer.resolve(ModuleRegistrationName.PRICING) remoteLink = appContainer.resolve("remoteLink") + promotionModule = appContainer.resolve(ModuleRegistrationName.PROMOTION) }) afterAll(async () => { @@ -288,6 +292,171 @@ describe("Store Carts API", () => { }) }) + describe("POST /store/carts/:id", () => { + it("should update a cart with promo codes with a replace action", async () => { + const targetRules = [ + { + attribute: "product_id", + operator: PromotionRuleOperator.IN, + values: ["prod_tshirt"], + }, + ] + + const appliedPromotion = await promotionModule.create({ + code: "PROMOTION_APPLIED", + type: PromotionType.STANDARD, + application_method: { + type: "fixed", + target_type: "items", + allocation: "each", + value: "300", + apply_to_quantity: 1, + max_quantity: 1, + target_rules: targetRules, + }, + }) + + const createdPromotion = await promotionModule.create({ + code: "PROMOTION_TEST", + type: PromotionType.STANDARD, + application_method: { + type: "fixed", + target_type: "items", + allocation: "across", + value: "1000", + apply_to_quantity: 1, + target_rules: targetRules, + }, + }) + + const cart = await cartModuleService.create({ + currency_code: "usd", + email: "tony@stark.com", + items: [ + { + id: "item-1", + unit_price: 2000, + quantity: 1, + title: "Test item", + product_id: "prod_tshirt", + } as any, + ], + }) + + const [adjustment] = await cartModuleService.addLineItemAdjustments([ + { + code: appliedPromotion.code!, + amount: 300, + item_id: "item-1", + promotion_id: appliedPromotion.id, + }, + ]) + + const api = useApi() as any + + // Should remove earlier adjustments from other promocodes + let updated = await api.post(`/store/carts/${cart.id}`, { + promo_codes: [createdPromotion.code], + }) + + expect(updated.status).toEqual(200) + expect(updated.data.cart).toEqual( + expect.objectContaining({ + id: cart.id, + items: [ + expect.objectContaining({ + id: "item-1", + adjustments: [ + expect.objectContaining({ + id: expect.not.stringContaining(adjustment.id), + code: createdPromotion.code, + }), + ], + }), + ], + }) + ) + + // Should remove all adjustments from other promo codes + updated = await api.post(`/store/carts/${cart.id}`, { + promo_codes: [], + }) + + expect(updated.status).toEqual(200) + expect(updated.data.cart).toEqual( + expect.objectContaining({ + id: cart.id, + items: [ + expect.objectContaining({ + id: "item-1", + adjustments: [], + }), + ], + }) + ) + }) + + it("should update a cart's region, sales channel and customer data", async () => { + const region = await regionModuleService.create({ + name: "US", + currency_code: "usd", + }) + + const salesChannel = await scModuleService.create({ + name: "Webshop", + }) + + const cart = await cartModuleService.create({ + currency_code: "eur", + }) + + const api = useApi() as any + + let updated = await api.post(`/store/carts/${cart.id}`, { + region_id: region.id, + email: "tony@stark.com", + sales_channel_id: salesChannel.id, + }) + + expect(updated.status).toEqual(200) + expect(updated.data.cart).toEqual( + expect.objectContaining({ + id: cart.id, + currency_code: "usd", + region: expect.objectContaining({ + id: region.id, + currency_code: "usd", + }), + email: "tony@stark.com", + customer: expect.objectContaining({ + email: "tony@stark.com", + }), + sales_channel_id: salesChannel.id, + }) + ) + + updated = await api.post(`/store/carts/${cart.id}`, { + email: null, + sales_channel_id: null, + }) + + expect(updated.status).toEqual(200) + expect(updated.data.cart).toEqual( + expect.objectContaining({ + id: cart.id, + currency_code: "usd", + email: null, + customer_id: null, + region: expect.objectContaining({ + id: region.id, + currency_code: "usd", + }), + sales_channel_id: null, + }) + ) + }) + }) + describe("GET /store/carts/:id", () => { it("should create and update a cart", async () => { const region = await regionModuleService.create({ diff --git a/packages/cart/src/types/cart.ts b/packages/cart/src/types/cart.ts index 46ffcf8358..0e1721f3a6 100644 --- a/packages/cart/src/types/cart.ts +++ b/packages/cart/src/types/cart.ts @@ -14,11 +14,11 @@ export interface CreateCartDTO { export interface UpdateCartDTO { id: string - region_id?: string | null + region_id?: string customer_id?: string | null sales_channel_id?: string | null email?: string | null - currency_code?: string | null + currency_code?: string metadata?: Record | null adjustments?: (CreateLineItemAdjustmentDTO | UpdateLineItemAdjustmentDTO)[] diff --git a/packages/core-flows/src/definition/cart/steps/find-or-create-customer.ts b/packages/core-flows/src/definition/cart/steps/find-or-create-customer.ts index 3ccc4ebbde..40db236869 100644 --- a/packages/core-flows/src/definition/cart/steps/find-or-create-customer.ts +++ b/packages/core-flows/src/definition/cart/steps/find-or-create-customer.ts @@ -4,13 +4,13 @@ import { validateEmail } from "@medusajs/utils" import { StepResponse, createStep } from "@medusajs/workflows-sdk" interface StepInput { - customerId?: string - email?: string + customerId?: string | null + email?: string | null } interface StepOutput { - customer?: CustomerDTO - email?: string + customer?: CustomerDTO | null + email?: string | null } interface StepCompensateInput { @@ -22,11 +22,27 @@ export const findOrCreateCustomerStepId = "find-or-create-customer" export const findOrCreateCustomerStep = createStep( findOrCreateCustomerStepId, async (data: StepInput, { container }) => { + if ( + typeof data.customerId === undefined && + typeof data.email === undefined + ) { + return new StepResponse( + { + customer: undefined, + email: undefined, + }, + { customerWasCreated: false } + ) + } + const service = container.resolve( ModuleRegistrationName.CUSTOMER ) - const customerData: StepOutput = {} + const customerData: StepOutput = { + customer: null, + email: null, + } let customerWasCreated = false if (data.customerId) { diff --git a/packages/core-flows/src/definition/cart/steps/find-sales-channel.ts b/packages/core-flows/src/definition/cart/steps/find-sales-channel.ts index 5dcc9f60b2..bc78528b62 100644 --- a/packages/core-flows/src/definition/cart/steps/find-sales-channel.ts +++ b/packages/core-flows/src/definition/cart/steps/find-sales-channel.ts @@ -4,7 +4,7 @@ import { MedusaError } from "@medusajs/utils" import { StepResponse, createStep } from "@medusajs/workflows-sdk" interface StepInput { - salesChannelId?: string + salesChannelId?: string | null } export const findSalesChannelStepId = "find-sales-channel" @@ -15,6 +15,10 @@ export const findSalesChannelStep = createStep( ModuleRegistrationName.SALES_CHANNEL ) + if (data.salesChannelId === null) { + return new StepResponse(null) + } + let salesChannel: SalesChannelDTO | undefined if (data.salesChannelId) { salesChannel = await salesChannelService.retrieve(data.salesChannelId) diff --git a/packages/core-flows/src/definition/cart/steps/get-actions-to-compute-from-promotions.ts b/packages/core-flows/src/definition/cart/steps/get-actions-to-compute-from-promotions.ts index 3e16e27f23..1efbd492f8 100644 --- a/packages/core-flows/src/definition/cart/steps/get-actions-to-compute-from-promotions.ts +++ b/packages/core-flows/src/definition/cart/steps/get-actions-to-compute-from-promotions.ts @@ -1,12 +1,15 @@ import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { CartDTO, IPromotionModuleService } from "@medusajs/types" -import { deduplicate, isString } from "@medusajs/utils" +import { PromotionActions, deduplicate, isString } from "@medusajs/utils" import { StepResponse, createStep } from "@medusajs/workflows-sdk" interface StepInput { cart: CartDTO - promoCodes: string[] - removePromotions: boolean + promoCodes?: string[] + action: + | PromotionActions.ADD + | PromotionActions.REMOVE + | PromotionActions.REPLACE } export const getActionsToComputeFromPromotionsStepId = @@ -18,7 +21,11 @@ export const getActionsToComputeFromPromotionsStep = createStep( ModuleRegistrationName.PROMOTION ) - const { removePromotions = false, promoCodes = [], cart } = data + const { action = PromotionActions.ADD, promoCodes, cart } = data + + if (!Array.isArray(promoCodes)) { + return new StepResponse([]) + } const appliedItemPromoCodes = cart.items ?.map((item) => item.adjustments?.map((adjustment) => adjustment.code)) @@ -38,12 +45,16 @@ export const getActionsToComputeFromPromotionsStep = createStep( ...appliedShippingMethodPromoCodes, ]) - if (removePromotions) { + if (action === PromotionActions.REMOVE) { promotionCodesToApply = promotionCodesToApply.filter( (code) => !promoCodes.includes(code) ) } + if (action === PromotionActions.REPLACE) { + promotionCodesToApply = promoCodes + } + const actionsToCompute = await promotionModuleService.computeActions( promotionCodesToApply, cart as any diff --git a/packages/core-flows/src/definition/cart/steps/retrieve-cart.ts b/packages/core-flows/src/definition/cart/steps/retrieve-cart.ts index fc70c4ba09..9ce4f9b368 100644 --- a/packages/core-flows/src/definition/cart/steps/retrieve-cart.ts +++ b/packages/core-flows/src/definition/cart/steps/retrieve-cart.ts @@ -3,7 +3,7 @@ import { CartDTO, FindConfig, ICartModuleService } from "@medusajs/types" import { StepResponse, createStep } from "@medusajs/workflows-sdk" interface StepInput { - cartId: string + id: string config: FindConfig } @@ -15,7 +15,7 @@ export const retrieveCartStep = createStep( ModuleRegistrationName.CART ) - const cart = await cartModuleService.retrieve(data.cartId, data.config) + const cart = await cartModuleService.retrieve(data.id, data.config) // TODO: remove this when cart handles totals calculation cart.items = cart.items?.map((item) => { diff --git a/packages/core-flows/src/definition/cart/steps/update-carts.ts b/packages/core-flows/src/definition/cart/steps/update-carts.ts index 978e6d94a6..f152e62da1 100644 --- a/packages/core-flows/src/definition/cart/steps/update-carts.ts +++ b/packages/core-flows/src/definition/cart/steps/update-carts.ts @@ -1,61 +1,53 @@ import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { - CartDTO, - FilterableCartProps, ICartModuleService, - UpdateCartDataDTO, + UpdateCartDTO, + UpdateCartWorkflowInputDTO, } from "@medusajs/types" import { getSelectsAndRelationsFromObjectArray } from "@medusajs/utils" import { StepResponse, createStep } from "@medusajs/workflows-sdk" -type UpdateCartsStepInput = { - selector: FilterableCartProps - update: UpdateCartDataDTO -} - export const updateCartsStepId = "update-carts" export const updateCartsStep = createStep( updateCartsStepId, - async (data: UpdateCartsStepInput, { container }) => { - const service = container.resolve( + async (data: UpdateCartWorkflowInputDTO[], { container }) => { + const cartModule = container.resolve( ModuleRegistrationName.CART ) - const { selects, relations } = getSelectsAndRelationsFromObjectArray([ - data.update, - ]) - - const prevCarts = await service.list(data.selector, { - select: selects, - relations, - }) - - const updatedCarts = await service.update( - data.selector as Partial, - data.update + const { selects, relations } = getSelectsAndRelationsFromObjectArray(data) + const cartsBeforeUpdate = await cartModule.list( + { id: data.map((d) => d.id) }, + { select: selects, relations } ) - return new StepResponse(updatedCarts, prevCarts) + const updatedCart = await cartModule.update(data) + + return new StepResponse(updatedCart, cartsBeforeUpdate) }, - async (previousCarts, { container }) => { - if (!previousCarts?.length) { + async (cartsBeforeUpdate, { container }) => { + if (!cartsBeforeUpdate) { return } - const service = container.resolve( + const cartModule = container.resolve( ModuleRegistrationName.CART ) - const toRestore = previousCarts.map((c) => ({ - id: c.id, - region_id: c.region_id, - customer_id: c.customer_id, - sales_channel_id: c.sales_channel_id, - email: c.email, - currency_code: c.currency_code, - metadata: c.metadata, - })) + const dataToUpdate: UpdateCartDTO[] = [] - await service.update(toRestore) + for (const cart of cartsBeforeUpdate) { + dataToUpdate.push({ + id: cart.id, + region_id: cart.region_id, + customer_id: cart.customer_id, + sales_channel_id: cart.sales_channel_id, + email: cart.email, + currency_code: cart.currency_code, + metadata: cart.metadata, + }) + } + + await cartModule.update(dataToUpdate) } ) diff --git a/packages/core-flows/src/definition/cart/workflows/index.ts b/packages/core-flows/src/definition/cart/workflows/index.ts index d5a918fb5d..f39530663d 100644 --- a/packages/core-flows/src/definition/cart/workflows/index.ts +++ b/packages/core-flows/src/definition/cart/workflows/index.ts @@ -1,4 +1,4 @@ export * from "./add-to-cart" export * from "./create-carts" +export * from "./update-cart" export * from "./update-cart-promotions" -export * from "./update-carts" diff --git a/packages/core-flows/src/definition/cart/workflows/update-cart-promotions.ts b/packages/core-flows/src/definition/cart/workflows/update-cart-promotions.ts index c86469f5c7..dd4550e5ca 100644 --- a/packages/core-flows/src/definition/cart/workflows/update-cart-promotions.ts +++ b/packages/core-flows/src/definition/cart/workflows/update-cart-promotions.ts @@ -1,4 +1,5 @@ import { CartDTO } from "@medusajs/types" +import { PromotionActions } from "@medusajs/utils" import { WorkflowData, createWorkflow, @@ -17,7 +18,10 @@ import { type WorkflowInput = { promoCodes: string[] cartId: string - removePromotions?: boolean + action?: + | PromotionActions.ADD + | PromotionActions.REMOVE + | PromotionActions.REPLACE } export const updateCartPromotionsWorkflowId = "update-cart-promotions" @@ -25,7 +29,7 @@ export const updateCartPromotionsWorkflow = createWorkflow( updateCartPromotionsWorkflowId, (input: WorkflowData): WorkflowData => { const retrieveCartInput = { - cartId: input.cartId, + id: input.cartId, config: { relations: [ "items", @@ -40,7 +44,7 @@ export const updateCartPromotionsWorkflow = createWorkflow( const actions = getActionsToComputeFromPromotionsStep({ cart, promoCodes: input.promoCodes, - removePromotions: input.removePromotions || false, + action: input.action || PromotionActions.ADD, }) const { diff --git a/packages/core-flows/src/definition/cart/workflows/update-cart.ts b/packages/core-flows/src/definition/cart/workflows/update-cart.ts new file mode 100644 index 0000000000..b5ac88d851 --- /dev/null +++ b/packages/core-flows/src/definition/cart/workflows/update-cart.ts @@ -0,0 +1,112 @@ +import { CartDTO, UpdateCartWorkflowInputDTO } from "@medusajs/types" +import { PromotionActions, isPresent } from "@medusajs/utils" +import { + WorkflowData, + createWorkflow, + parallelize, + transform, +} from "@medusajs/workflows-sdk" +import { + createLineItemAdjustmentsStep, + createShippingMethodAdjustmentsStep, + findOneOrAnyRegionStep, + findOrCreateCustomerStep, + findSalesChannelStep, + getActionsToComputeFromPromotionsStep, + prepareAdjustmentsFromPromotionActionsStep, + removeLineItemAdjustmentsStep, + removeShippingMethodAdjustmentsStep, + retrieveCartStep, + updateCartsStep, +} from "../steps" + +export const updateCartWorkflowId = "update-cart" +export const updateCartWorkflow = createWorkflow( + updateCartWorkflowId, + (input: WorkflowData): WorkflowData => { + const retrieveCartInput = { + id: input.id, + config: { + relations: [ + "items", + "items.adjustments", + "shipping_methods", + "shipping_methods.adjustments", + ], + }, + } + + const [salesChannel, region, customerData] = parallelize( + findSalesChannelStep({ + salesChannelId: input.sales_channel_id, + }), + findOneOrAnyRegionStep({ + regionId: input.region_id, + }), + findOrCreateCustomerStep({ + customerId: input.customer_id, + email: input.email, + }) + ) + + const cartInput = transform( + { input, region, customerData, salesChannel }, + (data) => { + const { promo_codes, ...updateCartData } = data.input + const data_ = { ...updateCartData } + + if (isPresent(updateCartData.region_id)) { + data_.currency_code = data.region.currency_code + data_.region_id = data.region.id + } + + if ( + updateCartData.customer_id !== undefined || + updateCartData.email !== undefined + ) { + data_.customer_id = data.customerData.customer?.id || null + data_.email = + data.input?.email ?? (data.customerData.customer?.email || null) + } + + if (updateCartData.sales_channel_id !== undefined) { + data_.sales_channel_id = data.salesChannel?.id || null + } + + return data_ + } + ) + + updateCartsStep([cartInput]) + + const cart = retrieveCartStep(retrieveCartInput) + const actions = getActionsToComputeFromPromotionsStep({ + cart, + promoCodes: input.promo_codes, + action: PromotionActions.REPLACE, + }) + + const { + lineItemAdjustmentsToCreate, + lineItemAdjustmentIdsToRemove, + shippingMethodAdjustmentsToCreate, + shippingMethodAdjustmentIdsToRemove, + } = prepareAdjustmentsFromPromotionActionsStep({ actions }) + + parallelize( + removeLineItemAdjustmentsStep({ lineItemAdjustmentIdsToRemove }), + removeShippingMethodAdjustmentsStep({ + shippingMethodAdjustmentIdsToRemove, + }) + ) + + parallelize( + createLineItemAdjustmentsStep({ lineItemAdjustmentsToCreate }), + createShippingMethodAdjustmentsStep({ shippingMethodAdjustmentsToCreate }) + ) + + return retrieveCartStep(retrieveCartInput).config({ + name: "retrieve-cart-result-step", + }) + } +) diff --git a/packages/core-flows/src/definition/cart/workflows/update-carts.ts b/packages/core-flows/src/definition/cart/workflows/update-carts.ts deleted file mode 100644 index b7f5b07a36..0000000000 --- a/packages/core-flows/src/definition/cart/workflows/update-carts.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { - CartDTO, - FilterableCartProps, - UpdateCartDataDTO, -} from "@medusajs/types" -import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk" -import { updateCartsStep } from "../steps/update-carts" - -type WorkflowInput = { - selector: FilterableCartProps - update: UpdateCartDataDTO -} - -export const updateCartsWorkflowId = "update-carts" -export const updateCartsWorkflow = createWorkflow( - updateCartsWorkflowId, - (input: WorkflowData): WorkflowData => { - return updateCartsStep(input) - } -) diff --git a/packages/medusa/src/api-v2/store/carts/[id]/promotions/route.ts b/packages/medusa/src/api-v2/store/carts/[id]/promotions/route.ts index 7bb51a6955..fd334b975b 100644 --- a/packages/medusa/src/api-v2/store/carts/[id]/promotions/route.ts +++ b/packages/medusa/src/api-v2/store/carts/[id]/promotions/route.ts @@ -1,4 +1,5 @@ import { updateCartPromotionsWorkflow } from "@medusajs/core-flows" +import { PromotionActions } from "@medusajs/utils" import { MedusaRequest, MedusaResponse } from "../../../../../types/routing" import { StorePostCartsCartPromotionsReq } from "../../validators" @@ -10,6 +11,7 @@ export const POST = async (req: MedusaRequest, res: MedusaResponse) => { input: { promoCodes: payload.promo_codes, cartId: req.params.id, + action: PromotionActions.ADD, }, throwOnError: false, }) @@ -29,7 +31,7 @@ export const DELETE = async (req: MedusaRequest, res: MedusaResponse) => { input: { promoCodes: payload.promo_codes, cartId: req.params.id, - removePromotions: true, + action: PromotionActions.REMOVE, }, throwOnError: false, }) diff --git a/packages/medusa/src/api-v2/store/carts/[id]/route.ts b/packages/medusa/src/api-v2/store/carts/[id]/route.ts index e392616790..dda5cf38af 100644 --- a/packages/medusa/src/api-v2/store/carts/[id]/route.ts +++ b/packages/medusa/src/api-v2/store/carts/[id]/route.ts @@ -1,9 +1,9 @@ -import { MedusaRequest, MedusaResponse } from "../../../../types/routing" - +import { updateCartWorkflow } from "@medusajs/core-flows" import { UpdateCartDataDTO } from "@medusajs/types" -import { defaultStoreCartFields } from "../query-config" import { remoteQueryObjectFromString } from "@medusajs/utils" -import { updateCartsWorkflow } from "@medusajs/core-flows" + +import { MedusaRequest, MedusaResponse } from "../../../../types/routing" +import { defaultStoreCartFields } from "../query-config" export const GET = async (req: MedusaRequest, res: MedusaResponse) => { const remoteQuery = req.scope.resolve("remoteQuery") @@ -24,15 +24,13 @@ export const POST = async ( req: MedusaRequest, res: MedusaResponse ) => { - const updateCartWorkflow = updateCartsWorkflow(req.scope) + const workflow = updateCartWorkflow(req.scope) - const workflowInput = { - selector: { id: req.params.id }, - update: req.validatedBody, - } - - const { result, errors } = await updateCartWorkflow.run({ - input: workflowInput, + const { errors } = await workflow.run({ + input: { + ...(req.validatedBody as UpdateCartDataDTO), + id: req.params.id, + }, throwOnError: false, }) @@ -40,5 +38,16 @@ export const POST = async ( throw errors[0].error } - res.status(200).json({ cart: result[0] }) + const remoteQuery = req.scope.resolve("remoteQuery") + + const query = remoteQueryObjectFromString({ + entryPoint: "cart", + fields: defaultStoreCartFields, + }) + + const [updatedCart] = await remoteQuery(query, { + cart: { id: req.params.id }, + }) + + res.status(200).json({ cart: updatedCart }) } diff --git a/packages/medusa/src/api-v2/store/carts/query-config.ts b/packages/medusa/src/api-v2/store/carts/query-config.ts index 4139bcee31..3d5fb30aa6 100644 --- a/packages/medusa/src/api-v2/store/carts/query-config.ts +++ b/packages/medusa/src/api-v2/store/carts/query-config.ts @@ -10,8 +10,14 @@ export const defaultStoreCartFields = [ "items.title", "items.quantity", "items.unit_price", + "items.adjustments.id", + "items.adjustments.code", + "items.adjustments.amount", "customer.id", "customer.email", + "shipping_methods.adjustments.id", + "shipping_methods.adjustments.code", + "shipping_methods.adjustments.amount", "shipping_address.id", "shipping_address.first_name", "shipping_address.last_name", @@ -40,20 +46,24 @@ export const defaultStoreCartFields = [ export const defaultStoreCartRelations = [ "items", + "items.adjustments", "region", "customer", "shipping_address", "billing_address", "shipping_methods", + "shipping_methods.adjustments", ] export const allowedRelations = [ "items", + "items.adjustments", "region", "customer", "shipping_address", "billing_address", "shipping_methods", + "shipping_methods.adjustments", "sales_channel", ] diff --git a/packages/medusa/src/api-v2/store/carts/validators.ts b/packages/medusa/src/api-v2/store/carts/validators.ts index 90c6e26495..041ed2f5e4 100644 --- a/packages/medusa/src/api-v2/store/carts/validators.ts +++ b/packages/medusa/src/api-v2/store/carts/validators.ts @@ -81,14 +81,19 @@ export class StorePostCartsCartReq { @IsType([AddressPayload, String]) shipping_address?: AddressPayload | string - @IsEmail() @IsOptional() + @IsString() sales_channel_id?: string @IsObject() @IsOptional() metadata?: Record + @IsOptional() + @IsArray() + @Type(() => String) + promo_codes?: string[] + // @IsOptional() // @IsArray() // @ValidateNested({ each: true }) diff --git a/packages/types/src/cart/mutations.ts b/packages/types/src/cart/mutations.ts index 0a20438f1e..6c0d00fbcb 100644 --- a/packages/types/src/cart/mutations.ts +++ b/packages/types/src/cart/mutations.ts @@ -41,12 +41,12 @@ export interface CreateCartDTO { } export interface UpdateCartDataDTO { - region_id?: string | null + region_id?: string customer_id?: string | null sales_channel_id?: string | null email?: string | null - currency_code?: string | null + currency_code?: string shipping_address_id?: string | null billing_address_id?: string | null @@ -58,7 +58,7 @@ export interface UpdateCartDataDTO { } export interface UpdateCartDTO extends UpdateCartDataDTO { - id?: string + id: string } /** CART END */ @@ -176,7 +176,7 @@ export interface UpdateLineItemWithSelectorDTO { export interface UpdateCartWithSelectorDTO { selector: Partial - data: UpdateCartDTO + data: UpdateCartDataDTO } export interface UpdateLineItemDTO diff --git a/packages/types/src/cart/workflows.ts b/packages/types/src/cart/workflows.ts index d7a808e216..174525aae6 100644 --- a/packages/types/src/cart/workflows.ts +++ b/packages/types/src/cart/workflows.ts @@ -65,3 +65,14 @@ export interface AddToCartWorkflowInputDTO { items: CreateCartCreateLineItemDTO[] cart: CartDTO } + +export interface UpdateCartWorkflowInputDTO { + id: string + promo_codes?: string[] + region_id?: string + customer_id?: string | null + sales_channel_id?: string | null + email?: string | null + currency_code?: string + metadata?: Record | null +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 3958e296eb..59efe99812 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,4 +1,5 @@ export * from "./address" +export * from "./api-key" export * from "./auth" export * from "./bundles" export * from "./cache" @@ -27,10 +28,9 @@ export * from "./sales-channel" export * from "./search" export * from "./shared-context" export * from "./stock-location" +export * from "./store" export * from "./tax" export * from "./totals" export * from "./transaction-base" export * from "./user" export * from "./workflow" -export * from "./api-key" -export * from "./store" diff --git a/packages/utils/src/promotion/index.ts b/packages/utils/src/promotion/index.ts index f3dc9d2409..453558124f 100644 --- a/packages/utils/src/promotion/index.ts +++ b/packages/utils/src/promotion/index.ts @@ -41,3 +41,9 @@ export enum ComputedActions { REMOVE_SHIPPING_METHOD_ADJUSTMENT = "removeShippingMethodAdjustment", CAMPAIGN_BUDGET_EXCEEDED = "campaignBudgetExceeded", } + +export enum PromotionActions { + ADD = "add", + REMOVE = "remove", + REPLACE = "replace", +}