795c9883eca499106f0b802300c11aa4ab8b44cf
[odoo/odoo.git] / addons / project_issue / project_issue.py
1  #-*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 import base64
23 import os
24 import re
25 import time
26 import mx.DateTime
27 from datetime import datetime, timedelta
28
29 import tools
30 from crm import crm
31 from osv import fields,osv,orm
32 from osv.orm import except_orm
33 from tools.translate import _
34
35 class project_issue(osv.osv, crm.crm_case):
36     _name = "project.issue"
37     _description = "Project Issue"
38     _order = "priority, id desc"
39     _inherits = {'mailgate.thread': 'thread_id'}
40
41     def case_open(self, cr, uid, ids, *args):
42         """
43         @param self: The object pointer
44         @param cr: the current row, from the database cursor,
45         @param uid: the current user’s ID for security checks,
46         @param ids: List of case's Ids
47         @param *args: Give Tuple Value
48         """
49
50         res = super(project_issue, self).case_open(cr, uid, ids, *args)
51         self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S')})
52         return res
53
54     def _compute_day(self, cr, uid, ids, fields, args, context={}):
55         """
56         @param cr: the current row, from the database cursor,
57         @param uid: the current user’s ID for security checks,
58         @param ids: List of Openday’s IDs
59         @return: difference between current date and log date
60         @param context: A standard dictionary for contextual values
61         """
62         cal_obj = self.pool.get('resource.calendar')
63         res_obj = self.pool.get('resource.resource')
64
65         res = {}
66         for issue in self.browse(cr, uid, ids , context):
67             for field in fields:
68                 res[issue.id] = {}
69                 duration = 0
70                 ans = False
71                 if field == 'day_open':
72                     if issue.date_open:
73                         date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S")
74                         date_open = datetime.strptime(issue.date_open, "%Y-%m-%d %H:%M:%S")
75                         ans = date_open - date_create
76                         date_until = issue.date_open
77                 elif field == 'day_close':
78                     if issue.date_closed:
79                         date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S")
80                         date_close = datetime.strptime(issue.date_closed, "%Y-%m-%d %H:%M:%S")
81                         date_until = issue.date_closed
82                         ans = date_close - date_create
83                 if ans:
84                     resource_id = False
85                     if issue.user_id:
86                         resource_ids = res_obj.search(cr, uid, [('user_id','=',issue.user_id.id)])
87                         if resource_ids and len(resource_ids):
88                             resource_id = resource_ids[0]
89
90                     duration = float(ans.days)
91                     if issue.section_id and issue.section_id.resource_calendar_id:
92                         duration =  float(ans.days) * 24
93                         new_dates = cal_obj.interval_get(cr,
94                             uid,
95                             issue.section_id.resource_calendar_id and issue.section_id.resource_calendar_id.id or False,
96                             mx.DateTime.strptime(issue.create_date, '%Y-%m-%d %H:%M:%S'),
97                             duration,
98                             resource=resource_id
99                         )
100                         no_days = []
101                         date_until = mx.DateTime.strptime(date_until, '%Y-%m-%d %H:%M:%S')
102                         for in_time, out_time in new_dates:
103                             if in_time.date not in no_days:
104                                 no_days.append(in_time.date)
105                             if out_time > date_until:
106                                 break
107                         duration =  len(no_days)
108                 res[issue.id][field] = abs(float(duration))
109
110         return res
111
112     _columns = {
113         'thread_id': fields.many2one('mailgate.thread', 'Thread', required=False), 
114         'id': fields.integer('ID'),  
115         'name': fields.char('Name', size=128, required=True),
116         'active': fields.boolean('Active', required=False),
117         'create_date': fields.datetime('Creation Date' , readonly=True),
118         'write_date': fields.datetime('Update Date' , readonly=True),
119         'date_deadline': fields.date('Deadline'),
120         'date_closed': fields.datetime('Closed', readonly=True),
121         'section_id': fields.many2one('crm.case.section', 'Sales Team', \
122                         select=True, help='Sales team to which Case belongs to.\
123                              Define Responsible user and Email account for mail gateway.'), 
124         'user_id': fields.many2one('res.users', 'Responsible'),
125         'partner_id': fields.many2one('res.partner', 'Partner'),
126         'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \
127                                  domain="[('partner_id','=',partner_id)]"), 
128         'company_id': fields.many2one('res.company', 'Company'), 
129         'description': fields.text('Description'), 
130         'state': fields.selection([
131                                     ('draft', 'Draft'), 
132                                     ('open', 'Todo'), 
133                                     ('cancel', 'Cancelled'), 
134                                     ('done', 'Closed'), 
135                                     ('pending', 'Pending'),
136                                 ], 'State', size=16, readonly=True, 
137                                   help='The state is set to \'Draft\', when a case is created.\
138                                   \nIf the case is in progress the state is set to \'Open\'.\
139                                   \nWhen the case is over, the state is set to \'Done\'.\
140                                   \nIf the case needs to be reviewed then the state is set to \'Pending\'.'), 
141         'email_from': fields.char('Email', size=128, help="These people will receive email."), 
142         'email_cc': fields.text('Watchers Emails', size=252 , help="These people\
143  will receive a copy of the future" \
144 " communication between partner and users by email"), 
145         'date_open': fields.datetime('Opened', readonly=True),
146         # Project Issue fields
147         'date_closed': fields.datetime('Closed', readonly=True),
148         'date': fields.datetime('Date'),
149         'canal_id': fields.many2one('res.partner.canal', 'Channel',help="The channels represent the different communication modes available with the customer." \
150                                                                         " With each commercial opportunity, you can indicate the canall which is this opportunity source."),
151         'categ_id': fields.many2one('crm.case.categ','Category', domain="[('object_id.model', '=', 'crm.project.bug')]"),
152         'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Severity'),
153         'type_id': fields.many2one('crm.case.resource.type', 'Version', domain="[('object_id.model', '=', 'project.issue')]"),
154         'partner_name': fields.char("Employee's Name", size=64),
155         'partner_mobile': fields.char('Mobile', size=32),
156         'partner_phone': fields.char('Phone', size=32),
157         'stage_id': fields.many2one ('crm.case.stage', 'Stage', domain="[('object_id.model', '=', 'project.issue')]"),
158         'project_id':fields.many2one('project.project', 'Project'),
159         'duration': fields.float('Duration'),
160         'task_id': fields.many2one('project.task', 'Task', domain="[('project_id','=',project_id)]"),
161         'date_open': fields.datetime('Opened', readonly=True),
162         'day_open': fields.function(_compute_day, string='Days to Open', \
163                                 method=True, multi='day_open', type="float", store=True),
164         'day_close': fields.function(_compute_day, string='Days to Close', \
165                                 method=True, multi='day_close', type="float", store=True),
166         'assigned_to' : fields.many2one('res.users', 'Assigned to'),
167         'working_hours_open': fields.float('Working Hours to Open the Issue'),
168         'working_hours_close': fields.float('Working Hours to Close the Issue'),
169     }
170     
171     def _get_project(self, cr, uid, context):
172        user = self.pool.get('res.users').browse(cr,uid,uid, context=context)
173        if user.context_project_id:
174            return user.context_project_id.id
175        return False
176
177     _defaults = {
178         'active': lambda *a: 1,
179         'user_id': crm.crm_case._get_default_user, 
180         'partner_id': crm.crm_case._get_default_partner, 
181         'partner_address_id': crm.crm_case._get_default_partner_address, 
182         'email_from': crm.crm_case. _get_default_email, 
183         'state': lambda *a: 'draft',
184         'section_id': crm.crm_case. _get_section, 
185         'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.helpdesk', context=c), 
186         'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0], 
187         'project_id':_get_project,
188     }
189
190     def convert_issue_task(self, cr, uid, ids, context=None):
191         case_obj = self.pool.get('project.issue')
192         data_obj = self.pool.get('ir.model.data')
193         task_obj = self.pool.get('project.task')
194
195         if context is None:
196             context = {}
197
198 #        for case in case_obj.browse(cr, uid, ids, context=context):
199 #            if case.state != 'open':
200 #                raise osv.except_osv(_('Warning !'),
201 #                    _('Issues or Feature Requests should be in \'Open\' state before converting into Task.'))
202
203         result = data_obj._get_id(cr, uid, 'project', 'view_task_search_form')
204         res = data_obj.read(cr, uid, result, ['res_id'])
205         id2 = data_obj._get_id(cr, uid, 'project', 'view_task_form2')
206         id3 = data_obj._get_id(cr, uid, 'project', 'view_task_tree2')
207         if id2:
208             id2 = data_obj.browse(cr, uid, id2, context=context).res_id
209         if id3:
210             id3 = data_obj.browse(cr, uid, id3, context=context).res_id
211
212         for bug in case_obj.browse(cr, uid, ids, context=context):
213             new_task_id = task_obj.create(cr, uid, {
214                 'name': bug.name,
215                 'partner_id': bug.partner_id.id,
216                 'description':bug.description,
217                 'date': bug.date,
218                 'project_id':bug.project_id.id,
219                 'priority':bug.priority,
220                 'user_id':bug.assigned_to.id,
221                 'planned_hours': 0.0,
222             })
223
224             new_task = task_obj.browse(cr, uid, new_task_id)
225
226             vals = {
227                 'task_id': new_task_id,
228                 }
229             case_obj.write(cr, uid, [bug.id], vals)
230
231         return  {
232             'name': _('Tasks'),
233             'view_type': 'form',
234             'view_mode': 'form,tree',
235             'res_model': 'project.task',
236             'res_id': int(new_task_id),
237             'view_id': False,
238             'views': [(id2,'form'),(id3,'tree'),(False,'calendar'),(False,'graph')],
239             'type': 'ir.actions.act_window',
240             'search_view_id': res['res_id'],
241             'nodestroy': True
242         }
243
244     def _convert(self, cr, uid, ids, xml_id, context=None):
245         data_obj = self.pool.get('ir.model.data')
246         id2 = data_obj._get_id(cr, uid, 'project_issue', xml_id)
247         categ_id = False
248         if id2:
249             categ_id = data_obj.browse(cr, uid, id2, context=context).res_id
250         if categ_id:
251             self.write(cr, uid, ids, {'categ_id': categ_id})
252         return True
253
254     def convert_to_feature(self, cr, uid, ids, context=None):
255         return self._convert(cr, uid, ids, 'feature_request_categ', context=context)
256
257     def convert_to_bug(self, cr, uid, ids, context=None):
258         return self._convert(cr, uid, ids, 'bug_categ', context=context)
259
260     def onchange_stage_id(self, cr, uid, ids, stage_id, context={}):
261         if not stage_id:
262             return {'value':{}}
263         stage = self.pool.get('crm.case.stage').browse(cr, uid, stage_id, context)
264         if not stage.on_change:
265             return {'value':{}}
266         return {'value':{}}
267     
268     def case_escalate(self, cr, uid, ids, *args):
269         """Escalates case to top level
270         @param self: The object pointer
271         @param cr: the current row, from the database cursor,
272         @param uid: the current user’s ID for security checks,
273         @param ids: List of case Ids
274         @param *args: Tuple Value for additional Params
275         """
276         res = super(project_issue, self).case_escalate(cr, uid, ids, args)
277         cases = self.browse(cr, uid, ids)
278         for case in cases:
279             data = {}
280             if case.project_id.project_escalation_id:
281                 data['project_id'] = case.project_id.project_escalation_id.id
282             else:
283                 raise osv.except_osv(_('Warning !'), _('You cannot escalate this case.\nThe relevant Project has not configured the Escalation Project!'))
284             self.write(cr, uid, [case.id], data)
285         return res
286
287 project_issue()
288