diff --git a/conf/Framework.sql b/conf/Framework.sql
index 35a8aabc01..5920a8cacf 100644
--- a/conf/Framework.sql
+++ b/conf/Framework.sql
@@ -160,7 +160,7 @@ DROP TABLE IF EXISTS `tabSeries`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tabSeries` (
- `name` varchar(40) DEFAULT NULL,
+ `name` varchar(100) DEFAULT NULL,
`current` int(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
diff --git a/core/doctype/letter_head/letter_head.py b/core/doctype/letter_head/letter_head.py
index 61ba745fba..d0d0471f27 100755
--- a/core/doctype/letter_head/letter_head.py
+++ b/core/doctype/letter_head/letter_head.py
@@ -25,8 +25,6 @@ import webnotes
sql = webnotes.conn.sql
-test_records = []
-
class DocType:
def __init__(self, doc, doclist=[]):
self.doc = doc
diff --git a/core/doctype/letter_head/test_letter_head.py b/core/doctype/letter_head/test_letter_head.py
new file mode 100644
index 0000000000..3afb472bae
--- /dev/null
+++ b/core/doctype/letter_head/test_letter_head.py
@@ -0,0 +1 @@
+test_records = []
\ No newline at end of file
diff --git a/core/doctype/profile/profile.py b/core/doctype/profile/profile.py
index e1a6788fe4..f4f02acd60 100644
--- a/core/doctype/profile/profile.py
+++ b/core/doctype/profile/profile.py
@@ -277,16 +277,4 @@ def get_perm_info(arg=None):
def get_defaults(arg=None):
return webnotes.conn.sql("""select defkey, defvalue from tabDefaultValue where
parent=%s and parenttype = 'Profile'""", webnotes.form_dict['profile'])
-
-test_records = [[{
- "doctype":"Profile",
- "email": "test@erpnext.com",
- "first_name": "_Test",
- "new_password": "testpassword"
-}],
-[{
- "doctype":"Profile",
- "email": "test1@erpnext.com",
- "first_name": "_Test1",
- "new_password": "testpassword"
-}]]
\ No newline at end of file
+
\ No newline at end of file
diff --git a/core/doctype/profile/test_profile.py b/core/doctype/profile/test_profile.py
new file mode 100644
index 0000000000..a041818853
--- /dev/null
+++ b/core/doctype/profile/test_profile.py
@@ -0,0 +1,12 @@
+test_records = [[{
+ "doctype":"Profile",
+ "email": "test@erpnext.com",
+ "first_name": "_Test",
+ "new_password": "testpassword"
+}],
+[{
+ "doctype":"Profile",
+ "email": "test1@erpnext.com",
+ "first_name": "_Test1",
+ "new_password": "testpassword"
+}]]
\ No newline at end of file
diff --git a/core/doctype/workflow_state/test_workflow_state.py b/core/doctype/workflow_state/test_workflow_state.py
new file mode 100644
index 0000000000..3afb472bae
--- /dev/null
+++ b/core/doctype/workflow_state/test_workflow_state.py
@@ -0,0 +1 @@
+test_records = []
\ No newline at end of file
diff --git a/core/doctype/workflow_state/workflow_state.py b/core/doctype/workflow_state/workflow_state.py
index 63a1d72dae..e669908514 100644
--- a/core/doctype/workflow_state/workflow_state.py
+++ b/core/doctype/workflow_state/workflow_state.py
@@ -22,8 +22,6 @@
from __future__ import unicode_literals
import webnotes
-test_records = []
-
class DocType:
def __init__(self, d, dl):
self.doc, self.doclist = d, dl
\ No newline at end of file
diff --git a/public/js/wn/views/communication.js b/public/js/wn/views/communication.js
index e11dc9fd3c..3484fcdaba 100644
--- a/public/js/wn/views/communication.js
+++ b/public/js/wn/views/communication.js
@@ -55,9 +55,17 @@ wn.views.CommunicationList = Class.extend({
},
add_reply: function() {
+ var subject = this.doc.subject;
+ if(!subject && this.list.length) {
+ // get subject from previous message
+ subject = this.list[0].subject;
+ if(strip(subject.toLowerCase().split(":")[0])!="re") {
+ subject = "Re: " + subject;
+ }
+ }
new wn.views.CommunicationComposer({
doc: this.doc,
- subject: this.doc.subject,
+ subject: subject,
recipients: this.recipients
})
},
diff --git a/webnotes/__init__.py b/webnotes/__init__.py
index c5f25e282a..7223584a7d 100644
--- a/webnotes/__init__.py
+++ b/webnotes/__init__.py
@@ -72,7 +72,8 @@ debug_log = []
message_log = []
mute_emails = False
test_objects = {}
-
+request_method = None
+print_messages = False
user_lang = False
lang = 'en'
@@ -114,20 +115,20 @@ def getTraceback():
return utils.getTraceback()
def errprint(msg):
- """
- Append to the :data:`debug log`
- """
+ if not request_method:
+ print repr(msg)
+
from utils import cstr
debug_log.append(cstr(msg or ''))
def msgprint(msg, small=0, raise_exception=0, as_table=False):
- """
- Append to the :data:`message_log`
- """
from utils import cstr
if as_table and type(msg) in (list, tuple):
msg = '
' + ''.join([''+''.join(['| %s | ' % c for c in r])+'
' for r in msg]) + '
'
+ if print_messages:
+ print "Message: " + repr(msg)
+
message_log.append((small and '__small:' or '')+cstr(msg or ''))
if raise_exception:
import inspect
@@ -137,11 +138,7 @@ def msgprint(msg, small=0, raise_exception=0, as_table=False):
raise ValidationError, msg
def create_folder(path):
- """
- Wrapper function for os.makedirs (does not throw exception if directory exists)
- """
import os
-
try:
os.makedirs(path)
except OSError, e:
@@ -149,11 +146,7 @@ def create_folder(path):
raise e
def create_symlink(source_path, link_path):
- """
- Wrapper function for os.symlink (does not throw exception if directory exists)
- """
import os
-
try:
os.symlink(source_path, link_path)
except OSError, e:
@@ -161,11 +154,7 @@ def create_symlink(source_path, link_path):
raise e
def remove_file(path):
- """
- Wrapper function for os.remove (does not throw exception if file/symlink does not exists)
- """
import os
-
try:
os.remove(path)
except OSError, e:
@@ -173,9 +162,6 @@ def remove_file(path):
raise e
def connect(db_name=None, password=None):
- """
- Connect to this db (or db), if called from command prompt
- """
import webnotes.db
global conn
conn = webnotes.db.Database(user=db_name, password=password)
@@ -185,7 +171,7 @@ def connect(db_name=None, password=None):
import webnotes.profile
global user
- user = webnotes.profile.Profile('Administrator')
+ user = webnotes.profile.Profile('Administrator')
def get_env_vars(env_var):
import os
@@ -391,4 +377,5 @@ def get_application_home_page(user='Guest'):
if hpl:
return hpl[0][0]
else:
- return conn.get_value('Control Panel',None,'home_page') or 'Login Page'
\ No newline at end of file
+ from startup import application_home_page
+ return application_home_page
diff --git a/webnotes/db.py b/webnotes/db.py
index 9c3ca5b1f6..b87e235e84 100644
--- a/webnotes/db.py
+++ b/webnotes/db.py
@@ -97,7 +97,11 @@ class Database:
if values!=():
if isinstance(values, dict):
values = dict(values)
- if debug: webnotes.errprint(query % values)
+ if debug:
+ try:
+ webnotes.errprint(query % values)
+ except TypeError:
+ webnotes.errprint([query, values])
self._cursor.execute(query, values)
else:
@@ -126,11 +130,15 @@ class Database:
else:
return self._cursor.fetchall()
- def sql_list(self, query, values=()):
- return [r[0] for r in self.sql(query, values)]
+ def sql_list(self, query, values=(), debug=False):
+ return [r[0] for r in self.sql(query, values, debug=debug)]
+
+ def sql_ddl(self, query, values=()):
+ self.commit()
+ self.sql(query)
def check_transaction_status(self, query):
- if self.in_transaction and query and query.strip().split()[0].lower() in ['start', 'alter', 'drop', 'create']:
+ if self.in_transaction and query and query.strip().split()[0].lower() in ['start', 'alter', 'drop', 'create', "begin"]:
raise Exception, 'This statement can cause implicit commit'
if query and query.strip().lower()=='start transaction':
@@ -240,15 +248,23 @@ class Database:
return " and ".join(conditions), filters
+ def get(self, doctype, filters=None, as_dict=False):
+ return self.get_value(doctype, filters, "*", as_dict=as_dict)
+
def get_value(self, doctype, filters=None, fieldname="name", ignore=None, as_dict=False):
"""Get a single / multiple value from a record.
For Single DocType, let filters be = None"""
+
+ if fieldname!="*" and isinstance(fieldname, basestring):
+ fieldname = "`" + fieldname + "`"
+
if filters is not None and (filters!=doctype or filters=='DocType'):
- fl = isinstance(fieldname, basestring) and fieldname or "`, `".join(fieldname)
+ fl = isinstance(fieldname, basestring) and fieldname or \
+ ("`" + "`, `".join(fieldname) + "`")
conditions, filters = self.build_conditions(filters)
try:
- r = self.sql("select `%s` from `tab%s` where %s" % (fl, doctype,
+ r = self.sql("select %s from `tab%s` where %s" % (fl, doctype,
conditions), filters, as_dict)
except Exception, e:
if e.args[0]==1054 and ignore:
@@ -360,7 +376,8 @@ class Database:
self.sql("start transaction")
def commit(self):
- self.sql("commit")
+ if self.in_transaction:
+ self.sql("commit")
def rollback(self):
self.sql("ROLLBACK")
diff --git a/webnotes/model/code.py b/webnotes/model/code.py
index ab528da9c2..ba083779b6 100644
--- a/webnotes/model/code.py
+++ b/webnotes/model/code.py
@@ -134,11 +134,16 @@ def get_doctype_class(doctype, module):
return DocType
+def get_module_name(doctype, module, prefix):
+ from webnotes.modules import scrub
+ _doctype, _module = scrub(doctype), scrub(module)
+ return '%s.doctype.%s.%s%s' % (_module, _doctype, prefix, _doctype)
+
def load_doctype_module(doctype, module, prefix=""):
from webnotes.modules import scrub
_doctype, _module = scrub(doctype), scrub(module)
try:
- module = __import__('%s.doctype.%s.%s%s' % (_module, _doctype, prefix, _doctype), fromlist=[''])
+ module = __import__(get_module_name(doctype, module, prefix), fromlist=[''])
return module
except ImportError, e:
return None
diff --git a/webnotes/model/meta.py b/webnotes/model/meta.py
index 05c41a6143..064eb15d22 100644
--- a/webnotes/model/meta.py
+++ b/webnotes/model/meta.py
@@ -25,8 +25,6 @@
from __future__ import unicode_literals
import webnotes
-#=================================================================================
-
def get_dt_values(doctype, fields, as_dict = 0):
return webnotes.conn.sql('SELECT %s FROM tabDocType WHERE name="%s"' % (fields, doctype), as_dict = as_dict)
@@ -39,21 +37,15 @@ def is_single(doctype):
except IndexError, e:
raise Exception, 'Cannot determine whether %s is single' % doctype
-#=================================================================================
-
def get_parent_dt(dt):
parent_dt = webnotes.conn.sql("""select parent from tabDocField
where fieldtype="Table" and options="%s" and (parent not like "old_parent:%%")
limit 1""" % dt)
return parent_dt and parent_dt[0][0] or ''
-#=================================================================================
-
def set_fieldname(field_id, fieldname):
webnotes.conn.set_value('DocField', field_id, 'fieldname', fieldname)
-#=================================================================================
-
def get_link_fields(doctype):
"""
Returns list of link fields for a doctype in tuple (fieldname, options, label)
@@ -71,8 +63,6 @@ def get_link_fields(doctype):
)
]
-#=================================================================================
-
def get_table_fields(doctype):
child_tables = [[d[0], d[1]] for d in webnotes.conn.sql("select options, fieldname from tabDocField \
where parent='%s' and fieldtype='Table'" % doctype, as_list=1)]
@@ -81,3 +71,8 @@ def get_table_fields(doctype):
where dt='%s' and fieldtype='Table'" % doctype, as_list=1)]
return child_tables + custom_child_tables
+
+def has_field(doctype, fieldname):
+ doclist = webnotes.model.doctype.get(doctype)
+ return doclist.get({"parent":doctype, "doctype":"DocField", "fieldname":fieldname})
+
\ No newline at end of file
diff --git a/webnotes/model/wrapper.py b/webnotes/model/wrapper.py
index b479f2402c..a704e71a0c 100644
--- a/webnotes/model/wrapper.py
+++ b/webnotes/model/wrapper.py
@@ -106,6 +106,7 @@ class ModelWrapper:
from webnotes.model.code import get_obj
self.obj = get_obj(doc=self.doc, doclist=self.doclist)
+ self.controller = self.obj
return self.obj
def to_dict(self):
@@ -195,6 +196,10 @@ class ModelWrapper:
self.set_doclist(self.obj.doclist)
+ def get_method(self, method):
+ self.make_obj()
+ return getattr(self.obj, method, None)
+
def save_main(self):
try:
self.doc.save(cint(self.doc.fields.get('__islocal')))
diff --git a/webnotes/test_runner.py b/webnotes/test_runner.py
index 575475b311..7032c76eed 100644
--- a/webnotes/test_runner.py
+++ b/webnotes/test_runner.py
@@ -5,65 +5,78 @@ if __name__=="__main__":
sys.path.extend([".", "app", "lib"])
import webnotes
-from webnotes.model.meta import get_link_fields
-from webnotes.model.code import load_doctype_module
+import unittest
+
+from webnotes.model.meta import get_link_fields, has_field
+from webnotes.model.code import load_doctype_module, get_module_name
+
def make_test_records(doctype, verbose=0):
webnotes.mute_emails = True
if not webnotes.conn:
webnotes.connect()
-
- # also include doctype itself
- options_list = list(set([options for fieldname, options, label
- in get_link_fields(doctype)] + [doctype]))
-
- for options in options_list:
+
+ for options in get_dependencies(doctype):
+
if options.startswith("link:"):
options = options[5:]
if options == "[Select]":
continue
-
+
if options not in webnotes.test_objects:
webnotes.test_objects[options] = []
make_test_records(options, verbose)
-
- load_module_and_make_records(options, verbose)
-
-def load_module_and_make_records(options, verbose=0):
- module = webnotes.conn.get_value("DocType", options, "module")
+ make_test_records_for_doctype(options, verbose)
- # get methods for either [doctype].py or test_[doctype].py
- doctype_module = load_doctype_module(options, module)
- test_module = load_doctype_module(options, module, "test_")
+def get_modules(doctype):
+ module = webnotes.conn.get_value("DocType", doctype, "module")
+ test_module = load_doctype_module(doctype, module, "test_")
+
+ return module, test_module
+
+def get_dependencies(doctype):
+ module, test_module = get_modules(doctype)
+
+ options_list = list(set([options for fieldname, options, label
+ in get_link_fields(doctype)] + [doctype]))
+
+ if hasattr(test_module, "test_dependencies"):
+ options_list += test_module.test_dependencies
+
+ if hasattr(test_module, "test_ignore"):
+ for doctype_name in test_module.test_ignore:
+ if doctype_name in options_list:
+ options_list.remove(doctype_name)
+
+ return options_list
+
+def make_test_records_for_doctype(doctype, verbose=0):
+ module, test_module = get_modules(doctype)
if hasattr(test_module, "make_test_records"):
- webnotes.test_objects[options] += test_module.make_test_records(verbose)
-
- elif hasattr(doctype_module, "make_test_records"):
- webnotes.test_objects[options] += doctype_module.make_test_records(verbose)
+ webnotes.test_objects[doctype] += test_module.make_test_records(verbose)
elif hasattr(test_module, "test_records"):
- webnotes.test_objects[options] += make_test_objects(test_module)
-
- elif hasattr(doctype_module, "test_records"):
- webnotes.test_objects[options] += make_test_objects(doctype_module)
+ webnotes.test_objects[doctype] += make_test_objects(doctype, test_module.test_records)
elif verbose:
- print_mandatory_fields(options)
+ print_mandatory_fields(doctype)
-def make_test_objects(obj):
- if isinstance(obj, list):
- test_records = obj
- else:
- # obj is a module object
- test_records = obj.test_records
-
+
+def make_test_objects(doctype, test_records):
records = []
+
for doclist in test_records:
+ if not "doctype" in doclist[0]:
+ doclist[0]["doctype"] = doctype
d = webnotes.model_wrapper((webnotes.doclist(doclist)).copy())
if webnotes.test_objects.get(d.doc.doctype):
# do not create test records, if already exists
return []
+ if has_field(d.doc.doctype, "naming_series"):
+ if not d.doc.naming_series:
+ d.doc.naming_series = "_T-" + d.doc.doctype + "-"
+
d.insert()
records.append(d.doc.name)
return records
@@ -78,6 +91,52 @@ def print_mandatory_fields(doctype):
print d.parent + ":" + d.fieldname + " | " + d.fieldtype + " | " + (d.options or "")
print
-if __name__=="__main__":
- make_test_records(sys.argv[1], verbose=1)
+def run_unittest(doctype):
+ module = webnotes.conn.get_value("DocType", doctype, "module")
+ test_module = get_module_name(doctype, module, "test_")
+ make_test_records(args.doctype[0], verbose=True)
+
+ try:
+ exec ('from %s import *' % test_module) in globals()
+ del sys.argv[1:]
+ unittest.main()
+
+ except ImportError, e:
+ print "No test module."
+
+def run_all_tests(verbose):
+ import os, imp
+ from webnotes.modules.utils import peval_doclist
+
+ for path, folders, files in os.walk("."):
+ for filename in files:
+ if filename.startswith("test_") and filename.endswith(".py"):
+ test_suite = unittest.TestSuite()
+ if os.path.basename(os.path.dirname(path))=="doctype":
+ txt_file = os.path.join(path, filename[5:].replace(".py", ".txt"))
+ with open(txt_file, 'r') as f:
+ doctype_doclist = peval_doclist(f.read())
+ doctype = doctype_doclist[0]["name"]
+ make_test_records(doctype, verbose)
+
+ module = imp.load_source('test', os.path.join(path, filename))
+ test_suite.addTest(unittest.TestLoader().loadTestsFromModule(module))
+ unittest.TextTestRunner(verbosity=2).run(test_suite)
+
+if __name__=="__main__":
+ import argparse
+
+ parser = argparse.ArgumentParser(description='Run tests.')
+ parser.add_argument('-d', '--doctype', nargs=1, metavar = "DOCTYPE",
+ help="test for doctype")
+ parser.add_argument('-v', '--verbose', default=False, action="store_true")
+
+ args = parser.parse_args()
+ webnotes.print_messages = args.verbose
+
+ if args.doctype:
+ run_unittest(args.doctype[0])
+ else:
+ run_all_tests(args.verbose)
+
diff --git a/webnotes/utils/email_lib/bulk.py b/webnotes/utils/email_lib/bulk.py
index 0f58f7ddaa..a9db2c4410 100644
--- a/webnotes/utils/email_lib/bulk.py
+++ b/webnotes/utils/email_lib/bulk.py
@@ -30,13 +30,10 @@ def send(recipients=None, sender=None, doctype='Profile', email_field='email',
subject='[No Subject]', message='[No Content]'):
"""send bulk mail if not unsubscribed and within conf.bulk_mail_limit"""
import webnotes
-
- if webnotes.mute_emails:
- return
-
+
def is_unsubscribed(rdata):
if not rdata: return 1
- return rdata[0]['unsubscribed']
+ return rdata.unsubscribed
def check_bulk_limit(new_mails):
import conf, startup
@@ -53,20 +50,22 @@ def send(recipients=None, sender=None, doctype='Profile', email_field='email',
webnotes.msgprint("""Monthly Bulk Mail Limit (%s) Crossed""" % monthly_bulk_mail_limit,
raise_exception=BulkLimitCrossedError)
- def add_unsubscribe_link(email):
+ def update_message(doc):
from webnotes.utils import get_request_site_address
import urllib
- return message + """""" % (get_request_site_address(),
urllib.urlencode({
"cmd": "webnotes.utils.email_lib.bulk.unsubscribe",
- "email": email,
+ "email": doc.get(email_field),
"type": doctype,
"email_field": email_field
}))
-
+
+ return updated
+
if not recipients: recipients = []
if not sender or sender == "Administrator":
sender = webnotes.conn.get_value('Email Settings', None, 'auto_mail_id')
@@ -85,9 +84,11 @@ def send(recipients=None, sender=None, doctype='Profile', email_field='email',
rdata = webnotes.conn.sql("""select * from `tab%s` where %s=%s""" % (doctype,
email_field, '%s'), r, as_dict=1)
- if not is_unsubscribed(rdata):
+ doc = rdata and rdata[0] or {}
+
+ if not is_unsubscribed(doc):
# add to queue
- add(r, sender, subject, add_unsubscribe_link(r), text_content)
+ add(r, sender, subject, update_message(doc), text_content)
def add(email, sender, subject, message, text_content = None):
"""add to bulk mail queue"""