[webshop] shopping cart with shipping rule selector, totals

This commit is contained in:
Anand Doshi 2013-07-04 23:45:22 +05:30
parent a749996747
commit 0b4943cec1
7 changed files with 171 additions and 49 deletions

View File

@ -58,7 +58,7 @@ class DocType(DocListController):
then condition y can only be like 50 to 99 or 301 to 400
hence, non-overlapping condition = (x1 <= x2 < y1 <= y2) or (y1 <= y2 < x1 <= x2)
"""
separate = (x1 <= x2 < y1 <= y2) or (y1 <= y2 < x1 <= x2)
separate = (x1 <= x2 <= y1 <= y2) or (y1 <= y2 <= x1 <= x2)
return (not separate)
overlaps = []

View File

@ -73,17 +73,16 @@ class SellingController(StockController):
if not condition.to_value or (flt(condition.from_value) <= value <= flt(condition.to_value)):
shipping_amount = condition.shipping_amount
break
if shipping_amount:
self.doclist.append({
"doctype": "Sales Taxes and Charges",
"parentfield": "other_charges",
"charge_type": "Actual",
"account_head": shipping_rule.doc.account,
"cost_center": shipping_rule.doc.cost_center,
"description": shipping_rule.doc.label,
"rate": shipping_amount
})
self.doclist.append({
"doctype": "Sales Taxes and Charges",
"parentfield": "other_charges",
"charge_type": "Actual",
"account_head": shipping_rule.doc.account,
"cost_center": shipping_rule.doc.cost_center,
"description": shipping_rule.doc.label,
"rate": shipping_amount
})
def set_total_in_words(self):
from webnotes.utils import money_in_words

View File

@ -63,12 +63,15 @@ class DocType(DocListController):
# if list against each territory has more than one element, raise exception
territory_name = webnotes.conn.sql("""select `territory`, `parent`
from `tabFor Territory`
where `parenttype`=%s and `parent` in (%s) """ %
where `parenttype`=%s and `parent` in (%s)""" %
("%s", ", ".join(["%s"]*len(names))), tuple([parenttype] + names))
for territory, name in territory_name:
territory_name_map.setdefault(territory, []).append(name)
if len(territory_name_map[territory]) > 1:
territory_name_map[territory].sort(key=lambda val: names.index(val))
return territory_name_map
def validate_exchange_rates_exist(self):
@ -101,24 +104,26 @@ class DocType(DocListController):
territory_name_map = self.get_territory_name_map(parentfield, fieldname)
if territory_name_map.get(territory):
name = territory_name_map.get(territory)[0]
name = territory_name_map.get(territory)
else:
territory_ancestry = self.get_territory_ancestry(territory)
for ancestor in territory_ancestry:
if territory_name_map.get(ancestor):
name = territory_name_map.get(ancestor)[0]
name = territory_name_map.get(ancestor)
break
return name
def get_price_list(self, billing_territory):
return self.get_name_from_territory(billing_territory, "price_lists", "price_list")
price_list = self.get_name_from_territory(billing_territory, "price_lists", "price_list")
return price_list and price_list[0] or None
def get_tax_master(self, billing_territory):
return self.get_name_from_territory(billing_territory, "sales_taxes_and_charges_masters",
tax_master = self.get_name_from_territory(billing_territory, "sales_taxes_and_charges_masters",
"sales_taxes_and_charges_master")
return tax_master and tax_master[0] or None
def get_shipping_rule(self, shipping_territory):
def get_shipping_rules(self, shipping_territory):
return self.get_name_from_territory(shipping_territory, "shipping_rules", "shipping_rule")
def get_territory_ancestry(self, territory):

View File

@ -2,7 +2,7 @@
{
"creation": "2013-06-19 15:57:32",
"docstatus": 0,
"modified": "2013-07-03 21:00:00",
"modified": "2013-07-03 21:01:00",
"modified_by": "Administrator",
"owner": "Administrator"
},
@ -69,7 +69,7 @@
},
{
"doctype": "DocField",
"fieldname": "shopping_cart_shipping_rules",
"fieldname": "shipping_rules",
"fieldtype": "Table",
"label": "Shopping Cart Shipping Rules",
"options": "Shopping Cart Shipping Rule",

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals
import webnotes
import webnotes.defaults
from webnotes.utils import cint, get_fullname, fmt_money
from webnotes.utils import flt, get_fullname, fmt_money
class WebsitePriceListMissingError(webnotes.ValidationError): pass
@ -18,14 +18,15 @@ def get_cart_quotation(doclist=None):
return {
"doclist": decorate_quotation_doclist(doclist),
"addresses": [{"name": address.name, "display": address.display}
for address in get_address_docs(party)]
for address in get_address_docs(party)],
"shipping_rules": get_applicable_shipping_rules(party)
}
@webnotes.whitelist()
def update_cart(item_code, qty, with_doclist=0):
quotation = _get_cart_quotation()
qty = cint(qty)
qty = flt(qty)
if qty == 0:
quotation.set_doclist(quotation.doclist.get({"item_code": ["!=", item_code]}))
else:
@ -40,8 +41,7 @@ def update_cart(item_code, qty, with_doclist=0):
else:
quotation_items[0].qty = qty
quotation.ignore_permissions = True
quotation.save()
apply_cart_settings(quotation=quotation)
if with_doclist:
return get_cart_quotation(quotation.doclist)
@ -158,7 +158,13 @@ def decorate_quotation_doclist(doclist):
["website_image", "web_short_description", "page_name"], as_dict=True))
d.formatted_rate = fmt_money(d.export_rate, currency=doclist[0].currency)
d.formatted_amount = fmt_money(d.export_amount, currency=doclist[0].currency)
elif d.charge_type:
d.formatted_tax_amount = fmt_money(d.tax_amount / doclist[0].conversion_rate,
currency=doclist[0].currency)
doclist[0].formatted_grand_total_export = fmt_money(doclist[0].grand_total_export,
currency=doclist[0].currency)
return [d.fields for d in doclist]
def _get_cart_quotation(party=None):
@ -199,16 +205,13 @@ def apply_cart_settings(party=None, quotation=None):
set_price_list_and_rate(quotation, cart_settings, billing_territory)
set_taxes(quotation, cart_settings, billing_territory)
# set shipping rule based on shipping territory
shipping_territory = get_address_territory(quotation.doc.shipping_address_name) or \
party.territory
apply_shipping_rule(quotation, cart_settings, shipping_territory)
quotation.run_method("calculate_taxes_and_totals")
set_taxes(quotation, cart_settings, billing_territory)
_apply_shipping_rule(party, quotation, cart_settings)
quotation.ignore_permissions = True
quotation.save()
def set_price_list_and_rate(quotation, cart_settings, billing_territory):
@ -235,10 +238,54 @@ def set_taxes(quotation, cart_settings, billing_territory):
controller = quotation.make_controller()
controller.append_taxes_from_master("other_charges", "charge")
quotation.set_doclist(controller.doclist)
@webnotes.whitelist()
def apply_shipping_rule(shipping_rule):
quotation = _get_cart_quotation()
quotation.doc.shipping_rule = shipping_rule
apply_cart_settings(quotation=quotation)
quotation.save()
return get_cart_quotation(quotation.doclist)
def _apply_shipping_rule(party=None, quotation=None, cart_settings=None):
shipping_rules = get_shipping_rules(party, quotation, cart_settings)
if not shipping_rules:
return
elif quotation.doc.shipping_rule not in shipping_rules:
quotation.doc.shipping_rule = shipping_rules[0]
def apply_shipping_rule(quotation, cart_settings, shipping_territory):
quotation.doc.shipping_rule = cart_settings.get_shipping_rule(shipping_territory)
quotation.run_method("apply_shipping_rule")
quotation.run_method("calculate_taxes_and_totals")
def get_applicable_shipping_rules(party=None, quotation=None):
shipping_rules = get_shipping_rules(party, quotation)
if shipping_rules:
rule_label_map = webnotes.conn.get_values("Shipping Rule", shipping_rules, "label")
# we need this in sorted order as per the position of the rule in the settings page
return [[rule, rule_label_map.get(rule)] for rule in shipping_rules]
def get_shipping_rules(party=None, quotation=None, cart_settings=None):
if not party:
party = get_lead_or_customer()
if not quotation:
quotation = _get_cart_quotation()
if not cart_settings:
cart_settings = webnotes.get_obj("Shopping Cart Settings")
# set shipping rule based on shipping territory
shipping_territory = get_address_territory(quotation.doc.shipping_address_name) or \
party.territory
shipping_rules = cart_settings.get_shipping_rules(shipping_territory)
return shipping_rules
def get_address_territory(address_name):
"""Tries to match city, state and country of address to existing territory"""
@ -254,9 +301,6 @@ def get_address_territory(address_name):
return territory
def get_cart_price_list(territory_list, territory_name_map):
pass
@webnotes.whitelist()
def checkout():
quotation = _get_cart_quotation()

View File

@ -38,7 +38,6 @@ $(document).ready(function() {
} else {
wn.cart.render(r.message);
}
}
});
});
@ -81,9 +80,10 @@ $.extend(wn.cart, {
render: function(out) {
var doclist = out.doclist;
var addresses = out.addresses;
var $cart_items = $("#cart-items").empty();
var $cart_taxes = $("#cart-taxes").empty();
var $cart_totals = $("#cart-totals").empty();
var $cart_billing_address = $("#cart-billing-address").empty();
var $cart_shipping_address = $("#cart-shipping-address").empty();
@ -94,12 +94,39 @@ $.extend(wn.cart, {
return;
}
var shipping_rule_added = false;
var taxes_exist = false;
var shipping_rule_labels = $.map(out.shipping_rules, function(rule) { return rule[1]; });
$.each(doclist, function(i, doc) {
if(doc.doctype === "Quotation Item") {
wn.cart.render_item_row($cart_items, doc);
} else if (doc.doctype === "Sales Taxes and Charges") {
if(out.shipping_rules && out.shipping_rules.length &&
shipping_rule_labels.indexOf(doc.description)!==-1) {
shipping_rule_added = true;
wn.cart.render_tax_row($cart_taxes, doc, out.shipping_rules);
} else {
wn.cart.render_tax_row($cart_taxes, doc);
}
taxes_exist = true;
}
});
if(out.shipping_rules && out.shipping_rules.length && !shipping_rule_added) {
wn.cart.render_tax_row($cart_taxes, {description: "", formatted_tax_amount: ""},
out.shipping_rules);
taxes_exist = true;
}
if(taxes_exist)
$('<hr>').appendTo($cart_taxes);
wn.cart.render_tax_row($cart_totals, {
description: "<strong>Total</strong>",
formatted_tax_amount: "<strong>" + doclist[0].formatted_grand_total_export + "</strong>"
});
if(!(addresses && addresses.length)) {
$cart_shipping_address.html('<div class="well">Hey! Go ahead and add an address</div>');
} else {
@ -125,10 +152,10 @@ $.extend(wn.cart, {
</div>\
</div>\
</div>\
<div class="col col-lg-3 col-sm-3">\
<div class="col col-lg-3 col-sm-3 text-right">\
<div class="input-group item-update-cart">\
<input type="text" placeholder="Qty" value="%(qty)s" \
data-item-code="%(item_code)s">\
data-item-code="%(item_code)s" class="text-right">\
<div class="input-group-btn">\
<button class="btn btn-primary" data-item-code="%(item_code)s">\
<i class="icon-ok"></i></button>\
@ -140,6 +167,53 @@ $.extend(wn.cart, {
</div><hr>', doc)).appendTo($cart_items);
},
render_tax_row: function($cart_taxes, doc, shipping_rules) {
var shipping_selector;
if(shipping_rules) {
shipping_selector = '<select>' + $.map(shipping_rules, function(rule) {
return '<option value="' + rule[0] + '">' + rule[1] + '</option>' }).join("\n") +
'</select>';
}
var $tax_row = $(repl('<div class="row">\
<div class="col col-lg-9 col-sm-9">\
<div class="row">\
<div class="col col-lg-9 col-offset-3">' +
(shipping_selector || '<p>%(description)s</p>') +
'</div>\
</div>\
</div>\
<div class="col col-lg-3 col-sm-3 text-right">\
<p' + (shipping_selector ? ' style="margin-top: 5px;"' : "") + '>%(formatted_tax_amount)s</p>\
</div>\
</div>', doc)).appendTo($cart_taxes);
if(shipping_selector) {
$tax_row.find('select option').each(function(i, opt) {
if($(opt).html() == doc.description) {
$(opt).attr("selected", "selected");
}
});
$tax_row.find('select').on("change", function() {
wn.cart.apply_shipping_rule($(this).val(), this);
});
}
},
apply_shipping_rule: function(rule, btn) {
wn.call({
btn: btn,
type: "POST",
method: "website.helpers.cart.apply_shipping_rule",
args: { shipping_rule: rule },
callback: function(r) {
if(!r.exc) {
wn.cart.render(r.message);
}
}
});
},
render_address: function($address_wrapper, addresses, address_name) {
$.each(addresses, function(i, address) {
$(repl('<div class="accordion-group"> \

View File

@ -19,16 +19,17 @@
<div class="row">
<div class="col col-lg-9 col-sm-9">
<div class="row">
<div class="col col-lg-3"></div>
<div class="col col-lg-9"><h4>Item Details</h4></div>
<div class="col col-lg-9 col-offset-3"><h4>Item Details</h4></div>
</div>
</div>
<div class="col col-lg-3 col-sm-3"><h4>Qty</h4></div>
<div class="col col-lg-3 col-sm-3 text-right"><h4>Qty, Amount</h4></div>
</div><hr>
<div id="cart-items">
</div>
<div id="cart-taxes">
</div>
<div id="cart-totals">
</div>
<hr>
<div id="cart-addresses">
<div class="row">
@ -37,16 +38,15 @@
<div id="cart-shipping-address" class="accordion"
data-fieldname="shipping_address_name"></div>
<button class="btn btn-default" type="button" id="cart-add-shipping-address">
<span class="icon icon-plus"></span> New Address</button>
<span class="icon icon-plus"></span> New Shipping Address</button>
</div>
<div class="col col-lg-6">
<h4>Billing Address</h4>
<div id="cart-billing-address" class="accordion"
data-fieldname="customer_address"></div>
<button class="btn btn-default" type="button" id="cart-add-billing-address">
<span class="icon icon-plus"></span> New Address</button>
<span class="icon icon-plus"></span> New Billing Address</button>
</div>
</div>
<hr>
</div>