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,86 @@
1
+ <h1 class="invoice">Invoice</h1>
2
+ <table class="invoice addresses">
3
+ <tr>
4
+ <th class="recipient">Recipient</th>
5
+ <th class="sender">Sender</th>
6
+ </tr>
7
+ <tr>
8
+ <td class="recipient vcard">
9
+ <div class="fn org">Unlimited Limited</div>
10
+ <div class="contact">Mr B. Badger</div>
11
+ <div class="adr">
12
+ <span class="street-address">The Sett<br />5 Badger Lane</span><br />
13
+ <span class="locality">Badgertown</span><br />
14
+ <span class="postal-code">Badger999</span><br />
15
+ <span class="country-name">England</span>
16
+ </div>
17
+ </td>
18
+ <td class="sender vcard">
19
+ <div class="fn org">Lovely Customer Inc.</div>
20
+ <div class="contact">Fred</div>
21
+ <div class="adr">
22
+ <span class="street-address">The pasture</span><br />
23
+ <span class="locality">Mootown</span><br />
24
+ <span class="region">Cow Kingdom</span><br />
25
+ <span class="postal-code">MOOO</span><br />
26
+ <span class="country-name">Scotland</span>
27
+ </div>
28
+ </td>
29
+ </tr>
30
+ <tr>
31
+ <td class="recipient">
32
+ VAT number:<br /><span class="tax-number">123456789</span>
33
+ </td>
34
+ <td class="sender">
35
+ VAT number:<br /><span class="tax-number">987654321</span>
36
+ </td>
37
+ </tr>
38
+ </table>
39
+ <table class="invoice metadata">
40
+ <tr class="identifier">
41
+ <th>Invoice no.:</th>
42
+ <td>12-ASDF</td>
43
+ </tr>
44
+ <tr class="issue-date">
45
+ <th>Issue date:</th>
46
+ <td>2009-01-01</td>
47
+ </tr>
48
+ <tr class="period-start">
49
+ <th>Period from:</th>
50
+ <td>2008-01-01</td>
51
+ </tr>
52
+ <tr class="period-end">
53
+ <th>Period until:</th>
54
+ <td>2009-01-01</td>
55
+ </tr>
56
+ <tr class="due-date">
57
+ <th>Payment due:</th>
58
+ <td>2009-01-31</td>
59
+ </tr>
60
+ </table>
61
+ <p class="invoice description">InvoiceSubtype 2</p>
62
+ <table class="invoice line-items">
63
+ <tr>
64
+ <th class="tax-point">Tax point</th>
65
+ <th class="quantity">Quantity</th>
66
+ <th class="description">Description</th>
67
+ <th class="net-amount">Net price</th>
68
+ <th class="tax-amount">VAT</th>
69
+ </tr>
70
+ <tr>
71
+ <td class="tax-point">2009-01-01</td>
72
+ <td class="quantity">1.0</td>
73
+ <td class="description">moo</td>
74
+ <td class="net-amount">£123.45</td>
75
+ <td class="tax-amount">£18.52</td>
76
+ </tr>
77
+ <tr class="subtotal">
78
+ <th colspan="3">Subtotal</th>
79
+ <td class="net-amount">Net: £123.45</td>
80
+ <td class="tax-amount">VAT: £18.52</td>
81
+ </tr>
82
+ <tr class="total">
83
+ <th colspan="4">Total</th>
84
+ <td class="total-amount">£141.97</td>
85
+ </tr>
86
+ </table>
@@ -0,0 +1,98 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <ubl:SelfBilledInvoice xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:ubl="urn:oasis:names:specification:ubl:schema:xsd:SelfBilledInvoice-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
3
+ <cbc:ID>12-ASDF</cbc:ID>
4
+ <cbc:UUID>fe4d20a0-d1b9-012b-48a5-0017f22d32c0</cbc:UUID>
5
+ <cbc:IssueDate>2009-01-01</cbc:IssueDate>
6
+ <cbc:IssueTime>00:00:00+00:00</cbc:IssueTime>
7
+ <cbc:TaxPointDate>2009-01-01</cbc:TaxPointDate>
8
+ <cbc:InvoiceTypeCode>InvoiceSubtype</cbc:InvoiceTypeCode>
9
+ <cbc:Note>InvoiceSubtype 2</cbc:Note>
10
+ <cac:InvoicePeriod>
11
+ <cbc:StartDate>2008-01-01</cbc:StartDate>
12
+ <cbc:StartTime>00:00:00+00:00</cbc:StartTime>
13
+ <cbc:EndDate>2009-01-01</cbc:EndDate>
14
+ <cbc:EndTime>00:00:00+00:00</cbc:EndTime>
15
+ </cac:InvoicePeriod>
16
+ <cac:AccountingCustomerParty>
17
+ <cac:Party>
18
+ <cac:PartyName>
19
+ <cbc:Name>Unlimited Limited</cbc:Name>
20
+ </cac:PartyName>
21
+ <cac:PostalAddress>
22
+ <cbc:StreetName>The Sett</cbc:StreetName>
23
+ <cbc:AdditionalStreetName>5 Badger Lane</cbc:AdditionalStreetName>
24
+ <cbc:CityName>Badgertown</cbc:CityName>
25
+ <cbc:PostalZone>Badger999</cbc:PostalZone>
26
+ <cbc:CountrySubentity></cbc:CountrySubentity>
27
+ <cac:Country>
28
+ <cbc:IdentificationCode>GB</cbc:IdentificationCode>
29
+ <cbc:Name>England</cbc:Name>
30
+ </cac:Country>
31
+ </cac:PostalAddress>
32
+ <cac:PartyTaxScheme>
33
+ <cbc:CompanyID>123456789</cbc:CompanyID>
34
+ <cac:TaxScheme>
35
+ <cbc:ID>VAT</cbc:ID>
36
+ </cac:TaxScheme>
37
+ </cac:PartyTaxScheme>
38
+ <cac:Contact>
39
+ <cbc:Name>Mr B. Badger</cbc:Name>
40
+ </cac:Contact>
41
+ </cac:Party>
42
+ </cac:AccountingCustomerParty>
43
+ <cac:AccountingSupplierParty>
44
+ <cbc:CustomerAssignedAccountID>2</cbc:CustomerAssignedAccountID>
45
+ <cac:Party>
46
+ <cac:PartyName>
47
+ <cbc:Name>Lovely Customer Inc.</cbc:Name>
48
+ </cac:PartyName>
49
+ <cac:PostalAddress>
50
+ <cbc:StreetName>The pasture</cbc:StreetName>
51
+ <cbc:CityName>Mootown</cbc:CityName>
52
+ <cbc:PostalZone>MOOO</cbc:PostalZone>
53
+ <cbc:CountrySubentity>Cow Kingdom</cbc:CountrySubentity>
54
+ <cac:Country>
55
+ <cbc:IdentificationCode>GB</cbc:IdentificationCode>
56
+ <cbc:Name>Scotland</cbc:Name>
57
+ </cac:Country>
58
+ </cac:PostalAddress>
59
+ <cac:PartyTaxScheme>
60
+ <cbc:CompanyID>987654321</cbc:CompanyID>
61
+ <cac:TaxScheme>
62
+ <cbc:ID>VAT</cbc:ID>
63
+ </cac:TaxScheme>
64
+ </cac:PartyTaxScheme>
65
+ <cac:Contact>
66
+ <cbc:Name>Fred</cbc:Name>
67
+ </cac:Contact>
68
+ </cac:Party>
69
+ </cac:AccountingSupplierParty>
70
+ <cac:PaymentTerms>
71
+ <cac:SettlementPeriod>
72
+ <cbc:StartDate>2009-01-01</cbc:StartDate>
73
+ <cbc:StartTime>00:00:00+00:00</cbc:StartTime>
74
+ <cbc:EndDate>2009-01-31</cbc:EndDate>
75
+ <cbc:EndTime>00:00:00+00:00</cbc:EndTime>
76
+ </cac:SettlementPeriod>
77
+ </cac:PaymentTerms>
78
+ <cac:TaxTotal>
79
+ <cbc:TaxAmount currencyID="GBP">18.52</cbc:TaxAmount>
80
+ </cac:TaxTotal>
81
+ <cac:LegalMonetaryTotal>
82
+ <cbc:TaxExclusiveAmount currencyID="GBP">123.45</cbc:TaxExclusiveAmount>
83
+ <cbc:PayableAmount currencyID="GBP">141.97</cbc:PayableAmount>
84
+ </cac:LegalMonetaryTotal>
85
+ <cac:InvoiceLine>
86
+ <cbc:ID>3</cbc:ID>
87
+ <cbc:UUID>0cc66060-cfac-012b-481d-0017f22d32c0</cbc:UUID>
88
+ <cbc:InvoicedQuantity>1.0</cbc:InvoicedQuantity>
89
+ <cbc:LineExtensionAmount currencyID="GBP">123.45</cbc:LineExtensionAmount>
90
+ <cbc:TaxPointDate>2009-01-01</cbc:TaxPointDate>
91
+ <cac:TaxTotal>
92
+ <cbc:TaxAmount currencyID="GBP">18.52</cbc:TaxAmount>
93
+ </cac:TaxTotal>
94
+ <cac:Item>
95
+ <cbc:Description>moo</cbc:Description>
96
+ </cac:Item>
97
+ </cac:InvoiceLine>
98
+ </ubl:SelfBilledInvoice>
@@ -0,0 +1,36 @@
1
+ <h1 class="invoice">Invoice</h1>
2
+ <table class="invoice metadata">
3
+ <tr class="identifier">
4
+ <th>Invoice no.:</th>
5
+ <td></td>
6
+ </tr>
7
+ </table>
8
+ <p class="invoice description">foo</p>
9
+ <table class="invoice line-items">
10
+ <tr>
11
+ <th class="tax-point">Tax point</th>
12
+ <th class="quantity">Quantity</th>
13
+ <th class="description">Description</th>
14
+ <th class="net-amount">Net price</th>
15
+ <th class="tax-amount">VAT</th>
16
+ <th class="gross-amount">Gross price</th>
17
+ </tr>
18
+ <tr>
19
+ <td class="tax-point"></td>
20
+ <td class="quantity"></td>
21
+ <td class="description">moo</td>
22
+ <td class="net-amount">—</td>
23
+ <td class="tax-amount">—</td>
24
+ <td class="gross-amount">—</td>
25
+ </tr>
26
+ <tr class="subtotal">
27
+ <th colspan="3">Subtotal</th>
28
+ <td class="net-amount">Net: —</td>
29
+ <td class="tax-amount">VAT: —</td>
30
+ <td class="gross-amount"></td>
31
+ </tr>
32
+ <tr class="total">
33
+ <th colspan="5">Total</th>
34
+ <td class="total-amount">—</td>
35
+ </tr>
36
+ </table>
@@ -0,0 +1,69 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), 'test_helper.rb')
4
+
5
+ class RenderHTMLTest < Test::Unit::TestCase
6
+
7
+ def reference_output(filename)
8
+ IO.readlines(File.join(File.dirname(__FILE__), 'ref-output', filename)).join
9
+ end
10
+
11
+ def test_render_default_html_invoice
12
+ assert_equal reference_output('invoice1.html'), MyInvoice.find(1).render_html
13
+ end
14
+
15
+ def test_render_self_billed_html_invoice
16
+ assert_equal reference_output('invoice2.html'), MyInvoice.find(2).render_html
17
+ end
18
+
19
+ def test_render_html_credit_note
20
+ #File.open(File.join(File.dirname(__FILE__), 'ref-output', 'debug3.html'), 'w') do |f|
21
+ # f.syswrite(MyCreditNote.find(3).render_html)
22
+ #end
23
+ assert_equal reference_output('creditnote3.html'), MyCreditNote.find(3).render_html
24
+ end
25
+
26
+ def test_render_with_custom_fragments
27
+ expected = reference_output('invoice1.html').split("\n")[0..60]
28
+ expected[0] = "<h1>INVOICE</h1>"
29
+ expected[3] = " <th class=\"recipient\">Customer</th>"
30
+ expected[4] = " <th class=\"sender\">Supplier</th>"
31
+ rendered = MyInvoice.find(1).render_html {|i|
32
+ i.invoice_label{ "INVOICE" }
33
+ i.sender_label "Supplier"
34
+ i.recipient_label "Customer"
35
+ i.title_tag {|param| "<h1>#{param[:title]}</h1>\n" }
36
+ i.line_items_table {|param| ""}
37
+ }
38
+ assert_equal expected.join("\n") + "\n", rendered
39
+ end
40
+
41
+ def test_render_empty_invoice
42
+ invoice = MyInvoice.new
43
+ invoice.line_items2 << SuperLineItem.new
44
+ invoice.save!
45
+ invoice.tax_amount2 = nil
46
+ invoice.total_amount2 = nil
47
+ rendered = invoice.render_html({:tax_point_column => true, :quantity_column => true,
48
+ :description_column => true, :net_amount_column => true, :tax_amount_column => true,
49
+ :gross_amount_column => true}) {|i| i.addresses_table{|x| ""}; i.description "foo" }
50
+ assert_equal reference_output('invoice_null.html'), rendered
51
+ end
52
+
53
+ def test_render_with_null_fragment
54
+ assert_raise ArgumentError do
55
+ MyInvoice.find(1).render_html do |i|
56
+ i.invoice_label
57
+ end
58
+ end
59
+ end
60
+
61
+ def test_render_with_too_many_fragments
62
+ assert_raise ArgumentError do
63
+ MyInvoice.find(1).render_html do |i|
64
+ i.invoice_label "a", "b"
65
+ end
66
+ end
67
+ end
68
+
69
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), 'test_helper.rb')
4
+
5
+ class RenderUBLTest < Test::Unit::TestCase
6
+
7
+ def reference_output(filename)
8
+ IO.readlines(File.join(File.dirname(__FILE__), 'ref-output', filename)).join
9
+ end
10
+
11
+ def test_render_ubl_invoice
12
+ assert_equal reference_output('invoice1.xml'), MyInvoice.find(1).render_ubl
13
+ end
14
+
15
+ def test_render_ubl_self_billed_invoice
16
+ assert_equal reference_output('invoice2.xml'), MyInvoice.find(2).render_ubl
17
+ end
18
+
19
+ def test_render_ubl_credit_note
20
+ #File.open(File.join(File.dirname(__FILE__), 'ref-output', 'debug3.xml'), 'w') do |f|
21
+ # f.syswrite(MyCreditNote.find(3).render_ubl)
22
+ #end
23
+ assert_equal reference_output('creditnote3.xml'), MyCreditNote.find(3).render_ubl
24
+ end
25
+
26
+ def test_cannot_render_unknown_ledger_item_subtype
27
+ assert_raise RuntimeError do
28
+ CorporationTaxLiability.find(6).render_ubl
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,37 @@
1
+ # This file is silently executed before the entire test suite runs, when run by 'rake test'.
2
+ # To see its output, set the environment variable VERBOSE=1
3
+
4
+ require File.join(File.dirname(__FILE__), "test_helper.rb")
5
+
6
+ connection = ActiveRecord::Base.connection
7
+
8
+ Dir.glob(File.join(File.dirname(__FILE__), 'fixtures', '*.sql')) do |filename|
9
+ file = File.new(File.expand_path(filename))
10
+
11
+ command = ''
12
+ file.each do |line|
13
+
14
+ # Hacks to make fixture loading work with postgres. Very very ugly. Sorry :-(
15
+ if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
16
+ line.gsub!(/datetime/, 'timestamp')
17
+ line.gsub!(/tinyint\(1\)/, 'boolean')
18
+ line.gsub!(/0(\).) \-\- false/, 'false\1')
19
+ line.gsub!(/1(\).) \-\- true/, 'true\1')
20
+ line.gsub!(/int primary key auto_increment/, 'serial primary key')
21
+ line.gsub!(/ENGINE=.*;/, ';')
22
+ else
23
+ line.gsub!(/ALTER SEQUENCE .*/, '')
24
+ end
25
+
26
+ line.gsub!(/\-\-.*/, '') # ignore comments
27
+
28
+ if line =~ /(.*);\s*\Z/ # cut off semicolons at the end of a command
29
+ command += ' ' + $1
30
+ puts command.strip
31
+ connection.execute command
32
+ command = ''
33
+ else
34
+ command += ' ' + line.strip
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,9 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper.rb')
2
+
3
+ class TaxRateTest < Test::Unit::TestCase
4
+
5
+ def test_should_be_true
6
+ assert_equal(1,1)
7
+ end
8
+
9
+ end
@@ -0,0 +1,180 @@
1
+ # encoding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), 'test_helper.rb')
4
+
5
+ class TaxableTest < Test::Unit::TestCase
6
+
7
+ class SimpleTaxLogic
8
+ def apply_tax(params)
9
+ if params[:attribute].to_s == 'amount'
10
+ params[:value] * (BigDecimal('1.0') + params[:model_object].tax_factor)
11
+ else
12
+ params[:value]
13
+ end
14
+ end
15
+
16
+ def remove_tax(params)
17
+ if params[:attribute].to_s == 'amount'
18
+ params[:value] / (BigDecimal('1.0') + params[:model_object].tax_factor)
19
+ else
20
+ params[:value]
21
+ end
22
+ end
23
+
24
+ def tax_info(params)
25
+ if params[:attribute].to_s == 'amount'
26
+ "(inc. tax)"
27
+ else
28
+ ""
29
+ end
30
+ end
31
+
32
+ def tax_details(params)
33
+ if params[:attribute].to_s == 'amount'
34
+ "(including #{sprintf('%.2f', 100*params[:model_object].tax_factor)}% tax)"
35
+ else
36
+ "(tax not applicable)"
37
+ end
38
+ end
39
+
40
+ def some_other_method(options, param1, param2)
41
+ param1 * param2 + options[:model_object].id
42
+ end
43
+
44
+ def mixin_methods
45
+ [:some_other_method]
46
+ end
47
+ end
48
+
49
+
50
+ class TaxableRecord < ActiveRecord::Base
51
+ validates_numericality_of :amount
52
+ acts_as_taxable :amount, :gross_amount, :tax_logic => SimpleTaxLogic.new, :currency => :currency_code
53
+ end
54
+
55
+ class NonsenseClass < ActiveRecord::Base
56
+ set_table_name 'taxable_record'
57
+ end
58
+
59
+
60
+ ######################################################################
61
+
62
+ def test_raises_error_if_no_tax_logic_is_specified
63
+ assert_raise ArgumentError do
64
+ NonsenseClass.class_eval do
65
+ acts_as_taxable :amount
66
+ end
67
+ end
68
+ end
69
+
70
+ def test_apply_tax_on_existing_record
71
+ record = TaxableRecord.find(1)
72
+ assert_equal BigDecimal('141.09'), record.amount_taxed
73
+ assert_equal BigDecimal('123.45'), record.amount
74
+ end
75
+
76
+ def test_apply_tax_on_new_record
77
+ record = TaxableRecord.new(:amount => '200', :tax_factor => '0.4', :currency_code => 'USD')
78
+ assert_equal BigDecimal('280'), record.amount_taxed
79
+ assert_equal BigDecimal('200'), record.amount
80
+ assert_equal '$280.00', record.amount_taxed_formatted
81
+ assert_equal '$200.00', record.amount_formatted
82
+ assert_equal '200', record.amount_before_type_cast
83
+ record.save!
84
+ assert_equal([{'amount' => '200.0000'}],
85
+ ActiveRecord::Base.connection.select_all("SELECT amount FROM taxable_records WHERE id=#{record.id}"))
86
+ end
87
+
88
+ def test_remove_tax_on_existing_record
89
+ record = TaxableRecord.find(1)
90
+ record.amount_taxed = 114.29
91
+ assert_equal BigDecimal('100.00'), record.amount
92
+ assert_equal BigDecimal('114.29'), record.amount_taxed
93
+ assert_equal '£100.00', record.amount_formatted
94
+ assert_equal '£114.29', record.amount_taxed_formatted
95
+ assert_equal 114.29, record.amount_taxed_before_type_cast
96
+ record.save!
97
+ assert_equal([{'amount' => '100.0000'}],
98
+ ActiveRecord::Base.connection.select_all("SELECT amount FROM taxable_records WHERE id=1"))
99
+ end
100
+
101
+ def test_remove_tax_on_new_record
102
+ record = TaxableRecord.new(:amount_taxed => '360', :tax_factor => '0.2', :currency_code => 'USD')
103
+ assert_equal BigDecimal('300'), record.amount
104
+ assert_equal BigDecimal('360'), record.amount_taxed
105
+ assert_equal '$300.00', record.amount_formatted
106
+ assert_equal '$360.00', record.amount_taxed_formatted
107
+ assert_equal '360', record.amount_taxed_before_type_cast
108
+ record.save!
109
+ assert_equal([{'amount' => '300.0000'}],
110
+ ActiveRecord::Base.connection.select_all("SELECT amount FROM taxable_records WHERE id=#{record.id}"))
111
+ end
112
+
113
+ def test_assign_taxed_then_untaxed
114
+ record = TaxableRecord.find(1)
115
+ record.amount_taxed = '333.33'
116
+ record.amount = '1210.11'
117
+ assert_equal BigDecimal('1382.98'), record.amount_taxed
118
+ assert_equal BigDecimal('1210.11'), record.amount
119
+ assert_equal '1210.11', record.amount_before_type_cast
120
+ end
121
+
122
+ def test_assign_untaxed_then_taxed
123
+ record = TaxableRecord.find(1)
124
+ record.amount = '0.02'
125
+ record.amount_taxed = '1142.86'
126
+ assert_equal BigDecimal('1000.00'), record.amount
127
+ assert_equal BigDecimal('1142.86'), record.amount_taxed
128
+ assert_equal '1142.86', record.amount_taxed_before_type_cast
129
+ end
130
+
131
+ def test_no_rounding_error
132
+ record = TaxableRecord.new(:amount_taxed => 100, :tax_factor => 1.0/3.0)
133
+ assert_equal BigDecimal('0'), record.amount_tax_rounding_error
134
+ assert_equal BigDecimal('100'), record.amount_taxed
135
+ assert_equal BigDecimal('75'), record.amount
136
+ end
137
+
138
+ def test_rounding_error_high
139
+ record = TaxableRecord.new(:amount_taxed => 1.04, :tax_factor => 0.175)
140
+ assert_equal BigDecimal('0.01'), record.amount_tax_rounding_error
141
+ assert_equal BigDecimal('0.89'), record.amount
142
+ assert_equal BigDecimal('1.05'), record.amount_taxed
143
+ end
144
+
145
+ def test_rounding_error_low
146
+ record = TaxableRecord.new(:amount_taxed => 1.11, :tax_factor => 0.175)
147
+ assert_equal BigDecimal('1.10'), record.amount_taxed
148
+ assert_equal BigDecimal('0.94'), record.amount
149
+ assert_equal BigDecimal('-0.01'), record.amount_tax_rounding_error
150
+ end
151
+
152
+ def test_tax_info
153
+ record = TaxableRecord.find(1)
154
+ assert_equal "(inc. tax)", record.amount_tax_info
155
+ assert_equal "", record.gross_amount_tax_info
156
+ end
157
+
158
+ def test_tax_details
159
+ record = TaxableRecord.find(1)
160
+ assert_equal "(including 14.29% tax)", record.amount_tax_details
161
+ assert_equal "(tax not applicable)", record.gross_amount_tax_details
162
+ end
163
+
164
+ def test_with_tax_info
165
+ record = TaxableRecord.find(1)
166
+ assert_equal "£141.09 (inc. tax)", record.amount_with_tax_info
167
+ assert_equal "£141.09", record.gross_amount_with_tax_info
168
+ end
169
+
170
+ def test_with_tax_details
171
+ record = TaxableRecord.find(1)
172
+ assert_equal "£141.09 (including 14.29% tax)", record.amount_with_tax_details
173
+ assert_equal "£141.09 (tax not applicable)", record.gross_amount_with_tax_details
174
+ end
175
+
176
+ def test_other_method
177
+ record = TaxableRecord.find(1)
178
+ assert_equal 49, record.some_other_method(6, 8)
179
+ end
180
+ end