Skip to content

Commit 1758bfb

Browse files
authored
fix(dashboard, core-flows): improvements to order page on canceled orders (#10888)
what: - Remove pending payment for canceled orders - Hide unfulfilled items for canceled orders - Disable non refundable payments from being refunded - Populate refund created_by - Disable order edit when canceled - Fix bug #10852 RESOLVES CMRC-842
1 parent 7232a8a commit 1758bfb

File tree

9 files changed

+77
-61
lines changed

9 files changed

+77
-61
lines changed

.changeset/eleven-rules-battle.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@medusajs/dashboard": patch
3+
"@medusajs/core-flows": patch
4+
---
5+
6+
fix(dashboard, core-flows): improvements to order page on canceled orders

integration-tests/http/__tests__/order/admin/order.spec.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -443,11 +443,12 @@ medusaIntegrationTestRunner({
443443
amount: 106,
444444
payments: [
445445
expect.objectContaining({
446-
// canceled_at: expect.any(String),
446+
canceled_at: null,
447447
refunds: [
448448
expect.objectContaining({
449449
id: expect.any(String),
450450
amount: 106,
451+
created_by: expect.any(String),
451452
}),
452453
],
453454
captures: [
@@ -488,11 +489,9 @@ medusaIntegrationTestRunner({
488489
})
489490
)
490491

491-
const response = await api.post(
492-
`/admin/orders/${order.id}/cancel`,
493-
{},
494-
adminHeaders
495-
)
492+
const response = await api
493+
.post(`/admin/orders/${order.id}/cancel`, {}, adminHeaders)
494+
.catch((e) => e)
496495

497496
expect(response.status).toBe(200)
498497
expect(response.data.order).toEqual(
@@ -514,11 +513,11 @@ medusaIntegrationTestRunner({
514513
amount: 106,
515514
payments: [
516515
expect.objectContaining({
517-
// canceled_at: expect.any(String),
518516
refunds: [
519517
expect.objectContaining({
520518
id: expect.any(String),
521519
amount: 50,
520+
created_by: expect.any(String),
522521
}),
523522
],
524523
captures: [

packages/admin/dashboard/src/routes/orders/order-create-refund/components/create-refund-form/create-refund-form.tsx

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ export const CreateRefundForm = ({
4444
const paymentId = searchParams.get("paymentId")
4545
const payments = getPaymentsFromOrder(order)
4646
const payment = payments.find((p) => p.id === paymentId)!
47-
const paymentAmount = (payment?.amount || 0) as number
48-
47+
const paymentAmount = payment.amount || 0
4948
const form = useForm<zod.infer<typeof CreateRefundSchema>>({
5049
defaultValues: {
5150
amount: paymentAmount,
@@ -121,19 +120,32 @@ export const CreateRefundForm = ({
121120
</Select.Trigger>
122121

123122
<Select.Content>
124-
{payments.map((payment) => (
125-
<Select.Item value={payment!.id} key={payment.id}>
126-
<span>
127-
{getLocaleAmount(
128-
payment.amount as number,
129-
payment.currency_code
130-
)}
131-
{" - "}
132-
</span>
133-
<span>{payment.provider_id}</span>
134-
<span> - ({payment.id.replace("pay_", "")})</span>
135-
</Select.Item>
136-
))}
123+
{payments.map((payment) => {
124+
const totalRefunded = payment.refunds.reduce(
125+
(acc, next) => next.amount + acc,
126+
0
127+
)
128+
129+
return (
130+
<Select.Item
131+
value={payment!.id}
132+
key={payment.id}
133+
disabled={
134+
!!payment.canceled_at || totalRefunded >= payment.amount
135+
}
136+
>
137+
<span>
138+
{getLocaleAmount(
139+
payment.amount as number,
140+
payment.currency_code
141+
)}
142+
{" - "}
143+
</span>
144+
<span>{payment.provider_id}</span>
145+
<span> - ({payment.id.replace("pay_", "")})</span>
146+
</Select.Item>
147+
)
148+
})}
137149
</Select.Content>
138150
</Select>
139151

@@ -154,16 +166,11 @@ export const CreateRefundForm = ({
154166
<CurrencyInput
155167
{...field}
156168
min={0}
157-
onChange={(e) => {
158-
const val =
159-
e.target.value === ""
160-
? null
161-
: Number(e.target.value)
169+
onValueChange={(value) => {
170+
const fieldValue = value ? parseInt(value) : ""
162171

163-
onChange(val)
164-
165-
if (val && !isNaN(val)) {
166-
if (val < 0 || val > paymentAmount) {
172+
if (fieldValue && !isNaN(fieldValue)) {
173+
if (fieldValue < 0 || fieldValue > paymentAmount) {
167174
form.setError(`amount`, {
168175
type: "manual",
169176
message: t(
@@ -175,6 +182,8 @@ export const CreateRefundForm = ({
175182
form.clearErrors(`amount`)
176183
}
177184
}
185+
186+
onChange(fieldValue)
178187
}}
179188
code={order.currency_code}
180189
symbol={getCurrencySymbol(order.currency_code)}

packages/admin/dashboard/src/routes/orders/order-detail/components/order-fulfillment-section/order-fulfillment-section.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ const UnfulfilledItemDisplay = ({
150150
}) => {
151151
const { t } = useTranslation()
152152

153+
if (order.status === "canceled") {
154+
return
155+
}
156+
153157
return (
154158
<Container className="divide-y p-0">
155159
<div className="flex items-center justify-between px-6 py-4">

packages/admin/dashboard/src/routes/orders/order-detail/components/order-payment-section/order-payment-section.tsx

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import { ArrowDownRightMini, DocumentText, XCircle } from "@medusajs/icons"
2-
import {
3-
AdminPayment,
4-
AdminPaymentCollection,
5-
HttpTypes,
6-
} from "@medusajs/types"
2+
import { AdminOrder, AdminPayment, HttpTypes } from "@medusajs/types"
73
import {
84
Badge,
95
Button,
@@ -58,10 +54,7 @@ export const OrderPaymentSection = ({ order }: OrderPaymentSectionProps) => {
5854
currencyCode={order.currency_code}
5955
/>
6056

61-
<Total
62-
paymentCollections={order.payment_collections}
63-
currencyCode={order.currency_code}
64-
/>
57+
<Total order={order} />
6558
</Container>
6659
)
6760
}
@@ -195,6 +188,11 @@ const Payment = ({
195188
const showCapture =
196189
payment.captured_at === null && payment.canceled_at === null
197190

191+
const totalRefunded = payment.refunds.reduce(
192+
(acc, next) => next.amount + acc,
193+
0
194+
)
195+
198196
return (
199197
<div className="divide-y divide-dashed">
200198
<div className="text-ui-fg-subtle grid grid-cols-[1fr_1fr_1fr_20px] items-center gap-x-4 px-6 py-4 sm:grid-cols-[1fr_1fr_1fr_1fr_20px]">
@@ -237,7 +235,10 @@ const Payment = ({
237235
label: t("orders.payment.refund"),
238236
icon: <XCircle />,
239237
to: `/orders/${order.id}/refund?paymentId=${payment.id}`,
240-
disabled: !payment.captured_at,
238+
disabled:
239+
!payment.captured_at ||
240+
!!payment.canceled_at ||
241+
totalRefunded >= payment.amount,
241242
},
242243
],
243244
},
@@ -341,15 +342,9 @@ const PaymentBreakdown = ({
341342
)
342343
}
343344

344-
const Total = ({
345-
paymentCollections,
346-
currencyCode,
347-
}: {
348-
paymentCollections: AdminPaymentCollection[]
349-
currencyCode: string
350-
}) => {
345+
const Total = ({ order }: { order: AdminOrder }) => {
351346
const { t } = useTranslation()
352-
const totalPending = getTotalPending(paymentCollections)
347+
const totalPending = getTotalPending(order.payment_collections)
353348

354349
return (
355350
<div>
@@ -360,20 +355,20 @@ const Total = ({
360355

361356
<Text size="small" weight="plus" leading="compact">
362357
{getStylizedAmount(
363-
getTotalCaptured(paymentCollections),
364-
currencyCode
358+
getTotalCaptured(order.payment_collections),
359+
order.currency_code
365360
)}
366361
</Text>
367362
</div>
368363

369-
{totalPending > 0 && (
364+
{order.status !== "canceled" && totalPending > 0 && (
370365
<div className="flex items-center justify-between px-6 py-4">
371366
<Text size="small" weight="plus" leading="compact">
372367
Total pending
373368
</Text>
374369

375370
<Text size="small" weight="plus" leading="compact">
376-
{getStylizedAmount(totalPending, currencyCode)}
371+
{getStylizedAmount(totalPending, order.currency_code)}
377372
</Text>
378373
</div>
379374
)}

packages/admin/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
AdminOrderPreview,
2121
AdminRegion,
2222
AdminReturn,
23+
AdminPaymentCollection,
2324
} from "@medusajs/types"
2425
import {
2526
Badge,
@@ -36,7 +37,6 @@ import {
3637
} from "@medusajs/ui"
3738

3839
import { AdminReservation } from "@medusajs/types/src/http"
39-
import { AdminPaymentCollection } from "../../../../../../../../core/types/dist/http/payment/admin/entities"
4040
import { ActionMenu } from "../../../../../components/common/action-menu"
4141
import { Thumbnail } from "../../../../../components/common/thumbnail"
4242
import { useClaims } from "../../../../../hooks/api/claims"
@@ -309,6 +309,7 @@ const Header = ({
309309
to: `/orders/${order.id}/edits`,
310310
icon: <PencilSquare />,
311311
disabled:
312+
order.status === "canceled" ||
312313
(orderPreview?.order_change &&
313314
orderPreview?.order_change?.change_type !== "edit") ||
314315
(orderPreview?.order_change?.change_type === "edit" &&

packages/core/core-flows/src/order/workflows/cancel-order.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,8 @@ export const cancelOrderWorkflow = createWorkflow(
140140
deleteReservationsByLineItemsStep(lineItemIds),
141141
cancelPaymentStep({ paymentIds: uncapturedPaymentIds }),
142142
refundCapturedPaymentsWorkflow.runAsStep({
143-
input: { order_id: order.id },
143+
input: { order_id: order.id, created_by: input.canceled_by },
144144
}),
145-
cancelOrdersStep({ orderIds: [order.id] }),
146145
emitEventStep({
147146
eventName: OrderWorkflowEvents.CANCELED,
148147
data: { id: order.id },
@@ -162,6 +161,8 @@ export const cancelOrderWorkflow = createWorkflow(
162161
})
163162
})
164163

164+
cancelOrdersStep({ orderIds: [order.id] })
165+
165166
const orderCanceled = createHook("orderCanceled", {
166167
order,
167168
})

packages/core/core-flows/src/order/workflows/payments/create-order-refund-credit-lines.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { createOrderChangeActionsWorkflow } from "../create-order-change-actions
2020
* This step validates that an order refund credit line can be issued
2121
*/
2222
export const validateOrderRefundCreditLinesStep = createStep(
23-
"begin-order-edit-validation",
23+
"validate-order-refund-credit-lines",
2424
async function ({ order }: { order: OrderDTO }) {
2525
throwIfOrderIsCancelled({ order })
2626
}

packages/medusa/src/api/admin/orders/[id]/cancel/route.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { cancelOrderWorkflow } from "@medusajs/core-flows"
2+
import {
3+
AuthenticatedMedusaRequest,
4+
MedusaResponse,
5+
} from "@medusajs/framework/http"
26
import { HttpTypes } from "@medusajs/framework/types"
37
import {
48
ContainerRegistrationKeys,
59
remoteQueryObjectFromString,
610
} from "@medusajs/framework/utils"
7-
import {
8-
AuthenticatedMedusaRequest,
9-
MedusaResponse,
10-
} from "@medusajs/framework/http"
1111

1212
export const POST = async (
1313
req: AuthenticatedMedusaRequest,
@@ -33,5 +33,6 @@ export const POST = async (
3333
})
3434

3535
const [order] = await remoteQuery(queryObject)
36+
3637
res.status(200).json({ order })
3738
}

0 commit comments

Comments
 (0)