sales tax

This commit is contained in:
Colin Kuskie 2006-10-21 01:13:07 +00:00
parent 4deff2a0a9
commit f3ab99bc02
23 changed files with 607 additions and 111 deletions

View file

@ -15,7 +15,10 @@ use WebGUI::International;
use WebGUI::Asset::Template;
use WebGUI::Paginator;
use WebGUI::Form;
use WebGUI::Utility;
use Storable;
use Locale::US;
use Tie::IxHash;
#-------------------------------------------------------------------
@ -144,6 +147,30 @@ sub _shippingSelected {
#-------------------------------------------------------------------
=head2 _validateState ( $state )
A utility method to tell if $state is a valid US state for determining
sales tax. Returns either the name of a US state in ALL CAPS or '' if
it isn't a valid US state.
=cut
sub _validateState {
my $state = shift;
$state = uc($state);
my $stateObj = Locale::US->new;
if (exists $stateObj->{code2state}->{$state}) {
##An abbreviation was used, translate back to state name
$state = $stateObj->{code2state}->{$state};
}
##Next, we validate the state. If it isn't a valid US state, null out
##the state field so we don't do sales tax.
$state = '' unless exists $stateObj->{state2code}->{$state};
return $state;
}
#-------------------------------------------------------------------
=head2 www_addToCart ( $session )
Adds the requested item to the user's shopping cart and creates a cart if necessary.
@ -252,10 +279,23 @@ sub www_checkoutConfirm {
$var{errorLoop} = [ map {{message => $_}} @{$errors} ] if $errors;
my $enableSalesTax = $var{useSalesTax} = $session->setting->get('commerceEnableSalesTax');
my $salesTaxRate = 0;
my $state = $session->form->process('state') || $session->user->profileField('homeState');
$state = _validateState($state);
if ($state and $enableSalesTax) {
($salesTaxRate) = $session->db->quickArray("select salesTax from commerceSalesTax where regionIdentifier=?",[$state]);
}
$var{salesTaxRate} = sprintf "%6.4f%%", $salesTaxRate;
$salesTaxRate /= 100; ##convert from percent to float
# Put contents of cart in template vars
$shoppingCart = WebGUI::Commerce::ShoppingCart->new($session);
($normal, $recurring) = $shoppingCart->getItems;
($normal, $recurring) = $shoppingCart->getItems($salesTaxRate);
my @copyOfNormal = @$normal;
my $totalSalesTax = 0;
##Note, if the special casing in the normal loop can be pushed back into the item,
##then the loops can be combined and collapsed.
foreach (@$normal) {
my $amount;
$_->{deleteIcon} = $session->icon->delete('op=deleteCartItem;itemId='.$_->{item}->id.';itemType='.$_->{item}->type);
@ -268,9 +308,17 @@ sub www_checkoutConfirm {
# use the item plugin's lineItem method for price override
# situations.
$amount = ($priceLineItem ne "") ? ($priceLineItem) : ($_->{totalPrice});
if ($priceLineItem ne "") { ##There was a discount, fix the amount and tax
$amount = $priceLineItem;
$_->{salesTax} = sprintf "%.2f", $priceLineItem * $salesTaxRate * $_->{item}->useSalesTax;
}
else {
$amount = $_->{totalPrice};
}
$_->{item}->{price} = $amount;
$total += $amount; # tracks discount
$subTotal += $_->{totalPrice}; # ignores discount (we need this to show them an accurate subtotal and to calculate the discount given.)
$totalSalesTax += $_->{salesTax};
}
foreach (@$recurring) {
$_->{deleteIcon} = $session->icon->delete('op=deleteCartItem;itemId='.$_->{item}->id.';itemType='.$_->{item}->type);
@ -280,6 +328,7 @@ sub www_checkoutConfirm {
size => 3,
});
$total += $_->{totalPrice};
$totalSalesTax += $_->{salesTax};
}
$var{normalItemsLoop} = $normal;
@ -288,13 +337,14 @@ sub www_checkoutConfirm {
$var{recurringItems} = scalar(@$recurring);
$var{subTotal} = sprintf('%.2f', $subTotal);
$var{totalSalesTax} = sprintf('%.2f', $totalSalesTax);
$shipping = WebGUI::Commerce::Shipping->load($session, $session->scratch->get('shippingMethod'));
$shipping->setOptions(Storable::thaw($session->scratch->get('shippingOptions'))) if ($session->scratch->get('shippingOptions'));
$var{shippingName} = $shipping->name;
$var{shippingCost} = sprintf('%.2f', $shipping->calc);
$var{total} = sprintf('%.2f', $total + $shipping->calc);
$var{total} = sprintf('%.2f', $total + $shipping->calc + $totalSalesTax);
$var{discountsApplied} = sprintf('%.2f', $total - $subTotal - $shipping->calc);
$var{'discountsApplied.label'} = $i18n->echo("Discount Applied");
@ -315,11 +365,11 @@ sub www_checkoutConfirm {
$var{'changePayment.label'} = $i18n->get('change payment gateway');
my $plugins = WebGUI::Commerce::Shipping->getEnabledPlugins($session);
if (scalar(@$plugins) > 1) {
$var{'changeShipping.url'} = $session->url->page('op=selectShippingMethod');
$var{'changeShipping.label'} = $i18n->get('change shipping method');
$var{'hasMultipleShipping'} = "true";
}
if (scalar(@$plugins) > 1) {
$var{'changeShipping.url'} = $session->url->page('op=selectShippingMethod');
$var{'changeShipping.label'} = $i18n->get('change shipping method');
$var{'hasMultipleShipping'} = "true";
}
$var{'viewShoppingCart.url'} = $session->url->page('op=viewCart');
$var{'viewShoppingCart.label'} = $i18n->get('view shopping cart');
@ -366,7 +416,15 @@ sub www_checkoutSubmit {
# Load payment plugin.
$plugin = WebGUI::Commerce::Payment->load($session, $session->scratch->get('paymentGateway'));
$shoppingCart = WebGUI::Commerce::ShoppingCart->new($session);
($normal, $recurring) = $shoppingCart->getItems;
my $enableSalesTax = $session->setting->get('commerceEnableSalesTax');
my $state = $session->form->process('state') || $session->user->profileField('homeState');
$state = _validateState($state);
my $salesTaxRate = 0;
if ($state and $enableSalesTax) {
($salesTaxRate) = $session->db->quickArray("select salesTax from commerceSalesTax where regionIdentifier=?",[$state]);
}
$salesTaxRate /= 100; ##convert from percent to float
($normal, $recurring) = $shoppingCart->getItems($salesTaxRate);
my @copyOfNormal = @$normal;
# Check if shoppingcart contains any items. If not the user probably clicked reload, so we redirect to the current page.
unless (@$normal || @$recurring) {
@ -396,17 +454,19 @@ sub www_checkoutSubmit {
# Write transaction to the log with status pending
$transaction = WebGUI::Commerce::Transaction->new($session, 'new');
my $salesTaxTotal = 0;
foreach (@{$currentPurchase->{items}}) {
my $priceLineItem = ($_->{item}->{priceLineItem}) ? ($_->{item}->priceLineItem($_->{quantity},\@copyOfNormal)) : undef; # pass in the quantity and the normal items in the cart.
#$session->errorHandler->warn("Price Line Item: $priceLineItem");
$transaction->addItem($_->{item}, $_->{quantity},$priceLineItem);
# use the item plugin's lineItem method for price override
# situations.
$amount += ($priceLineItem ne "")
?($priceLineItem)
:($_->{item}->price * $_->{quantity});
$amount += ($priceLineItem ne "")? $priceLineItem :
($_->{item}->price * $_->{quantity});
$var->{purchaseDescription} .= $_->{quantity}.' x '.$_->{item}->name.'<br />';
$salesTaxTotal += $_->{salesTax};
}
# Oy, the kludge.
$transaction->addItem(WebGUI::Commerce::Item::Fake->new($session, $salesTaxTotal.',Sales Tax'));
$transaction->shippingCost($shippingCost);
$transaction->shippingMethod($shipping->namespace);
$transaction->shippingOptions($shipping->getOptions);
@ -579,6 +639,7 @@ sub www_editCommerceSettings {
general=>{label=>$i18n->get('general tab')},
payment=>{label=>$i18n->get('payment tab')},
shipping=>{label=>$i18n->get('shipping tab')},
salesTax=>{label=>$i18n->get('salesTax tab')},
);
$paymentPlugin = $session->config->get("paymentPlugins")->[0];
@ -645,6 +706,18 @@ sub www_editCommerceSettings {
-value => $session->setting->get("commerceSendDailyReportTo")
);
$tabform->getTab('salesTax')->yesNo(
-name => 'commerceEnableSalesTax',
-label => $i18n->get('enable sales tax'),
-hoverHelp => $i18n->get('enable sales tax description'),
-value => $session->setting->get("commerceEnableSalesTax") || 0,
);
$tabform->getTab('salesTax')->raw('<tr><td colspan="2">');
$session->style->setScript($session->url->extras('/js/at/AjaxRequest.js'), {type=>"text/javascript"});
$session->style->setScript($session->url->extras('/operations/salesTaxAjax.js'), {type=>"text/javascript"});
my $stateForm = WebGUI::Operation::FormHelpers::www_salesTaxTable($session);
$tabform->getTab('salesTax')->raw('<div id="salesTaxFormDiv">'.$stateForm.'</div>');
$tabform->getTab('salesTax')->raw('</td></tr>');
# Check which payment plugins will compile, and load them.
foreach (@{$session->config->get("paymentPlugins")}) {
$plugin = WebGUI::Commerce::Payment->load($session, $_);
@ -742,7 +815,12 @@ sub www_editCommerceSettingsSave {
my $session = shift;
return $session->privilege->adminOnly() unless ($session->user->isInGroup(3));
foreach ($session->form->param) {
PARAM: foreach ($session->form->param) {
##Sales tax form parameters will also be in this list, but they
##should NOT be handled here. We'll skip them.
next PARAM if isIn($_, qw/stateChooser taxRate addTaxInfo/);
# Store the plugin configuration data in a special table for security and the general settings in the
# normal settings table for easy access.
if (/~([^~]*)~([^~]*)~([^~]*)/) {

View file

@ -17,6 +17,7 @@ use WebGUI::Asset::Wobject::Folder;
use WebGUI::Form::Group;
use WebGUI::HTMLForm;
use WebGUI::Storage::Image;
use WebGUI::Utility;
=head1 NAME
@ -480,5 +481,108 @@ sub www_richEditAddImageSave {
return "";
}
#-------------------------------------------------------------------
=head2 www_setupSalesTaxForm ( $session )
Create the AJAX form for displaying, adding and deleting sales tax information.
=cut
sub www_salesTaxTable {
my $session = shift;
my $returnTableOnly = 0;
if ($session->form->process('addDelete') eq 'add') {
my $state = $session->form->process('addStateId', 'selectBox');
my $taxRate = $session->form->process('taxRate', 'float');
my $commerceSalesTaxId = $session->id->generate();
if ( $state and $taxRate ) {
$session->db->write('insert into commerceSalesTax (commerceSalesTaxId,regionIdentifier,salesTax) VALUES (?,?,?)', [$commerceSalesTaxId, $state, $taxRate]);
}
$returnTableOnly = 1;
}
elsif ($session->form->process('addDelete') eq 'delete') {
my $commerceSalesTaxId = $session->form->process('entryId');
$session->db->write('delete from commerceSalesTax where commerceSalesTaxId=?',[$commerceSalesTaxId]);
$returnTableOnly = 1;
}
my $existingData = $session->db->buildArrayRefOfHashRefs('select * from commerceSalesTax order by regionIdentifier');
##To build the form, we need two pieces
##1: The table contains all information from the database
my @existingStates = map { $_->{regionIdentifier} } @{ $existingData };
my %existingStates = map { $_ => 1 } @existingStates;
##2: The list contains all states except for those in the table;
my $stateObj = Locale::US->new();
my @stateNames = $stateObj->all_state_names;
my @newStates = sort grep {! exists $existingStates{$_} } @stateNames;
my %orderedStates;
tie %orderedStates, 'Tie::IxHash';
my $i18n = WebGUI::International->new($session);
%orderedStates = map { $_ => $_ } 'Select State', @newStates;
$orderedStates{'Select State'} = $i18n->get('Select State');
my $statesField = WebGUI::Form::selectBox($session,
-name => 'stateChooser',
-options => \%orderedStates,
-default => 'Select State',
);
$session->errorHandler->warn($statesField);
my $taxField = WebGUI::Form::float($session,
-name => 'taxRate',
-value => '',
-size => 6,
);
my $addButton = WebGUI::Form::button($session,
-name=>"addTaxInfo",
-value=>"Add Tax Information",
-extras=>q!align="right" onclick="addState()"!,
);
##build the table to display all existing sales tax
my $tableRows = '';
my $deleteIcon = $session->config->get('extrasURL').'/toolbar/bullet/delete.gif';
foreach my $sRow ( @{$existingData} ) {
$tableRows .= sprintf <<EOTR, $deleteIcon, $sRow->{commerceSalesTaxId}, $sRow->{regionIdentifier}, $sRow->{salesTax};
<tr>
<td class="cell"><img style="cursor:pointer;" src="%s" onclick="deleteState(event,'%s')"></td>
<td class="cell"><span>%s</span></td>
<td class="cell"><span>%6.4f%%</span></td>
</tr>
EOTR
}
my $stateForm = sprintf <<EOSF, $taxField, $statesField, $addButton;
<table id="salesTaxEntryTable">
<tbody>
<tr>
<td class="cell">%s&nbsp;%% tax for</td>
<td class="cell">%s</td>
<td class="cell">%s</td>
</tr>
</tbody>
</table>
EOSF
my $stateTable = sprintf <<EOST, $tableRows;
<table id="salesTaxDataTable" border="1" cellpadding="3">
<tbody>
%s
</tbody>
</table>
EOST
$stateTable = '' unless $tableRows;
return $stateForm.$stateTable;
}
1;

View file

@ -146,15 +146,15 @@ sub www_editProduct {
$i18n = WebGUI::International->new($session, 'ProductManager');
$productId = $session->form->process("productId");
unless ($productId eq 'new') {
$product = WebGUI::Product->new($session,$productId)->get;
}
$f = WebGUI::TabForm->new($session);
$f->addTab("properties","Properties");
$f->addTab("actions","Actions");
$f->addTab("properties","Actions");
$f->submit;
$f->hidden({
name => 'op',
@ -164,7 +164,7 @@ sub www_editProduct {
name => 'productId',
value => $productId
});
$f->getTab("properties")->text(
-name => 'title',
-label => $i18n->get('title'),
@ -185,6 +185,12 @@ sub www_editProduct {
-value => $session->form->process("price") || $product->{price},
-maxlength => 13,
);
$f->getTab("properties")->yesNo(
-name => 'useSalesTax',
-label => $i18n->get('useSalesTax'),
-hoverHelp => $i18n->get('useSalesTax description'),
-value => $session->form->process("useSalesTax") || $product->{useSalesTax},
);
$f->getTab("properties")->float(
-name => 'weight',
-label => $i18n->get('weight'),
@ -213,35 +219,37 @@ sub www_editProduct {
-value => $session->form->process("skuTemplate") || $product->{skuTemplate},
-maxlength => 255,
);
$f->getTab("actions")->group(
-name => 'groupId',
-label => $i18n->get('group id'),
-hoverHelp => $i18n->get('group id description'),
-value => $session->form->process("groupId") || $product->{groupId},
);
my %groupExpiresOffsetOptions;
tie %groupExpiresOffsetOptions, 'Tie::IxHash',
'1month' => $i18n->get("1 month"),
'6month' => $i18n->get("6 months"),
'1year' => $i18n->get("1 year"),
'2year' => $i18n->get("2 years"),
'3year' => $i18n->get("3 years"),
'5year' => $i18n->get("5 years"),
'10year' => $i18n->get("10 years"),
'1000year' => $i18n->get("lifetime"),
;
$f->getTab("actions")->selectBox(
-name => 'groupExpiresOffset',
-label => $i18n->get('group expires offset'),
-hoverHelp => $i18n->get('group expires offset description'),
-value => $session->form->process("groupExpiresOffset") || $product->{groupExpiresOffset},
-options => \%groupExpiresOffsetOptions,
-defaultValue => '1000year',
);
$f->getTab("actions")->group(
-name => 'groupId',
-label => $i18n->get('group id'),
-hoverHelp => $i18n->get('group id description'),
-value => $session->form->process("groupId") || $product->{groupId},
);
my %groupExpiresOffsetOptions;
tie %groupExpiresOffsetOptions, 'Tie::IxHash',
'1month' => $i18n->get("1 month"),
'6month' => $i18n->get("6 months"),
'1year' => $i18n->get("1 year"),
'2year' => $i18n->get("2 years"),
'3year' => $i18n->get("3 years"),
'5year' => $i18n->get("5 years"),
'10year' => $i18n->get("10 years"),
'1000year' => $i18n->get("lifetime"),
;
$f->getTab("actions")->selectBox(
-name => 'groupExpiresOffset',
-label => $i18n->get('group expires offset'),
-hoverHelp => $i18n->get('group expires offset description'),
-value => $session->form->process("groupExpiresOffset") || $product->{groupExpiresOffset},
-options => \%groupExpiresOffsetOptions,
-defaultValue => '1000year',
);
return _submenu($session,$f->print, 'edit product', 'edit product', 'ProductManager');
}
@ -278,14 +286,15 @@ sub www_editProductSave {
title => $session->form->process("title"),
description => $session->form->process("description"),
price => $session->form->process("price"),
useSalesTax => $session->form->process("useSalesTax"),
weight => $session->form->process("weight"),
sku => $session->form->process("sku"),
templateId => $session->form->process("templateId"),
skuTemplate => $session->form->process("skuTemplate"),
groupId => $session->form->process('groupId'),
groupId => $session->form->process('groupId'),
groupExpiresOffset => $session->form->process('groupExpiresOffset'),
});
return www_manageProduct($session, $product->get('productId'));
@ -853,6 +862,10 @@ sub www_manageProduct {
$output .= "<h2>".$i18n->get('properties').$session->icon->edit('op=editProduct;productId='.$productId)."</h2>";
$output .= "<table>";
$output .= "<tr><td>".$i18n->get('price')."</td><td>".$product->get('price')."</td></tr>";
my $useSalesTax = $product->get('useSalesTax')
? $i18n->get(138, 'WebGUI')
: $i18n->get(139, 'WebGUI');
$output .= "<tr><td>".$i18n->get('useSalesTax')."</td><td>".$useSalesTax."</td></tr>";
$output .= "<tr><td>".$i18n->get('weight')."</td><td>".$product->get('weight')."</td></tr>";
$output .= "<tr><td>".$i18n->get('sku')."</td><td>".$product->get('sku')."</td></tr>";
$output .= "<tr><td>".$i18n->get('description')."</td><td>".$product->get('description')."</td></tr>";

View file

@ -338,6 +338,12 @@ sub www_editSubscription {
-hoverHelp => $i18n->get('subscription price description'),
-value => $properties->{price} || '0.00'
);
$f->yesNo(
-name => 'useSalesTax',
-label => $i18n->get('useSalesTax'),
-hoverHelp => $i18n->get('useSalesTax description'),
-value => $properties->{useSalesTax} || 0,
);
$f->textarea(
-name => 'description',
-label => $i18n->get('subscription description'),
@ -393,7 +399,7 @@ sub www_editSubscriptionSave {
return $session->privilege->adminOnly() unless ($session->user->isInGroup(3));
my $properties = {};
@relevantFields = qw(subscriptionId name price description subscriptionGroup duration executeOnSubscription karma);
@relevantFields = qw(subscriptionId name useSalesTax price description subscriptionGroup duration executeOnSubscription karma);
foreach (@relevantFields) {
$properties->{$_} = $session->form->process($_) if (defined $session->form->process($_));
}