From d3069fee4de6018ab835152ab3c5c313fec8f9ea Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 21 Feb 2018 15:15:43 +0530 Subject: [PATCH] Daily work summary refactor (#12944) * added daily work summary setting doctype and code to support feature this will allow multiple setting for daily work summary * added daily work summary setting user doctype * made changes in daily work summary code * [minor] entry change in hr config file * deleted previous daily work summary settings (and its company) doctype * removed unwanted permission check * toggled read_only option for enabled field * removed print statements * add patch for the changes * doc changes * [minor] indentation fix * fixed tests * indentation fixes * codacy issue fix * formatting fixes * renamed doctype Renamed Daily Work Summary Setting to Daily Work Summary Group and did related code and doc changes * fixed typo * updated doc * codacy issue fix * [minor] renamed doctype name in json * Renamed old doctype * fixed indentation * codacy fix * indentation fix * renamed doctype * handled patch exception * fixed exception * Update daily_work_summary_group.py * rename patch file removed abbreviation in file name * handled exception in patch code * removed Unnecessary pass statement * [minor] indentation fix --- erpnext/config/hr.py | 4 +- .../en/human-resources/daily-work-summary.md | 21 ++- erpnext/hooks.py | 4 +- .../daily_work_summary/daily_work_summary.js | 2 +- .../daily_work_summary.json | 23 ++- .../daily_work_summary/daily_work_summary.py | 95 ++++++------ .../test_daily_work_summary.js | 23 +++ .../test_daily_work_summary.py | 61 ++++---- .../__init__.py | 0 .../daily_work_summary_group.js | 12 ++ .../daily_work_summary_group.json} | 141 +++++++++++++++--- .../daily_work_summary_group.py | 58 +++++++ .../__init__.py | 0 .../daily_work_summary_group_user.json} | 36 +++-- .../daily_work_summary_group_user.py} | 4 +- .../daily_work_summary_settings.js | 10 -- .../daily_work_summary_settings.py | 37 ----- erpnext/hr/page/team_updates/team_updates.js | 6 +- erpnext/patches.txt | 1 + ...ry_settings_to_daily_work_summary_group.py | 45 ++++++ erpnext/setup/install.py | 2 +- 21 files changed, 410 insertions(+), 175 deletions(-) create mode 100644 erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.js rename erpnext/hr/doctype/{daily_work_summary_settings => daily_work_summary_group}/__init__.py (100%) create mode 100644 erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.js rename erpnext/hr/doctype/{daily_work_summary_settings/daily_work_summary_settings.json => daily_work_summary_group/daily_work_summary_group.json} (56%) create mode 100644 erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.py rename erpnext/hr/doctype/{daily_work_summary_settings_company => daily_work_summary_group_user}/__init__.py (100%) rename erpnext/hr/doctype/{daily_work_summary_settings_company/daily_work_summary_settings_company.json => daily_work_summary_group_user/daily_work_summary_group_user.json} (73%) rename erpnext/hr/doctype/{daily_work_summary_settings_company/daily_work_summary_settings_company.py => daily_work_summary_group_user/daily_work_summary_group_user.py} (61%) delete mode 100644 erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.js delete mode 100644 erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.py create mode 100644 erpnext/patches/v10_0/migrate_daily_work_summary_settings_to_daily_work_summary_group.py diff --git a/erpnext/config/hr.py b/erpnext/config/hr.py index b6b4cf1170..9d216005bf 100644 --- a/erpnext/config/hr.py +++ b/erpnext/config/hr.py @@ -244,8 +244,8 @@ def get_data(): }, { "type": "doctype", - "name": "Daily Work Summary Settings" - }, + "name": "Daily Work Summary Group" + } ] }, { diff --git a/erpnext/docs/user/manual/en/human-resources/daily-work-summary.md b/erpnext/docs/user/manual/en/human-resources/daily-work-summary.md index d3ba22a828..4a5c98953e 100644 --- a/erpnext/docs/user/manual/en/human-resources/daily-work-summary.md +++ b/erpnext/docs/user/manual/en/human-resources/daily-work-summary.md @@ -1,15 +1,24 @@ # Daily Work Summary -Daily Work Summary is way to get a automated way to get a summary of work done by all employees in a company. Once you set it up, each active Employee of the Company gets an email asking them what did they work on during the day. +Daily Work Summary is way to get a automated way to get a summary of work done by users. + +Replies of all users who choose to respond is collected and sent as a summary at midnight. Emails are only sent based on the Holiday List selected for the group + +**Note:** +> You must have one active incoming email account setup for this to work. -Replies of all employees who choose to respond is collected and sent as a summary at midnight. Emails are only sent based on the Holiday List setup in the Company or Employee master. -*Note:* You must have one active incoming email account setup for this to work. ### How to Use -Go to "Daily Work Summary Settings" via HR module or search bar and set the company for which you want to activate this feature. +Go to "Daily Work Summary Group" via HR module or search bar and set the users for whom you want to send the reminder. -You can also choose to Customize the Message you send to your employees: +You can set multiple groups with different set of _users_ from your user list with different _time to send emails_ and with separate _holiday list_ for each. + +You can also choose to customize the _Message_ you send to users. + +**Note:** +>1. If no holiday list is selected then the email will be sent every day. +>2. Name of a "Daily Work Summary Group" will be sent as the title for daily summary email. +>3. Mail will not be sent to the users of a disabled Daily Work Summary Group. -Department diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 442ddf507f..f7f1720551 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -218,7 +218,7 @@ doc_events = { scheduler_events = { "hourly": [ "erpnext.accounts.doctype.subscription.subscription.make_subscription_entry", - 'erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.trigger_emails' + 'erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails' ], "daily": [ "erpnext.stock.reorder_item.reorder_item", @@ -230,7 +230,7 @@ scheduler_events = { "erpnext.hr.doctype.employee.employee.send_birthday_reminders", "erpnext.projects.doctype.task.task.set_tasks_as_overdue", "erpnext.assets.doctype.asset.depreciation.post_depreciation_entries", - "erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.send_summary", + "erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.send_summary", "erpnext.stock.doctype.serial_no.serial_no.update_maintenance_status", "erpnext.buying.doctype.supplier_scorecard.supplier_scorecard.refresh_scorecards", "erpnext.setup.doctype.company.company.cache_companies_monthly_sales_history", diff --git a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.js b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.js index 1ac173a8d3..82364801ce 100644 --- a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.js +++ b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.js @@ -2,7 +2,7 @@ // For license information, please see license.txt frappe.ui.form.on('Daily Work Summary', { - refresh: function(frm) { + 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 index b457cf2157..259416edd4 100644 --- a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json +++ b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "beta": 0, @@ -12,22 +13,24 @@ "engine": "InnoDB", "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "company", + "fieldname": "daily_work_summary_group", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, - "in_list_view": 1, + "in_global_search": 0, + "in_list_view": 0, "in_standard_filter": 0, - "label": "Company", + "label": "Daily Work Summary Group", "length": 0, "no_copy": 0, - "options": "Company", + "options": "Daily Work Summary Group", "permlevel": 0, "precision": "", "print_hide": 0, @@ -41,6 +44,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -52,6 +56,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": "Status", @@ -71,6 +76,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -81,6 +87,7 @@ "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", @@ -99,17 +106,17 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 0, "image_view": 0, "in_create": 1, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-11-21 01:05:55.258867", + "modified": "2018-02-16 11:02:06.727749", "modified_by": "Administrator", "module": "HR", "name": "Daily Work Summary", @@ -126,7 +133,6 @@ "export": 0, "if_owner": 0, "import": 0, - "is_custom": 0, "permlevel": 0, "print": 0, "read": 1, @@ -147,7 +153,6 @@ "export": 1, "if_owner": 0, "import": 0, - "is_custom": 0, "permlevel": 0, "print": 1, "read": 1, @@ -162,7 +167,9 @@ "quick_entry": 1, "read_only": 1, "read_only_onload": 0, + "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", + "track_changes": 0, "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 index 9ac74aae15..4d3b173ff7 100644 --- a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py +++ b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py @@ -11,52 +11,66 @@ from erpnext.hr.doctype.employee.employee import is_holiday from frappe.utils import global_date_format from markdown2 import markdown + class DailyWorkSummary(Document): - def send_mails(self, settings, emails): - '''Send emails to get daily work summary to all employees''' + def send_mails(self, dws_group, emails): + '''Send emails to get daily work summary to all users \ + in selected daily work summary group''' incoming_email_account = frappe.db.get_value('Email Account', - dict(enable_incoming=1, default_incoming=1), 'email_id') + 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) + frappe.sendmail(recipients=emails, + message=dws_group.message, + subject=dws_group.subject, + reference_doctype=self.doctype, + reference_name=self.name, + reply_to=incoming_email_account) def send_summary(self): '''Send summary of all replies. Called at midnight''' args = self.get_message_details() - - frappe.sendmail(recipients = get_employee_emails(self.company, False), + emails = get_user_emails_from_group(self.daily_work_summary_group) + frappe.sendmail(recipients=emails, template='daily_work_summary', args=args, - subject = _('Daily Work Summary for {0}').format(self.company), - reference_doctype=self.doctype, reference_name=self.name) + subject=_(self.daily_work_summary_group), + reference_doctype=self.doctype, + reference_name=self.name) self.db_set('status', 'Sent') def get_message_details(self): '''Return args for template''' - settings = frappe.get_doc('Daily Work Summary Settings') + dws_group = frappe.get_doc('Daily Work Summary Group', + self.daily_work_summary_group) - 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'), - order_by='creation asc') + 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'), + order_by='creation asc') did_not_reply = self.email_sent_to.split() for d in replies: - emp = frappe.db.get_values("Employee", {"user_id": d.sender}, - ["employee_name", "image"], as_dict=True) + user = frappe.db.get_values("User", + {"email": d.sender}, + ["full_name", "user_image"], + as_dict=True) + + d.sender_name = user[0].full_name if user else d.sender + d.image = user[0].image if user and user[0].image else None - d.sender_name = emp[0].employee_name if emp else d.sender - d.image = emp[0].image if emp and emp[0].image else None - original_image = d.image # make thumbnail image try: if original_image: - file_name = frappe.get_list('File', {'file_url': original_image}) + file_name = frappe.get_list('File', + {'file_url': original_image}) if file_name: file_name = file_name[0].name @@ -74,34 +88,27 @@ class DailyWorkSummary(Document): if d.sender in did_not_reply: did_not_reply.remove(d.sender) if d.text_content: - d.content = markdown(EmailReplyParser.parse_reply(d.text_content)) + d.content = markdown( + EmailReplyParser.parse_reply(d.text_content) + ) - - did_not_reply = [(frappe.db.get_value("Employee", {"user_id": email}, "employee_name") or email) + did_not_reply = [(frappe.db.get_value("User", {"email": email}, "full_name") or email) for email in did_not_reply] return dict(replies=replies, - original_message=settings.message, - title=_('Daily Work Summary for {0}'.format(global_date_format(self.creation))), - did_not_reply= ', '.join(did_not_reply) or '', - did_not_reply_title = _('No replies from')) + original_message=dws_group.message, + title=_('Work Summary for {0}'.format( + global_date_format(self.creation) + )), + did_not_reply=', '.join(did_not_reply) or '', + did_not_reply_title=_('No replies from')) -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 - continue - out.append(e.user_id) - - return out +def get_user_emails_from_group(group): + '''Returns list of email of users from the given group + :param group: Daily Work Summary Group `name`''' + group_doc = frappe.get_doc('Daily Work Summary Group', group) + emails = [d.email for d in group_doc.users] + return emails diff --git a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.js b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.js new file mode 100644 index 0000000000..d2ceb8bd52 --- /dev/null +++ b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Daily Work Summary", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Daily Work Summary + () => frappe.tests.make('Daily Work Summary', [ + // values to be set + { key: 'value' } + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); \ 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 63e6fdf9f2..b4c2133dad 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 @@ -13,30 +13,31 @@ import frappe.utils class TestDailyWorkSummary(unittest.TestCase): def test_email_trigger(self): self.setup_and_prepare_test() - for d in self.employees: - # check that email is sent to this employee - self.assertTrue(d.user_id in [d.recipient for d in self.emails - if self.settings.subject in d.message]) + for d in self.users: + # check that email is sent to users + self.assertTrue(d.email in [d.recipient for d in self.emails + if self.groups.subject in d.message]) def test_email_trigger_failed(self): - hour = '00' - if frappe.utils.nowtime().split(':')[0]=='00': - hour = '01' + hour = '00:00' + if frappe.utils.nowtime().split(':')[0] == '00': + hour = '01:00' self.setup_and_prepare_test(hour) - for d in self.employees: - # check that email is sent to this employee - self.assertFalse(d.user_id in [d.recipient for d in self.emails - if self.settings.subject in d.message]) + for d in self.users: + # check that email is not sent to users + self.assertFalse(d.email in [d.recipient for d in self.emails + if self.groups.subject in d.message]) def test_incoming(self): # get test mail with message-id as in-reply-to self.setup_and_prepare_test() with open(os.path.join(os.path.dirname(__file__), "test_data", "test-reply.raw"), "r") as f: - test_mails = [f.read().replace('{{ sender }}', self.employees[-1].user_id)\ - .replace('{{ message_id }}', self.emails[-1].message_id)] + test_mails = [f.read().replace('{{ sender }}', + self.users[-1].email).replace('{{ message_id }}', + self.emails[-1].message_id)] # pull the mail email_account = frappe.get_doc("Email Account", "_Test Email Account 1") @@ -55,30 +56,38 @@ class TestDailyWorkSummary(unittest.TestCase): frappe.db.sql('delete from `tabEmail Queue`') frappe.db.sql('delete from `tabEmail Queue Recipient`') frappe.db.sql('delete from `tabCommunication`') + frappe.db.sql('delete from `tabDaily Work Summary Group`') - self.setup_settings(hour) + self.users = frappe.get_all('User', + fields=['email'], + filters=dict(email=('!=', 'test@example.com'))) + self.setup_groups(hour) - from erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings \ + from erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group \ import trigger_emails trigger_emails() # check if emails are created - self.employees = frappe.get_all('Employee', fields = ['user_id'], - filters=dict(company='_Test Company', status='Active', user_id=('!=', 'test@example.com'))) - self.emails = frappe.db.sql("""select r.recipient, q.message, q.message_id from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r where q.name = r.parent""", as_dict=1) + self.emails = frappe.db.sql("""select r.recipient, q.message, q.message_id \ + from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r \ + where q.name = r.parent""", as_dict=1) frappe.db.commit() - def setup_settings(self, hour=None): - # setup email to trigger at this our + def setup_groups(self, hour=None): + # setup email to trigger at this hour if not hour: hour = frappe.utils.nowtime().split(':')[0] - self.settings = frappe.get_doc('Daily Work Summary Settings') - self.settings.companies = [] + hour = hour+':00' - self.settings.append('companies', dict(company='_Test Company', - send_emails_at=hour + ':00')) - self.settings.test_subject = 'this is a subject for testing summary emails' - self.settings.save() + groups = frappe.get_doc(dict(doctype="Daily Work Summary Group", + name="Daily Work Summary", + users=self.users, + send_emails_at=hour, + subject="this is a subject for testing summary emails", + message='this is a message for testing summary emails')) + groups.insert() + self.groups = groups + self.groups.save() diff --git a/erpnext/hr/doctype/daily_work_summary_settings/__init__.py b/erpnext/hr/doctype/daily_work_summary_group/__init__.py similarity index 100% rename from erpnext/hr/doctype/daily_work_summary_settings/__init__.py rename to erpnext/hr/doctype/daily_work_summary_group/__init__.py diff --git a/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.js b/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.js new file mode 100644 index 0000000000..43206d5dcf --- /dev/null +++ b/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.js @@ -0,0 +1,12 @@ +// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Daily Work Summary Group', { + refresh: function (frm) { + if (!frm.is_new()) { + 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.json b/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.json similarity index 56% rename from erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.json rename to erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.json index f52af39c78..562183f354 100644 --- a/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.json +++ b/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.json @@ -1,31 +1,35 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, + "autoname": "Prompt", "beta": 0, - "creation": "2016-11-08 04:55:08.231715", + "creation": "2018-02-12 15:06:18.767239", "custom": 0, "docstatus": 0, "doctype": "DocType", - "document_type": "Document", + "document_type": "", "editable_grid": 1, "engine": "InnoDB", "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "description": "Emails will be sent to all Active Employees of the company at the given hour, if they do not have holiday. Summary of responses will be sent at midnight.", - "fieldname": "select_companies", - "fieldtype": "Section Break", + "default": "1", + "fieldname": "enabled", + "fieldtype": "Check", "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": "Select Companies", + "label": "Enabled", "length": 0, "no_copy": 0, "permlevel": 0, @@ -41,22 +45,85 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "companies", + "fieldname": "select_users", + "fieldtype": "Section Break", + "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": "Select Users", + "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_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "users", "fieldtype": "Table", "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": "Companies", + "label": "Users", "length": 0, "no_copy": 0, - "options": "Daily Work Summary Settings Company", + "options": "Daily Work Summary Group User", + "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_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "send_emails_at", + "fieldtype": "Select", + "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": "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, @@ -70,19 +137,52 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "message_section", + "fieldname": "holiday_list", + "fieldtype": "Link", + "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": "Holiday List", + "length": 0, + "no_copy": 0, + "options": "Holiday List", + "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_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "mail_details", "fieldtype": "Section Break", "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": "Message", + "label": "Reminder", "length": 0, "no_copy": 0, "permlevel": 0, @@ -98,6 +198,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -109,6 +210,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Subject", @@ -127,6 +229,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -138,6 +241,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Message", @@ -156,20 +260,20 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 0, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, - "issingle": 1, + "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-11-21 00:55:20.726328", + "modified": "2018-02-16 10:56:03.998495", "modified_by": "Administrator", "module": "HR", - "name": "Daily Work Summary Settings", + "name": "Daily Work Summary Group", "name_case": "", "owner": "Administrator", "permissions": [ @@ -180,14 +284,13 @@ "create": 1, "delete": 1, "email": 1, - "export": 0, + "export": 1, "if_owner": 0, "import": 0, - "is_custom": 0, "permlevel": 0, "print": 1, "read": 1, - "report": 0, + "report": 1, "role": "HR Manager", "set_user_permissions": 0, "share": 1, @@ -198,7 +301,9 @@ "quick_entry": 1, "read_only": 0, "read_only_onload": 0, + "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", + "track_changes": 0, "track_seen": 0 } \ No newline at end of file diff --git a/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.py b/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.py new file mode 100644 index 0000000000..a549a9b6c9 --- /dev/null +++ b/erpnext/hr/doctype/daily_work_summary_group/daily_work_summary_group.py @@ -0,0 +1,58 @@ +# # -*- 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 +from frappe import _ + + +class DailyWorkSummaryGroup(Document): + def validate(self): + if self.users: + if not frappe.flags.in_test and not is_incoming_account_enabled(): + frappe.throw(_('Please enable default incoming account before creating Daily Work Summary Group')) + + +def trigger_emails(): + '''Send emails to Employees at the given hour asking + them what did they work on today''' + groups = frappe.get_all("Daily Work Summary Group") + for d in groups: + group_doc = frappe.get_doc("Daily Work Summary Group", d) + if (is_current_hour(group_doc.send_emails_at) + and not is_holiday_today(group_doc.holiday_list) + and group_doc.enabled): + emails = [d.email for d in group_doc.users] + # find emails relating to a company + if emails: + daily_work_summary = frappe.get_doc( + dict(doctype='Daily Work Summary', daily_work_summary_group=group_doc.name) + ).insert() + daily_work_summary.send_mails(group_doc, emails) + + +def is_current_hour(hour): + return frappe.utils.nowtime().split(':')[0] == hour.split(':')[0] + + +def is_holiday_today(holiday_list): + date = frappe.utils.today() + if holiday_list: + return frappe.get_all('Holiday List', + dict(name=holiday_list, holiday_date=date)) and True or False + else: + return False + + +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() + + +def is_incoming_account_enabled(): + return frappe.db.get_value('Email Account', dict(enable_incoming=1, default_incoming=1)) diff --git a/erpnext/hr/doctype/daily_work_summary_settings_company/__init__.py b/erpnext/hr/doctype/daily_work_summary_group_user/__init__.py similarity index 100% rename from erpnext/hr/doctype/daily_work_summary_settings_company/__init__.py rename to erpnext/hr/doctype/daily_work_summary_group_user/__init__.py diff --git a/erpnext/hr/doctype/daily_work_summary_settings_company/daily_work_summary_settings_company.json b/erpnext/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.json similarity index 73% rename from erpnext/hr/doctype/daily_work_summary_settings_company/daily_work_summary_settings_company.json rename to erpnext/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.json index be27fa3dcb..c3edad6b9f 100644 --- a/erpnext/hr/doctype/daily_work_summary_settings_company/daily_work_summary_settings_company.json +++ b/erpnext/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.json @@ -1,9 +1,10 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "beta": 0, - "creation": "2016-11-08 05:44:02.502527", + "creation": "2018-02-12 14:57:38.332692", "custom": 0, "docstatus": 0, "doctype": "DocType", @@ -12,22 +13,24 @@ "engine": "InnoDB", "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "company", + "fieldname": "user", "fieldtype": "Link", "hidden": 0, "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", + "label": "User", "length": 0, "no_copy": 0, - "options": "Company", + "options": "User", "permlevel": 0, "precision": "", "print_hide": 0, @@ -35,29 +38,30 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "default": "17:00", - "fieldname": "send_emails_at", - "fieldtype": "Select", + "fieldname": "email", + "fieldtype": "Read Only", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, - "in_list_view": 1, + "in_global_search": 0, + "in_list_view": 0, "in_standard_filter": 0, - "label": "Send Emails At", + "label": "email", "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", + "options": "user.email", "permlevel": 0, "precision": "", "print_hide": 0, @@ -65,33 +69,35 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "unique": 0 } ], + "has_web_view": 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": "2018-02-12 15:57:01.332287", "modified_by": "Administrator", "module": "HR", - "name": "Daily Work Summary Settings Company", + "name": "Daily Work Summary Group User", "name_case": "", "owner": "Administrator", "permissions": [], "quick_entry": 1, "read_only": 0, "read_only_onload": 0, + "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", + "track_changes": 1, "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_group_user/daily_work_summary_group_user.py similarity index 61% rename from erpnext/hr/doctype/daily_work_summary_settings_company/daily_work_summary_settings_company.py rename to erpnext/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.py index cd051b4457..eefcc0c3a6 100644 --- a/erpnext/hr/doctype/daily_work_summary_settings_company/daily_work_summary_settings_company.py +++ b/erpnext/hr/doctype/daily_work_summary_group_user/daily_work_summary_group_user.py @@ -1,10 +1,10 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2018, 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): +class DailyWorkSummaryGroupUser(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 deleted file mode 100644 index f5c0a5cb16..0000000000 --- a/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.js +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Daily Work Summary Settings', { - 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 deleted file mode 100644 index aea4c354ed..0000000000 --- a/erpnext/hr/doctype/daily_work_summary_settings/daily_work_summary_settings.py +++ /dev/null @@ -1,37 +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 -import frappe.utils -from frappe import _ -from erpnext.hr.doctype.daily_work_summary.daily_work_summary import get_employee_emails - -class DailyWorkSummarySettings(Document): - def validate(self): - if self.companies: - 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: - daily_work_summary = frappe.get_doc(dict(doctype='Daily Work Summary', - company=d.company)).insert() - 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 diff --git a/erpnext/hr/page/team_updates/team_updates.js b/erpnext/hr/page/team_updates/team_updates.js index d6dd1f498d..da1f5316a0 100644 --- a/erpnext/hr/page/team_updates/team_updates.js +++ b/erpnext/hr/page/team_updates/team_updates.js @@ -8,9 +8,9 @@ frappe.pages['team-updates'].on_page_load = function(wrapper) { frappe.team_updates.make(page); frappe.team_updates.run(); - if(frappe.model.can_read('Daily Work Summary Settings')) { - page.add_menu_item(__('Daily Work Summary Settings'), function() { - frappe.set_route('Form', 'Daily Work Summary Settings'); + if(frappe.model.can_read('Daily Work Summary Group')) { + page.add_menu_item(__('Daily Work Summary Group'), function() { + frappe.set_route('Form', 'Daily Work Summary Group'); }); } } diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 6b30422f51..331cd1e10a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -501,3 +501,4 @@ erpnext.patches.v10_0.set_b2c_limit erpnext.patches.v10_0.update_translatable_fields erpnext.patches.v10_0.rename_offer_letter_to_job_offer execute:frappe.delete_doc('DocType', 'Production Planning Tool', ignore_missing=True) +erpnext.patches.v10_0.migrate_daily_work_summary_settings_to_daily_work_summary_group diff --git a/erpnext/patches/v10_0/migrate_daily_work_summary_settings_to_daily_work_summary_group.py b/erpnext/patches/v10_0/migrate_daily_work_summary_settings_to_daily_work_summary_group.py new file mode 100644 index 0000000000..207b96e241 --- /dev/null +++ b/erpnext/patches/v10_0/migrate_daily_work_summary_settings_to_daily_work_summary_group.py @@ -0,0 +1,45 @@ +# Copyright (c) 2018, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + + +def execute(): + # check if Daily Work Summary Settings Company table exists + try: + frappe.db.sql('DESC `tabDaily Work Summary Settings Company`') + except Exception: + return + + # get the previously saved settings + previous_setting = get_previous_setting() + if previous_setting["companies"]: + for d in previous_setting["companies"]: + users = frappe.get_list("Employee", dict( + company=d.company, user_id=("!=", " ")), "user_id as user") + if(len(users)): + # create new group entry for each company entry + new_group = frappe.get_doc(dict(doctype="Daily Work Summary Group", + name="Daily Work Summary for " + d.company, + users=users, + send_emails_at=d.send_emails_at, + subject=previous_setting["subject"], + message=previous_setting["message"])) + new_group.insert(ignore_permissions=True) + new_group.save() + frappe.delete_doc("Daily Work Summary Settings") + frappe.delete_doc("Daily Work Summary Settings Company") + +def get_setting_companies(): + return frappe.db.sql("select * from `tabDaily Work Summary Settings Company`", as_dict=True) + + +def get_previous_setting(): + obj = {} + setting_data = frappe.db.sql( + "select field, value from tabSingles where doctype='Daily Work Summary Settings'") + for field, value in setting_data: + obj[field] = value + obj["companies"] = get_setting_companies() + return obj diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 81f909aa84..0c93be99e7 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -29,7 +29,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', 'Daily Work Summary Settings'): + 'Selling Settings', 'Stock Settings'): default_values = frappe.db.sql("""select fieldname, `default` from `tabDocField` where parent=%s""", dt) if default_values: