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