webgui/lib/WebGUI/Shop/Tax.pm
2009-06-20 01:45:55 +00:00

329 lines
10 KiB
Perl

package WebGUI::Shop::Tax;
=head1 LEGAL
-------------------------------------------------------------------
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
-------------------------------------------------------------------
=cut
use strict;
use Class::InsideOut qw{ :std };
use WebGUI::Exception::Shop;
use WebGUI::Shop::Admin;
use WebGUI::Pluggable;
use List::Util qw{sum};
=head1 NAME
Package WebGUI::Shop::Tax
=head1 DESCRIPTION
This package manages tax information, and calculates taxes on a shopping cart. It isn't a classic object
in that the only data it contains is a WebGUI::Session object, but it does provide several methods for
handling the information in the tax tables.
=head1 SYNOPSIS
use WebGUI::Shop::Tax;
my $tax = WebGUI::Shop::Tax->new($session);
=head1 METHODS
These subroutines are available from this package:
=cut
readonly session => my %session;
##-------------------------------------------------------------------
#sub appendSkuForm {
# my $self = shift;
# my $assetId = shift;
# my $form = shift;
# my $db = $self->session->db;
#
# my $values = $db->buildHashRef( 'select name, value from skuTaxConfiguration where assetId=?', [
# $assetId,
# ] );
#
# my $definition = $self->getDriver->skuFormDefinition;
# foreach my $fieldName (keys %{ $definition }) {
# $form->dynamicField(
# %{ $definition->{ $fieldName } },
# name => $fieldName,
# value => $values->{ $fieldName },
# );
# }
#}
#-------------------------------------------------------------------
=head2 calculate ( $cart )
Calculate the tax for the contents of the cart.
=head3 cart
An instanciated cart object.
=cut
sub calculate {
my $self = shift;
my $cart = shift;
WebGUI::Error::InvalidParam->throw(error => 'Must pass in a WebGUI::Shop::Cart object')
unless ref($cart) eq 'WebGUI::Shop::Cart';
my $book = $cart->getAddressBook;
# Fetch the default shipping address for each item in the cart that hasn't set its own.
my $shippingAddress = $book->getAddress( $cart->get('shippingAddressId') ) if $cart->get('shippingAddressId');
my $driver = $self->getDriver;
my $tax = 0;
foreach my $item (@{ $cart->getItems }) {
my $sku = $item->getSku;
my $quantity = $item->get('quantity');
my $unitPrice = $sku->getPrice;
# Check if this cart item overrides the shipping address. If it doesn't, use the default shipping address.
my $itemAddress = $shippingAddress;
if (defined $item->get('shippingAddressId')) {
$itemAddress = $book->getAddress($item->get('shippingAddressId'));
}
my $taxRate = $driver->getTaxRate( $sku, $itemAddress );
# Calc the monetary tax for the given quantity of this item and add it to the total.
$tax += $unitPrice * $quantity * $taxRate / 100;
}
return $tax;
}
#-------------------------------------------------------------------
=head2 getDriver ( [ $session ] )
Return an instance of the enabled tax driver. This method can be invoked both as class or instance method. If you
invoke this method as a class method you must pass a WebGUI::Session object.
=head3 session
A WebGUI::Session object. Required in class context, optional in instance context.
=cut
sub getDriver {
my $self = shift;
my $session = shift || $self->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 ] );
};
if ($@) {
$session->log->error("Can't instanciate tax driver [$className] because $@");
return undef;
}
return $driver;
}
#-------------------------------------------------------------------
=head2 new ( $session )
Constructor for the WebGUI::Shop::Tax. Returns a WebGUI::Shop::Tax object.
=cut
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;
$session{ id $self } = $session;
return $self;
}
#-------------------------------------------------------------------
=head2 session ( )
Accessor for the session object. Returns the session object.
=cut
#-------------------------------------------------------------------
=head2 www_do ( )
Allows tax drivers to define their own www_ methods. Pass the www_ method that must be executed in the 'do' form
var.
=cut
sub www_do {
my $self = shift;
my $session = $self->session;
my $taxDriver = $self->getDriver;
my $method = 'www_' . $session->form->process( 'do' );
return "Invalid method name" unless $method =~ m{ ^[a-zA-Z0-9_]+$ }xms;
if ( $taxDriver->can( $method ) ) {
my $output = eval{ $taxDriver->$method };
if ($@) {
$session->log->error("An error occurred while executing method [$method] on active tax driver: $@");
return "An error occurred while executing a method on a tax driver. Please consult the webgui log.";
}
else {
return $output || $self->www_manage;
}
}
return "Cannot call method [$method] on active tax driver.";
}
#-------------------------------------------------------------------
=head2 www_manage ( $status_message )
User interface to manage taxes. Provides a list of current taxes, and forms for adding
new tax info, exporting and importing sets of taxes, and deleting individual tax data.
=head3 $status_message
A message to display to the user. This is usually a problem that was found during
import.
=cut
sub www_manage {
my $self = shift;
my $status_message = shift;
my $session = $self->session;
my $admin = WebGUI::Shop::Admin->new( $session );
return $session->privilege->insufficient unless $admin->canManage;
my ($style, $url) = $session->quick( qw(style url) );
my $i18n = WebGUI::International->new( $session, 'Tax' );
my $activePlugin = $session->setting->get( 'activeTaxPlugin' );
my $plugins = $session->config->get( 'taxDrivers' );
my %options = map { $_ => $_ } @{ $plugins };
my $pluginSwitcher =
'<fieldset><legend>'.$i18n->get('Active tax plugin') . '</legend>'
. WebGUI::Form::formHeader( $session )
. WebGUI::Form::hidden( $session, { name => 'shop', value => 'tax' } )
. WebGUI::Form::hidden( $session, { name => 'method', value => 'setActivePlugin' } )
. $i18n->get('Active tax plugin') . ' '
. WebGUI::Form::selectBox( $session, { name => 'className', value => $activePlugin, options => \%options } )
. WebGUI::Form::submit( $session, { value => $i18n->get('Switch') } )
. WebGUI::Form::formFooter( $session )
. '</fieldset>'
;
# my $output;
# if ($status_message) {
# $output = qq{<div class="error">$status_message</div>};
# }
my $taxDriver = $self->getDriver;
my $output =
$pluginSwitcher
. '<fieldset><legend>Plugin configuration</legend>'
. $taxDriver->getConfigurationScreen
. '</fieldset>'
;
return $admin->getAdminConsole->render($output, $i18n->get('taxes', 'Shop'));
}
#-------------------------------------------------------------------
=head2 www_setActivePlugin ( )
Displays a warning that informs users that they're about to change the active taxing plugin. Includes a confirm and
cancel button.
=cut
sub www_setActivePlugin {
my $self = shift;
my $session = $self->session;
my $admin = WebGUI::Shop::Admin->new( $session );
return $session->privilege->insufficient unless $admin->canManage;
my $i18n = WebGUI::International->new( $session, 'Tax' );
my $message = $i18n->get('Stern tax warning');
my $proceedForm =
WebGUI::Form::formHeader( $session )
. WebGUI::Form::hidden( $session, { name => 'shop', value => 'tax' } )
. WebGUI::Form::hidden( $session, { name => 'method', value => 'setActivePluginConfirm' } )
. WebGUI::Form::hidden( $session, { name => 'className', value => $session->form->process('className') } )
. WebGUI::Form::submit( $session, { value => $i18n->get('Proceed') } )
. WebGUI::Form::formFooter( $session );
my $cancelForm =
WebGUI::Form::formHeader( $session )
. WebGUI::Form::hidden( $session, { name => 'shop', value => 'tax' } )
. WebGUI::Form::hidden( $session, { name => 'method', value => 'manage' } )
. WebGUI::Form::submit( $session, { value => $i18n->get('cancel','WebGUI'), extras => 'class="backwardButton"' } )
. WebGUI::Form::formFooter( $session );
my $output = $message . $proceedForm . $cancelForm;
return $admin->getAdminConsole->render( $output, $i18n->get('Switch tax plugin') );
}
#-------------------------------------------------------------------
=head2 www_setActivePluginConfirm ( )
Actually changes the active tax driver.
=cut
sub www_setActivePluginConfirm {
my $self = shift;
my $session = $self->session;
my $admin = WebGUI::Shop::Admin->new( $session );
return $session->privilege->insufficient unless $admin->canManage;
my $className = $session->form->process( 'className', 'className' );
#### TODO: Check aginst list of available plugins.
$session->setting->set( 'activeTaxPlugin', $className );
return $self->www_manage;
}
1;