From 17405058b03f96e940deef699f715d74b00ebce8 Mon Sep 17 00:00:00 2001 From: Robert Moelker Date: Wed, 24 Dec 2025 09:29:44 +0100 Subject: [PATCH 1/2] feat: enable HighPrecisionMoney chore: add changeset and refactor refactor: formatting refactor: formatting fix: clear the outdated preciseAmount on required CentPrecisionMoney types refactor: validate if calculcated amount matches the external price --- .changeset/cold-phones-film.md | 5 + src/repositories/cart/actions.ts | 29 ++-- src/repositories/cart/helpers.ts | 14 +- src/repositories/cart/index.ts | 19 ++- src/repositories/helpers.ts | 124 +++++++++++++-- src/repositories/order/index.ts | 9 +- src/repositories/shipping-method/helpers.ts | 6 +- src/services/cart.test.ts | 162 ++++++++++++++++++++ 8 files changed, 322 insertions(+), 46 deletions(-) create mode 100644 .changeset/cold-phones-film.md diff --git a/.changeset/cold-phones-film.md b/.changeset/cold-phones-film.md new file mode 100644 index 00000000..c4f78d84 --- /dev/null +++ b/.changeset/cold-phones-film.md @@ -0,0 +1,5 @@ +--- +"@labdigital/commercetools-mock": minor +--- + +Add HighPrecisionMoney support diff --git a/src/repositories/cart/actions.ts b/src/repositories/cart/actions.ts index 4fc62c33..c3b39008 100644 --- a/src/repositories/cart/actions.ts +++ b/src/repositories/cart/actions.ts @@ -52,6 +52,7 @@ import type { Writable } from "#src/types.ts"; import type { UpdateHandlerInterface } from "../abstract.ts"; import { AbstractUpdateHandler, type RepositoryContext } from "../abstract.ts"; import { + calculateMoneyTotalCentAmount, createAddress, createCentPrecisionMoney, createCustomFields, @@ -183,6 +184,10 @@ export class CartUpdateHandler `No valid price found for ${productId} for country ${resource.country} and currency ${currency}`, ); } + const totalPrice = createCentPrecisionMoney({ + currencyCode: price.value.currencyCode, + centAmount: calculateMoneyTotalCentAmount(price.value, quantity), + }); resource.lineItems.push({ id: uuidv4(), key, @@ -196,11 +201,7 @@ export class CartUpdateHandler price: price, taxedPricePortions: [], perMethodTaxRate: [], - totalPrice: { - ...price.value, - type: "centPrecision", - centAmount: price.value.centAmount * quantity, - }, + totalPrice, quantity, discountedPricePerQuantity: [], lineItemMode: "Standard", @@ -427,8 +428,11 @@ export class CartUpdateHandler } customLineItem.quantity = quantity; customLineItem.totalPrice = createCentPrecisionMoney({ - ...customLineItem.money, - centAmount: (customLineItem.money.centAmount ?? 0) * quantity, + currencyCode: customLineItem.money.currencyCode, + centAmount: calculateMoneyTotalCentAmount( + customLineItem.money, + quantity, + ), }); }; @@ -470,8 +474,11 @@ export class CartUpdateHandler } customLineItem.money = createTypedMoney(money); customLineItem.totalPrice = createCentPrecisionMoney({ - ...money, - centAmount: (money.centAmount ?? 0) * customLineItem.quantity, + currencyCode: money.currencyCode, + centAmount: calculateMoneyTotalCentAmount( + money, + customLineItem.quantity, + ), }); }; @@ -608,7 +615,7 @@ export class CartUpdateHandler shippingMethodName, price: createCentPrecisionMoney(shippingRate.price), shippingRate: { - price: createTypedMoney(shippingRate.price), + price: createCentPrecisionMoney(shippingRate.price), tiers: [], }, taxCategory: tax @@ -798,7 +805,7 @@ export class CartUpdateHandler const lineItemTotal = calculateLineItemTotalPrice(lineItem); lineItem.totalPrice = createCentPrecisionMoney({ - ...lineItem.price!.value, + currencyCode: lineItem.price!.value.currencyCode, centAmount: lineItemTotal, }); resource.totalPrice.centAmount = calculateCartTotalPrice(resource); diff --git a/src/repositories/cart/helpers.ts b/src/repositories/cart/helpers.ts index d96a036b..813dbd5b 100644 --- a/src/repositories/cart/helpers.ts +++ b/src/repositories/cart/helpers.ts @@ -11,6 +11,7 @@ import { v4 as uuidv4 } from "uuid"; import { calculateTaxedPrice } from "#src/lib/tax.ts"; import type { AbstractStorage } from "#src/storage/abstract.ts"; import { + calculateMoneyTotalCentAmount, createCentPrecisionMoney, createCustomFields, createTypedMoney, @@ -40,8 +41,13 @@ export const selectPrice = ({ }); }; -export const calculateLineItemTotalPrice = (lineItem: LineItem): number => - lineItem.price?.value.centAmount * lineItem.quantity; +export const calculateLineItemTotalPrice = (lineItem: LineItem): number => { + if (!lineItem.price?.value) { + return 0; + } + + return calculateMoneyTotalCentAmount(lineItem.price.value, lineItem.quantity); +}; export const calculateCartTotalPrice = (cart: Cart): number => { const lineItemsTotal = cart.lineItems.reduce( @@ -83,8 +89,8 @@ export const createCustomLineItemFromDraft = ( } const totalPrice = createCentPrecisionMoney({ - ...draft.money, - centAmount: (draft.money.centAmount ?? 0) * quantity, + currencyCode: draft.money.currencyCode, + centAmount: calculateMoneyTotalCentAmount(draft.money, quantity), }); const taxedPrice = taxCategory diff --git a/src/repositories/cart/index.ts b/src/repositories/cart/index.ts index 75208c43..dea977b8 100644 --- a/src/repositories/cart/index.ts +++ b/src/repositories/cart/index.ts @@ -24,7 +24,12 @@ import { AbstractResourceRepository, type RepositoryContext, } from "../abstract.ts"; -import { createAddress, createCustomFields } from "../helpers.ts"; +import { + calculateMoneyTotalCentAmount, + createAddress, + createCentPrecisionMoney, + createCustomFields, +} from "../helpers.ts"; import { CartUpdateHandler } from "./actions.ts"; import { calculateCartTotalPrice, @@ -238,6 +243,11 @@ export class CartRepository extends AbstractResourceRepository<"cart"> { ); } + const totalPrice = createCentPrecisionMoney({ + currencyCode: price.value.currencyCode, + centAmount: calculateMoneyTotalCentAmount(price.value, quant), + }); + return { id: uuidv4(), productId: product.id, @@ -247,12 +257,7 @@ export class CartRepository extends AbstractResourceRepository<"cart"> { name: product.masterData.current.name, variant, price: price, - totalPrice: { - type: "centPrecision", - currencyCode: price.value.currencyCode, - fractionDigits: price.value.fractionDigits, - centAmount: price.value.centAmount * quant, - }, + totalPrice, taxedPricePortions: [], perMethodTaxRate: [], quantity: quant, diff --git a/src/repositories/helpers.ts b/src/repositories/helpers.ts index 0cf9ec99..89716fb7 100644 --- a/src/repositories/helpers.ts +++ b/src/repositories/helpers.ts @@ -28,6 +28,7 @@ import type { StoreReference, StoreResourceIdentifier, Type, + TypedMoney, } from "@commercetools/platform-sdk"; import { Decimal } from "decimal.js/decimal"; import type { Request } from "express"; @@ -109,10 +110,9 @@ export const roundDecimal = (decimal: Decimal, roundingMode: RoundingMode) => { } }; -export const createCentPrecisionMoney = (value: _Money): CentPrecisionMoney => { +export const getCurrencyFractionDigits = (currencyCode: string): number => { // Taken from https://docs.adyen.com/development-resources/currency-codes - let fractionDigits = 2; - switch (value.currencyCode.toUpperCase()) { + switch (currencyCode.toUpperCase()) { case "BHD": case "IQD": case "JOD": @@ -120,8 +120,7 @@ export const createCentPrecisionMoney = (value: _Money): CentPrecisionMoney => { case "LYD": case "OMR": case "TND": - fractionDigits = 3; - break; + return 3; case "CVE": case "DJF": case "GNF": @@ -137,29 +136,120 @@ export const createCentPrecisionMoney = (value: _Money): CentPrecisionMoney => { case "XAF": case "XOF": case "XPF": - fractionDigits = 0; - break; + return 0; default: - fractionDigits = 2; + return 2; } +}; + +export const calculateCentAmountFromPreciseAmount = ( + preciseAmount: number, + fractionDigits: number, + currencyCode: string, + roundingMode: RoundingMode = "HalfEven", +): number => { + const centFractionDigits = getCurrencyFractionDigits(currencyCode); + const diff = fractionDigits - centFractionDigits; + const scale = new Decimal(10).pow(Math.abs(diff)); + const decimal = + diff >= 0 + ? new Decimal(preciseAmount).div(scale) + : new Decimal(preciseAmount).mul(scale); + + return roundDecimal(decimal, roundingMode).toNumber(); +}; - if ((value as HighPrecisionMoney & HighPrecisionMoneyDraft).preciseAmount) { - throw new Error("HighPrecisionMoney not supported"); +export const createCentPrecisionMoney = (value: _Money): CentPrecisionMoney => { + const fractionDigits = getCurrencyFractionDigits(value.currencyCode); + const preciseValue = value as HighPrecisionMoney & HighPrecisionMoneyDraft; + let centAmount: number; + + centAmount = value.centAmount ?? 0; + + if ( + preciseValue.preciseAmount !== undefined && + preciseValue.fractionDigits !== undefined + ) { + centAmount = calculateCentAmountFromPreciseAmount( + preciseValue.preciseAmount, + preciseValue.fractionDigits, + value.currencyCode, + "HalfEven", + ); } return { type: "centPrecision", - // centAmont is only optional on HighPrecisionMoney, so this should never - // fallback to 0 - centAmount: value.centAmount ?? 0, + centAmount, currencyCode: value.currencyCode, - fractionDigits: fractionDigits, + fractionDigits, }; }; -export const createTypedMoney = (value: _Money): CentPrecisionMoney => { - const result = createCentPrecisionMoney(value); - return result; +export const createHighPrecisionMoney = ( + value: HighPrecisionMoney | HighPrecisionMoneyDraft, +): HighPrecisionMoney => { + if (value.preciseAmount === undefined) { + throw new Error("HighPrecisionMoney requires preciseAmount"); + } + + if (value.fractionDigits === undefined) { + throw new Error("HighPrecisionMoney requires fractionDigits"); + } + + const centAmount = + value.centAmount ?? + calculateCentAmountFromPreciseAmount( + value.preciseAmount, + value.fractionDigits, + value.currencyCode, + "HalfEven", + ); + + return { + type: "highPrecision", + centAmount, + currencyCode: value.currencyCode, + fractionDigits: value.fractionDigits, + preciseAmount: value.preciseAmount, + }; +}; + +export const createTypedMoney = (value: _Money): TypedMoney => { + const preciseValue = value as HighPrecisionMoney & HighPrecisionMoneyDraft; + if ( + ("type" in value && value.type === "highPrecision") || + preciseValue.preciseAmount !== undefined + ) { + return createHighPrecisionMoney( + value as HighPrecisionMoney | HighPrecisionMoneyDraft, + ); + } + + return createCentPrecisionMoney(value); +}; + +export const calculateMoneyTotalCentAmount = ( + money: _Money, + quantity: number, + roundingMode: RoundingMode = "HalfEven", +): number => { + const preciseValue = money as HighPrecisionMoney & HighPrecisionMoneyDraft; + + if ( + preciseValue.preciseAmount === undefined || + preciseValue.fractionDigits === undefined + ) { + return (money.centAmount ?? 0) * quantity; + } + + const totalPrecise = new Decimal(preciseValue.preciseAmount).mul(quantity); + return calculateCentAmountFromPreciseAmount( + totalPrecise.toNumber(), + preciseValue.fractionDigits, + money.currencyCode, + roundingMode, + ); }; export const resolveStoreReference = ( diff --git a/src/repositories/order/index.ts b/src/repositories/order/index.ts index 23b285ce..abfbc35f 100644 --- a/src/repositories/order/index.ts +++ b/src/repositories/order/index.ts @@ -35,6 +35,7 @@ import type { Writable } from "#src/types.ts"; import type { RepositoryContext } from "../abstract.ts"; import { AbstractResourceRepository, type QueryParams } from "../abstract.ts"; import { + calculateMoneyTotalCentAmount, createAddress, createCentPrecisionMoney, createCustomFields, @@ -260,8 +261,8 @@ export class OrderRepository extends AbstractResourceRepository<"order"> { const quantity = draft.quantity ?? 1; const totalPrice = createCentPrecisionMoney({ - ...draft.price.value, - centAmount: (draft.price.value.centAmount ?? 0) * quantity, + currencyCode: draft.price.value.currencyCode, + centAmount: calculateMoneyTotalCentAmount(draft.price.value, quantity), }); const lineItem: LineItem = { @@ -306,8 +307,8 @@ export class OrderRepository extends AbstractResourceRepository<"order"> { ): CustomLineItem { const quantity = draft.quantity ?? 1; const totalPrice = createCentPrecisionMoney({ - ...draft.money, - centAmount: (draft.money.centAmount ?? 0) * quantity, + currencyCode: draft.money.currencyCode, + centAmount: calculateMoneyTotalCentAmount(draft.money, quantity), }); const lineItem: CustomLineItem = { diff --git a/src/repositories/shipping-method/helpers.ts b/src/repositories/shipping-method/helpers.ts index 2c7db961..9872ddb0 100644 --- a/src/repositories/shipping-method/helpers.ts +++ b/src/repositories/shipping-method/helpers.ts @@ -2,12 +2,12 @@ import type { ShippingRate, ShippingRateDraft, } from "@commercetools/platform-sdk"; -import { createTypedMoney } from "../helpers.ts"; +import { createCentPrecisionMoney } from "../helpers.ts"; export const transformShippingRate = ( rate: ShippingRateDraft, ): ShippingRate => ({ - price: createTypedMoney(rate.price), - freeAbove: rate.freeAbove && createTypedMoney(rate.freeAbove), + price: createCentPrecisionMoney(rate.price), + freeAbove: rate.freeAbove && createCentPrecisionMoney(rate.freeAbove), tiers: rate.tiers || [], }); diff --git a/src/services/cart.test.ts b/src/services/cart.test.ts index 4eb81924..e2b3e739 100644 --- a/src/services/cart.test.ts +++ b/src/services/cart.test.ts @@ -3,6 +3,7 @@ import type { Address, Cart, CentPrecisionMoney, + HighPrecisionMoneyDraft, ProductDraft, ShippingMethod, ShippingMethodDraft, @@ -14,6 +15,7 @@ import type { import supertest from "supertest"; import { afterEach, beforeEach, describe, expect, test } from "vitest"; import { customerDraftFactory } from "#src/testing/customer.ts"; +import { calculateMoneyTotalCentAmount } from "#src/repositories/helpers.ts"; import { CommercetoolsMock } from "../index.ts"; describe("Carts Query", () => { @@ -786,6 +788,166 @@ describe("Cart Update Actions", () => { ); }); + test("setLineItemPrice supports high precision external price", async () => { + const product = await supertest(ctMock.app) + .post("/dummy/products") + .send(productDraft) + .then((x) => x.body); + + assert(product, "product not created"); + + const baseCartResponse = await supertest(ctMock.app) + .post("/dummy/carts") + .send({ currency: "EUR" }); + expect(baseCartResponse.status).toBe(201); + const baseCart = baseCartResponse.body as Cart; + + const addLineItemResponse = await supertest(ctMock.app) + .post(`/dummy/carts/${baseCart.id}`) + .send({ + version: baseCart.version, + actions: [ + { + action: "addLineItem", + sku: product.masterData.current.masterVariant.sku, + quantity: 2, + }, + ], + }); + expect(addLineItemResponse.status).toBe(200); + const cartWithLineItem = addLineItemResponse.body as Cart; + const lineItem = cartWithLineItem.lineItems[0]; + assert(lineItem, "lineItem not created"); + + const externalPrice: HighPrecisionMoneyDraft = { + type: "highPrecision", + currencyCode: "EUR", + fractionDigits: 3, + preciseAmount: 1015, + }; + const expectedUnitCentAmount = calculateMoneyTotalCentAmount( + externalPrice, + 1, + ); + const expectedTotalCentAmount = calculateMoneyTotalCentAmount( + externalPrice, + 2, + ); + + const response = await supertest(ctMock.app) + .post(`/dummy/carts/${cartWithLineItem.id}`) + .send({ + version: cartWithLineItem.version, + actions: [ + { + action: "setLineItemPrice", + lineItemId: lineItem.id, + externalPrice, + }, + ], + }); + + expect(response.status).toBe(200); + expect(response.body.version).toBe(cartWithLineItem.version + 1); + expect(response.body.lineItems).toHaveLength(1); + + const updatedLineItem = response.body.lineItems[0]; + expect(updatedLineItem.priceMode).toBe("ExternalPrice"); + expect(updatedLineItem.price.value.type).toBe("highPrecision"); + expect(updatedLineItem.price.value.currencyCode).toBe( + externalPrice.currencyCode, + ); + expect(updatedLineItem.price.value.fractionDigits).toBe( + externalPrice.fractionDigits, + ); + expect(updatedLineItem.price.value.preciseAmount).toBe( + externalPrice.preciseAmount, + ); + expect(updatedLineItem.price.value.centAmount).toBe(expectedUnitCentAmount); + expect(updatedLineItem.totalPrice.centAmount).toBe(expectedTotalCentAmount); + expect(response.body.totalPrice.centAmount).toBe(expectedTotalCentAmount); + }); + + test("setLineItemPrice supports high precision external price with fractionDigits 5", async () => { + const product = await supertest(ctMock.app) + .post("/dummy/products") + .send(productDraft) + .then((x) => x.body); + + assert(product, "product not created"); + + const baseCartResponse = await supertest(ctMock.app) + .post("/dummy/carts") + .send({ currency: "EUR" }); + expect(baseCartResponse.status).toBe(201); + const baseCart = baseCartResponse.body as Cart; + + const addLineItemResponse = await supertest(ctMock.app) + .post(`/dummy/carts/${baseCart.id}`) + .send({ + version: baseCart.version, + actions: [ + { + action: "addLineItem", + sku: product.masterData.current.masterVariant.sku, + quantity: 2, + }, + ], + }); + expect(addLineItemResponse.status).toBe(200); + const cartWithLineItem = addLineItemResponse.body as Cart; + const lineItem = cartWithLineItem.lineItems[0]; + assert(lineItem, "lineItem not created"); + + const externalPrice: HighPrecisionMoneyDraft = { + type: "highPrecision", + currencyCode: "EUR", + fractionDigits: 5, + preciseAmount: 101499, + }; + const expectedUnitCentAmount = calculateMoneyTotalCentAmount( + externalPrice, + 1, + ); + const expectedTotalCentAmount = calculateMoneyTotalCentAmount( + externalPrice, + 2, + ); + + const response = await supertest(ctMock.app) + .post(`/dummy/carts/${cartWithLineItem.id}`) + .send({ + version: cartWithLineItem.version, + actions: [ + { + action: "setLineItemPrice", + lineItemId: lineItem.id, + externalPrice, + }, + ], + }); + + expect(response.status).toBe(200); + expect(response.body.version).toBe(cartWithLineItem.version + 1); + expect(response.body.lineItems).toHaveLength(1); + + const updatedLineItem = response.body.lineItems[0]; + expect(updatedLineItem.priceMode).toBe("ExternalPrice"); + expect(updatedLineItem.price.value.type).toBe("highPrecision"); + expect(updatedLineItem.price.value.currencyCode).toBe( + externalPrice.currencyCode, + ); + expect(updatedLineItem.price.value.fractionDigits).toBe( + externalPrice.fractionDigits, + ); + expect(updatedLineItem.price.value.preciseAmount).toBe( + externalPrice.preciseAmount, + ); + expect(updatedLineItem.price.value.centAmount).toBe(expectedUnitCentAmount); + expect(updatedLineItem.totalPrice.centAmount).toBe(expectedTotalCentAmount); + expect(response.body.totalPrice.centAmount).toBe(expectedTotalCentAmount); + }); + test("setLineItemPrice fails when the money uses another currency", async () => { const product = await supertest(ctMock.app) .post("/dummy/products") From d8bbd3a633d996f57dae8ba497fee6895176574f Mon Sep 17 00:00:00 2001 From: Robert Moelker Date: Wed, 24 Dec 2025 12:47:01 +0100 Subject: [PATCH 2/2] chore: add some additional unit tests and handle auto-format --- src/repositories/helpers.test.ts | 115 ++++++++++++++++++++++++++++++- src/services/cart.test.ts | 2 +- 2 files changed, 114 insertions(+), 3 deletions(-) diff --git a/src/repositories/helpers.test.ts b/src/repositories/helpers.test.ts index cd917751..d03a4718 100644 --- a/src/repositories/helpers.test.ts +++ b/src/repositories/helpers.test.ts @@ -1,7 +1,16 @@ -import type { BaseAddress } from "@commercetools/platform-sdk"; +import type { + BaseAddress, + HighPrecisionMoneyDraft, + Money, +} from "@commercetools/platform-sdk"; import { describe, expect, test } from "vitest"; import { InMemoryStorage } from "#src/storage/index.ts"; -import { createAddress } from "./helpers.ts"; +import { + calculateCentAmountFromPreciseAmount, + calculateMoneyTotalCentAmount, + createAddress, + createHighPrecisionMoney, +} from "./helpers.ts"; describe("Helpers", () => { const storage = new InMemoryStorage(); @@ -22,4 +31,106 @@ describe("Helpers", () => { expect(result?.id).toMatch(/^[a-z0-9]{8}$/); }); }); + + describe("calculateCentAmountFromPreciseAmount", () => { + test("rounds half even by default", () => { + const centAmount = calculateCentAmountFromPreciseAmount(1005, 3, "EUR"); + expect(centAmount).toBe(100); + }); + + test("supports HalfUp and HalfDown rounding modes", () => { + const halfUp = calculateCentAmountFromPreciseAmount( + 1005, + 3, + "EUR", + "HalfUp", + ); + const halfDown = calculateCentAmountFromPreciseAmount( + 1005, + 3, + "EUR", + "HalfDown", + ); + expect(halfUp).toBe(101); + expect(halfDown).toBe(100); + }); + + test("uses currency fraction digits for conversion", () => { + const centAmount = calculateCentAmountFromPreciseAmount(1234, 2, "JPY"); + expect(centAmount).toBe(12); + }); + }); + + describe("createHighPrecisionMoney", () => { + test("computes centAmount when missing", () => { + const money = createHighPrecisionMoney({ + type: "highPrecision", + currencyCode: "EUR", + fractionDigits: 3, + preciseAmount: 1015, + }); + + expect(money.type).toBe("highPrecision"); + expect(money.centAmount).toBe(102); + expect(money.preciseAmount).toBe(1015); + expect(money.fractionDigits).toBe(3); + }); + + test("throws when preciseAmount is missing", () => { + expect(() => + createHighPrecisionMoney({ + type: "highPrecision", + currencyCode: "EUR", + fractionDigits: 3, + } as HighPrecisionMoneyDraft), + ).toThrow("HighPrecisionMoney requires preciseAmount"); + }); + + test("throws when fractionDigits is missing", () => { + expect(() => + createHighPrecisionMoney({ + type: "highPrecision", + currencyCode: "EUR", + preciseAmount: 1015, + } as HighPrecisionMoneyDraft), + ).toThrow("HighPrecisionMoney requires fractionDigits"); + }); + }); + + describe("calculateMoneyTotalCentAmount", () => { + test("uses preciseAmount when available", () => { + const money: HighPrecisionMoneyDraft = { + type: "highPrecision", + currencyCode: "EUR", + fractionDigits: 5, + preciseAmount: 101499, + centAmount: 101, + }; + + const totalCentAmount = calculateMoneyTotalCentAmount(money, 2); + expect(totalCentAmount).toBe(203); + }); + + test("falls back to centAmount when preciseAmount is missing", () => { + const money: Money = { + currencyCode: "EUR", + centAmount: 123, + }; + + const totalCentAmount = calculateMoneyTotalCentAmount(money, 3); + expect(totalCentAmount).toBe(369); + }); + + test("supports rounding mode overrides", () => { + const money: HighPrecisionMoneyDraft = { + type: "highPrecision", + currencyCode: "EUR", + fractionDigits: 3, + preciseAmount: 1005, + }; + + const totalCentAmount = calculateMoneyTotalCentAmount(money, 1, "HalfUp"); + expect(totalCentAmount).toBe(101); + }); + }); }); diff --git a/src/services/cart.test.ts b/src/services/cart.test.ts index e2b3e739..9fed9312 100644 --- a/src/services/cart.test.ts +++ b/src/services/cart.test.ts @@ -14,8 +14,8 @@ import type { } from "@commercetools/platform-sdk"; import supertest from "supertest"; import { afterEach, beforeEach, describe, expect, test } from "vitest"; -import { customerDraftFactory } from "#src/testing/customer.ts"; import { calculateMoneyTotalCentAmount } from "#src/repositories/helpers.ts"; +import { customerDraftFactory } from "#src/testing/customer.ts"; import { CommercetoolsMock } from "../index.ts"; describe("Carts Query", () => {