diff --git a/.changeset/empty-flowers-walk.md b/.changeset/empty-flowers-walk.md new file mode 100644 index 0000000000..8dcc5d535f --- /dev/null +++ b/.changeset/empty-flowers-walk.md @@ -0,0 +1,5 @@ +--- +"@medusajs/medusa": patch +--- + +fix(medusa): Cancel order missing refunds relation diff --git a/integration-tests/api/__tests__/admin/order/order.js b/integration-tests/api/__tests__/admin/order/order.js index 79a0f4e9bb..378d0b12bf 100644 --- a/integration-tests/api/__tests__/admin/order/order.js +++ b/integration-tests/api/__tests__/admin/order/order.js @@ -288,6 +288,48 @@ describe("/admin/orders", () => { await db.teardown() }) + it("cancels an order with refund should fail", async () => { + const api = useApi() + + const refundOrder = await simpleOrderFactory(dbConnection, { + id: "refunded-order", + customer_id: "test-customer", + email: "test@email.com", + fulfillment_status: "not_fulfilled", + payment_status: "refunded", + billing_address: { + id: "test-billing-address", + first_name: "lebron", + }, + shipping_address: { + id: "test-shipping-address", + first_name: "lebron", + country_code: "us", + }, + region_id: "test-region", + currency_code: "usd", + tax_rate: 0, + discounts: [], + payments: [], + items: [], + refunds: [ + { + amount: 1000, + reason: "return", + }, + ], + }) + + const err = await api + .post(`/admin/orders/${refundOrder.id}/cancel`, {}, adminReqConfig) + .catch((e) => e) + + expect(err.response.status).toEqual(400) + expect(err.response.data.message).toEqual( + "Order with refund(s) cannot be canceled" + ) + }) + it("cancels an order and increments inventory_quantity", async () => { const api = useApi() diff --git a/integration-tests/api/factories/simple-order-factory.ts b/integration-tests/api/factories/simple-order-factory.ts index c7aa1bec60..7d3a58a7c9 100644 --- a/integration-tests/api/factories/simple-order-factory.ts +++ b/integration-tests/api/factories/simple-order-factory.ts @@ -1,36 +1,13 @@ import { Connection } from "typeorm" import faker from "faker" -import { - Customer, - Order, - PaymentStatus, - FulfillmentStatus, -} from "@medusajs/medusa" -import { - DiscountFactoryData, - simpleDiscountFactory, -} from "./simple-discount-factory" +import { Discount, FulfillmentStatus, Order, PaymentStatus, Refund, } from "@medusajs/medusa" +import { DiscountFactoryData, simpleDiscountFactory, } from "./simple-discount-factory" import { RegionFactoryData, simpleRegionFactory } from "./simple-region-factory" -import { - LineItemFactoryData, - simpleLineItemFactory, -} from "./simple-line-item-factory" -import { - AddressFactoryData, - simpleAddressFactory, -} from "./simple-address-factory" -import { - ShippingMethodFactoryData, - simpleShippingMethodFactory, -} from "./simple-shipping-method-factory" -import { - SalesChannelFactoryData, - simpleSalesChannelFactory, -} from "./simple-sales-channel-factory" -import { - CustomerFactoryData, - simpleCustomerFactory, -} from "./simple-customer-factory" +import { LineItemFactoryData, simpleLineItemFactory, } from "./simple-line-item-factory" +import { AddressFactoryData, simpleAddressFactory, } from "./simple-address-factory" +import { ShippingMethodFactoryData, simpleShippingMethodFactory, } from "./simple-shipping-method-factory" +import { SalesChannelFactoryData, simpleSalesChannelFactory, } from "./simple-sales-channel-factory" +import { CustomerFactoryData, simpleCustomerFactory, } from "./simple-customer-factory" export type OrderFactoryData = { id?: string @@ -46,11 +23,12 @@ export type OrderFactoryData = { shipping_address?: AddressFactoryData shipping_methods?: ShippingMethodFactoryData[] sales_channel?: SalesChannelFactoryData + refunds: Refund[] } export const simpleOrderFactory = async ( connection: Connection, - data: OrderFactoryData = {}, + data: OrderFactoryData = {} as OrderFactoryData, seed?: number ): Promise => { if (typeof seed !== "undefined") { @@ -63,13 +41,13 @@ export const simpleOrderFactory = async ( let regionId: string let taxRate: number if (typeof data.region === "string") { - currencyCode = data.currency_code + currencyCode = data.currency_code as string regionId = data.region - taxRate = data.tax_rate + taxRate = data.tax_rate as number } else { const region = await simpleRegionFactory(connection, data.region) taxRate = - typeof data.tax_rate !== "undefined" ? data.tax_rate : region.tax_rate + (typeof data.tax_rate !== "undefined" ? data.tax_rate : region.tax_rate) as number currencyCode = region.currency_code regionId = region.id } @@ -80,7 +58,7 @@ export const simpleOrderFactory = async ( email: data.email ?? undefined, }) - let discounts = [] + let discounts: Discount[] = [] if (typeof data.discounts !== "undefined") { discounts = await Promise.all( data.discounts.map((d) => simpleDiscountFactory(connection, d, seed)) @@ -109,6 +87,7 @@ export const simpleOrderFactory = async ( tax_rate: taxRate, shipping_address_id: address.id, sales_channel_id: sales_channel?.id ?? null, + refunds: data.refunds ?? [] }) const order = await manager.save(toSave) @@ -131,7 +110,7 @@ export const simpleOrderFactory = async ( }) || [] for (const item of items) { - await simpleLineItemFactory(connection, { ...item, order_id: id }) + await simpleLineItemFactory(connection, { ...item, order_id: id } as unknown as LineItemFactoryData) } return order diff --git a/packages/medusa/src/services/__tests__/order.js b/packages/medusa/src/services/__tests__/order.js index 041dd4078e..15b27b21d0 100644 --- a/packages/medusa/src/services/__tests__/order.js +++ b/packages/medusa/src/services/__tests__/order.js @@ -647,6 +647,17 @@ describe("OrderService", () => { payment_status: "paid", status: "pending", }) + case IdMap.getId("refunded-order"): + return Promise.resolve({ + fulfillment_status: "fulfilled", + payment_status: "refunded", + refunds: [ + { + order_id: IdMap.getId("refunded-order"), + }, + ], + status: "pending", + }) default: return Promise.resolve({ fulfillment_status: "not_fulfilled", @@ -729,6 +740,12 @@ describe("OrderService", () => { ], }) }) + + it("fails if order has refunds", async () => { + await expect( + orderService.cancel(IdMap.getId("refunded-order")) + ).rejects.toThrow("Order with refund(s) cannot be canceled") + }) }) describe("capturePayment", () => { diff --git a/packages/medusa/src/services/order.ts b/packages/medusa/src/services/order.ts index 487ef00c40..548ec0c9b0 100644 --- a/packages/medusa/src/services/order.ts +++ b/packages/medusa/src/services/order.ts @@ -37,18 +37,18 @@ import { DiscountService, DraftOrderService, EventBusService, - FulfillmentService, FulfillmentProviderService, + FulfillmentService, GiftCardService, - ProductVariantInventoryService, LineItemService, + NewTotalsService, PaymentProviderService, + ProductVariantInventoryService, RegionService, ShippingOptionService, ShippingProfileService, - TotalsService, - NewTotalsService, TaxProviderService, + TotalsService, } from "." export const ORDER_CART_ALREADY_EXISTS_ERROR = "Order from cart already exists" @@ -1102,6 +1102,7 @@ class OrderService extends TransactionBaseService { return await this.atomicPhase_(async (manager) => { const order = await this.retrieve(orderId, { relations: [ + "refunds", "fulfillments", "payments", "returns",