From 8ea37d03c914a5004a3e42770668b2d1f7f8f564 Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Mon, 26 Feb 2024 16:59:36 +0530 Subject: [PATCH] fix(utils): bignumber util considers nullable options when setting value (#6499) what: - when setting null for nullable columns, big number utils should not throw error - remove circular soft delete depenedencies for cart module - trims zeros on big number values --- .changeset/itchy-bags-play.md | 5 ++ .../cart/src/models/line-item-adjustment.ts | 2 +- .../src/models/shipping-method-adjustment.ts | 2 +- .../services/payment-module/index.spec.ts | 12 ++--- packages/utils/src/common/index.ts | 7 +-- packages/utils/src/common/trim-zeros.ts | 15 ++++++ .../__tests__/big-number-field.spec.ts | 23 ++++++++-- .../src/dal/mikro-orm/big-number-field.ts | 29 +++++++++--- .../src/totals/__tests__/big-number.spec.ts | 10 +++- packages/utils/src/totals/big-number.ts | 46 +++++++++---------- 10 files changed, 106 insertions(+), 45 deletions(-) create mode 100644 .changeset/itchy-bags-play.md create mode 100644 packages/utils/src/common/trim-zeros.ts diff --git a/.changeset/itchy-bags-play.md b/.changeset/itchy-bags-play.md new file mode 100644 index 0000000000..2d6a1439f4 --- /dev/null +++ b/.changeset/itchy-bags-play.md @@ -0,0 +1,5 @@ +--- +"@medusajs/utils": patch +--- + +fix(utils): bignumber util considers nullable options when setting value diff --git a/packages/cart/src/models/line-item-adjustment.ts b/packages/cart/src/models/line-item-adjustment.ts index 5bf86a9a17..d3944b3192 100644 --- a/packages/cart/src/models/line-item-adjustment.ts +++ b/packages/cart/src/models/line-item-adjustment.ts @@ -24,7 +24,7 @@ import LineItem from "./line-item" export default class LineItemAdjustment extends AdjustmentLine { @ManyToOne({ entity: () => LineItem, - cascade: [Cascade.REMOVE, Cascade.PERSIST, "soft-remove"] as any, + cascade: [Cascade.REMOVE, Cascade.PERSIST], }) item: LineItem diff --git a/packages/cart/src/models/shipping-method-adjustment.ts b/packages/cart/src/models/shipping-method-adjustment.ts index 2d97ea3b05..f47c347d03 100644 --- a/packages/cart/src/models/shipping-method-adjustment.ts +++ b/packages/cart/src/models/shipping-method-adjustment.ts @@ -20,7 +20,7 @@ import ShippingMethod from "./shipping-method" export default class ShippingMethodAdjustment extends AdjustmentLine { @ManyToOne({ entity: () => ShippingMethod, - cascade: [Cascade.REMOVE, Cascade.PERSIST, "soft-remove"] as any, + cascade: [Cascade.REMOVE, Cascade.PERSIST], }) shipping_method: ShippingMethod diff --git a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index 603d7d7007..102845cd46 100644 --- a/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -1,15 +1,15 @@ -import { IPaymentModuleService } from "@medusajs/types" import { Modules } from "@medusajs/modules-sdk" +import { IPaymentModuleService } from "@medusajs/types" +import { + moduleIntegrationTestRunner, + SuiteOptions, +} from "medusa-test-utils/dist" import { createPaymentCollections, createPayments, createPaymentSessions, } from "../../../__fixtures__" -import { - moduleIntegrationTestRunner, - SuiteOptions, -} from "medusa-test-utils/dist" jest.setTimeout(30000) @@ -466,7 +466,7 @@ moduleIntegrationTestRunner({ id: expect.any(String), currency_code: "usd", amount: 100, - raw_amount: { value: "100.00000000000000000", precision: 20 }, + raw_amount: { value: "100", precision: 20 }, provider_id: "system", data: {}, status: "authorized", diff --git a/packages/utils/src/common/index.ts b/packages/utils/src/common/index.ts index 5305c8d5df..2078870306 100644 --- a/packages/utils/src/common/index.ts +++ b/packages/utils/src/common/index.ts @@ -1,6 +1,5 @@ export * from "./alter-columns-helper" export * from "./array-difference" -export * from "./get-set-difference" export * from "./build-query" export * from "./camel-to-snake-case" export * from "./container" @@ -12,8 +11,10 @@ export * from "./errors" export * from "./generate-entity-id" export * from "./generate-linkable-keys-map" export * from "./get-config-file" +export * from "./get-duplicates" export * from "./get-iso-string-from-date" export * from "./get-selects-and-relations-from-object-array" +export * from "./get-set-difference" export * from "./group-by" export * from "./handle-postgres-database-error" export * from "./is-big-number" @@ -35,6 +36,7 @@ export * from "./promise-all" export * from "./remote-query-object-from-string" export * from "./remote-query-object-to-string" export * from "./remove-nullisih" +export * from "./remove-undefined" export * from "./selector-constraints-to-string" export * from "./set-metadata" export * from "./simple-hash" @@ -45,7 +47,6 @@ export * from "./to-camel-case" export * from "./to-kebab-case" export * from "./to-pascal-case" export * from "./transaction" +export * from "./trim-zeros" export * from "./upper-case-first" export * from "./wrap-handler" -export * from "./remove-undefined" -export * from "./get-duplicates" diff --git a/packages/utils/src/common/trim-zeros.ts b/packages/utils/src/common/trim-zeros.ts new file mode 100644 index 0000000000..5dcab7d3c4 --- /dev/null +++ b/packages/utils/src/common/trim-zeros.ts @@ -0,0 +1,15 @@ +export function trimZeros(value: string) { + const [whole, fraction] = value.split(".") + + if (fraction) { + const decimal = fraction.replace(/0+$/, "") + + if (!decimal) { + return whole + } + + return `${whole}.${decimal}` + } + + return whole +} diff --git a/packages/utils/src/dal/mikro-orm/__tests__/big-number-field.spec.ts b/packages/utils/src/dal/mikro-orm/__tests__/big-number-field.spec.ts index 52d8613532..43a9e8e0a3 100644 --- a/packages/utils/src/dal/mikro-orm/__tests__/big-number-field.spec.ts +++ b/packages/utils/src/dal/mikro-orm/__tests__/big-number-field.spec.ts @@ -1,6 +1,6 @@ -import { MikroOrmBigNumberProperty } from "../big-number-field" import { BigNumberRawValue } from "@medusajs/types" import { BigNumber } from "../../../totals/big-number" +import { MikroOrmBigNumberProperty } from "../big-number-field" describe("@MikroOrmBigNumberProperty", () => { it("should correctly assign and update BigNumber values", () => { @@ -9,6 +9,11 @@ describe("@MikroOrmBigNumberProperty", () => { amount: BigNumber | number raw_amount: BigNumberRawValue + + @MikroOrmBigNumberProperty({ nullable: true }) + nullable_amount: BigNumber | number | null = null + + raw_nullable_amount: BigNumberRawValue | null = null } const testAmount = new TestAmount() @@ -21,10 +26,20 @@ describe("@MikroOrmBigNumberProperty", () => { expect(testAmount.amount).toEqual(100) expect((testAmount as any).amount_).toEqual(100) expect(testAmount.raw_amount).toEqual({ - value: "100.00000000000000000", + value: "100", precision: 20, }) + try { + ;(testAmount as any).amount = null + } catch (e) { + expect(e.message).toEqual( + "Invalid BigNumber value: null. Should be one of: string, number, BigNumber (bignumber.js), BigNumberRawValue" + ) + } + + testAmount.nullable_amount = null + expect(testAmount.nullable_amount).toEqual(null) // Update the amount testAmount.amount = 200 @@ -32,7 +47,7 @@ describe("@MikroOrmBigNumberProperty", () => { expect(testAmount.amount).toEqual(200) expect((testAmount as any).amount_).toEqual(200) expect(testAmount.raw_amount).toEqual({ - value: "200.00000000000000000", + value: "200", precision: 20, }) @@ -42,6 +57,6 @@ describe("@MikroOrmBigNumberProperty", () => { expect(testAmount.amount).toEqual(300) expect((testAmount as any).amount_).toEqual(300) - expect(testAmount.raw_amount).toEqual({ value: "300.00", precision: 5 }) + expect(testAmount.raw_amount).toEqual({ value: "300", precision: 5 }) }) }) diff --git a/packages/utils/src/dal/mikro-orm/big-number-field.ts b/packages/utils/src/dal/mikro-orm/big-number-field.ts index d58b0ba3e6..a931e392d0 100644 --- a/packages/utils/src/dal/mikro-orm/big-number-field.ts +++ b/packages/utils/src/dal/mikro-orm/big-number-field.ts @@ -1,6 +1,7 @@ -import { BigNumber } from "../../totals/big-number" -import { Property } from "@mikro-orm/core" import { BigNumberInput } from "@medusajs/types" +import { Property } from "@mikro-orm/core" +import { isPresent, trimZeros } from "../../common" +import { BigNumber } from "../../totals/big-number" export function MikroOrmBigNumberProperty( options: Parameters[0] & { @@ -16,21 +17,37 @@ export function MikroOrmBigNumberProperty( return this[targetColumn] }, set(value: BigNumberInput) { + if (options?.nullable && !isPresent(value)) { + this[targetColumn] = 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 = new BigNumber(value, { - precision, - }).raw!.value + + this[rawColumnName].value = trimZeros( + new BigNumber(value, { + precision, + }).raw!.value as string + ) + bigNumber = new BigNumber(this[rawColumnName]) } else { bigNumber = new BigNumber(value) } this[targetColumn] = bigNumber.numeric - this[rawColumnName] = bigNumber.raw + + const raw = bigNumber.raw! + raw.value = trimZeros(raw.value as string) + + this[rawColumnName] = raw }, }) diff --git a/packages/utils/src/totals/__tests__/big-number.spec.ts b/packages/utils/src/totals/__tests__/big-number.spec.ts index 7fd7702f4d..f028aa324d 100644 --- a/packages/utils/src/totals/__tests__/big-number.spec.ts +++ b/packages/utils/src/totals/__tests__/big-number.spec.ts @@ -29,7 +29,15 @@ describe("BigNumber", function () { it("should throw if not correct type", function () { // @ts-ignore expect(() => new BigNumber([])).toThrow( - "Invalid BigNumber value. Should be one of: string, number, BigNumber (bignumber.js), BigNumberRawValue" + "Invalid BigNumber value: . Should be one of: string, number, BigNumber (bignumber.js), BigNumberRawValue" + ) + + expect(() => new BigNumber(null as any)).toThrow( + "Invalid BigNumber value: null. Should be one of: string, number, BigNumber (bignumber.js), BigNumberRawValue" + ) + + expect(() => new BigNumber(undefined as any)).toThrow( + "Invalid BigNumber value: undefined. Should be one of: string, number, BigNumber (bignumber.js), BigNumberRawValue" ) }) }) diff --git a/packages/utils/src/totals/big-number.ts b/packages/utils/src/totals/big-number.ts index 16ea8b6f36..10514cc40f 100644 --- a/packages/utils/src/totals/big-number.ts +++ b/packages/utils/src/totals/big-number.ts @@ -8,61 +8,61 @@ export class BigNumber { private numeric_: number private raw_?: BigNumberRawValue - constructor(rawPrice: BigNumberInput, options?: { precision?: number }) { - this.setRawPriceOrThrow(rawPrice, options) + constructor(rawValue: BigNumberInput, options?: { precision?: number }) { + this.setRawValueOrThrow(rawValue, options) } - setRawPriceOrThrow( - rawPrice: BigNumberInput, + setRawValueOrThrow( + rawValue: BigNumberInput, { precision }: { precision?: number } = {} ) { precision ??= BigNumber.DEFAULT_PRECISION - if (BigNumberJS.isBigNumber(rawPrice)) { + if (BigNumberJS.isBigNumber(rawValue)) { /** * Example: - * const bnUnitPrice = new BigNumberJS("10.99") - * const unitPrice = new BigNumber(bnUnitPrice) + * const bnUnitValue = new BigNumberJS("10.99") + * const unitValue = new BigNumber(bnUnitValue) */ - this.numeric_ = rawPrice.toNumber() + this.numeric_ = rawValue.toNumber() this.raw_ = { - value: rawPrice.toPrecision(precision), + value: rawValue.toPrecision(precision), precision, } - } else if (isString(rawPrice)) { + } else if (isString(rawValue)) { /** - * Example: const unitPrice = "1234.1234" + * Example: const unitValue = "1234.1234" */ - const bigNum = new BigNumberJS(rawPrice) + const bigNum = new BigNumberJS(rawValue) this.numeric_ = bigNum.toNumber() this.raw_ = this.raw_ = { value: bigNum.toPrecision(precision), precision, } - } else if (isBigNumber(rawPrice)) { + } else if (isBigNumber(rawValue)) { /** - * Example: const unitPrice = { value: "1234.1234" } + * Example: const unitValue = { value: "1234.1234" } */ - this.numeric_ = BigNumberJS(rawPrice.value).toNumber() - + const definedPrecision = rawValue.precision ?? precision + this.numeric_ = BigNumberJS(rawValue.value).toNumber() this.raw_ = { - ...rawPrice, - precision, + ...rawValue, + precision: definedPrecision, } - } else if (typeof rawPrice === `number` && !Number.isNaN(rawPrice)) { + } else if (typeof rawValue === `number` && !Number.isNaN(rawValue)) { /** - * Example: const unitPrice = 1234 + * Example: const unitValue = 1234 */ - this.numeric_ = rawPrice as number + this.numeric_ = rawValue as number this.raw_ = { - value: BigNumberJS(rawPrice as number).toPrecision(precision), + value: BigNumberJS(rawValue as number).toPrecision(precision), precision, } } else { throw new Error( - "Invalid BigNumber value. Should be one of: string, number, BigNumber (bignumber.js), BigNumberRawValue" + `Invalid BigNumber value: ${rawValue}. Should be one of: string, number, BigNumber (bignumber.js), BigNumberRawValue` ) } }