瀏覽代碼

added email alerts

version-14
Rushabh Mehta 11 年之前
committed by Anand Doshi
父節點
當前提交
b9f6bc3423
共有 22 個文件被更改,包括 371 次插入47 次删除
  1. +8
    -4
      frappe/__init__.py
  2. +2
    -2
      frappe/cli.py
  3. +5
    -0
      frappe/config/setup.py
  4. +8
    -2
      frappe/core/doctype/comment/comment.json
  5. +6
    -0
      frappe/core/doctype/comment/test_records.json
  6. +2
    -2
      frappe/core/doctype/doctype/boilerplate/controller.py
  7. +10
    -0
      frappe/core/doctype/doctype/boilerplate/test_controller.py
  8. +6
    -0
      frappe/core/doctype/doctype/boilerplate/test_records.json
  9. +18
    -14
      frappe/core/doctype/doctype/doctype.py
  10. +13
    -4
      frappe/core/doctype/email_alert/email_alert.js
  11. +15
    -3
      frappe/core/doctype/email_alert/email_alert.json
  12. +82
    -1
      frappe/core/doctype/email_alert/email_alert.py
  13. +97
    -0
      frappe/core/doctype/email_alert/test_email_alert.py
  14. +55
    -0
      frappe/core/doctype/email_alert/test_records.json
  15. +13
    -3
      frappe/hooks.py
  16. +4
    -0
      frappe/model/base_document.py
  17. +5
    -0
      frappe/model/document.py
  18. +6
    -0
      frappe/public/js/frappe/desk.js
  19. +1
    -0
      frappe/public/js/frappe/request.js
  20. +11
    -8
      frappe/test_runner.py
  21. +2
    -2
      frappe/utils/email_lib/bulk.py
  22. +2
    -2
      frappe/utils/scheduler.py

+ 8
- 4
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 = []


+ 2
- 2
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:


+ 5
- 0
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",


+ 8
- 2
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",


+ 6
- 0
frappe/core/doctype/comment/test_records.json 查看文件

@@ -0,0 +1,6 @@
[
{
"doctype": "Comment",
"name":"_Test Comment 1",
}
]

frappe/core/doctype/doctype/doctype_template.py → 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
pass

+ 10
- 0
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

+ 6
- 0
frappe/core/doctype/doctype/boilerplate/test_records.json 查看文件

@@ -0,0 +1,6 @@
[
{{
"doctype": "{doctype}",
"name":"_Test {doctype} 1",
}}
]

+ 18
- 14
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):
"""


+ 13
- 4
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);
});

+ 15
- 3
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",


+ 82
- 1
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
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 = """<div style='margin-top: 20px; font-size: 80%; color: #888'>
This Email Alert {{alert.name}} was autogenerated for
{{ doc.doctype }} <a href="/desk#Form/{{doc.doctype}}/{{doc.name}}">{{doc.name}}</a>.
To update, modify it, go to Setup > Email > <a href="/desk#List/Email Alert">Email Alert</a>
"""

+ 97
- 0
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"}))

+ 55
- 0
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" }
]
}
]

+ 13
- 3
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"


+ 4
- 0
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={}


+ 5
- 0
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



+ 6
- 0
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();
}


+ 1
- 0
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();
}


+ 11
- 8
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:


+ 2
- 2
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:


+ 2
- 2
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()



Loading…
取消
儲存