@@ -5,14 +5,14 @@ | |||
{ | |||
'creation': '2009-05-12 11:19:11', | |||
'docstatus': 0, | |||
'modified': '2011-05-16 10:19:14', | |||
'modified': '2011-09-07 15:28:18', | |||
'modified_by': 'Administrator', | |||
'owner': 'Administrator' | |||
}, | |||
# These values are common for all DocType | |||
{ | |||
'_last_update': '1307624201', | |||
'_last_update': '1311674341', | |||
'allow_copy': 0, | |||
'allow_email': 0, | |||
'allow_print': 0, | |||
@@ -21,7 +21,6 @@ | |||
'doctype': 'DocType', | |||
'hide_heading': 0, | |||
'hide_toolbar': 0, | |||
'idx': 0, | |||
'issingle': 0, | |||
'istable': 0, | |||
'module': 'Core', | |||
@@ -32,7 +31,7 @@ | |||
'section_style': 'Simple', | |||
'server_code_error': ' ', | |||
'show_in_menu': 0, | |||
'version': 8 | |||
'version': 11 | |||
}, | |||
# These values are common for all DocField | |||
@@ -52,7 +51,8 @@ | |||
'parent': 'DocType', | |||
'parentfield': 'permissions', | |||
'parenttype': 'DocType', | |||
'read': 1 | |||
'read': 1, | |||
'role': 'Administrator' | |||
}, | |||
# DocType, DocType | |||
@@ -61,22 +61,13 @@ | |||
'name': 'DocType' | |||
}, | |||
# DocPerm | |||
{ | |||
'doctype': 'DocPerm', | |||
'permlevel': 0, | |||
'role': 'System Manager' | |||
}, | |||
# DocPerm | |||
{ | |||
'cancel': 0, | |||
'create': 1, | |||
'doctype': 'DocPerm', | |||
'execute': 0, | |||
'idx': 1, | |||
'permlevel': 0, | |||
'role': 'Administrator', | |||
'submit': 0, | |||
'write': 1 | |||
}, | |||
@@ -84,9 +75,7 @@ | |||
# DocPerm | |||
{ | |||
'doctype': 'DocPerm', | |||
'idx': 2, | |||
'permlevel': 1, | |||
'role': 'Administrator' | |||
'permlevel': 1 | |||
}, | |||
# DocField | |||
@@ -94,7 +83,6 @@ | |||
'doctype': 'DocField', | |||
'fieldtype': 'Section Break', | |||
'hidden': 0, | |||
'idx': 1, | |||
'label': 'Options', | |||
'oldfieldtype': 'Section Break', | |||
'reqd': 0, | |||
@@ -105,7 +93,6 @@ | |||
{ | |||
'doctype': 'DocField', | |||
'fieldtype': 'Column Break', | |||
'idx': 2, | |||
'label': 'Settings', | |||
'oldfieldtype': 'Column Break', | |||
'width': '50%' | |||
@@ -116,7 +103,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'module', | |||
'fieldtype': 'Link', | |||
'idx': 3, | |||
'label': 'Module', | |||
'oldfieldname': 'module', | |||
'oldfieldtype': 'Link', | |||
@@ -129,7 +115,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'version', | |||
'fieldtype': 'Int', | |||
'idx': 4, | |||
'label': 'Version', | |||
'oldfieldname': 'version', | |||
'oldfieldtype': 'Int' | |||
@@ -141,7 +126,6 @@ | |||
'fieldname': 'name', | |||
'fieldtype': 'Data', | |||
'hidden': 1, | |||
'idx': 5, | |||
'label': 'Name', | |||
'oldfieldname': 'name', | |||
'oldfieldtype': 'Data', | |||
@@ -155,7 +139,6 @@ | |||
'fieldname': 'autoname', | |||
'fieldtype': 'Data', | |||
'hidden': 0, | |||
'idx': 6, | |||
'label': 'Auto Name', | |||
'oldfieldname': 'autoname', | |||
'oldfieldtype': 'Data', | |||
@@ -169,7 +152,6 @@ | |||
'fieldname': 'owner', | |||
'fieldtype': 'Link', | |||
'hidden': 1, | |||
'idx': 7, | |||
'label': 'Owner', | |||
'oldfieldname': 'owner', | |||
'oldfieldtype': 'Link', | |||
@@ -182,7 +164,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'name_case', | |||
'fieldtype': 'Select', | |||
'idx': 8, | |||
'label': 'Name Case', | |||
'oldfieldname': 'name_case', | |||
'oldfieldtype': 'Select', | |||
@@ -195,7 +176,6 @@ | |||
'fieldname': 'search_fields', | |||
'fieldtype': 'Data', | |||
'hidden': 0, | |||
'idx': 9, | |||
'label': 'Search Fields', | |||
'oldfieldname': 'search_fields', | |||
'oldfieldtype': 'Data', | |||
@@ -210,7 +190,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'subject', | |||
'fieldtype': 'Data', | |||
'idx': 10, | |||
'label': 'Subject' | |||
}, | |||
@@ -221,7 +200,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'tag_fields', | |||
'fieldtype': 'Data', | |||
'idx': 11, | |||
'label': 'tag_fields' | |||
}, | |||
@@ -231,7 +209,6 @@ | |||
'fieldname': 'istable', | |||
'fieldtype': 'Check', | |||
'hidden': 0, | |||
'idx': 12, | |||
'label': 'Is Table', | |||
'oldfieldname': 'istable', | |||
'oldfieldtype': 'Check', | |||
@@ -245,7 +222,6 @@ | |||
'fieldname': 'read_only', | |||
'fieldtype': 'Check', | |||
'hidden': 0, | |||
'idx': 13, | |||
'label': 'Not In Search', | |||
'oldfieldname': 'read_only', | |||
'oldfieldtype': 'Check', | |||
@@ -258,7 +234,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'in_create', | |||
'fieldtype': 'Check', | |||
'idx': 14, | |||
'label': 'Not In Create', | |||
'oldfieldname': 'in_create', | |||
'oldfieldtype': 'Check' | |||
@@ -270,7 +245,6 @@ | |||
'fieldname': 'issingle', | |||
'fieldtype': 'Check', | |||
'hidden': 0, | |||
'idx': 15, | |||
'label': 'Is Single', | |||
'oldfieldname': 'issingle', | |||
'oldfieldtype': 'Check', | |||
@@ -284,7 +258,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'read_only_onload', | |||
'fieldtype': 'Check', | |||
'idx': 16, | |||
'label': 'Show Print First', | |||
'oldfieldname': 'read_only_onload', | |||
'oldfieldtype': 'Check' | |||
@@ -295,7 +268,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'show_in_menu', | |||
'fieldtype': 'Check', | |||
'idx': 17, | |||
'label': 'Show In Pages', | |||
'oldfieldname': 'show_in_menu', | |||
'oldfieldtype': 'Check' | |||
@@ -306,7 +278,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'document_type', | |||
'fieldtype': 'Select', | |||
'idx': 18, | |||
'label': 'Document Type', | |||
'oldfieldname': 'document_type', | |||
'oldfieldtype': 'Select', | |||
@@ -318,7 +289,6 @@ | |||
'doctype': 'DocField', | |||
'fieldtype': 'Column Break', | |||
'hidden': 0, | |||
'idx': 19, | |||
'label': 'Display', | |||
'oldfieldtype': 'Column Break', | |||
'reqd': 0, | |||
@@ -331,7 +301,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'is_transaction_doc', | |||
'fieldtype': 'Check', | |||
'idx': 20, | |||
'label': 'Is Transaction Doc', | |||
'oldfieldname': 'is_transaction_doc', | |||
'oldfieldtype': 'Check' | |||
@@ -342,7 +311,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'use_template', | |||
'fieldtype': 'Check', | |||
'idx': 21, | |||
'label': 'Use Template', | |||
'oldfieldname': 'use_template', | |||
'oldfieldtype': 'Check' | |||
@@ -354,7 +322,6 @@ | |||
'fieldname': 'print_outline', | |||
'fieldtype': 'Select', | |||
'hidden': 0, | |||
'idx': 22, | |||
'label': 'Print Outline', | |||
'oldfieldname': 'print_outline', | |||
'oldfieldtype': 'Select', | |||
@@ -369,7 +336,6 @@ | |||
'fieldname': 'allow_print', | |||
'fieldtype': 'Check', | |||
'hidden': 0, | |||
'idx': 23, | |||
'label': 'Hide Print', | |||
'oldfieldname': 'allow_print', | |||
'oldfieldtype': 'Check', | |||
@@ -383,7 +349,6 @@ | |||
'fieldname': 'allow_email', | |||
'fieldtype': 'Check', | |||
'hidden': 0, | |||
'idx': 24, | |||
'label': 'Hide Email', | |||
'oldfieldname': 'allow_email', | |||
'oldfieldtype': 'Check', | |||
@@ -396,7 +361,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'in_dialog', | |||
'fieldtype': 'Check', | |||
'idx': 25, | |||
'label': 'In Dialog', | |||
'oldfieldname': 'in_dialog', | |||
'oldfieldtype': 'Check' | |||
@@ -408,7 +372,6 @@ | |||
'fieldname': 'allow_copy', | |||
'fieldtype': 'Check', | |||
'hidden': 0, | |||
'idx': 26, | |||
'label': 'Hide Copy', | |||
'oldfieldname': 'allow_copy', | |||
'oldfieldtype': 'Check', | |||
@@ -422,7 +385,6 @@ | |||
'fieldname': 'hide_toolbar', | |||
'fieldtype': 'Check', | |||
'hidden': 0, | |||
'idx': 27, | |||
'label': 'Hide Toolbar', | |||
'oldfieldname': 'hide_toolbar', | |||
'oldfieldtype': 'Check', | |||
@@ -436,7 +398,6 @@ | |||
'fieldname': 'hide_heading', | |||
'fieldtype': 'Check', | |||
'hidden': 0, | |||
'idx': 28, | |||
'label': 'Hide Heading', | |||
'oldfieldname': 'hide_heading', | |||
'oldfieldtype': 'Check', | |||
@@ -449,7 +410,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'allow_attach', | |||
'fieldtype': 'Check', | |||
'idx': 29, | |||
'label': 'Allow Attach', | |||
'oldfieldname': 'allow_attach', | |||
'oldfieldtype': 'Check', | |||
@@ -462,7 +422,6 @@ | |||
'fieldname': 'max_attachments', | |||
'fieldtype': 'Int', | |||
'hidden': 1, | |||
'idx': 30, | |||
'label': 'Max Attachments', | |||
'oldfieldname': 'max_attachments', | |||
'oldfieldtype': 'Int' | |||
@@ -473,7 +432,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'allow_rename', | |||
'fieldtype': 'Check', | |||
'idx': 31, | |||
'label': 'Allow Rename', | |||
'oldfieldname': 'allow_rename', | |||
'oldfieldtype': 'Check' | |||
@@ -484,7 +442,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'section_style', | |||
'fieldtype': 'Select', | |||
'idx': 32, | |||
'label': 'Section Style', | |||
'oldfieldname': 'section_style', | |||
'oldfieldtype': 'Select', | |||
@@ -497,7 +454,6 @@ | |||
'fieldname': 'colour', | |||
'fieldtype': 'Select', | |||
'hidden': 0, | |||
'idx': 33, | |||
'label': 'Colour', | |||
'oldfieldname': 'colour', | |||
'oldfieldtype': 'Select', | |||
@@ -512,7 +468,6 @@ | |||
'fieldname': 'smallicon', | |||
'fieldtype': 'Select', | |||
'hidden': 0, | |||
'idx': 34, | |||
'label': 'Small Icon', | |||
'oldfieldname': 'smallicon', | |||
'oldfieldtype': 'Select', | |||
@@ -521,11 +476,18 @@ | |||
'search_index': 0 | |||
}, | |||
# DocField | |||
{ | |||
'doctype': 'DocField', | |||
'fieldname': 'default_print_format', | |||
'fieldtype': 'Data', | |||
'label': 'Default Print Format' | |||
}, | |||
# DocField | |||
{ | |||
'doctype': 'DocField', | |||
'fieldtype': 'Section Break', | |||
'idx': 35, | |||
'label': 'Permissions', | |||
'oldfieldtype': 'Section Break' | |||
}, | |||
@@ -534,7 +496,6 @@ | |||
{ | |||
'doctype': 'DocField', | |||
'fieldtype': 'Column Break', | |||
'idx': 36, | |||
'label': 'Roles and Permissions', | |||
'oldfieldtype': 'Column Break', | |||
'width': '70%' | |||
@@ -547,7 +508,6 @@ | |||
'fieldname': 'permissions', | |||
'fieldtype': 'Table', | |||
'hidden': 0, | |||
'idx': 37, | |||
'label': 'Permissions', | |||
'oldfieldname': 'permissions', | |||
'oldfieldtype': 'Table', | |||
@@ -561,7 +521,6 @@ | |||
'doctype': 'DocField', | |||
'fieldname': 'allow_trash', | |||
'fieldtype': 'Check', | |||
'idx': 38, | |||
'label': 'Allow Trash', | |||
'oldfieldname': 'allow_trash', | |||
'oldfieldtype': 'Check' | |||
@@ -571,7 +530,6 @@ | |||
{ | |||
'doctype': 'DocField', | |||
'fieldtype': 'Column Break', | |||
'idx': 39, | |||
'label': 'Print Formats', | |||
'oldfieldtype': 'Column Break', | |||
'width': '30%' | |||
@@ -583,7 +541,6 @@ | |||
'fieldname': 'formats', | |||
'fieldtype': 'Table', | |||
'hidden': 0, | |||
'idx': 40, | |||
'label': 'Formats', | |||
'oldfieldname': 'formats', | |||
'oldfieldtype': 'Table', | |||
@@ -597,7 +554,6 @@ | |||
'doctype': 'DocField', | |||
'fieldtype': 'Section Break', | |||
'hidden': 0, | |||
'idx': 41, | |||
'label': 'Fields', | |||
'oldfieldtype': 'Section Break', | |||
'reqd': 0, | |||
@@ -609,7 +565,6 @@ | |||
'doctype': 'DocField', | |||
'fieldtype': 'Column Break', | |||
'hidden': 0, | |||
'idx': 42, | |||
'label': 'Document Fields', | |||
'oldfieldtype': 'Column Break', | |||
'reqd': 0, | |||
@@ -622,7 +577,6 @@ | |||
'fieldname': 'fields', | |||
'fieldtype': 'Table', | |||
'hidden': 0, | |||
'idx': 43, | |||
'label': 'Fields', | |||
'oldfieldname': 'fields', | |||
'oldfieldtype': 'Table', | |||
@@ -636,7 +590,6 @@ | |||
'doctype': 'DocField', | |||
'fieldtype': 'Section Break', | |||
'hidden': 0, | |||
'idx': 44, | |||
'label': 'Description', | |||
'oldfieldtype': 'Section Break', | |||
'reqd': 0, | |||
@@ -649,7 +602,6 @@ | |||
'fieldname': 'description', | |||
'fieldtype': 'Text', | |||
'hidden': 0, | |||
'idx': 45, | |||
'label': 'Description', | |||
'oldfieldname': 'description', | |||
'oldfieldtype': 'Text', | |||
@@ -21,41 +21,17 @@ class DocType: | |||
def __init__(self, doc, doclist=[]): | |||
self.doc = doc | |||
self.doclist = doclist | |||
self.prefix = is_testing and 'test' or 'tab' | |||
self.ref_doc = '' | |||
# Autoname | |||
#--------- | |||
#--------------------------------------------------------------------------- | |||
def autoname(self): | |||
self.doc.name = make_autoname(self.doc.from_doctype + '-' + self.doc.to_doctype) | |||
def map_fields_with_same_name(self, from_doctype, to_doctype, from_doc, to_doc, fld_list): | |||
""" | |||
Returns field list with same name in from and to doctype | |||
""" | |||
exception_flds = [f[0] for f in fld_list if f[2] == 'No'] | |||
exception_flds += default_fields | |||
exception_flds += ['amended_from', 'amendment_date', 'file_list', 'naming_series', 'status'] | |||
map_fld_list = [ | |||
[d[0], d[0], 'Yes'] for d in sql(""" | |||
select t1.fieldname | |||
from `tabDocField` t1, `tabDocField` t2 | |||
where t1.parent = %s and t2.parent = %s | |||
and t1.fieldname = t2.fieldname | |||
and t1.docstatus != 2 and t2.docstatus != 2 | |||
and ifnull(t1.fieldname, '') != '' | |||
""",(from_doctype, to_doctype)) if d[0] not in exception_flds | |||
] | |||
self.set_value(map_fld_list, from_doc, to_doc) | |||
# Maps the fields in 'To DocType' | |||
#-------------------------------- | |||
#--------------------------------------------------------------------------- | |||
def dt_map(self, from_doctype, to_doctype, from_docname, to_doc, doclist, from_to_list = '[]'): | |||
# definition of arguments | |||
''' | |||
String <from_doctype> : contains the name of DocType initiating the function | |||
String <to_doctype> : contains the name of DocType created by the function | |||
@@ -64,6 +40,10 @@ class DocType: | |||
String <doclist> : contains doclist of 'to_doctype' | |||
String <from_to_list> : contains list of tables which will be mapped | |||
''' | |||
if not from_docname: | |||
msgprint(from_doctype + " not selected for mapping", raise_exception=1) | |||
# Validate reference doc docstatus | |||
self.ref_doc = from_docname | |||
self.check_ref_docstatus() | |||
@@ -71,85 +51,111 @@ class DocType: | |||
if not doclist: | |||
doclist.append(to_doc) | |||
tbl_list = sql("select from_table, to_table, from_field, to_field, match_id, validation_logic from `tabTable Mapper Detail` where parent ='%s' order by match_id" % (from_doctype + "-" + to_doctype)) | |||
for t in tbl_list: | |||
from_table_name = t[0] | |||
to_table_name = t[1] | |||
from_table_fname = t[2] | |||
to_table_fname = t[3] | |||
match_id = t[4] | |||
validation_logic = t[5] | |||
from_to = [from_table_name, to_table_name] | |||
tbl_list = sql("select from_table, to_table, from_field, to_field, match_id, validation_logic from `tabTable Mapper Detail` where parent ='%s' order by match_id" % self.doc.name, as_dict=1) | |||
if from_to in eval(from_to_list): | |||
fld_list = sql("select from_field, to_field, map from `tabField Mapper Detail` where parent = '%s' and match_id = %s" % (from_doctype + "-" + to_doctype, match_id)) | |||
if not from_docname: | |||
msgprint(from_doctype + " not selected for mapping") | |||
raise Exception | |||
# Parent to parent mapping | |||
if from_table_name == self.doc.from_doctype and to_table_name == self.doc.to_doctype: | |||
# Check validation | |||
nm = sql("select name from `tab%s` where name = '%s' and %s" % (from_doctype, from_docname, validation_logic)) | |||
nm = nm and nm[0][0] or '' | |||
# If validation failed raise exception | |||
if not nm: | |||
msgprint("Validation failed in doctype mapper. Please contact Administrator.") | |||
raise Exception | |||
from_doc = Document(from_doctype, nm) | |||
# Map fields with same name | |||
for t in tbl_list: | |||
if [t['from_table'], t['to_table']] in eval(from_to_list): | |||
self.map_fields(t, from_doctype, from_docname, to_doc, doclist) | |||
# Doclist is required when called from server side for refreshing table | |||
return doclist | |||
self.map_fields_with_same_name(from_doctype, to_doctype, from_doc, to_doc, fld_list) | |||
# Maps field in parent | |||
if fld_list: | |||
self.set_value(fld_list, from_doc, to_doc) | |||
#--------------------------------------------------------------------------- | |||
def map_fields(self, t, from_dt, from_dn, to_doc, doclist): | |||
""" | |||
Creates from, to obj and maps flds as per mapper and with same name | |||
""" | |||
flds = self.get_mapper_fields(t) | |||
flds += self.get_fields_with_same_name(t, flds) | |||
# Parent to child OR child to child mapping | |||
if flds: | |||
from_docnames = self.get_docnames(t, from_dt, from_dn) | |||
for dn in from_docnames: | |||
# Creates object for 'From DocType', it can be parent or child | |||
from_doc_obj = Document(t['from_table'], dn[0]) | |||
# Add a row in target table in 'To DocType' and returns obj | |||
if t['to_table'] != self.doc.to_doctype: | |||
to_doc_obj = addchild(to_doc, t['to_field'], t['to_table'], 1, doclist) | |||
else: | |||
dnlist = () | |||
if from_table_name == self.doc.from_doctype: | |||
dnlist = ((from_docname,),) | |||
else: | |||
dnlist = sql("select name from `tab%s` where parent='%s' and parenttype = '%s' and %s order by idx" % (from_table_name, from_docname, self.doc.from_doctype, validation_logic)) | |||
for dn in dnlist: | |||
# Add a row in target table in 'To DocType' and returns obj | |||
ch = addchild(to_doc, t[3], t[1], 1, doclist) | |||
# Creates object for 'From DocType', it can be parent or child | |||
d = Document(t[0], dn[0]) | |||
# Map fields with same name | |||
self.map_fields_with_same_name(from_table_name, t[1], d, ch, fld_list) | |||
# Map values | |||
if fld_list: | |||
self.set_value(fld_list, d, ch) | |||
to_doc_obj = to_doc | |||
self.set_value(flds, from_doc_obj, to_doc_obj) | |||
#--------------------------------------------------------------------------- | |||
def get_docnames(self, t, from_dt, from_dn): | |||
""" | |||
Returns docnames of source document (parent/child) | |||
""" | |||
docnames = () | |||
if t['from_table'] == self.doc.from_doctype: | |||
docnames = sql("select name from `tab%s` where name = '%s' and %s" % (from_dt, from_dn, t['validation_logic'])) | |||
if not docnames: | |||
msgprint("Validation failed in doctype mapper. Please contact Administrator.", raise_exception=1) | |||
else: | |||
docnames = sql("select name from `tab%s` where parent='%s' and parenttype = '%s' and %s order by idx" % (t['from_table'], from_dn, self.doc.from_doctype, t['validation_logic'])) | |||
return docnames | |||
#--------------------------------------------------------------------------- | |||
def get_mapper_fields(self, t): | |||
return [[f[0], f[1], f[2]] for f in sql(""" | |||
select from_field, to_field, map | |||
from `tabField Mapper Detail` | |||
where parent = '%s' and match_id = %s | |||
""" % (self.doc.name, t['match_id']))] | |||
# Required when called from server side for refreshing table | |||
return doclist | |||
#--------------------------------------------------------------------------- | |||
def get_fields_with_same_name(self, t, flds): | |||
""" | |||
Returns field list with same name in from and to doctype | |||
""" | |||
# Assigns value to "To Doctype" | |||
#------------------------------ | |||
exception_flds = default_fields | |||
exception_flds += [f[1] for f in flds] | |||
similar_flds = [ | |||
[d[0], d[0], 'Yes'] for d in sql(""" | |||
select t1.fieldname | |||
from `tabDocField` t1, `tabDocField` t2 | |||
where t1.parent = %s and t2.parent = %s | |||
and t1.fieldname = t2.fieldname | |||
and t1.docstatus != 2 and t2.docstatus != 2 | |||
and ifnull(t1.no_copy, 0) = 0 | |||
and ifnull(t1.fieldname, '') != '' | |||
and t1.fieldtype not in ('Table', 'Section Break', 'Column Break', 'HTML') | |||
""",(t['from_table'], t['to_table'])) if d[0] not in exception_flds | |||
] | |||
return similar_flds | |||
#--------------------------------------------------------------------------- | |||
def set_value(self, fld_list, obj, to_doc): | |||
""" | |||
Assigns value to fields in "To Doctype" | |||
""" | |||
for f in fld_list: | |||
if f[2] == 'Yes': | |||
if f[0].startswith('eval:'): | |||
to_doc.fields[f[1]] = eval(f[0][5:]) | |||
try: | |||
val = eval(f[0][5:]) | |||
except: | |||
val = '' | |||
to_doc.fields[f[1]] = val | |||
else: | |||
to_doc.fields[f[1]] = obj.fields.get(f[0]) | |||
# Validate | |||
#--------- | |||
#--------------------------------------------------------------------------- | |||
def validate(self): | |||
""" | |||
Validate mapper while saving | |||
""" | |||
for d in getlist(self.doclist, 'field_mapper_details'): | |||
# Automatically assigns default value if not entered | |||
if not d.match_id: | |||
@@ -164,10 +170,13 @@ class DocType: | |||
# Check wrong field name | |||
self.check_fields_in_dt() | |||
# Check if any wrong fieldname entered | |||
#-------------------------------------- | |||
#--------------------------------------------------------------------------- | |||
def check_fields_in_dt(self): | |||
""" | |||
Check if any wrong fieldname entered in mapper | |||
""" | |||
for d in getlist(self.doclist, 'field_mapper_details'): | |||
table_name = sql("select from_table, to_table from `tabTable Mapper Detail` where parent ='%s' and match_id = '%s'" % (self.doc.name, d.match_id)) | |||
@@ -181,6 +190,7 @@ class DocType: | |||
if not exists2 and d.to_field not in default_fields: | |||
msgprint('"' + cstr(d.to_field) + '" does not exists in DocType "' + cstr(table_name[0][1]) + '"') | |||
# Check consistency of value with reference document | |||
#--------------------------------------------------- | |||
def validate_reference_value(self, obj, to_docname): | |||
@@ -230,7 +240,6 @@ class DocType: | |||
if cl[2] == '=' and (ft[1] == 'Currency' or ft[1] == 'Float'): | |||
consistent = sql("select name, %s from `tab%s` where name = '%s' and '%s' - %s <= 0.5" % (cl[0], t.from_table, child_obj.fields[t.reference_key], flt(cur_val), cl[0])) | |||
else: | |||
#consistent = sql("select name, %s from `tab%s` where name = '%s' and '%s' %s %s" % (cl[0], t.from_table, child_obj.fields[t.reference_key], cur_val, cl[2], cl[0])) | |||
consistent = sql("select name, %s from `tab%s` where name = '%s' and '%s' %s ifnull(%s, '')" % (cl[0], t.from_table, child_obj.fields[t.reference_key], ft[1] in ('Currency', 'Float', 'Int') and flt(cur_val) or cstr(cur_val), cl[2], cl[0])) | |||
if not self.ref_doc: | |||
@@ -248,22 +257,21 @@ class DocType: | |||
from_fld_label = sql("select label from tabDocField where parent = '%s' and fieldname = '%s'" % (from_table, from_field)) | |||
op_in_words = {'=':'equal to ', '>=':'greater than equal to ', '>':'greater than ', '<=':'less than equal to ', '<':'less than '} | |||
msgprint(to_fld_label[0][0] + " should be " + op_in_words[operator] + from_fld_label[0][0] + " of " + self.doc.from_doctype + ": " + self.ref_doc) | |||
raise Exception, "Validation Error." | |||
msgprint(to_fld_label[0][0] + " should be " + op_in_words[operator] + from_fld_label[0][0] + " of " + self.doc.from_doctype + ": " + self.ref_doc, raise_exception=1) | |||
def check_ref_docstatus(self): | |||
if self.ref_doc: | |||
det = sql("select name, docstatus from `tab%s` where name = '%s'" % (self.doc.from_doctype, self.ref_doc)) | |||
if not det: | |||
msgprint(self.doc.from_doctype + ": " + self.ref_doc + " does not exists in the system") | |||
raise Exception, "Validation Error." | |||
msgprint(self.doc.from_doctype + ": " + self.ref_doc + " does not exists in the system", raise_exception=1) | |||
elif self.doc.ref_doc_submitted and det[0][1] != 1: | |||
msgprint(self.doc.from_doctype + ": " + self.ref_doc + " is not Submitted Document.") | |||
raise Exception, "Validation Error." | |||
msgprint(self.doc.from_doctype + ": " + self.ref_doc + " is not Submitted Document.", raise_exception=1) | |||
def on_update(self): | |||
""" | |||
If developer_mode = 1, mapper will be written to files | |||
""" | |||
import webnotes.defs | |||
if hasattr(webnotes.defs, 'developer_mode') and webnotes.defs.developer_mode: | |||
from webnotes.modules.export_module import export_to_files | |||
export_to_files(record_list=[[self.doc.doctype, self.doc.name]]) | |||
export_to_files(record_list=[[self.doc.doctype, self.doc.name]]) |
@@ -48,8 +48,26 @@ modules_path = '' | |||
# saved to the modules folder | |||
# | |||
developer_mode = 0 | |||
# | |||
# Auto Clear Cache: If set, automatically clears cache on refresh | |||
# | |||
auto_cache_clear = 0 | |||
# | |||
# Admin Email Notification: If the admin_email_notification is set, | |||
# then only sent notification email from the system | |||
# | |||
admin_email_notification = 0 | |||
# | |||
# Global Send Email: Global email settings, if 1 then only mail will go from the system | |||
# | |||
global_send_email = 1 | |||
# | |||
# Time Zone: Useful if your users are across timezones | |||
# | |||
@@ -119,7 +119,7 @@ class Installer: | |||
self.dbman.delete_user(target) | |||
# create user and db | |||
self.dbman.create_user(target,getattr(webnotes.defs,'db_password',None)) | |||
self.dbman.create_user(target,self.get_db_password(target)) | |||
if verbose: print "Created user %s" % target | |||
# create a database | |||
@@ -173,7 +173,7 @@ def make_scheduler(root_login, root_password, verbose): | |||
dbman.delete_user('master_scheduler') | |||
# create user and db | |||
dbman.create_user('master_scheduler', getattr(webnotes.defs,'db_password',None)) | |||
dbman.create_user('master_scheduler', getattr(webnotes.defs,'scheduler_password',None)) | |||
if verbose: print "Created user master_scheduler" | |||
# create a database | |||
@@ -291,6 +291,7 @@ class CSVImport: | |||
cur_doc = Document(fielddata = fd) | |||
cur_doc.doctype, cur_doc.parenttype, cur_doc.parentfield = self.dt_list[0], len(self.dt_list) > 1 and self.dt_list[1] or '', len(self.dt_list) > 1 and self.dt_list[2] or '' | |||
obj = '' | |||
webnotes.message_log = [] | |||
# save the document | |||
try: | |||
if webnotes.conn.in_transaction: | |||
@@ -321,7 +322,8 @@ class CSVImport: | |||
except Exception: | |||
sql("ROLLBACK") | |||
self.msg.append('<div style="color: RED"> Validation: %s</div>' % str(webnotes.message_log[-1:])) | |||
self.msg.append('<div style="color: RED"> Validation Error: %s</div>' % str((webnotes.message_log and webnotes.message_log[0]) or webnotes.utils.getTraceback())) | |||
self.msg.append('<div style="color: RED"> Did not import</div>') | |||
# do import | |||
# -------------------------------------------------------------------- | |||
@@ -5,6 +5,10 @@ def run(log_exception=1): | |||
from patches import patch | |||
from webnotes.utils import cint | |||
if webnotes.conn.cur_db_name=='accounts': | |||
# no patches on accounts | |||
return | |||
next_patch = cint(webnotes.conn.get_global('next_patch')) | |||
if next_patch <= patch.last_patch: | |||
@@ -31,7 +35,8 @@ def write_log(): | |||
patch_log.write(('\n\nError in %s:\n' % webnotes.conn.cur_db_name) + webnotes.getTraceback()) | |||
patch_log.close() | |||
from webnotes.utils import sendmail | |||
subj = 'Error in running patches in %s' % webnotes.conn.cur_db_name | |||
msg = subj + '<br><br>Login User: ' + webnotes.user.name + '<br><br>' + webnotes.getTraceback() | |||
sendmail(['developer@erpnext.com'], sender='automail@erpnext.com', subject= subj, parts=[['text/plain', msg]]) | |||
if getattr(webnotes.defs,'admin_email_notification',0): | |||
from webnotes.utils import sendmail | |||
subj = 'Error in running patches in %s' % webnotes.conn.cur_db_name | |||
msg = subj + '<br><br>Login User: ' + webnotes.user.name + '<br><br>' + webnotes.getTraceback() | |||
sendmail(['developers@erpnext.com'], sender='automail@erpnext.com', subject= subj, parts=[['text/plain', msg]]) |
@@ -162,12 +162,12 @@ class Profile: | |||
# update tab Profile | |||
webnotes.conn.sql("UPDATE tabProfile SET password=password(%s) WHERE name=%s", (pwd, profile[0][0])) | |||
self.send_email("Password Reset", "<p>Dear %s%s,</p><p>your password has been changed to %s</p><p>[Automatically Generated]</p>" % (profile[0][2], (profile[0][3] and (' ' + profile[0][3]) or ''), pwd), profile[0][1]) | |||
def send_email(self, subj, mess, email): | |||
import webnotes.utils.email_lib | |||
webnotes.utils.email_lib.sendmail(email, msg=mess, subject=subj) | |||
# update recent documents | |||
@@ -29,19 +29,18 @@ def sendmail(recipients, sender='', msg='', subject='[No Subject]', parts=[], cc | |||
email = EMail(sender, recipients, subject, reply_to=reply_to) | |||
email.cc = cc | |||
if msg: | |||
if template: | |||
msg = make_html_body(msg, template) | |||
if msg: | |||
if template: | |||
msg = make_html_body(msg, template).encode('utf-8') | |||
else: | |||
# if not html, then lets put some whitespace | |||
if (not '<br>' in msg) or (not '<p>' in msg): | |||
msg = msg.replace('\n','<br>') | |||
msg = msg.replace('\n','<br>') | |||
footer = get_footer() | |||
msg = msg + (footer or '') | |||
email.set_text(html2text(msg)) | |||
email.set_html(msg) | |||
msg = msg + (footer or '') | |||
email.set_text(html2text(msg)) | |||
email.set_html(msg) | |||
for p in parts: | |||
email.set_message(p[1]) | |||
for a in attach: | |||
@@ -447,7 +447,8 @@ def html2text_file(html, out=wrapwrite, baseurl=''): | |||
return h.close() | |||
def html2text(html, baseurl=''): | |||
return optwrap(html2text_file(html, None, baseurl)) | |||
txt = html2text_file(html.decode('utf-8'), None, baseurl) | |||
return optwrap(txt.encode('utf-8')) | |||
if __name__ == "__main__": | |||
baseurl = '' | |||
@@ -16,6 +16,9 @@ class EMail: | |||
""" | |||
def __init__(self, sender='', recipients=[], subject='', from_defs=0, alternative=0, reply_to=None): | |||
from email.mime.multipart import MIMEMultipart | |||
from email import Charset | |||
Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8') | |||
if type(recipients)==str: | |||
recipients = recipients.replace(';', ',') | |||
recipients = recipients.split(',') | |||
@@ -36,7 +39,8 @@ class EMail: | |||
Attach message in the text portion of multipart/alternative | |||
""" | |||
from email.mime.text import MIMEText | |||
part = MIMEText(message, 'plain') | |||
msg = unicode(message, 'utf-8') | |||
part = MIMEText(msg.encode('utf-8'), 'plain', 'UTF-8') | |||
self.msg_multipart.attach(part) | |||
def set_html(self, message): | |||
@@ -8,77 +8,222 @@ | |||
# ------------------------------------------ | |||
import webnotes | |||
import webnotes, unittest | |||
class TestNSM(unittest.TestCase): | |||
def setUp(self): | |||
from webnotes.model.doc import Document | |||
self.root = Document(fielddata={'doctype':'nsmtest', 'name':'T001', 'parent':None}) | |||
self.first_child = Document(fielddata={'doctype':'nsmtest', 'name':'C001', 'parent_node':'T001'}) | |||
self.first_sibling = Document(fielddata={'doctype':'nsmtest', 'name':'C002', 'parent_node':'T001'}) | |||
self.grand_child = Document(fielddata={'doctype':'nsmtest', 'name':'GC001', 'parent_node':'C001'}) | |||
webnotes.conn.sql(""" | |||
create table `tabnsmtest` ( | |||
name varchar(120) not null primary key, | |||
creation datetime, | |||
modified datetime, | |||
modified_by varchar(40), | |||
owner varchar(40), | |||
docstatus int(1) default '0', | |||
parent varchar(120), | |||
parentfield varchar(120), | |||
parenttype varchar(120), | |||
idx int(8), | |||
parent_node varchar(180), | |||
old_parent varchar(180), | |||
lft int, | |||
rgt int) ENGINE=InnoDB""") | |||
def test_root(self): | |||
self.root.save(1) | |||
update_nsm(self.root) | |||
self.assertTrue(self.root.lft==1) | |||
self.assertTrue(self.root.rgt==2) | |||
def test_first_child(self): | |||
self.root.save(1) | |||
update_nsm(self.root) | |||
self.first_child.save(1) | |||
update_nsm(self.first_child) | |||
self.root._loadfromdb() | |||
self.assertTrue(self.root.lft==1) | |||
self.assertTrue(self.first_child.lft==2) | |||
self.assertTrue(self.first_child.rgt==3) | |||
self.assertTrue(self.root.rgt==4) | |||
def test_sibling(self): | |||
self.test_first_child() | |||
self.first_sibling.save(1) | |||
update_nsm(self.first_sibling) | |||
self.root._loadfromdb() | |||
self.first_child._loadfromdb() | |||
self.assertTrue(self.root.lft==1) | |||
self.assertTrue(self.first_child.lft==2) | |||
self.assertTrue(self.first_child.rgt==3) | |||
self.assertTrue(self.first_sibling.lft==4) | |||
self.assertTrue(self.first_sibling.rgt==5) | |||
self.assertTrue(self.root.rgt==6) | |||
def test_remove_sibling(self): | |||
self.test_sibling() | |||
self.first_sibling.parent_node = '' | |||
update_nsm(self.first_sibling) | |||
self.root._loadfromdb() | |||
self.first_child._loadfromdb() | |||
self.assertTrue(self.root.lft==1) | |||
self.assertTrue(self.first_child.lft==2) | |||
self.assertTrue(self.first_child.rgt==3) | |||
self.assertTrue(self.root.rgt==4) | |||
self.assertTrue(self.first_sibling.lft==5) | |||
self.assertTrue(self.first_sibling.rgt==6) | |||
def test_change_parent(self): | |||
self.test_sibling() | |||
# add grand child | |||
self.grand_child.save(1) | |||
update_nsm(self.grand_child) | |||
# check lft rgt | |||
self.assertTrue(self.grand_child.lft==3) | |||
self.assertTrue(self.grand_child.rgt==4) | |||
# change parent | |||
self.grand_child.parent_node = 'C002' | |||
self.grand_child.save() | |||
# update | |||
update_nsm(self.grand_child) | |||
# check lft rgt | |||
self.assertTrue(self.grand_child.lft==5) | |||
self.assertTrue(self.grand_child.rgt==6) | |||
def tearDown(self): | |||
webnotes.conn.sql("drop table tabnsmtest") | |||
# called in the on_update method | |||
def update_nsm(doc_obj): | |||
# get fields, data from the DocType | |||
d = doc_obj.doc | |||
pf, opf = 'parent_node', 'old_parent' | |||
if hasattr(doc_obj,'nsm_parent_field'): | |||
pf = doc_obj.nsm_parent_field | |||
if hasattr(doc_obj,'nsm_oldparent_field'): | |||
opf = doc_obj.nsm_oldparent_field | |||
p, op = d.fields[pf], d.fields.get(opf, '') | |||
if str(doc_obj.__class__)=='webnotes.model.doc.Document': | |||
# passed as a Document object | |||
d = doc_obj | |||
else: | |||
# passed as a DocType object | |||
d = doc_obj.doc | |||
if hasattr(doc_obj,'nsm_parent_field'): | |||
pf = doc_obj.nsm_parent_field | |||
if hasattr(doc_obj,'nsm_oldparent_field'): | |||
opf = doc_obj.nsm_oldparent_field | |||
p, op = d.fields.get(pf, ''), d.fields.get(opf, '') | |||
# has parent changed (?) or parent is None (root) | |||
if not doc_obj.doc.lft and not doc_obj.doc.rgt: | |||
update_add_node(doc_obj.doc.doctype, doc_obj.doc.name, p or '', pf) | |||
if not d.lft and not d.rgt: | |||
update_add_node(d.doctype, d.name, p or '', pf) | |||
elif op != p: | |||
update_remove_node(doc_obj.doc.doctype, doc_obj.doc.name) | |||
update_add_node(doc_obj.doc.doctype, doc_obj.doc.name, p or '', pf) | |||
update_remove_node(d.doctype, d.name) | |||
update_add_node(d.doctype, d.name, p or '', pf) | |||
# set old parent | |||
webnotes.conn.set(d, opf, p or '') | |||
# reload | |||
d._loadfromdb() | |||
def rebuild_tree(doctype, parent_field): | |||
""" | |||
call rebuild_node for all root nodes | |||
""" | |||
# get all roots | |||
right = 1 | |||
result = webnotes.conn.sql("SELECT name FROM `tab%s` WHERE `%s`='' or `%s` IS NULL" % (doctype, parent_field, parent_field)) | |||
for r in result: | |||
right = rebuild_node(doctype, r[0], right, parent_field) | |||
def rebuild_node(doctype, parent, left, parent_field): | |||
def rebuild_node(doctype, parent, left, parent_field, cnt = 0): | |||
""" | |||
reset lft, rgt and recursive call for all children | |||
""" | |||
from webnotes.utils import now | |||
n = now() | |||
# the right value of this node is the left value + 1 | |||
right = left+1 | |||
right = left+1 | |||
# get all children of this node | |||
result = webnotes.conn.sql("SELECT name FROM `tab%s` WHERE `%s`='%s'" % (doctype, parent_field, parent)) | |||
for r in result: | |||
right = rebuild_node(doctype, r[0], right, parent_field) | |||
right = rebuild_node(doctype, r[0], right, parent_field, cnt) | |||
# we've got the left value, and now that we've processed | |||
# the children of this node we also know the right value | |||
webnotes.conn.sql('UPDATE `tab%s` SET lft=%s, rgt=%s WHERE name="%s"' % (doctype,left,right,parent)) | |||
webnotes.conn.sql("UPDATE `tab%s` SET lft=%s, rgt=%s, modified='%s' WHERE name='%s'" % (doctype,left,right,n,parent)) | |||
# commit after every 100 | |||
cnt += 1 | |||
if cnt % 100 == 0: | |||
cnt = 0 | |||
webnotes.conn.sql("commit") | |||
webnotes.conn.sql("start transaction") | |||
#return the right value of this node + 1 | |||
return right+1 | |||
def update_add_node(doctype, name, parent, parent_field): | |||
""" | |||
insert a new node | |||
""" | |||
from webnotes.utils import now | |||
n = now() | |||
# get the last sibling of the parent | |||
if parent: | |||
right = webnotes.conn.sql("select rgt from `tab%s` where name='%s'" % (doctype, parent))[0][0] | |||
else: # root | |||
right = webnotes.conn.sql("select ifnull(max(rgt),0)+1 from `tab%s` where ifnull(`%s`,'') =''" % (doctype, parent_field))[0][0] | |||
right = right or 1 | |||
# update all on the right | |||
webnotes.conn.sql("update `tab%s` set rgt = rgt+2 where rgt >= %s" %(doctype,right)) | |||
webnotes.conn.sql("update `tab%s` set lft = lft+2 where lft >= %s" %(doctype,right)) | |||
webnotes.conn.sql("update `tab%s` set rgt = rgt+2, modified='%s' where rgt >= %s" %(doctype,n,right)) | |||
webnotes.conn.sql("update `tab%s` set lft = lft+2, modified='%s' where lft >= %s" %(doctype,n,right)) | |||
# update index of new node | |||
if webnotes.conn.sql("select * from `tab%s` where lft=%s or rgt=%s"% (doctype, right, right+1)): | |||
webnotes.msgprint("Nested set error. Please send mail to support") | |||
raise Exception | |||
webnotes.conn.sql("update `tab%s` set lft=%s, rgt=%s where name='%s'" % (doctype,right,right+1,name)) | |||
webnotes.conn.sql("update `tab%s` set lft=%s, rgt=%s, modified='%s' where name='%s'" % (doctype,right,right+1,n,name)) | |||
return right | |||
def update_remove_node(doctype, name): | |||
""" | |||
remove a node | |||
""" | |||
from webnotes.utils import now | |||
n = now() | |||
left = webnotes.conn.sql("select lft from `tab%s` where name='%s'" % (doctype,name)) | |||
if left[0][0]: | |||
# reset this node | |||
webnotes.conn.sql("update `tab%s` set lft=0, rgt=0 where name='%s'" % (doctype,name)) | |||
webnotes.conn.sql("update `tab%s` set lft=0, rgt=0, modified='%s' where name='%s'" % (doctype,n,name)) | |||
# update all on the right | |||
webnotes.conn.sql("update `tab%s` set rgt = rgt-2 where rgt > %s" %(doctype,left[0][0])) | |||
webnotes.conn.sql("update `tab%s` set lft = lft-2 where lft > %s" %(doctype,left[0][0])) | |||
webnotes.conn.sql("update `tab%s` set rgt = rgt-2, modified='%s' where rgt > %s" %(doctype,n,left[0][0])) | |||
webnotes.conn.sql("update `tab%s` set lft = lft-2, modified='%s' where lft > %s" %(doctype,n,left[0][0])) |
@@ -162,7 +162,7 @@ def cancel_event(event): | |||
if __name__=='__main__': | |||
import os,sys | |||
cgi_bin_path = os.path.sep.join(__file__.split(os.path.sep)[:-3]) | |||
cgi_bin_path = os.path.sep.join(os.path.abspath(__file__).split(os.path.sep)[:-3]) | |||
sys.path.append(cgi_bin_path) | |||
@@ -107,8 +107,7 @@ function startup() { | |||
// for debug | |||
if(_startup_data.server_messages) msgprint(_startup_data.server_messages); | |||
} else { | |||
if($i('startup_div')) | |||
$c('startup',{},callback,null,1); | |||
$c('startup',{},callback,null,1); | |||
} | |||
} | |||
@@ -177,17 +176,6 @@ function setup_calendar() { | |||
startup_list.push(setup_calendar); | |||
// ie6 fixed pos fix | |||
if(isIE6) { | |||
var scroll_list = [] | |||
window.onscroll = function() { | |||
for(var i=0; i<scroll_list.length; i++) { | |||
scroll_list[i](); | |||
} | |||
} | |||
} | |||
window.onload = function() { startup() } | |||
var resize_observers = [] | |||
function set_resize_observer(fn) { | |||
if(resize_observers.indexOf(fn)==-1) resize_observers.push(fn); | |||
@@ -259,7 +259,7 @@ function open_url_post(URL, PARAMS, new_window) { | |||
temp.method="POST"; | |||
temp.style.display="none"; | |||
if(new_window){ | |||
temp.target = '_blank'; | |||
} | |||
for(var x in PARAMS) { | |||
var opt=document.createElement("textarea"); | |||
@@ -285,7 +285,7 @@ if(namespace?window[namespace][widget_name]:window[widget_name]){callback(create | |||
currently_loading[widget]=1;}} | |||
function makeArgString(dict){var varList=[];for(key in dict){varList[varList.length]=key+'='+encodeURIComponent(dict[key]);} | |||
return varList.join('&');} | |||
function open_url_post(URL,PARAMS,new_window){var temp=document.createElement("form");temp.action=URL;temp.method="POST";temp.style.display="none";if(new_window){} | |||
function open_url_post(URL,PARAMS,new_window){var temp=document.createElement("form");temp.action=URL;temp.method="POST";temp.style.display="none";if(new_window){temp.target='_blank';} | |||
for(var x in PARAMS){var opt=document.createElement("textarea");opt.name=x;opt.value=PARAMS[x];temp.appendChild(opt);} | |||
document.body.appendChild(temp);temp.submit();return temp;} | |||
var resume_dialog=null;function resume_session(){if(!resume_dialog){var d=new Dialog(400,200,'Session Expired');d.make_body([['Password','password','Re-enter your password to resume the session'],['Button','Go']]);d.widgets['Go'].onclick=function(){resume_dialog.widgets['Go'].set_working();var callback=function(r,rt){resume_dialog.widgets['Go'].done_working();if(r.message=='Logged In'){resume_dialog.allow_close=1;resume_dialog.hide();setTimeout('resume_dialog.allow_close=0',100);}else{msgprint('Wrong Password, try again');resume_dialog.wrong_count++;if(resume_dialog.wrong_count>2)logout();}} | |||