Forgot to 'svn add' a bunch of files.
This commit is contained in:
parent
2e4ce42b76
commit
b21c5f4389
4 changed files with 1327 additions and 0 deletions
Binary file not shown.
220
lib/WebGUI/Shop/TaxDriver.pm
Normal file
220
lib/WebGUI/Shop/TaxDriver.pm
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
package WebGUI::Shop::TaxDriver;
|
||||
|
||||
use strict;
|
||||
|
||||
use Class::InsideOut qw{ :std };
|
||||
use JSON qw{ from_json to_json };
|
||||
|
||||
readonly session => my %session;
|
||||
readonly messages => my %messages;
|
||||
private options => my %options;
|
||||
|
||||
#-----------------------------------------------------------
|
||||
sub appendTaxDetailVars {
|
||||
my $self = shift;
|
||||
my $var = shift;
|
||||
|
||||
return $var;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------
|
||||
sub canManage {
|
||||
my $self = shift;
|
||||
my $admin = WebGUI::Shop::Admin->new( $self->session );
|
||||
|
||||
return $admin->canManage;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------
|
||||
|
||||
=head2 className {
|
||||
|
||||
Returns the class name of your plugin. You must overload this method in you own plugin.
|
||||
|
||||
=cut
|
||||
|
||||
sub className {
|
||||
my $self = shift;
|
||||
|
||||
$self->session->log->fatal( "Tax plugin ($self) is required to overload the className method" );
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------
|
||||
|
||||
=head2 get ( [ property ] )
|
||||
|
||||
Returns the value of the requested configuration property. Returns a hash ref of all property/value pairs when no
|
||||
specific property is passed.
|
||||
|
||||
=head3 property
|
||||
|
||||
The property whose value should be returned.
|
||||
|
||||
=cut
|
||||
|
||||
sub get {
|
||||
my $self = shift;
|
||||
my $key = shift;
|
||||
|
||||
my $options = $options{ id $self };
|
||||
|
||||
# Return safe copy of options hash if no key is passed.
|
||||
return { %{ $options } } unless $key;
|
||||
|
||||
# Return option if key is passed.
|
||||
return $options->{ $key } if exists $options->{ $key };
|
||||
|
||||
# Key does not exist.
|
||||
$self->session->log->warn( "Non-existant option [$key] was queried by tax plugin $self" );
|
||||
return undef;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------
|
||||
|
||||
=head2 getConfigurationScreen ( )
|
||||
|
||||
Returns the configuration screen that contains the configuration options for this plugin in the admin console.
|
||||
|
||||
=cut
|
||||
|
||||
sub getConfigurationScreen {
|
||||
return 'This plugin has no configuration options';
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------
|
||||
|
||||
=head2 getTaxRate ( sku, [ address ] )
|
||||
|
||||
Returns the tax rate in percents (eg. 19 for a rate of 19%) for the given sku and shipping address. Your tax driver
|
||||
must overload this method.
|
||||
|
||||
Note that address is optional and that it's up to your plugin to handle that case.
|
||||
|
||||
=head3 sku
|
||||
|
||||
The sku for which the tax rate must be determined. Should be a WebGUI::Asset::Sku::* instance.
|
||||
|
||||
=head3 address
|
||||
|
||||
Optional, the shipping address for which to calculate the tax. Must be an instance of WebGUI::Shop::Address.
|
||||
|
||||
=head
|
||||
|
||||
=cut
|
||||
|
||||
sub getTaxRate {
|
||||
my $self = shift;
|
||||
|
||||
$self->session->log->fatal("Tax plugin ". $self->className ." is required to overload getTaxRate");
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------
|
||||
|
||||
=head2 getUserScreen ( )
|
||||
|
||||
Returns the screen for entering per user configuration for this tax driver.
|
||||
|
||||
=cut
|
||||
|
||||
sub getUserScreen {
|
||||
return 'There are no tax options to configure.';
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------
|
||||
|
||||
=head2 skuFormDefinition ( )
|
||||
|
||||
Returns a hash ref containing the form defintion for the per sku options for this tax driver.
|
||||
|
||||
=cut
|
||||
|
||||
sub skuFormDefinition {
|
||||
return {};
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 new ( $session )
|
||||
|
||||
Constructor
|
||||
|
||||
=head3 session
|
||||
|
||||
Instanciated WebGUI::Session object.
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my $session = shift;
|
||||
|
||||
my $self = {};
|
||||
bless $self, $class;
|
||||
register $self;
|
||||
|
||||
my $id = id $self;
|
||||
$session{ $id } = $session;
|
||||
$messages{ $id } = [];
|
||||
|
||||
# Load plugin configuration
|
||||
my $optionsJSON = $session->db->quickScalar( 'select options from taxDriver where className=?', [
|
||||
$self->className,
|
||||
] );
|
||||
$options{ $id } = $optionsJSON ? from_json( $optionsJSON ) : {};
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 processSkuFormPost ( )
|
||||
|
||||
Processes the form parameters defined in the skuFormDefinition method and returns a hash ref containing the result.
|
||||
|
||||
=cut
|
||||
|
||||
sub processSkuFormPost {
|
||||
my $self = shift;
|
||||
my $form = $self->session->form;
|
||||
my $configuration = {};
|
||||
|
||||
my $definition = $self->skuFormDefinition;
|
||||
|
||||
foreach my $fieldName ( keys %{ $definition } ) {
|
||||
my ($fieldType, $defaultValue) = @{ $definition->{ $fieldName } }{ qw{ fieldType defaultValue } };
|
||||
|
||||
$configuration->{ $fieldName } = $form->process( $fieldName, $fieldType, $defaultValue );
|
||||
}
|
||||
|
||||
return $configuration;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------
|
||||
|
||||
=head2 update ( properties )
|
||||
|
||||
Updates the properties of the tax driver according to those passed.
|
||||
|
||||
=head3 properties
|
||||
|
||||
Hash ref containing the properties to set.
|
||||
|
||||
=cut
|
||||
|
||||
sub update {
|
||||
my $self = shift;
|
||||
my $update = shift;
|
||||
my $db = $self->session->db;
|
||||
|
||||
# update local options hash
|
||||
$options{ id $self } = { %{ $options{ id $self } }, %{ $update } };
|
||||
|
||||
# Persist to db
|
||||
$db->write( 'replace into taxDriver (className, options) values (?,?)', [
|
||||
$self->className,
|
||||
to_json( $options{ id $self } ),
|
||||
] );
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
419
lib/WebGUI/Shop/TaxDriver/EU.pm
Normal file
419
lib/WebGUI/Shop/TaxDriver/EU.pm
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
package WebGUI::Shop::TaxDriver::EU;
|
||||
|
||||
use strict;
|
||||
|
||||
use SOAP::Lite;
|
||||
use WebGUI::Content::Account;
|
||||
use WebGUI::TabForm;
|
||||
use WebGUI::Utility qw{ isIn };
|
||||
|
||||
use base qw{ WebGUI::Shop::TaxDriver };
|
||||
|
||||
my $EU_COUNTRIES = {
|
||||
AT => 'Austria',
|
||||
BE => 'Belgium',
|
||||
BG => 'Bulgaria',
|
||||
CY => 'Cyprus',
|
||||
CZ => 'Czech Republic',
|
||||
DE => 'Germany',
|
||||
DK => 'Denmark',
|
||||
EE => 'Estonia',
|
||||
EL => 'Greece',
|
||||
ES => 'Spain',
|
||||
FI => 'Finland',
|
||||
FR => 'France ',
|
||||
GB => 'United Kingdom',
|
||||
HU => 'Hungary',
|
||||
IE => 'Ireland',
|
||||
IT => 'Italy',
|
||||
LT => 'Lithuania',
|
||||
LU => 'Luxembourg',
|
||||
LV => 'Latvia',
|
||||
MT => 'Malta',
|
||||
NL => 'Netherlands',
|
||||
PL => 'Poland',
|
||||
PT => 'Portugal',
|
||||
RO => 'Romania',
|
||||
SE => 'Sweden',
|
||||
SI => 'Slovenia',
|
||||
SK => 'Slovakia',
|
||||
};
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub className {
|
||||
return 'WebGUI::Shop::TaxDriver::EU';
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub getConfigurationScreen {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
|
||||
my $taxGroups = $self->get( 'taxGroups' ) || [];
|
||||
|
||||
# General setting form
|
||||
my $f = WebGUI::HTMLForm->new( $session );
|
||||
$f->hidden(
|
||||
name => 'shop',
|
||||
value => 'tax',
|
||||
);
|
||||
$f->hidden(
|
||||
name => 'method',
|
||||
value => 'do',
|
||||
);
|
||||
$f->hidden(
|
||||
name => 'do',
|
||||
value => 'saveConfiguration',
|
||||
);
|
||||
$f->selectBox(
|
||||
name => 'shopCountry',
|
||||
value => $self->get( 'shopCountry' ),
|
||||
label => 'Residential country',
|
||||
hoverHelp => 'The country where your shop resides.',
|
||||
options => $EU_COUNTRIES,
|
||||
);
|
||||
$f->submit;
|
||||
my $general = $f->print;
|
||||
|
||||
# VAT groups manager
|
||||
my $vatGroups = '<b>VAT groups</b><br />';
|
||||
$vatGroups .= q{<table><thead><tr><th>Group name</th><th>Rate</th></tr></thead><tbody>};
|
||||
foreach my $group ( @{ $taxGroups} ) {
|
||||
my $deleteUrl = $session->url->page('shop=tax;method=do;do=deleteGroup;groupId=' . $group->{ id });
|
||||
my $makeDefaultUrl = $session->url->page('shop=tax;method=do;do=setDefaultGroup;groupId=' . $group->{ id });
|
||||
|
||||
$vatGroups .=
|
||||
q{<tr><td>}
|
||||
. join( '</td><td>',
|
||||
$group->{ name } . ( $group->{ id } eq $self->get( 'defaultGroup' ) ? '<i>(default)</i>' : '' ),
|
||||
$group->{ rate },
|
||||
qq{<a href="$deleteUrl">delete</a>},
|
||||
qq{<a href="$makeDefaultUrl">Set as default group</a>},
|
||||
)
|
||||
. q{</td></tr>};
|
||||
}
|
||||
$vatGroups .= q{</tbody></table>};
|
||||
$vatGroups .=
|
||||
WebGUI::Form::formHeader( $session )
|
||||
. WebGUI::Form::hidden( $session, { name => 'shop', value => 'tax' } )
|
||||
. WebGUI::Form::hidden( $session, { name => 'method', value => 'do' } )
|
||||
. WebGUI::Form::hidden( $session, { name => 'do', value => 'addGroup' } )
|
||||
. 'Name '
|
||||
. WebGUI::Form::text( $session, { name => 'name' } )
|
||||
. ' Rate '
|
||||
. WebGUI::Form::float( $session, { name => 'rate' } )
|
||||
. '%'
|
||||
. WebGUI::Form::submit( $session, { value => 'Add' } )
|
||||
. WebGUI::Form::formFooter( $session );
|
||||
|
||||
# Wrap output in a YUI Tab widget.
|
||||
my ($style, $url) = $session->quick( qw{ style url } );
|
||||
$style->setLink($self->{_css},{rel=>"stylesheet", rel=>"stylesheet",type=>"text/css"});
|
||||
$style->setLink($url->extras('/yui/build/fonts/fonts-min.css'),{type=>"text/css", rel=>"stylesheet"});
|
||||
$style->setLink($url->extras('/yui/build/tabview/assets/skins/sam/tabview.css'),{type=>"text/css", rel=>"stylesheet"});
|
||||
$style->setLink($url->extras('/yui/build/container/assets/container.css'),{ type=>'text/css', rel=>"stylesheet" });
|
||||
$style->setLink($url->extras('/hoverhelp.css'),{ type=>'text/css', rel=>"stylesheet" });
|
||||
$style->setScript($url->extras('/yui/build/utilities/utilities.js'),{ type=>'text/javascript' });
|
||||
$style->setScript($url->extras('/yui/build/container/container-min.js'),{ type=>'text/javascript' });
|
||||
$style->setScript($url->extras('/yui/build/tabview/tabview-min.js'),{ type=>'text/javascript' });
|
||||
$style->setScript($url->extras('/hoverhelp.js'),{ type=>'text/javascript' });
|
||||
|
||||
my $output = <<EOHTML;
|
||||
<div class="yui-skin-sam">
|
||||
<div id="webguiTabForm" class="yui-navset">
|
||||
<ul class="yui-nav">
|
||||
<li class="selected"><a href="#tab1" ><em>General configuration</em></a></li>
|
||||
<li ><a href="#tab2" ><em>VAT Groups</em></a></li>
|
||||
</ul>
|
||||
<div class="yui-content">
|
||||
<div id="tab1">$general</div>
|
||||
<div id="tab2">$vatGroups</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript"> var tabView = new YAHOO.widget.TabView('webguiTabForm'); </script>
|
||||
EOHTML
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub getCountryCode {
|
||||
my $self = shift;
|
||||
my $countryName = shift;
|
||||
|
||||
# Do reverse lookup on eu countries hash
|
||||
return { reverse %{ $EU_COUNTRIES } }->{ $countryName };
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub getCountryName {
|
||||
my $self = shift;
|
||||
my $countryCode = shift;
|
||||
|
||||
return $EU_COUNTRIES->{ $countryCode };
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub getGroupRate {
|
||||
my $self = shift;
|
||||
my $taxGroupId = shift;
|
||||
|
||||
my $taxGroups = $self->get( 'taxGroups' );
|
||||
my ($group) = grep { $_->{ id } eq $taxGroupId } @{ $taxGroups };
|
||||
|
||||
return $group->{ rate };
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub getUserScreen {
|
||||
my $self = shift;
|
||||
my $url = $self->session->url;
|
||||
|
||||
|
||||
my $output = '<b>VAT Numbers</b><br />'
|
||||
. '<table><thead><tr><th>Country</th><th>VAT Number</th></tr></thead><tbody>';
|
||||
|
||||
foreach my $number ( @{ $self->getVATNumbers } ) {
|
||||
my $deleteUrl = $url->page('shop=tax;method=do;do=deleteVATNumber;vatNumber='.$number->{ vatNumber });
|
||||
$output .=
|
||||
'<tr><td>'
|
||||
. join( '</td><td>',
|
||||
$self->getCountryName( $number->{ countryCode } ),
|
||||
$number->{ vatNumber },
|
||||
$number->{ name },
|
||||
$number->{ address },
|
||||
$number->{ approved },
|
||||
qq{<a href="$deleteUrl">delete</a>},
|
||||
)
|
||||
. '</td></tr>'
|
||||
;
|
||||
}
|
||||
|
||||
$output .= '</tbody></table>';
|
||||
|
||||
my $f = WebGUI::HTMLForm->new( $self->session );
|
||||
$f->hidden(
|
||||
name => 'shop',
|
||||
value => 'tax',
|
||||
);
|
||||
$f->hidden(
|
||||
name => 'method',
|
||||
value => 'do',
|
||||
);
|
||||
$f->hidden(
|
||||
name => 'do',
|
||||
value => 'addVATNumber',
|
||||
);
|
||||
$f->text(
|
||||
name => 'vatNumber',
|
||||
label => 'VAT Number',
|
||||
);
|
||||
$f->submit(
|
||||
value => 'Add',
|
||||
);
|
||||
$output .= $f->print;
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub getTaxRate {
|
||||
my $self = shift;
|
||||
my $sku = shift;
|
||||
my $address = shift;
|
||||
|
||||
my $config = $sku->getTaxConfiguration( $self->className );
|
||||
|
||||
# Fetch the tax group from the sku. If the sku has none, use the default tax group.
|
||||
my $taxGroupId = $config->{ taxGroup } || $self->get( 'defaultGroup' );
|
||||
my $taxRate = $self->getGroupRate( $taxGroupId );
|
||||
|
||||
# No shipping address yet. Return group tax rate.
|
||||
return $taxRate unless defined $address;
|
||||
|
||||
# Shipping address outside EU? That means exporting so no VAT.
|
||||
my $country = $self->getCountryCode( $address->get( 'country' ) );
|
||||
return 0 unless defined $country;
|
||||
|
||||
# Shipping address in same country as shop? Pay VAT;
|
||||
return $taxRate if $country eq $self->get('shopCountry');
|
||||
|
||||
# Customer has VAT number in shipping country? Exempt from paying VAT.
|
||||
return 0 if $self->hasVATNumber( $country );
|
||||
|
||||
# Customer has no VAT number and resides in EU. Pay VAT;
|
||||
return $taxRate;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub getVATNumbers {
|
||||
my $self = shift;
|
||||
my $countryCode = shift;
|
||||
my $session = $self->session;
|
||||
|
||||
my $sql = 'select * from tax_eu_vatNumbers where userId=?';
|
||||
my $placeHolders = [ $session->user->userId ];
|
||||
|
||||
if ( $countryCode ) {
|
||||
$sql .= ' and countryCode=?';
|
||||
push @{ $placeHolders }, $countryCode;
|
||||
}
|
||||
|
||||
my $numbers = $session->db->buildArrayRefOfHashRefs( $sql, $placeHolders );
|
||||
|
||||
return $numbers;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub hasVATNumber {
|
||||
my $self = shift;
|
||||
my $countryCode = shift;
|
||||
|
||||
my $numbers = $self->getVATNumbers( $countryCode );
|
||||
return 0 unless @{ $numbers };
|
||||
|
||||
return $numbers->[0]->{ approved };
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub skuFormDefinition {
|
||||
my $self = shift;
|
||||
|
||||
my $taxGroups = $self->get( 'taxGroups' );
|
||||
|
||||
# If no tax groups are defined there's no need to add a form element.
|
||||
return {} unless $taxGroups;
|
||||
|
||||
my %options =
|
||||
map { $_->{ id } => "$_->{ name } ($_->{ rate } \%)" }
|
||||
@{ $taxGroups };
|
||||
|
||||
tie my %definition, 'Tie::IxHash', (
|
||||
taxGroup => {
|
||||
fieldType => 'selectBox',
|
||||
label => 'Tax group',
|
||||
options => \%options,
|
||||
}
|
||||
);
|
||||
|
||||
return \%definition;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub www_addGroup {
|
||||
my $self = shift;
|
||||
my $form = $self->session->form;
|
||||
|
||||
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 } );
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub www_addVATNumber {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my ($db, $form) = $session->quick( qw{ db form } );
|
||||
|
||||
return $session->privilege->insufficient if $session->user->isVisitor;
|
||||
|
||||
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 '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,
|
||||
] );
|
||||
|
||||
my $instance = WebGUI::Content::Account->createInstance($session,"shop");
|
||||
return $instance->displayContent( $instance->callMethod("manageTaxData", [], $session->user->userId) );
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub www_deleteGroup {
|
||||
my $self = shift;
|
||||
my $form = $self->session->form;
|
||||
|
||||
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 } );
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
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' ),
|
||||
] );
|
||||
|
||||
my $instance = WebGUI::Content::Account->createInstance($session,"shop");
|
||||
return $instance->displayContent( $instance->callMethod("manageTaxData", [], $session->user->userId) );
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub www_saveConfiguration {
|
||||
my $self = shift;
|
||||
my $form = $self->session->form;
|
||||
|
||||
return $self->session->privilege->insufficient unless $self->canManage;
|
||||
|
||||
$self->update( {
|
||||
shopCountry => $form->process( 'shopCountry', 'selectBox' ),
|
||||
} );
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub www_setDefaultGroup {
|
||||
my $self = shift;
|
||||
my $form = $self->session->form;
|
||||
|
||||
return $self->session->privilege->insufficient unless $self->canManage;
|
||||
|
||||
$self->update( {
|
||||
defaultGroup => $form->process( 'groupId' ),
|
||||
} );
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
688
lib/WebGUI/Shop/TaxDriver/Generic.pm
Normal file
688
lib/WebGUI/Shop/TaxDriver/Generic.pm
Normal file
|
|
@ -0,0 +1,688 @@
|
|||
package WebGUI::Shop::TaxDriver::Generic;
|
||||
|
||||
use strict;
|
||||
|
||||
use WebGUI::Text;
|
||||
use WebGUI::Storage;
|
||||
use WebGUI::Exception::Shop;
|
||||
use List::Util qw{ sum };
|
||||
|
||||
use base qw{ WebGUI::Shop::TaxDriver };
|
||||
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Package WebGUI::Shop::TaxDriver::Generic
|
||||
|
||||
=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.
|
||||
|
||||
Taxes are accumulated through increasingly specific geographic information. For example, you can
|
||||
specify the sales tax for a whole country, then the additional sales tax for a state in the country,
|
||||
all the way down to a single code inside of a city.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use WebGUI::Shop::Tax;
|
||||
|
||||
my $tax = WebGUI::Shop::Tax->new($session);
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
These subroutines are available from this package:
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 add ( [$params] )
|
||||
|
||||
Add tax information to the table. Returns the taxId of the newly created tax information.
|
||||
|
||||
=head3 $params
|
||||
|
||||
A hash ref of the geographic and rate information. The country and taxRate parameters
|
||||
must have defined values.
|
||||
|
||||
=head4 country
|
||||
|
||||
The country this tax information applies to.
|
||||
|
||||
=head4 state
|
||||
|
||||
The state this tax information applies to. state and country together are unique.
|
||||
|
||||
=head4 city
|
||||
|
||||
The ciy this tax information applies to. Cities are unique with state and country information.
|
||||
|
||||
=head4 code
|
||||
|
||||
The postal code this tax information applies to. codes are unique with state and country information.
|
||||
|
||||
=head4 taxRate
|
||||
|
||||
This is the tax rate for the location, as specified by the geographical
|
||||
fields country, state, city and/or code. The tax rate is stored as
|
||||
a percentage, like 5.5 .
|
||||
|
||||
=cut
|
||||
|
||||
sub add {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
|
||||
WebGUI::Error::InvalidParam->throw(error => 'Must pass in a hashref of params')
|
||||
unless ref($params) eq 'HASH';
|
||||
WebGUI::Error::InvalidParam->throw(error => "Missing required information.", param => 'country')
|
||||
unless exists($params->{country}) and $params->{country};
|
||||
WebGUI::Error::InvalidParam->throw(error => "Missing required information.", param => 'taxRate')
|
||||
unless exists($params->{taxRate}) and defined $params->{taxRate};
|
||||
|
||||
$params->{taxId} = 'new';
|
||||
my $id = $self->session->db->setRow('tax_generic_rates', 'taxId', $params);
|
||||
return $id;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getTaxRate ( sku, address )
|
||||
|
||||
Returns the tax rate for the given sku with the given shipping address.
|
||||
|
||||
=head3 sku
|
||||
|
||||
An instanciated WebGUI::Asset::Sku object.
|
||||
|
||||
=head3 address
|
||||
|
||||
An instanciated WebGUI::Shop::Address object containing the shipping address for the sku.
|
||||
|
||||
=cut
|
||||
|
||||
sub getTaxRate {
|
||||
my $self = shift;
|
||||
my $sku = shift;
|
||||
my $address = shift;
|
||||
my $session = $self->session;
|
||||
my $config = $sku->getTaxConfiguration( $self->className );
|
||||
|
||||
# Check params
|
||||
WebGUI::Error::InvalidParam->throw(error => 'Must pass in a WebGUI::Asset::Sku object')
|
||||
unless $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' );
|
||||
|
||||
# Check if the sku has a tax rate override, and return that if it has.
|
||||
if ( $config->{ overrideTaxRate } ) {
|
||||
return $config->{ taxRateOverride };
|
||||
}
|
||||
|
||||
# No tax rate override, so tax is calculated from the tax tables.
|
||||
|
||||
# If no address is supplied yet, return 0%
|
||||
return 0 unless defined $address;
|
||||
|
||||
# Fetch the taxes for this address and cache it for later use.
|
||||
my $taxables = $session->stow->get( 'genericTaxables_' . $address->getId );
|
||||
unless ($taxables) {
|
||||
$taxables = $self->getTaxRates($address);
|
||||
$session->stow->set( 'genericTaxables_' . $address->getId, $taxables );
|
||||
}
|
||||
|
||||
# Check for a SKU specific tax override rate
|
||||
my $itemTax = sum @{ $taxables };
|
||||
|
||||
return $itemTax;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub className {
|
||||
return 'WebGUI::Shop::TaxDriver::Generic';
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 delete ( [$params] )
|
||||
|
||||
Deletes data from the tax table by taxId.
|
||||
|
||||
=head3 $params
|
||||
|
||||
A hashref containing the taxId of the data to delete from the table.
|
||||
|
||||
=head4 taxId
|
||||
|
||||
The taxId of the data to delete from the table.
|
||||
|
||||
=cut
|
||||
|
||||
sub delete {
|
||||
my $self = shift;
|
||||
my $params = shift;
|
||||
WebGUI::Error::InvalidParam->throw(error => 'Must pass in a hashref of params')
|
||||
unless ref($params) eq 'HASH';
|
||||
WebGUI::Error::InvalidParam->throw(error => "Hash ref must contain a taxId key with a defined value")
|
||||
unless exists($params->{taxId}) and defined $params->{taxId};
|
||||
$self->session->db->write('delete from tax_generic_rates where taxId=?', [$params->{taxId}]);
|
||||
return;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 exportTaxData ( )
|
||||
|
||||
Creates a tab deliniated file containing all the information from
|
||||
the tax table. Returns a temporary WebGUI::Storage object containing
|
||||
the file. The file will be named "siteTaxData.csv".
|
||||
|
||||
=cut
|
||||
|
||||
sub exportTaxData {
|
||||
my $self = shift;
|
||||
my $taxIterator = $self->getItems;
|
||||
my @columns = grep { $_ ne 'taxId' } $taxIterator->getColumnNames;
|
||||
my $taxData = WebGUI::Text::joinCSV(@columns) . "\n";
|
||||
while (my $taxRow = $taxIterator->hashRef() ) {
|
||||
my @taxData = @{ $taxRow }{@columns};
|
||||
foreach my $column (@taxData) {
|
||||
$column =~ tr/,/|/; ##Convert to the alternation syntax for the text file
|
||||
}
|
||||
$taxData .= WebGUI::Text::joinCSV(@taxData) . "\n";
|
||||
}
|
||||
my $storage = WebGUI::Storage->createTemp($self->session);
|
||||
$storage->addFileFromScalar('siteTaxData.csv', $taxData);
|
||||
return $storage;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getAllItems ( )
|
||||
|
||||
Returns an arrayref of hashrefs, where each hashref is the data for one row of
|
||||
tax data. taxId is dropped from the dataset.
|
||||
|
||||
=cut
|
||||
|
||||
sub getAllItems {
|
||||
my $self = shift;
|
||||
my $taxes = $self->session->db->buildArrayRefOfHashRefs('select country,state,city,code,taxRate from tax_generic_rates order by country, state');
|
||||
return $taxes;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getItems ( )
|
||||
|
||||
Returns a WebGUI::SQL::Result object for accessing all of the data in the tax table. This
|
||||
is a convenience method for listing and/or exporting tax data.
|
||||
|
||||
=cut
|
||||
|
||||
sub getItems {
|
||||
my $self = shift;
|
||||
my $result = $self->session->db->read('select * from tax_generic_rates order by country, state');
|
||||
return $result;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getTaxRates ( $address )
|
||||
|
||||
Given a WebGUI::Shop::Address object, return all rates associated with the address as an arrayRef.
|
||||
|
||||
=cut
|
||||
|
||||
sub getTaxRates {
|
||||
my $self = shift;
|
||||
my $address = shift;
|
||||
WebGUI::Error::InvalidObject->throw(error => 'Need an address.', expected=>'WebGUI::Shop::Address', got=>(ref $address))
|
||||
unless ref($address) eq 'WebGUI::Shop::Address';
|
||||
my $country = $address->get('country');
|
||||
my $state = $address->get('state');
|
||||
my $city = $address->get('city');
|
||||
my $code = $address->get('code');
|
||||
my $result = $self->session->db->buildArrayRef(
|
||||
q{
|
||||
select taxRate from tax_generic_rates where find_in_set(?, country)
|
||||
and (state='' or find_in_set(?, state))
|
||||
and (city='' or find_in_set(?, city))
|
||||
and (code='' or find_in_set(?, code))
|
||||
},
|
||||
[ $country, $state, $city, $code, ]);
|
||||
return $result;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 importTaxData ( $filePath )
|
||||
|
||||
Import tax information from the specified file in CSV format. The
|
||||
first line of the file should contain only the name of the columns, in
|
||||
any order. It may not contain any comments.
|
||||
|
||||
These are the column names, each is required:
|
||||
|
||||
=over 4
|
||||
|
||||
=item *
|
||||
|
||||
country
|
||||
|
||||
=item *
|
||||
|
||||
state
|
||||
|
||||
=item *
|
||||
|
||||
city
|
||||
|
||||
=item *
|
||||
|
||||
code
|
||||
|
||||
=item *
|
||||
|
||||
taxRate
|
||||
|
||||
=back
|
||||
|
||||
The following lines will contain tax information. Blank
|
||||
lines and anything following a '#' sign will be ignored from
|
||||
the second line of the file, on to the end.
|
||||
|
||||
Returns 1 if the import has taken place. This is to help you know
|
||||
if old data has been deleted and new has been inserted. If an error is
|
||||
detected, it will throw exceptions.
|
||||
|
||||
=head3 $filePath
|
||||
|
||||
The path to a file with data to import into the Product system.
|
||||
|
||||
=cut
|
||||
|
||||
sub importTaxData {
|
||||
my $self = shift;
|
||||
my $filePath = shift;
|
||||
WebGUI::Error::InvalidParam->throw(error => q{Must provide the path to a file})
|
||||
unless $filePath;
|
||||
WebGUI::Error::InvalidFile->throw(error => qq{File could not be found}, brokenFile => $filePath)
|
||||
unless -e $filePath;
|
||||
WebGUI::Error::InvalidFile->throw(error => qq{File is not readable}, brokenFile => $filePath)
|
||||
unless -r $filePath;
|
||||
open my $table, '<', $filePath or
|
||||
WebGUI::Error->throw(error => qq{Unable to open $filePath for reading: $!\n});
|
||||
my $headers;
|
||||
$headers = <$table>;
|
||||
chomp $headers;
|
||||
my @headers = WebGUI::Text::splitCSV($headers);
|
||||
WebGUI::Error::InvalidFile->throw(error => qq{Bad header found in the CSV file}, brokenFile => $filePath)
|
||||
unless (join(q{-}, sort @headers) eq 'city-code-country-state-taxRate')
|
||||
and (scalar @headers == 5);
|
||||
my @taxData = ();
|
||||
my $line = 1;
|
||||
while (my $taxRow = <$table>) {
|
||||
chomp $taxRow;
|
||||
$taxRow =~ s/\s*#.+$//;
|
||||
next unless $taxRow;
|
||||
local $_;
|
||||
my @taxRow = map { tr/|/,/; $_; } WebGUI::Text::splitCSV($taxRow);
|
||||
WebGUI::Error::InvalidFile->throw(error => qq{Error found in the CSV file}, brokenFile => $filePath, brokenLine => $line)
|
||||
unless scalar @taxRow == 5;
|
||||
push @taxData, [ @taxRow ];
|
||||
}
|
||||
##Okay, if we got this far, then the data looks fine.
|
||||
return unless scalar @taxData;
|
||||
$self->session->db->beginTransaction;
|
||||
$self->session->db->write('delete from tax_generic_rates');
|
||||
foreach my $taxRow (@taxData) {
|
||||
my %taxRow;
|
||||
@taxRow{ @headers } = @{ $taxRow }; ##Must correspond 1:1, or else...
|
||||
$self->add(\%taxRow);
|
||||
}
|
||||
$self->session->db->commit;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub skuFormDefinition {
|
||||
my $self = shift;
|
||||
my $i18n = WebGUI::International->new( $self->session, 'Tax' );
|
||||
|
||||
tie my %definition, 'Tie::IxHash', (
|
||||
overrideTaxRate => {
|
||||
fieldType => "yesNo",
|
||||
defaultValue => 0,
|
||||
label => $i18n->get("override tax rate"),
|
||||
hoverHelp => $i18n->get("override tax rate help")
|
||||
},
|
||||
taxRateOverride => {
|
||||
fieldType => "float",
|
||||
defaultValue => 0.00,
|
||||
label => $i18n->get("tax rate override"),
|
||||
hoverHelp => $i18n->get("tax rate override help")
|
||||
},
|
||||
);
|
||||
|
||||
return \%definition;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 www_deleteTax ( )
|
||||
|
||||
Delete a row of tax information, using the form variable taxId as
|
||||
the id of the row to delete.
|
||||
|
||||
=cut
|
||||
|
||||
sub www_deleteTax {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
|
||||
return $session->privilege->insufficient unless $self->canManage;
|
||||
|
||||
my $taxId = $session->form->get('taxId');
|
||||
$self->delete({ taxId => $taxId });
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 www_addTax ( )
|
||||
|
||||
Add new tax information into the database, via the UI.
|
||||
|
||||
=cut
|
||||
|
||||
sub www_addTax {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
|
||||
return $session->privilege->insufficient unless $self->canManage;
|
||||
|
||||
my $params;
|
||||
my ($form) = $session->quick('form');
|
||||
$params->{country} = $form->get('country', 'text');
|
||||
$params->{state} = $form->get('state', 'text');
|
||||
$params->{city} = $form->get('city', 'text');
|
||||
$params->{code} = $form->get('code', 'text');
|
||||
$params->{taxRate} = $form->get('taxRate', 'float');
|
||||
$self->add($params);
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 www_exportTax ( )
|
||||
|
||||
Export the entire tax table as a CSV file the user can download.
|
||||
|
||||
=cut
|
||||
|
||||
sub www_exportTax {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
|
||||
return $session->privilege->insufficient unless $self->canManage;
|
||||
|
||||
my $storage = $self->exportTaxData();
|
||||
$self->session->http->setRedirect($storage->getUrl($storage->getFiles->[0]));
|
||||
return "redirect";
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 www_getTaxesAsJson ( )
|
||||
|
||||
Servers side pagination for tax data that is sent as JSON back to the browser to be
|
||||
displayed in a YUI DataTable.
|
||||
|
||||
=cut
|
||||
|
||||
sub www_getTaxesAsJson {
|
||||
my ($self) = @_;
|
||||
my $session = $self->session;
|
||||
|
||||
return $session->privilege->insufficient unless $self->canManage;
|
||||
|
||||
my ($db, $form) = $session->quick(qw(db form));
|
||||
my $startIndex = $form->get('startIndex') || 0;
|
||||
my $numberOfResults = $form->get('results') || 25;
|
||||
my %goodKeys = qw/country 1 state 1 city 1 code 1 'tax rate' 1/;
|
||||
my $sortKey = $form->get('sortKey');
|
||||
$sortKey = $goodKeys{$sortKey} == 1 ? $sortKey : 'country';
|
||||
my $sortDir = $form->get('sortDir');
|
||||
$sortDir = lc($sortDir) eq 'desc' ? 'desc' : 'asc';
|
||||
my @placeholders = ();
|
||||
my $sql = 'select SQL_CALC_FOUND_ROWS * from tax_generic_rates';
|
||||
my $keywords = $form->get("keywords");
|
||||
if ($keywords ne "") {
|
||||
$db->buildSearchQuery(\$sql, \@placeholders, $keywords, [qw{country state city code}])
|
||||
}
|
||||
push(@placeholders, $startIndex, $numberOfResults);
|
||||
$sql .= sprintf (" order by %s limit ?,?","$sortKey $sortDir");
|
||||
my %results = ();
|
||||
my @records = ();
|
||||
my $sth = $db->read($sql, \@placeholders);
|
||||
while (my $record = $sth->hashRef) {
|
||||
push(@records,$record);
|
||||
}
|
||||
$results{'recordsReturned'} = $sth->rows()+0;
|
||||
$sth->finish;
|
||||
$results{'records'} = \@records;
|
||||
$results{'totalRecords'} = $db->quickScalar('select found_rows()')+0; ##Convert to numeric
|
||||
$results{'startIndex'} = $startIndex;
|
||||
$results{'sort'} = undef;
|
||||
$results{'dir'} = $sortDir;
|
||||
$session->http->setMimeType('application/json');
|
||||
return JSON::to_json(\%results);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 www_importTax ( )
|
||||
|
||||
Import new tax data from a file provided by the user. This will replace the current
|
||||
data with the new data.
|
||||
|
||||
=cut
|
||||
|
||||
sub www_importTax {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
|
||||
return $session->privilege->insufficient unless $self->canManage;
|
||||
|
||||
my $storage = WebGUI::Storage->create($session);
|
||||
my $taxFile = $storage->addFileFromFormPost('importFile', 1);
|
||||
eval {
|
||||
$self->importTaxData($storage->getPath($taxFile)) if $taxFile;
|
||||
};
|
||||
my ($exception, $status_message);
|
||||
if ($exception = Exception::Class->caught('WebGUI::Error::InvalidFile')) {
|
||||
$status_message = sprintf 'A problem was found with your file: %s',
|
||||
$exception->error;
|
||||
if ($exception->brokenLine) {
|
||||
$status_message .= sprintf ' on line %d', $exception->brokenLine;
|
||||
}
|
||||
}
|
||||
elsif ($exception = Exception::Class->caught()) {
|
||||
$status_message = sprintf 'A problem happened during the import: %s', $exception->error;
|
||||
}
|
||||
|
||||
$session->stow->set( 'tax_message', $status_message );
|
||||
return '';
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=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 getConfigurationScreen {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $status_message = $session->stow->get( 'tax_message' );
|
||||
|
||||
return $session->privilege->insufficient unless $self->canManage;
|
||||
|
||||
##YUI specific datatable CSS
|
||||
my ($style, $url) = $session->quick(qw(style url));
|
||||
$style->setLink($url->extras('/yui/build/fonts/fonts-min.css'), {rel=>'stylesheet', type=>'text/css'});
|
||||
$style->setLink($url->extras('yui/build/datatable/assets/skins/sam/datatable.css'), {rel=>'stylesheet', type => 'text/CSS'});
|
||||
$style->setLink($url->extras('yui/build/paginator/assets/skins/sam/paginator.css'), {rel=>'stylesheet', type => 'text/CSS'});
|
||||
$style->setScript($url->extras('/yui/build/utilities/utilities.js'), {type=>'text/javascript'});
|
||||
$style->setScript($url->extras('yui/build/json/json-min.js'), {type => 'text/javascript'});
|
||||
$style->setScript($url->extras('yui/build/paginator/paginator-min.js'), {type => 'text/javascript'});
|
||||
$style->setScript($url->extras('yui/build/datasource/datasource-min.js'), {type => 'text/javascript'});
|
||||
##YUI Datatable
|
||||
$style->setScript($url->extras('yui/build/datatable/datatable-min.js'), {type => 'text/javascript'});
|
||||
##Default CSS
|
||||
$style->setRawHeadTags('<style type="text/css"> #paging a { color: #0000de; } #search, #export form { display: inline; } </style>');
|
||||
my $i18n=WebGUI::International->new($session, 'Tax');
|
||||
|
||||
my $exportForm = WebGUI::Form::formHeader($session,{action => $url->page('shop=tax;method=do;do=exportTax')})
|
||||
. WebGUI::Form::submit($session,{value=>$i18n->get('export tax','Shop'), extras=>q{style="float: left;"} })
|
||||
. WebGUI::Form::formFooter($session);
|
||||
my $importForm = WebGUI::Form::formHeader($session,{action => $url->page('shop=tax;method=do;do=importTax')})
|
||||
. WebGUI::Form::submit($session,{value=>$i18n->get('import tax','Shop'), extras=>q{style="float: left;"} })
|
||||
. q{<input type="file" name="importFile" size="10" />}
|
||||
. WebGUI::Form::formFooter($session);
|
||||
|
||||
my $addForm = WebGUI::HTMLForm->new($session,action=>$url->page('shop=tax;method=do;do=addTax'));
|
||||
$addForm->text(
|
||||
label => $i18n->get('country'),
|
||||
hoverHelp => $i18n->get('country help'),
|
||||
name => 'country',
|
||||
);
|
||||
$addForm->text(
|
||||
label => $i18n->get('state'),
|
||||
hoverHelp => $i18n->get('state help'),
|
||||
name => 'state',
|
||||
);
|
||||
$addForm->text(
|
||||
label => $i18n->get('city'),
|
||||
hoverHelp => $i18n->get('city help'),
|
||||
name => 'city',
|
||||
);
|
||||
$addForm->text(
|
||||
label => $i18n->get('code'),
|
||||
hoverHelp => $i18n->get('code help'),
|
||||
name => 'code',
|
||||
);
|
||||
$addForm->float(
|
||||
label => $i18n->get('tax rate'),
|
||||
hoverHelp => $i18n->get('tax rate help'),
|
||||
name => 'taxRate',
|
||||
);
|
||||
$addForm->submit(
|
||||
value => $i18n->get('add a tax'),
|
||||
);
|
||||
my $output;
|
||||
if ($status_message) {
|
||||
$output = <<EOSM;
|
||||
<div class="error">
|
||||
$status_message
|
||||
</div>
|
||||
EOSM
|
||||
}
|
||||
|
||||
$output .= q|
|
||||
|
||||
|
||||
<div class="yui-skin-sam">
|
||||
<div id="search"><form id="keywordSearchForm"><input type="text" name="keywords" id="keywordsField" /><input type="submit" value="|.$i18n->get(364, 'WebGUI').q|" /></form></div>
|
||||
<div id="dynamicdata"></div>
|
||||
<div id="adding">|.$addForm->print.q|</div>
|
||||
<div id="importExport">|.$exportForm.$importForm.q|</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var taxtable = function() {
|
||||
// Column definitions
|
||||
formatDeleteTaxId = function(elCell, oRecord, oColumn, orderNumber) {
|
||||
elCell.innerHTML = '<a href="|.$url->page(q{shop=tax;method=do;do=deleteTax}).q|;taxId='+oRecord.getData('taxId')+'">|.$i18n->get('delete').q|</a>';
|
||||
};
|
||||
var myColumnDefs = [ // sortable:true enables sorting
|
||||
{key:"country", label:"|.$i18n->get('country').q|", sortable: true},
|
||||
{key:"state", label:"|.$i18n->get('state').q|", sortable: true},
|
||||
{key:"city", label:"|.$i18n->get('city').q|", sortable: true},
|
||||
{key:"code", label:"|.$i18n->get('code').q|", sortable: true},
|
||||
{key:"taxRate", label:"|.$i18n->get('tax rate').q|"},
|
||||
{key:"taxId", label:"", formatter:formatDeleteTaxId}
|
||||
];
|
||||
|
||||
// DataSource instance
|
||||
var myDataSource = new YAHOO.util.DataSource("|.$url->page('shop=tax;method=do;do=getTaxesAsJson;').q|");
|
||||
myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
|
||||
myDataSource.responseSchema = {
|
||||
resultsList: "records",
|
||||
fields: [
|
||||
{key:"country", parser:"string"},
|
||||
{key:"state", parser:"string"},
|
||||
{key:"city", parser:"string"},
|
||||
{key:"code", parser:"string"},
|
||||
{key:"taxRate", parser:"number"},
|
||||
{key:"taxId", parser:"string"}
|
||||
],
|
||||
metaFields: {
|
||||
totalRecords: "totalRecords" // Access to value in the server response
|
||||
}
|
||||
};
|
||||
|
||||
// DataTable configuration
|
||||
var myConfigs = {
|
||||
initialRequest: 'startIndex=0;results=25', // Initial request for first page of data
|
||||
dynamicData: true, // Enables dynamic server-driven data
|
||||
sortedBy : {key:"country", dir:YAHOO.widget.DataTable.CLASS_ASC}, // Sets UI initial sort arrow
|
||||
paginator: new YAHOO.widget.Paginator({ rowsPerPage:25 }) // Enables pagination
|
||||
};
|
||||
|
||||
// DataTable instance
|
||||
var myDataTable = new YAHOO.widget.DataTable("dynamicdata", myColumnDefs, myDataSource, myConfigs);
|
||||
// Update totalRecords on the fly with value from server to allow pagination
|
||||
myDataTable.handleDataReturnPayload = function(oRequest, oResponse, oPayload) {
|
||||
oPayload.totalRecords = oResponse.meta.totalRecords;
|
||||
return oPayload;
|
||||
}
|
||||
|
||||
//Setup the form to submit an AJAX request back to the site.
|
||||
YAHOO.util.Dom.get('keywordSearchForm').onsubmit = function () {
|
||||
var state = myDataTable.getState();
|
||||
state.pagination.recordOffset = 0;
|
||||
myDataSource.sendRequest('keywords=' + YAHOO.util.Dom.get('keywordsField').value + ';startIndex=0;results=25', {success: myDataTable.onDataReturnInitializeTable, scope:myDataTable, argument:state});
|
||||
return false;
|
||||
};
|
||||
|
||||
return {
|
||||
ds: myDataSource,
|
||||
dt: myDataTable
|
||||
};
|
||||
|
||||
|
||||
}();
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|;
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
1;
|
||||
Loading…
Add table
Add a link
Reference in a new issue