invoicing 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/CHANGELOG +3 -0
  2. data/LICENSE +20 -0
  3. data/Manifest +60 -0
  4. data/README +48 -0
  5. data/Rakefile +75 -0
  6. data/invoicing.gemspec +41 -0
  7. data/lib/invoicing.rb +9 -0
  8. data/lib/invoicing/cached_record.rb +107 -0
  9. data/lib/invoicing/class_info.rb +187 -0
  10. data/lib/invoicing/connection_adapter_ext.rb +44 -0
  11. data/lib/invoicing/countries/uk.rb +24 -0
  12. data/lib/invoicing/currency_value.rb +212 -0
  13. data/lib/invoicing/find_subclasses.rb +193 -0
  14. data/lib/invoicing/ledger_item.rb +718 -0
  15. data/lib/invoicing/ledger_item/render_html.rb +515 -0
  16. data/lib/invoicing/ledger_item/render_ubl.rb +268 -0
  17. data/lib/invoicing/line_item.rb +246 -0
  18. data/lib/invoicing/price.rb +9 -0
  19. data/lib/invoicing/tax_rate.rb +9 -0
  20. data/lib/invoicing/taxable.rb +355 -0
  21. data/lib/invoicing/time_dependent.rb +388 -0
  22. data/lib/invoicing/version.rb +21 -0
  23. data/test/cached_record_test.rb +100 -0
  24. data/test/class_info_test.rb +253 -0
  25. data/test/connection_adapter_ext_test.rb +71 -0
  26. data/test/currency_value_test.rb +184 -0
  27. data/test/find_subclasses_test.rb +120 -0
  28. data/test/fixtures/README +7 -0
  29. data/test/fixtures/cached_record.sql +22 -0
  30. data/test/fixtures/class_info.sql +28 -0
  31. data/test/fixtures/currency_value.sql +29 -0
  32. data/test/fixtures/find_subclasses.sql +43 -0
  33. data/test/fixtures/ledger_item.sql +39 -0
  34. data/test/fixtures/line_item.sql +33 -0
  35. data/test/fixtures/price.sql +4 -0
  36. data/test/fixtures/tax_rate.sql +4 -0
  37. data/test/fixtures/taxable.sql +14 -0
  38. data/test/fixtures/time_dependent.sql +35 -0
  39. data/test/ledger_item_test.rb +352 -0
  40. data/test/line_item_test.rb +139 -0
  41. data/test/models/README +4 -0
  42. data/test/models/test_subclass_in_another_file.rb +3 -0
  43. data/test/models/test_subclass_not_in_database.rb +6 -0
  44. data/test/price_test.rb +9 -0
  45. data/test/ref-output/creditnote3.html +82 -0
  46. data/test/ref-output/creditnote3.xml +89 -0
  47. data/test/ref-output/invoice1.html +93 -0
  48. data/test/ref-output/invoice1.xml +111 -0
  49. data/test/ref-output/invoice2.html +86 -0
  50. data/test/ref-output/invoice2.xml +98 -0
  51. data/test/ref-output/invoice_null.html +36 -0
  52. data/test/render_html_test.rb +69 -0
  53. data/test/render_ubl_test.rb +32 -0
  54. data/test/setup.rb +37 -0
  55. data/test/tax_rate_test.rb +9 -0
  56. data/test/taxable_test.rb +180 -0
  57. data/test/test_helper.rb +48 -0
  58. data/test/time_dependent_test.rb +180 -0
  59. data/website/curvycorners.js +1 -0
  60. data/website/screen.css +149 -0
  61. data/website/template.html.erb +43 -0
  62. metadata +180 -0
@@ -0,0 +1,21 @@
1
+ module Invoicing
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ BUILD = 0
6
+
7
+ def to_a
8
+ [MAJOR, MINOR, BUILD]
9
+ end
10
+
11
+ def to_s
12
+ to_a.join(".")
13
+ end
14
+
15
+ module_function :to_a
16
+ module_function :to_s
17
+
18
+ STRING = Version.to_s
19
+ end
20
+ VERSION = Version.to_s
21
+ end
@@ -0,0 +1,100 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper.rb')
2
+
3
+ class CachedRecordTest < Test::Unit::TestCase
4
+
5
+ class CachedRecord < ActiveRecord::Base
6
+ set_primary_key 'id2'
7
+ acts_as_cached_record :id => 'id2'
8
+ has_many :referrers, :class_name => 'RefersToCachedRecord', :foreign_key => 'cached_record_id'
9
+ end
10
+
11
+ class RefersToCachedRecord < ActiveRecord::Base
12
+ belongs_to :cached_record
13
+ end
14
+
15
+ class CachedRecordMockDatabase < ActiveRecord::Base
16
+ set_table_name 'cached_records'
17
+ set_primary_key 'id2'
18
+ acts_as_cached_record :id => 'id2'
19
+
20
+ def self.connection
21
+ @connection_mock ||= FlexMock.new('connection')
22
+ end
23
+ end
24
+
25
+
26
+ def test_find_with_valid_id_should_return_record
27
+ record = CachedRecord.find(1)
28
+ assert_not_nil record
29
+ assert record.kind_of?(CachedRecord)
30
+ end
31
+
32
+ def test_find_with_invalid_id_should_raise_exception
33
+ assert_raise ActiveRecord::RecordNotFound do
34
+ CachedRecord.find(99)
35
+ end
36
+ end
37
+
38
+ def test_find_with_valid_id_should_not_access_database
39
+ CachedRecordMockDatabase.connection.should_receive(:select).and_throw('should not access database')
40
+ assert_not_nil CachedRecordMockDatabase.find(1)
41
+ end
42
+
43
+ def test_find_with_invalid_id_should_not_access_database
44
+ CachedRecordMockDatabase.connection.should_receive(:select).and_throw('should not access database')
45
+ assert_raise ActiveRecord::RecordNotFound do
46
+ CachedRecordMockDatabase.find(99)
47
+ end
48
+ end
49
+
50
+ def test_find_with_conditions_should_still_work
51
+ assert_equal CachedRecord.find_by_value('Two'), CachedRecord.find(2)
52
+ end
53
+
54
+ def test_find_with_conditions_should_not_use_the_cache
55
+ assert !CachedRecord.find_by_value('Two').equal?(CachedRecord.find(2))
56
+ end
57
+
58
+ def test_find_without_ids_should_raise_exception
59
+ assert_raise ActiveRecord::RecordNotFound do
60
+ CachedRecord.find
61
+ end
62
+ end
63
+
64
+ def test_find_with_empty_list_of_ids_should_raise_exception
65
+ assert_raise ActiveRecord::RecordNotFound do
66
+ CachedRecord.find(:conditions => {:id => []})
67
+ end
68
+ end
69
+
70
+ def test_find_with_list_of_ids_should_return_list_of_objects
71
+ expected = CachedRecord.cached_record_list.sort{|r1, r2| r1.id - r2.id}
72
+ assert_equal expected, CachedRecord.find([1,2])
73
+ end
74
+
75
+ def test_cached_record_associations_should_still_work
76
+ assert_equal 2, CachedRecord.find(1).referrers.length
77
+ end
78
+
79
+ def test_foreign_key_to_cached_record_should_use_cache
80
+ assert RefersToCachedRecord.find(1).cached_record.equal?(CachedRecord.find(1))
81
+ end
82
+
83
+ def test_cached_record_list_should_return_all_objects
84
+ assert_equal 2, CachedRecord.cached_record_list.length
85
+ end
86
+
87
+ def test_cached_record_list_should_not_access_database
88
+ CachedRecordMockDatabase.connection.should_receive(:select).and_throw('should not access database')
89
+ assert_not_nil CachedRecordMockDatabase.cached_record_list
90
+ end
91
+
92
+ def test_reload_cache_should_do_what_it_says_on_the_tin
93
+ CachedRecord.connection.execute "insert into cached_records (id2, value) values(3, 'Three')"
94
+ CachedRecord.reload_cache
95
+ record = CachedRecord.find(3)
96
+ assert_not_nil record
97
+ assert record.kind_of?(CachedRecord)
98
+ assert_equal 3, CachedRecord.cached_record_list.length
99
+ end
100
+ end
@@ -0,0 +1,253 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper.rb')
2
+
3
+ # Mini implementation of the ClassInfo pattern, at which we can fire our tests
4
+
5
+ module MyNamespace
6
+ module ClassInfoTestModule
7
+ module ActMethods
8
+ def acts_as_class_info_test(*args)
9
+ Invoicing::ClassInfo.acts_as(MyNamespace::ClassInfoTestModule, self, args)
10
+ end
11
+ end
12
+
13
+ def get_class_info
14
+ class_info_test_module_class_info
15
+ end
16
+
17
+ def my_instance_method
18
+ class_info_test_module_class_info.the_answer / value
19
+ end
20
+
21
+ def my_other_instance_method
22
+ class_info_test_module_class_info.not_the_answer / value
23
+ end
24
+
25
+ module ClassMethods
26
+ def my_class_method(number)
27
+ class_info_test_module_class_info.the_answer / number
28
+ end
29
+
30
+ def my_other_class_method(number)
31
+ class_info_test_module_class_info.not_the_answer / number
32
+ end
33
+
34
+ def get_class_info
35
+ class_info_test_module_class_info
36
+ end
37
+ end
38
+
39
+ class ClassInfo < Invoicing::ClassInfo::Base
40
+ def foo
41
+ 'foo'
42
+ end
43
+
44
+ def the_answer
45
+ all_args.first
46
+ end
47
+
48
+ def not_the_answer
49
+ all_args.last
50
+ end
51
+
52
+ def option_defaults
53
+ {:option1 => :baa, :option2 => :blah}
54
+ end
55
+ end
56
+ end
57
+
58
+
59
+ module ClassInfoTest2
60
+ module ActMethods
61
+ def acts_as_class_info_test2(*args)
62
+ Invoicing::ClassInfo.acts_as(MyNamespace::ClassInfoTest2, self, args)
63
+ end
64
+ end
65
+
66
+ module ClassMethods
67
+ def test2_class_info
68
+ class_info_test2_class_info
69
+ end
70
+ end
71
+
72
+ class ClassInfo < Invoicing::ClassInfo::Base
73
+ end
74
+ end
75
+ end
76
+
77
+ ActiveRecord::Base.send(:extend, MyNamespace::ClassInfoTestModule::ActMethods)
78
+ ActiveRecord::Base.send(:extend, MyNamespace::ClassInfoTest2::ActMethods)
79
+
80
+
81
+ # Model objects which use the acts_as feature defined above
82
+
83
+ class ClassInfoTestRecord < ActiveRecord::Base
84
+ acts_as_class_info_test 42, :option1 => :moo
85
+ acts_as_class_info_test 84, 42, 168, :option3 => :asdf
86
+
87
+ def self.class_foo
88
+ class_info_test_module_class_info.foo
89
+ end
90
+
91
+ def instance_foo
92
+ class_info_test_module_class_info.foo
93
+ end
94
+ end
95
+
96
+ class ClassInfoTestSubclass < ClassInfoTestRecord
97
+ acts_as_class_info_test 336, :option1 => :quack, :option4 => :fdsa
98
+ end
99
+
100
+ class ClassInfoTestSubclass2 < ClassInfoTestRecord
101
+ acts_as_class_info_test2 :option1 => :badger
102
+ end
103
+
104
+ class ClassInfoTestSubSubclass < ClassInfoTestSubclass2
105
+ acts_as_class_info_test 112, :option3 => 1234
106
+ end
107
+
108
+ class ClassInfoTestEmptySubclass < ClassInfoTestRecord
109
+ end
110
+
111
+ class ClassInfoTest2Record < ActiveRecord::Base
112
+ acts_as_class_info_test2 :option1 => :okapi, :option3 => :kangaroo
113
+
114
+ def option1; 'this is option1'; end
115
+ def option2; 'this is option2'; end
116
+ def kangaroo; 'bounce'; end
117
+ end
118
+
119
+
120
+ #######################################################################################
121
+
122
+ class ClassInfoTest < Test::Unit::TestCase
123
+
124
+ def test_call_into_class_info_via_class
125
+ assert_equal 'foo', ClassInfoTestRecord.class_foo
126
+ end
127
+
128
+ def test_call_into_class_info_via_instance
129
+ assert_equal 'foo', ClassInfoTestRecord.new.instance_foo
130
+ end
131
+
132
+ def test_mixin_superclass_instance_methods
133
+ assert_equal 21, ClassInfoTestRecord.find(1).my_instance_method
134
+ assert_equal 84, ClassInfoTestRecord.find(1).my_other_instance_method
135
+ end
136
+
137
+ def test_mixin_superclass_class_methods
138
+ assert_equal 14, ClassInfoTestRecord.my_class_method(3)
139
+ assert_equal 28, ClassInfoTestRecord.my_other_class_method(6)
140
+ end
141
+
142
+ def test_mixin_subclass_instance_methods
143
+ assert_equal 14, ClassInfoTestRecord.find(2).my_instance_method
144
+ assert_equal 112, ClassInfoTestRecord.find(2).my_other_instance_method
145
+ end
146
+
147
+ def test_mixin_subclass_class_methods
148
+ assert_equal 14, ClassInfoTestSubclass.my_class_method(3)
149
+ assert_equal 56, ClassInfoTestSubclass.my_other_class_method(6)
150
+ end
151
+
152
+ def test_all_args_in_superclass
153
+ assert_equal [42, 84, 168], ClassInfoTestRecord.get_class_info.all_args
154
+ end
155
+
156
+ def test_all_args_in_subclass
157
+ assert_equal [42, 84, 168, 336], ClassInfoTestSubclass.get_class_info.all_args
158
+ end
159
+
160
+ def test_all_args_in_sub_subclass
161
+ assert_equal [42, 84, 168, 112], ClassInfoTestSubSubclass.get_class_info.all_args
162
+ end
163
+
164
+ def test_current_args_in_superclass
165
+ assert_equal [84, 42, 168], ClassInfoTestRecord.get_class_info.current_args
166
+ end
167
+
168
+ def test_current_args_in_subclass
169
+ assert_equal [336], ClassInfoTestSubclass.get_class_info.current_args
170
+ end
171
+
172
+ def test_current_args_in_sub_subclass
173
+ assert_equal [112], ClassInfoTestSubSubclass.get_class_info.current_args
174
+ end
175
+
176
+ def test_new_args_in_superclass
177
+ assert_equal [84, 168], ClassInfoTestRecord.get_class_info.new_args
178
+ end
179
+
180
+ def test_new_args_in_subclass
181
+ assert_equal [336], ClassInfoTestSubclass.get_class_info.new_args
182
+ end
183
+
184
+ def test_new_args_in_sub_subclass
185
+ assert_equal [112], ClassInfoTestSubSubclass.get_class_info.new_args
186
+ end
187
+
188
+ def test_all_options_in_superclass
189
+ assert_equal :moo, ClassInfoTestRecord.get_class_info.all_options[:option1]
190
+ assert_equal :blah, ClassInfoTestRecord.get_class_info.all_options[:option2]
191
+ assert_equal :asdf, ClassInfoTestRecord.get_class_info.all_options[:option3]
192
+ assert_nil ClassInfoTestRecord.get_class_info.all_options[:option4]
193
+ end
194
+
195
+ def test_all_options_in_subclass
196
+ assert_equal :quack, ClassInfoTestSubclass.get_class_info.all_options[:option1]
197
+ assert_equal :blah, ClassInfoTestSubclass.get_class_info.all_options[:option2]
198
+ assert_equal :asdf, ClassInfoTestSubclass.get_class_info.all_options[:option3]
199
+ assert_equal :fdsa, ClassInfoTestSubclass.get_class_info.all_options[:option4]
200
+ end
201
+
202
+ def test_all_options_in_sub_subclass
203
+ assert_equal :moo, ClassInfoTestSubSubclass.get_class_info.all_options[:option1]
204
+ assert_equal :blah, ClassInfoTestSubSubclass.get_class_info.all_options[:option2]
205
+ assert_equal 1234, ClassInfoTestSubSubclass.get_class_info.all_options[:option3]
206
+ assert_nil ClassInfoTestSubSubclass.get_class_info.all_options[:option4]
207
+ end
208
+
209
+ def test_current_options_in_superclass
210
+ assert_equal({:option3 => :asdf}, ClassInfoTestRecord.get_class_info.current_options)
211
+ end
212
+
213
+ def test_current_options_in_subclass
214
+ assert_equal({:option1 => :quack, :option4 => :fdsa}, ClassInfoTestSubclass.get_class_info.current_options)
215
+ end
216
+
217
+ def test_two_features_in_the_same_model
218
+ assert_equal({:option1 => :badger}, ClassInfoTestSubclass2.test2_class_info.all_options)
219
+ assert_equal({:option1 => :badger}, ClassInfoTestSubSubclass.test2_class_info.all_options)
220
+ end
221
+
222
+ def test_the_same_feature_in_two_models
223
+ assert_equal({:option1 => :okapi, :option3 => :kangaroo}, ClassInfoTest2Record.test2_class_info.all_options)
224
+ end
225
+
226
+ def test_method_renamed
227
+ assert_equal 'kangaroo', ClassInfoTest2Record.test2_class_info.method(:option3)
228
+ assert_equal 'bounce', ClassInfoTest2Record.test2_class_info.get(ClassInfoTest2Record.find(1), :option3)
229
+ end
230
+
231
+ def test_database_column_renamed
232
+ assert_equal 'okapi', ClassInfoTest2Record.test2_class_info.method(:option1)
233
+ assert_equal 'OKAPI!', ClassInfoTest2Record.test2_class_info.get(ClassInfoTest2Record.find(1), :option1)
234
+ end
235
+
236
+ def test_method_not_renamed
237
+ assert_equal 'option2', ClassInfoTest2Record.test2_class_info.method(:option2)
238
+ assert_equal 'this is option2', ClassInfoTest2Record.test2_class_info.get(ClassInfoTest2Record.find(1), :option2)
239
+ end
240
+
241
+ def test_method_on_nil_object
242
+ assert_nil ClassInfoTest2Record.test2_class_info.get(nil, :option2)
243
+ end
244
+
245
+ def test_method_not_found_via_class_info
246
+ assert_nil ClassInfoTest2Record.test2_class_info.get(ClassInfoTest2Record.find(1), :this_method_does_not_exist)
247
+ end
248
+
249
+ def test_inherited_to_empty_subclass
250
+ assert_not_nil ClassInfoTestEmptySubclass.new.get_class_info
251
+ assert_equal ClassInfoTestEmptySubclass.new.get_class_info, ClassInfoTestRecord.new.get_class_info
252
+ end
253
+ end
@@ -0,0 +1,71 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper.rb')
2
+
3
+ class ConnectionAdapterExtTest < Test::Unit::TestCase
4
+
5
+ # Don't run these tests in database transactions.
6
+ def setup
7
+ end
8
+ def teardown
9
+ end
10
+
11
+ def using_database(database_type)
12
+ begin
13
+ ActiveRecord::Base.establish_connection(TEST_DB_CONFIG[database_type.to_sym])
14
+ yield
15
+ ensure
16
+ connect_to_testing_database
17
+ end
18
+ end
19
+
20
+ def test_conditional_function_as_mysql
21
+ using_database :mysql do
22
+ assert_equal "IF(true, foo, bar)", Invoicing::ConnectionAdapterExt.conditional_function('true', 'foo', 'bar')
23
+ end
24
+ end
25
+
26
+ def test_conditional_function_as_postgresql
27
+ using_database :postgresql do
28
+ assert_equal "CASE WHEN true THEN foo ELSE bar END",
29
+ Invoicing::ConnectionAdapterExt.conditional_function('true', 'foo', 'bar')
30
+ end
31
+ end
32
+
33
+ def test_conditional_function_as_sqlite3
34
+ using_database :sqlite3 do
35
+ assert_raise RuntimeError do
36
+ Invoicing::ConnectionAdapterExt.conditional_function('true', 'foo', 'bar')
37
+ end
38
+ end
39
+ end
40
+
41
+ def test_group_by_all_columns_as_mysql
42
+ using_database :mysql do
43
+ assert_equal "`ledger_item_records`.`id2`",
44
+ Invoicing::ConnectionAdapterExt.group_by_all_columns(MyLedgerItem)
45
+ end
46
+ end
47
+
48
+ def test_group_by_all_columns_as_postgresql
49
+ using_database :postgresql do
50
+ assert_equal(
51
+ '"ledger_item_records"."id2", "ledger_item_records"."type2", "ledger_item_records"."sender_id2", ' +
52
+ '"ledger_item_records"."recipient_id2", "ledger_item_records"."identifier2", ' +
53
+ '"ledger_item_records"."issue_date2", "ledger_item_records"."currency2", ' +
54
+ '"ledger_item_records"."total_amount2", "ledger_item_records"."tax_amount2", ' +
55
+ '"ledger_item_records"."status2", "ledger_item_records"."period_start2", ' +
56
+ '"ledger_item_records"."period_end2", "ledger_item_records"."uuid2", ' +
57
+ '"ledger_item_records"."due_date2", "ledger_item_records"."created_at", ' +
58
+ '"ledger_item_records"."updated_at"',
59
+ Invoicing::ConnectionAdapterExt.group_by_all_columns(MyLedgerItem))
60
+ end
61
+ end
62
+
63
+ def test_group_by_all_columns_as_sqlite3
64
+ using_database :sqlite3 do
65
+ assert_raise RuntimeError do
66
+ Invoicing::ConnectionAdapterExt.group_by_all_columns(MyLedgerItem)
67
+ end
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,184 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), 'test_helper.rb')
4
+
5
+ # Test extending the default list of currency codes: include the Zimbabwe Dollar.
6
+ # This also tests rounding and seriously large numbers. -- Sorry, you shouldn't make
7
+ # jokes about this sort of thing. The people are suffering badly.
8
+ Invoicing::CurrencyValue::CURRENCIES['ZWD'] = {:symbol => 'ZW$', :round => 5_000_000}
9
+
10
+ class CurrencyValueTest < Test::Unit::TestCase
11
+
12
+ class CurrencyValueRecord < ActiveRecord::Base
13
+ validates_numericality_of :amount
14
+ acts_as_currency_value :amount, :tax_amount, :currency => 'currency_code'
15
+ end
16
+
17
+ # In Finland and the Netherlands, Euro amounts are commonly rounded to the nearest 5 cents.
18
+ class EurosInFinlandRecord < ActiveRecord::Base
19
+ set_table_name 'no_currency_column_records'
20
+ acts_as_currency_value :amount, :currency_code => 'EUR', :round => 0.05, :space => true
21
+ end
22
+
23
+ def test_format_small_number
24
+ assert_equal "€0.02", CurrencyValueRecord.find(2).tax_amount_formatted
25
+ end
26
+
27
+ def test_format_thousands_separators
28
+ assert_equal "€98,765,432.00", CurrencyValueRecord.find(2).amount_formatted
29
+ end
30
+
31
+ def test_format_no_decimal_point
32
+ assert_equal "¥8,888", CurrencyValueRecord.find(4).amount_formatted
33
+ end
34
+
35
+ def test_format_suffix_unit
36
+ assert_equal "5,432.00 元", CurrencyValueRecord.find(3).amount_formatted
37
+ end
38
+
39
+ def test_format_unknown_currency
40
+ assert_equal "123.00 XXX", CurrencyValueRecord.find(5).amount_formatted
41
+ end
42
+
43
+ def test_format_with_custom_currency
44
+ record = CurrencyValueRecord.new(:currency_code => 'ZWD', :amount => BigDecimal('50000000000'))
45
+ assert_equal "ZW$50,000,000,000", record.amount_formatted # price of 1 egg on 18 July 2008
46
+ end
47
+
48
+ def test_format_without_currency_column
49
+ assert_equal "€ 95.15", EurosInFinlandRecord.find(1).amount_formatted
50
+ end
51
+
52
+ def test_format_custom_value
53
+ assert_equal "€1,357.90", CurrencyValueRecord.find(2).format_currency_value(BigDecimal('1357.9'))
54
+ end
55
+
56
+ def test_load_from_database_and_format
57
+ assert_equal BigDecimal('123.45'), CurrencyValueRecord.find(1).amount
58
+ assert_equal "£123.45", CurrencyValueRecord.find(1).amount_formatted
59
+ end
60
+
61
+ def test_new_record_from_string_and_format
62
+ record = CurrencyValueRecord.new(:amount => '44.44', :currency_code => 'USD')
63
+ assert_equal BigDecimal('44.44'), record.amount
64
+ assert_equal "$44.44", record.amount_formatted
65
+ end
66
+
67
+ def test_new_record_from_big_decimal_and_format
68
+ record = CurrencyValueRecord.new(:amount => BigDecimal('3.33'), :currency_code => 'USD')
69
+ assert_equal BigDecimal('3.33'), record.amount
70
+ assert_equal "$3.33", record.amount_formatted
71
+ end
72
+
73
+ def test_assign_float_to_new_record_and_format
74
+ record = CurrencyValueRecord.new
75
+ record.amount = 44.44
76
+ record.currency_code = 'USD'
77
+ assert_equal BigDecimal('44.44'), record.amount
78
+ assert_equal "$44.44", record.amount_formatted
79
+ end
80
+
81
+ def test_assign_to_new_record_omitting_currency
82
+ record = CurrencyValueRecord.new
83
+ record.amount = 44.44
84
+ assert_equal BigDecimal('44.44'), record.amount
85
+ assert_equal "44.44", record.amount_formatted
86
+ end
87
+
88
+ def test_assign_nothing_to_new_record_with_numericality_validation
89
+ record = CurrencyValueRecord.new(:currency_code => 'USD')
90
+ assert_nil record.amount
91
+ assert_equal '', record.amount_formatted
92
+ assert !record.valid?
93
+ end
94
+
95
+ def test_assign_nothing_to_new_record_without_numericality_validation
96
+ record = CurrencyValueRecord.new(:amount => 1, :currency_code => 'USD')
97
+ assert_nil record.tax_amount
98
+ assert_equal '', record.tax_amount_formatted
99
+ assert record.valid?
100
+ record.save!
101
+ assert_equal([{'amount' => '1.0000', 'tax_amount' => nil}],
102
+ ActiveRecord::Base.connection.select_all("SELECT amount, tax_amount FROM currency_value_records WHERE id=#{record.id}"))
103
+ end
104
+
105
+ def test_assign_invalid_value_to_new_record_with_numericality_validation
106
+ record = CurrencyValueRecord.new(:amount => 'plonk', :currency_code => 'USD')
107
+ assert_equal BigDecimal('0.00'), record.amount
108
+ assert_equal 'plonk', record.amount_before_type_cast
109
+ assert_equal '', record.amount_formatted
110
+ assert !record.valid?
111
+ end
112
+
113
+ def test_assign_invalid_value_to_new_record_without_numericality_validation
114
+ record = CurrencyValueRecord.new(:amount => 1, :tax_amount => 'plonk', :currency_code => 'USD')
115
+ assert_equal BigDecimal('0.00'), record.tax_amount
116
+ assert_equal 'plonk', record.tax_amount_before_type_cast
117
+ assert_equal '', record.tax_amount_formatted
118
+ assert record.valid?
119
+ record.save!
120
+ assert_equal([{'amount' => '1.0000', 'tax_amount' => '0.0000'}],
121
+ ActiveRecord::Base.connection.select_all("SELECT amount, tax_amount FROM currency_value_records WHERE id=#{record.id}"))
122
+ end
123
+
124
+ def test_overwrite_existing_record_with_valid_value
125
+ record = CurrencyValueRecord.find(4)
126
+ record.amount = '12.34'
127
+ record.currency_code = 'EUR'
128
+ assert_equal BigDecimal('12.34'), record.amount
129
+ assert_equal '12.34', record.amount_before_type_cast
130
+ assert_equal "€12.34", record.amount_formatted
131
+ record.save!
132
+ assert_equal([{'amount' => '12.3400', 'currency_code' => 'EUR'}],
133
+ ActiveRecord::Base.connection.select_all("SELECT amount, currency_code FROM currency_value_records WHERE id=#{record.id}"))
134
+ end
135
+
136
+ def test_overwrite_existing_record_with_nil
137
+ record = CurrencyValueRecord.find(4)
138
+ record.tax_amount = nil
139
+ assert_nil record.tax_amount
140
+ assert_nil record.tax_amount_before_type_cast
141
+ assert_equal '', record.tax_amount_formatted
142
+ record.save!
143
+ assert_equal([{'amount' => '8888.0000', 'tax_amount' => nil, 'currency_code' => 'JPY'}],
144
+ ActiveRecord::Base.connection.select_all("SELECT amount, tax_amount, currency_code FROM currency_value_records WHERE id=#{record.id}"))
145
+ end
146
+
147
+ def test_rounding_on_new_record_with_currency_column
148
+ record = CurrencyValueRecord.new(:amount => '1234.5678', :currency_code => 'JPY')
149
+ assert_equal BigDecimal('1235'), record.amount
150
+ assert_equal '1234.5678', record.amount_before_type_cast
151
+ record.save!
152
+ assert_equal([{'amount' => '1235.0000'}],
153
+ ActiveRecord::Base.connection.select_all("SELECT amount FROM currency_value_records WHERE id=#{record.id}"))
154
+ end
155
+
156
+ def test_rounding_on_overwriting_record_with_currency_column
157
+ record = CurrencyValueRecord.find(1)
158
+ record.amount = 10.0/3.0
159
+ assert_equal BigDecimal('3.33'), record.amount
160
+ assert_equal 10.0/3.0, record.amount_before_type_cast
161
+ record.save!
162
+ assert_equal([{'amount' => '3.3300'}],
163
+ ActiveRecord::Base.connection.select_all("SELECT amount FROM currency_value_records WHERE id=1"))
164
+ end
165
+
166
+ def test_rounding_on_new_record_with_default_currency
167
+ record = EurosInFinlandRecord.new(:amount => '1234.5678')
168
+ assert_equal BigDecimal('1234.55'), record.amount
169
+ assert_equal '1234.5678', record.amount_before_type_cast
170
+ record.save!
171
+ assert_equal([{'amount' => '1234.5500'}],
172
+ ActiveRecord::Base.connection.select_all("SELECT amount FROM no_currency_column_records WHERE id=#{record.id}"))
173
+ end
174
+
175
+ def test_rounding_on_overwriting_record_with_default_currency
176
+ record = EurosInFinlandRecord.find(1)
177
+ record.amount = '98.7654321'
178
+ assert_equal BigDecimal('98.75'), record.amount
179
+ assert_equal '98.7654321', record.amount_before_type_cast
180
+ record.save!
181
+ assert_equal([{'amount' => '98.7500'}],
182
+ ActiveRecord::Base.connection.select_all("SELECT amount FROM no_currency_column_records WHERE id=1"))
183
+ end
184
+ end