@@ -0,0 +1,69 @@ | |||||
# Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com) | |||||
# | |||||
# MIT License (MIT) | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining a | |||||
# copy of this software and associated documentation files (the "Software"), | |||||
# to deal in the Software without restriction, including without limitation | |||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense, | |||||
# and/or sell copies of the Software, and to permit persons to whom the | |||||
# Software is furnished to do so, subject to the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included in | |||||
# all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |||||
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | |||||
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |||||
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | |||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
"""docfield utililtes""" | |||||
import webnotes | |||||
def rename(doctype, fieldname, newname): | |||||
"""rename docfield""" | |||||
df = webnotes.conn.sql("""select * from tabDocField where parent=%s and fieldname=%s""", | |||||
(doctype, fieldname), as_dict=1) | |||||
if not df: | |||||
return | |||||
df = df[0] | |||||
if webnotes.conn.get_value('DocType', doctype, 'issingle'): | |||||
update_single(df, newname) | |||||
else: | |||||
update_table(df, newname) | |||||
update_parent_field(df, newname) | |||||
def update_single(f, new): | |||||
"""update in tabSingles""" | |||||
webnotes.conn.begin() | |||||
webnotes.conn.sql("""update tabSingles set field=%s where doctype=%s and field=%s""", | |||||
(new, f['parent'], f['fieldname'])) | |||||
webnotes.conn.commit() | |||||
def update_table(f, new): | |||||
"""update table""" | |||||
query = get_change_column_query(f, new) | |||||
if query: | |||||
webnotes.conn.sql(query) | |||||
def update_parent_field(f, new): | |||||
"""update 'parentfield' in tables""" | |||||
if f['fieldtype']=='Table': | |||||
webnotes.conn.begin() | |||||
webnotes.conn.sql("""update `tab%s` set parentfield=%s where parentfield=%s""" \ | |||||
% (f['options'], '%s', '%s'), (new, f['fieldname'])) | |||||
webnotes.conn.commit() | |||||
def get_change_column_query(f, new): | |||||
"""generate change fieldname query""" | |||||
desc = webnotes.conn.sql("desc `tab%s`" % f['parent']) | |||||
for d in desc: | |||||
if d[0]== f['fieldname']: | |||||
return 'alter table `tab%s` change `%s` `%s` %s' % \ | |||||
(f['parent'], f['fieldname'], new, d[1]) |
@@ -198,7 +198,6 @@ class DocList: | |||||
if hasattr(self.obj, 'custom_' + method): | if hasattr(self.obj, 'custom_' + method): | ||||
getattr(self.obj, 'custom_' + method)() | getattr(self.obj, 'custom_' + method)() | ||||
from webnotes.model.events import trigger | |||||
trigger(method, self.doc) | trigger(method, self.doc) | ||||
def save_main(self): | def save_main(self): | ||||
@@ -313,6 +312,19 @@ def clone(source_doclist): | |||||
return doclistobj | return doclistobj | ||||
def trigger(method, doc): | |||||
"""trigger doctype events""" | |||||
try: | |||||
import startup.event_handlers | |||||
except ImportError: | |||||
return | |||||
if hasattr(startup.event_handlers, method): | |||||
getattr(startup.event_handlers, method)(doc) | |||||
if hasattr(startup.event_handlers, 'doclist_all'): | |||||
startup.event_handlers.doclist_all(doc, method) | |||||
# for bc | # for bc | ||||
def getlist(doclist, parentfield): | def getlist(doclist, parentfield): | ||||
""" | """ | ||||
@@ -1,36 +0,0 @@ | |||||
# Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com) | |||||
# | |||||
# MIT License (MIT) | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining a | |||||
# copy of this software and associated documentation files (the "Software"), | |||||
# to deal in the Software without restriction, including without limitation | |||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense, | |||||
# and/or sell copies of the Software, and to permit persons to whom the | |||||
# Software is furnished to do so, subject to the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included in | |||||
# all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |||||
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | |||||
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |||||
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | |||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
"""trigger doctype events""" | |||||
def trigger(method, doc): | |||||
try: | |||||
import startup.event_handlers | |||||
except ImportError: | |||||
return | |||||
if hasattr(startup.event_handlers, method): | |||||
getattr(startup.event_handlers, method)(doc) | |||||
if hasattr(startup.event_handlers, 'doclist_all'): | |||||
startup.event_handlers.doclist_all(doc, method) | |||||
@@ -1,48 +0,0 @@ | |||||
# Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com) | |||||
# | |||||
# MIT License (MIT) | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining a | |||||
# copy of this software and associated documentation files (the "Software"), | |||||
# to deal in the Software without restriction, including without limitation | |||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense, | |||||
# and/or sell copies of the Software, and to permit persons to whom the | |||||
# Software is furnished to do so, subject to the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included in | |||||
# all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |||||
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | |||||
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |||||
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | |||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
# Modules | |||||
# ----------- | |||||
def get_module_items(mod, only_dt=0): | |||||
dl = [] | |||||
if only_dt: | |||||
transfer_types = ['DocType'] | |||||
else: | |||||
transfer_types = ['Role', 'Page', 'DocType', 'DocType Mapper', 'Search Criteria'] | |||||
dl = ['Module Def,'+mod] | |||||
for dt in transfer_types: | |||||
try: | |||||
dl2 = sql('select name from `tab%s` where module="%s"' % (dt,mod)) | |||||
dl += [(dt+','+e[0]) for e in dl2] | |||||
except: | |||||
pass | |||||
if not only_dt: | |||||
dl1 = sql('select doctype_list from `tabModule Def` where name=%s', mod) | |||||
dl += dl1[0][0].split('\n') | |||||
# build finally | |||||
dl = [e.split(',') for e in dl] | |||||
dl = [[e[0].strip(), e[1].strip()] for e in dl] # remove blanks | |||||
return dl |
@@ -1,80 +0,0 @@ | |||||
# Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com) | |||||
# | |||||
# MIT License (MIT) | |||||
# | |||||
# Permission is hereby granted, free of charge, to any person obtaining a | |||||
# copy of this software and associated documentation files (the "Software"), | |||||
# to deal in the Software without restriction, including without limitation | |||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense, | |||||
# and/or sell copies of the Software, and to permit persons to whom the | |||||
# Software is furnished to do so, subject to the following conditions: | |||||
# | |||||
# The above copyright notice and this permission notice shall be included in | |||||
# all copies or substantial portions of the Software. | |||||
# | |||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |||||
# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | |||||
# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |||||
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |||||
# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | |||||
# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
# | |||||
""" | |||||
Sync sql files (that contain triggers, stored procs etc) into the database | |||||
calling sync will walk through all .sql files in the modules file structure | |||||
and execute them if they are not executed or their timestamps have changed | |||||
All sql timestamps will be saved in a table '__sql_timestamps' | |||||
""" | |||||
# modules path | |||||
import webnotes | |||||
import webnotes.defs | |||||
def get_sql_files(): | |||||
""" | |||||
Returns list of .sql files from | |||||
""" | |||||
import os | |||||
ret = [] | |||||
for walk_tuple in os.walk(webnotes.defs.modules_path): | |||||
if os.path.split(walk_tuple[0])[-1]=='doctype': | |||||
for sql_file in filter(lambda x: x.endswith('.sql'), walk_tuple[2]): | |||||
ret.append[os.path.join(walk_tuple[0], sql_file)] | |||||
return ret | |||||
def run_sql_file(fn): | |||||
""" | |||||
Checks if timestamp matches, if not runs it | |||||
""" | |||||
from webnotes.modules import ModuleFile | |||||
mf = ModuleFile(fn) | |||||
if mf.is_new(): | |||||
webnotes.conn.sql(mf.read()) | |||||
mf.update() | |||||
def get_sql_timestamp(fn): | |||||
""" | |||||
Returns the last updated timestamp of the sql file | |||||
from the __sql_timestamps table. If the table does not | |||||
exist, it will create it | |||||
""" | |||||
try: | |||||
ts = webnotes.conn.sql("select tstamp from __sql_timestamp where file_name=%s", fn) | |||||
if ts: | |||||
return ts[0][0] | |||||
except Exception, e: | |||||
if e.args[0]==1147: | |||||
# create the table | |||||
webnotes.conn.commit() | |||||
webnotes.conn.sql(""" | |||||
create table __sql_timestamp ( | |||||
file_name varchar(320) primary key, | |||||
tstamp varchar(40))""") | |||||
webnotes.conn.begin() | |||||
else: | |||||
raise e | |||||
def update_sql_timestamp(fn, ts): | |||||
pass |
@@ -0,0 +1,29 @@ | |||||
import unittest, sys | |||||
sys.path.append('lib/py') | |||||
import webnotes | |||||
from webnotes.model import docfield | |||||
webnotes.connect() | |||||
class TestDocField(unittest.TestCase): | |||||
def test_rename(self): | |||||
docfield.rename('Event', 'notes', 'notes1') | |||||
# check in table | |||||
tf = webnotes.conn.sql("""desc tabEvent""") | |||||
self.assertTrue('notes' not in [d[0] for d in tf]) | |||||
self.assertTrue('notes1' in [d[0] for d in tf]) | |||||
docfield.rename('Event', 'notes1', 'notes') | |||||
def test_table_rename(self): | |||||
docfield.rename('Event', 'event_individuals', 'event_users') | |||||
self.assertFalse(webnotes.conn.sql("""select parent from `tabEvent User` where parentfield='event_individuals'""")) | |||||
self.assertTrue(webnotes.conn.sql("""select parent from `tabEvent User` where parentfield='event_users'""")) | |||||
if __name__=='__main__': | |||||
unittest.main() |
@@ -438,8 +438,7 @@ def in_words(integer): | |||||
Returns string in words for the given integer. | Returns string in words for the given integer. | ||||
""" | """ | ||||
in_million = webnotes.conn.get_value('Control Panel',None,'currency_format')=='Millions' and 1 or 0 | |||||
in_million = webnotes.conn.get_default('currency_format')=='Millions' and 1 or 0 | |||||
n=int(integer) | n=int(integer) | ||||
known = {0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six', 7: 'seven', 8: 'eight', 9: 'nine', 10: 'ten', | known = {0: 'zero', 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six', 7: 'seven', 8: 'eight', 9: 'nine', 10: 'ten', | ||||
@@ -0,0 +1,196 @@ | |||||
#!/usr/bin/python | |||||
""" | |||||
Utilty to review DocType fields: | |||||
- Will prompt each field and allow to change properties and fieldnames | |||||
- Run from command line of project home | |||||
- set fieldname_patch_file in defs.py | |||||
# todo | |||||
- update db | |||||
- parent field for table fields | |||||
""" | |||||
import os, sys | |||||
curpath = os.path.dirname(__file__) | |||||
sys.path.append(os.path.join(curpath, '..', '..')) | |||||
import webnotes | |||||
import webnotes.defs | |||||
from webnotes.modules.export_module import export_to_files | |||||
from termcolor import colored | |||||
sys.path.append(webnotes.defs.modules_path) | |||||
def update_field_property(f, property): | |||||
import webnotes | |||||
new = raw_input('New %s: ' % property) | |||||
if new: | |||||
webnotes.conn.begin() | |||||
webnotes.conn.set_value('DocField', f['name'], property, new) | |||||
webnotes.conn.commit() | |||||
export_to_files(record_list=[['DocType', f['parent']]]) | |||||
return new | |||||
def remove_field(f): | |||||
webnotes.conn.begin() | |||||
webnotes.conn.sql("""delete from tabDocField where name=%s""", f['name']) | |||||
webnotes.conn.commit() | |||||
export_to_files(record_list=[['DocType', f['parent']]]) | |||||
def replace_code(old, new): | |||||
"""find files with fieldname using grep and pass fieldnames to replace code""" | |||||
import subprocess | |||||
# js | |||||
proc = subprocess.Popen(['grep', '-rl', '--include=*.js', old, '.'], | |||||
shell=False, stdout = subprocess.PIPE, stderr=subprocess.PIPE) | |||||
stdout, stderr = proc.communicate() | |||||
for fpath in stdout.split(): | |||||
ret = search_replace_with_prompt(fpath, old, new) | |||||
if ret == 'quit': | |||||
break | |||||
# py | |||||
proc = subprocess.Popen(['grep', '-rl', '--include=*.py', old, '.'], | |||||
shell=False, stdout = subprocess.PIPE, stderr=subprocess.PIPE) | |||||
stdout, stderr = proc.communicate() | |||||
for fpath in stdout.split(): | |||||
ret = search_replace_with_prompt(fpath, old, new) | |||||
if ret == 'quit': | |||||
break | |||||
def update_fieldname_patch_file(fdata): | |||||
"""update patch file with list""" | |||||
with open(webnotes.defs.fieldname_patch_file, 'a') as patchfile: | |||||
patchfile.write(str(fdata) + '\n') | |||||
def search_replace_with_prompt(fpath, txt1, txt2): | |||||
""" Search and replace all txt1 by txt2 in the file with confirmation""" | |||||
from termcolor import colored | |||||
with open(fpath, 'r') as f: | |||||
content = f.readlines() | |||||
tmp = [] | |||||
for c in content: | |||||
if c.find(txt1) != -1: | |||||
print '\n', fpath | |||||
print colored(txt1, 'red').join(c[:-1].split(txt1)) | |||||
a = '' | |||||
while a not in ['y', 'n', 'Y', 'N', 's', 'q']: | |||||
a = raw_input('Do you want to Change [y/n/s/q]?') | |||||
if a.lower() == 'y': | |||||
c = c.replace(txt1, txt2) | |||||
if a.lower() == 's': | |||||
return | |||||
if a.lower() == 'q': | |||||
return 'quit' | |||||
tmp.append(c) | |||||
with open(fpath, 'w') as f: | |||||
f.write(''.join(tmp)) | |||||
print colored('Updated', 'green') | |||||
def review(): | |||||
"""review fields""" | |||||
start = 0 | |||||
flist = webnotes.conn.sql("""select t1.name, t1.parent, t1.fieldtype, t1.label, t1.fieldname, | |||||
t1.options, t1.description from tabDocField t1, tabDocType t2 where | |||||
t1.fieldtype not in ('Section Break', 'Column Break') and | |||||
t1.fieldname not in ('address_display', 'contact_display') and | |||||
t1.parent = t2.name and | |||||
t2.module != 'Core' and | |||||
replace(replace(replace(replace(lower(label), ' ', '_'), '-', '_'), ')', ''), '(', '') != fieldname | |||||
order by parent, label""", as_dict=1) | |||||
for f in flist[start:]: | |||||
os.system('clear') | |||||
print f['parent'] | |||||
print 'Fieldname: ' + colored(f['fieldname'], 'red') | |||||
print 'Label:' + f['label'] | |||||
print 'Description: ' + str(f['description']) | |||||
print 'Type: ' + str(f['fieldtype']) | |||||
print 'Options: ' + str(f['options']) | |||||
print str(start) + '/' + str(len(flist)) | |||||
action = '' | |||||
while action != 'n': | |||||
print '-----------------' | |||||
action = raw_input("""[n]ext, [f]ieldname, [l]abel, [d]escription, [r]emove, view [c]ode, [q]uit:""") | |||||
if action=='l': | |||||
old = f['label'] | |||||
new = update_field_property(f, 'label') | |||||
# replace code for search criteria | |||||
replace_code(old, new) | |||||
elif action=='d': | |||||
update_field_property(f, 'description') | |||||
elif action=='c': | |||||
print colored('js:', 'green') | |||||
os.system('grep -r --color --include=*.js "%s" ./' % f['fieldname']) | |||||
print colored('py:', 'green') | |||||
os.system('grep -r --color --include=*.py "%s" ./' % f['fieldname']) | |||||
elif action=='f': | |||||
old = f['fieldname'] | |||||
new = update_field_property(f, 'fieldname') | |||||
if new: | |||||
# rename in table and parentfield | |||||
from webnotes.model import docfield | |||||
docfield.rename(f['parent'], f['fieldname'], new) | |||||
# replace code | |||||
replace_code(old, new) | |||||
# write in fieldname patch file | |||||
update_fieldname_patch_file([f['parent'], f['fieldname'], new]) | |||||
elif action=='r': | |||||
remove_field(f) | |||||
action = 'n' | |||||
elif action=='q': | |||||
return | |||||
start += 1 | |||||
def setup_options(): | |||||
from optparse import OptionParser | |||||
parser = OptionParser() | |||||
parser.add_option("-a", "--all", | |||||
action="store_true", default=False, | |||||
help="Review all fields") | |||||
parser.add_option("-d", "--doctype", | |||||
nargs=1, default=False, metavar='DOCTYPE', | |||||
help="Review fields of a doctype") | |||||
parser.add_option("-f", "--field", | |||||
nargs=2, default=False, metavar='DOCTYPE FIELD', | |||||
help="Review a particular field") | |||||
return parser.parse_args() | |||||
if __name__=='__main__': | |||||
webnotes.connect() | |||||
options, args = setup_options() | |||||
if options.all: | |||||
review() | |||||
if options.doctype: | |||||
review(options.doctype) | |||||
if options.field: | |||||
review(options.field[0], options.field[1]) | |||||