[webshop] shopping cart with shipping rule selector, totals
This commit is contained in:
parent
a749996747
commit
0b4943cec1
@ -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 = []
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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",
|
||||
|
@ -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()
|
||||
|
@ -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"> \
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user