blob: 02416291bdedc3e1020aeafea40306dea69c9b81 [file] [log] [blame]
Rushabh Mehtaad45e312013-11-20 12:59:58 +05301# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
Rushabh Mehtae67d1fb2013-08-05 14:59:54 +05302# License: GNU General Public License v3. See license.txt
Rushabh Mehta3966f1d2012-02-23 12:35:32 +05303
Anand Doshi486f9df2012-07-19 13:40:31 +05304from __future__ import unicode_literals
Rushabh Mehta1f847992013-12-12 19:12:19 +05305import webnotes, json
Anand Doshi1dde46a2013-05-15 21:15:57 +05306from webnotes import msgprint, _
Rushabh Mehta1f847992013-12-12 19:12:19 +05307from webnotes.utils import cstr, flt, now_datetime, cint
Anand Doshi0fd99e72012-11-30 16:38:04 +05308from webnotes.model.doc import addchild
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +05309
Rushabh Mehta1f847992013-12-12 19:12:19 +053010from erpnext.controllers.status_updater import StatusUpdater
Anand Doshi756dca72013-01-15 18:39:21 +053011
Rushabh Mehtab09d9da2014-01-02 11:47:23 +053012
13class TransactionBase(StatusUpdater):
Anand Doshi2ac0a832013-07-10 20:49:44 +053014 def set_address_fields(self):
15 party_type, party_name = self.get_party_type_and_name()
16
17 if party_type in ("Customer", "Lead"):
18 if self.doc.customer_address:
19 self.doc.address_display = get_address_display(self.doc.customer_address)
20
21 if self.doc.shipping_address_name:
22 self.doc.shipping_address = get_address_display(self.doc.shipping_address_name)
23
24 elif self.doc.supplier_address:
25 self.doc.address_display = get_address_display(self.doc.supplier_address)
26
27 def set_contact_fields(self):
28 party_type, party_name = self.get_party_type_and_name()
29
30 if party_type == "Lead":
31 contact_dict = map_lead_contact_details(party_name)
32 else:
33 contact_dict = map_party_contact_details(self.doc.contact_person, party_type, party_name)
34
35 for fieldname, value in contact_dict.items():
36 if self.meta.get_field(fieldname):
37 self.doc.fields[fieldname] = value
38
39 def get_party_type_and_name(self):
40 if not hasattr(self, "_party_type_and_name"):
41 for party_type in ("Lead", "Customer", "Supplier"):
42 party_field = party_type.lower()
43 if self.meta.get_field(party_field) and self.doc.fields.get(party_field):
44 self._party_type_and_name = (party_type, self.doc.fields.get(party_field))
45 break
46
47 return self._party_type_and_name
Anand Doshif2045fb2013-07-25 17:46:02 +053048
Anand Doshi99100a42013-07-04 17:13:53 +053049 def get_lead_defaults(self):
50 out = self.get_default_address_and_contact("lead")
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +053051
Anand Doshi99100a42013-07-04 17:13:53 +053052 lead = webnotes.conn.get_value("Lead", self.doc.lead,
53 ["territory", "company_name", "lead_name"], as_dict=True) or {}
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +053054
Anand Doshi99100a42013-07-04 17:13:53 +053055 out["territory"] = lead.get("territory")
56 out["customer_name"] = lead.get("company_name") or lead.get("lead_name")
57
58 return out
59
60 def set_lead_defaults(self):
61 self.doc.fields.update(self.get_lead_defaults())
Rushabh Mehtab09d9da2014-01-02 11:47:23 +053062
Anand Doshi99100a42013-07-04 17:13:53 +053063 # TODO deprecate this - used only in sales_order.js
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +053064 def get_shipping_address(self, name):
Anand Doshi2ac0a832013-07-10 20:49:44 +053065 shipping_address = get_default_address("customer", name, is_shipping_address=True)
66 return {
67 'shipping_address_name' : shipping_address,
68 'shipping_address' : get_address_display(shipping_address) if shipping_address else None
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +053069 }
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +053070
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +053071 # Get Supplier Default Primary Address - first load
72 # -----------------------
73 def get_default_supplier_address(self, args):
Anand Doshi923d41d2013-05-28 17:23:36 +053074 if isinstance(args, basestring):
Rushabh Mehta1f847992013-12-12 19:12:19 +053075 args = json.loads(args)
Anand Doshi2ac0a832013-07-10 20:49:44 +053076
77 address_name = get_default_address("supplier", args["supplier"])
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +053078 ret = {
79 'supplier_address' : address_name,
Anand Doshi2ac0a832013-07-10 20:49:44 +053080 'address_display' : get_address_display(address_name),
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +053081 }
Anand Doshi2ac0a832013-07-10 20:49:44 +053082 ret.update(map_party_contact_details(None, "supplier", args["supplier"]))
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +053083 ret.update(self.get_supplier_details(args['supplier']))
Nabin Hait06c4de82011-08-16 16:38:11 +053084 return ret
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +053085
86 # Get Supplier Address
87 # -----------------------
88 def get_supplier_address(self, args):
Rushabh Mehta1f847992013-12-12 19:12:19 +053089 args = json.loads(args)
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +053090 ret = {
Anand Doshi2ac0a832013-07-10 20:49:44 +053091 'supplier_address' : args['address'],
92 'address_display' : get_address_display(args["address"]),
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +053093 }
Anand Doshi2ac0a832013-07-10 20:49:44 +053094 ret.update(map_party_contact_details(contact_name=args['contact']))
Nabin Hait06c4de82011-08-16 16:38:11 +053095 return ret
Nabin Haitdeff5782013-07-24 11:30:35 +053096
97 def set_supplier_address(self, args):
98 self.doc.fields.update(self.get_supplier_address(args))
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +053099
100 # Get Supplier Details
101 # -----------------------
Anand Doshif2f5c942012-07-18 20:13:52 +0530102 def get_supplier_details(self, name):
103 supplier_details = webnotes.conn.sql("""\
104 select supplier_name, default_currency
105 from `tabSupplier`
106 where name = %s and docstatus < 2""", name, as_dict=1)
107 if supplier_details:
108 return {
109 'supplier_name': (supplier_details[0]['supplier_name']
110 or self.doc.fields.get('supplier_name')),
111 'currency': (supplier_details[0]['default_currency']
112 or self.doc.fields.get('currency')),
113 }
114 else:
115 return {}
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +0530116
117 # Get Sales Person Details of Customer
118 # ------------------------------------
119 def get_sales_person(self, name):
Anand Doshi8ae5ba92012-06-25 20:05:35 +0530120 self.doclist = self.doc.clear_table(self.doclist,'sales_team')
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +0530121 idx = 0
Anand Doshi74d1b652012-01-27 12:25:09 +0530122 for d in webnotes.conn.sql("select sales_person, allocated_percentage, allocated_amount, incentives from `tabSales Team` where parent = '%s'" % name):
Anand Doshif5d90ab2012-12-24 17:02:38 +0530123 ch = addchild(self.doc, 'sales_team', 'Sales Team', self.doclist)
Pratik Vyasc1e6e4c2011-06-08 14:37:15 +0530124 ch.sales_person = d and cstr(d[0]) or ''
125 ch.allocated_percentage = d and flt(d[1]) or 0
126 ch.allocated_amount = d and flt(d[2]) or 0
127 ch.incentives = d and flt(d[3]) or 0
128 ch.idx = idx
129 idx += 1
Nabin Hait41cc3272012-04-30 14:36:18 +0530130
Rushabh Mehta35c017a2012-11-30 10:57:28 +0530131 def load_notification_message(self):
132 dt = self.doc.doctype.lower().replace(" ", "_")
133 if int(webnotes.conn.get_value("Notification Control", None, dt) or 0):
134 self.doc.fields["__notification_message"] = \
135 webnotes.conn.get_value("Notification Control", None, dt + "_message")
Rushabh Mehta96690eb2013-09-02 17:04:27 +0530136
Anand Doshiee3d5cc2013-03-13 12:58:54 +0530137 def validate_posting_time(self):
138 if not self.doc.posting_time:
Anand Doshiacec0222013-03-26 12:33:43 +0530139 self.doc.posting_time = now_datetime().strftime('%H:%M:%S')
Anand Doshie53a81d2013-06-10 15:15:40 +0530140
Anand Doshi670199b2013-06-10 15:38:01 +0530141 def add_calendar_event(self, opts, force=False):
Anand Doshie53a81d2013-06-10 15:15:40 +0530142 if self.doc.contact_by != cstr(self._prev.contact_by) or \
Anand Doshi670199b2013-06-10 15:38:01 +0530143 self.doc.contact_date != cstr(self._prev.contact_date) or force:
Anand Doshie53a81d2013-06-10 15:15:40 +0530144
145 self.delete_events()
146 self._add_calendar_event(opts)
147
148 def delete_events(self):
149 webnotes.delete_doc("Event", webnotes.conn.sql_list("""select name from `tabEvent`
Nabin Haitad6ce4e2013-09-19 11:02:24 +0530150 where ref_type=%s and ref_name=%s""", (self.doc.doctype, self.doc.name)),
151 ignore_permissions=True)
Anand Doshie53a81d2013-06-10 15:15:40 +0530152
153 def _add_calendar_event(self, opts):
154 opts = webnotes._dict(opts)
155
156 if self.doc.contact_date:
157 event_doclist = [{
158 "doctype": "Event",
159 "owner": opts.owner or self.doc.owner,
160 "subject": opts.subject,
161 "description": opts.description,
162 "starts_on": self.doc.contact_date + " 10:00:00",
163 "event_type": "Private",
164 "ref_type": self.doc.doctype,
165 "ref_name": self.doc.name
166 }]
167
168 if webnotes.conn.exists("Profile", self.doc.contact_by):
169 event_doclist.append({
170 "doctype": "Event User",
171 "parentfield": "event_individuals",
172 "person": self.doc.contact_by
173 })
174
175 webnotes.bean(event_doclist).insert()
Nabin Hait2bd37772013-07-08 19:00:29 +0530176
Rushabh Mehta4dca4012013-07-25 17:45:59 +0530177 def validate_uom_is_integer(self, uom_field, qty_fields):
178 validate_uom_is_integer(self.doclist, uom_field, qty_fields)
179
Nabin Hait2bd37772013-07-08 19:00:29 +0530180 def validate_with_previous_doc(self, source_dt, ref):
181 for key, val in ref.items():
Nabin Hait6e68e0e2013-07-10 15:33:29 +0530182 is_child = val.get("is_child_table")
Nabin Hait2bd37772013-07-08 19:00:29 +0530183 ref_doc = {}
Nabin Haita9ec49e2013-07-15 16:33:24 +0530184 item_ref_dn = []
Nabin Hait2bd37772013-07-08 19:00:29 +0530185 for d in self.doclist.get({"doctype": source_dt}):
Nabin Hait6e68e0e2013-07-10 15:33:29 +0530186 ref_dn = d.fields.get(val["ref_dn_field"])
187 if ref_dn:
188 if is_child:
189 self.compare_values({key: [ref_dn]}, val["compare_fields"], d)
Nabin Haita9ec49e2013-07-15 16:33:24 +0530190 if ref_dn not in item_ref_dn:
191 item_ref_dn.append(ref_dn)
Nabin Hait5e200e42013-07-29 15:29:51 +0530192 elif not val.get("allow_duplicate_prev_row_id"):
Nabin Haita9ec49e2013-07-15 16:33:24 +0530193 webnotes.msgprint(_("Row ") + cstr(d.idx + 1) +
194 _(": Duplicate row from same ") + key, raise_exception=1)
195 elif ref_dn:
Nabin Hait6e68e0e2013-07-10 15:33:29 +0530196 ref_doc.setdefault(key, [])
197 if ref_dn not in ref_doc[key]:
198 ref_doc[key].append(ref_dn)
199 if ref_doc:
Nabin Hait2bd37772013-07-08 19:00:29 +0530200 self.compare_values(ref_doc, val["compare_fields"])
201
202 def compare_values(self, ref_doc, fields, doc=None):
Nabin Hait6e68e0e2013-07-10 15:33:29 +0530203 for ref_doctype, ref_dn_list in ref_doc.items():
204 for ref_docname in ref_dn_list:
205 prevdoc_values = webnotes.conn.get_value(ref_doctype, ref_docname,
206 [d[0] for d in fields], as_dict=1)
207
208 for field, condition in fields:
Anand Doshi63d844b2013-07-10 20:55:15 +0530209 if prevdoc_values[field] is not None:
210 self.validate_value(field, condition, prevdoc_values[field], doc)
Anand Doshi99100a42013-07-04 17:13:53 +0530211
Anand Doshi2ac0a832013-07-10 20:49:44 +0530212def get_default_address(party_field, party_name, is_shipping_address=False):
213 if is_shipping_address:
214 order_by = "is_shipping_address desc, is_primary_address desc, name asc"
215 else:
216 order_by = "is_primary_address desc, name asc"
217
218 address = webnotes.conn.sql("""select name from `tabAddress` where `%s`=%s order by %s
219 limit 1""" % (party_field, "%s", order_by), party_name)
220
221 return address[0][0] if address else None
222
223def get_default_contact(party_field, party_name):
224 contact = webnotes.conn.sql("""select name from `tabContact` where `%s`=%s
225 order by is_primary_contact desc, name asc limit 1""" % (party_field, "%s"),
226 (party_name,))
227
228 return contact[0][0] if contact else None
Anand Doshi2ac0a832013-07-10 20:49:44 +0530229
Anand Doshi2ac0a832013-07-10 20:49:44 +0530230def map_lead_contact_details(party_name):
Anand Doshi99100a42013-07-04 17:13:53 +0530231 out = {}
232 for fieldname in ["contact_display", "contact_email", "contact_mobile", "contact_phone"]:
233 out[fieldname] = None
234
235 lead = webnotes.conn.sql("""select * from `tabLead` where name=%s""", party_name, as_dict=True)
236 if lead:
237 lead = lead[0]
238 out.update({
239 "contact_display": lead.get("lead_name"),
240 "contact_email": lead.get("email_id"),
241 "contact_mobile": lead.get("mobile_no"),
242 "contact_phone": lead.get("phone"),
243 })
244
245 return out
246
Anand Doshi2ac0a832013-07-10 20:49:44 +0530247def map_party_contact_details(contact_name=None, party_field=None, party_name=None):
Anand Doshi99100a42013-07-04 17:13:53 +0530248 out = {}
249 for fieldname in ["contact_person", "contact_display", "contact_email",
250 "contact_mobile", "contact_phone", "contact_designation", "contact_department"]:
251 out[fieldname] = None
Nabin Haitdeff5782013-07-24 11:30:35 +0530252
Anand Doshiba8ea7d2013-07-24 12:08:42 +0530253 if not contact_name and party_field:
254 contact_name = get_default_contact(party_field, party_name)
255
Nabin Hait02a3d7a2013-07-24 16:33:30 +0530256 if contact_name:
257 contact = webnotes.conn.sql("""select * from `tabContact` where name=%s""",
258 contact_name, as_dict=True)
Anand Doshi2ac0a832013-07-10 20:49:44 +0530259
Nabin Hait02a3d7a2013-07-24 16:33:30 +0530260 if contact:
261 contact = contact[0]
262 out.update({
263 "contact_person": contact.get("name"),
264 "contact_display": " ".join(filter(None,
265 [contact.get("first_name"), contact.get("last_name")])),
266 "contact_email": contact.get("email_id"),
267 "contact_mobile": contact.get("mobile_no"),
268 "contact_phone": contact.get("phone"),
269 "contact_designation": contact.get("designation"),
270 "contact_department": contact.get("department")
271 })
Anand Doshiba8ea7d2013-07-24 12:08:42 +0530272
Anand Doshi99100a42013-07-04 17:13:53 +0530273 return out
274
275def get_address_territory(address_doc):
276 territory = None
277 for fieldname in ("city", "state", "country"):
278 value = address_doc.fields.get(fieldname)
279 if value:
280 territory = webnotes.conn.get_value("Territory", value.strip())
281 if territory:
282 break
283
284 return territory
Anand Doshi1dde46a2013-05-15 21:15:57 +0530285
286def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, company):
287 """common validation for currency and price list currency"""
Akhilesh Darjee10dce342013-09-25 11:09:33 +0530288
Anand Doshi1dde46a2013-05-15 21:15:57 +0530289 company_currency = webnotes.conn.get_value("Company", company, "default_currency")
Akhilesh Darjeee0b32822013-11-12 19:27:16 +0530290
Akhilesh Darjee1797cfe2013-10-11 13:34:10 +0530291 if not conversion_rate:
Rushabh Mehta2b089852013-12-19 18:27:48 +0530292 msgprint(_('%(conversion_rate_label)s is mandatory. Maybe Currency Exchange record is not created for %(from_currency)s to %(to_currency)s') % {
Akhilesh Darjeee0b32822013-11-12 19:27:16 +0530293 "conversion_rate_label": conversion_rate_label,
294 "from_currency": currency,
295 "to_currency": company_currency
296 }, raise_exception=True)
Anand Doshi1dde46a2013-05-15 21:15:57 +0530297
298def validate_item_fetch(args, item):
Rushabh Mehta1f847992013-12-12 19:12:19 +0530299 from erpnext.stock.utils import validate_end_of_life
Anand Doshi1dde46a2013-05-15 21:15:57 +0530300 validate_end_of_life(item.name, item.end_of_life)
301
302 # validate company
303 if not args.company:
304 msgprint(_("Please specify Company"), raise_exception=True)
305
Anand Doshif3096132013-05-21 19:35:06 +0530306def validate_currency(args, item, meta=None):
Anand Doshifc777182013-05-27 19:29:07 +0530307 from webnotes.model.meta import get_field_precision
Anand Doshif3096132013-05-21 19:35:06 +0530308 if not meta:
309 meta = webnotes.get_doctype(args.doctype)
Anand Doshifc777182013-05-27 19:29:07 +0530310
Anand Doshif3096132013-05-21 19:35:06 +0530311 # validate conversion rate
Anand Doshi1dde46a2013-05-15 21:15:57 +0530312 if meta.get_field("currency"):
Anand Doshi1dde46a2013-05-15 21:15:57 +0530313 validate_conversion_rate(args.currency, args.conversion_rate,
314 meta.get_label("conversion_rate"), args.company)
Anand Doshifc777182013-05-27 19:29:07 +0530315
316 # round it
317 args.conversion_rate = flt(args.conversion_rate,
Anand Doshie961af42013-05-31 14:30:46 +0530318 get_field_precision(meta.get_field("conversion_rate"),
319 webnotes._dict({"fields": args})))
Anand Doshi1dde46a2013-05-15 21:15:57 +0530320
Anand Doshif3096132013-05-21 19:35:06 +0530321 # validate price list conversion rate
Rushabh Mehta4a404e92013-08-09 18:11:35 +0530322 if meta.get_field("price_list_currency") and (args.selling_price_list or args.buying_price_list) \
323 and args.price_list_currency:
Anand Doshif3096132013-05-21 19:35:06 +0530324 validate_conversion_rate(args.price_list_currency, args.plc_conversion_rate,
Anand Doshifc777182013-05-27 19:29:07 +0530325 meta.get_label("plc_conversion_rate"), args.company)
326
327 # round it
328 args.plc_conversion_rate = flt(args.plc_conversion_rate,
Anand Doshie961af42013-05-31 14:30:46 +0530329 get_field_precision(meta.get_field("plc_conversion_rate"),
330 webnotes._dict({"fields": args})))
Anand Doshi097ac352013-06-10 15:38:31 +0530331
Anand Doshi11d31132013-06-17 12:51:36 +0530332def delete_events(ref_type, ref_name):
333 webnotes.delete_doc("Event", webnotes.conn.sql_list("""select name from `tabEvent`
Anand Doshib5fd7882013-06-17 12:55:05 +0530334 where ref_type=%s and ref_name=%s""", (ref_type, ref_name)), for_reload=True)
Rushabh Mehta4dca4012013-07-25 17:45:59 +0530335
Rushabh Mehtac4f5e4f2013-07-26 11:21:45 +0530336class UOMMustBeIntegerError(webnotes.ValidationError): pass
337
Rushabh Mehta4dca4012013-07-25 17:45:59 +0530338def validate_uom_is_integer(doclist, uom_field, qty_fields):
339 if isinstance(qty_fields, basestring):
340 qty_fields = [qty_fields]
341
342 integer_uoms = filter(lambda uom: webnotes.conn.get_value("UOM", uom,
343 "must_be_whole_number") or None, doclist.get_distinct_values(uom_field))
344
345 if not integer_uoms:
346 return
347
348 for d in doclist:
349 if d.fields.get(uom_field) in integer_uoms:
350 for f in qty_fields:
351 if d.fields.get(f):
352 if cint(d.fields[f])!=d.fields[f]:
353 webnotes.msgprint(_("For UOM") + " '" + d.fields[uom_field] \
354 + "': " + _("Quantity cannot be a fraction.") \
355 + " " + _("In Row") + ": " + str(d.idx),
Rushabh Mehtac4f5e4f2013-07-26 11:21:45 +0530356 raise_exception=UOMMustBeIntegerError)