From 9a827affc49ad93e5331f96302a848cf8578d455 Mon Sep 17 00:00:00 2001 From: Paul Driver Date: Thu, 29 Apr 2010 17:35:43 -0700 Subject: [PATCH] Updating shipping/tax info based on address --- lib/WebGUI/Shop/Cart.pm | 54 ++++++++++++++ www/extras/shop/cart.js | 161 +++++++++++++++++++++++++++------------- 2 files changed, 163 insertions(+), 52 deletions(-) diff --git a/lib/WebGUI/Shop/Cart.pm b/lib/WebGUI/Shop/Cart.pm index 043033128..1199a261d 100644 --- a/lib/WebGUI/Shop/Cart.pm +++ b/lib/WebGUI/Shop/Cart.pm @@ -794,6 +794,57 @@ sub updateFromForm { #------------------------------------------------------------------- +=head2 www_ajaxPrices + +Input: shippingId (an addressId) + billingId (an addressId) + +Output: { + tax : 1.25, + subtotal : 12.00, + shipping : { + slfjaldjfalfja: { + label : 'USPS', + price : 12.50, + hasPrice : 1 || 0 + }, + { ... }, + { ... } + } +} + +Takes an addressId and returns JSON shipping options for it, in the form . + +=cut + +sub www_ajaxPrices { + my $self = shift; + my $session = $self->session; + my $form = $session->form; + my $billing = $form->get('billingId'); + my $shipping = $form->get('shippingId'); + my $response = { + subtotal => $self->calculateSubtotal(), + + tax => eval { + my $addr = $shipping || $billing or die; + $self->update({ shippingAddressId => $addr }); + $self->calculateTaxes(); + } || 0, + + shipping => eval { + die unless $shipping; + $self->update({ shippingAddressId => $shipping }); + my $ship = WebGUI::Shop::Ship->new($self->session); + $ship->getOptions($self); + } || [], + }; + $session->http->setMimeType('text/plain'); + return JSON->new->encode($response); +} + +#------------------------------------------------------------------- + =head2 www_continueShopping ( ) Update the cart and the return the user back to the asset. @@ -1160,6 +1211,9 @@ sub www_view { my $yui = $url->extras('/yui/build'); $style->setScript("$yui/yahoo/yahoo-min.js"); $style->setScript("$yui/json/json-min.js"); + $style->setScript("$yui/event/event-min.js"); + $style->setScript("$yui/connection/connection-min.js"); + $style->setScript($url->extras('underscore/underscore-min.js')); $style->setScript($url->extras('shop/cart.js'), undef, 1); return $session->style->userStyle($template->process(\%var)); } diff --git a/www/extras/shop/cart.js b/www/extras/shop/cart.js index 9d98bddba..e36e4f3f2 100644 --- a/www/extras/shop/cart.js +++ b/www/extras/shop/cart.js @@ -1,62 +1,120 @@ -/*global window, document, YAHOO */ +/*global _, window, document, YAHOO */ (function () { var $event = YAHOO.util.Event, $connect = YAHOO.util.Connect, $json = YAHOO.lang.JSON, + prices = null, addressCache = {}, elements = { - dropdowns: { - billing: 'billingAddressId_formId', + shipper : 'shipperId_formId', + tax : 'taxWrap', + total : 'totalPriceWrap', + credit : { + available : 'inShopCreditAvailableWrap', + used : 'inShopCreditDeductionWrap' + }, + dropdowns : { + billing : 'billingAddressId_formId', shipping: 'shippingAddressId_formId' } }, addressParts = [ 'label', 'firstName', 'lastName', 'organization', 'address1', - 'address2', 'address3', 'city', 'state', 'code', 'country', + 'address2', 'address3', 'city', 'state', 'code', 'country', 'phoneNumber', 'email' ]; - function omap(o, fn) { - var r = [], k; - for (k in o) { - if (o.hasOwnProperty(k)) { - r.push(fn.call(o, k, o[k])); - } - } - return r; - } - - function oeach(o, fn) { - omap(o, fn); - return; + function formatCurrency(n) { + return parseFloat(n.toString()).toFixed(2); } function addAddressKind(name) { - var i, key, obj = elements[name] = {}; - for (i = 0; i < addressParts.length; i += 1) { - key = addressParts[i]; + var obj = elements[name] = {}; + _.each(addressParts, function (key) { obj[key] = name + '_' + key + '_formId'; - } + }); } function getDomElements(o) { - oeach(o, function (k, v) { + _.each(o, function (v, k) { if (typeof v === 'object') { getDomElements(v); } else { - this[k] = document.getElementById(v); + o[k] = document.getElementById(v); } }); } function sameChange() { var d = elements.same.checked; - oeach(elements.shipping, function (k, v) { + _.each(elements.shipping, function (v, k) { v.disabled = d; }); - elements.dropdowns.shipping.disabled = d; + elements.dropdowns.shipping.disabled = d; + } + + function calculateSummary() { + var shipping = prices.shipping[elements.shipper.value], + shipPrice = (shipping ? + (shipping.hasPrice ? + parseFloat(shipping.price) : + 0) + : 0), + tax = parseFloat(prices.tax), + subtotal = parseFloat(prices.subtotal), + beforeCredit = tax + subtotal + shipPrice, + creditAvail = parseFloat(elements.credit.available.innerHTML), + creditUsed = Math.min(beforeCredit, creditAvail), + afterCredit = beforeCredit - creditUsed; + + elements.credit.used.innerHTML = formatCurrency(creditUsed); + elements.total.innerHTML = formatCurrency(afterCredit); + } + + function updatePrices() { + var selectedShipper = elements.shipper.value, + shipping = elements.dropdowns.shipping.value, + billing = elements.dropdowns.billing.value, + shipper = elements.shipper, + url = window.location.pathname + + '?shop=cart;method=ajaxPrices;' + + ( shipping === 'new_address' ? + '' : 'shippingId=' + shipping) + + ( billing === 'new_address' ? + '' : 'billingId=' + billing); + cb = { + success: function (o) { + var response = $json.parse(o.responseText); + if (response.error) { + return; + } + prices = response; + elements.tax.innerHTML = formatCurrency(response.tax); + _(shipper.options) + .chain() + .map(_.identity) + .each(function (o) { + if (o.value) { + o.parentNode.removeChild(o); + } + }); + _.each(response.shipping, function (o, id) { + var opt = document.createElement('option'), + label = o.label; + if (o.hasPrice) { + label += ' (' + formatCurrency(o.price) + ')'; + } + opt.innerHTML = label; + opt.value = id; + shipper.appendChild(opt); + }); + shipper.value = selectedShipper; + calculateSummary(); + } + }; + $connect.asyncRequest('GET', url, cb); } function updateAddressDropdowns(o) { @@ -64,24 +122,22 @@ id = o.responseText; function updateOne(dropdown) { - var options = dropdown.options, i, opt; - for (i = 0; i < options.length; i += 1) { - opt = options[i]; - if (opt.text === label) { - opt.value = id; - return; - } - } + var opt = _.detect(dropdown.options, function (o) { + return o.text === label; + }); - opt = document.createElement('option'); + if (!opt) { + opt = document.createElement('option'); + opt.text = label; + dropdown.appendChild(opt); + } opt.value = id; - opt.text = label; - dropdown.appendChild(opt); } updateOne(elements.dropdowns.billing); updateOne(elements.dropdowns.shipping); elements.dropdowns[o.argument.name].value = id; + updatePrices(); } function saveAddress(a, name) { @@ -89,7 +145,7 @@ success: updateAddressDropdowns, argument: { address: a, name: name } }, - post = 'shop=address;method=ajaxSave;address=' + + post = 'shop=address;method=ajaxSave;address=' + $json.stringify(a), url = window.location.pathname; @@ -97,13 +153,13 @@ } function validAddress(a) { - return a.label && - a.firstName && - a.lastName && - a.address1 && - a.city && - a.state && - a.code && + return a.label && + a.firstName && + a.lastName && + a.address1 && + a.city && + a.state && + a.code && a.country; } @@ -122,7 +178,7 @@ cached = addressCache[label] = {}; } - oeach(elements[name], function (k, v) { + _.each(elements[name], function (v, k) { v = v.value; address[k] = v; if (cached[k] !== v) { @@ -142,12 +198,13 @@ function addressUpdater(name) { var elems = elements[name]; function update(address) { - oeach(address, function (k, v) { + _.each(address, function (v, k) { var dom = elems[k]; if (dom) { dom.value = v; } }); + updatePrices(); } return function () { var id = this.value, @@ -159,8 +216,8 @@ return update(cached); } - url = window.location.pathname + - '?shop=address;method=ajaxGetAddress;addressId=' + + url = window.location.pathname + + '?shop=address;method=ajaxGetAddress;addressId=' + id; cb = { @@ -175,10 +232,7 @@ } function handleBlur(name) { - var values = omap(elements[name], function (k, v) { - return v; - }); - $event.on(values, 'focusout', addressChange(name)); + $event.on(_.values(elements[name]), 'focusout', addressChange(name)); } function handleDropdown(name) { @@ -207,6 +261,9 @@ else { delete elements.shipping; } + + $event.on(elements.shipper, 'change', calculateSummary); + updatePrices(); } $event.onDOMReady(main);