[MERGE] merge with trunk-server
bzr revid: ysa@tinyerp.co.in-20100408093736-996mqw3c8klknerc
This commit is contained in:
+78
-32
@@ -306,7 +306,10 @@ def load_information_from_description_file(module):
|
||||
if os.path.isfile(description_file):
|
||||
return eval(tools.file_open(description_file).read())
|
||||
|
||||
logging.warning('The module %s does not contain a description file: __openerp__.py or __terp__.py (deprecated)' % module)
|
||||
#TODO: refactor the logger in this file to follow the logging guidelines
|
||||
# for 6.0
|
||||
logging.getLogger('addons').debug('The module %s does not contain a description file:'\
|
||||
'__openerp__.py or __terp__.py (deprecated)', module)
|
||||
return {}
|
||||
|
||||
def get_modules_with_version():
|
||||
@@ -589,8 +592,75 @@ class MigrationManager(object):
|
||||
if mod:
|
||||
del mod
|
||||
|
||||
log = logging.getLogger('init')
|
||||
|
||||
def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
||||
|
||||
def process_sql_file(cr, file):
|
||||
queries = fp.read().split(';')
|
||||
for query in queries:
|
||||
new_query = ' '.join(query.split())
|
||||
if new_query:
|
||||
cr.execute(new_query)
|
||||
|
||||
def load_init_update_xml(cr, m, idref, mode, kind):
|
||||
for filename in package.data.get('%s_xml' % kind, []):
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, filename))
|
||||
_, ext = os.path.splitext(filename)
|
||||
fp = tools.file_open(opj(m, filename))
|
||||
if ext == '.csv':
|
||||
noupdate = (kind == 'init')
|
||||
tools.convert_csv_import(cr, m, os.path.basename(filename), fp.read(), idref, mode=mode, noupdate=noupdate)
|
||||
elif ext == '.sql':
|
||||
process_sql_file(cr, fp)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, m, fp, idref, mode=mode, **kwargs)
|
||||
else:
|
||||
tools.convert_xml_import(cr, m, fp, idref, mode=mode, **kwargs)
|
||||
fp.close()
|
||||
|
||||
def load_demo_xml(cr, m, idref, mode):
|
||||
for xml in package.data.get('demo_xml', []):
|
||||
name, ext = os.path.splitext(xml)
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, xml))
|
||||
fp = tools.file_open(opj(m, xml))
|
||||
if ext == '.csv':
|
||||
tools.convert_csv_import(cr, m, os.path.basename(xml), fp.read(), idref, mode=mode, noupdate=True)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs)
|
||||
else:
|
||||
tools.convert_xml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs)
|
||||
fp.close()
|
||||
|
||||
def load_data(cr, module_name, id_map, mode):
|
||||
_load_data(cr, module_name, id_map, mode, 'data')
|
||||
|
||||
def load_demo(cr, module_name, id_map, mode):
|
||||
_load_data(cr, module_name, id_map, mode, 'demo')
|
||||
|
||||
def load_test(cr, module_name, id_map, mode):
|
||||
cr.commit()
|
||||
try:
|
||||
_load_data(cr, module_name, id_map, mode, 'test')
|
||||
finally:
|
||||
cr.rollback()
|
||||
|
||||
def _load_data(cr, module_name, id_map, mode, kind):
|
||||
noupdate = (kind == 'demo')
|
||||
for filename in package.data.get(kind, []):
|
||||
_, ext = os.path.splitext(filename)
|
||||
log.info("module %s: loading %s", module_name, filename)
|
||||
pathname = os.path.join(module_name, filename)
|
||||
file = tools.file_open(pathname)
|
||||
# TODO manage .csv file with noupdate == (kind == 'init')
|
||||
if ext == '.sql':
|
||||
process_sql_file(cr, fp)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, module_name, file, id_map, mode, noupdate)
|
||||
else:
|
||||
tools.convert_xml_import(cr, module_name, file, id_map, mode, noupdate)
|
||||
file.close()
|
||||
|
||||
# **kwargs is passed directly to convert_xml_import
|
||||
if not status:
|
||||
status = {}
|
||||
@@ -647,40 +717,16 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
||||
'license': package.data.get('license', 'GPL-2'),
|
||||
'certificate': package.data.get('certificate') or None,
|
||||
})
|
||||
for filename in package.data.get('%s_xml' % kind, []):
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, filename))
|
||||
name, ext = os.path.splitext(filename)
|
||||
fp = tools.file_open(opj(m, filename))
|
||||
if ext == '.csv':
|
||||
noupdate=False
|
||||
if kind == 'init':
|
||||
noupdate=True
|
||||
tools.convert_csv_import(cr, m, os.path.basename(filename), fp.read(), idref, mode=mode, noupdate=noupdate)
|
||||
elif ext == '.sql':
|
||||
queries = fp.read().split(';')
|
||||
for query in queries:
|
||||
new_query = ' '.join(query.split())
|
||||
if new_query:
|
||||
cr.execute(new_query)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, m, fp, idref, mode=mode, **kwargs)
|
||||
else:
|
||||
tools.convert_xml_import(cr, m, fp, idref, mode=mode, **kwargs)
|
||||
fp.close()
|
||||
load_init_update_xml(cr, m, idref, mode, kind)
|
||||
load_data(cr, m, idref, mode)
|
||||
if hasattr(package, 'demo') or (package.dbdemo and package.state != 'installed'):
|
||||
status['progress'] = (float(statusi)+0.75) / len(graph)
|
||||
for xml in package.data.get('demo_xml', []):
|
||||
name, ext = os.path.splitext(xml)
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, xml))
|
||||
fp = tools.file_open(opj(m, xml))
|
||||
if ext == '.csv':
|
||||
tools.convert_csv_import(cr, m, os.path.basename(xml), fp.read(), idref, mode=mode, noupdate=True)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, m, fp, idref, mode=mode, **kwargs)
|
||||
else:
|
||||
tools.convert_xml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs)
|
||||
fp.close()
|
||||
load_demo_xml(cr, m, idref, mode)
|
||||
load_demo(cr, m, idref, mode)
|
||||
cr.execute('update ir_module_module set demo=%s where id=%s', (True, mid))
|
||||
|
||||
load_test(cr, m, idref, mode)
|
||||
|
||||
package_todo.append(package.name)
|
||||
|
||||
migrations.migrate_module(package, 'post')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
@@ -15,7 +15,7 @@
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
@@ -34,7 +34,8 @@
|
||||
'security/base_security.xml',
|
||||
'res/res_security.xml',
|
||||
'res/res_config.xml',
|
||||
'maintenance/maintenance_security.xml'
|
||||
'maintenance/maintenance_security.xml',
|
||||
'data/res.country.state.csv'
|
||||
],
|
||||
'update_xml': [
|
||||
'base_update.xml',
|
||||
@@ -61,8 +62,9 @@
|
||||
'security/base_security.xml',
|
||||
'maintenance/maintenance_view.xml',
|
||||
'security/ir.model.access.csv'
|
||||
|
||||
],
|
||||
'demo_xml': ['base_demo.xml', 'res/partner/partner_demo.xml', 'res/partner/crm_demo.xml'],
|
||||
'demo_xml': ['base_demo.xml', 'res/partner/partner_demo.xml', 'res/partner/crm_demo.xml','test/base_test.xml'],
|
||||
'installable': True,
|
||||
'active': True,
|
||||
'certificate': '0076807797149',
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
"country_id:id","name","code"
|
||||
us,"Alabama","AL"
|
||||
us,"Alaska","AK"
|
||||
us,"Arizona","AZ"
|
||||
us,"Arkansas","AR"
|
||||
us,"California","CA"
|
||||
us,"Colorado","CO"
|
||||
us,"Connecticut","CT"
|
||||
us,"Delaware","DE"
|
||||
us,"District of Columbia","DC"
|
||||
us,"Florida","FL"
|
||||
us,"Georgia","GA"
|
||||
us,"Hawaii","HI"
|
||||
us,"Idaho","ID"
|
||||
us,"Illinois","IL"
|
||||
us,"Indiana","IN"
|
||||
us,"Iowa","IA"
|
||||
us,"Kansas","KS"
|
||||
us,"Kentucky","KY"
|
||||
us,"Louisiana","LA"
|
||||
us,"Maine","ME"
|
||||
us,"Montana","MT"
|
||||
us,"Nebraska","NE"
|
||||
us,"Nevada","NV"
|
||||
us,"New Hampshire","NH"
|
||||
us,"New Jersey","NJ"
|
||||
us,"New Mexico","NM"
|
||||
us,"New York","NY"
|
||||
us,"North Carolina","NC"
|
||||
us,"North Dakota","ND"
|
||||
us,"Ohio","OH"
|
||||
us,"Oklahoma","OK"
|
||||
us,"Oregon","OR"
|
||||
us,"Maryland","MD"
|
||||
us,"Massachusetts","MA"
|
||||
us,"Michigan","MI"
|
||||
us,"Minnesota","MN"
|
||||
us,"Mississippi","MS"
|
||||
us,"Missouri","MO"
|
||||
us,"Pennsylvania","PA"
|
||||
us,"Rhode Island","RI"
|
||||
us,"South Carolina","SC"
|
||||
us,"South Dakota","SD"
|
||||
us,"Tennessee","TN"
|
||||
us,"Texas","TX"
|
||||
us,"Utah","UT"
|
||||
us,"Vermont","VT"
|
||||
us,"Virginia","VA"
|
||||
us,"Washington","WA"
|
||||
us,"West Virginia","WV"
|
||||
us,"Wisconsin","WI"
|
||||
us,"Wyoming","WY"
|
||||
|
@@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-04-06 04:00+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-04-08 07:18+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-04-05 04:33+0000\n"
|
||||
"PO-Revision-Date: 2010-04-07 04:18+0000\n"
|
||||
"Last-Translator: goranc <goranc@gmail.com>\n"
|
||||
"Language-Team: openerp-translators\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-04-06 04:00+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-04-08 07:18+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Language: hr\n"
|
||||
|
||||
|
||||
@@ -8,13 +8,13 @@ msgstr ""
|
||||
"Project-Id-Version: openobject-server\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-04-05 04:34+0000\n"
|
||||
"PO-Revision-Date: 2010-04-07 04:14+0000\n"
|
||||
"Last-Translator: Harry (Open ERP) <hmo@tinyerp.com>\n"
|
||||
"Language-Team: Japanese <ja@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-04-06 04:00+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-04-08 07:18+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.0\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-04-05 04:37+0000\n"
|
||||
"PO-Revision-Date: 2010-04-07 04:19+0000\n"
|
||||
"Last-Translator: Anders Wallenquist <anders.wallenquist@vertel.se>\n"
|
||||
"Language-Team: <>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-04-06 04:01+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-04-08 07:18+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.0\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-04-05 04:31+0000\n"
|
||||
"PO-Revision-Date: 2010-04-07 04:12+0000\n"
|
||||
"Last-Translator: Fabien (Open ERP) <fp@tinyerp.com>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-04-06 04:01+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-04-08 07:18+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2009-12-18 08:39+0000\n"
|
||||
"PO-Revision-Date: 2010-04-05 04:30+0000\n"
|
||||
"PO-Revision-Date: 2010-04-07 04:21+0000\n"
|
||||
"Last-Translator: OpenERP Administrators <Unknown>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2010-04-06 04:01+0000\n"
|
||||
"X-Launchpad-Export-Date: 2010-04-08 07:18+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -548,10 +548,10 @@ class ir_model_data(osv.osv):
|
||||
table.replace('.', '_'))] = (table, inherit_id)
|
||||
return res_id
|
||||
|
||||
def _unlink(self, cr, uid, model, ids, direct=False):
|
||||
for id in ids:
|
||||
self.unlink_mark[(model, id)]=False
|
||||
cr.execute('delete from ir_model_data where res_id=%s and model=%s', (id, model))
|
||||
def _unlink(self, cr, uid, model, res_ids):
|
||||
for res_id in res_ids:
|
||||
self.unlink_mark[(model, res_id)] = False
|
||||
cr.execute('delete from ir_model_data where res_id=%s and model=%s', (res_id, model))
|
||||
return True
|
||||
|
||||
def ir_set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=None, xml_id=False):
|
||||
|
||||
@@ -73,18 +73,15 @@ class ir_sequence(osv.osv):
|
||||
}
|
||||
|
||||
def get_id(self, cr, uid, sequence_id, test='id', context=None):
|
||||
try:
|
||||
assert test in ('code','id')
|
||||
cr.execute('SELECT id, number_next, prefix, suffix, padding FROM ir_sequence WHERE '+test+'=%s AND active=%s FOR UPDATE', (sequence_id, True))
|
||||
res = cr.dictfetchone()
|
||||
if res:
|
||||
cr.execute('UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s AND active=%s', (res['id'], True))
|
||||
if res['number_next']:
|
||||
return self._process(res['prefix']) + '%%0%sd' % res['padding'] % res['number_next'] + self._process(res['suffix'])
|
||||
else:
|
||||
return self._process(res['prefix']) + self._process(res['suffix'])
|
||||
finally:
|
||||
cr.commit()
|
||||
assert test in ('code','id')
|
||||
cr.execute('SELECT id, number_next, prefix, suffix, padding FROM ir_sequence WHERE '+test+'=%s AND active=%s FOR UPDATE NOWAIT', (sequence_id, True))
|
||||
res = cr.dictfetchone()
|
||||
if res:
|
||||
cr.execute('UPDATE ir_sequence SET number_next=number_next+number_increment WHERE id=%s AND active=%s', (res['id'], True))
|
||||
if res['number_next']:
|
||||
return self._process(res['prefix']) + '%%0%sd' % res['padding'] % res['number_next'] + self._process(res['suffix'])
|
||||
else:
|
||||
return self._process(res['prefix']) + self._process(res['suffix'])
|
||||
return False
|
||||
|
||||
def get(self, cr, uid, code):
|
||||
|
||||
@@ -50,10 +50,12 @@
|
||||
<filter icon="terp-sale" string="Uninstalled" domain="[('state', 'in', ['uninstalled', 'uninstallable'])]"/>
|
||||
<filter icon="terp-sale" string="To be upgraded" domain="[('state','in', ['to upgrade', 'to remove', 'to install'])]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="name" select="1"/>
|
||||
<field name="state" readonly="1" select="1"/>
|
||||
<filter icon="terp-sale" string="Certified" domain="[('certificate','<>', False)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="name" select="1"/>
|
||||
<field name="state" readonly="1" select="1"/>
|
||||
|
||||
</group>
|
||||
</group>
|
||||
|
||||
</search>
|
||||
</field>
|
||||
|
||||
@@ -50,9 +50,9 @@
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="State">
|
||||
<field name="country_id"/>
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="country_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -136,7 +136,7 @@ class res_partner(osv.osv):
|
||||
'child_ids': fields.one2many('res.partner', 'parent_id', 'Partner Ref.'),
|
||||
'ref': fields.char('Code', size=64),
|
||||
'lang': fields.selection(_lang_get, 'Language', size=5, help="If the selected language is loaded in the system, all documents related to this partner will be printed in this language. If not, it will be english."),
|
||||
'user_id': fields.many2one('res.users', 'Dedicated Salesman', help='The internal user that is in charge of communicating with this partner if any.'),
|
||||
'user_id': fields.many2one('res.users', 'Salesman', help='The internal user that is in charge of communicating with this partner if any.'),
|
||||
'vat': fields.char('VAT',size=32 ,help="Value Added Tax number. Check the box if the partner is subjected to the VAT. Used by the VAT legal statement."),
|
||||
'bank_ids': fields.one2many('res.partner.bank', 'partner_id', 'Banks'),
|
||||
'website': fields.char('Website',size=64),
|
||||
|
||||
@@ -60,6 +60,15 @@
|
||||
<field name="name">country_id</field>
|
||||
<field eval="True" name="readonly"/>
|
||||
<field name="bank_type_id" ref="bank_normal"/>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
Resource: res.request.link
|
||||
-->
|
||||
|
||||
<record id="req_link_partner" model="res.request.link">
|
||||
<field name="name">Partner</field>
|
||||
<field name="object">res.partner</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<!--
|
||||
Resource: res.partner.category
|
||||
-->
|
||||
|
||||
|
||||
<record id="res_partner_category_0" model="res.partner.category">
|
||||
<field name="name">Customer</field>
|
||||
</record>
|
||||
@@ -43,7 +43,7 @@
|
||||
<field name="name">Bad customers</field>
|
||||
<field name="parent_id" ref="res_partner_category_12"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="res_partner_category_8" model="res.partner.category">
|
||||
<field name="name">Supplier</field>
|
||||
</record>
|
||||
@@ -59,12 +59,12 @@
|
||||
<field name="name">Textile Suppliers</field>
|
||||
<field name="parent_id" ref="res_partner_category_8"/>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
|
||||
<!--
|
||||
Resource: res.partner
|
||||
-->
|
||||
|
||||
|
||||
<record id="res_partner_asus" model="res.partner">
|
||||
<field name="name">ASUStek</field>
|
||||
<field name="user_id" ref="user_demo"/>
|
||||
@@ -177,12 +177,12 @@
|
||||
<record id="res_partner_accent" model="res.partner">
|
||||
<field name="name">Université de Liège</field>
|
||||
<field eval="[(6, 0, [ref('res_partner_category_9')])]" name="category_id"/>
|
||||
</record>
|
||||
|
||||
</record>
|
||||
|
||||
<!--
|
||||
Resource: res.partner.address
|
||||
-->
|
||||
|
||||
|
||||
<record id="res_partner_address_1" model="res.partner.address">
|
||||
<field name="city">Bruxelles</field>
|
||||
<field name="name">Michel Schumacher</field>
|
||||
@@ -349,6 +349,15 @@
|
||||
<field name="street">Université de Liège</field>
|
||||
<field name="partner_id" ref="res_partner_accent"/>
|
||||
</record>
|
||||
|
||||
|
||||
<!--
|
||||
Resource: res.request
|
||||
-->
|
||||
|
||||
<record id="res_partner_asustek_request" model="res.request">
|
||||
<field name="name">ASUSTEK-Subject</field>
|
||||
<field name="act_to" ref="user_demo"/>
|
||||
<field name="ref_doc1">res.partner,2</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -329,7 +329,6 @@
|
||||
<field colspan="4" name="events" nolabel="1" widget="one2many_list"/>
|
||||
</page>
|
||||
<page string="Notes">
|
||||
<separator string="Description" colspan="4"/>
|
||||
<field colspan="4" name="comment" nolabel="1"/>
|
||||
</page>
|
||||
</notebook>
|
||||
@@ -344,7 +343,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Partner">
|
||||
<group col='10' colspan='4'>
|
||||
<filter string="My Partners" icon="terp-partner" domain="[('parent_id','!=',False)]"/>
|
||||
<filter string="My Partners" icon="terp-partner" domain="[('user_id','=',uid)]"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter string="Customers" icon="terp-partner" domain="[('customer','=',1)]" help="Customer Partners"/>
|
||||
<filter string="Suppliers" icon="terp-partner" domain="[('supplier','=',1)]" help="Supplier Partners"
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!--
|
||||
Assert:
|
||||
-->
|
||||
|
||||
<assert model="res.request" id="res_partner_asustek_request" string="Reference field request returns a browsable object">
|
||||
<test expr="ref_doc1.name">ASUStek</test>
|
||||
</assert>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
+4
-2
@@ -148,8 +148,10 @@
|
||||
<rng:define name="delete">
|
||||
<rng:element name="delete">
|
||||
<rng:attribute name="model" />
|
||||
<rng:optional><rng:attribute name="id" /> </rng:optional>
|
||||
<rng:optional><rng:attribute name="search" /></rng:optional>
|
||||
<rng:choice>
|
||||
<rng:attribute name="id" />
|
||||
<rng:attribute name="search" />
|
||||
</rng:choice>
|
||||
</rng:element>
|
||||
</rng:define>
|
||||
|
||||
|
||||
+4
-1
@@ -205,7 +205,10 @@ def init_logger():
|
||||
|
||||
for level, (fg, bg) in mapping.items():
|
||||
msg = "\x1b[%dm\x1b[%dm%s\x1b[0m" % (foreground(fg), background(bg), level)
|
||||
logging.addLevelName(getattr(logging, level), msg)
|
||||
# jth: we should not override default level name
|
||||
# jth: use this instead http://stackoverflow.com/questions/384076/how-can-i-make-the-python-logging-output-to-be-colored
|
||||
# jth: also, do not output ANSI if terminal doesn't support it (test it in Eclipse console and on windows)
|
||||
#logging.addLevelName(getattr(logging, level), msg)
|
||||
|
||||
|
||||
class Logger(object):
|
||||
|
||||
+283
-15
@@ -501,6 +501,16 @@ class orm_template(object):
|
||||
self._table = self._name.replace('.', '_')
|
||||
|
||||
def browse(self, cr, uid, select, context=None, list_class=None, fields_process={}):
|
||||
"""
|
||||
Fetch records as objects allowing to use dot notation to browse fields and relations
|
||||
|
||||
:param cr: database cursor
|
||||
:param user: current user id
|
||||
:param select: id or a list of ids
|
||||
:param context: context arguments, like lang, time zone
|
||||
:rtype: object or a list of objects requested
|
||||
|
||||
"""
|
||||
if not context:
|
||||
context = {}
|
||||
self._list_class = list_class or browse_record_list
|
||||
@@ -616,6 +626,19 @@ class orm_template(object):
|
||||
return [data] + lines
|
||||
|
||||
def export_data(self, cr, uid, ids, fields_to_export, context=None):
|
||||
"""
|
||||
Export fields for selected objects
|
||||
|
||||
:param cr: database cursor
|
||||
:param uid: current user id
|
||||
:param ids: a list of ids
|
||||
:param fields_to_export: a list of fields
|
||||
:param context: context arguments, like lang, time zone, may contain import_comp(default: False) to make exported data compatible with import_data()
|
||||
:rtype: a dictionary with a *datas* matrix
|
||||
|
||||
This method is used when exporting data via client menu
|
||||
|
||||
"""
|
||||
if not context:
|
||||
context = {}
|
||||
imp_comp = context.get('import_comp',False)
|
||||
@@ -645,6 +668,24 @@ class orm_template(object):
|
||||
return {'datas':datas}
|
||||
|
||||
def import_data(self, cr, uid, fields, datas, mode='init', current_module='', noupdate=False, context=None, filename=None):
|
||||
"""
|
||||
Import given data in given module
|
||||
|
||||
:param cr: database cursor
|
||||
:param uid: current user id
|
||||
:param ids: a list of ids
|
||||
:param fields: a list of fields
|
||||
:param data: data to import
|
||||
:param mode: 'init' or 'update' for record creation
|
||||
:param current_module: module name
|
||||
:param noupdate: flag for record creation
|
||||
:param context: context arguments, like lang, time zone,
|
||||
:param filename: optional file to store partial import state for recovery
|
||||
:rtype: a tuple
|
||||
|
||||
This method is used when importing data via client menu
|
||||
|
||||
"""
|
||||
if not context:
|
||||
context = {}
|
||||
fields = map(lambda x: x.split('/'), fields)
|
||||
@@ -984,16 +1025,13 @@ class orm_template(object):
|
||||
self._invalids.clear()
|
||||
|
||||
def default_get(self, cr, uid, fields_list, context=None):
|
||||
""" Set default values for the object's fields.
|
||||
"""
|
||||
Set default values for the object's fields.
|
||||
|
||||
Returns a dict of {field_name:default_value}
|
||||
:param fields_list: fields for which the object doesn't have any value yet, and default values need to be provided.
|
||||
If fields outside this list are returned, the user-provided values will be overwritten.
|
||||
:rtype: a dict of {field_name:default_value}
|
||||
|
||||
Arguments:
|
||||
`fields_list`: the fields for which the object doesn't have
|
||||
any value yet, and default values need to be
|
||||
provided. If fields outside this list are
|
||||
returned, the user-provided values will be
|
||||
overwritten.
|
||||
"""
|
||||
return {}
|
||||
|
||||
@@ -1316,6 +1354,23 @@ class orm_template(object):
|
||||
# if view_id, view_type is not required
|
||||
#
|
||||
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
|
||||
"""
|
||||
Get the detailed composition of the requested view like fields, model, view architecture
|
||||
|
||||
:param cr: database cursor
|
||||
:param user: current user id
|
||||
:param view_id: id of the view or None
|
||||
:param view_type: type of the view to return if view_id is None ('form', tree', ...)
|
||||
:param context: context arguments, like lang, time zone
|
||||
:param toolbar: true to include concextual actions
|
||||
:param submenu: example (portal_project module)
|
||||
:return: a dictionary describing the composition of the requested view (including inherited views and extensions)
|
||||
:raise AttributeError:
|
||||
* if the inherited view has unknown position to work with other than 'before', 'after', 'inside', 'replace'
|
||||
* if some tag other than 'position' is found in parent view
|
||||
:raise Invalid ArchitectureError: if there is view type other than form, tree, calendar, search etc defined on the structure
|
||||
|
||||
"""
|
||||
if not context:
|
||||
context = {}
|
||||
|
||||
@@ -1878,6 +1933,28 @@ class orm(orm_template):
|
||||
_protected = ['read','write','create','default_get','perm_read','unlink','fields_get','fields_view_get','search','name_get','distinct_field_get','name_search','copy','import_data','search_count', 'exists']
|
||||
|
||||
def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None):
|
||||
"""
|
||||
Get the list of records in list view sorted by the given fields
|
||||
|
||||
:param cr: database cursor
|
||||
:param uid: current user id
|
||||
:param domain: a list specifying search criteria [['field_name', 'operator', 'value'], ...]
|
||||
:param fields: a list of fields present in the list view specified on the object
|
||||
:param groupby: a list of fields on which to sort the records
|
||||
:type fields_list: a list (example ['field_name_1', ...])
|
||||
:param offset: optional number of records to skip
|
||||
:param limit: optional max number of records to return
|
||||
:param context: context arguments, like lang, time zone
|
||||
:return: a list of dictionaries(one dictionary for each record) containing:
|
||||
|
||||
* the values of fields sorted out by the fields in groupby arguement
|
||||
* __domain: a list of tuples specifying the search criteria
|
||||
* __context: a dictionary with argument like 'groupby'
|
||||
:rtype: [{'field_name_1': value, ...]
|
||||
:raise AccessError: * if user has no read rights on the requested object
|
||||
* if user tries to bypass access rules for read on the requested object
|
||||
|
||||
"""
|
||||
groupby_list = groupby
|
||||
if isinstance(groupby, list):
|
||||
groupby = groupby[0]
|
||||
@@ -1955,10 +2032,13 @@ class orm(orm_template):
|
||||
return data
|
||||
|
||||
def _inherits_join_calc(self, field, tables, where_clause):
|
||||
""" Adds missing table select and join clause(s) for reaching
|
||||
"""
|
||||
Adds missing table select and join clause(s) for reaching
|
||||
the field coming from an '_inherits' parent table.
|
||||
@param tables: list of table._table names enclosed in double quotes as returned
|
||||
|
||||
:param tables: a list of table._table names enclosed in double quotes as returned
|
||||
by _where_calc()
|
||||
|
||||
"""
|
||||
current_table = self
|
||||
while field in current_table._inherit_fields and not field in current_table._columns:
|
||||
@@ -2413,6 +2493,17 @@ class orm(orm_template):
|
||||
self._columns[f].restart()
|
||||
|
||||
def default_get(self, cr, uid, fields_list, context=None):
|
||||
"""
|
||||
Get default field values of the given field list of the model
|
||||
|
||||
:param cr: database cursor
|
||||
:param uid: current user id
|
||||
:param fields_list: a list of fields to get the default value
|
||||
:type fields_list: a list (example ['field_name_1', ...])
|
||||
:param context: context arguments, like lang, time zone
|
||||
:return: a dictionary of the default values for fields (set on the object class, by the user preferences, or via the context)
|
||||
|
||||
"""
|
||||
if not context:
|
||||
context = {}
|
||||
value = {}
|
||||
@@ -2504,12 +2595,38 @@ class orm(orm_template):
|
||||
self._inherits_reload_src()
|
||||
|
||||
def fields_get(self, cr, user, fields=None, context=None):
|
||||
"""
|
||||
Get the description of list of fields
|
||||
|
||||
:param cr: database cursor
|
||||
:param user: current user id
|
||||
:param fields: a list of fields
|
||||
:param context: context arguments, like lang, time zone
|
||||
:return: a dictionary of field dictionaries, each one describing a field of the business object
|
||||
:raise AccessError: * if user has no create/write rights on the requested object
|
||||
|
||||
"""
|
||||
ira = self.pool.get('ir.model.access')
|
||||
read_access = ira.check(cr, user, self._name, 'write', raise_exception=False, context=context) or \
|
||||
ira.check(cr, user, self._name, 'create', raise_exception=False, context=context)
|
||||
return super(orm, self).fields_get(cr, user, fields, context, read_access)
|
||||
|
||||
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
|
||||
"""
|
||||
Read records with given ids with the given fields
|
||||
|
||||
:param cr: database cursor
|
||||
:param user: current user id
|
||||
:param ids: id or a list of the ids of the records to read
|
||||
:param fields: optional list of field names to return (default: all fields would be returned)
|
||||
:type fields: a list (example ['field_name_1', ...])
|
||||
:param context(optional, highly recommended): context arguments, like lang, time zone
|
||||
:return: a list of dictionaries((a dictionary per record asked)) with requested field values
|
||||
:rtype: [{‘name_of_the_field’: value, ...}, ...]
|
||||
:raise AccessError: * if user has no read rights on the requested object
|
||||
* if user tries to bypass access rules for read on the requested object
|
||||
|
||||
"""
|
||||
if not context:
|
||||
context = {}
|
||||
self.pool.get('ir.model.access').check(cr, user, self._name, 'read', context=context)
|
||||
@@ -2695,6 +2812,24 @@ class orm(orm_template):
|
||||
return res
|
||||
|
||||
def perm_read(self, cr, user, ids, context=None, details=True):
|
||||
"""
|
||||
Read the permission for record of the given ids
|
||||
|
||||
:param cr: database cursor
|
||||
:param user: current user id
|
||||
:param ids: id or list of ids
|
||||
:param context: context arguments, like lang, time zone
|
||||
:param details: if True, *_uid fields are replaced with the name of the user
|
||||
:return: a list of ownership dictionaries for each requested record
|
||||
:rtype: a list of dictionaries with the following keys:
|
||||
|
||||
* id: object id
|
||||
* create_uid: user who created the record
|
||||
* create_date: date when the record was created
|
||||
* write_uid: last user who changed the record
|
||||
* write_date: date of the last change to the record
|
||||
|
||||
"""
|
||||
if not context:
|
||||
context = {}
|
||||
if not ids:
|
||||
@@ -2755,6 +2890,19 @@ class orm(orm_template):
|
||||
% (operation, self._name))
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
Delete records with given ids
|
||||
|
||||
:param cr: database cursor
|
||||
:param uid: current user id
|
||||
:param ids: id or a list of ids
|
||||
:param context(optional, highly recommended): context arguments, like lang, time zone
|
||||
:return: True
|
||||
:raise AccessError: * if user has no unlink rights on the requested object
|
||||
* if user tries to bypass access rules for unlink on the requested object
|
||||
:raise UserError: if the record is default property for other records
|
||||
|
||||
"""
|
||||
if not ids:
|
||||
return True
|
||||
if isinstance(ids, (int, long)):
|
||||
@@ -2787,7 +2935,6 @@ class orm(orm_template):
|
||||
for sub_ids in cr.split_for_in_conditions(ids):
|
||||
cr.execute('delete from ' + self._table + ' ' \
|
||||
'where id in %s', (sub_ids,))
|
||||
|
||||
for order, object, store_ids, fields in result_store:
|
||||
if object != self._name:
|
||||
obj = self.pool.get(object)
|
||||
@@ -2801,6 +2948,30 @@ class orm(orm_template):
|
||||
# TODO: Validate
|
||||
#
|
||||
def write(self, cr, user, ids, vals, context=None):
|
||||
"""
|
||||
Update records with given ids with the given field values
|
||||
|
||||
:param cr: database cursor
|
||||
:param user: current user id
|
||||
:type user: integer (example 1)
|
||||
:param ids: id or a list of ids
|
||||
:param vals: a dictionary of field values to update
|
||||
:type vals: a dictionary (example {'field_name':'value', ...})
|
||||
:param context(optional, highly recommended): context arguments, like lang, time zone
|
||||
:return: True
|
||||
:raise AccessError: * if user has no write rights on the requested object
|
||||
* if user tries to bypass access rules for write on the requested object
|
||||
:raise ValidateError: if user tries to enter invalid value for a field that is not in selection
|
||||
:raise UserError: if recurssion is found
|
||||
|
||||
vals format for relational field type.
|
||||
|
||||
+ many2many field : [(6, 0, list of ids)] (example: [(6, 0, [8, 5, 6, 4])])
|
||||
+ one2many field : [(0, 0, dictionary of values)] (example: [(0, 0, {'field_name':field_value, ...})])
|
||||
+ many2one field : ID of related record
|
||||
+ reference field : model name, id (example: 'product.product, 5')
|
||||
|
||||
"""
|
||||
readonly = None
|
||||
for field in vals.copy():
|
||||
fobj = None
|
||||
@@ -2987,10 +3158,28 @@ class orm(orm_template):
|
||||
# TODO: Should set perm to user.xxx
|
||||
#
|
||||
def create(self, cr, user, vals, context=None):
|
||||
""" create(cr, user, vals, context) -> int
|
||||
cr = database cursor
|
||||
user = user id
|
||||
vals = dictionary of the form {'field_name':field_value, ...}
|
||||
"""
|
||||
Create new record with specified value
|
||||
|
||||
:param cr: database cursor
|
||||
:param user: current user id
|
||||
:type user: integer (example 1)
|
||||
:param vals: a dictionary for new record {'field_name':field_value, ...}
|
||||
:type vals: a dictionary (example {'field_name':field_value, ...})
|
||||
:param context(optional, highly recommended): context arguements, like lang, time zone
|
||||
:type context: a dictionary (example {'lang':'en_us', ...})
|
||||
:return: id of new record created
|
||||
:raise AccessError: * if user has no create rights on the requested object
|
||||
* if user tries to bypass access rules for create on the requested object
|
||||
:raise ValidateError: if user tries to enter invalid value for a field that is not in selection
|
||||
|
||||
vals format for relational field type.
|
||||
|
||||
+ many2many field : [(6, 0, list of ids)] (example: [(6, 0, [8, 5, 6, 4])])
|
||||
+ one2many field : [(0, 0, dictionary of values)] (example: [(0, 0, {'field_name':field_value, ...})])
|
||||
+ many2one field : ID of related record
|
||||
+ reference field : model name, id (example: 'product.product, 5')
|
||||
|
||||
"""
|
||||
if not context:
|
||||
context = {}
|
||||
@@ -3337,6 +3526,27 @@ class orm(orm_template):
|
||||
|
||||
def search(self, cr, user, args, offset=0, limit=None, order=None,
|
||||
context=None, count=False):
|
||||
"""
|
||||
Search for record/s with or without domain
|
||||
|
||||
:param cr: database cursor
|
||||
:param user: current user id
|
||||
:param args: a list of tuples specifying search criteria [('field_name', 'operator', 'value'), ...]
|
||||
:param offset: optional number of records to skip
|
||||
:param limit: optional max number of records to return
|
||||
:param order: optional columns to sort by (default: self._order )
|
||||
:param context(optional, highly recommended): context arguments, like lang, time zone
|
||||
:param count: if True, returns only the number of records matching the criteria, not their ids
|
||||
:return: id or a list of ids of records matching the criteria
|
||||
:rtype: integer or a list of integers
|
||||
:raise AccessError: * if user tries to bypass access rules for read on the requested object.
|
||||
|
||||
Operators:
|
||||
* =, !=, >, >=, <, <=, like, ilike, in, not in, child_of, parent_left, parent_right
|
||||
Prefix operators:
|
||||
* '&' (default), '|', '!'
|
||||
|
||||
"""
|
||||
if not context:
|
||||
context = {}
|
||||
# compute the where, order by, limit and offset clauses
|
||||
@@ -3386,6 +3596,16 @@ class orm(orm_template):
|
||||
return self._columns[field].search(cr, self, args, field, value, offset, limit, uid)
|
||||
|
||||
def name_get(self, cr, user, ids, context=None):
|
||||
"""
|
||||
|
||||
:param cr: database cursor
|
||||
:param user: current user id
|
||||
:type user: integer (example 1)
|
||||
:param ids: a list of ids
|
||||
:param context: context arguments, like lang, time zone
|
||||
:return: tuples with the text representation of requested objects for to-many relationships
|
||||
|
||||
"""
|
||||
if not context:
|
||||
context = {}
|
||||
if not ids:
|
||||
@@ -3396,6 +3616,20 @@ class orm(orm_template):
|
||||
[self._rec_name], context, load='_classic_write')]
|
||||
|
||||
def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
|
||||
"""
|
||||
|
||||
:param cr: database cursor
|
||||
:param user: current user id
|
||||
:param name: object name to search
|
||||
:param args: a list of tuples specifying search criteria [('field_name', 'operator', 'value'), ...]
|
||||
:param operator: operator for search criterion
|
||||
:param context: context arguments, like lang, time zone
|
||||
:param limit: optional max number of records to return
|
||||
:return: a list of object names matching the search criteria, used to provide completition for to-many relationships
|
||||
|
||||
This method is equivalent of search() on name + name_get()
|
||||
|
||||
"""
|
||||
if not args:
|
||||
args = []
|
||||
if not context:
|
||||
@@ -3408,6 +3642,17 @@ class orm(orm_template):
|
||||
return res
|
||||
|
||||
def copy_data(self, cr, uid, id, default=None, context=None):
|
||||
"""
|
||||
Copy given record's data with all its fields values
|
||||
|
||||
:param cr: database cursor
|
||||
:param user: current user id
|
||||
:param ids: id of the record to copy
|
||||
:param default: a dictionary of field values to update before saving the duplicate object
|
||||
:param context: context arguments, like lang, time zone
|
||||
:return: a dictionary containing all the field values
|
||||
"""
|
||||
|
||||
if not context:
|
||||
context = {}
|
||||
if not default:
|
||||
@@ -3479,6 +3724,18 @@ class orm(orm_template):
|
||||
return data, trans_data
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
"""
|
||||
Duplicate record with given id updating it with default values
|
||||
|
||||
:param cr: database cursor
|
||||
:param uid: current user id
|
||||
:param id: id of the record to copy
|
||||
:param default: a dictionary of field values to update before saving the duplicate object
|
||||
:type default: a dictionary (example {'field_name':field_value, ...})
|
||||
:param context: context arguments, like lang, time zone
|
||||
:return: True
|
||||
|
||||
"""
|
||||
trans_obj = self.pool.get('ir.translation')
|
||||
data, trans_data = self.copy_data(cr, uid, id, default, context)
|
||||
new_id = self.create(cr, uid, data, context)
|
||||
@@ -3493,6 +3750,16 @@ class orm(orm_template):
|
||||
return bool(cr.fetchone()[0])
|
||||
|
||||
def check_recursion(self, cr, uid, ids, parent=None):
|
||||
"""
|
||||
Check recursion in records
|
||||
|
||||
:param cr: database cursor
|
||||
:param uid: current user id
|
||||
:param ids: a list of ids of records
|
||||
:param parent: parent field name
|
||||
:return: True or False based on recursion detection
|
||||
"""
|
||||
|
||||
if not parent:
|
||||
parent = self._parent_name
|
||||
ids_parent = ids[:]
|
||||
@@ -3511,3 +3778,4 @@ class orm(orm_template):
|
||||
return True
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
||||
+6
-9
@@ -29,7 +29,7 @@ import pooler
|
||||
import copy
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import logging
|
||||
from psycopg2 import IntegrityError
|
||||
from tools.func import wraps
|
||||
|
||||
@@ -57,8 +57,7 @@ class osv_pool(netsvc.Service):
|
||||
return f(self, dbname, *args, **kwargs)
|
||||
except orm.except_orm, inst:
|
||||
if inst.name == 'AccessError':
|
||||
tb_s = "AccessError\n" + "".join(traceback.format_exception(*sys.exc_info()))
|
||||
self.logger.notifyChannel('web-services', netsvc.LOG_DEBUG, tb_s)
|
||||
self.logger.debug("AccessError", exc_info=True)
|
||||
self.abortResponse(1, inst.name, 'warning', inst.value)
|
||||
except except_osv, inst:
|
||||
self.abortResponse(1, inst.name, inst.exc_type, inst.value)
|
||||
@@ -68,8 +67,7 @@ class osv_pool(netsvc.Service):
|
||||
self.abortResponse(1, 'Constraint Error', 'warning', self._sql_error[key])
|
||||
self.abortResponse(1, 'Integrity Error', 'warning', inst[0])
|
||||
except Exception, e:
|
||||
tb_s = "".join(traceback.format_exception(*sys.exc_info()))
|
||||
self.logger.notifyChannel('web-services', netsvc.LOG_ERROR, tb_s)
|
||||
self.logger.exception("Uncaught exception")
|
||||
raise
|
||||
|
||||
return wrapper
|
||||
@@ -84,7 +82,7 @@ class osv_pool(netsvc.Service):
|
||||
self._store_function = {}
|
||||
self._init = True
|
||||
self._init_parent = {}
|
||||
self.logger = netsvc.Logger()
|
||||
self.logger = logging.getLogger("web-services")
|
||||
netsvc.Service.__init__(self, 'object_proxy', audience='')
|
||||
self.exportMethod(self.obj_list)
|
||||
self.exportMethod(self.exec_workflow)
|
||||
@@ -116,11 +114,10 @@ class osv_pool(netsvc.Service):
|
||||
try:
|
||||
try:
|
||||
if method.startswith('_'):
|
||||
raise except_osv('Method Error', 'Private method %s can not be calleble.' % (method,))
|
||||
raise except_osv('Access Denied', 'Private methods (such as %s) cannot be called remotely.' % (method,))
|
||||
res = pool.execute_cr(cr, uid, obj, method, *args, **kw)
|
||||
if res is None:
|
||||
self.logger.notifyChannel("web-services", netsvc.LOG_WARNING,
|
||||
'Method can not return a None value (crash in XML-RPC)')
|
||||
self.logger.warning('RPC methods cannot return `None` at the moment!')
|
||||
cr.commit()
|
||||
except Exception:
|
||||
cr.rollback()
|
||||
|
||||
@@ -251,12 +251,11 @@ class OerpAuthProxy(AuthProxy):
|
||||
self.auth_tries = 0
|
||||
self.last_auth = None
|
||||
|
||||
def checkRequest(self,handler,path = '/'):
|
||||
if self.auth_creds:
|
||||
return True
|
||||
def checkRequest(self,handler,path, db=False):
|
||||
auth_str = handler.headers.get('Authorization',False)
|
||||
try:
|
||||
db = handler.get_db_from_path(path)
|
||||
if not db:
|
||||
db = handler.get_db_from_path(path)
|
||||
print "Got db:",db
|
||||
except:
|
||||
if path.startswith('/'):
|
||||
@@ -267,8 +266,9 @@ class OerpAuthProxy(AuthProxy):
|
||||
else:
|
||||
#FIXME!
|
||||
self.provider.log("Wrong path: %s, failing auth" %path)
|
||||
raise AuthRejectedExc("Authorization failed. Wrong sub-path.")
|
||||
|
||||
raise AuthRejectedExc("Authorization failed. Wrong sub-path.")
|
||||
if self.auth_creds.get(db):
|
||||
return True
|
||||
if auth_str and auth_str.startswith('Basic '):
|
||||
auth_str=auth_str[len('Basic '):]
|
||||
(user,passwd) = base64.decodestring(auth_str).split(':')
|
||||
@@ -276,7 +276,7 @@ class OerpAuthProxy(AuthProxy):
|
||||
acd = self.provider.authenticate(db,user,passwd,handler.client_address)
|
||||
if acd != False:
|
||||
self.auth_creds[db] = acd
|
||||
self.last_auth=db
|
||||
self.last_auth = db
|
||||
return True
|
||||
if self.auth_tries > 5:
|
||||
self.provider.log("Failing authorization after 5 requests w/o password")
|
||||
|
||||
@@ -234,6 +234,9 @@ class MultiHTTPHandler(FixSendError,BaseHTTPRequestHandler):
|
||||
if fore.close_connection:
|
||||
# print "Closing connection because of handler"
|
||||
self.close_connection = fore.close_connection
|
||||
if hasattr(fore, '_flush'):
|
||||
fore._flush()
|
||||
|
||||
|
||||
def parse_rawline(self):
|
||||
"""Parse a request (internal).
|
||||
|
||||
@@ -251,7 +251,7 @@ form: module.record_id""" % (xml_id,)
|
||||
pass
|
||||
if ids:
|
||||
self.pool.get(d_model).unlink(cr, self.uid, ids)
|
||||
self.pool.get('ir.model.data')._unlink(cr, self.uid, d_model, ids, direct=True)
|
||||
self.pool.get('ir.model.data')._unlink(cr, self.uid, d_model, ids)
|
||||
|
||||
def _tag_report(self, cr, rec, data_node=None):
|
||||
res = {}
|
||||
|
||||
+146
-215
@@ -7,6 +7,7 @@ import pooler
|
||||
import netsvc
|
||||
import misc
|
||||
from config import config
|
||||
import yaml_tag
|
||||
|
||||
import yaml
|
||||
|
||||
@@ -18,161 +19,6 @@ class YamlImportException(Exception):
|
||||
class YamlImportAbortion(Exception):
|
||||
pass
|
||||
|
||||
class YamlTag(object):
|
||||
"""
|
||||
Superclass for constructors of custom tags defined in yaml file.
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
self.__dict__.update(kwargs)
|
||||
def __getitem__(self, key):
|
||||
return getattr(self, key)
|
||||
def __getattr__(self, attr):
|
||||
return None
|
||||
def __repr__(self):
|
||||
return "<%s %s>" % (self.__class__.__name__, sorted(self.__dict__.items()))
|
||||
|
||||
class Assert(YamlTag):
|
||||
def __init__(self, model, id, severity=logging.ERROR, string="NONAME", **kwargs):
|
||||
self.model = model
|
||||
self.id = id
|
||||
self.severity = severity
|
||||
self.string = string
|
||||
super(Assert, self).__init__(**kwargs)
|
||||
|
||||
class Record(YamlTag):
|
||||
def __init__(self, model, id, **kwargs):
|
||||
self.model = model
|
||||
self.id = id
|
||||
super(Record, self).__init__(**kwargs)
|
||||
|
||||
class Python(YamlTag):
|
||||
def __init__(self, model, severity=logging.ERROR, name="", **kwargs):
|
||||
self.model= model
|
||||
self.severity = severity
|
||||
self.name = name
|
||||
super(Python, self).__init__(**kwargs)
|
||||
|
||||
class Menuitem(YamlTag):
|
||||
def __init__(self, id, name, **kwargs):
|
||||
self.id = id
|
||||
self.name = name
|
||||
super(Menuitem, self).__init__(**kwargs)
|
||||
|
||||
class Workflow(YamlTag):
|
||||
def __init__(self, model, action, **kwargs):
|
||||
self.model = model
|
||||
self.action = action
|
||||
super(Workflow, self).__init__(**kwargs)
|
||||
|
||||
class ActWindow(YamlTag):
|
||||
def __init__(self, model, action, **kwargs):
|
||||
self.model = model
|
||||
self.action = action
|
||||
super(ActWindow, self).__init__(**kwargs)
|
||||
|
||||
class Function(YamlTag):
|
||||
def __init__(self, model, name, **kwargs):
|
||||
self.model = model
|
||||
self.name = name
|
||||
super(Function, self).__init__(**kwargs)
|
||||
|
||||
class Report(YamlTag):
|
||||
def __init__(self, model, name, string, **kwargs):
|
||||
self.model = model
|
||||
self.name = name
|
||||
self.string = string
|
||||
super(Report, self).__init__(**kwargs)
|
||||
|
||||
class Delete(YamlTag):
|
||||
def __init__(self, model, id, search, **kwargs):
|
||||
self.model = model
|
||||
self.id = id
|
||||
self.search = search
|
||||
super(Delete, self).__init__(**kwargs)
|
||||
|
||||
class Context(YamlTag):
|
||||
def __init__(self, **kwargs):
|
||||
super(Context, self).__init__(**kwargs)
|
||||
|
||||
class Url(YamlTag):
|
||||
def __init__(self, **kwargs):
|
||||
super(Url, self).__init__(**kwargs)
|
||||
|
||||
class Eval(YamlTag):
|
||||
def __init__(self, expression):
|
||||
self.expression = expression
|
||||
super(Eval, self).__init__()
|
||||
|
||||
class IrSet(YamlTag):
|
||||
def __init__(self):
|
||||
super(IrSet, self).__init__()
|
||||
|
||||
def assert_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Assert(**kwargs)
|
||||
|
||||
def record_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Record(**kwargs)
|
||||
|
||||
def python_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Python(**kwargs)
|
||||
|
||||
def menuitem_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Menuitem(**kwargs)
|
||||
|
||||
def workflow_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Workflow(**kwargs)
|
||||
|
||||
def act_window_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return ActWindow(**kwargs)
|
||||
|
||||
def function_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Function(**kwargs)
|
||||
|
||||
def report_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Report(**kwargs)
|
||||
|
||||
def delete_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Delete(**kwargs)
|
||||
|
||||
def context_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Context(**kwargs)
|
||||
|
||||
def url_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Url(**kwargs)
|
||||
|
||||
def eval_constructor(loader, node):
|
||||
expression = loader.construct_scalar(node)
|
||||
return Eval(expression)
|
||||
|
||||
def ir_set_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return IrSet(**kwargs)
|
||||
|
||||
yaml.add_constructor(u"!assert", assert_constructor)
|
||||
yaml.add_constructor(u"!record", record_constructor)
|
||||
yaml.add_constructor(u"!python", python_constructor)
|
||||
yaml.add_constructor(u"!menuitem", menuitem_constructor)
|
||||
yaml.add_constructor(u"!workflow", workflow_constructor)
|
||||
yaml.add_constructor(u"!act_window", act_window_constructor)
|
||||
yaml.add_constructor(u"!function", function_constructor)
|
||||
yaml.add_constructor(u"!report", report_constructor)
|
||||
yaml.add_constructor(u"!context", context_constructor)
|
||||
yaml.add_constructor(u"!delete", delete_constructor)
|
||||
yaml.add_constructor(u"!url", url_constructor)
|
||||
yaml.add_constructor(u"!eval", eval_constructor)
|
||||
yaml.add_constructor(u"!ir_set", ir_set_constructor)
|
||||
|
||||
def _is_yaml_mapping(node, tag_constructor):
|
||||
value = isinstance(node, types.DictionaryType) \
|
||||
and len(node.keys()) == 1 \
|
||||
@@ -183,45 +29,49 @@ def is_comment(node):
|
||||
return isinstance(node, types.StringTypes)
|
||||
|
||||
def is_assert(node):
|
||||
return _is_yaml_mapping(node, Assert)
|
||||
return _is_yaml_mapping(node, yaml_tag.Assert)
|
||||
|
||||
def is_record(node):
|
||||
return _is_yaml_mapping(node, Record)
|
||||
return _is_yaml_mapping(node, yaml_tag.Record)
|
||||
|
||||
def is_python(node):
|
||||
return _is_yaml_mapping(node, Python)
|
||||
return _is_yaml_mapping(node, yaml_tag.Python)
|
||||
|
||||
def is_menuitem(node):
|
||||
return isinstance(node, Menuitem) \
|
||||
or _is_yaml_mapping(node, Menuitem)
|
||||
return isinstance(node, yaml_tag.Menuitem) \
|
||||
or _is_yaml_mapping(node, yaml_tag.Menuitem)
|
||||
|
||||
def is_function(node):
|
||||
return isinstance(node, Function) \
|
||||
or _is_yaml_mapping(node, Function)
|
||||
return isinstance(node, yaml_tag.Function) \
|
||||
or _is_yaml_mapping(node, yaml_tag.Function)
|
||||
|
||||
def is_report(node):
|
||||
return isinstance(node, Report)
|
||||
return isinstance(node, yaml_tag.Report)
|
||||
|
||||
def is_workflow(node):
|
||||
return isinstance(node, Workflow)
|
||||
return isinstance(node, yaml_tag.Workflow)
|
||||
|
||||
def is_act_window(node):
|
||||
return isinstance(node, ActWindow)
|
||||
return isinstance(node, yaml_tag.ActWindow)
|
||||
|
||||
def is_delete(node):
|
||||
return isinstance(node, Delete)
|
||||
return isinstance(node, yaml_tag.Delete)
|
||||
|
||||
def is_context(node):
|
||||
return isinstance(node, Context)
|
||||
return isinstance(node, yaml_tag.Context)
|
||||
|
||||
def is_url(node):
|
||||
return isinstance(node, Url)
|
||||
return isinstance(node, yaml_tag.Url)
|
||||
|
||||
def is_eval(node):
|
||||
return isinstance(node, Eval)
|
||||
return isinstance(node, yaml_tag.Eval)
|
||||
|
||||
def is_ref(node):
|
||||
return isinstance(node, yaml_tag.Ref) \
|
||||
or _is_yaml_mapping(node, yaml_tag.Ref)
|
||||
|
||||
def is_ir_set(node):
|
||||
return _is_yaml_mapping(node, IrSet)
|
||||
return _is_yaml_mapping(node, yaml_tag.IrSet)
|
||||
|
||||
|
||||
class TestReport(object):
|
||||
@@ -276,7 +126,7 @@ class YamlInterpreter(object):
|
||||
self.pool = pooler.get_pool(cr.dbname)
|
||||
self.uid = 1
|
||||
self.context = {} # opererp context
|
||||
self.eval_context = {'ref': self._ref, '_ref': self._ref} # added '_ref' so that record['ref'] is possible
|
||||
self.eval_context = {'ref': self._ref(), '_ref': self._ref(), 'time': time} # added '_ref' so that record['ref'] is possible
|
||||
|
||||
def _ref(self):
|
||||
return lambda xml_id: self.get_id(xml_id)
|
||||
@@ -309,10 +159,11 @@ class YamlInterpreter(object):
|
||||
id = self.id_map[xml_id]
|
||||
else:
|
||||
if '.' in xml_id:
|
||||
module, xml_id = xml_id.split('.', 1)
|
||||
module, checked_xml_id = xml_id.split('.', 1)
|
||||
else:
|
||||
module = self.module
|
||||
ir_id = self.pool.get('ir.model.data')._get_id(self.cr, self.uid, module, xml_id)
|
||||
checked_xml_id = xml_id
|
||||
ir_id = self.pool.get('ir.model.data')._get_id(self.cr, self.uid, module, checked_xml_id)
|
||||
obj = self.pool.get('ir.model.data').read(self.cr, self.uid, ir_id, ['res_id'])
|
||||
id = int(obj['res_id'])
|
||||
self.id_map[xml_id] = id
|
||||
@@ -327,14 +178,29 @@ class YamlInterpreter(object):
|
||||
def isnoupdate(self, node):
|
||||
return self.noupdate or node.noupdate or False
|
||||
|
||||
def _get_first_result(self, results, default=False):
|
||||
if len(results):
|
||||
value = results[0]
|
||||
if isinstance(value, types.TupleType):
|
||||
value = value[0]
|
||||
else:
|
||||
value = default
|
||||
return value
|
||||
|
||||
def process_comment(self, node):
|
||||
return node
|
||||
|
||||
def _log_assert_failure(self, severity, msg, *args):
|
||||
self.assert_report.record(False, severity)
|
||||
self.logger.log(severity, msg, *args)
|
||||
if severity >= config['assert_exit_level']:
|
||||
raise YamlImportAbortion('Severe assertion failure (%s), aborting.' % logging.getLevelName(severity))
|
||||
if isinstance(severity, types.StringTypes):
|
||||
levelname = severity.strip().upper()
|
||||
level = logging.getLevelName(levelname)
|
||||
else:
|
||||
level = severity
|
||||
levelname = logging.getLevelName(level)
|
||||
self.assert_report.record(False, levelname)
|
||||
self.logger.log(level, msg, *args)
|
||||
if level >= config['assert_exit_level']:
|
||||
raise YamlImportAbortion('Severe assertion failure (%s), aborting.' % levelname)
|
||||
return
|
||||
|
||||
def _get_assertion_id(self, assertion):
|
||||
@@ -351,6 +217,7 @@ class YamlInterpreter(object):
|
||||
assertion, expressions = node.items()[0]
|
||||
|
||||
if self.isnoupdate(assertion) and self.mode != 'init':
|
||||
self.logger.warn('This assertion was not evaluated ("%s").' % assertion.string)
|
||||
return
|
||||
model = self.get_model(assertion.model)
|
||||
ids = self._get_assertion_id(assertion)
|
||||
@@ -365,7 +232,7 @@ class YamlInterpreter(object):
|
||||
context = self.get_context(assertion, self.eval_context)
|
||||
for id in ids:
|
||||
record = model.browse(self.cr, self.uid, id, context)
|
||||
for test in expressions.get('test', ''):
|
||||
for test in expressions:
|
||||
try:
|
||||
success = eval(test, self.eval_context, RecordDictWrapper(record))
|
||||
except Exception, e:
|
||||
@@ -378,19 +245,39 @@ class YamlInterpreter(object):
|
||||
else: # all tests were successful for this assertion tag (no break)
|
||||
self.assert_report.record(True, assertion.severity)
|
||||
|
||||
def _coerce_bool(self, value, default=False):
|
||||
if isinstance(value, types.BooleanType):
|
||||
b = value
|
||||
if isinstance(value, types.StringTypes):
|
||||
b = value.strip().lower() not in ('0', 'false', 'off', 'no')
|
||||
elif isinstance(value, types.IntType):
|
||||
b = bool(value)
|
||||
else:
|
||||
b = default
|
||||
return b
|
||||
|
||||
def process_record(self, node):
|
||||
record, fields = node.items()[0]
|
||||
|
||||
self.validate_xml_id(record.id)
|
||||
if self.isnoupdate(record) and self.mode != 'init':
|
||||
model = self.get_model(record.model)
|
||||
record_dict = self._create_record(model, fields)
|
||||
self.logger.debug("RECORD_DICT %s" % record_dict)
|
||||
id = self.pool.get('ir.model.data')._update(self.cr, self.uid, record.model, \
|
||||
self.module, record_dict, record.id, noupdate=self.isnoupdate(record), mode=self.mode)
|
||||
self.id_map[record.id] = int(id)
|
||||
if config.get('import_partial', False):
|
||||
self.cr.commit()
|
||||
id = self.pool.get('ir.model.data')._update_dummy(self.cr, self.uid, record.model, self.module, record.id)
|
||||
# check if the resource already existed at the last update
|
||||
if id:
|
||||
self.id_map[record] = int(id)
|
||||
return None
|
||||
else:
|
||||
if not self._coerce_bool(record.forcecreate):
|
||||
return None
|
||||
|
||||
model = self.get_model(record.model)
|
||||
record_dict = self._create_record(model, fields)
|
||||
self.logger.debug("RECORD_DICT %s" % record_dict)
|
||||
id = self.pool.get('ir.model.data')._update(self.cr, self.uid, record.model, \
|
||||
self.module, record_dict, record.id, noupdate=self.isnoupdate(record), mode=self.mode)
|
||||
self.id_map[record.id] = int(id)
|
||||
if config.get('import_partial', False):
|
||||
self.cr.commit()
|
||||
|
||||
def _create_record(self, model, fields):
|
||||
record_dict = {}
|
||||
@@ -399,19 +286,50 @@ class YamlInterpreter(object):
|
||||
record_dict[field_name] = field_value
|
||||
return record_dict
|
||||
|
||||
def process_ref(self, node, column=None):
|
||||
if node.search:
|
||||
if node.model:
|
||||
model_name = node.model
|
||||
elif column:
|
||||
model_name = column._obj
|
||||
else:
|
||||
raise YamlImportException('You need to give a model for the search, or a column to infer it.')
|
||||
model = self.get_model(model_name)
|
||||
q = eval(node.search, self.eval_context)
|
||||
ids = model.search(self.cr, self.uid, q)
|
||||
if node.use:
|
||||
instances = model.browse(self.cr, self.uid, ids)
|
||||
value = [inst[node.use] for inst in instances]
|
||||
else:
|
||||
value = ids
|
||||
elif node.id:
|
||||
value = self.get_id(node.id)
|
||||
else:
|
||||
value = None
|
||||
return value
|
||||
|
||||
def process_eval(self, node):
|
||||
return eval(node.expression, self.eval_context)
|
||||
|
||||
def _eval_field(self, model, field_name, expression):
|
||||
column = model._columns[field_name]
|
||||
if column._type == "many2one":
|
||||
if is_ref(expression):
|
||||
elements = self.process_ref(expression)
|
||||
if column._type in ("many2many", "one2many"):
|
||||
value = [(6, 0, elements)]
|
||||
else: # many2one
|
||||
value = self._get_first_result(elements)
|
||||
elif column._type == "many2one":
|
||||
value = self.get_id(expression)
|
||||
elif column._type == "one2many":
|
||||
other_model = self.get_model(column._obj)
|
||||
value = [(0, 0, self._create_record(other_model, fields)) for fields in expression]
|
||||
elif column._type == "many2many":
|
||||
ids = [self.get_id(xml_id) for xml_id in expression]
|
||||
value= [(6, 0, ids)]
|
||||
value = [(6, 0, ids)]
|
||||
else: # scalar field
|
||||
if isinstance(expression, Eval):
|
||||
value = eval(expression.expression)
|
||||
if is_eval(expression):
|
||||
value = self.process_eval(expression)
|
||||
else:
|
||||
value = expression
|
||||
# raise YamlImportException('Unsupported column "%s" or value %s:%s' % (field_name, type(expression), expression))
|
||||
@@ -446,7 +364,6 @@ class YamlInterpreter(object):
|
||||
workflow, values = node.items()[0]
|
||||
if self.isnoupdate(workflow) and self.mode != 'init':
|
||||
return
|
||||
model = self.get_model(workflow.model)
|
||||
if workflow.ref:
|
||||
id = self.get_id(workflow.ref)
|
||||
else:
|
||||
@@ -467,26 +384,44 @@ class YamlInterpreter(object):
|
||||
else:
|
||||
uid = self.uid
|
||||
wf_service = netsvc.LocalService("workflow")
|
||||
wf_service.trg_validate(uid, model, id, workflow.action, self.cr)
|
||||
wf_service.trg_validate(uid, workflow.model, id, workflow.action, self.cr)
|
||||
|
||||
def _eval_params(self, model, params):
|
||||
args = []
|
||||
for i, param in enumerate(params):
|
||||
if isinstance(param, types.ListType):
|
||||
value = self._eval_params(model, param)
|
||||
elif is_ref(param):
|
||||
value = self.process_ref(param)
|
||||
elif is_eval(param):
|
||||
value = self.process_eval(param)
|
||||
elif isinstance(param, types.DictionaryType): # supports XML syntax
|
||||
param_model = self.get_model(param.get('model', model))
|
||||
if 'search' in param:
|
||||
q = eval(param['search'], self.eval_context)
|
||||
ids = param_model.search(cr, uid, q)
|
||||
value = self._get_first_result(ids)
|
||||
elif 'eval' in param:
|
||||
local_context = {'obj': lambda x: param_model.browse(self.cr, self.uid, x, context)}
|
||||
local_context.update(self.id_map)
|
||||
value = eval(param['eval'], self.eval_context, local_context)
|
||||
else:
|
||||
raise YamlImportException('You must provide either a !ref or at least a "eval" or a "search" to function parameter #%d.' % i)
|
||||
else:
|
||||
value = param # scalar value
|
||||
args.append(value)
|
||||
return args
|
||||
|
||||
def process_function(self, node):
|
||||
function, values = node.items()[0]
|
||||
function, params = node.items()[0]
|
||||
if self.isnoupdate(function) and self.mode != 'init':
|
||||
return
|
||||
context = self.get_context(function, self.eval_context)
|
||||
args = []
|
||||
if function.eval:
|
||||
args = eval(function.eval, self.eval_context)
|
||||
for value in values:
|
||||
if not 'model' in value and (not 'eval' in value or not 'search' in value):
|
||||
raise YamlImportException('You must provide a "model" and an "eval" or "search" to evaluate.')
|
||||
value_model = self.get_model(value['model'])
|
||||
local_context = {'obj': lambda x: value_model.browse(self.cr, self.uid, x, context=context)}
|
||||
local_context.update(self.id_map)
|
||||
id = eval(value['eval'], self.eval_context, local_context)
|
||||
if id != None:
|
||||
args.append(id)
|
||||
model = self.get_model(function.model)
|
||||
context = self.get_context(function, self.eval_context)
|
||||
if function.eval:
|
||||
args = self.process_eval(function.eval)
|
||||
else:
|
||||
args = self._eval_params(function.model, params)
|
||||
method = function.name
|
||||
getattr(model, method)(self.cr, self.uid, *args)
|
||||
|
||||
@@ -624,17 +559,13 @@ class YamlInterpreter(object):
|
||||
# TODO add remove ir.model.data
|
||||
|
||||
def process_delete(self, node):
|
||||
ids = []
|
||||
if len(node.search):
|
||||
ids = self.pool.get(node.model).search(self.cr, self.uid, eval(node.search, self.eval_context))
|
||||
if len(node.id):
|
||||
try:
|
||||
ids.append(self.get_id(node.id))
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
ids = [self.get_id(node.id)]
|
||||
if len(ids):
|
||||
self.pool.get(node.model).unlink(self.cr, self.uid, ids)
|
||||
self.pool.get('ir.model.data')._unlink(self.cr, self.uid, node.model, ids, direct=True)
|
||||
self.pool.get('ir.model.data')._unlink(self.cr, self.uid, node.model, ids)
|
||||
|
||||
def process_url(self, node):
|
||||
self.validate_xml_id(node.id)
|
||||
@@ -717,9 +648,9 @@ class YamlInterpreter(object):
|
||||
try:
|
||||
self._process_node(node)
|
||||
except YamlImportException, e:
|
||||
self.logger.log(logging.ERROR, e)
|
||||
self.logger.exception(e)
|
||||
except Exception, e:
|
||||
self.logger.log(logging.ERROR, e)
|
||||
self.logger.exception(e)
|
||||
raise e
|
||||
|
||||
def _process_node(self, node):
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
import yaml
|
||||
import logging
|
||||
|
||||
class YamlTag(object):
|
||||
"""
|
||||
Superclass for constructors of custom tags defined in yaml file.
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
self.__dict__.update(kwargs)
|
||||
def __getitem__(self, key):
|
||||
return getattr(self, key)
|
||||
def __getattr__(self, attr):
|
||||
return None
|
||||
def __repr__(self):
|
||||
return "<%s %s>" % (self.__class__.__name__, sorted(self.__dict__.items()))
|
||||
|
||||
class Assert(YamlTag):
|
||||
def __init__(self, model, id, severity=logging.ERROR, string="NONAME", **kwargs):
|
||||
self.model = model
|
||||
self.id = id
|
||||
self.severity = severity
|
||||
self.string = string
|
||||
super(Assert, self).__init__(**kwargs)
|
||||
|
||||
class Record(YamlTag):
|
||||
def __init__(self, model, id, use='id', **kwargs):
|
||||
self.model = model
|
||||
self.id = id
|
||||
super(Record, self).__init__(**kwargs)
|
||||
|
||||
class Python(YamlTag):
|
||||
def __init__(self, model, severity=logging.ERROR, name="", **kwargs):
|
||||
self.model= model
|
||||
self.severity = severity
|
||||
self.name = name
|
||||
super(Python, self).__init__(**kwargs)
|
||||
|
||||
class Menuitem(YamlTag):
|
||||
def __init__(self, id, name, **kwargs):
|
||||
self.id = id
|
||||
self.name = name
|
||||
super(Menuitem, self).__init__(**kwargs)
|
||||
|
||||
class Workflow(YamlTag):
|
||||
def __init__(self, model, action, **kwargs):
|
||||
self.model = model
|
||||
self.action = action
|
||||
super(Workflow, self).__init__(**kwargs)
|
||||
|
||||
class ActWindow(YamlTag):
|
||||
def __init__(self, model, action, **kwargs):
|
||||
self.model = model
|
||||
self.action = action
|
||||
super(ActWindow, self).__init__(**kwargs)
|
||||
|
||||
class Function(YamlTag):
|
||||
def __init__(self, model, name, **kwargs):
|
||||
self.model = model
|
||||
self.name = name
|
||||
super(Function, self).__init__(**kwargs)
|
||||
|
||||
class Report(YamlTag):
|
||||
def __init__(self, model, name, string, **kwargs):
|
||||
self.model = model
|
||||
self.name = name
|
||||
self.string = string
|
||||
super(Report, self).__init__(**kwargs)
|
||||
|
||||
class Delete(YamlTag):
|
||||
def __init__(self, model, id, search, **kwargs):
|
||||
self.model = model
|
||||
self.id = id
|
||||
self.search = search
|
||||
super(Delete, self).__init__(**kwargs)
|
||||
|
||||
class Context(YamlTag):
|
||||
def __init__(self, **kwargs):
|
||||
super(Context, self).__init__(**kwargs)
|
||||
|
||||
class Url(YamlTag):
|
||||
def __init__(self, **kwargs):
|
||||
super(Url, self).__init__(**kwargs)
|
||||
|
||||
class Eval(YamlTag):
|
||||
def __init__(self, expression):
|
||||
self.expression = expression
|
||||
super(Eval, self).__init__()
|
||||
|
||||
class Ref(YamlTag):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Ref, self).__init__(*args, **kwargs)
|
||||
|
||||
class IrSet(YamlTag):
|
||||
def __init__(self):
|
||||
super(IrSet, self).__init__()
|
||||
|
||||
def assert_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Assert(**kwargs)
|
||||
|
||||
def record_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Record(**kwargs)
|
||||
|
||||
def python_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Python(**kwargs)
|
||||
|
||||
def menuitem_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Menuitem(**kwargs)
|
||||
|
||||
def workflow_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Workflow(**kwargs)
|
||||
|
||||
def act_window_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return ActWindow(**kwargs)
|
||||
|
||||
def function_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Function(**kwargs)
|
||||
|
||||
def report_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Report(**kwargs)
|
||||
|
||||
def delete_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Delete(**kwargs)
|
||||
|
||||
def context_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Context(**kwargs)
|
||||
|
||||
def url_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Url(**kwargs)
|
||||
|
||||
def eval_constructor(loader, node):
|
||||
expression = loader.construct_scalar(node)
|
||||
return Eval(expression)
|
||||
|
||||
def ref_constructor(loader, tag_suffix, node):
|
||||
if tag_suffix == "id":
|
||||
kwargs = {"id": loader.construct_scalar(node)}
|
||||
else:
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return Ref(**kwargs)
|
||||
|
||||
def ir_set_constructor(loader, node):
|
||||
kwargs = loader.construct_mapping(node)
|
||||
return IrSet(**kwargs)
|
||||
|
||||
# Registers constructors for custom tags.
|
||||
# Constructors are actually defined globally: do not redefined them in another
|
||||
# class/file/package. This means that module recorder need import this file.
|
||||
yaml.add_constructor(u"!assert", assert_constructor)
|
||||
yaml.add_constructor(u"!record", record_constructor)
|
||||
yaml.add_constructor(u"!python", python_constructor)
|
||||
yaml.add_constructor(u"!menuitem", menuitem_constructor)
|
||||
yaml.add_constructor(u"!workflow", workflow_constructor)
|
||||
yaml.add_constructor(u"!act_window", act_window_constructor)
|
||||
yaml.add_constructor(u"!function", function_constructor)
|
||||
yaml.add_constructor(u"!report", report_constructor)
|
||||
yaml.add_constructor(u"!context", context_constructor)
|
||||
yaml.add_constructor(u"!delete", delete_constructor)
|
||||
yaml.add_constructor(u"!url", url_constructor)
|
||||
yaml.add_constructor(u"!eval", eval_constructor)
|
||||
yaml.add_multi_constructor(u"!ref", ref_constructor)
|
||||
yaml.add_constructor(u"!ir_set", ir_set_constructor)
|
||||
Reference in New Issue
Block a user