diff --git a/frappe/boot.py b/frappe/boot.py index 43a112548a..7d1618e5b3 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -21,7 +21,7 @@ def get_bootinfo(): get_user(bootinfo) # system info - bootinfo['control_panel'] = frappe.get_doc('Control Panel').as_dict() + bootinfo['control_panel'] = frappe._dict(frappe.get_doc('Control Panel').as_dict()) bootinfo['sysdefaults'] = frappe.defaults.get_defaults() bootinfo['server_date'] = frappe.utils.nowdate() bootinfo["send_print_in_body_and_attachment"] = frappe.db.get_value("Outgoing Email Settings", diff --git a/frappe/core/doctype/customize_form/customize_form.js b/frappe/core/doctype/customize_form/customize_form.js index 2a9bc3842b..2b95d6ba6e 100644 --- a/frappe/core/doctype/customize_form/customize_form.js +++ b/frappe/core/doctype/customize_form/customize_form.js @@ -41,37 +41,39 @@ frappe.ui.form.on("Customize Form", "doc_type", function(frm) { frappe.ui.form.on("Customize Form", "refresh", function(frm) { frm.disable_save(); frm.frm_head.appframe.iconbar.clear("1"); - frm.appframe.set_title_right("Update", function() { - if(frm.doc.doc_type) { - return frm.call({ - doc: frm.doc, - method: "save_customization", - callback: function(r) { - if(!r.exc && r.server_messages) { - frm.script_manager.trigger("doc_type"); - frm.frm_head.set_label(['Updated', 'label-success']); + + if(frm.doc.doc_type) { + frm.appframe.set_title_right("Update", function() { + if(frm.doc.doc_type) { + return frm.call({ + doc: frm.doc, + method: "save_customization", + callback: function(r) { + if(!r.exc) { + frappe.customize_form.clear_locals_and_refresh(frm); + } } - } - }); - } - }); + }); + } + }); - frm.add_custom_button('Refresh Form', function() { - frm.script_manager.trigger("doc_type"); - }, "icon-refresh"); + frm.add_custom_button('Refresh Form', function() { + frm.script_manager.trigger("doc_type"); + }, "icon-refresh"); - frm.add_custom_button('Reset to defaults', function() { - frappe.customize_form.confirm('This will remove the customizations defined for this form.

' - + 'Are you sure you want to reset to defaults?', frm); - }, "icon-eraser"); - - if(!frm.doc.doc_type) { - var frm_head = frm.frm_head.appframe; - $(frm_head.buttons['Update']).prop('disabled', true); - $(frm_head.buttons['Refresh Form']).prop('disabled', true); - $(frm_head.buttons['Reset to defaults']).prop('disabled', true); + frm.add_custom_button('Reset to defaults', function() { + frappe.customize_form.confirm('This will remove the customizations defined for this form.

' + + 'Are you sure you want to reset to defaults?', frm); + }, "icon-eraser"); } + // if(!frm.doc.doc_type) { + // var frm_head = frm.frm_head.appframe; + // $(frm_head.buttons['Update']).prop('disabled', true); + // $(frm_head.buttons['Refresh Form']).prop('disabled', true); + // $(frm_head.buttons['Reset to defaults']).prop('disabled', true); + // } + if(frappe.route_options) { frappe.model.set_value("Customize Form", null, "doc_type", frappe.route_options.doctype) frappe.route_options = null; @@ -79,6 +81,8 @@ frappe.ui.form.on("Customize Form", "refresh", function(frm) { }); frappe.customize_form.confirm = function(msg, frm) { + if(!frm.doc.doc_type) return; + var d = new frappe.ui.Dialog({ title: 'Reset To Defaults', width: 500 @@ -94,14 +98,13 @@ frappe.customize_form.confirm = function(msg, frm) { var proceed_btn = $btn(button_wrapper, 'Proceed', function() { return frm.call({ doc: frm.doc, - method: "delete", + method: "reset_to_defaults", callback: function(r) { if(r.exc) { msgprint(r.exc); } else { - frm.confirm.dialog.hide(); - frm.refresh(); - frm.frm_head.set_label(['Saved', 'label-success']); + frappe.customize_form.confirm.dialog.hide(); + frappe.customize_form.clear_locals_and_refresh(frm); } } }); @@ -110,16 +113,23 @@ frappe.customize_form.confirm = function(msg, frm) { $y(proceed_btn, {marginRight: '20px', fontWeight: 'bold'}); var cancel_btn = $btn(button_wrapper, 'Cancel', function() { - frm.confirm.dialog.hide(); + frappe.customize_form.confirm.dialog.hide(); }); $(cancel_btn).addClass('btn-small btn-info'); $y(cancel_btn, {fontWeight: 'bold'}); - frm.confirm.dialog = d; + frappe.customize_form.confirm.dialog = d; d.show(); } +frappe.customize_form.clear_locals_and_refresh = function(frm) { + // clear doctype from locals + frappe.model.clear_doc("DocType", frm.doc.doc_type); + delete frappe.meta.docfield_copy[frm.doc.doc_type]; + + frm.refresh(); +} frappe.customize_form.add_fields_help = function(frm) { $(frm.grids[0].parent).before( diff --git a/frappe/core/doctype/customize_form/customize_form.py b/frappe/core/doctype/customize_form/customize_form.py index a48ec4be2d..b871cab778 100644 --- a/frappe/core/doctype/customize_form/customize_form.py +++ b/frappe/core/doctype/customize_form/customize_form.py @@ -12,7 +12,7 @@ from frappe.utils import cstr from frappe.model.document import Document class CustomizeForm(Document): - doctype_properties = [ + doctype_properties = ( 'search_fields', 'default_print_format', 'read_only_onload', @@ -21,9 +21,9 @@ class CustomizeForm(Document): 'allow_copy', 'allow_attach', 'max_attachments' - ] + ) - docfield_properties = [ + docfield_properties = ( 'idx', 'label', 'fieldtype', @@ -44,69 +44,65 @@ class CustomizeForm(Document): 'description', 'default', 'name', - ] + ) + + allowed_fieldtype_change = (('Currency', 'Float'), ('Small Text', 'Data'), + ('Text', 'Text Editor', 'Code')) - property_restrictions = { - 'fieldtype': [['Currency', 'Float'], ['Small Text', 'Data'], ['Text', 'Text Editor', 'Code']], - } + # property_restrictions = { + # 'fieldtype': (('Currency', 'Float'), ('Small Text', 'Data'), ('Text', 'Text Editor', 'Code')), + # } def on_update(self): frappe.db.sql("delete from tabSingles where doctype='Customize Form'") frappe.db.sql("delete from `tabCustomize Form Field`") def fetch_to_customize(self): - pass + self.clear_existing_doc() + if not self.doc_type: + return + + meta = frappe.get_meta(self.doc_type) + + # doctype properties + for fieldname in self.doctype_properties: + self.set(fieldname, meta.get(fieldname)) + + for d in meta.get("fields"): + new_d = {} + for fieldname in self.docfield_properties: + new_d[fieldname] = d.get(fieldname) + self.append("fields", new_d) + + # NOTE doc is sent to clientside by run_method + + def clear_existing_doc(self): + doc_type = self.doc_type + + for fieldname in self.meta.get_valid_columns(): + self.set(fieldname, None) + + for df in self.meta.get_table_fields(): + self.set(df.fieldname, []) + + self.doc_type = doc_type + self.name = "Customize Form" def save_customization(self): - pass - - def _get(self): - """ - Gets DocFields applied with Property Setter customizations via Customize Form Field - """ - self.clear() - - if self.doc_type: - meta = frappe.get_meta(self.doc_type) - for d in meta.get("fields"): - new = self.append('fields', {}) - self._set({ 'list': self.docfield_properties, 'doc' : d, 'doc_to_set': new }) - self._set({ 'list': self.doctype_properties, 'doc': d }) - - def clear(self): - """ - Clear fields in the doc - """ - # Clear table before adding new doctype's fields - self.set('fields', []) - self._set({ 'list': self.doctype_properties, 'value': None }) - - - def _set(self, args): - """ - Set a list of attributes of a doc to a value - or to attribute values of a doc passed + if not self.doc_type: + return - args can contain: - * list --> list of attributes to set - * doc_to_set --> defaults to self - * value --> to set all attributes to one value eg. None - * doc --> copy attributes from doc to doc_to_set - """ - if not 'doc_to_set' in args: - args['doc_to_set'] = self - - if 'list' in args: - if 'value' in args: - for f in args['list']: - args['doc_to_set'].set(f, None) - elif 'doc' in args: - for f in args['list']: - args['doc_to_set'].set(f, args['doc'].get(f)) - else: - frappe.msgprint("Please specify args['list'] to set", raise_exception=1) - - + frappe.clear_cache(doctype=self.doc_type) + self.fetch_to_customize() + + def reset_to_defaults(self): + if not self.doc_type: + return + + frappe.db.sql("""delete from `tabProperty Setter` where doc_type=%s""", self.doc_type) + frappe.clear_cache(doctype=self.doc_type) + self.fetch_to_customize() + def post(self): """ Save diff between Customize Form Bean and DocType Bean as property setter entries @@ -310,25 +306,4 @@ class CustomizeForm(Document): if not d.delete: d.insert() - - def delete(self): - """ - Deletes all property setter entries for the selected doctype - and resets it to standard - """ - if self.doc_type: - frappe.db.sql(""" - DELETE FROM `tabProperty Setter` - WHERE doc_type = %s""", self.doc_type) - - frappe.clear_cache(doctype=self.doc_type) - - self._get() - - def remove_forbidden(self, string): - """ - Replace forbidden characters with a space - """ - forbidden = ['%', "'", '"', '#', '*', '?', '`'] - for f in forbidden: - string.replace(f, ' ') + \ No newline at end of file diff --git a/frappe/core/page/data_import_tool/data_import_tool.py b/frappe/core/page/data_import_tool/data_import_tool.py index 5bff89723b..674b226962 100644 --- a/frappe/core/page/data_import_tool/data_import_tool.py +++ b/frappe/core/page/data_import_tool/data_import_tool.py @@ -48,7 +48,7 @@ def export_json(doctype, name, path): if not name or name=="-": name = doctype with open(path, "w") as outfile: - doc = frappe.get_doc(doctype, name).as_dict() + doc = frappe.get_doc(doctype, name) for d in doc.get_all_children(): d.set("parent", None) d.set("name", None) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 0999149545..b0ad4a95a3 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -8,12 +8,14 @@ from frappe.utils import cint, flt, cstr, now from frappe.model.naming import set_new_name class BaseDocument(object): + ignore_in_getter = ("doctype", "_meta", "meta", "_table_fields", "_valid_columns") + def __init__(self, d): self.update(d) def __getattr__(self, key): # this is called only when something is not found in dir or __dict__ - if not key.startswith("__") and key not in ("doctype", "_meta", "meta", "_table_fields", "_valid_columns") \ + if not key.startswith("__") and key not in self.ignore_in_getter \ and key in self.meta.get_valid_columns(): return None else: @@ -44,7 +46,8 @@ class BaseDocument(object): else: value = self.__dict__.get(key, default) - if value is None and key!="_meta" and key in (d.fieldname for d in self.meta.get_table_fields()): + if value is None and key not in self.ignore_in_getter \ + and key in (d.fieldname for d in self.meta.get_table_fields()): self.set(key, []) value = self.__dict__.get(key) @@ -62,7 +65,7 @@ class BaseDocument(object): def append(self, key, value=None): if value==None: value={} - if isinstance(value, dict): + if isinstance(value, (dict, BaseDocument)): if not self.get(key): self.__dict__[key] = [] value = self._init_child(value, key) @@ -97,7 +100,7 @@ class BaseDocument(object): return value def get_valid_dict(self): - d = frappe._dict() + d = {} for fieldname in self.meta.get_valid_columns(): d[fieldname] = self.get(fieldname) return d diff --git a/frappe/model/document.py b/frappe/model/document.py index b8d7f073b7..d17150cbd2 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -383,16 +383,21 @@ class Document(BaseDocument): @staticmethod def hook(f): - def add_to_response(new_response): - if isinstance(new_response, dict): - frappe.local.response.update(new_response) + def add_to_return_value(self, new_return_value): + if isinstance(new_return_value, dict): + if not self.get("_return_value"): + self._return_value = {} + self._return_value.update(new_return_value) + else: + self._return_value = new_return_value or self.get("_return_value") def compose(fn, *hooks): def runner(self, method, *args, **kwargs): - add_to_response(fn(self, *args, **kwargs)) + add_to_return_value(self, fn(self, *args, **kwargs)) for f in hooks: - add_to_response(f(self, method, *args, **kwargs)) - return frappe.local.response + add_to_return_value(self, f(self, method, *args, **kwargs)) + + return self._return_value return runner diff --git a/frappe/model/meta.py b/frappe/model/meta.py index 3b7b854c83..534b03ad4d 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -23,14 +23,16 @@ class Meta(Document): _metaclass = True _fields = {} default_fields = default_fields[1:] + special_doctypes = ("DocField", "DocPerm", "Role", "DocType", "Module Def") def __init__(self, doctype): super(Meta, self).__init__("DocType", doctype) + self.process() def load_from_db(self): try: super(Meta, self).load_from_db() except frappe.DoesNotExistError: - if self.doctype=="DocType" and self.name in ("DocField", "DocPerm", "Role", "DocType", "Module Def"): + if self.doctype=="DocType" and self.name in self.special_doctypes: fname = frappe.scrub(self.name) with open(frappe.get_app_path("frappe", "core", "doctype", fname, fname + ".json"), "r") as f: txt = f.read() @@ -60,7 +62,7 @@ class Meta(Document): def get_valid_columns(self): if not hasattr(self, "_valid_columns"): - if self.name in ("DocType", "DocField", "DocPerm"): + if self.name in ("DocType", "DocField", "DocPerm", "Property Setter"): self._valid_columns = frappe.db.get_table_columns(self.name) else: self._valid_columns = self.default_fields + \ @@ -84,6 +86,11 @@ class Meta(Document): return self.get_field(fieldname).options def process(self): + # don't process for special doctypes + # prevent's circular dependency + if self.name in self.special_doctypes: + return + self.add_custom_fields() self.apply_property_setters() self.sort_fields() @@ -91,7 +98,7 @@ class Meta(Document): def add_custom_fields(self): try: self.extend("fields", frappe.db.sql("""SELECT * FROM `tabCustom Field` - WHERE dt = %s AND docstatus < 2""", (doctype,), as_dict=1)) + WHERE dt = %s AND docstatus < 2""", (self.name,), as_dict=1)) except Exception, e: if e.args[0]==1146: return @@ -100,7 +107,7 @@ class Meta(Document): def apply_property_setters(self): for ps in frappe.db.sql("""select * from `tabProperty Setter` where - doc_type=%s""", (doctype,), as_dict=1): + doc_type=%s""", (self.name,), as_dict=1): if ps['doctype_or_field']=='DocType': if ps.get('property_type', None) in ('Int', 'Check'): ps['value'] = cint(ps['value']) @@ -131,7 +138,7 @@ class Meta(Document): while (pending and maxloops>0): maxloops -= 1 for d in pending[:]: - if d.previous_field: + if d.get("previous_field"): # field already added for n in newlist: if n.fieldname==d.previous_field: @@ -190,20 +197,6 @@ def get_parent_dt(dt): def set_fieldname(field_id, fieldname): frappe.db.set_value('DocField', field_id, 'fieldname', fieldname) -def get_table_fields(doctype): - child_tables = [[d[0], d[1]] for d in frappe.db.sql("""select options, fieldname - from tabDocField where parent=%s and fieldtype='Table'""", doctype, as_list=1)] - - try: - custom_child_tables = [[d[0], d[1]] for d in frappe.db.sql("""select options, fieldname - from `tabCustom Field` where dt=%s and fieldtype='Table'""", doctype, as_list=1)] - except Exception, e: - if e.args[0]!=1146: - raise - custom_child_tables = [] - - return child_tables + custom_child_tables - def get_field_currency(df, doc): """get currency based on DocField options and fieldvalue in doc""" currency = None diff --git a/frappe/utils/response.py b/frappe/utils/response.py index 356cdf6061..ea13fe8bf2 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -100,7 +100,6 @@ def compressBuf(buf): def json_handler(obj): """serialize non-serializable data for json""" - # serialize date if isinstance(obj, (datetime.date, datetime.timedelta, datetime.datetime)): return unicode(obj) diff --git a/frappe/website/sitemap.py b/frappe/website/sitemap.py index d8bc775a2c..cb16efc62a 100644 --- a/frappe/website/sitemap.py +++ b/frappe/website/sitemap.py @@ -21,7 +21,7 @@ def get_sitemap_options(path): return frappe._dict(sitemap_options) def build_sitemap_options(path): - sitemap_options = frappe.get_doc("Website Route", path).as_dict() + sitemap_options = frappe._dict(frappe.get_doc("Website Route", path).as_dict()) home_page = get_home_page() sitemap_config = frappe.get_doc("Website Template", diff --git a/frappe/widgets/form/run_method.py b/frappe/widgets/form/run_method.py index 79ffaaf9c2..eb8919ddef 100644 --- a/frappe/widgets/form/run_method.py +++ b/frappe/widgets/form/run_method.py @@ -35,6 +35,8 @@ def runserverobj(): args = json.loads(args) except ValueError: r = doc.run_method(method, args) + except TypeError: + r = doc.run_method(method) else: r = doc.run_method(method, **args)