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   70831 use strict;
  1         11  
  1         28  
4 1     1   6 use warnings;
  1         2  
  1         30  
5 1     1   6 use vars qw($VERSION);
  1         2  
  1         49  
6 1     1   25 use 5.008;
  1         3  
7              
8             $VERSION = '1.12';
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.12
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 94 my $class = shift;
46 2         5 my %countries = map { $_ => 1 } @_;
  3         10  
47 2         12 bless {
48             default => $_[0],
49             countries => \%countries,
50             }, $class;
51             }
52              
53 8 100   8   36 sub _is_vat_country { $_[0]->{countries}->{ lc $_[1] } || 0 }
54 1     1   4 sub _default_country { $_[0]->{default} }
55              
56 7     7 1 3164 sub item { my $self = shift; $self->_item(1, @_) }
  7         18  
57 1     1 1 507 sub business_item { my $self = shift; $self->_item(0, @_) }
  1         5  
58              
59             sub _item {
60 8     8   13 my $self = shift;
61 8         10 my $incl = shift;
62 8 50       21 my $price = shift or die "items need a price";
63 8   66     22 my $country = shift || $self->_default_country;
64 8         22 return Business::Tax::VAT::Price->new($self, $price, $country, $incl);
65             }
66              
67             package Business::Tax::VAT::Price;
68              
69 1     1   9 use vars qw($VERSION);
  1         1  
  1         421  
70             $VERSION = '1.12';
71              
72             our %RATE;
73              
74             __PACKAGE__->_calculate_vat_rates();
75              
76             sub _calculate_vat_rates {
77 1     1   15 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 => 24, #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 => 19, #romania
102             se => 25, #sweden
103             si => 22, #slovenia
104             sk => 20, #slovakia
105             );
106             }
107              
108             sub new {
109 8     8   18 my ($class, $vat_obj, $price, $country, $incl) = @_;
110 8         15 my $self = {};
111              
112 8   100     37 my $rate = ($RATE{ lc $country } || 0) / 100;
113 8 100       19 $rate = 0 unless $vat_obj->_is_vat_country($country);
114              
115 8 100       29 if ($incl == 0) {
116 1         2 $self->{net} = $price;
117 1         5 $self->{vat} = $self->{net} * $rate;
118 1         4 $self->{full} = $self->{net} + $self->{vat};
119             } else {
120 7         15 $self->{full} = $price;
121 7         21 $self->{net} = $self->{full} / (1 + $rate);
122 7         15 $self->{vat} = $self->{full} - $self->{net};
123             }
124 8         25 bless $self, $class;
125             }
126              
127 8     8   51 sub full { $_[0]->{full} }
128 8     8   36 sub vat { $_[0]->{vat} }
129 8     8   37 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             bg, Bulgaria 20%
209             hr, Croatia 25%
210             cy, Cyprus, 19%
211             cz, Czech Republic, 21%
212             dk, Denmark, 25%
213             ee, Estonia, 20%
214             fi, Finland, 24%
215             fr, France, 20%
216             de, Germany, 19%
217             gr, Greece, 24%
218             hu, Hungary, 27%
219             ie, Ireland, 23%
220             it, Italy, 22%
221             lv, Latvia, 21%
222             lt, Lithuania, 21%
223             lu, Luxembourg, 17%
224             mt, Malta, 18%
225             nl, The Netherlands, 21%
226             pl, Poland, 23%
227             pt, Portugal, 23%
228             ro, Romania 19%
229             sk, Slovak Republic, 20%
230             si, Slovenia, 22%
231             es, Spain, 21%
232             se, Sweden, 25%
233              
234             If any of these rates become incorrect, or if you wish to use
235             different rates due to the nature of the product (e.g. transport is 0%
236             VAT in Austria), then you can (locally) set the rate by assigning to
237             %Business::Tax::VAT::Price::RATE. e.g.:
238              
239             local $Business::Tax::VAT::Price::RATE{at} = 0
240             if ($product_type eq 'book' and $country eq 'at');
241              
242             =head1 DYNAMICALLY ADDING A COUNTRY
243              
244             If you want to add your own country, do the following:
245              
246             my $vat = Business::Tax::VAT->new(qw/gb/);
247              
248             $Business::Tax::VAT::Price::RATE{'gb'} = 20;
249              
250             my $price = $vat->item(102 => 'gb');
251              
252             =head1 SEE ALSO
253              
254             =over
255              
256             =item * L
257              
258             =item * L
259              
260             =back
261              
262             =head1 AUTHOR
263              
264             =over
265              
266             =item * Tony Bowden
267              
268             =item * Jonas B. (jonasbn), current maintainer
269              
270             =back
271              
272             =head1 BUGS and QUERIES
273              
274             Please direct all correspondence regarding this distribution to:
275              
276             =over
277              
278             =item * L
279              
280             =back
281              
282             =head1 ACKNOWLEDGEMENTS
283              
284             =over
285              
286             =item * Documentation added in release 1.10 based on question from Tom Kirkpatrick
287              
288             =item * Sam Kington, patches leading to release of 1.07 and 1.08
289              
290             =back
291              
292             =head1 COPYRIGHT
293              
294             Copyright (C) 2001-2021 Tony Bowden.
295              
296             This program is free software; you can redistribute it and/or modify it under
297             the terms of the GNU General Public License; either version 2 of the License,
298             or (at your option) any later version.
299              
300             This program is distributed in the hope that it will be useful, but WITHOUT
301             ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
302             FOR A PARTICULAR PURPOSE.
303              
304             =cut
305              
306             1;