2010-04-19 08:38:25 +00:00
#-*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 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/>.
#
##############################################################################
2012-12-17 15:46:28 +00:00
from openerp . addons . base_status . base_stage import base_stage
2012-12-17 15:58:19 +00:00
from openerp . addons . crm import crm
2010-09-27 13:44:03 +00:00
from datetime import datetime
2012-12-06 14:56:32 +00:00
from openerp . osv import fields , osv
from openerp . tools . translate import _
2010-09-27 13:44:03 +00:00
import binascii
import time
2012-12-06 14:56:32 +00:00
from openerp import tools
from openerp . tools import html2plaintext
2011-09-07 21:15:47 +00:00
2010-09-28 12:50:08 +00:00
class project_issue_version ( osv . osv ) :
_name = " project.issue.version "
_order = " name desc "
_columns = {
' name ' : fields . char ( ' Version Number ' , size = 32 , required = True ) ,
' active ' : fields . boolean ( ' Active ' , required = False ) ,
}
_defaults = {
' active ' : 1 ,
}
project_issue_version ( )
2012-12-18 15:36:31 +00:00
_ISSUE_STATE = [ ( ' draft ' , ' New ' ) , ( ' open ' , ' In Progress ' ) , ( ' cancel ' , ' Cancelled ' ) , ( ' done ' , ' Done ' ) , ( ' pending ' , ' Pending ' ) ]
2012-04-06 09:45:40 +00:00
2012-05-29 14:03:14 +00:00
class project_issue ( base_stage , osv . osv ) :
2010-04-19 08:38:25 +00:00
_name = " project.issue "
_description = " Project Issue "
2011-04-07 11:41:45 +00:00
_order = " priority, create_date desc "
2012-08-22 15:31:45 +00:00
_inherit = [ ' mail.thread ' , ' ir.needaction_mixin ' ]
2010-09-20 06:11:01 +00:00
2012-12-18 15:36:31 +00:00
_track = {
2012-12-18 23:49:07 +00:00
' state ' : {
2012-12-19 21:23:30 +00:00
' project_issue.mt_issue_new ' : lambda self , cr , uid , obj , ctx = None : obj [ ' state ' ] == ' new ' ,
2012-12-19 16:39:23 +00:00
' project_issue.mt_issue_closed ' : lambda self , cr , uid , obj , ctx = None : obj [ ' state ' ] == ' done ' ,
' project_issue.mt_issue_started ' : lambda self , cr , uid , obj , ctx = None : obj [ ' state ' ] == ' open ' ,
2012-12-18 23:49:07 +00:00
} ,
2012-12-18 15:36:31 +00:00
' stage_id ' : {
2012-12-19 21:23:30 +00:00
' project_issue.mt_issue_stage ' : lambda self , cr , uid , obj , ctx = None : obj [ ' state ' ] not in [ ' new ' , ' done ' , ' open ' ] ,
2012-12-18 15:36:31 +00:00
} ,
' kanban_state ' : {
2012-12-19 16:39:23 +00:00
' project_issue.mt_issue_blocked ' : lambda self , cr , uid , obj , ctx = None : obj [ ' kanban_state ' ] == ' blocked ' ,
2012-12-18 15:36:31 +00:00
} ,
}
2012-12-28 08:45:27 +00:00
def create ( self , cr , uid , vals , context = None ) :
if not context . get ( ' default_project_id ' , False ) and vals . get ( ' project_id ' , False ) :
ctx = context . copy ( )
ctx [ ' default_project_id ' ] = vals [ ' project_id ' ]
vals [ ' stage_id ' ] = self . _get_default_stage_id ( cr , uid , context = ctx )
return super ( project_issue , self ) . create ( cr , uid , vals , context = context )
2012-05-25 13:08:32 +00:00
def _get_default_project_id ( self , cr , uid , context = None ) :
""" Gives default project by checking if present in the context """
return self . _resolve_project_id_from_context ( cr , uid , context = context )
def _get_default_stage_id ( self , cr , uid , context = None ) :
""" Gives default stage_id """
project_id = self . _get_default_project_id ( cr , uid , context = context )
return self . stage_find ( cr , uid , [ ] , project_id , [ ( ' state ' , ' = ' , ' draft ' ) ] , context = context )
def _resolve_project_id_from_context ( self , cr , uid , context = None ) :
""" Returns ID of project based on the value of ' default_project_id '
context key , or None if it cannot be resolved to a single
project .
"""
if context is None :
context = { }
if type ( context . get ( ' default_project_id ' ) ) in ( int , long ) :
return context . get ( ' default_project_id ' )
if isinstance ( context . get ( ' default_project_id ' ) , basestring ) :
project_name = context [ ' default_project_id ' ]
project_ids = self . pool . get ( ' project.project ' ) . name_search ( cr , uid , name = project_name , context = context )
if len ( project_ids ) == 1 :
return int ( project_ids [ 0 ] [ 0 ] )
return None
def _read_group_stage_ids ( self , cr , uid , ids , domain , read_group_order = None , access_rights_uid = None , context = None ) :
access_rights_uid = access_rights_uid or uid
stage_obj = self . pool . get ( ' project.task.type ' )
order = stage_obj . _order
# lame hack to allow reverting search, should just work in the trivial case
if read_group_order == ' stage_id desc ' :
order = " %s desc " % order
# retrieve section_id from the context and write the domain
2012-05-31 13:16:26 +00:00
# - ('id', 'in', 'ids'): add columns that should be present
# - OR ('case_default', '=', True), ('fold', '=', False): add default columns that are not folded
# - OR ('project_ids', 'in', project_id), ('fold', '=', False) if project_id: add project columns that are not folded
2012-05-25 13:08:32 +00:00
search_domain = [ ]
project_id = self . _resolve_project_id_from_context ( cr , uid , context = context )
if project_id :
2012-09-06 16:18:12 +00:00
search_domain + = [ ' | ' , ( ' project_ids ' , ' = ' , project_id ) ]
2012-11-29 11:27:37 +00:00
search_domain + = [ ( ' id ' , ' in ' , ids ) ]
2012-05-25 13:08:32 +00:00
# perform search
stage_ids = stage_obj . _search ( cr , uid , search_domain , order = order , access_rights_uid = access_rights_uid , context = context )
result = stage_obj . name_get ( cr , access_rights_uid , stage_ids , context = context )
# restore order of the search
result . sort ( lambda x , y : cmp ( stage_ids . index ( x [ 0 ] ) , stage_ids . index ( y [ 0 ] ) ) )
2012-09-06 15:23:03 +00:00
fold = { }
for stage in stage_obj . browse ( cr , access_rights_uid , stage_ids , context = context ) :
fold [ stage . id ] = stage . fold or False
return result , fold
2012-05-25 13:08:32 +00:00
2010-07-02 14:37:06 +00:00
def _compute_day ( self , cr , uid , ids , fields , args , context = None ) :
2010-04-19 08:38:25 +00:00
"""
@param cr : the current row , from the database cursor ,
@param uid : the current user ’ s ID for security checks ,
@param ids : List of Openday ’ s IDs
@return : difference between current date and log date
@param context : A standard dictionary for contextual values
"""
cal_obj = self . pool . get ( ' resource.calendar ' )
res_obj = self . pool . get ( ' resource.resource ' )
res = { }
2010-07-02 14:37:06 +00:00
for issue in self . browse ( cr , uid , ids , context = context ) :
2011-10-28 09:45:35 +00:00
res [ issue . id ] = { }
2010-04-19 08:38:25 +00:00
for field in fields :
duration = 0
ans = False
2010-06-04 10:49:47 +00:00
hours = 0
2010-06-09 05:49:31 +00:00
2011-04-22 11:46:05 +00:00
date_create = datetime . strptime ( issue . create_date , " % Y- % m- %d % H: % M: % S " )
2010-06-04 10:49:47 +00:00
if field in [ ' working_hours_open ' , ' day_open ' ] :
2010-04-19 08:38:25 +00:00
if issue . date_open :
date_open = datetime . strptime ( issue . date_open , " % Y- % m- %d % H: % M: % S " )
ans = date_open - date_create
date_until = issue . date_open
2010-06-07 14:32:47 +00:00
#Calculating no. of working hours to open the issue
2012-03-05 12:03:41 +00:00
if issue . project_id . resource_calendar_id :
hours = cal_obj . interval_hours_get ( cr , uid , issue . project_id . resource_calendar_id . id ,
2011-04-22 11:46:05 +00:00
date_create ,
date_open )
2010-06-04 10:49:47 +00:00
elif field in [ ' working_hours_close ' , ' day_close ' ] :
2010-04-19 08:38:25 +00:00
if issue . date_closed :
date_close = datetime . strptime ( issue . date_closed , " % Y- % m- %d % H: % M: % S " )
date_until = issue . date_closed
ans = date_close - date_create
2010-06-07 14:32:47 +00:00
#Calculating no. of working hours to close the issue
2012-03-05 12:03:41 +00:00
if issue . project_id . resource_calendar_id :
hours = cal_obj . interval_hours_get ( cr , uid , issue . project_id . resource_calendar_id . id ,
2011-11-08 22:53:37 +00:00
date_create ,
date_close )
2011-08-12 13:06:24 +00:00
elif field in [ ' days_since_creation ' ] :
if issue . create_date :
days_since_creation = datetime . today ( ) - datetime . strptime ( issue . create_date , " % Y- % m- %d % H: % M: % S " )
res [ issue . id ] [ field ] = days_since_creation . days
continue
2011-08-09 10:23:04 +00:00
elif field in [ ' inactivity_days ' ] :
2011-08-09 10:37:36 +00:00
res [ issue . id ] [ field ] = 0
2011-08-09 10:23:04 +00:00
if issue . date_action_last :
inactive_days = datetime . today ( ) - datetime . strptime ( issue . date_action_last , ' % Y- % m- %d % H: % M: % S ' )
res [ issue . id ] [ field ] = inactive_days . days
2011-08-09 10:37:36 +00:00
continue
2010-04-19 08:38:25 +00:00
if ans :
resource_id = False
if issue . user_id :
resource_ids = res_obj . search ( cr , uid , [ ( ' user_id ' , ' = ' , issue . user_id . id ) ] )
2010-04-20 05:57:54 +00:00
if resource_ids and len ( resource_ids ) :
resource_id = resource_ids [ 0 ]
2010-04-19 08:38:25 +00:00
duration = float ( ans . days )
2010-06-04 10:49:47 +00:00
if issue . project_id and issue . project_id . resource_calendar_id :
duration = float ( ans . days ) * 24
2011-04-22 11:46:05 +00:00
new_dates = cal_obj . interval_min_get ( cr , uid ,
issue . project_id . resource_calendar_id . id ,
date_create ,
duration , resource = resource_id )
2010-04-19 08:38:25 +00:00
no_days = [ ]
2010-08-12 07:31:45 +00:00
date_until = datetime . strptime ( date_until , ' % Y- % m- %d % H: % M: % S ' )
2010-04-19 08:38:25 +00:00
for in_time , out_time in new_dates :
if in_time . date not in no_days :
no_days . append ( in_time . date )
if out_time > date_until :
break
2010-06-04 10:49:47 +00:00
duration = len ( no_days )
2011-04-22 11:46:05 +00:00
2010-06-04 10:49:47 +00:00
if field in [ ' working_hours_open ' , ' working_hours_close ' ] :
res [ issue . id ] [ field ] = hours
else :
res [ issue . id ] [ field ] = abs ( float ( duration ) )
2011-04-22 11:46:05 +00:00
2010-04-19 08:38:25 +00:00
return res
2010-10-12 13:12:05 +00:00
2012-03-05 12:03:41 +00:00
def _hours_get ( self , cr , uid , ids , field_names , args , context = None ) :
task_pool = self . pool . get ( ' project.task ' )
res = { }
for issue in self . browse ( cr , uid , ids , context = context ) :
progress = 0.0
if issue . task_id :
progress = task_pool . _hours_get ( cr , uid , [ issue . task_id . id ] , field_names , args , context = context ) [ issue . task_id . id ] [ ' progress ' ]
res [ issue . id ] = { ' progress ' : progress }
return res
def on_change_project ( self , cr , uid , ids , project_id , context = None ) :
return { }
2010-10-12 13:12:05 +00:00
def _get_issue_task ( self , cr , uid , ids , context = None ) :
issues = [ ]
issue_pool = self . pool . get ( ' project.issue ' )
for task in self . pool . get ( ' project.task ' ) . browse ( cr , uid , ids , context = context ) :
2011-02-07 05:51:57 +00:00
issues + = issue_pool . search ( cr , uid , [ ( ' task_id ' , ' = ' , task . id ) ] )
2010-10-12 13:12:05 +00:00
return issues
def _get_issue_work ( self , cr , uid , ids , context = None ) :
issues = [ ]
issue_pool = self . pool . get ( ' project.issue ' )
for work in self . pool . get ( ' project.task.work ' ) . browse ( cr , uid , ids , context = context ) :
if work . task_id :
issues + = issue_pool . search ( cr , uid , [ ( ' task_id ' , ' = ' , work . task_id . id ) ] )
return issues
2010-04-19 08:38:25 +00:00
_columns = {
2011-10-12 11:11:42 +00:00
' id ' : fields . integer ( ' ID ' , readonly = True ) ,
2010-10-04 19:41:46 +00:00
' name ' : fields . char ( ' Issue ' , size = 128 , required = True ) ,
2010-06-09 05:49:31 +00:00
' active ' : fields . boolean ( ' Active ' , required = False ) ,
2011-01-17 13:12:57 +00:00
' create_date ' : fields . datetime ( ' Creation Date ' , readonly = True , select = True ) ,
2010-07-02 14:37:06 +00:00
' write_date ' : fields . datetime ( ' Update Date ' , readonly = True ) ,
2011-08-12 13:29:33 +00:00
' days_since_creation ' : fields . function ( _compute_day , string = ' Days since creation date ' , \
multi = ' compute_day ' , type = " integer " , help = " Difference in days between creation date and current date " ) ,
2010-05-27 14:18:54 +00:00
' date_deadline ' : fields . date ( ' Deadline ' ) ,
2010-05-12 07:30:22 +00:00
' section_id ' : fields . many2one ( ' crm.case.section ' , ' Sales Team ' , \
select = True , help = ' Sales team to which Case belongs to. \
2010-06-09 05:49:31 +00:00
Define Responsible user and Email account for mail gateway . ' ),
2012-08-09 10:50:09 +00:00
' partner_id ' : fields . many2one ( ' res.partner ' , ' Contact ' , select = 1 ) ,
2010-06-09 05:49:31 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' ) ,
2012-11-29 16:14:58 +00:00
' description ' : fields . text ( ' Private Note ' ) ,
2012-05-23 12:55:23 +00:00
' state ' : fields . related ( ' stage_id ' , ' state ' , type = " selection " , store = True ,
2012-10-12 11:42:58 +00:00
selection = _ISSUE_STATE , string = " Status " , readonly = True ,
help = ' The status is set to \' Draft \' , when a case is created. \
If the case is in progress the status is set to \' Open \' . \
When the case is over , the status is set to \' Done \' . \
If the case needs to be reviewed then the status is \
2012-05-23 09:24:56 +00:00
set to \' Pending \' . ' ) ,
2012-12-04 07:32:44 +00:00
' kanban_state ' : fields . selection ( [ ( ' normal ' , ' Normal ' ) , ( ' blocked ' , ' Blocked ' ) , ( ' done ' , ' Ready for next stage ' ) ] , ' Kanban State ' ,
2012-12-20 11:47:30 +00:00
track_visibility = ' onchange ' ,
2012-12-04 08:48:19 +00:00
help = " A Issue ' s kanban state indicates special situations affecting it: \n "
2012-12-04 07:32:44 +00:00
" * Normal is the default situation \n "
2012-12-04 08:48:19 +00:00
" * Blocked indicates something is preventing the progress of this issue \n "
" * Ready for next stage indicates the issue is ready to be pulled to the next stage " ,
2012-12-04 07:32:44 +00:00
readonly = True , required = False ) ,
2011-08-18 19:30:44 +00:00
' email_from ' : fields . char ( ' Email ' , size = 128 , help = " These people will receive email. " , select = 1 ) ,
2010-07-19 11:49:25 +00:00
' email_cc ' : fields . char ( ' Watchers Emails ' , size = 256 , help = " These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma " ) ,
2011-01-17 13:12:57 +00:00
' date_open ' : fields . datetime ( ' Opened ' , readonly = True , select = True ) ,
2010-05-12 07:30:22 +00:00
# Project Issue fields
2011-01-17 13:12:57 +00:00
' date_closed ' : fields . datetime ( ' Closed ' , readonly = True , select = True ) ,
2010-04-19 08:38:25 +00:00
' date ' : fields . datetime ( ' Date ' ) ,
2011-08-27 23:31:30 +00:00
' channel_id ' : fields . many2one ( ' crm.case.channel ' , ' Channel ' , help = " Communication channel. " ) ,
2012-10-30 12:44:05 +00:00
' categ_ids ' : fields . many2many ( ' project.category ' , string = ' Tags ' ) ,
2011-12-09 06:03:08 +00:00
' priority ' : fields . selection ( crm . AVAILABLE_PRIORITIES , ' Priority ' , select = True ) ,
2010-09-28 12:50:08 +00:00
' version_id ' : fields . many2one ( ' project.issue.version ' , ' Version ' ) ,
2012-06-01 14:51:34 +00:00
' stage_id ' : fields . many2one ( ' project.task.type ' , ' Stage ' ,
2012-12-20 11:47:30 +00:00
track_visibility = ' onchange ' ,
2012-11-29 11:27:37 +00:00
domain = " [ ' & ' , ( ' fold ' , ' = ' , False), ( ' project_ids ' , ' = ' , project_id)] " ) ,
2012-12-20 11:47:30 +00:00
' project_id ' : fields . many2one ( ' project.project ' , ' Project ' , track_visibility = ' onchange ' ) ,
2010-04-19 08:38:25 +00:00
' duration ' : fields . float ( ' Duration ' ) ,
' task_id ' : fields . many2one ( ' project.task ' , ' Task ' , domain = " [( ' project_id ' , ' = ' ,project_id)] " ) ,
' day_open ' : fields . function ( _compute_day , string = ' Days to Open ' , \
2011-07-05 12:28:57 +00:00
multi = ' compute_day ' , type = " float " , store = True ) ,
2010-04-19 08:38:25 +00:00
' day_close ' : fields . function ( _compute_day , string = ' Days to Close ' , \
2011-07-05 12:28:57 +00:00
multi = ' compute_day ' , type = " float " , store = True ) ,
2012-12-20 11:47:30 +00:00
' user_id ' : fields . many2one ( ' res.users ' , ' Assigned to ' , required = False , select = 1 , track_visibility = ' onchange ' ) ,
2010-06-04 10:49:47 +00:00
' working_hours_open ' : fields . function ( _compute_day , string = ' Working Hours to Open the Issue ' , \
2011-07-05 12:28:57 +00:00
multi = ' compute_day ' , type = " float " , store = True ) ,
2010-06-04 10:49:47 +00:00
' working_hours_close ' : fields . function ( _compute_day , string = ' Working Hours to Close the Issue ' , \
2011-07-05 12:28:57 +00:00
multi = ' compute_day ' , type = " float " , store = True ) ,
2011-08-09 10:23:04 +00:00
' inactivity_days ' : fields . function ( _compute_day , string = ' Days since last action ' , \
multi = ' compute_day ' , type = " integer " , help = " Difference in days between last action and current date " ) ,
2011-10-25 19:26:39 +00:00
' color ' : fields . integer ( ' Color Index ' ) ,
2012-08-10 14:43:39 +00:00
' user_email ' : fields . related ( ' user_id ' , ' email ' , type = ' char ' , string = ' User Email ' , readonly = True ) ,
2010-06-25 14:21:59 +00:00
' date_action_last ' : fields . datetime ( ' Last Action ' , readonly = 1 ) ,
' date_action_next ' : fields . datetime ( ' Next Action ' , readonly = 1 ) ,
2011-07-01 23:41:24 +00:00
' progress ' : fields . function ( _hours_get , string = ' Progress ( % ) ' , multi = ' hours ' , group_operator = " avg " , help = " Computed as: Time Spent / Total Time. " ,
2010-10-12 13:12:05 +00:00
store = {
' project.issue ' : ( lambda self , cr , uid , ids , c = { } : ids , [ ' task_id ' ] , 10 ) ,
' project.task ' : ( _get_issue_task , [ ' progress ' ] , 10 ) ,
' project.task.work ' : ( _get_issue_work , [ ' hours ' ] , 10 ) ,
} ) ,
2010-04-19 08:38:25 +00:00
}
2010-06-09 05:49:31 +00:00
2010-05-20 06:16:37 +00:00
_defaults = {
2010-07-02 14:37:06 +00:00
' active ' : 1 ,
2012-05-29 14:03:14 +00:00
' partner_id ' : lambda s , cr , uid , c : s . _get_default_partner ( cr , uid , c ) ,
' email_from ' : lambda s , cr , uid , c : s . _get_default_email ( cr , uid , c ) ,
2012-05-25 13:08:32 +00:00
' stage_id ' : lambda s , cr , uid , c : s . _get_default_stage_id ( cr , uid , c ) ,
' section_id ' : lambda s , cr , uid , c : s . _get_default_section_id ( cr , uid , c ) ,
2010-06-09 05:49:31 +00:00
' company_id ' : lambda s , cr , uid , c : s . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' crm.helpdesk ' , context = c ) ,
2010-07-02 14:37:06 +00:00
' priority ' : crm . AVAILABLE_PRIORITIES [ 2 ] [ 0 ] ,
2012-12-04 07:32:44 +00:00
' kanban_state ' : ' normal ' ,
2012-10-03 11:33:41 +00:00
}
2011-09-15 12:36:42 +00:00
2012-05-25 13:08:32 +00:00
_group_by_full = {
' stage_id ' : _read_group_stage_ids
}
2012-06-26 10:29:45 +00:00
def set_priority ( self , cr , uid , ids , priority , * args ) :
2011-09-15 12:36:42 +00:00
""" Set lead priority
"""
return self . write ( cr , uid , ids , { ' priority ' : priority } )
def set_high_priority ( self , cr , uid , ids , * args ) :
""" Set lead priority to high
"""
return self . set_priority ( cr , uid , ids , ' 1 ' )
def set_normal_priority ( self , cr , uid , ids , * args ) :
""" Set lead priority to normal
"""
return self . set_priority ( cr , uid , ids , ' 3 ' )
2010-05-20 06:16:37 +00:00
2010-04-19 08:38:25 +00:00
def convert_issue_task ( self , cr , uid , ids , context = None ) :
2012-04-03 10:19:41 +00:00
if context is None :
context = { }
2012-07-11 13:21:30 +00:00
2010-04-19 08:38:25 +00:00
case_obj = self . pool . get ( ' project.issue ' )
data_obj = self . pool . get ( ' ir.model.data ' )
task_obj = self . pool . get ( ' project.task ' )
2012-07-11 13:21:30 +00:00
2010-04-19 08:38:25 +00:00
result = data_obj . _get_id ( cr , uid , ' project ' , ' view_task_search_form ' )
res = data_obj . read ( cr , uid , result , [ ' res_id ' ] )
id2 = data_obj . _get_id ( cr , uid , ' project ' , ' view_task_form2 ' )
id3 = data_obj . _get_id ( cr , uid , ' project ' , ' view_task_tree2 ' )
if id2 :
id2 = data_obj . browse ( cr , uid , id2 , context = context ) . res_id
if id3 :
id3 = data_obj . browse ( cr , uid , id3 , context = context ) . res_id
for bug in case_obj . browse ( cr , uid , ids , context = context ) :
new_task_id = task_obj . create ( cr , uid , {
' name ' : bug . name ,
' partner_id ' : bug . partner_id . id ,
' description ' : bug . description ,
2011-12-06 11:22:58 +00:00
' date_deadline ' : bug . date ,
2010-07-02 14:37:06 +00:00
' project_id ' : bug . project_id . id ,
2011-12-15 12:09:29 +00:00
# priority must be in ['0','1','2','3','4'], while bug.priority is in ['1','2','3','4','5']
' priority ' : str ( int ( bug . priority ) - 1 ) ,
2011-08-30 08:45:10 +00:00
' user_id ' : bug . user_id . id ,
2010-04-19 08:38:25 +00:00
' planned_hours ' : 0.0 ,
} )
vals = {
' task_id ' : new_task_id ,
2012-10-18 13:57:28 +00:00
' stage_id ' : self . stage_find ( cr , uid , [ bug ] , bug . project_id . id , [ ( ' state ' , ' = ' , ' pending ' ) ] , context = context ) ,
2010-06-29 09:22:36 +00:00
}
2012-12-18 22:06:17 +00:00
message = _ ( " Project issue <b>converted</b> to task. " )
self . message_post ( cr , uid , [ bug . id ] , body = message , context = context )
2012-04-03 10:19:41 +00:00
case_obj . write ( cr , uid , [ bug . id ] , vals , context = context )
2010-04-19 08:38:25 +00:00
return {
' name ' : _ ( ' Tasks ' ) ,
' view_type ' : ' form ' ,
' view_mode ' : ' form,tree ' ,
' res_model ' : ' project.task ' ,
' res_id ' : int ( new_task_id ) ,
' view_id ' : False ,
' views ' : [ ( id2 , ' form ' ) , ( id3 , ' tree ' ) , ( False , ' calendar ' ) , ( False , ' graph ' ) ] ,
' type ' : ' ir.actions.act_window ' ,
' search_view_id ' : res [ ' res_id ' ] ,
' nodestroy ' : True
}
2012-05-23 12:55:23 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
issue = self . read ( cr , uid , id , [ ' name ' ] , context = context )
if not default :
default = { }
default = default . copy ( )
2012-09-24 16:26:45 +00:00
default . update ( name = _ ( ' %s (copy) ' ) % ( issue [ ' name ' ] ) )
2012-05-23 12:55:23 +00:00
return super ( project_issue , self ) . copy ( cr , uid , id , default = default ,
context = context )
2012-07-11 13:21:30 +00:00
2012-03-05 12:03:41 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
2012-03-15 06:29:03 +00:00
#Update last action date every time the user change the stage, the state or send a new email
2012-05-23 12:55:23 +00:00
logged_fields = [ ' stage_id ' , ' state ' , ' message_ids ' ]
2012-03-05 12:03:41 +00:00
if any ( [ field in vals for field in logged_fields ] ) :
vals [ ' date_action_last ' ] = time . strftime ( ' % Y- % m- %d % H: % M: % S ' )
2012-11-02 15:41:54 +00:00
2012-12-19 16:39:23 +00:00
return super ( project_issue , self ) . write ( cr , uid , ids , vals , context )
2012-12-18 15:36:31 +00:00
2010-09-29 15:07:02 +00:00
def onchange_task_id ( self , cr , uid , ids , task_id , context = None ) :
if not task_id :
2012-11-02 15:41:54 +00:00
return { ' value ' : { } }
2010-11-22 10:37:53 +00:00
task = self . pool . get ( ' project.task ' ) . browse ( cr , uid , task_id , context = context )
2012-11-02 15:41:54 +00:00
return { ' value ' : { ' user_id ' : task . user_id . id , } }
2010-06-09 05:49:31 +00:00
2012-03-05 12:03:41 +00:00
def case_reset ( self , cr , uid , ids , context = None ) :
""" Resets case as draft
2010-05-27 14:18:54 +00:00
"""
2012-03-05 12:03:41 +00:00
res = super ( project_issue , self ) . case_reset ( cr , uid , ids , context )
self . write ( cr , uid , ids , { ' date_open ' : False , ' date_closed ' : False } )
return res
2012-05-23 12:55:23 +00:00
# -------------------------------------------------------
# Stage management
# -------------------------------------------------------
2012-05-25 13:08:32 +00:00
2012-12-18 15:36:31 +00:00
def set_kanban_state_blocked ( self , cr , uid , ids , context = None ) :
return self . write ( cr , uid , ids , { ' kanban_state ' : ' blocked ' } , context = context )
def set_kanban_state_normal ( self , cr , uid , ids , context = None ) :
return self . write ( cr , uid , ids , { ' kanban_state ' : ' normal ' } , context = context )
def set_kanban_state_done ( self , cr , uid , ids , context = None ) :
return self . write ( cr , uid , ids , { ' kanban_state ' : ' done ' } , context = context )
2012-05-25 13:08:32 +00:00
def stage_find ( self , cr , uid , cases , section_id , domain = [ ] , order = ' sequence ' , context = None ) :
""" Override of the base.stage method
Parameter of the stage search taken from the issue :
- type : stage type must be the same or ' both '
- section_id : if set , stages must belong to this section or
be a default case
"""
if isinstance ( cases , ( int , long ) ) :
cases = self . browse ( cr , uid , cases , context = context )
2012-05-31 14:19:30 +00:00
# collect all section_ids
section_ids = [ ]
2012-05-25 13:08:32 +00:00
if section_id :
2012-05-31 14:19:30 +00:00
section_ids . append ( section_id )
for task in cases :
if task . project_id :
section_ids . append ( task . project_id . id )
# OR all section_ids and OR with case_default
search_domain = [ ]
if section_ids :
2012-11-29 11:27:37 +00:00
search_domain + = [ ( ' | ' ) ] * ( len ( section_ids ) - 1 )
2012-05-31 14:19:30 +00:00
for section_id in section_ids :
search_domain . append ( ( ' project_ids ' , ' = ' , section_id ) )
search_domain + = list ( domain )
# perform search, return the first found
stage_ids = self . pool . get ( ' project.task.type ' ) . search ( cr , uid , search_domain , order = order , context = context )
2012-05-25 13:08:32 +00:00
if stage_ids :
return stage_ids [ 0 ]
return False
2012-05-25 13:46:19 +00:00
def case_cancel ( self , cr , uid , ids , context = None ) :
""" Cancels case """
2012-12-21 12:17:53 +00:00
self . case_set ( cr , uid , ids , ' cancelled ' , { ' active ' : True } , context = context )
2012-05-25 13:46:19 +00:00
return True
2012-03-05 12:03:41 +00:00
def case_escalate ( self , cr , uid , ids , context = None ) :
2010-05-27 14:18:54 +00:00
cases = self . browse ( cr , uid , ids )
for case in cases :
2012-06-01 09:13:47 +00:00
data = { }
2010-05-27 14:18:54 +00:00
if case . project_id . project_escalation_id :
data [ ' project_id ' ] = case . project_id . project_escalation_id . id
2010-06-09 05:49:31 +00:00
if case . project_id . project_escalation_id . user_id :
data [ ' user_id ' ] = case . project_id . project_escalation_id . user_id . id
2010-07-13 21:09:48 +00:00
if case . task_id :
self . pool . get ( ' project.task ' ) . write ( cr , uid , [ case . task_id . id ] , { ' project_id ' : data [ ' project_id ' ] , ' user_id ' : False } )
2010-05-27 14:18:54 +00:00
else :
2012-08-07 11:34:14 +00:00
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' You cannot escalate this issue. \n The relevant Project has not configured the Escalation Project! ' ) )
2012-06-01 09:13:47 +00:00
self . case_set ( cr , uid , ids , ' draft ' , data , context = context )
2010-06-09 05:49:31 +00:00
return True
2010-04-19 08:38:25 +00:00
2012-05-23 12:55:23 +00:00
# -------------------------------------------------------
# Mail gateway
# -------------------------------------------------------
2012-07-11 13:21:30 +00:00
2011-08-22 17:16:59 +00:00
def message_new ( self , cr , uid , msg , custom_values = None , context = None ) :
2012-06-04 14:12:54 +00:00
""" Overrides mail_thread message_new that is called by the mailgateway
through message_process .
This override updates the document according to the email .
"""
if custom_values is None : custom_values = { }
if context is None : context = { }
context [ ' state_to ' ] = ' draft '
2012-10-09 15:54:20 +00:00
desc = html2plaintext ( msg . get ( ' body ' ) ) if msg . get ( ' body ' ) else ' '
2012-06-04 14:12:54 +00:00
custom_values . update ( {
' name ' : msg . get ( ' subject ' ) or _ ( " No Subject " ) ,
2012-10-09 15:54:20 +00:00
' description ' : desc ,
2012-06-04 14:12:54 +00:00
' email_from ' : msg . get ( ' from ' ) ,
2010-06-24 19:53:32 +00:00
' email_cc ' : msg . get ( ' cc ' ) ,
' user_id ' : False ,
2012-06-04 14:12:54 +00:00
} )
if msg . get ( ' priority ' ) :
custom_values [ ' priority ' ] = msg . get ( ' priority ' )
2011-04-20 06:06:05 +00:00
2012-06-04 14:12:54 +00:00
res_id = super ( project_issue , self ) . message_new ( cr , uid , msg , custom_values = custom_values , context = context )
2011-04-20 06:06:05 +00:00
return res_id
2010-06-24 19:53:32 +00:00
2012-06-04 14:12:54 +00:00
def message_update ( self , cr , uid , ids , msg , update_vals = None , context = None ) :
""" Overrides mail_thread message_update that is called by the mailgateway
through message_process .
This method updates the document according to the email .
"""
2010-06-24 19:53:32 +00:00
if isinstance ( ids , ( str , int , long ) ) :
ids = [ ids ]
2012-06-04 14:12:54 +00:00
if update_vals is None : update_vals = { }
2010-07-02 14:37:06 +00:00
2012-06-04 14:12:54 +00:00
# Update doc values according to the message
if msg . get ( ' priority ' ) :
update_vals [ ' priority ' ] = msg . get ( ' priority ' )
2012-08-17 12:06:06 +00:00
# Parse 'body' to find values to update
2010-06-24 19:53:32 +00:00
maps = {
2010-07-02 14:37:06 +00:00
' cost ' : ' planned_cost ' ,
2010-06-24 19:53:32 +00:00
' revenue ' : ' planned_revenue ' ,
2012-06-04 14:12:54 +00:00
' probability ' : ' probability ' ,
2010-06-24 19:53:32 +00:00
}
2012-08-17 12:06:06 +00:00
for line in msg . get ( ' body ' , ' ' ) . split ( ' \n ' ) :
2010-06-24 19:53:32 +00:00
line = line . strip ( )
2012-12-27 09:43:51 +00:00
res = tools . command_re . match ( line )
2010-06-24 19:53:32 +00:00
if res and maps . get ( res . group ( 1 ) . lower ( ) , False ) :
key = maps . get ( res . group ( 1 ) . lower ( ) )
2012-06-04 14:12:54 +00:00
update_vals [ key ] = res . group ( 2 ) . lower ( )
2010-07-02 14:37:06 +00:00
2012-08-09 17:15:39 +00:00
return super ( project_issue , self ) . message_update ( cr , uid , ids , msg , update_vals = update_vals , context = context )
2012-07-11 13:21:30 +00:00
2010-04-19 08:38:25 +00:00
2010-06-25 08:46:21 +00:00
class project ( osv . osv ) :
_inherit = " project.project "
2012-03-28 06:08:12 +00:00
2012-08-01 09:14:39 +00:00
def _get_alias_models ( self , cr , uid , context = None ) :
2012-08-06 00:44:17 +00:00
return [ ( ' project.task ' , " Tasks " ) , ( " project.issue " , " Issues " ) ]
2012-09-24 16:26:45 +00:00
2012-05-07 07:20:49 +00:00
def _issue_count ( self , cr , uid , ids , field_name , arg , context = None ) :
2012-05-07 10:07:42 +00:00
res = dict . fromkeys ( ids , 0 )
2012-05-09 06:56:44 +00:00
issue_ids = self . pool . get ( ' project.issue ' ) . search ( cr , uid , [ ( ' project_id ' , ' in ' , ids ) ] )
for issue in self . pool . get ( ' project.issue ' ) . browse ( cr , uid , issue_ids , context ) :
2012-05-07 10:07:42 +00:00
res [ issue . project_id . id ] + = 1
2012-03-28 06:08:12 +00:00
return res
2010-06-25 08:46:21 +00:00
_columns = {
2010-07-29 05:31:04 +00:00
' project_escalation_id ' : fields . many2one ( ' project.project ' , ' Project Escalation ' , help = ' If any issue is escalated from the current Project, it will be listed under the project selected here. ' , states = { ' close ' : [ ( ' readonly ' , True ) ] , ' cancelled ' : [ ( ' readonly ' , True ) ] } ) ,
2012-05-09 06:56:44 +00:00
' issue_count ' : fields . function ( _issue_count , type = ' integer ' ) ,
2010-06-25 08:46:21 +00:00
}
2012-05-09 06:56:44 +00:00
2010-11-19 13:48:01 +00:00
def _check_escalation ( self , cr , uid , ids , context = None ) :
2012-03-05 12:03:41 +00:00
project_obj = self . browse ( cr , uid , ids [ 0 ] , context = context )
if project_obj . project_escalation_id :
if project_obj . project_escalation_id . id == project_obj . id :
return False
return True
2010-07-26 09:41:14 +00:00
_constraints = [
( _check_escalation , ' Error! You cannot assign escalation to the same project! ' , [ ' project_escalation_id ' ] )
]
2012-09-24 16:26:45 +00:00
2010-06-25 08:46:21 +00:00
project ( )
2012-05-28 12:30:54 +00:00
class account_analytic_account ( osv . osv ) :
_inherit = ' account.analytic.account '
_description = ' Analytic Account '
2012-07-11 13:21:30 +00:00
2012-05-28 12:30:54 +00:00
_columns = {
2012-08-30 07:49:03 +00:00
' use_issues ' : fields . boolean ( ' Issues ' , help = " Check this field if this project manages issues " ) ,
2012-05-28 12:30:54 +00:00
}
2012-07-11 13:21:30 +00:00
2012-07-23 12:09:07 +00:00
def on_change_template ( self , cr , uid , ids , template_id , context = None ) :
res = super ( account_analytic_account , self ) . on_change_template ( cr , uid , ids , template_id , context = context )
if template_id and ' value ' in res :
template = self . browse ( cr , uid , template_id , context = context )
res [ ' value ' ] [ ' use_issues ' ] = template . use_issues
return res
2012-07-27 09:21:35 +00:00
2012-06-14 10:09:22 +00:00
def _trigger_project_creation ( self , cr , uid , vals , context = None ) :
2012-08-23 11:52:18 +00:00
if context is None : context = { }
2012-06-14 10:09:22 +00:00
res = super ( account_analytic_account , self ) . _trigger_project_creation ( cr , uid , vals , context = context )
2012-08-23 11:52:18 +00:00
return res or ( vals . get ( ' use_issues ' ) and not ' project_creation_in_progress ' in context )
2012-05-28 12:30:54 +00:00
account_analytic_account ( )
2012-08-06 20:07:10 +00:00
class project_project ( osv . osv ) :
_inherit = ' project.project '
_defaults = {
' use_issues ' : True
}
2010-07-02 14:37:06 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: