From 7a3d418aa5dded1da582547f02cd9228676e936a Mon Sep 17 00:00:00 2001 From: robert schouten Date: Fri, 13 Jan 2017 17:21:09 +0800 Subject: [PATCH] Email inbox based off of frappe email inbox pr (#7308) * email inbox * inbox fixes * separate email-inbox to pass tests * separate email-inbox to pass tests * fix import * add more info to contact and address --- erpnext/hooks.py | 3 + erpnext/public/js/templates/address_list.html | 2 +- erpnext/public/js/templates/contact_list.html | 4 +- .../utilities/doctype/address/address.json | 31 ++- erpnext/utilities/doctype/address/address.py | 6 +- erpnext/utilities/doctype/contact/__init__.py | 38 ++++ erpnext/utilities/doctype/contact/contact.js | 36 +++- .../utilities/doctype/contact/contact.json | 31 ++- erpnext/utilities/doctype/contact/contact.py | 85 +++++++- .../doctype/organisation/__init__.py | 0 .../doctype/organisation/organisation.js | 14 ++ .../doctype/organisation/organisation.json | 199 ++++++++++++++++++ .../doctype/organisation/organisation.py | 13 ++ .../doctype/organisation/test_organisation.py | 12 ++ 14 files changed, 465 insertions(+), 9 deletions(-) create mode 100644 erpnext/utilities/doctype/organisation/__init__.py create mode 100644 erpnext/utilities/doctype/organisation/organisation.js create mode 100644 erpnext/utilities/doctype/organisation/organisation.json create mode 100644 erpnext/utilities/doctype/organisation/organisation.py create mode 100644 erpnext/utilities/doctype/organisation/test_organisation.py diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 59a1e2b2b6..25444d9988 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -165,6 +165,9 @@ doc_events = { "Address": { "validate": "erpnext.shopping_cart.cart.set_customer_in_address" }, + "Communication":{ + "after_insert":"erpnext.utilities.doctype.contact.match_email_to_contact" + }, # bubble transaction notification on master ('Opportunity', 'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice', diff --git a/erpnext/public/js/templates/address_list.html b/erpnext/public/js/templates/address_list.html index 6ee7d782bf..f9a317f173 100644 --- a/erpnext/public/js/templates/address_list.html +++ b/erpnext/public/js/templates/address_list.html @@ -2,7 +2,7 @@
{% for(var i=0, l=addr_list.length; i - {%= i+1 %}. {%= addr_list[i].address_type %} + {%= i+1 %}. {%= addr_list[i].address_type!="Other" ? addr_list[i].address_type : addr_list[i].address_title %} {% if(addr_list[i].is_primary_address) { %} ({%= __("Primary") %}){% } %} {% if(addr_list[i].is_shipping_address) { %} diff --git a/erpnext/public/js/templates/contact_list.html b/erpnext/public/js/templates/contact_list.html index ddd3e5292d..765ddf802d 100644 --- a/erpnext/public/js/templates/contact_list.html +++ b/erpnext/public/js/templates/contact_list.html @@ -8,7 +8,9 @@ {% if(contact_list[i].is_primary_contact) { %} ({%= __("Primary") %}) {% } %} - + {% if(contact_list[i].designation){ %} + – {%= contact_list[i].designation %} + {% } %} {%= __("Edit") %} diff --git a/erpnext/utilities/doctype/address/address.json b/erpnext/utilities/doctype/address/address.json index 329e05f587..70564acd24 100644 --- a/erpnext/utilities/doctype/address/address.json +++ b/erpnext/utilities/doctype/address/address.json @@ -595,6 +595,35 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "organisation", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Organisation", + "length": 0, + "no_copy": 0, + "options": "Organisation", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -750,7 +779,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-11-07 05:47:06.911933", + "modified": "2016-12-22 13:49:22.968498", "modified_by": "Administrator", "module": "Utilities", "name": "Address", diff --git a/erpnext/utilities/doctype/address/address.py b/erpnext/utilities/doctype/address/address.py index 2952531f01..2c84a9df55 100644 --- a/erpnext/utilities/doctype/address/address.py +++ b/erpnext/utilities/doctype/address/address.py @@ -19,7 +19,7 @@ class Address(Document): def autoname(self): if not self.address_title: self.address_title = self.customer \ - or self.supplier or self.sales_partner or self.lead + or self.supplier or self.sales_partner or self.lead or self.organisation if self.address_title: self.name = (cstr(self.address_title).strip() + "-" + cstr(self.address_type).strip()) @@ -30,7 +30,7 @@ class Address(Document): throw(_("Address Title is mandatory.")) def validate(self): - self.link_fields = ("customer", "supplier", "sales_partner", "lead") + self.link_fields = ("customer", "supplier", "sales_partner", "lead", "organisation") self.link_address() self.validate_primary_address() self.validate_shipping_address() @@ -83,7 +83,7 @@ class Address(Document): frappe.throw(_("Remove reference of customer, supplier, sales partner and lead, as it is your company address")) def _unset_other(self, is_address_type): - for fieldname in ["customer", "supplier", "sales_partner", "lead"]: + for fieldname in ["customer", "supplier", "sales_partner", "lead", "organisation"]: if self.get(fieldname): frappe.db.sql("""update `tabAddress` set `%s`=0 where `%s`=%s and name!=%s""" % (is_address_type, fieldname, "%s", "%s"), (self.get(fieldname), self.name)) diff --git a/erpnext/utilities/doctype/contact/__init__.py b/erpnext/utilities/doctype/contact/__init__.py index baffc48825..e4075fda8e 100644 --- a/erpnext/utilities/doctype/contact/__init__.py +++ b/erpnext/utilities/doctype/contact/__init__.py @@ -1 +1,39 @@ from __future__ import unicode_literals +import frappe + +def match_email_to_contact(doc,method=None): + if doc.communication_type == "Communication": + origin_contact = frappe.db.sql( + "select name, email_id, supplier, supplier_name, customer, customer_name, user, organisation from `tabContact` where email_id <>''", + as_dict=1) + for comm in origin_contact: + if comm.email_id: + if (doc.sender and doc.sent_or_received == "Received" and doc.sender.find(comm.email_id) > -1) or ( + doc.recipients and doc.sent_or_received == "Sent" and doc.recipients.find( + comm.email_id) > -1): + if sum(1 for x in [comm.supplier, comm.customer, comm.user, comm.organisation] if x) > 1: + doc.db_set("timeline_doctype", "Contact") + doc.db_set("timeline_name", comm.name) + doc.db_set("timeline_label", doc.name) + + elif comm.supplier: + doc.db_set("timeline_doctype", "Supplier") + doc.db_set("timeline_name", comm.supplier) + doc.db_set("timeline_label", comm.supplier_name) + + elif comm.customer: + doc.db_set("timeline_doctype", "Customer") + doc.db_set("timeline_name", comm.customer) + doc.db_set("timeline_label", comm.customer_name) + elif comm.user: + doc.db_set("timeline_doctype", "User") + doc.db_set("timeline_name", comm.user) + doc.db_set("timeline_label", comm.user) + elif comm.organisation: + doc.db_set("timeline_doctype", "Organisation") + doc.db_set("timeline_name", comm.organisation) + doc.db_set("timeline_label", comm.organisation) + else: + doc.db_set("timeline_doctype", None) + doc.db_set("timeline_name", None) + doc.db_set("timeline_label", None) diff --git a/erpnext/utilities/doctype/contact/contact.js b/erpnext/utilities/doctype/contact/contact.js index 07d9d6f90b..db25e99dea 100644 --- a/erpnext/utilities/doctype/contact/contact.js +++ b/erpnext/utilities/doctype/contact/contact.js @@ -5,6 +5,21 @@ cur_frm.email_field = "email_id"; frappe.ui.form.on("Contact", { + onload:function(frm){ + if(frappe.route_titles["update_contact"]) + { + frappe.confirm("change email address from "+cur_frm.doc.email_id+ " to "+frappe.route_titles["update_contact"]["email_id"] + ,function(){ + cur_frm.doc.email_id = frappe.route_titles["update_contact"]["email_id"]; + cur_frm.refresh(); + cur_frm.dirty(); + delete frappe.route_titles["update_contact"]; + },function(){ + delete frappe.route_titles["update_contact"]; + }) + + } + }, refresh: function(frm) { if(!frm.doc.user && !frm.is_new() && frm.perm[0].write) { frm.add_custom_button(__("Invite as User"), function() { @@ -27,5 +42,24 @@ frappe.ui.form.on("Contact", { if(name && locals[doctype] && locals[doctype][name]) frappe.model.remove_from_locals(doctype, name); }); + var fieldlist = ["supplier","customer","user","organisation"] + if(frappe.route_titles["create_contact"]==1&&!($.map(fieldlist,function(v){return frm.doc[v]?true:false}).indexOf(true)!=-1)){ + $.each(fieldlist,function(i,v){ + cur_frm.set_df_property(v,"reqd",1); + }) + + } else { + $.each(fieldlist,function(i,v){ + cur_frm.set_df_property(v,"reqd",0); + }) + } + }, + after_save:function(frm){ + if (frappe.route_titles["create_contact"]) + { + delete frappe.route_titles["create_contact"] + frappe.set_route("email_inbox"); + frappe.pages['email_inbox'].Inbox.run() + } } -}); +}); \ No newline at end of file diff --git a/erpnext/utilities/doctype/contact/contact.json b/erpnext/utilities/doctype/contact/contact.json index 98061da23c..21046b7f57 100644 --- a/erpnext/utilities/doctype/contact/contact.json +++ b/erpnext/utilities/doctype/contact/contact.json @@ -469,6 +469,35 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "organisation", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Organisation", + "length": 0, + "no_copy": 0, + "options": "Organisation", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -653,7 +682,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-11-07 05:30:56.576752", + "modified": "2016-12-22 13:46:02.655141", "modified_by": "Administrator", "module": "Utilities", "name": "Contact", diff --git a/erpnext/utilities/doctype/contact/contact.py b/erpnext/utilities/doctype/contact/contact.py index be0f9858e9..03eb94cab1 100644 --- a/erpnext/utilities/doctype/contact/contact.py +++ b/erpnext/utilities/doctype/contact/contact.py @@ -15,7 +15,7 @@ class Contact(StatusUpdater): [cstr(self.get(f)).strip() for f in ["first_name", "last_name"]])) # concat party name if reqd - for fieldname in ("customer", "supplier", "sales_partner"): + for fieldname in ("customer", "supplier", "sales_partner", "organisation"): if self.get(fieldname): self.name = self.name + "-" + cstr(self.get(fieldname)).strip() break @@ -26,6 +26,7 @@ class Contact(StatusUpdater): self.set_user() if self.email_id: self.image = has_gravatar(self.email_id) + self.contact_update_communication_ref() def set_user(self): if not self.user and self.email_id: @@ -61,6 +62,88 @@ class Contact(StatusUpdater): frappe.db.sql("""update `tabIssue` set contact='' where contact=%s""", self.name) + def contact_update_communication_ref(self): + origin_communication = frappe.db.sql("select name, sender,recipients,sent_or_received from `tabCommunication`", + as_dict=1) + + if self.email_id: + self.email_id = self.email_id.lower() + comm = frappe._dict({"email_id": self.email_id, + "name": self.name, + "supplier": self.supplier, + "supplier_name": self.supplier_name, + "customer": self.customer, + "customer_name": self.customer_name, + "user": self.user, + "organisation": self.organisation + }) + for communication in origin_communication: + sender = communication.sender + recipients = communication.recipients + if comm.email_id: + if (sender and communication.sent_or_received == "Received" and sender.find( + comm.email_id) > -1) or ( + recipients and communication.sent_or_received == "Sent" and recipients.find( + comm.email_id) > -1): + if sum(1 for x in [comm.supplier, comm.customer, comm.user, comm.organisation] if x) > 1: + frappe.db.sql("""update `tabCommunication` + set timeline_doctype = %(timeline_doctype)s, + timeline_name = %(timeline_name)s, + timeline_label = %(timeline_label)s + where name = %(name)s""", { + "timeline_doctype": "Contact", + "timeline_name": comm.name, + "timeline_label": self.name, + "name": communication.name + }) + + elif comm.supplier: + frappe.db.sql("""update `tabCommunication` + set timeline_doctype = %(timeline_doctype)s, + timeline_name = %(timeline_name)s, + timeline_label = %(timeline_label)s + where name = %(name)s""", { + "timeline_doctype": "Supplier", + "timeline_name": comm.supplier, + "timeline_label": comm.supplier_name, + "name": communication.name + }) + + elif comm.customer: + + frappe.db.sql("""update `tabCommunication` + set timeline_doctype = %(timeline_doctype)s, + timeline_name = %(timeline_name)s, + timeline_label = %(timeline_label)s + where name = %(name)s""", { + "timeline_doctype": "Customer", + "timeline_name": comm.customer, + "timeline_label": comm.customer_name, + "name": communication.name + }) + elif comm.user: + frappe.db.sql("""update `tabCommunication` + set timeline_doctype = %(timeline_doctype)s, + timeline_name = %(timeline_name)s, + timeline_label = %(timeline_label)s + where name = %(name)s""", { + "timeline_doctype": "User", + "timeline_name": comm.user, + "timeline_label": comm.user, + "name": communication.name + }) + elif comm.organisation: + frappe.db.sql("""update `tabCommunication` + set timeline_doctype = %(timeline_doctype)s, + timeline_name = %(timeline_name)s, + timeline_label = %(timeline_label)s + where name = %(name)s""", { + "timeline_doctype": "Organisation", + "timeline_name": comm.organisation, + "timeline_label": comm.organisation, + "name": communication.name + }) + @frappe.whitelist() def invite_user(contact): contact = frappe.get_doc("Contact", contact) diff --git a/erpnext/utilities/doctype/organisation/__init__.py b/erpnext/utilities/doctype/organisation/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/utilities/doctype/organisation/organisation.js b/erpnext/utilities/doctype/organisation/organisation.js new file mode 100644 index 0000000000..2d3bb31bea --- /dev/null +++ b/erpnext/utilities/doctype/organisation/organisation.js @@ -0,0 +1,14 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Organisation', { + refresh: function(frm) { + frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal); + + if(!frm.doc.__islocal) { + erpnext.utils.render_address_and_contact(frm); + } else { + erpnext.utils.clear_address_and_contact(frm); + } + } +}); diff --git a/erpnext/utilities/doctype/organisation/organisation.json b/erpnext/utilities/doctype/organisation/organisation.json new file mode 100644 index 0000000000..9cbcc90cf9 --- /dev/null +++ b/erpnext/utilities/doctype/organisation/organisation.json @@ -0,0 +1,199 @@ +{ + "allow_copy": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:organisation_name", + "beta": 0, + "creation": "2016-08-22 11:08:27.151412", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 0, + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "organisation_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Name", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval:!doc.__islocal", + "fieldname": "address_contacts", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Address and Contact", + "length": 0, + "no_copy": 0, + "options": "icon-map-marker", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "address_html", + "fieldtype": "HTML", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Address HTML", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break1", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0, + "width": "50%" + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "contact_html", + "fieldtype": "HTML", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Contact HTML", + "length": 0, + "no_copy": 0, + "oldfieldtype": "HTML", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2016-12-23 08:33:29.997375", + "modified_by": "Administrator", + "module": "Utilities", + "name": "Organisation", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 1, + "is_custom": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Email User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/utilities/doctype/organisation/organisation.py b/erpnext/utilities/doctype/organisation/organisation.py new file mode 100644 index 0000000000..d04f226ea1 --- /dev/null +++ b/erpnext/utilities/doctype/organisation/organisation.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document +from erpnext.utilities.address_and_contact import load_address_and_contact + +class Organisation(Document): + def onload(self): + """Load address and contacts in `__onload`""" + load_address_and_contact(self, "organisation") \ No newline at end of file diff --git a/erpnext/utilities/doctype/organisation/test_organisation.py b/erpnext/utilities/doctype/organisation/test_organisation.py new file mode 100644 index 0000000000..499c63acbb --- /dev/null +++ b/erpnext/utilities/doctype/organisation/test_organisation.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +# test_records = frappe.get_test_records('Organisation') + +class TestOrganisation(unittest.TestCase): + pass