[MERGE] from trunk
bzr revid: rco@openerp.com-20120418100656-l0se3yr770qkg2yd
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
Need action mechanism
|
||||
=====================
|
||||
|
||||
ir.needaction mixin class
|
||||
++++++++++++++++++++++++++
|
||||
|
||||
This revision adds a mixin class for objects using the need action feature.
|
||||
|
||||
Need action feature can be used by objects willing to be able to signal that an action is required on a particular record. If in the business logic an action must be performed by somebody, for instance validation by a manager, this mechanism allows to set a list of users asked to perform an action.
|
||||
|
||||
This class wraps a class (ir.needaction_users) that behaves like a many2many field. However, no field is added to the model inheriting from base.needaction. The mixin class manages the low-level considerations of updating relationships. Every change made on the record calls a method that updates the relationships.
|
||||
|
||||
Objects using the need_action feature should override the ``get_needaction_user_ids`` method. This methods returns a dictionary whose keys are record ids, and values a list of user ids, like in a many2many relationship. Therefore by defining only one method, you can specify if an action is required by defining the users that have to do it, in every possible situation.
|
||||
|
||||
This class also offers several global services,:
|
||||
- ``needaction_get_record_ids``: for the current model and uid, get all record ids that ask this user to perform an action. This mechanism is used for instance to display the number of pending actions in menus, such as Leads (12)
|
||||
- ``needaction_get_action_count``: as ``needaction_get_record_ids`` but returns only the number of action, not the ids (performs a search with count=True)
|
||||
- ``needaction_get_user_record_references``: for a given uid, get all the records that ask this user to perform an action. Records are given as references, a list of tuples (model_name, record_id)
|
||||
|
||||
Menu modification
|
||||
+++++++++++++++++
|
||||
|
||||
This revision adds three functional fields to ``ir.ui.menu`` model :
|
||||
- ``uses_needaction``: boolean field. If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false.
|
||||
- ``needaction_uid_ctr``: integer field. If the target model uses the need action mechanism, this field gives the number of actions the current user has to perform.
|
||||
- ``needaction_record_ids``: many2many field. If the target model uses the need action mechanism, this field holds the ids of the record requesting the user to perform an action.
|
||||
|
||||
Those fields are functional, because they depend on the user and must therefore be computed at every refresh, each time menus are displayed. The use of the need action mechanism is done by taking into account the action domain in order to display accurate results. When computing the value of the functional fields, the ids of records asking the user to perform an action is concatenated to the action domain. A counting search is then performed on the model, giving back the number of action the users has to perform, limited to the domain of the action.
|
||||
|
||||
Addon implementation example
|
||||
++++++++++++++++++++++++++++
|
||||
|
||||
In your ``foo`` module, you want to specify that when it is in state ``confirmed``, it has to be validated by a manager, given by the field ``manager_id``. After making ``foo`` inheriting from ``base.needaction``, you override the ``get_needaction_user_ids`` method:
|
||||
|
||||
::
|
||||
|
||||
[...]
|
||||
_inherit = [base.needaction]
|
||||
[...]
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
result = dict.fromkeys(ids)
|
||||
for foo_obj in self.browse(cr, uid, ids, context=context):
|
||||
# set the list void by default
|
||||
result[foo_obj.id] = []
|
||||
# if foo_obj is confirmed: manager is required to perform an action
|
||||
if foo_obj.state == 'confirmed':
|
||||
result[foo_obj.id] = [foo_obj.manager_id]
|
||||
return result
|
||||
@@ -14,3 +14,4 @@ New feature merges
|
||||
:maxdepth: 1
|
||||
|
||||
api/user_img_specs
|
||||
api/need_action_specs
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
'module/wizard/base_update_translations_view.xml',
|
||||
'res/res_request_view.xml',
|
||||
'res/res_lang_view.xml',
|
||||
'res/res_log_view.xml',
|
||||
'res/res_partner_report.xml',
|
||||
'res/res_partner_view.xml',
|
||||
'res/res_partner_shortcut_data.xml',
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
<field name="name" readonly="1"/>
|
||||
<newline/>
|
||||
<group colspan="2" col="3">
|
||||
<group col="2" colspan="2">
|
||||
<group col="2" colspan="2" name="preferences">
|
||||
<separator string="Preferences" colspan="2"/>
|
||||
<field name="view" readonly="0"/>
|
||||
<field name="context_lang" readonly="0"/>
|
||||
@@ -94,7 +94,7 @@
|
||||
</group>
|
||||
<group col="2" colspan="1">
|
||||
<separator string="Avatar" colspan="2"/>
|
||||
<field name="avatar" widget='image' nolabel="1" colspan="2" on_change="onchange_avatar(avatar)"/>
|
||||
<field name="avatar" widget='image' nolabel="1" colspan="2" on_change="onchange_avatar(avatar)" readonly="0"/>
|
||||
</group>
|
||||
</group>
|
||||
<group name="default_filters" colspan="2" col="2">
|
||||
@@ -128,11 +128,11 @@
|
||||
<!-- Second nested group to avoid misalignment with email prefs groups
|
||||
in simplified view -->
|
||||
<group colspan="7" col="7">
|
||||
<group col="2" colspan="1">
|
||||
<group col="2" colspan="1" name="avatar">
|
||||
<separator string="Avatar" colspan="2"/>
|
||||
<field name="avatar" widget='image' nolabel="1" colspan="2" on_change="onchange_avatar(avatar)"/>
|
||||
</group>
|
||||
<group col="3" colspan="2">
|
||||
<group col="3" colspan="2" name="preferences">
|
||||
<separator string="Preferences" colspan="3"/>
|
||||
<field name="context_lang"/>
|
||||
<field name="context_tz"/>
|
||||
|
||||
+155
-122
File diff suppressed because it is too large
Load Diff
+1145
-559
File diff suppressed because it is too large
Load Diff
@@ -7,14 +7,14 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.0\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2012-02-08 00:44+0000\n"
|
||||
"PO-Revision-Date: 2012-01-31 16:45+0000\n"
|
||||
"Last-Translator: Antony Lesuisse (OpenERP) <al@openerp.com>\n"
|
||||
"PO-Revision-Date: 2012-04-03 15:31+0000\n"
|
||||
"Last-Translator: tamnil <Unknown>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-03-10 04:50+0000\n"
|
||||
"X-Generator: Launchpad (build 14914)\n"
|
||||
"X-Launchpad-Export-Date: 2012-04-04 04:40+0000\n"
|
||||
"X-Generator: Launchpad (build 15055)\n"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
@@ -171,12 +171,12 @@ msgstr "Janela alvo"
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_sale_analytic_plans
|
||||
msgid "Sales Analytic Distribution"
|
||||
msgstr ""
|
||||
msgstr "Distribuição analítica de Vendas"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_web_process
|
||||
msgid "Process"
|
||||
msgstr ""
|
||||
msgstr "Processo"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_analytic_journal_billing_rate
|
||||
@@ -236,7 +236,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_mrp_subproduct
|
||||
msgid "MRP Subproducts"
|
||||
msgstr ""
|
||||
msgstr "Subprodutos MRP"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/module/module.py:390
|
||||
@@ -269,7 +269,7 @@ msgstr ""
|
||||
#: model:ir.module.category,name:base.module_category_sales_management
|
||||
#: model:ir.module.module,shortdesc:base.module_sale
|
||||
msgid "Sales Management"
|
||||
msgstr ""
|
||||
msgstr "Gestão de Vendas"
|
||||
|
||||
#. module: base
|
||||
#: view:res.partner:0
|
||||
@@ -375,7 +375,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: view:ir.module.module:0
|
||||
msgid "Extra"
|
||||
msgstr ""
|
||||
msgstr "Extra"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/orm.py:2526
|
||||
@@ -576,7 +576,7 @@ msgstr "Spanish (VE) / Español (VE)"
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_hr_timesheet_invoice
|
||||
msgid "Invoice on Timesheets"
|
||||
msgstr ""
|
||||
msgstr "Factura de Horários"
|
||||
|
||||
#. module: base
|
||||
#: view:base.module.upgrade:0
|
||||
@@ -887,7 +887,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: view:res.users:0
|
||||
msgid "Email Preferences"
|
||||
msgstr ""
|
||||
msgstr "Preferências de Email"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_audittrail
|
||||
@@ -1070,7 +1070,7 @@ msgstr "Tipo"
|
||||
#. module: base
|
||||
#: field:ir.mail_server,smtp_user:0
|
||||
msgid "Username"
|
||||
msgstr ""
|
||||
msgstr "Nome de utilizador"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/orm.py:398
|
||||
@@ -1243,7 +1243,7 @@ msgstr ""
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_web_tests
|
||||
msgid "Tests"
|
||||
msgstr ""
|
||||
msgstr "Testes"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.ui.view_sc,res_id:0
|
||||
@@ -1916,7 +1916,7 @@ msgstr ""
|
||||
#: code:addons/base/res/res_users.py:420
|
||||
#, python-format
|
||||
msgid "%s (copy)"
|
||||
msgstr ""
|
||||
msgstr "%s (cópia)"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.module.module,shortdesc:base.module_account_chart
|
||||
@@ -2494,7 +2494,7 @@ msgstr "Nome do atalho"
|
||||
#. module: base
|
||||
#: help:ir.actions.act_window,limit:0
|
||||
msgid "Default limit for the list view"
|
||||
msgstr "Limite por defeito para a vista de lista"
|
||||
msgstr "Limite por omissão para a vista de lista"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.pg
|
||||
@@ -10604,7 +10604,7 @@ msgstr "Exemplos"
|
||||
#: field:ir.default,value:0
|
||||
#: view:ir.values:0
|
||||
msgid "Default Value"
|
||||
msgstr "Valor por defeito"
|
||||
msgstr "Valor por Omissão"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_res_country_state
|
||||
@@ -11448,7 +11448,7 @@ msgstr ""
|
||||
#: selection:ir.values,key:0
|
||||
#: selection:res.partner.address,type:0
|
||||
msgid "Default"
|
||||
msgstr "Por defeito"
|
||||
msgstr "Por omissão"
|
||||
|
||||
#. module: base
|
||||
#: view:partner.wizard.ean.check:0
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
import ir_model
|
||||
import ir_sequence
|
||||
import ir_needaction
|
||||
import ir_ui_menu
|
||||
import ir_ui_view
|
||||
import ir_default
|
||||
|
||||
@@ -661,6 +661,30 @@
|
||||
|
||||
<menuitem action="ir_action_wizard" id="menu_ir_action_wizard" parent="base.next_id_6"/>
|
||||
|
||||
<!-- Needaction mechanism -->
|
||||
<record model="ir.ui.view" id="view_ir_needaction_users_tree">
|
||||
<field name="name">ir.needaction_users.tree</field>
|
||||
<field name="model">ir.needaction_users</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Subscription">
|
||||
<field name="user_id"/>
|
||||
<field name="res_model"/>
|
||||
<field name="res_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_view_needaction_users" model="ir.actions.act_window">
|
||||
<field name="name">Need action relationships</field>
|
||||
<field name="res_model">ir.needaction_users</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_needaction_users" name="Need actions" parent="base.menu_users" sequence="20" action="action_view_needaction_users" groups="base.group_no_one"/>
|
||||
|
||||
<!-- Companies -->
|
||||
<menuitem id="menu_res_company_global"
|
||||
parent="base.menu_administration"
|
||||
@@ -2047,8 +2071,8 @@
|
||||
<field name="search_view_id" ref="view_ir_mail_server_search"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_email" name="Email" parent="base.menu_config" sequence="22" groups="base.group_extended"/>
|
||||
<menuitem id="menu_mail_servers" parent="menu_email" action="action_ir_mail_server_list" sequence="15"/>
|
||||
<menuitem id="menu_email" name="Email" parent="base.menu_config" sequence="22"/>
|
||||
<menuitem id="menu_mail_servers" parent="menu_email" action="action_ir_mail_server_list" sequence="15" groups="base.group_no_one"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -717,8 +717,8 @@ class ir_model_data(osv.osv):
|
||||
model_obj = self.pool.get(model)
|
||||
if not context:
|
||||
context = {}
|
||||
# records created during module install should result in res.log entries that are already read!
|
||||
context = dict(context, res_log_read=True)
|
||||
# records created during module install should not display the messages of OpenChatter
|
||||
context = dict(context, install_mode=True)
|
||||
if xml_id and ('.' in xml_id):
|
||||
assert len(xml_id.split('.'))==2, _("'%s' contains too many dots. XML ids should not contain dots ! These are used to refer to other modules data, as in module.reference_id") % (xml_id)
|
||||
module, xml_id = xml_id.split('.')
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2009-Today OpenERP SA (<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 <http://www.gnu.org/licenses/>
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import openerp.pooler as pooler
|
||||
from operator import itemgetter
|
||||
from osv import osv, fields
|
||||
from tools.translate import _
|
||||
|
||||
class ir_needaction_users(osv.osv):
|
||||
'''
|
||||
ir_needaction_users holds data related to the needaction
|
||||
mechanism inside OpenERP. A needaction is characterized by:
|
||||
- res_model: model of the record requiring an action
|
||||
- res_id: ID of the record requiring an action
|
||||
- user_id: foreign key to the res.users table, to the user that
|
||||
has to perform the action
|
||||
'''
|
||||
|
||||
_name = 'ir.needaction_users'
|
||||
_description = 'Needaction relationship table'
|
||||
_rec_name = 'id'
|
||||
_order = 'id desc'
|
||||
_columns = {
|
||||
'res_model': fields.char('Related Document Model', size=128,
|
||||
select=1, required=True),
|
||||
'res_id': fields.integer('Related Document ID',
|
||||
select=1, required=True),
|
||||
'user_id': fields.many2one('res.users', string='Related User',
|
||||
ondelete='cascade', select=1, required=True),
|
||||
}
|
||||
|
||||
def _get_users(self, cr, uid, res_ids, res_model, context=None):
|
||||
"""Given res_ids of res_model, get user_ids present in table"""
|
||||
if context is None:
|
||||
context = {}
|
||||
needact_ids = self.search(cr, uid, [('res_model', '=', res_model), ('res_id', 'in', res_ids)], context=context)
|
||||
return map(itemgetter('res_id'), self.read(cr, uid, needact_ids, context=context))
|
||||
|
||||
def create_users(self, cr, uid, res_ids, res_model, user_ids, context=None):
|
||||
"""Given res_ids of res_model, add user_ids to the relationship table"""
|
||||
if context is None:
|
||||
context = {}
|
||||
for res_id in res_ids:
|
||||
for user_id in user_ids:
|
||||
self.create(cr, uid, {'res_model': res_model, 'res_id': res_id, 'user_id': user_id}, context=context)
|
||||
return True
|
||||
|
||||
def unlink_users(self, cr, uid, res_ids, res_model, context=None):
|
||||
"""Given res_ids of res_model, delete all entries in the relationship table"""
|
||||
if context is None:
|
||||
context = {}
|
||||
to_del_ids = self.search(cr, uid, [('res_model', '=', res_model), ('res_id', 'in', res_ids)], context=context)
|
||||
return self.unlink(cr, uid, to_del_ids, context=context)
|
||||
|
||||
def update_users(self, cr, uid, res_ids, res_model, user_ids, context=None):
|
||||
"""Given res_ids of res_model, update their entries in the relationship table to user_ids"""
|
||||
# read current records
|
||||
cur_users = self._get_users(cr, uid, res_ids, res_model, context=context)
|
||||
if len(cur_users) == len(user_ids) and all([cur_user in user_ids for cur_user in cur_users]):
|
||||
return True
|
||||
# unlink old records
|
||||
self.unlink_users(cr, uid, res_ids, res_model, context=context)
|
||||
# link new records
|
||||
self.create_users(cr, uid, res_ids, res_model, user_ids, context=context)
|
||||
return True
|
||||
|
||||
|
||||
class ir_needaction_mixin(osv.osv):
|
||||
'''Mixin class for objects using the need action feature.
|
||||
|
||||
Need action feature can be used by objects willing to be able to
|
||||
signal that an action is required on a particular record. If in the
|
||||
business logic an action must be performed by somebody, for instance
|
||||
validation by a manager, this mechanism allows to set a list of
|
||||
users asked to perform an action.
|
||||
|
||||
This class wraps a class (ir.needaction_users) that behaves
|
||||
like a many2many field. However, no field is added to the model
|
||||
inheriting from base.needaction. The mixin class manages the low-level
|
||||
considerations of updating relationships. Every change made on the
|
||||
record calls a method that updates the relationships.
|
||||
|
||||
Objects using the need_action feature should override the
|
||||
``get_needaction_user_ids`` method. This methods returns a dictionary
|
||||
whose keys are record ids, and values a list of user ids, like
|
||||
in a many2many relationship. Therefore by defining only one method,
|
||||
you can specify if an action is required by defining the users
|
||||
that have to do it, in every possible situation.
|
||||
|
||||
This class also offers several global services,:
|
||||
- ``needaction_get_record_ids``: for the current model and uid, get
|
||||
all record ids that ask this user to perform an action. This
|
||||
mechanism is used for instance to display the number of pending
|
||||
actions in menus, such as Leads (12)
|
||||
- ``needaction_get_action_count``: as ``needaction_get_record_ids``
|
||||
but returns only the number of action, not the ids (performs a
|
||||
search with count=True)
|
||||
- ``needaction_get_user_record_references``: for a given uid, get all
|
||||
the records that ask this user to perform an action. Records
|
||||
are given as references, a list of tuples (model_name, record_id)
|
||||
'''
|
||||
_name = 'ir.needaction_mixin'
|
||||
_description = 'Need action of users on records API'
|
||||
|
||||
#------------------------------------------------------
|
||||
# Addon API
|
||||
#------------------------------------------------------
|
||||
|
||||
def get_needaction_user_ids(self, cr, uid, ids, context=None):
|
||||
""" Returns the user_ids that have to perform an action
|
||||
:return: dict { record_id: [user_ids], }
|
||||
"""
|
||||
return dict.fromkeys(ids, [])
|
||||
|
||||
def create(self, cr, uid, values, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
needact_table_obj = self.pool.get('ir.needaction_users')
|
||||
# perform create
|
||||
obj_id = super(ir_needaction_mixin, self).create(cr, uid, values, context=context)
|
||||
# link user_ids
|
||||
needaction_user_ids = self.get_needaction_user_ids(cr, uid, [obj_id], context=context)
|
||||
needact_table_obj.create_users(cr, uid, [obj_id], self._name, needaction_user_ids[obj_id], context=context)
|
||||
return obj_id
|
||||
|
||||
def write(self, cr, uid, ids, values, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
needact_table_obj = self.pool.get('ir.needaction_users')
|
||||
# perform write
|
||||
write_res = super(ir_needaction_mixin, self).write(cr, uid, ids, values, context=context)
|
||||
# get and update user_ids
|
||||
needaction_user_ids = self.get_needaction_user_ids(cr, uid, ids, context=context)
|
||||
for id in ids:
|
||||
needact_table_obj.update_users(cr, uid, [id], self._name, needaction_user_ids[id], context=context)
|
||||
return write_res
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
# unlink user_ids
|
||||
needact_table_obj = self.pool.get('ir.needaction_users')
|
||||
needact_table_obj.unlink_users(cr, uid, ids, self._name, context=context)
|
||||
# perform unlink
|
||||
return super(ir_needaction_mixin, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
#------------------------------------------------------
|
||||
# Need action API
|
||||
#------------------------------------------------------
|
||||
|
||||
def needaction_get_record_ids(self, cr, uid, user_id, limit=80, context=None):
|
||||
"""Given the current model and a user_id
|
||||
get the number of actions it has to perform"""
|
||||
if context is None:
|
||||
context = {}
|
||||
needact_table_obj = self.pool.get('ir.needaction_users')
|
||||
needact_table_ids = needact_table_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, context=context)
|
||||
return map(itemgetter('res_id'), needact_table_obj.read(cr, uid, needact_table_ids, context=context))
|
||||
|
||||
def needaction_get_action_count(self, cr, uid, user_id, limit=80, context=None):
|
||||
"""Given the current model and a user_id
|
||||
get the number of actions it has to perform"""
|
||||
if context is None:
|
||||
context = {}
|
||||
needact_table_obj = self.pool.get('ir.needaction_users')
|
||||
return needact_table_obj.search(cr, uid, [('res_model', '=', self._name), ('user_id', '=', user_id)], limit=limit, count=True, context=context)
|
||||
|
||||
def needaction_get_record_references(self, cr, uid, user_id, offset=None, limit=None, order=None, context=None):
|
||||
"""For a given user_id, get all the records that asks this user to
|
||||
perform an action. Records are given as references, a list of
|
||||
tuples (model_name, record_id).
|
||||
This method is trans-model."""
|
||||
if context is None:
|
||||
context = {}
|
||||
needact_table_obj = self.pool.get('ir.needaction_users')
|
||||
needact_table_ids = needact_table_obj.search(cr, uid, [('user_id', '=', user_id)], offset=offset, limit=limit, order=order, context=context)
|
||||
needact_records = needact_table_obj.read(cr, uid, needact_table_ids, context=context)
|
||||
return map(itemgetter('res_model', 'id'), needact_records)
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -255,6 +255,24 @@ class ir_ui_menu(osv.osv):
|
||||
|
||||
return res
|
||||
|
||||
def _get_needaction(self, cr, uid, ids, field_names, args, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
res = dict.fromkeys(ids)
|
||||
for menu in self.browse(cr, uid, ids, context=context):
|
||||
res[menu.id] = {}
|
||||
if menu.action and menu.action.type == 'ir.actions.act_window' and menu.action.res_model:
|
||||
menu_needaction_res = self.pool.get(menu.action.res_model).get_needaction_info(cr, uid, uid, domain=menu.action.domain, context=context)
|
||||
# TODO: find the addon that causes a bug on runbot, not on local
|
||||
if not isinstance(menu_needaction_res[1], (int, long)): menu_needaction_res[1] = 0
|
||||
else:
|
||||
menu_needaction_res = [False, 0, ()]
|
||||
res[menu.id]['needaction_enabled'] = menu_needaction_res[0]
|
||||
res[menu.id]['needaction_counter'] = menu_needaction_res[1]
|
||||
# not used currently, therefore set to a void list
|
||||
res[menu.id]['needaction_record_ids'] = []
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Menu', size=64, required=True, translate=True),
|
||||
'sequence': fields.integer('Sequence'),
|
||||
@@ -271,6 +289,9 @@ class ir_ui_menu(osv.osv):
|
||||
'web_icon_hover':fields.char('Web Icon File (hover)', size=128),
|
||||
'web_icon_data': fields.function(_get_image_icon, string='Web Icon Image', type='binary', readonly=True, store=True, multi='icon'),
|
||||
'web_icon_hover_data':fields.function(_get_image_icon, string='Web Icon Image (hover)', type='binary', readonly=True, store=True, multi='icon'),
|
||||
'needaction_enabled': fields.function(_get_needaction, string='Target model uses the need action mechanism', type='boolean', help='If the menu entry action is an act_window action, and if this action is related to a model that uses the need_action mechanism, this field is set to true. Otherwise, it is false.', multi='_get_needaction'),
|
||||
'needaction_counter': fields.function(_get_needaction, string='Number of actions the user has to perform', type='integer', help='If the target model uses the need action mechanism, this field gives the number of actions the current user has to perform.', multi='_get_needaction'),
|
||||
'needaction_record_ids': fields.function(_get_needaction, string='Ids of records requesting an action from the user', type='many2many', help='If the target model uses the need action mechanism, this field holds the ids of the record requesting the user to perform an action.', multi='_get_needaction'),
|
||||
'action': fields.function(_action, fnct_inv=_action_inv,
|
||||
type='reference', string='Action',
|
||||
selection=[
|
||||
|
||||
@@ -76,6 +76,8 @@ class view(osv.osv):
|
||||
def _check_xml(self, cr, uid, ids, context=None):
|
||||
for view in self.browse(cr, uid, ids, context):
|
||||
eview = etree.fromstring(view.arch.encode('utf8'))
|
||||
if eview.get('layout') or eview.get('validate'):
|
||||
continue
|
||||
frng = tools.file_open(os.path.join('base','rng','view.rng'))
|
||||
try:
|
||||
relaxng_doc = etree.parse(frng)
|
||||
|
||||
@@ -187,21 +187,21 @@ class publisher_warranty_contract(osv.osv):
|
||||
|
||||
|
||||
limit_date = (datetime.datetime.now() - _PREVIOUS_LOG_CHECK).strftime(misc.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
for message in result["messages"]:
|
||||
ids = self.pool.get("res.log").search(cr, uid, [("res_model", "=", "publisher_warranty.contract"),
|
||||
("create_date", ">=", limit_date),
|
||||
("name", "=", message)])
|
||||
if ids:
|
||||
continue
|
||||
self.pool.get('res.log').create(cr, uid,
|
||||
{
|
||||
'name': message,
|
||||
'res_model': "publisher_warranty.contract",
|
||||
"read": True,
|
||||
"user_id": False,
|
||||
},
|
||||
context=context
|
||||
)
|
||||
|
||||
# old behavior based on res.log; now on mail.message, that is not necessarily installed
|
||||
mail_message_obj = self.pool.get('mail.message')
|
||||
if mail_message_obj:
|
||||
for message in result["messages"]:
|
||||
ids = mail_message_obj.search(cr, uid, [("model", "=", "publisher_warranty.contract"),
|
||||
("create_date", ">=", limit_date),
|
||||
("body_text", "=", message)])
|
||||
if ids:
|
||||
continue
|
||||
mail_message_obj.create(cr, uid, {
|
||||
'name': message,
|
||||
'model': "publisher_warranty.contract",
|
||||
'user_id': False,
|
||||
}, context=context)
|
||||
except Exception:
|
||||
if cron_mode:
|
||||
return False # we don't want to see any stack trace in cron
|
||||
@@ -215,22 +215,15 @@ class publisher_warranty_contract(osv.osv):
|
||||
@return: A list of html messages with ids, can be False or empty.
|
||||
@rtype: list of tuples(int,string)
|
||||
"""
|
||||
ids = self.pool.get('res.log').search(cr, uid, [("res_model", "=", "publisher_warranty.contract")]
|
||||
, order="create_date desc", limit=limit)
|
||||
if not self.pool.get('mail.message'):
|
||||
return []
|
||||
ids = self.pool.get('mail.message').search(cr, uid, [("model", "=", "publisher_warranty.contract")]
|
||||
, order="create_date desc", limit=limit, context=context)
|
||||
if not ids:
|
||||
return []
|
||||
messages = [(x.id, x.name) for x in self.pool.get('res.log').browse(cr, uid, ids)]
|
||||
|
||||
messages = [(x.id, x.name) for x in self.pool.get('mail.message').browse(cr, uid, ids, context=context)]
|
||||
return messages
|
||||
|
||||
def del_user_message(self, cr, uid, id, context=None):
|
||||
"""
|
||||
Delete a message.
|
||||
"""
|
||||
self.pool.get('res.log').unlink(cr, uid, [id])
|
||||
|
||||
return True
|
||||
|
||||
_columns = {
|
||||
'name' : fields.char('Serial Key', size=384, required=True, help="Your OpenERP Publisher's Warranty Contract unique key, also called serial number."),
|
||||
'date_start' : fields.date('Starting Date', readonly=True),
|
||||
|
||||
@@ -32,7 +32,6 @@ import res_company
|
||||
import res_users
|
||||
import res_request
|
||||
import res_lang
|
||||
import res_log
|
||||
import res_widget
|
||||
import ir_property
|
||||
|
||||
|
||||
@@ -471,7 +471,6 @@ class res_config_settings(osv.osv_memory):
|
||||
|
||||
def default_get(self, cr, uid, fields, context=None):
|
||||
ir_values = self.pool.get('ir.values')
|
||||
ir_model_data = self.pool.get('ir.model.data')
|
||||
classified = self._get_classified_fields(cr, uid, context)
|
||||
|
||||
res = super(res_config_settings, self).default_get(cr, uid, fields, context)
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from osv import fields, osv
|
||||
|
||||
class res_log(osv.osv):
|
||||
_name = 'res.log'
|
||||
_columns = {
|
||||
'name': fields.char('Message', size=250, help='The logging message.', required=True, select=1),
|
||||
'user_id': fields.many2one('res.users','User'),
|
||||
'res_model': fields.char('Object', size=128, select=1),
|
||||
'context': fields.char('Context', size=250),
|
||||
'res_id': fields.integer('Object ID'),
|
||||
'secondary': fields.boolean('Secondary Log', help='Do not display this log if it belongs to the same object the user is working on'),
|
||||
'create_date': fields.datetime('Creation Date', readonly=True, select=1),
|
||||
'read': fields.boolean('Read', help="If this log item has been read, get() should not send it to the client"),
|
||||
}
|
||||
_defaults = {
|
||||
'user_id': lambda self,cr,uid,ctx: uid,
|
||||
'context': "{}",
|
||||
'read': False,
|
||||
}
|
||||
_order='create_date desc'
|
||||
|
||||
_index_name = 'res_log_uid_read'
|
||||
def _auto_init(self, cr, context=None):
|
||||
super(res_log, self)._auto_init(cr, context)
|
||||
cr.execute('SELECT 1 FROM pg_indexes WHERE indexname=%s',
|
||||
(self._index_name,))
|
||||
if not cr.fetchone():
|
||||
cr.execute('CREATE INDEX %s ON res_log (user_id, read)' %
|
||||
self._index_name)
|
||||
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
create_context = context and dict(context) or {}
|
||||
if 'res_log_read' in create_context:
|
||||
vals['read'] = create_context.pop('res_log_read')
|
||||
if create_context and not vals.get('context'):
|
||||
vals['context'] = create_context
|
||||
return super(res_log, self).create(cr, uid, vals, context=context)
|
||||
|
||||
# TODO: do not return secondary log if same object than in the model (but unlink it)
|
||||
def get(self, cr, uid, context=None):
|
||||
unread_log_ids = self.search(cr, uid,
|
||||
[('user_id','=',uid), ('read', '=', False)], context=context)
|
||||
res = self.read(cr, uid, unread_log_ids,
|
||||
['name','res_model','res_id','context'],
|
||||
context=context)
|
||||
res.reverse()
|
||||
result = []
|
||||
res_dict = {}
|
||||
for r in res:
|
||||
t = (r['res_model'], r['res_id'])
|
||||
if t not in res_dict:
|
||||
res_dict[t] = True
|
||||
result.insert(0,r)
|
||||
self.write(cr, uid, unread_log_ids, {'read': True}, context=context)
|
||||
return result
|
||||
|
||||
res_log()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -1,65 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="res_log_search" model="ir.ui.view">
|
||||
<field name="name">res.log.search</field>
|
||||
<field name="model">res.log</field>
|
||||
<field name="type">search</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Logs">
|
||||
<filter icon="terp-camera_test" string="Read" domain="[('read','=',True)]" name="read"/>
|
||||
<filter icon="terp-gtk-jump-to-ltr" string="Unread" domain="[('read','<>',True)]" name="unread"/>
|
||||
<separator orientation="vertical"/>
|
||||
<filter icon="terp-personal" string="My Logs" domain="[('user_id','=',uid)]" name="my"/>
|
||||
<filter icon="terp-stock_align_left_24" string="System Logs"
|
||||
domain="[('res_model','=','publisher_warranty.contract')]" name="sys"/>
|
||||
<separator orientation="vertical"/>
|
||||
<field name="name"/>
|
||||
<field name="user_id"/>
|
||||
<field name="res_model"/>
|
||||
<field name="create_date"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
<record id="res_log_tree" model="ir.ui.view">
|
||||
<field name="name">res.log.tree</field>
|
||||
<field name="model">res.log</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Logs">
|
||||
<field name="create_date"/>
|
||||
<field name="user_id"/>
|
||||
<field name="name"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="res_log_form" model="ir.ui.view">
|
||||
<field name="name">res.log.form</field>
|
||||
<field name="model">res.log</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Logs">
|
||||
<group colspan="4" col="6">
|
||||
<field name="name"/>
|
||||
<field name="create_date"/>
|
||||
<field name="read"/>
|
||||
<field name="user_id" />
|
||||
<field name="res_model"/>
|
||||
<field name="res_id"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="res_log_act_window" model="ir.actions.act_window">
|
||||
<field name="name">Client Logs</field>
|
||||
<field name="res_model">res.log</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="context">{'search_default_my': 1}</field>
|
||||
</record>
|
||||
<menuitem
|
||||
action="res_log_act_window"
|
||||
id="menu_res_log_act_window"
|
||||
parent="base.menu_audit"/>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -307,10 +307,15 @@ class res_partner(osv.osv):
|
||||
if name and operator in ('=', 'ilike', '=ilike', 'like'):
|
||||
# search on the name of the contacts and of its company
|
||||
name2 = operator == '=' and name or '%' + name + '%'
|
||||
limit_str = ''
|
||||
query_args = [name2]
|
||||
if limit:
|
||||
limit_str = ' limit %s'
|
||||
query_args += [limit]
|
||||
cr.execute('''SELECT partner.id FROM res_partner partner
|
||||
LEFT JOIN res_partner company ON partner.parent_id = company.id
|
||||
WHERE partner.name || ' (' || COALESCE(company.name,'') || ')'
|
||||
''' + operator + ''' %s ''', (name2,))
|
||||
''' + operator + ''' %s ''' + limit_str, query_args)
|
||||
ids = map(lambda x: x[0], cr.fetchall())
|
||||
if args:
|
||||
ids = self.search(cr, uid, [('id', 'in', ids)] + args, limit=limit, context=context)
|
||||
|
||||
@@ -331,27 +331,24 @@
|
||||
<field name="model">res.partner</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Partners">
|
||||
<form string="Partners" validate="0">
|
||||
<group col="8" colspan="4">
|
||||
<group col="4" colspan="4">
|
||||
<field name="name" required="1"/>
|
||||
<field name="title" size="0" groups="base.group_extended" domain="[('domain', '=', 'contact')]"/>
|
||||
<newline/>
|
||||
<field name="function" attrs="{'invisible': [('is_company', '=', True)]}" colspan="4"/>
|
||||
<field name="parent_id" string="Company" colspan="4" attrs="{'invisible': [('is_company','=', True)]}"
|
||||
domain="[('is_company', '=', True)]" context="{'default_is_company': True}"
|
||||
on_change="onchange_address(use_parent_address, parent_id)"/>
|
||||
<group col="2">
|
||||
<field name="photo" widget='image' nolabel="1"/>
|
||||
</group>
|
||||
<group colspan="4">
|
||||
<h2><field name="name" required="1" nolabel="1" colspan="4"/></h2><newline/>
|
||||
<field name="function" attrs="{'invisible': [('is_company', '=', True)]}"/>
|
||||
<field name="title" size="0" groups="base.group_extended" domain="[('domain', '=', 'contact')]"/>
|
||||
<field name="parent_id" string="Company" attrs="{'invisible': [('is_company','=', True)]}"
|
||||
domain="[('is_company', '=', True)]" context="{'default_is_company': True}"
|
||||
on_change="onchange_address(use_parent_address, parent_id)"/>
|
||||
</group>
|
||||
<group col="2">
|
||||
<field name="is_company" on_change="onchange_type(is_company)"/>
|
||||
</group>
|
||||
<group col="2">
|
||||
<field name="customer"/>
|
||||
<field name="supplier"/>
|
||||
</group>
|
||||
<group col="2">
|
||||
<field name="photo" widget='image' nolabel="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="General">
|
||||
@@ -372,7 +369,6 @@
|
||||
</group>
|
||||
<group colspan="2">
|
||||
<separator string="Communication" colspan="4"/>
|
||||
<field name="lang" colspan="4"/>
|
||||
<field name="phone" colspan="4"/>
|
||||
<field name="mobile" colspan="4"/>
|
||||
<field name="fax" colspan="4"/>
|
||||
@@ -381,77 +377,61 @@
|
||||
<field name="ref" groups="base.group_extended" colspan="4"/>
|
||||
</group>
|
||||
<group colspan="4" attrs="{'invisible': [('is_company','=', False)]}">
|
||||
<field name="child_ids" context="{'default_parent_id': active_id}" nolabel="1">
|
||||
<form string="Partners">
|
||||
<group col="8" colspan="4">
|
||||
<group col="4" colspan="4">
|
||||
<field name="name" required="1"/>
|
||||
<field name="title" size="0" groups="base.group_extended" domain="[('domain', '=', 'contact')]"/>
|
||||
<newline/>
|
||||
<field name="function" attrs="{'invisible': [('is_company', '=', True)]}" colspan="4"/>
|
||||
<field name="parent_id" string="Company" colspan="4" attrs="{'invisible': [('is_company','=', True)]}"
|
||||
domain="[('is_company', '=', True)]" context="{'default_is_company': True}"
|
||||
on_change="onchange_address(use_parent_address, parent_id)" invisible="1"/>
|
||||
</group>
|
||||
<group col="2">
|
||||
<field name="is_company" on_change="onchange_type(is_company)" invisible="1"/>
|
||||
</group>
|
||||
<group col="2">
|
||||
<field name="customer"/>
|
||||
<field name="supplier"/>
|
||||
</group>
|
||||
<group col="2">
|
||||
<field name="photo" widget='image' nolabel="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook colspan="4">
|
||||
<page string="General">
|
||||
<group colspan="2">
|
||||
<separator string="Address" colspan="4"/>
|
||||
<field name="type" string="Type" attrs="{'invisible': [('is_company','=', True)]}"/>
|
||||
<group colspan="2">
|
||||
<field name="use_parent_address" attrs="{'invisible': [('is_company','=', True)]}" on_change="onchange_address(use_parent_address, parent_id)"/>
|
||||
</group>
|
||||
<newline/>
|
||||
<field name="street" colspan="4"/>
|
||||
<field name="street2" colspan="4"/>
|
||||
<field name="zip"/>
|
||||
<field name="city"/>
|
||||
<field name="country_id"/>
|
||||
<field name="state_id"/>
|
||||
</group>
|
||||
<group colspan="2">
|
||||
<separator string="Communication" colspan="4"/>
|
||||
<field name="lang" colspan="4"/>
|
||||
<field name="phone" colspan="4"/>
|
||||
<field name="mobile" colspan="4"/>
|
||||
<field name="fax" colspan="4"/>
|
||||
<field name="email" widget="email" colspan="4"/>
|
||||
<field name="website" widget="url" colspan="4"/>
|
||||
<field name="ref" groups="base.group_extended" colspan="4"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Sales & Purchases" attrs="{'invisible': [('customer', '=', False), ('supplier', '=', False)]}">
|
||||
<separator string="General Information" colspan="4"/>
|
||||
<field name="user_id"/>
|
||||
<field name="active" groups="base.group_extended"/>
|
||||
<field name="date"/>
|
||||
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
|
||||
<newline/>
|
||||
</page>
|
||||
<page string="Categories" groups="base.group_extended">
|
||||
<field name="category_id" colspan="4" nolabel="1"/>
|
||||
</page>
|
||||
<page string="Notes">
|
||||
<field name="comment" colspan="4" nolabel="1"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</form>
|
||||
</field>
|
||||
<field name="child_ids" context="{'default_parent_id': active_id}" nolabel="1" mode="kanban,list">
|
||||
<kanban>
|
||||
<field name="color"/>
|
||||
<field name="name"/>
|
||||
<field name="title"/>
|
||||
<field name="email"/>
|
||||
<field name="parent_id"/>
|
||||
<field name="is_company"/>
|
||||
<field name="function"/>
|
||||
<field name="phone"/>
|
||||
<field name="street"/>
|
||||
<field name="street2"/>
|
||||
<field name="photo"/>
|
||||
<field name="zip"/>
|
||||
<field name="city"/>
|
||||
<field name="country_id"/>
|
||||
<field name="mobile"/>
|
||||
<field name="state_id"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<t t-set="color" t-value="kanban_color(record.color.raw_value)"/>
|
||||
<div t-att-class="color + (record.title.raw_value == 1 ? ' oe_kanban_color_alert' : '')">
|
||||
<div class="oe_module_vignette">
|
||||
<a type="edit">
|
||||
<img t-att-src="kanban_image('res.partner', 'photo', record.id.value)" class="oe_form_avatar oe_kanban_avatar_toto"/>
|
||||
</a>
|
||||
<div class="oe_module_desc">
|
||||
<div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_box_show_onclick_trigger oe_kanban_color_border">
|
||||
<table class="oe_kanban_table">
|
||||
<tr>
|
||||
<td class="oe_kanban_title1" align="left" valign="middle">
|
||||
<h4><a type="edit"><field name="name"/></a></h4>
|
||||
<i><div t-if="record.function.raw_value">
|
||||
<field name="function"/><br/></div></i>
|
||||
</td>
|
||||
<td valign="top" align="right">
|
||||
<a t-if="record.email.raw_value" title="Mail" t-att-href="'mailto:'+record.email.value" style="text-decoration: none;" >
|
||||
<img src="/web/static/src/img/icons/terp-mail-message-new.png" border="0" width="16" height="16"/>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Sales & Purchases" attrs="{'invisible': [('customer', '=', False), ('supplier', '=', False)]}">
|
||||
<separator string="General Information" colspan="4"/>
|
||||
<field name="lang" colspan="4"/>
|
||||
<field name="user_id"/>
|
||||
<field name="active" groups="base.group_extended"/>
|
||||
<field name="date"/>
|
||||
@@ -533,7 +513,7 @@
|
||||
<img t-att-src="kanban_image('res.partner', 'photo', record.id.value)" width="64" height="64" class="oe_module_icon"/>
|
||||
</a>
|
||||
<div class="oe_module_desc">
|
||||
<div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_box_show_onclick_trigger oe_kanban_color_border">
|
||||
<div class="oe_kanban_box_content oe_kanban_box_show_onclick_trigger oe_kanban_color_border">
|
||||
<table class="oe_kanban_table">
|
||||
<tr>
|
||||
<td class="oe_kanban_title1" align="left" valign="middle">
|
||||
@@ -541,7 +521,7 @@
|
||||
<div t-if="record.parent_id.raw_value"><field name="parent_id"/></div>
|
||||
</h4>
|
||||
<i><div t-if="record.contact_address.raw_value"><field name="contact_address"/><br/></div>
|
||||
<div t-if="record.email.raw_value">
|
||||
<div t-if="record.email.raw_value">
|
||||
<field name="email"/><br/></div>
|
||||
<div t-if="record.mobile.raw_value">
|
||||
<field name="mobile"/><br/>
|
||||
|
||||
@@ -40,7 +40,6 @@ from service import security
|
||||
import tools
|
||||
from tools.translate import _
|
||||
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class groups(osv.osv):
|
||||
@@ -205,7 +204,7 @@ class users(osv.osv):
|
||||
img_stream = StringIO.StringIO()
|
||||
img.save(img_stream, "PNG")
|
||||
return img_stream.getvalue().encode('base64')
|
||||
|
||||
|
||||
def _get_avatar(self, cr, uid, ids, name, args, context=None):
|
||||
result = dict.fromkeys(ids, False)
|
||||
for user in self.browse(cr, uid, ids, context=context):
|
||||
@@ -376,7 +375,7 @@ class users(osv.osv):
|
||||
}
|
||||
|
||||
# User can write to a few of her own fields (but not her groups for example)
|
||||
SELF_WRITEABLE_FIELDS = ['menu_tips','view', 'password', 'signature', 'action_id', 'company_id', 'user_email', 'name']
|
||||
SELF_WRITEABLE_FIELDS = ['menu_tips','view', 'password', 'signature', 'action_id', 'company_id', 'user_email', 'name', 'avatar', 'avatar_big']
|
||||
|
||||
def write(self, cr, uid, ids, values, context=None):
|
||||
if not hasattr(ids, '__iter__'):
|
||||
|
||||
@@ -117,8 +117,10 @@
|
||||
"access_ir_filters","ir_filters_all","model_ir_filters",,1,1,1,1
|
||||
"access_res_widget","res.widget","model_res_widget","group_erp_manager",1,1,1,1
|
||||
"access_res_widget_user","res.widget.user","model_res_widget",,1,0,0,0
|
||||
"access_res_log_all","res.log","model_res_log",,1,1,1,1
|
||||
"access_ir_config_parameter","ir_config_parameter","model_ir_config_parameter",,1,0,0,0
|
||||
"access_ir_mail_server_all","ir_mail_server","model_ir_mail_server",,1,0,0,0
|
||||
"access_ir_actions_todo_category","ir_actions_todo_category","model_ir_actions_todo_category","group_system",1,1,1,1
|
||||
"access_ir_actions_client","ir_actions_client all","model_ir_actions_client",,1,0,0,0
|
||||
"access_ir_needaction_users","ir_needaction_users","model_ir_needaction_users",,1,1,1,1
|
||||
"access_ir_needaction_mixin","ir_needaction_mixin","model_ir_needaction_mixin",,1,1,1,1
|
||||
|
||||
|
||||
|
@@ -212,10 +212,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=
|
||||
delattr(package, kind)
|
||||
|
||||
cr.commit()
|
||||
|
||||
# mark new res_log records as read
|
||||
cr.execute("update res_log set read=True where create_date >= %s", (dt_before_load,))
|
||||
|
||||
|
||||
cr.commit()
|
||||
|
||||
return loaded_modules, processed_modules
|
||||
|
||||
+32
-14
@@ -709,18 +709,8 @@ class BaseModel(object):
|
||||
CONCURRENCY_CHECK_FIELD = '__last_update'
|
||||
|
||||
def log(self, cr, uid, id, message, secondary=False, context=None):
|
||||
if context and context.get('disable_log'):
|
||||
return True
|
||||
return self.pool.get('res.log').create(cr, uid,
|
||||
{
|
||||
'name': message,
|
||||
'res_model': self._name,
|
||||
'secondary': secondary,
|
||||
'res_id': id,
|
||||
},
|
||||
context=context
|
||||
)
|
||||
|
||||
return _logger.warning("log() is deprecated. Please use OpenChatter notification system instead of the res.log mechanism.")
|
||||
|
||||
def view_init(self, cr, uid, fields_list, context=None):
|
||||
"""Override this method to do specific things when a view on the object is opened."""
|
||||
pass
|
||||
@@ -1661,7 +1651,7 @@ class BaseModel(object):
|
||||
children = False
|
||||
views = {}
|
||||
for f in node:
|
||||
if f.tag in ('form', 'tree', 'graph'):
|
||||
if f.tag in ('form', 'tree', 'graph', 'kanban'):
|
||||
node.remove(f)
|
||||
ctx = context.copy()
|
||||
ctx['base_model_name'] = self._name
|
||||
@@ -4875,7 +4865,35 @@ class BaseModel(object):
|
||||
# backwards compatibility
|
||||
get_xml_id = get_external_id
|
||||
_get_xml_ids = _get_external_ids
|
||||
|
||||
|
||||
def get_needaction_info(self, cr, uid, user_id, limit=None, order=None, domain=False, context=None):
|
||||
"""Base method for needaction mechanism
|
||||
- see ir.needaction for actual implementation
|
||||
- if the model uses the need action mechanism
|
||||
(hasattr(model_obj, 'needaction_get_record_ids')):
|
||||
- get the record ids on which the user has actions to perform
|
||||
- evaluate the menu domain
|
||||
- compose a new domain: menu domain, limited to ids of
|
||||
records requesting an action
|
||||
- count the number of records maching that domain, that
|
||||
is the number of actions the user has to perform
|
||||
- this method returns default values
|
||||
:param: model_name: the name of the model (ex: hr.holidays)
|
||||
:param: user_id: the id of user
|
||||
:return: [uses_needaction=True/False, needaction_uid_ctr=%d]
|
||||
"""
|
||||
if hasattr(self, 'needaction_get_record_ids'):
|
||||
ids = self.needaction_get_record_ids(cr, uid, user_id, limit=8192, context=context)
|
||||
if not ids:
|
||||
return [True, 0, []]
|
||||
if domain:
|
||||
new_domain = eval(domain, locals_dict={'uid': user_id}) + [('id', 'in', ids)]
|
||||
else:
|
||||
new_domain = [('id', 'in', ids)]
|
||||
return [True, self.search(cr, uid, new_domain, limit=limit, order=order, count=True, context=context), ids]
|
||||
else:
|
||||
return [False, 0, []]
|
||||
|
||||
# Transience
|
||||
def is_transient(self):
|
||||
""" Return whether the model is transient.
|
||||
|
||||
Reference in New Issue
Block a user