2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
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.
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.
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/>.
20 ##############################################################################
27 from datetime import datetime, timedelta
31 from osv import fields,osv,orm
32 from osv.orm import except_orm
33 from tools.translate import _
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'}
41 def case_open(self, cr, uid, ids, *args):
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
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')})
54 def _compute_day(self, cr, uid, ids, fields, args, context={}):
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
62 cal_obj = self.pool.get('resource.calendar')
63 res_obj = self.pool.get('resource.resource')
66 for issue in self.browse(cr, uid, ids , context):
71 if field == 'day_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':
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
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]
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,
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'),
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:
107 duration = len(no_days)
108 res[issue.id][field] = abs(float(duration))
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([
133 ('cancel', 'Cancelled'),
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'),
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
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,
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')
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.'))
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')
208 id2 = data_obj.browse(cr, uid, id2, context=context).res_id
210 id3 = data_obj.browse(cr, uid, id3, context=context).res_id
212 for bug in case_obj.browse(cr, uid, ids, context=context):
213 new_task_id = task_obj.create(cr, uid, {
215 'partner_id': bug.partner_id.id,
216 'description':bug.description,
218 'project_id':bug.project_id.id,
219 'priority':bug.priority,
220 'user_id':bug.assigned_to.id,
221 'planned_hours': 0.0,
224 new_task = task_obj.browse(cr, uid, new_task_id)
227 'task_id': new_task_id,
229 case_obj.write(cr, uid, [bug.id], vals)
234 'view_mode': 'form,tree',
235 'res_model': 'project.task',
236 'res_id': int(new_task_id),
238 'views': [(id2,'form'),(id3,'tree'),(False,'calendar'),(False,'graph')],
239 'type': 'ir.actions.act_window',
240 'search_view_id': res['res_id'],
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)
249 categ_id = data_obj.browse(cr, uid, id2, context=context).res_id
251 self.write(cr, uid, ids, {'categ_id': categ_id})
254 def convert_to_feature(self, cr, uid, ids, context=None):
255 return self._convert(cr, uid, ids, 'feature_request_categ', context=context)
257 def convert_to_bug(self, cr, uid, ids, context=None):
258 return self._convert(cr, uid, ids, 'bug_categ', context=context)
260 def onchange_stage_id(self, cr, uid, ids, stage_id, context={}):
263 stage = self.pool.get('crm.case.stage').browse(cr, uid, stage_id, context)
264 if not stage.on_change:
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
276 res = super(project_issue, self).case_escalate(cr, uid, ids, args)
277 cases = self.browse(cr, uid, ids)
280 if case.project_id.project_escalation_id:
281 data['project_id'] = case.project_id.project_escalation_id.id
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)