feat(utils): Fix big number decorator and cleanup (#6473)

**What**
- Fix big number decorator and cleanup
This commit is contained in:
Adrien de Peretti
2024-02-22 16:58:41 +00:00
committed by GitHub
parent 598ee6f49c
commit 36a61658f9
14 changed files with 757 additions and 773 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@medusajs/utils": patch
---
feat(utils): Fix big number decorator and cleanup
+2 -3
View File
@@ -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" })
+8 -6
View File
@@ -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" })
+8 -15
View File
@@ -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" })
+3 -4
View File
@@ -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" })
+2 -3
View File
@@ -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" })
+2 -3
View File
@@ -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" })
File diff suppressed because it is too large Load Diff
+12 -4
View File
@@ -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
+1 -1
View File
@@ -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)
}
}
+15 -6
View File
@@ -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(
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"include": ["src"],
"exclude": ["node_modules", "dist"],
"compilerOptions": {
"sourceMap": true
}
}