@@ -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): | |||
getattr(self.obj, 'custom_' + method)() | |||
from webnotes.model.events import trigger | |||
trigger(method, self.doc) | |||
def save_main(self): | |||
@@ -313,6 +312,19 @@ def clone(source_doclist): | |||
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 | |||
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. | |||
""" | |||
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) | |||
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]) | |||