diff --git a/css/fonts/cabin.woff b/css/fonts/cabin.woff new file mode 100644 index 0000000000..6f24a8af4e Binary files /dev/null and b/css/fonts/cabin.woff differ diff --git a/py/webnotes/model/docfield.py b/py/webnotes/model/docfield.py new file mode 100644 index 0000000000..fec1987710 --- /dev/null +++ b/py/webnotes/model/docfield.py @@ -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]) \ No newline at end of file diff --git a/py/webnotes/model/doclist.py b/py/webnotes/model/doclist.py index db994e798a..9ef58312f8 100644 --- a/py/webnotes/model/doclist.py +++ b/py/webnotes/model/doclist.py @@ -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): """ diff --git a/py/webnotes/model/events.py b/py/webnotes/model/events.py deleted file mode 100644 index d7fb5534ba..0000000000 --- a/py/webnotes/model/events.py +++ /dev/null @@ -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) - \ No newline at end of file diff --git a/py/webnotes/model/modules.py b/py/webnotes/model/modules.py deleted file mode 100644 index a90e258239..0000000000 --- a/py/webnotes/model/modules.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/py/webnotes/model/sql_sync.py b/py/webnotes/model/sql_sync.py deleted file mode 100644 index b6bf612043..0000000000 --- a/py/webnotes/model/sql_sync.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/py/webnotes/tests/test_docfield.py b/py/webnotes/tests/test_docfield.py new file mode 100644 index 0000000000..faa2646f98 --- /dev/null +++ b/py/webnotes/tests/test_docfield.py @@ -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() \ No newline at end of file diff --git a/py/webnotes/utils/__init__.py b/py/webnotes/utils/__init__.py index 2648d4248e..93a79cddf7 100644 --- a/py/webnotes/utils/__init__.py +++ b/py/webnotes/utils/__init__.py @@ -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', diff --git a/py/webnotes/utils/editdoctype.py b/py/webnotes/utils/editdoctype.py new file mode 100755 index 0000000000..38cb5153d7 --- /dev/null +++ b/py/webnotes/utils/editdoctype.py @@ -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]) + \ No newline at end of file