blob: d4ab64dea50e6a439e7cd44ef2184f61de25a8a2 [file] [log] [blame]
Anand Doshiabc10032013-06-14 17:44:03 +05301# Copyright (c) 2012 Web Notes Technologies Pvt Ltd.
2# License: GNU General Public License (v3). For more information see license.txt
3
4from __future__ import unicode_literals
5import webnotes
Anand Doshiabc10032013-06-14 17:44:03 +05306import webnotes.defaults
Anand Doshi0b4943c2013-07-04 23:45:22 +05307from webnotes.utils import flt, get_fullname, fmt_money
Anand Doshiabc10032013-06-14 17:44:03 +05308
Anand Doshi3dceb842013-06-19 14:57:14 +05309class WebsitePriceListMissingError(webnotes.ValidationError): pass
Anand Doshie8132122013-06-17 15:42:38 +053010
11@webnotes.whitelist()
Anand Doshiedbf3e12013-07-02 11:40:16 +053012def get_cart_quotation(doclist=None):
13 party = get_lead_or_customer()
14
15 if not doclist:
16 doclist = _get_cart_quotation(party).doclist
17
18 return {
19 "doclist": decorate_quotation_doclist(doclist),
20 "addresses": [{"name": address.name, "display": address.display}
Anand Doshi0b4943c2013-07-04 23:45:22 +053021 for address in get_address_docs(party)],
22 "shipping_rules": get_applicable_shipping_rules(party)
Anand Doshiedbf3e12013-07-02 11:40:16 +053023 }
24
25@webnotes.whitelist()
Anand Doshic2a35272013-06-19 17:19:20 +053026def update_cart(item_code, qty, with_doclist=0):
Anand Doshi3dceb842013-06-19 14:57:14 +053027 quotation = _get_cart_quotation()
Anand Doshiabc10032013-06-14 17:44:03 +053028
Anand Doshi0b4943c2013-07-04 23:45:22 +053029 qty = flt(qty)
Anand Doshi3dceb842013-06-19 14:57:14 +053030 if qty == 0:
Anand Doshie8132122013-06-17 15:42:38 +053031 quotation.set_doclist(quotation.doclist.get({"item_code": ["!=", item_code]}))
32 else:
33 quotation_items = quotation.doclist.get({"item_code": item_code})
34 if not quotation_items:
35 quotation.doclist.append({
36 "doctype": "Quotation Item",
37 "parentfield": "quotation_details",
38 "item_code": item_code,
Anand Doshi3dceb842013-06-19 14:57:14 +053039 "qty": qty
Anand Doshie8132122013-06-17 15:42:38 +053040 })
41 else:
Anand Doshi3dceb842013-06-19 14:57:14 +053042 quotation_items[0].qty = qty
Anand Doshiabc10032013-06-14 17:44:03 +053043
Anand Doshi0b4943c2013-07-04 23:45:22 +053044 apply_cart_settings(quotation=quotation)
Anand Doshi2862c9e2013-07-08 18:50:33 +053045
46 quotation.ignore_permissions = True
47 quotation.save()
Anand Doshiabc10032013-06-14 17:44:03 +053048
Anand Doshic2a35272013-06-19 17:19:20 +053049 if with_doclist:
Anand Doshiedbf3e12013-07-02 11:40:16 +053050 return get_cart_quotation(quotation.doclist)
Anand Doshic2a35272013-06-19 17:19:20 +053051 else:
52 return quotation.doc.name
Anand Doshiedbf3e12013-07-02 11:40:16 +053053
54@webnotes.whitelist()
55def update_cart_address(address_fieldname, address_name):
56 from utilities.transaction_base import get_address_display
57
58 quotation = _get_cart_quotation()
59 address_display = get_address_display(webnotes.doc("Address", address_name).fields)
60
61 if address_fieldname == "shipping_address_name":
62 quotation.doc.shipping_address_name = address_name
63 quotation.doc.shipping_address = address_display
64
65 if not quotation.doc.customer_address:
66 address_fieldname == "customer_address"
67
68 if address_fieldname == "customer_address":
69 quotation.doc.customer_address = address_name
70 quotation.doc.address_display = address_display
71
72
Anand Doshi2862c9e2013-07-08 18:50:33 +053073 apply_cart_settings(quotation=quotation)
74
Anand Doshiedbf3e12013-07-02 11:40:16 +053075 quotation.ignore_permissions = True
76 quotation.save()
77
78 return get_cart_quotation(quotation.doclist)
79
80@webnotes.whitelist()
81def get_addresses():
82 return [d.fields for d in get_address_docs()]
83
84@webnotes.whitelist()
85def save_address(fields, address_fieldname=None):
86 party = get_lead_or_customer()
87 fields = webnotes.load_json(fields)
88
89 if fields.get("name"):
90 bean = webnotes.bean("Address", fields.get("name"))
91 else:
92 bean = webnotes.bean({"doctype": "Address", "__islocal": 1})
93
94 bean.doc.fields.update(fields)
95
96 party_fieldname = party.doctype.lower()
97 bean.doc.fields.update({
98 party_fieldname: party.name,
99 (party_fieldname + "_name"): party.fields[party_fieldname + "_name"]
100 })
101 bean.ignore_permissions = True
102 bean.save()
103
104 if address_fieldname:
105 update_cart_address(address_fieldname, bean.doc.name)
106
107 return bean.doc.name
108
109def get_address_docs(party=None):
110 from webnotes.model.doclist import objectify
111 from utilities.transaction_base import get_address_display
112
113 if not party:
114 party = get_lead_or_customer()
115
116 address_docs = objectify(webnotes.conn.sql("""select * from `tabAddress`
117 where `%s`=%s order by name""" % (party.doctype.lower(), "%s"), party.name,
118 as_dict=True, update={"doctype": "Address"}))
119
120 for address in address_docs:
121 address.display = get_address_display(address.fields)
122 address.display = (address.display).replace("\n", "<br>\n")
123
124 return address_docs
Anand Doshie8132122013-06-17 15:42:38 +0530125
Anand Doshiabc10032013-06-14 17:44:03 +0530126def get_lead_or_customer():
127 customer = webnotes.conn.get_value("Contact", {"email_id": webnotes.session.user}, "customer")
128 if customer:
129 return webnotes.doc("Customer", customer)
130
131 lead = webnotes.conn.get_value("Lead", {"email_id": webnotes.session.user})
132 if lead:
133 return webnotes.doc("Lead", lead)
134 else:
135 lead_bean = webnotes.bean({
136 "doctype": "Lead",
137 "email_id": webnotes.session.user,
138 "lead_name": get_fullname(webnotes.session.user),
Anand Doshi99100a42013-07-04 17:13:53 +0530139 "territory": guess_territory(),
Anand Doshiabc10032013-06-14 17:44:03 +0530140 "status": "Open" # TODO: set something better???
141 })
Anand Doshiabc10032013-06-14 17:44:03 +0530142
Anand Doshi2862c9e2013-07-08 18:50:33 +0530143 if webnotes.session.user != "Guest":
144 lead_bean.ignore_permissions = True
145 lead_bean.insert()
146
Anand Doshiabc10032013-06-14 17:44:03 +0530147 return lead_bean.doc
Anand Doshi99100a42013-07-04 17:13:53 +0530148
149def guess_territory():
150 territory = None
151 geoip_country = webnotes.session.get("session_country")
152 if geoip_country:
153 territory = webnotes.conn.get_value("Territory", geoip_country)
154
155 return territory or \
156 webnotes.conn.get_value("Shopping Cart Settings", None, "territory") or \
157 "All Territories"
Anand Doshi3dceb842013-06-19 14:57:14 +0530158
Anand Doshic2a35272013-06-19 17:19:20 +0530159def decorate_quotation_doclist(doclist):
160 for d in doclist:
161 if d.item_code:
162 d.fields.update(webnotes.conn.get_value("Item", d.item_code,
163 ["website_image", "web_short_description", "page_name"], as_dict=True))
164 d.formatted_rate = fmt_money(d.export_rate, currency=doclist[0].currency)
165 d.formatted_amount = fmt_money(d.export_amount, currency=doclist[0].currency)
Anand Doshi0b4943c2013-07-04 23:45:22 +0530166 elif d.charge_type:
167 d.formatted_tax_amount = fmt_money(d.tax_amount / doclist[0].conversion_rate,
168 currency=doclist[0].currency)
Anand Doshic2a35272013-06-19 17:19:20 +0530169
Anand Doshi0b4943c2013-07-04 23:45:22 +0530170 doclist[0].formatted_grand_total_export = fmt_money(doclist[0].grand_total_export,
171 currency=doclist[0].currency)
172
Anand Doshic2a35272013-06-19 17:19:20 +0530173 return [d.fields for d in doclist]
Anand Doshi3dceb842013-06-19 14:57:14 +0530174
175def _get_cart_quotation(party=None):
176 if not party:
177 party = get_lead_or_customer()
178
Anand Doshiabc10032013-06-14 17:44:03 +0530179 quotation = webnotes.conn.get_value("Quotation",
180 {party.doctype.lower(): party.name, "order_type": "Shopping Cart", "docstatus": 0})
181
182 if quotation:
183 qbean = webnotes.bean("Quotation", quotation)
184 else:
185 qbean = webnotes.bean({
186 "doctype": "Quotation",
187 "naming_series": "QTN-CART-",
Anand Doshic2a35272013-06-19 17:19:20 +0530188 "quotation_to": party.doctype,
Anand Doshiabc10032013-06-14 17:44:03 +0530189 "company": webnotes.defaults.get_user_default("company"),
190 "order_type": "Shopping Cart",
191 "status": "Draft",
192 "__islocal": 1,
Anand Doshiabc10032013-06-14 17:44:03 +0530193 (party.doctype.lower()): party.name
194 })
Anand Doshi99100a42013-07-04 17:13:53 +0530195 qbean.run_method("onload_post_render")
196 apply_cart_settings(party, qbean)
Anand Doshiabc10032013-06-14 17:44:03 +0530197
198 return qbean
Anand Doshiabc10032013-06-14 17:44:03 +0530199
Anand Doshi99100a42013-07-04 17:13:53 +0530200def apply_cart_settings(party=None, quotation=None):
201 if not party:
202 party = get_lead_or_customer()
203 if not quotation:
204 quotation = _get_cart_quotation(party)
Anand Doshiabc10032013-06-14 17:44:03 +0530205
Anand Doshi99100a42013-07-04 17:13:53 +0530206 cart_settings = webnotes.get_obj("Shopping Cart Settings")
Anand Doshiabc10032013-06-14 17:44:03 +0530207
Anand Doshi99100a42013-07-04 17:13:53 +0530208 billing_territory = get_address_territory(quotation.doc.customer_address) or \
209 party.territory
Anand Doshi2862c9e2013-07-08 18:50:33 +0530210
Anand Doshi99100a42013-07-04 17:13:53 +0530211 set_price_list_and_rate(quotation, cart_settings, billing_territory)
212
Anand Doshi99100a42013-07-04 17:13:53 +0530213 quotation.run_method("calculate_taxes_and_totals")
214
Anand Doshi0b4943c2013-07-04 23:45:22 +0530215 set_taxes(quotation, cart_settings, billing_territory)
216
217 _apply_shipping_rule(party, quotation, cart_settings)
218
Anand Doshi99100a42013-07-04 17:13:53 +0530219def set_price_list_and_rate(quotation, cart_settings, billing_territory):
220 """set price list based on billing territory"""
221 quotation.doc.price_list_name = cart_settings.get_price_list(billing_territory)
222
223 # reset values
224 quotation.doc.price_list_currency = quotation.doc.currency = \
225 quotation.doc.plc_conversion_rate = quotation.doc.conversion_rate = None
226 for item in quotation.doclist.get({"parentfield": "quotation_details"}):
227 item.ref_rate = item.adj_rate = item.export_rate = item.export_amount = None
228
229 # refetch values
230 quotation.run_method("set_price_list_and_item_details")
231
Anand Doshi2862c9e2013-07-08 18:50:33 +0530232 # set it in cookies for using in product page
233 webnotes.cookies[b"price_list_name"] = quotation.doc.price_list_name
234
Anand Doshi99100a42013-07-04 17:13:53 +0530235def set_taxes(quotation, cart_settings, billing_territory):
236 """set taxes based on billing territory"""
237 quotation.doc.charge = cart_settings.get_tax_master(billing_territory)
Anand Doshiabc10032013-06-14 17:44:03 +0530238
Anand Doshi99100a42013-07-04 17:13:53 +0530239 # clear table
240 quotation.doclist = quotation.doc.clear_table(quotation.doclist, "other_charges")
Anand Doshiabc10032013-06-14 17:44:03 +0530241
Anand Doshi99100a42013-07-04 17:13:53 +0530242 # append taxes
243 controller = quotation.make_controller()
244 controller.append_taxes_from_master("other_charges", "charge")
245 quotation.set_doclist(controller.doclist)
Anand Doshi0b4943c2013-07-04 23:45:22 +0530246
247@webnotes.whitelist()
248def apply_shipping_rule(shipping_rule):
249 quotation = _get_cart_quotation()
Anand Doshi99100a42013-07-04 17:13:53 +0530250
Anand Doshi0b4943c2013-07-04 23:45:22 +0530251 quotation.doc.shipping_rule = shipping_rule
252
253 apply_cart_settings(quotation=quotation)
254
Anand Doshi2862c9e2013-07-08 18:50:33 +0530255 quotation.ignore_permissions = True
Anand Doshi0b4943c2013-07-04 23:45:22 +0530256 quotation.save()
257
258 return get_cart_quotation(quotation.doclist)
259
260def _apply_shipping_rule(party=None, quotation=None, cart_settings=None):
261 shipping_rules = get_shipping_rules(party, quotation, cart_settings)
262
263 if not shipping_rules:
264 return
265
266 elif quotation.doc.shipping_rule not in shipping_rules:
267 quotation.doc.shipping_rule = shipping_rules[0]
268
Anand Doshi99100a42013-07-04 17:13:53 +0530269 quotation.run_method("apply_shipping_rule")
Anand Doshi0b4943c2013-07-04 23:45:22 +0530270 quotation.run_method("calculate_taxes_and_totals")
271
272def get_applicable_shipping_rules(party=None, quotation=None):
273 shipping_rules = get_shipping_rules(party, quotation)
274
275 if shipping_rules:
276 rule_label_map = webnotes.conn.get_values("Shipping Rule", shipping_rules, "label")
277 # we need this in sorted order as per the position of the rule in the settings page
278 return [[rule, rule_label_map.get(rule)] for rule in shipping_rules]
279
280def get_shipping_rules(party=None, quotation=None, cart_settings=None):
281 if not party:
282 party = get_lead_or_customer()
283 if not quotation:
284 quotation = _get_cart_quotation()
285 if not cart_settings:
286 cart_settings = webnotes.get_obj("Shopping Cart Settings")
287
288 # set shipping rule based on shipping territory
289 shipping_territory = get_address_territory(quotation.doc.shipping_address_name) or \
290 party.territory
291
292 shipping_rules = cart_settings.get_shipping_rules(shipping_territory)
293
294 return shipping_rules
Anand Doshi99100a42013-07-04 17:13:53 +0530295
296def get_address_territory(address_name):
297 """Tries to match city, state and country of address to existing territory"""
298 territory = None
299
300 if address_name:
301 address_fields = webnotes.conn.get_value("Address", address_name,
302 ["city", "state", "country"])
303 for value in address_fields:
304 territory = webnotes.conn.get_value("Territory", value)
305 if territory:
306 break
307
308 return territory
309
Anand Doshie8132122013-06-17 15:42:38 +0530310@webnotes.whitelist()
311def checkout():
Anand Doshi3dceb842013-06-19 14:57:14 +0530312 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530313
314 quotation.ignore_permissions = True
315 quotation.submit()
316
Rushabh Mehtaa2a1ec72013-07-08 11:08:27 +0530317 from selling.doctype.quotation.quotation import make_sales_order
318
319 sales_order = webnotes.bean(make_sales_order(quotation.doc.name))
Anand Doshie8132122013-06-17 15:42:38 +0530320
321 sales_order.ignore_permissions = True
322 sales_order.insert()
323 sales_order.submit()
324
325 return sales_order
326
327import unittest
328test_dependencies = ["Item", "Price List", "Contact"]
Anand Doshiabc10032013-06-14 17:44:03 +0530329
330class TestCart(unittest.TestCase):
Anand Doshie8132122013-06-17 15:42:38 +0530331 def test_get_lead_or_customer(self):
332 webnotes.session.user = "test@example.com"
333 party1 = get_lead_or_customer()
334 party2 = get_lead_or_customer()
335 self.assertEquals(party1.name, party2.name)
336 self.assertEquals(party1.doctype, "Lead")
337
338 webnotes.session.user = "test_contact_customer@example.com"
339 party = get_lead_or_customer()
340 self.assertEquals(party.name, "_Test Customer")
341
Anand Doshiabc10032013-06-14 17:44:03 +0530342 def test_add_to_cart(self):
343 webnotes.session.user = "test@example.com"
Anand Doshi3dceb842013-06-19 14:57:14 +0530344 update_cart("_Test Item", 1)
Anand Doshiabc10032013-06-14 17:44:03 +0530345
Anand Doshi3dceb842013-06-19 14:57:14 +0530346 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530347 quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"})
348 self.assertTrue(quotation_items)
349 self.assertEquals(quotation_items[0].qty, 1)
350
351 return quotation
352
Anand Doshi3dceb842013-06-19 14:57:14 +0530353 def test_update_cart(self):
Anand Doshie8132122013-06-17 15:42:38 +0530354 self.test_add_to_cart()
355
Anand Doshi3dceb842013-06-19 14:57:14 +0530356 update_cart("_Test Item", 5)
Anand Doshie8132122013-06-17 15:42:38 +0530357
Anand Doshi3dceb842013-06-19 14:57:14 +0530358 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530359 quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"})
360 self.assertTrue(quotation_items)
361 self.assertEquals(quotation_items[0].qty, 5)
362
363 return quotation
Anand Doshiabc10032013-06-14 17:44:03 +0530364
365 def test_remove_from_cart(self):
Anand Doshie8132122013-06-17 15:42:38 +0530366 quotation0 = self.test_add_to_cart()
367
Anand Doshi3dceb842013-06-19 14:57:14 +0530368 update_cart("_Test Item", 0)
Anand Doshie8132122013-06-17 15:42:38 +0530369
Anand Doshi3dceb842013-06-19 14:57:14 +0530370 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530371 self.assertEquals(quotation0.doc.name, quotation.doc.name)
372
373 quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"})
374 self.assertEquals(quotation_items, [])
Anand Doshiabc10032013-06-14 17:44:03 +0530375
376 def test_checkout(self):
Anand Doshi3dceb842013-06-19 14:57:14 +0530377 quotation = self.test_update_cart()
Anand Doshie8132122013-06-17 15:42:38 +0530378 sales_order = checkout()
379 self.assertEquals(sales_order.doclist.getone({"item_code": "_Test Item"}).prevdoc_docname, quotation.doc.name)
Anand Doshiabc10032013-06-14 17:44:03 +0530380