mirror of
https://github.com/bitwarden/clients.git
synced 2026-07-04 21:05:54 +08:00
[PM-34223] discounts rounding bug fix (#19811)
This commit is contained in:
parent
9b7452e8e2
commit
13bf3bc3bf
@ -537,6 +537,29 @@ describe("CartSummaryComponent", () => {
|
||||
).toContain("-$10.00");
|
||||
});
|
||||
|
||||
it("should compute total consistent with displayed rounded line items when chained discounts produce fractional cents", () => {
|
||||
// Reproduces: 1x $47.88 seat with 20% off → $10 flat → 5% off, tax $2.15
|
||||
// Raw math: 47.88 - 9.576 - 10 - 1.4152 + 2.15 = 29.0388 → $29.04 (wrong)
|
||||
// Rounded: 47.88 - 9.58 - 10 - 1.42 + 2.15 = 29.03 (correct)
|
||||
const cart: Cart = {
|
||||
passwordManager: {
|
||||
seats: { quantity: 1, translationKey: "members", cost: 47.88 },
|
||||
},
|
||||
cadence: "annually",
|
||||
estimatedTax: 2.15,
|
||||
discounts: [
|
||||
{ type: DiscountTypes.PercentOff, value: 20 },
|
||||
{ type: DiscountTypes.AmountOff, value: 10 },
|
||||
{ type: DiscountTypes.PercentOff, value: 5 },
|
||||
],
|
||||
};
|
||||
fixture.componentRef.setInput("cart", cart);
|
||||
fixture.detectChanges();
|
||||
|
||||
const bottomTotal = fixture.debugElement.query(By.css("[data-testid='final-total']"));
|
||||
expect(bottomTotal.nativeElement.textContent).toContain("$29.03");
|
||||
});
|
||||
|
||||
it("should apply cascading subtotal when multiple percent-off discounts are stacked", () => {
|
||||
// Arrange
|
||||
const cartWithStackedPercents: Cart = {
|
||||
|
||||
@ -34,6 +34,30 @@ describe("getAmount", () => {
|
||||
const discount: Discount = { type: DiscountTypes.PercentOff, value: 100 };
|
||||
expect(getAmount(discount, 200)).toBe(200);
|
||||
});
|
||||
|
||||
it("should round result to 2 decimal places when percent produces fractional cents", () => {
|
||||
const discount: Discount = { type: DiscountTypes.PercentOff, value: 20 };
|
||||
// 20% of $47.88 = $9.576 → rounds to $9.58
|
||||
expect(getAmount(discount, 47.88)).toBe(9.58);
|
||||
});
|
||||
|
||||
it("should round result when applied to a running subtotal with fractional cents", () => {
|
||||
const discount: Discount = { type: DiscountTypes.PercentOff, value: 5 };
|
||||
// 5% of $28.304 = $1.4152 → rounds to $1.42
|
||||
expect(getAmount(discount, 28.304)).toBe(1.42);
|
||||
});
|
||||
|
||||
it("should round half-cent up", () => {
|
||||
const discount: Discount = { type: DiscountTypes.PercentOff, value: 50 };
|
||||
// 50% of $0.01 = $0.005 → rounds to $0.01
|
||||
expect(getAmount(discount, 0.01)).toBe(0.01);
|
||||
});
|
||||
|
||||
it("should round down when fractional cent is less than half", () => {
|
||||
const discount: Discount = { type: DiscountTypes.PercentOff, value: 20 };
|
||||
// 20% of $47.82 = $9.564 → rounds down to $9.56
|
||||
expect(getAmount(discount, 47.82)).toBe(9.56);
|
||||
});
|
||||
});
|
||||
|
||||
describe("AmountOff", () => {
|
||||
|
||||
@ -23,7 +23,7 @@ export const getAmount = (discount: Discount, baseAmount: number): number => {
|
||||
switch (discount.type) {
|
||||
case DiscountTypes.PercentOff: {
|
||||
const percentage = discount.value < 1 ? discount.value : discount.value / 100;
|
||||
return baseAmount * percentage;
|
||||
return Math.round(baseAmount * percentage * 100) / 100;
|
||||
}
|
||||
case DiscountTypes.AmountOff:
|
||||
return discount.value;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user