diff --git a/integration-tests/http/__tests__/claims/claims.spec.ts b/integration-tests/http/__tests__/claims/claims.spec.ts index e856a48076..3c0657e63c 100644 --- a/integration-tests/http/__tests__/claims/claims.spec.ts +++ b/integration-tests/http/__tests__/claims/claims.spec.ts @@ -436,238 +436,334 @@ medusaIntegrationTestRunner({ describe("Claims lifecycle", () => { let claimId - beforeEach(async () => { - let r2 = await api.post( - "/admin/claims", - { - order_id: order2.id, - type: ClaimType.REFUND, - }, - adminHeaders - ) - - const claimId2 = r2.data.claim.id - const item2 = order2.items[0] - - const { - data: { - order_preview: { - items: [previewItem], + describe("with inbound and outbound items", () => { + beforeEach(async () => { + let r2 = await api.post( + "/admin/claims", + { + order_id: order2.id, + type: ClaimType.REFUND, }, - }, - } = await api.post( - `/admin/claims/${claimId2}/inbound/items`, - { items: [{ id: item2.id, quantity: 1 }] }, - adminHeaders - ) - - // Delete & recreate again to ensure it works for both delete and create - await api.delete( - `/admin/claims/${claimId2}/inbound/items/${previewItem.actions[0].id}`, - adminHeaders - ) - - const { - data: { return: returnData }, - } = await api.post( - `/admin/claims/${claimId2}/inbound/items`, - { items: [{ id: item2.id, quantity: 1 }] }, - adminHeaders - ) - - await api.post( - `/admin/returns/${returnData.id}`, - { - location_id: location.id, - no_notification: true, - }, - adminHeaders - ) - - const { - data: { - order_preview: { shipping_methods: inboundShippingMethods }, - }, - } = await api.post( - `/admin/claims/${claimId2}/inbound/shipping-method`, - { shipping_option_id: returnShippingOption.id }, - adminHeaders - ) - const inboundShippingMethod = inboundShippingMethods.find( - (m) => m.shipping_option_id == returnShippingOption.id - ) - - // Delete & recreate again to ensure it works for both delete and create - await api.delete( - `/admin/claims/${claimId2}/inbound/shipping-method/${inboundShippingMethod.actions[0].id}`, - adminHeaders - ) - - await api.post( - `/admin/claims/${claimId2}/inbound/shipping-method`, - { shipping_option_id: returnShippingOption.id }, - adminHeaders - ) - - await api.post(`/admin/claims/${claimId2}/request`, {}, adminHeaders) - - claimId = baseClaim.id - const item = order.items[0] - - let result = await api.post( - `/admin/claims/${claimId}/inbound/items`, - { - items: [ - { - id: item.id, - reason_id: returnReason.id, - quantity: 2, - }, - ], - }, - adminHeaders - ) - - await api.post( - `/admin/claims/${claimId}/inbound/shipping-method`, - { shipping_option_id: returnShippingOption.id }, - adminHeaders - ) - - const { - data: { - order_preview: { shipping_methods: outboundShippingMethods }, - }, - } = await api.post( - `/admin/claims/${claimId}/outbound/shipping-method`, - { shipping_option_id: outboundShippingOption.id }, - adminHeaders - ) - - const outboundShippingMethod = outboundShippingMethods.find( - (m) => m.shipping_option_id == outboundShippingOption.id - ) - - // Delete & recreate again to ensure it works for both delete and create - await api.delete( - `/admin/claims/${claimId}/outbound/shipping-method/${outboundShippingMethod.actions[0].id}`, - adminHeaders - ) - - await api.post( - `/admin/claims/${claimId}/outbound/shipping-method`, - { shipping_option_id: outboundShippingOption.id }, - adminHeaders - ) - - // updated the requested quantity - const updateReturnItemActionId = - result.data.order_preview.items[0].actions[0].id - - result = await api.post( - `/admin/claims/${claimId}/inbound/items/${updateReturnItemActionId}`, - { - quantity: 1, - }, - adminHeaders - ) - - // New Items - await api.post( - `/admin/claims/${claimId}/outbound/items`, - { - items: [ - { - variant_id: productExtra.variants[0].id, - quantity: 2, - }, - ], - }, - adminHeaders - ) - - // Claim Items - await api.post( - `/admin/claims/${claimId}/claim-items`, - { - items: [ - { - id: item.id, - reason: ClaimReason.PRODUCTION_FAILURE, - quantity: 1, - }, - ], - }, - adminHeaders - ) - - result = await api.post( - `/admin/claims/${claimId}/request`, - {}, - adminHeaders - ) - - result = ( - await api.get( - `/admin/claims?fields=*claim_items,*additional_items`, adminHeaders ) - ).data.claims - expect(result).toHaveLength(2) - expect(result[0].additional_items).toHaveLength(1) - expect(result[0].claim_items).toHaveLength(1) - expect(result[0].canceled_at).toBeNull() + const claimId2 = r2.data.claim.id + const item2 = order2.items[0] - const reservationsResponse = ( - await api.get( - `/admin/reservations?location_id[]=${location.id}`, + const { + data: { + order_preview: { + items: [previewItem], + }, + }, + } = await api.post( + `/admin/claims/${claimId2}/inbound/items`, + { items: [{ id: item2.id, quantity: 1 }] }, adminHeaders ) - ).data - expect(reservationsResponse.reservations).toHaveLength(1) + // Delete & recreate again to ensure it works for both delete and create + await api.delete( + `/admin/claims/${claimId2}/inbound/items/${previewItem.actions[0].id}`, + adminHeaders + ) + + const { + data: { return: returnData }, + } = await api.post( + `/admin/claims/${claimId2}/inbound/items`, + { items: [{ id: item2.id, quantity: 1 }] }, + adminHeaders + ) + + await api.post( + `/admin/returns/${returnData.id}`, + { + location_id: location.id, + no_notification: true, + }, + adminHeaders + ) + + const { + data: { + order_preview: { shipping_methods: inboundShippingMethods }, + }, + } = await api.post( + `/admin/claims/${claimId2}/inbound/shipping-method`, + { shipping_option_id: returnShippingOption.id }, + adminHeaders + ) + const inboundShippingMethod = inboundShippingMethods.find( + (m) => m.shipping_option_id == returnShippingOption.id + ) + + // Delete & recreate again to ensure it works for both delete and create + await api.delete( + `/admin/claims/${claimId2}/inbound/shipping-method/${inboundShippingMethod.actions[0].id}`, + adminHeaders + ) + + await api.post( + `/admin/claims/${claimId2}/inbound/shipping-method`, + { shipping_option_id: returnShippingOption.id }, + adminHeaders + ) + + await api.post(`/admin/claims/${claimId2}/request`, {}, adminHeaders) + + claimId = baseClaim.id + const item = order.items[0] + + let result = await api.post( + `/admin/claims/${claimId}/inbound/items`, + { + items: [ + { + id: item.id, + reason_id: returnReason.id, + quantity: 2, + }, + ], + }, + adminHeaders + ) + + await api.post( + `/admin/claims/${claimId}/inbound/shipping-method`, + { shipping_option_id: returnShippingOption.id }, + adminHeaders + ) + + const { + data: { + order_preview: { shipping_methods: outboundShippingMethods }, + }, + } = await api.post( + `/admin/claims/${claimId}/outbound/shipping-method`, + { shipping_option_id: outboundShippingOption.id }, + adminHeaders + ) + + const outboundShippingMethod = outboundShippingMethods.find( + (m) => m.shipping_option_id == outboundShippingOption.id + ) + + // Delete & recreate again to ensure it works for both delete and create + await api.delete( + `/admin/claims/${claimId}/outbound/shipping-method/${outboundShippingMethod.actions[0].id}`, + adminHeaders + ) + + await api.post( + `/admin/claims/${claimId}/outbound/shipping-method`, + { shipping_option_id: outboundShippingOption.id }, + adminHeaders + ) + + // updated the requested quantity + const updateReturnItemActionId = + result.data.order_preview.items[0].actions[0].id + + result = await api.post( + `/admin/claims/${claimId}/inbound/items/${updateReturnItemActionId}`, + { + quantity: 1, + }, + adminHeaders + ) + + // New Items + await api.post( + `/admin/claims/${claimId}/outbound/items`, + { + items: [ + { + variant_id: productExtra.variants[0].id, + quantity: 2, + }, + ], + }, + adminHeaders + ) + + // Claim Items + await api.post( + `/admin/claims/${claimId}/claim-items`, + { + items: [ + { + id: item.id, + reason: ClaimReason.PRODUCTION_FAILURE, + quantity: 1, + }, + ], + }, + adminHeaders + ) + + result = await api.post( + `/admin/claims/${claimId}/request`, + {}, + adminHeaders + ) + + result = ( + await api.get( + `/admin/claims?fields=*claim_items,*additional_items`, + adminHeaders + ) + ).data.claims + + expect(result).toHaveLength(2) + expect(result[0].additional_items).toHaveLength(1) + expect(result[0].claim_items).toHaveLength(1) + expect(result[0].canceled_at).toBeNull() + + const reservationsResponse = ( + await api.get( + `/admin/reservations?location_id[]=${location.id}`, + adminHeaders + ) + ).data + + expect(reservationsResponse.reservations).toHaveLength(1) + }) + + it("should complete flow with fulfilled items successfully", async () => { + const fulfillOrder = ( + await api.get(`/admin/orders/${order.id}`, adminHeaders) + ).data.order + + const fulfillableItem = fulfillOrder.items.find( + (item) => item.detail.fulfilled_quantity === 0 + ) + + await api.post( + `/admin/orders/${order.id}/fulfillments`, + { + location_id: location.id, + items: [{ id: fulfillableItem.id, quantity: 1 }], + }, + adminHeaders + ) + }) + + it("should go through cancel flow successfully", async () => { + await api.post(`/admin/claims/${claimId}/cancel`, {}, adminHeaders) + + const [claim] = ( + await api.get( + `/admin/claims?fields=*claim_items,*additional_items`, + adminHeaders + ) + ).data.claims + + expect(claim.canceled_at).toBeDefined() + + const reservationsResponseAfterCanceling = ( + await api.get( + `/admin/reservations?location_id[]=${location.id}`, + adminHeaders + ) + ).data + + expect(reservationsResponseAfterCanceling.reservations).toHaveLength( + 0 + ) + }) }) - it("should complete flow with fulfilled items successfully", async () => { - const fulfillOrder = ( - await api.get(`/admin/orders/${order.id}`, adminHeaders) - ).data.order + describe("with only outbound items", () => { + beforeEach(async () => { + claimId = baseClaim.id + const item = order.items[0] - const fulfillableItem = fulfillOrder.items.find( - (item) => item.detail.fulfilled_quantity === 0 - ) - - await api.post( - `/admin/orders/${order.id}/fulfillments`, - { - location_id: location.id, - items: [{ id: fulfillableItem.id, quantity: 1 }], - }, - adminHeaders - ) - }) - - it("should go through cancel flow successfully", async () => { - await api.post(`/admin/claims/${claimId}/cancel`, {}, adminHeaders) - - const [claim] = ( - await api.get( - `/admin/claims?fields=*claim_items,*additional_items`, + const { + data: { + order_preview: { shipping_methods: outboundShippingMethods }, + }, + } = await api.post( + `/admin/claims/${claimId}/outbound/shipping-method`, + { shipping_option_id: outboundShippingOption.id }, adminHeaders ) - ).data.claims - expect(claim.canceled_at).toBeDefined() + const outboundShippingMethod = outboundShippingMethods.find( + (m) => m.shipping_option_id == outboundShippingOption.id + ) - const reservationsResponseAfterCanceling = ( - await api.get( - `/admin/reservations?location_id[]=${location.id}`, + // Delete & recreate again to ensure it works for both delete and create + await api.delete( + `/admin/claims/${claimId}/outbound/shipping-method/${outboundShippingMethod.actions[0].id}`, adminHeaders ) - ).data - expect(reservationsResponseAfterCanceling.reservations).toHaveLength(0) + await api.post( + `/admin/claims/${claimId}/outbound/shipping-method`, + { shipping_option_id: outboundShippingOption.id }, + adminHeaders + ) + + await api.post( + `/admin/claims/${claimId}/outbound/items`, + { + items: [ + { + variant_id: productExtra.variants[0].id, + quantity: 2, + }, + ], + }, + adminHeaders + ) + + await api.post( + `/admin/claims/${claimId}/claim-items`, + { + items: [ + { + id: item.id, + reason: ClaimReason.PRODUCTION_FAILURE, + quantity: 1, + }, + ], + }, + adminHeaders + ) + }) + + it("should complete flow with fulfilled items successfully", async () => { + await api.post(`/admin/claims/${claimId}/request`, {}, adminHeaders) + + const claim = ( + await api.get( + `/admin/claims/${claimId}?fields=*claim_items,*additional_items`, + adminHeaders + ) + ).data.claim + + expect(claim.additional_items).toHaveLength(1) + expect(claim.claim_items).toHaveLength(1) + expect(claim.canceled_at).toBeNull() + + const fulfillOrder = ( + await api.get(`/admin/orders/${order.id}`, adminHeaders) + ).data.order + + const fulfillableItem = fulfillOrder.items.find( + (item) => item.detail.fulfilled_quantity === 0 + ) + + await api.post( + `/admin/orders/${order.id}/fulfillments`, + { + location_id: location.id, + items: [{ id: fulfillableItem.id, quantity: 1 }], + }, + adminHeaders + ) + }) }) }) diff --git a/packages/admin-next/dashboard/src/i18n/translations/en.json b/packages/admin-next/dashboard/src/i18n/translations/en.json index 53fd101fb5..d39bd74adb 100644 --- a/packages/admin-next/dashboard/src/i18n/translations/en.json +++ b/packages/admin-next/dashboard/src/i18n/translations/en.json @@ -850,8 +850,9 @@ "sendNotification": "Send notification", "sendNotificationHint": "Notify customer about return.", "returnTotal": "Return total", + "inboundTotal": "Inbound total", "refundAmount": "Refund amount", - "outstandingAmount": "Outstanding amount", + "outstandingAmount": "Difference amount", "reason": "Reason", "reasonHint": "Choose why the customer want to return items.", "note": "Note", @@ -886,7 +887,10 @@ }, "claims": { "create": "Create Claim", + "manage": "Manage Claim", "outbound": "Outbound", + "outboundItemAdded": "{{itemsCount}}x added through claim", + "outboundTotal": "Outbound total", "outboundShipping": "Outbound shipping", "outboundShippingHint": "Choose which method you want to use.", "refundAmount": "Estimated difference", @@ -2369,8 +2373,8 @@ "variants": "Variants", "orders": "Orders", "account": "Account", - "total": "Total", - "paidTotal": "Paid total", + "total": "Order Total", + "paidTotal": "Total captured", "totalExclTax": "Total excl. tax", "subtotal": "Subtotal", "shipping": "Shipping", diff --git a/packages/admin-next/dashboard/src/lib/payment.ts b/packages/admin-next/dashboard/src/lib/payment.ts new file mode 100644 index 0000000000..8096ebe601 --- /dev/null +++ b/packages/admin-next/dashboard/src/lib/payment.ts @@ -0,0 +1,12 @@ +import { AdminPaymentCollection } from "@medusajs/types" + +export const getTotalCaptured = ( + paymentCollections: AdminPaymentCollection[] +) => + paymentCollections.reduce((acc, paymentCollection) => { + acc = + acc + + ((paymentCollection.captured_amount as number) - + (paymentCollection.refunded_amount as number)) + return acc + }, 0) diff --git a/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/claim-create-form/claim-create-form.tsx b/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/claim-create-form/claim-create-form.tsx index 6d1294c795..f34fc7c659 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/claim-create-form/claim-create-form.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/claim-create-form/claim-create-form.tsx @@ -232,6 +232,12 @@ export const ClaimCreateForm = ({ ) ) + const outboundShipping = preview.shipping_methods.find((s) => { + const action = s.actions?.find((a) => a.action === "SHIPPING_ADD") + + return action && !!!action?.return?.id + }) + const { fields: inboundItems, append, @@ -456,7 +462,6 @@ export const ClaimCreateForm = ({ }, [preview.shipping_methods]) const returnTotal = preview.return_requested_total - const refundAmount = returnTotal - shippingTotal return ( @@ -672,20 +677,49 @@ export const ClaimCreateForm = ({
- {t("orders.returns.returnTotal")} + {t("orders.returns.inboundTotal")} + {getStylizedAmount( - returnTotal ? -1 * returnTotal : returnTotal, + inboundPreviewItems.reduce((acc, item) => { + const action = item.actions?.find( + (act) => act.action === "RETURN_ITEM" + ) + acc = acc + (action?.amount || 0) + + return acc + }, 0) * -1, order.currency_code )}
-
+
+ + {t("orders.claims.outboundTotal")} + + + + {getStylizedAmount( + outboundPreviewItems.reduce((acc, item) => { + const action = item.actions?.find( + (act) => act.action === "ITEM_ADD" + ) + acc = acc + (action?.amount || 0) + + return acc + }, 0), + order.currency_code + )} + +
+ +
{t("orders.returns.inboundShipping")} + {!isShippingPriceEdit && ( )} + {isShippingPriceEdit ? (
+
+ + {t("orders.claims.outboundShipping")} + + + + {getStylizedAmount( + outboundShipping?.amount ?? 0, + order.currency_code + )} + +
+
{t("orders.claims.refundAmount")} {getStylizedAmount( - refundAmount ? -1 * refundAmount : refundAmount, + preview.summary.pending_difference, order.currency_code )} diff --git a/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/claim-create-form/claim-outbound-section.tsx b/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/claim-create-form/claim-outbound-section.tsx index 052f47027d..92277d7e8a 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/claim-create-form/claim-outbound-section.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/claim-create-form/claim-outbound-section.tsx @@ -169,12 +169,12 @@ export const ClaimOutboundSection = ({ )) for (const itemToRemove of itemsToRemove) { - const actionId = previewOutboundItems + const action = previewOutboundItems .find((i) => i.variant_id === itemToRemove) - ?.actions?.find((a) => a.action === "ITEM_ADD")?.id + ?.actions?.find((a) => a.action === "ITEM_ADD") - if (actionId) { - await removeOutboundItem(actionId, { + if (action?.id) { + await removeOutboundItem(action?.id, { onError: (error) => { toast.error(error.message) }, @@ -194,7 +194,15 @@ export const ClaimOutboundSection = ({ const promises = outboundShippingMethods .filter(Boolean) - .map((action) => deleteOutboundShipping(action.id)) + .map((outboundShippingMethod) => { + const action = outboundShippingMethod.actions?.find( + (a) => a.action === "SHIPPING_ADD" + ) + + if (action) { + deleteOutboundShipping(action.id) + } + }) await Promise.all(promises) diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-payment-section/order-payment-section.tsx b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-payment-section/order-payment-section.tsx index 3d3dd0738f..ccab6cc2b8 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-payment-section/order-payment-section.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-payment-section/order-payment-section.tsx @@ -3,7 +3,7 @@ import { Payment as MedusaPayment, Refund as MedusaRefund, } from "@medusajs/medusa" -import { HttpTypes } from "@medusajs/types" +import { AdminPaymentCollection, HttpTypes } from "@medusajs/types" import { Badge, Button, @@ -25,6 +25,7 @@ import { getStylizedAmount, } from "../../../../../lib/money-amount-helpers" import { getOrderPaymentStatus } from "../../../../../lib/order-helpers" +import { getTotalCaptured } from "../../../../../lib/payment" type OrderPaymentSectionProps = { order: HttpTypes.AdminOrder @@ -56,7 +57,10 @@ export const OrderPaymentSection = ({ order }: OrderPaymentSectionProps) => { currencyCode={order.currency_code} /> - + ) } @@ -310,30 +314,21 @@ const PaymentBreakdown = ({ } const Total = ({ - payments, + paymentCollections, currencyCode, }: { - payments: MedusaPayment[] + paymentCollections: AdminPaymentCollection[] currencyCode: string }) => { const { t } = useTranslation() - const refunds = payments.map((payment) => payment.refunds).flat(1) - const paid = payments.reduce((acc, payment) => acc + payment.amount, 0) - const refunded = refunds.reduce( - (acc, refund) => acc + (refund.amount || 0), - 0 - ) - - const total = paid - refunded - return (
{t("orders.payment.totalPaidByCustomer")} - {getStylizedAmount(total, currencyCode)} + {getStylizedAmount(getTotalCaptured(paymentCollections), currencyCode)}
) diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx index fbfced2a2a..31289c3294 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx @@ -1,38 +1,46 @@ +import { useMemo } from "react" import { useTranslation } from "react-i18next" import { useNavigate } from "react-router-dom" -import { useMemo } from "react" import { + ArrowDownRightMini, + ArrowLongRight, + ArrowUturnLeft, + DocumentText, + ExclamationCircle, +} from "@medusajs/icons" +import { + AdminClaim, AdminOrder, + AdminOrderPreview, AdminReturn, OrderLineItemDTO, ReservationItemDTO, } from "@medusajs/types" import { - ArrowDownRightMini, - ArrowUturnLeft, - ExclamationCircle, - ArrowLongRight, -} from "@medusajs/icons" -import { + Badge, Button, Container, Copy, Heading, StatusBadge, Text, + Tooltip, } from "@medusajs/ui" import { ActionMenu } from "../../../../../components/common/action-menu" +import { ButtonMenu } from "../../../../../components/common/button-menu/button-menu.tsx" import { Thumbnail } from "../../../../../components/common/thumbnail" +import { useClaims } from "../../../../../hooks/api/claims.tsx" +import { useOrderPreview } from "../../../../../hooks/api/orders.tsx" +import { useReservationItems } from "../../../../../hooks/api/reservations" +import { useReturns } from "../../../../../hooks/api/returns" +import { useDate } from "../../../../../hooks/use-date" import { getLocaleAmount, getStylizedAmount, } from "../../../../../lib/money-amount-helpers" -import { useReservationItems } from "../../../../../hooks/api/reservations" -import { useReturns } from "../../../../../hooks/api/returns" -import { useDate } from "../../../../../hooks/use-date" -import { ButtonMenu } from "../../../../../components/common/button-menu/button-menu.tsx" +import { getTotalCaptured } from "../../../../../lib/payment.ts" type OrderSummarySectionProps = { order: AdminOrder @@ -46,6 +54,8 @@ export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => { line_item_id: order.items.map((i) => i.id), }) + const { order: orderPreview } = useOrderPreview(order.id!) + const { returns = [] } = useReturns({ status: "requested", order_id: order.id, @@ -84,7 +94,7 @@ export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => { return ( -
+
@@ -125,7 +135,13 @@ export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => { ) } -const Header = ({ order }: { order: AdminOrder }) => { +const Header = ({ + order, + orderPreview, +}: { + order: AdminOrder + orderPreview?: AdminOrderPreview +}) => { const { t } = useTranslation() return ( @@ -151,7 +167,9 @@ const Header = ({ order }: { order: AdminOrder }) => { icon: , }, { - label: t("orders.claims.create"), + label: orderPreview?.order_change?.id + ? t("orders.claims.manage") + : t("orders.claims.create"), to: `/orders/${order.id}/claims`, icon: , }, @@ -168,11 +186,13 @@ const Item = ({ currencyCode, reservation, returns, + claims, }: { item: OrderLineItemDTO currencyCode: string reservation?: ReservationItemDTO | null returns: AdminReturn[] + claims: AdminClaim[] }) => { const { t } = useTranslation() const isInventoryManaged = item.variant.manage_inventory @@ -240,6 +260,10 @@ const Item = ({ {returns.map((r) => ( ))} + + {claims.map((claim) => ( + + ))} ) } @@ -249,9 +273,14 @@ const ItemBreakdown = ({ order }: { order: AdminOrder }) => { line_item_id: order.items.map((i) => i.id), }) + const { claims } = useClaims({ + order_id: order.id, + fields: "*additional_items", + }) + const { returns } = useReturns({ order_id: order.id, - fields: "*items", + fields: "*items,*items.reason", }) const itemsReturnsMap = useMemo(() => { @@ -290,6 +319,7 @@ const ItemBreakdown = ({ order }: { order: AdminOrder }) => { currencyCode={order.currency_code} reservation={reservation} returns={itemsReturnsMap[item.id] || []} + claims={claims || []} /> ) })} @@ -363,43 +393,100 @@ const ReturnBreakdown = ({ if ( !["requested", "received", "partially_received"].includes( - orderReturn.status + orderReturn.status || "" ) ) { return null } const isRequested = orderReturn.status === "requested" + const item = orderReturn?.items?.find((ri) => ri.item_id === itemId) return ( -
-
- - - {t( - `orders.returns.${ - isRequested ? "returnRequestedInfo" : "returnReceivedInfo" - }`, - { - requestedItemsCount: orderReturn.items.find( - (ri) => ri.item_id === itemId - )[isRequested ? "quantity" : "received_quantity"], - } + item && ( +
+
+ + + {t( + `orders.returns.${ + isRequested ? "returnRequestedInfo" : "returnReceivedInfo" + }`, + { + requestedItemsCount: + item?.[isRequested ? "quantity" : "received_quantity"], + } + )} + + + {item?.note && ( + + + )} + + {item?.reason && ( + + {item?.reason?.label} + + )} +
+ + {orderReturn && ( + + {getRelativeDate( + isRequested ? orderReturn.created_at : orderReturn.received_at + )} + + )} +
+ ) + ) +} + +const ClaimBreakdown = ({ + claim, + itemId, +}: { + claim: AdminClaim + itemId: string +}) => { + const { t } = useTranslation() + const { getRelativeDate } = useDate() + const items = claim.additional_items.filter( + (item) => item.item?.id === itemId + ) + + return ( + !!items.length && ( +
+
+ + + {t(`orders.claims.outboundItemAdded`, { + itemsCount: items.reduce( + (acc, item) => (acc = acc + item.quantity), + 0 + ), + })} + +
+ + + {getRelativeDate(claim.created_at)}
- - {isRequested && ( - - {getRelativeDate( - isRequested ? orderReturn.created_at : orderReturn.received_at - )} - - )} -
+ ) ) } @@ -416,12 +503,32 @@ const Total = ({ order }: { order: AdminOrder }) => { {getStylizedAmount(order.total, order.currency_code)}
+
{t("fields.paidTotal")} - {/*TODO*/}- + {getStylizedAmount( + getTotalCaptured(order.payment_collections || []), + order.currency_code + )} + +
+ +
+ + {t("orders.returns.outstandingAmount")} + + + {getStylizedAmount( + order.summary.difference_sum || 0, + order.currency_code + )}
diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/constants.ts b/packages/admin-next/dashboard/src/routes/orders/order-detail/constants.ts index 99bb6b76f7..feef070946 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-detail/constants.ts +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/constants.ts @@ -24,6 +24,7 @@ const DEFAULT_RELATIONS = [ "*items.variant.product", "*items.variant.options", "+items.variant.manage_inventory", + "+summary", "*shipping_address", "*billing_address", "*sales_channel", diff --git a/packages/core/core-flows/src/order/workflows/claim/confirm-claim-request.ts b/packages/core/core-flows/src/order/workflows/claim/confirm-claim-request.ts index 57fb85ffbc..4d3f91160d 100644 --- a/packages/core/core-flows/src/order/workflows/claim/confirm-claim-request.ts +++ b/packages/core/core-flows/src/order/workflows/claim/confirm-claim-request.ts @@ -252,13 +252,17 @@ export const confirmClaimRequestWorkflow = createWorkflow( } ) - updateReturnsStep([ - { - id: returnId, - status: ReturnStatus.REQUESTED, - requested_at: new Date(), - }, - ]) + when({ returnId }, ({ returnId }) => { + return !!returnId + }).then(() => { + updateReturnsStep([ + { + id: returnId, + status: ReturnStatus.REQUESTED, + requested_at: new Date(), + }, + ]) + }) const claimId = transform({ createClaimItems }, ({ createClaimItems }) => { return createClaimItems?.[0]?.claim_id @@ -329,9 +333,12 @@ export const confirmClaimRequestWorkflow = createWorkflow( reserveInventoryStep(formatedInventoryItems) }) - when({ returnShippingMethod }, ({ returnShippingMethod }) => { - return !!returnShippingMethod - }).then(() => { + when( + { returnShippingMethod, returnId }, + ({ returnShippingMethod, returnId }) => { + return !!returnShippingMethod && !!returnId + } + ).then(() => { const returnShippingOption = useRemoteQueryStep({ entry_point: "shipping_options", fields: [ diff --git a/packages/core/types/src/http/payment/common.ts b/packages/core/types/src/http/payment/common.ts index bf65e4cbd7..bbdf86ba90 100644 --- a/packages/core/types/src/http/payment/common.ts +++ b/packages/core/types/src/http/payment/common.ts @@ -56,6 +56,11 @@ export interface BasePaymentCollection { */ authorized_amount?: BigNumberValue + /** + * The amount captured within the associated payment sessions. + */ + captured_amount?: BigNumberValue + /** * The amount refunded within the associated payments. */ diff --git a/packages/core/types/src/http/return/common.ts b/packages/core/types/src/http/return/common.ts index 1b32289aaf..3f0d87d4cb 100644 --- a/packages/core/types/src/http/return/common.ts +++ b/packages/core/types/src/http/return/common.ts @@ -21,4 +21,6 @@ export interface BaseReturn { no_notification?: boolean refund_amount?: number items: BaseReturnItem[] + received_at: string + created_at: string } diff --git a/packages/medusa/src/api/admin/returns/query-config.ts b/packages/medusa/src/api/admin/returns/query-config.ts index 9b6b983908..ef67cbe938 100644 --- a/packages/medusa/src/api/admin/returns/query-config.ts +++ b/packages/medusa/src/api/admin/returns/query-config.ts @@ -14,6 +14,7 @@ export const defaultAdminReturnFields = [ "updated_at", "canceled_at", "requested_at", + "received_at", ] export const defaultAdminDetailsReturnFields = [