Daily Reminder (#12938)

* for email sending

* test commit for new branch

* Removed test changes

* test commit renzo

* Email sending

* Project Uo

* Project Update

* "My first commit"

* "My second commit"

* "My second commit"

* Holiday is included

* delete idea

* first pull

* first pull

* "My third commit"

* Delete idea

* delete again

* "Edit Time"

* "Edit Time"

* Naming series and bug fixing

* "Edit current day and time"

* "Proper naming series, hidden time and date"

* Project and Project Update dashboard

* dashboard

* Remove hooks for PR

* Remove hooks for PR

* Remove hooks for PR

* Deleted project_time.py

* Corrected indention

* Hook back to original

* Delete project_time.py

* "Modified time"

* Fix indention

* Sample

* FRM

* FRM

* Time fix

* Hooks original state

* "Modified time"

* Added permission to Project User

* Added function/method to be called in order to create project update for the specific project

* Naming series

* this is not included

* Fix minor bug

* Indent again

* "Reformat Code"

* "Check Indent"

* Indent again and again

* semi colon

* "Check Again Indent"

* "Check again Indent"

* "Check again Indent"

* ind

* "Check again Indent"

* "Check again Indent"

* Generate Project update
With button in email

* []

* "Erro Summary"

* "Add syntax for the communcation"

* "add summary code"

* "Modified Summary code"

* "Modified Summary code"

* "Fix update ID and set localhost"

* Fix time and date error in project_update
Fix naming series problem in project_update

* included "not updated" in project update

* Bug in Number of Drafts

* "add notes in summary"

* Correct code

* With error

* Removed the method

* Minor fixes

* Correction for daily summary
This commit is contained in:
renzodemie 2018-03-20 14:28:15 +08:00 committed by Nabin Hait
parent aa4d7a3436
commit 417dfed214
12 changed files with 981 additions and 92 deletions

View File

@ -12,6 +12,11 @@ def get_data():
"name": "Project", "name": "Project",
"description": _("Project master."), "description": _("Project master."),
}, },
{
"type": "doctype",
"name": "Project Update",
"description": _("Project Update."),
},
{ {
"type": "doctype", "type": "doctype",
"name": "Task", "name": "Task",

View File

@ -1,6 +1,5 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Project", { frappe.ui.form.on("Project", {
setup: function (frm) { setup: function (frm) {
frm.set_indicator_formatter('title', frm.set_indicator_formatter('title',
@ -8,11 +7,9 @@ frappe.ui.form.on("Project", {
let indicator = 'orange'; let indicator = 'orange';
if (doc.status == 'Overdue') { if (doc.status == 'Overdue') {
indicator = 'red'; indicator = 'red';
} } else if (doc.status == 'Cancelled') {
else if (doc.status == 'Cancelled') {
indicator = 'dark grey'; indicator = 'dark grey';
} } else if (doc.status == 'Closed') {
else if (doc.status == 'Closed') {
indicator = 'green'; indicator = 'green';
} }
return indicator; return indicator;
@ -62,7 +59,9 @@ frappe.ui.form.on("Project", {
if (frappe.model.can_read("Task")) { if (frappe.model.can_read("Task")) {
frm.add_custom_button(__("Gantt Chart"), function () { frm.add_custom_button(__("Gantt Chart"), function () {
frappe.route_options = {"project": frm.doc.name}; frappe.route_options = {
"project": frm.doc.name
};
frappe.set_route("List", "Task", "Gantt"); frappe.set_route("List", "Task", "Gantt");
}); });
} }
@ -80,29 +79,6 @@ frappe.ui.form.on("Project", {
} }
}); });
}, },
show_dashboard: function(frm) {
if(frm.doc.__onload.activity_summary.length) {
var hours = $.map(frm.doc.__onload.activity_summary, function(d) { return d.total_hours });
var max_count = Math.max.apply(null, hours);
var sum = hours.reduce(function(a, b) { return a + b; }, 0);
var section = frm.dashboard.add_section(
frappe.render_template('project_dashboard',
{
data: frm.doc.__onload.activity_summary,
max_count: max_count,
sum: sum
}));
section.on('click', '.time-sheet-link', function() {
var activity_type = $(this).attr('data-activity_type');
frappe.set_route('List', 'Timesheet',
{'activity_type': activity_type, 'project': frm.doc.name, 'status': ["!=", "Cancelled"]});
});
}
}
});
frappe.ui.form.on("Project Task", {
edit_task: function (frm, doctype, name) { edit_task: function (frm, doctype, name) {
var doc = frappe.get_doc(doctype, name); var doc = frappe.get_doc(doctype, name);
if (doc.task_id) { if (doc.task_id) {
@ -113,10 +89,12 @@ frappe.ui.form.on("Project Task", {
}, },
edit_timesheet: function (frm, cdt, cdn) { edit_timesheet: function (frm, cdt, cdn) {
var child = locals[cdt][cdn]; var child = locals[cdt][cdn];
frappe.route_options = {"project": frm.doc.project_name, "task": child.task_id}; frappe.route_options = {
"project": frm.doc.project_name,
"task": child.task_id
};
frappe.set_route("List", "Timesheet"); frappe.set_route("List", "Timesheet");
}, },
make_timesheet: function (frm, cdt, cdn) { make_timesheet: function (frm, cdt, cdn) {
var child = locals[cdt][cdn]; var child = locals[cdt][cdn];
frappe.model.with_doctype('Timesheet', function () { frappe.model.with_doctype('Timesheet', function () {
@ -131,4 +109,29 @@ frappe.ui.form.on("Project Task", {
status: function (frm, doctype, name) { status: function (frm, doctype, name) {
frm.trigger('tasks_refresh'); frm.trigger('tasks_refresh');
}, },
});
frappe.ui.form.on("Project", "validate", function (frm) {
frappe.call({
method: "erpnext.projects.doctype.project.project.times_check",
args: {
"from1": frm.doc.from,
"to": frm.doc.to,
"first_email": frm.doc.first_email,
"second_email": frm.doc.second_email,
"daily_time_to_send": frm.doc.daily_time_to_send,
"weekly_time_to_send": frm.doc.weekly_time_to_send
},
callback: function (r) {
frm.set_value("from", r.message.from1);
frm.set_value("to", r.message.to);
frm.set_value("first_email", r.message.first_email);
frm.set_value("second_email", r.message.second_email);
frm.set_value("daily_time_to_send", r.message.daily_time_to_send);
frm.set_value("weekly_time_to_send", r.message.weekly_time_to_send);
}
});
}); });

View File

@ -1272,6 +1272,347 @@
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"columns": 0,
"fieldname": "monitor_progress",
"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": "Monitor Progress",
"length": 0,
"no_copy": 0,
"options": "",
"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": "collect_progress",
"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": "Collect Progress",
"length": 0,
"no_copy": 0,
"options": "",
"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,
"depends_on": "eval:doc.collect_progress == true",
"fieldname": "frequency",
"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": "Frequency To Collect Progress",
"length": 0,
"no_copy": 0,
"options": "Hourly\nTwice Daily\nDaily\nWeekly",
"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": "column_break_45",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:(doc.frequency == \"Hourly\" && doc.collect_progress == true)",
"fieldname": "from",
"fieldtype": "Time",
"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": "From",
"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,
"depends_on": "eval:(doc.frequency == \"Hourly\" && doc.collect_progress == true)",
"fieldname": "to",
"fieldtype": "Time",
"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": "To",
"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,
"depends_on": "eval:(doc.frequency == \"Twice Daily\" && doc.collect_progress == true)\n\n",
"fieldname": "first_email",
"fieldtype": "Time",
"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": "First 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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:(doc.frequency == \"Twice Daily\" && doc.collect_progress == true)",
"fieldname": "second_email",
"fieldtype": "Time",
"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": "Second 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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:(doc.frequency == \"Daily\" && doc.collect_progress == true)",
"fieldname": "daily_time_to_send",
"fieldtype": "Time",
"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": "Time to send",
"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,
"depends_on": "eval:(doc.frequency == \"Weekly\" && doc.collect_progress == true)",
"fieldname": "day_to_send",
"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": "Day to Send",
"length": 0,
"no_copy": 0,
"options": "Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday",
"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,
"depends_on": "eval:(doc.frequency == \"Weekly\" && doc.collect_progress == true)",
"fieldname": "weekly_time_to_send",
"fieldtype": "Time",
"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": "Time to send",
"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
} }
], ],
"has_web_view": 0, "has_web_view": 0,
@ -1285,7 +1626,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 4, "max_attachments": 4,
"modified": "2017-12-10 08:40:46.843201", "modified": "2018-01-29 11:48:21.156697",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Projects", "module": "Projects",
"name": "Project", "name": "Project",

View File

@ -10,6 +10,7 @@ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.controllers.queries import get_filters_cond from erpnext.controllers.queries import get_filters_cond
from frappe.desk.reportview import get_match_cond from frappe.desk.reportview import get_match_cond
import datetime
from six import iteritems from six import iteritems
@ -79,7 +80,8 @@ class Project(Document):
if task.task_weight or 0 > 0: if task.task_weight or 0 > 0:
sum = sum + task.task_weight sum = sum + task.task_weight
if sum > 0 and sum != 1: if sum > 0 and sum != 1:
frappe.throw(_("Total of all task weights should be 1. Please adjust weights of all Project tasks accordingly")) frappe.throw(
_("Total of all task weights should be 1. Please adjust weights of all Project tasks accordingly"))
def sync_tasks(self): def sync_tasks(self):
"""sync tasks and remove table""" """sync tasks and remove table"""
@ -133,12 +135,12 @@ class Project(Document):
if self.sales_order: if self.sales_order:
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name) frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
def update_percent_complete(self): def update_percent_complete(self):
total = frappe.db.sql("""select count(name) from tabTask where project=%s""", self.name)[0][0] total = frappe.db.sql("""select count(name) from tabTask where project=%s""", self.name)[0][0]
if not total and self.percent_complete: if not total and self.percent_complete:
self.percent_complete = 0 self.percent_complete = 0
if (self.percent_complete_method == "Task Completion" and total > 0) or (not self.percent_complete_method and total > 0): if (self.percent_complete_method == "Task Completion" and total > 0) or (
not self.percent_complete_method and total > 0):
completed = frappe.db.sql("""select count(name) from tabTask where completed = frappe.db.sql("""select count(name) from tabTask where
project=%s and status in ('Closed', 'Cancelled')""", self.name)[0][0] project=%s and status in ('Closed', 'Cancelled')""", self.name)[0][0]
self.percent_complete = flt(flt(completed) / total * 100, 2) self.percent_complete = flt(flt(completed) / total * 100, 2)
@ -186,7 +188,8 @@ class Project(Document):
self.update_sales_amount() self.update_sales_amount()
self.update_billed_amount() self.update_billed_amount()
self.gross_margin = flt(self.total_billed_amount) - (flt(self.total_costing_amount) + flt(self.total_expense_claim) + flt(self.total_purchase_cost)) self.gross_margin = flt(self.total_billed_amount) - (
flt(self.total_costing_amount) + flt(self.total_expense_claim) + flt(self.total_purchase_cost))
if self.total_billed_amount: if self.total_billed_amount:
self.per_gross_margin = (self.gross_margin / flt(self.total_billed_amount)) * 100 self.per_gross_margin = (self.gross_margin / flt(self.total_billed_amount)) * 100
@ -209,7 +212,6 @@ class Project(Document):
self.total_billed_amount = total_billed_amount and total_billed_amount[0][0] or 0 self.total_billed_amount = total_billed_amount and total_billed_amount[0][0] or 0
def send_welcome_email(self): def send_welcome_email(self):
url = get_url("/project/?name={0}".format(self.name)) url = get_url("/project/?name={0}".format(self.name))
messages = ( messages = (
@ -225,7 +227,8 @@ class Project(Document):
for user in self.users: for user in self.users:
if user.welcome_email_sent == 0: if user.welcome_email_sent == 0:
frappe.sendmail(user.user, subject=_("Project Collaboration Invitation"), content=content.format(*messages)) frappe.sendmail(user.user, subject=_("Project Collaboration Invitation"),
content=content.format(*messages))
user.welcome_email_sent = 1 user.welcome_email_sent = 1
def on_update(self): def on_update(self):
@ -259,8 +262,12 @@ class Project(Document):
dependency_map[task.title] = [x['subject'] for x in frappe.get_list( dependency_map[task.title] = [x['subject'] for x in frappe.get_list(
'Task Depends On', {"parent": name}, ['subject'])] 'Task Depends On', {"parent": name}, ['subject'])]
for key, value in dependency_map.iteritems():
task_name = frappe.db.get_value('Task', {"subject": key, "project": self.name})
for key, value in iteritems(dependency_map): for key, value in iteritems(dependency_map):
task_name = frappe.db.get_value('Task', {"subject": key, "project": self.name }) task_name = frappe.db.get_value('Task', {"subject": key, "project": self.name })
task_doc = frappe.get_doc('Task', task_name) task_doc = frappe.get_doc('Task', task_name)
for dt in value: for dt in value:
@ -268,6 +275,7 @@ class Project(Document):
task_doc.append('depends_on', {"task": dt_name}) task_doc.append('depends_on', {"task": dt_name})
task_doc.save() task_doc.save()
def get_timeline_data(doctype, name): def get_timeline_data(doctype, name):
'''Return timeline for attendance''' '''Return timeline for attendance'''
return dict(frappe.db.sql('''select unix_timestamp(from_time), count(*) return dict(frappe.db.sql('''select unix_timestamp(from_time), count(*)
@ -276,6 +284,7 @@ def get_timeline_data(doctype, name):
and docstatus < 2 and docstatus < 2
group by date(from_time)''', name)) group by date(from_time)''', name))
def get_project_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by="modified"): def get_project_list(doctype, txt, filters, limit_start, limit_page_length=20, order_by="modified"):
return frappe.db.sql('''select distinct project.* return frappe.db.sql('''select distinct project.*
from tabProject project, `tabProject User` project_user from tabProject project, `tabProject User` project_user
@ -290,6 +299,7 @@ def get_project_list(doctype, txt, filters, limit_start, limit_page_length=20, o
as_dict=True, as_dict=True,
update={'doctype': 'Project'}) update={'doctype': 'Project'})
def get_list_context(context=None): def get_list_context(context=None):
return { return {
"show_sidebar": True, "show_sidebar": True,
@ -325,6 +335,75 @@ def get_users_for_project(doctype, txt, searchfield, start, page_len, filters):
'page_len': page_len 'page_len': page_len
}) })
@frappe.whitelist() @frappe.whitelist()
def get_cost_center_name(project): def get_cost_center_name(project):
return frappe.db.get_value("Project", project, "cost_center") 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)
@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)
@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)
@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)
@frappe.whitelist()
def times_check(from1, to, first_email, second_email, daily_time_to_send, weekly_time_to_send):
from1 = datetime.datetime.strptime(from1, "%H:%M:%S")
from1 = from1.strftime("%H:00:00")
to = datetime.datetime.strptime(to, "%H:%M:%S")
to = to.strftime("%H:00:00")
first_email = datetime.datetime.strptime(first_email, "%H:%M:%S")
first_email = first_email.strftime("%H:00:00")
second_email = datetime.datetime.strptime(second_email, "%H:%M:%S")
second_email = second_email.strftime("%H:00:00")
daily_time_to_send = datetime.datetime.strptime(daily_time_to_send, "%H:%M:%S")
daily_time_to_send = daily_time_to_send.strftime("%H:00:00")
weekly_time_to_send = datetime.datetime.strptime(weekly_time_to_send, "%H:%M:%S")
weekly_time_to_send = weekly_time_to_send.strftime("%H:00:00")
return {"from1": from1, "to": to, "first_email": first_email, "second_email": second_email,"daily_time_to_send": daily_time_to_send, "weekly_time_to_send": weekly_time_to_send}
#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.-"
}
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

View File

@ -8,7 +8,7 @@ def get_data():
'transactions': [ 'transactions': [
{ {
'label': _('Project'), 'label': _('Project'),
'items': ['Task', 'Timesheet', 'Expense Claim', 'Issue'] 'items': ['Task', 'Timesheet', 'Expense Claim', 'Issue' , 'Project Update']
}, },
{ {
'label': _('Material'), 'label': _('Material'),

View File

@ -0,0 +1,17 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Project Update', {
refresh: function() {
},
onload: function (frm) {
frm.set_value("naming_series", "UPDATE-.project.-.YY.MM.DD.-.####");
},
validate: function (frm) {
frm.set_value("time", frappe.datetime.now_time());
frm.set_value("date", frappe.datetime.nowdate());
}
});

View File

@ -0,0 +1,366 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "naming_series:",
"beta": 0,
"creation": "2018-01-18 09:44:47.565494",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "project",
"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": "Project",
"length": 0,
"no_copy": 0,
"options": "Project",
"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": "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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "date",
"fieldtype": "Date",
"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": "Date",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "time",
"fieldtype": "Time",
"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": "Time",
"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,
"unique": 0
},
{
"allow_bulk_edit": 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,
"unique": 0
},
{
"allow_bulk_edit": 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,
"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": "Users",
"length": 0,
"no_copy": 0,
"options": "Project 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": 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": "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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"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": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Project Update",
"permlevel": 0,
"print_hide": 1,
"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_bulk_edit": 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": "UPDATE-.project.-.YY.MM.DD.-.####",
"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
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-02-14 10:50:16.794621",
"modified_by": "Administrator",
"module": "Projects",
"name": "Project Update",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Projects User",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0,
"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

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# 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 ProjectUpdate(Document):
pass
@frappe.whitelist()
def daily_reminder():
project = frappe.db.sql("""SELECT `tabProject`.project_name,`tabProject`.frequency,`tabProject`.expected_start_date,`tabProject`.expected_end_date,`tabProject`.percent_complete FROM `tabProject`;""")
for projects in project:
project_name = projects[0]
frequency = projects[1]
date_start = projects[2]
date_end = projects [3]
progress = projects [4]
draft = frappe.db.sql("""SELECT count(docstatus) from `tabProject Update` WHERE `tabProject Update`.project = %s AND `tabProject Update`.docstatus = 0;""",project_name)
for drafts in draft:
number_of_drafts = drafts[0]
update = frappe.db.sql("""SELECT name,date,time,progress,progress_details FROM `tabProject Update` WHERE `tabProject Update`.project = %s AND date = DATE_ADD(CURDATE(), INTERVAL -1 DAY);""",project_name)
email_sending(project_name,frequency,date_start,date_end,progress,number_of_drafts,update)
def email_sending(project_name,frequency,date_start,date_end,progress,number_of_drafts,update):
holiday = frappe.db.sql("""SELECT holiday_date FROM `tabHoliday` where holiday_date = CURDATE();""")
msg = "<p>Project Name: " + project_name + "</p><p>Frequency: " + " " + frequency + "</p><p>Update Reminder:" + " " + str(date_start) + "</p><p>Expected Date End:" + " " + str(date_end) + "</p><p>Percent Progress:" + " " + str(progress) + "</p><p>Number of Updates:" + " " + str(len(update)) + "</p>" + "</p><p>Number of drafts:" + " " + str(number_of_drafts) + "</p>"
msg += """</u></b></p><table class='table table-bordered'><tr>
<th>Project ID</th><th>Date Updated</th><th>Time Updated</th><th>Project Status</th><th>Notes</th>"""
for updates in update:
msg += "<tr><td>" + str(updates[0]) + "</td><td>" + str(updates[1]) + "</td><td>" + str(updates[2]) + "</td><td>" + str(updates[3]) + "</td>" + "</td><td>" + str(updates[4]) + "</td></tr>"
msg += "</table>"
if len(holiday) == 0:
email = frappe.db.sql("""SELECT user from `tabProject User` WHERE parent = %s;""", project_name)
for emails in email:
frappe.sendmail(recipients=emails,subject=frappe._(project_name + ' ' + 'Summary'),message = msg)
else:
pass

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: Project Update", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Project Update
() => frappe.tests.make('Project Update', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
class TestProjectUpdate(unittest.TestCase):
pass
test_records = frappe.get_test_records('Project Update')
test_ignore = ["Sales Order"]