feat(utils): Fix big number decorator and cleanup (#6473)
**What** - Fix big number decorator and cleanup
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@medusajs/utils": patch
|
||||
---
|
||||
|
||||
feat(utils): Fix big number decorator and cleanup
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BigNumberRawValue, DAL } from "@medusajs/types"
|
||||
import { BigNumber, BigNumberField } from "@medusajs/utils"
|
||||
import { BigNumber, MikroOrmBigNumberProperty } from "@medusajs/utils"
|
||||
import { OptionalProps, PrimaryKey, Property } from "@mikro-orm/core"
|
||||
|
||||
type OptionalAdjustmentLineProps = DAL.EntityDateColumns
|
||||
@@ -26,8 +26,7 @@ export default abstract class AdjustmentLine {
|
||||
@Property({ columnType: "text", nullable: true })
|
||||
code: string | null = null
|
||||
|
||||
@Property({ columnType: "numeric" })
|
||||
@BigNumberField()
|
||||
@MikroOrmBigNumberProperty()
|
||||
amount: BigNumber | number
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { BigNumberRawValue, DAL } from "@medusajs/types"
|
||||
import {
|
||||
BigNumber,
|
||||
BigNumberField,
|
||||
createPsqlIndexStatementHelper,
|
||||
generateEntityId,
|
||||
MikroOrmBigNumberProperty,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
BeforeCreate,
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
Collection,
|
||||
Entity,
|
||||
ManyToOne,
|
||||
OnInit,
|
||||
OneToMany,
|
||||
OnInit,
|
||||
OptionalProps,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
@@ -114,15 +114,17 @@ export default class LineItem {
|
||||
@Property({ columnType: "boolean" })
|
||||
is_tax_inclusive = false
|
||||
|
||||
@Property({ columnType: "numeric", nullable: true })
|
||||
@BigNumberField({ nullable: true })
|
||||
@MikroOrmBigNumberProperty({
|
||||
nullable: true,
|
||||
})
|
||||
compare_at_unit_price?: BigNumber | number | null = null
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
raw_compare_at_unit_price: BigNumberRawValue | null = null
|
||||
|
||||
@Property({ columnType: "numeric" })
|
||||
@BigNumberField()
|
||||
@MikroOrmBigNumberProperty({
|
||||
nullable: true,
|
||||
})
|
||||
unit_price: BigNumber | number
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { BigNumberRawValue, DAL } from "@medusajs/types"
|
||||
import {
|
||||
BigNumber,
|
||||
BigNumberField,
|
||||
createPsqlIndexStatementHelper,
|
||||
generateEntityId,
|
||||
MikroOrmBigNumberProperty,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
BeforeCreate,
|
||||
@@ -57,50 +57,43 @@ export default class OrderDetail {
|
||||
})
|
||||
item: LineItem
|
||||
|
||||
@Property({ columnType: "numeric" })
|
||||
@BigNumberField()
|
||||
@MikroOrmBigNumberProperty()
|
||||
quantity: BigNumber | number
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
raw_quantity: BigNumberRawValue
|
||||
|
||||
@Property({ columnType: "numeric" })
|
||||
@BigNumberField()
|
||||
@MikroOrmBigNumberProperty()
|
||||
fulfilled_quantity: BigNumber | number
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
raw_fulfilled_quantity: BigNumberRawValue
|
||||
|
||||
@Property({ columnType: "numeric" })
|
||||
@BigNumberField()
|
||||
@MikroOrmBigNumberProperty()
|
||||
shipped_quantity: BigNumber | number
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
raw_shipped_quantity: BigNumberRawValue
|
||||
|
||||
@Property({ columnType: "numeric" })
|
||||
@BigNumberField()
|
||||
@MikroOrmBigNumberProperty()
|
||||
return_requested_quantity: BigNumber | number
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
raw_return_requested_quantity: BigNumberRawValue
|
||||
|
||||
@Property({ columnType: "numeric" })
|
||||
@BigNumberField()
|
||||
@MikroOrmBigNumberProperty()
|
||||
return_received_quantity: BigNumber | number
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
raw_return_received_quantity: BigNumberRawValue
|
||||
|
||||
@Property({ columnType: "numeric" })
|
||||
@BigNumberField()
|
||||
@MikroOrmBigNumberProperty()
|
||||
return_dismissed_quantity: BigNumber | number
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
raw_return_dismissed_quantity: BigNumberRawValue
|
||||
|
||||
@Property({ columnType: "numeric" })
|
||||
@BigNumberField()
|
||||
@MikroOrmBigNumberProperty()
|
||||
written_off_quantity: BigNumber | number
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { BigNumberRawValue } from "@medusajs/types"
|
||||
import {
|
||||
BigNumber,
|
||||
BigNumberField,
|
||||
createPsqlIndexStatementHelper,
|
||||
generateEntityId,
|
||||
MikroOrmBigNumberProperty,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
BeforeCreate,
|
||||
@@ -12,8 +12,8 @@ import {
|
||||
Collection,
|
||||
Entity,
|
||||
ManyToOne,
|
||||
OnInit,
|
||||
OneToMany,
|
||||
OnInit,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
@@ -54,8 +54,7 @@ export default class ShippingMethod {
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
description: string | null = null
|
||||
|
||||
@Property({ columnType: "numeric" })
|
||||
@BigNumberField()
|
||||
@MikroOrmBigNumberProperty()
|
||||
amount: BigNumber | number
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BigNumberRawValue } from "@medusajs/types"
|
||||
import { BigNumber, BigNumberField } from "@medusajs/utils"
|
||||
import {BigNumber, MikroOrmBigNumberProperty} from "@medusajs/utils"
|
||||
import { PrimaryKey, Property } from "@mikro-orm/core"
|
||||
|
||||
/**
|
||||
@@ -22,8 +22,7 @@ export default abstract class TaxLine {
|
||||
@Property({ columnType: "text" })
|
||||
code: string
|
||||
|
||||
@Property({ columnType: "numeric" })
|
||||
@BigNumberField()
|
||||
@MikroOrmBigNumberProperty()
|
||||
rate: BigNumber | number
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { BigNumberRawValue, DAL } from "@medusajs/types"
|
||||
import {
|
||||
BigNumber,
|
||||
BigNumberField,
|
||||
createPsqlIndexStatementHelper,
|
||||
generateEntityId,
|
||||
MikroOrmBigNumberProperty,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
BeforeCreate,
|
||||
@@ -52,8 +52,7 @@ export default class Transaction {
|
||||
})
|
||||
order: Order
|
||||
|
||||
@Property({ columnType: "numeric" })
|
||||
@BigNumberField()
|
||||
@MikroOrmBigNumberProperty()
|
||||
amount: BigNumber | number
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
|
||||
+598
-658
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,13 @@ import {
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from "@mikro-orm/core"
|
||||
import { generateEntityId, PaymentSessionStatus } from "@medusajs/utils"
|
||||
import {
|
||||
BigNumber,
|
||||
generateEntityId,
|
||||
MikroOrmBigNumberProperty,
|
||||
PaymentSessionStatus,
|
||||
} from "@medusajs/utils"
|
||||
import { BigNumberRawValue } from "@medusajs/types"
|
||||
|
||||
import PaymentCollection from "./payment-collection"
|
||||
import Payment from "./payment"
|
||||
@@ -24,11 +30,13 @@ export default class PaymentSession {
|
||||
@Property({ columnType: "text" })
|
||||
currency_code: string
|
||||
|
||||
@MikroOrmBigNumberProperty()
|
||||
amount: BigNumber | number
|
||||
|
||||
@Property({
|
||||
columnType: "numeric",
|
||||
serializer: Number,
|
||||
columnType: "jsonb",
|
||||
})
|
||||
amount: number
|
||||
raw_amount: BigNumberRawValue
|
||||
|
||||
@Property({ columnType: "text" })
|
||||
provider_id: string
|
||||
|
||||
@@ -3,7 +3,7 @@ module.exports = {
|
||||
"^.+\\.[jt]s?$": [
|
||||
"ts-jest",
|
||||
{
|
||||
tsConfig: "tsconfig.json",
|
||||
tsConfig: "tsconfig.spec.json",
|
||||
isolatedModules: true,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { MikroOrmBigNumberProperty } from "../big-number-field"
|
||||
import { BigNumberRawValue } from "@medusajs/types"
|
||||
import { BigNumber } from "../../../totals/big-number"
|
||||
|
||||
describe("@MikroOrmBigNumberProperty", () => {
|
||||
it("should correctly assign and update BigNumber values", () => {
|
||||
class TestAmount {
|
||||
@MikroOrmBigNumberProperty()
|
||||
amount: BigNumber | number
|
||||
|
||||
raw_amount: BigNumberRawValue
|
||||
}
|
||||
|
||||
const testAmount = new TestAmount()
|
||||
|
||||
expect(testAmount.amount).toBeUndefined()
|
||||
expect(testAmount.raw_amount).toBeUndefined()
|
||||
|
||||
testAmount.amount = 100
|
||||
|
||||
expect(testAmount.amount).toEqual(100)
|
||||
expect((testAmount as any).amount_).toEqual(100)
|
||||
expect(testAmount.raw_amount).toEqual({
|
||||
value: "100.00000000000000000",
|
||||
precision: 20,
|
||||
})
|
||||
|
||||
// Update the amount
|
||||
|
||||
testAmount.amount = 200
|
||||
|
||||
expect(testAmount.amount).toEqual(200)
|
||||
expect((testAmount as any).amount_).toEqual(200)
|
||||
expect(testAmount.raw_amount).toEqual({
|
||||
value: "200.00000000000000000",
|
||||
precision: 20,
|
||||
})
|
||||
|
||||
// Update with big number
|
||||
|
||||
testAmount.amount = new BigNumber(300, { precision: 5 })
|
||||
|
||||
expect(testAmount.amount).toEqual(300)
|
||||
expect((testAmount as any).amount_).toEqual(300)
|
||||
expect(testAmount.raw_amount).toEqual({ value: "300.00", precision: 5 })
|
||||
})
|
||||
})
|
||||
@@ -1,78 +1,54 @@
|
||||
import { BigNumber } from "../../totals/big-number"
|
||||
import { Property } from "@mikro-orm/core"
|
||||
import { BigNumberInput } from "@medusajs/types"
|
||||
|
||||
const bigNumberFields = new WeakMap<
|
||||
object,
|
||||
{ prop: string; options: { nullable?: boolean } }[]
|
||||
>()
|
||||
export function MikroOrmBigNumberProperty(
|
||||
options: Parameters<typeof Property>[0] & {
|
||||
rawColumnName?: string
|
||||
} = {}
|
||||
) {
|
||||
return function (target: any, columnName: string) {
|
||||
const targetColumn = columnName + "_"
|
||||
const rawColumnName = options.rawColumnName ?? `raw_${columnName}`
|
||||
|
||||
export function BigNumberField(options: { nullable?: boolean } = {}) {
|
||||
return function (target: any, prop: string) {
|
||||
const entity = target.constructor
|
||||
if (!bigNumberFields.has(entity)) {
|
||||
bigNumberFields.set(entity, [])
|
||||
}
|
||||
Object.defineProperty(target, columnName, {
|
||||
get() {
|
||||
return this[targetColumn]
|
||||
},
|
||||
set(value: BigNumberInput) {
|
||||
let bigNumber: BigNumber
|
||||
if (value instanceof BigNumber) {
|
||||
bigNumber = value
|
||||
} else if (this[rawColumnName]) {
|
||||
const precision = this[rawColumnName].precision
|
||||
this[rawColumnName].value = new BigNumber(value, {
|
||||
precision,
|
||||
}).raw!.value
|
||||
bigNumber = new BigNumber(this[rawColumnName])
|
||||
} else {
|
||||
bigNumber = new BigNumber(value)
|
||||
}
|
||||
|
||||
if (prop.startsWith("raw_")) {
|
||||
const suggestedPropName = prop.replace("raw_", "")
|
||||
throw new Error(
|
||||
`BigNumberField decorator has to be used on the property "${suggestedPropName}" and ${prop} typed as BigNumberRawValue.`
|
||||
)
|
||||
}
|
||||
this[targetColumn] = bigNumber.numeric
|
||||
this[rawColumnName] = bigNumber.raw
|
||||
},
|
||||
})
|
||||
|
||||
bigNumberFields.get(entity)?.push({ prop, options })
|
||||
Property({
|
||||
type: "number",
|
||||
columnType: "numeric",
|
||||
fieldName: columnName,
|
||||
serializer: () => {
|
||||
return undefined
|
||||
},
|
||||
...options,
|
||||
})(target, targetColumn)
|
||||
|
||||
if (!entity.prototype.__bigNumberInitialized) {
|
||||
entity.prototype.__bigNumberInitialized = true
|
||||
|
||||
registerGlobalHook(entity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function registerGlobalHook(entity: any) {
|
||||
const originalOnInit = entity.prototype.onInit
|
||||
const originalOnCreate = entity.prototype.onCreate
|
||||
const originalOnUpdate = entity.prototype.onUpdate
|
||||
|
||||
entity.prototype.onInit = function (...args: any[]) {
|
||||
initializeBigNumberFields(this)
|
||||
|
||||
if (originalOnInit) {
|
||||
originalOnInit.apply(this, args)
|
||||
}
|
||||
}
|
||||
|
||||
entity.prototype.onCreate = function (...args: any[]) {
|
||||
initializeBigNumberFields(this)
|
||||
|
||||
if (originalOnCreate) {
|
||||
originalOnCreate.apply(this, args)
|
||||
}
|
||||
}
|
||||
|
||||
entity.prototype.onUpdate = function (...args: any[]) {
|
||||
initializeBigNumberFields(this)
|
||||
|
||||
if (originalOnUpdate) {
|
||||
originalOnUpdate.apply(this, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initializeBigNumberFields(entity: any) {
|
||||
const fields = bigNumberFields.get(entity.constructor) ?? []
|
||||
|
||||
for (const field of fields) {
|
||||
const { prop, options } = field
|
||||
|
||||
const rawValue = entity[`raw_${prop}`]
|
||||
const value = entity[prop]
|
||||
if (options.nullable && rawValue === null && value === null) {
|
||||
return
|
||||
}
|
||||
|
||||
const val = new BigNumber(rawValue ?? value)
|
||||
entity[prop] = val.numeric
|
||||
entity[`raw_${prop}`] = val.raw
|
||||
Property({
|
||||
type: "number",
|
||||
persist: false,
|
||||
getter: true,
|
||||
setter: true,
|
||||
})(target, columnName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,16 @@ export class BigNumber {
|
||||
private numeric_: number
|
||||
private raw_?: BigNumberRawValue
|
||||
|
||||
constructor(rawPrice: BigNumberInput) {
|
||||
this.setRawPriceOrThrow(rawPrice)
|
||||
constructor(rawPrice: BigNumberInput, options?: { precision?: number }) {
|
||||
this.setRawPriceOrThrow(rawPrice, options)
|
||||
}
|
||||
|
||||
setRawPriceOrThrow(rawPrice: BigNumberInput) {
|
||||
setRawPriceOrThrow(
|
||||
rawPrice: BigNumberInput,
|
||||
{ precision }: { precision?: number } = {}
|
||||
) {
|
||||
precision ??= BigNumber.DEFAULT_PRECISION
|
||||
|
||||
if (BigNumberJS.isBigNumber(rawPrice)) {
|
||||
/**
|
||||
* Example:
|
||||
@@ -21,7 +26,8 @@ export class BigNumber {
|
||||
*/
|
||||
this.numeric_ = rawPrice.toNumber()
|
||||
this.raw_ = {
|
||||
value: rawPrice.toPrecision(BigNumber.DEFAULT_PRECISION),
|
||||
value: rawPrice.toPrecision(precision),
|
||||
precision,
|
||||
}
|
||||
} else if (isString(rawPrice)) {
|
||||
/**
|
||||
@@ -31,7 +37,8 @@ export class BigNumber {
|
||||
|
||||
this.numeric_ = bigNum.toNumber()
|
||||
this.raw_ = this.raw_ = {
|
||||
value: bigNum.toPrecision(BigNumber.DEFAULT_PRECISION),
|
||||
value: bigNum.toPrecision(precision),
|
||||
precision,
|
||||
}
|
||||
} else if (isBigNumber(rawPrice)) {
|
||||
/**
|
||||
@@ -41,6 +48,7 @@ export class BigNumber {
|
||||
|
||||
this.raw_ = {
|
||||
...rawPrice,
|
||||
precision,
|
||||
}
|
||||
} else if (typeof rawPrice === `number` && !Number.isNaN(rawPrice)) {
|
||||
/**
|
||||
@@ -49,7 +57,8 @@ export class BigNumber {
|
||||
this.numeric_ = rawPrice as number
|
||||
|
||||
this.raw_ = {
|
||||
value: BigNumberJS(rawPrice as number).toString(),
|
||||
value: BigNumberJS(rawPrice as number).toPrecision(precision),
|
||||
precision,
|
||||
}
|
||||
} else {
|
||||
throw new Error(
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"],
|
||||
"compilerOptions": {
|
||||
"sourceMap": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user