File Coverage

blib/lib/Business/Tax/VAT.pm
Criterion Covered Total %
statement 45 45 100.0
branch 7 8 87.5
condition 4 5 80.0
subroutine 16 16 100.0
pod 3 3 100.0
total 75 77 97.4


line stmt bran cond sub pod time code
1             package Business::Tax::VAT;
2              
3 1     1   26259 use strict;
  1         2  
  1         112  
4 1     1   5 use warnings;
  1         2  
  1         41  
5 1     1   4 use vars qw($VERSION);
  1         2  
  1         58  
6 1     1   27 use 5.008;
  1         3  
7              
8             $VERSION = '1.10';
9              
10             =begin markdown
11              
12             [![CPAN version](https://badge.fury.io/pl/Business-Tax-VAT.svg)](http://badge.fury.io/pl/Business-Tax-VAT)
13             [![Build Status](https://travis-ci.org/jonasbn/Business-Tax-VAT.svg?branch=master)](https://travis-ci.org/jonasbn/Business-Tax-VAT)
14             [![Coverage Status](https://coveralls.io/repos/jonasbn/Business-Tax-VAT/badge.png)](https://coveralls.io/r/jonasbn/Business-Tax-VAT)
15              
16             =end markdown
17              
18             =head1 NAME
19              
20             Business::Tax::VAT - perform European VAT calculations
21              
22             =head1 VERSION
23              
24             This pod describes version 1.10
25              
26             =head1 SYNOPSIS
27              
28             use Business::Tax::VAT;
29              
30             my $vat = Business::Tax::VAT->new(qw/uk ie/);
31              
32             my $price = $vat->item(120 => 'ie');
33             my $price_to_customer = $price->full; # 120
34             my $vat_charged = $price->vat; # 20
35             my $net_price_to_me = $price->net; # 100
36              
37             my $price = $vat->business_item(102 => 'uk');
38             my $price_to_customer = $price->full; # 102
39             my $vat_charged = $price->vat; # 17
40             my $net_price_to_me = $price->net; # 85
41              
42             =cut
43              
44             sub new {
45 2     2 1 12 my $class = shift;
46 2         4 my %countries = map { $_ => 1 } @_;
  3         8  
47 2         7 bless {
48             default => $_[0],
49             countries => \%countries,
50             }, $class;
51             }
52              
53 8 100   8   38 sub _is_vat_country { $_[0]->{countries}->{ lc $_[1] } || 0 }
54 1     1   4 sub _default_country { $_[0]->{default} }
55              
56 7     7 1 2830 sub item { my $self = shift; $self->_item(1, @_) }
  7         15  
57 1     1 1 424 sub business_item { my $self = shift; $self->_item(0, @_) }
  1         3  
58              
59             sub _item {
60 8     8   8 my $self = shift;
61 8         8 my $incl = shift;
62 8 50       14 my $price = shift or die "items need a price";
63 8   66     20 my $country = shift || $self->_default_country;
64 8         16 return Business::Tax::VAT::Price->new($self, $price, $country, $incl);
65             }
66              
67             package Business::Tax::VAT::Price;
68              
69 1     1   8 use vars qw($VERSION);
  1         2  
  1         527  
70             $VERSION = '1.10';
71              
72             our %RATE;
73              
74             __PACKAGE__->_calculate_vat_rates();
75              
76             sub _calculate_vat_rates {
77 1     1   22 our %RATE = (
78             at => 20, #austria
79             be => 21, #belgium
80             bg => 20, #bulgaria
81             cy => 19, #cypres
82             cz => 21, #czech republic
83             de => 19, #germany
84             dk => 25, #denmark
85             ee => 20, #estonia
86             es => 21, #spain
87             fi => 24, #finland
88             fr => 20, #france
89             gr => 23, #greece
90             hr => 25, #crotia
91             hu => 27, #hungary
92             ie => 23, #ireland
93             it => 22, #italy
94             lt => 21, #lithuania
95             lu => 17, #luxembourg
96             lv => 21, #latvia
97             mt => 18, #malta
98             nl => 21, #netherlands
99             pl => 23, #poland
100             pt => 23, #portugal
101             ro => 24, #romania
102             se => 25, #sweden
103             si => 22, #slovenia
104             sk => 20, #slovakia
105             uk => 20, #united kingdom
106             );
107             }
108              
109             sub new {
110 8     8   14 my ($class, $vat_obj, $price, $country, $incl) = @_;
111 8         9 my $self = {};
112              
113 8   100     29 my $rate = ($RATE{ lc $country } || 0) / 100;
114 8 100       15 $rate = 0 unless $vat_obj->_is_vat_country($country);
115              
116 8 100       13 if ($incl == 0) {
117 1         16 $self->{net} = $price;
118 1         2 $self->{vat} = $self->{net} * $rate;
119 1         3 $self->{full} = $self->{net} + $self->{vat};
120             } else {
121 7         10 $self->{full} = $price;
122 7         15 $self->{net} = $self->{full} / (1 + $rate);
123 7         13 $self->{vat} = $self->{full} - $self->{net};
124             }
125 8         21 bless $self, $class;
126             }
127              
128 8     8   44 sub full { $_[0]->{full} }
129 8     8   31 sub vat { $_[0]->{vat} }
130 8     8   33 sub net { $_[0]->{net} }
131              
132             =head1 DESCRIPTION
133              
134             Charging VAT across the European Union is quite complex. The rate of tax
135             you have to charge depends on whether you're dealing with consumers or
136             corporations, what types of goods you're selling, whether you've crossed
137             thresholds in certain countries, etc.
138              
139             This module aims to make some of this simpler.
140              
141             There are several key processes:
142              
143             =head1 CONSTRUCTING A VAT OBJECT
144              
145             =head2 new
146              
147             my $vat = Business::Tax::VAT->new(@country_codes);
148              
149             First of all you have to construct a VAT object, providing it with
150             a list of countries for which you have to charge VAT. This may only
151             be the country in which you are trading, or it may be any of the
152             15 EC territories in which VAT is collected.
153              
154             The full list of territories, and their abbreviations, is documented
155             below.
156              
157             =head1 PRICING AN ITEM
158              
159             =head2 item / business_item
160              
161             my $price = $vat->item($unit_price => $country_code);
162             my $price = $vat->business_item($unit_price => $country_code);
163              
164             You create a Price object by calling either the 'item' or 'business_item'
165             constructor, with the unit price, and the country to which you are
166             supplying the goods. This operates on the priciple that prices to
167             consumers are quoted with VAT included, but prices to business are
168             quoted ex-VAT.
169              
170             If you do not supply a country code, it will default to the first country
171             in the country list passed to VAT->new;
172              
173             =head1 CALCULATING THE COMPONENT PRICES
174              
175             =head2 full / vat / net
176              
177             my $price_to_customer = $price->full;
178             my $vat_charged = $price->vat;
179             my $net_price_to_me = $price->net;
180              
181             Once we have our price, we can query it for either the 'full' price
182             that will be charged (including VAT), the 'net' price (excluding VAT),
183             and the 'vat' portion itself.
184              
185             =head1 NON-VATABLE COUNTRIES
186              
187             If you send goods to many countries, some of which are in your VAT
188             territory and others not, you can avoid surrounding this code in
189             conditionals by calling $vat->item on all items anyway, listing the
190             country to which you are supplying the goods.
191              
192             If the country in question is not one of the territories in which you
193             should charge VAT then the 'full' and 'net' values will be the same,
194             and 'vat' will be zero.
195              
196             =head1 NON-VATABLE, ZERO-RATED, REDUCED-RATE GOODS
197              
198             This module does not cope with goods which are not at the 'standard'
199             rate of VAT for a country (as detailed below).
200              
201             Patches welcomed!
202              
203             =head1 COUNTRIES AND RATES
204              
205             This module uses the following rates and codes:
206              
207             at, Austria, 20%
208             be, Belgium, 21%
209             cy, Cyprus, 15%
210             cz, Czech Republic, 19%
211             dk, Denmark, 25%
212             ee, Estonia, 18%
213             fi, Finland, 22%
214             fr, France, 19.6%
215             de, Germany, 19%
216             gr, Greece, 17.5%
217             hu, Hungary, 25%
218             ie, Ireland, 21%
219             it, Italy, 22%
220             lv, Latvia, 18%
221             lt, Lithuania, 17.5%
222             lu, Luxembourg, 17%
223             mt, Malta, 18%
224             nl, The Netherlands, 19%
225             pl, Poland, 22%
226             pt, Portugal, 21%
227             sk, Slovak Republic, 19%
228             si, Slovenia, 20%
229             es, Spain, 16%
230             se, Sweden, 25%
231             uk, United Kingdom, 20%
232              
233             If any of these rates become incorrect, or if you wish to use
234             different rates due to the nature of the product (e.g. books are 0%
235             VAT in the UK), then you can (locally) set the rate by assigning to
236             %Business::Tax::VAT::Price::RATE. e.g.:
237              
238             local $Business::Tax::VAT::Price::RATE{uk} = 0
239             if ($product_type eq 'book' and $country eq 'uk');
240              
241             =head1 DYNAMICAALY ADDING A COUNTRY
242              
243             If you want to add your own country, do the following:
244              
245             my $vat = Business::Tax::VAT->new(qw/gb/);
246              
247             $Business::Tax::VAT::Price::RATE{'gb'} = 20;
248              
249             my $price = $vat->item(102 => 'gb');
250              
251             =head1 SEE ALSO
252              
253             =over
254              
255             =item * L current and historic VAT rates resource
256              
257             =back
258              
259             =head1 AUTHOR
260              
261             Tony Bowden
262              
263             =head1 BUGS and QUERIES
264              
265             Please direct all correspondence regarding this module to:
266             bug-Business-Tax-VAT@rt.cpan.org
267              
268             =head1 ACKNOWLEDGEMENTS
269              
270             =over
271              
272             =item * Documentation added in release 1.10 based on question from Tom Kirkpatrick
273              
274             =item * Sam Kington, patches leading to release of 1.07 and 1.08
275              
276             =back
277              
278             =head1 COPYRIGHT
279              
280             Copyright (C) 2001-2015 Tony Bowden.
281              
282             This program is free software; you can redistribute it and/or modify it under
283             the terms of the GNU General Public License; either version 2 of the License,
284             or (at your option) any later version.
285              
286             This program is distributed in the hope that it will be useful, but WITHOUT
287             ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
288             FOR A PARTICULAR PURPOSE.
289              
290             =cut
291              
292             1;