chore(order): aggregate statuses (#7497)

This commit is contained in:
Carlos R. L. Rodrigues
2024-05-29 07:05:42 -03:00
committed by GitHub
parent 8e66e10995
commit bbca54efa7
15 changed files with 577 additions and 48 deletions

View File

@@ -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")
})
})

View File

@@ -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
}

View File

@@ -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
}
)

View File

@@ -4,4 +4,5 @@ export * from "./create-fulfillment"
export * from "./create-orders"
export * from "./create-return"
export * from "./create-shipment"
export * from "./get-order-detail"
export * from "./update-tax-lines"