From 81c5566f1219707b2bfb3416e94d832170dd2cf0 Mon Sep 17 00:00:00 2001 From: Jon Parise Date: Fri, 9 Dec 2022 12:07:55 -0800 Subject: [PATCH] fix(toDecimal): teach `toDecimal` how to handle negative units (#690) Previously, `toDecimal` would format an amount like -1050 as '-10.-50', when we'd expect '-10.50'. We now use the absolute value of all but the first unit when building the decimal string. --- packages/core/src/api/toDecimal.ts | 17 +++++++++++++---- .../src/api/__tests__/toDecimal.test.ts | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/packages/core/src/api/toDecimal.ts b/packages/core/src/api/toDecimal.ts index 68113312a..d9c38064b 100644 --- a/packages/core/src/api/toDecimal.ts +++ b/packages/core/src/api/toDecimal.ts @@ -1,7 +1,7 @@ import { NON_DECIMAL_CURRENCY_MESSAGE } from '../checks'; import { assert } from '../helpers'; import type { Calculator, Dinero, Formatter, Transformer } from '../types'; -import { computeBase, equal, isArray } from '../utils'; +import { absolute, computeBase, equal, isArray } from '../utils'; import { toUnits } from './toUnits'; @@ -33,7 +33,7 @@ export function toDecimal(calculator: Calculator) { const units = toUnitsFn(dineroObject); - const getDecimalFn = getDecimal(dineroObject.formatter); + const getDecimalFn = getDecimal(calculator, dineroObject.formatter); const value = getDecimalFn(units, scale); if (!transformer) { @@ -44,12 +44,21 @@ export function toDecimal(calculator: Calculator) { }; } -function getDecimal(formatter: Formatter) { +function getDecimal( + calculator: Calculator, + formatter: Formatter +) { + const absoluteFn = absolute(calculator); + return (units: readonly TAmount[], scale: TAmount) => { return units .map((unit, index) => { + const isFirst = index === 0; const isLast = units.length - 1 === index; - const unitAsString = formatter.toString(unit); + + const unitAsString = formatter.toString( + isFirst ? unit : absoluteFn(unit) + ); if (isLast) { return unitAsString.padStart(formatter.toNumber(scale), '0'); diff --git a/packages/dinero.js/src/api/__tests__/toDecimal.test.ts b/packages/dinero.js/src/api/__tests__/toDecimal.test.ts index 56206d24b..1161ddc86 100644 --- a/packages/dinero.js/src/api/__tests__/toDecimal.test.ts +++ b/packages/dinero.js/src/api/__tests__/toDecimal.test.ts @@ -40,6 +40,11 @@ describe('toDecimal', () => { expect(toDecimal(d)).toBe('5.00'); }); + it('returns the negative amount in decimal format', () => { + const d = dinero({ amount: -1050, currency: USD }); + + expect(toDecimal(d)).toEqual('-10.50'); + }); it('uses a custom transformer', () => { const d = dinero({ amount: 1050, currency: USD }); @@ -108,6 +113,11 @@ describe('toDecimal', () => { expect(toDecimal(d)).toBe('5.00'); }); + it('returns the negative amount in decimal format', () => { + const d = dinero({ amount: -1050n, currency: bigintUSD }); + + expect(toDecimal(d)).toEqual('-10.50'); + }); it('uses a custom transformer', () => { const d = dinero({ amount: 1050n, currency: bigintUSD }); @@ -183,6 +193,11 @@ describe('toDecimal', () => { expect(toDecimal(d)).toBe('5.00'); }); + it('returns the negative amount in decimal format', () => { + const d = dinero({ amount: new Big(-1005), currency: bigjsUSD }); + + expect(toDecimal(d)).toEqual('-10.05'); + }); it('uses a custom transformer', () => { const d = dinero({ amount: new Big(1050), currency: bigjsUSD });