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,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