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.
- data/CHANGELOG +3 -0
- data/LICENSE +20 -0
- data/Manifest +60 -0
- data/README +48 -0
- data/Rakefile +75 -0
- data/invoicing.gemspec +41 -0
- data/lib/invoicing.rb +9 -0
- data/lib/invoicing/cached_record.rb +107 -0
- data/lib/invoicing/class_info.rb +187 -0
- data/lib/invoicing/connection_adapter_ext.rb +44 -0
- data/lib/invoicing/countries/uk.rb +24 -0
- data/lib/invoicing/currency_value.rb +212 -0
- data/lib/invoicing/find_subclasses.rb +193 -0
- data/lib/invoicing/ledger_item.rb +718 -0
- data/lib/invoicing/ledger_item/render_html.rb +515 -0
- data/lib/invoicing/ledger_item/render_ubl.rb +268 -0
- data/lib/invoicing/line_item.rb +246 -0
- data/lib/invoicing/price.rb +9 -0
- data/lib/invoicing/tax_rate.rb +9 -0
- data/lib/invoicing/taxable.rb +355 -0
- data/lib/invoicing/time_dependent.rb +388 -0
- data/lib/invoicing/version.rb +21 -0
- data/test/cached_record_test.rb +100 -0
- data/test/class_info_test.rb +253 -0
- data/test/connection_adapter_ext_test.rb +71 -0
- data/test/currency_value_test.rb +184 -0
- data/test/find_subclasses_test.rb +120 -0
- data/test/fixtures/README +7 -0
- data/test/fixtures/cached_record.sql +22 -0
- data/test/fixtures/class_info.sql +28 -0
- data/test/fixtures/currency_value.sql +29 -0
- data/test/fixtures/find_subclasses.sql +43 -0
- data/test/fixtures/ledger_item.sql +39 -0
- data/test/fixtures/line_item.sql +33 -0
- data/test/fixtures/price.sql +4 -0
- data/test/fixtures/tax_rate.sql +4 -0
- data/test/fixtures/taxable.sql +14 -0
- data/test/fixtures/time_dependent.sql +35 -0
- data/test/ledger_item_test.rb +352 -0
- data/test/line_item_test.rb +139 -0
- data/test/models/README +4 -0
- data/test/models/test_subclass_in_another_file.rb +3 -0
- data/test/models/test_subclass_not_in_database.rb +6 -0
- data/test/price_test.rb +9 -0
- data/test/ref-output/creditnote3.html +82 -0
- data/test/ref-output/creditnote3.xml +89 -0
- data/test/ref-output/invoice1.html +93 -0
- data/test/ref-output/invoice1.xml +111 -0
- data/test/ref-output/invoice2.html +86 -0
- data/test/ref-output/invoice2.xml +98 -0
- data/test/ref-output/invoice_null.html +36 -0
- data/test/render_html_test.rb +69 -0
- data/test/render_ubl_test.rb +32 -0
- data/test/setup.rb +37 -0
- data/test/tax_rate_test.rb +9 -0
- data/test/taxable_test.rb +180 -0
- data/test/test_helper.rb +48 -0
- data/test/time_dependent_test.rb +180 -0
- data/website/curvycorners.js +1 -0
- data/website/screen.css +149 -0
- data/website/template.html.erb +43 -0
- 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
|