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