From 1817b810fc8563a08119b74b86ec0587d9e443a1 Mon Sep 17 00:00:00 2001 From: Adrien de Peretti Date: Tue, 10 Jan 2023 10:38:59 +0100 Subject: [PATCH] fix(medusa): Cancel order missing refunds relation (#2976) **What** The order cancelation does not include the refunds relation. It means that the check of the length of the refund is never true and therefore no errors are thrown if the order contains the refunds. **How** Add the refunds relation and tests FIXES CORE-976 Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> --- .changeset/empty-flowers-walk.md | 5 ++ .../api/__tests__/admin/order/order.js | 42 +++++++++++++++ .../api/factories/simple-order-factory.ts | 51 ++++++------------- .../medusa/src/services/__tests__/order.js | 17 +++++++ packages/medusa/src/services/order.ts | 9 ++-- 5 files changed, 84 insertions(+), 40 deletions(-) create mode 100644 .changeset/empty-flowers-walk.md 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",