Launchpad automatic translations update.
[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 from base_status.base_state import base_state
23 import crm
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(base_state, osv.osv):
31     """ Model for CRM phonecalls """
32     _name = "crm.phonecall"
33     _description = "Phonecall"
34     _order = "id desc"
35     _inherit = ['mail.thread']
36     _columns = {
37         # base_state required fields
38         'date_action_last': fields.datetime('Last Action', readonly=1),
39         'date_action_next': fields.datetime('Next Action', readonly=1),
40         'create_date': fields.datetime('Creation Date' , readonly=True),
41         'section_id': fields.many2one('crm.case.section', 'Sales Team', \
42                         select=True, help='Sales team to which Case belongs to.'),
43         'user_id': fields.many2one('res.users', 'Responsible'),
44         'partner_id': fields.many2one('res.partner', 'Contact'),
45         'company_id': fields.many2one('res.company', 'Company'),
46         'description': fields.text('Description'),
47         'state': fields.selection([ ('draft', 'Draft'),
48                                     ('open', 'Confirmed'),
49                                     ('pending', 'Not Held'),
50                                     ('cancel', 'Cancelled'),
51                                     ('done', 'Held'),],
52                         string='Status', size=16, readonly=True,
53                         help='The status is set to \'Todo\', when a case is created.\
54                                 If the case is in progress the status is set to \'Open\'.\
55                                 When the call is over, the status is set to \'Held\'.\
56                                 If the call needs to be done then the status is set to \'Not Held\'.'),
57         'email_from': fields.char('Email', size=128, help="These people will receive email."),
58         'date_open': fields.datetime('Opened', readonly=True),
59         # phonecall fields
60         'name': fields.char('Call Summary', size=64, required=True),
61         'active': fields.boolean('Active', required=False),
62         'duration': fields.float('Duration', help="Duration in Minutes"),
63         'categ_id': fields.many2one('crm.case.categ', 'Category', \
64                         domain="['|',('section_id','=',section_id),('section_id','=',False),\
65                         ('object_id.model', '=', 'crm.phonecall')]"),
66         'partner_phone': fields.char('Phone', size=32),
67         'partner_mobile': fields.char('Mobile', size=32),
68         'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
69         'date_closed': fields.datetime('Closed', readonly=True),
70         'date': fields.datetime('Date'),
71         'opportunity_id': fields.many2one ('crm.lead', 'Lead/Opportunity'),
72     }
73
74     def _get_default_state(self, cr, uid, context=None):
75         if context and context.get('default_state', False):
76             return context.get('default_state')
77         return 'open'
78
79     _defaults = {
80         'date': fields.datetime.now,
81         'priority': crm.AVAILABLE_PRIORITIES[2][0],
82         'state':  _get_default_state,
83         'user_id': lambda self,cr,uid,ctx: uid,
84         'active': 1
85     }
86
87     def create(self, cr, uid, vals, context=None):
88         obj_id = super(crm_phonecall, self).create(cr, uid, vals, context)
89         for phonecall in self.browse(cr, uid, [obj_id], context=context):
90             if not phonecall.opportunity_id:
91                 self.case_open_send_note(cr, uid, [obj_id], context=context)
92         return obj_id
93
94     def case_close(self, cr, uid, ids, context=None):
95         """ Overrides close for crm_case for setting duration """
96         res = True
97         for phone in self.browse(cr, uid, ids, context=context):
98             phone_id = phone.id
99             data = {}
100             if phone.duration <=0:
101                 duration = datetime.now() - datetime.strptime(phone.date, DEFAULT_SERVER_DATETIME_FORMAT)
102                 data['duration'] = duration.seconds/float(60)
103             res = super(crm_phonecall, self).case_close(cr, uid, [phone_id], context=context)
104             self.write(cr, uid, [phone_id], data, context=context)
105         return res
106
107     def case_reset(self, cr, uid, ids, context=None):
108         """Resets case as Todo
109         """
110         res = super(crm_phonecall, self).case_reset(cr, uid, ids, context)
111         self.write(cr, uid, ids, {'duration': 0.0, 'state':'open'}, context=context)
112         return res
113
114     def schedule_another_phonecall(self, cr, uid, ids, schedule_time, call_summary, \
115                     user_id=False, section_id=False, categ_id=False, action='schedule', context=None):
116         """
117         action :('schedule','Schedule a call'), ('log','Log a call')
118         """
119         model_data = self.pool.get('ir.model.data')
120         phonecall_dict = {}
121         if not categ_id:
122             res_id = model_data._get_id(cr, uid, 'crm', 'categ_phone2')
123             if res_id:
124                 categ_id = model_data.browse(cr, uid, res_id, context=context).res_id
125         for call in self.browse(cr, uid, ids, context=context):
126             if not section_id:
127                 section_id = call.section_id and call.section_id.id or False
128             if not user_id:
129                 user_id = call.user_id and call.user_id.id or False
130             if not schedule_time:
131                 schedule_time = call.date
132             vals = {
133                     'name' : call_summary,
134                     'user_id' : user_id or False,
135                     'categ_id' : categ_id or False,
136                     'description' : call.description or False,
137                     'date' : schedule_time,
138                     'section_id' : section_id or False,
139                     'partner_id': call.partner_id and call.partner_id.id or False,
140                     'partner_phone' : call.partner_phone,
141                     'partner_mobile' : call.partner_mobile,
142                     'priority': call.priority,
143             }
144             new_id = self.create(cr, uid, vals, context=context)
145             if action == 'log':
146                 self.case_close(cr, uid, [new_id])
147             phonecall_dict[call.id] = new_id
148         return phonecall_dict
149
150     def _call_create_partner(self, cr, uid, phonecall, context=None):
151         partner = self.pool.get('res.partner')
152         partner_id = partner.create(cr, uid, {
153                     'name': phonecall.name,
154                     'user_id': phonecall.user_id.id,
155                     'comment': phonecall.description,
156                     'address': []
157         })
158         return partner_id
159
160     def on_change_opportunity(self, cr, uid, ids, opportunity_id, context=None):
161         values = {}
162         if opportunity_id:
163             opportunity = self.pool.get('crm.lead').browse(cr, uid, opportunity_id, context=context)
164             values = {
165                 'section_id' : opportunity.section_id and opportunity.section_id.id or False,
166                 'partner_phone' : opportunity.phone,
167                 'partner_mobile' : opportunity.mobile,
168                 'partner_id' : opportunity.partner_id and opportunity.partner_id.id or False,
169             }
170         return {'value' : values}
171
172     def _call_set_partner(self, cr, uid, ids, partner_id, context=None):
173         write_res = self.write(cr, uid, ids, {'partner_id' : partner_id}, context=context)
174         self._call_set_partner_send_note(cr, uid, ids, context)
175         return write_res
176
177     def _call_create_partner_address(self, cr, uid, phonecall, partner_id, context=None):
178         address = self.pool.get('res.partner')
179         return address.create(cr, uid, {
180                     'parent_id': partner_id,
181                     'name': phonecall.name,
182                     'phone': phonecall.partner_phone,
183         })
184
185     def handle_partner_assignation(self, cr, uid, ids, action='create', partner_id=False, context=None):
186         """
187         Handle partner assignation during a lead conversion.
188         if action is 'create', create new partner with contact and assign lead to new partner_id.
189         otherwise assign lead to specified partner_id
190
191         :param list ids: phonecalls ids to process
192         :param string action: what has to be done regarding partners (create it, assign an existing one, or nothing)
193         :param int partner_id: partner to assign if any
194         :return dict: dictionary organized as followed: {lead_id: partner_assigned_id}
195         """
196         #TODO this is a duplication of the handle_partner_assignation method of crm_lead
197         partner_ids = {}
198         # If a partner_id is given, force this partner for all elements
199         force_partner_id = partner_id
200         for call in self.browse(cr, uid, ids, context=context):
201             # If the action is set to 'create' and no partner_id is set, create a new one
202             if action == 'create':
203                 partner_id = force_partner_id or self._call_create_partner(cr, uid, call, context=context)
204                 self._call_create_partner_address(cr, uid, call, partner_id, context=context)
205             self._call_set_partner(cr, uid, [call.id], partner_id, context=context)
206             partner_ids[call.id] = partner_id
207         return partner_ids
208
209
210     def redirect_phonecall_view(self, cr, uid, phonecall_id, context=None):
211         model_data = self.pool.get('ir.model.data')
212         # Select the view
213         tree_view = model_data.get_object_reference(cr, uid, 'crm', 'crm_case_phone_tree_view')
214         form_view = model_data.get_object_reference(cr, uid, 'crm', 'crm_case_phone_form_view')
215         search_view = model_data.get_object_reference(cr, uid, 'crm', 'view_crm_case_phonecalls_filter')
216         value = {
217                 'name': _('Phone Call'),
218                 'view_type': 'form',
219                 'view_mode': 'tree,form',
220                 'res_model': 'crm.phonecall',
221                 'res_id' : int(phonecall_id),
222                 'views': [(form_view and form_view[1] or False, 'form'), (tree_view and tree_view[1] or False, 'tree'), (False, 'calendar')],
223                 'type': 'ir.actions.act_window',
224                 'search_view_id': search_view and search_view[1] or False,
225         }
226         return value
227
228     def convert_opportunity(self, cr, uid, ids, opportunity_summary=False, partner_id=False, planned_revenue=0.0, probability=0.0, context=None):
229         partner = self.pool.get('res.partner')
230         opportunity = self.pool.get('crm.lead')
231         opportunity_dict = {}
232         default_contact = False
233         for call in self.browse(cr, uid, ids, context=context):
234             if not partner_id:
235                 partner_id = call.partner_id and call.partner_id.id or False
236             if partner_id:
237                 address_id = partner.address_get(cr, uid, [partner_id])['default']
238                 if address_id:
239                     default_contact = partner.browse(cr, uid, address_id, context=context)
240             opportunity_id = opportunity.create(cr, uid, {
241                             'name': opportunity_summary or call.name,
242                             'planned_revenue': planned_revenue,
243                             'probability': probability,
244                             'partner_id': partner_id or False,
245                             'mobile': default_contact and default_contact.mobile,
246                             'section_id': call.section_id and call.section_id.id or False,
247                             'description': call.description or False,
248                             'priority': call.priority,
249                             'type': 'opportunity',
250                             'phone': call.partner_phone or False,
251                             'email_from': default_contact and default_contact.email,
252                         })
253             vals = {
254                     'partner_id': partner_id,
255                     'opportunity_id' : opportunity_id,
256             }
257             self.write(cr, uid, [call.id], vals)
258             self.case_close(cr, uid, [call.id])
259             opportunity.case_open(cr, uid, [opportunity_id])
260             opportunity_dict[call.id] = opportunity_id
261         return opportunity_dict
262
263     def action_make_meeting(self, cr, uid, ids, context=None):
264         """
265         Open meeting's calendar view to schedule a meeting on current phonecall.
266         :return dict: dictionary value for created meeting view
267         """
268         phonecall = self.browse(cr, uid, ids[0], context)
269         res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid, 'base_calendar', 'action_crm_meeting', context)
270         res['context'] = {
271             'default_phonecall_id': phonecall.id,
272             'default_partner_id': phonecall.partner_id and phonecall.partner_id.id or False,
273             'default_user_id': uid,
274             'default_email_from': phonecall.email_from,
275             'default_state': 'open',
276             'default_name': phonecall.name,
277         }
278         return res
279
280     def action_button_convert2opportunity(self, cr, uid, ids, context=None):
281         """
282         Convert a phonecall into an opp and then redirect to the opp view.
283
284         :param list ids: list of calls ids to convert (typically contains a single id)
285         :return dict: containing view information
286         """
287         if len(ids) != 1:
288             raise osv.except_osv(_('Warning!'),_('It\'s only possible to convert one phonecall at a time.'))
289
290         opportunity_dict = self.convert_opportunity(cr, uid, ids, context=context)
291         return self.pool.get('crm.lead').redirect_opportunity_view(cr, uid, opportunity_dict[ids[0]], context)
292
293     # ----------------------------------------
294     # OpenChatter
295     # ----------------------------------------
296
297     def case_get_note_msg_prefix(self, cr, uid, id, context=None):
298         return 'Phonecall'
299
300     def case_reset_send_note(self, cr, uid, ids, context=None):
301         message = _('Phonecall has been <b>reset and set as open</b>.')
302         return self.message_post(cr, uid, ids, body=message, context=context)
303
304     def case_open_send_note(self, cr, uid, ids, context=None):
305         lead_obj = self.pool.get('crm.lead')
306         for phonecall in self.browse(cr, uid, ids, context=context):
307             if phonecall.opportunity_id:
308                 lead = phonecall.opportunity_id
309                 # convert datetime field to a datetime, using server format, then
310                 # convert it to the user TZ and re-render it with %Z to add the timezone
311                 phonecall_datetime = fields.DT.datetime.strptime(phonecall.date, DEFAULT_SERVER_DATETIME_FORMAT)
312                 phonecall_date_str = fields.datetime.context_timestamp(cr, uid, phonecall_datetime, context=context).strftime(DATETIME_FORMATS_MAP['%+'] + " (%Z)")
313                 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)
314             else:
315                 message = _("Phonecall has been <b>created and opened</b>.")
316             phonecall.message_post(body=message)
317         return True
318
319     def _call_set_partner_send_note(self, cr, uid, ids, context=None):
320         return self.message_post(cr, uid, ids, body=_("Partner has been <b>created</b>."), context=context)
321
322 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: