chore(order): big number calculations (#6651)

Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
This commit is contained in:
Carlos R. L. RodriguesandAdrien de Peretti authored and GitHub committed 2024-03-11 17:51:00 +00:00
1 parent 7c46b0f88b
commit d48c076b77
23 files changed
+637 -197

No files matched your search

@@ -1,7 +1,8 @@
import { Modules } from "@medusajs/modules-sdk" import { Modules } from "@medusajs/modules-sdk"
import { CreateOrderDTO, IOrderModuleService } from "@medusajs/types" 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 { ChangeActionType } from "../../src/utils"
import { BigNumber } from "@medusajs/utils"
jest.setTimeout(100000) jest.setTimeout(100000)
@@ -17,7 +18,7 @@ moduleIntegrationTestRunner({
title: "Item 1", title: "Item 1",
subtitle: "Subtitle 1", subtitle: "Subtitle 1",
thumbnail: "thumbnail1.jpg", thumbnail: "thumbnail1.jpg",
quantity: 1, quantity: new BigNumber(1),
product_id: "product1", product_id: "product1",
product_title: "Product 1", product_title: "Product 1",
product_description: "Description 1", product_description: "Description 1",
@@ -96,18 +96,20 @@ describe("Order Exchange - Actions", function () {
actions: actions, actions: actions,
}) })
expect(changes.summary).toEqual({ const sumToJSON = JSON.parse(JSON.stringify(changes.summary))
transactionTotal: 0, expect(sumToJSON).toEqual({
transactionTotal: "0",
originalOrderTotal: 270, originalOrderTotal: 270,
currentOrderTotal: 312.5, currentOrderTotal: "312.5",
temporaryDifference: 62.5, temporaryDifference: "62.5",
futureDifference: 0, futureDifference: 0,
futureTemporaryDifference: 0, futureTemporaryDifference: "0",
pendingDifference: 312.5, pendingDifference: "312.5",
differenceSum: 42.5, differenceSum: "42.5",
}) })
expect(changes.order.items).toEqual([ const toJson = JSON.parse(JSON.stringify(changes.order.items))
expect(toJson).toEqual([
{ {
id: "1", id: "1",
quantity: 1, quantity: 1,
@@ -144,7 +146,7 @@ describe("Order Exchange - Actions", function () {
quantity: 3, quantity: 3,
shipped_quantity: 3, shipped_quantity: 3,
fulfilled_quantity: 3, fulfilled_quantity: 3,
return_requested_quantity: 1, return_requested_quantity: "1",
return_received_quantity: 0, return_received_quantity: 0,
return_dismissed_quantity: 0, return_dismissed_quantity: 0,
written_off_quantity: 0, written_off_quantity: 0,
@@ -125,7 +125,8 @@ describe("Order Return - Actions", function () {
actions, actions,
}) })
expect(changes.order.items).toEqual([ const toJson = JSON.parse(JSON.stringify(changes.order.items))
expect(toJson).toEqual([
{ {
id: "1", id: "1",
quantity: 1, quantity: 1,
@@ -148,7 +149,7 @@ describe("Order Return - Actions", function () {
quantity: 2, quantity: 2,
shipped_quantity: 1, shipped_quantity: 1,
fulfilled_quantity: 1, fulfilled_quantity: 1,
return_requested_quantity: 1, return_requested_quantity: "1",
return_received_quantity: 0, return_received_quantity: 0,
return_dismissed_quantity: 0, return_dismissed_quantity: 0,
written_off_quantity: 0, written_off_quantity: 0,
@@ -162,7 +163,7 @@ describe("Order Return - Actions", function () {
quantity: 3, quantity: 3,
shipped_quantity: 3, shipped_quantity: 3,
fulfilled_quantity: 3, fulfilled_quantity: 3,
return_requested_quantity: 2, return_requested_quantity: "2",
return_received_quantity: 0, return_received_quantity: 0,
return_dismissed_quantity: 0, return_dismissed_quantity: 0,
written_off_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", id: "1",
quantity: 1, quantity: 1,
@@ -276,7 +280,7 @@ describe("Order Return - Actions", function () {
quantity: 2, quantity: 2,
shipped_quantity: 1, shipped_quantity: 1,
fulfilled_quantity: 1, fulfilled_quantity: 1,
return_requested_quantity: 1, return_requested_quantity: "1",
return_received_quantity: 0, return_received_quantity: 0,
return_dismissed_quantity: 0, return_dismissed_quantity: 0,
written_off_quantity: 0, written_off_quantity: 0,
@@ -290,9 +294,9 @@ describe("Order Return - Actions", function () {
quantity: 3, quantity: 3,
shipped_quantity: 3, shipped_quantity: 3,
fulfilled_quantity: 3, fulfilled_quantity: 3,
return_requested_quantity: 0, return_requested_quantity: "0",
return_received_quantity: 1, return_received_quantity: "1",
return_dismissed_quantity: 1, return_dismissed_quantity: "1",
written_off_quantity: 0, written_off_quantity: 0,
}, },
}, },
+26 -24
View File
@@ -1,29 +1,31 @@
import { BigNumberInput } from "@medusajs/types"
export type VirtualOrder = { export type VirtualOrder = {
items: { items: {
id: string id: string
unit_price: number unit_price: BigNumberInput
quantity: number quantity: BigNumberInput
detail: { detail: {
id?: string id?: string
quantity: number quantity: BigNumberInput
shipped_quantity: number shipped_quantity: BigNumberInput
fulfilled_quantity: number fulfilled_quantity: BigNumberInput
return_requested_quantity: number return_requested_quantity: BigNumberInput
return_received_quantity: number return_received_quantity: BigNumberInput
return_dismissed_quantity: number return_dismissed_quantity: BigNumberInput
written_off_quantity: number written_off_quantity: BigNumberInput
metadata?: Record<string, unknown> metadata?: Record<string, unknown>
} }
}[] }[]
shipping_methods: { shipping_methods: {
id: string id: string
price: number price: BigNumberInput
}[] }[]
summary: { summary: {
total: number total: BigNumberInput
} }
metadata?: Record<string, unknown> metadata?: Record<string, unknown>
@@ -36,23 +38,23 @@ export enum EVENT_STATUS {
} }
export interface OrderSummaryCalculated { export interface OrderSummaryCalculated {
currentOrderTotal: number currentOrderTotal: BigNumberInput
originalOrderTotal: number originalOrderTotal: BigNumberInput
transactionTotal: number transactionTotal: BigNumberInput
futureDifference: number futureDifference: BigNumberInput
pendingDifference: number pendingDifference: BigNumberInput
futureTemporaryDifference: number futureTemporaryDifference: BigNumberInput
temporaryDifference: number temporaryDifference: BigNumberInput
differenceSum: number differenceSum: BigNumberInput
} }
export interface OrderTransaction { export interface OrderTransaction {
amount: number amount: BigNumberInput
} }
export interface OrderChangeEvent { export interface OrderChangeEvent {
action: string action: string
amount?: number amount?: BigNumberInput
reference?: string reference?: string
reference_id?: string reference_id?: string
@@ -66,7 +68,7 @@ export interface OrderChangeEvent {
resolve?: { resolve?: {
group_id?: string group_id?: string
reference_id?: string reference_id?: string
amount?: number amount?: BigNumberInput
} }
} }
@@ -91,8 +93,8 @@ export interface ActionTypeDefinition {
versioning?: boolean versioning?: boolean
void?: boolean void?: boolean
commitsAction?: string commitsAction?: string
operation?: (obj: OrderReferences) => number | void operation?: (obj: OrderReferences) => BigNumberInput | void
revert?: (obj: OrderReferences) => number | void revert?: (obj: OrderReferences) => BigNumberInput | void
validate?: (obj: OrderReferences) => void validate?: (obj: OrderReferences) => void
[key: string]: unknown [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 { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change" 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 ??= 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 return action.details.unit_price * action.details.quantity
}, },
@@ -19,7 +22,10 @@ OrderChangeProcessing.registerActionType(ChangeActionType.CANCEL_RETURN, {
(item) => item.id === action.details.reference_id (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 }) { validate({ action, currentOrder }) {
const refId = action.details?.reference_id 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( throw new MedusaError(
MedusaError.Types.INVALID_DATA, MedusaError.Types.INVALID_DATA,
`Cannot cancel more items than what was requested to return for item ${refId}.` `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 { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change" import { OrderChangeProcessing } from "../calculate-order-change"
@@ -10,14 +10,20 @@ OrderChangeProcessing.registerActionType(ChangeActionType.FULFILL_ITEM, {
existing.detail.fulfilled_quantity ??= 0 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 }) { revert({ action, currentOrder }) {
const existing = currentOrder.items.find( const existing = currentOrder.items.find(
(item) => item.id === action.reference_id (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 }) { validate({ action, currentOrder }) {
const refId = action.details?.reference_id const refId = action.details?.reference_id
@@ -50,11 +56,12 @@ OrderChangeProcessing.registerActionType(ChangeActionType.FULFILL_ITEM, {
) )
} }
const notFulfilled = const notFulfilled = MathBN.sub(
(existing.quantity as number) - existing.quantity,
(existing.detail?.fulfilled_quantity as number) existing.detail?.fulfilled_quantity
)
if (action.details?.quantity > notFulfilled) { const greater = MathBN.gt(action.details?.quantity, notFulfilled)
if (greater) {
throw new MedusaError( throw new MedusaError(
MedusaError.Types.INVALID_DATA, MedusaError.Types.INVALID_DATA,
`Cannot fulfill more items than what was ordered for item ${refId}.` `Cannot fulfill more items than what was ordered for item ${refId}.`
+15 -9
View File
@@ -1,4 +1,4 @@
import { MedusaError, isDefined } from "@medusajs/utils" import { MathBN, MedusaError, isDefined } from "@medusajs/utils"
import { VirtualOrder } from "@types" import { VirtualOrder } from "@types"
import { ChangeActionType } from "../action-key" import { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change" import { OrderChangeProcessing } from "../calculate-order-change"
@@ -12,18 +12,21 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_ADD, {
if (existing) { if (existing) {
existing.detail.quantity ??= 0 existing.detail.quantity ??= 0
existing.quantity += action.details.quantity existing.quantity = MathBN.add(existing.quantity, action.details.quantity)
existing.detail.quantity += action.details.quantity
existing.detail.quantity = MathBN.add(
existing.detail.quantity,
action.details.quantity
)
} else { } else {
currentOrder.items.push({ currentOrder.items.push({
id: action.reference_id!, id: action.reference_id!,
unit_price: action.details.unit_price, unit_price: action.details.unit_price,
quantity: action.details.quantity, quantity: action.details.quantity,
// detail: {}
} as VirtualOrder["items"][0]) } 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 }) { revert({ action, currentOrder }) {
const existingIndex = currentOrder.items.findIndex( const existingIndex = currentOrder.items.findIndex(
@@ -32,10 +35,13 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_ADD, {
if (existingIndex > -1) { if (existingIndex > -1) {
const existing = currentOrder.items[existingIndex] const existing = currentOrder.items[existingIndex]
existing.quantity -= action.details.quantity existing.quantity = MathBN.sub(existing.quantity, action.details.quantity)
existing.detail.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) 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( throw new MedusaError(
MedusaError.Types.INVALID_DATA, MedusaError.Types.INVALID_DATA,
`Quantity of item ${refId} must be greater than 0.` `Quantity of item ${refId} must be greater than 0.`
+20 -12
View File
@@ -1,4 +1,4 @@
import { MedusaError, isDefined } from "@medusajs/utils" import { MathBN, MedusaError, isDefined } from "@medusajs/utils"
import { VirtualOrder } from "@types" import { VirtualOrder } from "@types"
import { ChangeActionType } from "../action-key" import { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change" import { OrderChangeProcessing } from "../calculate-order-change"
@@ -14,14 +14,17 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_REMOVE, {
existing.detail.quantity ??= 0 existing.detail.quantity ??= 0
existing.quantity -= action.details.quantity existing.quantity = MathBN.sub(existing.quantity, action.details.quantity)
existing.detail.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) currentOrder.items.splice(existingIndex, 1)
} }
return existing.unit_price * action.details.quantity return MathBN.mult(existing.unit_price, action.details.quantity)
}, },
revert({ action, currentOrder }) { revert({ action, currentOrder }) {
const existing = currentOrder.items.find( const existing = currentOrder.items.find(
@@ -29,8 +32,11 @@ OrderChangeProcessing.registerActionType(ChangeActionType.ITEM_REMOVE, {
) )
if (existing) { if (existing) {
existing.quantity += action.details.quantity existing.quantity = MathBN.add(existing.quantity, action.details.quantity)
existing.detail.quantity += action.details.quantity existing.detail.quantity = MathBN.add(
existing.detail.quantity,
action.details.quantity
)
} else { } else {
currentOrder.items.push({ currentOrder.items.push({
id: action.reference_id!, 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( throw new MedusaError(
MedusaError.Types.INVALID_DATA, MedusaError.Types.INVALID_DATA,
`Quantity of item ${refId} must be greater than 0.` `Quantity of item ${refId} must be greater than 0.`
) )
} }
const notFulfilled = const notFulfilled = MathBN.sub(
(existing.quantity as number) - existing.quantity,
(existing.detail?.fulfilled_quantity as number) existing.detail?.fulfilled_quantity
)
if (action.details?.quantity > notFulfilled) { const greater = MathBN.gt(action.details?.quantity, notFulfilled)
if (greater) {
throw new MedusaError( throw new MedusaError(
MedusaError.Types.INVALID_DATA, MedusaError.Types.INVALID_DATA,
`Cannot remove fulfilled item: Item ${refId}.` `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 { EVENT_STATUS } from "@types"
import { ChangeActionType } from "../action-key" import { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change" import { OrderChangeProcessing } from "../calculate-order-change"
@@ -18,32 +18,47 @@ OrderChangeProcessing.registerActionType(
existing.detail.return_dismissed_quantity ??= 0 existing.detail.return_dismissed_quantity ??= 0
existing.detail.return_requested_quantity ??= 0 existing.detail.return_requested_quantity ??= 0
existing.detail.return_dismissed_quantity += toReturn existing.detail.return_dismissed_quantity = MathBN.add(
existing.detail.return_requested_quantity -= toReturn existing.detail.return_dismissed_quantity,
toReturn
)
existing.detail.return_requested_quantity = MathBN.sub(
existing.detail.return_requested_quantity,
toReturn
)
if (previousEvents) { if (previousEvents) {
for (const previousEvent of previousEvents) { for (const previousEvent of previousEvents) {
previousEvent.original_ = JSON.parse(JSON.stringify(previousEvent)) previousEvent.original_ = JSON.parse(JSON.stringify(previousEvent))
let ret = Math.min(toReturn, previousEvent.details.quantity) let ret = MathBN.min(toReturn, previousEvent.details.quantity)
toReturn -= ret toReturn = MathBN.sub(toReturn, ret)
previousEvent.details.quantity -= ret previousEvent.details.quantity = MathBN.sub(
if (previousEvent.details.quantity <= 0) { previousEvent.details.quantity,
ret
)
if (MathBN.lte(previousEvent.details.quantity, 0)) {
previousEvent.status = EVENT_STATUS.DONE 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 }) { revert({ action, currentOrder, previousEvents }) {
const existing = currentOrder.items.find( const existing = currentOrder.items.find(
(item) => item.id === action.details.reference_id (item) => item.id === action.details.reference_id
)! )!
existing.detail.return_dismissed_quantity -= action.details.quantity existing.detail.return_dismissed_quantity = MathBN.sub(
existing.detail.return_requested_quantity += action.details.quantity 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) { if (previousEvents) {
for (const previousEvent of 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 { EVENT_STATUS } from "@types"
import { ChangeActionType } from "../action-key" import { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change" 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_received_quantity ??= 0
existing.detail.return_requested_quantity ??= 0 existing.detail.return_requested_quantity ??= 0
existing.detail.return_received_quantity += toReturn existing.detail.return_received_quantity = MathBN.add(
existing.detail.return_requested_quantity -= toReturn existing.detail.return_received_quantity,
toReturn
)
existing.detail.return_requested_quantity = MathBN.sub(
existing.detail.return_requested_quantity,
toReturn
)
if (previousEvents) { if (previousEvents) {
for (const previousEvent of previousEvents) { for (const previousEvent of previousEvents) {
previousEvent.original_ = JSON.parse(JSON.stringify(previousEvent)) previousEvent.original_ = JSON.parse(JSON.stringify(previousEvent))
let ret = Math.min(toReturn, previousEvent.details.quantity) let ret = MathBN.min(toReturn, previousEvent.details.quantity)
toReturn -= ret toReturn = MathBN.sub(toReturn, ret)
previousEvent.details.quantity -= ret previousEvent.details.quantity = MathBN.sub(
if (previousEvent.details.quantity <= 0) { previousEvent.details.quantity,
ret
)
if (MathBN.lte(previousEvent.details.quantity, 0)) {
previousEvent.status = EVENT_STATUS.DONE 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 }) { revert({ action, currentOrder, previousEvents }) {
const existing = currentOrder.items.find( const existing = currentOrder.items.find(
(item) => item.id === action.details.reference_id (item) => item.id === action.details.reference_id
)! )!
existing.detail.return_received_quantity -= action.details.quantity existing.detail.return_received_quantity = MathBN.sub(
existing.detail.return_requested_quantity += action.details.quantity 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) { if (previousEvents) {
for (const previousEvent of previousEvents) { for (const previousEvent of previousEvents) {
@@ -52,6 +73,8 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RECEIVE_RETURN_ITEM, {
previousEvent.details = JSON.parse( previousEvent.details = JSON.parse(
JSON.stringify(previousEvent.original_.details) JSON.stringify(previousEvent.original_.details)
) )
transformPropertiesToBigNumber(previousEvent.details?.metadata)
delete previousEvent.original_ delete previousEvent.original_
previousEvent.status = EVENT_STATUS.PENDING previousEvent.status = EVENT_STATUS.PENDING
@@ -84,7 +107,9 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RECEIVE_RETURN_ITEM, {
} }
const quantityRequested = existing?.detail?.return_requested_quantity || 0 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( throw new MedusaError(
MedusaError.Types.INVALID_DATA, MedusaError.Types.INVALID_DATA,
`Cannot receive more items than what was requested to be returned for item ${refId}.` `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 { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change" 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 ??= 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 }) { revert({ action, currentOrder }) {
const existing = currentOrder.items.find( const existing = currentOrder.items.find(
(item) => item.id === action.details.reference_id (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 }) { validate({ action, currentOrder }) {
const refId = action.details?.reference_id const refId = action.details?.reference_id
@@ -47,11 +53,13 @@ OrderChangeProcessing.registerActionType(ChangeActionType.RETURN_ITEM, {
) )
} }
const quantityAvailable = const quantityAvailable = MathBN.sub(
(existing!.detail?.shipped_quantity ?? 0) - existing!.detail?.shipped_quantity ?? 0,
(existing!.detail?.return_requested_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( throw new MedusaError(
MedusaError.Types.INVALID_DATA, MedusaError.Types.INVALID_DATA,
`Cannot request to return more items than what was shipped for item ${refId}.` `Cannot request to return more items than what was shipped for item ${refId}.`
+16 -8
View File
@@ -1,4 +1,4 @@
import { MedusaError, isDefined } from "@medusajs/utils" import { MathBN, MedusaError, isDefined } from "@medusajs/utils"
import { ChangeActionType } from "../action-key" import { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change" import { OrderChangeProcessing } from "../calculate-order-change"
@@ -10,14 +10,20 @@ OrderChangeProcessing.registerActionType(ChangeActionType.SHIP_ITEM, {
existing.detail.shipped_quantity ??= 0 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 }) { revert({ action, currentOrder }) {
const existing = currentOrder.items.find( const existing = currentOrder.items.find(
(item) => item.id === action.reference_id (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 }) { validate({ action, currentOrder }) {
const refId = action.details?.reference_id 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( throw new MedusaError(
MedusaError.Types.INVALID_DATA, MedusaError.Types.INVALID_DATA,
`Quantity of item ${refId} must be greater than 0.` `Quantity of item ${refId} must be greater than 0.`
) )
} }
const notShipped = const notShipped = MathBN.sub(
(existing.detail?.fulfilled_quantity as number) - existing.detail?.fulfilled_quantity,
(existing.detail?.shipped_quantity as number) existing.detail?.shipped_quantity
)
if (action.details?.quantity > notShipped) { const greater = MathBN.gt(action.details?.quantity, notShipped)
if (greater) {
throw new MedusaError( throw new MedusaError(
MedusaError.Types.INVALID_DATA, MedusaError.Types.INVALID_DATA,
`Cannot ship more items than what was fulfilled for item ${refId}.` `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 { ChangeActionType } from "../action-key"
import { OrderChangeProcessing } from "../calculate-order-change" 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 ??= 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 }) { revert({ action, currentOrder }) {
const existing = currentOrder.items.find( const existing = currentOrder.items.find(
(item) => item.id === action.details.reference_id (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 }) { validate({ action, currentOrder }) {
const refId = action.details?.reference_id const refId = action.details?.reference_id
@@ -44,7 +50,8 @@ OrderChangeProcessing.registerActionType(ChangeActionType.WRITE_OFF_ITEM, {
} }
const quantityAvailable = existing!.quantity ?? 0 const quantityAvailable = existing!.quantity ?? 0
if (action.details?.quantity > quantityAvailable) { const greater = MathBN.gt(action.details?.quantity, quantityAvailable)
if (greater) {
throw new MedusaError( throw new MedusaError(
MedusaError.Types.INVALID_DATA, MedusaError.Types.INVALID_DATA,
"Cannot claim more items than what was ordered." "Cannot claim more items than what was ordered."
@@ -1,5 +1,9 @@
import { OrderSummaryDTO } from "@medusajs/types" import { BigNumberInput, OrderSummaryDTO } from "@medusajs/types"
import { isDefined } from "@medusajs/utils" import {
MathBN,
isDefined,
transformPropertiesToBigNumber,
} from "@medusajs/utils"
import { import {
ActionTypeDefinition, ActionTypeDefinition,
EVENT_STATUS, EVENT_STATUS,
@@ -11,7 +15,7 @@ import {
} from "@types" } from "@types"
type InternalOrderSummary = OrderSummaryCalculated & { type InternalOrderSummary = OrderSummaryCalculated & {
futureTemporarySum: number futureTemporarySum: BigNumberInput
} }
export class OrderChangeProcessing { export class OrderChangeProcessing {
@@ -26,7 +30,7 @@ export class OrderChangeProcessing {
private actions: InternalOrderChangeEvent[] private actions: InternalOrderChangeEvent[]
private actionsProcessed: { [key: string]: InternalOrderChangeEvent[] } = {} private actionsProcessed: { [key: string]: InternalOrderChangeEvent[] } = {}
private groupTotal: Record<string, number> = {} private groupTotal: Record<string, BigNumberInput> = {}
private summary: InternalOrderSummary private summary: InternalOrderSummary
public static registerActionType(key: string, type: ActionTypeDefinition) { public static registerActionType(key: string, type: ActionTypeDefinition) {
@@ -46,9 +50,9 @@ export class OrderChangeProcessing {
this.transactions = JSON.parse(JSON.stringify(transactions ?? [])) this.transactions = JSON.parse(JSON.stringify(transactions ?? []))
this.actions = JSON.parse(JSON.stringify(actions ?? [])) this.actions = JSON.parse(JSON.stringify(actions ?? []))
const transactionTotal = transactions.reduce((acc, transaction) => { const transactionTotal = MathBN.add(...transactions.map((tr) => tr.amount))
return acc + transaction.amount
}, 0) transformPropertiesToBigNumber(this.order.metadata)
this.summary = { this.summary = {
futureDifference: 0, futureDifference: 0,
@@ -57,8 +61,8 @@ export class OrderChangeProcessing {
pendingDifference: 0, pendingDifference: 0,
futureTemporarySum: 0, futureTemporarySum: 0,
differenceSum: 0, differenceSum: 0,
currentOrderTotal: order?.summary?.total ?? 0, currentOrderTotal: this.order.summary?.total ?? 0,
originalOrderTotal: order?.summary?.total ?? 0, originalOrderTotal: this.order.summary?.total ?? 0,
transactionTotal, transactionTotal,
} }
} }
@@ -97,55 +101,71 @@ export class OrderChangeProcessing {
...OrderChangeProcessing.typeDefinition[action.action], ...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) { if (action.group_id && !action.evaluationOnly) {
this.groupTotal[action.group_id] ??= 0 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 (type.awaitRequired && !this.isEventDone(action)) {
if (action.evaluationOnly) { if (action.evaluationOnly) {
summary.futureTemporarySum += amount summary.futureTemporarySum = MathBN.add(
summary.futureTemporarySum,
amount
)
} else { } else {
summary.temporaryDifference += amount summary.temporaryDifference = MathBN.add(
summary.temporaryDifference,
amount
)
} }
} }
if (action.evaluationOnly) { if (action.evaluationOnly) {
summary.futureDifference += amount summary.futureDifference = MathBN.add(summary.futureDifference, amount)
} else { } else {
if (!this.isEventDone(action) && !action.group_id) { 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) => { const groupSum = MathBN.add(...Object.values(this.groupTotal))
return acc + amount
}, 0)
summary.differenceSum += groupSum summary.differenceSum = MathBN.add(summary.differenceSum, groupSum)
summary.transactionTotal = this.transactions.reduce((acc, transaction) => { summary.transactionTotal = MathBN.sum(
return acc + transaction.amount ...this.transactions.map((tr) => tr.amount)
}, 0) )
summary.futureTemporaryDifference = summary.futureTemporaryDifference = MathBN.sub(
summary.futureDifference - summary.futureTemporarySum summary.futureDifference,
summary.futureTemporarySum
)
summary.temporaryDifference = summary.temporaryDifference = MathBN.sub(
summary.differenceSum - summary.temporaryDifference summary.differenceSum,
summary.temporaryDifference
)
summary.pendingDifference = summary.pendingDifference = MathBN.sub(
summary.currentOrderTotal - summary.transactionTotal summary.currentOrderTotal,
summary.transactionTotal
)
} }
private processAction_( private processAction_(
action: InternalOrderChangeEvent, action: InternalOrderChangeEvent,
isReplay = false isReplay = false
): number | void { ): BigNumberInput | void {
const type = { const type = {
...OrderChangeProcessing.defaultConfig, ...OrderChangeProcessing.defaultConfig,
...OrderChangeProcessing.typeDefinition[action.action], ...OrderChangeProcessing.typeDefinition[action.action],
@@ -166,7 +186,7 @@ export class OrderChangeProcessing {
) )
} }
let calculatedAmount: number = action.amount ?? 0 let calculatedAmount = action.amount ?? 0
const params = { const params = {
actions: this.actions, actions: this.actions,
action, action,
@@ -181,7 +201,7 @@ export class OrderChangeProcessing {
} }
if (typeof type.operation === "function") { 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 // the action.amount has priority over the calculated amount
if (!isDefined(action.amount)) { if (!isDefined(action.amount)) {
@@ -207,7 +227,10 @@ export class OrderChangeProcessing {
} }
if (action.resolve.amount && !action.evaluationOnly) { if (action.resolve.amount && !action.evaluationOnly) {
this.groupTotal[groupId] ??= 0 this.groupTotal[groupId] ??= 0
this.groupTotal[groupId] -= action.resolve.amount this.groupTotal[groupId] = MathBN.sub(
this.groupTotal[groupId],
action.resolve.amount
)
} }
} }
+2 -2
View File
@@ -1,8 +1,8 @@
import BigNumber from "bignumber.js" import BigNumberJS from "bignumber.js"
export type BigNumberRawValue = { export type BigNumberRawValue = {
value: string | number value: string | number
[key: string]: unknown [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 { Property } from "@mikro-orm/core"
import { isPresent, trimZeros } from "../../common" import { isPresent, trimZeros } from "../../common"
import { BigNumber } from "../../totals/big-number" import { BigNumber } from "../../totals/big-number"
import { BigNumberInput } from "@medusajs/types"
export function MikroOrmBigNumberProperty( export function MikroOrmBigNumberProperty(
options: Parameters<typeof Property>[0] & { options: Parameters<typeof Property>[0] & {
@@ -13,47 +13,52 @@ export function MikroOrmBigNumberProperty(
Object.defineProperty(target, columnName, { Object.defineProperty(target, columnName, {
get() { 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) { set(value: BigNumberInput) {
if (options?.nullable && !isPresent(value)) { if (options?.nullable && !isPresent(value)) {
this.__helper.__data[columnName] = null this.__helper.__data[columnName] = null
this.__helper.__data[rawColumnName]
this[rawColumnName] = null 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 { } 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() this.__helper.__touched = !this.__helper.hydrator.isRunning()
}, },
enumerable: true,
configurable: true,
}) })
Property({ Property({
type: "number", type: "any",
columnType: "numeric", columnType: "numeric",
trackChanges: false, trackChanges: false,
...options, ...options,
@@ -278,10 +278,7 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
async update(data: { entity; update }[], context?: Context): Promise<T[]> { async update(data: { entity; update }[], context?: Context): Promise<T[]> {
const manager = this.getActiveManager<EntityManager>(context) const manager = this.getActiveManager<EntityManager>(context)
const entities = data.map((data_) => { const entities = data.map((data_) => {
return manager.assign( return manager.assign(data_.entity, data_.update)
data_.entity,
data_.update as RequiredEntityData<T>
)
}) })
manager.persist(entities) 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)
})
})
+27 -9
View File
@@ -7,18 +7,24 @@ export class BigNumber {
private numeric_: number private numeric_: number
private raw_?: BigNumberRawValue private raw_?: BigNumberRawValue
private bignumber_?: BigNumberJS
constructor(rawValue: BigNumberInput, options?: { precision?: number }) { constructor(
rawValue: BigNumberInput | BigNumber,
options?: { precision?: number }
) {
this.setRawValueOrThrow(rawValue, options) this.setRawValueOrThrow(rawValue, options)
} }
setRawValueOrThrow( setRawValueOrThrow(
rawValue: BigNumberInput, rawValue: BigNumberInput | BigNumber,
{ precision }: { precision?: number } = {} { precision }: { precision?: number } = {}
) { ) {
precision ??= BigNumber.DEFAULT_PRECISION precision ??= BigNumber.DEFAULT_PRECISION
if (BigNumberJS.isBigNumber(rawValue)) { if (rawValue instanceof BigNumber) {
Object.assign(this, rawValue)
} else if (BigNumberJS.isBigNumber(rawValue)) {
/** /**
* Example: * Example:
* const bnUnitValue = new BigNumberJS("10.99") * const bnUnitValue = new BigNumberJS("10.99")
@@ -29,6 +35,7 @@ export class BigNumber {
value: rawValue.toPrecision(precision), value: rawValue.toPrecision(precision),
precision, precision,
} }
this.bignumber_ = rawValue
} else if (isString(rawValue)) { } else if (isString(rawValue)) {
/** /**
* Example: const unitValue = "1234.1234" * Example: const unitValue = "1234.1234"
@@ -40,26 +47,31 @@ export class BigNumber {
value: bigNum.toPrecision(precision), value: bigNum.toPrecision(precision),
precision, precision,
} }
this.bignumber_ = bigNum
} else if (isBigNumber(rawValue)) { } else if (isBigNumber(rawValue)) {
/** /**
* Example: const unitValue = { value: "1234.1234" } * Example: const unitValue = { value: "1234.1234" }
*/ */
const definedPrecision = rawValue.precision ?? precision const definedPrecision = rawValue.precision ?? precision
this.numeric_ = BigNumberJS(rawValue.value).toNumber() const bigNum = new BigNumberJS(rawValue.value)
this.numeric_ = bigNum.toNumber()
this.raw_ = { this.raw_ = {
...rawValue, ...rawValue,
precision: definedPrecision, precision: definedPrecision,
} }
this.bignumber_ = bigNum
} else if (typeof rawValue === `number` && !Number.isNaN(rawValue)) { } else if (typeof rawValue === `number` && !Number.isNaN(rawValue)) {
/** /**
* Example: const unitValue = 1234 * Example: const unitValue = 1234
*/ */
this.numeric_ = rawValue as number this.numeric_ = rawValue as number
const bigNum = new BigNumberJS(rawValue as number)
this.raw_ = { this.raw_ = {
value: BigNumberJS(rawValue as number).toPrecision(precision), value: bigNum.toPrecision(precision),
precision, precision,
} }
this.bignumber_ = bigNum
} else { } else {
throw new Error( throw new Error(
`Invalid BigNumber value: ${rawValue}. Should be one of: string, number, BigNumber (bignumber.js), BigNumberRawValue` `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) const newValue = new BigNumber(value)
this.numeric_ = newValue.numeric_ this.numeric_ = newValue.numeric_
this.raw_ = newValue.raw_ this.raw_ = newValue.raw_
this.bignumber_ = newValue.bignumber_
} }
get raw(): BigNumberRawValue | undefined { get raw(): BigNumberRawValue | undefined {
return this.raw_ return this.raw_
} }
get bigNumber(): BigNumberJS | undefined {
return this.bignumber_
}
set raw(rawValue: BigNumberInput) { set raw(rawValue: BigNumberInput) {
const newValue = new BigNumber(rawValue) const newValue = new BigNumber(rawValue)
this.numeric_ = newValue.numeric_ this.numeric_ = newValue.numeric_
this.raw_ = newValue.raw_ this.raw_ = newValue.raw_
this.bignumber_ = newValue.bignumber_
} }
toJSON() { toJSON() {
return this.raw_ return this.bignumber_
? this.bignumber_?.toNumber()
: this.raw_
? new BigNumberJS(this.raw_.value).toNumber() ? new BigNumberJS(this.raw_.value).toNumber()
: this.numeric_ : this.numeric_
} }
valueOf() { valueOf() {
return this.raw_ return this.bignumber_
? new BigNumberJS(this.raw_.value).toNumber()
: this.numeric_
} }
} }
+3
View File
@@ -7,7 +7,10 @@ import { BigNumber as BigNumberJs } from "bignumber.js"
import { BigNumber } from "./big-number" import { BigNumber } from "./big-number"
import { toBigNumberJs } from "./to-big-number-js" import { toBigNumberJs } from "./to-big-number-js"
export * from "./math"
export * from "./promotion" export * from "./promotion"
export * from "./to-big-number-js"
export * from "./transform-properties-to-bignumber"
type GetLineItemTotalsContext = { type GetLineItemTotalsContext = {
includeTax?: boolean includeTax?: boolean
+108
View 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 { BigNumber as BigNumberJs } from "bignumber.js"
import { isDefined, toCamelCase } from "../common" import { isDefined, toCamelCase } from "../common"
import { BigNumber } from "./big-number" import { BigNumber } from "./big-number"
type InputEntity<T, V extends string> = { [key in V]?: InputEntityField } type InputEntity<T, V extends string> = { [key in V]?: InputEntityField }
type InputEntityField = number | string | BigNumber type InputEntityField = number | string | BigNumber
type Camelize<V extends string> = V extends `${infer A}_${infer B}` type Camelize<V extends string> = V extends `${infer A}_${infer B}`
? `${A}${Camelize<Capitalize<B>>}` ? `${A}${Camelize<Capitalize<B>>}`
: V : V
type Output<V extends string> = { [key in Camelize<V>]: BigNumberJs } type Output<V extends string> = { [key in Camelize<V>]: BigNumberJs }
export function toBigNumberJs<T, V extends string>( 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 })
}
}
}
}