From 2e5db35856888a50fbdb077cc94ecac390dd4e32 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 16 Jan 2013 11:34:26 +0530 Subject: [PATCH 1/6] added automatic lead creation for sales email id --- home/page/desktop/desktop.js | 1 + .../job_applicant/get_job_applications.py | 8 +- public/js/startup.js | 1 + public/js/website_utils.js | 4 +- selling/doctype/lead/get_leads.py | 57 ++++++++++++ selling/doctype/lead/lead.py | 5 +- selling/doctype/lead/lead.txt | 66 +++----------- selling/doctype/lead/lead_list.js | 5 +- .../doctype/sales_email_settings/__init__.py | 0 .../sales_applicant_list.js | 12 +++ .../sales_email_settings.py | 17 ++++ .../sales_email_settings.txt | 89 +++++++++++++++++++ startup/open_count.py | 1 + startup/schedule_handlers.py | 6 +- startup/startup.py | 20 +++-- .../doctype/support_ticket/support_ticket.js | 2 +- website/__init__.py | 28 ------ website/helpers/contact.py | 63 +++++++++++++ 18 files changed, 284 insertions(+), 101 deletions(-) create mode 100644 selling/doctype/lead/get_leads.py create mode 100644 setup/doctype/sales_email_settings/__init__.py create mode 100644 setup/doctype/sales_email_settings/sales_applicant_list.js create mode 100644 setup/doctype/sales_email_settings/sales_email_settings.py create mode 100644 setup/doctype/sales_email_settings/sales_email_settings.txt create mode 100644 website/helpers/contact.py diff --git a/home/page/desktop/desktop.js b/home/page/desktop/desktop.js index 77e0a3df73d..37011bbca2b 100644 --- a/home/page/desktop/desktop.js +++ b/home/page/desktop/desktop.js @@ -82,6 +82,7 @@ erpnext.desktop.show_pending_notifications = function() { add_circle('module-icon-calendar', 'todays_events', 'Todays Events'); add_circle('module-icon-projects-home', 'open_tasks', 'Open Tasks'); add_circle('module-icon-questions', 'unanswered_questions', 'Unanswered Questions'); + add_circle('module-icon-selling-home', 'open_leads', 'Open Leads'); erpnext.update_messages(); diff --git a/hr/doctype/job_applicant/get_job_applications.py b/hr/doctype/job_applicant/get_job_applications.py index 7509380729d..f2b776c011b 100644 --- a/hr/doctype/job_applicant/get_job_applications.py +++ b/hr/doctype/job_applicant/get_job_applications.py @@ -28,16 +28,12 @@ class JobsMailbox(POP3Mailbox): return webnotes.conn.sql("select user from tabSessions where \ time_to_sec(timediff(now(), lastupdate)) < 1800") - def get_existing_application(self, email_id): - name = webnotes.conn.sql("""select name from `tabJob Applicant` where - email_id = %s""", email_id) - return name and name[0][0] or None - def process_message(self, mail): if mail.from_email == self.settings.email_id: return - name = self.get_existing_application(mail.from_email) + name = webnotes.conn.get_value("Job Applicant", {"email_id": mail.from_email}, + "name") if name: applicant = webnotes.model_wrapper("Job Applicant", name) if applicant.doc.status!="Rejected": diff --git a/public/js/startup.js b/public/js/startup.js index 817175bf474..e2413f124d4 100644 --- a/public/js/startup.js +++ b/public/js/startup.js @@ -108,6 +108,7 @@ erpnext.update_messages = function(reset) { show_in_circle('todays_events', r.message.todays_events); show_in_circle('open_tasks', r.message.open_tasks); show_in_circle('unanswered_questions', r.message.unanswered_questions); + show_in_circle('open_leads', r.message.open_leads); } else { clearInterval(wn.updates.id); diff --git a/public/js/website_utils.js b/public/js/website_utils.js index ff480ed5ee2..6f90434b7b2 100644 --- a/public/js/website_utils.js +++ b/public/js/website_utils.js @@ -11,7 +11,7 @@ erpnext.send_message = function(opts) { method: "POST", url: "server.py", data: { - cmd: "website.send_message", + cmd: "website.helpers.contact.send_message", subject: opts.subject, sender: opts.sender, status: opts.status, @@ -34,6 +34,8 @@ function valid_email(id) { if(id.toLowerCase().search("[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")==-1) return 0; else return 1; } +var validate_email = valid_email; + function get_url_arg(name) { name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]"); var regexS = "[\\?&]"+name+"=([^&#]*)"; diff --git a/selling/doctype/lead/get_leads.py b/selling/doctype/lead/get_leads.py new file mode 100644 index 00000000000..61274574b72 --- /dev/null +++ b/selling/doctype/lead/get_leads.py @@ -0,0 +1,57 @@ +# ERPNext - web based ERP (http://erpnext.com) +# Copyright (C) 2012 Web Notes Technologies Pvt Ltd +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import unicode_literals +import webnotes +from webnotes.utils import cstr, cint +from webnotes.utils.email_lib.receive import POP3Mailbox +from core.doctype.communication.communication import make + +class SalesMailbox(POP3Mailbox): + def setup(self): + self.settings = webnotes.doc("Sales Email Settings", "Sales Email Settings") + + def check_mails(self): + return webnotes.conn.sql("select user from tabSessions where \ + time_to_sec(timediff(now(), lastupdate)) < 1800") + + def process_message(self, mail): + if mail.from_email == self.settings.email_id: + return + + name = webnotes.conn.get_value("Lead", {"email_id": mail.from_email}, "name") + if name: + lead = webnotes.model_wrapper("Lead", name) + lead.doc.status = "Open" + lead.doc.save() + else: + lead = webnotes.model_wrapper({ + "doctype":"Lead", + "lead_name": mail.from_real_name or mail.from_email, + "email_id": mail.from_email, + "status": "Open", + "source": "Email" + }) + lead.insert() + + mail.save_attachments_in_doc(lead.doc) + + make(content=mail.content, sender=mail.from_email, + doctype="Lead", name=lead.doc.name, lead=lead.doc.name) + +def get_leads(): + if cint(webnotes.conn.get_value('Sales Email Settings', None, 'extract_emails')): + SalesMailbox() \ No newline at end of file diff --git a/selling/doctype/lead/lead.py b/selling/doctype/lead/lead.py index 63f9bd0258a..13d1714830b 100644 --- a/selling/doctype/lead/lead.py +++ b/selling/doctype/lead/lead.py @@ -77,8 +77,11 @@ class DocType(TransactionBase): event_user.person = self.doc.contact_by event_user.save() + def on_communication_sent(self, comm): + webnotes.conn.set(self.doc, 'status', 'Replied') + def on_trash(self): - webnotes.conn.sql("""update tabCommunication set lead='' where lead=%s""", + webnotes.conn.sql("""delete from tabCommunication where lead=%s""", self.doc.name) webnotes.conn.sql("""update `tabSupport Ticket` set lead='' where lead=%s""", self.doc.name) \ No newline at end of file diff --git a/selling/doctype/lead/lead.txt b/selling/doctype/lead/lead.txt index 6ffe3c42744..a4f0206070c 100644 --- a/selling/doctype/lead/lead.txt +++ b/selling/doctype/lead/lead.txt @@ -2,9 +2,9 @@ { "owner": "Administrator", "docstatus": 0, - "creation": "2012-11-02 17:16:46", + "creation": "2013-01-10 16:34:18", "modified_by": "Administrator", - "modified": "2012-11-27 18:27:47" + "modified": "2013-01-16 10:51:58" }, { "autoname": "naming_series:", @@ -29,6 +29,7 @@ "doctype": "DocPerm", "read": 1, "parenttype": "DocType", + "report": 1, "parentfield": "permissions" }, { @@ -39,19 +40,17 @@ "description": "To manage multiple series please go to Setup > Manage Series", "no_copy": 1, "oldfieldtype": "Select", - "colour": "White:FFF", "doctype": "DocField", "label": "Naming Series", "oldfieldname": "naming_series", - "permlevel": 0, + "options": "LEAD\nLEAD/10-11/\nLEAD/MUMBAI/", "fieldname": "naming_series", "fieldtype": "Select", "reqd": 0, - "options": "LEAD\nLEAD/10-11/\nLEAD/MUMBAI/" + "permlevel": 0 }, { "oldfieldtype": "Data", - "colour": "White:FFF", "doctype": "DocField", "label": "Contact Name", "oldfieldname": "lead_name", @@ -64,7 +63,6 @@ }, { "oldfieldtype": "Data", - "colour": "White:FFF", "doctype": "DocField", "label": "Email Id", "oldfieldname": "email_id", @@ -84,29 +82,25 @@ "permlevel": 0, "no_copy": 1, "oldfieldtype": "Select", - "colour": "White:FFF", "doctype": "DocField", "label": "Status", "oldfieldname": "status", "default": "Open", - "trigger": "Client", "fieldname": "status", "fieldtype": "Select", "search_index": 1, "reqd": 1, - "options": "\nOpen\nAttempted to Contact\nContact in Future\nContacted\nInterested\nNot interested\nLead Lost\nConverted", + "options": "\nOpen\nReplied\nAttempted to Contact\nContact in Future\nContacted\nInterested\nNot interested\nLead Lost\nConverted", "in_filter": 1 }, { "description": "Source of the lead. If via a campaign, select \"Campaign\"", "no_copy": 1, "oldfieldtype": "Select", - "colour": "White:FFF", "doctype": "DocField", "label": "Source", "oldfieldname": "source", "permlevel": 0, - "trigger": "Client", "fieldname": "source", "fieldtype": "Select", "search_index": 0, @@ -121,9 +115,8 @@ "permlevel": 0 }, { - "allow_on_submit": 0, "oldfieldtype": "Table", - "colour": "White:FFF", + "allow_on_submit": 0, "doctype": "DocField", "label": "Communication HTML", "oldfieldname": "follow_up", @@ -141,45 +134,41 @@ { "description": "Name of organization from where lead has come", "oldfieldtype": "Data", - "colour": "White:FFF", "doctype": "DocField", "label": "Company Name", "oldfieldname": "company_name", - "trigger": "Client", "fieldname": "company_name", "fieldtype": "Data", "search_index": 0, "reqd": 0, - "in_filter": 1, - "permlevel": 0 + "permlevel": 0, + "in_filter": 1 }, { "description": "Source of th", "oldfieldtype": "Link", - "colour": "White:FFF", "doctype": "DocField", "label": "From Customer", "oldfieldname": "customer", - "permlevel": 0, + "options": "Customer", "fieldname": "customer", "fieldtype": "Link", "depends_on": "eval:doc.source == 'Existing Customer'", "hidden": 0, - "options": "Customer" + "permlevel": 0 }, { "description": "Enter campaign name if the source of lead is campaign.", "oldfieldtype": "Link", - "colour": "White:FFF", "doctype": "DocField", "label": "Campaign Name", "oldfieldname": "campaign_name", - "permlevel": 0, + "options": "Campaign", "fieldname": "campaign_name", "fieldtype": "Link", "depends_on": "eval:doc.source == 'Campaign'", "hidden": 0, - "options": "Campaign" + "permlevel": 0 }, { "doctype": "DocField", @@ -190,7 +179,6 @@ }, { "oldfieldtype": "Select", - "colour": "White:FFF", "doctype": "DocField", "label": "Lead Type", "oldfieldname": "type", @@ -202,7 +190,6 @@ }, { "oldfieldtype": "Text", - "colour": "White:FFF", "doctype": "DocField", "label": "Remark", "oldfieldname": "remark", @@ -220,7 +207,6 @@ }, { "oldfieldtype": "Data", - "colour": "White:FFF", "doctype": "DocField", "label": "Phone", "oldfieldname": "contact_no", @@ -260,7 +246,6 @@ "print_hide": 1, "description": "To manage Territory, click here", "oldfieldtype": "Link", - "colour": "White:FFF", "doctype": "DocField", "label": "Territory", "oldfieldname": "territory", @@ -313,17 +298,15 @@ "doctype": "DocField", "label": "Country", "oldfieldname": "country", - "trigger": "Client", + "options": "link:Country", "fieldname": "country", "fieldtype": "Select", "reqd": 0, - "options": "link:Country", "permlevel": 0 }, { "print_hide": 1, "oldfieldtype": "Select", - "colour": "White:FFF", "doctype": "DocField", "label": "State", "oldfieldname": "state", @@ -344,7 +327,6 @@ }, { "oldfieldtype": "Section Break", - "colour": "White:FFF", "doctype": "DocField", "label": "More Info", "fieldname": "more_info", @@ -436,7 +418,6 @@ "description": "Your sales person who will contact the lead in future", "permlevel": 0, "oldfieldtype": "Link", - "colour": "White:FFF", "allow_on_submit": 0, "doctype": "DocField", "label": "Next Contact By", @@ -453,7 +434,6 @@ "description": "Your sales person will get a reminder on this date to contact the lead", "no_copy": 1, "oldfieldtype": "Date", - "colour": "White:FFF", "allow_on_submit": 0, "doctype": "DocField", "label": "Next Contact Date", @@ -470,7 +450,6 @@ "description": "Date on which the lead was last contacted", "no_copy": 1, "oldfieldtype": "Date", - "colour": "White:FFF", "doctype": "DocField", "label": "Last Contact Date", "oldfieldname": "last_contact_date", @@ -481,7 +460,6 @@ }, { "oldfieldtype": "Link", - "colour": "White:FFF", "doctype": "DocField", "label": "Company", "oldfieldname": "company", @@ -563,21 +541,5 @@ "role": "Sales User", "cancel": 0, "permlevel": 1 - }, - { - "create": 1, - "doctype": "DocPerm", - "write": 1, - "role": "System Manager", - "cancel": 0, - "permlevel": 0 - }, - { - "create": 1, - "doctype": "DocPerm", - "write": 1, - "role": "Guest", - "cancel": 0, - "permlevel": 0 } ] \ No newline at end of file diff --git a/selling/doctype/lead/lead_list.js b/selling/doctype/lead/lead_list.js index cfbfc36d679..3b2f53a57ff 100644 --- a/selling/doctype/lead/lead_list.js +++ b/selling/doctype/lead/lead_list.js @@ -16,7 +16,10 @@ wn.doclistviews['Lead'] = wn.views.ListView.extend({ if(data.status=='Interested') { data.label_type = 'success' } - else if(['Open', 'Attempted to Contact', 'Contacted', 'Contact in Future'].indexOf(data.status)!=-1) { + if(data.status=="Open") { + data.label_type = "important" + } + else if(['Attempted to Contact', 'Contacted', 'Contact in Future'].indexOf(data.status)!=-1) { data.label_type = 'info' } data.status_html = repl('%(status)s', data); diff --git a/setup/doctype/sales_email_settings/__init__.py b/setup/doctype/sales_email_settings/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/setup/doctype/sales_email_settings/sales_applicant_list.js b/setup/doctype/sales_email_settings/sales_applicant_list.js new file mode 100644 index 00000000000..0a75b89360b --- /dev/null +++ b/setup/doctype/sales_email_settings/sales_applicant_list.js @@ -0,0 +1,12 @@ +// For license information, please see license.txt + +cur_frm.cscript = { + refresh: function(doc) { + cur_frm.set_intro(""); + if(doc.extract_emails) { + cur_frm.set_intro(wn._("Active: Will extract emails from ") + doc.email_id); + } else { + cur_frm.set_intro(wn._("Not Active")); + } + } +} \ No newline at end of file diff --git a/setup/doctype/sales_email_settings/sales_email_settings.py b/setup/doctype/sales_email_settings/sales_email_settings.py new file mode 100644 index 00000000000..b09cefd5e2a --- /dev/null +++ b/setup/doctype/sales_email_settings/sales_email_settings.py @@ -0,0 +1,17 @@ +# For license information, please see license.txt + +from __future__ import unicode_literals +import webnotes +from webnotes import _ +from webnotes.utils import cint + +class DocType: + def __init__(self, d, dl): + self.doc, self.doclist = d, dl + + def validate(self): + if cint(self.doc.extract_emails) and not (self.doc.email_id and self.doc.host and \ + self.doc.username and self.doc.password): + + webnotes.msgprint(_("""Host, Email and Password required if emails are to be pulled"""), + raise_exception=True) \ No newline at end of file diff --git a/setup/doctype/sales_email_settings/sales_email_settings.txt b/setup/doctype/sales_email_settings/sales_email_settings.txt new file mode 100644 index 00000000000..d8042e970fe --- /dev/null +++ b/setup/doctype/sales_email_settings/sales_email_settings.txt @@ -0,0 +1,89 @@ +[ + { + "owner": "Administrator", + "docstatus": 0, + "creation": "2013-01-16 10:25:26", + "modified_by": "Administrator", + "modified": "2013-01-16 10:25:26" + }, + { + "issingle": 1, + "description": "Email settings to extract Leads from sales email id e.g. \"sales@example.com\"", + "doctype": "DocType", + "module": "Setup", + "name": "__common__" + }, + { + "name": "__common__", + "parent": "Sales Email Settings", + "doctype": "DocField", + "parenttype": "DocType", + "permlevel": 0, + "parentfield": "fields" + }, + { + "parent": "Sales Email Settings", + "read": 1, + "name": "__common__", + "create": 1, + "doctype": "DocPerm", + "write": 1, + "parenttype": "DocType", + "role": "System Manager", + "permlevel": 0, + "parentfield": "permissions" + }, + { + "name": "Sales Email Settings", + "doctype": "DocType" + }, + { + "description": "Email settings to extract Leads from sales email id e.g. \"sales@example.com\"", + "doctype": "DocField", + "label": "POP3 Mail Settings", + "fieldname": "pop3_mail_settings", + "fieldtype": "Section Break" + }, + { + "description": "Check to activate", + "doctype": "DocField", + "label": "Extract Emails", + "fieldname": "extract_emails", + "fieldtype": "Check" + }, + { + "description": "Email Id where a job applicant will email e.g. \"jobs@example.com\"", + "doctype": "DocField", + "label": "Email Id", + "fieldname": "email_id", + "fieldtype": "Data" + }, + { + "description": "POP3 server e.g. (pop.gmail.com)", + "doctype": "DocField", + "label": "Host", + "fieldname": "host", + "fieldtype": "Data" + }, + { + "doctype": "DocField", + "label": "Use SSL", + "fieldname": "use_ssl", + "fieldtype": "Check" + }, + { + "doctype": "DocField", + "label": "Username", + "fieldname": "username", + "fieldtype": "Data" + }, + { + "doctype": "DocField", + "label": "Password", + "fieldname": "password", + "fieldtype": "Password" + }, + { + "doctype": "DocPerm" + } +] \ No newline at end of file diff --git a/startup/open_count.py b/startup/open_count.py index e0767c318d0..a5fcc77559f 100644 --- a/startup/open_count.py +++ b/startup/open_count.py @@ -17,6 +17,7 @@ queries = { "Purchase Invoice": {"docstatus":0}, "Leave Application": {"status":"Open"}, "Expense Claim": {"approval_status":"Draft"}, + "Job Applicant": {"status":"Open"}, "Purchase Receipt": {"docstatus":0}, "Delivery Note": {"docstatus":0}, "Stock Entry": {"docstatus":0}, diff --git a/startup/schedule_handlers.py b/startup/schedule_handlers.py index ab53b211edb..b1ce1836c84 100644 --- a/startup/schedule_handlers.py +++ b/startup/schedule_handlers.py @@ -31,8 +31,10 @@ def execute_all(): from hr.doctype.job_applicant.get_job_applications import get_job_applications run_fn(get_job_applications) - - # bulk email + + from selling.doctype.lead.get_leads import get_leads + run_fn(get_job_applications) + from webnotes.utils.email_lib.bulk import flush run_fn(flush) diff --git a/startup/startup.py b/startup/startup.py index 15ea491b135..c74c5962354 100644 --- a/startup/startup.py +++ b/startup/startup.py @@ -29,38 +29,39 @@ def get_unread_messages(): def get_open_support_tickets(): """Returns a count of open support tickets""" - from webnotes.utils import cint open_support_tickets = webnotes.conn.sql("""\ SELECT COUNT(*) FROM `tabSupport Ticket` WHERE status = 'Open'""") - return open_support_tickets and cint(open_support_tickets[0][0]) or 0 + return open_support_tickets[0][0] def get_open_tasks(): """Returns a count of open tasks""" - from webnotes.utils import cint return webnotes.conn.sql("""\ SELECT COUNT(*) FROM `tabTask` WHERE status = 'Open'""")[0][0] def get_things_todo(): """Returns a count of incomplete todos""" - from webnotes.utils import cint incomplete_todos = webnotes.conn.sql("""\ SELECT COUNT(*) FROM `tabToDo` WHERE IFNULL(checked, 0) = 0 AND (owner = %s or assigned_by=%s)""", (webnotes.session.user, webnotes.session.user)) - return incomplete_todos and cint(incomplete_todos[0][0]) or 0 + return incomplete_todos[0][0] def get_todays_events(): """Returns a count of todays events in calendar""" - from webnotes.utils import nowdate, cint + from webnotes.utils import nowdate todays_events = webnotes.conn.sql("""\ SELECT COUNT(*) FROM `tabEvent` WHERE owner = %s AND event_type != 'Cancel' AND event_date = %s""", ( - webnotes.session.get('user'), nowdate())) - return todays_events and cint(todays_events[0][0]) or 0 + webnotes.session.user, nowdate())) + return todays_events[0][0] + +def get_open_leads(): + return webnotes.conn.sql("""select count(*) from tabLead + where status='Open'""")[0][0] def get_unanswered_questions(): return len(filter(lambda d: d[0]==0, @@ -75,5 +76,6 @@ def get_global_status_messages(arg=None): 'things_todo': get_things_todo(), 'todays_events': get_todays_events(), 'open_tasks': get_open_tasks(), - 'unanswered_questions': get_unanswered_questions() + 'unanswered_questions': get_unanswered_questions(), + 'open_leads': get_open_leads() } diff --git a/support/doctype/support_ticket/support_ticket.js b/support/doctype/support_ticket/support_ticket.js index bbaf95b523b..9abdff074aa 100644 --- a/support/doctype/support_ticket/support_ticket.js +++ b/support/doctype/support_ticket/support_ticket.js @@ -53,7 +53,7 @@ $.extend(cur_frm.cscript, { var sortfn = function (a, b) { return (b.creation > a.creation) ? 1 : -1; } comm_list = comm_list.sort(sortfn); - if(!comm_list.length || (comm_list[0].sender != doc.raised_by)) { + if(!comm_list.length || (comm_list[comm_list.length - 1].sender != doc.raised_by)) { comm_list.push({ "sender": doc.raised_by, "creation": doc.creation, diff --git a/website/__init__.py b/website/__init__.py index 909a9367689..aace68bb648 100644 --- a/website/__init__.py +++ b/website/__init__.py @@ -6,34 +6,6 @@ install_docs = [ import webnotes -max_tickets_per_hour = 200 - -@webnotes.whitelist(allow_guest=True) -def send_message(): - from webnotes.model.doc import Document - - d = Document('Support Ticket') - d.subject = webnotes.form_dict.get('subject', 'Website Query') - d.description = webnotes.form_dict.get('message') - d.raised_by = webnotes.form_dict.get('sender') - d.status = webnotes.form_dict.get("status") or "Open" - - if not d.description: - webnotes.response["message"] = 'Please write something' - return - - if not d.raised_by: - webnotes.response["message"] = 'Email Id Required' - return - - # guest method, cap max writes per hour - if webnotes.conn.sql("""select count(*) from `tabSupport Ticket` - where TIMEDIFF(NOW(), modified) < '01:00:00'""")[0][0] > max_tickets_per_hour: - webnotes.response["message"] = "Sorry: we believe we have received an unreasonably high number of requests of this kind. Please try later" - return - - d.save() - webnotes.response["message"] = 'Thank You' def get_site_address(): from webnotes.utils import get_request_site_address diff --git a/website/helpers/contact.py b/website/helpers/contact.py new file mode 100644 index 00000000000..df4510fa391 --- /dev/null +++ b/website/helpers/contact.py @@ -0,0 +1,63 @@ +# ERPNext - web based ERP (http://erpnext.com) +# Copyright (C) 2012 Web Notes Technologies Pvt Ltd +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import unicode_literals + +import webnotes +from core.doctype.communication.communication import make + +max_communications_per_hour = 300 + +@webnotes.whitelist(allow_guest=True) +def send_message(subject="Website Query", message="", sender="", status="Open"): + if not message: + webnotes.response["message"] = 'Please write something' + return + + if not sender: + webnotes.response["message"] = 'Email Id Required' + return + + # make lead / communication + + name = webnotes.conn.get_value("Lead", {"email_id": sender}, "name") + if name: + lead = webnotes.model_wrapper("Lead", name) + lead.doc.status = "Open" + lead.ignore_permissions = True + lead.save() + else: + lead = webnotes.model_wrapper({ + "doctype":"Lead", + "lead_name": sender, + "email_id": sender, + "status": "Open", + "source": "Website" + }) + lead.ignore_permissions = True + lead.insert() + + make(content=message, sender=sender, + doctype="Lead", name=lead.doc.name, lead=lead.doc.name) + + + # guest method, cap max writes per hour + if webnotes.conn.sql("""select count(*) from `tabCommunication` + where TIMEDIFF(NOW(), modified) < '01:00:00'""")[0][0] > max_communications_per_hour: + webnotes.response["message"] = "Sorry: we believe we have received an unreasonably high number of requests of this kind. Please try later" + return + + webnotes.response["message"] = 'Thank You' \ No newline at end of file From 83f51819870238a9a5c63d7d43e682517075b731 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 16 Jan 2013 11:35:09 +0530 Subject: [PATCH 2/6] added automatic lead creation for sales email id --- .../{sales_applicant_list.js => sales_email_settings.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename setup/doctype/sales_email_settings/{sales_applicant_list.js => sales_email_settings.js} (100%) diff --git a/setup/doctype/sales_email_settings/sales_applicant_list.js b/setup/doctype/sales_email_settings/sales_email_settings.js similarity index 100% rename from setup/doctype/sales_email_settings/sales_applicant_list.js rename to setup/doctype/sales_email_settings/sales_email_settings.js From 8d3fe553e578ca4dff1600e90d8570deaffeb461 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 16 Jan 2013 12:19:28 +0530 Subject: [PATCH 3/6] added automatic lead creation for sales email id --- hr/doctype/job_applicant/job_applicant.py | 3 + public/js/modules.js | 118 +--------------------- selling/doctype/lead/lead.py | 3 + 3 files changed, 7 insertions(+), 117 deletions(-) diff --git a/hr/doctype/job_applicant/job_applicant.py b/hr/doctype/job_applicant/job_applicant.py index b4db3c07f71..da722fbff4b 100644 --- a/hr/doctype/job_applicant/job_applicant.py +++ b/hr/doctype/job_applicant/job_applicant.py @@ -10,6 +10,9 @@ class DocType(TransactionBase): def onload(self): self.add_communication_list() + + def get_sender(self, comm): + return webnotes.conn.get_value('Jobs Email Settings',None,'email_id') def on_communication_sent(self, comm): webnotes.conn.set(self.doc, 'status', 'Replied') diff --git a/public/js/modules.js b/public/js/modules.js index 5c572dda8ed..5fcd64b5a3e 100644 --- a/public/js/modules.js +++ b/public/js/modules.js @@ -97,120 +97,4 @@ $.extend(wn.modules, { }); -wn.provide('erpnext.module_page'); - -erpnext.module_page.setup_page = function(module, wrapper) { - erpnext.module_page.hide_links(wrapper); - erpnext.module_page.make_list(module, wrapper); - $(wrapper).find("a[title]").tooltip({ - delay: { show: 500, hide: 100 } - }); - wrapper.appframe.add_home_breadcrumb(); - wrapper.appframe.add_breadcrumb(wn.modules[module].icon); -} - -// hide list links where the user does -// not have read permissions - -erpnext.module_page.hide_links = function(wrapper) { - function replace_link(link) { - var txt = $(link).text(); - $(link).parent().css('color', '#999'); - $(link).replaceWith('' - +txt+''); - } - - // lists - $(wrapper).find('[href*="List/"]').each(function() { - var href = $(this).attr('href'); - var dt = href.split('/')[1]; - if(wn.boot.profile.all_read.indexOf(dt)==-1) { - replace_link(this); - } - }); - - // reports - $(wrapper).find('[data-doctype]').each(function() { - var dt = $(this).attr('data-doctype'); - if(wn.boot.profile.all_read.indexOf(dt)==-1) { - replace_link(this); - } - }); - - // single (forms) - $(wrapper).find('[href*="Form/"]').each(function() { - var href = $(this).attr('href'); - var dt = href.split('/')[1]; - if(wn.boot.profile.all_read.indexOf(dt)==-1) { - replace_link(this); - } - }); - - // pages - $(wrapper).find('[data-role]').each(function() { - // can define multiple roles - var data_roles = $.map($(this).attr("data-role").split(","), function(role) { - return role.trim(); }); - if(!has_common(user_roles, ["System Manager"].concat(data_roles))) { - var html = $(this).html(); - $(this).parent().css('color', '#999'); - $(this).replaceWith(''+html+''); - } - }); -} - -// make list of reports - -erpnext.module_page.make_list = function(module, wrapper) { - // make project listing - var $w = $(wrapper).find('.reports-list'); - var $parent1 = $('
').appendTo($w); - var $parent2 = $('
').appendTo($w); - - wrapper.list1 = new wn.ui.Listing({ - parent: $parent1, - method: 'utilities.get_sc_list', - render_row: function(row, data) { - if(!data.parent_doc_type) data.parent_doc_type = data.doc_type; - $(row).html(repl('\ - %(criteria_name)s', data)) - }, - args: { module: module }, - no_refresh: true, - callback: function(r) { - erpnext.module_page.hide_links($parent1) - } - }); - wrapper.list1.run(); - - wrapper.list2 = new wn.ui.Listing({ - parent: $parent2, - method: 'utilities.get_report_list', - render_row: function(row, data) { - data.report_type = data.is_query_report - ? "query-report" - : repl("Report2/%(ref_doctype)s", data) - - $(row).html(repl('\ - %(name)s', data)) - }, - args: { module: module }, - no_refresh: true, - callback: function(r) { - erpnext.module_page.hide_links($parent2) - } - }); - wrapper.list2.run(); - - // show link to all reports - $parent1.find('.list-toolbar-wrapper') - .prepend(""); - $parent2.find('.list-toolbar-wrapper') - .prepend(""); -} \ No newline at end of file +wn.provide('erpnext.module_page'); \ No newline at end of file diff --git a/selling/doctype/lead/lead.py b/selling/doctype/lead/lead.py index 13d1714830b..dd24ff696ca 100644 --- a/selling/doctype/lead/lead.py +++ b/selling/doctype/lead/lead.py @@ -80,6 +80,9 @@ class DocType(TransactionBase): def on_communication_sent(self, comm): webnotes.conn.set(self.doc, 'status', 'Replied') + def get_sender(self, comm): + return webnotes.conn.get_value('Sales Email Settings',None,'email_id') + def on_trash(self): webnotes.conn.sql("""delete from tabCommunication where lead=%s""", self.doc.name) From 1a0b27ee7bf61114beaa8498dbc8bcbe4983f4e4 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 16 Jan 2013 12:36:07 +0530 Subject: [PATCH 4/6] added automatic lead creation for sales email id --- startup/schedule_handlers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/startup/schedule_handlers.py b/startup/schedule_handlers.py index b1ce1836c84..c710c54086c 100644 --- a/startup/schedule_handlers.py +++ b/startup/schedule_handlers.py @@ -33,7 +33,7 @@ def execute_all(): run_fn(get_job_applications) from selling.doctype.lead.get_leads import get_leads - run_fn(get_job_applications) + run_fn(get_leads) from webnotes.utils.email_lib.bulk import flush run_fn(flush) From 8f3916dd54822a6e6f358ed49ad9d60797432f4f Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 16 Jan 2013 12:50:55 +0530 Subject: [PATCH 5/6] add job applicant, sales and jobs email settings to module help --- hr/doctype/job_applicant/job_applicant.js | 7 +++++++ hr/page/hr_home/hr_home.js | 10 ++++++++++ selling/doctype/lead/lead.js | 6 ++++++ setup/page/setup/setup.js | 12 ++++++++++++ 4 files changed, 35 insertions(+) diff --git a/hr/doctype/job_applicant/job_applicant.js b/hr/doctype/job_applicant/job_applicant.js index a63f8335e30..c30125059ee 100644 --- a/hr/doctype/job_applicant/job_applicant.js +++ b/hr/doctype/job_applicant/job_applicant.js @@ -1,6 +1,13 @@ // For license information, please see license.txt cur_frm.cscript = { + onload: function(doc, dt, dn) { + if(in_list(user_roles,'System Manager')) { + cur_frm.page_layout.footer.help_area.innerHTML = '
\ +

Jobs Email Settings
\ + Automatically extract Job Applicants from a mail box e.g. "jobs@example.com"

'; + } + }, refresh: function(doc) { cur_frm.cscript.make_listing(doc); }, diff --git a/hr/page/hr_home/hr_home.js b/hr/page/hr_home/hr_home.js index c2c5cd9f883..e26cbd3ecc0 100644 --- a/hr/page/hr_home/hr_home.js +++ b/hr/page/hr_home/hr_home.js @@ -31,6 +31,11 @@ wn.module_page["HR"] = [ description: wn._("Performance appraisal."), doctype:"Appraisal" }, + { + label: wn._("Job Applicant"), + description: wn._("Applicant for a Job (extracted from jobs email)."), + doctype:"Job Applicant" + }, ] }, { @@ -108,6 +113,11 @@ wn.module_page["HR"] = [ title: wn._("Employee Setup"), icon: "icon-cog", items: [ + { + label: wn._("Job Opening"), + description: wn._("Opening for a Job."), + doctype:"Job Opening" + }, { "label": wn._("Employment Type"), "description": wn._("Type of employment master."), diff --git a/selling/doctype/lead/lead.js b/selling/doctype/lead/lead.js index 9dd64de25db..d8d322d3249 100644 --- a/selling/doctype/lead/lead.js +++ b/selling/doctype/lead/lead.js @@ -48,6 +48,12 @@ cur_frm.cscript.onload = function(doc, cdt, cdn) { if(cur_frm.fields_dict.contact_by.df.options.match(/^Profile/)) { cur_frm.fields_dict.contact_by.get_query = erpnext.utils.profile_query; } + + if(in_list(user_roles,'System Manager')) { + cur_frm.page_layout.footer.help_area.innerHTML = '
\ +

Sales Email Settings
\ + Automatically extract Leads from a mail box e.g. "sales@example.com"

'; + } } cur_frm.cscript.refresh_custom_buttons = function(doc) { diff --git a/setup/page/setup/setup.js b/setup/page/setup/setup.js index f9c8796fe3d..aa75893221a 100644 --- a/setup/page/setup/setup.js +++ b/setup/page/setup/setup.js @@ -116,6 +116,18 @@ wn.module_page["Setup"] = [ label: wn._("Email Settings"), "description":wn._("Out going mail server and support ticket mailbox") }, + { + "route":"Form/Sales Email Settings", + doctype:"Sales Email Settings", + label: wn._("Sales Email Settings"), + "description":wn._("Extract Leads from sales email id e.g. sales@example.com") + }, + { + "route":"Form/Jobs Email Settings", + doctype:"Jobs Email Settings", + label: wn._("Jobs Email Settings"), + "description":wn._("Extract Job Applicant from jobs email id e.g. jobs@example.com") + }, { "route":"Form/Notification Control/Notification Control", doctype:"Notification Control", From 23ac4369335513076275618261f4df74c0bed63d Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 16 Jan 2013 12:54:24 +0530 Subject: [PATCH 6/6] add job applicant, sales and jobs email settings to module help --- home/page/latest_updates/latest_updates.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/home/page/latest_updates/latest_updates.js b/home/page/latest_updates/latest_updates.js index c576e8866af..e5b0547d18a 100644 --- a/home/page/latest_updates/latest_updates.js +++ b/home/page/latest_updates/latest_updates.js @@ -1,4 +1,8 @@ erpnext.updates = [ + ["16th January, 2013", [ + "Job Applicant: Track Job Applicants and extract them from a mailbox like 'jobs@example.com'. See Jobs Email Settings.", + "Extract leads: Extract Leads from a mailbox like 'sales@example.com'. See Sales Email Settings.", + ]], ["14th January, 2013", [ "Stock Reconciliation: Ability to update Valuation Rate", "Time Field: Added Datetime and new Time Picker",