#478 start
This commit is contained in:
parent
cc5816ed5a
commit
6f00aaf9f7
7 changed files with 412 additions and 28 deletions
|
|
@ -510,11 +510,10 @@ def make_property_setter(args):
|
|||
|
||||
def get_application_home_page(user='Guest'):
|
||||
"""get home page for user"""
|
||||
roles = get_roles(user)
|
||||
hpl = db.sql("""select home_page
|
||||
from `tabDefault Home Page`
|
||||
where parent='Control Panel'
|
||||
and role in (%s) order by idx asc limit 1""" % ", ".join(['%s']*len(roles)), roles)
|
||||
and role in ('%s') order by idx asc limit 1""" % "', '".join(get_roles(user)))
|
||||
if hpl:
|
||||
return hpl[0][0]
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -325,10 +325,8 @@ class Database:
|
|||
|
||||
def get_values_from_single(self, fields, filters, doctype, as_dict=False, debug=False):
|
||||
if fields=="*" or isinstance(filters, dict):
|
||||
r = self.sql("""select field, value from tabSingles where doctype=%s""", doctype)
|
||||
|
||||
# check if single doc matches with filters
|
||||
values = frappe._dict(r)
|
||||
values = self.get_singles_dict(doctype)
|
||||
if isinstance(filters, dict):
|
||||
for key, value in filters.items():
|
||||
if values.get(key) != value:
|
||||
|
|
@ -351,6 +349,11 @@ class Database:
|
|||
else:
|
||||
return r and [[i[1] for i in r]] or []
|
||||
|
||||
def get_singles_dict(self, doctype):
|
||||
return frappe._dict(self.sql("""select field, value from
|
||||
tabSingles where doctype=%s""", doctype))
|
||||
|
||||
|
||||
def get_values_from_table(self, fields, filters, doctype, as_dict, debug):
|
||||
fl = []
|
||||
if isinstance(fields, (list, tuple)):
|
||||
|
|
|
|||
|
|
@ -13,25 +13,25 @@ import frappe
|
|||
from frappe.utils import cstr
|
||||
|
||||
type_map = {
|
||||
'currency': ('decimal', '18,6')
|
||||
,'int': ('int', '11')
|
||||
,'float': ('decimal', '18,6')
|
||||
,'percent': ('decimal', '18,6')
|
||||
,'check': ('int', '1')
|
||||
,'small text': ('text', '')
|
||||
,'long text': ('longtext', '')
|
||||
,'code': ('text', '')
|
||||
,'text editor': ('text', '')
|
||||
,'date': ('date', '')
|
||||
,'datetime': ('datetime', '')
|
||||
,'time': ('time', '')
|
||||
,'text': ('text', '')
|
||||
,'data': ('varchar', '255')
|
||||
,'link': ('varchar', '255')
|
||||
,'password': ('varchar', '255')
|
||||
,'select': ('varchar', '255')
|
||||
,'read only': ('varchar', '255')
|
||||
,'attach': ('varchar', '255')
|
||||
'Currency': ('decimal', '18,6')
|
||||
,'Int': ('int', '11')
|
||||
,'Float': ('decimal', '18,6')
|
||||
,'Percent': ('decimal', '18,6')
|
||||
,'Check': ('int', '1')
|
||||
,'Small Text': ('text', '')
|
||||
,'Long Text': ('longtext', '')
|
||||
,'Code': ('text', '')
|
||||
,'Text Editor': ('text', '')
|
||||
,'Date': ('date', '')
|
||||
,'Datetime': ('datetime', '')
|
||||
,'Time': ('time', '')
|
||||
,'Text': ('text', '')
|
||||
,'Data': ('varchar', '255')
|
||||
,'Link': ('varchar', '255')
|
||||
,'Password': ('varchar', '255')
|
||||
,'Select': ('varchar', '255')
|
||||
,'Read Only': ('varchar', '255')
|
||||
,'Attach': ('varchar', '255')
|
||||
}
|
||||
|
||||
default_columns = ['name', 'creation', 'modified', 'modified_by', 'owner', 'docstatus', 'parent',\
|
||||
|
|
@ -130,7 +130,7 @@ class DbTable:
|
|||
def get_index_definitions(self):
|
||||
ret = []
|
||||
for k in self.columns.keys():
|
||||
if type_map.get(self.columns[k].fieldtype) and type_map.get(self.columns[k].fieldtype.lower())[0] not in ('text', 'blob'):
|
||||
if type_map.get(self.columns[k].fieldtype) and type_map.get(self.columns[k].fieldtype)[0] not in ('text', 'blob'):
|
||||
ret.append('index `' + k + '`(`' + k + '`)')
|
||||
return ret
|
||||
|
||||
|
|
@ -422,7 +422,7 @@ def remove_all_foreign_keys():
|
|||
frappe.db.sql("alter table `tab%s` drop foreign key `%s`" % (t[0], f[1]))
|
||||
|
||||
def get_definition(fieldtype):
|
||||
d = type_map.get(fieldtype.lower())
|
||||
d = type_map.get(fieldtype)
|
||||
|
||||
if not d:
|
||||
return
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ from __future__ import unicode_literals
|
|||
Contains the Document class representing an object / record
|
||||
"""
|
||||
|
||||
_toc = ["frappe.model.doc.Document"]
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
import frappe.model.meta
|
||||
|
|
|
|||
180
frappe/model/document.py
Normal file
180
frappe/model/document.py
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import cint, flt
|
||||
from frappe.model import default_fields
|
||||
from frappe.model.db_schema import type_map
|
||||
from frappe.model.naming import set_new_name
|
||||
|
||||
# validation - links, mandatory
|
||||
# once_only validation
|
||||
# permissions
|
||||
# methods
|
||||
# timestamps and docstatus
|
||||
# defaults (insert)
|
||||
|
||||
class BaseDocument(object):
|
||||
def __init__(self, d):
|
||||
self.update(d)
|
||||
|
||||
def __getattr__(self, key):
|
||||
if self.__dict__.has_key(key):
|
||||
return self.__dict__[key]
|
||||
if key != "_table_columns" and key in self.get_table_columns():
|
||||
return None
|
||||
raise AttributeError, key
|
||||
|
||||
def update(self, d):
|
||||
if "doctype" in d:
|
||||
self.set("doctype", d.get("doctype"))
|
||||
for key, value in d.iteritems():
|
||||
self.set(key, value)
|
||||
|
||||
def get(self, key=None, default=None):
|
||||
if key:
|
||||
return self.__dict__.get(key, default)
|
||||
else:
|
||||
return self.__dict__
|
||||
|
||||
def set(self, key, value):
|
||||
if isinstance(value, list):
|
||||
for v in value:
|
||||
self.set(key, v)
|
||||
return
|
||||
else:
|
||||
if isinstance(value, dict):
|
||||
# appending
|
||||
if not self.get(key):
|
||||
self.__dict__[key] = []
|
||||
self.get(key).append(self._init_child(value, key))
|
||||
return
|
||||
|
||||
self.__dict__[key] = value
|
||||
|
||||
def _init_child(self, value, key):
|
||||
if not self.doctype:
|
||||
return value
|
||||
if not isinstance(value, BaseDocument):
|
||||
if not value.get("doctype"):
|
||||
value["doctype"] = self.meta.get({"fieldname":key})[0].options
|
||||
if not value.get("doctype"):
|
||||
raise AttributeError, key
|
||||
value = BaseDocument(value)
|
||||
|
||||
value.parent = self.name
|
||||
value.parenttype = self.doctype
|
||||
value.parentfield = key
|
||||
if not value.idx:
|
||||
value.idx = len(self.get(key))
|
||||
|
||||
return value
|
||||
|
||||
|
||||
@property
|
||||
def meta(self):
|
||||
if not self.get("_meta"):
|
||||
self._meta = frappe.get_doctype(self.doctype)
|
||||
return self._meta
|
||||
|
||||
def get_valid_dict(self):
|
||||
d = {}
|
||||
for fieldname in self.table_columns:
|
||||
d[fieldname] = self.get(fieldname)
|
||||
return d
|
||||
|
||||
@property
|
||||
def table_columns(self):
|
||||
return self.get_table_columns()
|
||||
|
||||
def get_table_columns(self):
|
||||
if not hasattr(self, "_table_columns"):
|
||||
doctype = self.__dict__.get("doctype")
|
||||
self._table_columns = default_fields[1:] + \
|
||||
[df.fieldname for df in frappe.get_doctype(doctype).get_docfields()
|
||||
if df.fieldtype in type_map]
|
||||
|
||||
return self._table_columns
|
||||
|
||||
def insert_row(self):
|
||||
set_new_name(self)
|
||||
d = self.get_valid_dict()
|
||||
columns = d.keys()
|
||||
frappe.db.sql("""insert into `tab{doctype}`
|
||||
({columns}) values ({values})""".format(
|
||||
doctype = self.doctype,
|
||||
columns = ", ".join(["`"+c+"`" for c in columns]),
|
||||
values = ", ".join(["%s"] * len(columns))
|
||||
), d.values())
|
||||
|
||||
def fix_numeric_types(self):
|
||||
for df in self.meta.get_docfields():
|
||||
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:
|
||||
self.docstatus = cint(self.docstatus)
|
||||
|
||||
|
||||
class Document(BaseDocument):
|
||||
def __init__(self, arg1, arg2=None):
|
||||
self.doctype = self.name = None
|
||||
if isinstance(arg1, basestring) and not arg2:
|
||||
# single
|
||||
self.doctype = self.name = arg1
|
||||
if arg1 and isinstance(arg1, basestring) and arg2:
|
||||
self.doctype = arg1
|
||||
if isinstance(arg2, dict):
|
||||
# filter
|
||||
self.name = frappe.db.get_value(arg1, arg2, "name")
|
||||
if self.name is None:
|
||||
raise frappe.DoesNotExistError
|
||||
else:
|
||||
self.name = arg2
|
||||
|
||||
self.load_from_db()
|
||||
elif isinstance(arg1, dict):
|
||||
super(Document, self).__init__(arg1)
|
||||
|
||||
def load_from_db(self):
|
||||
if self.meta[0].issingle:
|
||||
self.update(frappe.db.get_singles_dict(self.doctype))
|
||||
self.fix_numeric_types()
|
||||
else:
|
||||
d = frappe.db.get_value(self.doctype, self.name, "*", as_dict=1)
|
||||
for df in self.meta.get({"doctype":"DocField", "fieldtype":"Table"}):
|
||||
d[df.fieldname] = frappe.db.get_values(df.options,
|
||||
{"parent": self.name}, "*", as_dict=True)
|
||||
self.update(d)
|
||||
|
||||
def insert(self):
|
||||
# check links
|
||||
# check permissions
|
||||
|
||||
# parent
|
||||
if self.meta[0].issingle:
|
||||
self.update_single(self.get_valid_dict())
|
||||
else:
|
||||
self.insert_row()
|
||||
|
||||
# children
|
||||
for df in self.meta.get({"fieldtype":"Table"}):
|
||||
value = self.get(df.fieldname)
|
||||
if isinstance(value, list):
|
||||
for d in value:
|
||||
d.parent = self.name
|
||||
print d.__dict__
|
||||
d.insert_row()
|
||||
|
||||
def update_single(self, d):
|
||||
frappe.db.sql("""delete from tabSingles where doctype=%s""", d.get("doctype"))
|
||||
for field, value in d.iteritems():
|
||||
if field not in ("doctype"):
|
||||
frappe.db.sql("""insert into tabSingles(doctype, field, value)
|
||||
values (%s, %s, %s)""", (d.get("doctype", field, value)))
|
||||
|
||||
|
||||
|
||||
150
frappe/model/naming.py
Normal file
150
frappe/model/naming.py
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe.utils import now_datetime, cint
|
||||
|
||||
def set_new_name(doc):
|
||||
if getattr(doc, "_new_name_set", False):
|
||||
# already set by bean
|
||||
return
|
||||
|
||||
doc._new_name_set = True
|
||||
autoname = frappe.get_doctype(doc.doctype)[0].autoname
|
||||
doc.localname = doc.name # for passing back to client
|
||||
|
||||
# amendments
|
||||
if getattr(doc, "amended_from", None):
|
||||
return doc._get_amended_name()
|
||||
else:
|
||||
if hasattr(doc, "autoname"):
|
||||
doc.autoname()
|
||||
if doc.name and doc.localname != doc.name:
|
||||
return
|
||||
|
||||
# based on a field
|
||||
if autoname and autoname.startswith('field:'):
|
||||
n = doc.get(autoname[6:])
|
||||
if not n:
|
||||
raise Exception, 'Name is required'
|
||||
doc.name = n.strip()
|
||||
|
||||
elif autoname and autoname.startswith("naming_series:"):
|
||||
if not doc.naming_series:
|
||||
doc.naming_series = get_default_naming_series(doc.doctype)
|
||||
|
||||
if not doc.naming_series:
|
||||
frappe.msgprint(frappe._("Naming Series mandatory"), raise_exception=True)
|
||||
doc.name = make_autoname(doc.naming_series+'.#####')
|
||||
|
||||
# call the method!
|
||||
elif autoname and autoname!='Prompt':
|
||||
doc.name = make_autoname(autoname, doc.doctype)
|
||||
|
||||
# given
|
||||
elif doc.fields.get('__newname',''):
|
||||
doc.name = doc.fields['__newname']
|
||||
|
||||
# default name for table
|
||||
elif doc.meta.istable:
|
||||
doc.name = make_autoname('#########', doc.doctype)
|
||||
|
||||
# unable to determine a name, use global series
|
||||
if not doc.name:
|
||||
doc.name = make_autoname('#########', doc.doctype)
|
||||
|
||||
validate_name(doc.doctype, doc.name)
|
||||
|
||||
def make_autoname(key, doctype=''):
|
||||
"""
|
||||
Creates an autoname from the given key:
|
||||
|
||||
**Autoname rules:**
|
||||
|
||||
* The key is separated by '.'
|
||||
* '####' represents a series. The string before this part becomes the prefix:
|
||||
Example: ABC.#### creates a series ABC0001, ABC0002 etc
|
||||
* 'MM' represents the current month
|
||||
* 'YY' and 'YYYY' represent the current year
|
||||
|
||||
|
||||
*Example:*
|
||||
|
||||
* DE/./.YY./.MM./.##### will create a series like
|
||||
DE/09/01/0001 where 09 is the year, 01 is the month and 0001 is the series
|
||||
"""
|
||||
if key=="hash":
|
||||
return frappe.generate_hash(doctype)
|
||||
|
||||
if not "#" in key:
|
||||
key = key + ".#####"
|
||||
|
||||
n = ''
|
||||
l = key.split('.')
|
||||
series_set = False
|
||||
today = now_datetime()
|
||||
|
||||
for e in l:
|
||||
en = ''
|
||||
if e.startswith('#'):
|
||||
if not series_set:
|
||||
digits = len(e)
|
||||
en = getseries(n, digits, doctype)
|
||||
series_set = True
|
||||
elif e=='YY':
|
||||
en = today.strftime('%y')
|
||||
elif e=='MM':
|
||||
en = today.strftime('%m')
|
||||
elif e=='DD':
|
||||
en = today.strftime("%d")
|
||||
elif e=='YYYY':
|
||||
en = today.strftime('%Y')
|
||||
else: en = e
|
||||
n+=en
|
||||
return n
|
||||
|
||||
def getseries(key, digits, doctype=''):
|
||||
# series created ?
|
||||
current = frappe.db.sql("select `current` from `tabSeries` where name=%s for update", (key,))
|
||||
if current and current[0][0] is not None:
|
||||
current = current[0][0]
|
||||
# yes, update it
|
||||
frappe.db.sql("update tabSeries set current = current+1 where name=%s", (key,))
|
||||
current = cint(current) + 1
|
||||
else:
|
||||
# no, create it
|
||||
frappe.db.sql("insert into tabSeries (name, current) values (%s, 1)", (key,))
|
||||
current = 1
|
||||
return ('%0'+str(digits)+'d') % current
|
||||
|
||||
def get_default_naming_series(doctype):
|
||||
"""get default value for `naming_series` property"""
|
||||
from frappe.model.doctype import get_property
|
||||
naming_series = get_property(doctype, "options", "naming_series")
|
||||
if naming_series:
|
||||
naming_series = naming_series.split("\n")
|
||||
return naming_series[0] or naming_series[1]
|
||||
else:
|
||||
return None
|
||||
|
||||
def validate_name(doctype, name, case=None, merge=False):
|
||||
if not name: return 'No Name Specified for %s' % doctype
|
||||
if name.startswith('New '+doctype):
|
||||
raise NameError, 'There were some errors setting the name, please contact the administrator'
|
||||
if case=='Title Case': name = name.title()
|
||||
if case=='UPPER CASE': name = name.upper()
|
||||
name = name.strip()
|
||||
return name
|
||||
|
||||
def _get_amended_name(doc):
|
||||
am_id = 1
|
||||
am_prefix = doc.amended_from
|
||||
if frappe.db.get_value(doc.doctype, doc.amended_from, "amended_from"):
|
||||
am_id = cint(doc.amended_from.split('-')[-1]) + 1
|
||||
am_prefix = '-'.join(doc.amended_from.split('-')[:-1]) # except the last hyphen
|
||||
|
||||
doc.name = am_prefix + '-' + str(am_id)
|
||||
return doc.name
|
||||
|
||||
54
frappe/tests/test_document.py
Normal file
54
frappe/tests/test_document.py
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
import frappe, unittest
|
||||
|
||||
from frappe.model.document import Document
|
||||
|
||||
class TestDocument(unittest.TestCase):
|
||||
def test_load(self):
|
||||
d = Document("DocType", "User")
|
||||
self.assertEquals(d.doctype, "DocType")
|
||||
self.assertEquals(d.name, "User")
|
||||
self.assertEquals(d.allow_rename, 1)
|
||||
self.assertTrue(isinstance(d.fields, list))
|
||||
self.assertTrue(isinstance(d.permissions, list))
|
||||
self.assertTrue(filter(lambda d: d.fieldname=="email", d.fields))
|
||||
|
||||
def test_load_single(self):
|
||||
d = Document("Website Settings", "Website Settings")
|
||||
self.assertEquals(d.name, "Website Settings")
|
||||
self.assertEquals(d.doctype, "Website Settings")
|
||||
self.assertTrue(d.disable_signup in (0, 1))
|
||||
|
||||
def test_insert(self):
|
||||
d = Document({
|
||||
"doctype":"Event",
|
||||
"subject":"_Test Event 1",
|
||||
"starts_on": "2014-01-01",
|
||||
"event_type": "Public"
|
||||
})
|
||||
d.insert()
|
||||
self.assertTrue(d.name.startswith("EV"))
|
||||
self.assertEquals(frappe.db.get_value("Event", d.name, "subject"),
|
||||
"_Test Event 1")
|
||||
|
||||
def test_insert_with_child(self):
|
||||
d = Document({
|
||||
"doctype":"Event",
|
||||
"subject":"_Test Event 2",
|
||||
"starts_on": "2014-01-01",
|
||||
"event_type": "Public",
|
||||
"event_individuals": [
|
||||
{
|
||||
"person": "Administrator"
|
||||
}
|
||||
]
|
||||
})
|
||||
d.insert()
|
||||
self.assertTrue(d.name.startswith("EV"))
|
||||
self.assertEquals(frappe.db.get_value("Event", d.name, "subject"),
|
||||
"_Test Event 2")
|
||||
|
||||
d1 = Document("Event", d.name)
|
||||
self.assertTrue(d1.event_individuals[0].person, "Administrator")
|
||||
Loading…
Add table
Reference in a new issue