chore(order): big number calculations (#6651)
Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
7c46b0f88b
commit
d48c076b77
@@ -1,7 +1,8 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { CreateOrderDTO, IOrderModuleService } from "@medusajs/types"
|
||||
import { SuiteOptions, moduleIntegrationTestRunner } from "medusa-test-utils"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import { ChangeActionType } from "../../src/utils"
|
||||
import { BigNumber } from "@medusajs/utils"
|
||||
|
||||
jest.setTimeout(100000)
|
||||
|
||||
@@ -17,7 +18,7 @@ moduleIntegrationTestRunner({
|
||||
title: "Item 1",
|
||||
subtitle: "Subtitle 1",
|
||||
thumbnail: "thumbnail1.jpg",
|
||||
quantity: 1,
|
||||
quantity: new BigNumber(1),
|
||||
product_id: "product1",
|
||||
product_title: "Product 1",
|
||||
product_description: "Description 1",
|
||||
|
||||
@@ -96,18 +96,20 @@ describe("Order Exchange - Actions", function () {
|
||||
actions: actions,
|
||||
})
|
||||
|
||||
expect(changes.summary).toEqual({
|
||||
transactionTotal: 0,
|
||||
const sumToJSON = JSON.parse(JSON.stringify(changes.summary))
|
||||
expect(sumToJSON).toEqual({
|
||||
transactionTotal: "0",
|
||||
originalOrderTotal: 270,
|
||||
currentOrderTotal: 312.5,
|
||||
temporaryDifference: 62.5,
|
||||
currentOrderTotal: "312.5",
|
||||
temporaryDifference: "62.5",
|
||||
futureDifference: 0,
|
||||
futureTemporaryDifference: 0,
|
||||
pendingDifference: 312.5,
|
||||
differenceSum: 42.5,
|
||||
futureTemporaryDifference: "0",
|
||||
pendingDifference: "312.5",
|
||||
differenceSum: "42.5",
|
||||
})
|
||||
|
||||
expect(changes.order.items).toEqual([
|
||||
const toJson = JSON.parse(JSON.stringify(changes.order.items))
|
||||
expect(toJson).toEqual([
|
||||
{
|
||||
id: "1",
|
||||
quantity: 1,
|
||||
@@ -144,7 +146,7 @@ describe("Order Exchange - Actions", function () {
|
||||
quantity: 3,
|
||||
shipped_quantity: 3,
|
||||
fulfilled_quantity: 3,
|
||||
return_requested_quantity: 1,
|
||||
return_requested_quantity: "1",
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
|
||||
@@ -125,7 +125,8 @@ describe("Order Return - Actions", function () {
|
||||
actions,
|
||||
})
|
||||
|
||||
expect(changes.order.items).toEqual([
|
||||
const toJson = JSON.parse(JSON.stringify(changes.order.items))
|
||||
expect(toJson).toEqual([
|
||||
{
|
||||
id: "1",
|
||||
quantity: 1,
|
||||
@@ -148,7 +149,7 @@ describe("Order Return - Actions", function () {
|
||||
quantity: 2,
|
||||
shipped_quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 1,
|
||||
return_requested_quantity: "1",
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
@@ -162,7 +163,7 @@ describe("Order Return - Actions", function () {
|
||||
quantity: 3,
|
||||
shipped_quantity: 3,
|
||||
fulfilled_quantity: 3,
|
||||
return_requested_quantity: 2,
|
||||
return_requested_quantity: "2",
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
@@ -253,7 +254,10 @@ describe("Order Return - Actions", function () {
|
||||
],
|
||||
})
|
||||
|
||||
expect(receivedChanges.order.items).toEqual([
|
||||
const toJsonReceived = JSON.parse(
|
||||
JSON.stringify(receivedChanges.order.items)
|
||||
)
|
||||
expect(toJsonReceived).toEqual([
|
||||
{
|
||||
id: "1",
|
||||
quantity: 1,
|
||||
@@ -276,7 +280,7 @@ describe("Order Return - Actions", function () {
|
||||
quantity: 2,
|
||||
shipped_quantity: 1,
|
||||
fulfilled_quantity: 1,
|
||||
return_requested_quantity: 1,
|
||||
return_requested_quantity: "1",
|
||||
return_received_quantity: 0,
|
||||
return_dismissed_quantity: 0,
|
||||
written_off_quantity: 0,
|
||||
@@ -290,9 +294,9 @@ describe("Order Return - Actions", function () {
|
||||
quantity: 3,
|
||||
shipped_quantity: 3,
|
||||
fulfilled_quantity: 3,
|
||||
return_requested_quantity: 0,
|
||||
return_received_quantity: 1,
|
||||
return_dismissed_quantity: 1,
|
||||
return_requested_quantity: "0",
|
||||
return_received_quantity: "1",
|
||||
return_dismissed_quantity: "1",
|
||||
written_off_quantity: 0,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
import { BigNumberInput } from "@medusajs/types"
|
||||
|
||||
export type VirtualOrder = {
|
||||
items: {
|
||||
id: string
|
||||
unit_price: number
|
||||
quantity: number
|
||||
unit_price: BigNumberInput
|
||||
quantity: BigNumberInput
|
||||
|
||||
detail: {
|
||||
id?: string
|
||||
quantity: number
|
||||
shipped_quantity: number
|
||||
fulfilled_quantity: number
|
||||
return_requested_quantity: number
|
||||
return_received_quantity: number
|
||||
return_dismissed_quantity: number
|
||||
written_off_quantity: number
|
||||
quantity: BigNumberInput
|
||||
shipped_quantity: BigNumberInput
|
||||
fulfilled_quantity: BigNumberInput
|
||||
return_requested_quantity: BigNumberInput
|
||||
return_received_quantity: BigNumberInput
|
||||
return_dismissed_quantity: BigNumberInput
|
||||
written_off_quantity: BigNumberInput
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
}[]
|
||||
|
||||
shipping_methods: {
|
||||
id: string
|
||||
price: number
|
||||
price: BigNumberInput
|
||||
}[]
|
||||
|
||||
summary: {
|
||||
total: number
|
||||
total: BigNumberInput
|
||||
}
|
||||
|
||||
metadata?: Record<string, unknown>
|
||||
@@ -36,23 +38,23 @@ export enum EVENT_STATUS {
|
||||
}
|
||||
|
||||
export interface OrderSummaryCalculated {
|
||||
currentOrderTotal: number
|
||||
originalOrderTotal: number
|
||||
transactionTotal: number
|
||||
futureDifference: number
|
||||
pendingDifference: number
|
||||
futureTemporaryDifference: number
|
||||
temporaryDifference: number
|
||||
differenceSum: number
|
||||
currentOrderTotal: BigNumberInput
|
||||
originalOrderTotal: BigNumberInput
|
||||
transactionTotal: BigNumberInput
|
||||
futureDifference: BigNumberInput
|
||||
pendingDifference: BigNumberInput
|
||||
futureTemporaryDifference: BigNumberInput
|
||||
temporaryDifference: BigNumberInput
|
||||
differenceSum: BigNumberInput
|
||||
}
|
||||
|
||||
export interface OrderTransaction {
|
||||
amount: number
|
||||
amount: BigNumberInput
|
||||
}
|
||||
|
||||
export interface OrderChangeEvent {
|
||||
action: string
|
||||
amount?: number
|
||||
amount?: BigNumberInput
|
||||
|
||||
reference?: string
|
||||
reference_id?: string
|
||||
@@ -66,7 +68,7 @@ export interface OrderChangeEvent {
|
||||
resolve?: {
|
||||
group_id?: string
|
||||
reference_id?: string
|
||||
amount?: number
|
||||
amount?: BigNumberInput
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,8 +93,8 @@ export interface ActionTypeDefinition {
|
||||
versioning?: boolean
|
||||
void?: boolean
|
||||
commitsAction?: string
|
||||
operation?: (obj: OrderReferences) => number | void
|
||||
revert?: (obj: OrderReferences) => number | void
|
||||
operation?: (obj: OrderReferences) => BigNumberInput | void
|
||||
revert?: (obj: OrderReferences) => BigNumberInput | void
|
||||
validate?: (obj: OrderReferences) => void
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { MathBN, MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { ChangeActionType } from "../action-key"
|
||||
import { OrderChangeProcessing } from "../calculate-order-change"
|
||||
|
||||
@@ -10,7 +10,10 @@ OrderChangeProcessing.registerActionType(ChangeActionType.CANCEL_RETURN, {
|
||||
|
||||
existing.detail.return_requested_quantity ??= 0
|
||||
|
||||
existing.detail.return_requested_quantity -= action.details.quantity
|
||||
existing.detail.return_requested_quantity = MathBN.sub(
|
||||
existing.detail.return_requested_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
|
||||
return action.details.unit_price * action.details.quantity
|
||||
},
|
||||
@@ -19,7 +22,10 @@ OrderChangeProcessing.registerActionType(ChangeActionType.CANCEL_RETURN, {
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.detail.return_requested_quantity += action.details.quantity
|
||||
existing.detail.return_requested_quantity = MathBN.add(
|
||||
existing.detail.return_requested_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
},
|
||||
validate({ action, currentOrder }) {
|
||||
const refId = action.details?.reference_id
|
||||
@@ -53,7 +59,11 @@ OrderChangeProcessing.registerActionType(ChangeActionType.CANCEL_RETURN, {
|
||||
)
|
||||
}
|
||||
|
||||
if (action.details?.quantity > existing.detail?.return_requested_quantity) {
|
||||
const greater = MathBN.gt(
|
||||
action.details?.quantity,
|
||||
existing.detail?.return_requested_quantity
|
||||
)
|
||||
if (greater) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot cancel more items than what was requested to return for item ${refId}.`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { MathBN, MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { ChangeActionType } from "../action-key"
|
||||
import { OrderChangeProcessing } from "../calculate-order-change"
|
||||
|
||||
@@ -10,14 +10,20 @@ OrderChangeProcessing.registerActionType(ChangeActionType.FULFILL_ITEM, {
|
||||
|
||||
existing.detail.fulfilled_quantity ??= 0
|
||||
|
||||
existing.detail.fulfilled_quantity += action.details.quantity
|
||||
existing.detail.fulfilled_quantity = MathBN.add(
|
||||
existing.detail.fulfilled_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
},
|
||||
revert({ action, currentOrder }) {
|
||||
const existing = currentOrder.items.find(
|
||||
(item) => item.id === action.reference_id
|
||||
)!
|
||||
|
||||
existing.detail.fulfilled_quantity -= action.details.quantity
|
||||
existing.detail.fulfilled_quantity = MathBN.sub(
|
||||
existing.detail.fulfilled_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
},
|
||||
validate({ action, currentOrder }) {
|
||||
const refId = action.details?.reference_id
|
||||
@@ -50,11 +56,12 @@ OrderChangeProcessing.registerActionType(ChangeActionType.FULFILL_ITEM, {
|
||||
)
|
||||
}
|
||||
|
||||
const notFulfilled =
|
||||
(existing.quantity as number) -
|
||||
(existing.detail?.fulfilled_quantity as number)
|
||||
|
||||
if (action.details?.quantity > notFulfilled) {
|
||||
const notFulfilled = MathBN.sub(
|
||||
existing.quantity,
|
||||
existing.detail?.fulfilled_quantity
|
||||
)
|
||||
const greater = MathBN.gt(action.details?.quantity, notFulfilled)
|
||||
if (greater) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot fulfill more items than what was ordered for item ${refId}.`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { MathBN, MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { VirtualOrder } from "@types"
|
||||
import { ChangeActionType } from "../action-key"
|
||||
import { OrderChangeProcessing } from "../calculate-order-change"
|
||||
@@ -12,18 +12,21 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_ADD, {
|
||||
if (existing) {
|
||||
existing.detail.quantity ??= 0
|
||||
|
||||
existing.quantity += action.details.quantity
|
||||
existing.detail.quantity += action.details.quantity
|
||||
existing.quantity = MathBN.add(existing.quantity, action.details.quantity)
|
||||
|
||||
existing.detail.quantity = MathBN.add(
|
||||
existing.detail.quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
} else {
|
||||
currentOrder.items.push({
|
||||
id: action.reference_id!,
|
||||
unit_price: action.details.unit_price,
|
||||
quantity: action.details.quantity,
|
||||
// detail: {}
|
||||
} as VirtualOrder["items"][0])
|
||||
}
|
||||
|
||||
return action.details.unit_price * action.details.quantity
|
||||
return MathBN.mult(action.details.unit_price, action.details.quantity)
|
||||
},
|
||||
revert({ action, currentOrder }) {
|
||||
const existingIndex = currentOrder.items.findIndex(
|
||||
@@ -32,10 +35,13 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_ADD, {
|
||||
|
||||
if (existingIndex > -1) {
|
||||
const existing = currentOrder.items[existingIndex]
|
||||
existing.quantity -= action.details.quantity
|
||||
existing.detail.quantity -= action.details.quantity
|
||||
existing.quantity = MathBN.sub(existing.quantity, action.details.quantity)
|
||||
existing.detail.quantity = MathBN.sub(
|
||||
existing.detail.quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
|
||||
if (existing.quantity <= 0) {
|
||||
if (MathBN.lte(existing.quantity, 0)) {
|
||||
currentOrder.items.splice(existingIndex, 1)
|
||||
}
|
||||
}
|
||||
@@ -63,7 +69,7 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_ADD, {
|
||||
)
|
||||
}
|
||||
|
||||
if (action.details?.quantity < 1) {
|
||||
if (MathBN.lt(action.details?.quantity, 1)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Quantity of item ${refId} must be greater than 0.`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { MathBN, MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { VirtualOrder } from "@types"
|
||||
import { ChangeActionType } from "../action-key"
|
||||
import { OrderChangeProcessing } from "../calculate-order-change"
|
||||
@@ -14,14 +14,17 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_REMOVE, {
|
||||
|
||||
existing.detail.quantity ??= 0
|
||||
|
||||
existing.quantity -= action.details.quantity
|
||||
existing.detail.quantity -= action.details.quantity
|
||||
existing.quantity = MathBN.sub(existing.quantity, action.details.quantity)
|
||||
existing.detail.quantity = MathBN.sub(
|
||||
existing.detail.quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
|
||||
if (existing.quantity <= 0) {
|
||||
if (MathBN.lte(existing.quantity, 0)) {
|
||||
currentOrder.items.splice(existingIndex, 1)
|
||||
}
|
||||
|
||||
return existing.unit_price * action.details.quantity
|
||||
return MathBN.mult(existing.unit_price, action.details.quantity)
|
||||
},
|
||||
revert({ action, currentOrder }) {
|
||||
const existing = currentOrder.items.find(
|
||||
@@ -29,8 +32,11 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_REMOVE, {
|
||||
)
|
||||
|
||||
if (existing) {
|
||||
existing.quantity += action.details.quantity
|
||||
existing.detail.quantity += action.details.quantity
|
||||
existing.quantity = MathBN.add(existing.quantity, action.details.quantity)
|
||||
existing.detail.quantity = MathBN.add(
|
||||
existing.detail.quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
} else {
|
||||
currentOrder.items.push({
|
||||
id: action.reference_id!,
|
||||
@@ -70,18 +76,20 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_REMOVE, {
|
||||
)
|
||||
}
|
||||
|
||||
if (action.details?.quantity < 1) {
|
||||
if (MathBN.lt(action.details?.quantity, 1)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Quantity of item ${refId} must be greater than 0.`
|
||||
)
|
||||
}
|
||||
|
||||
const notFulfilled =
|
||||
(existing.quantity as number) -
|
||||
(existing.detail?.fulfilled_quantity as number)
|
||||
const notFulfilled = MathBN.sub(
|
||||
existing.quantity,
|
||||
existing.detail?.fulfilled_quantity
|
||||
)
|
||||
|
||||
if (action.details?.quantity > notFulfilled) {
|
||||
const greater = MathBN.gt(action.details?.quantity, notFulfilled)
|
||||
if (greater) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot remove fulfilled item: Item ${refId}.`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { MathBN, MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { EVENT_STATUS } from "@types"
|
||||
import { ChangeActionType } from "../action-key"
|
||||
import { OrderChangeProcessing } from "../calculate-order-change"
|
||||
@@ -18,32 +18,47 @@ OrderChangeProcessing.registerActionType(
|
||||
existing.detail.return_dismissed_quantity ??= 0
|
||||
existing.detail.return_requested_quantity ??= 0
|
||||
|
||||
existing.detail.return_dismissed_quantity += toReturn
|
||||
existing.detail.return_requested_quantity -= toReturn
|
||||
existing.detail.return_dismissed_quantity = MathBN.add(
|
||||
existing.detail.return_dismissed_quantity,
|
||||
toReturn
|
||||
)
|
||||
existing.detail.return_requested_quantity = MathBN.sub(
|
||||
existing.detail.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
|
||||
let ret = MathBN.min(toReturn, previousEvent.details.quantity)
|
||||
toReturn = MathBN.sub(toReturn, ret)
|
||||
|
||||
previousEvent.details.quantity -= ret
|
||||
if (previousEvent.details.quantity <= 0) {
|
||||
previousEvent.details.quantity = MathBN.sub(
|
||||
previousEvent.details.quantity,
|
||||
ret
|
||||
)
|
||||
if (MathBN.lte(previousEvent.details.quantity, 0)) {
|
||||
previousEvent.status = EVENT_STATUS.DONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return existing.unit_price * action.details.quantity
|
||||
return MathBN.mult(existing.unit_price, action.details.quantity)
|
||||
},
|
||||
revert({ action, currentOrder, previousEvents }) {
|
||||
const existing = currentOrder.items.find(
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.detail.return_dismissed_quantity -= action.details.quantity
|
||||
existing.detail.return_requested_quantity += action.details.quantity
|
||||
existing.detail.return_dismissed_quantity = MathBN.sub(
|
||||
existing.detail.return_dismissed_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
existing.detail.return_requested_quantity = MathBN.add(
|
||||
existing.detail.return_requested_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
|
||||
if (previousEvents) {
|
||||
for (const previousEvent of previousEvents) {
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { MedusaError, isDefined } from "@medusajs/utils"
|
||||
import {
|
||||
MathBN,
|
||||
MedusaError,
|
||||
isDefined,
|
||||
transformPropertiesToBigNumber,
|
||||
} from "@medusajs/utils"
|
||||
import { EVENT_STATUS } from "@types"
|
||||
import { ChangeActionType } from "../action-key"
|
||||
import { OrderChangeProcessing } from "../calculate-order-change"
|
||||
@@ -16,32 +21,48 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RECEIVE_RETURN_ITEM, {
|
||||
existing.detail.return_received_quantity ??= 0
|
||||
existing.detail.return_requested_quantity ??= 0
|
||||
|
||||
existing.detail.return_received_quantity += toReturn
|
||||
existing.detail.return_requested_quantity -= toReturn
|
||||
existing.detail.return_received_quantity = MathBN.add(
|
||||
existing.detail.return_received_quantity,
|
||||
toReturn
|
||||
)
|
||||
existing.detail.return_requested_quantity = MathBN.sub(
|
||||
existing.detail.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
|
||||
let ret = MathBN.min(toReturn, previousEvent.details.quantity)
|
||||
toReturn = MathBN.sub(toReturn, ret)
|
||||
|
||||
previousEvent.details.quantity -= ret
|
||||
if (previousEvent.details.quantity <= 0) {
|
||||
previousEvent.details.quantity = MathBN.sub(
|
||||
previousEvent.details.quantity,
|
||||
ret
|
||||
)
|
||||
|
||||
if (MathBN.lte(previousEvent.details.quantity, 0)) {
|
||||
previousEvent.status = EVENT_STATUS.DONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return existing.unit_price * action.details.quantity
|
||||
return MathBN.mult(existing.unit_price, action.details.quantity)
|
||||
},
|
||||
revert({ action, currentOrder, previousEvents }) {
|
||||
const existing = currentOrder.items.find(
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.detail.return_received_quantity -= action.details.quantity
|
||||
existing.detail.return_requested_quantity += action.details.quantity
|
||||
existing.detail.return_received_quantity = MathBN.sub(
|
||||
existing.detail.return_received_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
existing.detail.return_requested_quantity = MathBN.add(
|
||||
existing.detail.return_requested_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
|
||||
if (previousEvents) {
|
||||
for (const previousEvent of previousEvents) {
|
||||
@@ -52,6 +73,8 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RECEIVE_RETURN_ITEM, {
|
||||
previousEvent.details = JSON.parse(
|
||||
JSON.stringify(previousEvent.original_.details)
|
||||
)
|
||||
transformPropertiesToBigNumber(previousEvent.details?.metadata)
|
||||
|
||||
delete previousEvent.original_
|
||||
|
||||
previousEvent.status = EVENT_STATUS.PENDING
|
||||
@@ -84,7 +107,9 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RECEIVE_RETURN_ITEM, {
|
||||
}
|
||||
|
||||
const quantityRequested = existing?.detail?.return_requested_quantity || 0
|
||||
if (action.details?.quantity > quantityRequested) {
|
||||
|
||||
const greater = MathBN.gt(action.details?.quantity, quantityRequested)
|
||||
if (greater) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot receive more items than what was requested to be returned for item ${refId}.`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { MathBN, MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { ChangeActionType } from "../action-key"
|
||||
import { OrderChangeProcessing } from "../calculate-order-change"
|
||||
|
||||
@@ -11,16 +11,22 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RETURN_ITEM, {
|
||||
)!
|
||||
|
||||
existing.detail.return_requested_quantity ??= 0
|
||||
existing.detail.return_requested_quantity += action.details.quantity
|
||||
existing.detail.return_requested_quantity = MathBN.add(
|
||||
existing.detail.return_requested_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
|
||||
return existing.unit_price * action.details.quantity
|
||||
return MathBN.mult(existing.unit_price, action.details.quantity)
|
||||
},
|
||||
revert({ action, currentOrder }) {
|
||||
const existing = currentOrder.items.find(
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.detail.return_requested_quantity -= action.details.quantity
|
||||
existing.detail.return_requested_quantity = MathBN.sub(
|
||||
existing.detail.return_requested_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
},
|
||||
validate({ action, currentOrder }) {
|
||||
const refId = action.details?.reference_id
|
||||
@@ -47,11 +53,13 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RETURN_ITEM, {
|
||||
)
|
||||
}
|
||||
|
||||
const quantityAvailable =
|
||||
(existing!.detail?.shipped_quantity ?? 0) -
|
||||
(existing!.detail?.return_requested_quantity ?? 0)
|
||||
const quantityAvailable = MathBN.sub(
|
||||
existing!.detail?.shipped_quantity ?? 0,
|
||||
existing!.detail?.return_requested_quantity ?? 0
|
||||
)
|
||||
|
||||
if (action.details?.quantity > quantityAvailable) {
|
||||
const greater = MathBN.gt(action.details?.quantity, quantityAvailable)
|
||||
if (greater) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot request to return more items than what was shipped for item ${refId}.`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { MathBN, MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { ChangeActionType } from "../action-key"
|
||||
import { OrderChangeProcessing } from "../calculate-order-change"
|
||||
|
||||
@@ -10,14 +10,20 @@ OrderChangeProcessing.registerActionType(ChangeActionType.SHIP_ITEM, {
|
||||
|
||||
existing.detail.shipped_quantity ??= 0
|
||||
|
||||
existing.detail.shipped_quantity += action.details.quantity
|
||||
existing.detail.shipped_quantity = MathBN.add(
|
||||
existing.detail.shipped_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
},
|
||||
revert({ action, currentOrder }) {
|
||||
const existing = currentOrder.items.find(
|
||||
(item) => item.id === action.reference_id
|
||||
)!
|
||||
|
||||
existing.detail.shipped_quantity -= action.details.quantity
|
||||
existing.detail.shipped_quantity = MathBN.sub(
|
||||
existing.detail.shipped_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
},
|
||||
validate({ action, currentOrder }) {
|
||||
const refId = action.details?.reference_id
|
||||
@@ -43,18 +49,20 @@ OrderChangeProcessing.registerActionType(ChangeActionType.SHIP_ITEM, {
|
||||
)
|
||||
}
|
||||
|
||||
if (action.details?.quantity < 1) {
|
||||
if (MathBN.lt(action.details?.quantity, 1)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Quantity of item ${refId} must be greater than 0.`
|
||||
)
|
||||
}
|
||||
|
||||
const notShipped =
|
||||
(existing.detail?.fulfilled_quantity as number) -
|
||||
(existing.detail?.shipped_quantity as number)
|
||||
const notShipped = MathBN.sub(
|
||||
existing.detail?.fulfilled_quantity,
|
||||
existing.detail?.shipped_quantity
|
||||
)
|
||||
|
||||
if (action.details?.quantity > notShipped) {
|
||||
const greater = MathBN.gt(action.details?.quantity, notShipped)
|
||||
if (greater) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Cannot ship more items than what was fulfilled for item ${refId}.`
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { MathBN, MedusaError, isDefined } from "@medusajs/utils"
|
||||
import { ChangeActionType } from "../action-key"
|
||||
import { OrderChangeProcessing } from "../calculate-order-change"
|
||||
|
||||
@@ -9,14 +9,20 @@ OrderChangeProcessing.registerActionType(ChangeActionType.WRITE_OFF_ITEM, {
|
||||
)!
|
||||
|
||||
existing.detail.written_off_quantity ??= 0
|
||||
existing.detail.written_off_quantity += action.details.quantity
|
||||
existing.detail.written_off_quantity = MathBN.add(
|
||||
existing.detail.written_off_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
},
|
||||
revert({ action, currentOrder }) {
|
||||
const existing = currentOrder.items.find(
|
||||
(item) => item.id === action.details.reference_id
|
||||
)!
|
||||
|
||||
existing.detail.written_off_quantity -= action.details.quantity
|
||||
existing.detail.written_off_quantity = MathBN.sub(
|
||||
existing.detail.written_off_quantity,
|
||||
action.details.quantity
|
||||
)
|
||||
},
|
||||
validate({ action, currentOrder }) {
|
||||
const refId = action.details?.reference_id
|
||||
@@ -44,7 +50,8 @@ OrderChangeProcessing.registerActionType(ChangeActionType.WRITE_OFF_ITEM, {
|
||||
}
|
||||
|
||||
const quantityAvailable = existing!.quantity ?? 0
|
||||
if (action.details?.quantity > quantityAvailable) {
|
||||
const greater = MathBN.gt(action.details?.quantity, quantityAvailable)
|
||||
if (greater) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Cannot claim more items than what was ordered."
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { OrderSummaryDTO } from "@medusajs/types"
|
||||
import { isDefined } from "@medusajs/utils"
|
||||
import { BigNumberInput, OrderSummaryDTO } from "@medusajs/types"
|
||||
import {
|
||||
MathBN,
|
||||
isDefined,
|
||||
transformPropertiesToBigNumber,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
ActionTypeDefinition,
|
||||
EVENT_STATUS,
|
||||
@@ -11,7 +15,7 @@ import {
|
||||
} from "@types"
|
||||
|
||||
type InternalOrderSummary = OrderSummaryCalculated & {
|
||||
futureTemporarySum: number
|
||||
futureTemporarySum: BigNumberInput
|
||||
}
|
||||
|
||||
export class OrderChangeProcessing {
|
||||
@@ -26,7 +30,7 @@ export class OrderChangeProcessing {
|
||||
private actions: InternalOrderChangeEvent[]
|
||||
|
||||
private actionsProcessed: { [key: string]: InternalOrderChangeEvent[] } = {}
|
||||
private groupTotal: Record<string, number> = {}
|
||||
private groupTotal: Record<string, BigNumberInput> = {}
|
||||
private summary: InternalOrderSummary
|
||||
|
||||
public static registerActionType(key: string, type: ActionTypeDefinition) {
|
||||
@@ -46,9 +50,9 @@ export class OrderChangeProcessing {
|
||||
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)
|
||||
const transactionTotal = MathBN.add(...transactions.map((tr) => tr.amount))
|
||||
|
||||
transformPropertiesToBigNumber(this.order.metadata)
|
||||
|
||||
this.summary = {
|
||||
futureDifference: 0,
|
||||
@@ -57,8 +61,8 @@ export class OrderChangeProcessing {
|
||||
pendingDifference: 0,
|
||||
futureTemporarySum: 0,
|
||||
differenceSum: 0,
|
||||
currentOrderTotal: order?.summary?.total ?? 0,
|
||||
originalOrderTotal: order?.summary?.total ?? 0,
|
||||
currentOrderTotal: this.order.summary?.total ?? 0,
|
||||
originalOrderTotal: this.order.summary?.total ?? 0,
|
||||
transactionTotal,
|
||||
}
|
||||
}
|
||||
@@ -97,55 +101,71 @@ export class OrderChangeProcessing {
|
||||
...OrderChangeProcessing.typeDefinition[action.action],
|
||||
}
|
||||
|
||||
const amount = action.amount! * (type.isDeduction ? -1 : 1)
|
||||
const amount = MathBN.mult(action.amount!, type.isDeduction ? -1 : 1)
|
||||
|
||||
if (action.group_id && !action.evaluationOnly) {
|
||||
this.groupTotal[action.group_id] ??= 0
|
||||
this.groupTotal[action.group_id] += amount
|
||||
this.groupTotal[action.group_id] = MathBN.add(
|
||||
this.groupTotal[action.group_id],
|
||||
amount
|
||||
)
|
||||
}
|
||||
|
||||
if (type.awaitRequired && !this.isEventDone(action)) {
|
||||
if (action.evaluationOnly) {
|
||||
summary.futureTemporarySum += amount
|
||||
summary.futureTemporarySum = MathBN.add(
|
||||
summary.futureTemporarySum,
|
||||
amount
|
||||
)
|
||||
} else {
|
||||
summary.temporaryDifference += amount
|
||||
summary.temporaryDifference = MathBN.add(
|
||||
summary.temporaryDifference,
|
||||
amount
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (action.evaluationOnly) {
|
||||
summary.futureDifference += amount
|
||||
summary.futureDifference = MathBN.add(summary.futureDifference, amount)
|
||||
} else {
|
||||
if (!this.isEventDone(action) && !action.group_id) {
|
||||
summary.differenceSum += amount
|
||||
summary.differenceSum = MathBN.add(summary.differenceSum, amount)
|
||||
}
|
||||
summary.currentOrderTotal += amount
|
||||
summary.currentOrderTotal = MathBN.add(
|
||||
summary.currentOrderTotal,
|
||||
amount
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const groupSum = Object.values(this.groupTotal).reduce((acc, amount) => {
|
||||
return acc + amount
|
||||
}, 0)
|
||||
const groupSum = MathBN.add(...Object.values(this.groupTotal))
|
||||
|
||||
summary.differenceSum += groupSum
|
||||
summary.differenceSum = MathBN.add(summary.differenceSum, groupSum)
|
||||
|
||||
summary.transactionTotal = this.transactions.reduce((acc, transaction) => {
|
||||
return acc + transaction.amount
|
||||
}, 0)
|
||||
summary.transactionTotal = MathBN.sum(
|
||||
...this.transactions.map((tr) => tr.amount)
|
||||
)
|
||||
|
||||
summary.futureTemporaryDifference =
|
||||
summary.futureDifference - summary.futureTemporarySum
|
||||
summary.futureTemporaryDifference = MathBN.sub(
|
||||
summary.futureDifference,
|
||||
summary.futureTemporarySum
|
||||
)
|
||||
|
||||
summary.temporaryDifference =
|
||||
summary.differenceSum - summary.temporaryDifference
|
||||
summary.temporaryDifference = MathBN.sub(
|
||||
summary.differenceSum,
|
||||
summary.temporaryDifference
|
||||
)
|
||||
|
||||
summary.pendingDifference =
|
||||
summary.currentOrderTotal - summary.transactionTotal
|
||||
summary.pendingDifference = MathBN.sub(
|
||||
summary.currentOrderTotal,
|
||||
summary.transactionTotal
|
||||
)
|
||||
}
|
||||
|
||||
private processAction_(
|
||||
action: InternalOrderChangeEvent,
|
||||
isReplay = false
|
||||
): number | void {
|
||||
): BigNumberInput | void {
|
||||
const type = {
|
||||
...OrderChangeProcessing.defaultConfig,
|
||||
...OrderChangeProcessing.typeDefinition[action.action],
|
||||
@@ -166,7 +186,7 @@ export class OrderChangeProcessing {
|
||||
)
|
||||
}
|
||||
|
||||
let calculatedAmount: number = action.amount ?? 0
|
||||
let calculatedAmount = action.amount ?? 0
|
||||
const params = {
|
||||
actions: this.actions,
|
||||
action,
|
||||
@@ -181,7 +201,7 @@ export class OrderChangeProcessing {
|
||||
}
|
||||
|
||||
if (typeof type.operation === "function") {
|
||||
calculatedAmount = type.operation(params) as number
|
||||
calculatedAmount = type.operation(params) as BigNumberInput
|
||||
|
||||
// the action.amount has priority over the calculated amount
|
||||
if (!isDefined(action.amount)) {
|
||||
@@ -207,7 +227,10 @@ export class OrderChangeProcessing {
|
||||
}
|
||||
if (action.resolve.amount && !action.evaluationOnly) {
|
||||
this.groupTotal[groupId] ??= 0
|
||||
this.groupTotal[groupId] -= action.resolve.amount
|
||||
this.groupTotal[groupId] = MathBN.sub(
|
||||
this.groupTotal[groupId],
|
||||
action.resolve.amount
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import BigNumber from "bignumber.js"
|
||||
import BigNumberJS from "bignumber.js"
|
||||
|
||||
export type BigNumberRawValue = {
|
||||
value: string | number
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export type BigNumberInput = BigNumberRawValue | number | string | BigNumber
|
||||
export type BigNumberInput = BigNumberRawValue | number | string | BigNumberJS
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BigNumberInput } from "@medusajs/types"
|
||||
import { Property } from "@mikro-orm/core"
|
||||
import { isPresent, trimZeros } from "../../common"
|
||||
import { BigNumber } from "../../totals/big-number"
|
||||
import { BigNumberInput } from "@medusajs/types"
|
||||
|
||||
export function MikroOrmBigNumberProperty(
|
||||
options: Parameters<typeof Property>[0] & {
|
||||
@@ -13,47 +13,52 @@ export function MikroOrmBigNumberProperty(
|
||||
|
||||
Object.defineProperty(target, columnName, {
|
||||
get() {
|
||||
return this.__helper.__data[columnName]
|
||||
let value = this.__helper?.__data?.[columnName]
|
||||
|
||||
if (!value && this[rawColumnName]) {
|
||||
value = new BigNumber(this[rawColumnName].value, {
|
||||
precision: this[rawColumnName].precision,
|
||||
}).numeric
|
||||
}
|
||||
|
||||
return value
|
||||
},
|
||||
set(value: BigNumberInput) {
|
||||
if (options?.nullable && !isPresent(value)) {
|
||||
this.__helper.__data[columnName] = null
|
||||
this.__helper.__data[rawColumnName]
|
||||
this[rawColumnName] = null
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let bigNumber: BigNumber
|
||||
|
||||
if (value instanceof BigNumber) {
|
||||
bigNumber = value
|
||||
} else if (this[rawColumnName]) {
|
||||
const precision = this[rawColumnName].precision
|
||||
|
||||
this[rawColumnName].value = trimZeros(
|
||||
new BigNumber(value, {
|
||||
precision,
|
||||
}).raw!.value as string
|
||||
)
|
||||
|
||||
bigNumber = new BigNumber(this[rawColumnName])
|
||||
} else {
|
||||
bigNumber = new BigNumber(value)
|
||||
let bigNumber: BigNumber
|
||||
|
||||
if (value instanceof BigNumber) {
|
||||
bigNumber = value
|
||||
} else if (this[rawColumnName]) {
|
||||
const precision = this[rawColumnName].precision
|
||||
bigNumber = new BigNumber(value, {
|
||||
precision,
|
||||
})
|
||||
} else {
|
||||
bigNumber = new BigNumber(value)
|
||||
}
|
||||
|
||||
const raw = bigNumber.raw!
|
||||
raw.value = trimZeros(raw.value as string)
|
||||
|
||||
this.__helper.__data[columnName] = bigNumber.numeric
|
||||
this.__helper.__data[rawColumnName] = raw
|
||||
|
||||
this[rawColumnName] = raw
|
||||
}
|
||||
|
||||
this.__helper.__data[columnName] = bigNumber.numeric
|
||||
|
||||
const raw = bigNumber.raw!
|
||||
raw.value = trimZeros(raw.value as string)
|
||||
|
||||
this[rawColumnName] = raw
|
||||
|
||||
this.__helper.__touched = !this.__helper.hydrator.isRunning()
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
})
|
||||
|
||||
Property({
|
||||
type: "number",
|
||||
type: "any",
|
||||
columnType: "numeric",
|
||||
trackChanges: false,
|
||||
...options,
|
||||
|
||||
@@ -278,10 +278,7 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
||||
async update(data: { entity; update }[], context?: Context): Promise<T[]> {
|
||||
const manager = this.getActiveManager<EntityManager>(context)
|
||||
const entities = data.map((data_) => {
|
||||
return manager.assign(
|
||||
data_.entity,
|
||||
data_.update as RequiredEntityData<T>
|
||||
)
|
||||
return manager.assign(data_.entity, data_.update)
|
||||
})
|
||||
|
||||
manager.persist(entities)
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
import { BigNumber } from "../big-number"
|
||||
import { transformPropertiesToBigNumber } from "../transform-properties-to-bignumber"
|
||||
|
||||
describe("Transfor Properties to BigNumber", function () {
|
||||
it("should transform all properties containing matching prefix _raw to BigNumber", function () {
|
||||
const obj = {
|
||||
price: 42,
|
||||
raw_price: {
|
||||
value: "42",
|
||||
precision: 10,
|
||||
},
|
||||
field: 111,
|
||||
metadata: {
|
||||
numeric_field: 100,
|
||||
raw_numeric_field: {
|
||||
value: "100",
|
||||
},
|
||||
random_field: 134,
|
||||
},
|
||||
|
||||
abc: null,
|
||||
raw_abc: {
|
||||
value: "9.00000010000103991234",
|
||||
precision: 20,
|
||||
},
|
||||
}
|
||||
|
||||
transformPropertiesToBigNumber(obj)
|
||||
|
||||
const price = obj.price as unknown as BigNumber
|
||||
expect(price).toBeInstanceOf(BigNumber)
|
||||
expect(price.numeric).toEqual(42)
|
||||
expect(price.raw).toEqual({
|
||||
value: "42",
|
||||
precision: 10,
|
||||
})
|
||||
|
||||
expect(obj.field).toBe(111)
|
||||
|
||||
const metaNum = obj.metadata.numeric_field as unknown as BigNumber
|
||||
expect(metaNum).toBeInstanceOf(BigNumber)
|
||||
expect(metaNum.numeric).toEqual(100)
|
||||
expect(metaNum.raw).toEqual({
|
||||
value: "100",
|
||||
precision: 20,
|
||||
})
|
||||
expect(obj.metadata.random_field).toBe(134)
|
||||
|
||||
const abc = obj.abc as unknown as BigNumber
|
||||
expect(abc).toBeInstanceOf(BigNumber)
|
||||
expect(abc.numeric).toEqual(9.00000010000104)
|
||||
expect(abc.raw).toEqual({
|
||||
value: "9.00000010000103991234",
|
||||
precision: 20,
|
||||
})
|
||||
})
|
||||
|
||||
it("should transform all properties on the option 'include' to BigNumber", function () {
|
||||
const obj = {
|
||||
price: 42,
|
||||
raw_price: {
|
||||
value: "42",
|
||||
precision: 10,
|
||||
},
|
||||
field: 111,
|
||||
metadata: {
|
||||
random_field: 134,
|
||||
},
|
||||
}
|
||||
|
||||
transformPropertiesToBigNumber(obj, {
|
||||
include: ["metadata.random_field"],
|
||||
})
|
||||
|
||||
expect(obj.price).toBeInstanceOf(BigNumber)
|
||||
|
||||
const price = obj.price as unknown as BigNumber
|
||||
expect(price.numeric).toEqual(42)
|
||||
expect(price.raw).toEqual({
|
||||
value: "42",
|
||||
precision: 10,
|
||||
})
|
||||
|
||||
expect(obj.field).toBe(111)
|
||||
|
||||
const metaNum = obj.metadata.random_field as unknown as BigNumber
|
||||
expect(metaNum).toBeInstanceOf(BigNumber)
|
||||
expect(metaNum.numeric).toEqual(134)
|
||||
expect(metaNum.raw).toEqual({
|
||||
value: "134.00000000000000000",
|
||||
precision: 20,
|
||||
})
|
||||
})
|
||||
|
||||
it("should transform all properties containing matching prefix _raw to BigNumber excluding selected ones", function () {
|
||||
const obj = {
|
||||
price: 42,
|
||||
raw_price: {
|
||||
value: "42",
|
||||
precision: 10,
|
||||
},
|
||||
metadata: {
|
||||
numeric_field: 100,
|
||||
raw_numeric_field: {
|
||||
value: "100",
|
||||
},
|
||||
},
|
||||
|
||||
abc: null,
|
||||
raw_abc: {
|
||||
value: "9.00000010000103991234",
|
||||
precision: 20,
|
||||
},
|
||||
}
|
||||
|
||||
transformPropertiesToBigNumber(obj, {
|
||||
exclude: ["abc", "metadata.numeric_field"],
|
||||
})
|
||||
|
||||
const price = obj.price as unknown as BigNumber
|
||||
expect(obj.price).toBeInstanceOf(BigNumber)
|
||||
expect(price.numeric).toEqual(42)
|
||||
expect(price.raw).toEqual({
|
||||
value: "42",
|
||||
precision: 10,
|
||||
})
|
||||
|
||||
expect(obj.abc).toEqual(null)
|
||||
})
|
||||
})
|
||||
@@ -7,18 +7,24 @@ export class BigNumber {
|
||||
|
||||
private numeric_: number
|
||||
private raw_?: BigNumberRawValue
|
||||
private bignumber_?: BigNumberJS
|
||||
|
||||
constructor(rawValue: BigNumberInput, options?: { precision?: number }) {
|
||||
constructor(
|
||||
rawValue: BigNumberInput | BigNumber,
|
||||
options?: { precision?: number }
|
||||
) {
|
||||
this.setRawValueOrThrow(rawValue, options)
|
||||
}
|
||||
|
||||
setRawValueOrThrow(
|
||||
rawValue: BigNumberInput,
|
||||
rawValue: BigNumberInput | BigNumber,
|
||||
{ precision }: { precision?: number } = {}
|
||||
) {
|
||||
precision ??= BigNumber.DEFAULT_PRECISION
|
||||
|
||||
if (BigNumberJS.isBigNumber(rawValue)) {
|
||||
if (rawValue instanceof BigNumber) {
|
||||
Object.assign(this, rawValue)
|
||||
} else if (BigNumberJS.isBigNumber(rawValue)) {
|
||||
/**
|
||||
* Example:
|
||||
* const bnUnitValue = new BigNumberJS("10.99")
|
||||
@@ -29,6 +35,7 @@ export class BigNumber {
|
||||
value: rawValue.toPrecision(precision),
|
||||
precision,
|
||||
}
|
||||
this.bignumber_ = rawValue
|
||||
} else if (isString(rawValue)) {
|
||||
/**
|
||||
* Example: const unitValue = "1234.1234"
|
||||
@@ -40,26 +47,31 @@ export class BigNumber {
|
||||
value: bigNum.toPrecision(precision),
|
||||
precision,
|
||||
}
|
||||
this.bignumber_ = bigNum
|
||||
} else if (isBigNumber(rawValue)) {
|
||||
/**
|
||||
* Example: const unitValue = { value: "1234.1234" }
|
||||
*/
|
||||
const definedPrecision = rawValue.precision ?? precision
|
||||
this.numeric_ = BigNumberJS(rawValue.value).toNumber()
|
||||
const bigNum = new BigNumberJS(rawValue.value)
|
||||
this.numeric_ = bigNum.toNumber()
|
||||
this.raw_ = {
|
||||
...rawValue,
|
||||
precision: definedPrecision,
|
||||
}
|
||||
this.bignumber_ = bigNum
|
||||
} else if (typeof rawValue === `number` && !Number.isNaN(rawValue)) {
|
||||
/**
|
||||
* Example: const unitValue = 1234
|
||||
*/
|
||||
this.numeric_ = rawValue as number
|
||||
|
||||
const bigNum = new BigNumberJS(rawValue as number)
|
||||
this.raw_ = {
|
||||
value: BigNumberJS(rawValue as number).toPrecision(precision),
|
||||
value: bigNum.toPrecision(precision),
|
||||
precision,
|
||||
}
|
||||
this.bignumber_ = bigNum
|
||||
} else {
|
||||
throw new Error(
|
||||
`Invalid BigNumber value: ${rawValue}. Should be one of: string, number, BigNumber (bignumber.js), BigNumberRawValue`
|
||||
@@ -80,27 +92,33 @@ export class BigNumber {
|
||||
const newValue = new BigNumber(value)
|
||||
this.numeric_ = newValue.numeric_
|
||||
this.raw_ = newValue.raw_
|
||||
this.bignumber_ = newValue.bignumber_
|
||||
}
|
||||
|
||||
get raw(): BigNumberRawValue | undefined {
|
||||
return this.raw_
|
||||
}
|
||||
|
||||
get bigNumber(): BigNumberJS | undefined {
|
||||
return this.bignumber_
|
||||
}
|
||||
|
||||
set raw(rawValue: BigNumberInput) {
|
||||
const newValue = new BigNumber(rawValue)
|
||||
this.numeric_ = newValue.numeric_
|
||||
this.raw_ = newValue.raw_
|
||||
this.bignumber_ = newValue.bignumber_
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
return this.raw_
|
||||
return this.bignumber_
|
||||
? this.bignumber_?.toNumber()
|
||||
: this.raw_
|
||||
? new BigNumberJS(this.raw_.value).toNumber()
|
||||
: this.numeric_
|
||||
}
|
||||
|
||||
valueOf() {
|
||||
return this.raw_
|
||||
? new BigNumberJS(this.raw_.value).toNumber()
|
||||
: this.numeric_
|
||||
return this.bignumber_
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@ import { BigNumber as BigNumberJs } from "bignumber.js"
|
||||
import { BigNumber } from "./big-number"
|
||||
import { toBigNumberJs } from "./to-big-number-js"
|
||||
|
||||
export * from "./math"
|
||||
export * from "./promotion"
|
||||
export * from "./to-big-number-js"
|
||||
export * from "./transform-properties-to-bignumber"
|
||||
|
||||
type GetLineItemTotalsContext = {
|
||||
includeTax?: boolean
|
||||
|
||||
108
packages/utils/src/totals/math.ts
Normal file
108
packages/utils/src/totals/math.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { BigNumberInput, BigNumberRawValue } from "@medusajs/types"
|
||||
import { BigNumber as BigNumberJS } from "bignumber.js"
|
||||
import { isDefined } from "../common"
|
||||
import { BigNumber } from "./big-number"
|
||||
|
||||
type BNInput = BigNumberInput | BigNumber
|
||||
export class MathBN {
|
||||
static convert(num: BNInput): BigNumberJS {
|
||||
if (num instanceof BigNumber) {
|
||||
return num.bigNumber!
|
||||
} else if (num instanceof BigNumberJS) {
|
||||
return num
|
||||
} else if (isDefined((num as BigNumberRawValue)?.value)) {
|
||||
return new BigNumberJS((num as BigNumberRawValue).value)
|
||||
}
|
||||
|
||||
return new BigNumberJS(num as BigNumberJS | number)
|
||||
}
|
||||
|
||||
static add(...nums: BNInput[]): BigNumberJS {
|
||||
let sum = new BigNumberJS(0)
|
||||
for (const num of nums) {
|
||||
const n = MathBN.convert(num)
|
||||
sum = sum.plus(n)
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
static sum(...nums: BNInput[]): BigNumberJS {
|
||||
return MathBN.add(...nums)
|
||||
}
|
||||
|
||||
static sub(...nums: BNInput[]): BigNumberJS {
|
||||
let agg = MathBN.convert(nums[0])
|
||||
for (let i = 1; i < nums.length; i++) {
|
||||
const n = MathBN.convert(nums[i])
|
||||
agg = agg.minus(n)
|
||||
}
|
||||
return agg
|
||||
}
|
||||
|
||||
static mult(n1: BNInput, n2: BNInput): BigNumberJS {
|
||||
const num1 = MathBN.convert(n1)
|
||||
const num2 = MathBN.convert(n2)
|
||||
return num1.times(num2)
|
||||
}
|
||||
|
||||
static div(n1: BNInput, n2: BNInput): BigNumberJS {
|
||||
const num1 = MathBN.convert(n1)
|
||||
const num2 = MathBN.convert(n2)
|
||||
return num1.dividedBy(num2)
|
||||
}
|
||||
|
||||
static abs(n: BNInput): BigNumberJS {
|
||||
const num = MathBN.convert(n)
|
||||
return num.absoluteValue()
|
||||
}
|
||||
|
||||
static mod(n1: BNInput, n2: BNInput): BigNumberJS {
|
||||
const num1 = MathBN.convert(n1)
|
||||
const num2 = MathBN.convert(n2)
|
||||
return num1.modulo(num2)
|
||||
}
|
||||
|
||||
static exp(n: BNInput, exp = 2): BigNumberJS {
|
||||
const num = MathBN.convert(n)
|
||||
const expBy = MathBN.convert(exp)
|
||||
return num.exponentiatedBy(expBy)
|
||||
}
|
||||
|
||||
static min(...nums: BNInput[]): BigNumberJS {
|
||||
return BigNumberJS.minimum(...nums.map((num) => MathBN.convert(num)))
|
||||
}
|
||||
|
||||
static max(...nums: BNInput[]): BigNumberJS {
|
||||
return BigNumberJS.maximum(...nums.map((num) => MathBN.convert(num)))
|
||||
}
|
||||
|
||||
static gt(n1: BNInput, n2: BNInput): boolean {
|
||||
const num1 = MathBN.convert(n1)
|
||||
const num2 = MathBN.convert(n2)
|
||||
return num1.isGreaterThan(num2)
|
||||
}
|
||||
|
||||
static gte(n1: BNInput, n2: BNInput): boolean {
|
||||
const num1 = MathBN.convert(n1)
|
||||
const num2 = MathBN.convert(n2)
|
||||
return num1.isGreaterThanOrEqualTo(num2)
|
||||
}
|
||||
|
||||
static lt(n1: BNInput, n2: BNInput): boolean {
|
||||
const num1 = MathBN.convert(n1)
|
||||
const num2 = MathBN.convert(n2)
|
||||
return num1.isLessThan(num2)
|
||||
}
|
||||
|
||||
static lte(n1: BNInput, n2: BNInput): boolean {
|
||||
const num1 = MathBN.convert(n1)
|
||||
const num2 = MathBN.convert(n2)
|
||||
return num1.isLessThanOrEqualTo(num2)
|
||||
}
|
||||
|
||||
static eq(n1: BNInput, n2: BNInput): boolean {
|
||||
const num1 = MathBN.convert(n1)
|
||||
const num2 = MathBN.convert(n2)
|
||||
return num1.isEqualTo(num2)
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,11 @@ import { BigNumberInput } from "@medusajs/types"
|
||||
import { BigNumber as BigNumberJs } from "bignumber.js"
|
||||
import { isDefined, toCamelCase } from "../common"
|
||||
import { BigNumber } from "./big-number"
|
||||
|
||||
type InputEntity<T, V extends string> = { [key in V]?: InputEntityField }
|
||||
type InputEntityField = number | string | BigNumber
|
||||
|
||||
type Camelize<V extends string> = V extends `${infer A}_${infer B}`
|
||||
? `${A}${Camelize<Capitalize<B>>}`
|
||||
: V
|
||||
|
||||
type Output<V extends string> = { [key in Camelize<V>]: BigNumberJs }
|
||||
|
||||
export function toBigNumberJs<T, V extends string>(
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { BigNumber } from "./big-number"
|
||||
|
||||
export function transformPropertiesToBigNumber(
|
||||
obj,
|
||||
{
|
||||
prefix = "raw_",
|
||||
include = [],
|
||||
exclude = [],
|
||||
}: {
|
||||
prefix?: string
|
||||
include?: string[]
|
||||
exclude?: string[]
|
||||
} = {}
|
||||
) {
|
||||
const stack = [{ current: obj, path: "" }]
|
||||
|
||||
while (stack.length > 0) {
|
||||
const { current, path } = stack.pop()!
|
||||
|
||||
if (
|
||||
current == null ||
|
||||
typeof current !== "object" ||
|
||||
current instanceof BigNumber
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (Array.isArray(current)) {
|
||||
current.forEach((element, index) =>
|
||||
stack.push({ current: element, path })
|
||||
)
|
||||
} else {
|
||||
for (const key of Object.keys(current)) {
|
||||
const value = current[key]
|
||||
const currentPath = path ? `${path}.${key}` : key
|
||||
|
||||
if (value != null && !exclude.includes(currentPath)) {
|
||||
if (key.startsWith(prefix)) {
|
||||
const newKey = key.replace(prefix, "")
|
||||
|
||||
const newPath = path ? `${path}.${newKey}` : newKey
|
||||
if (!exclude.includes(newPath)) {
|
||||
current[newKey] = new BigNumber(value)
|
||||
continue
|
||||
}
|
||||
} else if (include.includes(currentPath)) {
|
||||
current[key] = new BigNumber(value)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
stack.push({ current: value, path: currentPath })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user