2009-10-20 10:52:23 +00:00
# -*- coding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
2009-10-30 12:33:59 +00:00
#
2009-10-14 12:32:15 +00:00
# OpenERP, Open Source Management Solution
2014-01-07 17:20:10 +00:00
# Copyright (C) 2004-2014 OpenERP S.A. <http://www.openerp.com>
2008-06-16 11:00:21 +00:00
#
2008-11-03 18:27:16 +00:00
# This program is free software: you can redistribute it and/or modify
2009-10-14 12:32:15 +00:00
# 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.
2006-12-07 13:41:40 +00:00
#
2008-11-03 18:27:16 +00:00
# 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
2009-10-14 12:32:15 +00:00
# GNU Affero General Public License for more details.
2006-12-07 13:41:40 +00:00
#
2009-10-14 12:32:15 +00:00
# You should have received a copy of the GNU Affero General Public License
2009-10-30 12:33:59 +00:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
2012-01-18 11:12:26 +00:00
2013-07-25 10:49:14 +00:00
from functools import partial
2011-06-30 11:51:34 +00:00
import logging
2013-03-27 15:47:14 +00:00
import operator
2010-10-01 11:25:52 +00:00
import os
2011-06-30 11:51:34 +00:00
import time
2014-02-10 14:09:33 +00:00
import datetime
import dateutil
2015-11-12 11:23:27 +00:00
import pytz
2011-06-30 11:51:34 +00:00
2013-03-27 15:47:14 +00:00
import openerp
2012-08-31 13:53:09 +00:00
from openerp import SUPERUSER_ID
2013-03-26 16:20:40 +00:00
from openerp import tools
2013-07-15 15:24:24 +00:00
from openerp import workflow
2014-10-08 11:09:13 +00:00
import openerp . api
2012-12-10 15:27:23 +00:00
from openerp . osv import fields , osv
2013-07-15 15:24:24 +00:00
from openerp . osv . orm import browse_record
2013-02-01 11:22:32 +00:00
import openerp . report . interface
2012-12-10 15:27:23 +00:00
from openerp . report . report_sxw import report_sxw , report_rml
from openerp . tools . safe_eval import safe_eval as eval
from openerp . tools . translate import _
2013-03-26 16:20:40 +00:00
import openerp . workflow
2006-12-07 13:41:40 +00:00
2012-01-24 11:47:30 +00:00
_logger = logging . getLogger ( __name__ )
2014-05-21 11:11:27 +00:00
2006-12-07 13:41:40 +00:00
class actions ( osv . osv ) :
2008-07-22 14:24:36 +00:00
_name = ' ir.actions.actions '
_table = ' ir_actions '
2010-12-10 22:42:58 +00:00
_order = ' name '
2008-07-22 14:24:36 +00:00
_columns = {
2014-05-21 09:52:05 +00:00
' name ' : fields . char ( ' Name ' , required = True ) ,
' type ' : fields . char ( ' Action Type ' , required = True ) ,
' usage ' : fields . char ( ' Action Usage ' ) ,
2012-09-24 14:57:50 +00:00
' help ' : fields . text ( ' Action description ' ,
help = ' Optional help text for the users with a description of the target view, such as its usage and purpose. ' ,
translate = True ) ,
2008-07-22 14:24:36 +00:00
}
_defaults = {
' usage ' : lambda * a : False ,
}
2006-12-07 13:41:40 +00:00
2014-05-20 11:30:15 +00:00
def unlink ( self , cr , uid , ids , context = None ) :
""" unlink ir.action.todo which are related to actions which will be deleted.
NOTE : ondelete cascade will not work on ir . actions . actions so we will need to do it manually . """
todo_obj = self . pool . get ( ' ir.actions.todo ' )
2014-10-07 14:07:03 +00:00
if not ids :
return True
if isinstance ( ids , ( int , long ) ) :
ids = [ ids ]
2014-05-20 11:30:15 +00:00
todo_ids = todo_obj . search ( cr , uid , [ ( ' action_id ' , ' in ' , ids ) ] , context = context )
todo_obj . unlink ( cr , uid , todo_ids , context = context )
return super ( actions , self ) . unlink ( cr , uid , ids , context = context )
2006-12-07 13:41:40 +00:00
2013-10-06 10:24:04 +00:00
class ir_actions_report_xml ( osv . osv ) :
2007-09-07 14:02:45 +00:00
2008-07-22 14:24:36 +00:00
def _report_content ( self , cursor , user , ids , name , arg , context = None ) :
res = { }
for report in self . browse ( cursor , user , ids , context = context ) :
data = report [ name + ' _data ' ]
if not data and report [ name [ : - 8 ] ] :
2011-01-04 10:13:35 +00:00
fp = None
2008-07-22 14:24:36 +00:00
try :
fp = tools . file_open ( report [ name [ : - 8 ] ] , mode = ' rb ' )
data = fp . read ( )
except :
data = False
2011-01-04 10:13:35 +00:00
finally :
if fp :
fp . close ( )
2008-07-22 14:24:36 +00:00
res [ report . id ] = data
return res
2007-09-07 14:02:45 +00:00
2008-07-22 14:24:36 +00:00
def _report_content_inv ( self , cursor , user , id , name , value , arg , context = None ) :
self . write ( cursor , user , id , { name + ' _data ' : value } , context = context )
2007-09-07 14:02:45 +00:00
2008-07-22 14:24:36 +00:00
def _report_sxw ( self , cursor , user , ids , name , arg , context = None ) :
res = { }
for report in self . browse ( cursor , user , ids , context = context ) :
if report . report_rml :
res [ report . id ] = report . report_rml . replace ( ' .rml ' , ' .sxw ' )
else :
res [ report . id ] = False
return res
2007-09-07 14:02:45 +00:00
2013-03-25 12:33:59 +00:00
def _lookup_report ( self , cr , name ) :
2013-02-22 12:48:01 +00:00
"""
2013-03-25 12:33:59 +00:00
Look up a report definition .
2013-02-22 12:48:01 +00:00
"""
opj = os . path . join
2013-02-22 14:24:00 +00:00
# First lookup in the deprecated place, because if the report definition
# has not been updated, it is more likely the correct definition is there.
2013-03-22 15:39:55 +00:00
# Only reports with custom parser sepcified in Python are still there.
2013-02-22 14:24:00 +00:00
if ' report. ' + name in openerp . report . interface . report_int . _reports :
new_report = openerp . report . interface . report_int . _reports [ ' report. ' + name ]
else :
cr . execute ( " SELECT * FROM ir_act_report_xml WHERE report_name= %s " , ( name , ) )
r = cr . dictfetchone ( )
if r :
2014-04-01 15:33:39 +00:00
if r [ ' report_type ' ] in [ ' qweb-pdf ' , ' qweb-html ' ] :
return r [ ' report_name ' ]
elif r [ ' report_rml ' ] or r [ ' report_rml_content_data ' ] :
2013-02-22 14:24:00 +00:00
if r [ ' parser ' ] :
kwargs = { ' parser ' : operator . attrgetter ( r [ ' parser ' ] ) ( openerp . addons ) }
else :
kwargs = { }
new_report = report_sxw ( ' report. ' + r [ ' report_name ' ] , r [ ' model ' ] ,
opj ( ' addons ' , r [ ' report_rml ' ] or ' / ' ) , header = r [ ' header ' ] , register = False , * * kwargs )
2013-09-12 15:06:16 +00:00
elif r [ ' report_xsl ' ] and r [ ' report_xml ' ] :
2013-02-22 14:24:00 +00:00
new_report = report_rml ( ' report. ' + r [ ' report_name ' ] , r [ ' model ' ] ,
opj ( ' addons ' , r [ ' report_xml ' ] ) ,
r [ ' report_xsl ' ] and opj ( ' addons ' , r [ ' report_xsl ' ] ) , register = False )
2013-02-22 12:48:01 +00:00
else :
2013-02-22 14:24:00 +00:00
raise Exception , " Unhandled report type: %s " % r
2013-02-22 12:48:01 +00:00
else :
2014-09-30 09:35:05 +00:00
raise Exception , " Required report does not exist: %s " % name
2013-02-22 12:48:01 +00:00
2013-03-25 12:33:59 +00:00
return new_report
def render_report ( self , cr , uid , res_ids , name , data , context = None ) :
"""
Look up a report definition and render the report for the provided IDs .
"""
new_report = self . _lookup_report ( cr , name )
2014-05-21 11:11:27 +00:00
if isinstance ( new_report , ( str , unicode ) ) : # Qweb report
# The only case where a QWeb report is rendered with this method occurs when running
# yml tests originally written for RML reports.
if openerp . tools . config [ ' test_enable ' ] and not tools . config [ ' test_report_directory ' ] :
# Only generate the pdf when a destination folder has been provided.
return self . pool [ ' report ' ] . get_html ( cr , uid , res_ids , new_report , data = data , context = context ) , ' html '
else :
return self . pool [ ' report ' ] . get_pdf ( cr , uid , res_ids , new_report , data = data , context = context ) , ' pdf '
2014-04-01 15:33:39 +00:00
else :
return new_report . create ( cr , uid , res_ids , data , context )
2010-10-01 11:25:52 +00:00
2008-07-22 14:24:36 +00:00
_name = ' ir.actions.report.xml '
2012-09-24 14:57:50 +00:00
_inherit = ' ir.actions.actions '
2008-07-22 14:24:36 +00:00
_table = ' ir_act_report_xml '
_sequence = ' ir_actions_id_seq '
2010-12-10 22:42:58 +00:00
_order = ' name '
2008-07-22 14:24:36 +00:00
_columns = {
2014-05-21 09:52:05 +00:00
' type ' : fields . char ( ' Action Type ' , required = True ) ,
' name ' : fields . char ( ' Name ' , required = True , translate = True ) ,
2014-03-06 23:46:57 +00:00
' model ' : fields . char ( ' Model ' , required = True ) ,
2014-03-07 01:13:05 +00:00
' report_type ' : fields . selection ( [ ( ' qweb-pdf ' , ' PDF ' ) ,
( ' qweb-html ' , ' HTML ' ) ,
2014-03-21 18:47:39 +00:00
( ' controller ' , ' Controller ' ) ,
2014-03-07 01:13:05 +00:00
( ' pdf ' , ' RML pdf (deprecated) ' ) ,
( ' sxw ' , ' RML sxw (deprecated) ' ) ,
( ' webkit ' , ' Webkit (deprecated) ' ) ,
2014-03-24 15:05:33 +00:00
] , ' Report Type ' , required = True , help = " HTML will open the report directly in your browser, PDF will use wkhtmltopdf to render the HTML into a PDF file and let you download it, Controller allows you to define the url of a custom controller outputting any kind of report. " ) ,
' report_name ' : fields . char ( ' Template Name ' , required = True , help = " For QWeb reports, name of the template used in the rendering. The method ' render_html ' of the model ' report.template_name ' will be called (if any) to give the html. For RML reports, this is the LocalService name. " ) ,
2008-09-04 00:22:48 +00:00
' groups_id ' : fields . many2many ( ' res.groups ' , ' res_groups_report_rel ' , ' uid ' , ' gid ' , ' Groups ' ) ,
2014-03-06 23:46:57 +00:00
# options
2012-04-23 11:32:57 +00:00
' 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. " ) ,
2010-07-21 07:22:52 +00:00
' attachment_use ' : fields . boolean ( ' Reload from Attachment ' , help = ' If you check this, then the second time the user prints with same attachment name, it returns the previous report. ' ) ,
2014-05-21 09:52:05 +00:00
' attachment ' : fields . char ( ' Save as Attachment Prefix ' , 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. ' ) ,
2010-07-21 07:13:32 +00:00
2014-03-06 23:46:57 +00:00
# Deprecated rml stuff
2014-05-21 09:52:05 +00:00
' usage ' : fields . char ( ' Action Usage ' ) ,
2012-04-23 11:32:57 +00:00
' header ' : fields . boolean ( ' Add RML Header ' , help = " Add or not the corporate RML header " ) ,
2014-03-06 23:46:57 +00:00
' parser ' : fields . char ( ' Parser Class ' ) ,
' auto ' : fields . boolean ( ' Custom Python Parser ' ) ,
2010-07-21 07:13:32 +00:00
2014-03-06 23:46:57 +00:00
' report_xsl ' : fields . char ( ' XSL Path ' ) ,
' report_xml ' : fields . char ( ' XML Path ' ) ,
2010-07-21 07:13:32 +00:00
2014-03-21 18:47:39 +00:00
' report_rml ' : fields . char ( ' Main Report File Path/controller ' , help = " The path to the main report file/controller (depending on Report Type) or NULL if the content is in another data field " ) ,
2014-03-06 23:46:57 +00:00
' report_file ' : fields . related ( ' report_rml ' , type = " char " , required = False , readonly = False , string = ' Report File ' , help = " The path to the main report file (depending on Report Type) or NULL if the content is in another field " , store = True ) ,
2010-10-08 14:23:01 +00:00
2012-04-23 11:32:57 +00:00
' report_sxw ' : fields . function ( _report_sxw , type = ' char ' , string = ' SXW Path ' ) ,
' report_sxw_content_data ' : fields . binary ( ' SXW Content ' ) ,
' report_rml_content_data ' : fields . binary ( ' RML Content ' ) ,
2012-05-10 09:35:27 +00:00
' report_sxw_content ' : fields . function ( _report_content , fnct_inv = _report_content_inv , type = ' binary ' , string = ' SXW Content ' , ) ,
2012-04-23 11:32:57 +00:00
' report_rml_content ' : fields . function ( _report_content , fnct_inv = _report_content_inv , type = ' binary ' , string = ' RML Content ' ) ,
2008-07-22 14:24:36 +00:00
}
_defaults = {
2012-11-02 09:47:05 +00:00
' type ' : ' ir.actions.report.xml ' ,
' multi ' : False ,
' auto ' : True ,
' header ' : True ,
' report_sxw_content ' : False ,
2014-03-07 00:32:55 +00:00
' report_type ' : ' pdf ' ,
2012-11-02 09:47:05 +00:00
' attachment ' : False ,
2008-07-22 14:24:36 +00:00
}
2007-07-31 14:08:58 +00:00
2006-12-07 13:41:40 +00:00
2013-10-06 10:24:04 +00:00
class ir_actions_act_window ( osv . osv ) :
2008-07-22 14:24:36 +00:00
_name = ' ir.actions.act_window '
_table = ' ir_act_window '
2012-09-24 14:57:50 +00:00
_inherit = ' ir.actions.actions '
2008-07-22 14:24:36 +00:00
_sequence = ' ir_actions_id_seq '
2010-12-10 22:42:58 +00:00
_order = ' name '
2009-10-30 12:33:59 +00:00
2010-11-23 13:52:24 +00:00
def _check_model ( self , cr , uid , ids , context = None ) :
2009-03-16 17:49:01 +00:00
for action in self . browse ( cr , uid , ids , context ) :
2013-03-29 14:07:23 +00:00
if action . res_model not in self . pool :
2009-03-16 17:49:01 +00:00
return False
2013-03-29 14:07:23 +00:00
if action . src_model and action . src_model not in self . pool :
2009-03-16 17:49:01 +00:00
return False
return True
2010-11-18 16:47:21 +00:00
def _invalid_model_msg ( self , cr , uid , ids , context = None ) :
return _ ( ' Invalid model name in the action definition. ' )
2011-03-29 11:53:09 +00:00
2009-03-16 17:49:01 +00:00
_constraints = [
2010-11-18 16:47:21 +00:00
( _check_model , _invalid_model_msg , [ ' res_model ' , ' src_model ' ] )
2009-03-16 17:49:01 +00:00
]
2008-09-18 14:02:05 +00:00
2010-11-23 13:52:24 +00:00
def _views_get_fnc ( self , cr , uid , ids , name , arg , context = None ) :
2011-07-14 10:38:07 +00:00
""" Returns an ordered list of the specific view modes that should be
2011-04-01 12:48:52 +00:00
enabled when displaying the result of this action , along with the
ID of the specific view to use for each mode , if any were required .
This function hides the logic of determining the precedence between
the view_modes string , the view_ids o2m , and the view_id m2o that can
be set on the action .
: rtype : dict in the form { action_id : list of pairs ( tuples ) }
: return : { action_id : [ ( view_id , view_mode ) , . . . ] , . . . } , where view_mode
is one of the possible values for ir . ui . view . type and view_id
is the ID of a specific view to use for this mode , or False for
the default one .
"""
res = { }
2008-07-22 14:24:36 +00:00
for act in self . browse ( cr , uid , ids ) :
2011-04-01 12:48:52 +00:00
res [ act . id ] = [ ( view . view_id . id , view . view_mode ) for view in act . view_ids ]
view_ids_modes = [ view . view_mode for view in act . view_ids ]
2008-11-17 16:56:01 +00:00
modes = act . view_mode . split ( ' , ' )
2011-04-01 12:48:52 +00:00
missing_modes = [ mode for mode in modes if mode not in view_ids_modes ]
if missing_modes :
if act . view_id and act . view_id . type in missing_modes :
# reorder missing modes to put view_id first if present
missing_modes . remove ( act . view_id . type )
2008-07-22 14:24:36 +00:00
res [ act . id ] . append ( ( act . view_id . id , act . view_id . type ) )
2011-04-01 12:48:52 +00:00
res [ act . id ] . extend ( [ ( False , mode ) for mode in missing_modes ] )
2008-07-22 14:24:36 +00:00
return res
2007-05-15 13:27:39 +00:00
2010-11-23 13:52:24 +00:00
def _search_view ( self , cr , uid , ids , name , arg , context = None ) :
2009-09-17 07:27:12 +00:00
res = { }
2010-11-23 13:52:24 +00:00
for act in self . browse ( cr , uid , ids , context = context ) :
2013-03-19 14:27:01 +00:00
field_get = self . pool [ act . res_model ] . fields_view_get ( cr , uid ,
2012-06-09 12:40:45 +00:00
act . search_view_id and act . search_view_id . id or False ,
' search ' , context = context )
res [ act . id ] = str ( field_get )
2009-09-17 07:27:12 +00:00
return res
2008-07-22 14:24:36 +00:00
_columns = {
2015-01-23 09:00:11 +00:00
' name ' : fields . char ( ' Action Name ' , required = True , translate = True ) ,
2014-05-21 09:52:05 +00:00
' type ' : fields . char ( ' Action Type ' , required = True ) ,
2014-12-02 15:46:29 +00:00
' view_id ' : fields . many2one ( ' ir.ui.view ' , ' View Ref. ' , ondelete = ' set null ' ) ,
2013-02-04 14:58:24 +00:00
' domain ' : fields . char ( ' Domain Value ' ,
2010-07-05 17:57:59 +00:00
help = " Optional domain filtering of the destination data, as a Python expression " ) ,
2013-02-04 14:58:24 +00:00
' context ' : fields . char ( ' Context Value ' , required = True ,
2010-07-05 17:57:59 +00:00
help = " Context dictionary as Python expression, empty by default (Default: {} ) " ) ,
2012-06-27 12:41:36 +00:00
' res_id ' : fields . integer ( ' Record ID ' , help = " Database ID of record to open in form view, when ``view_mode`` is set to ' form ' only " ) ,
2014-05-21 09:52:05 +00:00
' res_model ' : fields . char ( ' Destination Model ' , required = True ,
2010-07-05 17:57:59 +00:00
help = " Model name of the object to open in the view window " ) ,
2014-05-21 09:52:05 +00:00
' src_model ' : fields . char ( ' Source Model ' ,
2010-07-05 17:57:59 +00:00
help = " Optional model name of the objects on which this action should be visible " ) ,
2012-08-11 18:22:50 +00:00
' target ' : fields . selection ( [ ( ' current ' , ' Current Window ' ) , ( ' new ' , ' New Window ' ) , ( ' inline ' , ' Inline Edit ' ) , ( ' inlineview ' , ' Inline View ' ) ] , ' Target Window ' ) ,
2014-05-21 09:52:05 +00:00
' view_mode ' : fields . char ( ' View Mode ' , required = True ,
2010-07-05 17:57:59 +00:00
help = " Comma-separated list of allowed view modes, such as ' form ' , ' tree ' , ' calendar ' , etc. (Default: tree,form) " ) ,
2012-08-11 18:22:50 +00:00
' view_type ' : fields . selection ( ( ( ' tree ' , ' Tree ' ) , ( ' form ' , ' Form ' ) ) , string = ' View Type ' , required = True ,
help = " View type: Tree type to use for the tree view, set to ' tree ' for a hierarchical tree view, or ' form ' for a regular list view " ) ,
2014-05-21 09:52:05 +00:00
' usage ' : fields . char ( ' Action Usage ' ,
2011-11-08 21:55:55 +00:00
help = " Used to filter menu and home actions from the user form. " ) ,
2008-07-22 14:24:36 +00:00
' view_ids ' : fields . one2many ( ' ir.actions.act_window.view ' , ' act_window_id ' , ' Views ' ) ,
2012-01-04 13:30:27 +00:00
' views ' : fields . function ( _views_get_fnc , type = ' binary ' , string = ' Views ' ,
2011-04-01 12:48:52 +00:00
help = " This function field computes the ordered list of views that should be enabled " \
" when displaying the result of an action, federating view mode, views and " \
" reference view. The result is returned as an ordered list of pairs (view_id,view_mode). " ) ,
2008-07-22 14:24:36 +00:00
' limit ' : fields . integer ( ' Limit ' , help = ' Default limit for the list view ' ) ,
' auto_refresh ' : fields . integer ( ' Auto-Refresh ' ,
help = ' Add an auto-refresh on the view ' ) ,
2008-09-18 14:02:05 +00:00
' groups_id ' : fields . many2many ( ' res.groups ' , ' ir_act_window_group_rel ' ,
' act_id ' , ' gid ' , ' Groups ' ) ,
2009-09-17 07:27:12 +00:00
' search_view_id ' : fields . many2one ( ' ir.ui.view ' , ' Search View Ref. ' ) ,
' filter ' : fields . boolean ( ' Filter ' ) ,
2010-02-19 13:46:56 +00:00
' auto_search ' : fields . boolean ( ' Auto Search ' ) ,
2012-01-04 13:30:27 +00:00
' search_view ' : fields . function ( _search_view , type = ' text ' , string = ' Search View ' ) ,
2014-02-20 10:00:36 +00:00
' multi ' : fields . boolean ( ' Restrict to lists ' , help = " If checked and the action is bound to a model, it will only appear in the More menu on list views " ) ,
2008-07-22 14:24:36 +00:00
}
2010-10-15 14:07:29 +00:00
2008-07-22 14:24:36 +00:00
_defaults = {
2012-11-02 09:47:05 +00:00
' type ' : ' ir.actions.act_window ' ,
' view_type ' : ' form ' ,
' view_mode ' : ' tree,form ' ,
' context ' : ' {} ' ,
' limit ' : 80 ,
' target ' : ' current ' ,
' auto_refresh ' : 0 ,
' auto_search ' : True ,
2010-10-15 14:07:29 +00:00
' multi ' : False ,
2008-07-22 14:24:36 +00:00
}
2013-02-04 14:58:24 +00:00
def read ( self , cr , uid , ids , fields = None , context = None , load = ' _classic_read ' ) :
2013-03-22 09:37:59 +00:00
""" call the method get_empty_list_help of the model and set the window action help message
"""
ids_int = isinstance ( ids , ( int , long ) )
if ids_int :
2013-02-05 15:15:48 +00:00
ids = [ ids ]
2013-10-06 10:24:04 +00:00
results = super ( ir_actions_act_window , self ) . read ( cr , uid , ids , fields = fields , context = context , load = load )
2013-02-04 14:58:24 +00:00
2015-01-13 16:43:18 +00:00
if not fields or ' help ' in fields :
for res in results :
model = res . get ( ' res_model ' )
if model and self . pool . get ( model ) :
2015-04-01 08:52:37 +00:00
ctx = dict ( context or { } )
res [ ' help ' ] = self . pool [ model ] . get_empty_list_help ( cr , uid , res . get ( ' help ' , " " ) , context = ctx )
2013-03-22 09:37:59 +00:00
if ids_int :
2013-02-05 15:15:48 +00:00
return results [ 0 ]
2013-02-04 14:58:24 +00:00
return results
2010-11-25 16:57:20 +00:00
def for_xml_id ( self , cr , uid , module , xml_id , context = None ) :
""" Returns the act_window object created for the provided xml_id
: param module : the module the act_window originates in
: param xml_id : the namespace - less id of the action ( the @id
attribute from the XML file )
: return : A read ( ) view of the ir . actions . act_window
"""
dataobj = self . pool . get ( ' ir.model.data ' )
2012-08-31 13:53:09 +00:00
data_id = dataobj . _get_id ( cr , SUPERUSER_ID , module , xml_id )
2010-11-25 16:57:20 +00:00
res_id = dataobj . browse ( cr , uid , data_id , context ) . res_id
2014-07-06 14:44:26 +00:00
return self . read ( cr , uid , [ res_id ] , [ ] , context ) [ 0 ]
2010-11-25 16:57:20 +00:00
2011-12-06 14:12:04 +00:00
VIEW_TYPES = [
( ' tree ' , ' Tree ' ) ,
( ' form ' , ' Form ' ) ,
( ' graph ' , ' Graph ' ) ,
( ' calendar ' , ' Calendar ' ) ,
( ' gantt ' , ' Gantt ' ) ,
( ' kanban ' , ' Kanban ' ) ]
2013-10-06 10:24:04 +00:00
class ir_actions_act_window_view ( osv . osv ) :
2008-07-22 14:24:36 +00:00
_name = ' ir.actions.act_window.view '
_table = ' ir_act_window_view '
_rec_name = ' view_id '
2011-04-01 12:48:52 +00:00
_order = ' sequence '
2008-07-22 14:24:36 +00:00
_columns = {
' sequence ' : fields . integer ( ' Sequence ' ) ,
' view_id ' : fields . many2one ( ' ir.ui.view ' , ' View ' ) ,
2011-12-06 14:12:04 +00:00
' view_mode ' : fields . selection ( VIEW_TYPES , string = ' View Type ' , required = True ) ,
2008-07-22 14:24:36 +00:00
' act_window_id ' : fields . many2one ( ' ir.actions.act_window ' , ' Action ' , ondelete = ' cascade ' ) ,
2009-01-26 17:40:29 +00:00
' 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. " ) ,
2008-07-22 14:24:36 +00:00
}
_defaults = {
2012-11-02 09:47:05 +00:00
' multi ' : False ,
2008-07-22 14:24:36 +00:00
}
2011-04-01 12:48:52 +00:00
def _auto_init ( self , cr , context = None ) :
2016-05-17 11:16:50 +00:00
res = super ( ir_actions_act_window_view , self ) . _auto_init ( cr , context )
2011-04-01 12:48:52 +00:00
cr . execute ( ' SELECT indexname FROM pg_indexes WHERE indexname = \' act_window_view_unique_mode_per_action \' ' )
if not cr . fetchone ( ) :
cr . execute ( ' CREATE UNIQUE INDEX act_window_view_unique_mode_per_action ON ir_act_window_view (act_window_id, view_mode) ' )
2016-05-10 10:13:27 +00:00
return res
2007-05-15 13:27:39 +00:00
2013-10-06 10:24:04 +00:00
class ir_actions_act_window_close ( osv . osv ) :
_name = ' ir.actions.act_window_close '
2009-01-26 15:47:04 +00:00
_inherit = ' ir.actions.actions '
2013-10-06 10:24:04 +00:00
_table = ' ir_actions '
2008-07-22 14:24:36 +00:00
_defaults = {
2013-10-06 10:24:04 +00:00
' type ' : ' ir.actions.act_window_close ' ,
2008-07-22 14:24:36 +00:00
}
2006-12-07 13:41:40 +00:00
2013-10-06 10:24:04 +00:00
class ir_actions_act_url ( osv . osv ) :
2012-06-08 10:17:10 +00:00
_name = ' ir.actions.act_url '
2008-07-22 14:24:36 +00:00
_table = ' ir_act_url '
2012-09-24 14:57:50 +00:00
_inherit = ' ir.actions.actions '
2008-07-22 14:24:36 +00:00
_sequence = ' ir_actions_id_seq '
2010-12-10 22:42:58 +00:00
_order = ' name '
2008-07-22 14:24:36 +00:00
_columns = {
2015-01-23 09:00:11 +00:00
' name ' : fields . char ( ' Action Name ' , required = True , translate = True ) ,
2014-05-21 09:52:05 +00:00
' type ' : fields . char ( ' Action Type ' , required = True ) ,
2009-01-26 17:40:29 +00:00
' url ' : fields . text ( ' Action URL ' , required = True ) ,
2008-07-22 14:24:36 +00:00
' target ' : fields . selection ( (
( ' new ' , ' New Window ' ) ,
( ' self ' , ' This Window ' ) ) ,
' Action Target ' , required = True
)
}
_defaults = {
2012-11-02 09:47:05 +00:00
' type ' : ' ir.actions.act_url ' ,
' target ' : ' new '
2008-07-22 14:24:36 +00:00
}
2013-07-15 15:24:24 +00:00
2008-08-25 10:55:15 +00:00
2013-10-06 10:24:04 +00:00
class ir_actions_server ( osv . osv ) :
2013-07-25 10:49:14 +00:00
""" Server actions model. Server action work on a base model and offer various
type of actions that can be executed automatically , for example using base
action rules , of manually , by adding the action in the ' More ' contextual
menu .
Since OpenERP 8.0 a button ' Create Menu Action ' button is available on the
action form view . It creates an entry in the More menu of the base model .
This allows to create server actions and run them in mass mode easily through
the interface .
The available actions are :
- ' Execute Python Code ' : a block of python code that will be executed
- ' Trigger a Workflow Signal ' : send a signal to a workflow
- ' Run a Client Action ' : choose a client action to launch
- ' Create or Copy a new Record ' : create a new record with new values , or
copy an existing record in your database
- ' Write on a Record ' : update the values of a record
- ' Execute several actions ' : define an action that triggers several other
server actions
"""
2008-07-22 14:24:36 +00:00
_name = ' ir.actions.server '
_table = ' ir_act_server '
2012-09-24 14:57:50 +00:00
_inherit = ' ir.actions.actions '
2008-07-22 14:24:36 +00:00
_sequence = ' ir_actions_id_seq '
2010-12-10 22:42:58 +00:00
_order = ' sequence,name '
2013-07-15 15:24:24 +00:00
def _select_objects ( self , cr , uid , context = None ) :
model_pool = self . pool . get ( ' ir.model ' )
2014-01-07 17:20:10 +00:00
ids = model_pool . search ( cr , uid , [ ] , limit = None )
2013-07-15 15:24:24 +00:00
res = model_pool . read ( cr , uid , ids , [ ' model ' , ' name ' ] )
return [ ( r [ ' model ' ] , r [ ' name ' ] ) for r in res ] + [ ( ' ' , ' ' ) ]
def _get_states ( self , cr , uid , context = None ) :
""" Override me in order to add new states in the server action. Please
note that the added key length should not be higher than already - existing
ones . """
return [ ( ' code ' , ' Execute Python Code ' ) ,
( ' trigger ' , ' Trigger a Workflow Signal ' ) ,
( ' client_action ' , ' Run a Client Action ' ) ,
( ' object_create ' , ' Create or Copy a new Record ' ) ,
( ' object_write ' , ' Write on a Record ' ) ,
( ' multi ' , ' Execute several actions ' ) ]
def _get_states_wrapper ( self , cr , uid , context = None ) :
return self . _get_states ( cr , uid , context )
2008-07-22 14:24:36 +00:00
_columns = {
2014-05-21 09:52:05 +00:00
' name ' : fields . char ( ' Action Name ' , required = True , translate = True ) ,
2013-07-15 15:24:24 +00:00
' condition ' : fields . char ( ' Condition ' ,
help = " Condition verified before executing the server action. If it "
" is not verified, the action will not be executed. The condition is "
" a Python expression, like ' object.list_price > 5000 ' . A void "
2013-07-25 12:22:57 +00:00
" condition is considered as always True. Help about python expression "
2013-07-15 15:24:24 +00:00
" is given in the help tab. " ) ,
' state ' : fields . selection ( _get_states_wrapper , ' Action To Do ' , required = True ,
help = " Type of server action. The following values are available: \n "
" - ' Execute Python Code ' : a block of python code that will be executed \n "
" - ' Trigger a Workflow Signal ' : send a signal to a workflow \n "
" - ' Run a Client Action ' : choose a client action to launch \n "
2013-07-25 10:49:14 +00:00
" - ' Create or Copy a new Record ' : create a new record with new values, or copy an existing record in your database \n "
2013-07-15 15:24:24 +00:00
" - ' Write on a Record ' : update the values of a record \n "
2013-07-25 10:49:14 +00:00
" - ' Execute several actions ' : define an action that triggers several other server actions \n "
2013-07-15 15:24:24 +00:00
" - ' Send Email ' : automatically send an email (available in email_template) " ) ,
2014-05-21 09:52:05 +00:00
' usage ' : fields . char ( ' Action Usage ' ) ,
' type ' : fields . char ( ' Action Type ' , required = True ) ,
2013-07-15 15:24:24 +00:00
# Generic
' sequence ' : fields . integer ( ' Sequence ' ,
help = " When dealing with multiple actions, the execution order is "
" based on the sequence. Low number means high priority. " ) ,
' model_id ' : fields . many2one ( ' ir.model ' , ' Base Model ' , required = True , ondelete = ' cascade ' ,
help = " Base model on which the server action runs. " ) ,
2014-06-17 13:17:14 +00:00
' model_name ' : fields . related ( ' model_id ' , ' model ' , type = ' char ' ,
string = ' Model Name ' , readonly = True ) ,
2013-07-15 15:24:24 +00:00
' menu_ir_values_id ' : fields . many2one ( ' ir.values ' , ' More Menu entry ' , readonly = True ,
2014-07-30 16:56:56 +00:00
help = ' More menu entry. ' , copy = False ) ,
2013-07-15 15:24:24 +00:00
# Client Action
' action_id ' : fields . many2one ( ' ir.actions.actions ' , ' Client Action ' ,
help = " Select the client action that has to be executed. " ) ,
# Python code
' code ' : fields . text ( ' Python Code ' ,
help = " Write Python code that the action will execute. Some variables are "
" available for use; help about pyhon expression is given in the help tab. " ) ,
# Workflow signal
' use_relational_model ' : fields . selection ( [ ( ' base ' , ' Use the base model of the action ' ) ,
( ' relational ' , ' Use a relation field on the base model ' ) ] ,
string = ' Target Model ' , required = True ) ,
' wkf_transition_id ' : fields . many2one ( ' workflow.transition ' , string = ' Signal to Trigger ' ,
help = " Select the workflow signal to trigger. " ) ,
' wkf_model_id ' : fields . many2one ( ' ir.model ' , ' Target Model ' ,
help = " The model that will receive the workflow signal. Note that it should have a workflow associated with it. " ) ,
' wkf_model_name ' : fields . related ( ' wkf_model_id ' , ' model ' , type = ' char ' , string = ' Target Model Name ' , store = True , readonly = True ) ,
' wkf_field_id ' : fields . many2one ( ' ir.model.fields ' , string = ' Relation Field ' ,
oldname = ' trigger_obj_id ' ,
help = " The field on the current object that links to the target object record (must be a many2one, or an integer field with the record ID) " ) ,
# Multi
' child_ids ' : fields . many2many ( ' ir.actions.server ' , ' rel_server_actions ' ,
' server_id ' , ' action_id ' ,
string = ' Child Actions ' ,
help = ' Child server actions that will be executed. Note that the last return returned action value will be used as global return value. ' ) ,
# Create/Copy/Write
' use_create ' : fields . selection ( [ ( ' new ' , ' Create a new record in the Base Model ' ) ,
( ' new_other ' , ' Create a new record in another model ' ) ,
( ' copy_current ' , ' Copy the current record ' ) ,
2013-07-17 12:09:36 +00:00
( ' copy_other ' , ' Choose and copy a record in the database ' ) ] ,
2013-07-15 15:24:24 +00:00
string = " Creation Policy " , required = True ,
help = " " ) ,
' crud_model_id ' : fields . many2one ( ' ir.model ' , ' Target Model ' ,
oldname = ' srcmodel_id ' ,
help = " Model for record creation / update. Set this field only to specify a different model than the base model. " ) ,
2013-07-17 12:09:36 +00:00
' crud_model_name ' : fields . related ( ' crud_model_id ' , ' model ' , type = ' char ' ,
string = ' Create/Write Target Model Name ' ,
store = True , readonly = True ) ,
2013-07-15 15:24:24 +00:00
' ref_object ' : fields . reference ( ' Reference record ' , selection = _select_objects , size = 128 ,
oldname = ' copy_object ' ) ,
2013-07-25 12:22:57 +00:00
' link_new_record ' : fields . boolean ( ' Attach the new record ' ,
2013-07-15 15:24:24 +00:00
help = " Check this if you want to link the newly-created record "
" to the current record on which the server action runs. " ) ,
2013-07-25 12:22:57 +00:00
' link_field_id ' : fields . many2one ( ' ir.model.fields ' , ' Link using field ' ,
2013-07-15 15:24:24 +00:00
oldname = ' record_id ' ,
help = " Provide the field where the record id is stored after the operations. " ) ,
' use_write ' : fields . selection ( [ ( ' current ' , ' Update the current record ' ) ,
2013-07-17 12:09:36 +00:00
( ' expression ' , ' Update a record linked to the current record using python ' ) ,
( ' other ' , ' Choose and Update a record in the database ' ) ] ,
2013-07-15 15:24:24 +00:00
string = ' Update Policy ' , required = True ,
help = " " ) ,
2013-07-17 12:09:36 +00:00
' write_expression ' : fields . char ( ' Expression ' ,
2013-07-15 15:24:24 +00:00
oldname = ' write_id ' ,
2013-07-17 12:09:36 +00:00
help = " Provide an expression that, applied on the current record, gives the field to update. " ) ,
2013-07-15 15:24:24 +00:00
' fields_lines ' : fields . one2many ( ' ir.server.object.lines ' , ' server_id ' ,
string = ' Value Mapping ' ,
2014-07-06 14:44:26 +00:00
copy = True ) ,
2013-07-15 15:24:24 +00:00
# Fake fields used to implement the placeholder assistant
' model_object_field ' : fields . many2one ( ' ir.model.fields ' , string = " Field " ,
help = " Select target field from the related document model. \n "
" If it is a relationship field you will be able to select "
" a target field at the destination of the relationship. " ) ,
' sub_object ' : fields . many2one ( ' ir.model ' , ' Sub-model ' , readonly = True ,
help = " When a relationship field is selected as first field, "
" this field shows the document model the relationship goes to. " ) ,
' sub_model_object_field ' : fields . many2one ( ' ir.model.fields ' , ' Sub-field ' ,
help = " When a relationship field is selected as first field, "
" this field lets you select the target field within the "
" destination document model (sub-model). " ) ,
' copyvalue ' : fields . char ( ' Placeholder Expression ' , help = " Final placeholder expression, to be copy-pasted in the desired template field. " ) ,
2013-07-25 12:22:57 +00:00
# Fake fields used to implement the ID finding assistant
' id_object ' : fields . reference ( ' Record ' , selection = _select_objects , size = 128 ) ,
' id_value ' : fields . char ( ' Record ID ' ) ,
2008-07-22 14:24:36 +00:00
}
2013-07-15 15:24:24 +00:00
2008-07-22 14:24:36 +00:00
_defaults = {
2013-07-15 15:24:24 +00:00
' state ' : ' code ' ,
2012-11-02 09:47:05 +00:00
' condition ' : ' True ' ,
' type ' : ' ir.actions.server ' ,
' sequence ' : 5 ,
2015-01-28 22:06:30 +00:00
' code ' : """ # Available locals:
# - time, datetime, dateutil: Python libraries
# - env: Odoo Environement
# - model: Model of the record on which the action is triggered
2014-07-06 14:44:26 +00:00
# - object: Record on which the action is triggered if there is one, otherwise None
2014-02-21 11:53:47 +00:00
# - workflow: Workflow engine
2015-01-28 22:06:30 +00:00
# - Warning: Warning Exception to use with raise
# To return an action, assign: action = {...}""",
2013-07-15 15:24:24 +00:00
' use_relational_model ' : ' base ' ,
' use_create ' : ' new ' ,
' use_write ' : ' current ' ,
2008-07-22 14:24:36 +00:00
}
2008-09-18 14:02:05 +00:00
2013-07-15 15:24:24 +00:00
def _check_expression ( self , cr , uid , expression , model_id , context ) :
2013-07-26 08:41:38 +00:00
""" Check python expression (condition, write_expression). Each step of
the path must be a valid many2one field , or an integer field for the last
step .
: param str expression : a python expression , beginning by ' obj ' or ' object '
: param int model_id : the base model of the server action
: returns tuple : ( is_valid , target_model_name , error_msg )
"""
2013-07-15 15:24:24 +00:00
if not model_id :
return ( False , None , ' Your expression cannot be validated because the Base Model is not set. ' )
# fetch current model
current_model_name = self . pool . get ( ' ir.model ' ) . browse ( cr , uid , model_id , context ) . model
# transform expression into a path that should look like 'object.many2onefield.many2onefield'
path = expression . split ( ' . ' )
initial = path . pop ( 0 )
if initial not in [ ' obj ' , ' object ' ] :
return ( False , None , ' Your expression should begin with obj or object. \n An expression builder is available in the help tab. ' )
# analyze path
while path :
step = path . pop ( 0 )
[IMP] use model._fields instead of model._all_columns to cover all fields
The old-api model._all_columns contains information about model._columns and
inherited columns. This dictionary is missing new-api computed non-stored
fields, and the new field objects provide a more readable api...
This commit contains the following changes:
- adapt several methods of BaseModel to use fields instead of columns and
_all_columns
- copy all semantic-free attributes of related fields from their source
- add attribute 'group_operator' on integer and float fields
- base, base_action_rule, crm, edi, hr, mail, mass_mailing, pad,
payment_acquirer, share, website, website_crm, website_mail: simply use
_fields instead of _all_columns
- base, decimal_precision, website: adapt qweb rendering methods to use fields
instead of columns
2014-11-03 15:00:50 +00:00
field = self . pool [ current_model_name ] . _fields . get ( step )
if not field :
2013-07-15 15:24:24 +00:00
return ( False , None , ' Part of the expression ( %s ) is not recognized as a column in the model %s . ' % ( step , current_model_name ) )
[IMP] use model._fields instead of model._all_columns to cover all fields
The old-api model._all_columns contains information about model._columns and
inherited columns. This dictionary is missing new-api computed non-stored
fields, and the new field objects provide a more readable api...
This commit contains the following changes:
- adapt several methods of BaseModel to use fields instead of columns and
_all_columns
- copy all semantic-free attributes of related fields from their source
- add attribute 'group_operator' on integer and float fields
- base, base_action_rule, crm, edi, hr, mail, mass_mailing, pad,
payment_acquirer, share, website, website_crm, website_mail: simply use
_fields instead of _all_columns
- base, decimal_precision, website: adapt qweb rendering methods to use fields
instead of columns
2014-11-03 15:00:50 +00:00
ftype = field . type
if ftype not in [ ' many2one ' , ' int ' ] :
return ( False , None , ' Part of the expression ( %s ) is not a valid column type (is %s , should be a many2one or an int) ' % ( step , ftype ) )
if ftype == ' int ' and path :
2013-07-25 12:22:57 +00:00
return ( False , None , ' Part of the expression ( %s ) is an integer field that is only allowed at the end of an expression ' % ( step ) )
[IMP] use model._fields instead of model._all_columns to cover all fields
The old-api model._all_columns contains information about model._columns and
inherited columns. This dictionary is missing new-api computed non-stored
fields, and the new field objects provide a more readable api...
This commit contains the following changes:
- adapt several methods of BaseModel to use fields instead of columns and
_all_columns
- copy all semantic-free attributes of related fields from their source
- add attribute 'group_operator' on integer and float fields
- base, base_action_rule, crm, edi, hr, mail, mass_mailing, pad,
payment_acquirer, share, website, website_crm, website_mail: simply use
_fields instead of _all_columns
- base, decimal_precision, website: adapt qweb rendering methods to use fields
instead of columns
2014-11-03 15:00:50 +00:00
if ftype == ' many2one ' :
current_model_name = field . comodel_name
2013-07-15 15:24:24 +00:00
return ( True , current_model_name , None )
def _check_write_expression ( self , cr , uid , ids , context = None ) :
for record in self . browse ( cr , uid , ids , context = context ) :
if record . write_expression and record . model_id :
correct , model_name , message = self . _check_expression ( cr , uid , record . write_expression , record . model_id . id , context = context )
if not correct :
_logger . warning ( ' Invalid expression: %s ' % message )
return False
return True
2008-09-18 14:02:05 +00:00
2013-07-15 15:24:24 +00:00
_constraints = [
( _check_write_expression ,
' Incorrect Write Record Expression ' ,
[ ' write_expression ' ] ) ,
2013-07-25 10:49:14 +00:00
( partial ( osv . Model . _check_m2m_recursion , field_name = ' child_ids ' ) ,
' Recursion found in child server actions ' ,
[ ' child_ids ' ] ) ,
2013-07-15 15:24:24 +00:00
]
2008-09-18 14:02:05 +00:00
2013-07-15 15:24:24 +00:00
def on_change_model_id ( self , cr , uid , ids , model_id , wkf_model_id , crud_model_id , context = None ) :
""" When changing the action base model, reset workflow and crud config
2013-07-26 08:41:38 +00:00
to ease value coherence . """
2013-07-15 15:24:24 +00:00
values = {
' use_create ' : ' new ' ,
' use_write ' : ' current ' ,
' use_relational_model ' : ' base ' ,
' wkf_model_id ' : model_id ,
2013-07-17 12:09:36 +00:00
' wkf_field_id ' : False ,
2013-07-15 15:24:24 +00:00
' crud_model_id ' : model_id ,
}
2014-06-17 13:17:14 +00:00
if model_id :
values [ ' model_name ' ] = self . pool . get ( ' ir.model ' ) . browse ( cr , uid , model_id , context ) . model
2013-07-15 15:24:24 +00:00
return { ' value ' : values }
def on_change_wkf_wonfig ( self , cr , uid , ids , use_relational_model , wkf_field_id , wkf_model_id , model_id , context = None ) :
2013-07-26 08:41:38 +00:00
""" Update workflow type configuration
- update the workflow model ( for base ( model_id ) / relational ( field . relation ) )
- update wkf_transition_id to False if workflow model changes , to force
the user to choose a new one
2013-07-15 15:24:24 +00:00
"""
values = { }
if use_relational_model == ' relational ' and wkf_field_id :
field = self . pool [ ' ir.model.fields ' ] . browse ( cr , uid , wkf_field_id , context = context )
new_wkf_model_id = self . pool . get ( ' ir.model ' ) . search ( cr , uid , [ ( ' model ' , ' = ' , field . relation ) ] , context = context ) [ 0 ]
values [ ' wkf_model_id ' ] = new_wkf_model_id
else :
values [ ' wkf_model_id ' ] = model_id
return { ' value ' : values }
def on_change_wkf_model_id ( self , cr , uid , ids , wkf_model_id , context = None ) :
""" When changing the workflow model, update its stored name also """
wkf_model_name = False
if wkf_model_id :
wkf_model_name = self . pool . get ( ' ir.model ' ) . browse ( cr , uid , wkf_model_id , context ) . model
2013-07-17 12:09:36 +00:00
values = { ' wkf_transition_id ' : False , ' wkf_model_name ' : wkf_model_name }
return { ' value ' : values }
2013-07-15 15:24:24 +00:00
def on_change_crud_config ( self , cr , uid , ids , state , use_create , use_write , ref_object , crud_model_id , model_id , context = None ) :
2013-07-26 08:41:38 +00:00
""" Wrapper on CRUD-type (create or write) on_change """
2013-07-15 15:24:24 +00:00
if state == ' object_create ' :
return self . on_change_create_config ( cr , uid , ids , use_create , ref_object , crud_model_id , model_id , context = context )
elif state == ' object_write ' :
return self . on_change_write_config ( cr , uid , ids , use_write , ref_object , crud_model_id , model_id , context = context )
else :
return { }
2008-12-11 11:32:08 +00:00
2013-07-15 15:24:24 +00:00
def on_change_create_config ( self , cr , uid , ids , use_create , ref_object , crud_model_id , model_id , context = None ) :
2013-07-26 08:41:38 +00:00
""" When changing the object_create type configuration:
- ` new ` and ` copy_current ` : crud_model_id is the same as base model
- ` new_other ` : user choose crud_model_id
- ` copy_other ` : disassemble the reference object to have its model
- if the target model has changed , then reset the link field that is
probably not correct anymore
"""
2013-07-15 15:24:24 +00:00
values = { }
if use_create == ' new ' :
values [ ' crud_model_id ' ] = model_id
elif use_create == ' new_other ' :
pass
elif use_create == ' copy_current ' :
values [ ' crud_model_id ' ] = model_id
elif use_create == ' copy_other ' and ref_object :
ref_model , ref_id = ref_object . split ( ' , ' )
ref_model_id = self . pool [ ' ir.model ' ] . search ( cr , uid , [ ( ' model ' , ' = ' , ref_model ) ] , context = context ) [ 0 ]
values [ ' crud_model_id ' ] = ref_model_id
if values . get ( ' crud_model_id ' ) != crud_model_id :
values [ ' link_field_id ' ] = False
return { ' value ' : values }
def on_change_write_config ( self , cr , uid , ids , use_write , ref_object , crud_model_id , model_id , context = None ) :
2013-07-26 08:41:38 +00:00
""" When changing the object_write type configuration:
- ` current ` : crud_model_id is the same as base model
- ` other ` : disassemble the reference object to have its model
- ` expression ` : has its own on_change , nothing special here
"""
2013-07-15 15:24:24 +00:00
values = { }
if use_write == ' current ' :
values [ ' crud_model_id ' ] = model_id
elif use_write == ' other ' and ref_object :
ref_model , ref_id = ref_object . split ( ' , ' )
ref_model_id = self . pool [ ' ir.model ' ] . search ( cr , uid , [ ( ' model ' , ' = ' , ref_model ) ] , context = context ) [ 0 ]
values [ ' crud_model_id ' ] = ref_model_id
elif use_write == ' expression ' :
pass
if values . get ( ' crud_model_id ' ) != crud_model_id :
values [ ' link_field_id ' ] = False
return { ' value ' : values }
def on_change_write_expression ( self , cr , uid , ids , write_expression , model_id , context = None ) :
2013-07-26 08:41:38 +00:00
""" Check the write_expression and update crud_model_id accordingly """
2013-07-15 15:24:24 +00:00
values = { }
2014-08-21 08:02:39 +00:00
if write_expression :
valid , model_name , message = self . _check_expression ( cr , uid , write_expression , model_id , context = context )
else :
valid , model_name , message = True , None , False
if model_id :
model_name = self . pool [ ' ir.model ' ] . browse ( cr , uid , model_id , context ) . model
if not valid :
return {
' warning ' : {
' title ' : ' Incorrect expression ' ,
' message ' : message or ' Invalid expression ' ,
}
}
if model_name :
2013-07-15 15:24:24 +00:00
ref_model_id = self . pool [ ' ir.model ' ] . search ( cr , uid , [ ( ' model ' , ' = ' , model_name ) ] , context = context ) [ 0 ]
values [ ' crud_model_id ' ] = ref_model_id
return { ' value ' : values }
2014-08-21 08:02:39 +00:00
return { ' value ' : { } }
2008-12-11 11:32:08 +00:00
2013-07-17 12:09:36 +00:00
def on_change_crud_model_id ( self , cr , uid , ids , crud_model_id , context = None ) :
""" When changing the CRUD model, update its stored name also """
crud_model_name = False
if crud_model_id :
crud_model_name = self . pool . get ( ' ir.model ' ) . browse ( cr , uid , crud_model_id , context ) . model
values = { ' link_field_id ' : False , ' crud_model_name ' : crud_model_name }
return { ' value ' : values }
2013-07-25 12:22:57 +00:00
def _build_expression ( self , field_name , sub_field_name ) :
2013-07-26 08:41:38 +00:00
""" Returns a placeholder expression for use in a template field,
based on the values provided in the placeholder assistant .
2013-07-15 15:24:24 +00:00
2013-07-26 08:41:38 +00:00
: param field_name : main field name
: param sub_field_name : sub field name ( M2O )
: return : final placeholder expression
2013-07-15 15:24:24 +00:00
"""
expression = ' '
if field_name :
expression = " object. " + field_name
if sub_field_name :
expression + = " . " + sub_field_name
return expression
def onchange_sub_model_object_value_field ( self , cr , uid , ids , model_object_field , sub_model_object_field = False , context = None ) :
result = {
' sub_object ' : False ,
' copyvalue ' : False ,
' sub_model_object_field ' : False ,
}
if model_object_field :
fields_obj = self . pool . get ( ' ir.model.fields ' )
field_value = fields_obj . browse ( cr , uid , model_object_field , context )
if field_value . ttype in [ ' many2one ' , ' one2many ' , ' many2many ' ] :
res_ids = self . pool . get ( ' ir.model ' ) . search ( cr , uid , [ ( ' model ' , ' = ' , field_value . relation ) ] , context = context )
sub_field_value = False
if sub_model_object_field :
sub_field_value = fields_obj . browse ( cr , uid , sub_model_object_field , context )
if res_ids :
result . update ( {
' sub_object ' : res_ids [ 0 ] ,
2013-07-25 12:22:57 +00:00
' copyvalue ' : self . _build_expression ( field_value . name , sub_field_value and sub_field_value . name or False ) ,
2013-07-15 15:24:24 +00:00
' sub_model_object_field ' : sub_model_object_field or False ,
} )
else :
result . update ( {
2013-07-25 12:22:57 +00:00
' copyvalue ' : self . _build_expression ( field_value . name , False ) ,
2013-07-15 15:24:24 +00:00
} )
return { ' value ' : result }
2013-07-25 12:22:57 +00:00
def onchange_id_object ( self , cr , uid , ids , id_object , context = None ) :
if id_object :
ref_model , ref_id = id_object . split ( ' , ' )
return { ' value ' : { ' id_value ' : ref_id } }
return { ' value ' : { ' id_value ' : False } }
2013-07-15 15:24:24 +00:00
def create_action ( self , cr , uid , ids , context = None ) :
""" Create a contextual action for each of the server actions. """
for action in self . browse ( cr , uid , ids , context = context ) :
ir_values_id = self . pool . get ( ' ir.values ' ) . create ( cr , SUPERUSER_ID , {
' name ' : _ ( ' Run %s ' ) % action . name ,
' model ' : action . model_id . model ,
' key2 ' : ' client_action_multi ' ,
' value ' : " ir.actions.server, %s " % action . id ,
} , context )
action . write ( {
' menu_ir_values_id ' : ir_values_id ,
} )
return True
def unlink_action ( self , cr , uid , ids , context = None ) :
""" Remove the contextual actions created for the server actions. """
for action in self . browse ( cr , uid , ids , context = context ) :
if action . menu_ir_values_id :
try :
self . pool . get ( ' ir.values ' ) . unlink ( cr , SUPERUSER_ID , action . menu_ir_values_id . id , context )
except Exception :
raise osv . except_osv ( _ ( ' Warning ' ) , _ ( ' Deletion of the action record failed. ' ) )
return True
2008-12-11 11:32:08 +00:00
2013-07-15 15:24:24 +00:00
def run_action_client_action ( self , cr , uid , action , eval_context = None , context = None ) :
if not action . action_id :
raise osv . except_osv ( _ ( ' Error ' ) , _ ( " Please specify an action to launch! " ) )
2014-07-06 14:44:26 +00:00
return self . pool [ action . action_id . type ] . read ( cr , uid , [ action . action_id . id ] , context = context ) [ 0 ]
2013-07-15 15:24:24 +00:00
2013-11-12 16:27:26 +00:00
def run_action_code_multi ( self , cr , uid , action , eval_context = None , context = None ) :
2013-07-15 15:24:24 +00:00
eval ( action . code . strip ( ) , eval_context , mode = " exec " , nocopy = True ) # nocopy allows to return 'action'
if ' action ' in eval_context :
return eval_context [ ' action ' ]
def run_action_trigger ( self , cr , uid , action , eval_context = None , context = None ) :
""" Trigger a workflow signal, depending on the use_relational_model:
2013-07-26 08:41:38 +00:00
2014-07-07 09:50:30 +00:00
- ` base ` : base_model_pool . signal_workflow ( cr , uid , context . get ( ' active_id ' ) , < TRIGGER_NAME > )
2013-07-26 08:41:38 +00:00
- ` relational ` : find the related model and object , using the relational
2014-07-07 09:50:30 +00:00
field , then target_model_pool . signal_workflow ( cr , uid , target_id , < TRIGGER_NAME > )
2013-07-15 15:24:24 +00:00
"""
2014-10-08 11:09:13 +00:00
# weird signature and calling -> no self.env, use action param's
record = action . env [ action . model_id . model ] . browse ( context [ ' active_id ' ] )
if action . use_relational_model == ' relational ' :
record = getattr ( record , action . wkf_field_id . name )
if not isinstance ( record , openerp . models . BaseModel ) :
record = action . env [ action . wkf_model_id . model ] . browse ( record )
record . signal_workflow ( action . wkf_transition_id . signal )
2008-09-18 14:02:05 +00:00
2013-07-15 15:24:24 +00:00
def run_action_multi ( self , cr , uid , action , eval_context = None , context = None ) :
2014-10-08 11:07:32 +00:00
res = False
2013-07-15 15:24:24 +00:00
for act in action . child_ids :
2014-10-08 11:07:32 +00:00
result = self . run ( cr , uid , [ act . id ] , context = context )
2013-07-15 15:24:24 +00:00
if result :
2014-10-08 11:07:32 +00:00
res = result
return res
2013-07-15 15:24:24 +00:00
def run_action_object_write ( self , cr , uid , action , eval_context = None , context = None ) :
2013-07-26 08:41:38 +00:00
""" Write server action.
- 1. evaluate the value mapping
- 2. depending on the write configuration :
- ` current ` : id = active_id
- ` other ` : id = from reference object
- ` expression ` : id = from expression evaluation
"""
2013-07-15 15:24:24 +00:00
res = { }
for exp in action . fields_lines :
2014-08-21 08:32:29 +00:00
res [ exp . col1 . name ] = exp . eval_value ( eval_context = eval_context ) [ exp . id ]
2013-07-15 15:24:24 +00:00
if action . use_write == ' current ' :
model = action . model_id . model
ref_id = context . get ( ' active_id ' )
elif action . use_write == ' other ' :
model = action . crud_model_id . model
ref_id = action . ref_object . id
elif action . use_write == ' expression ' :
model = action . crud_model_id . model
ref = eval ( action . write_expression , eval_context )
if isinstance ( ref , browse_record ) :
ref_id = getattr ( ref , ' id ' )
else :
ref_id = int ( ref )
2008-09-18 14:02:05 +00:00
2013-07-15 15:24:24 +00:00
obj_pool = self . pool [ model ]
obj_pool . write ( cr , uid , [ ref_id ] , res , context = context )
2008-08-14 06:00:47 +00:00
2013-07-15 15:24:24 +00:00
def run_action_object_create ( self , cr , uid , action , eval_context = None , context = None ) :
2013-07-26 08:41:38 +00:00
""" Create and Copy server action.
- 1. evaluate the value mapping
- 2. depending on the write configuration :
- ` new ` : new record in the base model
- ` copy_current ` : copy the current record ( id = active_id ) + gives custom values
- ` new_other ` : new record in target model
- ` copy_other ` : copy the current record ( id from reference object )
+ gives custom values
"""
2013-07-15 15:24:24 +00:00
res = { }
for exp in action . fields_lines :
2014-08-21 08:32:29 +00:00
res [ exp . col1 . name ] = exp . eval_value ( eval_context = eval_context ) [ exp . id ]
2013-07-15 15:24:24 +00:00
if action . use_create in [ ' new ' , ' copy_current ' ] :
model = action . model_id . model
elif action . use_create in [ ' new_other ' , ' copy_other ' ] :
model = action . crud_model_id . model
obj_pool = self . pool [ model ]
if action . use_create == ' copy_current ' :
ref_id = context . get ( ' active_id ' )
res_id = obj_pool . copy ( cr , uid , ref_id , res , context = context )
elif action . use_create == ' copy_other ' :
ref_id = action . ref_object . id
res_id = obj_pool . copy ( cr , uid , ref_id , res , context = context )
else :
res_id = obj_pool . create ( cr , uid , res , context = context )
if action . link_new_record and action . link_field_id :
self . pool [ action . model_id . model ] . write ( cr , uid , [ context . get ( ' active_id ' ) ] , { action . link_field_id . name : res_id } )
2011-08-27 21:36:28 +00:00
2014-02-05 08:45:37 +00:00
def _get_eval_context ( self , cr , uid , action , context = None ) :
2014-02-05 09:08:17 +00:00
""" Prepare the context used when evaluating python code, like the
2014-02-05 08:45:37 +00:00
condition or code server actions .
: param action : the current server action
: type action : browse record
: returns : dict - - evaluation context given to ( safe_ ) eval """
obj_pool = self . pool [ action . model_id . model ]
2015-01-28 22:06:30 +00:00
env = openerp . api . Environment ( cr , uid , context )
model = env [ action . model_id . model ]
2014-02-05 08:45:37 +00:00
obj = None
if context . get ( ' active_model ' ) == action . model_id . model and context . get ( ' active_id ' ) :
2015-01-28 22:06:30 +00:00
obj = model . browse ( context [ ' active_id ' ] )
2014-02-05 08:45:37 +00:00
return {
2015-01-28 22:06:30 +00:00
# python libs
2014-02-05 08:45:37 +00:00
' time ' : time ,
2014-02-10 14:09:33 +00:00
' datetime ' : datetime ,
' dateutil ' : dateutil ,
2015-11-12 11:23:27 +00:00
# NOTE: only `timezone` function. Do not provide the whole `pytz` module as users
# will have access to `pytz.os` and `pytz.sys` to do nasty things...
' timezone ' : pytz . timezone ,
2015-01-28 22:06:30 +00:00
# orm
' env ' : env ,
' model ' : model ,
' workflow ' : workflow ,
# Exceptions
' Warning ' : openerp . exceptions . Warning ,
# record
# TODO: When porting to master move badly named obj and object to
# deprecated and define record (active_id) and records (active_ids)
' object ' : obj ,
' obj ' : obj ,
# Deprecated use env or model instead
' self ' : obj_pool ,
' pool ' : self . pool ,
2014-02-05 08:45:37 +00:00
' cr ' : cr ,
' uid ' : uid ,
2014-02-10 14:09:33 +00:00
' context ' : context ,
2015-01-28 22:06:30 +00:00
' user ' : env . user ,
2014-02-05 08:45:37 +00:00
}
2010-09-21 13:14:37 +00:00
def run ( self , cr , uid , ids , context = None ) :
2014-01-16 14:49:57 +00:00
""" Runs the server action. For each server action, the condition is
checked . Note that a void ( ` ` False ` ` ) condition is considered as always
2013-07-25 10:49:14 +00:00
valid . If it is verified , the run_action_ < STATE > method is called . This
2014-01-16 14:49:57 +00:00
allows easy overriding of the server actions .
2013-07-15 15:24:24 +00:00
2013-07-25 10:49:14 +00:00
: param dict context : context should contain following keys
2013-07-15 15:24:24 +00:00
2013-07-25 10:49:14 +00:00
- active_id : id of the current object ( single mode )
- active_model : current model that should equal the action ' s model
2013-07-15 15:24:24 +00:00
2013-07-25 10:49:14 +00:00
The following keys are optional :
2013-07-15 15:24:24 +00:00
2013-07-25 10:49:14 +00:00
- active_ids : ids of the current records ( mass mode ) . If active_ids
and active_id are present , active_ids is given precedence .
: return : an action_id to be executed , or False is finished correctly without
return action
2013-07-15 15:24:24 +00:00
"""
2010-09-21 13:14:37 +00:00
if context is None :
context = { }
2013-07-15 15:24:24 +00:00
res = False
2008-07-22 14:24:36 +00:00
for action in self . browse ( cr , uid , ids , context ) :
2014-02-05 08:45:37 +00:00
eval_context = self . _get_eval_context ( cr , uid , action , context = context )
2013-11-12 16:27:26 +00:00
condition = action . condition
if condition is False :
2013-11-12 18:04:23 +00:00
# Void (aka False) conditions are considered as True
2013-11-12 16:27:26 +00:00
condition = True
if hasattr ( self , ' run_action_ %s _multi ' % action . state ) :
2014-02-10 14:09:33 +00:00
run_context = eval_context [ ' context ' ]
2013-07-15 16:03:25 +00:00
expr = eval ( str ( condition ) , eval_context )
if not expr :
continue
2013-11-12 16:27:26 +00:00
# call the multi method
2013-11-12 18:04:23 +00:00
func = getattr ( self , ' run_action_ %s _multi ' % action . state )
res = func ( cr , uid , action , eval_context = eval_context , context = run_context )
2013-11-12 16:27:26 +00:00
elif hasattr ( self , ' run_action_ %s ' % action . state ) :
2013-11-12 18:04:23 +00:00
func = getattr ( self , ' run_action_ %s ' % action . state )
2014-02-10 14:09:33 +00:00
active_id = context . get ( ' active_id ' )
active_ids = context . get ( ' active_ids ' , [ active_id ] if active_id else [ ] )
2013-11-12 16:27:26 +00:00
for active_id in active_ids :
# run context dedicated to a particular active_id
run_context = dict ( context , active_ids = [ active_id ] , active_id = active_id )
eval_context [ " context " ] = run_context
expr = eval ( str ( condition ) , eval_context )
if not expr :
continue
# call the single method related to the action: run_action_<STATE>
2013-11-12 18:04:23 +00:00
res = func ( cr , uid , action , eval_context = eval_context , context = run_context )
2013-07-15 15:24:24 +00:00
return res
2009-10-30 12:33:59 +00:00
2008-06-15 00:25:47 +00:00
2013-10-06 10:24:04 +00:00
class ir_server_object_lines ( osv . osv ) :
_name = ' ir.server.object.lines '
2014-08-21 08:32:29 +00:00
_description = ' Server Action value mapping '
2013-10-06 10:24:04 +00:00
_sequence = ' ir_actions_id_seq '
2014-08-21 08:32:29 +00:00
2013-10-06 10:24:04 +00:00
_columns = {
2015-03-12 17:29:00 +00:00
' server_id ' : fields . many2one ( ' ir.actions.server ' , ' Related Server Action ' , ondelete = ' cascade ' ) ,
2013-10-06 10:24:04 +00:00
' col1 ' : fields . many2one ( ' ir.model.fields ' , ' Field ' , required = True ) ,
' value ' : fields . text ( ' Value ' , required = True , help = " Expression containing a value specification. \n "
" When Formula type is selected, this field may be a Python expression "
" that can use the same values as for the condition field on the server action. \n "
" If Value type is selected, the value will be used directly without evaluation. " ) ,
' type ' : fields . selection ( [
( ' value ' , ' Value ' ) ,
( ' equation ' , ' Python expression ' )
] , ' Evaluation Type ' , required = True , change_default = True ) ,
}
2014-08-21 08:32:29 +00:00
2008-07-22 14:24:36 +00:00
_defaults = {
2013-10-06 10:24:04 +00:00
' type ' : ' value ' ,
2008-07-22 14:24:36 +00:00
}
2008-09-29 08:49:06 +00:00
2014-08-21 08:32:29 +00:00
def eval_value ( self , cr , uid , ids , eval_context = None , context = None ) :
res = dict . fromkeys ( ids , False )
for line in self . browse ( cr , uid , ids , context = context ) :
expr = line . value
if line . type == ' equation ' :
expr = eval ( line . value , eval_context )
elif line . col1 . ttype in [ ' many2one ' , ' integer ' ] :
try :
expr = int ( line . value )
except Exception :
pass
res [ line . id ] = expr
return res
2013-10-06 10:24:04 +00:00
2010-04-18 20:07:32 +00:00
TODO_STATES = [ ( ' open ' , ' To Do ' ) ,
2011-07-29 14:26:22 +00:00
( ' done ' , ' Done ' ) ]
2011-10-02 11:51:31 +00:00
TODO_TYPES = [ ( ' manual ' , ' Launch Manually ' ) , ( ' once ' , ' Launch Manually Once ' ) ,
2011-07-29 14:26:22 +00:00
( ' automatic ' , ' Launch Automatically ' ) ]
2008-09-29 08:49:06 +00:00
class ir_actions_todo ( osv . osv ) :
2011-07-01 13:08:40 +00:00
"""
Configuration Wizards
"""
2009-10-30 12:33:59 +00:00
_name = ' ir.actions.todo '
2011-07-01 13:08:40 +00:00
_description = " Configuration Wizards "
2008-09-29 08:49:06 +00:00
_columns = {
2009-12-07 10:17:40 +00:00
' action_id ' : fields . many2one (
2012-08-04 21:53:00 +00:00
' ir.actions.actions ' , ' Action ' , select = True , required = True ) ,
2009-12-07 10:17:40 +00:00
' sequence ' : fields . integer ( ' Sequence ' ) ,
2012-10-12 12:24:19 +00:00
' state ' : fields . selection ( TODO_STATES , string = ' Status ' , required = True ) ,
2014-05-21 09:52:05 +00:00
' name ' : fields . char ( ' Name ' ) ,
2011-07-14 10:38:07 +00:00
' type ' : fields . selection ( TODO_TYPES , ' Type ' , required = True ,
2011-08-04 11:55:01 +00:00
help = """ Manual: Launched manually.
2011-10-02 11:51:31 +00:00
Automatic : Runs whenever the system is reconfigured .
2012-02-12 11:45:09 +00:00
Launch Manually Once : after having been launched manually , it sets automatically to Done . """ ),
2011-07-06 15:40:01 +00:00
' groups_id ' : fields . many2many ( ' res.groups ' , ' res_groups_action_rel ' , ' uid ' , ' gid ' , ' Groups ' ) ,
' note ' : fields . text ( ' Text ' , translate = True ) ,
2008-09-29 08:49:06 +00:00
}
_defaults = {
2010-09-23 10:53:45 +00:00
' state ' : ' open ' ,
' sequence ' : 10 ,
2011-07-29 14:26:22 +00:00
' type ' : ' manual ' ,
2008-09-29 08:49:06 +00:00
}
2012-08-04 21:53:00 +00:00
_order = " sequence,id "
2010-08-23 06:43:09 +00:00
2016-03-04 16:42:32 +00:00
@openerp.api.multi
def unlink ( self ) :
if self :
try :
todo_open_menu = self . env . ref ( ' base.open_menu ' )
# don't remove base.open_menu todo but set its original action
if todo_open_menu in self :
todo_open_menu . action_id = self . env . ref ( ' base.action_client_base_menu ' ) . id
self - = todo_open_menu
except ValueError :
pass
return super ( ir_actions_todo , self ) . unlink ( )
2013-11-06 11:54:51 +00:00
def name_get ( self , cr , uid , ids , context = None ) :
2014-04-03 11:14:45 +00:00
return [ ( rec . id , rec . action_id . name ) for rec in self . browse ( cr , uid , ids , context = context ) ]
2013-11-06 11:54:51 +00:00
2014-05-06 10:08:53 +00:00
def name_search ( self , cr , user , name , args = None , operator = ' ilike ' , context = None , limit = 100 ) :
2014-05-09 09:49:20 +00:00
if args is None :
2014-05-06 10:08:53 +00:00
args = [ ]
if name :
ids = self . search ( cr , user , [ ( ' action_id ' , operator , name ) ] + args , limit = limit )
2014-05-09 09:49:20 +00:00
return self . name_get ( cr , user , ids , context = context )
return super ( ir_actions_todo , self ) . name_search ( cr , user , name , args = args , operator = operator , context = context , limit = limit )
2014-05-06 10:08:53 +00:00
2010-08-30 13:04:48 +00:00
def action_launch ( self , cr , uid , ids , context = None ) :
""" Launch Action of Wizard """
wizard_id = ids and ids [ 0 ] or False
wizard = self . browse ( cr , uid , wizard_id , context = context )
2011-10-02 11:51:31 +00:00
if wizard . type in ( ' automatic ' , ' once ' ) :
2011-07-29 14:51:06 +00:00
wizard . write ( { ' state ' : ' done ' } )
2011-07-18 12:39:07 +00:00
2011-08-06 01:52:21 +00:00
# Load action
2014-07-06 14:44:26 +00:00
act_type = wizard . action_id . type
2012-08-04 21:53:00 +00:00
2014-07-06 14:44:26 +00:00
res = self . pool [ act_type ] . read ( cr , uid , [ wizard . action_id . id ] , [ ] , context = context ) [ 0 ]
if act_type != ' ir.actions.act_window ' :
2012-08-04 21:53:00 +00:00
return res
2011-08-06 01:52:21 +00:00
res . setdefault ( ' context ' , ' {} ' )
2011-07-18 10:18:12 +00:00
res [ ' nodestroy ' ] = True
2011-06-28 11:31:31 +00:00
2011-07-06 15:40:01 +00:00
# Open a specific record when res_id is provided in the context
2011-08-06 01:52:21 +00:00
user = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context )
ctx = eval ( res [ ' context ' ] , { ' user ' : user } )
if ctx . get ( ' res_id ' ) :
res . update ( { ' res_id ' : ctx . pop ( ' res_id ' ) } )
# disable log for automatic wizards
if wizard . type == ' automatic ' :
ctx . update ( { ' disable_log ' : True } )
res . update ( { ' context ' : ctx } )
2010-08-30 13:04:48 +00:00
return res
2010-08-23 06:43:09 +00:00
def action_open ( self , cr , uid , ids , context = None ) :
""" Sets configuration wizard in TODO state """
return self . write ( cr , uid , ids , { ' state ' : ' open ' } , context = context )
2011-07-14 10:38:07 +00:00
def progress ( self , cr , uid , context = None ) :
""" Returns a dict with 3 keys { todo, done, total}.
These keys all map to integers and provide the number of todos
marked as open , the total number of todos and the number of
todos not open ( which is basically a shortcut to total - todo )
: rtype : dict
"""
user_groups = set ( map (
lambda x : x . id ,
self . pool [ ' res.users ' ] . browse ( cr , uid , [ uid ] , context = context ) [ 0 ] . groups_id ) )
def groups_match ( todo ) :
""" Checks if the todo ' s groups match those of the current user
"""
return not todo . groups_id \
or bool ( user_groups . intersection ( (
group . id for group in todo . groups_id ) ) )
done = filter (
groups_match ,
self . browse ( cr , uid ,
2011-10-02 11:51:31 +00:00
self . search ( cr , uid , [ ( ' state ' , ' != ' , ' open ' ) ] , context = context ) ,
2011-07-14 10:38:07 +00:00
context = context ) )
total = filter (
groups_match ,
self . browse ( cr , uid ,
2011-10-02 11:51:31 +00:00
self . search ( cr , uid , [ ] , context = context ) ,
2011-07-14 10:38:07 +00:00
context = context ) )
return {
' done ' : len ( done ) ,
' total ' : len ( total ) ,
' todo ' : len ( total ) - len ( done )
}
2008-09-29 08:49:06 +00:00
2013-10-06 10:24:04 +00:00
class ir_actions_act_client ( osv . osv ) :
2011-06-30 11:51:34 +00:00
_name = ' ir.actions.client '
_inherit = ' ir.actions.actions '
_table = ' ir_act_client '
_sequence = ' ir_actions_id_seq '
_order = ' name '
2011-08-04 08:54:55 +00:00
def _get_params ( self , cr , uid , ids , field_name , arg , context ) :
2012-08-21 15:45:55 +00:00
result = { }
2014-10-22 14:20:05 +00:00
# Need to remove bin_size from context, to obtains the binary and not the length.
2016-07-06 14:22:40 +00:00
context = dict ( context , bin_size_params_store = False , bin_size = False )
2012-08-21 15:45:55 +00:00
for record in self . browse ( cr , uid , ids , context = context ) :
result [ record . id ] = record . params_store and eval ( record . params_store , { ' uid ' : uid } ) or False
return result
2011-06-30 11:51:34 +00:00
2011-08-25 12:30:49 +00:00
def _set_params ( self , cr , uid , id , field_name , field_value , arg , context ) :
2012-08-04 21:53:00 +00:00
if isinstance ( field_value , dict ) :
self . write ( cr , uid , id , { ' params_store ' : repr ( field_value ) } , context = context )
else :
self . write ( cr , uid , id , { ' params_store ' : field_value } , context = context )
2011-06-30 11:51:34 +00:00
_columns = {
2014-05-21 09:52:05 +00:00
' name ' : fields . char ( ' Action Name ' , required = True , translate = True ) ,
' tag ' : fields . char ( ' Client action tag ' , required = True ,
2011-06-30 11:51:34 +00:00
help = " An arbitrary string, interpreted by the client "
" according to its own needs and wishes. There "
" is no central tag repository across clients. " ) ,
2014-05-21 09:52:05 +00:00
' res_model ' : fields . char ( ' Destination Model ' ,
2012-08-22 13:34:06 +00:00
help = " Optional model, mostly used for needactions. " ) ,
2014-05-21 09:52:05 +00:00
' context ' : fields . char ( ' Context Value ' , required = True ,
2012-08-23 14:35:25 +00:00
help = " Context dictionary as Python expression, empty by default (Default: {} ) " ) ,
2011-08-04 08:54:55 +00:00
' params ' : fields . function ( _get_params , fnct_inv = _set_params ,
2012-01-04 13:30:27 +00:00
type = ' binary ' ,
2011-06-30 11:51:34 +00:00
string = " Supplementary arguments " ,
help = " Arguments sent to the client along with "
" the view tag " ) ,
2011-08-04 08:54:55 +00:00
' params_store ' : fields . binary ( " Params storage " , readonly = True )
2011-06-30 11:51:34 +00:00
}
_defaults = {
' type ' : ' ir.actions.client ' ,
2012-08-23 14:35:25 +00:00
' context ' : ' {} ' ,
2008-10-08 14:12:08 +00:00
2011-06-30 11:51:34 +00:00
}
2013-10-06 10:24:04 +00:00
2008-10-08 14:12:08 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: