@@ -1,5 +1,5 @@ | |||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals | |||
@@ -18,6 +18,8 @@ class DocType(Document): | |||
for c in [".", "/", "#", "&", "=", ":", "'", '"']: | |||
if c in self.name: | |||
frappe.msgprint(c + " not allowed in name", raise_exception=1) | |||
for d in self.get("fields"): | |||
d.parent = self.name | |||
self.validate_series() | |||
self.scrub_field_names() | |||
self.validate_title_field() | |||
@@ -29,7 +31,7 @@ class DocType(Document): | |||
def change_modified_of_parent(self): | |||
if frappe.flags.in_import: | |||
return | |||
parent_list = frappe.db.sql("""SELECT parent | |||
parent_list = frappe.db.sql("""SELECT parent | |||
from tabDocField where fieldtype="Table" and options=%s""", self.name) | |||
for p in parent_list: | |||
frappe.db.sql('UPDATE tabDocType SET modified=%s WHERE `name`=%s', (now(), p[0])) | |||
@@ -38,28 +40,28 @@ class DocType(Document): | |||
restricted = ('name','parent','idx','owner','creation','modified','modified_by', | |||
'parentfield','parenttype',"file_list") | |||
for d in self.get("fields"): | |||
if d.parent and d.fieldtype: | |||
if (not d.fieldname): | |||
if d.fieldtype: | |||
if (not getattr(d, "fieldname", None)): | |||
if d.label: | |||
d.fieldname = d.label.strip().lower().replace(' ','_') | |||
if d.fieldname in restricted: | |||
d.fieldname = d.fieldname + '1' | |||
else: | |||
d.fieldname = d.fieldtype.lower().replace(" ","_") + "_" + str(d.idx) | |||
def validate_title_field(self): | |||
if self.title_field and \ | |||
self.title_field not in [d.fieldname for d in self.get("fields")]: | |||
frappe.throw(_("Title field must be a valid fieldname")) | |||
def validate_series(self, autoname=None, name=None): | |||
if not autoname: autoname = self.autoname | |||
if not name: name = self.name | |||
if not autoname and self.get("fields", {"fieldname":"naming_series"}): | |||
self.autoname = "naming_series:" | |||
if autoname and (not autoname.startswith('field:')) and (not autoname.startswith('eval:')) \ | |||
and (not autoname=='Prompt') and (not autoname.startswith('naming_series:')): | |||
prefix = autoname.split('.')[0] | |||
@@ -73,12 +75,12 @@ class DocType(Document): | |||
self.change_modified_of_parent() | |||
make_module_and_roles(self) | |||
from frappe import conf | |||
if (not frappe.flags.in_import) and conf.get('developer_mode') or 0: | |||
self.export_doc() | |||
self.make_controller_template() | |||
# update index | |||
if not getattr(self, "custom", False): | |||
from frappe.modules import load_doctype_module | |||
@@ -99,29 +101,29 @@ class DocType(Document): | |||
frappe.db.sql("delete from `tabCustom Script` where dt = %s", self.name) | |||
frappe.db.sql("delete from `tabProperty Setter` where doc_type = %s", self.name) | |||
frappe.db.sql("delete from `tabReport` where ref_doctype=%s", self.name) | |||
def before_rename(self, old, new, merge=False): | |||
if merge: | |||
frappe.throw(_("DocType can not be merged")) | |||
def after_rename(self, old, new, merge=False): | |||
if self.issingle: | |||
frappe.db.sql("""update tabSingles set doctype=%s where doctype=%s""", (new, old)) | |||
else: | |||
frappe.db.sql("rename table `tab%s` to `tab%s`" % (old, new)) | |||
def export_doc(self): | |||
from frappe.modules.export_file import export_to_files | |||
export_to_files(record_list=[['DocType', self.name]]) | |||
def import_doc(self): | |||
from frappe.modules.import_module import import_from_files | |||
import_from_files(record_list=[[self.module, 'doctype', self.name]]) | |||
import_from_files(record_list=[[self.module, 'doctype', self.name]]) | |||
def make_controller_template(self): | |||
from frappe.modules import get_doc_path, get_module_path, scrub | |||
pypath = os.path.join(get_doc_path(self.module, | |||
pypath = os.path.join(get_doc_path(self.module, | |||
self.doctype, self.name), scrub(self.name) + '.py') | |||
if not os.path.exists(pypath): | |||
@@ -130,18 +132,18 @@ class DocType(Document): | |||
if not app: | |||
frappe.throw("App not found!") | |||
app_publisher = frappe.get_hooks(hook="app_publisher", app_name=app)[0] | |||
with open(pypath, 'w') as pyfile: | |||
with open(os.path.join(get_module_path("core"), "doctype", "doctype", | |||
with open(os.path.join(get_module_path("core"), "doctype", "doctype", | |||
"doctype_template.py"), 'r') as srcfile: | |||
pyfile.write(srcfile.read().format(app_publisher=app_publisher, classname=self.name.replace(" ", ""))) | |||
def make_amendable(self): | |||
""" | |||
if is_submittable is set, add amended_from docfields | |||
""" | |||
if self.is_submittable: | |||
if not frappe.db.sql("""select name from tabDocField | |||
if not frappe.db.sql("""select name from tabDocField | |||
where fieldname = 'amended_from' and parent = %s""", self.name): | |||
self.append("fields", { | |||
"label": "Amended From", | |||
@@ -152,43 +154,43 @@ class DocType(Document): | |||
"print_hide": 1, | |||
"no_copy": 1 | |||
}) | |||
def get_max_idx(self): | |||
max_idx = frappe.db.sql("""select max(idx) from `tabDocField` where parent = %s""", | |||
max_idx = frappe.db.sql("""select max(idx) from `tabDocField` where parent = %s""", | |||
self.name) | |||
return max_idx and max_idx[0][0] or 0 | |||
def validate_fields_for_doctype(doctype): | |||
validate_fields(frappe.get_meta(doctype).get("fields")) | |||
def validate_fields(fields): | |||
def check_illegal_characters(fieldname): | |||
for c in ['.', ',', ' ', '-', '&', '%', '=', '"', "'", '*', '$', | |||
for c in ['.', ',', ' ', '-', '&', '%', '=', '"', "'", '*', '$', | |||
'(', ')', '[', ']', '/']: | |||
if c in fieldname: | |||
frappe.msgprint("'%s' not allowed in fieldname (%s)" % (c, fieldname)) | |||
def check_unique_fieldname(fieldname): | |||
duplicates = filter(None, map(lambda df: df.fieldname==fieldname and str(df.idx) or None, fields)) | |||
if len(duplicates) > 1: | |||
frappe.msgprint('Fieldname <b>%s</b> appears more than once in rows (%s). Please rectify' \ | |||
% (fieldname, ', '.join(duplicates)), raise_exception=1) | |||
def check_illegal_mandatory(d): | |||
if d.fieldtype in ('HTML', 'Button', 'Section Break', 'Column Break') and d.reqd: | |||
print d.fieldname, d.reqd | |||
frappe.msgprint('%(parent)s, %(label)s [%(fieldtype)s] cannot be mandatory' % d.as_dict(), | |||
frappe.msgprint('%(parent)s, %(label)s [%(fieldtype)s] cannot be mandatory' % d.as_dict(), | |||
raise_exception=1) | |||
def check_link_table_options(d): | |||
if d.fieldtype in ("Link", "Table"): | |||
if not d.options: | |||
frappe.msgprint("""#%(idx)s %(label)s: Options must be specified for Link and Table type fields""" % d.as_dict(), | |||
frappe.msgprint("""#%(idx)s %(label)s: Options must be specified for Link and Table type fields""" % d.as_dict(), | |||
raise_exception=1) | |||
if d.options=="[Select]": | |||
if d.options=="[Select]" or d.options==d.parent: | |||
return | |||
if d.options != d.parent and not frappe.db.exists("DocType", d.options): | |||
frappe.msgprint("""#%(idx)s %(label)s: Options %(options)s must be a valid "DocType" for Link and Table type fields""" % d.as_dict(), | |||
frappe.msgprint("""#%(idx)s %(label)s: Options %(options)s must be a valid "DocType" for Link and Table type fields""" % d.as_dict(), | |||
raise_exception=1) | |||
def check_hidden_and_mandatory(d): | |||
@@ -200,7 +202,7 @@ def validate_fields(fields): | |||
if len(filter(lambda d: d.in_list_view, fields))==0: | |||
for d in fields[:5]: | |||
d.in_list_view = 1 | |||
def check_width(d): | |||
if d.fieldtype == "Currency" and cint(d.width) < 100: | |||
frappe.msgprint("Minimum width for FieldType 'Currency' is 100px", raise_exception=1) | |||
@@ -224,46 +226,52 @@ def validate_fields(fields): | |||
def validate_permissions_for_doctype(doctype, for_remove=False): | |||
validate_permissions(frappe.get_meta(doctype, cached=False).get("permissions"), for_remove) | |||
def validate_permissions(permissions, for_remove=False): | |||
doctype = permissions and permissions[0].parent | |||
issingle = issubmittable = isimportable = False | |||
if doctype and not doctype.startswith("New DocType"): | |||
values = frappe.db.get_value("DocType", doctype, | |||
["issingle", "is_submittable", "allow_import"], as_dict=True) | |||
values = frappe.db.get_value("DocType", doctype, | |||
["issingle", "is_submittable", "allow_import"], as_dict=True) | |||
if values: | |||
issingle = cint(values.issingle) | |||
issubmittable = cint(values.is_submittable) | |||
isimportable = cint(values.allow_import) | |||
def get_txt(d): | |||
return "For %s (level %s) in %s, row #%s:" % (d.role, d.permlevel, d.parent, d.idx) | |||
def check_atleast_one_set(d): | |||
if not d.read and not d.write and not d.submit and not d.cancel and not d.create: | |||
frappe.msgprint(get_txt(d) + " Atleast one of Read, Write, Create, Submit, Cancel must be set.", | |||
raise_exception=True) | |||
def check_double(d): | |||
similar = permissions.get({ | |||
"role":d.role, | |||
"permlevel":d.permlevel, | |||
"match": d.match | |||
}) | |||
if len(similar) > 1: | |||
frappe.msgprint(get_txt(d) + " Only one rule allowed for a particular Role and Level.", | |||
has_similar = False | |||
for p in permissions: | |||
if p.role==d.role and p.permlevel==d.permlevel and p.match==d.match and p!=d: | |||
has_similar = True | |||
break | |||
if has_similar: | |||
frappe.msgprint(get_txt(d) + " Only one rule allowed for a particular Role and Level.", | |||
raise_exception=True) | |||
def check_level_zero_is_set(d): | |||
if cint(d.permlevel) > 0 and d.role != 'All': | |||
if not permissions.get({"role": d.role, "permlevel": 0}): | |||
has_zero_perm = False | |||
for p in permissions: | |||
if p.role==d.role and (p.permlevel or 0)==0 and p!=d: | |||
has_zero_perm = True | |||
break | |||
if not has_zero_perm: | |||
frappe.msgprint(get_txt(d) + " Higher level permissions are meaningless if level 0 permission is not set.", | |||
raise_exception=True) | |||
if d.create or d.submit or d.cancel or d.amend or d.match: | |||
if d.create or d.submit or d.cancel or d.amend or d.match: | |||
frappe.msgprint("Create, Submit, Cancel, Amend, Match has no meaning at level " + d.permlevel, | |||
raise_exception=True) | |||
def check_permission_dependency(d): | |||
if d.cancel and not d.submit: | |||
frappe.msgprint(get_txt(d) + " Cannot set Cancel permission if Submit is not set.", | |||
@@ -280,23 +288,23 @@ def validate_permissions(permissions, for_remove=False): | |||
if d.get("import") and not d.create: | |||
frappe.msgprint(get_txt(d) + " Cannot set Import if Create is not set.", | |||
raise_exception=True) | |||
def remove_rights_for_single(d): | |||
if not issingle: | |||
return | |||
if d.report: | |||
frappe.msgprint("{doctype} {meaningless}".format(doctype=doctype, | |||
meaningless=_("is a single DocType, permission of type Report is meaningless."))) | |||
d.report = 0 | |||
d.set("import", 0) | |||
d.set("export", 0) | |||
if d.restrict: | |||
frappe.msgprint("{doctype} {meaningless}".format(doctype=doctype, | |||
meaningless=_("is a single DocType, permission of type Restrict is meaningless."))) | |||
d.restrict = 0 | |||
def check_if_submittable(d): | |||
if d.submit and not issubmittable: | |||
frappe.msgprint(doctype + " is not Submittable, cannot assign submit rights.", | |||
@@ -304,14 +312,14 @@ def validate_permissions(permissions, for_remove=False): | |||
elif d.amend and not issubmittable: | |||
frappe.msgprint(doctype + " is not Submittable, cannot assign amend rights.", | |||
raise_exception=True) | |||
def check_if_importable(d): | |||
if d.get("import") and not isimportable: | |||
frappe.throw("{doctype}: {not_importable}".format(doctype=doctype, | |||
not_importable=_("is not allowed to be imported, cannot assign import rights."))) | |||
for d in permissions: | |||
if not d.permlevel: | |||
if not d.permlevel: | |||
d.permlevel=0 | |||
check_atleast_one_set(d) | |||
if not for_remove: | |||
@@ -328,10 +336,10 @@ def make_module_and_roles(doc, perm_fieldname="permissions"): | |||
m = frappe.get_doc({"doctype": "Module Def", "module_name": doc.module}) | |||
m.ignore_mandatory = m.ignore_permissions = True | |||
m.insert() | |||
default_roles = ["Administrator", "Guest", "All"] | |||
roles = [p.role for p in doc.get("permissions") or []] + default_roles | |||
for role in list(set(roles)): | |||
if not frappe.db.exists("Role", role): | |||
r = frappe.get_doc({"doctype": "Role", "role_name": role}) | |||
@@ -1,26 +1,107 @@ | |||
{ | |||
"_last_update": null, | |||
"_user_tags": null, | |||
"allow_attach": null, | |||
"allow_copy": null, | |||
"allow_email": null, | |||
"allow_import": null, | |||
"allow_print": null, | |||
"allow_rename": 1, | |||
"allow_trash": null, | |||
"autoname": "field:module_name", | |||
"creation": "2013-01-10 16:34:03.000000", | |||
"change_log": null, | |||
"client_script": null, | |||
"client_script_core": null, | |||
"client_string": null, | |||
"colour": null, | |||
"creation": "2013-01-10 16:34:03", | |||
"custom": null, | |||
"default_print_format": null, | |||
"description": null, | |||
"docstatus": 0, | |||
"doctype": "DocType", | |||
"document_type": null, | |||
"dt_template": null, | |||
"fields": [ | |||
{ | |||
"allow_on_submit": null, | |||
"default": null, | |||
"depends_on": null, | |||
"description": null, | |||
"fieldname": "module_name", | |||
"fieldtype": "Data", | |||
"hidden": null, | |||
"ignore_restrictions": null, | |||
"in_filter": null, | |||
"in_list_view": 1, | |||
"label": "Module Name", | |||
"no_column": null, | |||
"no_copy": null, | |||
"oldfieldname": "module_name", | |||
"oldfieldtype": "Data", | |||
"permlevel": 0 | |||
"options": null, | |||
"permlevel": 0, | |||
"print_hide": null, | |||
"print_width": null, | |||
"read_only": null, | |||
"report_hide": null, | |||
"reqd": null, | |||
"search_index": null, | |||
"set_only_once": null, | |||
"trigger": null, | |||
"width": null | |||
}, | |||
{ | |||
"allow_on_submit": null, | |||
"default": null, | |||
"depends_on": null, | |||
"description": null, | |||
"fieldname": "app_name", | |||
"fieldtype": "Data", | |||
"hidden": null, | |||
"ignore_restrictions": null, | |||
"in_filter": null, | |||
"in_list_view": 1, | |||
"label": "App Name", | |||
"no_column": null, | |||
"no_copy": null, | |||
"oldfieldname": null, | |||
"oldfieldtype": null, | |||
"options": null, | |||
"permlevel": 0, | |||
"print_hide": null, | |||
"print_width": null, | |||
"read_only": null, | |||
"report_hide": null, | |||
"reqd": 1, | |||
"search_index": null, | |||
"set_only_once": null, | |||
"trigger": null, | |||
"width": null | |||
} | |||
], | |||
"hide_heading": null, | |||
"hide_toolbar": null, | |||
"icon": "icon-sitemap", | |||
"idx": 1, | |||
"modified": "2014-02-18 15:32:49.000000", | |||
"in_create": null, | |||
"in_dialog": null, | |||
"is_submittable": null, | |||
"is_transaction_doc": null, | |||
"issingle": null, | |||
"istable": null, | |||
"max_attachments": null, | |||
"menu_index": null, | |||
"modified": "2014-04-07 12:47:20.781686", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "Module Def", | |||
"name_case": null, | |||
"owner": "Administrator", | |||
"parent": null, | |||
"parent_node": null, | |||
"parentfield": null, | |||
"parenttype": null, | |||
"permissions": [ | |||
{ | |||
"amend": 0, | |||
@@ -28,21 +109,54 @@ | |||
"create": 1, | |||
"delete": 1, | |||
"email": 1, | |||
"export": null, | |||
"import": null, | |||
"match": null, | |||
"permlevel": 0, | |||
"print": 1, | |||
"read": 1, | |||
"report": 1, | |||
"restrict": null, | |||
"restricted": null, | |||
"role": "Administrator", | |||
"submit": 0, | |||
"write": 1 | |||
}, | |||
{ | |||
"amend": null, | |||
"cancel": null, | |||
"create": 1, | |||
"delete": 1, | |||
"email": null, | |||
"export": null, | |||
"import": null, | |||
"match": null, | |||
"permlevel": 0, | |||
"print": null, | |||
"read": 1, | |||
"report": null, | |||
"restrict": null, | |||
"restricted": null, | |||
"role": "System Manager", | |||
"submit": null, | |||
"write": 1 | |||
} | |||
] | |||
], | |||
"plugin": null, | |||
"print_outline": null, | |||
"read_only": null, | |||
"read_only_onload": null, | |||
"search_fields": null, | |||
"section_style": null, | |||
"server_code": null, | |||
"server_code_compiled": null, | |||
"server_code_core": null, | |||
"server_code_error": null, | |||
"show_in_menu": null, | |||
"smallicon": null, | |||
"subject": null, | |||
"tag_fields": null, | |||
"title_field": null, | |||
"use_template": null, | |||
"version": null | |||
} |
@@ -2,9 +2,29 @@ | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals | |||
import frappe | |||
import frappe, os | |||
from frappe.model.document import Document | |||
class ModuleDef(Document): | |||
pass | |||
def validate(self): | |||
if not frappe.local.module_app.get(self.name): | |||
with open(frappe.get_app_path(self.app_name, "modules.txt"), "r") as f: | |||
content = f.read() | |||
if not frappe.scrub(self.name) in content.splitlines(): | |||
content += "\n" + frappe.scrub(self.name) | |||
with open(frappe.get_app_path(self.app_name, "modules.txt"), "w") as f: | |||
f.write(content) | |||
frappe.clear_cache() | |||
frappe.setup_module_map() | |||
module_path = frappe.get_app_path(self.app_name, self.name) | |||
if not os.path.exists(module_path): | |||
os.mkdir(module_path) | |||
with open(os.path.join(module_path, "__init__.py"), "w") as f: | |||
f.write("") | |||
@@ -1,5 +1,5 @@ | |||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals | |||
import frappe | |||
@@ -10,7 +10,7 @@ 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) | |||
@@ -18,7 +18,7 @@ class BaseDocument(object): | |||
def meta(self): | |||
if not hasattr(self, "_meta"): | |||
self._meta = frappe.get_meta(self.doctype) | |||
return self._meta | |||
def update(self, d): | |||
@@ -26,17 +26,17 @@ class BaseDocument(object): | |||
self.set("doctype", d.get("doctype")) | |||
for key, value in d.iteritems(): | |||
self.set(key, value) | |||
def update_if_missing(self, d): | |||
if isinstance(d, BaseDocument): | |||
d = d.get_valid_dict() | |||
if "doctype" in d: | |||
self.set("doctype", d.get("doctype")) | |||
for key, value in d.iteritems(): | |||
if self.get(key) is None: | |||
self.set(key, value) | |||
def get_db_value(self, key): | |||
return frappe.db.get_value(self.doctype, self.name, key) | |||
@@ -53,26 +53,26 @@ class BaseDocument(object): | |||
value = self.__dict__.get(key, default) | |||
else: | |||
value = self.__dict__.get(key, default) | |||
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) | |||
return value | |||
else: | |||
return self.__dict__ | |||
def getone(self, key, filters=None): | |||
return self.get(key, filters=filters, limit=1)[0] | |||
def set(self, key, value): | |||
if isinstance(value, list): | |||
self.__dict__[key] = [] | |||
self.extend(key, value) | |||
else: | |||
self.__dict__[key] = value | |||
def append(self, key, value=None): | |||
if value==None: | |||
value={} | |||
@@ -84,17 +84,17 @@ class BaseDocument(object): | |||
return value | |||
else: | |||
raise ValueError | |||
def extend(self, key, value): | |||
if isinstance(value, list): | |||
for v in value: | |||
self.append(key, v) | |||
else: | |||
raise ValueError | |||
def remove(self, doc): | |||
self.get(doc.parentfield).remove(doc) | |||
def _init_child(self, value, key): | |||
if not self.doctype: | |||
return value | |||
@@ -105,7 +105,7 @@ class BaseDocument(object): | |||
raise AttributeError, key | |||
value = BaseDocument(value) | |||
value.init_valid_columns() | |||
value.parent = self.name | |||
value.parenttype = self.doctype | |||
value.parentfield = key | |||
@@ -119,22 +119,24 @@ class BaseDocument(object): | |||
for fieldname in self.meta.get_valid_columns(): | |||
d[fieldname] = self.get(fieldname) | |||
return d | |||
def init_valid_columns(self): | |||
for key in default_fields: | |||
if key not in self.__dict__: | |||
self.__dict__[key] = None | |||
if getattr(self, "_metaclass", False) or self.doctype in ("DocType", "DocField", "DocPerm"): | |||
return | |||
for key in self.meta.get_valid_columns(): | |||
valid = frappe.db.get_table_columns(self.doctype) | |||
else: | |||
valid = self.meta.get_valid_columns() | |||
for key in valid: | |||
if key not in self.__dict__: | |||
self.__dict__[key] = None | |||
def is_new(self): | |||
return self.get("__islocal") | |||
def as_dict(self): | |||
doc = self.get_valid_dict() | |||
doc["doctype"] = self.doctype | |||
@@ -142,19 +144,19 @@ class BaseDocument(object): | |||
children = self.get(df.fieldname) or [] | |||
doc[df.fieldname] = [d.as_dict() for d in children] | |||
return doc | |||
def get_table_field_doctype(self, fieldname): | |||
return self.meta.get_field(fieldname).options | |||
def get_parentfield_of_doctype(self, doctype): | |||
fieldname = [df.fieldname for df in self.meta.get_table_fields() if df.options==doctype] | |||
return fieldname[0] if fieldname else None | |||
def db_insert(self): | |||
set_new_name(self) | |||
d = self.get_valid_dict() | |||
columns = d.keys() | |||
frappe.db.sql("""insert into `tab{doctype}` | |||
frappe.db.sql("""insert into `tab{doctype}` | |||
({columns}) values ({values})""".format( | |||
doctype = self.doctype, | |||
columns = ", ".join(["`"+c+"`" for c in columns]), | |||
@@ -166,69 +168,69 @@ class BaseDocument(object): | |||
if self.get("__islocal") or not self.name: | |||
self.db_insert() | |||
return | |||
d = self.get_valid_dict() | |||
columns = d.keys() | |||
frappe.db.sql("""update `tab{doctype}` | |||
frappe.db.sql("""update `tab{doctype}` | |||
set {values} where name=%s""".format( | |||
doctype = self.doctype, | |||
values = ", ".join(["`"+c+"`=%s" for c in columns]) | |||
), d.values() + [d.get("name")]) | |||
def _fix_numeric_types(self): | |||
for df in self.meta.get("fields"): | |||
if df.fieldtype in ("Int", "Check"): | |||
self.set(df.fieldname, cint(self.get(df.fieldname))) | |||
elif df.fieldtype in ("Float", "Currency"): | |||
self.set(df.fieldname, flt(self.get(df.fieldname))) | |||
if self.docstatus is not None: | |||
if self.docstatus is not None: | |||
self.docstatus = cint(self.docstatus) | |||
def _get_missing_mandatory_fields(self): | |||
"""Get mandatory fields that do not have any values""" | |||
def get_msg(df): | |||
if df.fieldtype == "Table": | |||
return "{}: {}: {}".format(_("Error"), _("Data missing in table"), _(df.label)) | |||
elif self.parentfield: | |||
return "{}: {} #{}: {}: {}".format(_("Error"), _("Row"), self.idx, | |||
_("Value missing for"), _(df.label)) | |||
else: | |||
return "{}: {}: {}".format(_("Error"), _("Value missing for"), _(df.label)) | |||
missing = [] | |||
for df in self.meta.get("fields", {"reqd": 1}): | |||
if self.get(df.fieldname) in (None, []): | |||
missing.append((df.fieldname, get_msg(df))) | |||
return missing | |||
def get_invalid_links(self): | |||
def get_msg(df, docname): | |||
if self.parentfield: | |||
return "{} #{}: {}: {}".format(_("Row"), self.idx, _(df.label), docname) | |||
else: | |||
return "{}: {}".format(_(df.label), docname) | |||
invalid_links = [] | |||
for df in self.meta.get_link_fields(): | |||
doctype = df.options | |||
if not doctype: | |||
frappe.throw("Options not set for link field: {}".format(df.fieldname)) | |||
elif doctype.lower().startswith("link:"): | |||
doctype = doctype[5:] | |||
docname = self.get(df.fieldname) | |||
if docname and not frappe.db.get_value(doctype, docname): | |||
invalid_links.append((df.fieldname, docname, get_msg(df, docname))) | |||
return invalid_links | |||
def _validate_constants(self): | |||
if frappe.flags.in_import: | |||
return | |||
@@ -239,8 +241,8 @@ class BaseDocument(object): | |||
for fieldname in constants: | |||
if self.get(fieldname) != values.get(fieldname): | |||
frappe.throw("{0}: {1}".format(_("Value cannot be changed for"), | |||
_(self.meta.get_label(fieldname))), | |||
frappe.throw("{0}: {1}".format(_("Value cannot be changed for"), | |||
_(self.meta.get_label(fieldname))), | |||
frappe.CannotChangeConstantError) | |||
def _filter(data, filters, limit=None): | |||
@@ -250,12 +252,12 @@ def _filter(data, filters, limit=None): | |||
"key" : True (exists), "key": False (does not exist) }""" | |||
out = [] | |||
for d in data: | |||
add = True | |||
for f in filters: | |||
fval = filters[f] | |||
if fval is True: | |||
fval = ("not None", fval) | |||
elif fval is False: | |||
@@ -265,7 +267,7 @@ def _filter(data, filters, limit=None): | |||
fval = ("^", fval[1:]) | |||
else: | |||
fval = ("=", fval) | |||
if not frappe.compare(getattr(d, f, None), fval[0], fval[1]): | |||
add = False | |||
break | |||
@@ -274,5 +276,5 @@ def _filter(data, filters, limit=None): | |||
out.append(d) | |||
if limit and (len(out)-1)==limit: | |||
break | |||
return out |
@@ -1,5 +1,5 @@ | |||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
# MIT License. See license.txt | |||
# metadata | |||
@@ -23,12 +23,12 @@ class Meta(Document): | |||
_metaclass = True | |||
default_fields = default_fields[1:] | |||
special_doctypes = ("DocField", "DocPerm", "Role", "DocType", "Module Def") | |||
def __init__(self, doctype): | |||
self._fields = {} | |||
super(Meta, self).__init__("DocType", doctype) | |||
self.process() | |||
def load_from_db(self): | |||
try: | |||
super(Meta, self).load_from_db() | |||
@@ -37,34 +37,34 @@ class Meta(Document): | |||
fname = frappe.scrub(self.name) | |||
with open(frappe.get_app_path("frappe", "core", "doctype", fname, fname + ".json"), "r") as f: | |||
txt = json.loads(f.read()) | |||
for d in txt.get("fields", []): | |||
d["doctype"] = "DocField" | |||
for d in txt.get("permissions", []): | |||
d["doctype"] = "DocPerm" | |||
self.__dict__.update(txt) | |||
self.fields = [BaseDocument(d) for d in self.fields] | |||
if hasattr(self, "permissions"): | |||
self.permissions = [BaseDocument(d) for d in self.permissions] | |||
else: | |||
raise | |||
def get_link_fields(self): | |||
tmp = self.get("fields", {"fieldtype":"Link", "options":["!=", "[Select]"]}) | |||
for df in self.get("fields", {"fieldtype":"Select", "options": "^link:"}): | |||
tmp.append(frappe._dict({"fieldname":df.fieldname, "label":df.label, | |||
tmp.append(frappe._dict({"fieldname":df.fieldname, "label":df.label, | |||
"fieldtype":"Link", "options": df.options[5:]})) | |||
return tmp | |||
def get_table_fields(self): | |||
if not hasattr(self, "_table_fields"): | |||
if self.name!="DocType": | |||
self._table_fields = self.get('fields', {"fieldtype":"Table"}) | |||
else: | |||
self._table_fields = doctype_table_fields | |||
return self._table_fields | |||
def get_valid_columns(self): | |||
@@ -74,12 +74,12 @@ class Meta(Document): | |||
else: | |||
self._valid_columns = self.default_fields + \ | |||
[df.fieldname for df in self.get("fields") if df.fieldtype in type_map] | |||
return self._valid_columns | |||
def get_table_field_doctype(self, fieldname): | |||
return { "fields": "DocField", "permissions": "DocPerm"}.get(fieldname) | |||
def get_field(self, fieldname): | |||
if not fieldname in self._fields: | |||
fields = self.get("fields", {"fieldname":fieldname}) | |||
@@ -88,31 +88,31 @@ class Meta(Document): | |||
def get_label(self, fieldname): | |||
return self.get_field(fieldname).label | |||
def get_options(self, fieldname): | |||
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() | |||
def add_custom_fields(self): | |||
try: | |||
self.extend("fields", frappe.db.sql("""SELECT * FROM `tabCustom Field` | |||
WHERE dt = %s AND docstatus < 2""", (self.name,), as_dict=1, | |||
WHERE dt = %s AND docstatus < 2""", (self.name,), as_dict=1, | |||
update={"is_custom_field": True})) | |||
except Exception, e: | |||
if e.args[0]==1146: | |||
return | |||
else: | |||
raise | |||
def apply_property_setters(self): | |||
for ps in frappe.db.sql("""select * from `tabProperty Setter` where | |||
doc_type=%s""", (self.name,), as_dict=1): | |||
@@ -129,7 +129,7 @@ class Meta(Document): | |||
ps.value = cint(ps.value) | |||
docfield.set(ps.property, ps.value) | |||
def sort_fields(self): | |||
"""sort on basis of previous_field""" | |||
newlist = [] | |||
@@ -157,7 +157,7 @@ class Meta(Document): | |||
newlist.append(d) | |||
pending.remove(d) | |||
# recurring at end | |||
# recurring at end | |||
if pending: | |||
newlist += pending | |||
@@ -168,12 +168,12 @@ class Meta(Document): | |||
idx += 1 | |||
self.set("fields", newlist) | |||
def get_restricted_fields(self, restricted_types): | |||
restricted_fields = self.get("fields", { | |||
"fieldtype":"Link", | |||
"parent": self.name, | |||
"ignore_restrictions":("!=", 1), | |||
"fieldtype":"Link", | |||
"parent": self.name, | |||
"ignore_restrictions":("!=", 1), | |||
"options":("in", restricted_types) | |||
}) | |||
if self.name in restricted_types: | |||
@@ -184,7 +184,7 @@ class Meta(Document): | |||
doctype_table_fields = [ | |||
frappe._dict({"fieldname": "fields", "options": "DocField"}), | |||
frappe._dict({"fieldname": "fields", "options": "DocField"}), | |||
frappe._dict({"fieldname": "permissions", "options": "DocPerm"}) | |||
] | |||
@@ -197,8 +197,8 @@ def is_single(doctype): | |||
raise Exception, 'Cannot determine whether %s is single' % doctype | |||
def get_parent_dt(dt): | |||
parent_dt = frappe.db.sql("""select parent from tabDocField | |||
where fieldtype="Table" and options=%s and (parent not like "old_parent:%%") | |||
parent_dt = frappe.db.sql("""select parent from tabDocField | |||
where fieldtype="Table" and options=%s and (parent not like "old_parent:%%") | |||
limit 1""", dt) | |||
return parent_dt and parent_dt[0][0] or '' | |||
@@ -208,37 +208,37 @@ def set_fieldname(field_id, fieldname): | |||
def get_field_currency(df, doc): | |||
"""get currency based on DocField options and fieldvalue in doc""" | |||
currency = None | |||
if ":" in cstr(df.options): | |||
split_opts = df.options.split(":") | |||
if len(split_opts)==3: | |||
currency = frappe.db.get_value(split_opts[0], doc.get(split_opts[1]), | |||
currency = frappe.db.get_value(split_opts[0], doc.get(split_opts[1]), | |||
split_opts[2]) | |||
else: | |||
currency = doc.get(df.options) | |||
return currency | |||
def get_field_precision(df, doc): | |||
"""get precision based on DocField options and fieldvalue in doc""" | |||
from frappe.utils import get_number_format_info | |||
number_format = None | |||
if df.fieldtype == "Currency": | |||
currency = get_field_currency(df, doc) | |||
if currency: | |||
number_format = frappe.db.get_value("Currency", currency, "number_format") | |||
if not number_format: | |||
number_format = frappe.db.get_default("number_format") or "#,###.##" | |||
decimal_str, comma_str, precision = get_number_format_info(number_format) | |||
if df.fieldtype == "Float": | |||
precision = cint(frappe.db.get_default("float_precision")) or 3 | |||
return precision | |||
def clear_cache(doctype=None): | |||
def clear_single(dt): | |||
frappe.cache().delete_value("meta:" + dt) | |||
@@ -248,7 +248,7 @@ def clear_cache(doctype=None): | |||
clear_single(doctype) | |||
# clear all parent doctypes | |||
for dt in frappe.db.sql("""select parent from tabDocField | |||
for dt in frappe.db.sql("""select parent from tabDocField | |||
where fieldtype="Table" and options=%s""", (doctype,)): | |||
clear_single(dt[0]) | |||
@@ -261,4 +261,4 @@ def clear_cache(doctype=None): | |||
for dt in frappe.db.sql("""select name from tabDocType"""): | |||
clear_single(dt[0]) | |||
frappe.cache().delete_value("is_table") | |||
frappe.cache().delete_value("is_table") |
@@ -18,39 +18,40 @@ $.extend(frappe.model, { | |||
owner: user | |||
}; | |||
frappe.model.set_default_values(doc, parent_doc); | |||
if(parent_doc) { | |||
$.extend(doc, { | |||
parent: parent_doc.name, | |||
parentfield: parentfield, | |||
parenttype: parent_doc.doctype, | |||
}); | |||
if(!parent_doc[parentfield]) parent_doc[parentfield] = []; | |||
parent_doc[parentfield].push(doc); | |||
} else { | |||
frappe.provide("frappe.model.docinfo." + doctype + "." + doc.name); | |||
} | |||
frappe.model.add_to_locals(doc); | |||
frappe.model.add_to_locals(doc); | |||
return doc; | |||
}, | |||
make_new_doc_and_get_name: function(doctype) { | |||
return frappe.model.get_new_doc(doctype).name; | |||
}, | |||
get_new_name: function(doctype) { | |||
var cnt = frappe.model.new_name_count | |||
if(!cnt[doctype]) | |||
if(!cnt[doctype]) | |||
cnt[doctype] = 0; | |||
cnt[doctype]++; | |||
return frappe._('New') + ' '+ frappe._(doctype) + ' ' + cnt[doctype]; | |||
}, | |||
set_default_values: function(doc, parent_doc) { | |||
var doctype = doc.doctype; | |||
var docfields = frappe.meta.docfield_list[doctype] || []; | |||
var updated = []; | |||
for(var fid=0;fid<docfields.length;fid++) { | |||
var f = docfields[fid]; | |||
if(!in_list(frappe.model.no_value_type, f.fieldtype) && doc[f.fieldname]==null) { | |||
@@ -60,7 +61,7 @@ $.extend(frappe.model, { | |||
v = cint(v); | |||
else if(in_list(["Currency", "Float"], f.fieldtype)) | |||
v = flt(v); | |||
doc[f.fieldname] = v; | |||
updated.push(f.fieldname); | |||
} | |||
@@ -68,17 +69,17 @@ $.extend(frappe.model, { | |||
} | |||
return updated; | |||
}, | |||
get_default_value: function(df, doc, parent_doc) { | |||
var def_vals = { | |||
"__user": user, | |||
"Today": dateutil.get_today(), | |||
} | |||
var restrictions = frappe.defaults.get_restrictions(); | |||
if(df.fieldtype==="Link" && restrictions | |||
&& df.ignore_restrictions != 1 | |||
&& restrictions[df.options] | |||
&& restrictions[df.options] | |||
&& (restrictions[df.options].length===1)) | |||
return restrictions[df.options][0]; | |||
else if(frappe.defaults.get_user_default(df.fieldname)) | |||
@@ -92,22 +93,22 @@ $.extend(frappe.model, { | |||
else if(df["default"] && df["default"][0]!==":") | |||
return df["default"]; | |||
}, | |||
get_default_from_boot_docs: function(df, doc, parent_doc) { | |||
// set default from partial docs passed during boot like ":User" | |||
if(frappe.get_list(df["default"]).length > 0) { | |||
var ref_fieldname = df["default"].slice(1).toLowerCase().replace(" ", "_"); | |||
var ref_value = parent_doc ? | |||
var ref_value = parent_doc ? | |||
parent_doc[ref_fieldname] : | |||
frappe.defaults.get_user_default(ref_fieldname); | |||
var ref_doc = ref_value ? frappe.get_doc(df["default"], ref_value) : null; | |||
if(ref_doc && ref_doc[df.fieldname]) { | |||
return ref_doc[df.fieldname]; | |||
} | |||
} | |||
}, | |||
add_child: function(parent_doc, doctype, parentfield, idx) { | |||
// create row doc | |||
idx = idx ? idx - 0.1 : (parent_doc[parentfield] || []).length + 1; | |||
@@ -124,10 +125,10 @@ $.extend(frappe.model, { | |||
} | |||
cur_frm && cur_frm.dirty(); | |||
return d; | |||
}, | |||
copy_doc: function(doc, from_amend) { | |||
var no_copy_list = ['name','amended_from','amendment_date','cancel_reason']; | |||
var newdoc = frappe.model.get_new_doc(doc.doctype); | |||
@@ -135,10 +136,10 @@ $.extend(frappe.model, { | |||
for(var key in doc) { | |||
// dont copy name and blank fields | |||
var df = frappe.meta.get_docfield(doc.doctype, key); | |||
if(key.substr(0,2)!='__' | |||
&& !in_list(no_copy_list, key) | |||
&& !(df && (!from_amend && cint(df.no_copy)==1))) { | |||
if(key.substr(0,2)!='__' | |||
&& !in_list(no_copy_list, key) | |||
&& !(df && (!from_amend && cint(df.no_copy)==1))) { | |||
value = doc[key]; | |||
if(df.fieldtype==="Table") { | |||
newdoc[key] = []; | |||
@@ -160,7 +161,7 @@ $.extend(frappe.model, { | |||
return newdoc; | |||
}, | |||
open_mapped_doc: function(opts) { | |||
return frappe.call({ | |||
type: "GET", | |||
@@ -204,10 +205,10 @@ $.extend(frappe.model, { | |||
title: frappe._("Get From ") + frappe._(opts.source_doctype), | |||
fields: [ | |||
{ | |||
"fieldtype": "Link", | |||
"fieldtype": "Link", | |||
"label": frappe._(opts.source_doctype), | |||
"fieldname": opts.source_doctype, | |||
"options": opts.source_doctype, | |||
"fieldname": opts.source_doctype, | |||
"options": opts.source_doctype, | |||
"get_query": opts.get_query, | |||
reqd:1}, | |||
{ | |||
@@ -215,7 +216,7 @@ $.extend(frappe.model, { | |||
"label": frappe._("Get"), | |||
click: function() { | |||
var values = d.get_values(); | |||
if(!values) | |||
if(!values) | |||
return; | |||
opts.source_name = values[opts.source_doctype]; | |||
d.hide(); | |||
@@ -229,4 +230,4 @@ $.extend(frappe.model, { | |||
_map(); | |||
} | |||
} | |||
}) | |||
}) |