Merge commit 'origin/master' into mdv-gpl3

Conflicts:
	bin/report/interface.py
	bin/tools/translate.py

bzr revid: p_christ@hol.gr-20081215095422-9jc4m1qrknku6ux8
This commit is contained in:
P. Christeas 2008-12-15 11:54:22 +02:00
commit 087f5bc171
62 changed files with 1675 additions and 1213 deletions

View File

@ -138,7 +138,7 @@ def get_module_path(module):
if os.path.exists(opj(_ad, module)) or os.path.exists(opj(_ad, '%s.zip' % module)): if os.path.exists(opj(_ad, module)) or os.path.exists(opj(_ad, '%s.zip' % module)):
return opj(_ad, module) return opj(_ad, module)
logger.notifyChannel('init', netsvc.LOG_WARNING, 'addon %s: module not found' % (module,)) logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: module not found' % (module,))
return False return False
raise IOError, 'Module not found : %s' % module raise IOError, 'Module not found : %s' % module
@ -216,7 +216,7 @@ def create_graph(module_list, force=None):
try: try:
info = eval(tools.file_open(terp_file).read()) info = eval(tools.file_open(terp_file).read())
except: except:
logger.notifyChannel('init', netsvc.LOG_ERROR, 'addon %s: eval file %s' % (module, terp_file)) logger.notifyChannel('init', netsvc.LOG_ERROR, 'module %s: eval file %s' % (module, terp_file))
raise raise
if info.get('installable', True): if info.get('installable', True):
packages.append((module, info.get('depends', []), info)) packages.append((module, info.get('depends', []), info))
@ -246,18 +246,25 @@ def create_graph(module_list, force=None):
for package in later: for package in later:
unmet_deps = filter(lambda p: p not in graph, dependencies[package]) unmet_deps = filter(lambda p: p not in graph, dependencies[package])
logger.notifyChannel('init', netsvc.LOG_ERROR, 'addon %s: Unmet dependencies: %s' % (package, ', '.join(unmet_deps))) logger.notifyChannel('init', netsvc.LOG_ERROR, 'module %s: Unmet dependencies: %s' % (package, ', '.join(unmet_deps)))
return graph return graph
def init_module_objects(cr, module_name, obj_list): def init_module_objects(cr, module_name, obj_list):
pool = pooler.get_pool(cr.dbname) pool = pooler.get_pool(cr.dbname)
logger.notifyChannel('init', netsvc.LOG_INFO, 'addon %s: creating or updating database tables' % module_name) logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: creating or updating database tables' % module_name)
todo = []
for obj in obj_list: for obj in obj_list:
if hasattr(obj, 'init'): if hasattr(obj, 'init'):
obj.init(cr) obj.init(cr)
obj._auto_init(cr, {'module': module_name}) result = obj._auto_init(cr, {'module': module_name})
if result:
todo += result
cr.commit() cr.commit()
todo.sort()
for t in todo:
t[1](cr, *t[2])
cr.commit()
# #
# Register module named m, if not already registered # Register module named m, if not already registered
@ -266,7 +273,7 @@ def register_class(m):
global loaded global loaded
if m in loaded: if m in loaded:
return return
logger.notifyChannel('init', netsvc.LOG_INFO, 'addon %s: registering classes' % m) logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: registering objects' % m)
loaded.append(m) loaded.append(m)
mod_path = get_module_path(m) mod_path = get_module_path(m)
if not os.path.isfile(mod_path+'.zip'): if not os.path.isfile(mod_path+'.zip'):
@ -380,7 +387,7 @@ class MigrationManager(object):
from tools.parse_version import parse_version from tools.parse_version import parse_version
parsed_installed_version = parse_version(pkg.latest_version) parsed_installed_version = parse_version(pkg.installed_version or '')
current_version = parse_version(convert_version(pkg.data.get('version', '0'))) current_version = parse_version(convert_version(pkg.data.get('version', '0')))
versions = _get_migration_versions(pkg) versions = _get_migration_versions(pkg)
@ -401,20 +408,20 @@ class MigrationManager(object):
mod = None mod = None
try: try:
mod = imp.load_source(name, pyfile, fp) mod = imp.load_source(name, pyfile, fp)
logger.notifyChannel('migration', netsvc.LOG_INFO, 'addon %(addon)s: Running migration %(version)s %(name)s"' % mergedict({'name': mod.__name__},strfmt)) logger.notifyChannel('migration', netsvc.LOG_INFO, 'module %(addon)s: Running migration %(version)s %(name)s"' % mergedict({'name': mod.__name__},strfmt))
mod.migrate(self.cr, pkg.latest_version) mod.migrate(self.cr, pkg.installed_version)
except ImportError: except ImportError:
logger.notifyChannel('migration', netsvc.LOG_ERROR, 'addon %(addon)s: Unable to load %(stage)-migration file %(file)s' % mergedict({'file': opj(modulename,pyfile)}, strfmt)) logger.notifyChannel('migration', netsvc.LOG_ERROR, 'module %(addon)s: Unable to load %(stage)-migration file %(file)s' % mergedict({'file': opj(modulename,pyfile)}, strfmt))
raise raise
except AttributeError: except AttributeError:
logger.notifyChannel('migration', netsvc.LOG_ERROR, 'addon %(addon)s: Each %(stage)-migration file must have a "migrate(cr, installed_version)" function' % strfmt) logger.notifyChannel('migration', netsvc.LOG_ERROR, 'module %(addon)s: Each %(stage)-migration file must have a "migrate(cr, installed_version)" function' % strfmt)
except: except:
raise raise
fp.close() fp.close()
del mod del mod
def load_module_graph(cr, graph, status=None, **kwargs): def load_module_graph(cr, graph, status=None, check_access_rules=True, **kwargs):
# **kwargs is passed directly to convert_xml_import # **kwargs is passed directly to convert_xml_import
if not status: if not status:
status={} status={}
@ -427,13 +434,14 @@ def load_module_graph(cr, graph, status=None, **kwargs):
# update the graph with values from the database (if exist) # update the graph with values from the database (if exist)
## First, we set the default values for each package in graph ## First, we set the default values for each package in graph
additional_data = dict.fromkeys([p.name for p in graph], {'id': 0, 'state': 'uninstalled', 'dbdemo': False, 'latest_version': None}) additional_data = dict.fromkeys([p.name for p in graph], {'id': 0, 'state': 'uninstalled', 'dbdemo': False, 'installed_version': None})
## Then we get the values from the database ## Then we get the values from the database
cr.execute('SELECT name, id, state, demo AS dbdemo, latest_version' cr.execute('SELECT name, id, state, demo AS dbdemo, latest_version AS installed_version'
' FROM ir_module_module' ' FROM ir_module_module'
' WHERE name in (%s)' % (','.join(['%s'] * len(graph))), ' WHERE name in (%s)' % (','.join(['%s'] * len(graph))),
additional_data.keys() additional_data.keys()
) )
## and we update the default values with values from the database ## and we update the default values with values from the database
additional_data.update(dict([(x.pop('name'), x) for x in cr.dictfetchall()])) additional_data.update(dict([(x.pop('name'), x) for x in cr.dictfetchall()]))
@ -443,6 +451,7 @@ def load_module_graph(cr, graph, status=None, **kwargs):
migrations = MigrationManager(cr, graph) migrations = MigrationManager(cr, graph)
check_rules = False
for package in graph: for package in graph:
status['progress'] = (float(statusi)+0.1)/len(graph) status['progress'] = (float(statusi)+0.1)/len(graph)
m = package.name m = package.name
@ -451,19 +460,20 @@ def load_module_graph(cr, graph, status=None, **kwargs):
migrations.migrate_module(package, 'pre') migrations.migrate_module(package, 'pre')
register_class(m) register_class(m)
logger.notifyChannel('init', netsvc.LOG_INFO, 'addon %s' % m) logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s loading objects' % m)
modules = pool.instanciate(m, cr) modules = pool.instanciate(m, cr)
idref = {} idref = {}
status['progress'] = (float(statusi)+0.4)/len(graph) status['progress'] = (float(statusi)+0.4)/len(graph)
if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'): if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
check_rules = True
init_module_objects(cr, m, modules) init_module_objects(cr, m, modules)
for kind in ('init', 'update'): for kind in ('init', 'update'):
for filename in package.data.get('%s_xml' % kind, []): for filename in package.data.get('%s_xml' % kind, []):
mode = 'update' mode = 'update'
if hasattr(package, 'init') or package.state=='to install': if hasattr(package, 'init') or package.state=='to install':
mode = 'init' mode = 'init'
logger.notifyChannel('init', netsvc.LOG_INFO, 'addon %s: loading %s' % (m, filename)) logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, filename))
name, ext = os.path.splitext(filename) name, ext = os.path.splitext(filename)
fp = tools.file_open(opj(m, filename)) fp = tools.file_open(opj(m, filename))
if ext == '.csv': if ext == '.csv':
@ -481,17 +491,18 @@ def load_module_graph(cr, graph, status=None, **kwargs):
status['progress'] = (float(statusi)+0.75)/len(graph) status['progress'] = (float(statusi)+0.75)/len(graph)
for xml in package.data.get('demo_xml', []): for xml in package.data.get('demo_xml', []):
name, ext = os.path.splitext(xml) name, ext = os.path.splitext(xml)
logger.notifyChannel('init', netsvc.LOG_INFO, 'addon %s: loading %s' % (m, xml)) logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, xml))
fp = tools.file_open(opj(m, xml)) fp = tools.file_open(opj(m, xml))
if ext == '.csv': if ext == '.csv':
tools.convert_csv_import(cr, m, os.path.basename(xml), fp.read(), idref, noupdate=True) tools.convert_csv_import(cr, m, os.path.basename(xml), fp.read(), idref, noupdate=True)
else: else:
tools.convert_xml_import(cr, m, fp, idref, noupdate=True, **kwargs) tools.convert_xml_import(cr, m, fp, idref, noupdate=True, **kwargs)
fp.close() fp.close()
cr.execute('update ir_module_module set demo=%s where id=%d', (True, mid)) cr.execute('update ir_module_module set demo=%s where id=%s', (True, mid))
package_todo.append(package.name) package_todo.append(package.name)
ver = release.major_version + '.' + package.data.get('version', '1.0') ver = release.major_version + '.' + package.data.get('version', '1.0')
cr.execute("update ir_module_module set state='installed', latest_version=%s where id=%d", (ver, mid,)) # update the installed version in database...
cr.execute("update ir_module_module set state='installed', latest_version=%s where id=%s", (ver, mid,))
cr.commit() cr.commit()
# Set new modules and dependencies # Set new modules and dependencies
@ -501,18 +512,18 @@ def load_module_graph(cr, graph, status=None, **kwargs):
if modobj: if modobj:
modobj.update_translations(cr, 1, [mid], None) modobj.update_translations(cr, 1, [mid], None)
cr.commit() cr.commit()
migrations.migrate_module(package, 'post') migrations.migrate_module(package, 'post')
statusi+=1 statusi+=1
cr.execute("""select model,name from ir_model where id not in (select model_id from ir_model_access)""") if check_access_rules and check_rules:
for (model,name) in cr.fetchall(): cr.execute("""select model,name from ir_model where id not in (select model_id from ir_model_access)""")
logger.notifyChannel('init', netsvc.LOG_WARNING, 'addon object %s (%s) has no access rules!' % (model,name)) for (model,name) in cr.fetchall():
logger.notifyChannel('init', netsvc.LOG_WARNING, 'object %s (%s) has no access rules!' % (model,name))
pool = pooler.get_pool(cr.dbname) pool = pooler.get_pool(cr.dbname)
cr.execute('select * from ir_model where state=%s', ('manual',)) cr.execute('select model from ir_model where state=%s', ('manual',))
for model in cr.dictfetchall(): for model in cr.dictfetchall():
pool.get('ir.model').instanciate(cr, 1, model['model'], {}) pool.get('ir.model').instanciate(cr, 1, model['model'], {})
@ -527,31 +538,46 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
if force_demo: if force_demo:
force.append('demo') force.append('demo')
pool = pooler.get_pool(cr.dbname) pool = pooler.get_pool(cr.dbname)
report = tools.assertion_report()
if update_module: if update_module:
for module in tools.config['init']: basegraph = create_graph(['base'], force)
cr.execute('update ir_module_module set state=%s where state=%s and name=%s', ('to install', 'uninstalled', module)) load_module_graph(cr, basegraph, status, check_access_rules=False, report=report)
cr.commit()
register_class('base') modobj = pool.get('ir.module.module')
pool.instanciate('base', cr) logger.notifyChannel('init', netsvc.LOG_INFO, 'updating modules list')
modobj = pool.get('ir.module.module') cr.execute("select id from ir_module_module where state in ('to install','to upgrade') and name=%s", ('base',))
if cr.rowcount:
modobj.update_list(cr, 1) modobj.update_list(cr, 1)
mids = modobj.search(cr, 1, [('state','in',('installed','to install'))]) mods = [k for k in tools.config['init'] if tools.config['init'][k]]
for m in modobj.browse(cr, 1, mids): if mods:
for dep in m.dependencies_id: ids = modobj.search(cr, 1, ['&', ('state', '=', 'uninstalled'), ('name', 'in', mods)])
if dep.state=='uninstalled': if ids:
modobj.button_install(cr, 1, [m.id]) modobj.button_install(cr, 1, ids)
cr.execute("select name from ir_module_module where state in ('installed', 'to install', 'to upgrade','to remove')") mods = [k for k in tools.config['update'] if tools.config['update'][k]]
if mods:
ids = modobj.search(cr, 1, ['&',('state', '=', 'installed'), ('name', 'in', mods)])
if ids:
modobj.button_upgrade(cr, 1, ids)
cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base'))
cr.execute("select name from ir_module_module where state in ('installed', 'to install', 'to upgrade')")
else: else:
cr.execute("select name from ir_module_module where state in ('installed', 'to upgrade', 'to remove')") cr.execute("select name from ir_module_module where state in ('installed', 'to upgrade')")
module_list = [name for (name,) in cr.fetchall()] module_list = [name for (name,) in cr.fetchall()]
graph = create_graph(module_list, force) graph = create_graph(module_list, force)
report = tools.assertion_report()
# the 'base' module has already been updated
base = graph['base']
base.state = 'installed'
for kind in ('init', 'demo', 'update'):
if hasattr(base, kind):
delattr(base, kind)
load_module_graph(cr, graph, status, report=report) load_module_graph(cr, graph, status, report=report)
if report.get_report(): if report.get_report():
logger.notifyChannel('init', netsvc.LOG_INFO, 'assert: %s' % report) logger.notifyChannel('init', netsvc.LOG_INFO, report)
for kind in ('init', 'demo', 'update'): for kind in ('init', 'demo', 'update'):
tools.config[kind]={} tools.config[kind]={}
@ -563,29 +589,29 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
pool = pooler.get_pool(cr.dbname) pool = pooler.get_pool(cr.dbname)
cr.execute('select model,res_id from ir_model_data where not noupdate and module=%s order by id desc', (mod_name,)) cr.execute('select model,res_id from ir_model_data where not noupdate and module=%s order by id desc', (mod_name,))
for rmod,rid in cr.fetchall(): for rmod,rid in cr.fetchall():
#
# TO BE Improved:
# I can not use the class_pool has _table could be defined in __init__
# and I can not use the pool has the module could not be loaded in the pool
#
uid = 1 uid = 1
pool.get(rmod).unlink(cr, uid, [rid]) pool.get(rmod).unlink(cr, uid, [rid])
cr.commit() cr.commit()
# #
# TODO: remove menu without actions of childs # TODO: remove menu without actions of childs
# #
cr.execute('''delete from while True:
ir_ui_menu cr.execute('''delete from
where ir_ui_menu
(id not in (select parent_id from ir_ui_menu where parent_id is not null)) where
and (id not in (select parent_id from ir_ui_menu where parent_id is not null))
(id not in (select res_id from ir_values where model='ir.ui.menu')) and
and (id not in (select res_id from ir_values where model='ir.ui.menu'))
(id not in (select res_id from ir_model_data where model='ir.ui.menu'))''') and
(id not in (select res_id from ir_model_data where model='ir.ui.menu'))''')
if not cr.rowcount:
break
else:
logger.notifyChannel('init', netsvc.LOG_INFO, 'removed %d unused menus' % (cr.rowcount,))
cr.execute("update ir_module_module set state=%s where state in ('to remove')", ('uninstalled', )) cr.execute("update ir_module_module set state=%s where state in ('to remove')", ('uninstalled', ))
cr.commit() cr.commit()
pooler.restart_pool(cr.dbname) #pooler.restart_pool(cr.dbname)
cr.close() cr.close()

View File

@ -21,9 +21,9 @@
############################################################################## ##############################################################################
{ {
"name" : "Base", "name" : "Base",
"version" : "1.0", "version" : "1.1",
"author" : "Tiny", "author" : "Tiny",
"website" : "http://openerp.com", "website" : "http://www.openerp.com",
"category" : "Generic Modules/Base", "category" : "Generic Modules/Base",
"description": "The kernel of OpenERP, needed for all installation.", "description": "The kernel of OpenERP, needed for all installation.",
"depends" : [], "depends" : [],

View File

@ -2,7 +2,11 @@
<openerp> <openerp>
<data> <data>
<menuitem icon="terp-administration" id="menu_administration" name="Administration" sequence="20"/> <menuitem icon="terp-administration" id="menu_administration" name="Administration" sequence="20"/>
<menuitem id="menu_custom" name="Custom" parent="base.menu_administration" sequence="2"/> <menuitem id="next_id_4" name="Low Level Objects" parent="base.menu_administration" sequence="3"/>
<menuitem id="menu_low_workflow" name="Workflow Items" parent="base.next_id_4"/>
<menuitem id="menu_custom" name="Customization" parent="base.menu_administration" sequence="2"/>
<menuitem id="menu_custom_action" name="Actions" parent="base.menu_custom" sequence="20"/>
<menuitem id="menu_config" name="Configuration" parent="base.menu_administration" sequence="1"/>
<menuitem id="menu_translation" name="Translations" parent="base.menu_administration" sequence="4"/> <menuitem id="menu_translation" name="Translations" parent="base.menu_administration" sequence="4"/>
<menuitem id="menu_translation_app" name="Application Terms" parent="base.menu_translation" sequence="4"/> <menuitem id="menu_translation_app" name="Application Terms" parent="base.menu_translation" sequence="4"/>
<menuitem id="menu_translation_export" name="Import / Export" parent="base.menu_translation" sequence="4"/> <menuitem id="menu_translation_export" name="Import / Export" parent="base.menu_translation" sequence="4"/>

View File

@ -6,7 +6,7 @@
Languages Languages
====================== ======================
--> -->
<menuitem id="next_id_2" name="Interface" parent="base.menu_custom"/> <menuitem id="next_id_2" name="User Interface" parent="base.menu_custom"/>
<!-- <!--
====================== ======================

View File

@ -41,7 +41,7 @@
</record> </record>
<record id="act_values_form" model="ir.actions.act_window"> <record id="act_values_form" model="ir.actions.act_window">
<field name="name">Values</field> <field name="name">Client Actions Connections</field>
<field name="type">ir.actions.act_window</field> <field name="type">ir.actions.act_window</field>
<field name="res_model">ir.values</field> <field name="res_model">ir.values</field>
<field name="view_type">form</field> <field name="view_type">form</field>
@ -49,7 +49,6 @@
<field name="context">{'read':'default'}</field> <field name="context">{'read':'default'}</field>
</record> </record>
<menuitem id="next_id_4" name="Low Level" parent="menu_custom"/>
<menuitem action="act_values_form" id="menu_values_form" parent="next_id_4"/> <menuitem action="act_values_form" id="menu_values_form" parent="next_id_4"/>
<!-- Sequences --> <!-- Sequences -->
@ -114,7 +113,7 @@
<field name="view_id" ref="sequence_view_tree"/> <field name="view_id" ref="sequence_view_tree"/>
<field name="context">{'active_test': False}</field> <field name="context">{'active_test': False}</field>
</record> </record>
<menuitem id="next_id_5" name="Sequences" parent="base.menu_custom"/> <menuitem id="next_id_5" name="Sequences" parent="base.menu_config"/>
<menuitem action="ir_sequence_form" id="menu_ir_sequence_form" parent="next_id_5"/> <menuitem action="ir_sequence_form" id="menu_ir_sequence_form" parent="next_id_5"/>
<!-- Sequences Types --> <!-- Sequences Types -->
@ -152,7 +151,6 @@
<field name="name" select="1"/> <field name="name" select="1"/>
<field name="type" select="1"/> <field name="type" select="1"/>
<field name="usage"/> <field name="usage"/>
<field name="parent_id"/>
</form> </form>
</field> </field>
</record> </record>
@ -465,7 +463,7 @@
<field name="model">ir.ui.view</field> <field name="model">ir.ui.view</field>
<field name="type">form</field> <field name="type">form</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="User Interface - Views"> <form string="Views">
<field name="name" select="1"/> <field name="name" select="1"/>
<field name="type" select="1"/> <field name="type" select="1"/>
<field name="model" select="1"/> <field name="model" select="1"/>
@ -481,7 +479,7 @@
<field name="model">ir.ui.view</field> <field name="model">ir.ui.view</field>
<field name="type">tree</field> <field name="type">tree</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree> <tree string="Views">
<field name="name"/> <field name="name"/>
<field name="type"/> <field name="type"/>
<field name="model"/> <field name="model"/>
@ -489,7 +487,7 @@
</field> </field>
</record> </record>
<record id="action_ui_view" model="ir.actions.act_window"> <record id="action_ui_view" model="ir.actions.act_window">
<field name="name">View</field> <field name="name">Views</field>
<field name="type">ir.actions.act_window</field> <field name="type">ir.actions.act_window</field>
<field name="res_model">ir.ui.view</field> <field name="res_model">ir.ui.view</field>
<field name="view_id" ref="view_view_tree"/> <field name="view_id" ref="view_view_tree"/>
@ -690,9 +688,9 @@
<group colspan="2" col="2"> <group colspan="2" col="2">
<separator string="Field Type" colspan="2"/> <separator string="Field Type" colspan="2"/>
<field name="ttype" select="2"/> <field name="ttype" select="2"/>
<field name="relation" select="2" attrs="{'required': [('ttype','in',['many2one','one2many','many2many'])], <field name="relation_id" select="2" attrs="{'required': [('ttype','in',['many2one','one2many','many2many'])],
'readonly': [('ttype','not in', ['many2one','one2many','many2many'])]}"/> 'readonly': [('ttype','!=','one2many'), ('ttype','!=','many2one'), ('ttype','!=','many2many')]}"/>
<field name="relation_field" attrs="{'required': [('ttype','=','one2many')], <field name="relation_field_id" domain="[('model_id','=',relation_id)]" attrs="{'required': [('ttype','=','one2many')],
'readonly': [('ttype','!=','one2many')]}"/> 'readonly': [('ttype','!=','one2many')]}"/>
<field name="selection" attrs="{'required': [('ttype','=','selection')], 'readonly': [('ttype','!=','selection')]}"/> <field name="selection" attrs="{'required': [('ttype','=','selection')], 'readonly': [('ttype','!=','selection')]}"/>
<field name="size" attrs="{'required': [('ttype','=','char')], 'readonly': [('ttype','!=','char')]}"/> <field name="size" attrs="{'required': [('ttype','=','char')], 'readonly': [('ttype','!=','char')]}"/>
@ -720,7 +718,7 @@
</page> </page>
<page string="Access Rights"> <page string="Access Rights">
<field colspan="4" name="access_ids" select="1" nolabel="1"> <field colspan="4" name="access_ids" select="1" nolabel="1">
<tree string="Access Rules" editable="bottom" colspan="4"> <tree string="Access Rules" editable="bottom">
<field name="group_id"/> <field name="group_id"/>
<field name="perm_read"/> <field name="perm_read"/>
<field name="perm_write"/> <field name="perm_write"/>
@ -769,9 +767,11 @@
<page string="Properties"> <page string="Properties">
<group colspan="2" col="2"> <group colspan="2" col="2">
<field name="ttype" select="2"/> <field name="ttype" select="2"/>
<field name="relation" select="2" attrs="{'required': [('ttype','in', ['many2one','one2many','many2many'])], <field name="relation_id" select="2" on_change="on_relation_change(relation_id)" attrs="{'required': [('ttype','in', ['many2one','one2many','many2many'])],
'readonly': [('ttype','not in', ['many2one','one2many','many2many'])]}"/> 'readonly': [('ttype','not in', ['many2one','one2many','many2many'])]}"/>
<field name="relation_field" attrs="{'required': [('ttype','=','one2many')], <!-- <field name="relation" select="2" attrs="{'required': [('ttype','in', ['many2one','one2many','many2many'])],-->
<!-- 'readonly': [('ttype','not in', ['many2one','one2many','many2many'])]}"/>-->
<field name="relation_field_id" domain="[('model_id','=',relation_id)]" attrs="{'required': [('ttype','=','one2many')],
'readonly': [('ttype','!=','one2many')]}"/> 'readonly': [('ttype','!=','one2many')]}"/>
<field name="selection" attrs="{'required': [('ttype','=','selection')], 'readonly': [('ttype','!=','selection')]}"/> <field name="selection" attrs="{'required': [('ttype','=','selection')], 'readonly': [('ttype','!=','selection')]}"/>
<field name="size" attrs="{'required': [('ttype','=','char')], 'readonly': [('ttype','!=','char')]}"/> <field name="size" attrs="{'required': [('ttype','=','char')], 'readonly': [('ttype','!=','char')]}"/>
@ -787,7 +787,7 @@
<field name="on_delete" attrs="{'readonly': [('ttype','!=','many2one')]}"/> <field name="on_delete" attrs="{'readonly': [('ttype','!=','many2one')]}"/>
</group> </group>
</page> </page>
<page string="Security on Groups" colspan="4"> <page string="Security on Groups">
<field name="groups" colspan="4" nolabel="1"/> <field name="groups" colspan="4" nolabel="1"/>
</page> </page>
</notebook> </notebook>
@ -934,7 +934,7 @@
</field> </field>
</record> </record>
<record id="grant_menu_access" model="ir.actions.act_window"> <record id="grant_menu_access" model="ir.actions.act_window">
<field name="name">Grant access to menu</field> <field name="name">Grant Access To Menus</field>
<field name="res_model">ir.ui.menu</field> <field name="res_model">ir.ui.menu</field>
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="view_id" ref="edit_menu"/> <field name="view_id" ref="edit_menu"/>
@ -992,7 +992,7 @@
<field name="context">{'active_test': False}</field> <field name="context">{'active_test': False}</field>
<field name="view_id" ref="ir_cron_view_tree"/> <field name="view_id" ref="ir_cron_view_tree"/>
</record> </record>
<menuitem id="next_id_10" name="Scheduler" parent="base.menu_custom"/> <menuitem id="next_id_10" name="Scheduler" parent="base.menu_config"/>
<menuitem action="ir_cron_act" id="menu_ir_cron_act" parent="next_id_10"/> <menuitem action="ir_cron_act" id="menu_ir_cron_act" parent="next_id_10"/>
@ -1143,15 +1143,16 @@
<field name="model_id" select="1"/> <field name="model_id" select="1"/>
<field name="sequence" select="2"/> <field name="sequence" select="2"/>
<notebook colspan="4"> <notebook colspan="4">
<page string="Python Code" attrs="{'invisible':[('state','!=','python')]}"> <!--<page string="Python Code" attrs="{'invisible':[('state','!=','python')]}">
<separator colspan="4" string="Python code"/> <separator colspan="4" string="Python code"/>
<field name="code" colspan="4" nolabel="1" /> <field name="code" colspan="4" nolabel="1" />
<button string="Create Action" name="%(wizard_server_action_create)d" type="action"/> <button string="Create Action" name="%(wizard_server_action_create)d" type="action"/>
</page> </page>
<page string="Trigger" attrs="{'invisible':[('state','!=','trigger')]}"> --><page string="Trigger" attrs="{'invisible':[('state','!=','trigger')]}">
<separator colspan="4" string="Trigger Configuration"/> <separator colspan="4" string="Trigger Configuration"/>
<field name="trigger_obj_id" select="2" colspan="4"/> <field name="wkf_model_id"/>
<field name="trigger_obj_id" select="2" domain="[('model_id','=',model_id)]" on_change="on_trigger_obj_id(trigger_obj_id)"/>
<field name="trigger_name" select="2"/> <field name="trigger_name" select="2"/>
</page> </page>
@ -1159,42 +1160,49 @@
<field name="action_id" select="2" colspan="4"/> <field name="action_id" select="2" colspan="4"/>
</page> </page>
<page string="Email / SMS" attrs="{'invisible':[('state','!=','sms'),('state','!=','email')]}"> <page string="Email Configuration" attrs="{'invisible':[('state','!=','email')]}">
<separator colspan="4" string="Email Configuration"/> <separator colspan="4" string="Email Configuration"/>
<field name="address" domain="[('model_id','=',model_id)]"/> <field name="email" domain="[('model_id','=',model_id)]"/>
<field name="sms" colspan="4" attrs="{'readonly':[('state','!=','sms')]}"/> <field name="message" select="2" colspan="4"/>
<field name="message" select="2" colspan="4" attrs="{'readonly':[('state','!=','email')]}" /> <newline/>
<label colspan="4" string="Access all the fields related to the current object using expression in double brackets, i.e. [[ object.partner_id.name ]]" align="0.0"/>
</page>
<page string="SMS Configuration" attrs="{'invisible':[('state','!=','sms')]}">
<separator colspan="4" string="SMS Configuration"/>
<field name="mobile" domain="[('model_id','=',model_id)]"/>
<field name="sms" colspan="4"/>
<newline/> <newline/>
<label colspan="4" string="Access all the fields related to the current object using expression in double brackets, i.e. [[ object.partner_id.name ]]" align="0.0"/> <label colspan="4" string="Access all the fields related to the current object using expression in double brackets, i.e. [[ object.partner_id.name ]]" align="0.0"/>
</page> </page>
<page string="Create / Write" attrs="{'invisible':[('state','!=','object_create'),('state','!=','object_write')]}"> <page string="Create / Write" attrs="{'invisible':[('state','!=','object_create'),('state','!=','object_write')]}">
<separator colspan="4" string="Fields Mapping"/> <separator colspan="4" string="Fields Mapping"/>
<field name="otype"/>
<field name="srcmodel_id" select="2"/> <field name="srcmodel_id" select="2"/>
<field name="fields_lines" nolabel="1" select="2" colspan="4"> <field name="fields_lines" nolabel="1" select="2" colspan="4">
<tree string="Field Mappings" editable="top"> <tree string="Field Mappings" editable="top">
<field name="col1" domain="[('model_id','=',parent.srcmodel_id or parent.model_id)]"/> <field name="col1" domain="[('model_id','=',parent.srcmodel_id or parent.model_id)]"/>
<field name="type"/> <field name="type"/>
<field name="value" colsapan="4"/> <field name="value" colspan="4"/>
</tree> </tree>
<form string="Field Mapping"> <form string="Field Mapping">
<field name="col1" domain="[('model_id','=',parent.srcmodel_id or parent.model_id)]"/> <field name="col1" domain="[('model_id','=',parent.srcmodel_id or parent.model_id)]"/>
<field name="type"/> <field name="type"/>
<field name="value" colsapan="4"/> <field name="value" colspan="4"/>
</form> </form>
</field> </field>
<field name="record_id" attrs="{'invisible':[('state','!=','object_create')]}" domain="[('model_id','in',[model_id])]"/>
<label colspan="4" string="If you use a formula type, use a python expression using the variable 'object'." align="0.0"/> <label colspan="4" string="If you use a formula type, use a python expression using the variable 'object'." align="0.0"/>
</page> </page>
<page string="Other Actions" attrs="{'invisible':[('state','!=','other')]}"> <page string="Multi Actions" attrs="{'invisible':[('state','!=','other')]}">
<separator colspan="4" string="Other Actions Configuration"/> <separator colspan="4" string="Other Actions Configuration"/>
<field name="child_ids" nolabel="1" colspan="4" /> <field name="child_ids" nolabel="1" colspan="4"/>
<label colspan="4" string="Only one client action will be execute, last clinent action will be consider in case of multipls clinets actions" align="0.0"/>
</page> </page>
</notebook> </notebook>
<field name="usage"/> <field name="type" readonly="1"/>
<field name="type"/>
</form> </form>
</field> </field>
</record> </record>
@ -1219,7 +1227,7 @@
<field name="view_id" ref="view_server_action_tree"/> <field name="view_id" ref="view_server_action_tree"/>
<field name="context">{'key':'server_action'}</field> <field name="context">{'key':'server_action'}</field>
</record> </record>
<menuitem action="action_server_action" id="menu_server_action" parent="base.next_id_6"/> <menuitem action="action_server_action" id="menu_server_action" parent="base.menu_custom_action"/>
<record id="view_model_fields_tree" model="ir.ui.view"> <record id="view_model_fields_tree" model="ir.ui.view">
<field name="name">ir.model.fields.tree</field> <field name="name">ir.model.fields.tree</field>
@ -1279,7 +1287,8 @@
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="domain">[('type','=','configure')]</field> <field name="domain">[('type','=','configure')]</field>
</record> </record>
<menuitem id="next_id_11" name="Configuration Wizards" parent="base.menu_custom"/><menuitem action="act_ir_actions_todo_form" id="menu_ir_actions_todo_form" parent="next_id_11"/> <menuitem id="next_id_11" name="Configuration Wizards" parent="base.menu_config" sequence="1"/>
<menuitem action="act_ir_actions_todo_form" id="menu_ir_actions_todo_form" parent="next_id_11"/>
<record id="view_config_wizard_form" model="ir.ui.view"> <record id="view_config_wizard_form" model="ir.ui.view">
<field name="name">Main Configuration Wizard</field> <field name="name">Main Configuration Wizard</field>

View File

@ -34,7 +34,6 @@ class actions(osv.osv):
'name': fields.char('Action Name', required=True, size=64), 'name': fields.char('Action Name', required=True, size=64),
'type': fields.char('Action Type', required=True, size=32), 'type': fields.char('Action Type', required=True, size=32),
'usage': fields.char('Action Usage', size=32), 'usage': fields.char('Action Usage', size=32),
'parent_id': fields.many2one('ir.actions.server', 'Parent Action'),
} }
_defaults = { _defaults = {
'usage': lambda *a: False, 'usage': lambda *a: False,
@ -291,7 +290,7 @@ class ir_model_fields(osv.osv):
'complete_name': fields.char('Complete Name', size=64, select=1), 'complete_name': fields.char('Complete Name', size=64, select=1),
} }
def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=80): def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=800):
def get_fields(cr, uid, field, rel): def get_fields(cr, uid, field, rel):
result = [] result = []
mobj = self.pool.get('ir.model') mobj = self.pool.get('ir.model')
@ -366,6 +365,23 @@ server_object_lines()
# Actions that are run on the server side # Actions that are run on the server side
# #
class actions_server(osv.osv): class actions_server(osv.osv):
def _select_signals(self, cr, uid, context={}):
cr.execute("select distinct t.signal as key, t.signal || ' - [ ' || w.osv || ' ] ' as val from wkf w, wkf_activity a, wkf_transition t "\
" where w.id = a.wkf_id " \
" and t.act_from = a.wkf_id " \
" or t.act_to = a.wkf_id ")
return cr.fetchall()
def on_trigger_obj_id(self, cr, uid, ids, context={}):
cr.execute("select distinct t.signal as key, t.signal as val from wkf w, wkf_activity a, wkf_transition t "\
" where w.id = a.wkf_id " \
" and t.act_from = a.wkf_id " \
" or t.act_to = a.wkf_id " \
" and w.osv = %s ", ('account.invoice'))
data = cr.fetchall()
return {"values":{'trigger_name':data}}
_name = 'ir.actions.server' _name = 'ir.actions.server'
_table = 'ir_act_server' _table = 'ir_act_server'
_sequence = 'ir_actions_id_seq' _sequence = 'ir_actions_id_seq'
@ -373,33 +389,31 @@ class actions_server(osv.osv):
'name': fields.char('Action Name', required=True, size=64), 'name': fields.char('Action Name', required=True, size=64),
'state': fields.selection([ 'state': fields.selection([
('client_action','Client Action'), ('client_action','Client Action'),
('python','Python Code'),
('dummy','Dummy'), ('dummy','Dummy'),
('trigger','Trigger'), ('trigger','Trigger'),
('email','Email'), ('email','Email'),
('sms','SMS'), ('sms','SMS'),
('object_create','Create Object'), ('object_create','Create Object'),
('object_write','Write Object'), ('object_write','Write Object'),
('other','Others Actions'), ('other','Multi Actions'),
], 'Action State', required=True, size=32), ], 'Action State', required=True, size=32),
'code': fields.text('Python Code'), 'code': fields.text('Python Code'),
'sequence': fields.integer('Sequence'), 'sequence': fields.integer('Sequence'),
'model_id': fields.many2one('ir.model', 'Object', required=True), 'model_id': fields.many2one('ir.model', 'Object', required=True),
'action_id': fields.many2one('ir.actions.actions', 'Client Action'), 'action_id': fields.many2one('ir.actions.actions', 'Client Action'),
'trigger_name': fields.char('Trigger Name', size=128), 'trigger_name': fields.selection(_select_signals, string='Trigger Name', size=128),
'trigger_obj_id': fields.reference('Trigger On', selection=model_get, size=128), 'wkf_model_id': fields.many2one('ir.model', 'Workflow on'),
'trigger_obj_id': fields.many2one('ir.model.fields','Trigger On'),
'email': fields.many2one('ir.model.fields', 'Contact'),
'message': fields.text('Message', translate=True), 'message': fields.text('Message', translate=True),
'address': fields.many2one('ir.model.fields', 'Email / Mobile'), 'mobile': fields.many2one('ir.model.fields', 'Contact'),
'sms': fields.char('SMS', size=160, translate=True), 'sms': fields.char('SMS', size=160, translate=True),
'child_ids': fields.one2many('ir.actions.actions', 'parent_id', 'Others Actions'), 'child_ids': fields.many2many('ir.actions.server', 'rel_server_actions', 'server_id', 'action_id', 'Others Actions'),
'usage': fields.char('Action Usage', size=32), 'usage': fields.char('Action Usage', size=32),
'type': fields.char('Report Type', size=32, required=True), 'type': fields.char('Action Type', size=32, required=True),
'srcmodel_id': fields.many2one('ir.model', 'Model'), 'srcmodel_id': fields.many2one('ir.model', 'Model', help="In which object you want to create / write the object if its empty refer to the Object field"),
'fields_lines': fields.one2many('ir.server.object.lines', 'server_id', 'Fields Mapping'), 'fields_lines': fields.one2many('ir.server.object.lines', 'server_id', 'Fields Mapping'),
'otype': fields.selection([ 'record_id':fields.many2one('ir.model.fields', 'Record Id', help="privide the field name from where the record id refers, if its empty it will refer to the active id of the object")
('copy','Create in Same Model'),
('new','Create in Other Model')
], 'Create Model', size=32, change_default=True),
} }
_defaults = { _defaults = {
'state': lambda *a: 'dummy', 'state': lambda *a: 'dummy',
@ -414,26 +428,49 @@ class actions_server(osv.osv):
# - ids # - ids
# If you plan to return an action, assign: action = {...} # If you plan to return an action, assign: action = {...}
""", """,
'otype': lambda *a: 'copy',
} }
def get_field_value(self, cr, uid, action, context):
def get_email(self, cr, uid, action, context):
logger = netsvc.Logger()
obj_pool = self.pool.get(action.model_id.model) obj_pool = self.pool.get(action.model_id.model)
id = context.get('active_id') id = context.get('active_id')
obj = obj_pool.browse(cr, uid, id) obj = obj_pool.browse(cr, uid, id)
fields = None fields = None
if '/' in action.address.complete_name: if '/' in action.email.complete_name:
fields = action.address.complete_name.split('/') fields = action.email.complete_name.split('/')
elif '.' in action.address.complete_name: elif '.' in action.email.complete_name:
fields = action.address.complete_name.split('.') fields = action.email.complete_name.split('.')
for field in fields: for field in fields:
try: try:
obj = getattr(obj, field) obj = getattr(obj, field)
except Exception,e : except Exception,e :
logger.notifyChannel('Workflow', netsvc.LOG_ERROR, 'Failed to parse : %s' % (match.group())) logger.notifyChannel('Workflow', netsvc.LOG_ERROR, 'Failed to parse : %s' % (field))
return obj
def get_mobile(self, cr, uid, action, context):
logger = netsvc.Logger()
obj_pool = self.pool.get(action.model_id.model)
id = context.get('active_id')
obj = obj_pool.browse(cr, uid, id)
fields = None
if '/' in action.mobile.complete_name:
fields = action.mobile.complete_name.split('/')
elif '.' in action.mobile.complete_name:
fields = action.mobile.complete_name.split('.')
for field in fields:
try:
obj = getattr(obj, field)
except Exception,e :
logger.notifyChannel('Workflow', netsvc.LOG_ERROR, 'Failed to parse : %s' % (field))
return obj return obj
@ -443,13 +480,17 @@ class actions_server(osv.osv):
obj_pool = self.pool.get(action.model_id.model) obj_pool = self.pool.get(action.model_id.model)
id = context.get('active_id') id = context.get('active_id')
obj = obj_pool.browse(cr, uid, id) obj = obj_pool.browse(cr, uid, id)
return eval(match[2:-2], {'object':obj, 'context': context,'time':time}) exp = str(match.group()[2:-2]).strip()
result = eval(exp, {'object':obj, 'context': context,'time':time})
if result in (None, False):
return str("--------")
return str(result)
com = re.compile('(\[\[.+?\]\])') com = re.compile('(\[\[.+?\]\])')
message = com.sub(merge, keystr) message = com.sub(merge, keystr)
return message return message
#
# Context should contains: # Context should contains:
# ids : original ids # ids : original ids
# id : current id of the object # id : current id of the object
@ -459,10 +500,12 @@ class actions_server(osv.osv):
def run(self, cr, uid, ids, context={}): def run(self, cr, uid, ids, context={}):
logger = netsvc.Logger() logger = netsvc.Logger()
for action in self.browse(cr, uid, ids, context): for action in self.browse(cr, uid, ids, context):
if action.state=='client_action': if action.state=='client_action':
if not action.action_id: if not action.action_id:
raise osv.except_osv(_('Error'), _("Please specify an action to launch !")) raise osv.except_osv(_('Error'), _("Please specify an action to launch !"))
return self.pool.get(action.action_id.type).read(cr, uid, action.action_id.id, context=context) return self.pool.get(action.action_id.type).read(cr, uid, action.action_id.id, context=context)
if action.state=='python': if action.state=='python':
localdict = { localdict = {
'self': self.pool.get(action.model_id.model), 'self': self.pool.get(action.model_id.model),
@ -479,20 +522,24 @@ class actions_server(osv.osv):
if action.state == 'email': if action.state == 'email':
user = config['email_from'] user = config['email_from']
subject = action.name subject = action.name
address = self.get_field_value(cr, uid, action, context) address = self.get_email(cr, uid, action, context)
if not address: if not address:
raise osv.except_osv(_('Error'), _("Please specify the Partner Email address !")) raise osv.except_osv(_('Error'), _("Please specify the Partner Email address !"))
if not user:
raise osv.except_osv(_('Error'), _("Please specify server option --smtp-from !"))
body = self.merge_message(cr, uid, str(action.message), action, context) body = self.merge_message(cr, uid, str(action.message), action, context)
if tools.email_send_attach(user, address, subject, body, debug=False) == True: if tools.email_send(user, [address], subject, body, debug=False, subtype='html') == True:
logger.notifyChannel('email', netsvc.LOG_INFO, 'Email successfully send to : %s' % (address)) logger.notifyChannel('email', netsvc.LOG_INFO, 'Email successfully send to : %s' % (address))
else: else:
logger.notifyChannel('email', netsvc.LOG_ERROR, 'Failed to send email to : %s' % (address)) logger.notifyChannel('email', netsvc.LOG_ERROR, 'Failed to send email to : %s' % (address))
if action.state == 'trigger': if action.state == 'trigger':
wf_service = netsvc.LocalService("workflow") wf_service = netsvc.LocalService("workflow")
res = str(action.trigger_obj_id).split(',') model = action.wkf_model_id.model
model = res[0] obj_pool = self.pool.get(action.model_id.model)
id = res[1] res_id = self.pool.get(action.model_id.model).read(cr, uid, [context.get('active_id')], [action.trigger_obj_id.name])
id = res_id [0][action.trigger_obj_id.name]
wf_service.trg_validate(uid, model, int(id), action.trigger_name, cr) wf_service.trg_validate(uid, model, int(id), action.trigger_name, cr)
if action.state == 'sms': if action.state == 'sms':
@ -500,27 +547,20 @@ class actions_server(osv.osv):
# for the sms gateway user / password # for the sms gateway user / password
api_id = '' api_id = ''
text = action.sms text = action.sms
to = self.get_field_value(cr, uid, action, context) to = self.get_mobile(cr, uid, action, context)
#TODO: Apply message mearge with the field #TODO: Apply message mearge with the field
if tools.sms_send(user, password, api_id, text, to) == True: if tools.sms_send(user, password, api_id, text, to) == True:
logger.notifyChannel('sms', netsvc.LOG_INFO, 'SMS successfully send to : %s' % (action.address)) logger.notifyChannel('sms', netsvc.LOG_INFO, 'SMS successfully send to : %s' % (action.address))
else: else:
logger.notifyChannel('sms', netsvc.LOG_ERROR, 'Failed to send SMS to : %s' % (action.address)) logger.notifyChannel('sms', netsvc.LOG_ERROR, 'Failed to send SMS to : %s' % (action.address))
if action.state == 'other': if action.state == 'other':
localdict = { res = None
'self': self.pool.get(action.model_id.model),
'context': context,
'time': time,
'ids': ids,
'cr': cr,
'uid': uid
}
for act in action.child_ids: for act in action.child_ids:
code = """action = {'model':'%s','type':'%s', %s}""" % (action.model_id.model, act.type, act.usage) result = self.run(cr, uid, [act.id], context)
exec code in localdict if result:
if 'action' in localdict: res = result
return localdict['action'] return res
if action.state == 'object_write': if action.state == 'object_write':
res = {} res = {}
@ -533,9 +573,19 @@ class actions_server(osv.osv):
else: else:
expr = exp.value expr = exp.value
res[exp.col1.name] = expr res[exp.col1.name] = expr
obj_pool = self.pool.get(action.model_id.model)
obj_pool.write(cr, uid, [context.get('active_id')], res)
if not action.record_id:
if not action.srcmodel_id:
obj_pool = self.pool.get(action.model_id.model)
obj_pool.write(cr, uid, [context.get('active_id')], res)
else:
obj_pool = self.pool.get(action.srcmodel_id.model)
obj_pool.write(cr, uid, [context.get('active_id')], res)
else:
obj_pool = self.pool.get(action.srcmodel_id.model)
id = self.pool.get(action.model_id.model).read(cr, uid, [context.get('active_id')], [action.record_id.name])
obj_pool.write(cr, uid, [int(id[0][action.record_id.name])], res)
if action.state == 'object_create': if action.state == 'object_create':
res = {} res = {}
for exp in action.fields_lines: for exp in action.fields_lines:
@ -549,13 +599,10 @@ class actions_server(osv.osv):
res[exp.col1.name] = expr res[exp.col1.name] = expr
obj_pool = None obj_pool = None
if action.state == 'object_create' and action.otype == 'new': res_id = False
obj_pool = self.pool.get(action.srcmodel_id.model) obj_pool = self.pool.get(action.srcmodel_id.model)
obj_pool.create(cr, uid, res) res_id = obj_pool.create(cr, uid, res)
else: self.pool.get(action.model_id.model).write(cr, uid, [context.get('active_id')], {action.record_id.name:res_id})
obj_pool = self.pool.get(action.model_id.model)
id = context.get('active_id')
obj_pool.copy(cr, uid, id, res)
return False return False
actions_server() actions_server()

View File

@ -25,20 +25,16 @@ from osv.orm import except_orm
import tools import tools
class ir_attachment(osv.osv): class ir_attachment(osv.osv):
def check(self, cr, uid, ids, mode): def check(self, cr, uid, ids, mode):
if not ids: if not ids:
return return
ima = self.pool.get('ir.model.access') ima = self.pool.get('ir.model.access')
if isinstance(ids, (int, long)): if isinstance(ids, (int, long)):
ids = [ids] ids = [ids]
objs = self.browse(cr, uid, ids) or [] cr.execute('select distinct res_model from ir_attachment where id in ('+','.join(map(str, ids))+')')
for o in objs: for obj in cr.fetchall():
if o and o.res_model: ima.check(cr, uid, obj[0], mode)
ima.check(cr, uid, o.res_model, mode)
check = tools.cache()(check)
def search(self, cr, uid, args, offset=0, limit=None, order=None, def search(self, cr, uid, args, offset=0, limit=None, order=None,
context=None, count=False): context=None, count=False):
ids = super(ir_attachment, self).search(cr, uid, args, offset=offset, ids = super(ir_attachment, self).search(cr, uid, args, offset=offset,
@ -83,24 +79,12 @@ class ir_attachment(osv.osv):
self.pool.get('ir.model.access').check(cr, uid, values['res_model'], 'create') self.pool.get('ir.model.access').check(cr, uid, values['res_model'], 'create')
return super(ir_attachment, self).create(cr, uid, values, *args, **kwargs) return super(ir_attachment, self).create(cr, uid, values, *args, **kwargs)
def clear_cache(self):
self.check()
def action_get(self, cr, uid, context=None): def action_get(self, cr, uid, context=None):
dataobj = self.pool.get('ir.model.data') dataobj = self.pool.get('ir.model.data')
data_id = dataobj._get_id(cr, 1, 'base', 'action_attachment') data_id = dataobj._get_id(cr, 1, 'base', 'action_attachment')
res_id = dataobj.browse(cr, uid, data_id, context).res_id res_id = dataobj.browse(cr, uid, data_id, context).res_id
return self.pool.get('ir.actions.act_window').read(cr, uid, res_id, [], context) return self.pool.get('ir.actions.act_window').read(cr, uid, res_id, [], context)
def __init__(self, *args, **kwargs):
r = super(ir_attachment, self).__init__(*args, **kwargs)
self.pool.get('ir.model.access').register_cache_clearing_method(self._name, 'clear_cache')
return r
def __del__(self):
self.pool.get('ir.model.access').unregister_cache_clearing_method(self._name, 'clear_cache')
return super(ir_attachment, self).__del__()
def _get_preview(self, cr, uid, ids, name, arg, context=None): def _get_preview(self, cr, uid, ids, name, arg, context=None):
result = {} result = {}
if context is None: if context is None:

View File

@ -101,7 +101,7 @@ class ir_cron(osv.osv, netsvc.Agent):
addsql='' addsql=''
if not numbercall: if not numbercall:
addsql = ', active=False' addsql = ', active=False'
cr.execute("update ir_cron set nextcall=%s, numbercall=%d"+addsql+" where id=%d", (nextcall.strftime('%Y-%m-%d %H:%M:%S'), numbercall, job['id'])) cr.execute("update ir_cron set nextcall=%s, numbercall=%s"+addsql+" where id=%s", (nextcall.strftime('%Y-%m-%d %H:%M:%S'), numbercall, job['id']))
cr.commit() cr.commit()
finally: finally:
cr.close() cr.close()

View File

@ -193,7 +193,9 @@ class ir_model_fields(osv.osv):
_columns = { _columns = {
'name': fields.char('Name', required=True, size=64, select=1), 'name': fields.char('Name', required=True, size=64, select=1),
'model': fields.char('Object Name', size=64, required=True), 'model': fields.char('Object Name', size=64, required=True),
'relation_id':fields.many2one('ir.model', 'Object Relation'),
'relation': fields.char('Object Relation', size=64), 'relation': fields.char('Object Relation', size=64),
'relation_field_id':fields.many2one('ir.model.fields', 'Relation Field'),
'relation_field': fields.char('Relation Field', size=64), 'relation_field': fields.char('Relation Field', size=64),
'model_id': fields.many2one('ir.model', 'Object id', required=True, select=True, ondelete='cascade'), 'model_id': fields.many2one('ir.model', 'Object id', required=True, select=True, ondelete='cascade'),
'field_description': fields.char('Field Label', required=True, size=256), 'field_description': fields.char('Field Label', required=True, size=256),
@ -231,8 +233,26 @@ class ir_model_fields(osv.osv):
# MAY BE ADD A ALTER TABLE DROP ? # MAY BE ADD A ALTER TABLE DROP ?
# #
return super(ir_model_fields, self).unlink(cr, user, ids, context) return super(ir_model_fields, self).unlink(cr, user, ids, context)
def write(self, cr, uid, ids, vals, context=None):
res = False
if 'relation_id' in vals:
model_data = self.pool.get('ir.model').browse(cr, uid, vals['relation_id'])
vals['relation'] = model_data.model
if 'relation_field_id' in vals:
field_data = self.pool.get('ir.model.fields').browse(cr, uid, vals['relation_field_id'])
vals['relation'] = field_data.name
res = super(ir_model_fields, self).write(cr, uid, ids, vals, context)
return res
def create(self, cr, user, vals, context=None): def create(self, cr, user, vals, context=None):
if 'relation_id' in vals:
model_data = self.pool.get('ir.model').browse(cr,user,vals['relation_id'])
vals['relation']=model_data.model
if 'relation_field_id' in vals:
field_data = self.pool.get('ir.model.fields').browse(cr, uid, vals['relation_field_id'])
vals['relation_field'] = field_data.name
if 'model_id' in vals: if 'model_id' in vals:
model_data=self.pool.get('ir.model').browse(cr,user,vals['model_id']) model_data=self.pool.get('ir.model').browse(cr,user,vals['model_id'])
vals['model']=model_data.model vals['model']=model_data.model
@ -266,7 +286,7 @@ class ir_model_access(osv.osv):
if not grouparr: if not grouparr:
return False return False
cr.execute("select 1 from res_groups_users_rel where uid=%d and gid in(select res_id from ir_model_data where module=%s and name=%s)", (uid, grouparr[0], grouparr[1],)) cr.execute("select 1 from res_groups_users_rel where uid=%s and gid in(select res_id from ir_model_data where module=%s and name=%s)", (uid, grouparr[0], grouparr[1],))
return bool(cr.fetchone()) return bool(cr.fetchone())
def check_group(self, cr, uid, model, mode, group_ids): def check_group(self, cr, uid, model, mode, group_ids):
@ -285,7 +305,7 @@ class ir_model_access(osv.osv):
cr.execute("SELECT perm_" + mode + " " cr.execute("SELECT perm_" + mode + " "
" FROM ir_model_access a " " FROM ir_model_access a "
" JOIN ir_model m ON (m.id = a.model_id) " " JOIN ir_model m ON (m.id = a.model_id) "
" WHERE m.model = %s AND a.group_id = %d", (model_name, group_id) " WHERE m.model = %s AND a.group_id = %s", (model_name, group_id)
) )
r = cr.fetchone() r = cr.fetchone()
if r is None: if r is None:
@ -415,7 +435,7 @@ class ir_model_data(osv.osv):
ids = self.search(cr, uid, [('module','=',module),('name','=', xml_id)]) ids = self.search(cr, uid, [('module','=',module),('name','=', xml_id)])
assert len(ids)==1, '%d reference(s) to %s.%s. You should have one and only one !' % (len(ids), module, xml_id) assert len(ids)==1, '%d reference(s) to %s.%s. You should have one and only one !' % (len(ids), module, xml_id)
return ids[0] return ids[0]
_get_id = tools.cache()(_get_id) _get_id = tools.cache(skiparg=2)(_get_id)
def _update_dummy(self,cr, uid, model, module, xml_id=False, store=True): def _update_dummy(self,cr, uid, model, module, xml_id=False, store=True):
if not xml_id: if not xml_id:
@ -442,10 +462,10 @@ class ir_model_data(osv.osv):
cr.execute('select id,res_id from ir_model_data where module=%s and name=%s', (module,xml_id)) cr.execute('select id,res_id from ir_model_data where module=%s and name=%s', (module,xml_id))
results = cr.fetchall() results = cr.fetchall()
for action_id2,res_id2 in results: for action_id2,res_id2 in results:
cr.execute('select id from '+self.pool.get(model)._table+' where id=%d', (res_id2,)) cr.execute('select id from '+self.pool.get(model)._table+' where id=%s', (res_id2,))
result3 = cr.fetchone() result3 = cr.fetchone()
if not result3: if not result3:
cr.execute('delete from ir_model_data where id=%d', (action_id2,)) cr.execute('delete from ir_model_data where id=%s', (action_id2,))
else: else:
res_id,action_id = res_id2,action_id2 res_id,action_id = res_id2,action_id2
@ -513,7 +533,7 @@ class ir_model_data(osv.osv):
#self.pool.get(model).unlink(cr, uid, ids) #self.pool.get(model).unlink(cr, uid, ids)
for id in ids: for id in ids:
self.unlink_mark[(model, id)]=False self.unlink_mark[(model, id)]=False
cr.execute('delete from ir_model_data where res_id=%d and model=\'%s\'', (id,model)) cr.execute('delete from ir_model_data where res_id=%s and model=\'%s\'', (id,model))
return True return True
def ir_set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=None, xml_id=False): def ir_set(self, cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=None, xml_id=False):
@ -525,7 +545,7 @@ class ir_model_data(osv.osv):
model = models[0] model = models[0]
if res_id: if res_id:
where = ' and res_id=%d' % (res_id,) where = ' and res_id=%s' % (res_id,)
else: else:
where = ' and (res_id is null)' where = ' and (res_id is null)'
@ -552,10 +572,10 @@ class ir_model_data(osv.osv):
if (module,name) not in self.loads: if (module,name) not in self.loads:
self.unlink_mark[(model,res_id)] = id self.unlink_mark[(model,res_id)] = id
if model=='workflow.activity': if model=='workflow.activity':
cr.execute('select res_type,res_id from wkf_instance where id in (select inst_id from wkf_workitem where act_id=%d)', (res_id,)) cr.execute('select res_type,res_id from wkf_instance where id in (select inst_id from wkf_workitem where act_id=%s)', (res_id,))
wkf_todo.extend(cr.fetchall()) wkf_todo.extend(cr.fetchall())
cr.execute("update wkf_transition set condition='True', role_id=NULL, signal=NULL,act_to=act_from,act_from=%d where act_to=%d", (res_id,res_id)) cr.execute("update wkf_transition set condition='True', role_id=NULL, signal=NULL,act_to=act_from,act_from=%s where act_to=%s", (res_id,res_id))
cr.execute("delete from wkf_transition where act_to=%d", (res_id,)) cr.execute("delete from wkf_transition where act_to=%s", (res_id,))
for model,id in wkf_todo: for model,id in wkf_todo:
wf_service = netsvc.LocalService("workflow") wf_service = netsvc.LocalService("workflow")

View File

@ -25,6 +25,7 @@ from osv.orm import browse_null
import ir import ir
import report.custom import report.custom
from tools.translate import _ from tools.translate import _
import netsvc
class report_custom(osv.osv): class report_custom(osv.osv):
_name = 'ir.report.custom' _name = 'ir.report.custom'
@ -187,7 +188,7 @@ class report_custom_fields(osv.osv):
} }
} }
else: else:
print _("Warning: using a relation field which uses an unknown object") #TODO use the logger netsvc.Logger().notifyChannel('web-services', netsvc.LOG_WARNING, _("Using a relation field which uses an unknown object"))
return {'required': {next_level_field_name: True}} return {'required': {next_level_field_name: True}}
else: else:
return {'domain': {next_level_field_name: []}} return {'domain': {next_level_field_name: []}}

View File

@ -148,7 +148,7 @@ class ir_rule(osv.osv):
WHERE m.model = %s WHERE m.model = %s
AND (g.id IN (SELECT rule_group_id FROM group_rule_group_rel g_rel AND (g.id IN (SELECT rule_group_id FROM group_rule_group_rel g_rel
JOIN res_groups_users_rel u_rel ON (g_rel.group_id = u_rel.gid) JOIN res_groups_users_rel u_rel ON (g_rel.group_id = u_rel.gid)
WHERE u_rel.uid = %d) OR g.global)""", (model_name, uid)) WHERE u_rel.uid = %s) OR g.global)""", (model_name, uid))
ids = map(lambda x:x[0], cr.fetchall()) ids = map(lambda x:x[0], cr.fetchall())
if not ids: if not ids:
return '', [] return '', []

View File

@ -69,11 +69,11 @@ class ir_sequence(osv.osv):
'sec': time.strftime('%S'), 'sec': time.strftime('%S'),
} }
def get_id(self, cr, uid, sequence_id, test='id=%d'): def get_id(self, cr, uid, sequence_id, test='id=%s'):
cr.execute('select id,number_next,number_increment,prefix,suffix,padding from ir_sequence where '+test+' and active=True FOR UPDATE', (sequence_id,)) cr.execute('select id,number_next,number_increment,prefix,suffix,padding from ir_sequence where '+test+' and active=True FOR UPDATE', (sequence_id,))
res = cr.dictfetchone() res = cr.dictfetchone()
if res: if res:
cr.execute('update ir_sequence set number_next=number_next+number_increment where id=%d and active=True', (res['id'],)) cr.execute('update ir_sequence set number_next=number_next+number_increment where id=%s and active=True', (res['id'],))
if res['number_next']: if res['number_next']:
return self._process(res['prefix']) + '%%0%sd' % res['padding'] % res['number_next'] + self._process(res['suffix']) return self._process(res['prefix']) + '%%0%sd' % res['padding'] % res['number_next'] + self._process(res['suffix'])
else: else:

View File

@ -21,7 +21,6 @@
############################################################################## ##############################################################################
from osv import fields, osv from osv import fields, osv
from osv.osv import Cacheable
import tools import tools
TRANSLATION_TYPE = [ TRANSLATION_TYPE = [
@ -39,7 +38,7 @@ TRANSLATION_TYPE = [
('constraint', 'Constraint'), ('constraint', 'Constraint'),
] ]
class ir_translation(osv.osv, Cacheable): class ir_translation(osv.osv):
_name = "ir.translation" _name = "ir.translation"
_log_access = False _log_access = False
@ -48,7 +47,7 @@ class ir_translation(osv.osv, Cacheable):
lang_ids = lang_obj.search(cr, uid, [('translatable', '=', True)], lang_ids = lang_obj.search(cr, uid, [('translatable', '=', True)],
context=context) context=context)
langs = lang_obj.browse(cr, uid, lang_ids, context=context) langs = lang_obj.browse(cr, uid, lang_ids, context=context)
res = [(lang.code, unicode(lang.name,'utf-8')) for lang in langs] res = [(lang.code, lang.name) for lang in langs]
for lang_dict in tools.scan_languages(): for lang_dict in tools.scan_languages():
if lang_dict not in res: if lang_dict not in res:
res.append(lang_dict) res.append(lang_dict)
@ -81,27 +80,19 @@ class ir_translation(osv.osv, Cacheable):
cr.commit() cr.commit()
def _get_ids(self, cr, uid, name, tt, lang, ids): def _get_ids(self, cr, uid, name, tt, lang, ids):
translations, to_fetch = {}, [] translations = {}
for id in ids: if ids:
trans = self.get((lang, name, id))
if trans is not None:
translations[id] = trans
else:
to_fetch.append(id)
if to_fetch:
cr.execute('select res_id,value ' \ cr.execute('select res_id,value ' \
'from ir_translation ' \ 'from ir_translation ' \
'where lang=%s ' \ 'where lang=%s ' \
'and type=%s ' \ 'and type=%s ' \
'and name=%s ' \ 'and name=%s ' \
'and res_id in ('+','.join(map(str, to_fetch))+')', 'and res_id in ('+','.join(map(str, ids))+')',
(lang,tt,name)) (lang,tt,name))
for res_id, value in cr.fetchall(): for res_id, value in cr.fetchall():
self.add((lang, tt, name, res_id), value)
translations[res_id] = value translations[res_id] = value
for res_id in ids: for res_id in ids:
if res_id not in translations: if res_id not in translations:
self.add((lang, tt, name, res_id), False)
translations[res_id] = False translations[res_id] = False
return translations return translations
@ -122,11 +113,8 @@ class ir_translation(osv.osv, Cacheable):
}) })
return len(ids) return len(ids)
@tools.cache(skiparg=3)
def _get_source(self, cr, uid, name, tt, lang, source=None): def _get_source(self, cr, uid, name, tt, lang, source=None):
trans = self.get((lang, tt, name, source))
if trans is not None:
return trans
if source: if source:
#if isinstance(source, unicode): #if isinstance(source, unicode):
# source = source.encode('utf8') # source = source.encode('utf8')
@ -145,26 +133,9 @@ class ir_translation(osv.osv, Cacheable):
'and name=%s', 'and name=%s',
(lang, tt, str(name))) (lang, tt, str(name)))
res = cr.fetchone() res = cr.fetchone()
trad = res and res[0] or '' trad = res and res[0] or ''
self.add((lang, tt, name, source), trad)
return trad return trad
def unlink(self, cursor, user, ids, context=None):
self.clear()
return super(ir_translation, self).unlink(cursor, user, ids,
context=context)
def create(self, cursor, user, vals, context=None):
self.clear()
return super(ir_translation, self).create(cursor, user, vals,
context=context)
def write(self, cursor, user, ids, vals, context=None):
self.clear()
return super(ir_translation, self).write(cursor, user, ids, vals,
context=context)
ir_translation() ir_translation()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -41,7 +41,7 @@ class many2many_unique(fields.many2many):
for act in values: for act in values:
if act[0]==4: if act[0]==4:
cr.execute('SELECT * FROM '+self._rel+' \ cr.execute('SELECT * FROM '+self._rel+' \
WHERE '+self._id1+'=%d AND '+self._id2+'=%d', (id, act[1])) WHERE '+self._id1+'=%s AND '+self._id2+'=%s', (id, act[1]))
if cr.fetchall(): if cr.fetchall():
val.remove(act) val.remove(act)
return super(many2many_unique, self).set(cr, obj, id, name, val, user=user, return super(many2many_unique, self).set(cr, obj, id, name, val, user=user,

View File

@ -27,14 +27,13 @@ import netsvc
import os import os
def _check_xml(self, cr, uid, ids, context={}): def _check_xml(self, cr, uid, ids, context={}):
return True
for view in self.browse(cr, uid, ids, context): for view in self.browse(cr, uid, ids, context):
eview = etree.fromstring(view.arch) eview = etree.fromstring(view.arch)
frng = tools.file_open(os.path.join('base','rng','view.rng')) frng = tools.file_open(os.path.join('base','rng','view.rng'))
relaxng = etree.RelaxNG(file=frng) relaxng = etree.RelaxNG(file=frng)
if not relaxng.validate(eview): if not relaxng.validate(eview):
logger = netsvc.Logger() logger = netsvc.Logger()
logger.notifyChannel('init', netsvc.LOG_ERROR, 'The view do not fit the required schema !') logger.notifyChannel('init', netsvc.LOG_ERROR, 'The view does not fit the required schema !')
logger.notifyChannel('init', netsvc.LOG_ERROR, relaxng.error_log.last_error) logger.notifyChannel('init', netsvc.LOG_ERROR, relaxng.error_log.last_error)
return False return False
return True return True
@ -99,7 +98,7 @@ class view(osv.osv):
for rs in result: for rs in result:
if rs.get('model') == 'board.board': if rs.get('model') == 'board.board':
cr.execute("select id,arch,ref_id from ir_ui_view_custom where user_id=%d and ref_id=%d", (uid, rs['id'])) cr.execute("select id,arch,ref_id from ir_ui_view_custom where user_id=%s and ref_id=%s", (uid, rs['id']))
oview = cr.dictfetchall() oview = cr.dictfetchall()
if oview: if oview:
rs['arch'] = oview[0]['arch'] rs['arch'] = oview[0]['arch']

View File

@ -211,7 +211,7 @@ class ir_values(osv.osv):
#ir_del(cr, uid, x[0]) #ir_del(cr, uid, x[0])
return False return False
else: else:
datas = pickle.loads(x[2]) datas = pickle.loads(str(x[2]))
if meta: if meta:
meta2 = pickle.loads(x[4]) meta2 = pickle.loads(x[4])
return (x[0],x[1],datas,meta2) return (x[0],x[1],datas,meta2)

View File

@ -28,7 +28,7 @@ import report,pooler,tools
def graph_get(cr, graph, wkf_id, nested=False, workitem={}): def graph_get(cr, graph, wkf_id, nested=False, workitem={}):
import pydot import pydot
cr.execute('select * from wkf_activity where wkf_id=%d', (wkf_id,)) cr.execute('select * from wkf_activity where wkf_id=%s', (wkf_id,))
nodes = cr.dictfetchall() nodes = cr.dictfetchall()
activities = {} activities = {}
actfrom = {} actfrom = {}
@ -36,7 +36,7 @@ def graph_get(cr, graph, wkf_id, nested=False, workitem={}):
for n in nodes: for n in nodes:
activities[n['id']] = n activities[n['id']] = n
if n['subflow_id'] and nested: if n['subflow_id'] and nested:
cr.execute('select * from wkf where id=%d', (n['subflow_id'],)) cr.execute('select * from wkf where id=%s', (n['subflow_id'],))
wkfinfo = cr.dictfetchone() wkfinfo = cr.dictfetchone()
graph2 = pydot.Cluster('subflow'+str(n['subflow_id']), fontsize='12', label = """\"Subflow: %s\\nOSV: %s\"""" % ( n['name'], wkfinfo['osv']) ) graph2 = pydot.Cluster('subflow'+str(n['subflow_id']), fontsize='12', label = """\"Subflow: %s\\nOSV: %s\"""" % ( n['name'], wkfinfo['osv']) )
(s1,s2) = graph_get(cr, graph2, n['subflow_id'], nested,workitem) (s1,s2) = graph_get(cr, graph2, n['subflow_id'], nested,workitem)
@ -78,9 +78,9 @@ def graph_get(cr, graph, wkf_id, nested=False, workitem={}):
activity_to = actto[t['act_to']][1].get(t['signal'], actto[t['act_to']][0]) activity_to = actto[t['act_to']][1].get(t['signal'], actto[t['act_to']][0])
graph.add_edge(pydot.Edge( str(activity_from) ,str(activity_to), fontsize='10', **args)) graph.add_edge(pydot.Edge( str(activity_from) ,str(activity_to), fontsize='10', **args))
nodes = cr.dictfetchall() nodes = cr.dictfetchall()
cr.execute('select id from wkf_activity where flow_start=True and wkf_id=%d limit 1', (wkf_id,)) cr.execute('select id from wkf_activity where flow_start=True and wkf_id=%s limit 1', (wkf_id,))
start = cr.fetchone()[0] start = cr.fetchone()[0]
cr.execute("select 'subflow.'||name,id from wkf_activity where flow_stop=True and wkf_id=%d", (wkf_id,)) cr.execute("select 'subflow.'||name,id from wkf_activity where flow_stop=True and wkf_id=%s", (wkf_id,))
stop = cr.fetchall() stop = cr.fetchall()
stop = (stop[0][1], dict(stop)) stop = (stop[0][1], dict(stop))
return ((start,{}),stop) return ((start,{}),stop)
@ -88,14 +88,14 @@ def graph_get(cr, graph, wkf_id, nested=False, workitem={}):
def graph_instance_get(cr, graph, inst_id, nested=False): def graph_instance_get(cr, graph, inst_id, nested=False):
workitems = {} workitems = {}
cr.execute('select * from wkf_instance where id=%d', (inst_id,)) cr.execute('select * from wkf_instance where id=%s', (inst_id,))
inst = cr.dictfetchone() inst = cr.dictfetchone()
def workitem_get(instance): def workitem_get(instance):
cr.execute('select act_id,count(*) from wkf_workitem where inst_id=%d group by act_id', (instance,)) cr.execute('select act_id,count(*) from wkf_workitem where inst_id=%s group by act_id', (instance,))
workitems = dict(cr.fetchall()) workitems = dict(cr.fetchall())
cr.execute('select subflow_id from wkf_workitem where inst_id=%d', (instance,)) cr.execute('select subflow_id from wkf_workitem where inst_id=%s', (instance,))
for (subflow_id,) in cr.fetchall(): for (subflow_id,) in cr.fetchall():
workitems.update(workitem_get(subflow_id)) workitems.update(workitem_get(subflow_id))
return workitems return workitems
@ -130,7 +130,7 @@ class report_graph_instance(object):
showpage''' showpage'''
else: else:
cr.execute('SELECT id FROM wkf_instance \ cr.execute('SELECT id FROM wkf_instance \
WHERE res_id=%d AND wkf_id=%d \ WHERE res_id=%s AND wkf_id=%s \
ORDER BY state LIMIT 1', ORDER BY state LIMIT 1',
(data['id'], wkfinfo['id'])) (data['id'], wkfinfo['id']))
inst_id = cr.fetchone() inst_id = cr.fetchone()

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<openerp> <openerp>
<data> <data>
<menuitem id="menu_workflow_root" name="Workflows" parent="menu_custom"/> <menuitem id="menu_workflow_root" name="Workflow Definitions" parent="menu_custom"/>
<!-- <!--
================================ ================================
@ -110,7 +110,7 @@
</record> </record>
<record id="action_workflow_activity_form" model="ir.actions.act_window"> <record id="action_workflow_activity_form" model="ir.actions.act_window">
<field name="name">Activites</field> <field name="name">Activities</field>
<field name="res_model">workflow.activity</field> <field name="res_model">workflow.activity</field>
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="view_id" ref="view_workflow_activity_tree"/> <field name="view_id" ref="view_workflow_activity_tree"/>
@ -207,7 +207,7 @@
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="view_id" ref="view_workflow_instance_tree"/> <field name="view_id" ref="view_workflow_instance_tree"/>
</record> </record>
<menuitem action="action_workflow_instance_form" id="menu_workflow_instance" parent="base.menu_workflow_root"/> <menuitem action="action_workflow_instance_form" id="menu_workflow_instance" parent="base.menu_low_workflow"/>
<!-- <!--
================================ ================================
@ -248,7 +248,7 @@
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="view_id" ref="view_workflow_workitem_tree"/> <field name="view_id" ref="view_workflow_workitem_tree"/>
</record> </record>
<menuitem action="action_workflow_workitem_form" id="menu_workflow_workitem" parent="base.menu_workflow_root"/> <menuitem action="action_workflow_workitem_form" id="menu_workflow_workitem" parent="base.menu_low_workflow"/>
</data> </data>
</openerp> </openerp>

View File

@ -32,78 +32,46 @@ from osv import osv, fields
import pooler import pooler
import time import time
import math import math
from pprint import pprint as pp
from tools import config from tools import config
import xmlrpclib import xmlrpclib
class maintenance_contract_module(osv.osv):
_name ="maintenance.contract.module"
_description = "maintenance contract modules"
_columns = {
'name' : fields.char('Name', size=128, required=True),
'version': fields.char('Version', size=64,),
}
maintenance_contract_module()
class maintenance_contract(osv.osv): class maintenance_contract(osv.osv):
_name = "maintenance.contract" _name = "maintenance.contract"
_description = "Maintenance Contract" _description = "Maintenance Contract"
def _valid_get(self, cr, uid, ids, field_name, arg, context=None):
res = {}
for contract in self.browse(cr, uid, ids, context=context):
res[contract.id] = ("unvalid", "valid")[contract.date_stop >= time.strftime('%Y-%m-%d')]
return res
_columns = { _columns = {
'name' : fields.char('Contract ID', size=256, required=True, readonly=True), 'name' : fields.char('Contract ID', size=256, required=True, readonly=True),
'password' : fields.char('Password', size=64, invisible=True, required=True, readonly=True), 'password' : fields.char('Password', size=64, invisible=True, required=True, readonly=True),
'date_start' : fields.date('Starting Date', readonly=True), 'date_start' : fields.date('Starting Date', readonly=True),
'date_stop' : fields.date('Ending Date', readonly=True), 'date_stop' : fields.date('Ending Date', readonly=True),
'modules': fields.text('Covered Modules') 'module_ids' : fields.many2many('maintenance.contract.module', 'maintenance_contract_module_rel', 'contract_id', 'module_id', 'Covered Modules', readonly=True),
'state' : fields.function(_valid_get, method=True, string="State", type="selection", selection=[('valid', 'Valid'),('unvalid', 'Unvalid')], readonly=True),
'kind' : fields.selection([('full', 'Full'),('partial', 'Partial')], 'Kind', required=True, readonly=True),
} }
_defaults = { _defaults = {
'password' : lambda obj,cr,uid,context={} : '', 'password' : lambda obj,cr,uid,context={} : '',
} }
def _test_maintenance(self, cr, uid, ids, context): _sql_constraints = [
remote_db='trunk' ('uniq_name', 'unique(name)', "Your maintenance contract is already subscribed in the system !")
remote_server='localhost' ]
port=8069
module_obj=self.pool.get('ir.module.module')
contract_obj=self.pool.get('maintenance.contract')
module_ids=module_obj.search(cr, uid, [('state','=','installed')])
modules=module_obj.read(cr, uid, module_ids, ['name','installed_version'])
contract_obj=contract_obj.read(cr, uid, ids[0])
local_url = 'http://%s:%d/xmlrpc/common'%(remote_server,port)
rpc = xmlrpclib.ServerProxy(local_url)
ruid = rpc.login(remote_db, 'admin', 'admin')
local_url = 'http://%s:%d/xmlrpc/object'%(remote_server,port)
rrpc = xmlrpclib.ServerProxy(local_url)
try:
result=rrpc.execute(remote_db, ruid, 'admin', 'maintenance.maintenance', 'check_contract' , modules,contract_obj)
except:
raise osv.except_osv(_('Maintenance Error !'),('''Module Maintenance_Editor is not installed at server : %s Database : %s'''%(remote_server,remote_db)))
if context.get('active_id',False):
if result['status']=='ko':
raise osv.except_osv(_('Maintenance Error !'),('''Maintenance Contract
-----------------------------------------------------------
You have no valid maintenance contract! If you are using
Open ERP, it is highly suggested to take maintenance contract.
The maintenance program offers you:
* Migrations on new versions,
* Bugfix guarantee,
* Monthly announces of bugs,
* Security alerts,
* Access to the customer portal.
* Check the maintenance contract (www.openerp.com)'''))
elif result['status']=='partial':
raise osv.except_osv(_('Maintenance Error !'),('''Maintenance Contract
-----------------------------------------------------------
You have a maintenance contract, But you installed modules those
are not covered by your maintenance contract:
%s
It means we can not offer you the garantee of maintenance on
your whole installation.
The maintenance program includes:
* Migrations on new versions,
* Bugfix guarantee,
* Monthly announces of bugs,
* Security alerts,
* Access to the customer portal.
To include these modules in your maintenance contract, you should
extend your contract with the editor. We will review and validate
your installed modules.
* Extend your maintenance to the modules you used.
* Check your maintenance contract''') % ','.join(result['modules']))
else:
raise osv.except_osv(_('Valid Maintenance Contract !'),('''Your Maintenance Contract is up to date'''))
return result
maintenance_contract() maintenance_contract()
@ -114,11 +82,55 @@ class maintenance_contract_wizard(osv.osv_memory):
_columns = { _columns = {
'name' : fields.char('Contract ID', size=256, required=True ), 'name' : fields.char('Contract ID', size=256, required=True ),
'password' : fields.char('Password', size=64, required=True), 'password' : fields.char('Password', size=64, required=True),
'state' : fields.selection([('draft', 'Draft'),('validated', 'Validated'),('unvalidated', 'Unvalidated')], 'States'),
} }
def validate_cb(self, cr, uid, ids, context): _defaults = {
print "Validate_cb" 'state' : lambda *a: 'draft',
return False }
def action_validate(self, cr, uid, ids, context):
if not ids:
return False
module_proxy = self.pool.get('ir.module.module')
module_ids = module_proxy.search(cr, uid, [('state', '=', 'installed')])
modules = module_proxy.read(cr, uid, module_ids, ['name', 'installed_version'])
contract = self.read(cr, uid, ids, ['name', 'password'])[0]
login, password, remote_db, remote_server, port = 'admin', 'admin', 'trunk', 'localhost', 8069
rpc = xmlrpclib.ServerProxy('http://%s:%d/xmlrpc/common' % (remote_server, port))
ruid = rpc.login(remote_db, login, password)
rpc = xmlrpclib.ServerProxy('http://%s:%d/xmlrpc/object' % (remote_server, port))
contract_info = rpc.execute(remote_db, ruid, password, 'maintenance.maintenance', 'check_contract', modules, contract )
is_ok = contract_info['status'] in ('partial', 'full')
if is_ok:
if contract_info['modules_with_contract']:
module_ids = []
for name, version in contract_info['modules_with_contract']:
contract_module = self.pool.get('maintenance.contract.module')
res = contract_module.search(cr, uid, [('name', '=', name),('version', '=', version)])
if not res:
id = contract_module.create(cr, uid, { 'name' : name, 'version' : version } )
else:
id = res[0]
module_ids.append(id)
self.pool.get('maintenance.contract').create(
cr,
uid, {
'name' : contract['name'],
'password' : contract['password'],
'date_start' : contract_info['date_from'],
'date_stop' : contract_info['date_to'],
'kind' : contract_info['status'],
'module_ids' : [(6,0,module_ids)],
}
)
return self.write(cr, uid, ids, {'state' : ('unvalidated', 'validated')[is_ok] }, context=context)
maintenance_contract_wizard() maintenance_contract_wizard()

View File

@ -11,6 +11,7 @@
<field name="name"/> <field name="name"/>
<field name="date_start"/> <field name="date_start"/>
<field name="date_stop"/> <field name="date_stop"/>
<field name="state" />
</tree> </tree>
</field> </field>
</record> </record>
@ -22,11 +23,17 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Maintenance Contract"> <form string="Maintenance Contract">
<separator string="Information" colspan="4"/> <separator string="Information" colspan="4"/>
<field name="name" select="1" colspan="4"/> <field name="name" select="1" />
<field name="state" />
<field name="date_start" select="1"/> <field name="date_start" select="1"/>
<field name="date_stop" select="1"/> <field name="date_stop" select="1"/>
<separator string="Covered Modules" colspan="4"/> <separator string="Covered Modules" colspan="4"/>
<field name="modules" nolabel="1" colspan="4"/> <field name="module_ids" nolabel="1" colspan="4">
<tree string="Covered Modules">
<field name="name" />
<field name="version" />
</tree>
</field>
</form> </form>
</field> </field>
</record> </record>
@ -54,14 +61,28 @@
<field name="model">maintenance.contract.wizard</field> <field name="model">maintenance.contract.wizard</field>
<field name="type">form</field> <field name="type">form</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Add Maintenance Contract"> <form string="Add Maintenance Contract" col="2">
<separator string="Add Maintenance Contract" colspan="2"/> <image name="gtk-dialog-info" />
<newline /> <group col="1">
<field name="name" /> <separator string="Add Maintenance Contract" />
<newline /> <group states="draft">
<field name="password" password="True" /> <field name="name" width="250" />
<newline /> <newline />
<label string="" /><button name="validate_cb" type="object" string="Validate" /> <field name="password" password="True" />
<field name="state" invisible="1" />
</group>
<group states="validated">
<label string="Maintenance contract added !"/>
</group>
<group states="unvalidated">
<label string="Could you check your contract information ?" />
</group>
</group>
<group colspan="4">
<button type="object" string="_Cancel" icon="gtk-cancel" special="cancel" states="draft"/>
<button type="object" string="_Validate" icon="gtk-apply" name="action_validate" states="draft"/>
<button type="object" string="_Close" icon="gtk-close" special="cancel" states="validated,unvalidated"/>
</group>
</form> </form>
</field> </field>
</record> </record>

View File

@ -68,7 +68,7 @@ class module_category(osv.osv):
cr.execute('select category_id,count(*) from ir_module_module where category_id in ('+','.join(map(str,ids))+') or category_id in (select id from ir_module_category where parent_id in ('+','.join(map(str,ids))+')) group by category_id') cr.execute('select category_id,count(*) from ir_module_module where category_id in ('+','.join(map(str,ids))+') or category_id in (select id from ir_module_category where parent_id in ('+','.join(map(str,ids))+')) group by category_id')
result = dict(cr.fetchall()) result = dict(cr.fetchall())
for id in ids: for id in ids:
cr.execute('select id from ir_module_category where parent_id=%d', (id,)) cr.execute('select id from ir_module_category where parent_id=%s', (id,))
childs = [c for c, in cr.fetchall()] childs = [c for c, in cr.fetchall()]
result[id] = reduce(lambda x,y:x+y, [result.get(c, 0) for c in childs], result.get(id, 0)) result[id] = reduce(lambda x,y:x+y, [result.get(c, 0) for c in childs], result.get(id, 0))
return result return result
@ -98,58 +98,43 @@ class module(osv.osv):
return {} return {}
return info return info
def _get_installed_version(self, cr, uid, ids, field_name=None, arg=None, context={}): def _get_latest_version(self, cr, uid, ids, field_name=None, arg=None, context={}):
res = {} res = dict.fromkeys(ids, '')
for m in self.browse(cr, uid, ids): for m in self.browse(cr, uid, ids):
if m.state in ('installed', 'to upgrade', 'to remove'): res[m.id] = self.get_module_info(m.name).get('version', '')
res[m.id] = self.get_module_info(m.name).get('version', '')
else:
res[m.id] = ''
return res
def _get_menus(self, cr, uid, ids, field_name=None, arg=None, context={}):
res = {}
model_data_obj = self.pool.get('ir.model.data')
menu_obj = self.pool.get('ir.ui.menu')
for m in self.browse(cr, uid, ids):
if m.state == 'installed':
menu_txt = ''
menus_id = model_data_obj.search(cr,uid,[('module','=',m.name),('model','=','ir.ui.menu')])
for data_id in model_data_obj.browse(cr,uid,menus_id):
menu_txt += menu_obj.browse(cr,uid,data_id.res_id).complete_name + '\n'
res[m.id] = menu_txt
else:
res[m.id] = ''
return res
def _get_reports(self, cr, uid, ids, field_name=None, arg=None, context={}):
res = {}
model_data_obj = self.pool.get('ir.model.data')
report_obj = self.pool.get('ir.actions.report.xml')
for m in self.browse(cr, uid, ids):
if m.state == 'installed':
report_txt = ''
report_id = model_data_obj.search(cr,uid,[('module','=',m.name),('model','=','ir.actions.report.xml')])
for data_id in model_data_obj.browse(cr,uid,report_id):
report_txt += report_obj.browse(cr,uid,data_id.res_id).name + '\n'
res[m.id] = report_txt
else:
res[m.id] = ''
return res return res
def _get_views(self, cr, uid, ids, field_name=None, arg=None, context={}): def _get_views(self, cr, uid, ids, field_name=None, arg=None, context={}):
res = {} res = {}
model_data_obj = self.pool.get('ir.model.data') model_data_obj = self.pool.get('ir.model.data')
view_obj = self.pool.get('ir.ui.view') view_obj = self.pool.get('ir.ui.view')
for m in self.browse(cr, uid, ids, context=context): report_obj = self.pool.get('ir.actions.report.xml')
if m.state == 'installed': menu_obj = self.pool.get('ir.ui.menu')
view_txt = '' mlist = self.browse(cr, uid, ids, context=context)
view_id = model_data_obj.search(cr,uid,[('module','=',m.name),('model','=','ir.ui.view')]) mnames = {}
for data_id in model_data_obj.browse(cr,uid,view_id): for m in mlist:
view_txt += view_obj.browse(cr,uid,data_id.res_id).name + '\n' mnames[m.name] = m.id
res[m.id] = view_txt res[m.id] = {
else: 'menus_by_module':'',
res[m.id] = '' 'reports_by_module':'',
'views_by_module': ''
}
view_id = model_data_obj.search(cr,uid,[('module','in', mnames.keys()),
('model','in',('ir.ui.view','ir.actions.report.xml','ir.ui.menu'))])
for data_id in model_data_obj.browse(cr,uid,view_id,context):
# We use try except, because views or menus may not exist
try:
key = data_id['model']
if key=='ir.ui.view':
v = view_obj.browse(cr,uid,data_id.res_id)
aa = v.inherit_id and '* INHERIT ' or ''
res[mnames[data_id.module]]['views_by_module'] += aa + v.name + ' ('+v.type+')\n'
elif key=='ir.actions.report.xml':
res[mnames[data_id.module]]['reports_by_module'] += report_obj.browse(cr,uid,data_id.res_id).name + '\n'
elif key=='ir.ui.menu':
res[mnames[data_id.module]]['menus_by_module'] += menu_obj.browse(cr,uid,data_id.res_id).complete_name + '\n'
except KeyError, e:
pass
return res return res
_columns = { _columns = {
@ -159,10 +144,16 @@ class module(osv.osv):
'description': fields.text("Description", readonly=True, translate=True), 'description': fields.text("Description", readonly=True, translate=True),
'author': fields.char("Author", size=128, readonly=True), 'author': fields.char("Author", size=128, readonly=True),
'website': fields.char("Website", size=256, readonly=True), 'website': fields.char("Website", size=256, readonly=True),
'installed_version': fields.function(_get_installed_version, method=True,
string='Installed version', type='char'), # attention: Incorrect field names !!
'latest_version': fields.char('Latest version', size=64, readonly=True), # installed_version refer the latest version (the one on disk)
# latest_version refer the installed version (the one in database)
# published_version refer the version available on the repository
'installed_version': fields.function(_get_latest_version, method=True,
string='Latest version', type='char'),
'latest_version': fields.char('Installed version', size=64, readonly=True),
'published_version': fields.char('Published Version', size=64, readonly=True), 'published_version': fields.char('Published Version', size=64, readonly=True),
'url': fields.char('URL', size=128), 'url': fields.char('URL', size=128),
'dependencies_id': fields.one2many('ir.module.module.dependency', 'dependencies_id': fields.one2many('ir.module.module.dependency',
'module_id', 'Dependencies', readonly=True), 'module_id', 'Dependencies', readonly=True),
@ -182,9 +173,9 @@ class module(osv.osv):
('GPL-3 or any later version', 'GPL-3 or later version'), ('GPL-3 or any later version', 'GPL-3 or later version'),
('Other proprietary', 'Other proprietary') ('Other proprietary', 'Other proprietary')
], string='License', readonly=True), ], string='License', readonly=True),
'menus_by_module': fields.function(_get_menus, method=True, string='Menus', type='text'), 'menus_by_module': fields.function(_get_views, method=True, string='Menus', type='text', multi="meta", store=True),
'reports_by_module': fields.function(_get_reports, method=True, string='Reports', type='text'), 'reports_by_module': fields.function(_get_views, method=True, string='Reports', type='text', multi="meta", store=True),
'views_by_module': fields.function(_get_views, method=True, string='Views', type='text'), 'views_by_module': fields.function(_get_views, method=True, string='Views', type='text', multi="meta", store=True),
} }
_defaults = { _defaults = {
@ -270,9 +261,22 @@ class module(osv.osv):
_("Can not upgrade module '%s'. It is not installed.") % (mod.name,)) _("Can not upgrade module '%s'. It is not installed.") % (mod.name,))
iids = depobj.search(cr, uid, [('name', '=', mod.name)], context=context) iids = depobj.search(cr, uid, [('name', '=', mod.name)], context=context)
for dep in depobj.browse(cr, uid, iids, context=context): for dep in depobj.browse(cr, uid, iids, context=context):
if dep.module_id.state=='installed': if dep.module_id.state=='installed' and dep.module_id not in todo:
todo.append(dep.module_id) todo.append(dep.module_id)
self.write(cr,uid, map(lambda x: x.id, todo), {'state':'to upgrade'}, context=context)
ids = map(lambda x: x.id, todo)
self.write(cr, uid, ids, {'state':'to upgrade'}, context=context)
to_install = []
for mod in todo:
for dep in mod.dependencies_id:
if dep.state == 'unknown':
raise orm.except_orm(_('Error'), _('You try to upgrade a module that depends on the module: %s.\nBut this module is not available in your system.') % (dep.name,))
if dep.state == 'uninstalled':
ids2 = self.search(cr, uid, [('name','=',dep.name)])
to_install.extend(ids2)
self.button_install(cr, uid, to_install, context=context)
return True return True
def button_upgrade_cancel(self, cr, uid, ids, context={}): def button_upgrade_cancel(self, cr, uid, ids, context={}):
@ -301,7 +305,6 @@ class module(osv.osv):
self.write(cr, uid, id, {'state': 'uninstalled'}) self.write(cr, uid, id, {'state': 'uninstalled'})
if parse_version(terp.get('version', '')) > parse_version(mod.latest_version or ''): if parse_version(terp.get('version', '')) > parse_version(mod.latest_version or ''):
self.write(cr, uid, id, { self.write(cr, uid, id, {
'latest_version': terp.get('version'),
'url': ''}) 'url': ''})
res[0] += 1 res[0] += 1
self.write(cr, uid, id, { self.write(cr, uid, id, {
@ -312,7 +315,7 @@ class module(osv.osv):
'license': terp.get('license', 'GPL-2'), 'license': terp.get('license', 'GPL-2'),
}) })
cr.execute('DELETE FROM ir_module_module_dependency\ cr.execute('DELETE FROM ir_module_module_dependency\
WHERE module_id = %d', (id,)) WHERE module_id = %s', (id,))
self._update_dependencies(cr, uid, ids[0], terp.get('depends', self._update_dependencies(cr, uid, ids[0], terp.get('depends',
[])) []))
self._update_category(cr, uid, ids[0], terp.get('category', self._update_category(cr, uid, ids[0], terp.get('category',
@ -340,7 +343,6 @@ class module(osv.osv):
'shortdesc': terp.get('name', ''), 'shortdesc': terp.get('name', ''),
'author': terp.get('author', 'Unknown'), 'author': terp.get('author', 'Unknown'),
'website': terp.get('website', ''), 'website': terp.get('website', ''),
'latest_version': terp.get('version', ''),
'license': terp.get('license', 'GPL-2'), 'license': terp.get('license', 'GPL-2'),
}) })
res[1] += 1 res[1] += 1
@ -377,7 +379,6 @@ class module(osv.osv):
if not ids: if not ids:
self.create(cr, uid, { self.create(cr, uid, {
'name': name, 'name': name,
'latest_version': version,
'published_version': version, 'published_version': version,
'url': url, 'url': url,
'state': 'uninstalled', 'state': 'uninstalled',
@ -385,16 +386,15 @@ class module(osv.osv):
res[1] += 1 res[1] += 1
else: else:
id = ids[0] id = ids[0]
latest_version = self.read(cr, uid, id, ['latest_version'])\ installed_version = self.read(cr, uid, id, ['latest_version'])['latest_version']
['latest_version'] if installed_version == 'x': # 'x' version was a mistake
if latest_version == 'x': # 'x' version was a mistake installed_version = '0'
latest_version = '0' if parse_version(version) > parse_version(installed_version):
if parse_version(version) > parse_version(latest_version): self.write(cr, uid, id, {
self.write(cr, uid, id, 'url': url
{'latest_version': version, 'url': url}) })
res[0] += 1 res[0] += 1
published_version = self.read(cr, uid, id, ['published_version'])\ published_version = self.read(cr, uid, id, ['published_version'])['published_version']
['published_version']
if published_version == 'x' or not published_version: if published_version == 'x' or not published_version:
published_version = '0' published_version = '0'
if parse_version(version) > parse_version(published_version): if parse_version(version) > parse_version(published_version):
@ -433,7 +433,7 @@ class module(osv.osv):
'license': terp.get('license', 'GPL-2'), 'license': terp.get('license', 'GPL-2'),
}) })
cr.execute('DELETE FROM ir_module_module_dependency ' \ cr.execute('DELETE FROM ir_module_module_dependency ' \
'WHERE module_id = %d', (mod.id,)) 'WHERE module_id = %s', (mod.id,))
self._update_dependencies(cr, uid, mod.id, terp.get('depends', self._update_dependencies(cr, uid, mod.id, terp.get('depends',
[])) []))
self._update_category(cr, uid, mod.id, terp.get('category', self._update_category(cr, uid, mod.id, terp.get('category',
@ -445,21 +445,21 @@ class module(osv.osv):
def _update_dependencies(self, cr, uid, id, depends=[]): def _update_dependencies(self, cr, uid, id, depends=[]):
for d in depends: for d in depends:
cr.execute('INSERT INTO ir_module_module_dependency (module_id, name) values (%d, %s)', (id, d)) cr.execute('INSERT INTO ir_module_module_dependency (module_id, name) values (%s, %s)', (id, d))
def _update_category(self, cr, uid, id, category='Uncategorized'): def _update_category(self, cr, uid, id, category='Uncategorized'):
categs = category.split('/') categs = category.split('/')
p_id = None p_id = None
while categs: while categs:
if p_id is not None: if p_id is not None:
cr.execute('select id from ir_module_category where name=%s and parent_id=%d', (categs[0], p_id)) cr.execute('select id from ir_module_category where name=%s and parent_id=%s', (categs[0], p_id))
else: else:
cr.execute('select id from ir_module_category where name=%s and parent_id is NULL', (categs[0],)) cr.execute('select id from ir_module_category where name=%s and parent_id is NULL', (categs[0],))
c_id = cr.fetchone() c_id = cr.fetchone()
if not c_id: if not c_id:
cr.execute('select nextval(\'ir_module_category_id_seq\')') cr.execute('select nextval(\'ir_module_category_id_seq\')')
c_id = cr.fetchone()[0] c_id = cr.fetchone()[0]
cr.execute('insert into ir_module_category (id, name, parent_id) values (%d, %s, %d)', (c_id, categs[0], p_id)) cr.execute('insert into ir_module_category (id, name, parent_id) values (%s, %s, %s)', (c_id, categs[0], p_id))
else: else:
c_id = c_id[0] c_id = c_id[0]
p_id = c_id p_id = c_id

View File

@ -28,30 +28,30 @@
<field name="type">form</field> <field name="type">form</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form col="3" string="Export language"> <form col="3" string="Export language">
<notebook> <notebook>
<page string="Export Data"> <page string="Export Data">
<group col="2" fill="0" states="choose"> <group col="2" fill="0" states="choose">
<separator colspan="2" string="Export translation file"/> <separator colspan="2" string="Export translation file"/>
<field name="lang" required="1" width="300"/> <field name="lang" required="1" width="300"/>
<field name="format" required="1"/> <field name="format" required="1"/>
<field height="200" name="modules" width="500"/> <field height="200" name="modules" width="500"/>
<field invisible="1" name="state"/> <field invisible="1" name="state"/>
</group> </group>
<group col="1" fill="0" states="get"> <group col="1" fill="0" states="get">
<separator string="Export done"/> <separator string="Export done"/>
<field name="name" invisible="1"/> <field name="name" invisible="1"/>
<field name="data" nolabel="1" readonly="1" fieldname="name"/> <field name="data" nolabel="1" readonly="1" fieldname="name"/>
<field height="80" name="advice" nolabel="1"/> <field height="80" name="advice" nolabel="1"/>
</group> </group>
</page> </page>
<page string="Help"> <page string="Help">
<label align="0.0" colspan="4" string="The official translations pack of all OpenERP/OpenObjects module are managed through launchpad. We use their online interface to synchronize all translations efforts."/> <label align="0.0" colspan="4" string="The official translations pack of all OpenERP/OpenObjects module are managed through launchpad. We use their online interface to synchronize all translations efforts."/>
<label align="0.0" colspan="4" string="To improve some terms of the official translations of OpenERP, you should modify the terms directly on the launchpad interface. If you made lots of translations for your own module, you can also publish all your translation at once."/> <label align="0.0" colspan="4" string="To improve some terms of the official translations of OpenERP, you should modify the terms directly on the launchpad interface. If you made lots of translations for your own module, you can also publish all your translation at once."/>
<label align="0.0" colspan="4" string="To browse official translations, you can visit this link: "/> <label align="0.0" colspan="4" string="To browse official translations, you can visit this link: "/>
<label align="0.0" colspan="4" string="https://translations.launchpad.net/openobject"/> <label align="0.0" colspan="4" string="https://translations.launchpad.net/openobject"/>
</page> </page>
</notebook> </notebook>
<group col="2" colspan="3" fill="0"> <group col="2" colspan="3" fill="0">
<button icon="gtk-cancel" name="act_cancel" special="cancel" states="choose" string="Cancel" type="object"/> <button icon="gtk-cancel" name="act_cancel" special="cancel" states="choose" string="Cancel" type="object"/>
<button icon="gtk-ok" name="act_getfile" states="choose" string="Get file" type="object"/> <button icon="gtk-ok" name="act_getfile" states="choose" string="Get file" type="object"/>

View File

@ -13,57 +13,62 @@
<blockTableStyle id="Table1"> <blockTableStyle id="Table1">
<blockAlignment value="LEFT"/> <blockAlignment value="LEFT"/>
<blockValign value="TOP"/> <blockValign value="TOP"/>
<blockBackground colorName="#e6e6e6" start="0,0" stop="0,-1"/>
<blockBackground colorName="#e6e6e6" start="1,0" stop="1,-1"/>
<blockBackground colorName="#e6e6e6" start="2,0" stop="2,-1"/>
<blockBackground colorName="#e6e6e6" start="0,1" stop="0,-1"/>
<blockBackground colorName="#e6e6e6" start="1,1" stop="1,-1"/>
<blockBackground colorName="#e6e6e6" start="2,1" stop="2,-1"/>
</blockTableStyle> </blockTableStyle>
<blockTableStyle id="Tableau1"> <blockTableStyle id="module_tbl_heading">
<blockAlignment value="LEFT"/> <blockAlignment value="LEFT"/>
<blockValign value="TOP"/> <blockValign value="TOP"/>
<lineStyle kind="LINEBEFORE" colorName="#000000" start="0,0" stop="0,-1"/> <lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="0,0" stop="0,-1"/>
<lineStyle kind="LINEAFTER" colorName="#000000" start="0,0" stop="0,-1"/> <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="0,0" stop="0,0"/>
<lineStyle kind="LINEABOVE" colorName="#000000" start="0,0" stop="0,0"/> <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="0,-1" stop="0,-1"/>
<lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/> <lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="1,0" stop="1,-1"/>
<blockBackground colorName="#ff6633" start="0,0" stop="0,-1"/> <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="1,0" stop="1,0"/>
<lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="1,-1" stop="1,-1"/>
<lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="2,0" stop="2,-1"/>
<lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="2,0" stop="2,0"/>
<lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="2,-1" stop="2,-1"/>
<lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="3,0" stop="3,-1"/>
<lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="3,0" stop="3,0"/>
<lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="3,-1" stop="3,-1"/>
<lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="4,0" stop="4,-1"/>
<lineStyle kind="LINEAFTER" colorName="#e6e6e6" start="4,0" stop="4,-1"/>
<lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="4,0" stop="4,0"/>
<lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="4,-1" stop="4,-1"/>
</blockTableStyle> </blockTableStyle>
<blockTableStyle id="Tableau2"> <blockTableStyle id="module_tbl_content">
<blockAlignment value="LEFT"/> <blockAlignment value="LEFT"/>
<blockValign value="TOP"/> <blockValign value="TOP"/>
<blockBackground colorName="#e6e6e6" start="0,0" stop="0,-1"/> <lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="0,0" stop="0,-1"/>
<blockBackground colorName="#e6e6e6" start="1,0" stop="1,-1"/> <lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="0,0" stop="0,0"/>
<blockBackground colorName="#e6e6e6" start="0,1" stop="0,-1"/> <lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="0,-1" stop="0,-1"/>
<blockBackground colorName="#e6e6e6" start="1,1" stop="1,-1"/> <lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="1,0" stop="1,-1"/>
<lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="1,0" stop="1,0"/>
<lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="1,-1" stop="1,-1"/>
<lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="2,0" stop="2,-1"/>
<lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="2,0" stop="2,0"/>
<lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="2,-1" stop="2,-1"/>
<lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="3,0" stop="3,-1"/>
<lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="3,0" stop="3,0"/>
<lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="3,-1" stop="3,-1"/>
<lineStyle kind="LINEBEFORE" colorName="#e6e6e6" start="4,0" stop="4,-1"/>
<lineStyle kind="LINEAFTER" colorName="#e6e6e6" start="4,0" stop="4,-1"/>
<lineStyle kind="LINEABOVE" colorName="#e6e6e6" start="4,0" stop="4,0"/>
<lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="4,-1" stop="4,-1"/>
</blockTableStyle> </blockTableStyle>
<blockTableStyle id="Tableau3"> <blockTableStyle id="Tableau3">
<blockAlignment value="LEFT"/> <blockAlignment value="LEFT"/>
<blockValign value="TOP"/> <blockValign value="TOP"/>
<lineStyle kind="LINEBEFORE" colorName="#800000" start="0,0" stop="0,-1"/> <lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/>
<lineStyle kind="LINEAFTER" colorName="#800000" start="0,0" stop="0,-1"/>
<lineStyle kind="LINEABOVE" colorName="#800000" start="0,0" stop="0,0"/>
<lineStyle kind="LINEBELOW" colorName="#800000" start="0,-1" stop="0,-1"/>
<blockBackground colorName="#ffcc99" start="0,0" stop="0,-1"/>
</blockTableStyle> </blockTableStyle>
<blockTableStyle id="Tableau4"> <blockTableStyle id="Table2">
<blockAlignment value="LEFT"/> <blockAlignment value="LEFT"/>
<blockValign value="TOP"/> <blockValign value="TOP"/>
<lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="0,-1" stop="0,-1"/>
<lineStyle kind="LINEBELOW" colorName="#e6e6e6" start="1,-1" stop="1,-1"/>
</blockTableStyle> </blockTableStyle>
<initialize> <initialize>
<paraStyle name="all" alignment="justify"/> <paraStyle name="all" alignment="justify"/>
</initialize> </initialize>
<paraStyle name="P1" fontName="Times-Roman" fontSize="20.0" leading="25" alignment="CENTER" spaceBefore="0.0" spaceAfter="6.0"/> <paraStyle name="P1" fontName="Helvetica-Oblique" fontSize="2.0" leading="3" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
<paraStyle name="P2" fontName="Times-Roman" fontSize="10.0" leading="13" alignment="RIGHT" spaceBefore="0.0" spaceAfter="6.0"/>
<paraStyle name="P3" fontName="Times-Roman" alignment="CENTER" spaceBefore="0.0" spaceAfter="6.0"/>
<paraStyle name="P4" fontName="Times-Roman" fontSize="11.0" leading="14" alignment="RIGHT" spaceBefore="0.0" spaceAfter="6.0"/>
<paraStyle name="P5" fontName="Times-Roman" fontSize="11.0" leading="14" alignment="LEFT" spaceBefore="0.0" spaceAfter="6.0"/>
<paraStyle name="P6" fontName="Times-Roman"/>
<paraStyle name="P7" fontName="Times-Roman" alignment="LEFT" spaceBefore="0.0" spaceAfter="6.0"/>
<paraStyle name="P8" fontName="Times-Roman"/>
<paraStyle name="P9" fontName="Times-Roman" fontSize="16.0" leading="20" alignment="CENTER" spaceBefore="0.0" spaceAfter="6.0"/>
<paraStyle name="P10" fontName="Times-Roman" fontSize="15.0" leading="19" alignment="CENTER" spaceBefore="0.0" spaceAfter="6.0"/>
<paraStyle name="P11" fontName="Times-Roman" fontSize="12.0" leading="15" alignment="CENTER" spaceBefore="0.0" spaceAfter="6.0"/>
<paraStyle name="Standard" fontName="Times-Roman"/> <paraStyle name="Standard" fontName="Times-Roman"/>
<paraStyle name="Text body" fontName="Times-Roman" spaceBefore="0.0" spaceAfter="6.0"/> <paraStyle name="Text body" fontName="Times-Roman" spaceBefore="0.0" spaceAfter="6.0"/>
<paraStyle name="Heading" fontName="Helvetica" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/> <paraStyle name="Heading" fontName="Helvetica" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/>
@ -72,6 +77,28 @@
<paraStyle name="Table Heading" fontName="Times-Roman" alignment="CENTER" spaceBefore="0.0" spaceAfter="6.0"/> <paraStyle name="Table Heading" fontName="Times-Roman" alignment="CENTER" spaceBefore="0.0" spaceAfter="6.0"/>
<paraStyle name="Caption" fontName="Times-Roman" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/> <paraStyle name="Caption" fontName="Times-Roman" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/>
<paraStyle name="Index" fontName="Times-Roman"/> <paraStyle name="Index" fontName="Times-Roman"/>
<paraStyle name="Footer" fontName="Times-Roman"/>
<paraStyle name="Horizontal Line" fontName="Times-Roman" fontSize="6.0" leading="8" spaceBefore="0.0" spaceAfter="14.0"/>
<paraStyle name="terp_header" fontName="Helvetica-Bold" fontSize="15.0" leading="19" alignment="LEFT" spaceBefore="12.0" spaceAfter="6.0"/>
<paraStyle name="Heading 9" fontName="Helvetica-Bold" fontSize="75%" leading="NaN" spaceBefore="12.0" spaceAfter="6.0"/>
<paraStyle name="terp_tblheader_General" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="6.0" spaceAfter="6.0"/>
<paraStyle name="terp_tblheader_Details" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="LEFT" spaceBefore="6.0" spaceAfter="6.0"/>
<paraStyle name="terp_default_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
<paraStyle name="terp_default_Bold_8" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
<paraStyle name="terp_tblheader_General_Centre" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="6.0" spaceAfter="6.0"/>
<paraStyle name="terp_tblheader_General_Right" fontName="Helvetica-Bold" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="6.0" spaceAfter="6.0"/>
<paraStyle name="terp_tblheader_Details_Centre" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="CENTER" spaceBefore="6.0" spaceAfter="6.0"/>
<paraStyle name="terp_tblheader_Details_Right" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="RIGHT" spaceBefore="6.0" spaceAfter="6.0"/>
<paraStyle name="terp_default_Right_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/>
<paraStyle name="terp_default_Centre_8" fontName="Helvetica" fontSize="8.0" leading="10" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/>
<paraStyle name="terp_header_Right" fontName="Helvetica-Bold" fontSize="15.0" leading="19" alignment="LEFT" spaceBefore="12.0" spaceAfter="6.0"/>
<paraStyle name="terp_header_Centre" fontName="Helvetica-Bold" fontSize="15.0" leading="19" alignment="CENTER" spaceBefore="12.0" spaceAfter="6.0"/>
<paraStyle name="terp_default_address" fontName="Helvetica" fontSize="10.0" leading="13" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
<paraStyle name="terp_default_9" fontName="Helvetica" fontSize="9.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
<paraStyle name="terp_default_Bold_9" fontName="Helvetica-Bold" fontSize="9.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
<paraStyle name="terp_default_Centre_9" fontName="Helvetica" fontSize="9.0" leading="11" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/>
<paraStyle name="terp_default_Right_9" fontName="Helvetica" fontSize="9.0" leading="11" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/>
<paraStyle name="terp_default_1" fontName="Helvetica" fontSize="2.0" leading="3" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
</stylesheet> </stylesheet>
<images/> <images/>
<story> <story>
@ -81,28 +108,28 @@
<blockTable colWidths="139.0,220.0,152.0" repeatRows="1" style="Table1"> <blockTable colWidths="139.0,220.0,152.0" repeatRows="1" style="Table1">
<tr> <tr>
<td> <td>
<para style="Table Contents"> <para style="terp_header_Centre">
<font color="white"> </font> <font color="white"> </font>
</para> </para>
</td> </td>
<td> <td>
<para style="P1">Reference Guide</para> <para style="terp_header_Centre">Reference Guide</para>
</td> </td>
<td> <td>
<para style="P2"> <para style="terp_header_Centre">
<font color="white"> </font> <font color="white"> </font>
</para> </para>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<para style="Table Contents">[[ company.name ]]</para> <para style="terp_default_9">[[ company.name ]]</para>
</td> </td>
<td> <td>
<para style="P3">Introspection report on objects</para> <para style="terp_default_Centre_9">Introspection report on objects</para>
</td> </td>
<td> <td>
<para style="P4">Printed: [[ time.strftime('%y-%m-%d')]]</para> <para style="terp_default_Right_9">Printed: [[ time.strftime('%y-%m-%d')]]</para>
</td> </td>
</tr> </tr>
</blockTable> </blockTable>
@ -111,70 +138,86 @@
</para> </para>
<section> <section>
<para style="Text body">[[ repeatIn(objects, 'module') ]]</para> <para style="Text body">[[ repeatIn(objects, 'module') ]]</para>
<blockTable colWidths="510.0" repeatRows="1" style="Tableau1"> <blockTable colWidths="102.0,102.0,102.0,102.0,102.0" style="module_tbl_heading">
<tr> <tr>
<td> <td>
<para style="P9">Module: [[ module.name ]]</para> <para style="terp_tblheader_General_Centre">Module</para>
</td>
<td>
<para style="terp_tblheader_General_Centre">Name</para>
</td>
<td>
<para style="terp_tblheader_General_Centre">Version</para>
</td>
<td>
<para style="terp_tblheader_General_Centre">Directory</para>
</td>
<td>
<para style="terp_tblheader_General_Centre">Web</para>
</td> </td>
</tr> </tr>
</blockTable> </blockTable>
<blockTable colWidths="277.0,234.0" repeatRows="1" style="Tableau2"> <blockTable colWidths="102.0,102.0,102.0,102.0,102.0" style="module_tbl_content">
<tr> <tr>
<td> <td>
<para style="Standard">Name: [[ module.shortdesc]]</para> <para style="terp_default_Centre_8">[[ module.name ]]</para>
</td> </td>
<td> <td>
<para style="Standard">Version: [[module.latest_version]]</para> <para style="terp_default_Centre_8">[[ module.shortdesc]]</para>
</td>
</tr>
<tr>
<td>
<para style="Standard">Directory: [[ module.name ]]</para>
</td> </td>
<td> <td>
<para style="Standard">Web: [[ module.website ]]</para> <para style="terp_default_Centre_8">[[module.latest_version]]</para>
</td>
<td>
<para style="terp_default_Centre_8">[[ module.name ]]</para>
</td>
<td>
<para style="terp_default_Centre_8">[[ module.website ]]</para>
</td> </td>
</tr> </tr>
</blockTable> </blockTable>
<para style="P6">[[ format(module.description or '') ]]</para> <para style="terp_default_9">
<font color="white"> </font>
</para>
<para style="terp_default_9">[[ module.description ]]</para>
<section> <section>
<para style="Standard">[[ repeatIn(findobj(module.name) ,'object') ]]</para> <para style="terp_default_9">
<font color="white"> </font>
</para>
<para style="terp_default_9">[[ repeatIn(findobj(module.name) ,'object') ]]</para>
<para style="terp_default_1">
<font color="white"> </font>
</para>
<blockTable colWidths="510.0" repeatRows="1" style="Tableau3"> <blockTable colWidths="510.0" repeatRows="1" style="Tableau3">
<tr> <tr>
<td> <td>
<para style="P10">Object: [[ object.model ]]</para> <para style="terp_tblheader_Details">Object: [[ object.name ]] [[ objdoc(object.model) ]]</para>
<para style="P11">Description : [[ object.name ]]</para>
<para style="P5">[[ format(objdoc(object.model)) ]]</para>
</td> </td>
</tr> </tr>
</blockTable> </blockTable>
<blockTable colWidths="113.0,397.0" repeatRows="1" style="Tableau4"> <section>
<tr> <para style="terp_default_1">
<td> <font color="white"> </font>
<para style="P7">[[ repeatIn(findflds(object.model), 'field') ]] <i>[[ field[0] ]]</i></para> </para>
</td> </section>
<td> <section>
<para style="P8">[[ field[1].get('string', 'Unknown') ]], [[ field[1]['type'] ]] [[field[1].get('required',False) and ', required']] [[field[1].get('readonly',False) and ', readonly']] </para> <para style="P1">[[ repeatIn(findflds(object.model), 'field') ]]</para>
<para style="Standard">[[ field[1].get('help', '') ]]</para> <blockTable colWidths="113.0,397.0" repeatRows="1" style="Table2">
</td> <tr>
</tr> <td>
</blockTable> <para style="terp_default_9">[[ field[0] ]]</para>
<para style="Standard"> </td>
<font color="white"> </font> <td>
</para> <para style="terp_default_9">[[ field[1].get('string', 'Unknown') ]], [[ field[1]['type'] ]] [[field[1].get('required',False) and ', required']] [[field[1].get('readonly',False) and ', readonly']] </para>
<para style="terp_default_9">[[ field[1].get('help', '') ]]</para>
</td>
</tr>
</blockTable>
<para style="terp_default_1">
<font color="white"> </font>
</para>
</section>
</section> </section>
<para style="Standard">
<font color="white"> </font>
</para>
<para style="Standard">
<font color="white"> </font>
</para>
<para style="Standard">
<font color="white"> </font>
</para>
</section> </section>
<para style="Standard">
<font color="white"> </font>
</para>
</story> </story>
</document> </document>

View File

@ -38,7 +38,8 @@
<field name="domain">[('res_id','=',False)]</field> <field name="domain">[('res_id','=',False)]</field>
<field name="view_id" ref="ir_property_view_tree"/> <field name="view_id" ref="ir_property_view_tree"/>
</record> </record>
<menuitem id="next_id_15" name="Properties" parent="base.menu_custom"/><menuitem action="ir_property_form" id="menu_ir_property_form" parent="next_id_15"/> <menuitem id="next_id_15" name="Properties" parent="base.menu_config"/>
<menuitem action="ir_property_form" id="menu_ir_property_form" parent="next_id_15"/>
<record id="ir_property_form_all" model="ir.actions.act_window"> <record id="ir_property_form_all" model="ir.actions.act_window">
<field name="name">All Properties</field> <field name="name">All Properties</field>

View File

@ -259,7 +259,7 @@ class res_partner(osv.osv):
raise osv.except_osv(_('Warning'), _("Couldn't generate the next id because some partners have an alphabetic id !")) raise osv.except_osv(_('Warning'), _("Couldn't generate the next id because some partners have an alphabetic id !"))
# update the current partner # update the current partner
cr.execute("update res_partner set ref=%d where id=%d", (nextref, ids[0])) cr.execute("update res_partner set ref=%s where id=%s", (nextref, ids[0]))
return True return True
def view_header_get(self, cr, uid, view_id, view_type, context): def view_header_get(self, cr, uid, view_id, view_type, context):
@ -306,10 +306,10 @@ class res_partner_address(osv.osv):
if context.get('contact_display', 'contact')=='partner': if context.get('contact_display', 'contact')=='partner':
res.append((r['id'], r['partner_id'][1])) res.append((r['id'], r['partner_id'][1]))
else: else:
addr = str(r['name'] or '') addr = r['name'] or ''
if r['name'] and (r['zip'] or r['city']): if r['name'] and (r['zip'] or r['city']):
addr += ', ' addr += ', '
addr += str(r['street'] or '') + ' ' + str(r['zip'] or '') + ' ' + str(r['city'] or '') addr += (r['street'] or '') + ' ' + (r['zip'] or '') + ' ' + (r['city'] or '')
res.append((r['id'], addr.strip() or '/')) res.append((r['id'], addr.strip() or '/'))
return res return res

View File

@ -94,7 +94,7 @@
<field name="fax"/> <field name="fax"/>
<newline/> <newline/>
<field name="mobile" select="2"/> <field name="mobile" select="2"/>
<field name="email" select="2"/> <field name="email" select="2" widget="email"/>
</form> </form>
</field> </field>
</record> </record>
@ -146,7 +146,7 @@
<field name="fax"/> <field name="fax"/>
<newline/> <newline/>
<field name="mobile"/> <field name="mobile"/>
<field name="email"/> <field name="email" widget="email"/>
</form> </form>
</field> </field>
</record> </record>
@ -183,7 +183,7 @@
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="domain">[('domain','=','partner')]</field> <field name="domain">[('domain','=','partner')]</field>
</record> </record>
<menuitem action="action_partner_title_partner" id="menu_partner_title_partner" parent="action_partner_title"/> <menuitem action="action_partner_title_partner" id="menu_partner_title_partner" parent="menu_partner_title"/>
<record id="action_partner_title_contact" model="ir.actions.act_window"> <record id="action_partner_title_contact" model="ir.actions.act_window">
<field name="name">Contacts Titles</field> <field name="name">Contacts Titles</field>
@ -192,7 +192,7 @@
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="domain">[('domain','=','contact')]</field> <field name="domain">[('domain','=','contact')]</field>
</record> </record>
<menuitem action="action_partner_title_contact" id="menu_partner_title_contact" parent="action_partner_title"/> <menuitem action="action_partner_title_contact" id="menu_partner_title_contact" parent="menu_partner_title"/>
<!-- <!--
======================= =======================

View File

@ -39,7 +39,7 @@ class res_currency(osv.osv):
date=time.strftime('%Y-%m-%d') date=time.strftime('%Y-%m-%d')
date= date or time.strftime('%Y-%m-%d') date= date or time.strftime('%Y-%m-%d')
for id in ids: for id in ids:
cr.execute("SELECT currency_id, rate FROM res_currency_rate WHERE currency_id = %d AND name <= '%s' ORDER BY name desc LIMIT 1" % (id, date)) cr.execute("SELECT currency_id, rate FROM res_currency_rate WHERE currency_id = %s AND name <= '%s' ORDER BY name desc LIMIT 1" % (id, date))
if cr.rowcount: if cr.rowcount:
id, rate=cr.fetchall()[0] id, rate=cr.fetchall()[0]
res[id]=rate res[id]=rate
@ -91,7 +91,7 @@ class res_currency(osv.osv):
if account and (account.currency_mode=='average') and account.currency_id: if account and (account.currency_mode=='average') and account.currency_id:
q = self.pool.get('account.move.line')._query_get(cr, uid, context=context) q = self.pool.get('account.move.line')._query_get(cr, uid, context=context)
cr.execute('select sum(debit-credit),sum(amount_currency) from account_move_line l ' \ cr.execute('select sum(debit-credit),sum(amount_currency) from account_move_line l ' \
'where l.currency_id=%d and l.account_id=%d and '+q, (account.currency_id.id,account.id,)) 'where l.currency_id=%s and l.account_id=%s and '+q, (account.currency_id.id,account.id,))
tot1,tot2 = cr.fetchone() tot1,tot2 = cr.fetchone()
if tot2 and not account_invert: if tot2 and not account_invert:
rate = float(tot1)/float(tot2) rate = float(tot1)/float(tot2)

View File

@ -1,7 +1,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved # Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$ # $Id$
# #
@ -21,24 +21,118 @@
############################################################################## ##############################################################################
from osv import fields, osv from osv import fields, osv
from locale import localeconv
class lang(osv.osv): class lang(osv.osv):
_name = "res.lang" _name = "res.lang"
_description = "Languages"
def _get_default_date_format(self,cursor,user,context={}):
return '%Y-%m-%d'
def _get_default_time_format(self,cursor,user,context={}):
return '%H:%M:%S'
_columns = { _columns = {
'name': fields.char('Name', size=64, required=True), 'name': fields.char('Name', size=64, required=True),
'code': fields.char('Code', size=5, required=True), 'code': fields.char('Code', size=5, required=True),
'translatable': fields.boolean('Translatable'), 'translatable': fields.boolean('Translatable'),
'active': fields.boolean('Active'), 'active': fields.boolean('Active'),
'direction': fields.selection([('ltr', 'Left-to-right'), ('rtl', 'Right-to-left')], 'Direction',resuired=True), 'direction': fields.selection([('ltr', 'Left-to-Right'), ('rtl', 'Right-to-Left')], 'Direction',required=True),
'date_format':fields.char('Date Format',size=64,required=True),
'time_format':fields.char('Time Format',size=64,required=True),
'grouping':fields.char('Separator Format',size=64,required=True,help="The Separator Format should be like [,n] where 0 < n :starting from Unit digit.-1 will end the separation. e.g. [3,2,-1] will represent 106500 to be 1,06,500;[1,2,-1] will represent it to be 106,50,0;[3] will represent it as 106,500. Provided ',' as the thousand separator in each case."),
'decimal_point':fields.char('Decimal Separator', size=64,required=True),
'thousands_sep':fields.char('Thousands Separator',size=64),
} }
_defaults = { _defaults = {
'active': lambda *a: 1, 'active': lambda *a: 1,
'translatable': lambda *a: 0, 'translatable': lambda *a: 0,
'direction': lambda *a: 'ltr', 'direction': lambda *a: 'ltr',
'date_format':_get_default_date_format,
'time_format':_get_default_time_format,
'grouping':lambda *a: '[]',
'decimal_point':lambda *a: '.',
'thousands_sep':lambda *a: ',',
} }
def _group(self,cr,uid,ids,s, monetary=False):
conv = localeconv()
lang_obj=self.browse(cr,uid,ids[0])
thousands_sep = lang_obj.thousands_sep or conv[monetary and 'mon_thousands_sep' or 'thousands_sep']
grouping = eval(lang_obj.grouping)
if not grouping:
return (s, 0)
result = ""
seps = 0
spaces = ""
if s[-1] == ' ':
sp = s.find(' ')
spaces = s[sp:]
s = s[:sp]
while s and grouping:
# if grouping is -1, we are done
if grouping[0] == -1:
break
# 0: re-use last group ad infinitum
elif grouping[0] != 0:
#process last group
group = grouping[0]
grouping = grouping[1:]
if result:
result = s[-group:] + thousands_sep + result
seps += 1
else:
result = s[-group:]
s = s[:-group]
if s and s[-1] not in "0123456789":
# the leading string is only spaces and signs
return s + result + spaces, seps
if not result:
return s + spaces, seps
if s:
result = s + thousands_sep + result
seps += 1
return result + spaces, seps
def format(self,cr,uid,ids,percent, value, grouping=False, monetary=False):
""" Format() will return the language-specific output for float values"""
if percent[0] != '%':
raise ValueError("format() must be given exactly one %char format specifier")
lang_obj=self.browse(cr,uid,ids[0])
formatted = percent % value
# floats and decimal ints need special action!
if percent[-1] in 'eEfFgG':
seps = 0
parts = formatted.split('.')
if grouping:
parts[0], seps = self._group(cr,uid,ids,parts[0], monetary=monetary)
decimal_point=lang_obj.decimal_point
formatted = decimal_point.join(parts)
while seps:
sp = formatted.find(' ')
if sp == -1: break
formatted = formatted[:sp] + formatted[sp+1:]
seps -= 1
elif percent[-1] in 'diu':
if grouping:
formatted = self._group(cr,uid,ids,formatted, monetary=monetary)[0]
return formatted
# import re, operator
# _percent_re = re.compile(r'%(?:\((?P<key>.*?)\))?'
# r'(?P<modifiers>[-#0-9 +*.hlL]*?)[eEfFgGdiouxXcrs%]')
lang() lang()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -15,6 +15,88 @@
</tree> </tree>
</field> </field>
</record> </record>
<record id="res_lang_form" model="ir.ui.view">
<field name="name">res.lang.form</field>
<field name="model">res.lang</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Languages">
<group col="4" colspan="4">
<field name="name" />
<field name="code" />
<field name="active" />
<field name="translatable"/>
<field name="grouping" />
<field name="direction" />
<newline/>
<field name="date_format"/>
<field name="time_format"/>
<field name="decimal_point"/>
<field name="thousands_sep"/>
<newline/>
</group>
<separator colspan="4" string="Legends for Date and Time Formats"/>
<group col="4" colspan="4">
<label align="0.0" string="%%a - Abbreviated weekday name."/>
<label align="0.0" string="%%A - Full weekday name."/>
<newline/>
<label align="0.0" string="%%b - Abbreviated month name."/>
<label align="0.0" string="%%B - Full month name." />
<newline/>
<label align="0.0" string="%%c - Appropriate date and time representation." />
<label align="0.0" string="%%d - Day of the month as a decimal number [01,31]." />
<newline/>
<label align="0.0" string="%%H - Hour (24-hour clock) as a decimal number [00,23]." />
<label align="0.0" string="%%I - Hour (12-hour clock) as a decimal number [01,12]." />
<newline/>
<label align="0.0" string="%%j - Day of the year as a decimal number [001,366]." />
<label align="0.0" string="%%m - Month as a decimal number [01,12]." />
<newline/>
<label align="0.0" string="%%M - Minute as a decimal number [00,59]." />
<label align="0.0" string="%%p - Equivalent of either AM or PM." />
<newline/>
<label align="0.0" string="%%S - Second as a decimal number [00,61]." />
<label align="0.0" string="%%w - Weekday as a decimal number [0(Sunday),6]." />
<newline/>
<label align="0.0" string="%%x - Appropriate date representation." />
<label align="0.0" string="%%X - Appropriate time representation." />
<newline/>
<label align="0.0" string="%%y - Year without century as a decimal number [00,99]." />
<label align="0.0" string="%%Y - Year with century as a decimal number." />
<newline/>
<label align="0.0" string="%%U - Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0." />
<label align="0.0" string="%%W - Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0." />
<newline/>
<label align="0.0" string="======================================================" />
</group>
<newline/>
<group colspan="4" col="4">
<separator string="Examples"/>
<newline/>
<label align="0.0" string="1. %%c ==> Fri Dec 5 18:25:20 2008"/>
<label align="0.0" string="2. %%a ,%%A ==> Fri, Friday"/>
<newline/>
<label align="0.0" string="3. %%x ,%%X ==> 12/05/08, 18:25:20"/>
<label align="0.0" string="4. %%b, %%B ==> Dec, December"/>
<newline/>
<label align="0.0" string="5. %%y, %%Y ==> 08, 2008"/>
<label align="0.0" string="6. %%d, %%m ==> 05, 12"/>
<newline/>
<label align="0.0" string="7. %%H:%%M:%%S ==> 18:25:20"/>
<label align="0.0" string="8. %%I:%%M:%%S %%p ==> 06:25:20 PM"/>
<newline/>
<label align="0.0" string="9. %%j ==> 340"/>
<label align="0.0" string="10. %%S ==> 20"/>
<newline/>
<label align="0.0" string="11. %%U or %%W ==> 48 (49th week)"/>
<label align="0.0" string="12. %%w ==> 5 ( Friday is the 6th day)"/>
<newline/>
</group>
</form>
</field>
</record>
<record id="res_lang_act_window" model="ir.actions.act_window"> <record id="res_lang_act_window" model="ir.actions.act_window">
<field name="name">Languages</field> <field name="name">Languages</field>
<field name="res_model">res.lang</field> <field name="res_model">res.lang</field>

View File

@ -34,8 +34,8 @@ class res_request(osv.osv):
def request_send(self, cr, uid, ids, *args): def request_send(self, cr, uid, ids, *args):
for id in ids: for id in ids:
cr.execute('update res_request set state=%s,date_sent=%s where id=%d', ('waiting', time.strftime('%Y-%m-%d %H:%M:%S'), id)) cr.execute('update res_request set state=%s,date_sent=%s where id=%s', ('waiting', time.strftime('%Y-%m-%d %H:%M:%S'), id))
cr.execute('select act_from,act_to,body,date_sent from res_request where id=%d', (id,)) cr.execute('select act_from,act_to,body,date_sent from res_request where id=%s', (id,))
values = cr.dictfetchone() values = cr.dictfetchone()
if values['body'] and (len(values['body']) > 128): if values['body'] and (len(values['body']) > 128):
values['name'] = values['body'][:125] + '...' values['name'] = values['body'][:125] + '...'
@ -47,7 +47,7 @@ class res_request(osv.osv):
def request_reply(self, cr, uid, ids, *args): def request_reply(self, cr, uid, ids, *args):
for id in ids: for id in ids:
cr.execute("update res_request set state='active', act_from=%d, act_to=act_from, trigger_date=NULL, body='' where id=%d", (uid,id)) cr.execute("update res_request set state='active', act_from=%s, act_to=act_from, trigger_date=NULL, body='' where id=%s", (uid,id))
return True return True
def request_close(self, cr, uid, ids, *args): def request_close(self, cr, uid, ids, *args):
@ -55,9 +55,9 @@ class res_request(osv.osv):
return True return True
def request_get(self, cr, uid): def request_get(self, cr, uid):
cr.execute('select id from res_request where act_to=%d and (trigger_date<=%s or trigger_date is null) and active=True', (uid,time.strftime('%Y-%m-%d'))) cr.execute('select id from res_request where act_to=%s and (trigger_date<=%s or trigger_date is null) and active=True', (uid,time.strftime('%Y-%m-%d')))
ids = map(lambda x:x[0], cr.fetchall()) ids = map(lambda x:x[0], cr.fetchall())
cr.execute('select id from res_request where act_from=%d and (act_to<>%d) and (trigger_date<=%s or trigger_date is null) and active=True', (uid,uid,time.strftime('%Y-%m-%d'))) cr.execute('select id from res_request where act_from=%s and (act_to<>%s) and (trigger_date<=%s or trigger_date is null) and active=True', (uid,uid,time.strftime('%Y-%m-%d')))
ids2 = map(lambda x:x[0], cr.fetchall()) ids2 = map(lambda x:x[0], cr.fetchall())
return (ids, ids2) return (ids, ids2)

View File

@ -74,7 +74,7 @@ class roles(osv.osv):
def check(self, cr, uid, ids, role_id): def check(self, cr, uid, ids, role_id):
if role_id in ids: if role_id in ids:
return True return True
cr.execute('select parent_id from res_roles where id=%d', (role_id,)) cr.execute('select parent_id from res_roles where id=%s', (role_id,))
roles = cr.fetchone()[0] roles = cr.fetchone()[0]
if roles: if roles:
return self.check(cr, uid, ids, roles) return self.check(cr, uid, ids, roles)
@ -152,7 +152,7 @@ class users(osv.osv):
'groups_id': _get_group, 'groups_id': _get_group,
} }
def company_get(self, cr, uid, uid2): def company_get(self, cr, uid, uid2):
company_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.id company_id = self.pool.get('res.users').browse(cr, uid, uid2).company_id.id
return company_id return company_id
company_get = tools.cache()(company_get) company_get = tools.cache()(company_get)

View File

@ -7,6 +7,7 @@
<rng:optional><rng:attribute name="editable"/></rng:optional> <rng:optional><rng:attribute name="editable"/></rng:optional>
<rng:optional><rng:attribute name="type"/></rng:optional> <rng:optional><rng:attribute name="type"/></rng:optional>
<rng:optional><rng:attribute name="position"/></rng:optional> <rng:optional><rng:attribute name="position"/></rng:optional>
<rng:optional><rng:attribute name="link"/></rng:optional>
<rng:zeroOrMore> <rng:zeroOrMore>
<rng:choice> <rng:choice>
<rng:ref name="notebook"/> <rng:ref name="notebook"/>
@ -33,12 +34,15 @@
<rng:optional><rng:attribute name="editable"/></rng:optional> <rng:optional><rng:attribute name="editable"/></rng:optional>
<rng:optional><rng:attribute name="toolbar"/></rng:optional> <rng:optional><rng:attribute name="toolbar"/></rng:optional>
<rng:optional><rng:attribute name="position"/></rng:optional> <rng:optional><rng:attribute name="position"/></rng:optional>
<rng:optional><rng:attribute name="link"/></rng:optional>
<rng:optional><rng:attribute name="type"/></rng:optional>
<rng:zeroOrMore> <rng:zeroOrMore>
<rng:choice> <rng:choice>
<rng:ref name="field"/> <rng:ref name="field"/>
<rng:ref name="separator"/> <rng:ref name="separator"/>
<rng:ref name="tree"/> <rng:ref name="tree"/>
<rng:ref name="group"/> <rng:ref name="group"/>
<rng:ref name="button"/>
<rng:element name="newline"><rng:empty/></rng:element> <rng:element name="newline"><rng:empty/></rng:element>
</rng:choice> </rng:choice>
</rng:zeroOrMore> </rng:zeroOrMore>
@ -57,6 +61,35 @@
<rng:optional><rng:attribute name="nolabel"/></rng:optional> <rng:optional><rng:attribute name="nolabel"/></rng:optional>
<rng:optional><rng:attribute name="colspan"/></rng:optional> <rng:optional><rng:attribute name="colspan"/></rng:optional>
<rng:optional><rng:attribute name="string"/></rng:optional> <rng:optional><rng:attribute name="string"/></rng:optional>
<rng:optional><rng:attribute name="angle"/></rng:optional>
<rng:zeroOrMore>
<rng:text/>
</rng:zeroOrMore>
</rng:element>
</rng:define>
<rng:define name="level">
<rng:element name="level">
<rng:optional><rng:attribute name="object"/></rng:optional>
<rng:optional><rng:attribute name="link"/></rng:optional>
<rng:optional><rng:attribute name="domain"/></rng:optional>
<rng:oneOrMore>
<rng:optional><rng:ref name="field"/></rng:optional>
</rng:oneOrMore>
</rng:element>
</rng:define>
<rng:define name="gantt">
<rng:element name="gantt">
<rng:optional><rng:attribute name="color"/></rng:optional>
<rng:optional><rng:attribute name="date_delay"/></rng:optional>
<rng:optional><rng:attribute name="date_start"/></rng:optional>
<rng:optional><rng:attribute name="date_string"/></rng:optional>
<rng:optional><rng:attribute name="string"/></rng:optional>
<rng:zeroOrMore>
<rng:optional><rng:ref name="level"/></rng:optional>
<rng:optional><rng:ref name="field"/></rng:optional>
</rng:zeroOrMore>
</rng:element> </rng:element>
</rng:define> </rng:define>
@ -123,6 +156,17 @@
<rng:optional><rng:attribute name="col"/></rng:optional> <rng:optional><rng:attribute name="col"/></rng:optional>
<rng:optional><rng:attribute name="select"/></rng:optional> <rng:optional><rng:attribute name="select"/></rng:optional>
<rng:optional><rng:attribute name="position"/></rng:optional> <rng:optional><rng:attribute name="position"/></rng:optional>
<rng:zeroOrMore>
<rng:choice>
<rng:ref name="separator"/>
<rng:ref name="button"/>
<rng:ref name="field"/>
<rng:ref name="label" />
<rng:ref name="group" />
<rng:element name="properties"><rng:empty/></rng:element>
<rng:element name="newline"><rng:empty/></rng:element>
</rng:choice>
</rng:zeroOrMore>
</rng:element> </rng:element>
</rng:define> </rng:define>
@ -132,9 +176,10 @@
<rng:optional><rng:attribute name="position"/></rng:optional> <rng:optional><rng:attribute name="position"/></rng:optional>
<rng:zeroOrMore> <rng:zeroOrMore>
<rng:choice> <rng:choice>
<rng:ref name="notebook" /> <rng:ref name="notebook" />
<rng:ref name="graph" /> <rng:ref name="graph" />
<rng:ref name="calendar" /> <rng:ref name="calendar" />
<rng:ref name="gantt" />
<rng:ref name="form"/> <rng:ref name="form"/>
<rng:ref name="tree"/> <rng:ref name="tree"/>
<rng:ref name="field"/> <rng:ref name="field"/>
@ -181,17 +226,25 @@
<rng:optional><rng:attribute name="digits"/></rng:optional> <rng:optional><rng:attribute name="digits"/></rng:optional>
<rng:optional><rng:attribute name="icon"/></rng:optional> <rng:optional><rng:attribute name="icon"/></rng:optional>
<rng:optional><rng:attribute name="mode"/></rng:optional> <rng:optional><rng:attribute name="mode"/></rng:optional>
<rng:optional><rng:attribute name="img_width"/></rng:optional>
<rng:optional><rng:attribute name="img_height"/></rng:optional>
<rng:optional><rng:attribute name="size"/></rng:optional> <rng:optional><rng:attribute name="size"/></rng:optional>
<rng:optional><rng:attribute name="filename"/></rng:optional>
<rng:optional><rng:attribute name="fieldname"/></rng:optional>
<rng:optional><rng:attribute name="height"/></rng:optional> <rng:optional><rng:attribute name="height"/></rng:optional>
<rng:optional><rng:attribute name="rowspan"/></rng:optional>
<rng:optional><rng:attribute name="align"/></rng:optional>
<rng:zeroOrMore> <rng:zeroOrMore>
<rng:choice> <rng:choice>
<rng:ref name="form"/> <rng:ref name="form"/>
<rng:ref name="tree"/> <rng:ref name="tree"/>
<rng:ref name="field"/> <rng:ref name="field"/>
<rng:ref name="label"/>
<rng:ref name="separator"/> <rng:ref name="separator"/>
<rng:ref name="xpath"/> <rng:ref name="xpath"/>
<rng:ref name="button"/> <rng:ref name="button"/>
<rng:ref name="group"/> <rng:ref name="group"/>
<rng:ref name="graph"/>
<rng:element name="newline"><rng:empty/></rng:element> <rng:element name="newline"><rng:empty/></rng:element>
</rng:choice> </rng:choice>
</rng:zeroOrMore> </rng:zeroOrMore>
@ -203,12 +256,15 @@
<rng:optional><rng:attribute name="attrs"/></rng:optional> <rng:optional><rng:attribute name="attrs"/></rng:optional>
<rng:optional><rng:attribute name="col"/></rng:optional> <rng:optional><rng:attribute name="col"/></rng:optional>
<rng:optional><rng:attribute name="colspan"/></rng:optional> <rng:optional><rng:attribute name="colspan"/></rng:optional>
<rng:optional><rng:attribute name="position"/></rng:optional>
<rng:optional><rng:attribute name="expand"/></rng:optional> <rng:optional><rng:attribute name="expand"/></rng:optional>
<rng:optional><rng:attribute name="states"/></rng:optional> <rng:optional><rng:attribute name="states"/></rng:optional>
<rng:optional><rng:attribute name="groups"/></rng:optional> <rng:optional><rng:attribute name="groups"/></rng:optional>
<rng:optional><rng:attribute name="string"/></rng:optional> <rng:optional><rng:attribute name="string"/></rng:optional>
<rng:optional><rng:attribute name="fill"/></rng:optional> <rng:optional><rng:attribute name="fill"/></rng:optional>
<rng:optional><rng:attribute name="height"/></rng:optional> <rng:optional><rng:attribute name="height"/></rng:optional>
<rng:optional><rng:attribute name="name"/></rng:optional>
<rng:optional><rng:attribute name="color" /></rng:optional>
<rng:zeroOrMore> <rng:zeroOrMore>
<rng:choice> <rng:choice>
<rng:ref name="separator"/> <rng:ref name="separator"/>
@ -231,6 +287,7 @@
<rng:optional><rng:attribute name="date_stop" /></rng:optional> <rng:optional><rng:attribute name="date_stop" /></rng:optional>
<rng:optional><rng:attribute name="day_length" /></rng:optional> <rng:optional><rng:attribute name="day_length" /></rng:optional>
<rng:optional><rng:attribute name="date_delay" /></rng:optional> <rng:optional><rng:attribute name="date_delay" /></rng:optional>
<rng:optional><rng:attribute name="type" /></rng:optional>
<rng:oneOrMore> <rng:oneOrMore>
<rng:ref name="field"/> <rng:ref name="field"/>
</rng:oneOrMore> </rng:oneOrMore>
@ -242,6 +299,7 @@
<rng:optional><rng:attribute name="string" /></rng:optional> <rng:optional><rng:attribute name="string" /></rng:optional>
<rng:optional><rng:attribute name="orientation" /></rng:optional> <rng:optional><rng:attribute name="orientation" /></rng:optional>
<rng:optional><rng:attribute name="type" /></rng:optional> <rng:optional><rng:attribute name="type" /></rng:optional>
<rng:optional><rng:attribute name="color"/></rng:optional>
<rng:oneOrMore> <rng:oneOrMore>
<rng:ref name="field"/> <rng:ref name="field"/>
</rng:oneOrMore> </rng:oneOrMore>
@ -263,18 +321,20 @@
<rng:optional><rng:attribute name="target"/></rng:optional> <rng:optional><rng:attribute name="target"/></rng:optional>
<rng:optional><rng:attribute name="readonly"/></rng:optional> <rng:optional><rng:attribute name="readonly"/></rng:optional>
<rng:optional><rng:attribute name="position"/></rng:optional> <rng:optional><rng:attribute name="position"/></rng:optional>
<rng:optional><rng:attribute name="context"/></rng:optional>
<rng:zeroOrMore> <rng:zeroOrMore>
<rng:choice> <rng:choice>
<rng:ref name="form" /> <rng:ref name="form" />
<rng:ref name="field" /> <rng:ref name="field" />
<rng:ref name="tree" /> <rng:ref name="tree" />
<rng:ref name="notebook" /> <rng:ref name="notebook" />
<rng:ref name="graph" /> <rng:ref name="graph" />
<rng:ref name="calendar" /> <rng:ref name="calendar" />
<rng:ref name="xpath" /> <rng:ref name="gantt" />
<rng:ref name="page" /> <rng:ref name="xpath" />
<rng:ref name="separator"/> <rng:ref name="page" />
<rng:ref name="button"/> <rng:ref name="separator"/>
<rng:ref name="button"/>
<rng:element name="properties"><rng:empty/></rng:element> <rng:element name="properties"><rng:empty/></rng:element>
<rng:element name="newline"><rng:empty/></rng:element> <rng:element name="newline"><rng:empty/></rng:element>
</rng:choice> </rng:choice>
@ -286,11 +346,13 @@
<rng:start> <rng:start>
<rng:choice> <rng:choice>
<rng:ref name="form" /> <rng:ref name="form" />
<rng:ref name="group" />
<rng:ref name="field" /> <rng:ref name="field" />
<rng:ref name="tree" /> <rng:ref name="tree" />
<rng:ref name="notebook" /> <rng:ref name="notebook" />
<rng:ref name="graph" /> <rng:ref name="graph" />
<rng:ref name="calendar" /> <rng:ref name="calendar" />
<rng:ref name="gantt" />
<rng:ref name="xpath" /> <rng:ref name="xpath" />
<rng:ref name="page" /> <rng:ref name="page" />
<rng:ref name="separator"/> <rng:ref name="separator"/>

View File

@ -2,7 +2,7 @@
# -*- encoding: utf-8 -*- # -*- encoding: utf-8 -*-
############################################################################## ##############################################################################
# #
# OpenERP, Open Source Management Solution # OpenERP, Open Source Management Solution
# Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved # Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$ # $Id$
# #
@ -157,13 +157,12 @@ def init_logger():
if config['logfile']: if config['logfile']:
logf = config['logfile'] logf = config['logfile']
# test if the directories exist, else create them
try: try:
dirname = os.path.dirname(logf) dirname = os.path.dirname(logf)
if not os.path.isdir(dirname): if dirname and not os.path.isdir(dirname):
res = os.makedirs(dirname) os.makedirs(dirname)
handler = logging.handlers.TimedRotatingFileHandler(logf,'D',1,30) handler = logging.handlers.TimedRotatingFileHandler(logf,'D',1,30)
except: except Exception, ex:
sys.stderr.write("ERROR: couldn't create the logfile directory\n") sys.stderr.write("ERROR: couldn't create the logfile directory\n")
handler = logging.StreamHandler(sys.stdout) handler = logging.StreamHandler(sys.stdout)
else: else:
@ -177,7 +176,7 @@ def init_logger():
# add the handler to the root logger # add the handler to the root logger
logging.getLogger().addHandler(handler) logging.getLogger().addHandler(handler)
logging.getLogger().setLevel(logging.INFO) logging.getLogger().setLevel(config['log_level'])
if (not isinstance(handler, logging.FileHandler)) and os.name != 'nt': if (not isinstance(handler, logging.FileHandler)) and os.name != 'nt':
# change color of level names # change color of level names
@ -204,8 +203,16 @@ def init_logger():
class Logger(object): class Logger(object):
def notifyChannel(self, name, level, msg): def notifyChannel(self, name, level, msg):
log = logging.getLogger(name) log = logging.getLogger(name)
getattr(log, level)(msg) level_method = getattr(log, level)
result = str(msg).strip().split('\n')
if len(result)>1:
for idx, s in enumerate(result):
level_method('[%02d]: %s' % (idx+1, s,))
elif result:
level_method(result[0])
init_logger()
class Agent(object): class Agent(object):
_timers = [] _timers = []
@ -391,12 +398,14 @@ class TinySocketClientThread(threading.Thread):
ts.mysend(r) ts.mysend(r)
except Exception, e: except Exception, e:
tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback)) tb_s = reduce(lambda x, y: x+y, traceback.format_exception(sys.exc_type, sys.exc_value, sys.exc_traceback))
s = str(e) print e
print tb_s
import tools import tools
if tools.config['debug_mode']: if tools.config['debug_mode']:
import pdb import pdb
tb = sys.exc_info()[2] tb = sys.exc_info()[2]
pdb.post_mortem(tb) pdb.post_mortem(tb)
e = Exception(e.message)
ts.mysend(e, exception=True, traceback=tb_s) ts.mysend(e, exception=True, traceback=tb_s)
except: except:
pass pass

View File

@ -53,23 +53,24 @@ __version__ = release.version
# get logger # get logger
#---------------------------------------------------------- #----------------------------------------------------------
import netsvc import netsvc
netsvc.init_logger()
logger = netsvc.Logger() logger = netsvc.Logger()
#----------------------------------------------------------------------- #-----------------------------------------------------------------------
# import the tools module so that the commandline parameters are parsed # import the tools module so that the commandline parameters are parsed
#----------------------------------------------------------------------- #-----------------------------------------------------------------------
import tools import tools
logger.notifyChannel("server", netsvc.LOG_INFO, "version - %s" % release.version )
for name, value in [('addons_path', tools.config['addons_path']),
('database hostname', tools.config['db_host'] or 'localhost')]:
logger.notifyChannel("server", netsvc.LOG_INFO, "%s - %s" % ( name, value ))
import time import time
if sys.platform == 'win32': if sys.platform == 'win32':
import mx.DateTime import mx.DateTime
mx.DateTime.strptime = lambda x, y: mx.DateTime.mktime(time.strptime(x, y)) mx.DateTime.strptime = lambda x, y: mx.DateTime.mktime(time.strptime(x, y))
#os.chdir(tools.file_path_root)
#---------------------------------------------------------- #----------------------------------------------------------
# init net service # init net service
#---------------------------------------------------------- #----------------------------------------------------------
@ -81,35 +82,13 @@ dispatcher.monitor(signal.SIGINT)
#--------------------------------------------------------------- #---------------------------------------------------------------
# connect to the database and initialize it with base if needed # connect to the database and initialize it with base if needed
#--------------------------------------------------------------- #---------------------------------------------------------------
import psycopg
import pooler import pooler
db_name = tools.config["db_name"]
# test whether it is needed to initialize the db (the db is empty)
#try:
# cr = pooler.get_db_only(db_name).cursor()
#except psycopg.OperationalError:
# logger.notifyChannel("init", netsvc.LOG_INFO, "could not connect to database '%s'!" % db_name,)
# cr = None
#if cr:
# cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname='ir_ui_menu'")
# if len(cr.fetchall())==0:
##if False:
# logger.notifyChannel("init", netsvc.LOG_INFO, "init db")
# tools.init_db(cr)
# # in that case, force --init=all
# tools.config["init"]["all"] = 1
# tools.config['update']['all'] = 1
# if not tools.config['without_demo']:
# tools.config["demo"]['all'] = 1
# cr.close()
#---------------------------------------------------------- #----------------------------------------------------------
# launch modules install/upgrade/removes if needed # launch modules install/upgrade/removes if needed
#---------------------------------------------------------- #----------------------------------------------------------
if tools.config['upgrade']: if tools.config['upgrade']:
print 'Upgrading new modules...' logger.notifyChannel('init', netsvc.LOG_INFO, 'Upgrading new modules...')
import tools.upgrade import tools.upgrade
(toinit, toupdate) = tools.upgrade.upgrade() (toinit, toupdate) = tools.upgrade.upgrade()
for m in toinit: for m in toinit:
@ -120,15 +99,24 @@ if tools.config['upgrade']:
#---------------------------------------------------------- #----------------------------------------------------------
# import basic modules # import basic modules
#---------------------------------------------------------- #----------------------------------------------------------
import osv, workflow, report, service import osv
import workflow
import report
import service
#---------------------------------------------------------- #----------------------------------------------------------
# import addons # import addons
#---------------------------------------------------------- #----------------------------------------------------------
import addons import addons
if tools.config['init'] or tools.config['update']: #----------------------------------------------------------
pooler.get_db_and_pool(tools.config['db_name'], update_module=True) # Load and update databases if requested
#----------------------------------------------------------
if tools.config['db_name']:
for db in tools.config['db_name'].split(','):
pooler.get_db_and_pool(db, update_module=tools.config['init'] or tools.config['update'])
#---------------------------------------------------------- #----------------------------------------------------------
# translation stuff # translation stuff
@ -179,17 +167,14 @@ if tools.config['xmlrpc']:
if tools.config["xmlrpc"]: if tools.config["xmlrpc"]:
xml_gw = netsvc.xmlrpc.RpcGateway('web-services') xml_gw = netsvc.xmlrpc.RpcGateway('web-services')
httpd.attach("/xmlrpc", xml_gw) httpd.attach("/xmlrpc", xml_gw)
logger.notifyChannel("web-services", netsvc.LOG_INFO, logger.notifyChannel("web-services", netsvc.LOG_INFO, "starting XML-RPC%s services, port %s" % ((tools.config['secure'] and ' Secure' or ''), port))
"starting XML-RPC" + \
(tools.config['secure'] and ' Secure' or '') + \
" services, port " + str(port))
# #
#if tools.config["soap"]: #if tools.config["soap"]:
# soap_gw = netsvc.xmlrpc.RpcGateway('web-services') # soap_gw = netsvc.xmlrpc.RpcGateway('web-services')
# httpd.attach("/soap", soap_gw ) # httpd.attach("/soap", soap_gw )
# logger.notifyChannel("web-services", netsvc.LOG_INFO, 'starting SOAP services, port '+str(port)) # logger.notifyChannel("web-services", netsvc.LOG_INFO, 'starting SOAP services, port '+str(port))
# #
if tools.config['netrpc']: if tools.config['netrpc']:
try: try:

View File

@ -51,7 +51,7 @@ class expression(object):
subids = ids[i:i+cr.IN_MAX] subids = ids[i:i+cr.IN_MAX]
cr.execute('SELECT "%s"' \ cr.execute('SELECT "%s"' \
' FROM "%s"' \ ' FROM "%s"' \
' WHERE "%s" in (%s)' % (s, f, w, ','.join(['%d']*len(subids))), ' WHERE "%s" in (%s)' % (s, f, w, ','.join(['%s']*len(subids))),
subids) subids)
res.extend([r[0] for r in cr.fetchall()]) res.extend([r[0] for r in cr.fetchall()])
return res return res
@ -244,7 +244,7 @@ class expression(object):
if len_after: if len_after:
if left == 'id': if left == 'id':
instr = ','.join(['%d'] * len_after) instr = ','.join(['%s'] * len_after)
else: else:
instr = ','.join([table._columns[left]._symbol_set[0]] * len_after) instr = ','.join([table._columns[left]._symbol_set[0]] * len_after)
query = '(%s.%s %s (%s))' % (table._table, left, operator, instr) query = '(%s.%s %s (%s))' % (table._table, left, operator, instr)

View File

@ -35,7 +35,7 @@
import string import string
import netsvc import netsvc
import psycopg from psycopg2 import Binary
import warnings import warnings
import tools import tools
@ -86,7 +86,7 @@ class _column(object):
pass pass
def set(self, cr, obj, id, name, value, user=None, context=None): def set(self, cr, obj, id, name, value, user=None, context=None):
cr.execute('update '+obj._table+' set '+name+'='+self._symbol_set[0]+' where id=%d', (self._symbol_set[1](value), id)) cr.execute('update '+obj._table+' set '+name+'='+self._symbol_set[0]+' where id=%s', (self._symbol_set[1](value), id))
def set_memory(self, cr, obj, id, name, value, user=None, context=None): def set_memory(self, cr, obj, id, name, value, user=None, context=None):
raise Exception(_('Not implemented set_memory method !')) raise Exception(_('Not implemented set_memory method !'))
@ -118,13 +118,13 @@ class boolean(_column):
class integer_big(_column): class integer_big(_column):
_type = 'integer_big' _type = 'integer_big'
_symbol_c = '%d' _symbol_c = '%s'
_symbol_f = lambda x: int(x or 0) _symbol_f = lambda x: int(x or 0)
_symbol_set = (_symbol_c, _symbol_f) _symbol_set = (_symbol_c, _symbol_f)
class integer(_column): class integer(_column):
_type = 'integer' _type = 'integer'
_symbol_c = '%d' _symbol_c = '%s'
_symbol_f = lambda x: int(x or 0) _symbol_f = lambda x: int(x or 0)
_symbol_set = (_symbol_c, _symbol_f) _symbol_set = (_symbol_c, _symbol_f)
@ -168,10 +168,9 @@ class text(_column):
import __builtin__ import __builtin__
class float(_column): class float(_column):
_type = 'float' _type = 'float'
_symbol_c = '%f' _symbol_c = '%s'
_symbol_f = lambda x: __builtin__.float(x or 0.0) _symbol_f = lambda x: __builtin__.float(x or 0.0)
_symbol_set = (_symbol_c, _symbol_f) _symbol_set = (_symbol_c, _symbol_f)
@ -195,8 +194,9 @@ class time(_column):
class binary(_column): class binary(_column):
_type = 'binary' _type = 'binary'
_symbol_c = '%s' _symbol_c = '%s'
_symbol_f = lambda symb: symb and psycopg.Binary(symb) or None _symbol_f = lambda symb: symb and Binary(symb) or None
_symbol_set = (_symbol_c, _symbol_f) _symbol_set = (_symbol_c, _symbol_f)
_symbol_get = lambda self, x: x and str(x)
_classic_read = False _classic_read = False
@ -209,7 +209,6 @@ class binary(_column):
context = {} context = {}
if not values: if not values:
values = [] values = []
res = {} res = {}
for i in ids: for i in ids:
val = None val = None
@ -217,10 +216,10 @@ class binary(_column):
if v['id'] == i: if v['id'] == i:
val = v[name] val = v[name]
break break
res.setdefault(i, val)
if context.get('bin_size', False): if context.get('bin_size', False):
res[i] = tools.human_size(val) res[i] = tools.human_size(val)
else:
res[i] = val
return res return res
get = get_memory get = get_memory
@ -263,9 +262,9 @@ class one2one(_column):
self._table = obj_src.pool.get(self._obj)._table self._table = obj_src.pool.get(self._obj)._table
if act[0] == 0: if act[0] == 0:
id_new = obj.create(cr, user, act[1]) id_new = obj.create(cr, user, act[1])
cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (id_new, id)) cr.execute('update '+obj_src._table+' set '+field+'=%s where id=%s', (id_new, id))
else: else:
cr.execute('select '+field+' from '+obj_src._table+' where id=%d', (act[0],)) cr.execute('select '+field+' from '+obj_src._table+' where id=%s', (act[0],))
id = cr.fetchone()[0] id = cr.fetchone()[0]
obj.write(cr, user, [id], act[1], context=context) obj.write(cr, user, [id], act[1], context=context)
@ -277,6 +276,9 @@ class many2one(_column):
_classic_read = False _classic_read = False
_classic_write = True _classic_write = True
_type = 'many2one' _type = 'many2one'
_symbol_c = '%s'
_symbol_f = lambda x: x or None
_symbol_set = (_symbol_c, _symbol_f)
def __init__(self, obj, string='unknown', **args): def __init__(self, obj, string='unknown', **args):
_column.__init__(self, string=string, **args) _column.__init__(self, string=string, **args)
@ -331,20 +333,20 @@ class many2one(_column):
for act in values: for act in values:
if act[0] == 0: if act[0] == 0:
id_new = obj.create(cr, act[2]) id_new = obj.create(cr, act[2])
cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (id_new, id)) cr.execute('update '+obj_src._table+' set '+field+'=%s where id=%s', (id_new, id))
elif act[0] == 1: elif act[0] == 1:
obj.write(cr, [act[1]], act[2], context=context) obj.write(cr, [act[1]], act[2], context=context)
elif act[0] == 2: elif act[0] == 2:
cr.execute('delete from '+self._table+' where id=%d', (act[1],)) cr.execute('delete from '+self._table+' where id=%s', (act[1],))
elif act[0] == 3 or act[0] == 5: elif act[0] == 3 or act[0] == 5:
cr.execute('update '+obj_src._table+' set '+field+'=null where id=%d', (id,)) cr.execute('update '+obj_src._table+' set '+field+'=null where id=%s', (id,))
elif act[0] == 4: elif act[0] == 4:
cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (act[1], id)) cr.execute('update '+obj_src._table+' set '+field+'=%s where id=%s', (act[1], id))
else: else:
if values: if values:
cr.execute('update '+obj_src._table+' set '+field+'=%d where id=%d', (values, id)) cr.execute('update '+obj_src._table+' set '+field+'=%s where id=%s', (values, id))
else: else:
cr.execute('update '+obj_src._table+' set '+field+'=null where id=%d', (id,)) cr.execute('update '+obj_src._table+' set '+field+'=null where id=%s', (id,))
def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None): def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None):
return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name', 'like', value)], offset, limit) return obj.pool.get(self._obj).search(cr, uid, args+self._domain+[('name', 'like', value)], offset, limit)
@ -435,19 +437,19 @@ class one2many(_column):
elif act[0] == 2: elif act[0] == 2:
obj.unlink(cr, user, [act[1]], context=context) obj.unlink(cr, user, [act[1]], context=context)
elif act[0] == 3: elif act[0] == 3:
cr.execute('update '+_table+' set '+self._fields_id+'=null where id=%d', (act[1],)) cr.execute('update '+_table+' set '+self._fields_id+'=null where id=%s', (act[1],))
elif act[0] == 4: elif act[0] == 4:
cr.execute('update '+_table+' set '+self._fields_id+'=%d where id=%d', (id, act[1])) cr.execute('update '+_table+' set '+self._fields_id+'=%s where id=%s', (id, act[1]))
elif act[0] == 5: elif act[0] == 5:
cr.execute('update '+_table+' set '+self._fields_id+'=null where '+self._fields_id+'=%d', (id,)) cr.execute('update '+_table+' set '+self._fields_id+'=null where '+self._fields_id+'=%s', (id,))
elif act[0] == 6: elif act[0] == 6:
if not act[2]: if not act[2]:
ids2 = [0] ids2 = [0]
else: else:
ids2 = act[2] ids2 = act[2]
cr.execute('update '+_table+' set '+self._fields_id+'=NULL where '+self._fields_id+'=%d and id not in ('+','.join(map(str, ids2))+')', (id,)) cr.execute('update '+_table+' set '+self._fields_id+'=NULL where '+self._fields_id+'=%s and id not in ('+','.join(map(str, ids2))+')', (id,))
if act[2]: if act[2]:
cr.execute('update '+_table+' set '+self._fields_id+'=%d where id in ('+','.join(map(str, act[2]))+')', (id,)) cr.execute('update '+_table+' set '+self._fields_id+'=%s where id in ('+','.join(map(str, act[2]))+')', (id,))
def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like'): def search(self, cr, obj, args, name, value, offset=0, limit=None, uid=None, operator='like'):
return obj.pool.get(self._obj).name_search(cr, uid, value, self._domain, offset, limit) return obj.pool.get(self._obj).name_search(cr, uid, value, self._domain, offset, limit)
@ -500,7 +502,7 @@ class many2many(_column):
FROM '+self._rel+' , '+obj._table+' \ FROM '+self._rel+' , '+obj._table+' \
WHERE '+self._rel+'.'+self._id1+' in ('+ids_s+') \ WHERE '+self._rel+'.'+self._id1+' in ('+ids_s+') \
AND '+self._rel+'.'+self._id2+' = '+obj._table+'.id '+d1 AND '+self._rel+'.'+self._id2+' = '+obj._table+'.id '+d1
+limit_str+' order by '+obj._table+'.'+obj._order+' offset %d', +limit_str+' order by '+obj._table+'.'+obj._order+' offset %s',
d2+[offset]) d2+[offset])
for r in cr.fetchall(): for r in cr.fetchall():
res[r[1]].append(r[0]) res[r[1]].append(r[0])
@ -515,26 +517,26 @@ class many2many(_column):
for act in values: for act in values:
if act[0] == 0: if act[0] == 0:
idnew = obj.create(cr, user, act[2]) idnew = obj.create(cr, user, act[2])
cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%d,%d)', (id, idnew)) cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%s,%s)', (id, idnew))
elif act[0] == 1: elif act[0] == 1:
obj.write(cr, user, [act[1]], act[2], context=context) obj.write(cr, user, [act[1]], act[2], context=context)
elif act[0] == 2: elif act[0] == 2:
obj.unlink(cr, user, [act[1]], context=context) obj.unlink(cr, user, [act[1]], context=context)
elif act[0] == 3: elif act[0] == 3:
cr.execute('delete from '+self._rel+' where ' + self._id1 + '=%d and '+ self._id2 + '=%d', (id, act[1])) cr.execute('delete from '+self._rel+' where ' + self._id1 + '=%s and '+ self._id2 + '=%s', (id, act[1]))
elif act[0] == 4: elif act[0] == 4:
cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%d,%d)', (id, act[1])) cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%s,%s)', (id, act[1]))
elif act[0] == 5: elif act[0] == 5:
cr.execute('update '+self._rel+' set '+self._id2+'=null where '+self._id2+'=%d', (id,)) cr.execute('update '+self._rel+' set '+self._id2+'=null where '+self._id2+'=%s', (id,))
elif act[0] == 6: elif act[0] == 6:
d1, d2 = obj.pool.get('ir.rule').domain_get(cr, user, obj._name) d1, d2 = obj.pool.get('ir.rule').domain_get(cr, user, obj._name)
if d1: if d1:
d1 = ' and ' + d1 d1 = ' and ' + d1
cr.execute('delete from '+self._rel+' where '+self._id1+'=%d AND '+self._id2+' IN (SELECT '+self._rel+'.'+self._id2+' FROM '+self._rel+', '+obj._table+' WHERE '+self._rel+'.'+self._id1+'=%d AND '+self._rel+'.'+self._id2+' = '+obj._table+'.id '+ d1 +')', [id, id]+d2) cr.execute('delete from '+self._rel+' where '+self._id1+'=%s AND '+self._id2+' IN (SELECT '+self._rel+'.'+self._id2+' FROM '+self._rel+', '+obj._table+' WHERE '+self._rel+'.'+self._id1+'=%s AND '+self._rel+'.'+self._id2+' = '+obj._table+'.id '+ d1 +')', [id, id]+d2)
for act_nbr in act[2]: for act_nbr in act[2]:
cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%d, %d)', (id, act_nbr)) cr.execute('insert into '+self._rel+' ('+self._id1+','+self._id2+') values (%s, %s)', (id, act_nbr))
# #
# TODO: use a name_search # TODO: use a name_search
@ -601,9 +603,9 @@ class function(_column):
self._classic_read = True self._classic_read = True
self._classic_write = True self._classic_write = True
if type == 'float': if type == 'float':
self._symbol_c = '%f' self._symbol_c = float._symbol_c
self._symbol_f = lambda x: __builtin__.float(x or 0.0) self._symbol_f = float._symbol_f
self._symbol_set = (self._symbol_c, self._symbol_f) self._symbol_set = float._symbol_set
def search(self, cr, uid, obj, name, args): def search(self, cr, uid, obj, name, args):
if not self._fnct_search: if not self._fnct_search:
@ -655,8 +657,33 @@ class related(function):
i -= 1 i -= 1
return [(self._arg[0], 'in', sarg)] return [(self._arg[0], 'in', sarg)]
def _fnct_write(self,obj,cr, uid, ids,values, field_name, args, context=None): def _fnct_write(self,obj,cr, uid, ids, field_name, values, args, context=None):
raise 'Not Implemented Yet' print 'Related Write', obj._name
if values and field_name:
self._field_get2(cr, uid, obj, context)
relation = obj._name
res = {}
if type(ids) != type([]):
ids=[ids]
objlst = obj.browse(cr, uid, ids)
for data in objlst:
t_id=None
t_data = data
relation = obj._name
for i in range(len(self.arg)):
field_detail = self._relations[i]
relation = field_detail['object']
if not t_data[self.arg[i]]:
t_data = False
break
if field_detail['type'] in ('one2many', 'many2many'):
t_id=t_data.id
t_data = t_data[self.arg[i]][0]
else:
t_id=t_data['id']
t_data = t_data[self.arg[i]]
if t_id:
obj.pool.get(field_detail['object']).write(cr,uid,[t_id],{args[-1]:values})
def _fnct_read(self, obj, cr, uid, ids, field_name, args, context=None): def _fnct_read(self, obj, cr, uid, ids, field_name, args, context=None):
self._field_get2(cr, uid, obj, context) self._field_get2(cr, uid, obj, context)
@ -686,7 +713,7 @@ class related(function):
def __init__(self, *arg, **args): def __init__(self, *arg, **args):
self.arg = arg self.arg = arg
self._relations = [] self._relations = []
super(related, self).__init__(self._fnct_read, arg, fnct_inv_arg=arg, method=True, fnct_search=self._fnct_search, **args) super(related, self).__init__(self._fnct_read, arg, self._fnct_write, fnct_inv_arg=arg, method=True, fnct_search=self._fnct_search, **args)
def _field_get2(self, cr, uid, obj, context={}): def _field_get2(self, cr, uid, obj, context={}):
if self._relations: if self._relations:
@ -728,7 +755,7 @@ class property(function):
nid = property.search(cr, uid, [('fields_id', '=', definition_id), nid = property.search(cr, uid, [('fields_id', '=', definition_id),
('res_id', '=', obj._name+','+str(id))]) ('res_id', '=', obj._name+','+str(id))])
while len(nid): while len(nid):
cr.execute('DELETE FROM ir_property WHERE id=%d', (nid.pop(),)) cr.execute('DELETE FROM ir_property WHERE id=%s', (nid.pop(),))
nid = property.search(cr, uid, [('fields_id', '=', definition_id), nid = property.search(cr, uid, [('fields_id', '=', definition_id),
('res_id', '=', False)]) ('res_id', '=', False)])

View File

@ -349,7 +349,7 @@ class orm_template(object):
id, model_id, model, name, field_description, ttype, id, model_id, model, name, field_description, ttype,
relation,view_load,state,select_level relation,view_load,state,select_level
) VALUES ( ) VALUES (
%d,%s,%s,%s,%s,%s,%s,%s,%s,%s %s,%s,%s,%s,%s,%s,%s,%s,%s,%s
)""", ( )""", (
id, vals['model_id'], vals['model'], vals['name'], vals['field_description'], vals['ttype'], id, vals['model_id'], vals['model'], vals['name'], vals['field_description'], vals['ttype'],
vals['relation'], bool(vals['view_load']), 'base', vals['relation'], bool(vals['view_load']), 'base',
@ -732,7 +732,6 @@ class orm_template(object):
and getattr(self._columns[f], arg): and getattr(self._columns[f], arg):
res[f][arg] = getattr(self._columns[f], arg) res[f][arg] = getattr(self._columns[f], arg)
# translate the field label
res_trans = translation_obj._get_source(cr, user, res_trans = translation_obj._get_source(cr, user,
self._name + ',' + f, 'field', context.get('lang', False) or 'en_US') self._name + ',' + f, 'field', context.get('lang', False) or 'en_US')
if res_trans: if res_trans:
@ -843,15 +842,15 @@ class orm_template(object):
# translate view # translate view
if ('lang' in context) and not result: if ('lang' in context) and not result:
if node.hasAttribute('string') and node.getAttribute('string'): if node.hasAttribute('string') and node.getAttribute('string'):
trans = tools.translate(cr, self._name, 'view', context['lang'], node.getAttribute('string').encode('utf8')) trans = self.pool.get('ir.translation')._get_source(cr, user, self._name, 'view', context['lang'], node.getAttribute('string').encode('utf8'))
if not trans and ('base_model_name' in context): if not trans and ('base_model_name' in context):
trans = tools.translate(cr, context['base_model_name'], 'view', context['lang'], node.getAttribute('string').encode('utf8')) trans = self.pool.get('ir.translation')._get_source(cr, user, context['base_model_name'], 'view', context['lang'], node.getAttribute('string').encode('utf8'))
if trans: if trans:
node.setAttribute('string', trans.decode('utf8')) node.setAttribute('string', trans)
if node.hasAttribute('sum') and node.getAttribute('sum'): if node.hasAttribute('sum') and node.getAttribute('sum'):
trans = tools.translate(cr, self._name, 'view', context['lang'], node.getAttribute('sum').encode('utf8')) trans = self.pool.get('ir.translation')._get_source(cr, user, self._name, 'view', context['lang'], node.getAttribute('sum').encode('utf8'))
if trans: if trans:
node.setAttribute('sum', trans.decode('utf8')) node.setAttribute('sum', trans)
if childs: if childs:
for f in node.childNodes: for f in node.childNodes:
@ -1004,7 +1003,7 @@ class orm_template(object):
while ok: while ok:
if view_id: if view_id:
where = (model and (" and model='%s'" % (self._name,))) or '' where = (model and (" and model='%s'" % (self._name,))) or ''
cr.execute('SELECT arch,name,field_parent,id,type,inherit_id FROM ir_ui_view WHERE id=%d'+where, (view_id,)) cr.execute('SELECT arch,name,field_parent,id,type,inherit_id FROM ir_ui_view WHERE id=%s'+where, (view_id,))
else: else:
cr.execute('''SELECT cr.execute('''SELECT
arch,name,field_parent,id,type,inherit_id arch,name,field_parent,id,type,inherit_id
@ -1030,7 +1029,7 @@ class orm_template(object):
def _inherit_apply_rec(result, inherit_id): def _inherit_apply_rec(result, inherit_id):
# get all views which inherit from (ie modify) this view # get all views which inherit from (ie modify) this view
cr.execute('select arch,id from ir_ui_view where inherit_id=%d and model=%s order by priority', (inherit_id, self._name)) cr.execute('select arch,id from ir_ui_view where inherit_id=%s and model=%s order by priority', (inherit_id, self._name))
sql_inherit = cr.fetchall() sql_inherit = cr.fetchall()
for (inherit, id) in sql_inherit: for (inherit, id) in sql_inherit:
result = _inherit_apply(result, inherit) result = _inherit_apply(result, inherit)
@ -1377,15 +1376,36 @@ class orm(orm_template):
childs = cr.fetchall() childs = cr.fetchall()
for id in childs: for id in childs:
pos2 = browse_rec(id[0], pos2) pos2 = browse_rec(id[0], pos2)
cr.execute('update '+self._table+' set parent_left=%d, parent_right=%d where id=%d', (pos,pos2,root)) cr.execute('update '+self._table+' set parent_left=%s, parent_right=%s where id=%s', (pos,pos2,root))
return pos2+1 return pos2+1
browse_rec(None) browse_rec(None)
return True return True
def _update_store(self, cr, f, k):
logger = netsvc.Logger()
logger.notifyChannel('init', netsvc.LOG_INFO, "storing computed values of fields.function '%s'" % (k,))
ss = self._columns[k]._symbol_set
update_query = 'UPDATE "%s" SET "%s"=%s WHERE id=%%s' % (self._table, k, ss[0])
cr.execute('select id from '+self._table)
ids_lst = map(lambda x: x[0], cr.fetchall())
while ids_lst:
iids = ids_lst[:40]
ids_lst = ids_lst[40:]
res = f.get(cr, self, iids, k, 1, {})
for key,val in res.items():
if f._multi:
val = val[k]
# if val is a many2one, just write the ID
if type(val)==tuple:
val = val[0]
if (val<>False) or (type(val)<>bool):
cr.execute(update_query, (ss[1](val), key))
def _auto_init(self, cr, context={}): def _auto_init(self, cr, context={}):
store_compute = False store_compute = False
logger = netsvc.Logger() logger = netsvc.Logger()
create = False create = False
todo_end = []
self._field_create(cr, context=context) self._field_create(cr, context=context)
if not hasattr(self, "_auto") or self._auto: if not hasattr(self, "_auto") or self._auto:
cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname='%s'" % self._table) cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname='%s'" % self._table)
@ -1405,8 +1425,8 @@ class orm(orm_template):
logger.notifyChannel('init', netsvc.LOG_ERROR, 'create a column parent_right on object %s: fields.integer(\'Right Parent\', select=1)' % (self._table, )) logger.notifyChannel('init', netsvc.LOG_ERROR, 'create a column parent_right on object %s: fields.integer(\'Right Parent\', select=1)' % (self._table, ))
if self._columns[self._parent_name].ondelete<>'cascade': if self._columns[self._parent_name].ondelete<>'cascade':
logger.notifyChannel('init', netsvc.LOG_ERROR, "the columns %s on object must be set as ondelete='cascasde'" % (self._name, self._parent_name)) logger.notifyChannel('init', netsvc.LOG_ERROR, "the columns %s on object must be set as ondelete='cascasde'" % (self._name, self._parent_name))
cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" INTEGER" % (self._table, 'parent_left')) cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_left" INTEGER' % (self._table,))
cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" INTEGER" % (self._table, 'parent_right')) cr.execute('ALTER TABLE "%s" ADD COLUMN "parent_right" INTEGER' % (self._table,))
cr.commit() cr.commit()
store_compute = True store_compute = True
@ -1418,15 +1438,13 @@ class orm(orm_template):
'write_date': 'TIMESTAMP' 'write_date': 'TIMESTAMP'
} }
for k in logs: for k in logs:
cr.execute( cr.execute("""
"""
SELECT c.relname SELECT c.relname
FROM pg_class c, pg_attribute a FROM pg_class c, pg_attribute a
WHERE c.relname='%s' AND a.attname='%s' AND c.oid=a.attrelid WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid
""" % (self._table, k)) """, (self._table, k))
if not cr.rowcount: if not cr.rowcount:
cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s" % cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, logs[k]))
(self._table, k, logs[k]))
cr.commit() cr.commit()
# iterate on the database columns to drop the NOT NULL constraints # iterate on the database columns to drop the NOT NULL constraints
@ -1434,12 +1452,12 @@ class orm(orm_template):
cr.execute( cr.execute(
"SELECT a.attname, a.attnotnull "\ "SELECT a.attname, a.attnotnull "\
"FROM pg_class c, pg_attribute a "\ "FROM pg_class c, pg_attribute a "\
"WHERE c.oid=a.attrelid AND c.relname='%s'" % self._table) "WHERE c.oid=a.attrelid AND c.relname=%s", (self._table,))
db_columns = cr.dictfetchall() db_columns = cr.dictfetchall()
for column in db_columns: for column in db_columns:
if column['attname'] not in ('id', 'oid', 'tableoid', 'ctid', 'xmin', 'xmax', 'cmin', 'cmax'): if column['attname'] not in ('id', 'oid', 'tableoid', 'ctid', 'xmin', 'xmax', 'cmin', 'cmax'):
if column['attnotnull'] and column['attname'] not in self._columns: if column['attnotnull'] and column['attname'] not in self._columns:
cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" DROP NOT NULL" % (self._table, column['attname'])) cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" DROP NOT NULL' % (self._table, column['attname']))
# iterate on the "object columns" # iterate on the "object columns"
todo_update_store = [] todo_update_store = []
@ -1452,10 +1470,10 @@ class orm(orm_template):
if isinstance(f, fields.one2many): if isinstance(f, fields.one2many):
cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname=%s", (f._obj,)) cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname=%s", (f._obj,))
if cr.fetchone(): if cr.fetchone():
cr.execute("SELECT count(*) as c FROM pg_class c,pg_attribute a WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid", (f._obj, f._fields_id)) cr.execute("SELECT count(1) as c FROM pg_class c,pg_attribute a WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid", (f._obj, f._fields_id))
res = cr.fetchone()[0] res = cr.fetchone()[0]
if not res: if not res:
cr.execute("ALTER TABLE \"%s\" ADD FOREIGN KEY (%s) REFERENCES \"%s\" ON DELETE SET NULL" % (self._obj, f._fields_id, f._table)) cr.execute('ALTER TABLE "%s" ADD FOREIGN KEY (%s) REFERENCES "%s" ON DELETE SET NULL' % (self._obj, f._fields_id, f._table))
elif isinstance(f, fields.many2many): elif isinstance(f, fields.many2many):
cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname=%s", (f._rel,)) cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname=%s", (f._rel,))
if not cr.dictfetchall(): if not cr.dictfetchall():
@ -1464,29 +1482,40 @@ class orm(orm_template):
ref = self.pool.get(f._obj)._table ref = self.pool.get(f._obj)._table
except AttributeError: except AttributeError:
ref = f._obj.replace('.', '_') ref = f._obj.replace('.', '_')
cr.execute("CREATE TABLE \"%s\" (\"%s\" INTEGER NOT NULL REFERENCES \"%s\" ON DELETE CASCADE, \"%s\" INTEGER NOT NULL REFERENCES \"%s\" ON DELETE CASCADE) WITH OIDS"%(f._rel, f._id1, self._table, f._id2, ref)) cr.execute('CREATE TABLE "%s" ("%s" INTEGER NOT NULL REFERENCES "%s" ON DELETE CASCADE, "%s" INTEGER NOT NULL REFERENCES "%s" ON DELETE CASCADE) WITH OIDS' % (f._rel, f._id1, self._table, f._id2, ref))
cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (f._rel, f._id1, f._rel, f._id1)) cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (f._rel, f._id1, f._rel, f._id1))
cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (f._rel, f._id2, f._rel, f._id2)) cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (f._rel, f._id2, f._rel, f._id2))
cr.commit() cr.commit()
else: else:
cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size FROM pg_class c,pg_attribute a,pg_type t WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid AND a.atttypid=t.oid", (self._table, k)) cr.execute("SELECT c.relname,a.attname,a.attlen,a.atttypmod,a.attnotnull,a.atthasdef,t.typname,CASE WHEN a.attlen=-1 THEN a.atttypmod-4 ELSE a.attlen END as size " \
"FROM pg_class c,pg_attribute a,pg_type t " \
"WHERE c.relname=%s " \
"AND a.attname=%s " \
"AND c.oid=a.attrelid " \
"AND a.atttypid=t.oid", (self._table, k))
res = cr.dictfetchall() res = cr.dictfetchall()
if not res: if not res:
if not isinstance(f, fields.function) or f.store: if not isinstance(f, fields.function) or f.store:
# add the missing field # add the missing field
cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" %s" % (self._table, k, get_pg_type(f)[1])) cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, get_pg_type(f)[1]))
# initialize it # initialize it
if not create and k in self._defaults: if not create and k in self._defaults:
default = self._defaults[k](self, cr, 1, {}) default = self._defaults[k](self, cr, 1, {})
if not default: ss = self._columns[k]._symbol_set
cr.execute("UPDATE \"%s\" SET \"%s\"=NULL" % (self._table, k)) query = 'UPDATE "%s" SET "%s"=%s' % (self._table, k, ss[0])
else: cr.execute(query, (ss[1](default),))
cr.execute("UPDATE \"%s\" SET \"%s\"='%s'" % (self._table, k, default)) cr.commit()
logger.notifyChannel('init', netsvc.LOG_DEBUG, 'setting default value of new column %s of table %s'% (k, self._table))
elif not create:
logger.notifyChannel('init', netsvc.LOG_DEBUG, 'creating new column %s of table %s'% (k, self._table))
if isinstance(f, fields.function): if isinstance(f, fields.function):
todo_update_store.append((f,k)) order = 10
if f.store is not True:
order = f.store[f.store.keys()[0]][2]
todo_update_store.append((order, f,k))
# and add constraints if needed # and add constraints if needed
if isinstance(f, fields.many2one): if isinstance(f, fields.many2one):
@ -1497,14 +1526,14 @@ class orm(orm_template):
ref = f._obj.replace('.', '_') ref = f._obj.replace('.', '_')
# ir_actions is inherited so foreign key doesn't work on it # ir_actions is inherited so foreign key doesn't work on it
if ref != 'ir_actions': if ref != 'ir_actions':
cr.execute("ALTER TABLE \"%s\" ADD FOREIGN KEY (\"%s\") REFERENCES \"%s\" ON DELETE %s" % (self._table, k, ref, f.ondelete)) cr.execute('ALTER TABLE "%s" ADD FOREIGN KEY ("%s") REFERENCES "%s" ON DELETE %s' % (self._table, k, ref, f.ondelete))
if f.select: if f.select:
cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (self._table, k, self._table, k)) cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (self._table, k, self._table, k))
if f.required: if f.required:
cr.commit()
try: try:
cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" SET NOT NULL" % (self._table, k)) cr.commit()
except: cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" SET NOT NULL' % (self._table, k))
except Exception, e:
logger.notifyChannel('init', netsvc.LOG_WARNING, 'WARNING: unable to set column %s of table %s not null !\nTry to re-run: openerp-server.py --update=module\nIf it doesn\'t work, update records and execute manually:\nALTER TABLE %s ALTER COLUMN %s SET NOT NULL' % (k, self._table, self._table, k)) logger.notifyChannel('init', netsvc.LOG_WARNING, 'WARNING: unable to set column %s of table %s not null !\nTry to re-run: openerp-server.py --update=module\nIf it doesn\'t work, update records and execute manually:\nALTER TABLE %s ALTER COLUMN %s SET NOT NULL' % (k, self._table, self._table, k))
cr.commit() cr.commit()
elif len(res)==1: elif len(res)==1:
@ -1513,78 +1542,89 @@ class orm(orm_template):
f_pg_size = f_pg_def['size'] f_pg_size = f_pg_def['size']
f_pg_notnull = f_pg_def['attnotnull'] f_pg_notnull = f_pg_def['attnotnull']
if isinstance(f, fields.function) and not f.store: if isinstance(f, fields.function) and not f.store:
logger.notifyChannel('init', netsvc.LOG_WARNING, 'column %s (%s) in table %s was converted to a function !\nYou should remove this column from your database.' % (k, f.string, self._table)) logger.notifyChannel('init', netsvc.LOG_INFO, 'column %s (%s) in table %s removed: converted to a function !\n' % (k, f.string, self._table))
cr.execute('ALTER TABLE %s DROP COLUMN %s'% (self._table, k))
cr.commit()
f_obj_type = None f_obj_type = None
else: else:
f_obj_type = get_pg_type(f) and get_pg_type(f)[0] f_obj_type = get_pg_type(f) and get_pg_type(f)[0]
if f_obj_type: if f_obj_type:
if f_pg_type != f_obj_type: ok = False
logger.notifyChannel('init', netsvc.LOG_WARNING, "column '%s' in table '%s' has changed type (DB = %s, def = %s) !" % (k, self._table, f_pg_type, f._type)) casts = [
('text', 'char', 'VARCHAR(%d)' % (f.size or 0,), '::VARCHAR(%d)'%(f.size or 0,)),
('varchar', 'text', 'TEXT', ''),
('int4', 'float', get_pg_type(f)[1], '::'+get_pg_type(f)[1]),
('date', 'datetime', 'TIMESTAMP', '::TIMESTAMP'),
]
if f_pg_type == 'varchar' and f._type == 'char' and f_pg_size != f.size: if f_pg_type == 'varchar' and f._type == 'char' and f_pg_size != f.size:
# columns with the name 'type' cannot be changed for an unknown reason?! logger.notifyChannel('init', netsvc.LOG_INFO, "column '%s' in table '%s' changed size" % (k, self._table))
if k != 'type': cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k))
if f_pg_size > f.size: cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" VARCHAR(%d)' % (self._table, k, f.size))
logger.notifyChannel('init', netsvc.LOG_WARNING, "column '%s' in table '%s' has changed size (DB = %d, def = %d), DB size will be kept !" % (k, self._table, f_pg_size, f.size)) cr.execute('UPDATE "%s" SET "%s"=temp_change_size::VARCHAR(%d)' % (self._table, k, f.size))
# If actual DB size is < than new cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size' % (self._table,))
# We update varchar size, otherwise, we keep DB size cr.commit()
# to avoid truncated string... for c in casts:
if f_pg_size < f.size: if (f_pg_type==c[0]) and (f._type==c[1]):
cr.execute("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO temp_change_size" % (self._table, k)) logger.notifyChannel('init', netsvc.LOG_INFO, "column '%s' in table '%s' changed type to %s." % (k, self._table, c[1]))
cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" VARCHAR(%d)" % (self._table, k, f.size)) ok = True
cr.execute("UPDATE \"%s\" SET \"%s\"=temp_change_size::VARCHAR(%d)" % (self._table, k, f.size)) cr.execute('ALTER TABLE "%s" RENAME COLUMN "%s" TO temp_change_size' % (self._table, k))
cr.execute("ALTER TABLE \"%s\" DROP COLUMN temp_change_size" % (self._table,)) cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, c[2]))
cr.commit() cr.execute(('UPDATE "%s" SET "%s"=temp_change_size'+c[3]) % (self._table, k))
if f_pg_type == 'date' and f._type == 'datetime': cr.execute('ALTER TABLE "%s" DROP COLUMN temp_change_size CASCADE' % (self._table,))
cr.execute("ALTER TABLE \"%s\" RENAME COLUMN \"%s\" TO temp_change_type" % (self._table, k)) cr.commit()
cr.execute("ALTER TABLE \"%s\" ADD COLUMN \"%s\" TIMESTAMP " % (self._table, k))
cr.execute("UPDATE \"%s\" SET \"%s\"=temp_change_type::TIMESTAMP" % (self._table, k)) if f_pg_type != f_obj_type:
cr.execute("ALTER TABLE \"%s\" DROP COLUMN temp_change_type" % (self._table,)) if not ok:
cr.commit() logger.notifyChannel('init', netsvc.LOG_WARNING, "column '%s' in table '%s' has changed type (DB = %s, def = %s) but unable to migrate this change !" % (k, self._table, f_pg_type, f._type))
# if the field is required and hasn't got a NOT NULL constraint # if the field is required and hasn't got a NOT NULL constraint
if f.required and f_pg_notnull == 0: if f.required and f_pg_notnull == 0:
# set the field to the default value if any # set the field to the default value if any
if k in self._defaults: if k in self._defaults:
default = self._defaults[k](self, cr, 1, {}) default = self._defaults[k](self, cr, 1, {})
if not (default is False): if (default is not None):
cr.execute("UPDATE \"%s\" SET \"%s\"='%s' WHERE %s is NULL" % (self._table, k, default, k)) ss = self._columns[k]._symbol_set
cr.commit() query = 'UPDATE "%s" SET "%s"=%s WHERE %s is NULL' % (self._table, k, ss[0], k)
cr.execute(query, (ss[1](default),))
# add the NOT NULL constraint # add the NOT NULL constraint
cr.commit()
try: try:
cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" SET NOT NULL" % (self._table, k)) cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" SET NOT NULL' % (self._table, k))
cr.commit() cr.commit()
except: except Exception, e:
logger.notifyChannel('init', netsvc.LOG_WARNING, 'unable to set a NOT NULL constraint on column %s of the %s table !\nIf you want to have it, you should update the records and execute manually:\nALTER TABLE %s ALTER COLUMN %s SET NOT NULL' % (k, self._table, self._table, k)) logger.notifyChannel('init', netsvc.LOG_WARNING, 'unable to set a NOT NULL constraint on column %s of the %s table !\nIf you want to have it, you should update the records and execute manually:\nALTER TABLE %s ALTER COLUMN %s SET NOT NULL' % (k, self._table, self._table, k))
cr.commit() cr.commit()
elif not f.required and f_pg_notnull == 1: elif not f.required and f_pg_notnull == 1:
cr.execute("ALTER TABLE \"%s\" ALTER COLUMN \"%s\" DROP NOT NULL" % (self._table, k)) cr.execute('ALTER TABLE "%s" ALTER COLUMN "%s" DROP NOT NULL' % (self._table, k))
cr.commit() cr.commit()
cr.execute("SELECT indexname FROM pg_indexes WHERE indexname = '%s_%s_index' and tablename = '%s'" % (self._table, k, self._table)) indexname = '%s_%s_index' % (self._table, k)
cr.execute("SELECT indexname FROM pg_indexes WHERE indexname = %s and tablename = %s", (indexname, self._table))
res = cr.dictfetchall() res = cr.dictfetchall()
if not res and f.select: if not res and f.select:
cr.execute("CREATE INDEX \"%s_%s_index\" ON \"%s\" (\"%s\")" % (self._table, k, self._table, k)) cr.execute('CREATE INDEX "%s_%s_index" ON "%s" ("%s")' % (self._table, k, self._table, k))
cr.commit() cr.commit()
if res and not f.select: if res and not f.select:
cr.execute("DROP INDEX \"%s_%s_index\"" % (self._table, k)) cr.execute('DROP INDEX "%s_%s_index"' % (self._table, k))
cr.commit() cr.commit()
if isinstance(f, fields.many2one): if isinstance(f, fields.many2one):
ref = self.pool.get(f._obj)._table ref = self.pool.get(f._obj)._table
if ref != 'ir_actions': if ref != 'ir_actions':
cr.execute('SELECT confdeltype, conname FROM pg_constraint as con, pg_class as cl1, pg_class as cl2, ' \ cr.execute('SELECT confdeltype, conname FROM pg_constraint as con, pg_class as cl1, pg_class as cl2, '
'pg_attribute as att1, pg_attribute as att2 ' \ 'pg_attribute as att1, pg_attribute as att2 '
'WHERE con.conrelid = cl1.oid ' \ 'WHERE con.conrelid = cl1.oid '
'AND cl1.relname = %s ' \ 'AND cl1.relname = %s '
'AND con.confrelid = cl2.oid ' \ 'AND con.confrelid = cl2.oid '
'AND cl2.relname = %s ' \ 'AND cl2.relname = %s '
'AND array_lower(con.conkey, 1) = 1 ' \ 'AND array_lower(con.conkey, 1) = 1 '
'AND con.conkey[1] = att1.attnum ' \ 'AND con.conkey[1] = att1.attnum '
'AND att1.attrelid = cl1.oid ' \ 'AND att1.attrelid = cl1.oid '
'AND att1.attname = %s ' \ 'AND att1.attname = %s '
'AND array_lower(con.confkey, 1) = 1 ' \ 'AND array_lower(con.confkey, 1) = 1 '
'AND con.confkey[1] = att2.attnum ' \ 'AND con.confkey[1] = att2.attnum '
'AND att2.attrelid = cl2.oid ' \ 'AND att2.attrelid = cl2.oid '
'AND att2.attname = %s ' \ 'AND att2.attname = %s '
'AND con.contype = \'f\'', (self._table, ref, k, 'id')) "AND con.contype = 'f'", (self._table, ref, k, 'id'))
res = cr.dictfetchall() res = cr.dictfetchall()
if res: if res:
confdeltype = { confdeltype = {
@ -1599,31 +1639,21 @@ class orm(orm_template):
cr.execute('ALTER TABLE "' + self._table + '" ADD FOREIGN KEY ("' + k + '") REFERENCES "' + ref + '" ON DELETE ' + f.ondelete) cr.execute('ALTER TABLE "' + self._table + '" ADD FOREIGN KEY ("' + k + '") REFERENCES "' + ref + '" ON DELETE ' + f.ondelete)
cr.commit() cr.commit()
else: else:
print "ERROR" logger = netsvc.Logger()
for f,k in todo_update_store: logger.notifyChannel('orm', netsvc.LOG_ERROR, "Programming error !")
cr.execute('select id from '+self._table) for order,f,k in todo_update_store:
ids_lst = map(lambda x: x[0], cr.fetchall()) todo_end.append((order, self._update_store, (f, k)))
while ids_lst:
iids = ids_lst[:40]
ids_lst = ids_lst[40:]
res = f.get(cr, self, iids, k, 1, {})
for key,val in res.items():
if f._multi:
val = val[k]
if (val<>False) or (type(val)<>bool):
cr.execute("UPDATE \"%s\" SET \"%s\"='%s' where id=%d"% (self._table, k, val, key))
#else:
# cr.execute("UPDATE \"%s\" SET \"%s\"=NULL where id=%d"% (self._table, k, key))
else: else:
cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname='%s'" % self._table) cr.execute("SELECT relname FROM pg_class WHERE relkind in ('r','v') AND relname=%s", (self._table,))
create = not bool(cr.fetchone()) create = not bool(cr.fetchone())
for (key, con, _) in self._sql_constraints: for (key, con, _) in self._sql_constraints:
cr.execute("SELECT conname FROM pg_constraint where conname='%s_%s'" % (self._table, key)) conname = '%s_%s' % (self._table, key)
cr.execute("SELECT conname FROM pg_constraint where conname=%s", (conname,))
if not cr.dictfetchall(): if not cr.dictfetchall():
try: try:
cr.execute('alter table \"%s\" add constraint \"%s_%s\" %s' % (self._table, self._table, key, con,)) cr.execute('alter table "%s" add constraint "%s_%s" %s' % (self._table, self._table, key, con,))
cr.commit() cr.commit()
except: except:
logger.notifyChannel('init', netsvc.LOG_WARNING, 'unable to add \'%s\' constraint on table %s !\n If you want to have it, you should update the records and execute manually:\nALTER table %s ADD CONSTRAINT %s_%s %s' % (con, self._table, self._table, self._table, key, con,)) logger.notifyChannel('init', netsvc.LOG_WARNING, 'unable to add \'%s\' constraint on table %s !\n If you want to have it, you should update the records and execute manually:\nALTER table %s ADD CONSTRAINT %s_%s %s' % (con, self._table, self._table, self._table, key, con,))
@ -1637,6 +1667,7 @@ class orm(orm_template):
cr.commit() cr.commit()
if store_compute: if store_compute:
self._parent_store_compute(cr) self._parent_store_compute(cr)
return todo_end
def __init__(self, cr): def __init__(self, cr):
super(orm, self).__init__(cr) super(orm, self).__init__(cr)
@ -1648,18 +1679,15 @@ class orm(orm_template):
if not f.store: if not f.store:
continue continue
if self._columns[store_field].store is True: if self._columns[store_field].store is True:
sm = {self._name:(lambda self,cr, uid, ids, c={}: ids, None)} sm = {self._name:(lambda self,cr, uid, ids, c={}: ids, None, 10)}
else: else:
sm = self._columns[store_field].store sm = self._columns[store_field].store
for object, aa in sm.items(): for object, aa in sm.items():
if len(aa)==2: if len(aa)==3:
(fnct,fields2)=aa
order = 1
elif len(aa)==3:
(fnct,fields2,order)=aa (fnct,fields2,order)=aa
else: else:
raise except_orm(_('Error'), raise except_orm('Error',
_('Invalid function definition %s in object %s !' % (store_field, self._name))) ('Invalid function definition %s in object %s !\nYou must use the definition: store={object:(fnct, fields, priority)}.' % (store_field, self._name)))
self.pool._store_function.setdefault(object, []) self.pool._store_function.setdefault(object, [])
ok = True ok = True
for x,y,z,e,f in self.pool._store_function[object]: for x,y,z,e,f in self.pool._store_function[object]:
@ -1780,7 +1808,6 @@ class orm(orm_template):
value[key[8:]] = context[key] value[key[8:]] = context[key]
return value return value
# #
# Update objects that uses this one to update their _inherits fields # Update objects that uses this one to update their _inherits fields
# #
@ -1845,7 +1872,6 @@ class orm(orm_template):
if isinstance(self._columns[f], fields.binary) and context.get('bin_size', False): if isinstance(self._columns[f], fields.binary) and context.get('bin_size', False):
return "length(%s) as %s" % (f,f) return "length(%s) as %s" % (f,f)
return '"%s"' % (f,) return '"%s"' % (f,)
#fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) and ('date_trunc(\'second\', '+x+') as '+x) or '"'+x+'"', fields_pre)
fields_pre2 = map(convert_field, fields_pre) fields_pre2 = map(convert_field, fields_pre)
for i in range(0, len(ids), cr.IN_MAX): for i in range(0, len(ids), cr.IN_MAX):
sub_ids = ids[i:i+cr.IN_MAX] sub_ids = ids[i:i+cr.IN_MAX]
@ -1897,7 +1923,7 @@ class orm(orm_template):
# to get the _symbol_get in each occurence # to get the _symbol_get in each occurence
for r in res: for r in res:
for f in fields_post: for f in fields_post:
r[f] = self.columns[f]._symbol_get(r[f]) r[f] = self._columns[f]._symbol_get(r[f])
ids = map(lambda x: x['id'], res) ids = map(lambda x: x['id'], res)
# all non inherited fields for which the attribute whose name is in load is False # all non inherited fields for which the attribute whose name is in load is False
@ -1995,12 +2021,7 @@ class orm(orm_template):
if isinstance(ids, (int, long)): if isinstance(ids, (int, long)):
ids = [ids] ids = [ids]
fn_list = [] result_store = self._store_get_values(cr, uid, ids, None, context)
for fnct in self.pool._store_function.get(self._name, []):
ids2 = filter(None, fnct[2](self,cr, uid, ids, context))
if ids2:
fn_list.append( (fnct[0], fnct[1], ids2) )
delta = context.get('read_delta', False) delta = context.get('read_delta', False)
if delta and self._log_access: if delta and self._log_access:
for i in range(0, len(ids), cr.IN_MAX): for i in range(0, len(ids), cr.IN_MAX):
@ -2031,7 +2052,7 @@ class orm(orm_template):
for i in range(0, len(ids), cr.IN_MAX): for i in range(0, len(ids), cr.IN_MAX):
sub_ids = ids[i:i+cr.IN_MAX] sub_ids = ids[i:i+cr.IN_MAX]
str_d = string.join(('%d',)*len(sub_ids), ',') str_d = string.join(('%s',)*len(sub_ids), ',')
if d1: if d1:
cr.execute('SELECT id FROM "'+self._table+'" ' \ cr.execute('SELECT id FROM "'+self._table+'" ' \
'WHERE id IN ('+str_d+')'+d1, sub_ids+d2) 'WHERE id IN ('+str_d+')'+d1, sub_ids+d2)
@ -2047,10 +2068,8 @@ class orm(orm_template):
cr.execute('delete from "'+self._table+'" ' \ cr.execute('delete from "'+self._table+'" ' \
'where id in ('+str_d+')', sub_ids) 'where id in ('+str_d+')', sub_ids)
for object,field,ids in fn_list: for order, object, ids, fields in result_store:
ids = self.pool.get(object).search(cr, uid, [('id','in', ids)], context=context) self.pool.get(object)._store_set_values(cr, uid, ids, fields, context)
if ids:
self.pool.get(object)._store_set_values(cr, uid, ids, field, context)
return True return True
# #
@ -2148,7 +2167,7 @@ class orm(orm_template):
% (vals[field], field)) % (vals[field], field))
if self._log_access: if self._log_access:
upd0.append('write_uid=%d') upd0.append('write_uid=%s')
upd0.append('write_date=now()') upd0.append('write_date=now()')
upd1.append(user) upd1.append(user)
@ -2214,14 +2233,14 @@ class orm(orm_template):
if self.pool._init: if self.pool._init:
self.pool._init_parent[self._name]=True self.pool._init_parent[self._name]=True
else: else:
cr.execute('select parent_left,parent_right from '+self._table+' where id=%d', (vals[self._parent_name],)) cr.execute('select parent_left,parent_right from '+self._table+' where id=%s', (vals[self._parent_name],))
res = cr.fetchone() res = cr.fetchone()
if res: if res:
pleft,pright = res pleft,pright = res
else: else:
cr.execute('select max(parent_right),max(parent_right)+1 from '+self._table) cr.execute('select max(parent_right),max(parent_right)+1 from '+self._table)
pleft,pright = cr.fetchone() pleft,pright = cr.fetchone()
cr.execute('select parent_left,parent_right,id from '+self._table+' where id in ('+','.join(map(lambda x:'%d',ids))+')', ids) cr.execute('select parent_left,parent_right,id from '+self._table+' where id in ('+','.join(map(lambda x:'%s',ids))+')', ids)
dest = pleft + 1 dest = pleft + 1
for cleft,cright,cid in cr.fetchall(): for cleft,cright,cid in cr.fetchall():
if cleft > pleft: if cleft > pleft:
@ -2241,37 +2260,31 @@ class orm(orm_template):
cr.execute('UPDATE '+self._table+''' cr.execute('UPDATE '+self._table+'''
SET SET
parent_left = CASE parent_left = CASE
WHEN parent_left BETWEEN %d AND %d THEN parent_left + %d WHEN parent_left BETWEEN %s AND %s THEN parent_left + %s
WHEN parent_left BETWEEN %d AND %d THEN parent_left + %d WHEN parent_left BETWEEN %s AND %s THEN parent_left + %s
ELSE parent_left ELSE parent_left
END, END,
parent_right = CASE parent_right = CASE
WHEN parent_right BETWEEN %d AND %d THEN parent_right + %d WHEN parent_right BETWEEN %s AND %s THEN parent_right + %s
WHEN parent_right BETWEEN %d AND %d THEN parent_right + %d WHEN parent_right BETWEEN %s AND %s THEN parent_right + %s
ELSE parent_right ELSE parent_right
END END
WHERE WHERE
parent_left<%d OR parent_right>%d; parent_left<%s OR parent_right>%s;
''', (leftbound,rightbound,cwidth,cleft,cright,treeshift,leftbound,rightbound, ''', (leftbound,rightbound,cwidth,cleft,cright,treeshift,leftbound,rightbound,
cwidth,cleft,cright,treeshift,leftrange,rightrange)) cwidth,cleft,cright,treeshift,leftrange,rightrange))
if 'read_delta' in context: if 'read_delta' in context:
del context['read_delta'] del context['read_delta']
result = self._store_get_values(cr, user, ids, vals.keys(), context)
for order, object, ids, fields in result:
self.pool.get(object)._store_set_values(cr, user, ids, fields, context)
wf_service = netsvc.LocalService("workflow") wf_service = netsvc.LocalService("workflow")
for id in ids: for id in ids:
wf_service.trg_write(user, self._name, id, cr) wf_service.trg_write(user, self._name, id, cr)
for fnct in self.pool._store_function.get(self._name, []):
ok = False
for key in vals.keys():
if (not fnct[3]) or (key in fnct[3]):
ok = True
if ok:
ids2 = fnct[2](self,cr, user, ids, context)
ids2 = filter(None, ids2)
if ids2:
self.pool.get(fnct[0])._store_set_values(cr, user, ids2, fnct[1], context)
return True return True
# #
@ -2322,7 +2335,7 @@ class orm(orm_template):
for table in tocreate: for table in tocreate:
id = self.pool.get(table).create(cr, user, tocreate[table]) id = self.pool.get(table).create(cr, user, tocreate[table])
upd0 += ','+self._inherits[table] upd0 += ','+self._inherits[table]
upd1 += ',%d' upd1 += ',%s'
upd2.append(id) upd2.append(id)
for field in vals: for field in vals:
@ -2352,7 +2365,7 @@ class orm(orm_template):
% (vals[field], field)) % (vals[field], field))
if self._log_access: if self._log_access:
upd0 += ',create_uid,create_date' upd0 += ',create_uid,create_date'
upd1 += ',%d,now()' upd1 += ',%s,now()'
upd2.append(user) upd2.append(user)
cr.execute('insert into "'+self._table+'" (id'+upd0+") values ("+str(id_new)+upd1+')', tuple(upd2)) cr.execute('insert into "'+self._table+'" (id'+upd0+") values ("+str(id_new)+upd1+')', tuple(upd2))
upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority) upd_todo.sort(lambda x, y: self._columns[x].priority-self._columns[y].priority)
@ -2367,43 +2380,83 @@ class orm(orm_template):
else: else:
parent = vals.get(self._parent_name, False) parent = vals.get(self._parent_name, False)
if parent: if parent:
cr.execute('select parent_left from '+self._table+' where id=%d', (parent,)) cr.execute('select parent_left from '+self._table+' where id=%s', (parent,))
pleft = cr.fetchone()[0] pleft = cr.fetchone()[0]
else: else:
cr.execute('select max(parent_right) from '+self._table) cr.execute('select max(parent_right) from '+self._table)
pleft = cr.fetchone()[0] or 0 pleft = cr.fetchone()[0] or 0
cr.execute('update '+self._table+' set parent_left=parent_left+2 where parent_left>%d', (pleft,)) cr.execute('update '+self._table+' set parent_left=parent_left+2 where parent_left>%s', (pleft,))
cr.execute('update '+self._table+' set parent_right=parent_right+2 where parent_right>%d', (pleft,)) cr.execute('update '+self._table+' set parent_right=parent_right+2 where parent_right>%s', (pleft,))
cr.execute('update '+self._table+' set parent_left=%d,parent_right=%d where id=%d', (pleft+1,pleft+2,id_new)) cr.execute('update '+self._table+' set parent_left=%s,parent_right=%s where id=%s', (pleft+1,pleft+2,id_new))
result = self._store_get_values(cr, user, [id_new], vals.keys(), context)
for order, object, ids, fields in result:
self.pool.get(object)._store_set_values(cr, user, ids, fields, context)
wf_service = netsvc.LocalService("workflow") wf_service = netsvc.LocalService("workflow")
wf_service.trg_create(user, self._name, id_new, cr) wf_service.trg_create(user, self._name, id_new, cr)
for fnct in self.pool._store_function.get(self._name, []):
ids2 = fnct[2](self,cr, user, [id_new], context)
ids2 = filter(None, ids2)
if ids2:
self.pool.get(fnct[0])._store_set_values(cr, user, ids2, fnct[1], context)
return id_new return id_new
def _store_set_values(self, cr, uid, ids, field, context): def _store_get_values(self, cr, uid, ids, fields, context):
args = {} result = {}
result = self._columns[field].get(cr, self, ids, field, uid, context=context) fncts = self.pool._store_function.get(self._name, [])
for id,value in result.items(): for fnct in range(len(fncts)):
upd0 = [] result.setdefault(fncts[fnct][0], {})
upd1 = [] ids2 = fncts[fnct][2](self,cr, uid, ids, context)
if self._columns[field]._multi: for id in filter(None, ids2):
value = value[field] result[fncts[fnct][0]].setdefault(id, [])
if self._columns[field]._type in ('many2one', 'one2one'): result[fncts[fnct][0]][id].append(fnct)
try: result2 = []
value = value[0] for object in result:
except: k2 = {}
pass for id,fnct in result[object].items():
upd0.append('"'+field+'"='+self._columns[field]._symbol_set[0]) k2.setdefault(tuple(fnct), [])
upd1.append(self._columns[field]._symbol_set[1](value)) k2[tuple(fnct)].append(id)
upd1.append(id) for fnct,id in k2.items():
cr.execute('update "' + self._table + '" set ' + \ result2.append((fncts[fnct[0]][4],object,id,map(lambda x: fncts[x][1], fnct)))
string.join(upd0, ',') + ' where id = %d', upd1) result2.sort()
return result2
def _store_set_values(self, cr, uid, ids, fields, context):
todo = {}
keys = []
for f in fields:
if self._columns[f]._multi not in keys:
keys.append(self._columns[f]._multi)
todo.setdefault(self._columns[f]._multi, [])
todo[self._columns[f]._multi].append(f)
for key in keys:
val = todo[key]
if key:
result = self._columns[val[0]].get(cr, self, ids, val, uid, context=context)
for id,value in result.items():
upd0 = []
upd1 = []
for v in value:
if v not in val:
continue
if self._columns[v]._type in ('many2one', 'one2one'):
try:
value[v] = value[v][0]
except:
pass
upd0.append('"'+v+'"='+self._columns[v]._symbol_set[0])
upd1.append(self._columns[v]._symbol_set[1](value[v]))
upd1.append(id)
cr.execute('update "' + self._table + '" set ' + \
string.join(upd0, ',') + ' where id = %s', upd1)
else:
for f in val:
result = self._columns[f].get(cr, self, ids, f, uid, context=context)
for id,value in result.items():
if self._columns[f]._type in ('many2one', 'one2one'):
try:
value = value[0]
except:
pass
cr.execute('update "' + self._table + '" set ' + \
'"'+f+'"='+self._columns[f]._symbol_set[0] + ' where id = %s', (self._columns[f]._symbol_set[1](value),id))
return True return True
# #
@ -2501,7 +2554,7 @@ class orm(orm_template):
return [] return []
if isinstance(ids, (int, long)): if isinstance(ids, (int, long)):
ids = [ids] ids = [ids]
return [(r['id'], str(r[self._rec_name])) for r in self.read(cr, user, ids, return [(r['id'], tools.ustr(r[self._rec_name])) for r in self.read(cr, user, ids,
[self._rec_name], context, load='_classic_write')] [self._rec_name], context, load='_classic_write')]
def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=None): def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=None):

View File

@ -30,7 +30,7 @@ import pooler
import copy import copy
import sys import sys
import psycopg from psycopg2 import IntegrityError
from netsvc import Logger, LOG_ERROR from netsvc import Logger, LOG_ERROR
from tools.misc import UpdateableDict from tools.misc import UpdateableDict
@ -87,7 +87,7 @@ class osv_pool(netsvc.Service):
self.abortResponse(1, inst.name, 'warning', inst.value) self.abortResponse(1, inst.name, 'warning', inst.value)
except except_osv, inst: except except_osv, inst:
self.abortResponse(1, inst.name, inst.exc_type, inst.value) self.abortResponse(1, inst.name, inst.exc_type, inst.value)
except psycopg.IntegrityError, inst: except IntegrityError, inst:
for key in self._sql_error.keys(): for key in self._sql_error.keys():
if key in inst[0]: if key in inst[0]:
self.abortResponse(1, 'Constraint Error', 'warning', self._sql_error[key]) self.abortResponse(1, 'Constraint Error', 'warning', self._sql_error[key])
@ -96,8 +96,7 @@ class osv_pool(netsvc.Service):
import traceback import traceback
tb_s = reduce(lambda x, y: x+y, traceback.format_exception( sys.exc_type, sys.exc_value, sys.exc_traceback)) tb_s = reduce(lambda x, y: x+y, traceback.format_exception( sys.exc_type, sys.exc_value, sys.exc_traceback))
logger = Logger() logger = Logger()
for idx, s in enumerate(tb_s.split('\n')): logger.notifyChannel('web-services', LOG_ERROR, tb_s)
logger.notifyChannel("web-services", LOG_ERROR, '[%2d]: %s' % (idx, s,))
raise raise
def execute(self, db, uid, obj, method, *args, **kw): def execute(self, db, uid, obj, method, *args, **kw):
@ -185,7 +184,7 @@ class osv_memory(orm.orm_memory):
name = hasattr(cls, '_name') and cls._name or cls._inherit name = hasattr(cls, '_name') and cls._name or cls._inherit
parent_name = hasattr(cls, '_inherit') and cls._inherit parent_name = hasattr(cls, '_inherit') and cls._inherit
if parent_name: if parent_name:
print 'Inherit not supported in osv_memory object !' raise 'Inherit not supported in osv_memory object !'
obj = object.__new__(cls) obj = object.__new__(cls)
obj.__init__(pool, cr) obj.__init__(pool, cr)
return obj return obj
@ -252,122 +251,3 @@ class osv(orm.orm):
self.pool = pool self.pool = pool
orm.orm.__init__(self, cr) orm.orm.__init__(self, cr)
class Cacheable(object):
_cache = UpdateableDict()
def add(self, key, value):
self._cache[key] = value
def invalidate(self, key):
del self._cache[key]
def get(self, key):
try:
w = self._cache[key]
return w
except KeyError:
return None
def clear(self):
self._cache.clear()
self._items = []
def filter_dict(d, fields):
res = {}
for f in fields + ['id']:
if f in d:
res[f] = d[f]
return res
class cacheable_osv(osv, Cacheable):
_relevant = ['lang']
def __init__(self):
super(cacheable_osv, self).__init__()
def read(self, cr, user, ids, fields=None, context=None, load='_classic_read'):
if not fields:
fields = []
if not context:
context = {}
fields = fields or self._columns.keys()
ctx = [context.get(x, False) for x in self._relevant]
result, tofetch = [], []
for id in ids:
res = self.get(self._name, id, ctx)
if not res:
tofetch.append(id)
else:
result.append(filter_dict(res, fields))
# gen the list of "local" (ie not inherited) fields which are classic or many2one
nfields = filter(lambda x: x[1]._classic_write, self._columns.items())
# gen the list of inherited fields
inherits = map(lambda x: (x[0], x[1][2]), self._inherit_fields.items())
# complete the field list with the inherited fields which are classic or many2one
nfields += filter(lambda x: x[1]._classic_write, inherits)
nfields = [x[0] for x in nfields]
res = super(cacheable_osv, self).read(cr, user, tofetch, nfields, context, load)
for r in res:
self.add((self._name, r['id'], ctx), r)
result.append(filter_dict(r, fields))
# Appel de fonction si necessaire
tofetch = []
for f in fields:
if f not in nfields:
tofetch.append(f)
for f in tofetch:
fvals = self._columns[f].get(cr, self, ids, f, user, context=context)
for r in result:
r[f] = fvals[r['id']]
# TODO: tri par self._order !!
return result
def invalidate(self, key):
del self._cache[key[0]][key[1]]
def write(self, cr, user, ids, values, context=None):
if not context:
context = {}
for id in ids:
self.invalidate((self._name, id))
return super(cacheable_osv, self).write(cr, user, ids, values, context)
def unlink(self, cr, user, ids):
self.clear()
return super(cacheable_osv, self).unlink(cr, user, ids)
#cacheable_osv = osv
#class FakePool(object):
# def __init__(self, module):
# self.preferred_module = module
# def get(self, name):
# localpool = module_objects_dict.get(self.preferred_module, {'dict': {}})['dict']
# if name in localpool:
# obj = localpool[name]
# else:
# obj = pooler.get_pool(cr.dbname).get(name)
# return obj
# fake_pool = self
# class fake_class(obj.__class__):
# def __init__(self):
# super(fake_class, self).__init__()
# self.pool = fake_pool
# return fake_class()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -20,30 +20,19 @@
# #
############################################################################## ##############################################################################
import sql_db
import osv.osv
import tools
import addons
import netsvc
db_dic = {}
pool_dic = {} pool_dic = {}
def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False): def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False):
if not status: if not status:
status={} status={}
if db_name in db_dic:
db = db_dic[db_name] db = get_db_only(db_name)
else:
logger = netsvc.Logger()
logger.notifyChannel('pooler', netsvc.LOG_INFO, 'Connecting to %s' % (db_name.lower()))
db = sql_db.db_connect(db_name)
db_dic[db_name] = db
if db_name in pool_dic: if db_name in pool_dic:
pool = pool_dic[db_name] pool = pool_dic[db_name]
else: else:
import addons
import osv.osv
pool = osv.osv.osv_pool() pool = osv.osv.osv_pool()
pool_dic[db_name] = pool pool_dic[db_name] = pool
addons.load_modules(db, force_demo, status, update_module) addons.load_modules(db, force_demo, status, update_module)
@ -60,49 +49,28 @@ def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False)
def restart_pool(db_name, force_demo=False, update_module=False): def restart_pool(db_name, force_demo=False, update_module=False):
# del db_dic[db_name]
del pool_dic[db_name] del pool_dic[db_name]
return get_db_and_pool(db_name, force_demo, update_module=update_module) return get_db_and_pool(db_name, force_demo, update_module=update_module)
def close_db(db_name):
if db_name in db_dic:
db_dic[db_name].truedb.close()
del db_dic[db_name]
if db_name in pool_dic:
del pool_dic[db_name]
def get_db_only(db_name): def get_db_only(db_name):
if db_name in db_dic: # ATTENTION:
db = db_dic[db_name] # do not put this import outside this function
else: # sql_db must not be loaded before the logger is initialized.
db = sql_db.db_connect(db_name) # sql_db import psycopg2.tool which create a default logger if there is not.
db_dic[db_name] = db # this resulting of having the logs outputed twice...
import sql_db
db = sql_db.db_connect(db_name)
return db return db
def get_db(db_name): def get_db(db_name):
# print "get_db", db_name
return get_db_and_pool(db_name)[0] return get_db_and_pool(db_name)[0]
def get_pool(db_name, force_demo=False, status=None, update_module=False): def get_pool(db_name, force_demo=False, status=None, update_module=False):
# print "get_pool", db_name
pool = get_db_and_pool(db_name, force_demo, status, update_module)[1] pool = get_db_and_pool(db_name, force_demo, status, update_module)[1]
# addons.load_modules(db_name, False)
# if not pool.obj_list():
# pool.instanciate()
# print "pool", pool
return pool return pool
# return get_db_and_pool(db_name)[1]
def init():
global db
# db = get_db_only(tools.config['db_name'])
sql_db.init()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -77,29 +77,22 @@ class report_rml(report_int):
self.xsl = xsl self.xsl = xsl
self.bin_datas = {} self.bin_datas = {}
self.generators = { self.generators = {
'pdf': self.create_pdf, 'pdf': self.create_pdf,
'html': self.create_html, 'html': self.create_html,
'raw': self.create_raw, 'raw': self.create_raw,
'sxw': self.create_sxw, 'sxw': self.create_sxw,
'txt': self.create_txt, 'txt': self.create_txt,
} }
def create(self, cr, uid, ids, datas, context): def create(self, cr, uid, ids, datas, context):
xml = self.create_xml(cr, uid, ids, datas, context) xml = self.create_xml(cr, uid, ids, datas, context)
#file('/tmp/terp.xml','wb+').write(xml)
if datas.get('report_type', 'pdf') == 'raw': if datas.get('report_type', 'pdf') == 'raw':
return xml return xml
rml = self.create_rml(cr, xml, uid, context) rml = self.create_rml(cr, xml, uid, context)
#file('/tmp/terp.rml','wb+').write(rml)
pool = pooler.get_pool(cr.dbname) pool = pooler.get_pool(cr.dbname)
ir_actions_report_xml_obj = pool.get('ir.actions.report.xml') ir_actions_report_xml_obj = pool.get('ir.actions.report.xml')
try: report_xml_ids = ir_actions_report_xml_obj.search(cr, uid, [('report_name', '=', self.name[7:])], context=context)
report_xml_ids = ir_actions_report_xml_obj.search(cr, uid, self.title = report_xml_ids and ir_actions_report_xml_obj.browse(cr,uid,report_xml_ids)[0].name or 'OpenERP Report'
[('report_name', '=', self.name[7:])], context=context)
self.title = ir_actions_report_xml_obj.browse(cr,uid,report_xml_ids)[0].name
except:
print 'Report not Found !'
self.title = 'Unknown'
report_type = datas.get('report_type', 'pdf') report_type = datas.get('report_type', 'pdf')
create_doc = self.generators[report_type] create_doc = self.generators[report_type]
pdf = create_doc(rml, title=self.title) pdf = create_doc(rml, title=self.title)

View File

@ -78,17 +78,13 @@ class document(object):
fields = field_path.split('.') fields = field_path.split('.')
if not len(fields): if not len(fields):
print "WARNING: field name is empty!"
return '' return ''
value = browser value = browser
for f in fields: for f in fields:
if isinstance(value, list): if isinstance(value, list):
if len(value)==0: if len(value)==0:
print "WARNING: empty list found!"
return '' return ''
# elif len(value)>1:
# print "WARNING:", len(value), "possibilities for", value[0]._table_name , "picking first..."
value = value[0] value = value[0]
if isinstance(value, browse_null): if isinstance(value, browse_null):
return '' return ''
@ -128,12 +124,6 @@ class document(object):
# parent = the parent node in the xml data tree we are creating # parent = the parent node in the xml data tree we are creating
if node.nodeType == node.ELEMENT_NODE: if node.nodeType == node.ELEMENT_NODE:
# print '-'*60
# print "parse_node", node
# print "parent: ", parent
# print "ids:", ids
# print "model:", model
# print "datas:", datas
# convert the attributes of the node to a dictionary # convert the attributes of the node to a dictionary
@ -203,9 +193,6 @@ class document(object):
el.appendChild(el_txt) el.appendChild(el_txt)
elif attrs['type']=='eval': elif attrs['type']=='eval':
#TODO: faire ca plus proprement
if isinstance(browser, list):
print "ERROR: EVAL!"
el = self.doc.createElement(node.localName) el = self.doc.createElement(node.localName)
parent.appendChild(el) parent.appendChild(el)
value = self.eval(browser, attrs['expr']) value = self.eval(browser, attrs['expr'])
@ -270,8 +257,6 @@ class document(object):
parent.appendChild(el) parent.appendChild(el)
atr = self.node_attrs_get(node) atr = self.node_attrs_get(node)
if 'value' in atr: if 'value' in atr:
#print "type=>",type(datas[atr['value']])
#print "value=>",datas[atr['value']]
if not isinstance(datas[atr['value']], (str, unicode)): if not isinstance(datas[atr['value']], (str, unicode)):
txt = self.doc.createTextNode(str(datas[atr['value']])) txt = self.doc.createTextNode(str(datas[atr['value']]))
else: else:

View File

@ -36,6 +36,7 @@ import copy
import StringIO import StringIO
import zipfile import zipfile
import os import os
import mx.DateTime
DT_FORMAT = '%Y-%m-%d' DT_FORMAT = '%Y-%m-%d'
DHM_FORMAT = '%Y-%m-%d %H:%M:%S' DHM_FORMAT = '%Y-%m-%d %H:%M:%S'
@ -255,6 +256,7 @@ class rml_parse(object):
'format': self.format, 'format': self.format,
'formatLang': self.formatLang, 'formatLang': self.formatLang,
'logo' : user.company_id.logo, 'logo' : user.company_id.logo,
'lang' : user.company_id.partner_id.lang,
} }
self.localcontext.update(context) self.localcontext.update(context)
self.rml_header = user.company_id.rml_header self.rml_header = user.company_id.rml_header
@ -333,31 +335,61 @@ class rml_parse(object):
else: else:
obj._cache[table][id] = {'id': id} obj._cache[table][id] = {'id': id}
def formatLang(self, value, digit=2, date=False): def formatLang(self, value, digits=2, date=False,date_time=False, grouping=True, monetary=False, currency=None):
if not value: if not value:
return '' return ''
lc, encoding = locale.getdefaultlocale()
if not encoding: pool_lang=self.pool.get('res.lang')
encoding = 'UTF-8'
if encoding == 'utf':
encoding = 'UTF-8'
if encoding == 'cp1252':
encoding= '1252'
lang = self.localcontext.get('lang', 'en_US') or 'en_US' lang = self.localcontext.get('lang', 'en_US') or 'en_US'
try: lang_obj = pool_lang.browse(self.cr,self.uid,pool_lang.search(self.cr,self.uid,[('code','=',lang)])[0])
if os.name == 'nt':
locale.setlocale(locale.LC_ALL, _LOCALE2WIN32.get(lang, lang) + '.' + encoding) if date or date_time:
date_format = lang_obj.date_format
if date_time:
date_format = lang_obj.date_format + " " + lang_obj.time_format
if not isinstance(value, time.struct_time):
# assume string, parse it
if len(str(value)) == 10:
# length of date like 2001-01-01 is ten
# assume format '%Y-%m-%d'
date = mx.DateTime.strptime(str(value),DT_FORMAT)
else:
# assume format '%Y-%m-%d %H:%M:%S'
value = str(value)[:19]
date = mx.DateTime.strptime(str(value),DHM_FORMAT)
else: else:
locale.setlocale(locale.LC_ALL, lang + '.' + encoding) date = mx.DateTime.DateTime(*(value.timetuple()[:6]))
except Exception:
netsvc.Logger().notifyChannel('report', netsvc.LOG_WARNING, return date.strftime(date_format)
'report %s: unable to set locale "%s"' % (self.name,
self.localcontext.get('lang', 'en_US') or 'en_US')) return lang_obj.format('%.' + str(digits) + 'f', value, grouping=grouping, monetary=monetary)
if date:
date = time.strptime(value, DT_FORMAT) # def formatLang(self, value, digit=2, date=False):
return time.strftime(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y'), # if not value:
date) # return ''
return locale.format('%.' + str(digit) + 'f', value, True) # lc, encoding = locale.getdefaultlocale()
# if not encoding:
# encoding = 'UTF-8'
# if encoding == 'utf':
# encoding = 'UTF-8'
# if encoding == 'cp1252':
# encoding= '1252'
# lang = self.localcontext.get('lang', 'en_US') or 'en_US'
# try:
# if os.name == 'nt':
# locale.setlocale(locale.LC_ALL, _LOCALE2WIN32.get(lang, lang) + '.' + encoding)
# else:
# locale.setlocale(locale.LC_ALL, lang + '.' + encoding)
# except Exception:
# netsvc.Logger().notifyChannel('report', netsvc.LOG_WARNING,
# 'report %s: unable to set locale "%s"' % (self.name,
# self.localcontext.get('lang', 'en_US') or 'en_US'))
# if date:
# date = time.strptime(value, DT_FORMAT)
# return time.strftime(locale.nl_langinfo(locale.D_FMT).replace('%y', '%Y'),
# date)
# return locale.format('%.' + str(digit) + 'f', value, True)
def repeatIn(self, lst, name, nodes_parent=False): def repeatIn(self, lst, name, nodes_parent=False):
self._node.data = '' self._node.data = ''
@ -555,7 +587,7 @@ class report_sxw(report_rml):
def getObjects(self, cr, uid, ids, context): def getObjects(self, cr, uid, ids, context):
table_obj = pooler.get_pool(cr.dbname).get(self.table) table_obj = pooler.get_pool(cr.dbname).get(self.table)
return table_obj.browse(cr, uid, ids, list_class=browse_record_list, context=context, fields_process=_fields_process) return table_obj.browse(cr, uid, ids, list_class=browse_record_list, context=context)
def create(self, cr, uid, ids, data, context=None): def create(self, cr, uid, ids, data, context=None):
logo = None logo = None

View File

@ -46,7 +46,7 @@ def check(db, uid, passwd):
return True return True
cr = pooler.get_db(db).cursor() cr = pooler.get_db(db).cursor()
cr.execute('select count(*) from res_users where id=%d and password=%s', (int(uid), passwd)) cr.execute('select count(*) from res_users where id=%s and password=%s', (int(uid), passwd))
res = cr.fetchone()[0] res = cr.fetchone()[0]
cr.close() cr.close()
if not bool(res): if not bool(res):

View File

@ -29,7 +29,6 @@ import logging
import threading, thread import threading, thread
import time import time
import base64
import addons import addons
import sql_db import sql_db
@ -65,8 +64,8 @@ class db(netsvc.Service):
self.actions[id] = {'clean': False} self.actions[id] = {'clean': False}
db = sql_db.db_connect('template1', serialize=1) db = sql_db.db_connect('template1', serialize=1)
db.truedb.autocommit()
cr = db.cursor() cr = db.cursor()
cr.autocommit(True)
time.sleep(0.2) time.sleep(0.2)
cr.execute('CREATE DATABASE ' + db_name + ' ENCODING \'unicode\'') cr.execute('CREATE DATABASE ' + db_name + ' ENCODING \'unicode\'')
cr.close() cr.close()
@ -108,13 +107,12 @@ class db(netsvc.Service):
traceback.print_exc(file=e_str) traceback.print_exc(file=e_str)
traceback_str = e_str.getvalue() traceback_str = e_str.getvalue()
e_str.close() e_str.close()
print traceback_str netsvc.Logger().notifyChannel('web-services', netsvc.LOG_ERROR, 'CREATE DATABASE\n%s' % (traceback_str))
serv.actions[id]['traceback'] = traceback_str serv.actions[id]['traceback'] = traceback_str
if cr: if cr:
cr.close() cr.close()
logger = netsvc.Logger() logger = netsvc.Logger()
logger.notifyChannel("web-services", netsvc.LOG_INFO, logger.notifyChannel("web-services", netsvc.LOG_INFO, 'CREATE DATABASE: %s' % (db_name.lower()))
'CREATE DB: %s' % (db_name.lower()))
dbi = DBInitialize() dbi = DBInitialize()
create_thread = threading.Thread(target=dbi, create_thread = threading.Thread(target=dbi,
args=(self, id, db_name, demo, lang, user_password)) args=(self, id, db_name, demo, lang, user_password))
@ -140,19 +138,19 @@ class db(netsvc.Service):
def drop(self, password, db_name): def drop(self, password, db_name):
security.check_super(password) security.check_super(password)
pooler.close_db(db_name) sql_db.close_db(db_name)
logger = netsvc.Logger() logger = netsvc.Logger()
db = sql_db.db_connect('template1', serialize=1) db = sql_db.db_connect('template1', serialize=1)
db.truedb.autocommit()
cr = db.cursor() cr = db.cursor()
cr.autocommit(True)
try: try:
try: try:
cr.execute('DROP DATABASE ' + db_name) cr.execute('DROP DATABASE ' + db_name)
except: except Exception, e:
logger.notifyChannel("web-services", netsvc.LOG_ERROR, logger.notifyChannel("web-services", netsvc.LOG_ERROR,
'DROP DB: %s failed' % (db_name,)) 'DROP DB: %s failed:\n%s' % (db_name, e))
raise raise Exception("Couldn't drop database %s: %s" % (db_name, e))
else: else:
logger.notifyChannel("web-services", netsvc.LOG_INFO, logger.notifyChannel("web-services", netsvc.LOG_INFO,
'DROP DB: %s' % (db_name)) 'DROP DB: %s' % (db_name))
@ -195,8 +193,8 @@ class db(netsvc.Service):
raise Exception, "Database already exists" raise Exception, "Database already exists"
db = sql_db.db_connect('template1', serialize=1) db = sql_db.db_connect('template1', serialize=1)
db.truedb.autocommit()
cr = db.cursor() cr = db.cursor()
cr.autocommit(True)
cr.execute('CREATE DATABASE ' + db_name + ' ENCODING \'unicode\'') cr.execute('CREATE DATABASE ' + db_name + ' ENCODING \'unicode\'')
cr.close() cr.close()
@ -231,7 +229,6 @@ class db(netsvc.Service):
def db_exist(self, db_name): def db_exist(self, db_name):
try: try:
db = sql_db.db_connect(db_name) db = sql_db.db_connect(db_name)
db.truedb.close()
return True return True
except: except:
return False return False
@ -256,7 +253,6 @@ class db(netsvc.Service):
cr.close() cr.close()
except: except:
res = [] res = []
db.truedb.close()
res.sort() res.sort()
return res return res
@ -285,6 +281,7 @@ class common(netsvc.Service):
self.exportMethod(self.ir_del) self.exportMethod(self.ir_del)
self.exportMethod(self.about) self.exportMethod(self.about)
self.exportMethod(self.login) self.exportMethod(self.login)
self.exportMethod(self.logout)
self.exportMethod(self.timezone_get) self.exportMethod(self.timezone_get)
def ir_set(self, db, uid, password, keys, args, name, value, replace=True, isobject=False): def ir_set(self, db, uid, password, keys, args, name, value, replace=True, isobject=False):
@ -319,9 +316,14 @@ class common(netsvc.Service):
res = security.login(db, login, password) res = security.login(db, login, password)
logger = netsvc.Logger() logger = netsvc.Logger()
msg = res and 'successful login' or 'bad login or password' msg = res and 'successful login' or 'bad login or password'
logger.notifyChannel("web-services", netsvc.LOG_INFO, "%s from '%s' using database '%s'" % (msg, login, db.lower())) logger.notifyChannel("web-service", netsvc.LOG_INFO, "%s from '%s' using database '%s'" % (msg, login, db.lower()))
return res or False return res or False
def logout(self, db, login, password):
logger = netsvc.Logger()
logger.notifyChannel("web-service", netsvc.LOG_INFO,'Logout %s from database %s'%(login,db))
return True
def about(self, extended=False): def about(self, extended=False):
"""Return information about the OpenERP Server. """Return information about the OpenERP Server.

View File

@ -20,135 +20,218 @@
# #
############################################################################## ##############################################################################
import psycopg import netsvc
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT, ISOLATION_LEVEL_SERIALIZABLE
from psycopg2.pool import ThreadedConnectionPool
from psycopg2.psycopg1 import cursor as psycopg1cursor
import psycopg2.extensions
psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
types_mapping = {
'date': (1082,),
'time': (1083,),
'datetime': (1114,),
}
def unbuffer(symb, cr):
if symb is None: return None
return str(symb)
def undecimalize(symb, cr):
if symb is None: return None
return float(symb)
for name, typeoid in types_mapping.items():
psycopg2.extensions.register_type(psycopg2.extensions.new_type(typeoid, name, lambda x, cr: x))
psycopg2.extensions.register_type(psycopg2.extensions.new_type((700, 701, 1700,), 'float', undecimalize))
import tools import tools
import sys,os
#try:
# import decimal
#except ImportError:
# from tools import decimal
import re import re
from mx import DateTime as mdt from mx import DateTime as mdt
re_from = re.compile('.* from "?([a-zA-Z_0-9]+)"? .*$'); re_from = re.compile('.* from "?([a-zA-Z_0-9]+)"? .*$');
re_into = re.compile('.* into "?([a-zA-Z_0-9]+)"? .*$'); re_into = re.compile('.* into "?([a-zA-Z_0-9]+)"? .*$');
class fake_cursor: def log(msg, lvl=netsvc.LOG_DEBUG):
logger = netsvc.Logger()
logger.notifyChannel('sql', lvl, msg)
class Cursor(object):
IN_MAX = 1000 IN_MAX = 1000
nbr = 0
_tables = {}
sql_from_log = {} sql_from_log = {}
sql_into_log = {} sql_into_log = {}
sql_log = False sql_log = False
count = 0 count = 0
def check(f):
from tools.func import wraps
def __init__(self, db, con, dbname): @wraps(f)
self.db = db def wrapper(self, *args, **kwargs):
self.obj = db.cursor() if not hasattr(self, '_obj'):
self.con = con raise psycopg2.ProgrammingError('Unable to use the cursor after having closing it')
self.dbname = dbname return f(self, *args, **kwargs)
return wrapper
def execute(self, sql, params=None): def __init__(self, pool):
if not params: self._pool = pool
self._cnx = pool.getconn()
self._obj = self._cnx.cursor(cursor_factory=psycopg1cursor)
self.autocommit(False)
self.dbname = pool.dbname
def __del__(self):
if hasattr(self, '_obj'):
# Oops. 'self' has not been closed explicitly.
# The cursor will be deleted by the garbage collector,
# but the database connection is not put back into the connection
# pool, preventing some operation on the database like dropping it.
# This can also lead to a server overload.
log('Cursor not closed explicitly', netsvc.LOG_WARNING)
self.close()
@check
def execute(self, query, params=None):
if params is None:
params=() params=()
if not isinstance(params, (tuple, list)):
params = (params,)
def base_string(s): def base_string(s):
if isinstance(s, unicode): if isinstance(s, unicode):
return s.encode('utf-8') return s.encode('utf-8')
return s return s
p=map(base_string, params) p=map(base_string, params)
if isinstance(sql, unicode): query = base_string(query)
sql = sql.encode('utf-8')
if '%d' in query or '%f' in query:
#import traceback
#traceback.print_stack()
log(query, netsvc.LOG_WARNING)
log("SQL queries mustn't containt %d or %f anymore. Use only %s", netsvc.LOG_WARNING)
if p:
query = query.replace('%d', '%s').replace('%f', '%s')
if self.sql_log: if self.sql_log:
now = mdt.now() now = mdt.now()
print "SQL LOG query:", sql
print "SQL LOG params:", repr(p) res = self._obj.execute(query, p or None)
if p:
res = self.obj.execute(sql, p)
else:
res = self.obj.execute(sql)
if self.sql_log: if self.sql_log:
log("query: %s" % self._obj.query)
self.count+=1 self.count+=1
res_from = re_from.match(sql.lower()) res_from = re_from.match(query.lower())
if res_from: if res_from:
self.sql_from_log.setdefault(res_from.group(1), [0, 0]) self.sql_from_log.setdefault(res_from.group(1), [0, 0])
self.sql_from_log[res_from.group(1)][0] += 1 self.sql_from_log[res_from.group(1)][0] += 1
self.sql_from_log[res_from.group(1)][1] += mdt.now() - now self.sql_from_log[res_from.group(1)][1] += mdt.now() - now
res_into = re_into.match(sql.lower()) res_into = re_into.match(query.lower())
if res_into: if res_into:
self.sql_into_log.setdefault(res_into.group(1), [0, 0]) self.sql_into_log.setdefault(res_into.group(1), [0, 0])
self.sql_into_log[res_into.group(1)][0] += 1 self.sql_into_log[res_into.group(1)][0] += 1
self.sql_into_log[res_into.group(1)][1] += mdt.now() - now self.sql_into_log[res_into.group(1)][1] += mdt.now() - now
return res return res
def print_log(self, type='from'): def print_log(self):
print "SQL LOG %s:" % (type,) def process(type):
if type == 'from': sqllogs = {'from':self.sql_from_log, 'into':self.sql_into_log}
logs = self.sql_from_log.items() if not sqllogs[type]:
else: return
logs = self.sql_into_log.items() sqllogitems = sqllogs[type].items()
logs.sort(lambda x, y: cmp(x[1][1], y[1][1])) sqllogitems.sort(key=lambda k: k[1][1])
sum=0 sum = 0
for r in logs: log("SQL LOG %s:" % (type,))
print "table:", r[0], ":", str(r[1][1]), "/", r[1][0] for r in sqllogitems:
sum+= r[1][1] log("table: %s: %s/%s" %(r[0], str(r[1][1]), r[1][0]))
print "SUM:%s/%d"% (sum, self.count) sum+= r[1][1]
log("SUM:%s/%d" % (sum, self.count))
sqllogs[type].clear()
process('from')
process('into')
self.count = 0
@check
def close(self): def close(self):
if self.sql_log: self.print_log()
self.print_log('from') self._obj.close()
self.print_log('into')
self.obj.close()
# This force the cursor to be freed, and thus, available again. It is # This force the cursor to be freed, and thus, available again. It is
# important because otherwise we can overload the server very easily # important because otherwise we can overload the server very easily
# because of a cursor shortage (because cursors are not garbage # because of a cursor shortage (because cursors are not garbage
# collected as fast as they should). The problem is probably due in # collected as fast as they should). The problem is probably due in
# part because browse records keep a reference to the cursor. # part because browse records keep a reference to the cursor.
del self.obj del self._obj
self._pool.putconn(self._cnx)
@check
def autocommit(self, on):
self._cnx.set_isolation_level([ISOLATION_LEVEL_SERIALIZABLE, ISOLATION_LEVEL_AUTOCOMMIT][bool(on)])
@check
def commit(self):
return self._cnx.commit()
@check
def rollback(self):
return self._cnx.rollback()
@check
def __getattr__(self, name): def __getattr__(self, name):
return getattr(self.obj, name) return getattr(self._obj, name)
class fakedb: class ConnectionPool(object):
def __init__(self, truedb, dbname): def __init__(self, pool, dbname):
self.truedb = truedb
self.dbname = dbname self.dbname = dbname
self._pool = pool
def cursor(self): def cursor(self):
return fake_cursor(self.truedb, {}, self.dbname) return Cursor(self)
def decimalize(symb): def __getattr__(self, name):
if symb is None: return None return getattr(self._pool, name)
if isinstance(symb, float):
return decimal.Decimal('%f' % symb) class PoolManager(object):
return decimal.Decimal(symb) _pools = {}
_dsn = None
maxconn = int(tools.config['db_maxconn']) or 64
def dsn(db_name):
if PoolManager._dsn is None:
PoolManager._dsn = ''
for p in ('host', 'port', 'user', 'password'):
cfg = tools.config['db_' + p]
if cfg:
PoolManager._dsn += '%s=%s ' % (p, cfg)
return '%s dbname=%s' % (PoolManager._dsn, db_name)
dsn = staticmethod(dsn)
def get(db_name):
if db_name not in PoolManager._pools:
logger = netsvc.Logger()
try:
logger.notifyChannel('dbpool', netsvc.LOG_INFO, 'Connecting to %s' % (db_name,))
PoolManager._pools[db_name] = ConnectionPool(ThreadedConnectionPool(0, PoolManager.maxconn, PoolManager.dsn(db_name)), db_name)
except Exception, e:
logger.notifyChannel('dbpool', netsvc.LOG_CRITICAL, 'Unable to connect to %s: %r' % (db_name, e))
raise
return PoolManager._pools[db_name]
get = staticmethod(get)
def close(db_name):
if db_name is PoolManager._pools:
logger.notifyChannel('dbpool', netsvc.LOG_INFO, 'Closing all connections to %s' % (db_name,))
PoolManager._pools[db_name].closeall()
del PoolManager._pools[db_name]
close = staticmethod(close)
def db_connect(db_name, serialize=0): def db_connect(db_name, serialize=0):
host = tools.config['db_host'] and "host=%s" % tools.config['db_host'] or '' return PoolManager.get(db_name)
port = tools.config['db_port'] and "port=%s" % tools.config['db_port'] or ''
name = "dbname=%s" % db_name
user = tools.config['db_user'] and "user=%s" % tools.config['db_user'] or ''
password = tools.config['db_password'] and "password=%s" % tools.config['db_password'] or ''
maxconn = int(tools.config['db_maxconn']) or 64
tdb = psycopg.connect('%s %s %s %s %s' % (host, port, name, user, password),
serialize=serialize, maxconn=maxconn)
fdb = fakedb(tdb, db_name)
return fdb
def init():
#define DATEOID 1082, define TIMESTAMPOID 1114 see pgtypes.h
psycopg.register_type(psycopg.new_type((1082,), "date", lambda x:x))
psycopg.register_type(psycopg.new_type((1083,), "time", lambda x:x))
psycopg.register_type(psycopg.new_type((1114,), "datetime", lambda x:x))
#psycopg.register_type(psycopg.new_type((700, 701, 1700), 'decimal', decimalize))
psycopg.register_type(psycopg.new_type((1082,), "date", lambda x:x))
psycopg.register_type(psycopg.new_type((1083,), "time", lambda x:x))
psycopg.register_type(psycopg.new_type((1114,), "datetime", lambda x:x))
def close_db(db_name):
return PoolManager.close(db_name)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -116,15 +116,14 @@ def amount_to_text(nbr, lang='en', currency='euro'):
Example: Example:
1654: thousands six cent cinquante-quatre. 1654: thousands six cent cinquante-quatre.
""" """
import netsvc
if nbr > 10000000: if nbr > 10000000:
#TODO: use logger netsvc.Logger().notifyChannel('translate', netsvc.LOG_WARNING, _("Number too large '%d', can not translate it"))
print "WARNING: number too large '%d', can't translate it!" % (nbr,)
return str(nbr) return str(nbr)
if not _translate_funcs.has_key(lang): if not _translate_funcs.has_key(lang):
#TODO: use logger netsvc.Logger().notifyChannel('translate', netsvc.LOG_WARNING, _("no translation function found for lang: '%s'" % (lang,)))
print "WARNING: no translation function found for lang: '%s'" % (lang,) #TODO: (default should be en) same as above
#TODO: (default should be en) same as above
lang = 'en' lang = 'en'
return _translate_funcs[lang](nbr, currency) return _translate_funcs[lang](nbr, currency)

View File

@ -35,7 +35,7 @@ class configmanager(object):
'netport': '8070', 'netport': '8070',
'db_host': False, 'db_host': False,
'db_port': False, 'db_port': False,
'db_name': 'terp', 'db_name': False,
'db_user': False, 'db_user': False,
'db_password': False, 'db_password': False,
'db_maxconn': 64, 'db_maxconn': 64,
@ -62,10 +62,11 @@ class configmanager(object):
'stop_after_init': False, # this will stop the server after initialization 'stop_after_init': False, # this will stop the server after initialization
'price_accuracy': 2, 'price_accuracy': 2,
'assert_exit_level': logging.WARNING, # level above which a failed assert will 'log_level': logging.INFO,
'assert_exit_level': logging.WARNING, # level above which a failed assert will be raise
} }
assert_exit_levels = (netsvc.LOG_CRITICAL, netsvc.LOG_ERROR, netsvc.LOG_WARNING, netsvc.LOG_INFO, netsvc.LOG_DEBUG) loglevels = dict([(getattr(netsvc, 'LOG_%s' % x), getattr(logging, x)) for x in ('CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG')])
version = "%s %s" % (release.description, release.version) version = "%s %s" % (release.description, release.version)
parser = optparse.OptionParser(version=version) parser = optparse.OptionParser(version=version)
@ -89,7 +90,8 @@ class configmanager(object):
# stops the server from launching after initialization # stops the server from launching after initialization
parser.add_option("--stop-after-init", action="store_true", dest="stop_after_init", default=False, help="stop the server after it initializes") parser.add_option("--stop-after-init", action="store_true", dest="stop_after_init", default=False, help="stop the server after it initializes")
parser.add_option('--debug', dest='debug_mode', action='store_true', default=False, help='enable debug mode') parser.add_option('--debug', dest='debug_mode', action='store_true', default=False, help='enable debug mode')
parser.add_option("--assert-exit-level", dest='assert_exit_level', help="specify the level at which a failed assertion will stop the server " + str(assert_exit_levels)) parser.add_option('--log-level', dest='log_level', type='choice', choices=loglevels.keys(), help='specify the level of the logging. Accepted values: ' + str(loglevels.keys()))
parser.add_option("--assert-exit-level", dest='assert_exit_level', type="choice", choices=loglevels.keys(), help="specify the level at which a failed assertion will stop the server. Accepted values: " + str(loglevels.keys()))
parser.add_option("-S", "--secure", dest="secure", action="store_true", help="launch server over https instead of http", default=False) parser.add_option("-S", "--secure", dest="secure", action="store_true", help="launch server over https instead of http", default=False)
parser.add_option('--email-from', dest='email_from', default='', help='specify the SMTP email address for sending email') parser.add_option('--email-from', dest='email_from', default='', help='specify the SMTP email address for sending email')
@ -163,8 +165,9 @@ class configmanager(object):
self.options[arg] = getattr(opt, arg) self.options[arg] = getattr(opt, arg)
if opt.assert_exit_level: if opt.assert_exit_level:
assert opt.assert_exit_level in assert_exit_levels, 'ERROR: The assert-exit-level must be one of those values: '+str(assert_exit_levels) self.options['assert_exit_level'] = loglevels[opt.assert_exit_level]
self.options['assert_exit_level'] = getattr(logging, opt.assert_exit_level.upper()) if opt.log_level:
self.options['log_level'] = loglevels[opt.log_level]
if not self.options['root_path'] or self.options['root_path']=='None': if not self.options['root_path'] or self.options['root_path']=='None':
self.options['root_path'] = os.path.abspath(os.path.dirname(sys.argv[0])) self.options['root_path'] = os.path.abspath(os.path.dirname(sys.argv[0]))

View File

@ -100,7 +100,7 @@ def _eval_xml(self,node, pool, cr, uid, idref, context=None):
import pytz import pytz
except: except:
logger = netsvc.Logger() logger = netsvc.Logger()
logger.notifyChannel("init", netsvc.LOG_INFO, 'could not find pytz library') logger.notifyChannel("init", netsvc.LOG_WARNING, 'could not find pytz library')
class pytzclass(object): class pytzclass(object):
all_timezones=[] all_timezones=[]
pytz=pytzclass() pytz=pytzclass()
@ -479,7 +479,7 @@ form: module.record_id""" % (xml_id,)
pid = False pid = False
for idx, menu_elem in enumerate(m_l): for idx, menu_elem in enumerate(m_l):
if pid: if pid:
cr.execute('select id from ir_ui_menu where parent_id=%d and name=%s', (pid, menu_elem)) cr.execute('select id from ir_ui_menu where parent_id=%s and name=%s', (pid, menu_elem))
else: else:
cr.execute('select id from ir_ui_menu where parent_id is null and name=%s', (menu_elem,)) cr.execute('select id from ir_ui_menu where parent_id is null and name=%s', (menu_elem,))
res = cr.fetchone() res = cr.fetchone()
@ -491,7 +491,7 @@ form: module.record_id""" % (xml_id,)
try: try:
npid = self.pool.get('ir.model.data')._update_dummy(cr, self.uid, 'ir.ui.menu', self.module, xml_id, idx==len(m_l)-1) npid = self.pool.get('ir.model.data')._update_dummy(cr, self.uid, 'ir.ui.menu', self.module, xml_id, idx==len(m_l)-1)
except: except:
print 'Menu Error', self.module, xml_id, idx==len(m_l)-1 self.logger.notifyChannel('init', netsvc.LOG_ERROR, "addon: %s xml_id: %s" % (self.module, xml_id))
else: else:
# the menuitem does't exist but we are in branch (not a leaf) # the menuitem does't exist but we are in branch (not a leaf)
self.logger.notifyChannel("init", netsvc.LOG_WARNING, 'Warning no ID for submenu %s of menu %s !' % (menu_elem, str(m_l))) self.logger.notifyChannel("init", netsvc.LOG_WARNING, 'Warning no ID for submenu %s of menu %s !' % (menu_elem, str(m_l)))
@ -518,15 +518,15 @@ form: module.record_id""" % (xml_id,)
values['icon'] = icons.get(a_type,'STOCK_NEW') values['icon'] = icons.get(a_type,'STOCK_NEW')
if a_type=='act_window': if a_type=='act_window':
a_id = self.id_get(cr, 'ir.actions.%s'% a_type, a_action) a_id = self.id_get(cr, 'ir.actions.%s'% a_type, a_action)
cr.execute('select view_type,view_mode,name,view_id,target from ir_act_window where id=%d', (int(a_id),)) cr.execute('select view_type,view_mode,name,view_id,target from ir_act_window where id=%s', (int(a_id),))
rrres = cr.fetchone() rrres = cr.fetchone()
assert rrres, "No window action defined for this id %s !\n" \ assert rrres, "No window action defined for this id %s !\n" \
"Verify that this is a window action or add a type argument." % (a_action,) "Verify that this is a window action or add a type argument." % (a_action,)
action_type,action_mode,action_name,view_id,target = rrres action_type,action_mode,action_name,view_id,target = rrres
if view_id: if view_id:
cr.execute('SELECT type FROM ir_ui_view WHERE id=%d', (int(view_id),)) cr.execute('SELECT type FROM ir_ui_view WHERE id=%s', (int(view_id),))
action_mode, = cr.fetchone() action_mode, = cr.fetchone()
cr.execute('SELECT view_mode FROM ir_act_window_view WHERE act_window_id=%d ORDER BY sequence LIMIT 1', (int(a_id),)) cr.execute('SELECT view_mode FROM ir_act_window_view WHERE act_window_id=%s ORDER BY sequence LIMIT 1', (int(a_id),))
if cr.rowcount: if cr.rowcount:
action_mode, = cr.fetchone() action_mode, = cr.fetchone()
if action_type=='tree': if action_type=='tree':
@ -543,7 +543,7 @@ form: module.record_id""" % (xml_id,)
values['name'] = action_name values['name'] = action_name
elif a_type=='wizard': elif a_type=='wizard':
a_id = self.id_get(cr, 'ir.actions.%s'% a_type, a_action) a_id = self.id_get(cr, 'ir.actions.%s'% a_type, a_action)
cr.execute('select name from ir_act_wizard where id=%d', (int(a_id),)) cr.execute('select name from ir_act_wizard where id=%s', (int(a_id),))
resw = cr.fetchone() resw = cr.fetchone()
if (not values.get('name', False)) and resw: if (not values.get('name', False)) and resw:
values['name'] = resw[0] values['name'] = resw[0]
@ -612,7 +612,12 @@ form: module.record_id""" % (xml_id,)
count = int(rec_src_count) count = int(rec_src_count)
if len(ids) != count: if len(ids) != count:
self.assert_report.record_assertion(False, severity) self.assert_report.record_assertion(False, severity)
self.logger.notifyChannel('init', severity, 'assertion "' + rec_string + '" failed ! (search count is incorrect: ' + str(len(ids)) + ')' ) msg = 'assertion "%s" failed!\n' \
' Incorrect search count:\n' \
' expected count: %d\n' \
' obtained count: %d\n' \
% (rec_string, count, len(ids))
self.logger.notifyChannel('init', severity, msg)
sevval = getattr(logging, severity.upper()) sevval = getattr(logging, severity.upper())
if sevval >= config['assert_exit_level']: if sevval >= config['assert_exit_level']:
# TODO: define a dedicated exception # TODO: define a dedicated exception
@ -635,10 +640,16 @@ form: module.record_id""" % (xml_id,)
globals['_ref'] = ref globals['_ref'] = ref
for test in [i for i in rec.childNodes if (i.nodeType == i.ELEMENT_NODE and i.nodeName=="test")]: for test in [i for i in rec.childNodes if (i.nodeType == i.ELEMENT_NODE and i.nodeName=="test")]:
f_expr = test.getAttribute("expr").encode('utf-8') f_expr = test.getAttribute("expr").encode('utf-8')
f_val = _eval_xml(self, test, self.pool, cr, uid, self.idref, context=context) or True expected_value = _eval_xml(self, test, self.pool, cr, uid, self.idref, context=context) or True
if eval(f_expr, globals) != f_val: # assertion failed expression_value = eval(f_expr, globals)
if expression_value != expected_value: # assertion failed
self.assert_report.record_assertion(False, severity) self.assert_report.record_assertion(False, severity)
self.logger.notifyChannel('init', severity, 'assertion "' + rec_string + '" failed ! (tag ' + test.toxml() + ')' ) msg = 'assertion "%s" failed!\n' \
' xmltag: %s\n' \
' expected value: %r\n' \
' obtained value: %r\n' \
% (rec_string, test.toxml(), expected_value, expression_value)
self.logger.notifyChannel('init', severity, msg)
sevval = getattr(logging, severity.upper()) sevval = getattr(logging, severity.upper())
if sevval >= config['assert_exit_level']: if sevval >= config['assert_exit_level']:
# TODO: define a dedicated exception # TODO: define a dedicated exception
@ -657,7 +668,7 @@ form: module.record_id""" % (xml_id,)
# if not rec_id and not self.isnoupdate(data_node): # if not rec_id and not self.isnoupdate(data_node):
# print "Warning", rec_model # print "Warning", rec_model
if self.isnoupdate(data_node) and not self.mode == 'init': if self.isnoupdate(data_node) and not self.mode in ('init','update'):
# check if the xml record has an id string # check if the xml record has an id string
if rec_id: if rec_id:
id = self.pool.get('ir.model.data')._update_dummy(cr, self.uid, rec_model, self.module, rec_id) id = self.pool.get('ir.model.data')._update_dummy(cr, self.uid, rec_model, self.module, rec_id)
@ -718,10 +729,11 @@ form: module.record_id""" % (xml_id,)
if isinstance(model._columns[f_name], osv.fields.integer): if isinstance(model._columns[f_name], osv.fields.integer):
f_val = int(f_val) f_val = int(f_val)
res[f_name] = f_val res[f_name] = f_val
id = self.pool.get('ir.model.data')._update(cr, self.uid, rec_model, self.module, res, rec_id or False, not self.isnoupdate(data_node), noupdate=self.isnoupdate(data_node), mode=self.mode ) id = self.pool.get('ir.model.data')._update(cr, self.uid, rec_model, self.module, res, rec_id or False, not self.isnoupdate(data_node), noupdate=self.isnoupdate(data_node), mode=self.mode )
if rec_id: if rec_id:
self.idref[rec_id] = int(id) self.idref[rec_id] = int(id)
if config.get('import_partial', False): if config.get('i mport_partial', False):
cr.commit() cr.commit()
return rec_model, id return rec_model, id
@ -743,7 +755,7 @@ form: module.record_id""" % (xml_id,)
raise Exception( "Mismatch xml format: only terp or openerp as root tag" ) raise Exception( "Mismatch xml format: only terp or openerp as root tag" )
if de.nodeName == 'terp': if de.nodeName == 'terp':
self.logger.notifyChannel("init", netsvc.LOG_WARNING, "The tag <terp /> is deprecated, use <openerp/>") self.logger.notifyChannel("init", netsvc.LOG_WARNING, "The tag <terp/> is deprecated, use <openerp/>")
for n in [i for i in de.childNodes if (i.nodeType == i.ELEMENT_NODE and i.nodeName=="data")]: for n in [i for i in de.childNodes if (i.nodeType == i.ELEMENT_NODE and i.nodeName=="data")]:
for rec in n.childNodes: for rec in n.childNodes:
@ -752,7 +764,7 @@ form: module.record_id""" % (xml_id,)
try: try:
self._tags[rec.nodeName](self.cr, rec, n) self._tags[rec.nodeName](self.cr, rec, n)
except: except:
self.logger.notifyChannel("init", netsvc.LOG_INFO, '\n'+rec.toxml()) self.logger.notifyChannel("init", netsvc.LOG_ERROR, '\n'+rec.toxml())
self.cr.rollback() self.cr.rollback()
raise raise
return True return True
@ -824,9 +836,10 @@ def convert_csv_import(cr, module, fname, csvcontent, idref=None, mode='init',
if (not line) or not reduce(lambda x,y: x or y, line) : if (not line) or not reduce(lambda x,y: x or y, line) :
continue continue
try: try:
datas.append( map(lambda x:x.decode('utf8').encode('utf8'), line)) datas.append(map(lambda x: misc.ustr(x), line))
except: except:
print "ERROR while importing the line: ", line logger = netsvc.Logger()
logger.notifyChannel("init", netsvc.LOG_ERROR, "Can not import the line: %s" % line)
pool.get(model).import_data(cr, uid, fields, datas,mode, module,noupdate,filename=fname_partial) pool.get(model).import_data(cr, uid, fields, datas,mode, module,noupdate,filename=fname_partial)
if config.get('import_partial'): if config.get('import_partial'):
data = pickle.load(file(config.get('import_partial'))) data = pickle.load(file(config.get('import_partial')))

31
bin/tools/func.py Normal file
View File

@ -0,0 +1,31 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved
# $Id$
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
try:
from functools import wraps
except ImportError:
# this module doesn't exist in python < 2.5
# we define a identity decorator
def wraps(f):
def identity(x):
return x
return identity

View File

@ -759,8 +759,6 @@ if __name__=='__main__':
g.process(starting_node) g.process(starting_node)
g.scale(radius*3,radius*3, radius, radius) g.scale(radius*3,radius*3, radius, radius)
print g
import Image import Image
import ImageDraw import ImageDraw
img = Image.new("RGB", (800, 600), "#ffffff") img = Image.new("RGB", (800, 600), "#ffffff")

View File

@ -27,10 +27,7 @@ Miscelleanous tools used by OpenERP.
import os, time, sys import os, time, sys
import inspect import inspect
import psycopg
#import netsvc
from config import config from config import config
#import tools
import zipfile import zipfile
import release import release
@ -71,7 +68,7 @@ def init_db(cr):
if p_id is not None: if p_id is not None:
cr.execute('select id \ cr.execute('select id \
from ir_module_category \ from ir_module_category \
where name=%s and parent_id=%d', (categs[0], p_id)) where name=%s and parent_id=%s', (categs[0], p_id))
else: else:
cr.execute('select id \ cr.execute('select id \
from ir_module_category \ from ir_module_category \
@ -82,7 +79,7 @@ def init_db(cr):
c_id = cr.fetchone()[0] c_id = cr.fetchone()[0]
cr.execute('insert into ir_module_category \ cr.execute('insert into ir_module_category \
(id, name, parent_id) \ (id, name, parent_id) \
values (%d, %s, %d)', (c_id, categs[0], p_id)) values (%s, %s, %s)', (c_id, categs[0], p_id))
else: else:
c_id = c_id[0] c_id = c_id[0]
p_id = c_id p_id = c_id
@ -100,11 +97,10 @@ def init_db(cr):
cr.execute('select nextval(\'ir_module_module_id_seq\')') cr.execute('select nextval(\'ir_module_module_id_seq\')')
id = cr.fetchone()[0] id = cr.fetchone()[0]
cr.execute('insert into ir_module_module \ cr.execute('insert into ir_module_module \
(id, author, latest_version, website, name, shortdesc, description, \ (id, author, website, name, shortdesc, description, \
category_id, state) \ category_id, state) \
values (%d, %s, %s, %s, %s, %s, %s, %d, %s)', ( values (%s, %s, %s, %s, %s, %s, %s, %s)', (
id, info.get('author', ''), id, info.get('author', ''),
release.major_version + '.' + info.get('version', ''),
info.get('website', ''), i, info.get('name', False), info.get('website', ''), i, info.get('name', False),
info.get('description', ''), p_id, state)) info.get('description', ''), p_id, state))
dependencies = info.get('depends', []) dependencies = info.get('depends', [])
@ -388,7 +384,7 @@ def sms_send(user, password, api_id, text, to):
params = urllib.urlencode({'user': user, 'password': password, 'api_id': api_id, 'text': text, 'to':to}) params = urllib.urlencode({'user': user, 'password': password, 'api_id': api_id, 'text': text, 'to':to})
#f = urllib.urlopen("http://api.clickatell.com/http/sendmsg", params) #f = urllib.urlopen("http://api.clickatell.com/http/sendmsg", params)
f = urllib.urlopen("http://196.7.150.220/http/sendmsg", params) f = urllib.urlopen("http://196.7.150.220/http/sendmsg", params)
print f.read() # FIXME: Use the logger if there is an error
return True return True
#--------------------------------------------------------- #---------------------------------------------------------
@ -540,29 +536,36 @@ def is_hashable(h):
# Timeout: 0 = no timeout, otherwise in seconds # Timeout: 0 = no timeout, otherwise in seconds
# #
class cache(object): class cache(object):
def __init__(self, timeout=10000, skiparg=2): def __init__(self, timeout=10000, skiparg=2, multi=None):
self.timeout = timeout self.timeout = timeout
self.skiparg = skiparg
self.multi = multi
self.cache = {} self.cache = {}
def __call__(self, fn): def __call__(self, fn):
arg_names = inspect.getargspec(fn)[0][2:] arg_names = inspect.getargspec(fn)[0][self.skiparg:]
def cached_result(self2, cr=None, *args, **kwargs): def cached_result(self2, cr=None, *args, **kwargs):
if cr is None: if cr is None:
self.cache = {} self.cache = {}
return True return True
if ('clear_keys' in kwargs):
if (kwargs['clear_keys'] in self.cache):
del self.cache[kwargs['clear_keys']]
return True
# Update named arguments with positional argument values # Update named arguments with positional argument values
kwargs.update(dict(zip(arg_names, args))) kwargs2 = kwargs.copy()
for k in kwargs: kwargs2.update(dict(zip(arg_names, args)))
if isinstance(kwargs[k], (list, dict, set)): for k in kwargs2:
kwargs[k] = tuple(kwargs[k]) if isinstance(kwargs2[k], (list, dict, set)):
elif not is_hashable(kwargs[k]): kwargs2[k] = tuple(kwargs2[k])
kwargs[k] = repr(kwargs[k]) elif not is_hashable(kwargs2[k]):
kwargs = kwargs.items() kwargs2[k] = repr(kwargs2[k])
kwargs.sort() kwargs2 = kwargs2.items()
kwargs2.sort()
# Work out key as a tuple of ('argname', value) pairs # Work out key as a tuple of ('argname', value) pairs
key = (('dbname', cr.dbname),) + tuple(kwargs) key = (('dbname', cr.dbname),) + tuple(kwargs2)
# Check cache and return cached value if possible # Check cache and return cached value if possible
if key in self.cache: if key in self.cache:
@ -574,7 +577,7 @@ class cache(object):
# Work out new value, cache it and return it # Work out new value, cache it and return it
# FIXME Should copy() this value to avoid futur modifications of the cache ? # FIXME Should copy() this value to avoid futur modifications of the cache ?
# FIXME What about exceptions ? # FIXME What about exceptions ?
result = fn(self2,cr,**dict(kwargs)) result = fn(self2,cr,*args, **kwargs)
self.cache[key] = (result, time.time()) self.cache[key] = (result, time.time())
return result return result
@ -583,6 +586,28 @@ class cache(object):
def to_xml(s): def to_xml(s):
return s.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;') return s.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;')
def ustr(value):
"""This method is similar to the builtin `str` method, except
it will return Unicode string.
@param value: the value to convert
@rtype: unicode
@return: unicode string
"""
if isinstance(value, unicode):
return value
if hasattr(value, '__unicode__'):
return unicode(value)
if not isinstance(value, str):
value = str(value)
return unicode(value, 'utf-8')
def get_languages(): def get_languages():
languages={ languages={
'bg_BG': u'Bulgarian / български', 'bg_BG': u'Bulgarian / български',
@ -633,7 +658,7 @@ def get_user_companies(cr, user):
res=[x[0] for x in cr.fetchall()] res=[x[0] for x in cr.fetchall()]
res.extend(_get_company_children(cr, res)) res.extend(_get_company_children(cr, res))
return res return res
cr.execute('SELECT comp.id FROM res_company AS comp, res_users AS u WHERE u.id = %d AND comp.id = u.company_id' % (user,)) cr.execute('SELECT comp.id FROM res_company AS comp, res_users AS u WHERE u.id = %s AND comp.id = u.company_id' % (user,))
compids=[cr.fetchone()[0]] compids=[cr.fetchone()[0]]
compids.extend(_get_company_children(cr, compids)) compids.extend(_get_company_children(cr, compids))
return compids return compids
@ -669,39 +694,30 @@ def human_size(sz):
i = i + 1 i = i + 1
return "%0.2f %s" % (s, units[i]) return "%0.2f %s" % (s, units[i])
def logged(when): def logged(f):
def log(f, res, *args, **kwargs): from tools.func import wraps
vector = ['Call -> function: %s' % f]
@wraps(f)
def wrapper(*args, **kwargs):
import netsvc
from pprint import pformat
vector = ['Call -> function: %r' % f]
for i, arg in enumerate(args): for i, arg in enumerate(args):
vector.append( ' arg %02d: %r' % ( i, arg ) ) vector.append(' arg %02d: %s' % (i, pformat(arg)))
for key, value in kwargs.items(): for key, value in kwargs.items():
vector.append( ' kwarg %10s: %r' % ( key, value ) ) vector.append(' kwarg %10s: %s' % (key, pformat(value)))
vector.append( ' result: %r' % res )
print "\n".join(vector)
def pre_logged(f): timeb4 = time.time()
def wrapper(*args, **kwargs): res = f(*args, **kwargs)
res = f(*args, **kwargs)
log(f, res, *args, **kwargs) vector.append(' result: %s' % pformat(res))
return res vector.append(' time delta: %s' % (time.time() - timeb4))
return wrapper #netsvc.Logger().notifyChannel('logged', netsvc.LOG_DEBUG, '\n'.join(vector))
return res
def post_logged(f): return wrapper
def wrapper(*args, **kwargs):
now = time.time()
res = None
try:
res = f(*args, **kwargs)
return res
finally:
log(f, res, *args, **kwargs)
print " time delta: %s" % (time.time() - now)
return wrapper
try:
return { "pre" : pre_logged, "post" : post_logged}[when]
except KeyError, e:
raise ValueError(e), "must to be 'pre' or 'post'"
icons = map(lambda x: (x,x), ['STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_BOLD', icons = map(lambda x: (x,x), ['STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_BOLD',
'STOCK_CANCEL', 'STOCK_CDROM', 'STOCK_CLEAR', 'STOCK_CLOSE', 'STOCK_COLOR_PICKER', 'STOCK_CANCEL', 'STOCK_CDROM', 'STOCK_CLEAR', 'STOCK_CLOSE', 'STOCK_COLOR_PICKER',

View File

@ -34,7 +34,7 @@ def listdir(dir, recursive=False):
return os.listdir(dir) return os.listdir(dir)
res = [] res = []
for root, dirs, files in os.walk(dir): for root, dirs, files in walksymlinks(dir):
root = root[len(dir)+1:] root = root[len(dir)+1:]
res.extend([opj(root, f) for f in files]) res.extend([opj(root, f) for f in files])
return res return res

View File

@ -41,7 +41,7 @@ class UNIX_LINE_TERMINATOR(csv.excel):
csv.register_dialect("UNIX", UNIX_LINE_TERMINATOR) csv.register_dialect("UNIX", UNIX_LINE_TERMINATOR)
# #
# TODO: a caching method # Warning: better use self.pool.get('ir.translation')._get_source if you can
# #
def translate(cr, name, source_type, lang, source=None): def translate(cr, name, source_type, lang, source=None):
if source and name: if source and name:
@ -64,8 +64,10 @@ class GettextAlias(object):
return source return source
except: except:
return source return source
return translate(cr, None, 'code', lang, source) or source
cr.execute('select value from ir_translation where lang=%s and type=%s and src=%s', (lang, 'code', source))
res_trans = cr.fetchone()
return res_trans and res_trans[0] or source
_ = GettextAlias() _ = GettextAlias()
@ -570,7 +572,6 @@ def trans_load_data(db_name, fileobj, fileformat, lang, strict=False, lang_name=
line += 1 line += 1
# skip empty rows and rows where the translation field (=last fiefd) is empty # skip empty rows and rows where the translation field (=last fiefd) is empty
if (not row) or (not row[-1]): if (not row) or (not row[-1]):
#print "translate: skip %s" % repr(row)
continue continue
# dictionary which holds values for this line of the csv file # dictionary which holds values for this line of the csv file

View File

@ -59,7 +59,7 @@ def upgrade():
for module_id,name,url in cr.fetchall(): for module_id,name,url in cr.fetchall():
print '\tremoving module %s' % name print '\tremoving module %s' % name
remove(name) remove(name)
cr.execute('update ir_module_module set state=%s where id=%d', ('uninstalled', module_id)) cr.execute('update ir_module_module set state=%s where id=%s', ('uninstalled', module_id))
cr.commit() cr.commit()
print 'Check for modules to upgrade...' print 'Check for modules to upgrade...'
@ -68,7 +68,7 @@ def upgrade():
print '\tupgrading module %s' % name print '\tupgrading module %s' % name
remove(name) remove(name)
install(name, url) install(name, url)
cr.execute('update ir_module_module set state=%s where id=%d', ('installed', module_id)) cr.execute('update ir_module_module set state=%s where id=%s', ('installed', module_id))
cr.commit() cr.commit()
toupdate.append(name) toupdate.append(name)
@ -77,7 +77,7 @@ def upgrade():
for module_id,name,url in cr.fetchall(): for module_id,name,url in cr.fetchall():
print '\tinstalling module %s' % name print '\tinstalling module %s' % name
install(name, url) install(name, url)
cr.execute('update ir_module_module set state=%s where id=%d', ('installed', module_id)) cr.execute('update ir_module_module set state=%s where id=%s', ('installed', module_id))
cr.commit() cr.commit()
toinit.append(name) toinit.append(name)

View File

@ -54,7 +54,7 @@ class interface(netsvc.Service):
if node.hasAttribute('string') and node.getAttribute('string'): if node.hasAttribute('string') and node.getAttribute('string'):
trans = translate(cr, self.wiz_name+','+state, 'wizard_view', lang, node.getAttribute('string').encode('utf8')) trans = translate(cr, self.wiz_name+','+state, 'wizard_view', lang, node.getAttribute('string').encode('utf8'))
if trans: if trans:
node.setAttribute('string', trans.decode('utf8')) node.setAttribute('string', trans)
for n in node.childNodes: for n in node.childNodes:
self.translate_view(cr, n, state, lang) self.translate_view(cr, n, state, lang)

View File

@ -30,8 +30,8 @@ def create(cr, ident, wkf_id):
(uid,res_type,res_id) = ident (uid,res_type,res_id) = ident
cr.execute("select nextval('wkf_instance_id_seq')") cr.execute("select nextval('wkf_instance_id_seq')")
id_new = cr.fetchone()[0] id_new = cr.fetchone()[0]
cr.execute('insert into wkf_instance (id,res_type,res_id,uid,wkf_id) values (%d,%s,%s,%s,%s)', (id_new,res_type,res_id,uid,wkf_id)) cr.execute('insert into wkf_instance (id,res_type,res_id,uid,wkf_id) values (%s,%s,%s,%s,%s)', (id_new,res_type,res_id,uid,wkf_id))
cr.execute('select * from wkf_activity where flow_start=True and wkf_id=%d', (wkf_id,)) cr.execute('select * from wkf_activity where flow_start=True and wkf_id=%s', (wkf_id,))
res = cr.dictfetchall() res = cr.dictfetchall()
stack = [] stack = []
workitem.create(cr, res, id_new, ident, stack=stack) workitem.create(cr, res, id_new, ident, stack=stack)
@ -40,10 +40,10 @@ def create(cr, ident, wkf_id):
def delete(cr, ident): def delete(cr, ident):
(uid,res_type,res_id) = ident (uid,res_type,res_id) = ident
cr.execute('delete from wkf_instance where res_id=%d and res_type=%s', (res_id,res_type)) cr.execute('delete from wkf_instance where res_id=%s and res_type=%s', (res_id,res_type))
def validate(cr, inst_id, ident, signal, force_running=False): def validate(cr, inst_id, ident, signal, force_running=False):
cr.execute("select * from wkf_workitem where inst_id=%d", (inst_id,)) cr.execute("select * from wkf_workitem where inst_id=%s", (inst_id,))
for witem in cr.dictfetchall(): for witem in cr.dictfetchall():
stack = [] stack = []
workitem.process(cr, witem, ident, signal, force_running, stack=stack) workitem.process(cr, witem, ident, signal, force_running, stack=stack)
@ -52,27 +52,27 @@ def validate(cr, inst_id, ident, signal, force_running=False):
return stack and stack[0] or False return stack and stack[0] or False
def update(cr, inst_id, ident): def update(cr, inst_id, ident):
cr.execute("select * from wkf_workitem where inst_id=%d", (inst_id,)) cr.execute("select * from wkf_workitem where inst_id=%s", (inst_id,))
for witem in cr.dictfetchall(): for witem in cr.dictfetchall():
stack = [] stack = []
workitem.process(cr, witem, ident, stack=stack) workitem.process(cr, witem, ident, stack=stack)
return _update_end(cr, inst_id, ident) return _update_end(cr, inst_id, ident)
def _update_end(cr, inst_id, ident): def _update_end(cr, inst_id, ident):
cr.execute('select wkf_id from wkf_instance where id=%d', (inst_id,)) cr.execute('select wkf_id from wkf_instance where id=%s', (inst_id,))
wkf_id = cr.fetchone()[0] wkf_id = cr.fetchone()[0]
cr.execute('select state,flow_stop from wkf_workitem w left join wkf_activity a on (a.id=w.act_id) where w.inst_id=%d', (inst_id,)) cr.execute('select state,flow_stop from wkf_workitem w left join wkf_activity a on (a.id=w.act_id) where w.inst_id=%s', (inst_id,))
ok=True ok=True
for r in cr.fetchall(): for r in cr.fetchall():
if (r[0]<>'complete') or not r[1]: if (r[0]<>'complete') or not r[1]:
ok=False ok=False
break break
if ok: if ok:
cr.execute('select distinct a.name from wkf_activity a left join wkf_workitem w on (a.id=w.act_id) where w.inst_id=%d', (inst_id,)) cr.execute('select distinct a.name from wkf_activity a left join wkf_workitem w on (a.id=w.act_id) where w.inst_id=%s', (inst_id,))
act_names = cr.fetchall() act_names = cr.fetchall()
cr.execute("update wkf_instance set state='complete' where id=%d", (inst_id,)) cr.execute("update wkf_instance set state='complete' where id=%s", (inst_id,))
cr.execute("update wkf_workitem set state='complete' where subflow_id=%d", (inst_id,)) cr.execute("update wkf_workitem set state='complete' where subflow_id=%s", (inst_id,))
cr.execute("select i.id,w.osv,i.res_id from wkf_instance i left join wkf w on (i.wkf_id=w.id) where i.id in (select inst_id from wkf_workitem where subflow_id=%d)", (inst_id,)) cr.execute("select i.id,w.osv,i.res_id from wkf_instance i left join wkf w on (i.wkf_id=w.id) where i.id in (select inst_id from wkf_workitem where subflow_id=%s)", (inst_id,))
for i in cr.fetchall(): for i in cr.fetchall():
for act_name in act_names: for act_name in act_names:
validate(cr, i[0], (ident[0],i[1],i[2]), 'subflow.'+act_name[0]) validate(cr, i[0], (ident[0],i[1],i[2]), 'subflow.'+act_name[0])

View File

@ -66,7 +66,7 @@ def _eval_expr(cr, ident, workitem, action):
ret=False ret=False
assert action, 'You used a NULL action in a workflow, use dummy node instead.' assert action, 'You used a NULL action in a workflow, use dummy node instead.'
for line in action.split('\n'): for line in action.split('\n'):
line = line.replace(chr(13),'') line = line.strip()
uid=ident[0] uid=ident[0]
model=ident[1] model=ident[1]
ids=[ident[2]] ids=[ident[2]]

View File

@ -23,10 +23,19 @@
# #
# May be uncommented to logs workflows modifications # May be uncommented to logs workflows modifications
# #
import netsvc
def log(cr,ident,act_id,info=''): def log(cr,ident,act_id,info=''):
pass msg = """
#cr.execute('insert into wkf_logs (res_type, res_id, uid, act_id, time, info) values (%s,%d,%d,%d,current_time,%s)', (ident[1],int(ident[2]),int(ident[0]),int(act_id),info)) res_type: %r
res_id: %d
uid: %d
act_id: %d
info: %s
""" % (ident[1], ident[2], ident[0], act_id, info)
netsvc.Logger().notifyChannel('wkf_log', netsvc.LOG_DEBUG, msg)
#cr.execute('insert into wkf_logs (res_type, res_id, uid, act_id, time, info) values (%s,%s,%s,%s,current_time,%s)', (ident[1],int(ident[2]),int(ident[0]),int(act_id),info))
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -44,15 +44,15 @@ class workflow_service(netsvc.Service):
def trg_write(self, uid, res_type, res_id, cr): def trg_write(self, uid, res_type, res_id, cr):
ident = (uid,res_type,res_id) ident = (uid,res_type,res_id)
cr.execute('select id from wkf_instance where res_id=%d and res_type=%s and state=%s', (res_id,res_type, 'active')) cr.execute('select id from wkf_instance where res_id=%s and res_type=%s and state=%s', (res_id or None,res_type or None, 'active'))
for (id,) in cr.fetchall(): for (id,) in cr.fetchall():
instance.update(cr, id, ident) instance.update(cr, id, ident)
def trg_trigger(self, uid, res_type, res_id, cr): def trg_trigger(self, uid, res_type, res_id, cr):
cr.execute('select instance_id from wkf_triggers where res_id=%d and model=%s', (res_id,res_type)) cr.execute('select instance_id from wkf_triggers where res_id=%s and model=%s', (res_id,res_type))
res = cr.fetchall() res = cr.fetchall()
for (instance_id,) in res: for (instance_id,) in res:
cr.execute('select uid,res_type,res_id from wkf_instance where id=%d', (instance_id,)) cr.execute('select uid,res_type,res_id from wkf_instance where id=%s', (instance_id,))
ident = cr.fetchone() ident = cr.fetchone()
instance.update(cr, instance_id, ident) instance.update(cr, instance_id, ident)
@ -76,7 +76,7 @@ class workflow_service(netsvc.Service):
result = False result = False
ident = (uid,res_type,res_id) ident = (uid,res_type,res_id)
# ids of all active workflow instances for a corresponding resource (id, model_nam) # ids of all active workflow instances for a corresponding resource (id, model_nam)
cr.execute('select id from wkf_instance where res_id=%d and res_type=%s and state=%s', (res_id, res_type, 'active')) cr.execute('select id from wkf_instance where res_id=%s and res_type=%s and state=%s', (res_id, res_type, 'active'))
for (id,) in cr.fetchall(): for (id,) in cr.fetchall():
res2 = instance.validate(cr, id, ident, signal) res2 = instance.validate(cr, id, ident, signal)
result = result or res2 result = result or res2
@ -88,21 +88,21 @@ class workflow_service(netsvc.Service):
def trg_redirect(self, uid, res_type, res_id, new_rid, cr): def trg_redirect(self, uid, res_type, res_id, new_rid, cr):
# get ids of wkf instances for the old resource (res_id) # get ids of wkf instances for the old resource (res_id)
#CHECKME: shouldn't we get only active instances? #CHECKME: shouldn't we get only active instances?
cr.execute('select id, wkf_id from wkf_instance where res_id=%d and res_type=%s', (res_id, res_type)) cr.execute('select id, wkf_id from wkf_instance where res_id=%s and res_type=%s', (res_id, res_type))
for old_inst_id, wkf_id in cr.fetchall(): for old_inst_id, wkf_id in cr.fetchall():
# first active instance for new resource (new_rid), using same wkf # first active instance for new resource (new_rid), using same wkf
cr.execute( cr.execute(
'SELECT id '\ 'SELECT id '\
'FROM wkf_instance '\ 'FROM wkf_instance '\
'WHERE res_id=%d AND res_type=%s AND wkf_id=%d AND state=%s', 'WHERE res_id=%s AND res_type=%s AND wkf_id=%s AND state=%s',
(new_rid, res_type, wkf_id, 'active')) (new_rid, res_type, wkf_id, 'active'))
new_id = cr.fetchone() new_id = cr.fetchone()
if new_id: if new_id:
# select all workitems which "wait" for the old instance # select all workitems which "wait" for the old instance
cr.execute('select id from wkf_workitem where subflow_id=%d', (old_inst_id,)) cr.execute('select id from wkf_workitem where subflow_id=%s', (old_inst_id,))
for (item_id,) in cr.fetchall(): for (item_id,) in cr.fetchall():
# redirect all those workitems to the wkf instance of the new resource # redirect all those workitems to the wkf instance of the new resource
cr.execute('update wkf_workitem set subflow_id=%d where id=%d', (new_id[0], item_id)) cr.execute('update wkf_workitem set subflow_id=%s where id=%s', (new_id[0], item_id))
workflow_service() workflow_service()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -22,7 +22,7 @@
# #
# TODO: # TODO:
# cr.execute('delete from wkf_triggers where model=%s and res_id=%d', (res_type,res_id)) # cr.execute('delete from wkf_triggers where model=%s and res_id=%s', (res_type,res_id))
# #
import netsvc import netsvc
@ -35,8 +35,8 @@ def create(cr, act_datas, inst_id, ident, stack):
for act in act_datas: for act in act_datas:
cr.execute("select nextval('wkf_workitem_id_seq')") cr.execute("select nextval('wkf_workitem_id_seq')")
id_new = cr.fetchone()[0] id_new = cr.fetchone()[0]
cr.execute("insert into wkf_workitem (id,act_id,inst_id,state) values (%d,%s,%s,'active')", (id_new, act['id'], inst_id)) cr.execute("insert into wkf_workitem (id,act_id,inst_id,state) values (%s,%s,%s,'active')", (id_new, act['id'], inst_id))
cr.execute('select * from wkf_workitem where id=%d',(id_new,)) cr.execute('select * from wkf_workitem where id=%s',(id_new,))
res = cr.dictfetchone() res = cr.dictfetchone()
wkf_logs.log(cr,ident,act['id'],'active') wkf_logs.log(cr,ident,act['id'],'active')
process(cr, res, ident, stack=stack) process(cr, res, ident, stack=stack)
@ -45,7 +45,7 @@ def process(cr, workitem, ident, signal=None, force_running=False, stack=None):
if stack is None: if stack is None:
raise 'Error !!!' raise 'Error !!!'
result = True result = True
cr.execute('select * from wkf_activity where id=%d', (workitem['act_id'],)) cr.execute('select * from wkf_activity where id=%s', (workitem['act_id'],))
activity = cr.dictfetchone() activity = cr.dictfetchone()
triggers = False triggers = False
@ -63,7 +63,7 @@ def process(cr, workitem, ident, signal=None, force_running=False, stack=None):
triggers = triggers and not ok triggers = triggers and not ok
if triggers: if triggers:
cr.execute('select * from wkf_transition where act_from=%d', (workitem['act_id'],)) cr.execute('select * from wkf_transition where act_from=%s', (workitem['act_id'],))
alltrans = cr.dictfetchall() alltrans = cr.dictfetchall()
for trans in alltrans: for trans in alltrans:
if trans['trigger_model']: if trans['trigger_model']:
@ -71,7 +71,7 @@ def process(cr, workitem, ident, signal=None, force_running=False, stack=None):
for res_id in ids: for res_id in ids:
cr.execute('select nextval(\'wkf_triggers_id_seq\')') cr.execute('select nextval(\'wkf_triggers_id_seq\')')
id =cr.fetchone()[0] id =cr.fetchone()[0]
cr.execute('insert into wkf_triggers (model,res_id,instance_id,workitem_id,id) values (%s,%d,%d,%d,%d)', (trans['trigger_model'],res_id,workitem['inst_id'], workitem['id'], id)) cr.execute('insert into wkf_triggers (model,res_id,instance_id,workitem_id,id) values (%s,%s,%s,%s,%s)', (trans['trigger_model'],res_id,workitem['inst_id'], workitem['id'], id))
return result return result
@ -79,7 +79,7 @@ def process(cr, workitem, ident, signal=None, force_running=False, stack=None):
# ---------------------- PRIVATE FUNCS -------------------------------- # ---------------------- PRIVATE FUNCS --------------------------------
def _state_set(cr, workitem, activity, state, ident): def _state_set(cr, workitem, activity, state, ident):
cr.execute('update wkf_workitem set state=%s where id=%d', (state,workitem['id'])) cr.execute('update wkf_workitem set state=%s where id=%s', (state,workitem['id']))
workitem['state'] = state workitem['state'] = state
wkf_logs.log(cr,ident,activity['id'],state) wkf_logs.log(cr,ident,activity['id'],state)
@ -89,7 +89,7 @@ def _execute(cr, workitem, activity, ident, stack):
# send a signal to parent workflow (signal: subflow.signal_name) # send a signal to parent workflow (signal: subflow.signal_name)
# #
if (workitem['state']=='active') and activity['signal_send']: if (workitem['state']=='active') and activity['signal_send']:
cr.execute("select i.id,w.osv,i.res_id from wkf_instance i left join wkf w on (i.wkf_id=w.id) where i.id in (select inst_id from wkf_workitem where subflow_id=%d)", (workitem['inst_id'],)) cr.execute("select i.id,w.osv,i.res_id from wkf_instance i left join wkf w on (i.wkf_id=w.id) where i.id in (select inst_id from wkf_workitem where subflow_id=%s)", (workitem['inst_id'],))
for i in cr.fetchall(): for i in cr.fetchall():
instance.validate(cr, i[0], (ident[0],i[1],i[2]), activity['signal_send'], force_running=True) instance.validate(cr, i[0], (ident[0],i[1],i[2]), activity['signal_send'], force_running=True)
@ -97,7 +97,6 @@ def _execute(cr, workitem, activity, ident, stack):
if workitem['state']=='active': if workitem['state']=='active':
_state_set(cr, workitem, activity, 'complete', ident) _state_set(cr, workitem, activity, 'complete', ident)
if activity['action_id']: if activity['action_id']:
print 'ICI'
res2 = wkf_expr.execute_action(cr, ident, workitem, activity) res2 = wkf_expr.execute_action(cr, ident, workitem, activity)
if res2: if res2:
stack.append(res2) stack.append(res2)
@ -116,7 +115,7 @@ def _execute(cr, workitem, activity, ident, stack):
elif activity['kind']=='stopall': elif activity['kind']=='stopall':
if workitem['state']=='active': if workitem['state']=='active':
_state_set(cr, workitem, activity, 'running', ident) _state_set(cr, workitem, activity, 'running', ident)
cr.execute('delete from wkf_workitem where inst_id=%d and id<>%d', (workitem['inst_id'], workitem['id'])) cr.execute('delete from wkf_workitem where inst_id=%s and id<>%s', (workitem['inst_id'], workitem['id']))
if activity['action']: if activity['action']:
wkf_expr.execute(cr, ident, workitem, activity) wkf_expr.execute(cr, ident, workitem, activity)
_state_set(cr, workitem, activity, 'complete', ident) _state_set(cr, workitem, activity, 'complete', ident)
@ -129,14 +128,14 @@ def _execute(cr, workitem, activity, ident, stack):
cr.execute('delete from wkf_workitem where id=%s', (workitem['id'],)) cr.execute('delete from wkf_workitem where id=%s', (workitem['id'],))
return False return False
assert type(id_new)==type(1) or type(id_new)==type(1L), 'Wrong return value: '+str(id_new)+' '+str(type(id_new)) assert type(id_new)==type(1) or type(id_new)==type(1L), 'Wrong return value: '+str(id_new)+' '+str(type(id_new))
cr.execute('select id from wkf_instance where res_id=%d and wkf_id=%d', (id_new,activity['subflow_id'])) cr.execute('select id from wkf_instance where res_id=%s and wkf_id=%s', (id_new,activity['subflow_id']))
id_new = cr.fetchone()[0] id_new = cr.fetchone()[0]
else: else:
id_new = instance.create(cr, ident, activity['subflow_id']) id_new = instance.create(cr, ident, activity['subflow_id'])
cr.execute('update wkf_workitem set subflow_id=%d where id=%s', (id_new, workitem['id'])) cr.execute('update wkf_workitem set subflow_id=%s where id=%s', (id_new, workitem['id']))
workitem['subflow_id'] = id_new workitem['subflow_id'] = id_new
if workitem['state']=='running': if workitem['state']=='running':
cr.execute("select state from wkf_instance where id=%d", (workitem['subflow_id'],)) cr.execute("select state from wkf_instance where id=%s", (workitem['subflow_id'],))
state= cr.fetchone()[0] state= cr.fetchone()[0]
if state=='complete': if state=='complete':
_state_set(cr, workitem, activity, 'complete', ident) _state_set(cr, workitem, activity, 'complete', ident)
@ -145,7 +144,7 @@ def _execute(cr, workitem, activity, ident, stack):
def _split_test(cr, workitem, split_mode, ident, signal=None, stack=None): def _split_test(cr, workitem, split_mode, ident, signal=None, stack=None):
if stack is None: if stack is None:
raise 'Error !!!' raise 'Error !!!'
cr.execute('select * from wkf_transition where act_from=%d', (workitem['act_id'],)) cr.execute('select * from wkf_transition where act_from=%s', (workitem['act_id'],))
test = False test = False
transitions = [] transitions = []
alltrans = cr.dictfetchall() alltrans = cr.dictfetchall()
@ -162,36 +161,36 @@ def _split_test(cr, workitem, split_mode, ident, signal=None, stack=None):
if not wkf_expr.check(cr, workitem, ident, transition,signal): if not wkf_expr.check(cr, workitem, ident, transition,signal):
test = False test = False
break break
cr.execute('select count(*) from wkf_witm_trans where trans_id=%d and inst_id=%d', (transition['id'], workitem['inst_id'])) cr.execute('select count(*) from wkf_witm_trans where trans_id=%s and inst_id=%s', (transition['id'], workitem['inst_id']))
if not cr.fetchone()[0]: if not cr.fetchone()[0]:
transitions.append((transition['id'], workitem['inst_id'])) transitions.append((transition['id'], workitem['inst_id']))
if test and len(transitions): if test and len(transitions):
cr.executemany('insert into wkf_witm_trans (trans_id,inst_id) values (%d,%d)', transitions) cr.executemany('insert into wkf_witm_trans (trans_id,inst_id) values (%s,%s)', transitions)
cr.execute('delete from wkf_workitem where id=%d', (workitem['id'],)) cr.execute('delete from wkf_workitem where id=%s', (workitem['id'],))
for t in transitions: for t in transitions:
_join_test(cr, t[0], t[1], ident, stack) _join_test(cr, t[0], t[1], ident, stack)
return True return True
return False return False
def _join_test(cr, trans_id, inst_id, ident, stack): def _join_test(cr, trans_id, inst_id, ident, stack):
cr.execute('select * from wkf_activity where id=(select act_to from wkf_transition where id=%d)', (trans_id,)) cr.execute('select * from wkf_activity where id=(select act_to from wkf_transition where id=%s)', (trans_id,))
activity = cr.dictfetchone() activity = cr.dictfetchone()
if activity['join_mode']=='XOR': if activity['join_mode']=='XOR':
create(cr,[activity], inst_id, ident, stack) create(cr,[activity], inst_id, ident, stack)
cr.execute('delete from wkf_witm_trans where inst_id=%d and trans_id=%d', (inst_id,trans_id)) cr.execute('delete from wkf_witm_trans where inst_id=%s and trans_id=%s', (inst_id,trans_id))
else: else:
cr.execute('select id from wkf_transition where act_to=%d', (activity['id'],)) cr.execute('select id from wkf_transition where act_to=%s', (activity['id'],))
trans_ids = cr.fetchall() trans_ids = cr.fetchall()
ok = True ok = True
for (id,) in trans_ids: for (id,) in trans_ids:
cr.execute('select count(*) from wkf_witm_trans where trans_id=%d and inst_id=%d', (id,inst_id)) cr.execute('select count(*) from wkf_witm_trans where trans_id=%s and inst_id=%s', (id,inst_id))
res = cr.fetchone()[0] res = cr.fetchone()[0]
if not res: if not res:
ok = False ok = False
break break
if ok: if ok:
for (id,) in trans_ids: for (id,) in trans_ids:
cr.execute('delete from wkf_witm_trans where trans_id=%d and inst_id=%d', (id,inst_id)) cr.execute('delete from wkf_witm_trans where trans_id=%s and inst_id=%s', (id,inst_id))
create(cr, [activity], inst_id, ident, stack) create(cr, [activity], inst_id, ident, stack)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -54,7 +54,7 @@ execfile(opj('bin', 'release.py'))
py_short_version = '%s.%s' % sys.version_info[:2] py_short_version = '%s.%s' % sys.version_info[:2]
required_modules = [ required_modules = [
('psycopg', 'PostgreSQL module'), ('psycopg2', 'PostgreSQL module'),
('xml', 'XML Tools for python'), ('xml', 'XML Tools for python'),
('libxml2', 'libxml2 python bindings'), ('libxml2', 'libxml2 python bindings'),
('libxslt', 'libxslt python bindings'), ('libxslt', 'libxslt python bindings'),