[ADD] crm: mass assign apply deduplication
[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             try:
121                 res_id = model_data._get_id(cr, uid, 'crm', 'categ_phone2')
122                 categ_id = model_data.browse(cr, uid, res_id, context=context).res_id
123             except ValueError:
124                 pass
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.write(cr, uid, [new_id], {'state': 'done'}, context=context)
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                 'state': 'done',
257             }
258             self.write(cr, uid, [call.id], vals, context=context)
259             opportunity_dict[call.id] = opportunity_id
260         return opportunity_dict
261
262     def action_make_meeting(self, cr, uid, ids, context=None):
263         """
264         Open meeting's calendar view to schedule a meeting on current phonecall.
265         :return dict: dictionary value for created meeting view
266         """
267         phonecall = self.browse(cr, uid, ids[0], context)
268         res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid, 'calendar', 'action_calendar_event', context)
269         res['context'] = {
270             'default_phonecall_id': phonecall.id,
271             'default_partner_id': phonecall.partner_id and phonecall.partner_id.id or False,
272             'default_user_id': uid,
273             'default_email_from': phonecall.email_from,
274             'default_state': 'open',
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: