From 3464617553770353a5d781caea8bdf880c72909f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frane=20Poli=C4=87?= <16856471+fPolic@users.noreply.github.com> Date: Mon, 12 Dec 2022 19:04:36 +0100 Subject: [PATCH] fix(medusa): Payment status on refunds (#2770) --- .changeset/nervous-roses-agree.md | 5 + .../api/__tests__/admin/order/order.js | 104 +++++++++++++++++- packages/medusa/src/services/order.ts | 19 +++- .../medusa/src/services/payment-provider.ts | 1 + 4 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 .changeset/nervous-roses-agree.md diff --git a/.changeset/nervous-roses-agree.md b/.changeset/nervous-roses-agree.md new file mode 100644 index 0000000000..6e25251bbf --- /dev/null +++ b/.changeset/nervous-roses-agree.md @@ -0,0 +1,5 @@ +--- +"@medusajs/medusa": patch +--- + +Properly setting "refunded" and "partially refunded" statuses on orders diff --git a/integration-tests/api/__tests__/admin/order/order.js b/integration-tests/api/__tests__/admin/order/order.js index 02be314a2c..84e1d85c03 100644 --- a/integration-tests/api/__tests__/admin/order/order.js +++ b/integration-tests/api/__tests__/admin/order/order.js @@ -6,6 +6,7 @@ const { CustomShippingOption, ShippingMethod, } = require("@medusajs/medusa") +const idMap = require("medusa-test-utils/src/id-map").default const setupServer = require("../../../../helpers/setup-server") const { useApi } = require("../../../../helpers/use-api") @@ -22,7 +23,18 @@ const { callGet, partial, } = require("../../../helpers/call-helpers") -const { simpleShippingOptionFactory } = require("../../../factories") +const { + simpleShippingOptionFactory, + simpleOrderFactory, + simplePaymentFactory, + simpleProductFactory, +} = require("../../../factories") + +const adminReqConfig = { + headers: { + Authorization: "Bearer test_token", + }, +} jest.setTimeout(30000) @@ -2360,4 +2372,94 @@ describe("/admin/orders", () => { } }) }) + + describe("POST /orders/:id/refund", () => { + const orderId = idMap.getId("refund-order-1") + + beforeEach(async () => { + await adminSeeder(dbConnection) + await orderSeeder(dbConnection) + + const product1 = await simpleProductFactory(dbConnection, {}) + + await simpleOrderFactory(dbConnection, { + id: orderId, + tax_rate: null, + email: "test@testson.com", + fulfillment_status: "fulfilled", + payment_status: "captured", + line_items: [ + { + variant_id: product1.variants[0].id, + id: idMap.getId("item-1"), + quantity: 1, + fulfilled_quantity: 1, + shipped_quantity: 1, + unit_price: 1000, + }, + ], + }) + + await simplePaymentFactory(dbConnection, { + provider_id: "test-pay", + order: orderId, + amount: 300, + captured: true, + }) + + await simplePaymentFactory(dbConnection, { + provider_id: "test-pay", + order: orderId, + amount: 700, + captured: true, + }) + }) + + afterEach(async () => { + const db = useDb() + await db.teardown() + }) + + it("set status on refunded order", async () => { + const api = useApi() + + const response = await api.post( + `/admin/orders/${orderId}/refund`, + { amount: 1000, reason: "other" }, + adminReqConfig + ) + + expect(response.data.order).toEqual( + expect.objectContaining({ + payment_status: "refunded", + refunded_total: 1000, + subtotal: 1000, + total: 1000, + paid_total: 1000, + refundable_amount: 0, + }) + ) + }) + + it("set correct status on partially refunded order", async () => { + const api = useApi() + + const response = await api.post( + `/admin/orders/${orderId}/refund`, + { amount: 500, reason: "other" }, + adminReqConfig + ) + + expect(response.data.order).toEqual( + expect.objectContaining({ + payment_status: "partially_refunded", + refunded_total: 500, + subtotal: 1000, + total: 1000, + paid_total: 1000, + refundable_amount: 500, + }) + ) + }) + }) }) diff --git a/packages/medusa/src/services/order.ts b/packages/medusa/src/services/order.ts index 3759aef96c..f98f08f63d 100644 --- a/packages/medusa/src/services/order.ts +++ b/packages/medusa/src/services/order.ts @@ -1461,6 +1461,8 @@ class OrderService extends TransactionBaseService { const { no_notification } = config return await this.atomicPhase_(async (manager) => { + const orderRepo = manager.getCustomRepository(this.orderRepository_) + const order = await this.retrieve(orderId, { select: ["refundable_amount", "total", "refunded_total"], relations: ["payments"], @@ -1484,7 +1486,22 @@ class OrderService extends TransactionBaseService { .withTransaction(manager) .refundPayment(order.payments, refundAmount, reason, note) - const result = await this.retrieve(orderId) + let result = await this.retrieveWithTotals(orderId, { + relations: ["payments"], + }) + + if (result.refunded_total > 0 && result.refundable_amount > 0) { + result.payment_status = PaymentStatus.PARTIALLY_REFUNDED + result = await orderRepo.save(result) + } + + if ( + result.paid_total > 0 && + result.refunded_total === result.paid_total + ) { + result.payment_status = PaymentStatus.REFUNDED + result = await orderRepo.save(result) + } const evaluatedNoNotification = no_notification !== undefined ? no_notification : order.no_notification diff --git a/packages/medusa/src/services/payment-provider.ts b/packages/medusa/src/services/payment-provider.ts index c1002ee44f..f10a9714f9 100644 --- a/packages/medusa/src/services/payment-provider.ts +++ b/packages/medusa/src/services/payment-provider.ts @@ -589,6 +589,7 @@ export default class PaymentProviderService extends TransactionBaseService { const paymentRepo = transactionManager.getCustomRepository( this.paymentRepository_ ) + let paymentToRefund = payments.find( (payment) => payment.amount - payment.amount_refunded > 0 )