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
This commit is contained in:
Suraj Shetty 2018-02-21 15:15:43 +05:30 committed by Rushabh Mehta
parent b8c088edb7
commit d3069fee4d
21 changed files with 410 additions and 175 deletions

View File

@ -244,8 +244,8 @@ def get_data():
},
{
"type": "doctype",
"name": "Daily Work Summary Settings"
},
"name": "Daily Work Summary Group"
}
]
},
{

View File

@ -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.
<img class="screenshot" alt="Department" src="{{docs_base_url}}/assets/img/human-resources/department.png">

View File

@ -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",

View File

@ -2,7 +2,7 @@
// For license information, please see license.txt
frappe.ui.form.on('Daily Work Summary', {
refresh: function(frm) {
refresh: function (frm) {
}
});

View File

@ -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
}

View File

@ -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

View File

@ -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()
]);
});

View File

@ -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()

View File

@ -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');
});
}
}
});

View File

@ -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
}

View File

@ -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))

View File

@ -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
}

View File

@ -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

View File

@ -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');
});
}
});

View File

@ -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()

View File

@ -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');
});
}
}

View File

@ -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

View File

@ -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

View File

@ -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: