Skip to content

Commit

Permalink
Merge pull request #8 from dleitee/convertTo
Browse files Browse the repository at this point in the history
add convertCurrency and fix errors
  • Loading branch information
dleitee committed Feb 21, 2017
2 parents 0d01133 + d0b781b commit 48794f6
Show file tree
Hide file tree
Showing 16 changed files with 114 additions and 96 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![Coverage Status](https://coveralls.io/repos/github/dleitee/walletjs/badge.svg?branch=master)](https://coveralls.io/github/dleitee/walletjs?branch=master)
[![Code Climate](https://codeclimate.com/github/dleitee/walletjs/badges/gpa.svg)](https://codeclimate.com/github/dleitee/walletjs)

**IMPORTANT:** This library doesn't support huge numbers.
**IMPORTANT:** We use [`big.js`](https://github.com/MikeMcl/big.js/) to handle huge numbers.

Now you can handle money without headaches!

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
"jest-babel": "^1.0.1"
},
"dependencies": {
"big.js": "^3.1.3",
"intl": "^1.2.5"
}
}
8 changes: 4 additions & 4 deletions src/helpers/handler.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Money } from '../'

export default (handler, wallet) =>
export default (handler, money) =>
new Money(handler(), {
locale: wallet.locale,
currency: wallet.currency,
currencyFractionals: wallet.currencyFractionals,
locale: money.locale,
currency: money.currency,
currencyFractionals: money.currencyFractionals,
normalized: true,
})
7 changes: 5 additions & 2 deletions src/helpers/normalization.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Big from 'big.js'

const fraction = (currencyFractionals) => {
if (currencyFractionals === 0) {
return 1
Expand All @@ -6,6 +8,7 @@ const fraction = (currencyFractionals) => {
}

export const normalize = (currencyFractionals, value) =>
Math.round(value * fraction(currencyFractionals))
Big(value).times(fraction(currencyFractionals))

export const denormalize = (currencyFractionals, value) => (value / fraction(currencyFractionals))
export const denormalize = (currencyFractionals, value) =>
value.div(fraction(currencyFractionals)).toFixed(currencyFractionals)
8 changes: 4 additions & 4 deletions src/helpers/operations.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { normalize } from './normalization'

export function sum(newValue) {
return normalize(this.currencyFractionals, newValue) + this.value
return normalize(this.currencyFractionals, newValue).add(this.value)
}

export function subtract(newValue) {
return this.value - normalize(this.currencyFractionals, newValue)
return this.value.minus(normalize(this.currencyFractionals, newValue))
}

export function multiply(factor) {
return this.value * factor
return this.value.times(factor)
}

export function divide(factor) {
return this.value / factor
return this.value.div(factor)
}
3 changes: 0 additions & 3 deletions src/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,3 @@ export const getValue = (value, defaultValue) => {

return value
}

export const isNumber = value =>
Object.prototype.toString.call(value) === '[object Number]' && !isNaN(value)
13 changes: 13 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ export default class Wallet {
const current = Money.init(this.getAmount(currency))
return new Wallet(this.amount.set(currency, current.subtract(money).getValue()))
}

/**
* Convert money with currency {from} in currency {true} using {exchangeRate}
* @param {currency} from
* @param {currency} to
* @param {number} exchangeRate
* @return {Wallet}
*/
convertCurrency = (from, to, exchangeRate) => {
const fromAmount = Money.init(this.getAmount(from), { currency: from })
const converted = fromAmount.multiplyBy(exchangeRate).getValue()
return new Wallet(this.amount.set(from, 0).set(to, converted))
}
}

export { Money }
Expand Down
17 changes: 8 additions & 9 deletions src/money.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Intl from 'intl'

import { isNumber, getValue, isValidCurrencyFractionals } from './helpers/utils'
import { getValue, isValidCurrencyFractionals } from './helpers/utils'
import handleMoney from './helpers/handler'
import { normalize, denormalize } from './helpers/normalization'
import { sum, subtract, multiply, divide } from './helpers/operations'
Expand Down Expand Up @@ -28,10 +28,6 @@ export default class Money {
constructor(value, {
locale = DEFAULT_LOCALE, currency = DEFAULT_CURRENCY, normalized = false, ...options
} = {}) {
if (!isNumber(value)) {
throw new Error('Value should be a number')
}

this.currencyFractionals = getValue(options.currencyFractionals, DEFAULT_CURRENCY_FRACTIONALS)
isValidCurrencyFractionals(this.currencyFractionals)
this.DEFAULT_INTL_OPTIONS = {
Expand Down Expand Up @@ -60,10 +56,7 @@ export default class Money {
* @param {number} [currency=USD] - The currency to use in currency formatting.
* @return {Money} The money with value
*/
static fromString = (string = '0', { ...options } = {}) => {
const value = Number.parseFloat(string)
return new Money(value, options)
}
static fromString = (string = '0', { ...options } = {}) => new Money(string, options)

/**
* Adds a value to money
Expand Down Expand Up @@ -99,6 +92,12 @@ export default class Money {
*/
getValue = () => denormalize(this.currencyFractionals, this.value)

/**
* Get money locale
* @return {string}
*/
getLocale = () => this.locale

/**
* Return a formatted currency of Money
* @param {number} [currencyDisplay=symbol] - How to display the currency in currency formatting.
Expand Down
6 changes: 3 additions & 3 deletions tests/currencyfractionals.test.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { Money } from '../src'

test('default currencyFractionals', () => {
expect(Money.init(100)).toMatchObject({ value: 10000 })
expect(Money.init(100).getValue()).toBe('100.00')
})

test('with currencyFractionals = 3', () => {
expect(Money.init(100, { currencyFractionals: 3 })).toMatchObject({ value: 100000 })
expect(Money.init(100, { currencyFractionals: 3 }).getValue()).toBe('100.000')
})

test('with currencyFractionals = 0', () => {
expect(Money.init(100, { currencyFractionals: 0 })).toMatchObject({ value: 100 })
expect(Money.init(100, { currencyFractionals: 0 }).getValue()).toBe('100')
})

test('with currencyFractionals < 0', () => {
Expand Down
8 changes: 5 additions & 3 deletions tests/fromstring.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { Money } from '../src'

test('should be returned a Money object', () => {
expect(Money.fromString('100')).toMatchObject({ value: 10000 })
expect(Money.fromString('100').getValue()).toBe('100.00')
})

test('should be returned a Money object with value = 0', () => {
expect(Money.fromString()).toMatchObject({ value: 0, locale: 'en' })
expect(Money.fromString().getValue()).toBe('0.00')
})

test('should be returned a Money object with value = 100 and locale pt-BR', () => {
expect(Money.fromString(1, { locale: 'pt-BR' })).toMatchObject({ value: 100, locale: 'pt-BR' })
const money = Money.fromString(1, { locale: 'pt-BR' })
expect(money.getValue()).toBe('1.00')
expect(money.getLocale()).toBe('pt-BR')
})
10 changes: 3 additions & 7 deletions tests/init.test.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import { Money } from '../src'

test('should be returned a Money object', () => {
expect(Money.init(100)).toMatchObject({ value: 10000 })
expect(Money.init(100).getValue()).toBe('100.00')
})

test('should be returned a Money object with value = 0', () => {
expect(Money.init()).toMatchObject({ value: 0, locale: 'en' })
expect(Money.init().getValue()).toBe('0.00')
})

test('should be returned a Money object with value = 100 and locale pt-BR', () => {
expect(Money.init(1, { locale: 'pt-BR' })).toMatchObject({ value: 100, locale: 'pt-BR' })
})

test('should be returned an exception when value is a string', () => {
expect(() => Money.init('100')).toThrow(Error)
expect(Money.init(1, { locale: 'pt-BR' }).getValue()).toBe('1.00')
})

test('should be returned an exception when value is a NaN', () => {
Expand Down
17 changes: 0 additions & 17 deletions tests/normalize.test.js

This file was deleted.

70 changes: 35 additions & 35 deletions tests/operations.test.js
Original file line number Diff line number Diff line change
@@ -1,99 +1,99 @@
import { Money } from '../src'

test('[add] should be returned a Money object with value = 200', () => {
const initialValue = 100
const initialValue = '100.00'
const amount = Money.init(initialValue)
expect(amount.add(100).getValue()).toBe(200)
expect(amount.getValue()).toBe(initialValue)
expect(amount.add(100).toString()).toBe('200.00')
expect(amount.toString()).toBe(initialValue)
})

test('[add] should be returned a Money object with small values', () => {
const initialValue = 0.2
const initialValue = '0.20'
const amount = Money.init(initialValue)
expect(amount.add(0.1).getValue()).toBe(0.3)
expect(amount.getValue()).toBe(initialValue)
expect(amount.add(0.1).toString()).toBe('0.30')
expect(amount.toString()).toBe(initialValue)
})

test('[add] should be returned a Money object with very small values', () => {
const initialValue = 0.0000000002
const initialValue = '0.0000000002'
const amount = Money.init(initialValue, { currencyFractionals: 10 })
expect(amount.add(0.0000000001).getValue()).toBe(0.0000000003)
expect(amount.getValue()).toBe(initialValue)
expect(amount.add(0.0000000001).toString()).toBe('0.0000000003')
expect(amount.toString()).toBe(initialValue)
})

test('[add] should be returned a Money object with very very small values', () => {
const initialValue = 0.00000000000000000009
const initialValue = '0.00000000000000000009'
const amount = Money.init(initialValue, { currencyFractionals: 20 })
expect(amount.add('0.00000000000000000009').toString()).toBe('0.00000000000000000018')
expect(amount.getValue()).toBe(initialValue)
expect(amount.toString()).toBe(initialValue)
})

test('[add] should be returned a Money object with bigest values', () => {
const initialValue = 99999999999999
const initialValue = '99999999999999'
const amount = Money.init(initialValue)
expect(amount.add(9999999999999).toString()).toBe('109,999,999,999,998.00')
expect(amount.getValue()).toBe(initialValue)
expect(amount.toString()).toBe('99,999,999,999,999.00')
})

test('[subtract] should be returned a Money object with value = 200', () => {
const initialValue = 300
const initialValue = '300.00'
const amount = Money.init(initialValue)
expect(amount.subtract(100).getValue()).toBe(200)
expect(amount.getValue()).toBe(initialValue)
expect(amount.subtract(100).toString()).toBe('200.00')
expect(amount.toString()).toBe(initialValue)
})

test('[subtract] should be returned a Money object with value = 0.1', () => {
const initialValue = 0.3
const initialValue = '0.30'
const amount = Money.init(initialValue)
expect(amount.subtract(0.2).getValue()).toBe(0.1)
expect(amount.getValue()).toBe(initialValue)
expect(amount.subtract(0.2).toString()).toBe('0.10')
expect(amount.toString()).toBe(initialValue)
})

test('[subtract] should be returned a Money object with value = 0.1', () => {
const initialValue = 10100.03
const initialValue = '10100.03'
const amount = Money.init(initialValue)
expect(amount.subtract(0.2).getValue()).toBe(10099.83)
expect(amount.getValue()).toBe(initialValue)
expect(amount.subtract(0.2).toString()).toBe('10,099.83')
expect(amount.toString()).toBe('10,100.03')
})

test('[multiply] with real values', () => {
const initialValue = 2.02
const initialValue = '2.02'
const amount = Money.init(initialValue, { currencyFractionals: 2 })
expect(amount.multiplyBy(3.15).toString()).toBe('6.36')
expect(amount.getValue()).toBe(initialValue)
expect(amount.toString()).toBe(initialValue)
})

test('[multiply] with smallest values', () => {
const initialValue = 200.000000000002
const initialValue = '200.000000000002'
const amount = Money.init(initialValue, { currencyFractionals: 12 })
expect(amount.multiplyBy(2).toString()).toBe('400.000000000004')
expect(amount.getValue()).toBe(initialValue)
expect(amount.toString()).toBe(initialValue)
})

test('[multiply] with biggest values', () => {
const initialValue = 9999999999999.99
const initialValue = '9999999999999.99'
const amount = Money.init(initialValue)
expect(amount.multiplyBy(10).toString()).toBe('99,999,999,999,999.90')
expect(amount.getValue()).toBe(initialValue)
expect(amount.toString()).toBe('9,999,999,999,999.99')
})

test('[divide] with real values', () => {
const initialValue = 2.02
const initialValue = '2.02'
const amount = Money.init(initialValue, { currencyFractionals: 2 })
expect(amount.divideBy(2).toString()).toBe('1.01')
expect(amount.getValue()).toBe(initialValue)
expect(amount.toString()).toBe(initialValue)
})

test('[divide] with smallest values', () => {
const initialValue = 200.000000000002
const initialValue = '200.000000000002'
const amount = Money.init(initialValue, { currencyFractionals: 12 })
expect(amount.divideBy(2).toString()).toBe('100.000000000001')
expect(amount.getValue()).toBe(initialValue)
expect(amount.toString()).toBe(initialValue)
})

test('[divide] with biggest values', () => {
const initialValue = 999999999999999.00
const initialValue = '99999999999999.00'
const amount = Money.init(initialValue)
expect(amount.divideBy(10).toString()).toBe('99,999,999,999,999.90')
expect(amount.getValue()).toBe(initialValue)
expect(amount.divideBy(10).toString()).toBe('9,999,999,999,999.90')
expect(amount.toString()).toBe('99,999,999,999,999.00')
})
24 changes: 22 additions & 2 deletions tests/wallet.operations.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,34 @@ test('add amount to wallet', () => {
const amount = Money.init(100)
const wallet = Wallet.init()
const addWallet = wallet.add(amount)
expect(addWallet.getAmount(amount.currency)).toBe(100)
expect(addWallet.getAmount(amount.currency)).toBe('100.00')
expect(wallet.getAmount('USD')).toBe(0)
})

test('subtract amount to wallet', () => {
const amount = Money.init(100)
const wallet = Wallet.init()
const subtractWallet = wallet.subtract(amount)
expect(subtractWallet.getAmount(amount.currency)).toBe(-100)
expect(subtractWallet.getAmount(amount.currency)).toBe('-100.00')
expect(wallet.getAmount('USD')).toBe(0)
})

test('convertCurrency from USD to BRL', () => {
const amount = Money.init(100)
const wallet = Wallet.init(amount)
const converted = wallet.convertCurrency('USD', 'BRL', 3.0870)
expect(converted.getAmount('USD')).toBe(0)
expect(converted.getAmount('BRL')).toBe('308.70')
expect(wallet.getAmount('USD')).toBe('100.00')
expect(wallet.getAmount('BRL')).toBe(0)
})

test('convertCurrency from BRL to USD', () => {
const amount = Money.init(100, { currency: 'BRL' })
const wallet = Wallet.init(amount)
const converted = wallet.convertCurrency('BRL', 'USD', 0.3239)
expect(converted.getAmount('BRL')).toBe(0)
expect(converted.getAmount('USD')).toBe('32.39')
expect(wallet.getAmount('BRL')).toBe('100.00')
expect(wallet.getAmount('USD')).toBe(0)
})
Loading

0 comments on commit 48794f6

Please sign in to comment.