[MERGE] atp team backlog 3.4
[odoo/odoo.git] / addons / import_sugarcrm / import_sugarcrm.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 from osv import fields, osv
22 import sugar
23 from tools.translate import _
24 from import_base.import_framework import *
25 from import_base.mapper import *
26 from datetime import datetime
27 import base64
28 import pprint
29 from papyon.service.SOAPService import url_split
30 pp = pprint.PrettyPrinter(indent=4)
31 #copy old import here
32
33 class related_ref(dbmapper):
34     def __init__(self, type):
35         self.type = type
36         
37     def __call__(self, external_val):
38         if external_val.get('parent_type') in self.type and external_val.get('parent_id'):
39             return self.parent.xml_id_exist(external_val['parent_type'], external_val['parent_id'])
40         return ''
41
42 class sugar_import(import_framework):
43     URL = False   
44     TABLE_CONTACT = 'Contacts'
45     TABLE_ACCOUNT = 'Accounts'
46     TABLE_USER = 'Users'
47     TABLE_EMPLOYEE = 'Employees'
48     TABLE_RESSOURCE = "resource"
49     TABLE_OPPORTUNITY = 'Opportunities'
50     TABLE_LEAD = 'Leads'
51     TABLE_STAGE = 'crm_stage'
52     TABLE_ATTENDEE = 'calendar_attendee'
53     TABLE_CALL = 'Calls'
54     TABLE_MEETING = 'Meetings'
55     TABLE_TASK = 'Tasks'
56     TABLE_PROJECT = 'Project'
57     TABLE_PROJECT_TASK = 'ProjectTask'
58     TABLE_BUG = 'Bugs'
59     TABLE_CASE = 'Cases'
60     TABLE_NOTE = 'Notes'
61     TABLE_EMAIL = 'Emails'
62     TABLE_COMPAIGN = 'Campaigns'
63     TABLE_DOCUMENT = 'DocumentRevisions'
64     TABLE_HISTORY_ATTACHMNET = 'history_attachment'
65     
66     def initialize(self):
67         #login
68         PortType,sessionid = sugar.login(self.context.get('username',''), self.context.get('password',''), self.context.get('url',''))
69         if sessionid == '-1':
70             raise osv.except_osv(_('Error !'), _('Authentication error !\nBad Username or Password or bad SugarSoap Api url !'))
71         self.context['port'] = PortType
72         self.context['session_id'] = sessionid
73         
74     def get_data(self, table):
75         return sugar.search(self.context.get('port'), self.context.get('session_id'), table, 0, 500)
76     
77     """
78     Common import method
79     """
80     def get_category(self, val, model, name):
81         fields = ['name', 'object_id']
82         data = [name, model]
83         return self.import_object(fields, data, 'crm.case.categ', 'crm_categ', name, [('object_id.model','=',model), ('name', 'ilike', name)])
84
85     def get_job_title(self, dict, salutation):
86         fields = ['shortcut', 'name', 'domain']
87         if salutation:
88             data = [salutation, salutation, 'Contact']
89             return self.import_object(fields, data, 'res.partner.title', 'contact_title', salutation, [('shortcut', '=', salutation)])
90
91     def get_channel_id(self, dict, val):
92         if not val:
93             return False
94         fields = ['name']
95         data = [val]
96         return self.import_object(fields, data, 'res.partner.canal', 'crm_channel', val)
97     
98     def get_all_states(self, external_val, country_id):
99         """Get states or create new state unless country_id is False"""
100         state_code = external_val[0:3] #take the tree first char
101         fields = ['country_id/id', 'name', 'code']
102         data = [country_id, external_val, state_code]
103         if country_id:
104             return self.import_object(fields, data, 'res.country.state', 'country_state', external_val) 
105         return False
106
107     def get_all_countries(self, val):
108         """Get Country, if no country match do not create anything, to avoid duplicate country code"""
109         return self.mapped_id_if_exist('res.country', [('name', 'ilike', val)], 'country', val)
110     
111     def get_float_time(self, dict, hour, min):
112         min = int(min) * 100 / 60
113         return "%s.%i" % (hour, min)
114     
115     """
116     import Documents
117     """
118     
119     def import_document(self, val):
120         File,Filename = sugar.get_document_revision_search(self.context.get('port'), self.context.get('session_id'), val.get('id'))
121         val['datas'] = base64.encodestring(File)
122         val['datas_fname'] = Filename
123         return val   
124         
125     def get_document_mapping(self): 
126         return { 
127                 'model' : 'ir.attachment',
128                 'dependencies' : [self.TABLE_USER],
129                 'hook' : self.import_document,
130                 'map' : {'name':'filename',
131                          'description': ppconcat('description'),
132                          'datas': 'datas',
133                          'datas_fname': 'datas_fname',
134                 }
135             }     
136         
137     
138     """
139     import Emails
140     """
141
142     def get_email_attachment(self, val):
143         File, Filename = sugar.attachment_search(self.context.get('port'), self.context.get('session_id'), self.TABLE_EMAIL, val.get('id')) 
144         attach_xml_id = False
145         if File:
146             fields = ['name', 'datas', 'datas_fname','res_id', 'res_model']
147             name = 'attachment_'+ (Filename or val.get('name'))
148             datas = [Filename or val.get('name'), File, Filename, val.get('res_id'),val.get('model',False)]
149             attach_xml_id = self.import_object(fields, datas, 'ir.attachment', self.TABLE_HISTORY_ATTACHMNET, name, [('res_id', '=', val.get('res_id'), ('model', '=', val.get('model')))])
150         return attach_xml_id
151     
152     def import_email(self, val):
153         vals = sugar.email_search(self.context.get('port'), self.context.get('session_id'), self.TABLE_EMAIL, val.get('id'))
154         model_obj =  self.obj.pool.get('ir.model.data')
155         for val in vals:
156             xml_id = self.xml_id_exist(val.get('parent_type'), val.get('parent_id'))
157             model_ids = model_obj.search(self.cr, self.uid, [('name', 'like', xml_id)])
158             if model_ids:
159                 model = model_obj.browse(self.cr, self.uid, model_ids)[0]
160                 if model.model == 'res.partner':
161                     val['partner_id/.id'] = model.res_id
162                 else:    
163                     val['res_id'] = model.res_id
164                     val['model'] = model.model
165         return val   
166         
167     def get_email_mapping(self): 
168         return { 
169                 'model' : 'mailgate.message',
170                 'dependencies' : [self.TABLE_USER, self.TABLE_PROJECT, self.TABLE_PROJECT_TASK, self.TABLE_ACCOUNT, self.TABLE_CONTACT, self.TABLE_LEAD, self.TABLE_OPPORTUNITY, self.TABLE_MEETING, self.TABLE_CALL],
171                 'hook' : self.import_email,
172                 'map' : {'name':'name',
173                          'history' : const("1"),
174                         'date':'date_sent',
175                         'email_from': 'from_addr_name',
176                         'email_to': 'to_addrs_names',
177                         'email_cc': 'cc_addrs_names',
178                         'email_bcc': 'bcc_addrs_names',
179                         'message_id': 'message_id',
180                         'res_id': 'res_id',
181                         'model': 'model',
182                         'partner_id/.id': 'partner_id/.id',                         
183                         'attachment_ids/id': self.get_email_attachment,
184                         'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
185                         'description': ppconcat('description', 'description_html'),
186                 }
187             } 
188     
189     """
190     import History(Notes)
191     """
192     def get_note_attachment(self, val):
193         File, Filename = sugar.attachment_search(self.context.get('port'), self.context.get('session_id'), self.TABLE_NOTE, val.get('id')) 
194         attach_xml_id = False
195         if File:
196             fields = ['name', 'datas', 'datas_fname','res_id', 'res_model']
197             name = 'attachment_'+ (Filename or val.get('name'))
198             datas = [Filename or val.get('name'), File, Filename, val.get('res_id'),val.get('model',False)]
199             attach_xml_id = self.import_object(fields, datas, 'ir.attachment', self.TABLE_HISTORY_ATTACHMNET, name, [('res_id', '=', val.get('res_id'), ('model', '=', val.get('model')))])
200         return attach_xml_id
201
202     def import_history(self, val):
203         model_obj =  self.obj.pool.get('ir.model.data')
204         xml_id = self.xml_id_exist(val.get('parent_type'), val.get('parent_id'))
205         model_ids = model_obj.search(self.cr, self.uid, [('name', 'like', xml_id)])
206         if model_ids:
207             model = model_obj.browse(self.cr, self.uid, model_ids)[0]
208             if model.model == 'res.partner':
209                 val['partner_id/.id'] = model.res_id
210                 val['history'] = "1"
211             else:    
212                 val['res_id'] = model.res_id
213                 val['model'] = model.model
214         return val    
215     
216     def get_history_mapping(self): 
217         return { 
218                 'model' : 'mailgate.message',
219                 'dependencies' : [self.TABLE_USER, self.TABLE_PROJECT, self.TABLE_PROJECT_TASK, self.TABLE_ACCOUNT, self.TABLE_CONTACT, self.TABLE_LEAD, self.TABLE_OPPORTUNITY, self.TABLE_MEETING, self.TABLE_CALL],
220                 'hook' : self.import_history,
221                 'map' : {
222                       'name':'name',
223                       'date': 'date_entered',
224                       'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
225                       'description': ppconcat('description', 'description_html'),
226                       'res_id': 'res_id',
227                       'model': 'model',
228                       'attachment_ids/id': self.get_note_attachment,
229                       'partner_id/.id' : 'partner_id/.id',
230                       'history' : 'history',
231                 }
232             }     
233     
234     """
235     import Claims(Cases)
236     """
237     def get_claim_priority(self, val):
238         priority_dict = {            
239                 'P1': '2',
240                 'P2': '3',
241                 'P3': '4'
242         }
243         return priority_dict.get(val.get('priority'), '')
244         
245     def get_contact_info_from_account(self, val):
246         partner_id = self.get_mapped_id(self.TABLE_ACCOUNT, val.get('account_id'))
247         partner_address_id = False
248         partner_phone = False
249         partner_email = False
250         partner = self.obj.pool.get('res.partner').browse(self.cr, self.uid, [partner_id])[0]
251         if partner.address and partner.address[0]:
252             address = partner.address[0]
253             partner_address_id = address.id
254             partner_phone = address.phone
255             partner_email = address.email
256         return partner_address_id, partner_phone,partner_email
257     
258     def import_crm_claim(self, val):
259         partner_address_id, partner_phone,partner_email =  self.get_contact_info_from_account(val)
260         val['partner_address_id/.id'] = partner_address_id
261         val['partner_phone'] = partner_phone
262         val['email_from'] = partner_email
263         return val
264     
265     def get_crm_claim_mapping(self): 
266         return { 
267                 'model' : 'crm.claim',
268                 'dependencies' : [self.TABLE_USER, self.TABLE_ACCOUNT, self.TABLE_CONTACT, self.TABLE_LEAD],
269                 'hook' : self.import_crm_claim,
270                 'map' : {
271                     'name': concat('case_number','name', delimiter='-'),
272                     'date': 'date_entered',
273                     'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
274                     'description': ppconcat('description', 'resolution', 'work_log'),
275                     'partner_id/id': ref(self.TABLE_ACCOUNT, 'account_id'),
276                     'partner_address_id/.id': 'partner_address_id/.id',
277                     'categ_id/id': call(self.get_category, 'crm.claim', value('type')),
278                     'partner_phone': 'partner_phone',
279                     'email_from': 'email_from',                                        
280                     'priority': self.get_claim_priority,
281                     'state': map_val('status', self.project_issue_state)
282                 }
283             }    
284     """
285     Import Project Issue(Bugs)
286     """
287     project_issue_state = {
288             'New' : 'draft',
289             'Assigned':'open',
290             'Closed': 'done',
291             'Pending': 'pending',
292             'Rejected': 'cancel',
293     }
294      
295     def get_project_issue_priority(self, val):
296         priority_dict = {
297                 'Urgent': '1',
298                 'High': '2',
299                 'Medium': '3',
300                 'Low': '4'
301          }
302         return priority_dict.get(val.get('priority'), '')     
303       
304     def get_bug_project_id(self, dict, val):
305         fields = ['name']
306         data = [val]
307         return self.import_object(fields, data, 'project.project', 'project_issue', val)    
308     
309     def get_project_issue_mapping(self):
310         return { 
311                 'model' : 'project.issue',
312                 'dependencies' : [self.TABLE_USER],
313                 'map' : {
314                     'name': concat('bug_number', 'name', delimiter='-'),
315                     'project_id/id': call(self.get_bug_project_id, 'sugarcrm_bugs'),
316                     'categ_id/id': call(self.get_category, 'project.issue', value('type')),
317                     'description': ppconcat('description', 'source', 'resolution', 'work_log', 'found_in_release', 'release_name', 'fixed_in_release_name', 'fixed_in_release'),
318                     'priority': self.get_project_issue_priority,
319                     'state': map_val('status', self.project_issue_state),
320                     'assigned_to/id' : ref(self.TABLE_USER, 'assigned_user_id'),
321                 }
322             }
323     
324     """
325     import Project Tasks
326     """
327     project_task_state = {
328             'Not Started': 'draft',
329             'In Progress': 'open',
330             'Completed': 'done',
331             'Pending Input': 'pending',
332             'Deferred': 'cancelled',
333      }
334     
335     def get_project_task_priority(self, val):
336         priority_dict = {
337             'High': '0',
338             'Medium': '2',
339             'Low': '3'
340         }
341         return priority_dict.get(val.get('priority'), '')
342     
343     def get_project_task_mapping(self):
344         return { 
345                 'model' : 'project.task',
346                 'dependencies' : [self.TABLE_USER, self.TABLE_PROJECT],
347                 'map' : {
348                     'name': 'name',
349                     'date_start': 'date_start',
350                     'date_end': 'date_finish',
351                     'project_id/id': ref(self.TABLE_PROJECT, 'project_id'),
352                     'planned_hours': 'estimated_effort',
353                     'priority': self.get_project_task_priority,
354                     'description': ppconcat('description','milestone_flag', 'project_task_id', 'task_number', 'percent_complete'),
355                     'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
356                     'partner_id/id': 'partner_id/id',
357                     'contact_id/id': 'contact_id/id',
358                     'state': map_val('status', self.project_task_state)
359                 }
360             }
361
362     """
363     import Projects
364     """
365     project_state = {
366             'Draft' : 'draft',
367             'In Review': 'open',
368             'Published': 'close'
369      }
370     
371     def import_project_account(self, val):
372         partner_id = False
373         partner_invoice_id = False        
374         sugar_project_account = sugar.relation_search(self.context.get('port'), self.context.get('session_id'), 'Project', module_id=val.get('id'), related_module=self.TABLE_ACCOUNT, query=None, deleted=None)
375         sugar_project_contact = sugar.relation_search(self.context.get('port'), self.context.get('session_id'), 'Project', module_id=val.get('id'), related_module=self.TABLE_CONTACT, query=None, deleted=None)
376         for contact_id in sugar_project_contact:
377             partner_invoice_id = self.get_mapped_id(self.TABLE_CONTACT, contact_id)
378         for account_id in sugar_project_account:
379             partner_id = self.get_mapped_id(self.TABLE_ACCOUNT, account_id)
380         return partner_id, partner_invoice_id      
381            
382     def import_project(self, val):
383         partner_id, partner_invoice_id  = self.import_project_account(val)    
384         val['partner_id/.id'] = partner_id
385         val['contact_id/.id'] = partner_invoice_id
386         return val
387     
388     def get_project_mapping(self):
389         return { 
390                 'model' : 'project.project',
391                 'dependencies' : [self.TABLE_CONTACT, self.TABLE_ACCOUNT, self.TABLE_USER],
392                 'hook' : self.import_project,
393                 'map' : {
394                     'name': 'name',
395                     'date_start': 'estimated_start_date',
396                     'date': 'estimated_end_date',
397                     'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
398                     'partner_id/.id': 'partner_id/.id',
399                     'contact_id/.id': 'contact_id/.id',
400                     'state': map_val('status', self.project_state)
401                 }
402             }
403     
404     """
405     import Tasks
406     """
407     task_state = {
408             'Completed' : 'done',
409             'Not Started':'draft',
410             'In Progress': 'open',
411             'Pending Input': 'draft',
412             'deferred': 'cancel'
413         }
414
415     def import_task(self, val):
416         val['date'] = val.get('date_start') or datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
417         val['date_deadline'] = val.get('date_due') or datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
418         return val
419
420     def get_task_mapping(self):
421         return { 
422                 'model' : 'crm.meeting',
423                 'dependencies' : [self.TABLE_CONTACT, self.TABLE_ACCOUNT, self.TABLE_USER],
424                 'hook' : self.import_task,
425                 'map' : {
426                     'name': 'name',
427                     'date': 'date',
428                     'date_deadline': 'date_deadline',
429                     'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
430                     'categ_id/id': call(self.get_category, 'crm.meeting', const('Tasks')),
431                     'partner_id/id': related_ref(self.TABLE_ACCOUNT),
432                     'partner_address_id/id': ref(self.TABLE_CONTACT,'contact_id'),
433                     'state': map_val('status', self.task_state)
434                 }
435             }
436        
437     """
438     import Calls
439     """  
440     #TODO adapt with project trunk-crm-imp   
441     call_state = {   
442             'Planned' : 'open',
443             'Held':'done',
444             'Not Held': 'pending',
445         }
446
447     def get_calls_mapping(self):
448         return { 
449                 'model' : 'crm.phonecall',
450                 'dependencies' : [self.TABLE_ACCOUNT, self.TABLE_CONTACT, self.TABLE_OPPORTUNITY, self.TABLE_LEAD],
451                 'map' : {
452                     'name': 'name',
453                     'date': 'date_start',
454                     'duration': call(self.get_float_time, value('duration_hours'), value('duration_minutes')),
455                     'user_id/id':  ref(self.TABLE_USER, 'assigned_user_id'),
456                     'partner_id/id': related_ref(self.TABLE_ACCOUNT),
457                     'partner_address_id/id': related_ref(self.TABLE_CONTACT),
458                     'categ_id/id': call(self.get_category, 'crm.phonecall', value('direction')),
459                     'opportunity_id/id': related_ref(self.TABLE_OPPORTUNITY),
460                     'description': ppconcat('description'),   
461                     'state': map_val('status', self.call_state)                      
462                 }
463             }       
464          
465     """
466         import meeting
467     """
468     meeting_state = {
469             'Planned' : 'draft',
470             'Held': 'open',
471             'Not Held': 'draft', 
472         }
473 #TODO    
474     def get_attendee_id(self, cr, uid, module_name, module_id):
475         contact_id = False
476         user_id = False
477         attendee_id= []
478         attendee_dict = sugar.user_get_attendee_list(self.context.get('port'), self.context.get('session_id'), module_name, module_id)
479         for attendee in attendee_dict:
480             user_id = self.xml_id_exist(self.TABLE_USER, attendee.get('id', False))
481             contact_id = False
482             if not user_id:
483                 contact_id = self.xml_id_exist(self.TABLE_CONTACT, attendee.get('id', False))
484             fields = ['user_id/id', 'email', 'partner_address_id/id']
485             data = [user_id, attendee.get('email1'), contact_id]
486             attendee_xml_id = self.import_object(fields, data, 'calendar.attendee', self.TABLE_ATTENDEE, user_id or contact_id or attendee.get('email1'), ['|',('user_id', '=', attendee.get('id')),('partner_address_id','=',attendee.get('id')),('email', '=', attendee.get('email1'))])
487             attendee_id.append(attendee_xml_id)
488         return ','.join(attendee_id) 
489     
490     def get_alarm_id(self, dict_val, val):
491         alarm_dict = {
492             '60': '1 minute before',
493             '300': '5 minutes before',
494             '600': '10 minutes before',
495             '900': '15 minutes before',
496             '1800':'30 minutes before',
497             '3600': '1 hour before',
498         }
499         return self.mapped_id_if_exist('res.alarm', [('name', 'like', alarm_dict.get(val))], 'alarm', val)
500     
501     #TODO attendees
502
503     def import_meeting(self, val):
504         attendee_id = self.get_attendee_id(self.cr, self.uid, 'Meetings', val.get('id')) #TODO
505         val['attendee_ids/id'] = attendee_id
506         return val
507
508     def get_meeting_mapping(self):
509         return { 
510                 'model' : 'crm.meeting',
511                 'dependencies' : [self.TABLE_CONTACT, self.TABLE_OPPORTUNITY, self.TABLE_LEAD, self.TABLE_TASK],
512                 'hook': self.import_meeting,
513                 'map' : {
514                     'name': 'name',
515                     'date': 'date_start',
516                     'duration': call(self.get_float_time, value('duration_hours'), value('duration_minutes')),
517                     'location': 'location',
518                     'attendee_ids/id':'attendee_ids/id',
519                     'alarm_id/id': call(self.get_alarm_id, value('reminder_time')),
520                     'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
521                     'partner_id/id': related_ref(self.TABLE_ACCOUNT),
522                     'partner_address_id/id': related_ref(self.TABLE_CONTACT),
523                     'state': map_val('status', self.meeting_state)
524                 }
525             }
526     
527     """
528         import Opportunity
529     """
530     opp_state = {
531             'Need Analysis' : 'New',
532             'Closed Lost': 'Lost',
533             'Closed Won': 'Won', 
534             'Value Proposition': 'Proposition',
535             'Negotiation/Review': 'Negotiation'
536         }
537         
538     def get_opportunity_status(self, sugar_val):
539         fields = ['name', 'type']
540         name = 'Opportunity_' + sugar_val['sales_stage']
541         data = [sugar_val['sales_stage'], 'Opportunity']
542         return self.import_object(fields, data, 'crm.case.stage', self.TABLE_STAGE, name, [('type', '=', 'opportunity'), ('name', 'ilike', sugar_val['sales_stage'])])
543     
544     def import_opportunity_contact(self, val):
545         sugar_opportunities_contact = set(sugar.relation_search(self.context.get('port'), self.context.get('session_id'), 'Opportunities', module_id=val.get('id'), related_module='Contacts', query=None, deleted=None))
546             
547         partner_contact_id = False 
548         partner_contact_email = False       
549         partner_address_obj = self.obj.pool.get('res.partner.address')
550         partner_xml_id = self.name_exist(self.TABLE_ACCOUNT, val['account_name'], 'res.partner')
551         
552         for contact in sugar_opportunities_contact:
553             address_id = self.get_mapped_id(self.TABLE_CONTACT, contact)
554             if address_id:                    
555                 address = partner_address_obj.browse(self.cr, self.uid, address_id)
556                 partner_name = address.partner_id and address.partner_id.name or False
557                 if not partner_name: #link with partner id 
558                     fields = ['partner_id/id']
559                     data = [partner_xml_id]
560                     self.import_object(fields, data, 'res.partner.address', self.TABLE_CONTACT, contact, self.DO_NOT_FIND_DOMAIN)
561                 if not partner_name or partner_name == val.get('account_name'):
562                     partner_contact_id = self.xml_id_exist(self.TABLE_CONTACT, contact)
563                     partner_contact_email = address.email
564         return partner_contact_id, partner_contact_email
565
566     def import_opp(self, val):    
567         partner_contact_id, partner_contact_email = self.import_opportunity_contact(val)
568         val['partner_address_id/id'] = partner_contact_id
569         val['email_from'] = partner_contact_email
570         return val
571     
572     def get_opp_mapping(self):
573         return {
574             'model' : 'crm.lead',
575             'dependencies' : [self.TABLE_USER, self.TABLE_ACCOUNT, self.TABLE_CONTACT,self.TABLE_COMPAIGN],
576             'hook' : self.import_opp,
577             'map' :  {
578                 'name': 'name',
579                 'probability': 'probability',
580                 'partner_id/id': refbyname(self.TABLE_ACCOUNT, 'account_name', 'res.partner'),
581                 'title_action': 'next_step',
582                 'partner_address_id/id': 'partner_address_id/id',
583                 'planned_revenue': 'amount',
584                 'date_deadline': 'date_closed',
585                 'user_id/id' : ref(self.TABLE_USER, 'assigned_user_id'),
586                 'stage_id/id' : self.get_opportunity_status,
587                 'type' : const('opportunity'),
588                 'categ_id/id': call(self.get_category, 'crm.lead', value('opportunity_type')),
589                 'email_from': 'email_from',
590                 'state': map_val('status', self.opp_state)  , #TODO
591             }
592         }
593         
594     """
595     import campaign
596     """
597     
598     def get_compaign_mapping(self):
599         return {
600             'model' : 'crm.case.resource.type',
601             'map' : {
602                 'name': 'name',
603                 } 
604         }    
605         
606     """
607         import lead
608     """
609     def get_lead_status(self, sugar_val):
610         fields = ['name', 'type']
611         name = 'lead_' + sugar_val.get('status', '')
612         data = [sugar_val.get('status', ''), 'lead']
613         return self.import_object(fields, data, 'crm.case.stage', self.TABLE_STAGE, name, [('type', '=', 'lead'), ('name', 'ilike', sugar_val.get('status', ''))])
614
615     lead_state = {
616         'New' : 'draft',
617         'Assigned':'open',
618         'In Progress': 'open',
619         'Recycled': 'cancel',
620         'Dead': 'done',
621         'Converted': 'done',
622     }
623     
624     def import_lead(self, val):
625         if val.get('opportunity_id'): #if lead is converted into opp, don't import as lead
626             return False
627         if val.get('primary_address_country'):
628             country_id = self.get_all_countries(val.get('primary_address_country'))
629             val['country_id/id'] =  country_id
630             val['state_id/id'] =  self.get_all_states(val.get('primary_address_state'), country_id)
631         return val
632     
633     def get_lead_mapping(self):
634         return {
635             'model' : 'crm.lead',
636             'dependencies' : [self.TABLE_COMPAIGN, self.TABLE_USER],
637             'hook' : self.import_lead,
638             'map' : {
639                 'name': concat('first_name', 'last_name'),
640                 'contact_name': concat('first_name', 'last_name'),
641                 'description': ppconcat('description', 'refered_by', 'lead_source', 'lead_source_description', 'website', 'email2', 'status_description', 'lead_source_description', 'do_not_call'),
642                 'partner_name': 'account_name',
643                 'email_from': 'email1',
644                 'phone': 'phone_work',
645                 'mobile': 'phone_mobile',
646                 'title/id': call(self.get_job_title, value('salutation')),
647                 'function':'title',
648                 'street': 'primary_address_street',
649                 'street2': 'alt_address_street',
650                 'zip': 'primary_address_postalcode',
651                 'city':'primary_address_city',
652                 'user_id/id' : ref(self.TABLE_USER, 'assigned_user_id'),
653                 'stage_id/id' : self.get_lead_status,
654                 'type' : const('lead'),
655                 'state': map_val('status', self.lead_state) ,
656                 'fax': 'phone_fax',
657                 'referred': 'refered_by',
658                 'optout': 'do_not_call',
659                 'channel_id/id': call(self.get_channel_id, value('lead_source')),
660                 'type_id/id': ref(self.TABLE_COMPAIGN, 'campaign_id'),
661                 'country_id/id': 'country_id/id',
662                 'state_id/id': 'state_id/id'
663                 } 
664         }
665     
666     """
667         import contact
668     """
669     
670     def get_email(self, val):
671         email_address = sugar.get_contact_by_email(self.context.get('port'), self.context.get('username'), self.context.get('password'), val.get('email1'))
672         if email_address:
673             return ','.join(email_address)     
674     
675     def import_contact(self, val):
676         if val.get('primary_address_country'):
677             country_id = self.get_all_countries(val.get('primary_address_country'))
678             state = self.get_all_states(val.get('primary_address_state'), country_id)
679             val['country_id/id'] =  country_id
680             val['state_id/id'] =  state
681         return val    
682         
683     def get_contact_mapping(self):
684         return { 
685             'model' : 'res.partner.address',
686             'dependencies' : [self.TABLE_ACCOUNT],
687             'hook' : self.import_contact,
688             'map' :  {
689                 'name': concat('first_name', 'last_name'),
690                 'partner_id/id': ref(self.TABLE_ACCOUNT,'account_id'),
691                 'phone': 'phone_work',
692                 'mobile': 'phone_mobile',
693                 'fax': 'phone_fax',
694                 'function': 'title',
695                 'street': 'primary_address_street',
696                 'zip': 'primary_address_postalcode',
697                 'city': 'primary_address_city',
698                 'country_id/id': 'country_id/id',
699                 'state_id/id': 'state_id/id',
700                 'email': self.get_email,
701                 'type': const('contact')
702             }
703         }
704     
705     """ 
706         import Account
707     """
708     
709     def get_address_type(self, val, type):
710         if type == 'invoice':
711             type_address = 'billing'
712         else:
713             type_address = 'shipping' 
714             
715         if type == 'default':
716              map_partner_address = {
717             'name': 'name',
718             'type': const('default'),
719             'email': 'email1' 
720             }
721         else:        
722             map_partner_address = {
723                 'name': 'name',
724                 'phone': 'phone_office',
725                 'mobile': 'phone_mobile',
726                 'fax': 'phone_fax',
727                 'type': 'type',
728                 'street': type_address + '_address_street',
729                 'zip': type_address +'_address_postalcode',
730                 'city': type_address +'_address_city',
731                  'country_id/id': 'country_id/id',
732                  'type': 'type',
733                 }
734             
735         if val.get(type_address +'_address_country'):
736             country_id = self.get_all_countries(val.get(type_address +'_address_country'))
737             state = self.get_all_states(val.get(type_address +'_address_state'), country_id)
738             val['country_id/id'] =  country_id
739             val['state_id/id'] =  state
740             
741         val['type'] = type
742         val['id_new'] = val['id'] + '_address_' + type
743         return self.import_object_mapping(map_partner_address, val, 'res.partner.address', self.TABLE_CONTACT, val['id_new'], self.DO_NOT_FIND_DOMAIN) 
744         
745     def get_partner_address(self, val):
746         address_id=[]
747         type_dict = {'billing_address_street' : 'invoice', 'shipping_address_street' : 'delivery', 'type': 'default'}
748         for key, type_value in type_dict.items():
749             if val.get(key):
750                 id = self.get_address_type(val, type_value)
751                 address_id.append(id)
752           
753         return ','.join(address_id)
754     
755     def get_partner_mapping(self):
756         return {
757                 'model' : 'res.partner',
758                 'dependencies' : [self.TABLE_USER],
759                 'map' : {
760                     'name': 'name',
761                     'website': 'website',
762                     'user_id/id': ref(self.TABLE_USER,'assigned_user_id'),
763                     'ref': 'sic_code',
764                     'comment': ppconcat('description', 'employees', 'ownership', 'annual_revenue', 'rating', 'industry', 'ticker_symbol'),
765                     'customer': const('1'),
766                     'supplier': const('0'),
767                     'address/id':'address/id', 
768                     'parent_id/id_parent' : 'parent_id',
769                     'address/id' : self.get_partner_address,
770                 }
771         }
772
773     """
774         import Employee
775     """
776     def get_ressource(self, val):
777         map_resource = { 
778             'name': concat('first_name', 'last_name'),
779         }        
780         return self.import_object_mapping(map_resource, val, 'resource.resource', self.TABLE_RESSOURCE, val['id'], self.DO_NOT_FIND_DOMAIN)
781     
782     def get_job_id(self, val):
783         fields = ['name']
784         data = [val.get('title')]
785         return self.import_object(fields, data, 'hr.job', 'hr_job', val.get('title'))
786
787     def get_user_address(self, val):
788         map_user_address = {
789             'name': concat('first_name', 'last_name'),
790             'city': 'address_city',
791             'country_id/id': 'country_id/id',
792             'state_id/id': 'state_id/id',
793             'street': 'address_street',
794             'zip': 'address_postalcode',
795             'fax': 'phone_fax',
796             'phone': 'phone_work',
797             'mobile':'phone_mobile',
798             'email': 'email1'
799         }
800         
801         if val.get('address_country'):
802             country_id = self.get_all_countries(val.get('address_country'))
803             state_id = self.get_all_states(val.get('address_state'), country_id)
804             val['country_id/id'] =  country_id
805             val['state_id/id'] =  state_id
806             
807         return self.import_object_mapping(map_user_address, val, 'res.partner.address', self.TABLE_CONTACT, val['id'], self.DO_NOT_FIND_DOMAIN)
808
809     def get_employee_mapping(self):
810         return {
811             'model' : 'hr.employee',
812             'dependencies' : [self.TABLE_USER],
813             'map' : {
814                 'resource_id/id': self.get_ressource, 
815                 'name': concat('first_name', 'last_name'),
816                 'work_phone': 'phone_work',
817                 'mobile_phone':  'phone_mobile',
818                 'user_id/id': ref(self.TABLE_USER, 'id'), 
819                 'address_home_id/id': self.get_user_address,
820                 'notes': ppconcat('messenger_type', 'messenger_id', 'description'),
821                 'job_id/id': self.get_job_id,
822                 'work_email' : 'email1',
823                 'coach_id/id_parent' : 'reports_to_id',
824             }
825      }
826     
827     """
828         import user
829     """  
830     def import_user(self, val):
831         user_obj = self.obj.pool.get('res.users')
832         user_ids = user_obj.search(self.cr, self.uid, [('login', '=', val.get('user_name'))])
833         if user_ids: 
834             val['.id'] = str(user_ids[0])
835         else:
836             val['password'] = 'sugarcrm' #default password for all user #TODO needed in documentation
837             
838         val['context_lang'] = self.context.get('lang','en_US')
839         return val
840     
841     def get_users_department(self, val):
842         dep = val.get('department')
843         fields = ['name']
844         data = [dep]
845         if not dep:
846             return False
847         return self.import_object(fields, data, 'hr.department', 'hr_department_user', dep)
848
849     def get_user_mapping(self):
850         return {
851             'model' : 'res.users',
852             'hook' : self.import_user,
853             'map' : { 
854                 'name': concat('first_name', 'last_name'),
855                 'login': 'user_name',
856                 'context_lang' : 'context_lang',
857                 'password' : 'password',
858                 '.id' : '.id',
859                 'context_department_id/id': self.get_users_department,
860                 'user_email' : 'email1',
861             }
862         }
863
864
865     def get_mapping(self):
866         return {
867             self.TABLE_USER : self.get_user_mapping(),
868             self.TABLE_EMPLOYEE : self.get_employee_mapping(),
869             self.TABLE_ACCOUNT : self.get_partner_mapping(),
870             self.TABLE_CONTACT : self.get_contact_mapping(),
871             self.TABLE_LEAD : self.get_lead_mapping(),
872             self.TABLE_OPPORTUNITY : self.get_opp_mapping(),
873             self.TABLE_MEETING : self.get_meeting_mapping(),
874             self.TABLE_CALL : self.get_calls_mapping(),
875             self.TABLE_TASK : self.get_task_mapping(),
876             self.TABLE_PROJECT : self.get_project_mapping(),
877             self.TABLE_PROJECT_TASK: self.get_project_task_mapping(),
878             self.TABLE_BUG: self.get_project_issue_mapping(),
879             self.TABLE_CASE: self.get_crm_claim_mapping(),
880             self.TABLE_NOTE: self.get_history_mapping(),
881             self.TABLE_EMAIL: self.get_email_mapping(),
882             self.TABLE_DOCUMENT: self.get_document_mapping(),
883             self.TABLE_COMPAIGN: self.get_compaign_mapping()
884         }
885         
886     """
887         Email notification
888     """   
889     def get_email_subject(self, result):
890         return "your sugarcrm data were successfully imported at %s" % self.date_ended 
891     
892     def get_body_header(self, result):
893         return "Sugarcrm import : report of last import" 
894
895
896 class import_sugarcrm(osv.osv):
897     """Import SugarCRM DATA"""
898     
899     _name = "import.sugarcrm"
900     _description = __doc__
901     _columns = {
902         'username': fields.char('User Name', size=64, required=True),
903         'password': fields.char('Password', size=24,required=True),
904          'url' : fields.char('SugarSoap Api url:', size=264, required=True, help="Webservice's url where to get the data.\
905                       example : 'http://example.com/sugarcrm/soap.php', or copy the address of your sugarcrm application http://trial.sugarcrm.com/qbquyj4802/index.php?module=Home&action=index"),
906                 
907         'opportunity': fields.boolean('Leads and Opportunities', help="If Opportunities are checked, SugarCRM opportunities data imported in OpenERP crm-Opportunity form"),
908         'contact': fields.boolean('Contacts', help="If Contacts are checked, SugarCRM Contacts data imported in OpenERP partner address form"),
909         'account': fields.boolean('Accounts', help="If Accounts are checked, SugarCRM  Accounts data imported in OpenERP partners form"),
910         'employee': fields.boolean('Employee', help="If Employees is checked, SugarCRM Employees data imported in OpenERP employees form"),
911         'meeting': fields.boolean('Meetings', help="If Meetings is checked, SugarCRM Meetings and Meeting Tasks data imported in OpenERP meetings form"),
912         'call': fields.boolean('Calls', help="If Calls is checked, SugarCRM Calls data imported in OpenERP phonecalls form"),
913         'claim': fields.boolean('Cases', help="If Cases is checked, SugarCRM Cases data imported in OpenERP Claims form"),
914         'email_history': fields.boolean('Email and History',help="If Email and History is checked, SugarCRM Notes and Emails data imported in OpenERP's Related module's History with attachment"),
915         'project': fields.boolean('Projects', help="If Projects is checked, SugarCRM Projects data imported in OpenERP Projects form"),
916         'project_task': fields.boolean('Project Tasks', help="If Project Tasks is checked, SugarCRM Project Tasks data imported in OpenERP Project Tasks form"),
917         'bug': fields.boolean('Bugs', help="If Bugs is checked, SugarCRM Bugs data imported in OpenERP Project Issues form"),
918         'document': fields.boolean('Documents', help="If Documents is checked, SugarCRM Documents data imported in OpenERP Document Form"),
919         'email_from': fields.char('Notify End Of Import To:', size=128),
920         'instance_name': fields.char("Instance's Name", size=64, help="Prefix of SugarCRM id to differentiate xml_id of SugarCRM models datas come from different server."),
921         
922     }
923     _defaults = {#to be set to true, but easier for debugging
924        'opportunity': False,
925        'contact' : False,
926        'account' : False,
927         'employee' : False,
928         'meeting' : False,
929         'call' : False,
930         'claim' : False,    
931         'email_history' : False, 
932         'project' : False,   
933         'project_task': False,     
934         'bug': False,
935         'document': False,
936         'instance_name': 'sugarcrm',
937         'email_from': 'tfr@openerp.com',
938         'username' : 'tfr',
939         'password' : 'a',
940         'url':  "http://localhost/sugarcrm/soap.php"        
941     }
942     
943     def check_url(self, url, context):
944         if not context:
945             context = {}
946         user = context.get('username')
947         password = context.get('password')
948         
949         try :
950             sugar.login(user, password, url)
951             return True
952         except Exception:
953             return False
954                 
955         
956     def parse_valid_url(self, context=None):
957         if not context:
958             context = {}
959         url = context.get('url')
960         
961         
962         url_split = str(url).split('/')
963         while len(url_split) >= 3:
964             #3 case, soap.php is already at the end of url should be valid
965             #url end by / or not
966             if url_split[-1] == 'soap.php':
967                 url = "/".join(url_split)
968             elif url_split[-1] == '':
969                 url = "/".join(url_split) + "soap.php"
970             else:
971                 url = "/".join(url_split) + "/soap.php"
972             
973             if self.check_url(url, context):
974                 return url
975             else: 
976                 url_split = url_split[:-1] #take parent folder
977         return url
978             
979     def get_key(self, cr, uid, ids, context=None):
980         """Select Key as For which Module data we want import data."""
981         if not context:
982             context = {}
983         key_list = []
984         for current in self.browse(cr, uid, ids, context):
985             context.update({'username': current.username, 'password': current.password, 'url': current.url, 'email_user': current.email_from or False, 'instance_name': current.instance_name or False})
986             if current.opportunity:
987                 key_list.append('Leads')
988                 key_list.append('Opportunities')
989             if current.contact:
990                 key_list.append('Contacts')
991             if current.account:
992                 key_list.append('Accounts') 
993             if current.employee:
994                 key_list.append('Employees')  
995             if current.meeting:
996                 key_list.append('Meetings')
997             if current.call:
998                 key_list.append('Calls')
999             if current.claim:
1000                 key_list.append('Cases')                
1001             if current.email_history:
1002                 key_list.append('Emails') 
1003                 key_list.append('Notes') 
1004             if current.project:
1005                 key_list.append('Project')
1006             if current.project_task:
1007                 key_list.append('ProjectTask')
1008             if current.bug:
1009                 key_list.append('Bugs')
1010             if current.document:
1011                 key_list.append('DocumentRevisions')
1012         return key_list
1013
1014
1015     def do_import_all(self, cr, uid, *args):
1016         """
1017         scheduler Method
1018         """
1019         context = {'username': args[4], 'password': args[5], 'url': args[3], 'instance_name': args[3]}
1020         imp = sugar_import(self, cr, uid, args[2], "import_sugarcrm", [args[1]], context)
1021         imp.set_table_list(args[0])
1022         imp.start()
1023         return True 
1024
1025     def import_from_scheduler_all(self, cr, uid, ids, context=None):
1026         keys = self.get_key(cr, uid, ids, context)
1027         if not keys:
1028             raise osv.except_osv(_('Warning !'), _('Select Module to Import.'))
1029         cron_obj = self.pool.get('ir.cron')
1030         url = self.parse_valid_url(context)
1031         args = (keys,context.get('email_user'), context.get('instance_name'), url, context.get('username'), context.get('password') )
1032         new_create_id = cron_obj.create(cr, uid, {'name': 'Import SugarCRM datas','interval_type': 'hours','interval_number': 1, 'numbercall': -1,'model': 'import.sugarcrm','function': 'do_import_all', 'args': args, 'active': False})
1033         return {
1034             'name': 'SugarCRM Scheduler',
1035             'view_type': 'form',
1036             'view_mode': 'form,tree',
1037             'res_model': 'ir.cron',
1038             'res_id': new_create_id,
1039             'type': 'ir.actions.act_window',
1040         }
1041     
1042     
1043     def import_all(self, cr, uid, ids, context=None):
1044         
1045 #        """Import all sugarcrm data into openerp module"""
1046         keys = self.get_key(cr, uid, ids, context)
1047         url = self.parse_valid_url(context)
1048         context.update({'url': url})
1049         imp = sugar_import(self, cr, uid, context.get('instance_name'), "import_sugarcrm", [context.get('email_user')], context)
1050         imp.set_table_list(keys)
1051         imp.start()
1052         
1053         obj_model = self.pool.get('ir.model.data')
1054         model_data_ids = obj_model.search(cr,uid,[('model','=','ir.ui.view'),('name','=','import.message.form')])
1055         resource_id = obj_model.read(cr, uid, model_data_ids, fields=['res_id'])
1056         return {
1057                 'view_type': 'form',
1058                 'view_mode': 'form',
1059                 'res_model': 'import.message',
1060                 'views': [(resource_id,'form')],
1061                 'type': 'ir.actions.act_window',
1062                 'target': 'new',
1063             }
1064         
1065 import_sugarcrm()