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