diff --git a/frappe/__init__.py b/frappe/__init__.py index b9adbd6aeb..cae99368a1 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -503,6 +503,10 @@ def get_meta(doctype, cached=True): import frappe.model.meta return frappe.model.meta.get_meta(doctype, cached=cached) +def get_meta_module(doctype): + import frappe.modules + return frappe.modules.load_doctype_module(doctype) + def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reload=False, ignore_permissions=False, flags=None): """Delete a document. Calls `frappe.model.delete_doc.delete_doc`. diff --git a/frappe/desk/doctype/todo/test_todo.py b/frappe/desk/doctype/todo/test_todo.py new file mode 100644 index 0000000000..2bbe82b8d0 --- /dev/null +++ b/frappe/desk/doctype/todo/test_todo.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('ToDo') + +class TestToDo(unittest.TestCase): + pass diff --git a/frappe/desk/doctype/todo/todo.json b/frappe/desk/doctype/todo/todo.json index 1baa47c378..bc15fd397e 100644 --- a/frappe/desk/doctype/todo/todo.json +++ b/frappe/desk/doctype/todo/todo.json @@ -160,6 +160,14 @@ "label": "Assigned By", "options": "User", "permlevel": 0 + }, + { + "fieldname": "sender", + "fieldtype": "Data", + "hidden": 1, + "label": "Sender", + "permlevel": 0, + "precision": "" } ], "hide_heading": 0, @@ -170,7 +178,7 @@ "in_dialog": 0, "issingle": 0, "max_attachments": 0, - "modified": "2015-02-05 05:11:48.421852", + "modified": "2015-05-04 07:44:46.567785", "modified_by": "Administrator", "module": "Desk", "name": "ToDo", diff --git a/frappe/desk/doctype/todo/todo.py b/frappe/desk/doctype/todo/todo.py index d791618166..1a62f3b851 100644 --- a/frappe/desk/doctype/todo/todo.py +++ b/frappe/desk/doctype/todo/todo.py @@ -8,11 +8,10 @@ import json from frappe.model.document import Document from frappe.utils import get_fullname -class ToDo(Document): - def set_subject(self, subject): - """Set description (called via incoming email)""" - self.description = subject +subject_field = "description" +sender_field = "sender" +class ToDo(Document): def validate(self): if self.is_new(): self.add_assign_comment(frappe._("Assigned to {0}").format(get_fullname(self.owner)), "Assigned") diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 248e269f7b..48add3c6c0 100644 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -5,11 +5,12 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import validate_email_add, cint +from frappe.utils import validate_email_add, cint, get_datetime, DATE_FORMAT from frappe.email.smtp import SMTPServer from frappe.email.receive import POP3Server, Email from poplib import error_proto import markdown2 +from dateutil.relativedelta import relativedelta from datetime import datetime, timedelta class EmailAccount(Document): @@ -157,6 +158,18 @@ class EmailAccount(Document): it will create a new parent transaction (e.g. Issue)""" in_reply_to = (email.mail.get("In-Reply-To") or "").strip(" <>") parent = None + + if self.append_to: + # set subject_field and sender_field + meta_module = frappe.get_meta_module(self.append_to) + meta = frappe.get_meta(self.append_to) + subject_field = getattr(meta_module, "subject_field", "subject") + if not meta.get_field(subject_field): + subject_field = None + sender_field = getattr(meta_module, "sender_field", "sender") + if not meta.get_field(sender_field): + sender_field = None + if in_reply_to: if "@" in in_reply_to: @@ -170,22 +183,30 @@ class EmailAccount(Document): parent = frappe.get_doc(parent.reference_doctype, parent.reference_name) + elif self.append_to and subject_field and sender_field: + # try and match by subject and sender + # if sent by same sender with same subject, + # append it to old coversation + + parent = frappe.db.get_all(self.append_to, filters={ + sender_field: email.from_email, + subject_field: ("like", "%{0}%".format(email.subject)), + "creation": (">", (get_datetime() - relativedelta(days=10)).strftime(DATE_FORMAT)) + }, fields="name") + + if parent: + parent = frappe.get_doc(self.append_to, parent[0].name) + if not parent and self.append_to: # no parent found, but must be tagged # insert parent type doc parent = frappe.new_doc(self.append_to) - if parent.meta.get_field("subject"): - parent.subject = email.subject - - if parent.meta.get_field("sender"): - parent.sender = email.from_email - - if hasattr(parent, "set_subject"): - parent.set_subject(email.subject) + if subject_field: + parent.set(subject_field, email.subject) - if hasattr(parent, "set_sender"): - parent.set_sender(email.from_email) + if sender_field: + parent.set(sender_field, email.from_email) parent.flags.ignore_mandatory = True diff --git a/frappe/email/doctype/email_account/test_email_account.py b/frappe/email/doctype/email_account/test_email_account.py index c0713dd2a4..f09ae9a0bd 100644 --- a/frappe/email/doctype/email_account/test_email_account.py +++ b/frappe/email/doctype/email_account/test_email_account.py @@ -108,7 +108,26 @@ class TestEmailAccount(unittest.TestCase): self.assertEquals(comm.reference_doctype, sent.doctype) self.assertEquals(comm.reference_name, sent.name) + def test_threading_by_subject(self): + frappe.db.sql("""delete from tabCommunication + where sender in ('test_sender@example.com', 'test@example.com')""") + + with open(os.path.join(os.path.dirname(__file__), "test_mails", "reply-2.raw"), "r") as f: + test_mails = [f.read()] + + with open(os.path.join(os.path.dirname(__file__), "test_mails", "reply-3.raw"), "r") as f: + test_mails.append(f.read()) + + # parse reply + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + email_account.receive(test_mails=test_mails) + + comm_list = frappe.get_all("Communication", filters={"sender":"test_sender@example.com"}, + fields=["name", "reference_doctype", "reference_name"]) + # both communications attached to the same reference + self.assertEquals(comm_list[0].reference_doctype, comm_list[1].reference_doctype) + self.assertEquals(comm_list[0].reference_name, comm_list[1].reference_name) diff --git a/frappe/email/doctype/email_account/test_mails/reply-2.raw b/frappe/email/doctype/email_account/test_mails/reply-2.raw new file mode 100644 index 0000000000..6a2e27fea5 --- /dev/null +++ b/frappe/email/doctype/email_account/test_mails/reply-2.raw @@ -0,0 +1,45 @@ +Return-Path: +Received: from [192.168.0.100] ([27.106.4.70]) + by mx.google.com with ESMTPSA id n15sm4041161pdj.34.2014.09.15.23.48.02 + for + (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); + Mon, 15 Sep 2014 23:48:04 -0700 (PDT) +From: Rushabh Mehta +X-Google-Original-From: Rushabh Mehta +Content-Type: multipart/alternative; boundary="Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040" +Mime-Version: 1.0 (Mac OS X Mail 7.3 \(1878.6\)) +Subject: weird subject +Date: Tue, 16 Sep 2014 12:17:58 +0530 +References: <54A4EFFA-AD17-456A-9851-9715574DF0C9@gmail.com> +To: Rushabh Mehta +X-Mailer: Apple Mail (2.1878.6) + + +--Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040 +Content-Transfer-Encoding: 7bit +Content-Type: text/plain; + charset=us-ascii + +test-reply-2 + + +On 16-Sep-2014, at 11:57 am, Rushabh Mehta wrote: + +> with attachment +> +> +> +> + + +--Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040 +Content-Transfer-Encoding: 7bit +Content-Type: text/html; + charset=us-ascii + +test-reply
+

On 16-Sep-2014, at 11:57 am, Rushabh Mehta <test_sender@example.com> wrote:

with attachment

+
<erpnext-conf-14.png>

+
+

+--Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040-- diff --git a/frappe/email/doctype/email_account/test_mails/reply-3.raw b/frappe/email/doctype/email_account/test_mails/reply-3.raw new file mode 100644 index 0000000000..fa1b2bbc5c --- /dev/null +++ b/frappe/email/doctype/email_account/test_mails/reply-3.raw @@ -0,0 +1,45 @@ +Return-Path: +Received: from [192.168.0.100] ([27.106.4.70]) + by mx.google.com with ESMTPSA id n15sm4041161pdj.34.2014.09.15.23.48.02 + for + (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); + Mon, 15 Sep 2014 23:48:04 -0700 (PDT) +From: Rushabh Mehta +X-Google-Original-From: Rushabh Mehta +Content-Type: multipart/alternative; boundary="Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040" +Mime-Version: 1.0 (Mac OS X Mail 7.3 \(1878.6\)) +Subject: Re: weird subject +Date: Tue, 16 Sep 2014 12:17:58 +0530 +References: <54A4EFFA-AD17-456A-9851-9715574DF0C9@gmail.com> +To: Rushabh Mehta +X-Mailer: Apple Mail (2.1878.6) + + +--Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040 +Content-Transfer-Encoding: 7bit +Content-Type: text/plain; + charset=us-ascii + +test-reply-3 + + +On 16-Sep-2014, at 11:57 am, Rushabh Mehta wrote: + +> with attachment +> +> +> +> + + +--Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040 +Content-Transfer-Encoding: 7bit +Content-Type: text/html; + charset=us-ascii + +test-reply
+

On 16-Sep-2014, at 11:57 am, Rushabh Mehta <test_sender@example.com> wrote:

with attachment

+
<erpnext-conf-14.png>

+
+

+--Apple-Mail=_C996D08F-7A29-4DA2-99B3-17133FA73040--