blob: 03387098e9547ec57bcc79aba6a7837672e3c9df [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 Doshic2a35272013-06-19 17:19:20 +05307from webnotes.utils import cint, 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}
21 for address in get_address_docs(party)]
22 }
23
24@webnotes.whitelist()
Anand Doshic2a35272013-06-19 17:19:20 +053025def update_cart(item_code, qty, with_doclist=0):
Anand Doshi3dceb842013-06-19 14:57:14 +053026 quotation = _get_cart_quotation()
Anand Doshiabc10032013-06-14 17:44:03 +053027
Anand Doshic2a35272013-06-19 17:19:20 +053028 qty = cint(qty)
Anand Doshi3dceb842013-06-19 14:57:14 +053029 if qty == 0:
Anand Doshie8132122013-06-17 15:42:38 +053030 quotation.set_doclist(quotation.doclist.get({"item_code": ["!=", item_code]}))
31 else:
32 quotation_items = quotation.doclist.get({"item_code": item_code})
33 if not quotation_items:
34 quotation.doclist.append({
35 "doctype": "Quotation Item",
36 "parentfield": "quotation_details",
37 "item_code": item_code,
Anand Doshi3dceb842013-06-19 14:57:14 +053038 "qty": qty
Anand Doshie8132122013-06-17 15:42:38 +053039 })
40 else:
Anand Doshi3dceb842013-06-19 14:57:14 +053041 quotation_items[0].qty = qty
Anand Doshiabc10032013-06-14 17:44:03 +053042
43 quotation.ignore_permissions = True
44 quotation.save()
45
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)
161
162 return [d.fields for d in doclist]
Anand Doshi3dceb842013-06-19 14:57:14 +0530163
164def _get_cart_quotation(party=None):
165 if not party:
166 party = get_lead_or_customer()
167
Anand Doshiabc10032013-06-14 17:44:03 +0530168 quotation = webnotes.conn.get_value("Quotation",
169 {party.doctype.lower(): party.name, "order_type": "Shopping Cart", "docstatus": 0})
170
171 if quotation:
172 qbean = webnotes.bean("Quotation", quotation)
173 else:
174 qbean = webnotes.bean({
175 "doctype": "Quotation",
176 "naming_series": "QTN-CART-",
Anand Doshic2a35272013-06-19 17:19:20 +0530177 "quotation_to": party.doctype,
Anand Doshiabc10032013-06-14 17:44:03 +0530178 "company": webnotes.defaults.get_user_default("company"),
179 "order_type": "Shopping Cart",
180 "status": "Draft",
181 "__islocal": 1,
Anand Doshiabc10032013-06-14 17:44:03 +0530182 (party.doctype.lower()): party.name
183 })
Anand Doshi99100a42013-07-04 17:13:53 +0530184 qbean.run_method("onload_post_render")
185 apply_cart_settings(party, qbean)
Anand Doshiabc10032013-06-14 17:44:03 +0530186
187 return qbean
Anand Doshiabc10032013-06-14 17:44:03 +0530188
Anand Doshi99100a42013-07-04 17:13:53 +0530189def apply_cart_settings(party=None, quotation=None):
190 if not party:
191 party = get_lead_or_customer()
192 if not quotation:
193 quotation = _get_cart_quotation(party)
Anand Doshiabc10032013-06-14 17:44:03 +0530194
Anand Doshi99100a42013-07-04 17:13:53 +0530195 cart_settings = webnotes.get_obj("Shopping Cart Settings")
Anand Doshiabc10032013-06-14 17:44:03 +0530196
Anand Doshi99100a42013-07-04 17:13:53 +0530197 billing_territory = get_address_territory(quotation.doc.customer_address) or \
198 party.territory
199
200 set_price_list_and_rate(quotation, cart_settings, billing_territory)
201
202 set_taxes(quotation, cart_settings, billing_territory)
203
204 # set shipping rule based on shipping territory
205 shipping_territory = get_address_territory(quotation.doc.shipping_address_name) or \
206 party.territory
207
208 apply_shipping_rule(quotation, cart_settings, shipping_territory)
209
210 quotation.run_method("calculate_taxes_and_totals")
211
212 quotation.save()
213
214def set_price_list_and_rate(quotation, cart_settings, billing_territory):
215 """set price list based on billing territory"""
216 quotation.doc.price_list_name = cart_settings.get_price_list(billing_territory)
217
218 # reset values
219 quotation.doc.price_list_currency = quotation.doc.currency = \
220 quotation.doc.plc_conversion_rate = quotation.doc.conversion_rate = None
221 for item in quotation.doclist.get({"parentfield": "quotation_details"}):
222 item.ref_rate = item.adj_rate = item.export_rate = item.export_amount = None
223
224 # refetch values
225 quotation.run_method("set_price_list_and_item_details")
226
227def set_taxes(quotation, cart_settings, billing_territory):
228 """set taxes based on billing territory"""
229 quotation.doc.charge = cart_settings.get_tax_master(billing_territory)
Anand Doshiabc10032013-06-14 17:44:03 +0530230
Anand Doshi99100a42013-07-04 17:13:53 +0530231 # clear table
232 quotation.doclist = quotation.doc.clear_table(quotation.doclist, "other_charges")
Anand Doshiabc10032013-06-14 17:44:03 +0530233
Anand Doshi99100a42013-07-04 17:13:53 +0530234 # append taxes
235 controller = quotation.make_controller()
236 controller.append_taxes_from_master("other_charges", "charge")
237 quotation.set_doclist(controller.doclist)
238
239def apply_shipping_rule(quotation, cart_settings, shipping_territory):
240 quotation.doc.shipping_rule = cart_settings.get_shipping_rule(shipping_territory)
241 quotation.run_method("apply_shipping_rule")
242
243def get_address_territory(address_name):
244 """Tries to match city, state and country of address to existing territory"""
245 territory = None
246
247 if address_name:
248 address_fields = webnotes.conn.get_value("Address", address_name,
249 ["city", "state", "country"])
250 for value in address_fields:
251 territory = webnotes.conn.get_value("Territory", value)
252 if territory:
253 break
254
255 return territory
256
257def get_cart_price_list(territory_list, territory_name_map):
258 pass
259
Anand Doshie8132122013-06-17 15:42:38 +0530260@webnotes.whitelist()
261def checkout():
Anand Doshi3dceb842013-06-19 14:57:14 +0530262 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530263
264 quotation.ignore_permissions = True
265 quotation.submit()
266
267 sales_order = webnotes.bean(webnotes.map_doclist([["Quotation", "Sales Order"], ["Quotation Item", "Sales Order Item"],
268 ["Sales Taxes and Charges", "Sales Taxes and Charges"]], quotation.doc.name))
269
270 sales_order.ignore_permissions = True
271 sales_order.insert()
272 sales_order.submit()
273
274 return sales_order
275
276import unittest
277test_dependencies = ["Item", "Price List", "Contact"]
Anand Doshiabc10032013-06-14 17:44:03 +0530278
279class TestCart(unittest.TestCase):
Anand Doshie8132122013-06-17 15:42:38 +0530280 def test_get_lead_or_customer(self):
281 webnotes.session.user = "test@example.com"
282 party1 = get_lead_or_customer()
283 party2 = get_lead_or_customer()
284 self.assertEquals(party1.name, party2.name)
285 self.assertEquals(party1.doctype, "Lead")
286
287 webnotes.session.user = "test_contact_customer@example.com"
288 party = get_lead_or_customer()
289 self.assertEquals(party.name, "_Test Customer")
290
Anand Doshiabc10032013-06-14 17:44:03 +0530291 def test_add_to_cart(self):
292 webnotes.session.user = "test@example.com"
Anand Doshi3dceb842013-06-19 14:57:14 +0530293 update_cart("_Test Item", 1)
Anand Doshiabc10032013-06-14 17:44:03 +0530294
Anand Doshi3dceb842013-06-19 14:57:14 +0530295 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530296 quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"})
297 self.assertTrue(quotation_items)
298 self.assertEquals(quotation_items[0].qty, 1)
299
300 return quotation
301
Anand Doshi3dceb842013-06-19 14:57:14 +0530302 def test_update_cart(self):
Anand Doshie8132122013-06-17 15:42:38 +0530303 self.test_add_to_cart()
304
Anand Doshi3dceb842013-06-19 14:57:14 +0530305 update_cart("_Test Item", 5)
Anand Doshie8132122013-06-17 15:42:38 +0530306
Anand Doshi3dceb842013-06-19 14:57:14 +0530307 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530308 quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"})
309 self.assertTrue(quotation_items)
310 self.assertEquals(quotation_items[0].qty, 5)
311
312 return quotation
Anand Doshiabc10032013-06-14 17:44:03 +0530313
314 def test_remove_from_cart(self):
Anand Doshie8132122013-06-17 15:42:38 +0530315 quotation0 = self.test_add_to_cart()
316
Anand Doshi3dceb842013-06-19 14:57:14 +0530317 update_cart("_Test Item", 0)
Anand Doshie8132122013-06-17 15:42:38 +0530318
Anand Doshi3dceb842013-06-19 14:57:14 +0530319 quotation = _get_cart_quotation()
Anand Doshie8132122013-06-17 15:42:38 +0530320 self.assertEquals(quotation0.doc.name, quotation.doc.name)
321
322 quotation_items = quotation.doclist.get({"parentfield": "quotation_details", "item_code": "_Test Item"})
323 self.assertEquals(quotation_items, [])
Anand Doshiabc10032013-06-14 17:44:03 +0530324
325 def test_checkout(self):
Anand Doshi3dceb842013-06-19 14:57:14 +0530326 quotation = self.test_update_cart()
Anand Doshie8132122013-06-17 15:42:38 +0530327 sales_order = checkout()
328 self.assertEquals(sales_order.doclist.getone({"item_code": "_Test Item"}).prevdoc_docname, quotation.doc.name)
Anand Doshiabc10032013-06-14 17:44:03 +0530329