diff --git a/bin/addons/base/base.sql b/bin/addons/base/base.sql index 84676a7f59b..eb9eb5b9d75 100644 --- a/bin/addons/base/base.sql +++ b/bin/addons/base/base.sql @@ -148,7 +148,6 @@ CREATE TABLE res_users ( context_lang varchar(64) default '', -- No FK references below, will be added later by ORM -- (when the destination rows exist) - action_id int, company_id int, primary key(id) ); @@ -345,7 +344,7 @@ CREATE TABLE ir_model_data ( -- Users --------------------------------- -insert into res_users (id,login,password,name,action_id,active,company_id) values (1,'admin','admin','Administrator',NULL,True,1); +insert into res_users (id,login,password,name,active,company_id) values (1,'admin','admin','Administrator',True,1); insert into ir_model_data (name,module,model,noupdate,res_id) values ('user_root','base','res.users',True,1); -- Compatibility purpose, to remove V6.0 diff --git a/bin/addons/base/base_data.xml b/bin/addons/base/base_data.xml index 5082adfbfe0..9597ac83f92 100644 --- a/bin/addons/base/base_data.xml +++ b/bin/addons/base/base_data.xml @@ -1053,8 +1053,6 @@ - - @@ -1375,6 +1373,9 @@ - + + Reserve + RSV + diff --git a/bin/addons/base/base_demo.xml b/bin/addons/base/base_demo.xml index 235e7e77b75..7779171fe08 100644 --- a/bin/addons/base/base_demo.xml +++ b/bin/addons/base/base_demo.xml @@ -6,8 +6,6 @@ demo Demo User Mr Demo - - diff --git a/bin/addons/base/base_update.xml b/bin/addons/base/base_update.xml index 361bbfa67a0..4218d6ce278 100644 --- a/bin/addons/base/base_update.xml +++ b/bin/addons/base/base_update.xml @@ -123,7 +123,7 @@ - + diff --git a/bin/addons/base/i18n/bs.po b/bin/addons/base/i18n/bs.po index af99a2b5185..5eace0482b4 100644 --- a/bin/addons/base/i18n/bs.po +++ b/bin/addons/base/i18n/bs.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.0\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-09-12 21:46+0000\n" -"Last-Translator: Bojan Markovic \n" +"PO-Revision-Date: 2010-09-29 08:03+0000\n" +"Last-Translator: OpenERP Administrators \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-09-29 04:42+0000\n" +"X-Launchpad-Export-Date: 2010-09-30 04:36+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/en_GB.po b/bin/addons/base/i18n/en_GB.po index 0e2166dc01f..098e3d1129a 100644 --- a/bin/addons/base/i18n/en_GB.po +++ b/bin/addons/base/i18n/en_GB.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: openobject-server\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-09-11 15:00+0000\n" -"Last-Translator: Martin Hood \n" +"PO-Revision-Date: 2010-09-29 08:03+0000\n" +"Last-Translator: OpenERP Administrators \n" "Language-Team: English (United Kingdom) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-09-29 04:46+0000\n" +"X-Launchpad-Export-Date: 2010-09-30 04:38+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/et.po b/bin/addons/base/i18n/et.po index 0814b45f278..2d639fd8d86 100644 --- a/bin/addons/base/i18n/et.po +++ b/bin/addons/base/i18n/et.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-09-21 14:25+0000\n" -"Last-Translator: Tanel Kurvits \n" +"PO-Revision-Date: 2010-09-29 07:51+0000\n" +"Last-Translator: OpenERP Administrators \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-09-29 04:43+0000\n" +"X-Launchpad-Export-Date: 2010-09-30 04:36+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/fr.po b/bin/addons/base/i18n/fr.po index 49ba7240160..d3bbc43f35c 100644 --- a/bin/addons/base/i18n/fr.po +++ b/bin/addons/base/i18n/fr.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-09-16 19:03+0000\n" -"Last-Translator: Olivier Dony (OpenERP) \n" +"PO-Revision-Date: 2010-09-29 08:01+0000\n" +"Last-Translator: OpenERP Administrators \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-09-29 04:43+0000\n" +"X-Launchpad-Export-Date: 2010-09-30 04:37+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/it.po b/bin/addons/base/i18n/it.po index 75981ae4507..a43aa4a42de 100644 --- a/bin/addons/base/i18n/it.po +++ b/bin/addons/base/i18n/it.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-09-19 17:45+0000\n" -"Last-Translator: Nicola Riolini - Micronaet \n" +"PO-Revision-Date: 2010-09-29 08:28+0000\n" +"Last-Translator: OpenERP Administrators \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-09-29 04:44+0000\n" +"X-Launchpad-Export-Date: 2010-09-30 04:37+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/mn.po b/bin/addons/base/i18n/mn.po index 521063fcfbe..247e7f202fe 100644 --- a/bin/addons/base/i18n/mn.po +++ b/bin/addons/base/i18n/mn.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-09-13 08:00+0000\n" +"PO-Revision-Date: 2010-09-29 08:13+0000\n" "Last-Translator: ub121 \n" "Language-Team: Mongolian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-09-29 04:44+0000\n" +"X-Launchpad-Export-Date: 2010-09-30 04:37+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/nl.po b/bin/addons/base/i18n/nl.po index 0e1b62fa594..534a06b7f62 100644 --- a/bin/addons/base/i18n/nl.po +++ b/bin/addons/base/i18n/nl.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.0\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-09-09 18:36+0000\n" -"Last-Translator: Anup (OpenERP) \n" +"PO-Revision-Date: 2010-10-01 11:47+0000\n" +"Last-Translator: OpenERP Administrators \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-09-29 04:42+0000\n" +"X-Launchpad-Export-Date: 2010-10-02 05:05+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base @@ -3366,7 +3366,7 @@ msgstr "U kunt het menu opnieuw laden met (Ctrl+t Ctrl+r)." #: view:ir.ui.view_sc:0 #: field:res.partner.title,shortcut:0 msgid "Shortcut" -msgstr "Sneltoets" +msgstr "Snelkoppeling" #. module: base #: field:ir.model.data,date_init:0 @@ -8109,34 +8109,35 @@ msgstr "U kunt niet twee gebruikers hebben met dezelfde gebruikersnaam !" #. module: base #: sql_constraint:ir.model.fields:0 msgid "Size of the field can never be less than 1 !" -msgstr "" +msgstr "De veldlengte kan nooit kleiner dan 1 zijn !" #. module: base #: sql_constraint:ir.ui.view_sc:0 msgid "Shortcut for this menu already exists!" -msgstr "" +msgstr "Dit menu heeft al een snelkoppeling!" #. module: base #: sql_constraint:ir.model.data:0 msgid "" "You cannot have multiple records with the same id for the same module !" msgstr "" +"U kunt niet meer records hebben met dezelfde id voor dezelfde module !" #. module: base #: sql_constraint:maintenance.contract:0 msgid "Your maintenance contract is already subscribed in the system !" -msgstr "" +msgstr "Uw onderhoudscontract is al ingeschreven in het systeem !" #. module: base #: sql_constraint:ir.module.module:0 #: sql_constraint:ir.module.web:0 msgid "The name of the module must be unique !" -msgstr "" +msgstr "De modulenaam moet uniek zijn !" #. module: base #: sql_constraint:ir.module.module:0 msgid "The certificate ID of the module must be unique !" -msgstr "" +msgstr "Het kwaliteitscertificaat id van de module moet uniek zijn !" #. module: base #: sql_constraint:res.partner.function:0 @@ -8146,38 +8147,38 @@ msgstr "" #. module: base #: sql_constraint:res.partner:0 msgid "The name of the Partner must be unique !" -msgstr "" +msgstr "De relatienaam moet uniek zijn !" #. module: base #: sql_constraint:res.country:0 msgid "The name of the country must be unique !" -msgstr "" +msgstr "De landnaam moet uniek zijn !" #. module: base #: sql_constraint:res.country:0 msgid "The code of the country must be unique !" -msgstr "" +msgstr "De landcode moet uniek zijn !" #. module: base #: sql_constraint:res.lang:0 msgid "The name of the language must be unique !" -msgstr "" +msgstr "De taalnaam moet uniek zijn !" #. module: base #: sql_constraint:res.lang:0 msgid "The code of the language must be unique !" -msgstr "" +msgstr "De taalcode moet uniek zijn !" #. module: base #: sql_constraint:res.groups:0 msgid "The name of the group must be unique !" -msgstr "" +msgstr "De groepsnaam moet uniek zijn !" #. module: base #: code:addons/osv/osv.py:0 #, python-format msgid "Constraint Error" -msgstr "" +msgstr "Voorwaarde Fout" #. module: base #: code:addons/osv/osv.py:0 @@ -8188,6 +8189,10 @@ msgid "" "reference it\n" "- creation/update: a mandatory field is not correctly set" msgstr "" +"De bewerking kan niet worden afgerond, waarschijnlijk vanwege:\n" +"- verwijdering: u probeert een record te verwijderen terwijl er nog door een " +"ander aan wordt gerefereerd\n" +"- maken/wijzigen: een verplicht veld is niet correct ingevuld" #. module: base #: code:addons/osv/osv.py:0 @@ -8197,12 +8202,15 @@ msgid "" "\n" "[object with reference: %s - %s]" msgstr "" +"\n" +"\n" +"[object met referentie: %s - %s]" #. module: base #: code:addons/osv/osv.py:0 #, python-format msgid "Integrity Error" -msgstr "" +msgstr "Integriteit Fout" #~ msgid "Main Company" #~ msgstr "Moederorganisatie" diff --git a/bin/addons/base/i18n/pl.po b/bin/addons/base/i18n/pl.po index e9604cd0de8..eca6c8a4393 100644 --- a/bin/addons/base/i18n/pl.po +++ b/bin/addons/base/i18n/pl.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.4\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-09-26 10:02+0000\n" -"Last-Translator: Michał Węgrzynek \n" +"PO-Revision-Date: 2010-09-29 08:34+0000\n" +"Last-Translator: OpenERP Administrators \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-09-29 04:45+0000\n" +"X-Launchpad-Export-Date: 2010-09-30 04:37+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/pt_BR.po b/bin/addons/base/i18n/pt_BR.po index 9bae937bb20..01a30d302a4 100644 --- a/bin/addons/base/i18n/pt_BR.po +++ b/bin/addons/base/i18n/pt_BR.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: pt_BR\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-09-09 18:12+0000\n" +"PO-Revision-Date: 2010-09-29 08:12+0000\n" "Last-Translator: Anup (OpenERP) \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-09-29 04:46+0000\n" +"X-Launchpad-Export-Date: 2010-09-30 04:37+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/i18n/sv.po b/bin/addons/base/i18n/sv.po index ed47e2be7c6..657c35a352f 100644 --- a/bin/addons/base/i18n/sv.po +++ b/bin/addons/base/i18n/sv.po @@ -7,13 +7,13 @@ msgstr "" "Project-Id-Version: OpenERP Server 5.0.0\n" "Report-Msgid-Bugs-To: support@openerp.com\n" "POT-Creation-Date: 2009-12-18 08:39+0000\n" -"PO-Revision-Date: 2010-09-27 12:19+0000\n" -"Last-Translator: Daniel Stenlöv (XCLUDE) \n" +"PO-Revision-Date: 2010-09-29 08:22+0000\n" +"Last-Translator: OpenERP Administrators \n" "Language-Team: <>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-09-29 04:46+0000\n" +"X-Launchpad-Export-Date: 2010-09-30 04:37+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. module: base diff --git a/bin/addons/base/ir/ir_actions.py b/bin/addons/base/ir/ir_actions.py index 4b9d59743cb..27704687f93 100644 --- a/bin/addons/base/ir/ir_actions.py +++ b/bin/addons/base/ir/ir_actions.py @@ -28,8 +28,9 @@ from tools.translate import _ import netsvc import re import copy -import sys +import os from xml import dom +from report.report_sxw import report_sxw, report_rml class actions(osv.osv): _name = 'ir.actions.actions' @@ -89,6 +90,27 @@ class report_xml(osv.osv): res[report.id] = False return res + def register_all(self, cr): + """Report registration handler that may be overridden by subclasses to + add their own kinds of report services. + Loads all reports with no manual loaders (auto==True) and + registers the appropriate services to implement them. + """ + opj = os.path.join + cr.execute("SELECT * FROM ir_act_report_xml WHERE auto=%s ORDER BY id", (True,)) + result = cr.dictfetchall() + svcs = netsvc.Service._services + for r in result: + if svcs.has_key('report.'+r['report_name']): + continue + if r['report_rml'] or r['report_rml_content_data']: + report_sxw('report.'+r['report_name'], r['model'], + opj('addons',r['report_rml'] or '/'), header=r['header']) + if r['report_xsl']: + report_rml('report.'+r['report_name'], r['model'], + opj('addons',r['report_xml']), + r['report_xsl'] and opj('addons',r['report_xsl'])) + _name = 'ir.actions.report.xml' _table = 'ir_act_report_xml' _sequence = 'ir_actions_id_seq' @@ -98,7 +120,7 @@ class report_xml(osv.osv): 'type': fields.char('Action Type', size=32, required=True), 'report_name': fields.char('Service Name', size=64, required=True), 'usage': fields.char('Action Usage', size=32), - 'report_type': fields.selection([ ('pdf','pdf'), ('html','html'), ('raw','raw'), ('sxw','sxw'), ('txt','txt'), ('odt','odt'), ('html2html','HTML from HTML'), ('mako2html','HTML from Mako'), ], string='Report Type', required=True), + 'report_type': fields.char('Report Type', size=32, required=True, help="Report Type, e.g. pdf, html, raw, sxw, odt, html2html, mako2html, ..."), 'groups_id': fields.many2many('res.groups', 'res_groups_report_rel', 'uid', 'gid', 'Groups'), 'multi': fields.boolean('On multiple doc.', help="If set to true, the action will not be displayed on the right toolbar of a form view."), 'attachment': fields.char('Save As Attachment Prefix', size=128, help='This is the filename of the attachment used to store the printing result. Keep empty to not save the printed reports. You can use a python expression with the object and time variables.'), diff --git a/bin/addons/base/ir/ir_rule.py b/bin/addons/base/ir/ir_rule.py index 16c2c8275d9..799a411c15f 100644 --- a/bin/addons/base/ir/ir_rule.py +++ b/bin/addons/base/ir/ir_rule.py @@ -137,7 +137,8 @@ class ir_rule(osv.osv): def domain_get(self, cr, uid, model_name, mode='read', context={}): dom = self._compute_domain(cr, uid, model_name, mode=mode) if dom: - return self.pool.get(model_name)._where_calc(cr, uid, dom, active_test=False) + query = self.pool.get(model_name)._where_calc(cr, uid, dom, active_test=False) + return query.where_clause, query.where_clause_params, query.tables return [], [], ['"'+self.pool.get(model_name)._table+'"'] def unlink(self, cr, uid, ids, context=None): diff --git a/bin/addons/base/module/module.py b/bin/addons/base/module/module.py index ad10e4e8e5f..b446f39f5b5 100644 --- a/bin/addons/base/module/module.py +++ b/bin/addons/base/module/module.py @@ -19,27 +19,24 @@ # along with this program. If not, see . # ############################################################################## - -import tarfile -import re -import urllib -import os import imp import logging -import tools -from osv import fields, osv, orm -import zipfile -import release +import os +import re +import urllib import zipimport -import wizard import addons -import pooler import netsvc +import pooler +import release +import tools from tools.parse_version import parse_version from tools.translate import _ +from osv import fields, osv, orm + class module_category(osv.osv): _name = "ir.module.category" _description = "Module Category" @@ -80,8 +77,9 @@ class module(osv.osv): info = addons.load_information_from_description_file(name) if 'version' in info: info['version'] = release.major_version + '.' + info['version'] - except: - pass + except Exception: + self.__logger.debug('Error when trying to fetch informations for ' + 'module %s', name, exc_info=True) return info def _get_latest_version(self, cr, uid, ids, field_name=None, arg=None, context={}): @@ -116,17 +114,21 @@ class module(osv.osv): 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' - except: - pass + except Exception: + self.__logger.debug( + 'Unknown error while browsing ir.ui.view[%s]', + data_id.res_id, exc_info=True) 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': try: m = menu_obj.browse(cr,uid,data_id.res_id) res[mnames[data_id.module]]['menus_by_module'] += m.complete_name + '\n' - except: - pass - except KeyError, e: + except Exception: + self.__logger.debug( + 'Unknown error while browsing ir.ui.menu[%s]', + data_id.res_id, exc_info=True) + except KeyError: pass return res @@ -393,7 +395,9 @@ class module(osv.osv): fp = file(fname, 'wb') fp.write(zipfile) fp.close() - except Exception, e: + except Exception: + self.__logger.exception('Error when trying to create module ' + 'file %s', fname) raise orm.except_orm(_('Error'), _('Can not create the module file:\n %s') % (fname,)) terp = self.get_module_info(mod.name) self.write(cr, uid, mod.id, self.get_values_from_terp(terp)) diff --git a/bin/addons/base/res/partner/partner.py b/bin/addons/base/res/partner/partner.py index 466a8de0786..05630dea85b 100644 --- a/bin/addons/base/res/partner/partner.py +++ b/bin/addons/base/res/partner/partner.py @@ -392,7 +392,7 @@ class res_partner_bank(osv.osv): _columns = { 'name': fields.char('Description', size=128), 'acc_number': fields.char('Account Number', size=64, required=False), - 'bank': fields.many2one('res.bank', 'Bank'), + 'bank': fields.many2one('res.bank', 'Bank', required=True), 'owner_name': fields.char('Account Owner', size=64), 'street': fields.char('Street', size=128), 'zip': fields.char('Zip', change_default=True, size=24), diff --git a/bin/addons/base/res/res_user.py b/bin/addons/base/res/res_user.py index 3b25343d724..e310f5716b8 100644 --- a/bin/addons/base/res/res_user.py +++ b/bin/addons/base/res/res_user.py @@ -242,8 +242,8 @@ class users(osv.osv): 'signature': fields.text('Signature', size=64), 'address_id': fields.many2one('res.partner.address', 'Company Address'), 'active': fields.boolean('Active'), - 'action_id': fields.many2one('ir.actions.actions', 'Home Action'), - 'menu_id': fields.many2one('ir.actions.actions', 'Menu Action'), + 'action_id': fields.many2one('ir.actions.actions', 'Home Action', help="If specified, this action will be opened at logon for this user, in addition to the standard menu."), + 'menu_id': fields.many2one('ir.actions.actions', 'Menu Action', help="If specified, the action will replace the standard menu for this user."), 'groups_id': fields.many2many('res.groups', 'res_groups_users_rel', 'uid', 'gid', 'Groups'), 'roles_id': fields.many2many('res.roles', 'res_roles_users_rel', 'uid', 'rid', 'Roles'), @@ -288,15 +288,6 @@ class users(osv.osv): result = override_password(result) else: result = map(override_password, result) - - if isinstance(result, list): - for rec in result: - if not rec.get('action_id',True): - rec['action_id'] = (self._get_menu(cr, uid),'Menu') - else: - if not result.get('action_id',True): - result['action_id'] = (self._get_menu(cr, uid),'Menu') - return result @@ -327,10 +318,6 @@ class users(osv.osv): self.__admin_ids[cr.dbname] = ir_model_data_obj.read(cr, 1, [mdid], ['res_id'])[0]['res_id'] return self.__admin_ids[cr.dbname] - def _get_action(self,cr, uid, context={}): - ids = self.pool.get('ir.ui.menu').search(cr, uid, [('usage','=','menu')]) - return ids and ids[0] or False - def _get_company(self,cr, uid, context=None, uid2=False): if not uid2: uid2 = uid @@ -344,12 +331,12 @@ class users(osv.osv): return [c] return False - def _get_menu(self,cr, uid, context={}): - ids = self.pool.get('ir.actions.act_window').search(cr, uid, [('usage','=','menu')]) + def _get_menu(self,cr, uid, context=None): + ids = self.pool.get('ir.actions.act_window').search(cr, uid, [('usage','=','menu')], context=context) return ids and ids[0] or False - def _get_group(self,cr, uid, context={}): - ids = self.pool.get('res.groups').search(cr, uid, [('name','=','Employee')]) + def _get_group(self,cr, uid, context=None): + ids = self.pool.get('res.groups').search(cr, uid, [('name','=','Employee')], context=context) return ids or False _defaults = { @@ -357,7 +344,6 @@ class users(osv.osv): 'context_lang': lambda *args: 'en_US', 'active' : lambda *a: True, 'menu_id': _get_menu, - 'action_id': _get_menu, 'company_id': _get_company, 'company_ids': _get_companies, 'groups_id': _get_group, diff --git a/bin/addons/base/security/ir.model.access.csv b/bin/addons/base/security/ir.model.access.csv index 9168d93078d..67c79c552c1 100644 --- a/bin/addons/base/security/ir.model.access.csv +++ b/bin/addons/base/security/ir.model.access.csv @@ -18,6 +18,7 @@ "access_ir_model_fields_all","ir_model_fields all","model_ir_model_fields",,1,0,0,0 "access_ir_module_category_group_user","ir_module_category group_user","model_ir_module_category","group_system",1,0,0,0 "access_ir_module_module_group_user","ir_module_module group_user","model_ir_module_module","group_system",1,1,1,1 +"access_ir_module_module_user","ir_module_module user","model_ir_module_module",,1,0,0,0 "access_ir_module_module_dependency_group_system","ir_module_module_dependency group_system","model_ir_module_module_dependency","group_system",1,1,1,1 "access_ir_property_group_user","ir_property group_user","model_ir_property",,1,0,0,0 "access_ir_property_group_user_manager","ir_property group_manager","model_ir_property","base.group_partner_manager",1,1,1,0 diff --git a/bin/osv/orm.py b/bin/osv/orm.py index 1024136093f..55d2a4e226e 100644 --- a/bin/osv/orm.py +++ b/bin/osv/orm.py @@ -54,6 +54,7 @@ from tools.config import config from tools.translate import _ import fields +from query import Query import tools from tools.safe_eval import safe_eval as eval @@ -914,7 +915,7 @@ class orm_template(object): nbrmax = max(nbrmax, max2) warning = warning + w2 reduce(lambda x, y: x and y, newrow) - row[field] = (reduce(lambda x, y: x or y, newrow.values()) and \ + row[field] = newrow and (reduce(lambda x, y: x or y, newrow.values()) and \ [(0, 0, newrow)]) or [] i = max2 while (position+i) < len(datas): @@ -929,7 +930,7 @@ class orm_template(object): (newrow, max2, w2, translate2, data_id2, data_res_id2) = process_liness( self, datas, prefix+[field], current_module, relation_obj._name, newfd, position+i) warning = warning + w2 - if reduce(lambda x, y: x or y, newrow.values()): + if newrow and reduce(lambda x, y: x or y, newrow.values()): row[field].append((0, 0, newrow)) i += max2 nbrmax = max(nbrmax, i) @@ -2203,35 +2204,15 @@ class orm(orm_template): if not fields: fields = self._columns.keys() - # compute the where, order by, limit and offset clauses - (where_clause, where_clause_params, tables) = self._where_calc(cr, uid, domain, context=context) - - # apply direct ir.rules from current model - self._apply_ir_rules(cr, uid, where_clause, where_clause_params, tables, 'read', context=context) - - # then apply the ir.rules from the parents (through _inherits), adding the appropriate JOINs if needed - for inherited_model in self._inherits: - previous_tables = list(tables) - if self._apply_ir_rules(cr, uid, where_clause, where_clause_params, tables, 'read', model_name=inherited_model, context=context): - # if some rules were applied, need to add the missing JOIN for them to make sense, passing the previous - # list of table in case the inherited table was not in the list before (as that means the corresponding - # JOIN(s) was(were) not present) - self._inherits_join_add(inherited_model, previous_tables, where_clause) - tables = list(set(tables).union(set(previous_tables))) + query = self._where_calc(cr, uid, domain, context=context) + self._apply_ir_rules(cr, uid, query, 'read', context=context) # Take care of adding join(s) if groupby is an '_inherits'ed field groupby_list = groupby if groupby: if isinstance(groupby, list): groupby = groupby[0] - tables, where_clause, qfield = self._inherits_join_calc(groupby, tables, where_clause) - - if len(where_clause): - where_clause = ' where ' + ' and '.join(where_clause) - else: - where_clause = '' - limit_str = limit and ' limit %d' % limit or '' - offset_str = offset and ' offset %d' % offset or '' + self._inherits_join_calc(groupby, query) assert not groupby or groupby in fields, "Fields in 'groupby' must appear in the list of fields to read (perhaps it's missing in the list view?)" @@ -2263,8 +2244,13 @@ class orm(orm_template): flist += ',' flist += operator+'('+f+') as '+f - gb = groupby and (' group by '+groupby) or '' - cr.execute('select min(%s.id) as id,' % self._table + flist + ' from ' + ','.join(tables) + where_clause + gb + limit_str + offset_str, where_clause_params) + gb = groupby and (' GROUP BY '+groupby) or '' + + from_clause, where_clause, where_clause_params = query.get_sql() + where_clause = where_clause and ' WHERE ' + where_clause + limit_str = limit and ' limit %d' % limit or '' + offset_str = offset and ' offset %d' % offset or '' + cr.execute('SELECT min(%s.id) AS id,' % self._table + flist + ' FROM ' + from_clause + where_clause + gb + limit_str + offset_str, where_clause_params) alldata = {} groupby = group_by for r in cr.dictfetchall(): @@ -2303,43 +2289,37 @@ class orm(orm_template): del d['id'] return data - def _inherits_join_add(self, parent_model_name, tables, where_clause): + def _inherits_join_add(self, parent_model_name, query): """ - Add missing table SELECT and JOIN clause for reaching the parent table (no duplicates) + Add missing table SELECT and JOIN clause to ``query`` for reaching the parent table (no duplicates) :param parent_model_name: name of the parent model for which the clauses should be added - :param tables: list of table._table names enclosed in double quotes as returned - by _where_calc() - :param where_clause: current list of WHERE clause params + :param query: query object on which the JOIN should be added """ inherits_field = self._inherits[parent_model_name] parent_model = self.pool.get(parent_model_name) parent_table_name = parent_model._table quoted_parent_table_name = '"%s"' % parent_table_name - if quoted_parent_table_name not in tables: - tables.append(quoted_parent_table_name) - where_clause.append('("%s".%s = %s.id)' % (self._table, inherits_field, parent_table_name)) - return (tables, where_clause) + if quoted_parent_table_name not in query.tables: + query.tables.append(quoted_parent_table_name) + query.where_clause.append('("%s".%s = %s.id)' % (self._table, inherits_field, parent_table_name)) - def _inherits_join_calc(self, field, tables, where_clause): + def _inherits_join_calc(self, field, query): """ - Adds missing table select and join clause(s) for reaching + Adds missing table select and join clause(s) to ``query`` for reaching the field coming from an '_inherits' parent table (no duplicates). - :param tables: list of table._table names enclosed in double quotes as returned - by _where_calc() - :param where_clause: current list of WHERE clause params - :return: (tables, where_clause, qualified_field) where ``tables`` and ``where_clause`` are the updated - versions of the parameters, and ``qualified_field`` is the qualified name of ``field`` - in the form ``table.field``, to be referenced in queries. + :param field: name of inherited field to reach + :param query: query object on which the JOIN should be added + :return: qualified name of field, to be used in SELECT clause """ current_table = self while field in current_table._inherit_fields and not field in current_table._columns: parent_model_name = current_table._inherit_fields[field][0] parent_table = self.pool.get(parent_model_name) - self._inherits_join_add(parent_model_name, tables, where_clause) + self._inherits_join_add(parent_model_name, query) current_table = parent_table - return (tables, where_clause, '"%s".%s' % (current_table._table, field)) + return '"%s".%s' % (current_table._table, field) def _parent_store_compute(self, cr): if not self._parent_store: @@ -3899,84 +3879,87 @@ class orm(orm_template): raise NotImplementedError(_('This method does not exist anymore')) # TODO: ameliorer avec NULL - def _where_calc(self, cr, user, args, active_test=True, context=None): + def _where_calc(self, cr, user, domain, active_test=True, context=None): """Computes the WHERE clause needed to implement an OpenERP domain. - :param args: the domain to compute - :type args: list + :param domain: the domain to compute + :type domain: list :param active_test: whether the default filtering of records with ``active`` field set to ``False`` should be applied. - :return: tuple with 3 elements: (where_clause, where_clause_params, tables) where - ``where_clause`` contains a list of where clause elements (to be joined with 'AND'), - ``where_clause_params`` is a list of parameters to be passed to the db layer - for the where_clause expansion, and ``tables`` is the list of double-quoted - table names that need to be included in the FROM clause. - :rtype: tuple + :return: the query expressing the given domain as provided in domain + :rtype: osv.query.Query """ if not context: context = {} - args = args[:] + domain = domain[:] # if the object has a field named 'active', filter out all inactive # records unless they were explicitely asked for if 'active' in self._columns and (active_test and context.get('active_test', True)): - if args: + if domain: active_in_args = False - for a in args: + for a in domain: if a[0] == 'active': active_in_args = True if not active_in_args: - args.insert(0, ('active', '=', 1)) + domain.insert(0, ('active', '=', 1)) else: - args = [('active', '=', 1)] + domain = [('active', '=', 1)] - if args: + if domain: import expression - e = expression.expression(args) + e = expression.expression(domain) e.parse(cr, user, self, context) tables = e.get_tables() - qu1, qu2 = e.to_sql() - qu1 = qu1 and [qu1] or [] + where_clause, where_params = e.to_sql() + where_clause = where_clause and [where_clause] or [] else: - qu1, qu2, tables = [], [], ['"%s"' % self._table] + where_clause, where_params, tables = [], [], ['"%s"' % self._table] - return (qu1, qu2, tables) + return Query(tables, where_clause, where_params) def _check_qorder(self, word): if not regex_order.match(word): raise except_orm(_('AccessError'), _('Invalid "order" specified. A valid "order" specification is a comma-separated list of valid field names (optionally followed by asc/desc for the direction)')) return True - def _apply_ir_rules(self, cr, uid, where_clause, where_clause_params, tables, mode='read', model_name=None, context=None): - """Add what's missing in ``where_clause``, ``where_params``, ``tables`` to implement - all appropriate ir.rules (on the current object but also from it's _inherits parents) + def _apply_ir_rules(self, cr, uid, query, mode='read', context=None): + """Add what's missing in ``query`` to implement all appropriate ir.rules + (using the ``model_name``'s rules or the current model's rules if ``model_name`` is None) - :param where_clause: list with current elements of the WHERE clause (strings) - :param where_clause_params: list with parameters for ``where_clause`` - :param tables: list with double-quoted names of the tables that are joined - in ``where_clause`` - :param model_name: optional name of the model whose ir.rules should be applied (default:``self._name``) - This could be useful for inheritance for example, but there is no provision to include - the appropriate JOIN for linking the current model to the one referenced in model_name. - :return: True if additional clauses where applied. + :param query: the current query object """ - added_clause, added_params, added_tables = self.pool.get('ir.rule').domain_get(cr, uid, model_name or self._name, mode, context=context) - if added_clause: - where_clause += added_clause - where_clause_params += added_params - for table in added_tables: - if table not in tables: - tables.append(table) - return True - return False + def apply_rule(added_clause, added_params, added_tables): + if added_clause: + query.where_clause += added_clause + query.where_clause_params += added_params + for table in added_tables: + if table not in query.tables: + query.tables.append(table) + return True + return False - def _generate_m2o_order_by(self, order_field, tables, where_clause): + # apply main rules on the object + rule_obj = self.pool.get('ir.rule') + apply_rule(*rule_obj.domain_get(cr, uid, self._name, mode, context=context)) + + # apply ir.rules from the parents (through _inherits), adding the appropriate JOINs if needed + for inherited_model in self._inherits: + if apply_rule(*rule_obj.domain_get(cr, uid, inherited_model, mode, context=context)): + # if some rules were applied, need to add the missing JOIN for them to make sense, passing the previous + # list of table in case the inherited table was not in the list before (as that means the corresponding + # JOIN(s) was(were) not present) + self._inherits_join_add(inherited_model, query) + + def _generate_m2o_order_by(self, order_field, query): """ - Add possibly missing JOIN and generate the ORDER BY clause for m2o fields, + Add possibly missing JOIN to ``query`` and generate the ORDER BY clause for m2o fields, either native m2o fields or function/related fields that are stored, including intermediate JOINs for inheritance if required. + + :return: the qualified field name to use in an ORDER BY clause to sort by ``order_field`` """ if order_field not in self._columns and order_field in self._inherit_fields: # also add missing joins for reaching the table containing the m2o field - tables, where_clause, qualified_field = self._inherits_join_calc(order_field, tables, where_clause) + qualified_field = self._inherits_join_calc(order_field, query) order_field_column = self._inherit_fields[order_field][2] else: qualified_field = '"%s"."%s"' % (self._table, order_field) @@ -3995,16 +3978,14 @@ class orm(orm_template): # extract the first field name, to be able to qualify it and add desc/asc m2o_order = m2o_order.split(",",1)[0].strip().split(" ",1)[0] - # the perhaps missing join: - quoted_model_table = '"%s"' % dest_model._table - if quoted_model_table not in tables: - tables.append(quoted_model_table) - where_clause.append('%s = %s.id' % (qualified_field, quoted_model_table)) - - return ('%s.%s' % (quoted_model_table, m2o_order), tables, where_clause) + # Join the dest m2o table if it's not joined yet. We use [LEFT] OUTER join here + # as we don't want to exclude results that have NULL values for the m2o + src_table, src_field = qualified_field.replace('"','').split('.', 1) + query.join((src_table, dest_model._table, src_field, 'id'), outer=True) + return '"%s"."%s"' % (dest_model._table, m2o_order) - def _generate_order_by(self, order_spec, tables, where_clause): + def _generate_order_by(self, order_spec, query): """ Attempt to consruct an appropriate ORDER BY clause based on order_spec, which must be a comma-separated list of valid field names, optionally followed by an ASC or DESC direction. @@ -4024,16 +4005,16 @@ class orm(orm_template): if order_column._classic_read: order_by_clause = '"%s"."%s"' % (self._table, order_field) elif order_column._type == 'many2one': - order_by_clause, tables, where_clause = self._generate_m2o_order_by(order_field, tables, where_clause) + order_by_clause = self._generate_m2o_order_by(order_field, query) else: continue # ignore non-readable or "non-joignable" fields elif order_field in self._inherit_fields: parent_obj = self.pool.get(self._inherit_fields[order_field][0]) order_column = parent_obj._columns[order_field] if order_column._classic_read: - tables, where_clause, order_by_clause = self._inherits_join_calc(order_field, tables, where_clause) + order_by_clause = self._inherits_join_calc(order_field, query) elif order_column._type == 'many2one': - order_by_clause, tables, where_clause = self._generate_m2o_order_by(order_field, tables, where_clause) + order_by_clause = self._generate_m2o_order_by(order_field, query) else: continue # ignore non-readable or "non-joignable" fields order_by_elements.append("%s %s" % (order_by_clause, order_direction)) @@ -4054,34 +4035,21 @@ class orm(orm_template): if context is None: context = {} self.pool.get('ir.model.access').check(cr, access_rights_uid or user, self._name, 'read', context=context) - # compute the where, order by, limit and offset clauses - (where_clause, where_clause_params, tables) = self._where_calc(cr, user, args, context=context) - # apply direct ir.rules from current model - self._apply_ir_rules(cr, user, where_clause, where_clause_params, tables, 'read', context=context) + query = self._where_calc(cr, user, args, context=context) + self._apply_ir_rules(cr, user, query, 'read', context=context) + order_by = self._generate_order_by(order, query) + from_clause, where_clause, where_clause_params = query.get_sql() - # then apply the ir.rules from the parents (through _inherits), adding the appropriate JOINs if needed - for inherited_model in self._inherits: - previous_tables = list(tables) - if self._apply_ir_rules(cr, user, where_clause, where_clause_params, tables, 'read', model_name=inherited_model, context=context): - # if some rules were applied, need to add the missing JOIN for them to make sense, passing the previous - # list of table in case the inherited table was not in the list before (as that means the corresponding - # JOIN(s) was(were) not present) - self._inherits_join_add(inherited_model, previous_tables, where_clause) - tables = list(set(tables).union(set(previous_tables))) - - where = where_clause - order_by = self._generate_order_by(order, tables, where_clause) limit_str = limit and ' limit %d' % limit or '' offset_str = offset and ' offset %d' % offset or '' - where_str = where and (" WHERE %s" % " AND ".join(where)) or '' + where_str = where_clause and (" WHERE %s" % where_clause) or '' if count: - cr.execute('select count(%s.id) from ' % self._table + - ','.join(tables) + where_str + limit_str + offset_str, where_clause_params) + cr.execute('SELECT count("%s".id) FROM ' % self._table + from_clause + where_str + limit_str + offset_str, where_clause_params) res = cr.fetchall() return res[0][0] - cr.execute('select %s.id from ' % self._table + ','.join(tables) + where_str + order_by + limit_str+offset_str, where_clause_params) + cr.execute('SELECT "%s".id FROM ' % self._table + from_clause + where_str + order_by + limit_str + offset_str, where_clause_params) res = cr.fetchall() return [x[0] for x in res] diff --git a/bin/osv/query.py b/bin/osv/query.py new file mode 100644 index 00000000000..ab4f4143634 --- /dev/null +++ b/bin/osv/query.py @@ -0,0 +1,122 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010 OpenERP S.A. http://www.openerp.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + + +def _quote(to_quote): + if '"' not in to_quote: + return '"%s"' % to_quote + return to_quote + + +class Query(object): + """ + Dumb implementation of a Query object, using 3 string lists so far + for backwards compatibility with the (table, where_clause, where_params) previously used. + + TODO: To be improved after v6.0 to rewrite part of the ORM and add support for: + - auto-generated multiple table aliases + - multiple joins to the same table with different conditions + - dynamic right-hand-side values in domains (e.g. a.name = a.description) + - etc. + """ + + def __init__(self, tables=None, where_clause=None, where_clause_params=None, joins=None): + + # holds the list of tables joined using default JOIN. + # the table names are stored double-quoted (backwards compatibility) + self.tables = tables or [] + + # holds the list of WHERE clause elements, to be joined with + # 'AND' when generating the final query + self.where_clause = where_clause or [] + + # holds the parameters for the formatting of `where_clause`, to be + # passed to psycopg's execute method. + self.where_clause_params = where_clause_params or [] + + # holds table joins done explicitly, supporting outer joins. The JOIN + # condition should not be in `where_clause`. The dict is used as follows: + # self.joins = { + # 'table_a': [ + # ('table_b', 'table_a_col1', 'table_b_col', 'LEFT JOIN'), + # ('table_c', 'table_a_col2', 'table_c_col', 'LEFT JOIN'), + # ('table_d', 'table_a_col3', 'table_d_col', 'JOIN'), + # ] + # } + # which should lead to the following SQL: + # SELECT ... FROM "table_a" LEFT JOIN "table_b" ON ("table_a"."table_a_col1" = "table_b"."table_b_col") + # LEFT JOIN "table_c" ON ("table_a"."table_a_col2" = "table_c"."table_c_col") + self.joins = joins or {} + + def join(self, connection, outer=False): + """Adds the JOIN specified in ``connection``. + + :param connection: a tuple ``(lhs, table, lhs_col, col)``. + The join corresponds to the SQL equivalent of:: + + ``(lhs.lhs_col = table.col)`` + + :param outer: True if a LEFT OUTER JOIN should be used, if possible + (no promotion to OUTER JOIN is supported in case the JOIN + was already present in the query, as for the moment + implicit INNER JOINs are only connected from NON-NULL + columns so it would not be correct (e.g. for + ``_inherits`` or when a domain criterion explicitly + adds filtering) + """ + (lhs, table, lhs_col, col) = connection + lhs = _quote(lhs) + table = _quote(table) + assert lhs in self.tables, "Left-hand-side table must already be part of the query!" + if table in self.tables: + # already joined, must ignore (promotion to outer and multiple joins not supported yet) + pass + else: + # add JOIN + self.tables.append(table) + self.joins.setdefault(lhs, []).append((table, lhs_col, col, outer and 'LEFT JOIN' or 'JOIN')) + return self + + def get_sql(self): + """Returns (query_from, query_where, query_params)""" + query_from = '' + tables_to_process = list(self.tables) + + def add_joins_for_table(table, query_from): + for (dest_table, lhs_col, col, join) in self.joins.get(table,[]): + tables_to_process.remove(dest_table) + query_from += ' %s %s ON (%s."%s" = %s."%s")' % \ + (join, dest_table, table, lhs_col, dest_table, col) + query_from = add_joins_for_table(dest_table, query_from) + return query_from + + for table in tables_to_process: + query_from += table + if table in self.joins: + query_from = add_joins_for_table(table, query_from) + query_from += ',' + query_from = query_from[:-1] # drop last comma + return (query_from, " AND ".join(self.where_clause), self.where_clause_params) + + def __str__(self): + return '' % self.get_sql() + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file diff --git a/bin/pooler.py b/bin/pooler.py index 7a572e71e0d..684bc6210d4 100644 --- a/bin/pooler.py +++ b/bin/pooler.py @@ -44,12 +44,11 @@ def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False, cr = db.cursor() try: pool.init_set(cr, False) + pool.get('ir.actions.report.xml').register_all(cr) cr.commit() finally: cr.close() - import report - report.interface.register_all(db) if pooljobs: pool.get('ir.cron').restart(db.dbname) return db, pool diff --git a/bin/report/interface.py b/bin/report/interface.py index 6aecfe66133..0ee9f432883 100644 --- a/bin/report/interface.py +++ b/bin/report/interface.py @@ -219,25 +219,5 @@ class report_rml(report_int): obj.render() return obj.get() -from report_sxw import report_sxw - -def register_all(db): - opj = os.path.join - cr = db.cursor() - cr.execute("SELECT * FROM ir_act_report_xml WHERE auto=%s ORDER BY id", (True,)) - result = cr.dictfetchall() - cr.close() - svcs = netsvc.Service._services - for r in result: - if svcs.has_key('report.'+r['report_name']): - continue - if r['report_rml'] or r['report_rml_content_data']: - report_sxw('report.'+r['report_name'], r['model'], - opj('addons',r['report_rml'] or '/'), header=r['header']) - if r['report_xsl']: - report_rml('report.'+r['report_name'], r['model'], - opj('addons',r['report_xml']), - r['report_xsl'] and opj('addons',r['report_xsl'])) - # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/bin/report/printscreen/ps_list.py b/bin/report/printscreen/ps_list.py index 142cb41d447..aa95eedeff8 100644 --- a/bin/report/printscreen/ps_list.py +++ b/bin/report/printscreen/ps_list.py @@ -98,6 +98,7 @@ class report_printscreen_list(report_int): continue child_ids = model.search(cr, uid, inner_domain) res = model.read(cr, uid, child_ids, result['fields'].keys(), context) + res.sort(lambda x,y: cmp(ids.index(x['id']), ids.index(y['id']))) rows.extend(res) dom = [('id','in',ids)] if self.groupby_no_leaf and len(ids) and not ids[0]: diff --git a/bin/report/report_sxw.py b/bin/report/report_sxw.py index aeb04ed21b6..2c505e98622 100644 --- a/bin/report/report_sxw.py +++ b/bin/report/report_sxw.py @@ -39,6 +39,7 @@ import tools import warnings import zipfile import common +from osv.fields import float as float_class, function as function_class DT_FORMAT = '%Y-%m-%d' DHM_FORMAT = '%Y-%m-%d %H:%M:%S' @@ -240,9 +241,36 @@ class rml_parse(object): self.default_lang[lang] = self.lang_dict.copy() return True - def formatLang(self, value, digits=2, date=False,date_time=False, grouping=True, monetary=False): + def digits_fmt(self, obj=None, f=None, dp=None): + digits = self.get_digits(obj, f, dp) + return "%%.%df" % (digits, ) + + def get_digits(self, obj=None, f=None, dp=None): + d = DEFAULT_DIGITS = 2 + if dp: + decimal_precision_obj = self.pool.get('decimal.precision') + ids = decimal_precision_obj.search(self.cr, self.uid, [('name', '=', dp)]) + if ids: + d = decimal_precision_obj.browse(self.cr, self.uid, ids)[0].digits + elif obj and f: + res_digits = getattr(obj._columns[f], 'digits', lambda x: ((16,DEFAULT_DIGITS))) + if isinstance(res_digits, tuple): + d = res_digits[1] + else: + d = res_digits(self.cr)[1] + elif (hasattr(obj, '_field') and\ + isinstance(obj._field, (float_class, function_class)) and\ + obj._field.digits): + d = obj._field.digits[1] or DEFAULT_DIGITS + return d + + def formatLang(self, value, digits=None, date=False, date_time=False, grouping=True, monetary=False): + if digits is None: + digits = self.get_digits(value) + if isinstance(value, (str, unicode)) and not value: return '' + if not self.lang_dict_called: self._get_lang_dict() self.lang_dict_called = True @@ -250,6 +278,7 @@ class rml_parse(object): if date or date_time: if not str(value): return '' + date_format = self.lang_dict['date_format'] parse_format = DT_FORMAT if date_time: @@ -258,9 +287,11 @@ class rml_parse(object): parse_format = DHM_FORMAT if not isinstance(value, time.struct_time): return time.strftime(date_format, time.strptime(value, parse_format)) + else: date = datetime(*value.timetuple()[:6]) return date.strftime(date_format) + return self.lang_dict['lang_obj'].format('%.' + str(digits) + 'f', value, grouping=grouping, monetary=monetary) def repeatIn(self, lst, name,nodes_parent=False): @@ -305,6 +336,8 @@ class rml_parse(object): def set_context(self, objects, data, ids, report_type = None): self.localcontext['data'] = data self.localcontext['objects'] = objects + self.localcontext['digits_fmt'] = self.digits_fmt + self.localcontext['get_digits'] = self.get_digits self.datas = data self.ids = ids self.objects = objects diff --git a/bin/run_tests.py b/bin/run_tests.py new file mode 100644 index 00000000000..7c0ee2a2431 --- /dev/null +++ b/bin/run_tests.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010 OpenERP S.A. http://www.openerp.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import unittest + +import test + +if __name__ == '__main__': + unittest.TextTestRunner(verbosity=2).run(unittest.defaultTestLoader.loadTestsFromModule(test)) diff --git a/bin/test/__init__.py b/bin/test/__init__.py new file mode 100644 index 00000000000..86872258874 --- /dev/null +++ b/bin/test/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010 OpenERP S.A. http://www.openerp.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from test_osv import * diff --git a/bin/test/test_osv.py b/bin/test/test_osv.py new file mode 100644 index 00000000000..191ca792a2f --- /dev/null +++ b/bin/test/test_osv.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010 OpenERP S.A. http://www.openerp.com +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import unittest +from osv.query import Query + +class QueryTestCase(unittest.TestCase): + + def test_basic_query(self): + query = Query() + query.tables.extend(['"product_product"','"product_template"']) + query.where_clause.append("product_product.template_id = product_template.id") + query.join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join + query.join(("product_product", "res_user", "user_id", "id"), outer=True) # outer join + self.assertEquals(query.get_sql()[0].strip(), + """"product_product" LEFT JOIN "res_user" ON ("product_product"."user_id" = "res_user"."id"),"product_template" JOIN "product_category" ON ("product_template"."categ_id" = "product_category"."id") """.strip()) + self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip()) + + def test_query_chained_explicit_joins(self): + query = Query() + query.tables.extend(['"product_product"','"product_template"']) + query.where_clause.append("product_product.template_id = product_template.id") + query.join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join + query.join(("product_category", "res_user", "user_id", "id"), outer=True) # CHAINED outer join + self.assertEquals(query.get_sql()[0].strip(), + """"product_product","product_template" JOIN "product_category" ON ("product_template"."categ_id" = "product_category"."id") LEFT JOIN "res_user" ON ("product_category"."user_id" = "res_user"."id")""".strip()) + self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id""".strip()) + + def test_mixed_query_chained_explicit_implicit_joins(self): + query = Query() + query.tables.extend(['"product_product"','"product_template"']) + query.where_clause.append("product_product.template_id = product_template.id") + query.join(("product_template", "product_category", "categ_id", "id"), outer=False) # add normal join + query.join(("product_category", "res_user", "user_id", "id"), outer=True) # CHAINED outer join + query.tables.append('"account.account"') + query.where_clause.append("product_category.expense_account_id = account_account.id") # additional implicit join + self.assertEquals(query.get_sql()[0].strip(), + """"product_product","product_template" JOIN "product_category" ON ("product_template"."categ_id" = "product_category"."id") LEFT JOIN "res_user" ON ("product_category"."user_id" = "res_user"."id"),"account.account" """.strip()) + self.assertEquals(query.get_sql()[1].strip(), """product_product.template_id = product_template.id AND product_category.expense_account_id = account_account.id""".strip()) + + + def test_raise_missing_lhs(self): + query = Query() + query.tables.append('"product_product"') + self.assertRaises(AssertionError, query.join, ("product_template", "product_category", "categ_id", "id"), outer=False) + diff --git a/debian/po/ko.po b/debian/po/ko.po index 5218c4ce019..155d2266c9a 100644 --- a/debian/po/ko.po +++ b/debian/po/ko.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: openobject-server\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2009-08-24 22:41+0300\n" -"PO-Revision-Date: 2010-09-15 02:58+0000\n" -"Last-Translator: barge \n" +"PO-Revision-Date: 2010-09-29 08:30+0000\n" +"Last-Translator: OpenERP Administrators \n" "Language-Team: Korean \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-09-29 04:47+0000\n" +"X-Launchpad-Export-Date: 2010-09-30 04:38+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. Type: string diff --git a/debian/po/mn.po b/debian/po/mn.po index 6d686d6e110..47a4d6a3e6c 100644 --- a/debian/po/mn.po +++ b/debian/po/mn.po @@ -8,13 +8,13 @@ msgstr "" "Project-Id-Version: openobject-server\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2009-08-24 22:41+0300\n" -"PO-Revision-Date: 2010-09-10 03:13+0000\n" +"PO-Revision-Date: 2010-09-29 08:02+0000\n" "Last-Translator: ub121 \n" "Language-Team: Mongolian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2010-09-29 04:47+0000\n" +"X-Launchpad-Export-Date: 2010-09-30 04:38+0000\n" "X-Generator: Launchpad (build Unknown)\n" #. Type: string