invoicing 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|