457 lines
13 KiB
Python
457 lines
13 KiB
Python
# 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.
|
|
#
|
|
|
|
# TODO:
|
|
# Patch: Remove DocFormat
|
|
|
|
# imports
|
|
from __future__ import unicode_literals
|
|
import webnotes
|
|
import webnotes.model
|
|
import webnotes.model.doc
|
|
from webnotes.utils.cache import CacheItem
|
|
|
|
class _DocType:
|
|
"""
|
|
The _DocType object is created internally using the module's `get` method.
|
|
"""
|
|
def __init__(self, name):
|
|
self.name = name
|
|
|
|
def make_doclist(self, form=1):
|
|
"""
|
|
|
|
"""
|
|
# do not load from cache if auto cache clear is enabled
|
|
import conf
|
|
from_cache = True
|
|
if hasattr(conf, 'auto_cache_clear'):
|
|
from_cache = not conf.auto_cache_clear
|
|
|
|
if form and from_cache:
|
|
cached_doclist = self.load_from_cache()
|
|
if cached_doclist: return cached_doclist
|
|
|
|
# Get parent doc and its fields
|
|
doclist = webnotes.model.doc.get('DocType', self.name, 1)
|
|
doclist += self.get_custom_fields(self.name)
|
|
|
|
if form:
|
|
table_fields = [t[0] for t in self.get_table_fields(doclist)]
|
|
# for each unique table
|
|
for t in list(set(table_fields)):
|
|
# Get child doc and its fields
|
|
table_doclist = webnotes.model.doc.get('DocType', t, 1)
|
|
table_doclist += self.get_custom_fields(t)
|
|
doclist += table_doclist
|
|
|
|
self.apply_property_setters(doclist)
|
|
|
|
if form:
|
|
self.load_select_options(doclist)
|
|
self.add_code(doclist[0])
|
|
self.load_print_formats(doclist)
|
|
self.insert_into_cache(doclist)
|
|
|
|
return doclist
|
|
|
|
def get_custom_fields(self, doc_type):
|
|
"""
|
|
Gets a list of custom field docs masked as type DocField
|
|
"""
|
|
custom_doclist = []
|
|
res = webnotes.conn.sql("""SELECT * FROM `tabCustom Field`
|
|
WHERE dt = %s AND docstatus < 2""", doc_type, as_dict=1)
|
|
for r in res:
|
|
# Cheat! Mask Custom Field as DocField
|
|
custom_field = webnotes.model.doc.Document(fielddata=r)
|
|
self.mask_custom_field(custom_field, doc_type)
|
|
custom_doclist.append(custom_field)
|
|
|
|
return custom_doclist
|
|
|
|
def mask_custom_field(self, custom_field, doc_type):
|
|
"""
|
|
Masks doctype and parent related properties of Custom Field as that
|
|
of DocField
|
|
"""
|
|
custom_field.fields.update({
|
|
'doctype': 'DocField',
|
|
'parent': doc_type,
|
|
'parentfield': 'fields',
|
|
'parenttype': 'DocType',
|
|
})
|
|
|
|
def get_table_fields(self, doclist):
|
|
"""
|
|
Returns [[options, fieldname]] of fields of type 'Table'
|
|
"""
|
|
table_fields = []
|
|
for d in doclist:
|
|
if d.doctype=='DocField' and d.fieldtype == 'Table':
|
|
table_fields.append([d.options, d.fieldname])
|
|
return table_fields
|
|
|
|
def apply_property_setters(self, doclist):
|
|
"""
|
|
|
|
"""
|
|
property_dict, doc_type_list = self.get_property_setters(doclist)
|
|
for d in doclist:
|
|
self.update_field_properties(d, property_dict)
|
|
|
|
self.apply_previous_field_properties(doclist, property_dict,
|
|
doc_type_list)
|
|
|
|
def get_property_setters(self, doclist):
|
|
"""
|
|
Returns a dict of property setter lists and doc_type_list
|
|
"""
|
|
from webnotes.utils import cstr
|
|
property_dict = {}
|
|
# final property dict will be
|
|
# {
|
|
# doc_type: {
|
|
# fieldname: [list of property setter dicts]
|
|
# }
|
|
# }
|
|
|
|
doc_type_list = list(set(
|
|
d.doctype=='DocType' and d.name or d.parent
|
|
for d in doclist))
|
|
in_string = '", "'.join(doc_type_list)
|
|
for ps in webnotes.conn.sql("""\
|
|
SELECT doc_type, field_name, property, property_type, value
|
|
FROM `tabProperty Setter`
|
|
WHERE doc_type IN ("%s")""" % in_string, as_dict=1):
|
|
property_dict.setdefault(ps.get('doc_type'),
|
|
{}).setdefault(cstr(ps.get('field_name')), []).append(ps)
|
|
|
|
return property_dict, doc_type_list
|
|
|
|
def update_field_properties(self, d, property_dict):
|
|
"""
|
|
apply properties except previous_field ones
|
|
"""
|
|
from webnotes.utils import cstr
|
|
# get property setters for a given doctype's fields
|
|
doctype_property_dict = (d.doctype=='DocField' and property_dict.get(d.parent) or
|
|
property_dict.get(d.name))
|
|
if not (doctype_property_dict and doctype_property_dict.get(cstr(d.fieldname))): return
|
|
|
|
from webnotes.utils import cint
|
|
prop_updates = []
|
|
for prop in doctype_property_dict.get(cstr(d.fieldname)):
|
|
if prop.get('property')=='previous_field': continue
|
|
if prop.get('property_type') == 'Check' or \
|
|
prop.get('value') in ['0', '1']:
|
|
prop_updates.append([prop.get('property'), cint(prop.get('value'))])
|
|
else:
|
|
prop_updates.append([prop.get('property'), prop.get('value')])
|
|
|
|
prop_updates and d.fields.update(dict(prop_updates))
|
|
|
|
def apply_previous_field_properties(self, doclist, property_dict,
|
|
doc_type_list):
|
|
"""
|
|
|
|
"""
|
|
prev_field_dict = self.get_previous_field_properties(property_dict)
|
|
if not prev_field_dict: return
|
|
|
|
for doc_type in doc_type_list:
|
|
docfields = self.get_sorted_docfields(doclist, doc_type)
|
|
docfields = self.sort_docfields(doc_type, docfields, prev_field_dict)
|
|
if docfields: self.change_idx(doclist, docfields, doc_type)
|
|
|
|
def get_previous_field_properties(self, property_dict):
|
|
"""
|
|
setup prev_field_dict
|
|
"""
|
|
from webnotes.utils import cstr
|
|
doctype_prev_field_list = []
|
|
for doc_type in property_dict:
|
|
prev_field_list = []
|
|
for prop_list in property_dict.get(doc_type).values():
|
|
for prop in prop_list:
|
|
if prop.get('property') == 'previous_field':
|
|
prev_field_list.append([prop.get('value'),
|
|
prop.get('field_name')])
|
|
break
|
|
if not prev_field_list: continue
|
|
doctype_prev_field_list.append([doc_type, dict(prev_field_list)])
|
|
if not doctype_prev_field_list: return
|
|
return dict(doctype_prev_field_list)
|
|
|
|
def get_sorted_docfields(self, doclist, doc_type):
|
|
"""
|
|
get a sorted list of docfield names
|
|
"""
|
|
sorted_list = sorted([
|
|
d for d in doclist
|
|
if d.doctype == 'DocField'
|
|
and d.parent == doc_type
|
|
], key=lambda df: df.idx)
|
|
return [d.fieldname for d in sorted_list]
|
|
|
|
def sort_docfields(self, doc_type, docfields, prev_field_dict):
|
|
"""
|
|
|
|
"""
|
|
temp_dict = prev_field_dict.get(doc_type)
|
|
if not temp_dict: return
|
|
|
|
prev_field = 'None' in temp_dict and 'None' or docfields[0]
|
|
i = 0
|
|
while temp_dict:
|
|
get_next_docfield = True
|
|
cur_field = temp_dict.get(prev_field)
|
|
if cur_field and cur_field in docfields:
|
|
try:
|
|
del temp_dict[prev_field]
|
|
if prev_field in docfields:
|
|
docfields.remove(cur_field)
|
|
docfields.insert(docfields.index(prev_field) + 1,
|
|
cur_field)
|
|
elif prev_field == 'None':
|
|
docfields.remove(cur_field)
|
|
docfields.insert(0, cur_field)
|
|
except ValueError:
|
|
pass
|
|
|
|
if cur_field in temp_dict:
|
|
prev_field = cur_field
|
|
get_next_docfield = False
|
|
|
|
if get_next_docfield:
|
|
i += 1
|
|
if i>=len(docfields): break
|
|
prev_field = docfields[i]
|
|
keys, vals = temp_dict.keys(), temp_dict.values()
|
|
if prev_field in vals:
|
|
i -= 1
|
|
prev_field = keys[vals.index(prev_field)]
|
|
|
|
return docfields
|
|
|
|
def change_idx(self, doclist, docfields, doc_type):
|
|
for d in doclist:
|
|
if d.fieldname and d.fieldname in docfields and d.parent == doc_type:
|
|
d.idx = docfields.index(d.fieldname) + 1
|
|
|
|
def add_code(self, doc):
|
|
"""add js, css code"""
|
|
import os
|
|
from webnotes.modules import scrub, get_module_path
|
|
import conf
|
|
|
|
module_path = get_module_path(doc.module)
|
|
|
|
path = os.path.join(module_path, 'doctype', scrub(doc.name))
|
|
|
|
def _add_code(fname, fieldname):
|
|
fpath = os.path.join(path, fname)
|
|
if os.path.exists(fpath):
|
|
with open(fpath, 'r') as f:
|
|
doc.fields[fieldname] = f.read()
|
|
|
|
_add_code(scrub(doc.name) + '.js', '__js')
|
|
_add_code(scrub(doc.name) + '.css', '__css')
|
|
_add_code('%s_list.js' % scrub(doc.name), '__listjs')
|
|
_add_code('help.md', 'description')
|
|
|
|
# custom script
|
|
from webnotes.model.code import get_custom_script
|
|
custom = get_custom_script(doc.name, 'Client') or ''
|
|
doc.fields['__js'] = doc.fields.setdefault('__js', '') + '\n' + custom
|
|
|
|
# embed all require files
|
|
import re
|
|
def _sub(match):
|
|
fpath = os.path.join(os.path.dirname(conf.__file__),
|
|
re.search('["\'][^"\']*["\']', match.group(0)).group(0)[1:-1])
|
|
if os.path.exists(fpath):
|
|
with open(fpath, 'r') as f:
|
|
return '\n' + f.read() + '\n'
|
|
else:
|
|
return '\n// no file "%s" found \n' % fpath
|
|
|
|
if doc.fields.get('__js'):
|
|
doc.fields['__js'] = re.sub('(wn.require\([^\)]*.)', _sub, doc.fields['__js'])
|
|
|
|
|
|
def load_select_options(self, doclist):
|
|
"""
|
|
Loads Select options for 'Select' fields
|
|
with link: as start of options
|
|
"""
|
|
for d in doclist:
|
|
if (d.doctype == 'DocField' and d.fieldtype == 'Select' and
|
|
d.options and d.options[:5].lower() == 'link:'):
|
|
|
|
# Get various options
|
|
opt_list = self._get_select_options(d)
|
|
|
|
opt_list = [''] + [o[0] or '' for o in opt_list]
|
|
d.options = "\n".join(opt_list)
|
|
|
|
def _get_select_options(self, d):
|
|
"""
|
|
Queries and returns select options
|
|
(called by load_select_options)
|
|
"""
|
|
op = d.options.split('\n')
|
|
if len(op) > 1 and op[1][:4].lower() == 'sql:':
|
|
# Execute the sql query
|
|
query = op[1][4:].replace('__user',
|
|
webnotes.session.get('user'))
|
|
else:
|
|
# Extract DocType and Conditions
|
|
# and execute the resulting query
|
|
dt = op[0][5:].strip()
|
|
cond_list = [cond.replace('__user',
|
|
webnotes.session.get('user')) for cond in op[1:]]
|
|
query = """\
|
|
SELECT name FROM `tab%s`
|
|
WHERE %s docstatus!=2
|
|
ORDER BY name ASC""" % (dt,
|
|
cond_list and (" AND ".join(cond_list) + " AND ") or "")
|
|
try:
|
|
opt_list = webnotes.conn.sql(query)
|
|
except:
|
|
# WARNING: Exception suppressed
|
|
opt_list = []
|
|
|
|
return opt_list
|
|
|
|
def load_print_formats(self, doclist):
|
|
"""
|
|
Load Print Formats in doclist
|
|
"""
|
|
# TODO: Process Print Formats for $import
|
|
# to deprecate code in print_format.py
|
|
# if this is implemented, clear CacheItem on saving print format
|
|
print_formats = webnotes.conn.sql("""\
|
|
SELECT * FROM `tabPrint Format`
|
|
WHERE doc_type=%s AND docstatus<2""", doclist[0].fields.get('name'),
|
|
as_dict=1)
|
|
for pf in print_formats:
|
|
if not pf: continue
|
|
print_format_doc = webnotes.model.doc.Document('Print Format', fielddata=pf)
|
|
doclist.append(print_format_doc)
|
|
|
|
def load_from_cache(self):
|
|
import json
|
|
json_doclist = CacheItem(self.name).get()
|
|
if json_doclist:
|
|
return [webnotes.model.doc.Document(fielddata=d)
|
|
for d in json.loads(json_doclist)]
|
|
|
|
def insert_into_cache(self, doclist):
|
|
import json
|
|
json_doclist = json.dumps([d.fields for d in doclist])
|
|
CacheItem(self.name).set(json_doclist)
|
|
|
|
def get(dt, form=1):
|
|
"""
|
|
Load "DocType" - called by form builder, report buider and from code.py (when there is no cache)
|
|
"""
|
|
if not dt: return []
|
|
|
|
doclist = _DocType(dt).make_doclist(form)
|
|
return doclist
|
|
|
|
# Deprecate after import_docs rewrite
|
|
def get_field_property(dt, fieldname, property):
|
|
"""
|
|
get a field property, override it from property setter if specified
|
|
"""
|
|
field = webnotes.conn.sql("""
|
|
select name, `%s`
|
|
from tabDocField
|
|
where parent=%s and fieldname=%s""" % (property, '%s', '%s'), (dt, fieldname))
|
|
|
|
prop = webnotes.conn.sql("""
|
|
select value
|
|
from `tabProperty Setter`
|
|
where doc_type=%s and field_name=%s and property=%s""", (dt, fieldname, property))
|
|
if prop:
|
|
return prop[0][0]
|
|
else:
|
|
return field[0][1]
|
|
|
|
def get_property(dt, property, fieldname=None):
|
|
"""
|
|
get a doctype property, override it from property setter if specified
|
|
"""
|
|
if fieldname:
|
|
prop = webnotes.conn.sql("""
|
|
select value
|
|
from `tabProperty Setter`
|
|
where doc_type=%s and field_name=%s
|
|
and property=%s""", (dt, fieldname, property))
|
|
if prop:
|
|
return prop[0][0]
|
|
else:
|
|
val = webnotes.conn.sql("""\
|
|
SELECT %s FROM `tabDocField`
|
|
WHERE parent = %s AND fieldname = %s""" % \
|
|
(property, '%s', '%s'), (dt, fieldname))
|
|
if val and val[0][0]: return val[0][0] or ''
|
|
else:
|
|
prop = webnotes.conn.sql("""
|
|
select value
|
|
from `tabProperty Setter`
|
|
where doc_type=%s and doctype_or_field='DocType'
|
|
and property=%s""", (dt, property))
|
|
if prop:
|
|
return prop[0][0]
|
|
else:
|
|
return webnotes.conn.get_value('DocType', dt, property)
|
|
|
|
# Test Cases
|
|
import unittest
|
|
|
|
class DocTypeTest(unittest.TestCase):
|
|
def setUp(self):
|
|
self.name = 'Sales Order'
|
|
self.dt = _DocType(self.name)
|
|
|
|
def tearDown(self):
|
|
webnotes.conn.rollback()
|
|
|
|
def test_make_doclist(self):
|
|
doclist = self.dt.make_doclist()
|
|
for d in doclist:
|
|
print d.idx, d.doctype, d.name, d.parent
|
|
if not d.doctype: print d.fields
|
|
#print "--", d.name, "--"
|
|
#print d.doctype
|
|
self.assertTrue(doclist)
|
|
|
|
def test_get_custom_fields(self):
|
|
return
|
|
doclist = self.dt.get_custom_fields(self.name)
|
|
for d in doclist:
|
|
print "--", d.name, "--"
|
|
print d.fields
|
|
self.assertTrue(doclist)
|