feat(order): order change actions engine (#6467)

This commit is contained in:
Carlos R. L. Rodrigues
2024-02-29 16:22:14 -03:00
committed by GitHub
parent cdb01e073b
commit 03ca5c814e
70 changed files with 5727 additions and 992 deletions
+12
View File
@@ -0,0 +1,12 @@
export enum ChangeActionType {
CANCEL = "CANCEL",
CANCEL_RETURN = "CANCEL_RETURN",
FULFILL_ITEM = "FULFILL_ITEM",
ITEM_ADD = "ITEM_ADD",
ITEM_REMOVE = "ITEM_REMOVE",
RECEIVE_DAMAGED_RETURN_ITEM = "RECEIVE_DAMAGED_RETURN_ITEM",
RECEIVE_RETURN_ITEM = "RECEIVE_RETURN_ITEM",
RETURN_ITEM = "RETURN_ITEM",
SHIPPING_ADD = "SHIPPING_ADD",
WRITE_OFF_ITEM = "WRITE_OFF_ITEM",
}
@@ -0,0 +1,50 @@
import { MedusaError, isDefined } from "@medusajs/utils"
import { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change"
OrderChangeProcessing.registerActionType(ChangeActionType.CANCEL_RETURN, {
operation({ action, currentOrder }) {
const existing = currentOrder.items.find(
(item) => item.id === action.details.reference_id
)!
existing.return_requested_quantity -= action.details.quantity
return action.details.unit_price * action.details.quantity
},
revert({ action, currentOrder }) {
const existing = currentOrder.items.find(
(item) => item.id === action.details.reference_id
)!
existing.return_requested_quantity += action.details.quantity
},
validate({ action, currentOrder }) {
const refId = action.details?.reference_id
if (!isDefined(refId)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Details reference ID is required."
)
}
const existing = currentOrder.items.find((item) => item.id === refId)
if (!existing) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Reference ID "${refId}" not found.`
)
}
const notFulfilled =
(existing.quantity as number) - (existing.fulfilled_quantity as number)
if (action.details.quantity > notFulfilled) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Cannot fulfill more items than what was ordered."
)
}
},
})
@@ -0,0 +1,6 @@
import { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change"
OrderChangeProcessing.registerActionType(ChangeActionType.CANCEL, {
void: true,
})
@@ -0,0 +1,44 @@
import { MedusaError, isDefined } from "@medusajs/utils"
import { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change"
OrderChangeProcessing.registerActionType(ChangeActionType.FULFILL_ITEM, {
operation({ action, currentOrder }) {
const existing = currentOrder.items.find(
(item) => item.id === action.details.reference_id
)!
existing.fulfilled_quantity += action.details.quantity
},
revert({ action, currentOrder }) {
const existing = currentOrder.items.find(
(item) => item.id === action.reference_id
)!
existing.fulfilled_quantity -= action.details.quantity
},
validate({ action, currentOrder }) {
const refId = action.details.reference_id
if (!isDefined(refId)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Reference ID is required."
)
}
const existing = currentOrder.items.find((item) => item.id === refId)
if (!existing) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Reference ID "${refId}" not found.`
)
}
if (action.details.quantity < 1) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Quantity must be greater than 0."
)
}
},
})
@@ -0,0 +1,9 @@
export * from "./cancel"
export * from "./cancel-return"
export * from "./fulfill-item"
export * from "./item-add"
export * from "./item-remove"
export * from "./receive-damaged-return-item"
export * from "./receive-return-item"
export * from "./return-item"
export * from "./shipping-add"
@@ -0,0 +1,60 @@
import { MedusaError, isDefined } from "@medusajs/utils"
import { VirtualOrder } from "@types"
import { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change"
OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_ADD, {
operation({ action, currentOrder }) {
const existing = currentOrder.items.find(
(item) => item.id === action.reference_id
)
if (existing) {
existing.quantity += action.details.quantity
} else {
currentOrder.items.push({
id: action.reference_id!,
unit_price: action.details.unit_price,
quantity: action.details.quantity,
} as VirtualOrder["items"][0])
}
return action.details.unit_price * action.details.quantity
},
revert({ action, currentOrder }) {
const existingIndex = currentOrder.items.findIndex(
(item) => item.id === action.reference_id
)
if (existingIndex > -1) {
const existing = currentOrder.items[existingIndex]
existing.quantity -= action.details.quantity
if (existing.quantity <= 0) {
currentOrder.items.splice(existingIndex, 1)
}
}
},
validate({ action }) {
if (!isDefined(action.reference_id)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Reference ID is required."
)
}
if (!isDefined(action.details.unit_price)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Unit price is required."
)
}
if (action.details.quantity < 1) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Quantity must be greater than 0."
)
}
},
})
@@ -0,0 +1,78 @@
import { MedusaError, isDefined } from "@medusajs/utils"
import { VirtualOrder } from "@types"
import { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change"
OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_REMOVE, {
isDeduction: true,
operation({ action, currentOrder }) {
const existingIndex = currentOrder.items.findIndex(
(item) => item.id === action.reference_id
)
const existing = currentOrder.items[existingIndex]
existing.quantity -= action.details.quantity
if (existing.quantity <= 0) {
currentOrder.items.splice(existingIndex, 1)
}
return existing.unit_price * action.details.quantity
},
revert({ action, currentOrder }) {
const existing = currentOrder.items.find(
(item) => item.id === action.reference_id
)
if (existing) {
existing.quantity += action.details.quantity
} else {
currentOrder.items.push({
id: action.reference_id!,
unit_price: action.details.unit_price,
quantity: action.details.quantity,
} as VirtualOrder["items"][0])
}
},
validate({ action, currentOrder }) {
const refId = action.reference_id
if (!isDefined(refId)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Reference ID is required."
)
}
const existing = currentOrder.items.find((item) => item.id === refId)
if (!existing) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Reference ID "${refId}" not found.`
)
}
if (!isDefined(action.details.unit_price)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Unit price is required."
)
}
if (action.details.quantity < 1) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Quantity must be greater than 0."
)
}
const notFulfilled =
(existing.quantity as number) - (existing.fulfilled_quantity as number)
if (action.details.quantity > notFulfilled) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Cannot remove fulfilled items."
)
}
},
})
@@ -0,0 +1,88 @@
import { MedusaError, isDefined } from "@medusajs/utils"
import { EVENT_STATUS } from "@types"
import { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change"
OrderChangeProcessing.registerActionType(
ChangeActionType.RECEIVE_DAMAGED_RETURN_ITEM,
{
isDeduction: true,
commitsAction: "return_item",
operation({ action, currentOrder, previousEvents }) {
const existing = currentOrder.items.find(
(item) => item.id === action.details.reference_id
)!
let toReturn = action.details.quantity
existing.return_dismissed_quantity ??= 0
existing.return_dismissed_quantity += toReturn
existing.return_requested_quantity -= toReturn
if (previousEvents) {
for (const previousEvent of previousEvents) {
previousEvent.original_ = JSON.parse(JSON.stringify(previousEvent))
let ret = Math.min(toReturn, previousEvent.details.quantity)
toReturn -= ret
previousEvent.details.quantity -= ret
if (previousEvent.details.quantity <= 0) {
previousEvent.status = EVENT_STATUS.DONE
}
}
}
return existing.unit_price * action.details.quantity
},
revert({ action, currentOrder, previousEvents }) {
const existing = currentOrder.items.find(
(item) => item.id === action.details.reference_id
)!
existing.return_dismissed_quantity -= action.details.quantity
existing.return_requested_quantity += action.details.quantity
if (previousEvents) {
for (const previousEvent of previousEvents) {
if (!previousEvent.original_) {
continue
}
previousEvent.details = JSON.parse(
JSON.stringify(previousEvent.original_.details)
)
delete previousEvent.original_
previousEvent.status = EVENT_STATUS.PENDING
}
}
},
validate({ action, currentOrder }) {
const refId = action.details?.reference_id
if (!isDefined(refId)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Details reference ID is required."
)
}
const existing = currentOrder.items.find((item) => item.id === refId)
if (!existing) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Reference ID "${refId}" not found.`
)
}
const quantityRequested = existing?.return_requested_quantity || 0
if (action.details.quantity > quantityRequested) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Cannot receive more items than what was requested to be returned."
)
}
},
}
)
@@ -0,0 +1,85 @@
import { MedusaError, isDefined } from "@medusajs/utils"
import { EVENT_STATUS } from "@types"
import { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change"
OrderChangeProcessing.registerActionType(ChangeActionType.RECEIVE_RETURN_ITEM, {
isDeduction: true,
commitsAction: "return_item",
operation({ action, currentOrder, previousEvents }) {
const existing = currentOrder.items.find(
(item) => item.id === action.details.reference_id
)!
let toReturn = action.details.quantity
existing.return_received_quantity ??= 0
existing.return_received_quantity += toReturn
existing.return_requested_quantity -= toReturn
if (previousEvents) {
for (const previousEvent of previousEvents) {
previousEvent.original_ = JSON.parse(JSON.stringify(previousEvent))
let ret = Math.min(toReturn, previousEvent.details.quantity)
toReturn -= ret
previousEvent.details.quantity -= ret
if (previousEvent.details.quantity <= 0) {
previousEvent.status = EVENT_STATUS.DONE
}
}
}
return existing.unit_price * action.details.quantity
},
revert({ action, currentOrder, previousEvents }) {
const existing = currentOrder.items.find(
(item) => item.id === action.details.reference_id
)!
existing.return_received_quantity -= action.details.quantity
existing.return_requested_quantity += action.details.quantity
if (previousEvents) {
for (const previousEvent of previousEvents) {
if (!previousEvent.original_) {
continue
}
previousEvent.details = JSON.parse(
JSON.stringify(previousEvent.original_.details)
)
delete previousEvent.original_
previousEvent.status = EVENT_STATUS.PENDING
}
}
},
validate({ action, currentOrder }) {
const refId = action.details?.reference_id
if (!isDefined(refId)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Details reference ID is required."
)
}
const existing = currentOrder.items.find((item) => item.id === refId)
if (!existing) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Reference ID "${refId}" not found.`
)
}
const quantityRequested = existing?.return_requested_quantity || 0
if (action.details.quantity > quantityRequested) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Cannot receive more items than what was requested to be returned."
)
}
},
})
@@ -0,0 +1,54 @@
import { MedusaError, isDefined } from "@medusajs/utils"
import { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change"
OrderChangeProcessing.registerActionType(ChangeActionType.RETURN_ITEM, {
isDeduction: true,
awaitRequired: true,
operation({ action, currentOrder }) {
const existing = currentOrder.items.find(
(item) => item.id === action.details.reference_id
)!
existing.return_requested_quantity ??= 0
existing.return_requested_quantity += action.details.quantity
return existing.unit_price * action.details.quantity
},
revert({ action, currentOrder }) {
const existing = currentOrder.items.find(
(item) => item.id === action.details.reference_id
)!
existing.return_requested_quantity -= action.details.quantity
},
validate({ action, currentOrder }) {
const refId = action.details?.reference_id
if (!isDefined(refId)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Details reference ID is required."
)
}
const existing = currentOrder.items.find((item) => item.id === refId)
if (!existing) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Reference ID "${refId}" not found.`
)
}
const quantityAvailable =
(existing!.fulfilled_quantity ?? 0) -
(existing!.return_requested_quantity ?? 0)
if (action.details.quantity > quantityAvailable) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Cannot request to return more items than what was fulfilled."
)
}
},
})
@@ -0,0 +1,47 @@
import { MedusaError, isDefined } from "@medusajs/utils"
import { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change"
OrderChangeProcessing.registerActionType(ChangeActionType.SHIPPING_ADD, {
operation({ action, currentOrder }) {
const shipping = Array.isArray(currentOrder.shipping_methods)
? currentOrder.shipping_methods
: [currentOrder.shipping_methods]
shipping.push({
id: action.reference_id!,
price: action.amount as number,
})
currentOrder.shipping_methods = shipping
return action.amount
},
revert({ action, currentOrder }) {
const shipping = Array.isArray(currentOrder.shipping_methods)
? currentOrder.shipping_methods
: [currentOrder.shipping_methods]
const existingIndex = shipping.findIndex(
(item) => item.id === action.reference_id
)
if (existingIndex > -1) {
shipping.splice(existingIndex, 1)
}
},
validate({ action }) {
if (!action.reference_id) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Reference ID is required."
)
}
if (!isDefined(action.amount)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Amount is required."
)
}
},
})
@@ -0,0 +1,47 @@
import { MedusaError, isDefined } from "@medusajs/utils"
import { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change"
OrderChangeProcessing.registerActionType(ChangeActionType.WRITE_OFF_ITEM, {
operation({ action, currentOrder }) {
const existing = currentOrder.items.find(
(item) => item.id === action.details.reference_id
)!
existing.written_off_quantity ??= 0
existing.written_off_quantity += action.details.quantity
},
revert({ action, currentOrder }) {
const existing = currentOrder.items.find(
(item) => item.id === action.details.reference_id
)!
existing.written_off_quantity -= action.details.quantity
},
validate({ action, currentOrder }) {
const refId = action.details?.reference_id
if (!isDefined(refId)) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Details reference ID is required."
)
}
const existing = currentOrder.items.find((item) => item.id === refId)
if (!existing) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Reference ID "${refId}" not found.`
)
}
const quantityAvailable = existing!.quantity ?? 0
if (action.details.quantity > quantityAvailable) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Cannot claim more items than what was ordered."
)
}
},
})
@@ -0,0 +1,353 @@
import {
ActionTypeDefinition,
EVENT_STATUS,
InternalOrderChangeEvent,
OrderChangeEvent,
OrderSummary,
OrderTransaction,
VirtualOrder,
} from "@types"
type InternalOrderSummary = OrderSummary & {
futureTemporarySum: number
}
export class OrderChangeProcessing {
private static typeDefinition: { [key: string]: ActionTypeDefinition } = {}
private static defaultConfig = {
awaitRequired: false,
isDeduction: false,
}
private order: VirtualOrder
private transactions: OrderTransaction[]
private actions: InternalOrderChangeEvent[]
private actionsProcessed: { [key: string]: InternalOrderChangeEvent[] } = {}
private groupTotal: Record<string, number> = {}
private summary: InternalOrderSummary
public static registerActionType(key: string, type: ActionTypeDefinition) {
OrderChangeProcessing.typeDefinition[key] = type
}
constructor({
order,
transactions,
actions,
}: {
order: VirtualOrder
transactions: OrderTransaction[]
actions: InternalOrderChangeEvent[]
}) {
this.order = JSON.parse(JSON.stringify(order))
this.transactions = JSON.parse(JSON.stringify(transactions ?? []))
this.actions = JSON.parse(JSON.stringify(actions ?? []))
const transactionTotal = transactions.reduce((acc, transaction) => {
return acc + transaction.amount
}, 0)
this.summary = {
futureDifference: 0,
futureTemporaryDifference: 0,
temporaryDifference: 0,
pendingDifference: 0,
futureTemporarySum: 0,
differenceSum: 0,
currentOrderTotal: order.total as number,
originalOrderTotal: order.total as number,
transactionTotal,
}
}
private isEventActive(action: InternalOrderChangeEvent): boolean {
const status = action.status
return (
status === undefined ||
status === EVENT_STATUS.PENDING ||
status === EVENT_STATUS.DONE
)
}
private isEventDone(action: InternalOrderChangeEvent): boolean {
const status = action.status
return status === EVENT_STATUS.DONE
}
private isEventPending(action: InternalOrderChangeEvent): boolean {
const status = action.status
return status === undefined || status === EVENT_STATUS.PENDING
}
public processActions() {
for (const action of this.actions) {
this.processAction_(action)
}
const summary = this.summary
for (const action of this.actions) {
if (!this.isEventActive(action)) {
continue
}
const type = {
...OrderChangeProcessing.defaultConfig,
...OrderChangeProcessing.typeDefinition[action.action],
}
const amount = action.amount! * (type.isDeduction ? -1 : 1)
if (action.group_id && !action.evaluationOnly) {
this.groupTotal[action.group_id] ??= 0
this.groupTotal[action.group_id] += amount
}
if (type.awaitRequired && !this.isEventDone(action)) {
if (action.evaluationOnly) {
summary.futureTemporarySum += amount
} else {
summary.temporaryDifference += amount
}
}
if (action.evaluationOnly) {
summary.futureDifference += amount
} else {
if (!this.isEventDone(action) && !action.group_id) {
summary.differenceSum += amount
}
summary.currentOrderTotal += amount
}
}
const groupSum = Object.values(this.groupTotal).reduce((acc, amount) => {
return acc + amount
}, 0)
summary.differenceSum += groupSum
summary.transactionTotal = this.transactions.reduce((acc, transaction) => {
return acc + transaction.amount
}, 0)
summary.futureTemporaryDifference =
summary.futureDifference - summary.futureTemporarySum
summary.temporaryDifference =
summary.differenceSum - summary.temporaryDifference
summary.pendingDifference =
summary.currentOrderTotal - summary.transactionTotal
}
private processAction_(
action: InternalOrderChangeEvent,
isReplay = false
): number | void {
const type = {
...OrderChangeProcessing.defaultConfig,
...OrderChangeProcessing.typeDefinition[action.action],
}
this.actionsProcessed[action.action] ??= []
if (!isReplay) {
this.actionsProcessed[action.action].push(action)
}
let previousEvents: InternalOrderChangeEvent[] | undefined
if (type.commitsAction) {
previousEvents = (this.actionsProcessed[type.commitsAction] ?? []).filter(
(ac_) =>
ac_.reference_id === action.reference_id &&
ac_.status !== EVENT_STATUS.VOIDED
)
}
let calculatedAmount: number = action.amount ?? 0
const params = {
actions: this.actions,
action,
previousEvents,
currentOrder: this.order,
summary: this.summary,
transactions: this.transactions,
type,
}
if (typeof type.validate === "function") {
type.validate(params)
}
if (typeof type.operation === "function") {
calculatedAmount = type.operation(params) as number
action.amount = calculatedAmount ?? 0
}
// If an action commits previous ones, replay them with updated values
if (type.commitsAction) {
for (const previousEvent of previousEvents ?? []) {
this.processAction_(previousEvent, true)
}
}
if (action.resolve) {
if (action.resolve.reference_id) {
this.resolveReferences(action)
}
const groupId = action.resolve.group_id ?? "__default"
if (action.resolve.group_id) {
// resolve all actions in the same group
this.resolveGroup(action)
}
if (action.resolve.amount && !action.evaluationOnly) {
this.groupTotal[groupId] ??= 0
this.groupTotal[groupId] -= action.resolve.amount
}
}
return calculatedAmount
}
private resolveReferences(self: InternalOrderChangeEvent) {
const resolve = self.resolve
const resolveType = OrderChangeProcessing.typeDefinition[self.action]
Object.keys(this.actionsProcessed).forEach((actionKey) => {
const type = OrderChangeProcessing.typeDefinition[actionKey]
const actions = this.actionsProcessed[actionKey]
for (const action of actions) {
if (
action === self ||
!this.isEventPending(action) ||
action.reference_id !== resolve?.reference_id
) {
continue
}
if (type.revert && (action.evaluationOnly || resolveType.void)) {
let previousEvents: InternalOrderChangeEvent[] | undefined
if (type.commitsAction) {
previousEvents = (
this.actionsProcessed[type.commitsAction] ?? []
).filter(
(ac_) =>
ac_.reference_id === action.reference_id &&
ac_.status !== EVENT_STATUS.VOIDED
)
}
type.revert({
actions: this.actions,
action,
previousEvents,
currentOrder: this.order,
summary: this.summary,
transactions: this.transactions,
type,
})
for (const previousEvent of previousEvents ?? []) {
this.processAction_(previousEvent, true)
}
action.status =
action.evaluationOnly || resolveType.void
? EVENT_STATUS.VOIDED
: EVENT_STATUS.DONE
}
}
})
}
private resolveGroup(self: InternalOrderChangeEvent) {
const resolve = self.resolve
Object.keys(this.actionsProcessed).forEach((actionKey) => {
const type = OrderChangeProcessing.typeDefinition[actionKey]
const actions = this.actionsProcessed[actionKey]
for (const action of actions) {
if (!resolve?.group_id || action?.group_id !== resolve.group_id) {
continue
}
if (
type.revert &&
action.status !== EVENT_STATUS.DONE &&
action.status !== EVENT_STATUS.VOIDED &&
(action.evaluationOnly || type.void)
) {
let previousEvents: InternalOrderChangeEvent[] | undefined
if (type.commitsAction) {
previousEvents = (
this.actionsProcessed[type.commitsAction] ?? []
).filter(
(ac_) =>
ac_.reference_id === action.reference_id &&
ac_.status !== EVENT_STATUS.VOIDED
)
}
type.revert({
actions: this.actions,
action: action,
previousEvents,
currentOrder: this.order,
summary: this.summary,
transactions: this.transactions,
type: OrderChangeProcessing.typeDefinition[action.action],
})
for (const previousEvent of previousEvents ?? []) {
this.processAction_(previousEvent, true)
}
action.status =
action.evaluationOnly || type.void
? EVENT_STATUS.VOIDED
: EVENT_STATUS.DONE
}
}
})
}
public getSummary(): OrderSummary {
const summary = this.summary
const orderSummary = {
transactionTotal: summary.transactionTotal,
originalOrderTotal: summary.originalOrderTotal,
currentOrderTotal: summary.currentOrderTotal,
temporaryDifference: summary.temporaryDifference,
futureDifference: summary.futureDifference,
futureTemporaryDifference: summary.futureTemporaryDifference,
pendingDifference: summary.pendingDifference,
differenceSum: summary.differenceSum,
}
return orderSummary
}
public getCurrentOrder(): VirtualOrder {
return this.order
}
}
export function calculateOrderChange({
order,
transactions = [],
actions = [],
}: {
order: VirtualOrder
transactions?: OrderTransaction[]
actions?: OrderChangeEvent[]
}) {
const calc = new OrderChangeProcessing({ order, transactions, actions })
calc.processActions()
return {
summary: calc.getSummary(),
order: calc.getCurrentOrder(),
}
}
+3
View File
@@ -0,0 +1,3 @@
export * from "./action-key"
export * from "./actions"
export * from "./calculate-order-change"
@@ -0,0 +1,86 @@
import { OrderTypes } from "@medusajs/types"
import { isDefined } from "@medusajs/utils"
export function formatOrder(
order
): OrderTypes.OrderDTO | OrderTypes.OrderDTO[] {
const isArray = Array.isArray(order)
const orders = isArray ? order : [order]
orders.map((order) => {
order.items = order.items?.map((orderItem) => {
const detail = { ...orderItem }
delete detail.order
delete detail.item
return {
...orderItem.item,
quantity: detail.quantity,
raw_quantity: detail.raw_quantity,
detail,
}
})
return order
})
return isArray ? orders : orders[0]
}
export function mapRepositoryToOrderModel(config) {
const conf = { ...config }
function replace(obj, type): string[] | undefined {
if (!isDefined(obj[type])) {
return
}
return [
...new Set<string>(
obj[type].sort().map((rel) => {
if (rel == "items.quantity") {
if (type === "fields") {
obj.populate.push("items.item")
}
return "items.item.quantity"
} else if (rel.includes("items.detail")) {
return rel.replace("items.detail", "items")
} else if (rel == "items") {
return "items.item"
} else if (rel.includes("items.") && !rel.includes("items.item")) {
return rel.replace("items.", "items.item.")
}
return rel
})
),
]
}
conf.options.fields = replace(config.options, "fields")
conf.options.populate = replace(config.options, "populate")
if (conf.where?.items) {
const original = { ...conf.where.items }
if (original.detail) {
delete conf.where.items.detail
}
conf.where.items = {
item: conf.where?.items,
}
if (original.quantity) {
conf.where.items.quantity = original.quantity
delete conf.where.items.item.quantity
}
if (original.detail) {
conf.where.items = {
...original.detail,
...conf.where.items,
}
}
}
return conf
}