invoicing 0.1.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.
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