Skip to content

Commit b77ac26

Browse files
lkostrowskiclaude
andauthored
Fix return/replace refund excluding lines from waiting fulfillments (#6642)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 9452013 commit b77ac26

3 files changed

Lines changed: 119 additions & 11 deletions

File tree

.changeset/great-pots-refuse.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"saleor-dashboard": patch
3+
---
4+
5+
Fixed an issue where the Return/Replace flow excluded order lines from the granted refund when their fulfillment was waiting for approval. Previously, only shipping costs were sent to the payment provider in such cases - now the refund correctly includes both the returned items and shipping.

src/orders/views/OrderReturn/useRefundWithinReturn.test.ts

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,93 @@
1-
import { type GrantRefundInputLine, squashLines } from "./useRefundWithinReturn";
1+
import { type FormsetQuantityData } from "@dashboard/orders/components/OrderReturnPage/form";
2+
3+
import {
4+
type GrantRefundInputLine,
5+
prepareGrantRefundLines,
6+
squashLines,
7+
} from "./useRefundWithinReturn";
8+
9+
const getQuantityLine = ({
10+
id,
11+
orderLineId,
12+
value,
13+
}: {
14+
id: string;
15+
orderLineId: string;
16+
value: number;
17+
}): FormsetQuantityData[number] => ({
18+
id,
19+
label: "",
20+
value,
21+
data: {
22+
isFulfillment: true,
23+
isRefunded: false,
24+
orderLineId,
25+
},
26+
});
27+
28+
describe("prepareGrantRefundLines", () => {
29+
it("includes fulfilled, waiting for approval and unfulfilled lines in the refund payload", () => {
30+
// Arrange
31+
const fulfilledItemsQuantities: FormsetQuantityData = [
32+
getQuantityLine({ id: "fulfillment-line-1", orderLineId: "order-line-1", value: 2 }),
33+
];
34+
const waitingItemsQuantities: FormsetQuantityData = [
35+
getQuantityLine({ id: "fulfillment-line-2", orderLineId: "order-line-2", value: 1 }),
36+
];
37+
const unfulfilledItemsQuantities: FormsetQuantityData = [
38+
getQuantityLine({ id: "order-line-3", orderLineId: "order-line-3", value: 3 }),
39+
];
40+
41+
// Act
42+
const result = prepareGrantRefundLines({
43+
fulfilledItemsQuantities,
44+
waitingItemsQuantities,
45+
unfulfilledItemsQuantities,
46+
});
47+
48+
// Assert
49+
expect(result).toEqual([
50+
{ id: "order-line-1", quantity: 2 },
51+
{ id: "order-line-2", quantity: 1 },
52+
{ id: "order-line-3", quantity: 3 },
53+
]);
54+
});
55+
it("maps waiting for approval lines to order line ids", () => {
56+
// Arrange
57+
const waitingItemsQuantities: FormsetQuantityData = [
58+
getQuantityLine({ id: "fulfillment-line-1", orderLineId: "order-line-1", value: 4 }),
59+
];
60+
61+
// Act
62+
const result = prepareGrantRefundLines({
63+
fulfilledItemsQuantities: [],
64+
waitingItemsQuantities,
65+
unfulfilledItemsQuantities: [],
66+
});
67+
68+
// Assert
69+
expect(result).toEqual([{ id: "order-line-1", quantity: 4 }]);
70+
});
71+
it("squashes lines referencing the same order line", () => {
72+
// Arrange
73+
const fulfilledItemsQuantities: FormsetQuantityData = [
74+
getQuantityLine({ id: "fulfillment-line-1", orderLineId: "order-line-1", value: 2 }),
75+
];
76+
const waitingItemsQuantities: FormsetQuantityData = [
77+
getQuantityLine({ id: "fulfillment-line-2", orderLineId: "order-line-1", value: 1 }),
78+
];
79+
80+
// Act
81+
const result = prepareGrantRefundLines({
82+
fulfilledItemsQuantities,
83+
waitingItemsQuantities,
84+
unfulfilledItemsQuantities: [],
85+
});
86+
87+
// Assert
88+
expect(result).toEqual([{ id: "order-line-1", quantity: 3 }]);
89+
});
90+
});
291

392
describe("squashLines", () => {
493
it("merges items with the same ID", () => {

src/orders/views/OrderReturn/useRefundWithinReturn.ts

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,7 @@ export function useRefundWithinReturn({
3939
amount: formData.amount,
4040
transactionId: formData.transactionId,
4141
reason: "",
42-
lines: squashLines([
43-
...formData.fulfilledItemsQuantities.map(line => ({
44-
id: line.data.orderLineId,
45-
quantity: line.value,
46-
})),
47-
...formData.unfulfilledItemsQuantities.map(({ id, value }) => ({
48-
id,
49-
quantity: value,
50-
})),
51-
]),
42+
lines: prepareGrantRefundLines(formData),
5243
grantRefundForShipping: formData.refundShipmentCosts,
5344
},
5445
})
@@ -80,6 +71,29 @@ export function useRefundWithinReturn({
8071
};
8172
}
8273

74+
export const prepareGrantRefundLines = (
75+
formData: Pick<
76+
OrderReturnFormData,
77+
"fulfilledItemsQuantities" | "waitingItemsQuantities" | "unfulfilledItemsQuantities"
78+
>,
79+
): GrantRefundInputLine[] =>
80+
squashLines([
81+
// Fulfillment lines (fulfilled and waiting for approval) use formset ids pointing
82+
// to fulfillment lines - map them to order line ids required by the grant refund mutation
83+
...formData.fulfilledItemsQuantities.map(line => ({
84+
id: line.data.orderLineId,
85+
quantity: line.value,
86+
})),
87+
...formData.waitingItemsQuantities.map(line => ({
88+
id: line.data.orderLineId,
89+
quantity: line.value,
90+
})),
91+
...formData.unfulfilledItemsQuantities.map(({ id, value }) => ({
92+
id,
93+
quantity: value,
94+
})),
95+
]);
96+
8397
export const squashLines = (items: GrantRefundInputLine[]): GrantRefundInputLine[] =>
8498
Object.values(
8599
items.reduce<Record<string, GrantRefundInputLine>>(

0 commit comments

Comments
 (0)