blob: ea72e40ff0231580d2cb4271e7c9211c40b82b26 [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 Doshi2ac0a832013-07-10 20:49:44 +05306from webnotes import msgprint, _
Anand Doshiabc10032013-06-14 17:44:03 +05307import webnotes.defaults
Anand Doshi2ac0a832013-07-10 20:49:44 +05308from webnotes.utils import flt, get_fullname, fmt_money, cstr
Anand Doshiabc10032013-06-14 17:44:03 +05309
Anand Doshi3dceb842013-06-19 14:57:14 +053010class WebsitePriceListMissingError(webnotes.ValidationError): pass
Anand Doshie8132122013-06-17 15:42:38 +053011
Anand Doshi2ac0a832013-07-10 20:49:44 +053012def set_cart_count(quotation=None):
13 if not quotation:
14 quotation = _get_cart_quotation()
15 webnotes.add_cookies["cart_count"] = cstr(len(quotation.doclist.get(
16 {"parentfield": "quotation_details"})) or "")
17
Anand Doshie8132122013-06-17 15:42:38 +053018@webnotes.whitelist()
Anand Doshiedbf3e12013-07-02 11:40:16 +053019def get_cart_quotation(doclist=None):
20 party = get_lead_or_customer()
21
22 if not doclist:
Anand Doshi2ac0a832013-07-10 20:49:44 +053023 quotation = _get_cart_quotation(party)
24 doclist = quotation.doclist
25 set_cart_count(quotation)
Anand Doshiedbf3e12013-07-02 11:40:16 +053026
27 return {
28 "doclist": decorate_quotation_doclist(doclist),
29 "addresses": [{"name": address.name, "display": address.display}
Anand Doshi0b4943c2013-07-04 23:45:22 +053030 for address in get_address_docs(party)],
31 "shipping_rules": get_applicable_shipping_rules(party)
Anand Doshiedbf3e12013-07-02 11:40:16 +053032 }
Anand Doshi2ac0a832013-07-10 20:49:44 +053033
34@webnotes.whitelist()
35def place_order():
36 quotation = _get_cart_quotation()
37 controller = quotation.make_controller()
38 for fieldname in ["customer_address", "shipping_address_name"]:
39 if not quotation.doc.fields.get(fieldname):
40 msgprint(_("Please select a") + " " + _(controller.meta.get_label(fieldname)), raise_exception=True)
41
42 quotation.ignore_permissions = True
43 quotation.submit()
44
45 from selling.doctype.quotation.quotation import _make_sales_order
46 sales_order = webnotes.bean(_make_sales_order(quotation.doc.name, ignore_permissions=True))
47 sales_order.ignore_permissions = True
48 sales_order.insert()
49 sales_order.submit()
50 webnotes.add_cookies["cart_count"] = ""
51
52 return sales_order.doc.name
Anand Doshiedbf3e12013-07-02 11:40:16 +053053
54@webnotes.whitelist()
Anand Doshic2a35272013-06-19 17:19:20 +053055def update_cart(item_code, qty, with_doclist=0):
Anand Doshi3dceb842013-06-19 14:57:14 +053056 quotation = _get_cart_quotation()
Anand Doshiabc10032013-06-14 17:44:03 +053057
Anand Doshi0b4943c2013-07-04 23:45:22 +053058 qty = flt(qty)
Anand Doshi3dceb842013-06-19 14:57:14 +053059 if qty == 0:
Anand Doshie8132122013-06-17 15:42:38 +053060 quotation.set_doclist(quotation.doclist.get({"item_code": ["!=", item_code]}))
61 else:
62 quotation_items = quotation.doclist.get({"item_code": item_code})
63 if not quotation_items:
64 quotation.doclist.append({
65 "doctype": "Quotation Item",
66 "parentfield": "quotation_details",
67 "item_code": item_code,
Anand Doshi3dceb842013-06-19 14:57:14 +053068 "qty": qty
Anand Doshie8132122013-06-17 15:42:38 +053069 })
70 else:
Anand Doshi3dceb842013-06-19 14:57:14 +053071 quotation_items[0].qty = qty
Anand Doshiabc10032013-06-14 17:44:03 +053072
Anand Doshi0b4943c2013-07-04 23:45:22 +053073 apply_cart_settings(quotation=quotation)
Anand Doshi2862c9e2013-07-08 18:50:33 +053074
75 quotation.ignore_permissions = True
76 quotation.save()
Anand Doshiabc10032013-06-14 17:44:03 +053077
Anand Doshi2ac0a832013-07-10 20:49:44 +053078 set_cart_count(quotation)
79
Anand Doshic2a35272013-06-19 17:19:20 +053080 if with_doclist:
Anand Doshiedbf3e12013-07-02 11:40:16 +053081 return get_cart_quotation(quotation.doclist)
Anand Doshic2a35272013-06-19 17:19:20 +053082 else:
83 return quotation.doc.name
Anand Doshiedbf3e12013-07-02 11:40:16 +053084
85@webnotes.whitelist()
86def update_cart_address(address_fieldname, address_name):
87 from utilities.transaction_base import get_address_display
88
89 quotation = _get_cart_quotation()
90 address_display = get_address_display(webnotes.doc("Address", address_name).fields)
91
92 if address_fieldname == "shipping_address_name":
93 quotation.doc.shipping_address_name = address_name
94 quotation.doc.shipping_address = address_display
95
96 if not quotation.doc.customer_address:
97 address_fieldname == "customer_address"
98
99 if address_fieldname == "customer_address":
100 quotation.doc.customer_address = address_name
101 quotation.doc.address_display = address_display
102
103
Anand Doshi2862c9e2013-07-08 18:50:33 +0530104 apply_cart_settings(quotation=quotation)
105
Anand Doshiedbf3e12013-07-02 11:40:16 +0530106 quotation.ignore_permissions = True
107 quotation.save()
108
109 return get_cart_quotation(quotation.doclist)
110
111@webnotes.whitelist()
112def get_addresses():
113 return [d.fields for d in get_address_docs()]
114
115@webnotes.whitelist()
116def save_address(fields, address_fieldname=None):
117 party = get_lead_or_customer()
118 fields = webnotes.load_json(fields)
119
120 if fields.get("name"):
121 bean = webnotes.bean("Address", fields.get("name"))
122 else:
123 bean = webnotes.bean({"doctype": "Address", "__islocal": 1})
124
125 bean.doc.fields.update(fields)
126
127 party_fieldname = party.doctype.lower()
128 bean.doc.fields.update({
129 party_fieldname: party.name,
130 (party_fieldname + "_name"): party.fields[party_fieldname + "_name"]
131 })
132 bean.ignore_permissions = True
133 bean.save()
134
135 if address_fieldname:
136 update_cart_address(address_fieldname, bean.doc.name)
137
138 return bean.doc.name
139
140def get_address_docs(party=None):
141 from webnotes.model.doclist import objectify
142 from utilities.transaction_base import get_address_display
143
144 if not party:
145 party = get_lead_or_customer()
146
147 address_docs = objectify(webnotes.conn.sql("""select * from `tabAddress`
148 where `%s`=%s order by name""" % (party.doctype.lower(), "%s"), party.name,
149 as_dict=True, update={"doctype": "Address"}))
150
151 for address in address_docs:
152 address.display = get_address_display(address.fields)
153 address.display = (address.display).replace("\n", "<br>\n")
154
155 return address_docs
Anand Doshie8132122013-06-17 15:42:38 +0530156
Anand Doshiabc10032013-06-14 17:44:03 +0530157def get_lead_or_customer():
158 customer = webnotes.conn.get_value("Contact", {"email_id": webnotes.session.user}, "customer")
159 if customer:
160 return webnotes.doc("Customer", customer)
161
162 lead = webnotes.conn.get_value("Lead", {"email_id": webnotes.session.user})
163 if lead:
164 return webnotes.doc("Lead", lead)
165 else:
166 lead_bean = webnotes.bean({
167 "doctype": "Lead",
168 "email_id": webnotes.session.user,
169 "lead_name": get_fullname(webnotes.session.user),
Anand Doshi99100a42013-07-04 17:13:53 +0530170 "territory": guess_territory(),
Anand Doshiabc10032013-06-14 17:44:03 +0530171 "status": "Open" # TODO: set something better???
172 })
Anand Doshiabc10032013-06-14 17:44:03 +0530173
Anand Doshi2862c9e2013-07-08 18:50:33 +0530174 if webnotes.session.user != "Guest":
175 lead_bean.ignore_permissions = True
176 lead_bean.insert()
177
Anand Doshiabc10032013-06-14 17:44:03 +0530178 return lead_bean.doc
Anand Doshi99100a42013-07-04 17:13:53 +0530179
180def guess_territory():
181 territory = None
182 geoip_country = webnotes.session.get("session_country")
183 if geoip_country:
184 territory = webnotes.conn.get_value("Territory", geoip_country)
185
186 return territory or \
187 webnotes.conn.get_value("Shopping Cart Settings", None, "territory") or \
188 "All Territories"
Anand Doshi3dceb842013-06-19 14:57:14 +0530189
Anand Doshic2a35272013-06-19 17:19:20 +0530190def decorate_quotation_doclist(doclist):
191 for d in doclist:
192 if d.item_code:
193 d.fields.update(webnotes.conn.get_value("Item", d.item_code,
194 ["website_image", "web_short_description", "page_name"], as_dict=True))
195 d.formatted_rate = fmt_money(d.export_rate, currency=doclist[0].currency)
196 d.formatted_amount = fmt_money(d.export_amount, currency=doclist[0].currency)
Anand Doshi0b4943c2013-07-04 23:45:22 +0530197 elif d.charge_type:
198 d.formatted_tax_amount = fmt_money(d.tax_amount / doclist[0].conversion_rate,
199 currency=doclist[0].currency)
Anand Doshic2a35272013-06-19 17:19:20 +0530200
Anand Doshi0b4943c2013-07-04 23:45:22 +0530201 doclist[0].formatted_grand_total_export = fmt_money(doclist[0].grand_total_export,
202 currency=doclist[0].currency)
203
Anand Doshic2a35272013-06-19 17:19:20 +0530204 return [d.fields for d in doclist]
Anand Doshi3dceb842013-06-19 14:57:14 +0530205
206def _get_cart_quotation(party=None):
207 if not party:
208 party = get_lead_or_customer()
209
Anand Doshiabc10032013-06-14 17:44:03 +0530210 quotation = webnotes.conn.get_value("Quotation",
211 {party.doctype.lower(): party.name, "order_type": "Shopping Cart", "docstatus": 0})
212
213 if quotation:
214 qbean = webnotes.bean("Quotation", quotation)
215 else:
216 qbean = webnotes.bean({
217 "doctype": "Quotation",
218 "naming_series": "QTN-CART-",
Anand Doshic2a35272013-06-19 17:19:20 +0530219 "quotation_to": party.doctype,
Anand Doshiabc10032013-06-14 17:44:03 +0530220 "company": webnotes.defaults.get_user_default("company"),
221 "order_type": "Shopping Cart",
222 "status": "Draft",
223 "__islocal": 1,
Anand Doshiabc10032013-06-14 17:44:03 +0530224 (party.doctype.lower()): party.name
225 })
Anand Doshi2ac0a832013-07-10 20:49:44 +0530226
227 # map_contact_fields(qbean, party)
228
Anand Doshi99100a42013-07-04 17:13:53 +0530229 qbean.run_method("onload_post_render")
230 apply_cart_settings(party, qbean)
Anand Doshiabc10032013-06-14 17:44:03 +0530231
232 return qbean
Anand Doshi2ac0a832013-07-10 20:49:44 +0530233
234def update_party(fullname, company_name=None, mobile_no=None, phone=None):
235 party = get_lead_or_customer()
236
237 if party.doctype == "Lead":
238 party.company_name = company_name
239 party.lead_name = fullname
240 party.mobile_no = mobile_no
241 party.phone = phone
242 else:
243 party.customer_name = company_name or fullname
244 party.customer_type == "Company" if company_name else "Individual"
245
246 contact_name = webnotes.conn.get_value("Contact", {"email_id": webnotes.session.user,
247 "customer": party.name})
248 contact = webnotes.bean("Contact", contact_name)
249 contact.doc.first_name = fullname
250 contact.doc.last_name = None
251 contact.doc.customer_name = party.customer_name
252 contact.doc.mobile_no = mobile_no
253 contact.doc.phone = phone
254 contact.ignore_permissions = True
255 contact.save()
Anand Doshiabc10032013-06-14 17:44:03 +0530256
Anand Doshi2ac0a832013-07-10 20:49:44 +0530257 party_bean = webnotes.bean(party.fields)
258 party_bean.ignore_permissions = True
259 party_bean.save()
260
261 qbean = _get_cart_quotation(party)
262 qbean.doc.customer_name = company_name or fullname
263 qbean.run_method("set_contact_fields")
264 qbean.ignore_permissions = True
265 qbean.save()
266
Anand Doshi99100a42013-07-04 17:13:53 +0530267def apply_cart_settings(party=None, quotation=None):
268 if not party:
269 party = get_lead_or_customer()
270 if not quotation:
271 quotation = _get_cart_quotation(party)
Anand Doshiabc10032013-06-14 17:44:03 +0530272
Anand Doshi99100a42013-07-04 17:13:53 +0530273 cart_settings = webnotes.get_obj("Shopping Cart Settings")
Anand Doshiabc10032013-06-14 17:44:03 +0530274
Anand Doshi99100a42013-07-04 17:13:53 +0530275 billing_territory = get_address_territory(quotation.doc.customer_address) or \
276 party.territory
Anand Doshi2862c9e2013-07-08 18:50:33 +0530277
Anand Doshi99100a42013-07-04 17:13:53 +0530278 set_price_list_and_rate(quotation, cart_settings, billing_territory)
279
Anand Doshi99100a42013-07-04 17:13:53 +0530280 quotation.run_method("calculate_taxes_and_totals")
281
Anand Doshi0b4943c2013-07-04 23:45:22 +0530282 set_taxes(quotation, cart_settings, billing_territory)
283
284 _apply_shipping_rule(party, quotation, cart_settings)
285
Anand Doshi99100a42013-07-04 17:13:53 +0530286def set_price_list_and_rate(quotation, cart_settings, billing_territory):
287 """set price list based on billing territory"""
288 quotation.doc.price_list_name = cart_settings.get_price_list(billing_territory)
289
290 # reset values
291 quotation.doc.price_list_currency = quotation.doc.currency = \
292 quotation.doc.plc_conversion_rate = quotation.doc.conversion_rate = None
293 for item in quotation.doclist.get({"parentfield": "quotation_details"}):
294 item.ref_rate = item.adj_rate = item.export_rate = item.export_amount = None
295
296 # refetch values
297 quotation.run_method("set_price_list_and_item_details")
298
Anand Doshi2862c9e2013-07-08 18:50:33 +0530299 # set it in cookies for using in product page
300 webnotes.cookies[b"price_list_name"] = quotation.doc.price_list_name
301
Anand Doshi99100a42013-07-04 17:13:53 +0530302def set_taxes(quotation, cart_settings, billing_territory):
303 """set taxes based on billing territory"""
304 quotation.doc.charge = cart_settings.get_tax_master(billing_territory)
Anand Doshiabc10032013-06-14 17:44:03 +0530305
Anand Doshi99100a42013-07-04 17:13:53 +0530306 # clear table
Anand Doshi2ac0a832013-07-10 20:49:44 +0530307 quotation.set_doclist(quotation.doclist.get({"parentfield": ["!=", "other_charges"]}))
308
Anand Doshi99100a42013-07-04 17:13:53 +0530309 # append taxes
310 controller = quotation.make_controller()
311 controller.append_taxes_from_master("other_charges", "charge")
312 quotation.set_doclist(controller.doclist)
Anand Doshi0b4943c2013-07-04 23:45:22 +0530313
314@webnotes.whitelist()
315def apply_shipping_rule(shipping_rule):
316 quotation = _get_cart_quotation()
Anand Doshi99100a42013-07-04 17:13:53 +0530317
Anand Doshi0b4943c2013-07-04 23:45:22 +0530318 quotation.doc.shipping_rule = shipping_rule
319
320 apply_cart_settings(quotation=quotation)
321
Anand Doshi2862c9e2013-07-08 18:50:33 +0530322 quotation.ignore_permissions = True
Anand Doshi0b4943c2013-07-04 23:45:22 +0530323 quotation.save()
324
325 return get_cart_quotation(quotation.doclist)
326
327def _apply_shipping_rule(party=None, quotation=None, cart_settings=None):
328 shipping_rules = get_shipping_rules(party, quotation, cart_settings)
329
330 if not shipping_rules:
331 return
332
333 elif quotation.doc.shipping_rule not in shipping_rules:
334 quotation.doc.shipping_rule = shipping_rules[0]
335
Anand Doshi99100a42013-07-04 17:13:53 +0530336 quotation.run_method("apply_shipping_rule")
Anand Doshi0b4943c2013-07-04 23:45:22 +0530337 quotation.run_method("calculate_taxes_and_totals")
338
339def get_applicable_shipping_rules(party=None, quotation=None):
340 shipping_rules = get_shipping_rules(party, quotation)
341
342 if shipping_rules:
343 rule_label_map = webnotes.conn.get_values("Shipping Rule", shipping_rules, "label")
344 # we need this in sorted order as per the position of the rule in the settings page
345 return [[rule, rule_label_map.get(rule)] for rule in shipping_rules]
346
347def get_shipping_rules(party=None, quotation=None, cart_settings=None):
348 if not party:
349 party = get_lead_or_customer()
350 if not quotation:
351 quotation = _get_cart_quotation()
352 if not cart_settings:
353 cart_settings = webnotes.get_obj("Shopping Cart Settings")
354
355 # set shipping rule based on shipping territory
356 shipping_territory = get_address_territory(quotation.doc.shipping_address_name) or \
357 party.territory
358
359 shipping_rules = cart_settings.get_shipping_rules(shipping_territory)
360
361 return shipping_rules
Anand Doshi99100a42013-07-04 17:13:53 +0530362
363def get_address_territory(address_name):
364 """Tries to match city, state and country of address to existing territory"""
365 territory = None
366
367 if address_name:
368 address_fields = webnotes.conn.get_value("Address", address_name,
369 ["city", "state", "country"])
370 for value in address_fields:
371 territory = webnotes.conn.get_value("Territory", value)
372 if territory:
373 break
374
375 return territory
376
Anand Doshie8132122013-06-17 15:42:38 +0530377@webnotes.whitelist()
378def checkout():
Anand Doshi3dceb842013-06-19 14:57:14 +0530379 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530380
381 quotation.ignore_permissions = True
382 quotation.submit()
383
Rushabh Mehtaa2a1ec72013-07-08 11:08:27 +0530384 from selling.doctype.quotation.quotation import make_sales_order
385
386 sales_order = webnotes.bean(make_sales_order(quotation.doc.name))
Anand Doshie8132122013-06-17 15:42:38 +0530387
388 sales_order.ignore_permissions = True
389 sales_order.insert()
390 sales_order.submit()
391
392 return sales_order
393
394import unittest
395test_dependencies = ["Item", "Price List", "Contact"]
Anand Doshiabc10032013-06-14 17:44:03 +0530396
397class TestCart(unittest.TestCase):
Anand Doshie8132122013-06-17 15:42:38 +0530398 def test_get_lead_or_customer(self):
399 webnotes.session.user = "test@example.com"
400 party1 = get_lead_or_customer()
401 party2 = get_lead_or_customer()
402 self.assertEquals(party1.name, party2.name)
403 self.assertEquals(party1.doctype, "Lead")
404
405 webnotes.session.user = "test_contact_customer@example.com"
406 party = get_lead_or_customer()
407 self.assertEquals(party.name, "_Test Customer")
408
Anand Doshiabc10032013-06-14 17:44:03 +0530409 def test_add_to_cart(self):
410 webnotes.session.user = "test@example.com"
Anand Doshi3dceb842013-06-19 14:57:14 +0530411 update_cart("_Test Item", 1)
Anand Doshiabc10032013-06-14 17:44:03 +0530412
Anand Doshi3dceb842013-06-19 14:57:14 +0530413 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530414 quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"})
415 self.assertTrue(quotation_items)
416 self.assertEquals(quotation_items[0].qty, 1)
417
418 return quotation
419
Anand Doshi3dceb842013-06-19 14:57:14 +0530420 def test_update_cart(self):
Anand Doshie8132122013-06-17 15:42:38 +0530421 self.test_add_to_cart()
422
Anand Doshi3dceb842013-06-19 14:57:14 +0530423 update_cart("_Test Item", 5)
Anand Doshie8132122013-06-17 15:42:38 +0530424
Anand Doshi3dceb842013-06-19 14:57:14 +0530425 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530426 quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"})
427 self.assertTrue(quotation_items)
428 self.assertEquals(quotation_items[0].qty, 5)
429
430 return quotation
Anand Doshiabc10032013-06-14 17:44:03 +0530431
432 def test_remove_from_cart(self):
Anand Doshie8132122013-06-17 15:42:38 +0530433 quotation0 = self.test_add_to_cart()
434
Anand Doshi3dceb842013-06-19 14:57:14 +0530435 update_cart("_Test Item", 0)
Anand Doshie8132122013-06-17 15:42:38 +0530436
Anand Doshi3dceb842013-06-19 14:57:14 +0530437 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530438 self.assertEquals(quotation0.doc.name, quotation.doc.name)
439
440 quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"})
441 self.assertEquals(quotation_items, [])
Anand Doshiabc10032013-06-14 17:44:03 +0530442
443 def test_checkout(self):
Anand Doshi3dceb842013-06-19 14:57:14 +0530444 quotation = self.test_update_cart()
Anand Doshie8132122013-06-17 15:42:38 +0530445 sales_order = checkout()
446 self.assertEquals(sales_order.doclist.getone({"item_code": "_Test Item"}).prevdoc_docname, quotation.doc.name)
Anand Doshiabc10032013-06-14 17:44:03 +0530447