shopify-money 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.document +5 -0
- data/.gitignore +51 -0
- data/.rspec +1 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +20 -0
- data/README.md +156 -0
- data/Rakefile +42 -0
- data/bin/console +14 -0
- data/circle.yml +13 -0
- data/config/currency_historic.json +157 -0
- data/config/currency_iso.json +2642 -0
- data/config/currency_non_iso.json +82 -0
- data/dev.yml +9 -0
- data/lib/money.rb +10 -0
- data/lib/money/accounting_money_parser.rb +8 -0
- data/lib/money/core_extensions.rb +18 -0
- data/lib/money/currency.rb +59 -0
- data/lib/money/currency/loader.rb +26 -0
- data/lib/money/deprecations.rb +18 -0
- data/lib/money/helpers.rb +71 -0
- data/lib/money/money.rb +408 -0
- data/lib/money/money_parser.rb +152 -0
- data/lib/money/null_currency.rb +35 -0
- data/lib/money/version.rb +3 -0
- data/lib/money_accessor.rb +32 -0
- data/lib/money_column.rb +3 -0
- data/lib/money_column/active_record_hooks.rb +95 -0
- data/lib/money_column/active_record_type.rb +6 -0
- data/lib/money_column/railtie.rb +7 -0
- data/money.gemspec +27 -0
- data/spec/accounting_money_parser_spec.rb +204 -0
- data/spec/core_extensions_spec.rb +44 -0
- data/spec/currency/loader_spec.rb +21 -0
- data/spec/currency_spec.rb +113 -0
- data/spec/helpers_spec.rb +103 -0
- data/spec/money_accessor_spec.rb +86 -0
- data/spec/money_column_spec.rb +298 -0
- data/spec/money_parser_spec.rb +355 -0
- data/spec/money_spec.rb +853 -0
- data/spec/null_currency_spec.rb +46 -0
- data/spec/schema.rb +9 -0
- data/spec/spec_helper.rb +74 -0
- metadata +196 -0
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Parse an amount from a string
|
4
|
+
class MoneyParser
|
5
|
+
class MoneyFormatError < ArgumentError; end
|
6
|
+
|
7
|
+
MARKS = %w[. , · ’ ˙ '] + [' ']
|
8
|
+
|
9
|
+
ESCAPED_MARKS = Regexp.escape(MARKS.join)
|
10
|
+
ESCAPED_NON_SPACE_MARKS = Regexp.escape((MARKS - [' ']).join)
|
11
|
+
ESCAPED_NON_DOT_MARKS = Regexp.escape((MARKS - ['.']).join)
|
12
|
+
ESCAPED_NON_COMMA_MARKS = Regexp.escape((MARKS - [',']).join)
|
13
|
+
|
14
|
+
NUMERIC_REGEX = /(
|
15
|
+
[\+\-]?
|
16
|
+
[\d#{ESCAPED_NON_SPACE_MARKS}][\d#{ESCAPED_MARKS}]*
|
17
|
+
)/ix
|
18
|
+
|
19
|
+
# 1,234,567.89
|
20
|
+
DOT_DECIMAL_REGEX = /\A
|
21
|
+
[\+\-]?
|
22
|
+
(?:
|
23
|
+
(?:\d+)
|
24
|
+
(?:[#{ESCAPED_NON_DOT_MARKS}]\d{3})+
|
25
|
+
(?:\.\d{2,})?
|
26
|
+
)
|
27
|
+
\z/ix
|
28
|
+
|
29
|
+
# 1.234.567,89
|
30
|
+
COMMA_DECIMAL_REGEX = /\A
|
31
|
+
[\+\-]?
|
32
|
+
(?:
|
33
|
+
(?:\d+)
|
34
|
+
(?:[#{ESCAPED_NON_COMMA_MARKS}]\d{3})+
|
35
|
+
(?:\,\d{2,})?
|
36
|
+
)
|
37
|
+
\z/ix
|
38
|
+
|
39
|
+
# 12,34,567.89
|
40
|
+
INDIAN_NUMERIC_REGEX = /\A
|
41
|
+
[\+\-]?
|
42
|
+
(?:
|
43
|
+
(?:\d+)
|
44
|
+
(?:\,\d{2})+
|
45
|
+
(?:\,\d{3})
|
46
|
+
(?:\.\d{2})?
|
47
|
+
)
|
48
|
+
\z/ix
|
49
|
+
|
50
|
+
# 1,1123,4567.89
|
51
|
+
CHINESE_NUMERIC_REGEX = /\A
|
52
|
+
[\+\-]?
|
53
|
+
(?:
|
54
|
+
(?:\d+)
|
55
|
+
(?:\,\d{4})+
|
56
|
+
(?:\.\d{2})?
|
57
|
+
)
|
58
|
+
\z/ix
|
59
|
+
|
60
|
+
def self.parse(input, currency = nil, **options)
|
61
|
+
new.parse(input, currency, **options)
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse(input, currency = nil, strict: false)
|
65
|
+
currency = Money::Helpers.value_to_currency(currency)
|
66
|
+
amount = extract_money(input.to_s, currency, strict)
|
67
|
+
Money.new(amount, currency)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def extract_money(input, currency, strict)
|
73
|
+
if input.empty?
|
74
|
+
return '0'
|
75
|
+
end
|
76
|
+
|
77
|
+
number = input.scan(NUMERIC_REGEX).flatten.first
|
78
|
+
number = number.to_s.strip
|
79
|
+
|
80
|
+
if number.empty?
|
81
|
+
raise MoneyFormatError, "invalid money string: #{input}" if strict
|
82
|
+
Money.deprecate("invalid money strings will raise in the next major release \"#{input}\"")
|
83
|
+
return '0'
|
84
|
+
end
|
85
|
+
|
86
|
+
marks = number.scan(/[#{ESCAPED_MARKS}]/).flatten
|
87
|
+
if marks.empty?
|
88
|
+
return number
|
89
|
+
end
|
90
|
+
|
91
|
+
if marks.size == 1
|
92
|
+
return normalize_number(number, marks, currency)
|
93
|
+
end
|
94
|
+
|
95
|
+
# remove end of string mark
|
96
|
+
number.sub!(/[#{ESCAPED_MARKS}]\z/, '')
|
97
|
+
|
98
|
+
if amount = number[DOT_DECIMAL_REGEX] || number[INDIAN_NUMERIC_REGEX] || number[CHINESE_NUMERIC_REGEX]
|
99
|
+
return amount.tr(ESCAPED_NON_DOT_MARKS, '')
|
100
|
+
end
|
101
|
+
|
102
|
+
if amount = number[COMMA_DECIMAL_REGEX]
|
103
|
+
return amount.tr(ESCAPED_NON_COMMA_MARKS, '').sub(',', '.')
|
104
|
+
end
|
105
|
+
|
106
|
+
raise MoneyFormatError, "invalid money string: #{input}" if strict
|
107
|
+
Money.deprecate("invalid money strings will raise in the next major release \"#{input}\"")
|
108
|
+
|
109
|
+
normalize_number(number, marks, currency)
|
110
|
+
end
|
111
|
+
|
112
|
+
def normalize_number(number, marks, currency)
|
113
|
+
digits = number.rpartition(marks.last)
|
114
|
+
digits.first.tr!(ESCAPED_MARKS, '')
|
115
|
+
|
116
|
+
if last_digits_decimals?(digits, marks, currency)
|
117
|
+
"#{digits.first}.#{digits.last}"
|
118
|
+
else
|
119
|
+
"#{digits.first}#{digits.last}"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def last_digits_decimals?(digits, marks, currency)
|
124
|
+
# Thousands marks are always different from decimal marks
|
125
|
+
# Example: 1,234,456
|
126
|
+
*other_marks, last_mark = marks
|
127
|
+
other_marks.uniq!
|
128
|
+
if other_marks.size == 1
|
129
|
+
return other_marks.first != last_mark
|
130
|
+
end
|
131
|
+
|
132
|
+
# Thousands always have more than 2 digits
|
133
|
+
# Example: 1,23 must be 1 dollar and 23 cents
|
134
|
+
if digits.last.size < 3
|
135
|
+
return !digits.last.empty?
|
136
|
+
end
|
137
|
+
|
138
|
+
# 0 before the final mark indicates last digits are decimals
|
139
|
+
# Example: 0,23
|
140
|
+
if digits.first.to_i.zero?
|
141
|
+
return true
|
142
|
+
end
|
143
|
+
|
144
|
+
# legacy support for 1.000 USD
|
145
|
+
if digits.last.size == 3 && digits.first.size <= 3 && currency.minor_units < 3
|
146
|
+
return false
|
147
|
+
end
|
148
|
+
|
149
|
+
# The last mark matches the one used by the provided currency to delimiter decimals
|
150
|
+
currency.decimal_mark == last_mark
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Money
|
2
|
+
class NullCurrency
|
3
|
+
|
4
|
+
attr_reader :iso_code, :iso_numeric, :name, :smallest_denomination, :subunit_symbol,
|
5
|
+
:subunit_to_unit, :minor_units, :symbol, :disambiguate_symbol, :decimal_mark
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@symbol = '$'
|
9
|
+
@disambiguate_symbol = nil
|
10
|
+
@subunit_symbol = nil
|
11
|
+
@iso_code = 'XXX' # Valid ISO4217
|
12
|
+
@iso_numeric = '999'
|
13
|
+
@name = 'No Currency'
|
14
|
+
@smallest_denomination = 1
|
15
|
+
@subunit_to_unit = 100
|
16
|
+
@minor_units = 2
|
17
|
+
@decimal_mark = '.'
|
18
|
+
freeze
|
19
|
+
end
|
20
|
+
|
21
|
+
def compatible?(other)
|
22
|
+
other.is_a?(Currency) || other.is_a?(NullCurrency)
|
23
|
+
end
|
24
|
+
|
25
|
+
def eql?(other)
|
26
|
+
self.class == other.class && iso_code == other.iso_code
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
''
|
31
|
+
end
|
32
|
+
|
33
|
+
alias_method :==, :eql?
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module MoneyAccessor
|
2
|
+
def self.included(base)
|
3
|
+
base.extend(ClassMethods)
|
4
|
+
end
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def money_accessor(*columns)
|
8
|
+
variable_get = self <= Struct ? :[] : :instance_variable_get
|
9
|
+
variable_set = self <= Struct ? :[]= : :instance_variable_set
|
10
|
+
|
11
|
+
Array(columns).flatten.each do |name|
|
12
|
+
variable_name = self <= Struct ? name : "@#{name}"
|
13
|
+
|
14
|
+
define_method(name) do
|
15
|
+
value = public_send(variable_get, variable_name)
|
16
|
+
value.blank? ? nil : Money.new(value)
|
17
|
+
end
|
18
|
+
|
19
|
+
define_method("#{name}=") do |value|
|
20
|
+
if value.blank? || !value.respond_to?(:to_money)
|
21
|
+
public_send(variable_set, variable_name, nil)
|
22
|
+
nil
|
23
|
+
else
|
24
|
+
money = value.to_money
|
25
|
+
public_send(variable_set, variable_name, money.value)
|
26
|
+
money
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/money_column.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
module MoneyColumn
|
2
|
+
module ActiveRecordHooks
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
def reload(*)
|
8
|
+
clear_money_column_cache
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize_dup(*)
|
13
|
+
@money_column_cache = {}
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def clear_money_column_cache
|
20
|
+
@money_column_cache.clear if persisted?
|
21
|
+
end
|
22
|
+
|
23
|
+
def init_internals
|
24
|
+
@money_column_cache = {}
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
module ClassMethods
|
29
|
+
def money_column(*columns, currency_column: nil, currency: nil, currency_read_only: false, coerce_null: false)
|
30
|
+
raise ArgumentError, 'cannot set both currency_column and a fixed currency' if currency && currency_column
|
31
|
+
|
32
|
+
if currency
|
33
|
+
currency_iso = Money::Currency.find!(currency).to_s
|
34
|
+
currency_read_only = true
|
35
|
+
elsif currency_column
|
36
|
+
clear_cache_on_currency_change(currency_column)
|
37
|
+
else
|
38
|
+
raise ArgumentError, 'must set one of :currency_column or :currency options'
|
39
|
+
end
|
40
|
+
currency_column = currency_column.to_s.freeze
|
41
|
+
|
42
|
+
columns.flatten.each do |column|
|
43
|
+
column_string = column.to_s.freeze
|
44
|
+
attribute(column_string, MoneyColumn::ActiveRecordType.new)
|
45
|
+
money_column_reader(column_string, currency_column, currency_iso, coerce_null)
|
46
|
+
money_column_writer(column_string, currency_column, currency_iso, currency_read_only)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def clear_cache_on_currency_change(currency_column)
|
53
|
+
define_method "#{currency_column}=" do |value|
|
54
|
+
@money_column_cache.clear
|
55
|
+
super(value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def money_column_reader(column, currency_column, currency_iso, coerce_null)
|
60
|
+
define_method column do
|
61
|
+
return @money_column_cache[column] if @money_column_cache[column]
|
62
|
+
value = read_attribute(column)
|
63
|
+
return if value.nil? && !coerce_null
|
64
|
+
iso = currency_iso || send(currency_column)
|
65
|
+
@money_column_cache[column] = Money.new(value, iso)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def money_column_writer(column, currency_column, currency_iso, currency_read_only)
|
70
|
+
define_method "#{column}=" do |money|
|
71
|
+
@money_column_cache[column] = nil
|
72
|
+
|
73
|
+
if money.blank?
|
74
|
+
write_attribute(column, nil)
|
75
|
+
return nil
|
76
|
+
end
|
77
|
+
|
78
|
+
currency_raw_source = currency_iso || (send(currency_column) rescue nil)
|
79
|
+
currency_source = Money::Helpers.value_to_currency(currency_raw_source)
|
80
|
+
|
81
|
+
if !money.is_a?(Money)
|
82
|
+
return write_attribute(column, Money.new(money, currency_source).value)
|
83
|
+
end
|
84
|
+
|
85
|
+
if currency_raw_source && !currency_source.compatible?(money.currency)
|
86
|
+
Money.deprecate("[money_column] currency mismatch between #{currency_source} and #{money.currency}.")
|
87
|
+
end
|
88
|
+
|
89
|
+
write_attribute(column, money.value)
|
90
|
+
write_attribute(currency_column, money.currency.to_s) unless currency_read_only || money.no_currency?
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/money.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require_relative "lib/money/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "shopify-money"
|
6
|
+
s.version = Money::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ["Shopify Inc"]
|
9
|
+
s.email = "gems@shopify.com"
|
10
|
+
s.description = "Manage money in Shopify with a class that wont lose pennies during division!"
|
11
|
+
s.homepage = "https://github.com/Shopify/money"
|
12
|
+
s.licenses = "MIT"
|
13
|
+
s.summary = "Shopify's money gem"
|
14
|
+
|
15
|
+
s.add_development_dependency("bundler", ">= 1.5")
|
16
|
+
s.add_development_dependency("simplecov", ">= 0")
|
17
|
+
s.add_development_dependency("rails", "~> 5.0")
|
18
|
+
s.add_development_dependency("rspec", "~> 3.2")
|
19
|
+
s.add_development_dependency("database_cleaner", "~> 1.6")
|
20
|
+
s.add_development_dependency("sqlite3", "~> 1.3")
|
21
|
+
s.add_development_dependency("bigdecimal", ">= 1.3.2")
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split($/)
|
24
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
25
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
26
|
+
s.require_paths = ["lib"]
|
27
|
+
end
|
@@ -0,0 +1,204 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe AccountingMoneyParser do
|
4
|
+
describe "parsing of amounts with period decimal separator" do
|
5
|
+
before(:each) do
|
6
|
+
@parser = AccountingMoneyParser.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "parses parenthesis as a negative amount eg (99.00)" do
|
10
|
+
expect(@parser.parse("(99.00)")).to eq(Money.new(-99.00))
|
11
|
+
end
|
12
|
+
|
13
|
+
it "parses parenthesis as a negative amount regardless of currency sign" do
|
14
|
+
expect(@parser.parse("($99.00)")).to eq(Money.new(-99.00))
|
15
|
+
end
|
16
|
+
|
17
|
+
it "parses an empty string to $0" do
|
18
|
+
expect(@parser.parse("")).to eq(Money.new)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "parses an invalid string to $0" do
|
22
|
+
expect(Money).to receive(:deprecate).once
|
23
|
+
expect(@parser.parse("no money")).to eq(Money.new)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "parses a single digit integer string" do
|
27
|
+
expect(@parser.parse("1")).to eq(Money.new(1.00))
|
28
|
+
end
|
29
|
+
|
30
|
+
it "parses a double digit integer string" do
|
31
|
+
expect(@parser.parse("10")).to eq(Money.new(10.00))
|
32
|
+
end
|
33
|
+
|
34
|
+
it "parses an integer string amount with a leading $" do
|
35
|
+
expect(@parser.parse("$1")).to eq(Money.new(1.00))
|
36
|
+
end
|
37
|
+
|
38
|
+
it "parses a float string amount" do
|
39
|
+
expect(@parser.parse("1.37")).to eq(Money.new(1.37))
|
40
|
+
end
|
41
|
+
|
42
|
+
it "parses a float string amount with a leading $" do
|
43
|
+
expect(@parser.parse("$1.37")).to eq(Money.new(1.37))
|
44
|
+
end
|
45
|
+
|
46
|
+
it "parses a float string with a single digit after the decimal" do
|
47
|
+
expect(@parser.parse("10.0")).to eq(Money.new(10.00))
|
48
|
+
end
|
49
|
+
|
50
|
+
it "parses a float string with two digits after the decimal" do
|
51
|
+
expect(@parser.parse("10.00")).to eq(Money.new(10.00))
|
52
|
+
end
|
53
|
+
|
54
|
+
it "parses the amount from an amount surrounded by whitespace and garbage" do
|
55
|
+
expect(@parser.parse("Rubbish $1.00 Rubbish")).to eq(Money.new(1.00))
|
56
|
+
end
|
57
|
+
|
58
|
+
it "parses the amount from an amount surrounded by garbage" do
|
59
|
+
expect(@parser.parse("Rubbish$1.00Rubbish")).to eq(Money.new(1.00))
|
60
|
+
end
|
61
|
+
|
62
|
+
it "parses a negative integer amount in the hundreds" do
|
63
|
+
expect(@parser.parse("-100")).to eq(Money.new(-100.00))
|
64
|
+
end
|
65
|
+
|
66
|
+
it "parses an integer amount in the hundreds" do
|
67
|
+
expect(@parser.parse("410")).to eq(Money.new(410.00))
|
68
|
+
end
|
69
|
+
|
70
|
+
it "parses a positive amount with a thousands separator" do
|
71
|
+
expect(@parser.parse("100,000.00")).to eq(Money.new(100_000.00))
|
72
|
+
end
|
73
|
+
|
74
|
+
it "parses a negative amount with a thousands separator" do
|
75
|
+
expect(@parser.parse("-100,000.00")).to eq(Money.new(-100_000.00))
|
76
|
+
end
|
77
|
+
|
78
|
+
it "parses negative $1.00" do
|
79
|
+
expect(@parser.parse("-1.00")).to eq(Money.new(-1.00))
|
80
|
+
end
|
81
|
+
|
82
|
+
it "parses a negative cents amount" do
|
83
|
+
expect(@parser.parse("-0.90")).to eq(Money.new(-0.90))
|
84
|
+
end
|
85
|
+
|
86
|
+
it "parses amount with 3 decimals and 0 dollar amount" do
|
87
|
+
expect(@parser.parse("0.123")).to eq(Money.new(0.12))
|
88
|
+
end
|
89
|
+
|
90
|
+
it "parses negative amount with 3 decimals and 0 dollar amount" do
|
91
|
+
expect(@parser.parse("-0.123")).to eq(Money.new(-0.12))
|
92
|
+
end
|
93
|
+
|
94
|
+
it "parses negative amount with multiple leading - signs" do
|
95
|
+
expect(@parser.parse("--0.123")).to eq(Money.new(-0.12))
|
96
|
+
end
|
97
|
+
|
98
|
+
it "parses negative amount with multiple - signs" do
|
99
|
+
expect(@parser.parse("--0.123--")).to eq(Money.new(-0.12))
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "parsing of amounts with comma decimal separator" do
|
104
|
+
before(:each) do
|
105
|
+
@parser = AccountingMoneyParser.new
|
106
|
+
end
|
107
|
+
|
108
|
+
it "parses dollar amount $1,00 with leading $" do
|
109
|
+
expect(@parser.parse("$1,00")).to eq(Money.new(1.00))
|
110
|
+
end
|
111
|
+
|
112
|
+
it "parses dollar amount $1,37 with leading $, and non-zero cents" do
|
113
|
+
expect(@parser.parse("$1,37")).to eq(Money.new(1.37))
|
114
|
+
end
|
115
|
+
|
116
|
+
it "parses the amount from an amount surrounded by whitespace and garbage" do
|
117
|
+
expect(@parser.parse("Rubbish $1,00 Rubbish")).to eq(Money.new(1.00))
|
118
|
+
end
|
119
|
+
|
120
|
+
it "parses the amount from an amount surrounded by garbage" do
|
121
|
+
expect(@parser.parse("Rubbish$1,00Rubbish")).to eq(Money.new(1.00))
|
122
|
+
end
|
123
|
+
|
124
|
+
it "parses thousands amount" do
|
125
|
+
Money.with_currency(Money::NULL_CURRENCY) do
|
126
|
+
expect(@parser.parse("1.000")).to eq(Money.new(1000.00))
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
it "parses negative hundreds amount" do
|
131
|
+
expect(@parser.parse("-100,00")).to eq(Money.new(-100.00))
|
132
|
+
end
|
133
|
+
|
134
|
+
it "parses positive hundreds amount" do
|
135
|
+
expect(@parser.parse("410,00")).to eq(Money.new(410.00))
|
136
|
+
end
|
137
|
+
|
138
|
+
it "parses a positive amount with a thousands separator" do
|
139
|
+
expect(@parser.parse("100.000,00")).to eq(Money.new(100_000.00))
|
140
|
+
end
|
141
|
+
|
142
|
+
it "parses a negative amount with a thousands separator" do
|
143
|
+
expect(@parser.parse("-100.000,00")).to eq(Money.new(-100_000.00))
|
144
|
+
end
|
145
|
+
|
146
|
+
it "parses amount with 3 decimals and 0 dollar amount" do
|
147
|
+
expect(@parser.parse("0,123")).to eq(Money.new(0.12))
|
148
|
+
end
|
149
|
+
|
150
|
+
it "parses negative amount with 3 decimals and 0 dollar amount" do
|
151
|
+
expect(@parser.parse("-0,123")).to eq(Money.new(-0.12))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "parsing of decimal cents amounts from 0 to 10" do
|
156
|
+
before(:each) do
|
157
|
+
@parser = AccountingMoneyParser.new
|
158
|
+
end
|
159
|
+
|
160
|
+
it "parses 50.0" do
|
161
|
+
expect(@parser.parse("50.0")).to eq(Money.new(50.00))
|
162
|
+
end
|
163
|
+
|
164
|
+
it "parses 50.1" do
|
165
|
+
expect(@parser.parse("50.1")).to eq(Money.new(50.10))
|
166
|
+
end
|
167
|
+
|
168
|
+
it "parses 50.2" do
|
169
|
+
expect(@parser.parse("50.2")).to eq(Money.new(50.20))
|
170
|
+
end
|
171
|
+
|
172
|
+
it "parses 50.3" do
|
173
|
+
expect(@parser.parse("50.3")).to eq(Money.new(50.30))
|
174
|
+
end
|
175
|
+
|
176
|
+
it "parses 50.4" do
|
177
|
+
expect(@parser.parse("50.4")).to eq(Money.new(50.40))
|
178
|
+
end
|
179
|
+
|
180
|
+
it "parses 50.5" do
|
181
|
+
expect(@parser.parse("50.5")).to eq(Money.new(50.50))
|
182
|
+
end
|
183
|
+
|
184
|
+
it "parses 50.6" do
|
185
|
+
expect(@parser.parse("50.6")).to eq(Money.new(50.60))
|
186
|
+
end
|
187
|
+
|
188
|
+
it "parses 50.7" do
|
189
|
+
expect(@parser.parse("50.7")).to eq(Money.new(50.70))
|
190
|
+
end
|
191
|
+
|
192
|
+
it "parses 50.8" do
|
193
|
+
expect(@parser.parse("50.8")).to eq(Money.new(50.80))
|
194
|
+
end
|
195
|
+
|
196
|
+
it "parses 50.9" do
|
197
|
+
expect(@parser.parse("50.9")).to eq(Money.new(50.90))
|
198
|
+
end
|
199
|
+
|
200
|
+
it "parses 50.10" do
|
201
|
+
expect(@parser.parse("50.10")).to eq(Money.new(50.10))
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|