Pluggable Tax: more POD, more tests, better code and less bugs

This commit is contained in:
Martin Kamerbeek 2009-05-06 13:33:16 +00:00
parent f3ed30d939
commit 461c80a6b8
7 changed files with 602 additions and 57 deletions

View file

@ -8,6 +8,10 @@ versions. Be sure to heed the warnings contained herein as they will
save you many hours of grief.
7.7.6
--------------------------------------------------------------------
* WebGUI now requires Business::Tax::VAT::Validation.
7.7.5
--------------------------------------------------------------------
* Due to a long standing bug in the Profile system, if the type of a

View file

@ -33,6 +33,7 @@ my $session = start();
# upgrade functions go here
addTemplateAttachmentsTable($session);
revertUsePacked( $session );
addEuVatDbColumns( $session );
finish($session);
@ -77,6 +78,16 @@ sub revertUsePacked {
print "DONE!\n" unless $quiet;
}
#----------------------------------------------------------------------------
sub addEuVatDbColumns {
my $session = shift;
print "\tAdding columns for improved VAT number checking..." unless $quiet;
$session->db->write( 'alter table tax_eu_vatNumbers add column viesErrorCode int(3) default NULL' );
print "Done\n" unless $quiet;
}
# -------------- DO NOT EDIT BELOW THIS LINE --------------------------------
#----------------------------------------------------------------------------

View file

@ -128,10 +128,10 @@ A WebGUI::Session object. Required in class context, optional in instance contex
sub getDriver {
my $self = shift;
my $session = shift || $self->session;
unless (defined $session && $session->isa("WebGUI::Session")) {
WebGUI::Error::InvalidObject->throw(expected=>"WebGUI::Session", got=>(ref $session), error=>"Need a session.");
}
WebGUI::Error::InvalidObject->throw( expected => "WebGUI::Session", got => (ref $session), error => "Need a session." )
unless $session && $session->isa( 'WebGUI::Session' );
my $className = $session->setting->get( 'activeTaxPlugin' );
my $driver = eval {
WebGUI::Pluggable::instanciate( $className, 'new', [ $session ] );
@ -155,9 +155,10 @@ Constructor for the WebGUI::Shop::Tax. Returns a WebGUI::Shop::Tax object.
sub new {
my $class = shift;
my $session = shift;
unless (defined $session && $session->isa("WebGUI::Session")) {
WebGUI::Error::InvalidObject->throw(expected=>"WebGUI::Session", got=>(ref $session), error=>"Need a session.");
}
WebGUI::Error::InvalidObject->throw( expected => "WebGUI::Session", got => (ref $session), error => "Need a session." )
unless $session && $session->isa( 'WebGUI::Session' );
my $self = {};
bless $self, $class;
register $self;

View file

@ -105,6 +105,8 @@ sub className {
my $self = shift;
$self->session->log->fatal( "Tax plugin ($self) is required to overload the className method" );
return 'WebGUI::Shop:TaxDriver';
}
#-----------------------------------------------------------
@ -214,6 +216,9 @@ sub new {
my $class = shift;
my $session = shift;
WebGUI::Error::InvalidObject->throw( expected => "WebGUI::Session", got => (ref $session), error => "Need a session." )
unless $session && $session->isa( 'WebGUI::Session' );
my $self = {};
bless $self, $class;
register $self;

View file

@ -20,6 +20,8 @@ use SOAP::Lite;
use WebGUI::Content::Account;
use WebGUI::TabForm;
use WebGUI::Utility qw{ isIn };
use Business::Tax::VAT::Validation;
use Tie::IxHash;
use base qw{ WebGUI::Shop::TaxDriver };
@ -30,7 +32,8 @@ Package WebGUI::Shop::TaxDriver::EU
=head1 DESCRIPTION
This package manages tax information, and calculates taxes on a shopping cart specifically handling
European Union VAT taxes.
European Union VAT taxes. It allows you to define VAT groups (eg. in the Netherlands there are two VAT tariffs:
high (19%) and low (6%) ) that can be applied to SKU assets.
=head1 SYNOPSIS
@ -44,7 +47,8 @@ These subroutines are available from this package:
=cut
my $EU_COUNTRIES = {
tie my %EU_COUNTRIES, 'Tie::IxHash', (
AT => 'Austria',
BE => 'Belgium',
BG => 'Bulgaria',
@ -72,7 +76,105 @@ my $EU_COUNTRIES = {
SE => 'Sweden',
SI => 'Slovenia',
SK => 'Slovakia',
};
);
#-------------------------------------------------------------------
=head2 addGroup ( name, rate )
Adds a tax group. Returns the group id.
=head3 name
The display name of the tax group.
=head3 rate
The tax rate for this group in percents.
=cut
sub addGroup {
my $self = shift;
my $name = shift;
my $rate = shift;
WebGUI::Error::InvalidParam->throw( 'A group name is required' )
unless $name;
WebGUI::Error::InvalidParam->throw( 'Group rate must be within 0 and 100' )
unless defined $rate && $rate >= 0 && $rate <= 100;
my $id = $self->session->id->generate;
my $groups = $self->get( 'taxGroups' ) || [];
push @{ $groups }, {
name => $name,
rate => $rate,
id => $id,
};
$self->update( { taxGroups => $groups } );
return $id;
}
#-------------------------------------------------------------------
=head2 addVATNumber ( VATNumber, localCheckOnly )
Adds a VAT number to the database. Checks the number through the VIES database. Returns and error message if a
validation error occurred. If the number validates undef is returned.
=head3 VATNumber
The number that is to be added.
=head3 user
The user for which the number should be added. Defaults to the session user.
=head3 localCheckOnly
If set to a true value the the remote VAT number validation in the VIES database will not be preformed. The VAT
number will be checked against regexes, however. Mostly convenient for testing purposes.
=cut
sub addVATNumber {
my $self = shift;
my $number = shift;
my $user = shift || $self->session->user;
my $localCheckOnly = shift;
my $db = $self->session->db;
WebGUI::Error::InvalidParam->throw( 'A VAT number is required' )
unless $number;
WebGUI::Error::InvalidParam->throw( 'The second argument must be an instanciated WebGUI::User object' )
unless ref $user eq 'WebGUI::User';
WebGUI::Error::InvalidParam->throw( 'Visitor cannot add VAT numbers' )
if $user->isVisitor;
# Check number
my $validator = Business::Tax::VAT::Validation->new;
my $numberIsValid = $localCheckOnly ? $validator->local_check( $number ) : $validator->check( $number );
# Number contains syntax error does not exist. Do not write the code to the db.
if ( !$numberIsValid && $validator->get_last_error_code <= 16 ) {
return 'The entered VAT number is invalid.';
}
# Write the code to the db.
$db->write( 'replace into tax_eu_vatNumbers (userId,countryCode,vatNumber,approved,viesErrorCode) values (?,?,?,?,?)', [
$user->userId,
substr( $number, 0 , 2 ),
$number,
$numberIsValid ? 1 : 0,
$numberIsValid ? undef : $validator->get_last_error_code,
] );
return $numberIsValid ? undef : 'Number validation currently not available. Check later.';
}
#-------------------------------------------------------------------
@ -86,6 +188,59 @@ sub className {
return 'WebGUI::Shop::TaxDriver::EU';
}
#-------------------------------------------------------------------
=head2 deleteGroup ( groupId )
Deletes a tax group.
=head3 groupId
The id of the tax group that is to be deleted.
=cut
sub deleteGroup {
my $self = shift;
my $removeGroupId = shift;
WebGUI::Error::InvalidParam->throw( 'A group id is required' )
unless $removeGroupId;
my $taxGroups = $self->get( 'taxGroups' );
my @newGroups = grep { $_->{ id } ne $removeGroupId } @{ $taxGroups };
$self->update( { taxGroups => \@newGroups } );
}
#-----------------------------------------------------------
=head2 deleteVATNumber ( VATNumber, [ user ] )
Deletes a VAT number.
=head3 VATNumber
The VATNumber to delete.
=head3 user
The user whose VATNumber must be deleted, in the form of a WebGUI::User object.
=cut
sub deleteVATNumber {
my $self = shift;
my $number = shift;
my $user = shift || $self->session->user;
my $session = $self->session;
$session->db->write( 'delete from tax_eu_vatNumbers where userId=? and vatNumber=?', [
$user->userId,
$number,
] );
}
#-----------------------------------------------------------
=head2 getConfigurationScreen ( )
@ -99,7 +254,12 @@ sub getConfigurationScreen {
my $session = $self->session;
my $taxGroups = $self->get( 'taxGroups' ) || [];
tie my %countryOptions, 'Tie::IxHash', (
'' => ' - select a country - ',
%EU_COUNTRIES,
);
# General setting form
my $f = WebGUI::HTMLForm->new( $session );
$f->hidden(
@ -119,7 +279,7 @@ sub getConfigurationScreen {
value => $self->get( 'shopCountry' ),
label => 'Residential country',
hoverHelp => 'The country where your shop resides.',
options => $EU_COUNTRIES,
options => \%countryOptions,
);
$f->submit;
my $general = $f->print;
@ -203,12 +363,12 @@ sub getCountryCode {
my $countryName = shift;
# Do reverse lookup on eu countries hash
return { reverse %{ $EU_COUNTRIES } }->{ $countryName };
return { reverse %EU_COUNTRIES }->{ $countryName };
}
#-------------------------------------------------------------------
=head2 getCountryName ($countryCode)
=head2 getCountryName ( $countryCode )
Given a 2 character country code, return the name of the country.
@ -222,15 +382,19 @@ sub getCountryName {
my $self = shift;
my $countryCode = shift;
return $EU_COUNTRIES->{ $countryCode };
return $EU_COUNTRIES{ $countryCode };
}
#-------------------------------------------------------------------
=head2 getGroupRate ($taxGroupId)
=head2 getGroupRate ( $taxGroupId )
Returns the tax rate for a given tax group.
=head3 $taxGroupId
The id of the tax group whose rate should be returned.
=cut
sub getGroupRate {
@ -303,7 +467,7 @@ sub getUserScreen {
#-------------------------------------------------------------------
=head2 getTaxRate ( sku, [ address ] )
=head2 getTaxRate ( sku, [ address, user ] )
Returns the tax rate in percents (eg. 19 for a rate of 19%) for the given sku and shipping address. Implements
EU VAT taxes and group rates.
@ -315,6 +479,11 @@ sub getTaxRate {
my $sku = shift;
my $address = shift;
WebGUI::Error::InvalidParam->throw(error => 'Must pass in a WebGUI::Asset::Sku object')
unless $sku && $sku->isa( 'WebGUI::Asset::Sku' );
WebGUI::Error::InvalidParam->throw(error => 'Must pass in a WebGUI::Shop::Address object')
if $address && !$address->isa( 'WebGUI::Shop::Address' );
my $config = $sku->getTaxConfiguration( $self->className );
# Fetch the tax group from the sku. If the sku has none, use the default tax group.
@ -340,26 +509,34 @@ sub getTaxRate {
#-------------------------------------------------------------------
=head2 getVATNumbers ($countryCode)
=head2 getVATNumbers ( $countryCode )
Returns an array ref of hash refs containing the properties of the VAT numbers a user s registered for a given
country. Returns an empty array ref if the user has no VAT numbers in the requested country.
The hash keys of interest for most people are vatNumber, which contains the actual number, and approved, which
indicates whether or not the number has been approved for use yet.
=head3 $countryCode
The two letter country code of the country the VAT numbers are requested for.
=cut
sub getVATNumbers {
my $self = shift;
my $countryCode = shift;
my $session = $self->session;
my $user = shift || $self->session->user;
my $sql = 'select * from tax_eu_vatNumbers where userId=?';
my $placeHolders = [ $session->user->userId ];
my $placeHolders = [ $user->userId ];
if ( $countryCode ) {
$sql .= ' and countryCode=?';
push @{ $placeHolders }, $countryCode;
}
my $numbers = $session->db->buildArrayRefOfHashRefs( $sql, $placeHolders );
my $numbers = $self->session->db->buildArrayRefOfHashRefs( $sql, $placeHolders );
return $numbers;
}
@ -368,8 +545,12 @@ sub getVATNumbers {
=head2 hasVATNumber ($countrycode)
Returns a boolean indicating whether or not the user has VAT numbers registered for the given country.
=head3 $countryCode
The two letter country code of contry for which the existance of VAT numbers is requested.
=cut
sub hasVATNumber {
@ -417,6 +598,8 @@ sub skuFormDefinition {
=head2 www_addGroup
Adds a VAT group.
=cut
sub www_addGroup {
@ -425,18 +608,7 @@ sub www_addGroup {
return $self->session->privilege->insufficient unless $self->canManage;
my $groups = $self->get( 'taxGroups' ) || [];
my $name = $form->process( 'name' );
my $rate = $form->process( 'rate' );
my $id = $self->session->id->generate;
push @{ $groups }, {
name => $name,
rate => $rate,
id => $id,
};
$self->update( { taxGroups => $groups } );
$self->addGroup( $form->process( 'name' ), $form->process( 'rate' ) );
return '';
}
@ -445,6 +617,11 @@ sub www_addGroup {
=head2 www_addVATNumber
Allows a user to add a VAT number. The validity of VAT numbers will be automatically checked using the VIES service
provided by the European Union. See http://ec.europa.eu/taxation_customs/vies/vieshome.do for more information
conerning the service. Please also read the disclamer information located at
http://ec.europa.eu/taxation_customs/vies/viesdisc.do.
=cut
sub www_addVATNumber {
@ -457,22 +634,12 @@ sub www_addVATNumber {
my $vatNumber = uc $form->process( 'vatNumber' );
my ($countryCode, $number) = $vatNumber =~ m/^([A-Z]{2})([A-Z0-9]+)$/;
return 'Illegal country code' unless isIn( $countryCode, keys %{ $EU_COUNTRIES } );
return 'Illegal country code' unless isIn( $countryCode, keys %EU_COUNTRIES );
return 'You already have a VAT number for this country.' if @{ $self->getVATNumbers( $countryCode ) };
# Check VAT number via SOAP interface.
# TODO: Handle timeouts.
my $soap = SOAP::Lite->service('http://ec.europa.eu/taxation_customs/vies/api/checkVatPort?wsdl');
my $isValid = ( $soap->checkVat( $countryCode, $number ) )[ 3 ] || 0;
# Write the code to the db.
$db->write( 'replace into tax_eu_vatNumbers (userId,countryCode,vatNumber,approved) values (?,?,?,?)', [
$self->session->user->userId,
$countryCode,
$vatNumber,
$isValid,
] );
#### TODO: Handle errorMessage.
my $errorMessage = $self->addVATNumber( $vatNumber );
my $instance = WebGUI::Content::Account->createInstance($session,"shop");
return $instance->displayContent( $instance->callMethod("manageTaxData", [], $session->user->userId) );
@ -482,6 +649,8 @@ sub www_addVATNumber {
=head2 www_deleteGroup
Deletes a VAT group.
=cut
sub www_deleteGroup {
@ -490,11 +659,7 @@ sub www_deleteGroup {
return $self->session->privilege->insufficient unless $self->canManage;
my $taxGroups = $self->get( 'taxGroups' );
my $removeGroupId = $form->process( 'groupId' );
my @newGroups = grep { $_->{ id } ne $removeGroupId } @{ $taxGroups };
$self->update( { taxGroups => \@newGroups } );
$self->deleteGroup( $form->process( 'groupId' ) );
return '';
}
@ -503,19 +668,18 @@ sub www_deleteGroup {
=head2 www_deleteVATNumber
Deletes a VAT number.
=cut
sub www_deleteVATNumber {
my $self = shift;
my $session = $self->session;
return $session->privilege->insufficient unless $session->user->isVisitor;
$session->db->write( 'delete from tax_eu_vatNumbers where userId=? and vatNumber=?', [
$session->user->userId,
$session->form->process( 'vatNumber' ),
] );
return $session->privilege->insufficient if $session->user->isVisitor;
$self->deleteVATNumber( $session->form->process( 'vatNumber' ) );
my $instance = WebGUI::Content::Account->createInstance($session,"shop");
return $instance->displayContent( $instance->callMethod("manageTaxData", [], $session->user->userId) );
}
@ -524,6 +688,8 @@ sub www_deleteVATNumber {
=head2 www_saveConfiguration
Updates the configuration properties for this plugin, as passed by the form on the configuration screen.
=cut
sub www_saveConfiguration {
@ -543,6 +709,8 @@ sub www_saveConfiguration {
=head2 www_setDefaultGroup
Sets a VAT group to be used as default for SKU's that do not have a VAT group defined yet.
=cut
sub www_setDefaultGroup {

View file

@ -126,6 +126,7 @@ checkModule("Clone", "0.31" );
checkModule('HTML::Packer', "0.4" );
checkModule('JavaScript::Packer', '0.02' );
checkModule('CSS::Packer', '0.2' );
checkModule('Business::Tax::VAT::Validation', '0.20' );
failAndExit("Required modules are missing, running no more checks.") if $missingModule;

355
t/Shop/TaxDriver/EU.t Normal file
View file

@ -0,0 +1,355 @@
# vim:syntax=perl
#-------------------------------------------------------------------
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
#-------------------------------------------------------------------
# Please read the legal notices (docs/legal.txt) and the license
# (docs/license.txt) that came with this distribution before using
# this software.
#------------------------------------------------------------------
# http://www.plainblack.com info@plainblack.com
#------------------------------------------------------------------
# Write a little about what this script tests.
#
#
use FindBin;
use strict;
use lib "$FindBin::Bin/../../lib";
use Test::More;
use Test::Deep;
use Exception::Class;
use Data::Dumper;
use WebGUI::Test; # Must use this before any other WebGUI modules
use WebGUI::Session;
use WebGUI::Text;
use WebGUI::Shop::Cart;
use WebGUI::Shop::AddressBook;
#----------------------------------------------------------------------------
# Init
my $session = WebGUI::Test->session;
my $taxUser = WebGUI::User->new( $session, 'new' );
$taxUser->username( 'MrEvasion' );
#----------------------------------------------------------------------------
# Tests
my $tests = 44;
plan tests => 1 + $tests;
#----------------------------------------------------------------------------
# put your tests here
my $loaded = use_ok('WebGUI::Shop::TaxDriver::EU');
SKIP: {
skip 'Unable to load module WebGUI::Shop::TaxDriver::EU', $tests unless $loaded;
#######################################################################
#
# new
#
#######################################################################
my $taxer = WebGUI::Shop::TaxDriver::EU->new($session);
isa_ok($taxer, 'WebGUI::Shop::TaxDriver::EU');
isa_ok($taxer->session, 'WebGUI::Session', 'session method returns a session object');
is($session->getId, $taxer->session->getId, 'session method returns OUR session object');
#######################################################################
#
# className
#
#######################################################################
is( $taxer->className, 'WebGUI::Shop::TaxDriver::EU', 'className returns correct class name' );
#######################################################################
#
# getConfigurationScreen
#
#######################################################################
#### TODO: Figure out how to test this.
#######################################################################
#
# getCountryCode
#
#######################################################################
is( $taxer->getCountryCode( 'Netherlands' ), 'NL', 'getCountryCode returns correct code for country inside EU.' );
is( $taxer->getCountryCode( 'United States' ), undef, 'getCountryCode returns undef for countries outside EU.' );
#######################################################################
#
# getCountryName
#
#######################################################################
is( $taxer->getCountryName( 'NL' ), 'Netherlands', 'getCountryName returns correct name for country code within EU.' );
is( $taxer->getCountryName( 'US' ), undef, 'getCountryName returns undef for county codes outside EU.' );
#######################################################################
#
# addVATNumber
#
#######################################################################
$session->user( {userId=>$taxUser->userId} );
my $testVAT_NL = 'NL123456789B12';
my $testVAT_BE = 'BE0123456789';
my $invalidVAT = 'ByNoMeansAllowed';
my $visitorUser = WebGUI::User->new( $session, 1 );
eval { $taxer->addVATNumber };
my $e = Exception::Class->caught();
isa_ok( $e, 'WebGUI::Error::InvalidParam', 'A VAT number is required' );
is( $e, 'A VAT number is required', 'addVATNumber returns correct message for missing VAT number' );
eval { $taxer->addVATNumber( $testVAT_NL, 'NotAUserObject' ) };
$e = Exception::Class->caught();
isa_ok( $e, 'WebGUI::Error::InvalidParam', 'Second argument must be a user object' );
is( $e, 'The second argument must be an instanciated WebGUI::User object', 'addVATNumber returns correct message when user object is of wrong type' );
eval { $taxer->addVATNumber( $testVAT_NL, $visitorUser ) };
$e = Exception::Class->caught();
isa_ok( $e, 'WebGUI::Error::InvalidParam', 'User may not be visitor' );
is( $e, 'Visitor cannot add VAT numbers', 'addVATNumber returns correct message when user is visitor' );
my $response = $taxer->addVATNumber( $invalidVAT, $taxUser, 1 );
is( $response, 'The entered VAT number is invalid.', 'Invalid VAT numbers return an error message' );
my $responseNL = $taxer->addVATNumber( $testVAT_NL, $taxUser, 1 );
my $responseBE = $taxer->addVATNumber( $testVAT_BE, $taxUser, 1 );
ok( !defined $responseNL && !defined $responseBE, 'Valid VAT numbers return undef.' );
#######################################################################
#
# getVATNumbers
#
#######################################################################
my $expectNL = {
userId => $taxUser->userId,
countryCode => 'NL',
vatNumber => $testVAT_NL,
approved => 1,
viesErrorCode => undef,
};
my $expectBE = {
userId => $taxUser->userId,
countryCode => 'BE',
vatNumber => $testVAT_BE,
approved => 1,
viesErrorCode => undef,
};
my $vatNumbers = $taxer->getVATNumbers( undef, $taxUser );
cmp_bag( $vatNumbers, [ $expectNL, $expectBE ], 'VAT Numbers are correctly returned by getVATNumbers' );
$vatNumbers = $taxer->getVATNumbers( 'BE', $taxUser );
cmp_bag( $vatNumbers, [ $expectBE ], 'getVATNumbers filters on country code when one is passed' );
#######################################################################
#
# deleteVATNumber
#
#######################################################################
$taxer->deleteVATNumber( $testVAT_BE, $taxUser );
$vatNumbers = $taxer->getVATNumbers( undef, $taxUser );
cmp_bag( $vatNumbers, [ $expectNL ], 'deleteVATNumber deletes number' );
$taxer->deleteVATNumber( $testVAT_NL, $taxUser );
#######################################################################
#
# addGroupRate
#
#######################################################################
eval { $taxer->addGroup };
$e = Exception::Class->caught();
isa_ok( $e, 'WebGUI::Error::InvalidParam', 'addGroup requires a group name' );
is( $e, 'A group name is required', 'addGroup returns correct message for omitted group name' );
eval { $taxer->addGroup( 'Dummy' ) };
$e = Exception::Class->caught();
isa_ok( $e, 'WebGUI::Error::InvalidParam', 'addGroup requires a tax rate' );
is( $e, 'Group rate must be within 0 and 100', 'addGroup returns correct message on omitted tax rate' );
eval { $taxer->addGroup( 'Dummy', -1 ) };
$e = Exception::Class->caught();
isa_ok( $e, 'WebGUI::Error::InvalidParam', 'addGroup: tax rate cannot be < 0' );
is( $e, 'Group rate must be within 0 and 100', 'addGroup returns correct message on tax rate < 0' );
eval { $taxer->addGroup( 'Dummy', 101 ) };
$e = Exception::Class->caught();
isa_ok( $e, 'WebGUI::Error::InvalidParam', 'addGroup: tax rate cannot be > 100' );
is( $e, 'Group rate must be within 0 and 100', 'addGroup returns correct message on tax rate > 100' );
my $id0 = eval { $taxer->addGroup( 'Group0', 0 ) };
$e = Exception::Class->caught();
ok( !$e, 'addGroup: 0% is a valid group rate' );
my $id100 = eval { $taxer->addGroup( 'Group100', 100 ) };
$e = Exception::Class->caught();
ok( !$e, 'addGroup: 100% is a valid group rate' );
my $id50_5 = eval { $taxer->addGroup( 'Group50.5', 50.5 ) };
$e = Exception::Class->caught();
ok( !$e, 'addGroup: floats are a valid group rate' );
my $taxGroups = $taxer->get( 'taxGroups' );
my $expectGroups = [
{
name => 'Group0',
rate => 0,
id => $id0,
},
{
name => 'Group100',
rate => 100,
id => $id100,
},
{
name => 'Group50.5',
rate => 50.5,
id => $id50_5,
},
];
cmp_bag( $taxGroups, $expectGroups, 'addGroup saves correctly' );
#######################################################################
#
# getGroupRate
#
#######################################################################
ok( $taxer->getGroupRate( $id0 ) == 0
&& $taxer->getGroupRate( $id100 ) == 100
&& $taxer->getGroupRate( $id50_5 ) == 50.5,
'getGroup rate gets correct rates'
);
#######################################################################
#
# getTaxRate
#
#######################################################################
my $book = WebGUI::Shop::AddressBook->create($session);
# setup address in EU but not in residential country of merchant
my $beAddress = $book->addAddress({
label => 'BE',
city => 'Antwerpen',
country => 'Belgium',
});
# setup address in residential country of merchant
my $nlAddress = $book->addAddress({
label => 'NL',
city => 'Delft',
country => 'Netherlands',
});
# setup address outside EU
my $usAddress = $book->addAddress({
label => 'outside eu',
city => 'New Amsterdam',
country => 'US',
});
eval { $taxer->getTaxRate(); };
$e = Exception::Class->caught();
isa_ok($e, 'WebGUI::Error::InvalidParam', 'getTaxRate: error handling for not sending a sku');
is($e->error, 'Must pass in a WebGUI::Asset::Sku object', 'getTaxRate: error handling for not sending a sku');
# Build a cart, add some Donation SKUs to it. Set one to be taxable.
my $cart = WebGUI::Shop::Cart->newBySession( $session );
my $sku = WebGUI::Asset->getRoot($session)->addChild( {
className => 'WebGUI::Asset::Sku::Donation',
title => 'Taxable donation',
defaultPrice => 100.00,
} );
# Set defaultTaxGroup and residential country
$taxer->update( { defaultGroup => $id50_5, shopCountry => 'NL' } );
# Check default tax group
is( $taxer->getTaxRate( $sku ), 50.5, 'getTaxRate returns default tax group when no address is given and sku has no tax group set');
# Check case when no address is given
$sku->setTaxConfiguration( 'WebGUI::Shop::TaxDriver::EU', { taxGroup => $id100 } );
is( $taxer->getTaxRate( $sku ), 100, 'getTaxRate returns tax group set by sku when no address is given');
# Address outside EU
is( $taxer->getTaxRate( $sku, $usAddress ), 0, 'getTaxRate: shipping addresses outside EU are tax exempt' );
# Addresses inside EU
is( $taxer->getTaxRate( $sku, $beAddress ), 100, 'getTaxRate: shipping addresses inside EU w/o VAT number pay tax' );
is( $taxer->getTaxRate( $sku, $nlAddress ), 100, 'getTaxRate: shipping addresses in country of merchant w/o VAT number pay tax' );
# Add VAT numbers
$taxer->addVATNumber( $testVAT_NL, $taxUser, 1);
$taxer->addVATNumber( $testVAT_BE, $taxUser, 1);
is( $taxer->getTaxRate( $sku, $beAddress ), 0,
'getTaxRate: shipping addresses inside EU but other country than merchant w/ VAT number are tax exempt.'
);
is( $taxer->getTaxRate( $sku, $nlAddress ), 100, 'getTaxRate: shipping addresses in country of merchant w/ VAT number pay tax' );
#######################################################################
#
# deleteGroup
#
#######################################################################
eval { $taxer->deleteGroup };
$e = Exception::Class->caught();
isa_ok( $e, 'WebGUI::Error::InvalidParam', 'addGroup requires a group id' );
is( $e, 'A group id is required', 'addGroup returns correct message for missing group id' );
$taxer->deleteGroup( $id50_5 );
$taxGroups = $taxer->get( 'taxGroups' );
cmp_bag( $taxGroups, [
{
name => 'Group0',
rate => 0,
id => $id0,
},
{
name => 'Group100',
rate => 100,
id => $id100,
},
], 'deleteGroup deletes correctly' );
}
#----------------------------------------------------------------------------
# Cleanup
END {
$session->db->write('delete from tax_eu_vatNumbers');
$session->db->write('delete from cart');
$session->db->write('delete from addressBook');
$session->db->write('delete from address');
$taxUser->delete;
}