1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
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.
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.
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/>.
20 ##############################################################################
21 from osv import fields, osv
23 from tools.translate import _
24 from import_base.import_framework import *
25 from import_base.mapper import *
26 from datetime import datetime
29 pp = pprint.PrettyPrinter(indent=4)
34 def unescape_htmlentities(s):
35 p = htmllib.HTMLParser(None)
40 class related_ref(dbmapper):
41 def __init__(self, type):
44 def __call__(self, external_val):
45 if external_val.get('parent_type') in self.type and external_val.get('parent_id'):
46 return self.parent.xml_id_exist(external_val['parent_type'], external_val['parent_id'])
49 class sugar_import(import_framework):
51 TABLE_CONTACT = 'Contacts'
52 TABLE_ACCOUNT = 'Accounts'
54 TABLE_EMPLOYEE = 'Employees'
55 TABLE_RESSOURCE = "resource"
56 TABLE_OPPORTUNITY = 'Opportunities'
58 TABLE_STAGE = 'crm_stage'
59 TABLE_ATTENDEE = 'calendar_attendee'
61 TABLE_MEETING = 'Meetings'
63 TABLE_PROJECT = 'Project'
64 TABLE_PROJECT_TASK = 'ProjectTask'
68 TABLE_EMAIL = 'Emails'
69 TABLE_COMPAIGN = 'Campaigns'
70 TABLE_DOCUMENT = 'Documents'
71 TABLE_HISTORY_ATTACHMNET = 'history_attachment'
73 MAX_RESULT_PER_PAGE = 200
77 PortType,sessionid = sugar.login(self.context.get('username',''), self.context.get('password',''), self.context.get('url',''))
79 raise osv.except_osv(_('Error !'), _('Authentication error !\nBad Username or Password or bad SugarSoap Api url !'))
80 self.context['port'] = PortType
81 self.context['session_id'] = sessionid
83 def get_data(self, table):
87 r = sugar.search(self.context.get('port'), self.context.get('session_id'), table, offset, self.MAX_RESULT_PER_PAGE)
89 if len(r) < self.MAX_RESULT_PER_PAGE:
91 offset += self.MAX_RESULT_PER_PAGE
94 #def get_link(self, from_table, ids, to_table):
95 #return sugar.relation_search(self.context.get('port'), self.context.get('session_id'), from_table, module_id=ids, related_module=to_table)
100 def get_category(self, val, model, name):
101 fields = ['name', 'object_id']
103 return self.import_object(fields, data, 'crm.case.categ', 'crm_categ', name, [('object_id.model','=',model), ('name', 'ilike', name)])
105 def get_job_title(self, dict, salutation):
106 fields = ['shortcut', 'name', 'domain']
108 data = [salutation, salutation, 'Contact']
109 return self.import_object(fields, data, 'res.partner.title', 'contact_title', salutation, [('shortcut', '=', salutation)])
111 def get_channel_id(self, dict, val):
116 return self.import_object(fields, data, 'crm.case.channel', 'crm_channel', val)
118 def get_all_states(self, external_val, country_id):
119 """Get states or create new state unless country_id is False"""
120 state_code = external_val[0:3] #take the tree first char
121 fields = ['country_id/id', 'name', 'code']
122 data = [country_id, external_val, state_code]
124 return self.import_object(fields, data, 'res.country.state', 'country_state', external_val)
127 def get_all_countries(self, val):
128 """Get Country, if no country match do not create anything, to avoid duplicate country code"""
129 return self.mapped_id_if_exist('res.country', [('name', 'ilike', val)], 'country', val)
131 def get_float_time(self, dict, hour, min):
132 min = int(min) * 100 / 60
133 return "%s.%i" % (hour, min)
139 def import_related_document(self, val):
142 sugar_document_account = sugar.relation_search(self.context.get('port'), self.context.get('session_id'), 'Documents', module_id=val.get('id'), related_module='Accounts', query=None, deleted=None)
143 sugar_document_contact = sugar.relation_search(self.context.get('port'), self.context.get('session_id'), 'Documents', module_id=val.get('id'), related_module=self.TABLE_CONTACT, query=None, deleted=None)
144 sugar_document_opportunity = sugar.relation_search(self.context.get('port'), self.context.get('session_id'), 'Documents', module_id=val.get('id'), related_module=self.TABLE_OPPORTUNITY, query=None, deleted=None)
145 sugar_document_case = sugar.relation_search(self.context.get('port'), self.context.get('session_id'), 'Documents', module_id=val.get('id'), related_module=self.TABLE_CASE, query=None, deleted=None)
146 sugar_document_bug = sugar.relation_search(self.context.get('port'), self.context.get('session_id'), 'Documents', module_id=val.get('id'), related_module=self.TABLE_BUG, query=None, deleted=None)
147 if sugar_document_account:
148 res_id = self.get_mapped_id(self.TABLE_ACCOUNT,sugar_document_account[0])
149 res_model = 'res.partner'
150 elif sugar_document_contact:
151 res_id = self.get_mapped_id(self.TABLE_CONTACT, sugar_document_contact[0])
152 res_model = 'res.partner.address'
153 elif sugar_document_opportunity:
154 res_id = self.get_mapped_id(self.TABLE_OPPORTUNITY, sugar_document_opportunity[0])
155 res_model = 'crm.lead'
156 elif sugar_document_case:
157 res_id = self.get_mapped_id(self.TABLE_CASE, sugar_document_case[0])
158 res_model = 'crm.claim'
159 elif sugar_document_bug:
160 res_id = self.get_mapped_id(self.TABLE_BUG, sugar_document_bug[0])
161 res_model = 'project.issue'
162 return res_id,res_model
164 def import_document(self, val):
165 File,Filename = sugar.get_document_revision_search(self.context.get('port'), self.context.get('session_id'), val.get('document_revision_id'))
166 #File = base64.encodestring(File)
167 res_id, res_model = self.import_related_document(val)
168 val['res_id'] = res_id
169 val['res_model'] = res_model
172 val['datas_fname'] = Filename
175 def get_document_mapping(self):
177 'model' : 'ir.attachment',
178 'dependencies' : [self.TABLE_USER],
179 'hook' : self.import_document,
181 'name':'document_name',
182 'description': ppconcat('description'),
184 'datas_fname': 'datas_fname',
185 'res_model': 'res_model',
196 def import_email(self, val):
197 vals = sugar.email_search(self.context.get('port'), self.context.get('session_id'), self.TABLE_EMAIL, val.get('id'))
198 model_obj = self.obj.pool.get('ir.model.data')
200 xml_id = self.xml_id_exist(val.get('parent_type'), val.get('parent_id'))
201 model_ids = model_obj.search(self.cr, self.uid, [('name', 'like', xml_id)])
203 model = model_obj.browse(self.cr, self.uid, model_ids)[0]
204 if model.model == 'res.partner':
205 val['partner_id/.id'] = model.res_id
207 val['res_id'] = model.res_id
208 val['model'] = model.model
211 def get_email_mapping(self):
213 'model' : 'mail.message',
214 'dependencies' : [self.TABLE_USER, self.TABLE_ACCOUNT, self.TABLE_CONTACT, self.TABLE_LEAD, self.TABLE_OPPORTUNITY, self.TABLE_MEETING, self.TABLE_CALL],
215 'hook' : self.import_email,
218 'state' : const('received'),
220 'email_from': 'from_addr_name',
221 'email_to': 'to_addrs_names',
222 'email_cc': 'cc_addrs_names',
223 'email_bcc': 'bcc_addrs_names',
224 'message_id': 'message_id',
227 'partner_id/.id': 'partner_id/.id',
228 'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
229 'body_text': 'description',
230 'body_html' : 'description_html',
236 import History(Notes)
240 def import_history(self, val):
241 model_obj = self.obj.pool.get('ir.model.data')
242 xml_id = self.xml_id_exist(val.get('parent_type'), val.get('parent_id'))
243 model_ids = model_obj.search(self.cr, self.uid, [('name', 'like', xml_id)])
245 model = model_obj.browse(self.cr, self.uid, model_ids)[0]
246 if model.model == 'res.partner':
247 val['partner_id/.id'] = model.res_id
248 val['res_id'] = model.res_id
249 val['model'] = model.model
250 File, Filename = sugar.attachment_search(self.context.get('port'), self.context.get('session_id'), self.TABLE_NOTE, val.get('id'))
253 val['datas_fname'] = Filename
256 def get_history_mapping(self):
258 'model' : 'ir.attachment',
259 'dependencies' : [self.TABLE_USER, self.TABLE_ACCOUNT, self.TABLE_CONTACT, self.TABLE_LEAD, self.TABLE_OPPORTUNITY, self.TABLE_MEETING, self.TABLE_CALL, self.TABLE_EMAIL],
260 'hook' : self.import_history,
263 'user_id/id': ref(self.TABLE_USER, 'created_by'),
264 'description': ppconcat('description', 'description_html'),
266 'res_model': 'model',
267 'partner_id/.id' : 'partner_id/.id',
269 'datas_fname' : 'datas_fname'
276 def get_claim_priority(self, val):
282 return priority_dict.get(val.get('priority'), '')
284 def get_contact_info_from_account(self, val):
285 partner_id = self.get_mapped_id(self.TABLE_ACCOUNT, val.get('account_id'))
286 partner_address_id = False
287 partner_phone = False
288 partner_email = False
289 partner = self.obj.pool.get('res.partner').browse(self.cr, self.uid, [partner_id])[0]
290 if partner.address and partner.address[0]:
291 address = partner.address[0]
292 partner_address_id = address.id
293 partner_phone = address.phone
294 partner_email = address.email
295 return partner_address_id, partner_phone,partner_email
297 def import_crm_claim(self, val):
298 partner_address_id, partner_phone,partner_email = self.get_contact_info_from_account(val)
299 val['partner_address_id/.id'] = partner_address_id
300 val['partner_phone'] = partner_phone
301 val['email_from'] = partner_email
304 def get_crm_claim_mapping(self):
306 'model' : 'crm.claim',
307 'dependencies' : [self.TABLE_USER, self.TABLE_ACCOUNT, self.TABLE_CONTACT, self.TABLE_LEAD],
308 'hook' : self.import_crm_claim,
310 'name': concat('case_number','name', delimiter='-'),
311 'date': 'date_entered',
312 'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
313 'description': ppconcat('description', 'resolution', 'work_log'),
314 'partner_id/id': ref(self.TABLE_ACCOUNT, 'account_id'),
315 'partner_address_id/.id': 'partner_address_id/.id',
316 'categ_id/id': call(self.get_category, 'crm.claim', value('type')),
317 'partner_phone': 'partner_phone',
318 'email_from': 'email_from',
319 'priority': self.get_claim_priority,
320 'state': map_val('status', self.project_issue_state)
324 Import Project Issue(Bugs)
326 project_issue_state = {
330 'Pending': 'pending',
331 'Rejected': 'cancel',
334 def get_project_issue_priority(self, val):
341 return priority_dict.get(val.get('priority'), '')
343 def get_bug_project_id(self, dict, val):
346 return self.import_object(fields, data, 'project.project', 'project_issue', val)
348 def get_project_issue_mapping(self):
350 'model' : 'project.issue',
351 'dependencies' : [self.TABLE_USER],
353 'name': concat('bug_number', 'name', delimiter='-'),
354 'project_id/id': call(self.get_bug_project_id, 'sugarcrm_bugs'),
355 'categ_id/id': call(self.get_category, 'project.issue', value('type')),
356 'description': ppconcat('description', 'source', 'resolution', 'work_log', 'found_in_release', 'release_name', 'fixed_in_release_name', 'fixed_in_release'),
357 'priority': self.get_project_issue_priority,
358 'state': map_val('status', self.project_issue_state),
359 'assigned_to/id' : ref(self.TABLE_USER, 'assigned_user_id'),
366 project_task_state = {
367 'Not Started': 'draft',
368 'In Progress': 'open',
370 'Pending Input': 'pending',
371 'Deferred': 'cancelled',
374 def get_project_task_priority(self, val):
380 return priority_dict.get(val.get('priority'), '')
382 def get_project_task_mapping(self):
384 'model' : 'project.task',
385 'dependencies' : [self.TABLE_USER, self.TABLE_PROJECT],
388 'date_start': 'date_start',
389 'date_end': 'date_finish',
390 'project_id/id': ref(self.TABLE_PROJECT, 'project_id'),
391 'planned_hours': 'estimated_effort',
392 'priority': self.get_project_task_priority,
393 'description': ppconcat('description','milestone_flag', 'project_task_id', 'task_number', 'percent_complete'),
394 'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
395 'partner_id/id': 'partner_id/id',
396 'contact_id/id': 'contact_id/id',
397 'state': map_val('status', self.project_task_state)
410 def import_project_account(self, val):
412 partner_invoice_id = False
413 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)
414 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)
415 for contact_id in sugar_project_contact:
416 partner_invoice_id = self.get_mapped_id(self.TABLE_CONTACT, contact_id)
417 for account_id in sugar_project_account:
418 partner_id = self.get_mapped_id(self.TABLE_ACCOUNT, account_id)
419 return partner_id, partner_invoice_id
421 def import_project(self, val):
422 partner_id, partner_invoice_id = self.import_project_account(val)
423 val['partner_id/.id'] = partner_id
424 val['contact_id/.id'] = partner_invoice_id
427 def get_project_mapping(self):
429 'model' : 'project.project',
430 'dependencies' : [self.TABLE_CONTACT, self.TABLE_ACCOUNT, self.TABLE_USER],
431 'hook' : self.import_project,
434 'date_start': 'estimated_start_date',
435 'date': 'estimated_end_date',
436 'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
437 'partner_id/.id': 'partner_id/.id',
438 'contact_id/.id': 'contact_id/.id',
439 'state': map_val('status', self.project_state)
447 'Completed' : 'done',
448 'Not Started':'draft',
449 'In Progress': 'open',
450 'Pending Input': 'draft',
454 def import_task(self, val):
455 date = val.get('date_start') or datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
456 val['date'] = ''.join(date)
457 date_deadline = val.get('date_due') or datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
458 val['date_deadline'] = ''.join(date_deadline)
461 def get_task_mapping(self):
463 'model' : 'crm.meeting',
464 'dependencies' : [self.TABLE_CONTACT, self.TABLE_ACCOUNT, self.TABLE_USER],
465 'hook' : self.import_task,
469 'date_deadline': 'date_deadline',
470 'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
471 'categ_id/id': call(self.get_category, 'crm.meeting', const('Tasks')),
472 'partner_id/id': related_ref(self.TABLE_ACCOUNT),
473 'partner_address_id/id': ref(self.TABLE_CONTACT,'contact_id'),
474 'state': map_val('status', self.task_state)
481 #TODO adapt with project trunk-crm-imp
485 'Not Held': 'pending',
488 def get_calls_mapping(self):
490 'model' : 'crm.phonecall',
491 'dependencies' : [self.TABLE_ACCOUNT, self.TABLE_CONTACT, self.TABLE_OPPORTUNITY, self.TABLE_LEAD],
494 'date': 'date_start',
495 'duration': call(self.get_float_time, value('duration_hours'), value('duration_minutes')),
496 'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
497 'partner_id/id': related_ref(self.TABLE_ACCOUNT),
498 'partner_address_id/id': related_ref(self.TABLE_CONTACT),
499 'categ_id/id': call(self.get_category, 'crm.phonecall', value('direction')),
500 'opportunity_id/id': related_ref(self.TABLE_OPPORTUNITY),
501 'description': ppconcat('description'),
502 'state': map_val('status', self.call_state)
515 def get_attendee_id(self, cr, uid, module_name, module_id):
519 attendee_dict = sugar.user_get_attendee_list(self.context.get('port'), self.context.get('session_id'), module_name, module_id)
520 for attendee in attendee_dict:
521 user_id = self.xml_id_exist(self.TABLE_USER, attendee.get('id', False))
524 contact_id = self.xml_id_exist(self.TABLE_CONTACT, attendee.get('id', False))
525 fields = ['user_id/id', 'email', 'partner_address_id/id']
526 data = [user_id, attendee.get('email1'), contact_id]
527 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'))])
528 attendee_id.append(attendee_xml_id)
529 return ','.join(attendee_id)
531 def get_alarm_id(self, dict_val, val):
533 '60': '1 minute before',
534 '300': '5 minutes before',
535 '600': '10 minutes before',
536 '900': '15 minutes before',
537 '1800':'30 minutes before',
538 '3600': '1 hour before',
540 return self.mapped_id_if_exist('res.alarm', [('name', 'like', alarm_dict.get(val))], 'alarm', val)
544 def import_meeting(self, val):
545 attendee_id = self.get_attendee_id(self.cr, self.uid, 'Meetings', val.get('id')) #TODO
546 val['attendee_ids/id'] = attendee_id
549 def get_meeting_mapping(self):
551 'model' : 'crm.meeting',
552 'dependencies' : [self.TABLE_CONTACT, self.TABLE_OPPORTUNITY, self.TABLE_LEAD, self.TABLE_TASK],
553 'hook': self.import_meeting,
556 'date': 'date_start',
557 'duration': call(self.get_float_time, value('duration_hours'), value('duration_minutes')),
558 'location': 'location',
559 'attendee_ids/id':'attendee_ids/id',
560 'alarm_id/id': call(self.get_alarm_id, value('reminder_time')),
561 'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
562 'partner_id/id': related_ref(self.TABLE_ACCOUNT),
563 'partner_address_id/id': related_ref(self.TABLE_CONTACT),
564 'state': map_val('status', self.meeting_state)
572 'Need Analysis' : 'New',
573 'Closed Lost': 'Lost',
575 'Value Proposition': 'Proposition',
576 'Negotiation/Review': 'Negotiation'
579 def get_opportunity_status(self, sugar_val):
580 fields = ['name', 'case_default']
581 name = 'Opportunity_' + sugar_val['sales_stage']
582 data = [sugar_val['sales_stage'], '1']
583 return self.import_object(fields, data, 'crm.case.stage', self.TABLE_STAGE, name, [('name', 'ilike', sugar_val['sales_stage'])])
585 def import_opportunity_contact(self, val):
586 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))
588 partner_contact_id = False
589 partner_contact_email = False
590 partner_address_obj = self.obj.pool.get('res.partner.address')
591 partner_xml_id = self.name_exist(self.TABLE_ACCOUNT, val['account_name'], 'res.partner')
593 for contact in sugar_opportunities_contact:
594 address_id = self.get_mapped_id(self.TABLE_CONTACT, contact)
596 address = partner_address_obj.browse(self.cr, self.uid, address_id)
597 partner_name = address.partner_id and address.partner_id.name or False
598 if not partner_name: #link with partner id
599 fields = ['partner_id/id']
600 data = [partner_xml_id]
601 self.import_object(fields, data, 'res.partner.address', self.TABLE_CONTACT, contact, self.DO_NOT_FIND_DOMAIN)
602 if not partner_name or partner_name == val.get('account_name'):
603 partner_contact_id = self.xml_id_exist(self.TABLE_CONTACT, contact)
604 partner_contact_email = address.email
605 return partner_contact_id, partner_contact_email
607 def import_opp(self, val):
608 partner_contact_id, partner_contact_email = self.import_opportunity_contact(val)
609 val['partner_address_id/id'] = partner_contact_id
610 val['email_from'] = partner_contact_email
613 def get_opp_mapping(self):
615 'model' : 'crm.lead',
616 'dependencies' : [self.TABLE_USER, self.TABLE_ACCOUNT, self.TABLE_CONTACT,self.TABLE_COMPAIGN],
617 'hook' : self.import_opp,
620 'probability': 'probability',
621 'partner_id/id': refbyname(self.TABLE_ACCOUNT, 'account_name', 'res.partner'),
622 'title_action': 'next_step',
623 'partner_address_id/id': 'partner_address_id/id',
624 'planned_revenue': 'amount',
625 'date_deadline': 'date_closed',
626 'user_id/id' : ref(self.TABLE_USER, 'assigned_user_id'),
627 'stage_id/id' : self.get_opportunity_status,
628 'type' : const('opportunity'),
629 'categ_id/id': call(self.get_category, 'crm.lead', value('opportunity_type')),
630 'email_from': 'email_from',
631 'state': map_val('status', self.opp_state),
632 'description' : 'description',
640 def get_compaign_mapping(self):
642 'model' : 'crm.case.resource.type',
651 def get_lead_status(self, sugar_val):
652 fields = ['name', 'case_default']
653 name = 'lead_' + sugar_val.get('status', '')
654 data = [sugar_val.get('status', ''), '1']
655 return self.import_object(fields, data, 'crm.case.stage', self.TABLE_STAGE, name, [('name', 'ilike', sugar_val.get('status', ''))])
660 'In Progress': 'open',
661 'Recycled': 'cancel',
666 def import_lead(self, val):
667 if val.get('opportunity_id'): #if lead is converted into opp, don't import as lead
669 if val.get('primary_address_country'):
670 country_id = self.get_all_countries(val.get('primary_address_country'))
671 val['country_id/id'] = country_id
672 val['state_id/id'] = self.get_all_states(val.get('primary_address_state'), country_id)
675 def get_lead_mapping(self):
677 'model' : 'crm.lead',
678 'dependencies' : [self.TABLE_COMPAIGN, self.TABLE_USER],
679 'hook' : self.import_lead,
681 'name': concat('first_name', 'last_name'),
682 'contact_name': concat('first_name', 'last_name'),
683 'description': ppconcat('description', 'refered_by', 'lead_source', 'lead_source_description', 'website', 'email2', 'status_description', 'lead_source_description', 'do_not_call'),
684 'partner_name': 'account_name',
685 'email_from': 'email1',
686 'phone': 'phone_work',
687 'mobile': 'phone_mobile',
688 'title/id': call(self.get_job_title, value('salutation')),
690 'street': 'primary_address_street',
691 'street2': 'alt_address_street',
692 'zip': 'primary_address_postalcode',
693 'city':'primary_address_city',
694 'user_id/id' : ref(self.TABLE_USER, 'assigned_user_id'),
695 'stage_id/id' : self.get_lead_status,
696 'type' : const('lead'),
697 'state': map_val('status', self.lead_state) ,
699 'referred': 'refered_by',
700 'opt_out': 'do_not_call',
701 'channel_id/id': call(self.get_channel_id, value('lead_source')),
702 'type_id/id': ref(self.TABLE_COMPAIGN, 'campaign_id'),
703 'country_id/id': 'country_id/id',
704 'state_id/id': 'state_id/id',
712 def get_email(self, val):
713 email_address = sugar.get_contact_by_email(self.context.get('port'), self.context.get('username'), self.context.get('password'), val.get('email1'))
715 return ','.join(email_address)
717 def import_contact(self, val):
718 if val.get('primary_address_country'):
719 country_id = self.get_all_countries(val.get('primary_address_country'))
720 state = self.get_all_states(val.get('primary_address_state'), country_id)
721 val['country_id/id'] = country_id
722 val['state_id/id'] = state
725 def get_contact_mapping(self):
727 'model' : 'res.partner.address',
728 'dependencies' : [self.TABLE_ACCOUNT],
729 'hook' : self.import_contact,
731 'name': concat('first_name', 'last_name'),
732 'partner_id/id': ref(self.TABLE_ACCOUNT,'account_id'),
733 'phone': 'phone_work',
734 'mobile': 'phone_mobile',
737 'street': 'primary_address_street',
738 'zip': 'primary_address_postalcode',
739 'city': 'primary_address_city',
740 'country_id/id': 'country_id/id',
741 'state_id/id': 'state_id/id',
742 'email': self.get_email,
743 'type': const('contact')
751 def get_address_type(self, val, type):
752 if type == 'invoice':
753 type_address = 'billing'
755 type_address = 'shipping'
757 if type == 'default':
758 map_partner_address = {
760 'type': const('default'),
764 map_partner_address = {
766 'phone': 'phone_office',
767 'mobile': 'phone_mobile',
770 'street': type_address + '_address_street',
771 'zip': type_address +'_address_postalcode',
772 'city': type_address +'_address_city',
773 'country_id/id': 'country_id/id',
777 if val.get(type_address +'_address_country'):
778 country_id = self.get_all_countries(val.get(type_address +'_address_country'))
779 state = self.get_all_states(val.get(type_address +'_address_state'), country_id)
780 val['country_id/id'] = country_id
781 val['state_id/id'] = state
784 val['id_new'] = val['id'] + '_address_' + type
785 return self.import_object_mapping(map_partner_address, val, 'res.partner.address', self.TABLE_CONTACT, val['id_new'], self.DO_NOT_FIND_DOMAIN)
787 def get_partner_address(self, val):
789 type_dict = {'billing_address_street' : 'invoice', 'shipping_address_street' : 'delivery', 'type': 'default'}
790 for key, type_value in type_dict.items():
792 id = self.get_address_type(val, type_value)
793 address_id.append(id)
795 return ','.join(address_id)
797 def get_partner_mapping(self):
799 'model' : 'res.partner',
800 'dependencies' : [self.TABLE_USER],
803 'website': 'website',
804 'user_id/id': ref(self.TABLE_USER,'assigned_user_id'),
806 'comment': ppconcat('description', 'employees', 'ownership', 'annual_revenue', 'rating', 'industry', 'ticker_symbol'),
807 'customer': const('1'),
808 'supplier': const('0'),
809 'address/id':'address/id',
810 'parent_id/id_parent' : 'parent_id',
811 'address/id' : self.get_partner_address,
818 def get_ressource(self, val):
820 'name': concat('first_name', 'last_name'),
822 return self.import_object_mapping(map_resource, val, 'resource.resource', self.TABLE_RESSOURCE, val['id'], self.DO_NOT_FIND_DOMAIN)
824 def get_job_id(self, val):
826 data = [val.get('title')]
827 return self.import_object(fields, data, 'hr.job', 'hr_job', val.get('title'))
829 def get_user_address(self, val):
831 'name': concat('first_name', 'last_name'),
832 'city': 'address_city',
833 'country_id/id': 'country_id/id',
834 'state_id/id': 'state_id/id',
835 'street': 'address_street',
836 'zip': 'address_postalcode',
838 'phone': 'phone_work',
839 'mobile':'phone_mobile',
843 if val.get('address_country'):
844 country_id = self.get_all_countries(val.get('address_country'))
845 state_id = self.get_all_states(val.get('address_state'), country_id)
846 val['country_id/id'] = country_id
847 val['state_id/id'] = state_id
849 return self.import_object_mapping(map_user_address, val, 'res.partner.address', self.TABLE_CONTACT, val['id'], self.DO_NOT_FIND_DOMAIN)
851 def get_employee_mapping(self):
853 'model' : 'hr.employee',
854 'dependencies' : [self.TABLE_USER],
856 'resource_id/id': self.get_ressource,
857 'name': concat('first_name', 'last_name'),
858 'work_phone': 'phone_work',
859 'mobile_phone': 'phone_mobile',
860 'user_id/id': ref(self.TABLE_USER, 'id'),
861 'address_home_id/id': self.get_user_address,
862 'notes': ppconcat('messenger_type', 'messenger_id', 'description'),
863 'job_id/id': self.get_job_id,
864 'work_email' : 'email1',
865 'coach_id/id_parent' : 'reports_to_id',
872 def import_user(self, val):
873 user_obj = self.obj.pool.get('res.users')
874 user_ids = user_obj.search(self.cr, self.uid, [('login', '=', val.get('user_name'))])
876 val['.id'] = str(user_ids[0])
878 val['password'] = 'sugarcrm' #default password for all user #TODO needed in documentation
880 val['context_lang'] = self.context.get('lang','en_US')
883 def get_users_department(self, val):
884 dep = val.get('department')
889 return self.import_object(fields, data, 'hr.department', 'hr_department_user', dep)
891 def get_user_mapping(self):
893 'model' : 'res.users',
894 'hook' : self.import_user,
896 'name': concat('first_name', 'last_name'),
897 'login': value('user_name', fallback='last_name'),
898 'context_lang' : 'context_lang',
899 'password' : 'password',
901 'user_email' : 'email1',
906 def get_mapping(self):
908 self.TABLE_USER : self.get_user_mapping(),
909 self.TABLE_EMPLOYEE : self.get_employee_mapping(),
910 self.TABLE_ACCOUNT : self.get_partner_mapping(),
911 self.TABLE_CONTACT : self.get_contact_mapping(),
912 self.TABLE_LEAD : self.get_lead_mapping(),
913 self.TABLE_OPPORTUNITY : self.get_opp_mapping(),
914 self.TABLE_MEETING : self.get_meeting_mapping(),
915 self.TABLE_CALL : self.get_calls_mapping(),
916 self.TABLE_TASK : self.get_task_mapping(),
917 self.TABLE_PROJECT : self.get_project_mapping(),
918 self.TABLE_PROJECT_TASK: self.get_project_task_mapping(),
919 self.TABLE_BUG: self.get_project_issue_mapping(),
920 self.TABLE_CASE: self.get_crm_claim_mapping(),
921 self.TABLE_NOTE: self.get_history_mapping(),
922 self.TABLE_EMAIL: self.get_email_mapping(),
923 self.TABLE_DOCUMENT: self.get_document_mapping(),
924 self.TABLE_COMPAIGN: self.get_compaign_mapping()
930 def get_email_subject(self, result, error=False):
932 return "Sugarcrm data import failed at %s due to an unexpected error" % self.date_ended
933 return "your sugarcrm data were successfully imported at %s" % self.date_ended
935 def get_body_header(self, result):
936 return "Sugarcrm import : report of last import"
939 class import_sugarcrm(osv.osv):
940 """Import SugarCRM DATA"""
942 _name = "import.sugarcrm"
943 _description = __doc__
945 'username': fields.char('User Name', size=64, required=True),
946 'password': fields.char('Password', size=24,required=True),
947 'url' : fields.char('SugarSoap Api url:', size=264, required=True, help="Webservice's url where to get the data.\
948 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"),
949 'user' : fields.boolean('User', help="Check this box to import sugarCRM Users into OpenERP users, warning if a user with the same login exist in OpenERP, user information will be erase by sugarCRM user information", readonly=True),
950 'opportunity': fields.boolean('Leads & Opp', help="Check this box to import sugarCRM Leads and Opportunities into OpenERP Leads and Opportunities"),
951 'contact': fields.boolean('Contacts', help="Check this box to import sugarCRM Contacts into OpenERP addresses"),
952 'account': fields.boolean('Accounts', help="Check this box to import sugarCRM Accounts into OpenERP partners"),
953 'employee': fields.boolean('Employee', help="Check this box to import sugarCRM Employees into OpenERP employees"),
954 'meeting': fields.boolean('Meetings', help="Check this box to import sugarCRM Meetings and Tasks into OpenERP meetings"),
955 'call': fields.boolean('Calls', help="Check this box to import sugarCRM Calls into OpenERP calls"),
956 'claim': fields.boolean('Cases', help="Check this box to import sugarCRM Cases into OpenERP claims"),
957 'email_history': fields.boolean('Email and Note',help="Check this box to import sugarCRM Emails, Notes and Attachments into OpenERP Messages and Attachments"),
958 'project': fields.boolean('Projects', help="Check this box to import sugarCRM Projects into OpenERP projects"),
959 'project_task': fields.boolean('Project Tasks', help="Check this box to import sugarCRM Project Tasks into OpenERP tasks"),
960 'bug': fields.boolean('Bugs', help="Check this box to import sugarCRM Bugs into OpenERP project issues"),
961 'document': fields.boolean('Documents', help="Check this box to import sugarCRM Documents into OpenERP documents"),
962 'email_from': fields.char('Notify End Of Import To:', size=128),
963 '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."),
966 def _get_email_id(self, cr, uid, context=None):
967 return self.pool.get('res.users').browse(cr, uid, uid, context=context).user_email
969 def _module_installed(self, cr, uid, model, context=None):
970 module_id = self.pool.get('ir.module.module').search(cr, uid, [('name', '=', model), ('state', "=", "installed")], context=context)
971 return bool(module_id)
973 def _project_installed(self, cr, uid, context=None):
974 return self._module_installed(cr,uid,'project',context=context)
976 def _crm_claim_installed(self, cr, uid, context=None):
977 return self._module_installed(cr,uid,'crm_claim',context=context)
979 def _project_issue_installed(self, cr, uid, context=None):
980 return self._module_installed(cr,uid,'project_issue',context=context)
982 def _hr_installed(self, cr, uid, context=None):
983 return self._module_installed(cr,uid,'hr',context=context)
985 _defaults = {#to be set to true, but easier for debugging
990 'employee' : _hr_installed,
993 'claim' : _crm_claim_installed,
994 'email_history' : True,
995 'project' : _project_installed,
996 'project_task': _project_installed,
997 'bug': _project_issue_installed,
999 'instance_name': 'sugarcrm',
1000 'email_from': _get_email_id,
1001 'username' : 'admin',
1003 'url': "http://sugarcrm.example.com/soap.php"
1006 def check_url(self, url, context):
1009 user = context.get('username')
1010 password = context.get('password')
1013 sugar.login(user, password, url)
1019 def parse_valid_url(self, context=None):
1022 url = context.get('url')
1023 url_split = str(url).split('/')
1024 while len(url_split) >= 3:
1025 #3 case, soap.php is already at the end of url should be valid
1026 #url end by / or not
1027 if url_split[-1] == 'soap.php':
1028 url = "/".join(url_split)
1029 elif url_split[-1] == '':
1030 url = "/".join(url_split) + "soap.php"
1032 url = "/".join(url_split) + "/soap.php"
1034 if self.check_url(url, context):
1037 url_split = url_split[:-1] #take parent folder
1040 def get_key(self, cr, uid, ids, context=None):
1041 """Select Key as For which Module data we want import data."""
1046 for current in self.browse(cr, uid, ids, context):
1047 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})
1049 key_list.append('Users')
1051 key_list.append('Contacts')
1053 key_list.append('Accounts')
1054 if current.opportunity:
1055 key_list.append('Leads')
1056 key_list.append('Opportunities')
1057 if current.employee:
1058 key_list.append('Employees')
1059 module.update({'Employees':'hr'})
1061 key_list.append('Meetings')
1063 key_list.append('Calls')
1065 key_list.append('Cases')
1066 module.update({'Cases':'crm_claim'})
1067 if current.email_history:
1068 key_list.append('Emails')
1069 key_list.append('Notes')
1071 key_list.append('Project')
1072 module.update({'Project':'project'})
1073 if current.project_task:
1074 key_list.append('ProjectTask')
1075 module.update({'ProjectTask':'project'})
1077 key_list.append('Bugs')
1078 module.update({'Bugs':'project_issue'})
1079 if current.document:
1080 key_list.append('Documents')
1081 return key_list,module
1084 def do_import_all(self, cr, uid, *args):
1088 context = {'username': args[4], 'password': args[5], 'url': args[3], 'instance_name': args[3]}
1089 imp = sugar_import(self, cr, uid, args[2], "import_sugarcrm", args[1], context)
1090 imp.set_table_list(args[0])
1094 def import_from_scheduler_all(self, cr, uid, ids, context=None):
1095 keys, module_list = self.get_key(cr, uid, ids, context)
1097 raise osv.except_osv(_('Warning !'), _('Select Module to Import.'))
1098 key_list = module_list.keys()
1099 for module in key_list :
1100 module = module_list[module]
1101 state = self.get_all(cr,uid,module,context=context)
1103 keys = ', '.join(key_list)
1104 raise osv.except_osv(_('Error !!'), _("%s data required %s Module to be installed, Please install %s module") %(keys,module,module))
1105 cron_obj = self.pool.get('ir.cron')
1106 url = self.parse_valid_url(context)
1107 args = (keys,context.get('email_user'), context.get('instance_name'), url, context.get('username'), context.get('password') )
1108 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})
1110 'name': 'SugarCRM Scheduler',
1111 'view_type': 'form',
1112 'view_mode': 'form,tree',
1113 'res_model': 'ir.cron',
1114 'res_id': new_create_id,
1115 'type': 'ir.actions.act_window',
1118 def import_all(self, cr, uid, ids, context=None):
1120 # """Import all sugarcrm data into openerp module"""
1121 keys, module_list = self.get_key(cr, uid, ids, context)
1123 raise osv.except_osv(_('Warning !'), _('Select Module to Import.'))
1124 key_list = module_list.keys()
1125 for module in key_list :
1126 module = module_list[module]
1127 state = self._module_installed(cr,uid,module,context=context)
1129 keys = ', '.join(key_list)
1130 raise osv.except_osv(_('Error !!'), _("%s data required %s Module to be installed, Please install %s module") %(keys,module,module))
1131 url = self.parse_valid_url(context)
1132 context.update({'url': url})
1133 imp = sugar_import(self, cr, uid, context.get('instance_name'), "import_sugarcrm", context.get('email_user'), context)
1134 imp.set_table_list(keys)
1136 obj_model = self.pool.get('ir.model.data')
1137 model_data_ids = obj_model.search(cr,uid,[('model','=','ir.ui.view'),('name','=','import.message.form')])
1138 resource_id = obj_model.read(cr, uid, model_data_ids, fields=['res_id'])
1140 'view_type': 'form',
1141 'view_mode': 'form',
1142 'res_model': 'import.message',
1143 'views': [(resource_id,'form')],
1144 'type': 'ir.actions.act_window',
1150 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: