[FIX] Fixed date formatting in several messages. It now adds the user timezone to...
[odoo/odoo.git] / addons / crm / crm_phonecall.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 from crm import crm_base
23 from osv import fields, osv
24 from tools.translate import _
25 import crm
26 import time
27 from datetime import datetime
28
29 class crm_phonecall(crm_base, osv.osv):
30     """ Phonecall Cases """
31
32     _name = "crm.phonecall"
33     _description = "Phonecall"
34     _order = "id desc"
35     _inherit = ['ir.needaction_mixin', 'mail.thread']
36     _columns = {
37         # From crm.case
38         'id': fields.integer('ID', readonly=True),
39         'name': fields.char('Call Summary', size=64, required=True),
40         'active': fields.boolean('Active', required=False),
41         'date_action_last': fields.datetime('Last Action', readonly=1),
42         'date_action_next': fields.datetime('Next Action', readonly=1),
43         'create_date': fields.datetime('Creation Date' , readonly=True),
44         'section_id': fields.many2one('crm.case.section', 'Sales Team', \
45                         select=True, help='Sales team to which Case belongs to.'),
46         'user_id': fields.many2one('res.users', 'Responsible'),
47         'partner_id': fields.many2one('res.partner', 'Partner'),
48         'company_id': fields.many2one('res.company', 'Company'),
49         'description': fields.text('Description'),
50         'state': fields.selection([
51                                     ('draft', 'Draft'),
52                                     ('open', 'Todo'),
53                                     ('cancel', 'Cancelled'),
54                                     ('done', 'Held'),
55                                     ('pending', 'Not Held'),
56                                 ], 'State', size=16, readonly=True,
57                                   help='The state is set to \'Todo\', when a case is created.\
58                                   \nIf the case is in progress the state is set to \'Open\'.\
59                                   \nWhen the call is over, the state is set to \'Held\'.\
60                                   \nIf the call needs to be done then the state is set to \'Not Held\'.'),
61         'email_from': fields.char('Email', size=128, help="These people will receive email."),
62         'date_open': fields.datetime('Opened', readonly=True),
63         # phonecall fields
64         'duration': fields.float('Duration', help="Duration in Minutes"),
65         'categ_id': fields.many2one('crm.case.categ', 'Category', \
66                         domain="['|',('section_id','=',section_id),('section_id','=',False),\
67                         ('object_id.model', '=', 'crm.phonecall')]"),
68         'partner_phone': fields.char('Phone', size=32),
69         'partner_mobile': fields.char('Mobile', size=32),
70         'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
71         'date_closed': fields.datetime('Closed', readonly=True),
72         'date': fields.datetime('Date'),
73         'opportunity_id': fields.many2one ('crm.lead', 'Lead/Opportunity'),
74         'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
75     }
76
77     def _get_default_state(self, cr, uid, context=None):
78         if context and context.get('default_state', False):
79             return context.get('default_state')
80         return 'open'
81
82     _defaults = {
83         'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
84         'priority': crm.AVAILABLE_PRIORITIES[2][0],
85         'state':  _get_default_state,
86         'user_id': lambda self,cr,uid,ctx: uid,
87         'active': 1,
88     }
89
90     def create(self, cr, uid, vals, context=None):
91         obj_id = super(crm_phonecall, self).create(cr, uid, vals, context)
92         for phonecall in self.browse(cr, uid, [obj_id], context=context):
93             if not phonecall.opportunity_id:
94                 self.case_open_send_note(cr, uid, [obj_id], context=context)
95         return obj_id
96
97     def case_close(self, cr, uid, ids, context=None):
98         """Overrides close for crm_case for setting close date
99         """
100         res = True
101         for phone in self.browse(cr, uid, ids):
102             phone_id = phone.id
103             data = {'date_closed': time.strftime('%Y-%m-%d %H:%M:%S')}
104             if phone.duration <=0:
105                 duration = datetime.now() - datetime.strptime(phone.date, '%Y-%m-%d %H:%M:%S')
106                 data.update({'duration': duration.seconds/float(60)})
107             res = super(crm_phonecall, self).case_close(cr, uid, [phone_id], context)
108             self.write(cr, uid, [phone_id], data)
109         return res
110
111     def case_reset(self, cr, uid, ids, context=None):
112         """Resets case as Todo
113         """
114         res = super(crm_phonecall, self).case_reset(cr, uid, ids, context)
115         self.write(cr, uid, ids, {'duration': 0.0, 'state':'open'})
116         return res
117
118
119     def case_open(self, cr, uid, ids, context=None):
120         """Overrides cancel for crm_case for setting Open Date
121         """
122         res = super(crm_phonecall, self).case_open(cr, uid, ids, context)
123         self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S')})
124         return res
125
126     def schedule_another_phonecall(self, cr, uid, ids, schedule_time, call_summary, \
127                     user_id=False, section_id=False, categ_id=False, action='schedule', context=None):
128         """
129         action :('schedule','Schedule a call'), ('log','Log a call')
130         """
131         model_data = self.pool.get('ir.model.data')
132         phonecall_dict = {}
133         if not categ_id:
134             res_id = model_data._get_id(cr, uid, 'crm', 'categ_phone2')
135             if res_id:
136                 categ_id = model_data.browse(cr, uid, res_id, context=context).res_id
137         for call in self.browse(cr, uid, ids, context=context):
138             if not section_id:
139                 section_id = call.section_id and call.section_id.id or False
140             if not user_id:
141                 user_id = call.user_id and call.user_id.id or False
142             vals = {
143                     'name' : call_summary,
144                     'user_id' : user_id or False,
145                     'categ_id' : categ_id or False,
146                     'description' : call.description or False,
147                     'date' : schedule_time,
148                     'section_id' : section_id or False,
149                     'partner_id': call.partner_id and call.partner_id.id or False,
150                     'partner_phone' : call.partner_phone,
151                     'partner_mobile' : call.partner_mobile,
152                     'priority': call.priority,
153             }
154             new_id = self.create(cr, uid, vals, context=context)
155             if action == 'log':
156                 self.case_close(cr, uid, [new_id])
157             phonecall_dict[call.id] = new_id
158         return phonecall_dict
159
160     def _call_create_partner(self, cr, uid, phonecall, context=None):
161         partner = self.pool.get('res.partner')
162         partner_id = partner.create(cr, uid, {
163                     'name': phonecall.name,
164                     'user_id': phonecall.user_id.id,
165                     'comment': phonecall.description,
166                     'address': []
167         })
168         return partner_id
169
170     def _call_set_partner(self, cr, uid, ids, partner_id, context=None):
171         write_res = self.write(cr, uid, ids, {'partner_id' : partner_id}, context=context)
172         self._call_set_partner_send_note(cr, uid, ids, context)
173         return write_res
174
175     def _call_create_partner_address(self, cr, uid, phonecall, partner_id, context=None):
176         address = self.pool.get('res.partner')
177         return address.create(cr, uid, {
178                     'parent_id': partner_id,
179                     'name': phonecall.name,
180                     'phone': phonecall.partner_phone,
181         })
182
183     def convert_partner(self, cr, uid, ids, action='create', partner_id=False, context=None):
184         """
185         This function convert partner based on action.
186         if action is 'create', create new partner with contact and assign lead to new partner_id.
187         otherwise assign lead to specified partner_id
188         """
189         if context is None:
190             context = {}
191         partner_ids = {}
192         for call in self.browse(cr, uid, ids, context=context):
193             if action == 'create':
194                if not partner_id:
195                    partner_id = self._call_create_partner(cr, uid, call, context=context)
196                self._call_create_partner_address(cr, uid, call, partner_id, context=context)
197             self._call_set_partner(cr, uid, [call.id], partner_id, context=context)
198             partner_ids[call.id] = partner_id
199         return partner_ids
200
201
202     def redirect_phonecall_view(self, cr, uid, phonecall_id, context=None):
203         model_data = self.pool.get('ir.model.data')
204         # Select the view
205         tree_view = model_data.get_object_reference(cr, uid, 'crm', 'crm_case_phone_tree_view')
206         form_view = model_data.get_object_reference(cr, uid, 'crm', 'crm_case_phone_form_view')
207         search_view = model_data.get_object_reference(cr, uid, 'crm', 'view_crm_case_phonecalls_filter')
208         value = {
209                 'name': _('Phone Call'),
210                 'view_type': 'form',
211                 'view_mode': 'tree,form',
212                 'res_model': 'crm.phonecall',
213                 'res_id' : int(phonecall_id),
214                 'views': [(form_view and form_view[1] or False, 'form'), (tree_view and tree_view[1] or False, 'tree'), (False, 'calendar')],
215                 'type': 'ir.actions.act_window',
216                 'search_view_id': search_view and search_view[1] or False,
217         }
218         return value
219
220
221     def convert_opportunity(self, cr, uid, ids, opportunity_summary=False, partner_id=False, planned_revenue=0.0, probability=0.0, context=None):
222         partner = self.pool.get('res.partner')
223         opportunity = self.pool.get('crm.lead')
224         opportunity_dict = {}
225         default_contact = False
226         for call in self.browse(cr, uid, ids, context=context):
227             if not partner_id:
228                 partner_id = call.partner_id and call.partner_id.id or False
229             if partner_id:
230                 address_id = partner.address_get(cr, uid, [partner_id])['default']
231                 if address_id:
232                     default_contact = partner.browse(cr, uid, address_id, context=context)
233             opportunity_id = opportunity.create(cr, uid, {
234                             'name': opportunity_summary or call.name,
235                             'planned_revenue': planned_revenue,
236                             'probability': probability,
237                             'partner_id': partner_id or False,
238                             'mobile': default_contact and default_contact.mobile,
239                             'section_id': call.section_id and call.section_id.id or False,
240                             'description': call.description or False,
241                             'priority': call.priority,
242                             'type': 'opportunity',
243                             'phone': call.partner_phone or False,
244                             'email_from': default_contact and default_contact.email,
245                         })
246             vals = {
247                     'partner_id': partner_id,
248                     'opportunity_id' : opportunity_id,
249             }
250             self.write(cr, uid, [call.id], vals)
251             self.case_close(cr, uid, [call.id])
252             opportunity.case_open(cr, uid, [opportunity_id])
253             opportunity_dict[call.id] = opportunity_id
254         return opportunity_dict
255
256     def action_make_meeting(self, cr, uid, ids, context=None):
257         """
258         This opens Meeting's calendar view to schedule meeting on current Phonecall
259         @return : Dictionary value for created Meeting view
260         """
261         value = {}
262         for phonecall in self.browse(cr, uid, ids, context=context):
263             data_obj = self.pool.get('ir.model.data')
264
265             # Get meeting views
266             result = data_obj._get_id(cr, uid, 'crm', 'view_crm_case_meetings_filter')
267             res = data_obj.read(cr, uid, result, ['res_id'])
268             id1 = data_obj._get_id(cr, uid, 'crm', 'crm_case_calendar_view_meet')
269             id2 = data_obj._get_id(cr, uid, 'crm', 'crm_case_form_view_meet')
270             id3 = data_obj._get_id(cr, uid, 'crm', 'crm_case_tree_view_meet')
271             if id1:
272                 id1 = data_obj.browse(cr, uid, id1, context=context).res_id
273             if id2:
274                 id2 = data_obj.browse(cr, uid, id2, context=context).res_id
275             if id3:
276                 id3 = data_obj.browse(cr, uid, id3, context=context).res_id
277
278             context = {
279                         'default_phonecall_id': phonecall.id,
280                         'default_partner_id': phonecall.partner_id and phonecall.partner_id.id or False,
281                         'default_email': phonecall.email_from ,
282                         'default_name': phonecall.name
283                     }
284
285             value = {
286                 'name': _('Meetings'),
287                 'domain' : "[('user_id','=',%s)]" % (uid),
288                 'context': context,
289                 'view_type': 'form',
290                 'view_mode': 'calendar,form,tree',
291                 'res_model': 'crm.meeting',
292                 'view_id': False,
293                 'views': [(id1, 'calendar'), (id2, 'form'), (id3, 'tree')],
294                 'type': 'ir.actions.act_window',
295                 'search_view_id': res['res_id'],
296                 'nodestroy': True
297                 }
298
299         return value
300     
301     # ----------------------------------------
302     # OpenChatter methods and notifications
303     # ----------------------------------------
304     
305     def get_needaction_user_ids(self, cr, uid, ids, context=None):
306         result = dict.fromkeys(ids)
307         for obj in self.browse(cr, uid, ids, context=context):
308             result[obj.id] = []
309             if (obj.state == 'draft' and obj.user_id):
310                 result[obj.id] = [obj.user_id.id]
311         return result
312
313     def case_get_note_msg_prefix(self, cr, uid, id, context=None):
314         return 'Phonecall'
315     
316     def case_reset_send_note(self, cr, uid, ids, context=None):
317         message = _('Phonecall has been <b>reset and set as open</b>.')
318         return self.message_append_note(cr, uid, ids, body=message, context=context)
319
320     def case_open_send_note(self, cr, uid, ids, context=None):
321         lead_obj = self.pool.get('crm.lead')
322         for phonecall in self.browse(cr, uid, ids, context=context):
323             phonecall.message_subscribe([phonecall.user_id.id], context=context)
324             if phonecall.opportunity_id:
325                 lead = phonecall.opportunity_id
326                 # convert datetime field to a datetime, using server format, then
327                 # convert it to the user TZ and re-render it with %Z to add the timezone
328                 phonecall_datetime = fields.DT.datetime.strptime(phonecall.date, tools.DEFAULT_SERVER_DATETIME_FORMAT)
329                 phonecall_date_str = fields.datetime.context_timestamp(cr, uid, phonecall_datetime, context=context).strftime(tools.DATETIME_FORMATS_MAP['%+'] + " (%Z)")
330                 message = _("Phonecall linked to the opportunity <em>%s</em> has been <b>created</b> and <b>scheduled</b> on <em>%s</em>.") % (lead.name, phonecall_date_str)
331             else:
332                 message = _("Phonecall has been <b>created and opened</b>.")
333             phonecall.message_append_note(body=message)
334         return True
335
336     def _call_set_partner_send_note(self, cr, uid, ids, context=None):
337         return self.message_append_note(cr, uid, ids, body=_("Partner has been <b>created</b>"), context=context)
338     
339
340 crm_phonecall()
341
342
343 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: