chore(order): aggregate statuses (#7497)
This commit is contained in:
1 parent
8e66e10995
commit
bbca54efa7
15 files changed
+577
-48
No files matched your search
@@ -155,6 +155,9 @@ medusaIntegrationTestRunner({
|
|||||||
version: 1,
|
version: 1,
|
||||||
display_id: 1,
|
display_id: 1,
|
||||||
payment_collections: [],
|
payment_collections: [],
|
||||||
|
payment_status: "not_paid",
|
||||||
|
fulfillments: [],
|
||||||
|
fulfillment_status: "not_fulfilled",
|
||||||
summary: expect.objectContaining({
|
summary: expect.objectContaining({
|
||||||
// TODO: add all summary fields
|
// TODO: add all summary fields
|
||||||
}),
|
}),
|
||||||
|
|||||||
+18
-15
@@ -1,27 +1,27 @@
|
|||||||
import React, { useEffect, useState } from "react"
|
|
||||||
import * as zod from "zod"
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
|
import * as zod from "zod"
|
||||||
|
|
||||||
import { useForm, useWatch } from "react-hook-form"
|
import { AdminOrder } from "@medusajs/types"
|
||||||
import { Alert, Button, Select, toast } from "@medusajs/ui"
|
import { Alert, Button, Select, toast } from "@medusajs/ui"
|
||||||
import { OrderDTO } from "@medusajs/types"
|
import { useForm, useWatch } from "react-hook-form"
|
||||||
|
|
||||||
|
import { Form } from "../../../../../components/common/form"
|
||||||
import {
|
import {
|
||||||
RouteFocusModal,
|
RouteFocusModal,
|
||||||
useRouteModal,
|
useRouteModal,
|
||||||
} from "../../../../../components/route-modal"
|
} from "../../../../../components/route-modal"
|
||||||
import { CreateFulfillmentSchema } from "./constants"
|
|
||||||
import { Form } from "../../../../../components/common/form"
|
|
||||||
import { OrderCreateFulfillmentItem } from "./order-create-fulfillment-item"
|
|
||||||
import { getFulfillableQuantity } from "../../../../../lib/order-item"
|
|
||||||
import { useCreateFulfillment } from "../../../../../hooks/api/fulfillment"
|
import { useCreateFulfillment } from "../../../../../hooks/api/fulfillment"
|
||||||
import { useStockLocations } from "../../../../../hooks/api/stock-locations"
|
|
||||||
import { useFulfillmentProviders } from "../../../../../hooks/api/fulfillment-providers"
|
import { useFulfillmentProviders } from "../../../../../hooks/api/fulfillment-providers"
|
||||||
|
import { useStockLocations } from "../../../../../hooks/api/stock-locations"
|
||||||
import { cleanNonValues, pick } from "../../../../../lib/common"
|
import { cleanNonValues, pick } from "../../../../../lib/common"
|
||||||
|
import { getFulfillableQuantity } from "../../../../../lib/order-item"
|
||||||
|
import { CreateFulfillmentSchema } from "./constants"
|
||||||
|
import { OrderCreateFulfillmentItem } from "./order-create-fulfillment-item"
|
||||||
|
|
||||||
type OrderCreateFulfillmentFormProps = {
|
type OrderCreateFulfillmentFormProps = {
|
||||||
order: OrderDTO
|
order: AdminOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export function OrderCreateFulfillmentForm({
|
export function OrderCreateFulfillmentForm({
|
||||||
@@ -43,10 +43,13 @@ export function OrderCreateFulfillmentForm({
|
|||||||
|
|
||||||
const form = useForm<zod.infer<typeof CreateFulfillmentSchema>>({
|
const form = useForm<zod.infer<typeof CreateFulfillmentSchema>>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
quantity: fulfillableItems.reduce((acc, item) => {
|
quantity: fulfillableItems.reduce(
|
||||||
acc[item.id] = getFulfillableQuantity(item)
|
(acc, item) => {
|
||||||
return acc
|
acc[item.id] = getFulfillableQuantity(item)
|
||||||
}, {} as Record<string, number>),
|
return acc
|
||||||
|
},
|
||||||
|
{} as Record<string, number>
|
||||||
|
),
|
||||||
// send_notification: !order.no_notification,
|
// send_notification: !order.no_notification,
|
||||||
},
|
},
|
||||||
resolver: zodResolver(CreateFulfillmentSchema),
|
resolver: zodResolver(CreateFulfillmentSchema),
|
||||||
|
|||||||
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
|
import { AdminOrder } from "@medusajs/types"
|
||||||
import { Container, Heading } from "@medusajs/ui"
|
import { Container, Heading } from "@medusajs/ui"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { OrderNoteForm } from "./order-note-form"
|
import { OrderNoteForm } from "./order-note-form"
|
||||||
import { OrderTimeline } from "./order-timeline"
|
import { OrderTimeline } from "./order-timeline"
|
||||||
import { OrderDTO } from "@medusajs/types"
|
|
||||||
|
|
||||||
type OrderActivityProps = {
|
type OrderActivityProps = {
|
||||||
order: OrderDTO
|
order: AdminOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OrderActivitySection = ({ order }: OrderActivityProps) => {
|
export const OrderActivitySection = ({ order }: OrderActivityProps) => {
|
||||||
|
|||||||
+2
-2
@@ -5,12 +5,12 @@ import { useRef } from "react"
|
|||||||
import { useForm } from "react-hook-form"
|
import { useForm } from "react-hook-form"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
|
import { AdminOrder } from "@medusajs/types"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { Form } from "../../../../../components/common/form"
|
import { Form } from "../../../../../components/common/form"
|
||||||
import { OrderDTO } from "@medusajs/types"
|
|
||||||
|
|
||||||
type OrderNoteFormProps = {
|
type OrderNoteFormProps = {
|
||||||
order: OrderDTO
|
order: AdminOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
const OrderNoteSchema = z.object({
|
const OrderNoteSchema = z.object({
|
||||||
|
|||||||
+2
-3
@@ -6,14 +6,13 @@ import { PropsWithChildren, ReactNode, useMemo, useState } from "react"
|
|||||||
import { Link } from "react-router-dom"
|
import { Link } from "react-router-dom"
|
||||||
|
|
||||||
import { XMarkMini } from "@medusajs/icons"
|
import { XMarkMini } from "@medusajs/icons"
|
||||||
|
import { AdminOrder } from "@medusajs/types"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
import { Skeleton } from "../../../../../components/common/skeleton"
|
import { Skeleton } from "../../../../../components/common/skeleton"
|
||||||
import { useDate } from "../../../../../hooks/use-date"
|
import { useDate } from "../../../../../hooks/use-date"
|
||||||
import { getStylizedAmount } from "../../../../../lib/money-amount-helpers"
|
|
||||||
import { OrderDTO } from "@medusajs/types"
|
|
||||||
|
|
||||||
type OrderTimelineProps = {
|
type OrderTimelineProps = {
|
||||||
order: OrderDTO
|
order: AdminOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+4
-8
@@ -1,7 +1,7 @@
|
|||||||
import { Buildings, XCircle } from "@medusajs/icons"
|
import { Buildings, XCircle } from "@medusajs/icons"
|
||||||
import {
|
import {
|
||||||
|
AdminOrder,
|
||||||
FulfillmentDTO,
|
FulfillmentDTO,
|
||||||
OrderDTO,
|
|
||||||
OrderLineItemDTO,
|
OrderLineItemDTO,
|
||||||
ProductVariantDTO,
|
ProductVariantDTO,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
@@ -27,7 +27,7 @@ import { formatProvider } from "../../../../../lib/format-provider"
|
|||||||
import { getLocaleAmount } from "../../../../../lib/money-amount-helpers"
|
import { getLocaleAmount } from "../../../../../lib/money-amount-helpers"
|
||||||
|
|
||||||
type OrderFulfillmentSectionProps = {
|
type OrderFulfillmentSectionProps = {
|
||||||
order: OrderDTO & { fulfillments: FulfillmentDTO[] }
|
order: AdminOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OrderFulfillmentSection = ({
|
export const OrderFulfillmentSection = ({
|
||||||
@@ -103,11 +103,7 @@ const UnfulfilledItem = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const UnfulfilledItemBreakdown = ({
|
const UnfulfilledItemBreakdown = ({ order }: { order: AdminOrder }) => {
|
||||||
order,
|
|
||||||
}: {
|
|
||||||
order: OrderDTO & { fulfillments: FulfillmentDTO[] }
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
// Create an array of order items that haven't been fulfilled or at least not fully fulfilled
|
// Create an array of order items that haven't been fulfilled or at least not fully fulfilled
|
||||||
@@ -161,7 +157,7 @@ const Fulfillment = ({
|
|||||||
index,
|
index,
|
||||||
}: {
|
}: {
|
||||||
fulfillment: FulfillmentDTO
|
fulfillment: FulfillmentDTO
|
||||||
order: OrderDTO
|
order: AdminOrder
|
||||||
index: number
|
index: number
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|||||||
+10
-7
@@ -1,5 +1,8 @@
|
|||||||
import { Buildings, PencilSquare, ArrowUturnLeft } from "@medusajs/icons"
|
import {
|
||||||
import { OrderDTO, OrderLineItemDTO, ReservationItemDTO } from "@medusajs/types"
|
AdminOrder,
|
||||||
|
OrderLineItemDTO,
|
||||||
|
ReservationItemDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
import { Container, Copy, Heading, StatusBadge, Text } from "@medusajs/ui"
|
import { Container, Copy, Heading, StatusBadge, Text } from "@medusajs/ui"
|
||||||
import { useTranslation } from "react-i18next"
|
import { useTranslation } from "react-i18next"
|
||||||
|
|
||||||
@@ -11,7 +14,7 @@ import {
|
|||||||
} from "../../../../../lib/money-amount-helpers"
|
} from "../../../../../lib/money-amount-helpers"
|
||||||
|
|
||||||
type OrderSummarySectionProps = {
|
type OrderSummarySectionProps = {
|
||||||
order: OrderDTO
|
order: AdminOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => {
|
export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => {
|
||||||
@@ -25,7 +28,7 @@ export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Header = ({ order }: { order: OrderDTO }) => {
|
const Header = ({ order }: { order: AdminOrder }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -129,7 +132,7 @@ const Item = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ItemBreakdown = ({ order }: { order: OrderDTO }) => {
|
const ItemBreakdown = ({ order }: { order: AdminOrder }) => {
|
||||||
// const { reservations, isError, error } = useAdminReservations({
|
// const { reservations, isError, error } = useAdminReservations({
|
||||||
// line_item_id: order.items.map((i) => i.id),
|
// line_item_id: order.items.map((i) => i.id),
|
||||||
// })
|
// })
|
||||||
@@ -184,7 +187,7 @@ const Cost = ({
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
const CostBreakdown = ({ order }: { order: OrderDTO }) => {
|
const CostBreakdown = ({ order }: { order: AdminOrder }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -230,7 +233,7 @@ const CostBreakdown = ({ order }: { order: OrderDTO }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Total = ({ order }: { order: OrderDTO }) => {
|
const Total = ({ order }: { order: AdminOrder }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -0,0 +1,227 @@
|
|||||||
|
import {
|
||||||
|
getLastFulfillmentStatus,
|
||||||
|
getLastPaymentStatus,
|
||||||
|
} from "../aggregate-status"
|
||||||
|
|
||||||
|
describe("Aggregate Order Status", () => {
|
||||||
|
it("should return aggregated payment collection status", () => {
|
||||||
|
expect(
|
||||||
|
getLastPaymentStatus({
|
||||||
|
payment_collections: [],
|
||||||
|
})
|
||||||
|
).toEqual("not_paid")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastPaymentStatus({
|
||||||
|
payment_collections: [{ status: "not_paid" }],
|
||||||
|
})
|
||||||
|
).toEqual("not_paid")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastPaymentStatus({
|
||||||
|
payment_collections: [{ status: "not_paid" }, { status: "awaiting" }],
|
||||||
|
})
|
||||||
|
).toEqual("awaiting")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastPaymentStatus({
|
||||||
|
payment_collections: [
|
||||||
|
{ status: "requires_action" },
|
||||||
|
{ status: "refunded" },
|
||||||
|
{ status: "refunded" },
|
||||||
|
{ status: "captured" },
|
||||||
|
{ status: "captured" },
|
||||||
|
{ status: "canceled" },
|
||||||
|
{ status: "authorized" },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toEqual("requires_action")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastPaymentStatus({
|
||||||
|
payment_collections: [
|
||||||
|
{ status: "awaiting" },
|
||||||
|
{ status: "awaiting" },
|
||||||
|
{ status: "canceled" },
|
||||||
|
{ status: "awaiting" },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toEqual("awaiting")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastPaymentStatus({
|
||||||
|
payment_collections: [
|
||||||
|
{ status: "authorized" },
|
||||||
|
{ status: "authorized" },
|
||||||
|
{ status: "canceled" },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toEqual("authorized")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastPaymentStatus({
|
||||||
|
payment_collections: [
|
||||||
|
{ status: "awaiting" },
|
||||||
|
{ status: "authorized" },
|
||||||
|
{ status: "canceled" },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toEqual("partially_authorized")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastPaymentStatus({
|
||||||
|
payment_collections: [
|
||||||
|
{ status: "authorized", refunded_amount: 10, amount: 10 },
|
||||||
|
{ status: "authorized", refunded_amount: 5, amount: 10 },
|
||||||
|
{ status: "canceled" },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toEqual("partially_refunded")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastPaymentStatus({
|
||||||
|
payment_collections: [
|
||||||
|
{ status: "authorized", captured_amount: 10, amount: 10 },
|
||||||
|
{ status: "authorized", refunded_amount: 10, amount: 10 },
|
||||||
|
{ status: "authorized", refunded_amount: 10, amount: 10 },
|
||||||
|
{ status: "authorized" },
|
||||||
|
{ status: "canceled" },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toEqual("partially_refunded")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastPaymentStatus({
|
||||||
|
payment_collections: [
|
||||||
|
{ status: "authorized", captured_amount: 10, amount: 10 },
|
||||||
|
{ status: "authorized", captured_amount: 12, amount: 12 },
|
||||||
|
{ status: "canceled" },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toEqual("captured")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastPaymentStatus({
|
||||||
|
payment_collections: [
|
||||||
|
{ status: "authorized", captured_amount: 10, amount: 10 },
|
||||||
|
{ status: "authorized", captured_amount: 5, amount: 10 },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toEqual("partially_captured")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastPaymentStatus({
|
||||||
|
payment_collections: [
|
||||||
|
{ status: "authorized", captured_amount: 10, amount: 10 },
|
||||||
|
{ status: "authorized", captured_amount: 10, amount: 10 },
|
||||||
|
{ status: "authorized" },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toEqual("partially_captured")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastPaymentStatus({
|
||||||
|
payment_collections: [
|
||||||
|
{ status: "authorized", captured_amount: 10, amount: 10 },
|
||||||
|
{ status: "authorized", captured_amount: 12, amount: 12 },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toEqual("captured")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastPaymentStatus({
|
||||||
|
payment_collections: [{ status: "canceled" }, { status: "canceled" }],
|
||||||
|
})
|
||||||
|
).toEqual("canceled")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return aggregated fulfillment status", () => {
|
||||||
|
expect(
|
||||||
|
getLastFulfillmentStatus({
|
||||||
|
fulfillments: [],
|
||||||
|
})
|
||||||
|
).toEqual("not_fulfilled")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastFulfillmentStatus({
|
||||||
|
fulfillments: [{ created_at: new Date() }],
|
||||||
|
})
|
||||||
|
).toEqual("not_fulfilled")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastFulfillmentStatus({
|
||||||
|
fulfillments: [{ created_at: new Date() }, { packed_at: new Date() }],
|
||||||
|
})
|
||||||
|
).toEqual("partially_fulfilled")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastFulfillmentStatus({
|
||||||
|
fulfillments: [{ packed_at: new Date() }, { packed_at: new Date() }],
|
||||||
|
})
|
||||||
|
).toEqual("fulfilled")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastFulfillmentStatus({
|
||||||
|
fulfillments: [{ shipped_at: new Date() }, { packed_at: new Date() }],
|
||||||
|
})
|
||||||
|
).toEqual("partially_shipped")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastFulfillmentStatus({
|
||||||
|
fulfillments: [{ shipped_at: new Date() }, { shipped_at: new Date() }],
|
||||||
|
})
|
||||||
|
).toEqual("shipped")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastFulfillmentStatus({
|
||||||
|
fulfillments: [
|
||||||
|
{ shipped_at: new Date() },
|
||||||
|
{ delivered_at: new Date() },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toEqual("partially_delivered")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastFulfillmentStatus({
|
||||||
|
fulfillments: [
|
||||||
|
{ delivered_at: new Date() },
|
||||||
|
{ delivered_at: new Date() },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toEqual("delivered")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastFulfillmentStatus({
|
||||||
|
fulfillments: [
|
||||||
|
{ delivered_at: new Date() },
|
||||||
|
{ canceled_at: new Date() },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toEqual("delivered")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastFulfillmentStatus({
|
||||||
|
fulfillments: [{ shipped_at: new Date() }, { canceled_at: new Date() }],
|
||||||
|
})
|
||||||
|
).toEqual("shipped")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastFulfillmentStatus({
|
||||||
|
fulfillments: [
|
||||||
|
{ packed_at: new Date() },
|
||||||
|
{ shipped_at: new Date() },
|
||||||
|
{ canceled_at: new Date() },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toEqual("partially_shipped")
|
||||||
|
|
||||||
|
expect(
|
||||||
|
getLastFulfillmentStatus({
|
||||||
|
fulfillments: [
|
||||||
|
{ canceled_at: new Date() },
|
||||||
|
{ canceled_at: new Date() },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
).toEqual("canceled")
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
import { OrderDetailDTO } from "@medusajs/types"
|
||||||
|
import { MathBN } from "@medusajs/utils"
|
||||||
|
|
||||||
|
export const getLastPaymentStatus = (order: OrderDetailDTO) => {
|
||||||
|
const PaymentStatus = {
|
||||||
|
NOT_PAID: "not_paid",
|
||||||
|
AWAITING: "awaiting",
|
||||||
|
CAPTURED: "captured",
|
||||||
|
PARTIALLY_CAPTURED: "partially_captured",
|
||||||
|
PARTIALLY_REFUNDED: "partially_refunded",
|
||||||
|
REFUNDED: "refunded",
|
||||||
|
CANCELED: "canceled",
|
||||||
|
REQUIRES_ACTION: "requires_action",
|
||||||
|
AUTHORIZED: "authorized",
|
||||||
|
PARTIALLY_AUTHORIZED: "partially_authorized",
|
||||||
|
}
|
||||||
|
|
||||||
|
let paymentStatus = {}
|
||||||
|
for (const status in PaymentStatus) {
|
||||||
|
paymentStatus[PaymentStatus[status]] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const paymentCollection of order.payment_collections) {
|
||||||
|
if (MathBN.gt(paymentCollection.captured_amount ?? 0, 0)) {
|
||||||
|
paymentStatus[PaymentStatus.CAPTURED] += MathBN.eq(
|
||||||
|
paymentCollection.captured_amount as number,
|
||||||
|
paymentCollection.amount
|
||||||
|
)
|
||||||
|
? 1
|
||||||
|
: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MathBN.gt(paymentCollection.refunded_amount ?? 0, 0)) {
|
||||||
|
paymentStatus[PaymentStatus.REFUNDED] += MathBN.eq(
|
||||||
|
paymentCollection.refunded_amount as number,
|
||||||
|
paymentCollection.amount
|
||||||
|
)
|
||||||
|
? 1
|
||||||
|
: 0.5
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentStatus[paymentCollection.status] += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalPayments = order.payment_collections.length
|
||||||
|
const totalPaymentExceptCanceled =
|
||||||
|
totalPayments - paymentStatus[PaymentStatus.CANCELED]
|
||||||
|
|
||||||
|
if (paymentStatus[PaymentStatus.REQUIRES_ACTION] > 0) {
|
||||||
|
return PaymentStatus.REQUIRES_ACTION
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paymentStatus[PaymentStatus.REFUNDED] > 0) {
|
||||||
|
if (paymentStatus[PaymentStatus.REFUNDED] === totalPaymentExceptCanceled) {
|
||||||
|
return PaymentStatus.REFUNDED
|
||||||
|
}
|
||||||
|
|
||||||
|
return PaymentStatus.PARTIALLY_REFUNDED
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paymentStatus[PaymentStatus.CAPTURED] > 0) {
|
||||||
|
if (paymentStatus[PaymentStatus.CAPTURED] === totalPaymentExceptCanceled) {
|
||||||
|
return PaymentStatus.CAPTURED
|
||||||
|
}
|
||||||
|
|
||||||
|
return PaymentStatus.PARTIALLY_CAPTURED
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paymentStatus[PaymentStatus.AUTHORIZED] > 0) {
|
||||||
|
if (
|
||||||
|
paymentStatus[PaymentStatus.AUTHORIZED] === totalPaymentExceptCanceled
|
||||||
|
) {
|
||||||
|
return PaymentStatus.AUTHORIZED
|
||||||
|
}
|
||||||
|
|
||||||
|
return PaymentStatus.PARTIALLY_AUTHORIZED
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
paymentStatus[PaymentStatus.CANCELED] > 0 &&
|
||||||
|
paymentStatus[PaymentStatus.CANCELED] === totalPayments
|
||||||
|
) {
|
||||||
|
return PaymentStatus.CANCELED
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paymentStatus[PaymentStatus.AWAITING] > 0) {
|
||||||
|
return PaymentStatus.AWAITING
|
||||||
|
}
|
||||||
|
|
||||||
|
return PaymentStatus.NOT_PAID
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getLastFulfillmentStatus = (order: OrderDetailDTO) => {
|
||||||
|
const FulfillmentStatus = {
|
||||||
|
NOT_FULFILLED: "not_fulfilled",
|
||||||
|
PARTIALLY_FULFILLED: "partially_fulfilled",
|
||||||
|
FULFILLED: "fulfilled",
|
||||||
|
PARTIALLY_SHIPPED: "partially_shipped",
|
||||||
|
SHIPPED: "shipped",
|
||||||
|
DELIVERED: "delivered",
|
||||||
|
PARTIALLY_DELIVERED: "partially_delivered",
|
||||||
|
CANCELED: "canceled",
|
||||||
|
}
|
||||||
|
|
||||||
|
let fulfillmentStatus = {}
|
||||||
|
for (const status in FulfillmentStatus) {
|
||||||
|
fulfillmentStatus[FulfillmentStatus[status]] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusMap = {
|
||||||
|
packed_at: FulfillmentStatus.FULFILLED,
|
||||||
|
shipped_at: FulfillmentStatus.SHIPPED,
|
||||||
|
delivered_at: FulfillmentStatus.DELIVERED,
|
||||||
|
canceled_at: FulfillmentStatus.CANCELED,
|
||||||
|
}
|
||||||
|
for (const fulfillmentCollection of order.fulfillments) {
|
||||||
|
for (const key in statusMap) {
|
||||||
|
if (fulfillmentCollection[key]) {
|
||||||
|
fulfillmentStatus[statusMap[key]] += 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalFulfillments = order.fulfillments.length
|
||||||
|
const totalFulfillmentsExceptCanceled =
|
||||||
|
totalFulfillments - fulfillmentStatus[FulfillmentStatus.CANCELED]
|
||||||
|
|
||||||
|
if (fulfillmentStatus[FulfillmentStatus.DELIVERED] > 0) {
|
||||||
|
if (
|
||||||
|
fulfillmentStatus[FulfillmentStatus.DELIVERED] ===
|
||||||
|
totalFulfillmentsExceptCanceled
|
||||||
|
) {
|
||||||
|
return FulfillmentStatus.DELIVERED
|
||||||
|
}
|
||||||
|
|
||||||
|
return FulfillmentStatus.PARTIALLY_DELIVERED
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fulfillmentStatus[FulfillmentStatus.SHIPPED] > 0) {
|
||||||
|
if (
|
||||||
|
fulfillmentStatus[FulfillmentStatus.SHIPPED] ===
|
||||||
|
totalFulfillmentsExceptCanceled
|
||||||
|
) {
|
||||||
|
return FulfillmentStatus.SHIPPED
|
||||||
|
}
|
||||||
|
|
||||||
|
return FulfillmentStatus.PARTIALLY_SHIPPED
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fulfillmentStatus[FulfillmentStatus.FULFILLED] > 0) {
|
||||||
|
if (
|
||||||
|
fulfillmentStatus[FulfillmentStatus.FULFILLED] ===
|
||||||
|
totalFulfillmentsExceptCanceled
|
||||||
|
) {
|
||||||
|
return FulfillmentStatus.FULFILLED
|
||||||
|
}
|
||||||
|
|
||||||
|
return FulfillmentStatus.PARTIALLY_FULFILLED
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
fulfillmentStatus[FulfillmentStatus.CANCELED] > 0 &&
|
||||||
|
fulfillmentStatus[FulfillmentStatus.CANCELED] === totalFulfillments
|
||||||
|
) {
|
||||||
|
return FulfillmentStatus.CANCELED
|
||||||
|
}
|
||||||
|
|
||||||
|
return FulfillmentStatus.NOT_FULFILLED
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { OrderDTO, OrderDetailDTO } from "@medusajs/types"
|
||||||
|
import { deduplicate } from "@medusajs/utils"
|
||||||
|
import {
|
||||||
|
WorkflowData,
|
||||||
|
createWorkflow,
|
||||||
|
transform,
|
||||||
|
} from "@medusajs/workflows-sdk"
|
||||||
|
import { useRemoteQueryStep } from "../../common"
|
||||||
|
import {
|
||||||
|
getLastFulfillmentStatus,
|
||||||
|
getLastPaymentStatus,
|
||||||
|
} from "../utils/aggregate-status"
|
||||||
|
|
||||||
|
export const getOrderDetailWorkflowId = "get-order-detail"
|
||||||
|
export const getOrderDetailWorkflow = createWorkflow(
|
||||||
|
getOrderDetailWorkflowId,
|
||||||
|
(
|
||||||
|
input: WorkflowData<{ fields: string[]; order_id: string }>
|
||||||
|
): WorkflowData<OrderDetailDTO> => {
|
||||||
|
const fields = transform(input, ({ fields }) => {
|
||||||
|
return deduplicate([
|
||||||
|
...fields,
|
||||||
|
"id",
|
||||||
|
"status",
|
||||||
|
"version",
|
||||||
|
"payment_collections.*",
|
||||||
|
"fulfillments.*",
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
|
const order: OrderDTO = useRemoteQueryStep({
|
||||||
|
entry_point: "orders",
|
||||||
|
fields,
|
||||||
|
variables: { id: input.order_id },
|
||||||
|
list: false,
|
||||||
|
throw_if_key_not_found: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
const aggregatedOrder = transform({ order }, ({ order }) => {
|
||||||
|
const order_ = order as OrderDetailDTO
|
||||||
|
|
||||||
|
order_.payment_status = getLastPaymentStatus(
|
||||||
|
order_
|
||||||
|
) as OrderDetailDTO["payment_status"]
|
||||||
|
order_.fulfillment_status = getLastFulfillmentStatus(
|
||||||
|
order_
|
||||||
|
) as OrderDetailDTO["fulfillment_status"]
|
||||||
|
return order_
|
||||||
|
})
|
||||||
|
|
||||||
|
return aggregatedOrder
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -4,4 +4,5 @@ export * from "./create-fulfillment"
|
|||||||
export * from "./create-orders"
|
export * from "./create-orders"
|
||||||
export * from "./create-return"
|
export * from "./create-return"
|
||||||
export * from "./create-shipment"
|
export * from "./create-shipment"
|
||||||
|
export * from "./get-order-detail"
|
||||||
export * from "./update-tax-lines"
|
export * from "./update-tax-lines"
|
||||||
@@ -221,6 +221,43 @@ export interface BaseOrderTransaction {
|
|||||||
updated_at: Date | string
|
updated_at: Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BaseOrderFulfillment {
|
||||||
|
id: string
|
||||||
|
location_id: string
|
||||||
|
packed_at: Date | null
|
||||||
|
shipped_at: Date | null
|
||||||
|
delivered_at: Date | null
|
||||||
|
canceled_at: Date | null
|
||||||
|
data: Record<string, unknown> | null
|
||||||
|
provider_id: string
|
||||||
|
shipping_option_id: string | null
|
||||||
|
metadata: Record<string, unknown> | null
|
||||||
|
created_at: Date
|
||||||
|
updated_at: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
type PaymentStatus =
|
||||||
|
| "not_paid"
|
||||||
|
| "awaiting"
|
||||||
|
| "authorized"
|
||||||
|
| "partially_authorized"
|
||||||
|
| "captured"
|
||||||
|
| "partially_captured"
|
||||||
|
| "partially_refunded"
|
||||||
|
| "refunded"
|
||||||
|
| "canceled"
|
||||||
|
| "requires_action"
|
||||||
|
|
||||||
|
type FulfillmentStatus =
|
||||||
|
| "not_fulfilled"
|
||||||
|
| "partially_fulfilled"
|
||||||
|
| "fulfilled"
|
||||||
|
| "partially_shipped"
|
||||||
|
| "shipped"
|
||||||
|
| "partially_delivered"
|
||||||
|
| "delivered"
|
||||||
|
| "canceled"
|
||||||
|
|
||||||
export interface BaseOrder {
|
export interface BaseOrder {
|
||||||
id: string
|
id: string
|
||||||
version: number
|
version: number
|
||||||
@@ -234,7 +271,10 @@ export interface BaseOrder {
|
|||||||
billing_address?: BaseOrderAddress
|
billing_address?: BaseOrderAddress
|
||||||
items: BaseOrderLineItem[] | null
|
items: BaseOrderLineItem[] | null
|
||||||
shipping_methods: BaseOrderShippingMethod[] | null
|
shipping_methods: BaseOrderShippingMethod[] | null
|
||||||
payment_collection?: BasePaymentCollection
|
payment_collections?: BasePaymentCollection[]
|
||||||
|
payment_status: PaymentStatus
|
||||||
|
fulfillments?: BaseOrderFulfillment[]
|
||||||
|
fulfillment_status: FulfillmentStatus
|
||||||
transactions?: BaseOrderTransaction[]
|
transactions?: BaseOrderTransaction[]
|
||||||
summary: BaseOrderSummary
|
summary: BaseOrderSummary
|
||||||
metadata: Record<string, unknown> | null
|
metadata: Record<string, unknown> | null
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { BaseFilterable } from "../dal"
|
import { BaseFilterable } from "../dal"
|
||||||
import { OperatorMap } from "../dal/utils"
|
import { OperatorMap } from "../dal/utils"
|
||||||
|
import { FulfillmentDTO } from "../fulfillment"
|
||||||
|
import { PaymentCollectionDTO } from "../payment"
|
||||||
import { BigNumberRawValue, BigNumberValue } from "../totals"
|
import { BigNumberRawValue, BigNumberValue } from "../totals"
|
||||||
|
|
||||||
export type ChangeActionType =
|
export type ChangeActionType =
|
||||||
@@ -1102,6 +1104,35 @@ export interface OrderDTO {
|
|||||||
raw_original_shipping_tax_total: BigNumberRawValue
|
raw_original_shipping_tax_total: BigNumberRawValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PaymentStatus =
|
||||||
|
| "not_paid"
|
||||||
|
| "awaiting"
|
||||||
|
| "authorized"
|
||||||
|
| "partially_authorized"
|
||||||
|
| "captured"
|
||||||
|
| "partially_captured"
|
||||||
|
| "partially_refunded"
|
||||||
|
| "refunded"
|
||||||
|
| "canceled"
|
||||||
|
| "requires_action"
|
||||||
|
|
||||||
|
type FulfillmentStatus =
|
||||||
|
| "not_fulfilled"
|
||||||
|
| "partially_fulfilled"
|
||||||
|
| "fulfilled"
|
||||||
|
| "partially_shipped"
|
||||||
|
| "shipped"
|
||||||
|
| "partially_delivered"
|
||||||
|
| "delivered"
|
||||||
|
| "canceled"
|
||||||
|
|
||||||
|
export interface OrderDetailDTO extends OrderDTO {
|
||||||
|
payment_collections: PaymentCollectionDTO[]
|
||||||
|
payment_status: PaymentStatus
|
||||||
|
fulfillments: FulfillmentDTO[]
|
||||||
|
fulfillment_status: FulfillmentStatus
|
||||||
|
}
|
||||||
|
|
||||||
export interface OrderChangeDTO {
|
export interface OrderChangeDTO {
|
||||||
/**
|
/**
|
||||||
* The ID of the order change
|
* The ID of the order change
|
||||||
|
|||||||
@@ -100,6 +100,11 @@ export interface PaymentCollectionDTO {
|
|||||||
*/
|
*/
|
||||||
refunded_amount?: BigNumberValue
|
refunded_amount?: BigNumberValue
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The amount captured within the associated payments.
|
||||||
|
*/
|
||||||
|
captured_amount?: BigNumberValue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the payment collection was completed.
|
* When the payment collection was completed.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { getOrderDetailWorkflow } from "@medusajs/core-flows"
|
||||||
import {
|
import {
|
||||||
ContainerRegistrationKeys,
|
ContainerRegistrationKeys,
|
||||||
remoteQueryObjectFromString,
|
remoteQueryObjectFromString,
|
||||||
@@ -11,18 +12,15 @@ export const GET = async (
|
|||||||
req: AuthenticatedMedusaRequest,
|
req: AuthenticatedMedusaRequest,
|
||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
) => {
|
) => {
|
||||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
const worklow = getOrderDetailWorkflow(req.scope)
|
||||||
|
const { result } = await worklow.run({
|
||||||
const variables = { id: req.params.id }
|
input: {
|
||||||
|
fields: req.remoteQueryConfig.fields,
|
||||||
const queryObject = remoteQueryObjectFromString({
|
order_id: req.params.id,
|
||||||
entryPoint: "order",
|
},
|
||||||
variables,
|
|
||||||
fields: req.remoteQueryConfig.fields,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const [order] = await remoteQuery(queryObject)
|
res.status(200).json({ order: result })
|
||||||
res.status(200).json({ order })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const POST = async (
|
export const POST = async (
|
||||||
|
|||||||
Reference in new issue
Block a user