diff --git a/frappe/__init__.py b/frappe/__init__.py index 12f1157196..11378a1fa5 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -226,19 +226,23 @@ def get_request_header(key, default=None): return request.headers.get(key, default) def sendmail(recipients=(), sender="", subject="No Subject", message="No Message", - as_markdown=False, bulk=False): + as_markdown=False, bulk=False, ref_doctype=None, ref_docname=None, + add_unsubscribe_link=False): if bulk: import frappe.utils.email_lib.bulk frappe.utils.email_lib.bulk.send(recipients=recipients, sender=sender, - subject=subject, message=message, add_unsubscribe_link=False) + subject=subject, message=message, ref_doctype = ref_doctype, + ref_docname = ref_docname, add_unsubscribe_link=add_unsubscribe_link) else: import frappe.utils.email_lib if as_markdown: - frappe.utils.email_lib.sendmail_md(recipients, sender=sender, subject=subject, msg=message) + frappe.utils.email_lib.sendmail_md(recipients, sender=sender, + subject=subject, msg=message) else: - frappe.utils.email_lib.sendmail(recipients, sender=sender, subject=subject, msg=message) + frappe.utils.email_lib.sendmail(recipients, sender=sender, + subject=subject, msg=message) logger = None whitelisted = [] diff --git a/frappe/cli.py b/frappe/cli.py index 0116bed406..f874d31f96 100755 --- a/frappe/cli.py +++ b/frappe/cli.py @@ -772,7 +772,7 @@ def smtp_debug_server(): os.execv(python, [python, '-m', "smtpd", "-n", "-c", "DebuggingServer", "localhost:25"]) @cmd -def run_tests(app=None, module=None, doctype=None, verbose=False, tests=(), driver=None): +def run_tests(app=None, module=None, doctype=None, verbose=False, tests=(), driver=None, force=False): import frappe.test_runner from frappe.utils import sel @@ -781,7 +781,7 @@ def run_tests(app=None, module=None, doctype=None, verbose=False, tests=(), driv ret = 1 try: ret = frappe.test_runner.main(app and app[0], module and module[0], doctype and doctype[0], verbose, - tests=tests) + tests=tests, force=force) if len(ret.failures) == 0 and len(ret.errors) == 0: ret = 0 finally: diff --git a/frappe/config/setup.py b/frappe/config/setup.py index ce0d4e08ab..e00aed270c 100644 --- a/frappe/config/setup.py +++ b/frappe/config/setup.py @@ -121,6 +121,11 @@ def get_data(): "name": "Outgoing Email Settings", "description": _("Set outgoing mail server.") }, + { + "type": "doctype", + "name": "Email Alert", + "description": _("Setup Email Alert based on various criteria.") + }, { "type": "doctype", "name": "Standard Reply", diff --git a/frappe/core/doctype/comment/comment.json b/frappe/core/doctype/comment/comment.json index 9dcff41adb..66a2736f9e 100644 --- a/frappe/core/doctype/comment/comment.json +++ b/frappe/core/doctype/comment/comment.json @@ -1,22 +1,25 @@ { "autoname": "CWR/.#####", - "creation": "2012-08-08 10:40:11.000000", + "creation": "2012-08-08 10:40:11", "docstatus": 0, "doctype": "DocType", "fields": [ { "fieldname": "comment", "fieldtype": "Text", + "in_list_view": 1, "label": "Comment", "no_copy": 0, "oldfieldname": "comment", "oldfieldtype": "Text", "permlevel": 0, + "reqd": 1, "search_index": 0 }, { "fieldname": "comment_by", "fieldtype": "Data", + "in_list_view": 1, "label": "Comment By", "no_copy": 0, "oldfieldname": "comment_by", @@ -27,6 +30,7 @@ { "fieldname": "comment_by_fullname", "fieldtype": "Data", + "in_list_view": 1, "label": "Comment By Fullname", "no_copy": 0, "oldfieldname": "comment_by_fullname", @@ -37,6 +41,7 @@ { "fieldname": "comment_date", "fieldtype": "Date", + "in_list_view": 1, "label": "Comment Date", "no_copy": 0, "oldfieldname": "comment_date", @@ -47,6 +52,7 @@ { "fieldname": "comment_time", "fieldtype": "Data", + "in_list_view": 1, "label": "Comment Time", "no_copy": 0, "oldfieldname": "comment_time", @@ -94,7 +100,7 @@ "icon": "icon-comments", "idx": 1, "issingle": 0, - "modified": "2014-01-24 13:00:20.000000", + "modified": "2014-07-14 12:14:08.315217", "modified_by": "Administrator", "module": "Core", "name": "Comment", diff --git a/frappe/core/doctype/comment/test_records.json b/frappe/core/doctype/comment/test_records.json new file mode 100644 index 0000000000..29b35716bc --- /dev/null +++ b/frappe/core/doctype/comment/test_records.json @@ -0,0 +1,6 @@ +[ + { + "doctype": "Comment", + "name":"_Test Comment 1", + } +] diff --git a/frappe/core/doctype/doctype/doctype_template.py b/frappe/core/doctype/doctype/boilerplate/controller.py similarity index 74% rename from frappe/core/doctype/doctype/doctype_template.py rename to frappe/core/doctype/doctype/boilerplate/controller.py index 369bd592d8..1d5ae0895d 100644 --- a/frappe/core/doctype/doctype/doctype_template.py +++ b/frappe/core/doctype/doctype/boilerplate/controller.py @@ -1,4 +1,4 @@ -# Copyright (c) 2013, {app_publisher} +# Copyright (c) 2013, {app_publisher} and contributors # For license information, please see license.txt from __future__ import unicode_literals @@ -6,4 +6,4 @@ import frappe from frappe.model.document import Document class {classname}(Document): - pass \ No newline at end of file + pass diff --git a/frappe/core/doctype/doctype/boilerplate/test_controller.py b/frappe/core/doctype/doctype/boilerplate/test_controller.py new file mode 100644 index 0000000000..cc12c9b9c8 --- /dev/null +++ b/frappe/core/doctype/doctype/boilerplate/test_controller.py @@ -0,0 +1,10 @@ +# Copyright (c) 2013, {app_publisher} and Contributors +# See license.txt + +import frappe +import unittest + +test_records = frappe.get_test_records('{doctype}') + +class Test{classname}(unittest.TestCase): + pass diff --git a/frappe/core/doctype/doctype/boilerplate/test_records.json b/frappe/core/doctype/doctype/boilerplate/test_records.json new file mode 100644 index 0000000000..9c261424c3 --- /dev/null +++ b/frappe/core/doctype/doctype/boilerplate/test_records.json @@ -0,0 +1,6 @@ +[ + {{ + "doctype": "{doctype}", + "name":"_Test {doctype} 1", + }} +] diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 12333f289a..269bade04c 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -145,20 +145,24 @@ class DocType(Document): def make_controller_template(self): from frappe.modules import get_doc_path, get_module_path, scrub - pypath = os.path.join(get_doc_path(self.module, - self.doctype, self.name), scrub(self.name) + '.py') - - if not os.path.exists(pypath): - # get app publisher for copyright - app = frappe.local.module_app[frappe.scrub(self.module)] - if not app: - frappe.throw(_("App not found")) - app_publisher = frappe.get_hooks(hook="app_publisher", app_name=app)[0] - - with open(pypath, 'w') as pyfile: - with open(os.path.join(get_module_path("core"), "doctype", "doctype", - "doctype_template.py"), 'r') as srcfile: - pyfile.write(srcfile.read().format(app_publisher=app_publisher, classname=self.name.replace(" ", ""))) + target_path = get_doc_path(self.module, self.doctype, self.name) + + app = frappe.local.module_app[scrub(self.module)] + if not app: + frappe.throw(_("App not found")) + app_publisher = frappe.get_hooks(hook="app_publisher", app_name=app)[0] + + for template in ["controller.py", "test_controller.py", "test_records.json"]: + template_name = template.replace("controller", scrub(self.name)) + target_file_path = os.path.join(target_path, template_name) + if not os.path.exists(target_file_path): + + with open(target_file_path, 'w') as target: + with open(os.path.join(get_module_path("core"), "doctype", "doctype", + "boilerplate", template), 'r') as source: + target.write(source.read().format(app_publisher=app_publisher, + classname=self.name.replace(" ", ""), doctype=self.name)) + def make_amendable(self): """ diff --git a/frappe/core/doctype/email_alert/email_alert.js b/frappe/core/doctype/email_alert/email_alert.js index a7352296ca..b7fb717063 100644 --- a/frappe/core/doctype/email_alert/email_alert.js +++ b/frappe/core/doctype/email_alert/email_alert.js @@ -3,14 +3,23 @@ frappe.email_alert = { // get the doctype to update fields frappe.model.with_doctype(frm.doc.document_type, function() { - frm._doctype_fields = $.map(frappe.get_doc("DocType", frm.doc.document_type).fields, + var fields = frappe.get_doc("DocType", frm.doc.document_type).fields; + + var options = $.map(fields, function(d) { return in_list(frappe.model.no_value_type, d.fieldtype) ? null : d.fieldname; }); - var options = "\n" + frm._doctype_fields.join("\n"); + options = "\n" + options.join("\n"); + // set value changed options frm.set_df_property("value_changed", "options", options); + // set date changed options + frm.set_df_property("date_changed", "options", $.map(fields, + function(d) { return (d.fieldtype=="Date" || d.fieldtype=="Datetime") ? + d.fieldname : null; })); + + // set email recipient options frappe.meta.get_docfield("Email Alert Recipient", "email_by_document_field", frm.doc.name).options = options; @@ -19,9 +28,9 @@ frappe.email_alert = { } frappe.ui.form.on("Email Alert", "refresh", function(frm) { - frappe.email_alert.setup_fieldname_select(frm) + frappe.email_alert.setup_fieldname_select(frm); }); frappe.ui.form.on("Email Alert", "document_type", function(frm) { - frappe.email_alert.setup_fieldname_select(frm) + frappe.email_alert.setup_fieldname_select(frm); }); diff --git a/frappe/core/doctype/email_alert/email_alert.json b/frappe/core/doctype/email_alert/email_alert.json index ee32ead908..5e29174184 100644 --- a/frappe/core/doctype/email_alert/email_alert.json +++ b/frappe/core/doctype/email_alert/email_alert.json @@ -26,7 +26,8 @@ "label": "Document Type", "options": "DocType", "permlevel": 0, - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "fieldname": "event", @@ -35,16 +36,27 @@ "label": "Send Alert On", "options": "\nNew\nSave\nSubmit\nCancel\nDate Change\nValue Change", "permlevel": 0, - "reqd": 1 + "reqd": 1, + "search_index": 1 + }, + { + "depends_on": "eval:doc.event==\"Date Change\"", + "description": "Send alert if date matches this field's value", + "fieldname": "date_changed", + "fieldtype": "Select", + "label": "Date Changed", + "permlevel": 0 }, { "depends_on": "eval:doc.event==\"Value Change\"", + "description": "Send alert if this field's value changes", "fieldname": "value_changed", "fieldtype": "Select", "label": "Value Changed", "permlevel": 0 }, { + "depends_on": "", "description": "Optional: The alert will be sent if this expression is true", "fieldname": "condition", "fieldtype": "Data", @@ -99,7 +111,7 @@ } ], "icon": "icon-envelope", - "modified": "2014-07-11 17:57:11.471260", + "modified": "2014-07-14 12:55:10.467991", "modified_by": "Administrator", "module": "Core", "name": "Email Alert", diff --git a/frappe/core/doctype/email_alert/email_alert.py b/frappe/core/doctype/email_alert/email_alert.py index 9d397369ed..3e28dd9a32 100644 --- a/frappe/core/doctype/email_alert/email_alert.py +++ b/frappe/core/doctype/email_alert/email_alert.py @@ -3,7 +3,88 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.model.document import Document +from frappe.utils import validate_email_add class EmailAlert(Document): - pass \ No newline at end of file + def validate(self): + if self.event=="Date Changed" and not self.date_changed: + frappe.throw(_("Please specify which date field must be checked")) + + if self.event=="Value Changed" and not self.value_changed: + frappe.throw(_("Please specify which value field must be checked")) + +def trigger_daily_alerts(): + trigger_email_alerts(None, "Date Change") + +def trigger_email_alerts(doc, method=None): + if method=="Date Change": + for alert in frappe.db.sql_list("""select name from `tabEmail Alert` + where event='Date Change'"""): + + alert = frappe.get_doc("Email Alert", alert) + for name in frappe.db.sql_list("""select name from `tab%s` where + DATE(%s) = CURDATE()""" % (alert.document_type, alert.date_changed)): + + evaluate_alert(frappe.get_doc(alert.document_type, name), + alert, "Date Change") + else: + if method in ("on_update", "validate") and doc.get("__in_insert"): + # don't call email alerts multiple times for inserts + # on insert only "New" type alert must be called + return + + eevent = { + "on_update": "Save", + "after_insert": "New", + "validate": "Value Change", + "on_submit": "Submit", + "on_cancel": "Cancel", + }[method] + + for alert in frappe.db.sql_list("""select name from `tabEmail Alert` + where document_type=%s and event=%s""", (doc.doctype, eevent)): + evaluate_alert(doc, alert, eevent) + +def evaluate_alert(doc, alert, event): + if isinstance(alert, basestring): + alert = frappe.get_doc("Email Alert", alert) + if alert.condition: + if not eval(alert.condition): + return + + if event=="Value Change" and not doc.is_new(): + if doc.get(alert.value_changed) == frappe.db.get_value(doc.doctype, + doc.name, alert.value_changed): + return # value not changed + + for recipient in alert.email_alert_recipients: + recipients = [] + if recipient.condition: + if not eval(recipient.condition): + continue + if recipient.email_by_document_field: + if validate_email_add(doc.get(recipient.email_by_document_field)): + recipients.append(doc.get(recipient.email_by_document_field)) + # else: + # print "invalid email" + if recipient.cc: + recipient.cc = recipient.cc.replace(",", "\n") + recipients = recipients + recipient.cc.split("\n") + + if not recipients: + return + + template = alert.message + footer + # send alert + frappe.sendmail(recipients=recipients, subject=alert.subject, + message= frappe.render_template(template, {"doc": doc, "alert":alert}), + bulk=True, ref_doctype = doc.doctype, ref_docname = doc.name) + + +footer = """
+This Email Alert {{alert.name}} was autogenerated for +{{ doc.doctype }} {{doc.name}}. +To update, modify it, go to Setup > Email > Email Alert +""" diff --git a/frappe/core/doctype/email_alert/test_email_alert.py b/frappe/core/doctype/email_alert/test_email_alert.py new file mode 100644 index 0000000000..8b9bbc8582 --- /dev/null +++ b/frappe/core/doctype/email_alert/test_email_alert.py @@ -0,0 +1,97 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# See license.txt + +import frappe, frappe.utils, frappe.utils.scheduler +import unittest + +test_records = frappe.get_test_records('Email Alert') + +class TestEmailAlert(unittest.TestCase): + def setUp(self): + frappe.db.sql("""delete from `tabBulk Email`""") + + def test_new_and_save(self): + frappe.set_user("test1@example.com") + comment = frappe.new_doc("Comment") + comment.comment = "test" + comment.insert(ignore_permissions=True) + + self.assertTrue(frappe.db.get_value("Bulk Email", {"ref_doctype": "Comment", + "ref_docname": comment.name, "status":"Not Sent"})) + + frappe.db.sql("""delete from `tabBulk Email`""") + + comment.description = "test" + comment.save() + + self.assertTrue(frappe.db.get_value("Bulk Email", {"ref_doctype": "Comment", + "ref_docname": comment.name, "status":"Not Sent"})) + + def test_condition(self): + frappe.set_user("test1@example.com") + event = frappe.new_doc("Event") + event.subject = "test", + event.event_type = "Private" + event.starts_on = "2014-06-06 12:00:00" + event.insert() + + self.assertFalse(frappe.db.get_value("Bulk Email", {"ref_doctype": "Event", + "ref_docname": event.name, "status":"Not Sent"})) + + event.event_type = "Public" + event.save() + + self.assertTrue(frappe.db.get_value("Bulk Email", {"ref_doctype": "Event", + "ref_docname": event.name, "status":"Not Sent"})) + + def test_value_changed(self): + frappe.set_user("test1@example.com") + event = frappe.new_doc("Event") + event.subject = "test", + event.event_type = "Private" + event.starts_on = "2014-06-06 12:00:00" + event.insert() + + self.assertFalse(frappe.db.get_value("Bulk Email", {"ref_doctype": "Event", + "ref_docname": event.name, "status":"Not Sent"})) + + event.subject = "test 1" + event.save() + + self.assertFalse(frappe.db.get_value("Bulk Email", {"ref_doctype": "Event", + "ref_docname": event.name, "status":"Not Sent"})) + + event.description = "test" + event.save() + + self.assertTrue(frappe.db.get_value("Bulk Email", {"ref_doctype": "Event", + "ref_docname": event.name, "status":"Not Sent"})) + + def test_date_changed(self): + frappe.set_user("test1@example.com") + event = frappe.new_doc("Event") + event.subject = "test", + event.event_type = "Private" + event.starts_on = "2014-01-01 12:00:00" + event.insert() + + self.assertFalse(frappe.db.get_value("Bulk Email", {"ref_doctype": "Event", + "ref_docname": event.name, "status":"Not Sent"})) + + frappe.utils.scheduler.trigger(frappe.local.site, "daily", now=True) + + # not today, so no alert + self.assertFalse(frappe.db.get_value("Bulk Email", {"ref_doctype": "Event", + "ref_docname": event.name, "status":"Not Sent"})) + + event.starts_on = frappe.utils.now() + event.save() + + self.assertFalse(frappe.db.get_value("Bulk Email", {"ref_doctype": "Event", + "ref_docname": event.name, "status":"Not Sent"})) + + frappe.utils.scheduler.trigger(frappe.local.site, "daily", now=True) + + # today so show alert + self.assertTrue(frappe.db.get_value("Bulk Email", {"ref_doctype": "Event", + "ref_docname": event.name, "status":"Not Sent"})) diff --git a/frappe/core/doctype/email_alert/test_records.json b/frappe/core/doctype/email_alert/test_records.json new file mode 100644 index 0000000000..39e4ee5bc7 --- /dev/null +++ b/frappe/core/doctype/email_alert/test_records.json @@ -0,0 +1,55 @@ +[ + { + "doctype": "Email Alert", + "subject":"_Test Email Alert 1", + "document_type": "Comment", + "event": "New", + "message": "New comment {{ doc.comment }} created", + "email_alert_recipients": [ + { "email_by_document_field": "owner" } + ] + }, + { + "doctype": "Email Alert", + "subject":"_Test Email Alert 2", + "document_type": "Comment", + "event": "Save", + "message": "New comment {{ doc.comment }} saved", + "email_alert_recipients": [ + { "email_by_document_field": "owner" } + ] + }, + { + "doctype": "Email Alert", + "subject":"_Test Email Alert 3", + "document_type": "Event", + "event": "Save", + "condition": "doc.event_type=='Public'", + "message": "A new public event {{ doc.subject }} on {{ doc.starts_on }} is created", + "email_alert_recipients": [ + { "email_by_document_field": "owner" } + ] + }, + { + "doctype": "Email Alert", + "subject":"_Test Email Alert 4", + "document_type": "Event", + "event": "Value Change", + "value_changed": "description", + "message": "Description changed", + "email_alert_recipients": [ + { "email_by_document_field": "owner" } + ] + }, + { + "doctype": "Email Alert", + "subject":"_Test Email Alert 5", + "document_type": "Event", + "event": "Date Change", + "date_changed": "starts_on", + "message": "Description changed", + "email_alert_recipients": [ + { "email_by_document_field": "owner" } + ] + } +] diff --git a/frappe/hooks.py b/frappe/hooks.py index 03c3a8121a..5534abb362 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -2,7 +2,7 @@ from frappe.__version__ import __version__ app_name = "frappe" app_title = "Frappe Framework" -app_publisher = "Web Notes Technologies Pvt. Ltd. and Contributors" +app_publisher = "Web Notes Technologies Pvt. Ltd." app_description = "Full Stack Web Application Framwork in Python" app_icon = "assets/frappe/images/frappe.svg" app_version = __version__ @@ -51,8 +51,17 @@ has_permission = { doc_events = { "*": { - "on_update": "frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications", - "on_cancel": "frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications", + "after_insert": "frappe.core.doctype.email_alert.email_alert.trigger_email_alerts", + "validate": "frappe.core.doctype.email_alert.email_alert.trigger_email_alerts", + "on_update": [ + "frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications", + "frappe.core.doctype.email_alert.email_alert.trigger_email_alerts" + ], + "on_submit": "frappe.core.doctype.email_alert.email_alert.trigger_email_alerts", + "on_cancel": [ + "frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications", + "frappe.core.doctype.email_alert.email_alert.trigger_email_alerts" + ], "on_trash": "frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications" }, "User Vote": { @@ -70,6 +79,7 @@ scheduler_events = { "frappe.core.doctype.notification_count.notification_count.delete_event_notification_count", "frappe.core.doctype.event.event.send_event_digest", "frappe.sessions.clear_expired_sessions", + "frappe.core.doctype.email_alert.email_alert.trigger_daily_alerts", ], "hourly": [ "frappe.website.doctype.website_group.website_group.clear_event_cache" diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 1095edf4bb..1661b33118 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -83,6 +83,10 @@ class BaseDocument(object): else: self.__dict__[key] = value + def delete(self, key): + if key in self.__dict__: + del self.__dict__[key] + def append(self, key, value=None): if value==None: value={} diff --git a/frappe/model/document.py b/frappe/model/document.py index 296173aa74..a8a0c08514 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -124,8 +124,11 @@ class Document(BaseDocument): self.set_new_name() self.run_method("before_insert") self.set_parent_in_children() + + self.set("__in_insert", True) self.run_before_save_methods() self._validate() + self.delete("__in_insert") # run validate, on update etc. @@ -140,7 +143,9 @@ class Document(BaseDocument): d.db_insert() self.run_method("after_insert") + self.set("__in_insert", True) self.run_post_save_methods() + self.delete("__in_insert") return self diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index b793af826c..96a9e4432a 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -51,7 +51,13 @@ frappe.Application = Class.extend({ // control panel startup code this.run_startup_js(); + if(frappe.boot) { + if(localStorage.getItem("session_lost_route")) { + window.location.hash = localStorage.getItem("session_lost_route"); + localStorage.removeItem("session_lost_route"); + } + // route to home page frappe.route(); } diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index 25951c5078..a848e44124 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -176,6 +176,7 @@ frappe.request.cleanup = function(opts, r) { // session expired? - Guest has no business here! if(r.session_expired || frappe.get_cookie("sid")==="Guest") { if(!frappe.app.logged_out) { + localStorage.setItem("session_lost_route", location.hash); msgprint(__('Session Expired. Logging you out')); frappe.app.logout(); } diff --git a/frappe/test_runner.py b/frappe/test_runner.py index 78ff15dce9..2e98a419e7 100644 --- a/frappe/test_runner.py +++ b/frappe/test_runner.py @@ -10,7 +10,7 @@ from frappe.modules import load_doctype_module, get_module_name from frappe.utils import cstr -def main(app=None, module=None, doctype=None, verbose=False, tests=()): +def main(app=None, module=None, doctype=None, verbose=False, tests=(), force=False): frappe.flags.print_messages = verbose frappe.flags.in_test = True @@ -29,7 +29,7 @@ def main(app=None, module=None, doctype=None, verbose=False, tests=()): frappe.get_attr(fn)() if doctype: - ret = run_tests_for_doctype(doctype, verbose=verbose, tests=tests) + ret = run_tests_for_doctype(doctype, verbose=verbose, tests=tests, force=force) elif module: ret = run_tests_for_module(module, verbose=verbose, tests=tests) else: @@ -63,10 +63,13 @@ def run_all_tests(app=None, verbose=False): return unittest.TextTestRunner(verbosity=1+(verbose and 1 or 0)).run(test_suite) -def run_tests_for_doctype(doctype, verbose=False, tests=()): +def run_tests_for_doctype(doctype, verbose=False, tests=(), force=False): module = frappe.db.get_value("DocType", doctype, "module") test_module = get_module_name(doctype, module, "test_") - make_test_records(doctype, verbose=verbose) + if force: + for name in frappe.db.sql_list("select name from `tab%s`" % doctype): + frappe.delete_doc(doctype, name, force=True) + make_test_records(doctype, verbose=verbose, force=force) module = frappe.get_module(test_module) return _run_unittest(module, verbose=verbose, tests=tests) @@ -107,7 +110,7 @@ def _add_test(path, filename, verbose, test_suite=None): module = imp.load_source(filename[:-3], os.path.join(path, filename)) test_suite.addTest(unittest.TestLoader().loadTestsFromModule(module)) -def make_test_records(doctype, verbose=0): +def make_test_records(doctype, verbose=0, force=False): frappe.flags.mute_emails = True if not frappe.db: @@ -119,8 +122,8 @@ def make_test_records(doctype, verbose=0): if options not in frappe.local.test_objects: frappe.local.test_objects[options] = [] - make_test_records(options, verbose) - make_test_records_for_doctype(options, verbose) + make_test_records(options, verbose, force) + make_test_records_for_doctype(options, verbose, force) def get_modules(doctype): module = frappe.db.get_value("DocType", doctype, "module") @@ -155,7 +158,7 @@ def get_dependencies(doctype): return options_list -def make_test_records_for_doctype(doctype, verbose=0): +def make_test_records_for_doctype(doctype, verbose=0, force=False): module, test_module = get_modules(doctype) if verbose: diff --git a/frappe/utils/email_lib/bulk.py b/frappe/utils/email_lib/bulk.py index ec15f4367b..2d09bddf8e 100644 --- a/frappe/utils/email_lib/bulk.py +++ b/frappe/utils/email_lib/bulk.py @@ -14,8 +14,8 @@ from frappe.utils import cint, get_url, nowdate class BulkLimitCrossedError(frappe.ValidationError): pass def send(recipients=None, sender=None, doctype='User', email_field='email', - subject='[No Subject]', message='[No Content]', ref_doctype=None, ref_docname=None, - add_unsubscribe_link=True): + subject='[No Subject]', message='[No Content]', ref_doctype=None, + ref_docname=None, add_unsubscribe_link=True): def is_unsubscribed(rdata): if not rdata: diff --git a/frappe/utils/scheduler.py b/frappe/utils/scheduler.py index 5491093b12..8772159907 100644 --- a/frappe/utils/scheduler.py +++ b/frappe/utils/scheduler.py @@ -12,7 +12,7 @@ from __future__ import unicode_literals import frappe import frappe.utils -from frappe.utils.file_lock import create_lock, check_lock, delete_lock, LockTimeoutError +from frappe.utils.file_lock import create_lock, check_lock, delete_lock from datetime import datetime DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S' @@ -95,7 +95,7 @@ def log(method, message=None): d = frappe.new_doc("Scheduler Log") d.method = method d.error = message - d.insert() + d.insert(ignore_permissions=True) frappe.db.commit()