fix(medusa): Refund amount on returns in claim flow (#3237)
This commit is contained in:
committed by
GitHub
parent
d48606d5dd
commit
968eb8fc6b
5
.changeset/rotten-hairs-thank.md
Normal file
5
.changeset/rotten-hairs-thank.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@medusajs/medusa": patch
|
||||
---
|
||||
|
||||
fix(medusa): Refund amount on returns in claim flow
|
||||
@@ -1441,6 +1441,57 @@ describe("/admin/orders", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("Receives return with custom refund amount passed on receive", async () => {
|
||||
const api = useApi()
|
||||
|
||||
const orderId = "test-order"
|
||||
const itemId = "test-item"
|
||||
|
||||
const returned = await api.post(
|
||||
`/admin/orders/${orderId}/return`,
|
||||
{
|
||||
items: [
|
||||
{
|
||||
// item has a unit_price of 8000 with a 800 adjustment
|
||||
item_id: itemId,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
adminReqConfig
|
||||
)
|
||||
|
||||
const returnOrder = returned.data.order.returns[0]
|
||||
|
||||
expect(returned.status).toEqual(200)
|
||||
expect(returnOrder.refund_amount).toEqual(7200)
|
||||
|
||||
const received = await api.post(
|
||||
`/admin/returns/${returnOrder.id}/receive`,
|
||||
{
|
||||
items: [
|
||||
{
|
||||
item_id: itemId,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
refund: 0,
|
||||
},
|
||||
adminReqConfig
|
||||
)
|
||||
|
||||
const receivedReturn = received.data.return
|
||||
|
||||
expect(received.status).toEqual(200)
|
||||
expect(receivedReturn.refund_amount).toEqual(0)
|
||||
|
||||
const orderRes = await api.get(`/admin/orders/${orderId}`, adminReqConfig)
|
||||
|
||||
const order = orderRes.data.order
|
||||
|
||||
expect(order.refunds.length).toEqual(0)
|
||||
})
|
||||
|
||||
it("increases inventory_quantity when return is received", async () => {
|
||||
const api = useApi()
|
||||
|
||||
|
||||
@@ -121,6 +121,83 @@ describe("Claims", () => {
|
||||
)
|
||||
})
|
||||
|
||||
test("creates a refund claim + return", async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
const order = await createReturnableOrder(dbConnection)
|
||||
const api = useApi()
|
||||
|
||||
const response = await api.post(
|
||||
`/admin/orders/${order.id}/claims`,
|
||||
{
|
||||
type: "refund",
|
||||
claim_items: [
|
||||
{
|
||||
item_id: "test-item",
|
||||
reason: "missing_item",
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
return_shipping: {
|
||||
price: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const claim = response.data.order.claims[0]
|
||||
const refund = response.data.order.refunds[0]
|
||||
const returnOrder = response.data.order.returns[0]
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(claim.refund_amount).toEqual(1200)
|
||||
expect(refund.amount).toEqual(1200)
|
||||
expect(returnOrder.refund_amount).toEqual(1200)
|
||||
})
|
||||
|
||||
test("creates a refund claim + return with a custom amount", async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
const order = await createReturnableOrder(dbConnection)
|
||||
const api = useApi()
|
||||
|
||||
const response = await api.post(
|
||||
`/admin/orders/${order.id}/claims`,
|
||||
{
|
||||
type: "refund",
|
||||
refund_amount: 500,
|
||||
claim_items: [
|
||||
{
|
||||
item_id: "test-item",
|
||||
reason: "missing_item",
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
return_shipping: {
|
||||
price: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
authorization: "Bearer test_token",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const claim = response.data.order.claims[0]
|
||||
const refund = response.data.order.refunds[0]
|
||||
const returnOrder = response.data.order.returns[0]
|
||||
|
||||
expect(response.status).toEqual(200)
|
||||
expect(claim.refund_amount).toEqual(500)
|
||||
expect(refund.amount).toEqual(500)
|
||||
expect(returnOrder.refund_amount).toEqual(500)
|
||||
})
|
||||
|
||||
test("creates a replace claim", async () => {
|
||||
await adminSeeder(dbConnection)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IdMap, MockRepository, MockManager } from "medusa-test-utils"
|
||||
import { IdMap, MockManager, MockRepository } from "medusa-test-utils"
|
||||
import ClaimService from "../claim"
|
||||
import { ProductVariantInventoryServiceMock } from "../__mocks__/product-variant-inventory"
|
||||
|
||||
@@ -14,6 +14,7 @@ const eventBusService = {
|
||||
const totalsService = {
|
||||
getCalculationContext: jest.fn(() => {}),
|
||||
getRefundTotal: jest.fn(() => 1000),
|
||||
getLineItemRefund: jest.fn(() => 8000),
|
||||
}
|
||||
|
||||
describe("ClaimService", () => {
|
||||
@@ -28,6 +29,7 @@ describe("ClaimService", () => {
|
||||
{
|
||||
id: "itm_1",
|
||||
unit_price: 8000,
|
||||
shipped_quantity: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -134,6 +136,7 @@ describe("ClaimService", () => {
|
||||
expect(returnService.create).toHaveBeenCalledWith({
|
||||
order_id: "1234",
|
||||
claim_order_id: "claim_134",
|
||||
refund_amount: 8000,
|
||||
shipping_method: {
|
||||
option_id: "opt_13",
|
||||
price: 0,
|
||||
@@ -181,7 +184,7 @@ describe("ClaimService", () => {
|
||||
expect(claimRepo.create).toHaveBeenCalledWith({
|
||||
payment_status: "not_refunded",
|
||||
no_notification: true,
|
||||
refund_amount: 1000,
|
||||
refund_amount: 8000,
|
||||
type: "refund",
|
||||
order_id: "1234",
|
||||
additional_items: [
|
||||
@@ -291,6 +294,206 @@ describe("ClaimService", () => {
|
||||
)
|
||||
})
|
||||
|
||||
describe("getRefundTotalForClaimLinesOnOrder", () => {
|
||||
const testOrder = (items = [], swaps = [], claims = []) => ({
|
||||
id: "1234",
|
||||
region_id: "order_region",
|
||||
no_notification: true,
|
||||
items: [
|
||||
{
|
||||
id: "itm_1",
|
||||
unit_price: 8000,
|
||||
shipped_quantity: 1,
|
||||
},
|
||||
...items,
|
||||
],
|
||||
...swaps,
|
||||
...claims,
|
||||
})
|
||||
|
||||
const claimRepo = MockRepository({})
|
||||
|
||||
const claimService = new ClaimService({
|
||||
manager: MockManager,
|
||||
claimRepository: claimRepo,
|
||||
totalsService,
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calculates refund total for claim with one shipped item", async () => {
|
||||
const order = testOrder([
|
||||
{
|
||||
id: "itm_1",
|
||||
unit_price: 8000,
|
||||
shipped_quantity: 1,
|
||||
},
|
||||
])
|
||||
|
||||
const refund = await claimService.getRefundTotalForClaimLinesOnOrder(
|
||||
order,
|
||||
[
|
||||
{
|
||||
item_id: "itm_1",
|
||||
quantity: 1,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
expect(totalsService.getLineItemRefund).toHaveBeenCalledTimes(1)
|
||||
expect(totalsService.getLineItemRefund).toHaveBeenCalledWith(order, {
|
||||
id: "itm_1",
|
||||
unit_price: 8000,
|
||||
quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
})
|
||||
|
||||
expect(refund).toEqual(8000)
|
||||
})
|
||||
|
||||
it("calculates refund total for claim with one shipped + one pending item", async () => {
|
||||
const order = testOrder([{ id: "itm_2", shipped_quantity: 0 }])
|
||||
|
||||
const refund = await claimService.getRefundTotalForClaimLinesOnOrder(
|
||||
order,
|
||||
[
|
||||
{
|
||||
item_id: "itm_1",
|
||||
quantity: 1,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
expect(totalsService.getLineItemRefund).toHaveBeenCalledTimes(1)
|
||||
expect(totalsService.getLineItemRefund).toHaveBeenCalledWith(order, {
|
||||
id: "itm_1",
|
||||
unit_price: 8000,
|
||||
quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
})
|
||||
|
||||
expect(refund).toEqual(8000)
|
||||
})
|
||||
|
||||
it("calculates refund total for claim with two shipped + one pending item", async () => {
|
||||
const order = testOrder([
|
||||
{ id: "itm_2", shipped_quantity: 0 },
|
||||
{ id: "itm_3", shipped_quantity: 1, unit_price: 5000 },
|
||||
])
|
||||
|
||||
const refund = await claimService.getRefundTotalForClaimLinesOnOrder(
|
||||
order,
|
||||
[
|
||||
{
|
||||
item_id: "itm_1",
|
||||
quantity: 1,
|
||||
},
|
||||
{ item_id: "itm_3", quantity: 1 },
|
||||
]
|
||||
)
|
||||
|
||||
expect(totalsService.getLineItemRefund).toHaveBeenCalledTimes(2)
|
||||
expect(totalsService.getLineItemRefund.mock.calls).toEqual([
|
||||
[
|
||||
order,
|
||||
{
|
||||
id: "itm_1",
|
||||
unit_price: 8000,
|
||||
quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
},
|
||||
],
|
||||
[
|
||||
order,
|
||||
{
|
||||
id: "itm_3",
|
||||
unit_price: 5000,
|
||||
quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
expect(refund).toEqual(16000)
|
||||
})
|
||||
|
||||
it("calculates refund total for claim on swap items", async () => {
|
||||
const order = testOrder([], [[{ id: "itm_1", shipped_quantity: 1 }]])
|
||||
|
||||
const refund = await claimService.getRefundTotalForClaimLinesOnOrder(
|
||||
order,
|
||||
[
|
||||
{
|
||||
item_id: "itm_1",
|
||||
quantity: 1,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
expect(totalsService.getLineItemRefund).toHaveBeenCalledTimes(1)
|
||||
expect(totalsService.getLineItemRefund.mock.calls).toEqual([
|
||||
[
|
||||
order,
|
||||
{
|
||||
id: "itm_1",
|
||||
unit_price: 8000,
|
||||
quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
expect(refund).toEqual(8000)
|
||||
})
|
||||
|
||||
it("calculates refund total for claim on claim items", async () => {
|
||||
const order = testOrder([], [], [{ id: "itm_1", shipped_quantity: 1 }])
|
||||
|
||||
const refund = await claimService.getRefundTotalForClaimLinesOnOrder(
|
||||
order,
|
||||
[
|
||||
{
|
||||
item_id: "itm_1",
|
||||
quantity: 1,
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
expect(totalsService.getLineItemRefund).toHaveBeenCalledTimes(1)
|
||||
expect(totalsService.getLineItemRefund.mock.calls).toEqual([
|
||||
[
|
||||
order,
|
||||
{
|
||||
id: "itm_1",
|
||||
unit_price: 8000,
|
||||
quantity: 1,
|
||||
shipped_quantity: 1,
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
expect(refund).toEqual(8000)
|
||||
})
|
||||
|
||||
it("return 0 when claim lines cannot be found", async () => {
|
||||
const order = testOrder([
|
||||
{ id: "itm_2", shipped_quantity: 0 },
|
||||
{ id: "itm_3", shipped_quantity: 1, unit_price: 5000 },
|
||||
])
|
||||
|
||||
const refund = await claimService.getRefundTotalForClaimLinesOnOrder(
|
||||
order,
|
||||
[{ item_id: "itm_10", quantity: 1 }]
|
||||
)
|
||||
|
||||
expect(totalsService.getLineItemRefund).toHaveBeenCalledTimes(0)
|
||||
|
||||
expect(refund).toEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe("retrieve", () => {
|
||||
const claimRepo = MockRepository()
|
||||
const claimService = new ClaimService({
|
||||
|
||||
@@ -8,13 +8,18 @@ import {
|
||||
ClaimType,
|
||||
FulfillmentItem,
|
||||
LineItem,
|
||||
Order,
|
||||
ReturnItem,
|
||||
} from "../models"
|
||||
import { AddressRepository } from "../repositories/address"
|
||||
import { ClaimRepository } from "../repositories/claim"
|
||||
import { LineItemRepository } from "../repositories/line-item"
|
||||
import { ShippingMethodRepository } from "../repositories/shipping-method"
|
||||
import { CreateClaimInput, UpdateClaimInput } from "../types/claim"
|
||||
import {
|
||||
CreateClaimInput,
|
||||
CreateClaimItemInput,
|
||||
UpdateClaimInput,
|
||||
} from "../types/claim"
|
||||
import { FindConfig } from "../types/common"
|
||||
import { buildQuery, setMetadata } from "../utils"
|
||||
import ClaimItemService from "./claim-item"
|
||||
@@ -204,6 +209,124 @@ export default class ClaimService extends TransactionBaseService {
|
||||
)
|
||||
}
|
||||
|
||||
protected async validateCreateClaimInput(
|
||||
data: CreateClaimInput
|
||||
): Promise<void> {
|
||||
const lineItemServiceTx = this.lineItemService_.withTransaction(
|
||||
this.manager_
|
||||
)
|
||||
|
||||
const { type, claim_items, additional_items, refund_amount } = data
|
||||
|
||||
if (type !== ClaimType.REFUND && type !== ClaimType.REPLACE) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Claim type must be one of "refund" or "replace".`
|
||||
)
|
||||
}
|
||||
|
||||
if (type === ClaimType.REPLACE && !additional_items?.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Claims with type "replace" must have at least one additional item.`
|
||||
)
|
||||
}
|
||||
|
||||
if (!claim_items?.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Claims must have at least one claim item.`
|
||||
)
|
||||
}
|
||||
|
||||
if (refund_amount && type !== ClaimType.REFUND) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Claim has type "${type}" but must be type "refund" to have a refund_amount.`
|
||||
)
|
||||
}
|
||||
|
||||
const claimLineItems = await lineItemServiceTx.list(
|
||||
{ id: claim_items.map((c) => c.item_id) },
|
||||
{ relations: ["order", "swap", "claim_order", "tax_lines"] }
|
||||
)
|
||||
|
||||
for (const line of claimLineItems) {
|
||||
if (
|
||||
line.order?.canceled_at ||
|
||||
line.swap?.canceled_at ||
|
||||
line.claim_order?.canceled_at
|
||||
) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot create a claim on a canceled item.`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds claim line items on an order and calculates the refund amount.
|
||||
* There are three places too look:
|
||||
* - Order items
|
||||
* - Swap items
|
||||
* - Claim items (from previous claims)
|
||||
* Note, it will attempt to return early from each of these places to avoid having to iterate over all items every time.
|
||||
* @param order - the order to find claim lines on
|
||||
* @param claimItems - the claim items to match against
|
||||
* @return the refund amount
|
||||
*/
|
||||
protected async getRefundTotalForClaimLinesOnOrder(
|
||||
order: Order,
|
||||
claimItems: CreateClaimItemInput[]
|
||||
) {
|
||||
const claimLines = claimItems
|
||||
.map((ci) => {
|
||||
const predicate = (it: LineItem) =>
|
||||
it.shipped_quantity! > 0 &&
|
||||
ci.quantity <= it.shipped_quantity! &&
|
||||
it.id === ci.item_id
|
||||
|
||||
const claimLine = order.items.find(predicate)
|
||||
|
||||
if (claimLine) {
|
||||
return { ...claimLine, quantity: ci.quantity }
|
||||
}
|
||||
|
||||
if (order.swaps?.length) {
|
||||
for (const swap of order.swaps) {
|
||||
const claimLine = swap.additional_items.find(predicate)
|
||||
|
||||
if (claimLine) {
|
||||
return { ...claimLine, quantity: ci.quantity }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (order.claims?.length) {
|
||||
for (const claim of order.claims) {
|
||||
const claimLine = claim.additional_items.find(predicate)
|
||||
|
||||
if (claimLine) {
|
||||
return { ...claimLine, quantity: ci.quantity }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
.filter(Boolean) as LineItem[]
|
||||
|
||||
const refunds: number[] = []
|
||||
|
||||
for (const item of claimLines) {
|
||||
const refund = await this.totalsService_.getLineItemRefund(order, item)
|
||||
refunds.push(refund)
|
||||
}
|
||||
|
||||
return Math.round(refunds.reduce((acc, next) => acc + next, 0))
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Claim on an Order. Claims consists of items that are claimed and
|
||||
* optionally items to be sent as replacement for the claimed items. The
|
||||
@@ -232,25 +355,7 @@ export default class ClaimService extends TransactionBaseService {
|
||||
...rest
|
||||
} = data
|
||||
|
||||
const lineItemServiceTx =
|
||||
this.lineItemService_.withTransaction(transactionManager)
|
||||
|
||||
for (const item of claim_items) {
|
||||
const line = await lineItemServiceTx.retrieve(item.item_id, {
|
||||
relations: ["order", "swap", "claim_order", "tax_lines"],
|
||||
})
|
||||
|
||||
if (
|
||||
line.order?.canceled_at ||
|
||||
line.swap?.canceled_at ||
|
||||
line.claim_order?.canceled_at
|
||||
) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot create a claim on a canceled item.`
|
||||
)
|
||||
}
|
||||
}
|
||||
await this.validateCreateClaimInput(data)
|
||||
|
||||
let addressId = shipping_address_id || order.shipping_address_id
|
||||
if (shipping_address) {
|
||||
@@ -262,76 +367,18 @@ export default class ClaimService extends TransactionBaseService {
|
||||
addressId = saved.id
|
||||
}
|
||||
|
||||
if (type !== ClaimType.REFUND && type !== ClaimType.REPLACE) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Claim type must be one of "refund" or "replace".`
|
||||
)
|
||||
}
|
||||
|
||||
if (type === ClaimType.REPLACE && !additional_items?.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Claims with type "replace" must have at least one additional item.`
|
||||
)
|
||||
}
|
||||
|
||||
if (!claim_items?.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Claims must have at least one claim item.`
|
||||
)
|
||||
}
|
||||
|
||||
if (refund_amount && type !== ClaimType.REFUND) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Claim has type "${type}" but must be type "refund" to have a refund_amount.`
|
||||
)
|
||||
}
|
||||
|
||||
let toRefund = refund_amount
|
||||
if (type === ClaimType.REFUND && typeof refund_amount === "undefined") {
|
||||
const lines = claim_items.map((ci) => {
|
||||
const allOrderItems = order.items
|
||||
|
||||
if (order.swaps?.length) {
|
||||
for (const swap of order.swaps) {
|
||||
swap.additional_items.forEach((it) => {
|
||||
if (
|
||||
it.shipped_quantity ||
|
||||
it.shipped_quantity === it.fulfilled_quantity
|
||||
) {
|
||||
allOrderItems.push(it)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (order.claims?.length) {
|
||||
for (const claim of order.claims) {
|
||||
claim.additional_items.forEach((it) => {
|
||||
if (
|
||||
it.shipped_quantity ||
|
||||
it.shipped_quantity === it.fulfilled_quantity
|
||||
) {
|
||||
allOrderItems.push(it)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const orderItem = allOrderItems.find((oi) => oi.id === ci.item_id)
|
||||
return {
|
||||
...orderItem,
|
||||
quantity: ci.quantity,
|
||||
}
|
||||
})
|
||||
toRefund = await this.totalsService_.getRefundTotal(
|
||||
// In case no refund amount is passed, we calculate it based on the claim items on the order
|
||||
toRefund = await this.getRefundTotalForClaimLinesOnOrder(
|
||||
order,
|
||||
lines as LineItem[]
|
||||
claim_items
|
||||
)
|
||||
}
|
||||
|
||||
const lineItemServiceTx =
|
||||
this.lineItemService_.withTransaction(transactionManager)
|
||||
|
||||
let newItems: LineItem[] = []
|
||||
|
||||
if (isDefined(additional_items)) {
|
||||
@@ -425,6 +472,7 @@ export default class ClaimService extends TransactionBaseService {
|
||||
|
||||
if (return_shipping) {
|
||||
await this.returnService_.withTransaction(transactionManager).create({
|
||||
refund_amount: toRefund,
|
||||
order_id: order.id,
|
||||
claim_order_id: result.id,
|
||||
items: claim_items.map(
|
||||
|
||||
@@ -1883,7 +1883,7 @@ class OrderService extends TransactionBaseService {
|
||||
)
|
||||
}
|
||||
|
||||
const refundAmount = customRefundAmount || receivedReturn.refund_amount
|
||||
const refundAmount = customRefundAmount ?? receivedReturn.refund_amount
|
||||
|
||||
const orderRepo = manager.getCustomRepository(this.orderRepository_)
|
||||
|
||||
@@ -1907,10 +1907,10 @@ class OrderService extends TransactionBaseService {
|
||||
}
|
||||
}
|
||||
|
||||
if (receivedReturn.refund_amount > 0) {
|
||||
if (refundAmount > 0) {
|
||||
const refund = await this.paymentProviderService_
|
||||
.withTransaction(manager)
|
||||
.refundPayment(order.payments, receivedReturn.refund_amount, "return")
|
||||
.refundPayment(order.payments, refundAmount, "return")
|
||||
|
||||
order.refunds = [...(order.refunds || []), refund]
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ import { buildQuery, setMetadata } from "../utils"
|
||||
|
||||
import {
|
||||
FulfillmentProviderService,
|
||||
ProductVariantInventoryService,
|
||||
LineItemService,
|
||||
OrderService,
|
||||
ProductVariantInventoryService,
|
||||
ReturnReasonService,
|
||||
ShippingOptionService,
|
||||
TaxProviderService,
|
||||
@@ -653,7 +653,7 @@ class ReturnService extends TransactionBaseService {
|
||||
returnStatus = ReturnStatus.REQUIRES_ACTION
|
||||
}
|
||||
|
||||
const totalRefundableAmount = refundAmount || returnObj.refund_amount
|
||||
const totalRefundableAmount = refundAmount ?? returnObj.refund_amount
|
||||
|
||||
const now = new Date()
|
||||
const updateObj = {
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
import { isDefined, MedusaError } from "medusa-core-utils"
|
||||
import { EntityManager, In } from "typeorm"
|
||||
|
||||
import { buildQuery, setMetadata, validateId } from "../utils"
|
||||
import { TransactionBaseService } from "../interfaces"
|
||||
import { buildQuery, setMetadata, validateId } from "../utils"
|
||||
|
||||
import LineItemAdjustmentService from "./line-item-adjustment"
|
||||
import { FindConfig, Selector, WithRequiredProperty } from "../types/common"
|
||||
import {
|
||||
Cart,
|
||||
CartType,
|
||||
FulfillmentItem,
|
||||
FulfillmentStatus,
|
||||
LineItem,
|
||||
Order,
|
||||
PaymentSessionStatus,
|
||||
PaymentStatus,
|
||||
ReturnItem,
|
||||
ReturnStatus,
|
||||
Swap,
|
||||
SwapFulfillmentStatus,
|
||||
SwapPaymentStatus,
|
||||
} from "../models"
|
||||
import { SwapRepository } from "../repositories/swap"
|
||||
import { FindConfig, Selector, WithRequiredProperty } from "../types/common"
|
||||
import { CreateShipmentConfig } from "../types/fulfillment"
|
||||
import { OrdersReturnItem } from "../types/orders"
|
||||
import CartService from "./cart"
|
||||
import {
|
||||
CustomShippingOptionService,
|
||||
@@ -20,21 +36,7 @@ import {
|
||||
ShippingOptionService,
|
||||
TotalsService,
|
||||
} from "./index"
|
||||
import {
|
||||
Cart,
|
||||
CartType,
|
||||
FulfillmentItem,
|
||||
LineItem,
|
||||
Order,
|
||||
PaymentSessionStatus,
|
||||
ReturnItem,
|
||||
ReturnStatus,
|
||||
Swap,
|
||||
SwapFulfillmentStatus,
|
||||
SwapPaymentStatus,
|
||||
} from "../models"
|
||||
import { CreateShipmentConfig } from "../types/fulfillment"
|
||||
import { OrdersReturnItem } from "../types/orders"
|
||||
import LineItemAdjustmentService from "./line-item-adjustment"
|
||||
|
||||
type InjectedProps = {
|
||||
manager: EntityManager
|
||||
@@ -325,13 +327,17 @@ class SwapService extends TransactionBaseService {
|
||||
): Promise<Swap | never> {
|
||||
const { no_notification, ...rest } = custom
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
if (
|
||||
order.fulfillment_status === "not_fulfilled" ||
|
||||
order.payment_status !== "captured"
|
||||
) {
|
||||
if (order.payment_status !== PaymentStatus.CAPTURED) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"Order cannot be swapped"
|
||||
"Cannot swap an order that has not been captured"
|
||||
)
|
||||
}
|
||||
|
||||
if (order.fulfillment_status === FulfillmentStatus.NOT_FULFILLED) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"Cannot swap an order that has not been fulfilled"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user