From fe816c385268552dccdf39f76e6235af70e8040c Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 8 Nov 2016 18:18:40 +0530 Subject: [PATCH 1/3] [fix] [wip] daily work summary --- erpnext/hooks.py | 3 +- .../hr/doctype/daily_work_summary/__init__.py | 0 .../daily_work_summary/daily_work_summary.js | 8 + .../daily_work_summary.json | 139 ++++++++++++ .../daily_work_summary/daily_work_summary.py | 45 ++++ .../test_daily_work_summary.py | 55 +++++ .../daily_work_summary_response/__init__.py | 0 .../daily_work_summary_response.json | 95 ++++++++ .../daily_work_summary_response.py | 10 + .../daily_work_summary_settings/__init__.py | 0 .../daily_work_summary_settings.js | 7 + .../daily_work_summary_settings.json | 203 ++++++++++++++++++ .../daily_work_summary_settings.py | 20 ++ .../__init__.py | 0 .../daily_work_summary_settings_company.json | 97 +++++++++ .../daily_work_summary_settings_company.py | 10 + .../v7_1/set_prefered_contact_email.py | 4 +- erpnext/setup/install.py | 2 +- 18 files changed, 694 insertions(+), 4 deletions(-) create mode 100644 erpnext/hr/doctype/daily_work_summary/__init__.py create mode 100644 erpnext/hr/doctype/daily_work_summary/daily_work_summary.js create mode 100644 erpnext/hr/doctype/daily_work_summary/daily_work_summary.json create mode 100644 erpnext/hr/doctype/daily_work_summary/daily_work_summary.py create mode 100644 erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py create mode 100644 erpnext/hr/doctype/daily_work_summary_response/__init__.py create mode 100644 erpnext/hr/doctype/daily_work_summary_response/daily_work_summary_response.json create mode 100644 erpnext/hr/doctype/daily_work_summary_response/daily_work_summary_response.py create mode 100644 erpnext/hr/doctype/daily_work_summary_settings/__init__.py create mode 100644 erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.js create mode 100644 erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.json create mode 100644 erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.py create mode 100644 erpnext/hr/doctype/daily_work_summary_settings_company/__init__.py create mode 100644 erpnext/hr/doctype/daily_work_summary_settings_company/daily_work_summary_settings_company.json create mode 100644 erpnext/hr/doctype/daily_work_summary_settings_company/daily_work_summary_settings_company.py diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 4a984704a1f..1cede9047fb 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -183,7 +183,8 @@ doc_events = { scheduler_events = { "hourly": [ - "erpnext.controllers.recurring_document.create_recurring_documents" + "erpnext.controllers.recurring_document.create_recurring_documents", + 'erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.trigger_emails' ], "daily": [ "erpnext.stock.reorder_item.reorder_item", diff --git a/erpnext/hr/doctype/daily_work_summary/__init__.py b/erpnext/hr/doctype/daily_work_summary/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.js b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.js new file mode 100644 index 00000000000..1ac173a8d3d --- /dev/null +++ b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.js @@ -0,0 +1,8 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Daily Work Summary', { + refresh: function(frm) { + + } +}); diff --git a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json new file mode 100644 index 00000000000..281a6abd169 --- /dev/null +++ b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json @@ -0,0 +1,139 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2016-11-08 04:58:20.001780", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "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, + "fieldname": "responses", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Responses", + "length": 0, + "no_copy": 0, + "options": "Daily Work Summary Response", + "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 + } + ], + "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-11-08 06:03:21.121442", + "modified_by": "Administrator", + "module": "HR", + "name": "Daily Work Summary", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 0, + "export": 0, + "if_owner": 0, + "import": 0, + "is_custom": 0, + "permlevel": 0, + "print": 0, + "read": 1, + "report": 0, + "role": "Employee", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 0 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "is_custom": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "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/hr/doctype/daily_work_summary/daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py new file mode 100644 index 00000000000..d3e92e9a74f --- /dev/null +++ b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py @@ -0,0 +1,45 @@ +# -*- 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 frappe import _ + +class DailyWorkSummary(Document): + def send_mails(self, settings): + '''Send emails to get daily work summary to all employees''' + frappe.sendmail(recipients = self.get_employee_emails(), message = settings.message, + subject = settings.subject, reference_doctype=self.doctype, reference_name=self.name) + + def send_summary(self): + '''Send summary of all replies''' + settings = frappe.get_doc('Daily Work Summary Settings') + + replies = frappe.get_doc('Communication', fields=['content', 'sender'], + filters=dict(reference_doctype=self.doctype, reference_name=self.name, + communication_type='Email', sent_or_received='Received')) + + message = frappe.render_template(template, dict(replies=replies, + original_message=settings.message)) + + frappe.sendmail(recipients = self.get_employee_emails(), message = message, + subject = _('Daily Work Summary for {0}').format(self.company), + reference_doctype=self.doctype, reference_name=self.name) + + def get_employee_emails(self): + return filter(None, [d.user_id for d in + frappe.get_all('Employee', fields=['user_id'], + filters={'status': 'Active', 'company': self.company})]) + +template = ''' +

Summary of replies:

+ +{% for reply in replies %} +
{{ frappe.db.get_value("Employee", reply.sender, "full_name") }}
+
+ {{ reply.content.split(original_message)[0].strip() }} +
+{% endfor %} +''' \ No newline at end of file diff --git a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py new file mode 100644 index 00000000000..7cc2bc2cfc0 --- /dev/null +++ b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +import frappe.utils + +# test_records = frappe.get_test_records('Daily Work Summary') + +class TestDailyWorkSummary(unittest.TestCase): + def test_email_trigger(self): + settings, employees, emails = self.setup_and_prepare_test(frappe.utils.nowtime().split(':')[0]) + + for d in employees: + # check that email is sent to this employee + self.assertTrue(d.user_id in [d.recipient for d in emails + if settings.subject in d.message]) + + def test_email_trigger_failed(self): + hour = '00' + if frappe.utils.nowtime().split(':')[0]=='00': + hour = '01' + + settings, employees, emails = self.setup_and_prepare_test(hour) + + for d in employees: + # check that email is sent to this employee + self.assertFalse(d.user_id in [d.recipient for d in emails + if settings.subject in d.message]) + + def setup_and_prepare_test(self, hour): + frappe.db.sql('delete from `tabEmail Queue`') + + # setup email to trigger at this our + settings = frappe.get_doc('Daily Work Summary Settings') + settings.companies = [] + + settings.append('companies', dict(company='_Test Company', + send_emails_at=hour + ':00')) + settings.test_subject = 'this is a subject for testing summary emails' + settings.save() + + from erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings \ + import trigger_emails + trigger_emails() + + # check if emails are created + employees = frappe.get_all('Employee', fields = ['user_id'], + filters=dict(company='_Test Company', status='Active')) + + emails = frappe.get_all('Email Queue', fields=['recipient', 'message']) + + return settings, employees, emails \ No newline at end of file diff --git a/erpnext/hr/doctype/daily_work_summary_response/__init__.py b/erpnext/hr/doctype/daily_work_summary_response/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/daily_work_summary_response/daily_work_summary_response.json b/erpnext/hr/doctype/daily_work_summary_response/daily_work_summary_response.json new file mode 100644 index 00000000000..b4295d4a496 --- /dev/null +++ b/erpnext/hr/doctype/daily_work_summary_response/daily_work_summary_response.json @@ -0,0 +1,95 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2016-11-08 04:56:39.588230", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "employee", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Employee", + "length": 0, + "no_copy": 0, + "options": "Employee", + "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, + "fieldname": "response", + "fieldtype": "Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Response", + "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 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2016-11-08 04:56:39.588230", + "modified_by": "Administrator", + "module": "HR", + "name": "Daily Work Summary Response", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "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/hr/doctype/daily_work_summary_response/daily_work_summary_response.py b/erpnext/hr/doctype/daily_work_summary_response/daily_work_summary_response.py new file mode 100644 index 00000000000..739eaa771a9 --- /dev/null +++ b/erpnext/hr/doctype/daily_work_summary_response/daily_work_summary_response.py @@ -0,0 +1,10 @@ +# -*- 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 + +class DailyWorkSummaryResponse(Document): + pass diff --git a/erpnext/hr/doctype/daily_work_summary_settings/__init__.py b/erpnext/hr/doctype/daily_work_summary_settings/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.js b/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.js new file mode 100644 index 00000000000..cb0b6086ac9 --- /dev/null +++ b/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.js @@ -0,0 +1,7 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Daily Work Summary Settings', { + onload: function(frm) { + } +}); diff --git a/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.json b/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.json new file mode 100644 index 00000000000..00e90eaaa1a --- /dev/null +++ b/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.json @@ -0,0 +1,203 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2016-11-08 04:55:08.231715", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "select_companies", + "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": "Select Companies", + "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, + "fieldname": "companies", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Companies", + "length": 0, + "no_copy": 0, + "options": "Daily Work Summary Settings Company", + "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, + "fieldname": "message_section", + "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": "Message", + "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, + "default": "What did you work on today?", + "fieldname": "subject", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Subject", + "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, + "default": "

Please share what did you do today. If you reply by midnight, your response will be recorded!

", + "fieldname": "message", + "fieldtype": "Text Editor", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Message", + "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 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 0, + "modified": "2016-11-08 05:48:53.068957", + "modified_by": "Administrator", + "module": "HR", + "name": "Daily Work Summary Settings", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "is_custom": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "HR Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "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/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.py b/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.py new file mode 100644 index 00000000000..d36f348e9f0 --- /dev/null +++ b/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.py @@ -0,0 +1,20 @@ +# -*- 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 +import frappe.utils + +class DailyWorkSummarySettings(Document): + pass + +def trigger_emails(): + settings = frappe.get_doc('Daily Work Summary Settings') + for d in settings.companies: + # if current hour + if frappe.utils.nowtime().split(':')[0] == d.send_emails_at.split(':')[0]: + work_summary = frappe.get_doc(dict(doctype='Daily Work Summary', + company=d.company)).insert() + work_summary.send_mails(settings) \ No newline at end of file diff --git a/erpnext/hr/doctype/daily_work_summary_settings_company/__init__.py b/erpnext/hr/doctype/daily_work_summary_settings_company/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/daily_work_summary_settings_company/daily_work_summary_settings_company.json b/erpnext/hr/doctype/daily_work_summary_settings_company/daily_work_summary_settings_company.json new file mode 100644 index 00000000000..be27fa3dcb3 --- /dev/null +++ b/erpnext/hr/doctype/daily_work_summary_settings_company/daily_work_summary_settings_company.json @@ -0,0 +1,97 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2016-11-08 05:44:02.502527", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "17:00", + "fieldname": "send_emails_at", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Send Emails At", + "length": 0, + "no_copy": 0, + "options": "00:00\n01:00\n02:00\n03:00\n04:00\n05:00\n06:00\n07:00\n08:00\n09:00\n10:00\n11:00\n12:00\n13:00\n14:00\n15:00\n16:00\n17:00\n18:00\n19:00\n20:00\n21:00\n22:00\n23:00", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "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": 1, + "max_attachments": 0, + "modified": "2016-11-08 05:46:09.198788", + "modified_by": "Administrator", + "module": "HR", + "name": "Daily Work Summary Settings Company", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "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/hr/doctype/daily_work_summary_settings_company/daily_work_summary_settings_company.py b/erpnext/hr/doctype/daily_work_summary_settings_company/daily_work_summary_settings_company.py new file mode 100644 index 00000000000..cd051b4457d --- /dev/null +++ b/erpnext/hr/doctype/daily_work_summary_settings_company/daily_work_summary_settings_company.py @@ -0,0 +1,10 @@ +# -*- 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 + +class DailyWorkSummarySettingsCompany(Document): + pass diff --git a/erpnext/patches/v7_1/set_prefered_contact_email.py b/erpnext/patches/v7_1/set_prefered_contact_email.py index d083811c84e..3b68e22269a 100644 --- a/erpnext/patches/v7_1/set_prefered_contact_email.py +++ b/erpnext/patches/v7_1/set_prefered_contact_email.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import frappe def execute(): + frappe.reload_doctype('User') for d in frappe.get_all("Employee"): employee = frappe.get_doc("Employee", d.name) if employee.company_email: @@ -13,5 +14,4 @@ def execute(): elif employee.user_id: employee.prefered_contact_email = "User ID" employee.prefered_email = employee.user_id - - employee.db_update() \ No newline at end of file + employee.db_update() diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 9971baaaa1a..7b71675f754 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -27,7 +27,7 @@ def check_setup_wizard_not_completed(): def set_single_defaults(): for dt in ('Accounts Settings', 'Print Settings', 'HR Settings', 'Buying Settings', - 'Selling Settings', 'Stock Settings'): + 'Selling Settings', 'Stock Settings', 'Daily Work Summary Settings'): default_values = frappe.db.sql("""select fieldname, `default` from `tabDocField` where parent=%s""", dt) if default_values: From c10d2a00254f403d2ed33aa24ec3aa1c1f27e548 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 14 Nov 2016 10:52:01 +0530 Subject: [PATCH 2/3] [wip] daily work summary --- .../daily_work_summary.json | 19 ++-- .../daily_work_summary/daily_work_summary.py | 65 +++++++++---- .../test_daily_work_summary.py | 3 + .../daily_work_summary_response/__init__.py | 0 .../daily_work_summary_response.json | 95 ------------------- .../daily_work_summary_response.py | 10 -- .../daily_work_summary_settings.js | 5 +- .../daily_work_summary_settings.py | 15 ++- erpnext/hr/doctype/employee/employee.py | 15 ++- 9 files changed, 88 insertions(+), 139 deletions(-) delete mode 100644 erpnext/hr/doctype/daily_work_summary_response/__init__.py delete mode 100644 erpnext/hr/doctype/daily_work_summary_response/daily_work_summary_response.json delete mode 100644 erpnext/hr/doctype/daily_work_summary_response/daily_work_summary_response.py diff --git a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json index 281a6abd169..d4de3b2e7c7 100644 --- a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json +++ b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json @@ -22,7 +22,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Company", "length": 0, @@ -32,7 +32,7 @@ "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, @@ -45,23 +45,24 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "responses", - "fieldtype": "Table", + "default": "Open", + "fieldname": "status", + "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, - "label": "Responses", + "label": "Status", "length": 0, "no_copy": 0, - "options": "Daily Work Summary Response", + "options": "Open\nSummary Sent", "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, @@ -80,7 +81,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-11-08 06:03:21.121442", + "modified": "2016-11-10 16:09:11.619822", "modified_by": "Administrator", "module": "HR", "name": "Daily Work Summary", diff --git a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py index d3e92e9a74f..45b0c9a2fe7 100644 --- a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py +++ b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py @@ -6,40 +6,69 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ +from email_reply_parser import EmailReplyParser +from erpnext.hr.doctype.employee.employee import is_holiday class DailyWorkSummary(Document): - def send_mails(self, settings): + def send_mails(self, settings, emails): '''Send emails to get daily work summary to all employees''' - frappe.sendmail(recipients = self.get_employee_emails(), message = settings.message, - subject = settings.subject, reference_doctype=self.doctype, reference_name=self.name) + incoming_email_account = frappe.db.get_value('Email Account', + dict(enable_incoming=1, default_incoming=1), 'email_id') + frappe.sendmail(recipients = emails, message = settings.message, + subject = settings.subject, reference_doctype=self.doctype, + reference_name=self.name, reply_to = incoming_email_account) def send_summary(self): '''Send summary of all replies''' - settings = frappe.get_doc('Daily Work Summary Settings') + message = self.get_summary_message() - replies = frappe.get_doc('Communication', fields=['content', 'sender'], - filters=dict(reference_doctype=self.doctype, reference_name=self.name, - communication_type='Email', sent_or_received='Received')) - - message = frappe.render_template(template, dict(replies=replies, - original_message=settings.message)) - - frappe.sendmail(recipients = self.get_employee_emails(), message = message, + frappe.sendmail(recipients = get_employee_emails(self.company, False), message = message, subject = _('Daily Work Summary for {0}').format(self.company), reference_doctype=self.doctype, reference_name=self.name) - def get_employee_emails(self): - return filter(None, [d.user_id for d in - frappe.get_all('Employee', fields=['user_id'], - filters={'status': 'Active', 'company': self.company})]) + def get_summary_message(self): + '''Return summary of replies as HTML''' + settings = frappe.get_doc('Daily Work Summary Settings') + + replies = frappe.get_all('Communication', fields=['content', 'text_content', 'sender'], + filters=dict(reference_doctype=self.doctype, reference_name=self.name, + communication_type='Communication', sent_or_received='Received')) + + if not replies: + return None + + for d in replies: + if d.text_content: + d.content = EmailReplyParser.parse_reply(d.text_content) + + return frappe.render_template(template, dict(replies=replies, + original_message=settings.message)) + +def get_employee_emails(company, only_working=True): + '''Returns list of Employee user ids for the given company who are working today + + :param company: Company `name`''' + employee_list = frappe.get_all('Employee', fields=['name', 'user_id'], + filters={'status': 'Active', 'company': company}) + + out = [] + for e in employee_list: + if e.user_id: + if only_working and is_holiday(e.name): + # don't add if holiday + pass + out.append(e.user_id) + + return out + template = '''

Summary of replies:

{% for reply in replies %} -
{{ frappe.db.get_value("Employee", reply.sender, "full_name") }}
+
{{ frappe.db.get_value("Employee", {"user_id": reply.sender}, "employee_name") or reply.sender }}
- {{ reply.content.split(original_message)[0].strip() }} + {{ reply.content }}
{% endfor %} ''' \ No newline at end of file diff --git a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py index 7cc2bc2cfc0..12b3e2f072b 100644 --- a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py +++ b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py @@ -30,6 +30,9 @@ class TestDailyWorkSummary(unittest.TestCase): self.assertFalse(d.user_id in [d.recipient for d in emails if settings.subject in d.message]) + def test_summary(self): + pass + def setup_and_prepare_test(self, hour): frappe.db.sql('delete from `tabEmail Queue`') diff --git a/erpnext/hr/doctype/daily_work_summary_response/__init__.py b/erpnext/hr/doctype/daily_work_summary_response/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/hr/doctype/daily_work_summary_response/daily_work_summary_response.json b/erpnext/hr/doctype/daily_work_summary_response/daily_work_summary_response.json deleted file mode 100644 index b4295d4a496..00000000000 --- a/erpnext/hr/doctype/daily_work_summary_response/daily_work_summary_response.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-11-08 04:56:39.588230", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "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, - "fieldname": "response", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Response", - "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 - } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "in_dialog": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2016-11-08 04:56:39.588230", - "modified_by": "Administrator", - "module": "HR", - "name": "Daily Work Summary Response", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "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/hr/doctype/daily_work_summary_response/daily_work_summary_response.py b/erpnext/hr/doctype/daily_work_summary_response/daily_work_summary_response.py deleted file mode 100644 index 739eaa771a9..00000000000 --- a/erpnext/hr/doctype/daily_work_summary_response/daily_work_summary_response.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- 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 - -class DailyWorkSummaryResponse(Document): - pass diff --git a/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.js b/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.js index cb0b6086ac9..f5c0a5cb16a 100644 --- a/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.js +++ b/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.js @@ -2,6 +2,9 @@ // For license information, please see license.txt frappe.ui.form.on('Daily Work Summary Settings', { - onload: function(frm) { + refresh: function(frm) { + frm.add_custom_button(__('Daily Work Summary'), function() { + frappe.set_route('List', 'Daily Work Summary'); + }); } }); diff --git a/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.py b/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.py index d36f348e9f0..259b976bb5d 100644 --- a/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.py +++ b/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.py @@ -6,15 +6,22 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document import frappe.utils +from frappe import _ +from erpnext.hr.doctype.daily_work_summary.daily_work_summary import get_employee_emails class DailyWorkSummarySettings(Document): - pass + def validate(self): + if self.companies: + if not frappe.db.get_value('Email Account', dict(enable_incoming=1, default_incoming=1)): + frappe.throw(_('There must be a default incoming Email Account enabled for this to work. Please setup a default incoming Email Account (POP/IMAP) and try again.')) def trigger_emails(): settings = frappe.get_doc('Daily Work Summary Settings') for d in settings.companies: # if current hour if frappe.utils.nowtime().split(':')[0] == d.send_emails_at.split(':')[0]: - work_summary = frappe.get_doc(dict(doctype='Daily Work Summary', - company=d.company)).insert() - work_summary.send_mails(settings) \ No newline at end of file + emails = get_employee_emails(d.company) + if emails: + work_summary = frappe.get_doc(dict(doctype='Daily Work Summary', + company=d.company)).insert() + work_summary.send_mails(settings, emails) \ No newline at end of file diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index 2e5fb260934..e2e541bdb0c 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -9,7 +9,6 @@ from frappe.model.naming import make_autoname from frappe import throw, _ import frappe.permissions from frappe.model.document import Document -from frappe.model.mapper import get_mapped_doc from erpnext.utilities.transaction_base import delete_events @@ -164,7 +163,6 @@ def get_timeline_data(doctype, name): @frappe.whitelist() def get_retirement_date(date_of_birth=None): - import datetime ret = {} if date_of_birth: try: @@ -233,3 +231,16 @@ def get_holiday_list_for_employee(employee, raise_exception=True): return holiday_list +def is_holiday(employee, date=None): + '''Returns True if given Employee has an holiday on the given date + + :param employee: Employee `name` + :param date: Date to check. Will check for today if None''' + + holiday_list = get_holiday_list_for_employee(employee) + if not date: + date = today() + + if holiday_list: + return frappe.get_all('Holiday List', dict(name=holiday_list, holiday_date=date)) and True or False + From 9dd58d42df47308a3fd06607e4244cef00f42050 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 18 Nov 2016 12:39:24 +0530 Subject: [PATCH 3/3] [fix] test cases and final wiring for daily work summary --- erpnext/hooks.py | 3 +- .../daily_work_summary.json | 35 ++++++++- .../daily_work_summary/daily_work_summary.py | 55 ++++++++++---- .../test_daily_work_summary.py | 32 ++++++-- .../test_data/test-reply.raw | 75 +++++++++++++++++++ .../daily_work_summary_settings.py | 16 +++- 6 files changed, 189 insertions(+), 27 deletions(-) create mode 100644 erpnext/hr/doctype/daily_work_summary/test_data/test-reply.raw diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 1cede9047fb..3ebe6f0095f 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -194,7 +194,8 @@ scheduler_events = { "erpnext.accounts.doctype.fiscal_year.fiscal_year.auto_create_fiscal_year", "erpnext.hr.doctype.employee.employee.send_birthday_reminders", "erpnext.projects.doctype.task.task.set_tasks_as_overdue", - "erpnext.accounts.doctype.asset.depreciation.post_depreciation_entries" + "erpnext.accounts.doctype.asset.depreciation.post_depreciation_entries", + 'erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.send_summary' ] } diff --git a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json index d4de3b2e7c7..43cef682030 100644 --- a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json +++ b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json @@ -22,6 +22,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, "label": "Company", @@ -52,12 +53,42 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, "label": "Status", "length": 0, "no_copy": 0, - "options": "Open\nSummary Sent", + "options": "Open\nSent", + "permlevel": 0, + "precision": "", + "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": "email_sent_to", + "fieldtype": "Code", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Email Sent To", + "length": 0, + "no_copy": 0, "permlevel": 0, "precision": "", "print_hide": 0, @@ -81,7 +112,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-11-10 16:09:11.619822", + "modified": "2016-11-18 12:09:01.580414", "modified_by": "Administrator", "module": "HR", "name": "Daily Work Summary", diff --git a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py index 45b0c9a2fe7..fe5fd8e5619 100644 --- a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py +++ b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py @@ -8,24 +8,30 @@ from frappe.model.document import Document from frappe import _ from email_reply_parser import EmailReplyParser from erpnext.hr.doctype.employee.employee import is_holiday +from frappe.utils import formatdate class DailyWorkSummary(Document): def send_mails(self, settings, emails): '''Send emails to get daily work summary to all employees''' incoming_email_account = frappe.db.get_value('Email Account', dict(enable_incoming=1, default_incoming=1), 'email_id') + + self.db_set('email_sent_to', '\n'.join(emails)) frappe.sendmail(recipients = emails, message = settings.message, subject = settings.subject, reference_doctype=self.doctype, reference_name=self.name, reply_to = incoming_email_account) def send_summary(self): - '''Send summary of all replies''' + '''Send summary of all replies. Called at midnight''' message = self.get_summary_message() - frappe.sendmail(recipients = get_employee_emails(self.company, False), message = message, + frappe.sendmail(recipients = get_employee_emails(self.company, False), + message = message, subject = _('Daily Work Summary for {0}').format(self.company), reference_doctype=self.doctype, reference_name=self.name) + self.db_set('status', 'Sent') + def get_summary_message(self): '''Return summary of replies as HTML''' settings = frappe.get_doc('Daily Work Summary Settings') @@ -34,15 +40,42 @@ class DailyWorkSummary(Document): filters=dict(reference_doctype=self.doctype, reference_name=self.name, communication_type='Communication', sent_or_received='Received')) - if not replies: - return None + did_not_reply = self.email_sent_to.split() for d in replies: + if d.sender in did_not_reply: + did_not_reply.remove(d.sender) if d.text_content: d.content = EmailReplyParser.parse_reply(d.text_content) - return frappe.render_template(template, dict(replies=replies, - original_message=settings.message)) + + did_not_reply = [(frappe.db.get_value("Employee", {"user_id": email}, "employee_name") or email) + for email in did_not_reply] + + return frappe.render_template(self.get_summary_template(), + dict(replies=replies, + original_message=settings.message, + title=_('Daily Work Summary for {0}'.format(formatdate(self.creation))), + did_not_reply= ', '.join(did_not_reply) or '', + did_not_reply_title = _('No replies from'))) + + def get_summary_template(self): + return ''' +

{{ title }}

+ +{% for reply in replies %} +
{{ frappe.db.get_value("Employee", {"user_id": reply.sender}, "employee_name") or reply.sender }}
+

+ {{ reply.content }} +

+{% endfor %} + +{% if did_not_reply %} +
+

{{ did_not_reply_title }}: {{ did_not_reply }}

+{% endif %} + +''' def get_employee_emails(company, only_working=True): '''Returns list of Employee user ids for the given company who are working today @@ -62,13 +95,3 @@ def get_employee_emails(company, only_working=True): return out -template = ''' -

Summary of replies:

- -{% for reply in replies %} -
{{ frappe.db.get_value("Employee", {"user_id": reply.sender}, "employee_name") or reply.sender }}
-
- {{ reply.content }} -
-{% endfor %} -''' \ No newline at end of file diff --git a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py index 12b3e2f072b..b8e70e2431b 100644 --- a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py +++ b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py @@ -3,6 +3,7 @@ # See license.txt from __future__ import unicode_literals +import os import frappe import unittest import frappe.utils @@ -11,7 +12,7 @@ import frappe.utils class TestDailyWorkSummary(unittest.TestCase): def test_email_trigger(self): - settings, employees, emails = self.setup_and_prepare_test(frappe.utils.nowtime().split(':')[0]) + settings, employees, emails = self.setup_and_prepare_test() for d in employees: # check that email is sent to this employee @@ -30,11 +31,32 @@ class TestDailyWorkSummary(unittest.TestCase): self.assertFalse(d.user_id in [d.recipient for d in emails if settings.subject in d.message]) - def test_summary(self): - pass + def test_incoming(self): + settings, employees, emails = self.setup_and_prepare_test() - def setup_and_prepare_test(self, hour): + # get test mail with message-id as in-reply-to + with open(os.path.join(os.path.dirname(__file__), "test_data", "test-reply.raw"), "r") as f: + test_mails = [f.read().replace('{{ sender }}', employees[-1].user_id)\ + .replace('{{ message_id }}', emails[-1].message_id)] + + # pull the mail + email_account = frappe.get_doc("Email Account", "_Test Email Account 1") + email_account.db_set('enable_incoming', 1) + email_account.receive(test_mails=test_mails) + + daily_work_summary = frappe.get_doc('Daily Work Summary', + frappe.get_all('Daily Work Summary')[0].name) + + summary = daily_work_summary.get_summary_message() + + self.assertTrue('I built Daily Work Summary!' in summary) + + def setup_and_prepare_test(self, hour=None): + if not hour: + hour = frappe.utils.nowtime().split(':')[0] + frappe.db.sql('delete from `tabDaily Work Summary`') frappe.db.sql('delete from `tabEmail Queue`') + frappe.db.sql('delete from `tabCommunication`') # setup email to trigger at this our settings = frappe.get_doc('Daily Work Summary Settings') @@ -53,6 +75,6 @@ class TestDailyWorkSummary(unittest.TestCase): employees = frappe.get_all('Employee', fields = ['user_id'], filters=dict(company='_Test Company', status='Active')) - emails = frappe.get_all('Email Queue', fields=['recipient', 'message']) + emails = frappe.get_all('Email Queue', fields=['recipient', 'message', 'message_id']) return settings, employees, emails \ No newline at end of file diff --git a/erpnext/hr/doctype/daily_work_summary/test_data/test-reply.raw b/erpnext/hr/doctype/daily_work_summary/test_data/test-reply.raw new file mode 100644 index 00000000000..ba01bc28273 --- /dev/null +++ b/erpnext/hr/doctype/daily_work_summary/test_data/test-reply.raw @@ -0,0 +1,75 @@ +From: {{ sender }} +Content-Type: multipart/alternative; + boundary="Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361" +Message-Id: <07D687F6-10AA-4B9F-82DE-27753096164E@gmail.com> +Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\)) +X-Smtp-Server: 73CC8281-7E8F-4B47-8324-D5DA86EEDD4F +Subject: Re: What did you work on today? +Date: Thu, 10 Nov 2016 16:04:43 +0530 +X-Universally-Unique-Identifier: A4D9669F-179C-42D8-A3D3-AA6A8C49A6F2 +References: <{{ message_id }}> +To: test_in@iwebnotes.com +In-Reply-To: <{{ message_id }}> + + +--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361 +Content-Transfer-Encoding: quoted-printable +Content-Type: text/plain; + charset=us-ascii + +I built Daily Work Summary! + +> On 10-Nov-2016, at 3:20 PM, Frappe wrote: +>=20 +> Please share what did you do today. If you reply by midnight, your = +response will be recorded! +>=20 +> This email was sent to rmehta@gmail.com +> Unsubscribe from this list = + +> Sent via ERPNext + + +--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361 +Content-Transfer-Encoding: 7bit +Content-Type: text/html; + charset=us-ascii + +I built Daily Work Summary!

On 10-Nov-2016, at 3:20 PM, Frappe <test@erpnext.com> wrote:

+ + + + +What did you work on today? + +
+ +

Please share what did you do today. If you reply by midnight, your response will be recorded!

+ +
+ + + + + + +
+

+--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361-- diff --git a/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.py b/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.py index 259b976bb5d..aea4c354ed7 100644 --- a/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.py +++ b/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.py @@ -12,16 +12,26 @@ from erpnext.hr.doctype.daily_work_summary.daily_work_summary import get_employe class DailyWorkSummarySettings(Document): def validate(self): if self.companies: - if not frappe.db.get_value('Email Account', dict(enable_incoming=1, default_incoming=1)): + if not frappe.flags.in_test and not frappe.db.get_value('Email Account', dict(enable_incoming=1, + default_incoming=1)): frappe.throw(_('There must be a default incoming Email Account enabled for this to work. Please setup a default incoming Email Account (POP/IMAP) and try again.')) def trigger_emails(): + '''Send emails to Employees of the enabled companies at the give hour asking + them what did they work on today''' settings = frappe.get_doc('Daily Work Summary Settings') for d in settings.companies: # if current hour if frappe.utils.nowtime().split(':')[0] == d.send_emails_at.split(':')[0]: emails = get_employee_emails(d.company) + # find emails relating to a company if emails: - work_summary = frappe.get_doc(dict(doctype='Daily Work Summary', + daily_work_summary = frappe.get_doc(dict(doctype='Daily Work Summary', company=d.company)).insert() - work_summary.send_mails(settings, emails) \ No newline at end of file + daily_work_summary.send_mails(settings, emails) + +def send_summary(): + '''Send summary to everyone''' + for d in frappe.get_all('Daily Work Summary', dict(status='Open')): + daily_work_summary = frappe.get_doc('Daily Work Summary', d.name) + daily_work_summary.send_summary() \ No newline at end of file