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,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
|
data/test/setup.rb
ADDED
@@ -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,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
|