Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cold-phones-film.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@labdigital/commercetools-mock": minor
---

Add HighPrecisionMoney support
29 changes: 18 additions & 11 deletions src/repositories/cart/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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",
Expand Down Expand Up @@ -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,
),
});
};

Expand Down Expand Up @@ -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,
),
});
};

Expand Down Expand Up @@ -608,7 +615,7 @@ export class CartUpdateHandler
shippingMethodName,
price: createCentPrecisionMoney(shippingRate.price),
shippingRate: {
price: createTypedMoney(shippingRate.price),
price: createCentPrecisionMoney(shippingRate.price),
tiers: [],
},
taxCategory: tax
Expand Down Expand Up @@ -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);
Expand Down
14 changes: 10 additions & 4 deletions src/repositories/cart/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down
19 changes: 12 additions & 7 deletions src/repositories/cart/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down
115 changes: 113 additions & 2 deletions src/repositories/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -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);
});
});
});
Loading