blob: 98edc07663b20e7e4111d30c3ddb2155bd29e546 [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 Doshiabc10032013-06-14 17:44:03 +053045
Anand Doshic2a35272013-06-19 17:19:20 +053046 if with_doclist:
Anand Doshiedbf3e12013-07-02 11:40:16 +053047 return get_cart_quotation(quotation.doclist)
Anand Doshic2a35272013-06-19 17:19:20 +053048 else:
49 return quotation.doc.name
Anand Doshiedbf3e12013-07-02 11:40:16 +053050
51@webnotes.whitelist()
52def update_cart_address(address_fieldname, address_name):
53 from utilities.transaction_base import get_address_display
54
55 quotation = _get_cart_quotation()
56 address_display = get_address_display(webnotes.doc("Address", address_name).fields)
57
58 if address_fieldname == "shipping_address_name":
59 quotation.doc.shipping_address_name = address_name
60 quotation.doc.shipping_address = address_display
61
62 if not quotation.doc.customer_address:
63 address_fieldname == "customer_address"
64
65 if address_fieldname == "customer_address":
66 quotation.doc.customer_address = address_name
67 quotation.doc.address_display = address_display
68
69
70 quotation.ignore_permissions = True
71 quotation.save()
Anand Doshi99100a42013-07-04 17:13:53 +053072
73 apply_cart_settings(quotation=quotation)
Anand Doshiedbf3e12013-07-02 11:40:16 +053074
75 return get_cart_quotation(quotation.doclist)
76
77@webnotes.whitelist()
78def get_addresses():
79 return [d.fields for d in get_address_docs()]
80
81@webnotes.whitelist()
82def save_address(fields, address_fieldname=None):
83 party = get_lead_or_customer()
84 fields = webnotes.load_json(fields)
85
86 if fields.get("name"):
87 bean = webnotes.bean("Address", fields.get("name"))
88 else:
89 bean = webnotes.bean({"doctype": "Address", "__islocal": 1})
90
91 bean.doc.fields.update(fields)
92
93 party_fieldname = party.doctype.lower()
94 bean.doc.fields.update({
95 party_fieldname: party.name,
96 (party_fieldname + "_name"): party.fields[party_fieldname + "_name"]
97 })
98 bean.ignore_permissions = True
99 bean.save()
100
101 if address_fieldname:
102 update_cart_address(address_fieldname, bean.doc.name)
103
104 return bean.doc.name
105
106def get_address_docs(party=None):
107 from webnotes.model.doclist import objectify
108 from utilities.transaction_base import get_address_display
109
110 if not party:
111 party = get_lead_or_customer()
112
113 address_docs = objectify(webnotes.conn.sql("""select * from `tabAddress`
114 where `%s`=%s order by name""" % (party.doctype.lower(), "%s"), party.name,
115 as_dict=True, update={"doctype": "Address"}))
116
117 for address in address_docs:
118 address.display = get_address_display(address.fields)
119 address.display = (address.display).replace("\n", "<br>\n")
120
121 return address_docs
Anand Doshie8132122013-06-17 15:42:38 +0530122
Anand Doshiabc10032013-06-14 17:44:03 +0530123def get_lead_or_customer():
124 customer = webnotes.conn.get_value("Contact", {"email_id": webnotes.session.user}, "customer")
125 if customer:
126 return webnotes.doc("Customer", customer)
127
128 lead = webnotes.conn.get_value("Lead", {"email_id": webnotes.session.user})
129 if lead:
130 return webnotes.doc("Lead", lead)
131 else:
132 lead_bean = webnotes.bean({
133 "doctype": "Lead",
134 "email_id": webnotes.session.user,
135 "lead_name": get_fullname(webnotes.session.user),
Anand Doshi99100a42013-07-04 17:13:53 +0530136 "territory": guess_territory(),
Anand Doshiabc10032013-06-14 17:44:03 +0530137 "status": "Open" # TODO: set something better???
138 })
139 lead_bean.ignore_permissions = True
140 lead_bean.insert()
141
142 return lead_bean.doc
Anand Doshi99100a42013-07-04 17:13:53 +0530143
144def guess_territory():
145 territory = None
146 geoip_country = webnotes.session.get("session_country")
147 if geoip_country:
148 territory = webnotes.conn.get_value("Territory", geoip_country)
149
150 return territory or \
151 webnotes.conn.get_value("Shopping Cart Settings", None, "territory") or \
152 "All Territories"
Anand Doshi3dceb842013-06-19 14:57:14 +0530153
Anand Doshic2a35272013-06-19 17:19:20 +0530154def decorate_quotation_doclist(doclist):
155 for d in doclist:
156 if d.item_code:
157 d.fields.update(webnotes.conn.get_value("Item", d.item_code,
158 ["website_image", "web_short_description", "page_name"], as_dict=True))
159 d.formatted_rate = fmt_money(d.export_rate, currency=doclist[0].currency)
160 d.formatted_amount = fmt_money(d.export_amount, currency=doclist[0].currency)
Anand Doshi0b4943c2013-07-04 23:45:22 +0530161 elif d.charge_type:
162 d.formatted_tax_amount = fmt_money(d.tax_amount / doclist[0].conversion_rate,
163 currency=doclist[0].currency)
Anand Doshic2a35272013-06-19 17:19:20 +0530164
Anand Doshi0b4943c2013-07-04 23:45:22 +0530165 doclist[0].formatted_grand_total_export = fmt_money(doclist[0].grand_total_export,
166 currency=doclist[0].currency)
167
Anand Doshic2a35272013-06-19 17:19:20 +0530168 return [d.fields for d in doclist]
Anand Doshi3dceb842013-06-19 14:57:14 +0530169
170def _get_cart_quotation(party=None):
171 if not party:
172 party = get_lead_or_customer()
173
Anand Doshiabc10032013-06-14 17:44:03 +0530174 quotation = webnotes.conn.get_value("Quotation",
175 {party.doctype.lower(): party.name, "order_type": "Shopping Cart", "docstatus": 0})
176
177 if quotation:
178 qbean = webnotes.bean("Quotation", quotation)
179 else:
180 qbean = webnotes.bean({
181 "doctype": "Quotation",
182 "naming_series": "QTN-CART-",
Anand Doshic2a35272013-06-19 17:19:20 +0530183 "quotation_to": party.doctype,
Anand Doshiabc10032013-06-14 17:44:03 +0530184 "company": webnotes.defaults.get_user_default("company"),
185 "order_type": "Shopping Cart",
186 "status": "Draft",
187 "__islocal": 1,
Anand Doshiabc10032013-06-14 17:44:03 +0530188 (party.doctype.lower()): party.name
189 })
Anand Doshi99100a42013-07-04 17:13:53 +0530190 qbean.run_method("onload_post_render")
191 apply_cart_settings(party, qbean)
Anand Doshiabc10032013-06-14 17:44:03 +0530192
193 return qbean
Anand Doshiabc10032013-06-14 17:44:03 +0530194
Anand Doshi99100a42013-07-04 17:13:53 +0530195def apply_cart_settings(party=None, quotation=None):
196 if not party:
197 party = get_lead_or_customer()
198 if not quotation:
199 quotation = _get_cart_quotation(party)
Anand Doshiabc10032013-06-14 17:44:03 +0530200
Anand Doshi99100a42013-07-04 17:13:53 +0530201 cart_settings = webnotes.get_obj("Shopping Cart Settings")
Anand Doshiabc10032013-06-14 17:44:03 +0530202
Anand Doshi99100a42013-07-04 17:13:53 +0530203 billing_territory = get_address_territory(quotation.doc.customer_address) or \
204 party.territory
205
206 set_price_list_and_rate(quotation, cart_settings, billing_territory)
207
Anand Doshi99100a42013-07-04 17:13:53 +0530208 quotation.run_method("calculate_taxes_and_totals")
209
Anand Doshi0b4943c2013-07-04 23:45:22 +0530210 set_taxes(quotation, cart_settings, billing_territory)
211
212 _apply_shipping_rule(party, quotation, cart_settings)
213
214 quotation.ignore_permissions = True
Anand Doshi99100a42013-07-04 17:13:53 +0530215 quotation.save()
216
217def set_price_list_and_rate(quotation, cart_settings, billing_territory):
218 """set price list based on billing territory"""
219 quotation.doc.price_list_name = cart_settings.get_price_list(billing_territory)
220
221 # reset values
222 quotation.doc.price_list_currency = quotation.doc.currency = \
223 quotation.doc.plc_conversion_rate = quotation.doc.conversion_rate = None
224 for item in quotation.doclist.get({"parentfield": "quotation_details"}):
225 item.ref_rate = item.adj_rate = item.export_rate = item.export_amount = None
226
227 # refetch values
228 quotation.run_method("set_price_list_and_item_details")
229
230def set_taxes(quotation, cart_settings, billing_territory):
231 """set taxes based on billing territory"""
232 quotation.doc.charge = cart_settings.get_tax_master(billing_territory)
Anand Doshiabc10032013-06-14 17:44:03 +0530233
Anand Doshi99100a42013-07-04 17:13:53 +0530234 # clear table
235 quotation.doclist = quotation.doc.clear_table(quotation.doclist, "other_charges")
Anand Doshiabc10032013-06-14 17:44:03 +0530236
Anand Doshi99100a42013-07-04 17:13:53 +0530237 # append taxes
238 controller = quotation.make_controller()
239 controller.append_taxes_from_master("other_charges", "charge")
240 quotation.set_doclist(controller.doclist)
Anand Doshi0b4943c2013-07-04 23:45:22 +0530241
242@webnotes.whitelist()
243def apply_shipping_rule(shipping_rule):
244 quotation = _get_cart_quotation()
Anand Doshi99100a42013-07-04 17:13:53 +0530245
Anand Doshi0b4943c2013-07-04 23:45:22 +0530246 quotation.doc.shipping_rule = shipping_rule
247
248 apply_cart_settings(quotation=quotation)
249
250 quotation.save()
251
252 return get_cart_quotation(quotation.doclist)
253
254def _apply_shipping_rule(party=None, quotation=None, cart_settings=None):
255 shipping_rules = get_shipping_rules(party, quotation, cart_settings)
256
257 if not shipping_rules:
258 return
259
260 elif quotation.doc.shipping_rule not in shipping_rules:
261 quotation.doc.shipping_rule = shipping_rules[0]
262
Anand Doshi99100a42013-07-04 17:13:53 +0530263 quotation.run_method("apply_shipping_rule")
Anand Doshi0b4943c2013-07-04 23:45:22 +0530264 quotation.run_method("calculate_taxes_and_totals")
265
266def get_applicable_shipping_rules(party=None, quotation=None):
267 shipping_rules = get_shipping_rules(party, quotation)
268
269 if shipping_rules:
270 rule_label_map = webnotes.conn.get_values("Shipping Rule", shipping_rules, "label")
271 # we need this in sorted order as per the position of the rule in the settings page
272 return [[rule, rule_label_map.get(rule)] for rule in shipping_rules]
273
274def get_shipping_rules(party=None, quotation=None, cart_settings=None):
275 if not party:
276 party = get_lead_or_customer()
277 if not quotation:
278 quotation = _get_cart_quotation()
279 if not cart_settings:
280 cart_settings = webnotes.get_obj("Shopping Cart Settings")
281
282 # set shipping rule based on shipping territory
283 shipping_territory = get_address_territory(quotation.doc.shipping_address_name) or \
284 party.territory
285
286 shipping_rules = cart_settings.get_shipping_rules(shipping_territory)
287
288 return shipping_rules
Anand Doshi99100a42013-07-04 17:13:53 +0530289
290def get_address_territory(address_name):
291 """Tries to match city, state and country of address to existing territory"""
292 territory = None
293
294 if address_name:
295 address_fields = webnotes.conn.get_value("Address", address_name,
296 ["city", "state", "country"])
297 for value in address_fields:
298 territory = webnotes.conn.get_value("Territory", value)
299 if territory:
300 break
301
302 return territory
303
Anand Doshie8132122013-06-17 15:42:38 +0530304@webnotes.whitelist()
305def checkout():
Anand Doshi3dceb842013-06-19 14:57:14 +0530306 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530307
308 quotation.ignore_permissions = True
309 quotation.submit()
310
311 sales_order = webnotes.bean(webnotes.map_doclist([["Quotation", "Sales Order"], ["Quotation Item", "Sales Order Item"],
312 ["Sales Taxes and Charges", "Sales Taxes and Charges"]], quotation.doc.name))
313
314 sales_order.ignore_permissions = True
315 sales_order.insert()
316 sales_order.submit()
317
318 return sales_order
319
320import unittest
321test_dependencies = ["Item", "Price List", "Contact"]
Anand Doshiabc10032013-06-14 17:44:03 +0530322
323class TestCart(unittest.TestCase):
Anand Doshie8132122013-06-17 15:42:38 +0530324 def test_get_lead_or_customer(self):
325 webnotes.session.user = "test@example.com"
326 party1 = get_lead_or_customer()
327 party2 = get_lead_or_customer()
328 self.assertEquals(party1.name, party2.name)
329 self.assertEquals(party1.doctype, "Lead")
330
331 webnotes.session.user = "test_contact_customer@example.com"
332 party = get_lead_or_customer()
333 self.assertEquals(party.name, "_Test Customer")
334
Anand Doshiabc10032013-06-14 17:44:03 +0530335 def test_add_to_cart(self):
336 webnotes.session.user = "test@example.com"
Anand Doshi3dceb842013-06-19 14:57:14 +0530337 update_cart("_Test Item", 1)
Anand Doshiabc10032013-06-14 17:44:03 +0530338
Anand Doshi3dceb842013-06-19 14:57:14 +0530339 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530340 quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"})
341 self.assertTrue(quotation_items)
342 self.assertEquals(quotation_items[0].qty, 1)
343
344 return quotation
345
Anand Doshi3dceb842013-06-19 14:57:14 +0530346 def test_update_cart(self):
Anand Doshie8132122013-06-17 15:42:38 +0530347 self.test_add_to_cart()
348
Anand Doshi3dceb842013-06-19 14:57:14 +0530349 update_cart("_Test Item", 5)
Anand Doshie8132122013-06-17 15:42:38 +0530350
Anand Doshi3dceb842013-06-19 14:57:14 +0530351 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530352 quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"})
353 self.assertTrue(quotation_items)
354 self.assertEquals(quotation_items[0].qty, 5)
355
356 return quotation
Anand Doshiabc10032013-06-14 17:44:03 +0530357
358 def test_remove_from_cart(self):
Anand Doshie8132122013-06-17 15:42:38 +0530359 quotation0 = self.test_add_to_cart()
360
Anand Doshi3dceb842013-06-19 14:57:14 +0530361 update_cart("_Test Item", 0)
Anand Doshie8132122013-06-17 15:42:38 +0530362
Anand Doshi3dceb842013-06-19 14:57:14 +0530363 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530364 self.assertEquals(quotation0.doc.name, quotation.doc.name)
365
366 quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"})
367 self.assertEquals(quotation_items, [])
Anand Doshiabc10032013-06-14 17:44:03 +0530368
369 def test_checkout(self):
Anand Doshi3dceb842013-06-19 14:57:14 +0530370 quotation = self.test_update_cart()
Anand Doshie8132122013-06-17 15:42:38 +0530371 sales_order = checkout()
372 self.assertEquals(sales_order.doclist.getone({"item_code": "_Test Item"}).prevdoc_docname, quotation.doc.name)
Anand Doshiabc10032013-06-14 17:44:03 +0530373