From f3ab99bc023ffba436ce3d4d5879bd0cc5a2b3df Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Sat, 21 Oct 2006 01:13:07 +0000 Subject: [PATCH] sales tax --- docs/changelog/7.x.x.txt | 1 + .../commerce_checkout_confirmation.tmpl | 78 +++++++++++++ docs/upgrades/upgrade_7.1.2-7.2.0.pl | 21 ++++ .../Asset/Wobject/EventManagementSystem.pm | 10 +- lib/WebGUI/Commerce/Item.pm | 14 +++ lib/WebGUI/Commerce/Item/Event.pm | 8 +- lib/WebGUI/Commerce/Item/Fake.pm | 33 ++++++ lib/WebGUI/Commerce/Item/Product.pm | 63 ++++++----- lib/WebGUI/Commerce/Item/Subscription.pm | 6 + lib/WebGUI/Commerce/ShoppingCart.pm | 6 +- lib/WebGUI/Help/Commerce.pm | 16 ++- lib/WebGUI/Operation.pm | 1 + lib/WebGUI/Operation/Commerce.pm | 104 +++++++++++++++--- lib/WebGUI/Operation/FormHelpers.pm | 104 ++++++++++++++++++ lib/WebGUI/Operation/ProductManager.pm | 87 ++++++++------- lib/WebGUI/Operation/Subscription.pm | 8 +- lib/WebGUI/Subscription.pm | 2 +- .../English/Asset_EventManagementSystem.pm | 10 ++ lib/WebGUI/i18n/English/Commerce.pm | 77 +++++++++---- lib/WebGUI/i18n/English/ProductManager.pm | 10 ++ lib/WebGUI/i18n/English/Subscription.pm | 10 ++ lib/WebGUI/i18n/English/WebGUI.pm | 11 +- www/extras/operations/salesTaxAjax.js | 38 +++++++ 23 files changed, 607 insertions(+), 111 deletions(-) create mode 100644 docs/upgrades/templates-7.2.0/commerce_checkout_confirmation.tmpl create mode 100644 lib/WebGUI/Commerce/Item/Fake.pm create mode 100755 www/extras/operations/salesTaxAjax.js diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index 9f9037dbf..c2c37ea70 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -1,5 +1,6 @@ 7.2.0 - Added server side spellchecker (Martin Kamerbeek / Procolix) + - Added configurable sales tax. (Tiffany Patterson / Elite Marketing) - change: made Text::Aspell optional, nullifying spellchecker if not present 7.1.2 diff --git a/docs/upgrades/templates-7.2.0/commerce_checkout_confirmation.tmpl b/docs/upgrades/templates-7.2.0/commerce_checkout_confirmation.tmpl new file mode 100644 index 000000000..8726d5763 --- /dev/null +++ b/docs/upgrades/templates-7.2.0/commerce_checkout_confirmation.tmpl @@ -0,0 +1,78 @@ +#PBtmpl0000000000000016 + · + · +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ProductQuantityPriceAmountSales TaxEach
 
 Subtotal
Shipping
Sales Tax Rate 
Total Sales Tax 
Total
+ +

+ + diff --git a/docs/upgrades/upgrade_7.1.2-7.2.0.pl b/docs/upgrades/upgrade_7.1.2-7.2.0.pl index 66372d097..5d643b25c 100644 --- a/docs/upgrades/upgrade_7.1.2-7.2.0.pl +++ b/docs/upgrades/upgrade_7.1.2-7.2.0.pl @@ -20,11 +20,32 @@ my $quiet; # this line required my $session = start(); # this line required +commerceSalesTax($session); createDictionaryStorage($session); finish($session); # this line required +##------------------------------------------------- +sub commerceSalesTax { + my $session = shift; + print "\tAdding tables and columns to support sales tax in the Commerce System.\n" unless ($quiet); + $session->db->write(<db->write(<db->write(<db->write(<new($self->session,'Asset_EventManagementSystem'); my $event = $self->session->db->quickHashRef(" - select p.productId, p.title, p.description, p.price, p.weight, p.sku, p.templateId, p.skuTemplate, e.prerequisiteId, e.passType, e.passId, + select p.productId, p.title, p.description, p.price, p.useSalesTax, p.weight, p.sku, p.templateId, p.skuTemplate, e.prerequisiteId, e.passType, e.passId, e.startDate, e.endDate, e.maximumAttendees, e.approved from products as p, EventManagementSystem_products as e @@ -1662,6 +1662,13 @@ sub www_editEvent { -value => $storageId ); + $f->yesNo( + -name => "useSalesTax", + -value => $self->session->form->get("useSalesTax") || $event->{useSalesTax}, + -hoverHelp => $i18n->get('add/edit useSalesTax description'), + -label => $i18n->get('add/edit useSalesTax') + ); + $f->float( -name => "price", -value => $self->session->form->get("price") || $event->{price}, @@ -1893,6 +1900,7 @@ sub www_editEventSave { title => $self->session->form->get("title", "text"), description => $self->session->form->get("description", "HTMLArea"), price => $self->session->form->get("price", "float"), + useSalesTax => $self->session->form->get("useSalesTax", "yesNo"), weight => $self->session->form->get("weight", "float"), sku => $self->session->form->get("sku", "text"), skuTemplate => $self->session->form->get("skuTemplate", "text"), diff --git a/lib/WebGUI/Commerce/Item.pm b/lib/WebGUI/Commerce/Item.pm index ec8176b07..058608e46 100644 --- a/lib/WebGUI/Commerce/Item.pm +++ b/lib/WebGUI/Commerce/Item.pm @@ -230,6 +230,20 @@ sub type { #------------------------------------------------------------------- +=head2 useSalesTax ( ) + +This method should return whether or not the item uses sales tax. +This must be implemented by an item plugin. + +=cut + +sub useSalesTax { + my $self = shift; + return $self->session->errorHandler->fatalError('The useSalesTax method of WebGUI::Commerce::Item must be overridden.'); +} + +#------------------------------------------------------------------- + =head2 weight ( ) Returns the weight of the item. If your item has a weight, you'll want to overload this method. Weight is calculated on a unit based scale. diff --git a/lib/WebGUI/Commerce/Item/Event.pm b/lib/WebGUI/Commerce/Item/Event.pm index bd813b3a2..97c22d926 100644 --- a/lib/WebGUI/Commerce/Item/Event.pm +++ b/lib/WebGUI/Commerce/Item/Event.pm @@ -88,7 +88,7 @@ sub new { $session = shift; $eventId = shift; - my $eventData = $session->db->quickHashRef("select p.productId, p.title, p.description, p.price, p.sku, e.approved, e.passId, e.passType + my $eventData = $session->db->quickHashRef("select p.productId, p.title, p.description, p.price, p.useSalesTax, p.sku, e.approved, e.passId, e.passType from EventManagementSystem_products as e, products as p where p.productId = e.productId and p.productId=".$session->db->quote($eventId)); @@ -183,6 +183,12 @@ sub type { return 'Event'; } +#------------------------------------------------------------------- +sub useSalesTax { + my $self = shift; + return $self->{_event}->{useSalesTax} ? 1 : 0; +} + #------------------------------------------------------------------- sub weight { return 0; diff --git a/lib/WebGUI/Commerce/Item/Fake.pm b/lib/WebGUI/Commerce/Item/Fake.pm new file mode 100644 index 000000000..a9a2a6703 --- /dev/null +++ b/lib/WebGUI/Commerce/Item/Fake.pm @@ -0,0 +1,33 @@ +package WebGUI::Commerce::Item::Fake; +# Adding this to cope with sales tax without changing the schema. + +#------------------------------------------------------------------- +# WebGUI is Copyright 2001-2006 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 +#------------------------------------------------------------------- + +use strict; +use WebGUI::Commerce::Item; +use base 'WebGUI::Commerce::Item'; + +sub new { + my $class = shift; + my $session = shift; + my $id = shift; + my $namespace = shift; + my ($price, $name) = split /\,/, $id, 2; + bless { _name => $name, _price => $price }, $class; +} + +sub useSalesTax { 0 } +sub name { $_[0]{_name} } +sub price { $_[0]{_price} } +sub type { 'Fake' } +sub id { "$_[0]{_price},$_[0]{_name}" } +sub description { $_[0]{_name} } +1; diff --git a/lib/WebGUI/Commerce/Item/Product.pm b/lib/WebGUI/Commerce/Item/Product.pm index d8dd36979..883f752ba 100755 --- a/lib/WebGUI/Commerce/Item/Product.pm +++ b/lib/WebGUI/Commerce/Item/Product.pm @@ -44,36 +44,37 @@ sub description { #------------------------------------------------------------------- sub handler { - my $self = shift; - - ### Add to group action - # If group is 'everyone', skip - unless ($self->{_product}->get('groupId') eq '7') { - my $g = WebGUI::Group->new($self->session,$self->{_product}->get('groupId')); - my $expiresOffset; - - # Parse the value - if ($self->{_product}->get('groupExpiresOffset') =~ /^(\d+)month/i) { - $expiresOffset = $1 * 3600*24*30; # One month - } elsif ($self->{_product}->get('groupExpiresOffset') =~ /^(\d+)year/i) { - $expiresOffset = $1 * 3600*24*365; # One year - } - - # Multiply by how many quantity we're purchasing - #!!! TODO !!! - handlers don't know how many we're purchasing - - # If user has time left - my $remains = $g->userGroupExpireDate($self->session->user->userId); - if ($remains) { - # Add any remaining time to the offset - $expiresOffset += $remains - time(); - } - - # Add user to group - $g->addUsers([$self->session->user->userId],$expiresOffset); - } + my $self = shift; + + ### Add to group action + # If group is 'everyone', skip + unless ($self->{_product}->get('groupId') eq '7') { + my $g = WebGUI::Group->new($self->session,$self->{_product}->get('groupId')); + my $expiresOffset; + + # Parse the value + if ($self->{_product}->get('groupExpiresOffset') =~ /^(\d+)month/i) { + $expiresOffset = $1 * 3600*24*30; # One month + } elsif ($self->{_product}->get('groupExpiresOffset') =~ /^(\d+)year/i) { + $expiresOffset = $1 * 3600*24*365; # One year + } + + # Multiply by how many quantity we're purchasing + #!!! TODO !!! - handlers don't know how many we're purchasing + + # If user has time left + my $remains = $g->userGroupExpireDate($self->session->user->userId); + if ($remains) { + # Add any remaining time to the offset + $expiresOffset += $remains - time(); + } + + # Add user to group + $g->addUsers([$self->session->user->userId],$expiresOffset); + } } + #------------------------------------------------------------------- sub id { return $_[0]->{_variant}->{variantId}; @@ -121,6 +122,12 @@ sub price { return $_[0]->{_variant}->{price}; } +#------------------------------------------------------------------- +sub useSalesTax { + my $self = shift; + return $self->{_product}->get('useSalesTax') ? 1 : 0; +} + #------------------------------------------------------------------- sub type { return 'Product'; diff --git a/lib/WebGUI/Commerce/Item/Subscription.pm b/lib/WebGUI/Commerce/Item/Subscription.pm index af68c8840..be781e5c4 100644 --- a/lib/WebGUI/Commerce/Item/Subscription.pm +++ b/lib/WebGUI/Commerce/Item/Subscription.pm @@ -82,6 +82,12 @@ sub price { return $_[0]->{_subscription}->get('price'); } +#------------------------------------------------------------------- +sub useSalesTax { + my $self = shift; + return $self->{_subscription}->get('useSalesTax') ? 1 : 0; +} + #------------------------------------------------------------------- sub type { return 'Subscription'; diff --git a/lib/WebGUI/Commerce/ShoppingCart.pm b/lib/WebGUI/Commerce/ShoppingCart.pm index 96c56bbae..002a2f903 100644 --- a/lib/WebGUI/Commerce/ShoppingCart.pm +++ b/lib/WebGUI/Commerce/ShoppingCart.pm @@ -219,16 +219,20 @@ The instantiated plugin of this item. See WebGUI::Commerce::Item for a detailed sub getItems { my ($self, $periodResolve, %cartContent, $item, $properties, @recurring, @normal); $self = shift; + my $salesTaxRate = shift; $periodResolve = WebGUI::Commerce::Payment->recurringPeriodValues($self->session); %cartContent = %{$self->{_items}}; foreach (values(%cartContent)) { $item = WebGUI::Commerce::Item->new($self->session,$_->{itemId}, $_->{itemType}); + my $totalPrice = $item->price * $_->{quantity}; + my $productTax = $salesTaxRate * $item->useSalesTax; $properties = { quantity => $_->{quantity}, period => lc($periodResolve->{$item->duration}), name => $item->name, price => sprintf('%.2f', $item->price), - totalPrice => sprintf('%.2f', $item->price * $_->{quantity}), + totalPrice => sprintf('%.2f', $totalPrice), + salesTax => sprintf('%.2f', $totalPrice * $productTax), item => $item, }; diff --git a/lib/WebGUI/Help/Commerce.pm b/lib/WebGUI/Help/Commerce.pm index 52e757ba4..29791d9e0 100644 --- a/lib/WebGUI/Help/Commerce.pm +++ b/lib/WebGUI/Help/Commerce.pm @@ -50,6 +50,11 @@ our $HELP = { description => 'shipping plugin label description', namespace => 'Commerce', }, + { + title => 'enable sales tax', + description => 'enable sales tax description', + namespace => 'Commerce', + }, ], related => [ ] @@ -106,6 +111,9 @@ our $HELP = { { 'name' => 'name' }, + { + 'name' => 'salesTax' + }, { 'name' => 'price' }, @@ -122,7 +130,13 @@ our $HELP = { }, { 'name' => 'form' - } + }, + { + 'name' => 'salesTaxRate' + }, + { + 'name' => 'totalSalesTax' + }, ], related => [ { diff --git a/lib/WebGUI/Operation.pm b/lib/WebGUI/Operation.pm index c767e0266..10e0e91df 100644 --- a/lib/WebGUI/Operation.pm +++ b/lib/WebGUI/Operation.pm @@ -137,6 +137,7 @@ sub getOperations { 'richEditImageTree' => 'WebGUI::Operation::FormHelpers', 'richEditPageTree' => 'WebGUI::Operation::FormHelpers', 'richEditViewThumbnail' => 'WebGUI::Operation::FormHelpers', + 'salesTaxTable' => 'WebGUI::Operation::FormHelpers', 'addGroupsToGroupSave' => 'WebGUI::Operation::Group', 'addUsersToGroupSave' => 'WebGUI::Operation::Group', diff --git a/lib/WebGUI/Operation/Commerce.pm b/lib/WebGUI/Operation/Commerce.pm index 18fa00eb5..600ee6e36 100644 --- a/lib/WebGUI/Operation/Commerce.pm +++ b/lib/WebGUI/Operation/Commerce.pm @@ -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.'
'; + $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(''); + $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('
'.$stateForm.'
'); + $tabform->getTab('salesTax')->raw(''); # 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 (/~([^~]*)~([^~]*)~([^~]*)/) { diff --git a/lib/WebGUI/Operation/FormHelpers.pm b/lib/WebGUI/Operation/FormHelpers.pm index 834c279bb..fe87ad708 100644 --- a/lib/WebGUI/Operation/FormHelpers.pm +++ b/lib/WebGUI/Operation/FormHelpers.pm @@ -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 <{commerceSalesTaxId}, $sRow->{regionIdentifier}, $sRow->{salesTax}; + + +%s +%6.4f%% + +EOTR + } + my $stateForm = sprintf < + + +%s %% tax for +%s +%s + + + +EOSF + +my $stateTable = sprintf < + +%s + + +EOST + $stateTable = '' unless $tableRows; + return $stateForm.$stateTable; +} + + 1; diff --git a/lib/WebGUI/Operation/ProductManager.pm b/lib/WebGUI/Operation/ProductManager.pm index bf73d7b4f..f945d00a0 100755 --- a/lib/WebGUI/Operation/ProductManager.pm +++ b/lib/WebGUI/Operation/ProductManager.pm @@ -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 .= "

".$i18n->get('properties').$session->icon->edit('op=editProduct;productId='.$productId)."

"; $output .= ""; $output .= ""; + my $useSalesTax = $product->get('useSalesTax') + ? $i18n->get(138, 'WebGUI') + : $i18n->get(139, 'WebGUI'); + $output .= ""; $output .= ""; $output .= ""; $output .= ""; diff --git a/lib/WebGUI/Operation/Subscription.pm b/lib/WebGUI/Operation/Subscription.pm index a7a9957aa..a3e77a840 100644 --- a/lib/WebGUI/Operation/Subscription.pm +++ b/lib/WebGUI/Operation/Subscription.pm @@ -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($_)); } diff --git a/lib/WebGUI/Subscription.pm b/lib/WebGUI/Subscription.pm index 016e818cd..a46c3e5ea 100644 --- a/lib/WebGUI/Subscription.pm +++ b/lib/WebGUI/Subscription.pm @@ -177,7 +177,7 @@ sub set { $properties = shift; foreach (keys(%{$properties})) { - if (isIn($_, qw(name price description subscriptionGroup duration executeOnSubscription karma))) { + if (isIn($_, qw(name price useSalesTax description subscriptionGroup duration executeOnSubscription karma))) { $self->{_properties}{$_} = $value; push(@fieldsToUpdate, $_); } diff --git a/lib/WebGUI/i18n/English/Asset_EventManagementSystem.pm b/lib/WebGUI/i18n/English/Asset_EventManagementSystem.pm index cc3776f81..21ae11408 100644 --- a/lib/WebGUI/i18n/English/Asset_EventManagementSystem.pm +++ b/lib/WebGUI/i18n/English/Asset_EventManagementSystem.pm @@ -187,6 +187,16 @@ our $I18N = { ##hashref of hashes lastUpdated => 1138312761, }, + 'add/edit useSalesTax' => { + message => q|Use Sales Tax?|, + lastUpdated => 1160109884, + }, + + 'add/edit useSalesTax description' => { + message => q|Should this event have sales tax applied to it?|, + lastUpdated => 1160109886, + }, + 'add/edit event maximum attendees' => { message => q|Maximum Attendees|, lastUpdated => 1138312761, diff --git a/lib/WebGUI/i18n/English/Commerce.pm b/lib/WebGUI/i18n/English/Commerce.pm index 1b63727e1..438265aa6 100755 --- a/lib/WebGUI/i18n/English/Commerce.pm +++ b/lib/WebGUI/i18n/English/Commerce.pm @@ -332,10 +332,6 @@ our $I18N = { context => q|The body of the help page of the list pending transactions screen.| }, - - - - 'help cancel checkout template title' => { message => q|Cancel checkout template|, lastUpdated => 0, @@ -370,28 +366,43 @@ our $I18N = { }, 'quantity' => { - message => q| The quantity of the current item in the shopping cart.
|, - lastUpdated => 1149221320, + message => q|The quantity of the current item in the shopping cart.
|, + lastUpdated => 1161319738, }, 'period' => { - message => q| The period of the recurring payment.
|, - lastUpdated => 1149221320, + message => q|The period of the recurring payment.
|, + lastUpdated => 1161319740, }, 'name' => { - message => q| The name of this item.
|, - lastUpdated => 1149221320, + message => q|The name of this item.
|, + lastUpdated => 1161319741, }, 'price' => { - message => q| The price of one item.
|, - lastUpdated => 1149221320, + message => q|The price of one item.
|, + lastUpdated => 1161319747, }, 'totalPrice' => { - message => q| The price of the quantity of this item. (totalPrice = quantity * price)|, - lastUpdated => 1149221320, + message => q|The price of the quantity of this item. (totalPrice = quantity * price)|, + lastUpdated => 1161319749, + }, + + 'salesTax' => { + message => q|The amount of sales tax for this item.|, + lastUpdated => 1161319799, + }, + + 'salesTaxRate' => { + message => q|The sales tax rate, as determined by the user's homeState in their profile.|, + lastUpdated => 1161319799, + }, + + 'totalSalesTax' => { + message => q|The sum of all sales taxes applied to eligible items.|, + lastUpdated => 1161319799, }, 'recurringItems' => { @@ -400,8 +411,8 @@ our $I18N = { }, 'recurringItemLoop' => { - message => q|A loop containing the recurring items in the shopping cart. For available template variables seen

normalItemLoop|, - lastUpdated => 1149221320, + message => q|A loop containing the recurring items in the shopping cart. For available template variables see

normalItemLoop|, + lastUpdated => 1161320125, }, 'form' => { @@ -437,23 +448,23 @@ our $I18N = { }, 'purchaseDescription' => { - message => q| The description of this transaction.
|, - lastUpdated => 1149221449, + message => q|The description of this transaction.
|, + lastUpdated => 1161319762, }, 'status' => { - message => q| The status of this item.
|, - lastUpdated => 1149221449, + message => q|The status of this item.
|, + lastUpdated => 1161319763, }, 'error' => { - message => q| The error text returned from the payment plugin.
|, - lastUpdated => 1149221449, + message => q|The error text returned from the payment plugin.
|, + lastUpdated => 1161319765, }, 'errorCode' => { - message => q| The error code returned from the payment plugin.
|, - lastUpdated => 1149221449, + message => q|The error code returned from the payment plugin.
|, + lastUpdated => 1161319767, }, @@ -570,6 +581,24 @@ our $I18N = { context => q|The label of the SHipping tab in the commerce settings manager.| }, + 'salesTax tab' => { + message => q|SalesTax|, + lastUpdated => 1159845482, + context => q|The label of the sales tax tab in the commerce settings manager.| + }, + + 'enable sales tax' => { + message => q|Enable Sales Tax?|, + lastUpdated => 1160189717, + context => q|The label field in the commerce setting for enabling sales tax.| + }, + + 'enable sales tax description' => { + message => q|Set this to "Yes" if you would like Sales Tax enabled in the Commerce System. Sales Tax will be applied to any product, subscription or EMS event that has it enabled.|, + lastUpdated => 1160189808, + context => q|The label field in the commerce setting for enabling sales tax.| + }, + 'shipping plugin label' => { message => q|Shipping plugin|, lastUpdated => 0, diff --git a/lib/WebGUI/i18n/English/ProductManager.pm b/lib/WebGUI/i18n/English/ProductManager.pm index fd233165b..3869e0fc5 100644 --- a/lib/WebGUI/i18n/English/ProductManager.pm +++ b/lib/WebGUI/i18n/English/ProductManager.pm @@ -279,6 +279,16 @@ left of it. Use the delete button to remove it.
lastUpdated => 1120449422, }, + 'useSalesTax' => { + message => q|Use Sales Tax?|, + lastUpdated => 1159844899, + }, + + 'useSalesTax description' => { + message => q|Should this product have sales tax applied to it?|, + lastUpdated => 1159844899, + }, + 'weight description' => { message => q|The default weight of the product.|, lastUpdated => 1120449422, diff --git a/lib/WebGUI/i18n/English/Subscription.pm b/lib/WebGUI/i18n/English/Subscription.pm index db6b097fb..10174d5bc 100755 --- a/lib/WebGUI/i18n/English/Subscription.pm +++ b/lib/WebGUI/i18n/English/Subscription.pm @@ -118,6 +118,16 @@ our $I18N = { lastUpdated => 1120861450, }, + 'useSalesTax' => { + message => q|Use Sales Tax?|, + lastUpdated => 1159845025, + }, + + 'useSalesTax description' => { + message => q|Should this subscription have sales tax applied to it?|, + lastUpdated => 1159845045, + }, + 'subscription description description' => { message => q|

Detailed description of the subscription.

|, lastUpdated => 1120861450, diff --git a/lib/WebGUI/i18n/English/WebGUI.pm b/lib/WebGUI/i18n/English/WebGUI.pm index 50c2b792f..8bdd8aeb9 100644 --- a/lib/WebGUI/i18n/English/WebGUI.pm +++ b/lib/WebGUI/i18n/English/WebGUI.pm @@ -4007,11 +4007,16 @@ Get a copy of wget and use this: wget -p -r --html-extension -k http://the message => q|Country|, lastUpdated => 0, }, - + 'noldaplink' => { - message => q|No LDAP Connection|, + message => q|No LDAP Connection|, lastUpdated => 0, - } + }, + + 'Select State' => { + message => q|Select State|, + lastUpdated => 1161388472, + }, }; diff --git a/www/extras/operations/salesTaxAjax.js b/www/extras/operations/salesTaxAjax.js new file mode 100755 index 000000000..4099aa40a --- /dev/null +++ b/www/extras/operations/salesTaxAjax.js @@ -0,0 +1,38 @@ +//---------------------------------------------------------------------- +function deleteState (e,entryId) { + //Do nothing with e + paint('delete',entryId); +} + + +//---------------------------------------------------------------------- +function addState () { + if(document.getElementById('stateChooser_formId').value == "Select State") { + alert("You have not selected a state"); //optional + return; + } + if(document.getElementById('taxRate_formId').value == "") { + alert("You have not entered a tax rate"); //optional + return; + } + paint('add'); +} + + +//---------------------------------------------------------------------- +function paint (submitType,entryId) { + AjaxRequest.post({ + 'op':'salesTaxTable', + 'addDelete':submitType, + 'taxRate':document.getElementById('taxRate_formId').value, + 'entryId':entryId, + 'addStateId':document.getElementById('stateChooser_formId').value, + 'onSuccess':function(req){ + document.getElementById('salesTaxFormDiv').innerHTML = req.responseText; + } + }); +} + + + +
".$i18n->get('price')."".$product->get('price')."
".$i18n->get('useSalesTax')."".$useSalesTax."
".$i18n->get('weight')."".$product->get('weight')."
".$i18n->get('sku')."".$product->get('sku')."
".$i18n->get('description')."".$product->get('description')."