Merge pull request #16419 from rohitwaghchaure/project_update_status_code_and_design_refactoring

fix: Project update status feature's code and design refactored
This commit is contained in:
Nabin Hait 2019-01-22 10:02:34 +05:30 committed by GitHub
commit 9adfd2d7b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 2289 additions and 1905 deletions

View File

@ -223,10 +223,15 @@ doc_events = {
}
scheduler_events = {
"all": [
"erpnext.projects.doctype.project.project.project_status_update_reminder"
],
"hourly": [
'erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails',
"erpnext.accounts.doctype.subscription.subscription.process_all",
"erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_mws_settings.schedule_get_order_details"
"erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_mws_settings.schedule_get_order_details",
"erpnext.projects.doctype.project.project.hourly_reminder",
"erpnext.projects.doctype.project.project.collect_project_status"
],
"daily": [
"erpnext.stock.reorder_item.reorder_item",
@ -245,7 +250,8 @@ scheduler_events = {
"erpnext.assets.doctype.asset.asset.update_maintenance_status",
"erpnext.assets.doctype.asset.asset.make_post_gl_entry",
"erpnext.crm.doctype.contract.contract.update_status_for_contracts",
"erpnext.projects.doctype.project.project.update_project_sales_billing"
"erpnext.projects.doctype.project.project.update_project_sales_billing",
"erpnext.projects.doctype.project.project.send_project_status_email_to_users"
],
"daily_long": [
"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms"

View File

@ -112,6 +112,10 @@ def get_user_emails_from_group(group):
if isinstance(group_doc, string_types):
group_doc = frappe.get_doc('Daily Work Summary Group', group)
emails = [d.email for d in group_doc.users if frappe.db.get_value("User", d.user, "enabled")]
emails = get_users_email(group_doc)
return emails
def get_users_email(doc):
return [d.email for d in doc.users
if frappe.db.get_value("User", d.user, "enabled")]

View File

@ -580,4 +580,5 @@ erpnext.patches.v11_0.update_delivery_trip_status
erpnext.patches.v10_0.repost_gle_for_purchase_receipts_with_rejected_items
erpnext.patches.v11_0.set_missing_gst_hsn_code
erpnext.patches.v11_0.rename_bom_wo_fields
erpnext.patches.v11_0.rename_additional_salary_component_additional_salary
erpnext.patches.v11_0.rename_additional_salary_component_additional_salary
erpnext.patches.v11_0.renamed_from_to_fields_in_project

View File

@ -0,0 +1,13 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.utils.rename_field import rename_field
def execute():
frappe.reload_doc('projects', 'doctype', 'project')
if frappe.db.has_column('Project', 'from'):
rename_field('Project', 'from', 'from_time')
rename_field('Project', 'to', 'to_time')

File diff suppressed because it is too large Load Diff

View File

@ -3,16 +3,16 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import flt, getdate, get_url, now
from frappe import _
from frappe.model.document import Document
from six import iteritems
from email_reply_parser import EmailReplyParser
from frappe.utils import (flt, getdate, get_url, now,
nowtime, get_time, today, get_datetime, add_days)
from erpnext.controllers.queries import get_filters_cond
from frappe.desk.reportview import get_match_cond
import datetime
from six import iteritems
from erpnext.hr.doctype.daily_work_summary.daily_work_summary import get_users_email
from erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group import is_holiday_today
from frappe.model.document import Document
class Project(Document):
def get_feed(self):
@ -406,56 +406,137 @@ def get_users_for_project(doctype, txt, searchfield, start, page_len, filters):
def get_cost_center_name(project):
return frappe.db.get_value("Project", project, "cost_center")
@frappe.whitelist()
def hourly_reminder():
project = frappe.db.sql("""SELECT `tabProject`.name FROM `tabProject` WHERE `tabProject`.frequency = "Hourly" and (CURTIME() BETWEEN `tabProject`.from and `tabProject`.to) AND `tabProject`.collect_progress = 1 ORDER BY `tabProject`.name;""")
create_project_update(project)
fields = ["from_time", "to_time"]
projects = get_projects_for_collect_progress("Hourly", fields)
@frappe.whitelist()
def twice_daily_reminder():
project = frappe.db.sql("""SELECT `tabProject User`.user FROM `tabProject User` INNER JOIN `tabProject` ON `tabProject`.project_name = `tabProject User`.parent WHERE (`tabProject`.frequency = "Twice Daily") AND ((`tabProject`.first_email BETWEEN DATE_ADD(curtime(), INTERVAL -15 MINUTE) AND DATE_ADD(curtime(), INTERVAL 15 MINUTE)) OR (`tabProject`.second_email BETWEEN DATE_ADD(curtime(), INTERVAL -15 MINUTE) AND DATE_ADD(curtime(), INTERVAL 15 MINUTE))) AND `tabProject`.collect_progress = 1;""")
create_project_update(project)
for project in projects:
if (get_time(nowtime()) >= get_time(project.from_time) or
get_time(nowtime()) <= get_time(project.to_time)):
send_project_update_email_to_users(project.name)
def project_status_update_reminder():
daily_reminder()
twice_daily_reminder()
weekly_reminder()
@frappe.whitelist()
def daily_reminder():
project = frappe.db.sql("""SELECT `tabProject User`.user FROM `tabProject User` INNER JOIN `tabProject` ON `tabProject`.project_name = `tabProject User`.parent WHERE (`tabProject`.frequency = "Daily") AND (`tabProject`.daily_time_to_send BETWEEN DATE_ADD(curtime(), INTERVAL -15 MINUTE) AND DATE_ADD(curtime(), INTERVAL 15 MINUTE)) AND `tabProject`.collect_progress = 1;""")
create_project_update(project)
fields = ["daily_time_to_send"]
projects = get_projects_for_collect_progress("Daily", fields)
@frappe.whitelist()
def weekly():
today = datetime.datetime.now().strftime("%A")
project = frappe.db.sql("""SELECT `tabProject User`.user FROM `tabProject User` INNER JOIN `tabProject` ON `tabProject`.project_name = `tabProject User`.parent WHERE (`tabProject`.frequency = "Weekly") AND (`tabProject`.day_to_send = %s) AND (`tabProject`.weekly_time_to_send BETWEEN DATE_ADD(curtime(), INTERVAL -15 MINUTE) AND DATE_ADD(curtime(), INTERVAL 15 MINUTE)) AND `tabProject`.collect_progress = 1""", today)
create_project_update(project)
for project in projects:
if not check_project_update_exists(project.name, project.get("daily_time_to_send")):
send_project_update_email_to_users(project.name)
#Call this function in order to generate the Project Update for a specific project
def create_project_update(project):
data = []
date_today = datetime.date.today()
time_now = frappe.utils.now_datetime().strftime('%H:%M:%S')
for projects in project:
project_update_dict = {
"doctype" : "Project Update",
"project" : projects[0],
"date": date_today,
"time": time_now,
"naming_series": "UPDATE-.project.-.YY.MM.DD.-"
def twice_daily_reminder():
fields = ["first_email", "second_email"]
projects = get_projects_for_collect_progress("Twice Daily", fields)
for project in projects:
for d in fields:
if not check_project_update_exists(project.name, project.get(d)):
send_project_update_email_to_users(project.name)
def weekly_reminder():
fields = ["day_to_send", "weekly_time_to_send"]
projects = get_projects_for_collect_progress("Weekly", fields)
current_day = get_datetime().strftime("%A")
for project in projects:
if current_day != project.day_to_send:
continue
if not check_project_update_exists(project.name, project.get("weekly_time_to_send")):
send_project_update_email_to_users(project.name)
def check_project_update_exists(project, time):
data = frappe.db.sql(""" SELECT name from `tabProject Update`
WHERE project = %s and date = %s and time >= %s """, (project, today(), time))
return True if data and data[0][0] else False
def get_projects_for_collect_progress(frequency, fields):
fields.extend(["name"])
return frappe.get_all("Project", fields = fields,
filters = {'collect_progress': 1, 'frequency': frequency})
def send_project_update_email_to_users(project):
doc = frappe.get_doc('Project', project)
if is_holiday_today(doc.holiday_list) or not doc.users: return
project_update = frappe.get_doc({
"doctype" : "Project Update",
"project" : project,
"sent": 0,
"date": today(),
"time": nowtime(),
"naming_series": "UPDATE-.project.-.YY.MM.DD.-",
}).insert()
subject = "For project %s, update your status" % (project)
incoming_email_account = frappe.db.get_value('Email Account',
dict(enable_incoming=1, default_incoming=1), 'email_id')
frappe.sendmail(recipients=get_users_email(doc),
message=doc.message,
subject=_(subject),
reference_doctype=project_update.doctype,
reference_name=project_update.name,
reply_to=incoming_email_account
)
def collect_project_status():
for data in frappe.get_all("Project Update",
{'date': today(), 'sent': 0}):
replies = frappe.get_all('Communication',
fields=['content', 'text_content', 'sender'],
filters=dict(reference_doctype="Project Update",
reference_name=data.name,
communication_type='Communication',
sent_or_received='Received'),
order_by='creation asc')
for d in replies:
doc = frappe.get_doc("Project Update", data.name)
user_data = frappe.db.get_values("User", {"email": d.sender},
["full_name", "user_image", "name"], as_dict=True)[0]
doc.append("users", {
'user': user_data.name,
'full_name': user_data.full_name,
'image': user_data.user_image,
'project_status': frappe.utils.md_to_html(
EmailReplyParser.parse_reply(d.text_content) or d.content
)
})
doc.save(ignore_permissions=True)
def send_project_status_email_to_users():
yesterday = add_days(today(), -1)
for d in frappe.get_all("Project Update",
{'date': yesterday, 'sent': 0}):
doc = frappe.get_doc("Project Update", d.name)
project_doc = frappe.get_doc('Project', doc.project)
args = {
"users": doc.users,
"title": _("Project Summary for {0}").format(yesterday)
}
project_update = frappe.get_doc(project_update_dict)
project_update.insert()
#you can edit your local_host
local_host = "http://localhost:8003"
project_update_url = "<a class = 'btn btn-primary' href=%s target='_blank'>" % (local_host +"/desk#Form/Project%20Update/" + (project_update.name)) + ("CREATE PROJECT UPDATE" + "</a>")
data.append(project_update_url)
email = frappe.db.sql("""SELECT user from `tabProject User` WHERE parent = %s;""", project[0])
for emails in email:
frappe.sendmail(
recipients=emails,
subject=frappe._(projects[0]),
header=[frappe._("Please Update your Project Status"), 'blue'],
message= project_update_url
)
return data
frappe.sendmail(recipients=get_users_email(project_doc),
template='daily_project_summary',
args=args,
subject=_("Daily Project Summary for {0}").format(d.name),
reference_doctype="Project Update",
reference_name=d.name)
doc.db_set('sent', 1)
def update_project_sales_billing():
sales_update_frequency = frappe.db.get_single_value("Selling Settings", "sales_update_frequency")

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@ -13,6 +14,40 @@
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "naming_series",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Series",
"length": 0,
"no_copy": 0,
"options": "PROJ-UPD-.YYYY.-",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -46,6 +81,39 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "sent",
"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": "Sent",
"length": 0,
"no_copy": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -172,39 +240,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "progress",
"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": "How is the Project Progressing Right Now?",
"length": 0,
"no_copy": 0,
"options": "Not Updated\nGreat/Quickly\nGood/Steady\nChallenging/Slow\nProblematic/Stuck",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -238,38 +273,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "progress_details",
"fieldtype": "Text Editor",
"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": "Progress Details",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -301,40 +304,6 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "naming_series",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Series",
"length": 0,
"no_copy": 0,
"options": "PROJ-UPD-.YYYY.-",
"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,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
@ -347,7 +316,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-21 14:44:21.287709",
"modified": "2019-01-16 19:31:05.210656",
"modified_by": "Administrator",
"module": "Projects",
"name": "Project Update",

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@ -44,6 +45,136 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "user.email",
"fieldname": "email",
"fieldtype": "Read Only",
"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",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "user.user_image",
"fieldname": "image",
"fieldtype": "Read Only",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Image",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column 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,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "user.full_name",
"fieldname": "full_name",
"fieldtype": "Read Only",
"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": "Full Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@ -107,6 +238,70 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_5",
"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,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:parent.doctype == 'Project Update'",
"fieldname": "project_status",
"fieldtype": "Text",
"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": "Project Status",
"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,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
@ -119,7 +314,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-09-09 12:39:38.376816",
"modified": "2019-01-17 17:10:05.339735",
"modified_by": "Administrator",
"module": "Projects",
"name": "Project User",

View File

@ -0,0 +1,46 @@
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<h3>{{ title }}</h3>
</tr>
</table>
{% for user in users %}
<table class="panel-header" border="0" cellpadding="0" cellspacing="0" width="100%">
<tr height="10"></tr>
<tr>
<td width="15"></td>
<td valign="top" width="24">
{% if user.image %}
<img class="sender-avatar" width="24" height="24" embed="{{ user.image }}"/>
{% else %}
<div class="sender-avatar-placeholder">
{{ user.full_name[0] }}
</div>
{% endif %}
</td>
<td width="10"></td>
<td>
<div class="text-medium text-muted">
<span>{{ user.full_name }}</span>
</div>
</td>
<td width="15"></td>
</tr>
<tr height="10"></tr>
</table>
<table class="panel-body" border="0" cellpadding="0" cellspacing="0" width="100%">
<tr height="10"></tr>
<tr>
<td width="15"></td>
<td>
<div>
{{ user.project_status }}
</div>
</td>
<td width="15"></td>
</tr>
<tr height="10"></tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tr height="20"></tr>
</table>
{% endfor %}