diff --git a/.changeset/sixty-mice-sleep.md b/.changeset/sixty-mice-sleep.md new file mode 100644 index 0000000000000..ef821a610ee07 --- /dev/null +++ b/.changeset/sixty-mice-sleep.md @@ -0,0 +1,5 @@ +--- +"@medusajs/types": patch +--- + +feat(cart): Shipping method adjustments diff --git a/packages/cart/integration-tests/__tests__/services/cart-module/index.spec.ts b/packages/cart/integration-tests/__tests__/services/cart-module/index.spec.ts index 0e467ce23ea6c..d07367ecb1523 100644 --- a/packages/cart/integration-tests/__tests__/services/cart-module/index.spec.ts +++ b/packages/cart/integration-tests/__tests__/services/cart-module/index.spec.ts @@ -1005,6 +1005,7 @@ describe("Cart Module Service", () => { await service.setLineItemAdjustments(createdCart.id, [ { id: adjustments[0].id, + item_id: itemOne.id, amount: 50, code: "50%", }, @@ -1310,4 +1311,553 @@ describe("Cart Module Service", () => { expect(adjustments?.length).toBe(0) }) }) + + describe("setShippingMethodAdjustments", () => { + it("should set shipping method adjustments for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [shippingMethodOne] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) + + const [shippingMethodTwo] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 200, + name: "test-2", + }, + ] + ) + + const adjustments = await service.setShippingMethodAdjustments( + createdCart.id, + [ + { + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }, + { + shipping_method_id: shippingMethodTwo.id, + amount: 200, + code: "FREE-2", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }), + expect.objectContaining({ + shipping_method_id: shippingMethodTwo.id, + amount: 200, + code: "FREE-2", + }), + ]) + ) + }) + + it("should replace shipping method adjustments for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [shippingMethodOne] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) + + const adjustments = await service.setShippingMethodAdjustments( + createdCart.id, + [ + { + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }), + ]) + ) + + await service.setShippingMethodAdjustments(createdCart.id, [ + { + shipping_method_id: shippingMethodOne.id, + amount: 50, + code: "50%", + }, + ]) + + const cart = await service.retrieve(createdCart.id, { + relations: ["shipping_methods.adjustments"], + }) + + expect(cart.shipping_methods).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: shippingMethodOne.id, + cart_id: createdCart.id, + adjustments: expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodOne.id, + amount: 50, + code: "50%", + }), + ]), + }), + ]) + ) + + expect(cart.shipping_methods?.length).toBe(1) + expect(cart.shipping_methods?.[0].adjustments?.length).toBe(1) + }) + + it("should remove all shipping method adjustments for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [shippingMethodOne] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) + + const adjustments = await service.setShippingMethodAdjustments( + createdCart.id, + [ + { + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }), + ]) + ) + + await service.setShippingMethodAdjustments(createdCart.id, []) + + const cart = await service.retrieve(createdCart.id, { + relations: ["shipping_methods.adjustments"], + }) + + expect(cart.shipping_methods).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: shippingMethodOne.id, + adjustments: [], + }), + ]) + ) + + expect(cart.shipping_methods?.length).toBe(1) + expect(cart.shipping_methods?.[0].adjustments?.length).toBe(0) + }) + + it("should update shipping method adjustments for a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [shippingMethodOne] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) + + const adjustments = await service.setShippingMethodAdjustments( + createdCart.id, + [ + { + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }), + ]) + ) + + await service.setShippingMethodAdjustments(createdCart.id, [ + { + id: adjustments[0].id, + amount: 50, + code: "50%", + }, + ]) + + const cart = await service.retrieve(createdCart.id, { + relations: ["shipping_methods.adjustments"], + }) + + expect(cart.shipping_methods).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: shippingMethodOne.id, + adjustments: [ + expect.objectContaining({ + id: adjustments[0].id, + shipping_method_id: shippingMethodOne.id, + amount: 50, + code: "50%", + }), + ], + }), + ]) + ) + + expect(cart.shipping_methods?.length).toBe(1) + expect(cart.shipping_methods?.[0].adjustments?.length).toBe(1) + }) + }) + + describe("addShippingMethodAdjustments", () => { + it("should add shipping method adjustments in a cart", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [shippingMethodOne] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) + + const adjustments = await service.addShippingMethodAdjustments( + createdCart.id, + [ + { + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }), + ]) + ) + }) + + it("should add multiple shipping method adjustments for multiple shipping methods", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [shippingMethodOne] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) + const [shippingMethodTwo] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 200, + name: "test-2", + }, + ] + ) + + const adjustments = await service.addShippingMethodAdjustments( + createdCart.id, + [ + { + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }, + { + shipping_method_id: shippingMethodTwo.id, + amount: 150, + code: "CODE-2", + }, + ] + ) + + expect(adjustments).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }), + expect.objectContaining({ + shipping_method_id: shippingMethodTwo.id, + amount: 150, + code: "CODE-2", + }), + ]) + ) + }) + + it("should add shipping method adjustments for shipping methods on multiple carts", async () => { + const [cartOne] = await service.create([ + { + currency_code: "eur", + }, + ]) + const [cartTwo] = await service.create([ + { + currency_code: "usd", + }, + ]) + + const [shippingMethodOne] = await service.addShippingMethods(cartOne.id, [ + { + amount: 100, + name: "test", + }, + ]) + const [shippingMethodTwo] = await service.addShippingMethods(cartTwo.id, [ + { + amount: 200, + name: "test-2", + }, + ]) + + await service.addShippingMethodAdjustments([ + // item from cart one + { + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }, + // item from cart two + { + shipping_method_id: shippingMethodTwo.id, + amount: 150, + code: "CODE-2", + }, + ]) + + const cartOneMethods = await service.listShippingMethods( + { cart_id: cartOne.id }, + { relations: ["adjustments"] } + ) + + const cartTwoMethods = await service.listShippingMethods( + { cart_id: cartTwo.id }, + { relations: ["adjustments"] } + ) + + expect(cartOneMethods).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + adjustments: expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }), + ]), + }), + ]) + ) + expect(cartTwoMethods).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + adjustments: expect.arrayContaining([ + expect.objectContaining({ + shipping_method_id: shippingMethodTwo.id, + amount: 150, + code: "CODE-2", + }), + ]), + }), + ]) + ) + }) + + it("should throw if shipping method is not associated with cart", async () => { + const [cartOne] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [cartTwo] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [shippingMethodOne] = await service.addShippingMethods(cartOne.id, [ + { + amount: 100, + name: "test", + }, + ]) + + const error = await service + .addShippingMethodAdjustments(cartTwo.id, [ + { + shipping_method_id: shippingMethodOne.id, + amount: 100, + code: "FREE", + }, + ]) + .catch((e) => e) + + expect(error.message).toBe( + `Shipping method with id ${shippingMethodOne.id} does not exist on cart with id ${cartTwo.id}` + ) + }) + }) + + describe("removeShippingMethodAdjustments", () => { + it("should remove a shipping method succesfully", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [method] = await service.addShippingMethods(createdCart.id, [ + { + amount: 100, + name: "test", + }, + ]) + + const [adjustment] = await service.addShippingMethodAdjustments( + createdCart.id, + [ + { + shipping_method_id: method.id, + amount: 50, + code: "50%", + }, + ] + ) + + expect(adjustment.shipping_method_id).toBe(method.id) + + await service.removeShippingMethodAdjustments(adjustment.id) + + const adjustments = await service.listShippingMethodAdjustments({ + shipping_method_id: method.id, + }) + + expect(adjustments?.length).toBe(0) + }) + + it("should remove a shipping method succesfully with selector", async () => { + const [createdCart] = await service.create([ + { + currency_code: "eur", + }, + ]) + + const [shippingMethod] = await service.addShippingMethods( + createdCart.id, + [ + { + amount: 100, + name: "test", + }, + ] + ) + + const [adjustment] = await service.addShippingMethodAdjustments( + createdCart.id, + [ + { + shipping_method_id: shippingMethod.id, + amount: 50, + code: "50%", + }, + ] + ) + + expect(adjustment.shipping_method_id).toBe(shippingMethod.id) + + await service.removeShippingMethodAdjustments({ + shipping_method_id: shippingMethod.id, + }) + + const adjustments = await service.listShippingMethodAdjustments({ + shipping_method_id: shippingMethod.id, + }) + + expect(adjustments?.length).toBe(0) + }) + }) }) diff --git a/packages/cart/src/models/index.ts b/packages/cart/src/models/index.ts index 33f78f0616d42..7f5c45b194832 100644 --- a/packages/cart/src/models/index.ts +++ b/packages/cart/src/models/index.ts @@ -4,6 +4,6 @@ export { default as LineItem } from "./line-item" export { default as LineItemAdjustment } from "./line-item-adjustment" export { default as LineItemTaxLine } from "./line-item-tax-line" export { default as ShippingMethod } from "./shipping-method" -export { default as ShippingMethodAdjustmentLine } from "./shipping-method-adjustment-line" +export { default as ShippingMethodAdjustment } from "./shipping-method-adjustment" export { default as ShippingMethodTaxLine } from "./shipping-method-tax-line" diff --git a/packages/cart/src/models/line-item-adjustment.ts b/packages/cart/src/models/line-item-adjustment.ts index 7e355ca463e04..341b22b793de4 100644 --- a/packages/cart/src/models/line-item-adjustment.ts +++ b/packages/cart/src/models/line-item-adjustment.ts @@ -10,7 +10,7 @@ import { import AdjustmentLine from "./adjustment-line" import LineItem from "./line-item" -@Entity({ tableName: "cart_line_item_adjustment_line" }) +@Entity({ tableName: "cart_line_item_adjustment" }) @Check({ expression: (columns) => `${columns.amount} >= 0`, }) @@ -18,7 +18,7 @@ export default class LineItemAdjustment extends AdjustmentLine { @ManyToOne(() => LineItem, { onDelete: "cascade", nullable: true, - index: "IDX_adjustment_line_item_id", + index: "IDX_adjustment_item_id", }) item?: LineItem | null diff --git a/packages/cart/src/models/shipping-method-adjustment-line.ts b/packages/cart/src/models/shipping-method-adjustment-line.ts deleted file mode 100644 index e56e82313ab1d..0000000000000 --- a/packages/cart/src/models/shipping-method-adjustment-line.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { generateEntityId } from "@medusajs/utils" -import { BeforeCreate, Entity, ManyToOne, OnInit } from "@mikro-orm/core" -import AdjustmentLine from "./adjustment-line" -import ShippingMethod from "./shipping-method" - -@Entity({ tableName: "cart_shipping_method_adjustment_line" }) -export default class ShippingMethodAdjustmentLine extends AdjustmentLine { - @ManyToOne(() => ShippingMethod, { - joinColumn: "shipping_method", - fieldName: "shipping_method_id", - }) - shipping_method: ShippingMethod - - @BeforeCreate() - onCreate() { - this.id = generateEntityId(this.id, "casmadj") - } - - @OnInit() - onInit() { - this.id = generateEntityId(this.id, "casmadj") - } -} diff --git a/packages/cart/src/models/shipping-method-adjustment.ts b/packages/cart/src/models/shipping-method-adjustment.ts new file mode 100644 index 0000000000000..63b647379165d --- /dev/null +++ b/packages/cart/src/models/shipping-method-adjustment.ts @@ -0,0 +1,33 @@ +import { generateEntityId } from "@medusajs/utils" +import { + BeforeCreate, + Entity, + ManyToOne, + OnInit, + Property, +} from "@mikro-orm/core" +import AdjustmentLine from "./adjustment-line" +import ShippingMethod from "./shipping-method" + +@Entity({ tableName: "cart_shipping_method_adjustment" }) +export default class ShippingMethodAdjustment extends AdjustmentLine { + @ManyToOne(() => ShippingMethod, { + onDelete: "cascade", + nullable: true, + index: "IDX_adjustment_shipping_method_id", + }) + shipping_method: ShippingMethod | null + + @Property({ columnType: "text" }) + shipping_method_id: string + + @BeforeCreate() + onCreate() { + this.id = generateEntityId(this.id, "casmadj") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "casmadj") + } +} diff --git a/packages/cart/src/models/shipping-method.ts b/packages/cart/src/models/shipping-method.ts index 9d8eab459c31e..a46bbcc6b635f 100644 --- a/packages/cart/src/models/shipping-method.ts +++ b/packages/cart/src/models/shipping-method.ts @@ -12,7 +12,7 @@ import { Property, } from "@mikro-orm/core" import Cart from "./cart" -import ShippingMethodAdjustmentLine from "./shipping-method-adjustment-line" +import ShippingMethodAdjustment from "./shipping-method-adjustment" import ShippingMethodTaxLine from "./shipping-method-tax-line" @Entity({ tableName: "cart_shipping_method" }) @@ -62,13 +62,13 @@ export default class ShippingMethod { tax_lines = new Collection(this) @OneToMany( - () => ShippingMethodAdjustmentLine, + () => ShippingMethodAdjustment, (adjustment) => adjustment.shipping_method, { cascade: [Cascade.REMOVE], } ) - adjustments = new Collection(this) + adjustments = new Collection(this) /** COMPUTED PROPERTIES - START */ diff --git a/packages/cart/src/services/cart-module.ts b/packages/cart/src/services/cart-module.ts index f9d479284cf1f..255253c3ba0e7 100644 --- a/packages/cart/src/services/cart-module.ts +++ b/packages/cart/src/services/cart-module.ts @@ -15,7 +15,13 @@ import { isObject, isString, } from "@medusajs/utils" -import { Cart, LineItem, LineItemAdjustment, ShippingMethod } from "@models" +import { + Cart, + LineItem, + LineItemAdjustment, + ShippingMethod, + ShippingMethodAdjustment, +} from "@models" import { CreateLineItemDTO, UpdateLineItemDTO } from "@types" import { joinerConfig } from "../joiner-config" import * as services from "../services" @@ -25,6 +31,7 @@ type InjectedDependencies = { cartService: services.CartService addressService: services.AddressService lineItemService: services.LineItemService + shippingMethodAdjustmentService: services.ShippingMethodAdjustmentService shippingMethodService: services.ShippingMethodService lineItemAdjustmentService: services.LineItemAdjustmentService } @@ -34,6 +41,7 @@ export default class CartModuleService implements ICartModuleService { protected cartService_: services.CartService protected addressService_: services.AddressService protected lineItemService_: services.LineItemService + protected shippingMethodAdjustmentService_: services.ShippingMethodAdjustmentService protected shippingMethodService_: services.ShippingMethodService protected lineItemAdjustmentService_: services.LineItemAdjustmentService @@ -43,6 +51,7 @@ export default class CartModuleService implements ICartModuleService { cartService, addressService, lineItemService, + shippingMethodAdjustmentService, shippingMethodService, lineItemAdjustmentService, }: InjectedDependencies, @@ -52,6 +61,7 @@ export default class CartModuleService implements ICartModuleService { this.cartService_ = cartService this.addressService_ = addressService this.lineItemService_ = lineItemService + this.shippingMethodAdjustmentService_ = shippingMethodAdjustmentService this.shippingMethodService_ = shippingMethodService this.lineItemAdjustmentService_ = lineItemAdjustmentService } @@ -584,7 +594,7 @@ export default class CartModuleService implements ICartModuleService { ): Promise async addShippingMethods( cartId: string, - methods: CartTypes.CreateShippingMethodDTO[], + methods: CartTypes.CreateShippingMethodForSingleCartDTO[], sharedContext?: Context ): Promise @@ -618,9 +628,7 @@ export default class CartModuleService implements ICartModuleService { return await this.baseRepository_.serialize< CartTypes.CartShippingMethodDTO[] - >(methods, { - populate: true, - }) + >(methods, { populate: true }) } @InjectTransactionManager("baseRepository_") @@ -882,4 +890,214 @@ export default class CartModuleService implements ICartModuleService { await this.lineItemAdjustmentService_.delete(ids, sharedContext) } + + @InjectManager("baseRepository_") + async listShippingMethodAdjustments( + filters: CartTypes.FilterableShippingMethodAdjustmentProps = {}, + config: FindConfig = {}, + @MedusaContext() sharedContext: Context = {} + ) { + const adjustments = await this.shippingMethodAdjustmentService_.list( + filters, + config, + sharedContext + ) + + return await this.baseRepository_.serialize< + CartTypes.ShippingMethodAdjustmentDTO[] + >(adjustments, { + populate: true, + }) + } + + @InjectTransactionManager("baseRepository_") + async setShippingMethodAdjustments( + cartId: string, + adjustments: ( + | CartTypes.CreateShippingMethodAdjustmentDTO + | CartTypes.UpdateShippingMethodAdjustmentDTO + )[], + @MedusaContext() sharedContext: Context = {} + ): Promise { + const cart = await this.retrieve( + cartId, + { select: ["id"], relations: ["shipping_methods.adjustments"] }, + sharedContext + ) + + const methodIds = cart.shipping_methods?.map((method) => method.id) + + const existingAdjustments = await this.listShippingMethodAdjustments( + { shipping_method_id: methodIds ?? [] }, + { select: ["id"] }, + sharedContext + ) + + let toUpdate: CartTypes.UpdateShippingMethodAdjustmentDTO[] = [] + let toCreate: CartTypes.CreateShippingMethodAdjustmentDTO[] = [] + for (const adj of adjustments) { + if ("id" in adj) { + toUpdate.push(adj as CartTypes.UpdateShippingMethodAdjustmentDTO) + } else { + toCreate.push(adj as CartTypes.CreateShippingMethodAdjustmentDTO) + } + } + + const adjustmentsSet = new Set(toUpdate.map((a) => a.id)) + + const toDelete: CartTypes.ShippingMethodAdjustmentDTO[] = [] + + // From the existing adjustments, find the ones that are not passed in adjustments + existingAdjustments.forEach( + (adj: CartTypes.ShippingMethodAdjustmentDTO) => { + if (!adjustmentsSet.has(adj.id)) { + toDelete.push(adj) + } + } + ) + + if (toDelete.length) { + await this.shippingMethodAdjustmentService_.delete( + toDelete.map((adj) => adj!.id), + sharedContext + ) + } + + let result: ShippingMethodAdjustment[] = [] + + if (toCreate.length) { + const created = await this.shippingMethodAdjustmentService_.create( + toCreate, + sharedContext + ) + + result.push(...created) + } + + if (toUpdate.length) { + const updated = await this.shippingMethodAdjustmentService_.update( + toUpdate, + sharedContext + ) + result.push(...updated) + } + + return await this.baseRepository_.serialize< + CartTypes.ShippingMethodAdjustmentDTO[] + >(result, { + populate: true, + }) + } + + async addShippingMethodAdjustments( + adjustments: CartTypes.CreateShippingMethodAdjustmentDTO[] + ): Promise + async addShippingMethodAdjustments( + adjustment: CartTypes.CreateShippingMethodAdjustmentDTO + ): Promise + async addShippingMethodAdjustments( + cartId: string, + adjustments: CartTypes.CreateShippingMethodAdjustmentDTO[], + sharedContext?: Context + ): Promise + + @InjectTransactionManager("baseRepository_") + async addShippingMethodAdjustments( + cartIdOrData: + | string + | CartTypes.CreateShippingMethodAdjustmentDTO[] + | CartTypes.CreateShippingMethodAdjustmentDTO, + adjustments?: CartTypes.CreateShippingMethodAdjustmentDTO[], + @MedusaContext() sharedContext: Context = {} + ): Promise< + | CartTypes.ShippingMethodAdjustmentDTO[] + | CartTypes.ShippingMethodAdjustmentDTO + > { + let addedAdjustments: ShippingMethodAdjustment[] = [] + if (isString(cartIdOrData)) { + const cart = await this.retrieve( + cartIdOrData, + { select: ["id"], relations: ["shipping_methods"] }, + sharedContext + ) + + const methodIds = cart.shipping_methods?.map((method) => method.id) + + for (const adj of adjustments || []) { + if (!methodIds?.includes(adj.shipping_method_id)) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + `Shipping method with id ${adj.shipping_method_id} does not exist on cart with id ${cartIdOrData}` + ) + } + } + + addedAdjustments = await this.shippingMethodAdjustmentService_.create( + adjustments as CartTypes.CreateShippingMethodAdjustmentDTO[], + sharedContext + ) + } else { + const data = Array.isArray(cartIdOrData) ? cartIdOrData : [cartIdOrData] + + addedAdjustments = await this.shippingMethodAdjustmentService_.create( + data as CartTypes.CreateShippingMethodAdjustmentDTO[], + sharedContext + ) + } + + if (isObject(cartIdOrData)) { + return await this.baseRepository_.serialize( + addedAdjustments[0], + { + populate: true, + } + ) + } + + return await this.baseRepository_.serialize< + CartTypes.ShippingMethodAdjustmentDTO[] + >(addedAdjustments, { + populate: true, + }) + } + + async removeShippingMethodAdjustments( + adjustmentIds: string[], + sharedContext?: Context + ): Promise + async removeShippingMethodAdjustments( + adjustmentId: string, + sharedContext?: Context + ): Promise + async removeShippingMethodAdjustments( + selector: Partial, + sharedContext?: Context + ): Promise + + async removeShippingMethodAdjustments( + adjustmentIdsOrSelector: + | string + | string[] + | Partial, + @MedusaContext() sharedContext: Context = {} + ): Promise { + let ids: string[] = [] + if (isObject(adjustmentIdsOrSelector)) { + const adjustments = await this.listShippingMethodAdjustments( + { + ...adjustmentIdsOrSelector, + } as Partial, + { select: ["id"] }, + sharedContext + ) + + ids = adjustments.map((adj) => adj.id) + } else { + ids = Array.isArray(adjustmentIdsOrSelector) + ? adjustmentIdsOrSelector + : [adjustmentIdsOrSelector] + } + + await this.shippingMethodAdjustmentService_.delete(ids, sharedContext) + } } diff --git a/packages/cart/src/services/index.ts b/packages/cart/src/services/index.ts index c982c3e826ca3..cd2616864ad55 100644 --- a/packages/cart/src/services/index.ts +++ b/packages/cart/src/services/index.ts @@ -4,4 +4,5 @@ export { default as CartModuleService } from "./cart-module" export { default as LineItemService } from "./line-item" export { default as LineItemAdjustmentService } from "./line-item-adjustment" export { default as ShippingMethodService } from "./shipping-method" +export { default as ShippingMethodAdjustmentService } from "./shipping-method-adjustment" diff --git a/packages/cart/src/services/shipping-method-adjustment.ts b/packages/cart/src/services/shipping-method-adjustment.ts new file mode 100644 index 0000000000000..5688cd662babe --- /dev/null +++ b/packages/cart/src/services/shipping-method-adjustment.ts @@ -0,0 +1,26 @@ +import { DAL } from "@medusajs/types" +import { ModulesSdkUtils } from "@medusajs/utils" +import { ShippingMethodAdjustment } from "@models" +import { + CreateShippingMethodAdjustmentDTO, + UpdateShippingMethodAdjustmentDTO, +} from "@types" + +type InjectedDependencies = { + shippingMethodAdjustmentRepository: DAL.RepositoryService +} + +export default class ShippingMethodAdjustmentService< + TEntity extends ShippingMethodAdjustment = ShippingMethodAdjustment +> extends ModulesSdkUtils.abstractServiceFactory< + InjectedDependencies, + { + create: CreateShippingMethodAdjustmentDTO + update: UpdateShippingMethodAdjustmentDTO + } +>(ShippingMethodAdjustment) { + constructor(container: InjectedDependencies) { + // @ts-ignore + super(...arguments) + } +} diff --git a/packages/cart/src/types/index.ts b/packages/cart/src/types/index.ts index d5866dad06a01..98891da01912b 100644 --- a/packages/cart/src/types/index.ts +++ b/packages/cart/src/types/index.ts @@ -4,8 +4,9 @@ export * from "./address" export * from "./cart" export * from "./line-item" export * from "./line-item-adjustment" -export * from "./shipping-method" export * from "./repositories" +export * from "./shipping-method" +export * from "./shipping-method-adjustment" export type InitializeModuleInjectableDependencies = { logger?: Logger diff --git a/packages/cart/src/types/repositories.ts b/packages/cart/src/types/repositories.ts index 2b38ef909cdbf..60d732c873b37 100644 --- a/packages/cart/src/types/repositories.ts +++ b/packages/cart/src/types/repositories.ts @@ -1,5 +1,11 @@ import { DAL } from "@medusajs/types" -import { Cart, LineItem, LineItemAdjustment, ShippingMethod } from "@models" +import { + Cart, + LineItem, + LineItemAdjustment, + ShippingMethod, + ShippingMethodAdjustment, +} from "@models" import { CreateAddressDTO, UpdateAddressDTO } from "./address" import { CreateCartDTO, UpdateCartDTO } from "./cart" import { CreateLineItemDTO, UpdateLineItemDTO } from "./line-item" @@ -11,6 +17,10 @@ import { CreateShippingMethodDTO, UpdateShippingMethodDTO, } from "./shipping-method" +import { + CreateShippingMethodAdjustmentDTO, + UpdateShippingMethodAdjustmentDTO, +} from "./shipping-method-adjustment" // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface IAddressRepository @@ -62,3 +72,13 @@ export interface ILineItemAdjustmentRepository< update: UpdateLineItemAdjustmentDTO } > {} + +export interface IShippingMethodAdjustmentRepository< + TEntity extends ShippingMethodAdjustment = ShippingMethodAdjustment +> extends DAL.RepositoryService< + TEntity, + { + create: CreateShippingMethodAdjustmentDTO + update: UpdateShippingMethodAdjustmentDTO + } + > {} diff --git a/packages/cart/src/types/shipping-method-adjustment.ts b/packages/cart/src/types/shipping-method-adjustment.ts new file mode 100644 index 0000000000000..e6e5c945dba26 --- /dev/null +++ b/packages/cart/src/types/shipping-method-adjustment.ts @@ -0,0 +1,17 @@ +export interface CreateShippingMethodAdjustmentDTO { + shipping_method_id: string + code: string + amount: number + description?: string + promotion_id?: string + provider_id?: string +} + +export interface UpdateShippingMethodAdjustmentDTO { + id: string + code?: string + amount?: number + description?: string + promotion_id?: string + provider_id?: string +} diff --git a/packages/types/src/cart/common.ts b/packages/types/src/cart/common.ts index 08d9a567700c2..b163809a9a5f9 100644 --- a/packages/types/src/cart/common.ts +++ b/packages/types/src/cart/common.ts @@ -40,11 +40,15 @@ export interface AdjustmentLineDTO { updated_at: Date | string } -export interface ShippingMethodAdjustmentLineDTO extends AdjustmentLineDTO { +export interface ShippingMethodAdjustmentDTO extends AdjustmentLineDTO { /** * The associated shipping method */ shipping_method: CartShippingMethodDTO + /** + * The ID of the associated shipping method + */ + shipping_method_id: string } export interface LineItemAdjustmentDTO extends AdjustmentLineDTO { @@ -227,7 +231,7 @@ export interface CartShippingMethodDTO { * * @expandable */ - adjustments?: ShippingMethodAdjustmentLineDTO[] + adjustments?: ShippingMethodAdjustmentDTO[] /** * When the shipping method was created. @@ -518,6 +522,14 @@ export interface FilterableShippingMethodProps shipping_option_id?: string | string[] } +export interface FilterableShippingMethodAdjustmentProps + extends BaseFilterable { + id?: string | string[] + shipping_method_id?: string | string[] + promotion_id?: string | string[] + provider_id?: string | string[] +} + /** * TODO: Remove this in favor of CartDTO, when module is released * @deprecated Use CartDTO instead diff --git a/packages/types/src/cart/mutations.ts b/packages/types/src/cart/mutations.ts index 98da700bbbe27..ef9247c34957e 100644 --- a/packages/types/src/cart/mutations.ts +++ b/packages/types/src/cart/mutations.ts @@ -208,4 +208,22 @@ export interface UpdateShippingMethodDTO { adjustments?: CreateAdjustmentDTO[] | UpdateAdjustmentDTO[] } +export interface CreateShippingMethodAdjustmentDTO { + shipping_method_id: string + code: string + amount: number + description?: string + promotion_id?: string + provider_id?: string +} + +export interface UpdateShippingMethodAdjustmentDTO { + id: string + code?: string + amount?: number + description?: string + promotion_id?: string + provider_id?: string +} + /** SHIPPING METHODS END */ diff --git a/packages/types/src/cart/service.ts b/packages/types/src/cart/service.ts index 1af01d344d5d7..58f7adcd22474 100644 --- a/packages/types/src/cart/service.ts +++ b/packages/types/src/cart/service.ts @@ -2,29 +2,34 @@ import { FindConfig } from "../common" import { IModuleService } from "../modules-sdk" import { Context } from "../shared-context" import { - CartAddressDTO, - CartDTO, - CartLineItemDTO, - CartShippingMethodDTO, - FilterableAddressProps, - FilterableCartProps, - FilterableLineItemAdjustmentProps, - FilterableLineItemProps, - FilterableShippingMethodProps, - LineItemAdjustmentDTO, + CartAddressDTO, + CartDTO, + CartLineItemDTO, + CartShippingMethodDTO, + FilterableAddressProps, + FilterableCartProps, + FilterableLineItemAdjustmentProps, + FilterableLineItemProps, + FilterableShippingMethodAdjustmentProps, + FilterableShippingMethodProps, + LineItemAdjustmentDTO, + ShippingMethodAdjustmentDTO, } from "./common" import { - CreateAddressDTO, - CreateAdjustmentDTO, - CreateCartDTO, - CreateLineItemDTO, - CreateLineItemForCartDTO, - CreateShippingMethodDTO, - UpdateAddressDTO, - UpdateCartDTO, - UpdateLineItemDTO, - UpdateLineItemWithSelectorDTO, - UpsertLineItemAdjustmentDTO + CreateAddressDTO, + CreateAdjustmentDTO, + CreateCartDTO, + CreateLineItemDTO, + CreateLineItemForCartDTO, + CreateShippingMethodAdjustmentDTO, + CreateShippingMethodDTO, + CreateShippingMethodForSingleCartDTO, + UpdateAddressDTO, + UpdateCartDTO, + UpdateLineItemDTO, + UpdateLineItemWithSelectorDTO, + UpdateShippingMethodAdjustmentDTO, + UpsertLineItemAdjustmentDTO, } from "./mutations" export interface ICartModuleService extends IModuleService { @@ -126,7 +131,7 @@ export interface ICartModuleService extends IModuleService { listShippingMethods( filters: FilterableShippingMethodProps, config: FindConfig, - sharedContext: Context + sharedContext?: Context ): Promise addShippingMethods( @@ -137,7 +142,7 @@ export interface ICartModuleService extends IModuleService { ): Promise addShippingMethods( cartId: string, - methods: CreateShippingMethodDTO[], + methods: CreateShippingMethodForSingleCartDTO[], sharedContext?: Context ): Promise @@ -189,4 +194,44 @@ export interface ICartModuleService extends IModuleService { selector: Partial, sharedContext?: Context ): Promise + + listShippingMethodAdjustments( + filters: FilterableShippingMethodAdjustmentProps, + config?: FindConfig, + sharedContext?: Context + ): Promise + + addShippingMethodAdjustments( + data: CreateShippingMethodAdjustmentDTO[] + ): Promise + addShippingMethodAdjustments( + data: CreateShippingMethodAdjustmentDTO + ): Promise + addShippingMethodAdjustments( + cartId: string, + data: CreateShippingMethodAdjustmentDTO[], + sharedContext?: Context + ): Promise + + setShippingMethodAdjustments( + cartId: string, + data: ( + | CreateShippingMethodAdjustmentDTO + | UpdateShippingMethodAdjustmentDTO + )[], + sharedContext?: Context + ): Promise + + removeShippingMethodAdjustments( + adjustmentIds: string[], + sharedContext?: Context + ): Promise + removeShippingMethodAdjustments( + adjustmentId: string, + sharedContext?: Context + ): Promise + removeShippingMethodAdjustments( + selector: Partial, + sharedContext?: Context + ): Promise }