462 lines
14 KiB
Python
462 lines
14 KiB
Python
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
|
|
# License: GNU General Public License v3. See license.txt
|
|
|
|
from __future__ import unicode_literals
|
|
import webnotes
|
|
from webnotes import msgprint, _
|
|
import webnotes.defaults
|
|
from webnotes.utils import flt, get_fullname, fmt_money, cstr
|
|
|
|
class WebsitePriceListMissingError(webnotes.ValidationError): pass
|
|
|
|
def set_cart_count(quotation=None):
|
|
if not quotation:
|
|
quotation = _get_cart_quotation()
|
|
webnotes.add_cookies["cart_count"] = cstr(len(quotation.doclist.get(
|
|
{"parentfield": "quotation_details"})) or "")
|
|
|
|
@webnotes.whitelist()
|
|
def get_cart_quotation(doclist=None):
|
|
party = get_lead_or_customer()
|
|
|
|
if not doclist:
|
|
quotation = _get_cart_quotation(party)
|
|
doclist = quotation.doclist
|
|
set_cart_count(quotation)
|
|
|
|
return {
|
|
"doclist": decorate_quotation_doclist(doclist),
|
|
"addresses": [{"name": address.name, "display": address.display}
|
|
for address in get_address_docs(party)],
|
|
"shipping_rules": get_applicable_shipping_rules(party)
|
|
}
|
|
|
|
@webnotes.whitelist()
|
|
def place_order():
|
|
quotation = _get_cart_quotation()
|
|
controller = quotation.make_controller()
|
|
for fieldname in ["customer_address", "shipping_address_name"]:
|
|
if not quotation.doc.fields.get(fieldname):
|
|
msgprint(_("Please select a") + " " + _(controller.meta.get_label(fieldname)), raise_exception=True)
|
|
|
|
quotation.ignore_permissions = True
|
|
quotation.submit()
|
|
|
|
from selling.doctype.quotation.quotation import _make_sales_order
|
|
sales_order = webnotes.bean(_make_sales_order(quotation.doc.name, ignore_permissions=True))
|
|
sales_order.ignore_permissions = True
|
|
sales_order.insert()
|
|
sales_order.submit()
|
|
webnotes.add_cookies["cart_count"] = ""
|
|
|
|
return sales_order.doc.name
|
|
|
|
@webnotes.whitelist()
|
|
def update_cart(item_code, qty, with_doclist=0):
|
|
quotation = _get_cart_quotation()
|
|
|
|
qty = flt(qty)
|
|
if qty == 0:
|
|
quotation.set_doclist(quotation.doclist.get({"item_code": ["!=", item_code]}))
|
|
if not quotation.doclist.get({"parentfield": "quotation_details"}) and \
|
|
not quotation.doc.fields.get("__islocal"):
|
|
quotation.__delete = True
|
|
|
|
else:
|
|
quotation_items = quotation.doclist.get({"item_code": item_code})
|
|
if not quotation_items:
|
|
quotation.doclist.append({
|
|
"doctype": "Quotation Item",
|
|
"parentfield": "quotation_details",
|
|
"item_code": item_code,
|
|
"qty": qty
|
|
})
|
|
else:
|
|
quotation_items[0].qty = qty
|
|
|
|
apply_cart_settings(quotation=quotation)
|
|
|
|
if hasattr(quotation, "__delete"):
|
|
webnotes.delete_doc("Quotation", quotation.doc.name, ignore_permissions=True)
|
|
quotation = _get_cart_quotation()
|
|
else:
|
|
quotation.ignore_permissions = True
|
|
quotation.save()
|
|
|
|
set_cart_count(quotation)
|
|
|
|
if with_doclist:
|
|
return get_cart_quotation(quotation.doclist)
|
|
else:
|
|
return quotation.doc.name
|
|
|
|
@webnotes.whitelist()
|
|
def update_cart_address(address_fieldname, address_name):
|
|
from utilities.transaction_base import get_address_display
|
|
|
|
quotation = _get_cart_quotation()
|
|
address_display = get_address_display(webnotes.doc("Address", address_name).fields)
|
|
|
|
if address_fieldname == "shipping_address_name":
|
|
quotation.doc.shipping_address_name = address_name
|
|
quotation.doc.shipping_address = address_display
|
|
|
|
if not quotation.doc.customer_address:
|
|
address_fieldname == "customer_address"
|
|
|
|
if address_fieldname == "customer_address":
|
|
quotation.doc.customer_address = address_name
|
|
quotation.doc.address_display = address_display
|
|
|
|
|
|
apply_cart_settings(quotation=quotation)
|
|
|
|
quotation.ignore_permissions = True
|
|
quotation.save()
|
|
|
|
return get_cart_quotation(quotation.doclist)
|
|
|
|
@webnotes.whitelist()
|
|
def get_addresses():
|
|
return [d.fields for d in get_address_docs()]
|
|
|
|
@webnotes.whitelist()
|
|
def save_address(fields, address_fieldname=None):
|
|
party = get_lead_or_customer()
|
|
fields = webnotes.load_json(fields)
|
|
|
|
if fields.get("name"):
|
|
bean = webnotes.bean("Address", fields.get("name"))
|
|
else:
|
|
bean = webnotes.bean({"doctype": "Address", "__islocal": 1})
|
|
|
|
bean.doc.fields.update(fields)
|
|
|
|
party_fieldname = party.doctype.lower()
|
|
bean.doc.fields.update({
|
|
party_fieldname: party.name,
|
|
(party_fieldname + "_name"): party.fields[party_fieldname + "_name"]
|
|
})
|
|
bean.ignore_permissions = True
|
|
bean.save()
|
|
|
|
if address_fieldname:
|
|
update_cart_address(address_fieldname, bean.doc.name)
|
|
|
|
return bean.doc.name
|
|
|
|
def get_address_docs(party=None):
|
|
from webnotes.model.doclist import objectify
|
|
from utilities.transaction_base import get_address_display
|
|
|
|
if not party:
|
|
party = get_lead_or_customer()
|
|
|
|
address_docs = objectify(webnotes.conn.sql("""select * from `tabAddress`
|
|
where `%s`=%s order by name""" % (party.doctype.lower(), "%s"), party.name,
|
|
as_dict=True, update={"doctype": "Address"}))
|
|
|
|
for address in address_docs:
|
|
address.display = get_address_display(address.fields)
|
|
address.display = (address.display).replace("\n", "<br>\n")
|
|
|
|
return address_docs
|
|
|
|
def get_lead_or_customer():
|
|
customer = webnotes.conn.get_value("Contact", {"email_id": webnotes.session.user}, "customer")
|
|
if customer:
|
|
return webnotes.doc("Customer", customer)
|
|
|
|
lead = webnotes.conn.get_value("Lead", {"email_id": webnotes.session.user})
|
|
if lead:
|
|
return webnotes.doc("Lead", lead)
|
|
else:
|
|
lead_bean = webnotes.bean({
|
|
"doctype": "Lead",
|
|
"email_id": webnotes.session.user,
|
|
"lead_name": get_fullname(webnotes.session.user),
|
|
"territory": guess_territory(),
|
|
"status": "Open" # TODO: set something better???
|
|
})
|
|
|
|
if webnotes.session.user != "Guest":
|
|
lead_bean.ignore_permissions = True
|
|
lead_bean.insert()
|
|
|
|
return lead_bean.doc
|
|
|
|
def guess_territory():
|
|
territory = None
|
|
geoip_country = webnotes.session.get("session_country")
|
|
if geoip_country:
|
|
territory = webnotes.conn.get_value("Territory", geoip_country)
|
|
|
|
return territory or \
|
|
webnotes.conn.get_value("Shopping Cart Settings", None, "territory") or \
|
|
"All Territories"
|
|
|
|
def decorate_quotation_doclist(doclist):
|
|
for d in doclist:
|
|
if d.item_code:
|
|
d.fields.update(webnotes.conn.get_value("Item", d.item_code,
|
|
["website_image", "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):
|
|
if not party:
|
|
party = get_lead_or_customer()
|
|
|
|
quotation = webnotes.conn.get_value("Quotation",
|
|
{party.doctype.lower(): party.name, "order_type": "Shopping Cart", "docstatus": 0})
|
|
|
|
if quotation:
|
|
qbean = webnotes.bean("Quotation", quotation)
|
|
else:
|
|
qbean = webnotes.bean({
|
|
"doctype": "Quotation",
|
|
"naming_series": webnotes.defaults.get_user_default("shopping_cart_quotation_series") or "QTN-CART-",
|
|
"quotation_to": party.doctype,
|
|
"company": webnotes.defaults.get_user_default("company"),
|
|
"order_type": "Shopping Cart",
|
|
"status": "Draft",
|
|
"docstatus": 0,
|
|
"__islocal": 1,
|
|
(party.doctype.lower()): party.name
|
|
})
|
|
|
|
if party.doctype == "Customer":
|
|
qbean.doc.contact_person = webnotes.conn.get_value("Contact", {"email_id": webnotes.session.user,
|
|
"customer": party.name})
|
|
qbean.run_method("set_contact_fields")
|
|
|
|
qbean.run_method("onload_post_render")
|
|
apply_cart_settings(party, qbean)
|
|
|
|
return qbean
|
|
|
|
def update_party(fullname, company_name=None, mobile_no=None, phone=None):
|
|
party = get_lead_or_customer()
|
|
|
|
if party.doctype == "Lead":
|
|
party.company_name = company_name
|
|
party.lead_name = fullname
|
|
party.mobile_no = mobile_no
|
|
party.phone = phone
|
|
else:
|
|
party.customer_name = company_name or fullname
|
|
party.customer_type == "Company" if company_name else "Individual"
|
|
|
|
contact_name = webnotes.conn.get_value("Contact", {"email_id": webnotes.session.user,
|
|
"customer": party.name})
|
|
contact = webnotes.bean("Contact", contact_name)
|
|
contact.doc.first_name = fullname
|
|
contact.doc.last_name = None
|
|
contact.doc.customer_name = party.customer_name
|
|
contact.doc.mobile_no = mobile_no
|
|
contact.doc.phone = phone
|
|
contact.ignore_permissions = True
|
|
contact.save()
|
|
|
|
party_bean = webnotes.bean(party.fields)
|
|
party_bean.ignore_permissions = True
|
|
party_bean.save()
|
|
|
|
qbean = _get_cart_quotation(party)
|
|
if not qbean.doc.fields.get("__islocal"):
|
|
qbean.doc.customer_name = company_name or fullname
|
|
qbean.run_method("set_contact_fields")
|
|
qbean.ignore_permissions = True
|
|
qbean.save()
|
|
|
|
def apply_cart_settings(party=None, quotation=None):
|
|
if not party:
|
|
party = get_lead_or_customer()
|
|
if not quotation:
|
|
quotation = _get_cart_quotation(party)
|
|
|
|
cart_settings = webnotes.get_obj("Shopping Cart Settings")
|
|
|
|
billing_territory = get_address_territory(quotation.doc.customer_address) or \
|
|
party.territory
|
|
|
|
set_price_list_and_rate(quotation, cart_settings, billing_territory)
|
|
|
|
quotation.run_method("calculate_taxes_and_totals")
|
|
|
|
set_taxes(quotation, cart_settings, billing_territory)
|
|
|
|
_apply_shipping_rule(party, quotation, cart_settings)
|
|
|
|
def set_price_list_and_rate(quotation, cart_settings, billing_territory):
|
|
"""set price list based on billing territory"""
|
|
quotation.doc.selling_price_list = cart_settings.get_price_list(billing_territory)
|
|
|
|
# reset values
|
|
quotation.doc.price_list_currency = quotation.doc.currency = \
|
|
quotation.doc.plc_conversion_rate = quotation.doc.conversion_rate = None
|
|
for item in quotation.doclist.get({"parentfield": "quotation_details"}):
|
|
item.ref_rate = item.adj_rate = item.export_rate = item.export_amount = None
|
|
|
|
# refetch values
|
|
quotation.run_method("set_price_list_and_item_details")
|
|
|
|
# set it in cookies for using in product page
|
|
webnotes.cookies[b"selling_price_list"] = quotation.doc.selling_price_list
|
|
|
|
def set_taxes(quotation, cart_settings, billing_territory):
|
|
"""set taxes based on billing territory"""
|
|
quotation.doc.charge = cart_settings.get_tax_master(billing_territory)
|
|
|
|
# clear table
|
|
quotation.set_doclist(quotation.doclist.get({"parentfield": ["!=", "other_charges"]}))
|
|
|
|
# append taxes
|
|
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.ignore_permissions = True
|
|
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]
|
|
|
|
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"""
|
|
territory = None
|
|
|
|
if address_name:
|
|
address_fields = webnotes.conn.get_value("Address", address_name,
|
|
["city", "state", "country"])
|
|
for value in address_fields:
|
|
territory = webnotes.conn.get_value("Territory", value)
|
|
if territory:
|
|
break
|
|
|
|
return territory
|
|
|
|
import unittest
|
|
test_dependencies = ["Item", "Price List", "Contact", "Shopping Cart Settings"]
|
|
|
|
class TestCart(unittest.TestCase):
|
|
def tearDown(self):
|
|
return
|
|
|
|
cart_settings = webnotes.bean("Shopping Cart Settings")
|
|
cart_settings.ignore_permissions = True
|
|
cart_settings.doc.enabled = 0
|
|
cart_settings.save()
|
|
|
|
def enable_shopping_cart(self):
|
|
return
|
|
if not webnotes.conn.get_value("Shopping Cart Settings", None, "enabled"):
|
|
cart_settings = webnotes.bean("Shopping Cart Settings")
|
|
cart_settings.ignore_permissions = True
|
|
cart_settings.doc.enabled = 1
|
|
cart_settings.save()
|
|
|
|
def test_get_lead_or_customer(self):
|
|
webnotes.session.user = "test@example.com"
|
|
party1 = get_lead_or_customer()
|
|
party2 = get_lead_or_customer()
|
|
self.assertEquals(party1.name, party2.name)
|
|
self.assertEquals(party1.doctype, "Lead")
|
|
|
|
webnotes.session.user = "test_contact_customer@example.com"
|
|
party = get_lead_or_customer()
|
|
self.assertEquals(party.name, "_Test Customer")
|
|
|
|
def test_add_to_cart(self):
|
|
self.enable_shopping_cart()
|
|
webnotes.session.user = "test@example.com"
|
|
|
|
update_cart("_Test Item", 1)
|
|
|
|
quotation = _get_cart_quotation()
|
|
quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"})
|
|
self.assertTrue(quotation_items)
|
|
self.assertEquals(quotation_items[0].qty, 1)
|
|
|
|
return quotation
|
|
|
|
def test_update_cart(self):
|
|
self.test_add_to_cart()
|
|
|
|
update_cart("_Test Item", 5)
|
|
|
|
quotation = _get_cart_quotation()
|
|
quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"})
|
|
self.assertTrue(quotation_items)
|
|
self.assertEquals(quotation_items[0].qty, 5)
|
|
|
|
return quotation
|
|
|
|
def test_remove_from_cart(self):
|
|
quotation0 = self.test_add_to_cart()
|
|
|
|
update_cart("_Test Item", 0)
|
|
|
|
quotation = _get_cart_quotation()
|
|
self.assertEquals(quotation0.doc.name, quotation.doc.name)
|
|
|
|
quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"})
|
|
self.assertEquals(quotation_items, [])
|
|
|
|
def test_place_order(self):
|
|
quotation = self.test_update_cart()
|
|
sales_order_name = place_order()
|
|
sales_order = webnotes.bean("Sales Order", sales_order_name)
|
|
self.assertEquals(sales_order.doclist.getone({"item_code": "_Test Item"}).prevdoc_docname, quotation.doc.name)
|
|
|