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 from papyon.service.SOAPService import url_split
30 pp = pprint.PrettyPrinter(indent=4)
33 class related_ref(dbmapper):
34 def __init__(self, type):
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'])
42 class sugar_import(import_framework):
44 TABLE_CONTACT = 'Contacts'
45 TABLE_ACCOUNT = 'Accounts'
47 TABLE_EMPLOYEE = 'Employees'
48 TABLE_RESSOURCE = "resource"
49 TABLE_OPPORTUNITY = 'Opportunities'
51 TABLE_STAGE = 'crm_stage'
52 TABLE_ATTENDEE = 'calendar_attendee'
54 TABLE_MEETING = 'Meetings'
56 TABLE_PROJECT = 'Project'
57 TABLE_PROJECT_TASK = 'ProjectTask'
61 TABLE_EMAIL = 'Emails'
62 TABLE_COMPAIGN = 'Campaigns'
63 TABLE_DOCUMENT = 'DocumentRevisions'
64 TABLE_HISTORY_ATTACHMNET = 'history_attachment'
66 MAX_RESULT_PER_PAGE = 200
70 PortType,sessionid = sugar.login(self.context.get('username',''), self.context.get('password',''), self.context.get('url',''))
72 raise osv.except_osv(_('Error !'), _('Authentication error !\nBad Username or Password or bad SugarSoap Api url !'))
73 self.context['port'] = PortType
74 self.context['session_id'] = sessionid
76 def get_data(self, table):
81 r = sugar.search(self.context.get('port'), self.context.get('session_id'), table, offset, self.MAX_RESULT_PER_PAGE)
83 if len(r) < self.MAX_RESULT_PER_PAGE:
85 offset += self.MAX_RESULT_PER_PAGE
88 #def get_link(self, from_table, ids, to_table):
89 #return sugar.relation_search(self.context.get('port'), self.context.get('session_id'), from_table, module_id=ids, related_module=to_table)
94 def get_category(self, val, model, name):
95 fields = ['name', 'object_id']
97 return self.import_object(fields, data, 'crm.case.categ', 'crm_categ', name, [('object_id.model','=',model), ('name', 'ilike', name)])
99 def get_job_title(self, dict, salutation):
100 fields = ['shortcut', 'name', 'domain']
102 data = [salutation, salutation, 'Contact']
103 return self.import_object(fields, data, 'res.partner.title', 'contact_title', salutation, [('shortcut', '=', salutation)])
105 def get_channel_id(self, dict, val):
110 return self.import_object(fields, data, 'res.partner.canal', 'crm_channel', val)
112 def get_all_states(self, external_val, country_id):
113 """Get states or create new state unless country_id is False"""
114 state_code = external_val[0:3] #take the tree first char
115 fields = ['country_id/id', 'name', 'code']
116 data = [country_id, external_val, state_code]
118 return self.import_object(fields, data, 'res.country.state', 'country_state', external_val)
121 def get_all_countries(self, val):
122 """Get Country, if no country match do not create anything, to avoid duplicate country code"""
123 return self.mapped_id_if_exist('res.country', [('name', 'ilike', val)], 'country', val)
125 def get_float_time(self, dict, hour, min):
126 min = int(min) * 100 / 60
127 return "%s.%i" % (hour, min)
133 def import_document(self, val):
134 File,Filename = sugar.get_document_revision_search(self.context.get('port'), self.context.get('session_id'), val.get('id'))
136 val['datas_fname'] = Filename
139 def get_document_mapping(self):
141 'model' : 'ir.attachment',
142 'dependencies' : [self.TABLE_USER],
143 'hook' : self.import_document,
144 'map' : {'name':'filename',
145 'description': ppconcat('description'),
147 'datas_fname': 'datas_fname',
157 def import_email(self, val):
158 vals = sugar.email_search(self.context.get('port'), self.context.get('session_id'), self.TABLE_EMAIL, val.get('id'))
159 model_obj = self.obj.pool.get('ir.model.data')
161 xml_id = self.xml_id_exist(val.get('parent_type'), val.get('parent_id'))
162 model_ids = model_obj.search(self.cr, self.uid, [('name', 'like', xml_id)])
164 model = model_obj.browse(self.cr, self.uid, model_ids)[0]
165 if model.model == 'res.partner':
166 val['partner_id/.id'] = model.res_id
168 val['res_id'] = model.res_id
169 val['model'] = model.model
172 def get_email_mapping(self):
174 'model' : 'mailgate.message',
175 '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],
176 'hook' : self.import_email,
177 'map' : {'name':'name',
178 'history' : const("1"),
180 'email_from': 'from_addr_name',
181 'email_to': 'to_addrs_names',
182 'email_cc': 'cc_addrs_names',
183 'email_bcc': 'bcc_addrs_names',
184 'message_id': 'message_id',
187 'partner_id/.id': 'partner_id/.id',
188 'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
189 'description': ppconcat('description', 'description_html'),
194 import History(Notes)
198 def import_history(self, val):
199 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
206 val['res_id'] = model.res_id
207 val['model'] = model.model
208 File, Filename = sugar.attachment_search(self.context.get('port'), self.context.get('session_id'), self.TABLE_NOTE, val.get('id'))
211 val['datas_fname'] = Filename
214 def get_history_mapping(self):
216 'model' : 'ir.attachment',
217 '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, self.TABLE_EMAIL],
218 'hook' : self.import_history,
221 'user_id/id': ref(self.TABLE_USER, 'created_by'),
222 'description': ppconcat('description', 'description_html'),
224 'res_model': 'model',
225 'partner_id/.id' : 'partner_id/.id',
227 'datas_fname' : 'datas_fname'
234 def get_claim_priority(self, val):
240 return priority_dict.get(val.get('priority'), '')
242 def get_contact_info_from_account(self, val):
243 partner_id = self.get_mapped_id(self.TABLE_ACCOUNT, val.get('account_id'))
244 partner_address_id = False
245 partner_phone = False
246 partner_email = False
247 partner = self.obj.pool.get('res.partner').browse(self.cr, self.uid, [partner_id])[0]
248 if partner.address and partner.address[0]:
249 address = partner.address[0]
250 partner_address_id = address.id
251 partner_phone = address.phone
252 partner_email = address.email
253 return partner_address_id, partner_phone,partner_email
255 def import_crm_claim(self, val):
256 partner_address_id, partner_phone,partner_email = self.get_contact_info_from_account(val)
257 val['partner_address_id/.id'] = partner_address_id
258 val['partner_phone'] = partner_phone
259 val['email_from'] = partner_email
262 def get_crm_claim_mapping(self):
264 'model' : 'crm.claim',
265 'dependencies' : [self.TABLE_USER, self.TABLE_ACCOUNT, self.TABLE_CONTACT, self.TABLE_LEAD],
266 'hook' : self.import_crm_claim,
268 'name': concat('case_number','name', delimiter='-'),
269 'date': 'date_entered',
270 'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
271 'description': ppconcat('description', 'resolution', 'work_log'),
272 'partner_id/id': ref(self.TABLE_ACCOUNT, 'account_id'),
273 'partner_address_id/.id': 'partner_address_id/.id',
274 'categ_id/id': call(self.get_category, 'crm.claim', value('type')),
275 'partner_phone': 'partner_phone',
276 'email_from': 'email_from',
277 'priority': self.get_claim_priority,
278 'state': map_val('status', self.project_issue_state)
282 Import Project Issue(Bugs)
284 project_issue_state = {
288 'Pending': 'pending',
289 'Rejected': 'cancel',
292 def get_project_issue_priority(self, val):
299 return priority_dict.get(val.get('priority'), '')
301 def get_bug_project_id(self, dict, val):
304 return self.import_object(fields, data, 'project.project', 'project_issue', val)
306 def get_project_issue_mapping(self):
308 'model' : 'project.issue',
309 'dependencies' : [self.TABLE_USER],
311 'name': concat('bug_number', 'name', delimiter='-'),
312 'project_id/id': call(self.get_bug_project_id, 'sugarcrm_bugs'),
313 'categ_id/id': call(self.get_category, 'project.issue', value('type')),
314 'description': ppconcat('description', 'source', 'resolution', 'work_log', 'found_in_release', 'release_name', 'fixed_in_release_name', 'fixed_in_release'),
315 'priority': self.get_project_issue_priority,
316 'state': map_val('status', self.project_issue_state),
317 'assigned_to/id' : ref(self.TABLE_USER, 'assigned_user_id'),
324 project_task_state = {
325 'Not Started': 'draft',
326 'In Progress': 'open',
328 'Pending Input': 'pending',
329 'Deferred': 'cancelled',
332 def get_project_task_priority(self, val):
338 return priority_dict.get(val.get('priority'), '')
340 def get_project_task_mapping(self):
342 'model' : 'project.task',
343 'dependencies' : [self.TABLE_USER, self.TABLE_PROJECT],
346 'date_start': 'date_start',
347 'date_end': 'date_finish',
348 'project_id/id': ref(self.TABLE_PROJECT, 'project_id'),
349 'planned_hours': 'estimated_effort',
350 'priority': self.get_project_task_priority,
351 'description': ppconcat('description','milestone_flag', 'project_task_id', 'task_number', 'percent_complete'),
352 'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
353 'partner_id/id': 'partner_id/id',
354 'contact_id/id': 'contact_id/id',
355 'state': map_val('status', self.project_task_state)
368 def import_project_account(self, val):
370 partner_invoice_id = False
371 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)
372 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)
373 for contact_id in sugar_project_contact:
374 partner_invoice_id = self.get_mapped_id(self.TABLE_CONTACT, contact_id)
375 for account_id in sugar_project_account:
376 partner_id = self.get_mapped_id(self.TABLE_ACCOUNT, account_id)
377 return partner_id, partner_invoice_id
379 def import_project(self, val):
380 partner_id, partner_invoice_id = self.import_project_account(val)
381 val['partner_id/.id'] = partner_id
382 val['contact_id/.id'] = partner_invoice_id
385 def get_project_mapping(self):
387 'model' : 'project.project',
388 'dependencies' : [self.TABLE_CONTACT, self.TABLE_ACCOUNT, self.TABLE_USER],
389 'hook' : self.import_project,
392 'date_start': 'estimated_start_date',
393 'date': 'estimated_end_date',
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_state)
405 'Completed' : 'done',
406 'Not Started':'draft',
407 'In Progress': 'open',
408 'Pending Input': 'draft',
412 def import_task(self, val):
413 val['date'] = val.get('date_start') or datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
414 val['date_deadline'] = val.get('date_due') or datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
417 def get_task_mapping(self):
419 'model' : 'crm.meeting',
420 'dependencies' : [self.TABLE_CONTACT, self.TABLE_ACCOUNT, self.TABLE_USER],
421 'hook' : self.import_task,
425 'date_deadline': 'date_deadline',
426 'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
427 'categ_id/id': call(self.get_category, 'crm.meeting', const('Tasks')),
428 'partner_id/id': related_ref(self.TABLE_ACCOUNT),
429 'partner_address_id/id': ref(self.TABLE_CONTACT,'contact_id'),
430 'state': map_val('status', self.task_state)
437 #TODO adapt with project trunk-crm-imp
441 'Not Held': 'pending',
444 def get_calls_mapping(self):
446 'model' : 'crm.phonecall',
447 'dependencies' : [self.TABLE_ACCOUNT, self.TABLE_CONTACT, self.TABLE_OPPORTUNITY, self.TABLE_LEAD],
450 'date': 'date_start',
451 'duration': call(self.get_float_time, value('duration_hours'), value('duration_minutes')),
452 'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
453 'partner_id/id': related_ref(self.TABLE_ACCOUNT),
454 'partner_address_id/id': related_ref(self.TABLE_CONTACT),
455 'categ_id/id': call(self.get_category, 'crm.phonecall', value('direction')),
456 'opportunity_id/id': related_ref(self.TABLE_OPPORTUNITY),
457 'description': ppconcat('description'),
458 'state': map_val('status', self.call_state)
471 def get_attendee_id(self, cr, uid, module_name, module_id):
475 attendee_dict = sugar.user_get_attendee_list(self.context.get('port'), self.context.get('session_id'), module_name, module_id)
476 for attendee in attendee_dict:
477 user_id = self.xml_id_exist(self.TABLE_USER, attendee.get('id', False))
480 contact_id = self.xml_id_exist(self.TABLE_CONTACT, attendee.get('id', False))
481 fields = ['user_id/id', 'email', 'partner_address_id/id']
482 data = [user_id, attendee.get('email1'), contact_id]
483 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'))])
484 attendee_id.append(attendee_xml_id)
485 return ','.join(attendee_id)
487 def get_alarm_id(self, dict_val, val):
489 '60': '1 minute before',
490 '300': '5 minutes before',
491 '600': '10 minutes before',
492 '900': '15 minutes before',
493 '1800':'30 minutes before',
494 '3600': '1 hour before',
496 return self.mapped_id_if_exist('res.alarm', [('name', 'like', alarm_dict.get(val))], 'alarm', val)
500 def import_meeting(self, val):
501 attendee_id = self.get_attendee_id(self.cr, self.uid, 'Meetings', val.get('id')) #TODO
502 val['attendee_ids/id'] = attendee_id
505 def get_meeting_mapping(self):
507 'model' : 'crm.meeting',
508 'dependencies' : [self.TABLE_CONTACT, self.TABLE_OPPORTUNITY, self.TABLE_LEAD, self.TABLE_TASK],
509 'hook': self.import_meeting,
512 'date': 'date_start',
513 'duration': call(self.get_float_time, value('duration_hours'), value('duration_minutes')),
514 'location': 'location',
515 'attendee_ids/id':'attendee_ids/id',
516 'alarm_id/id': call(self.get_alarm_id, value('reminder_time')),
517 'user_id/id': ref(self.TABLE_USER, 'assigned_user_id'),
518 'partner_id/id': related_ref(self.TABLE_ACCOUNT),
519 'partner_address_id/id': related_ref(self.TABLE_CONTACT),
520 'state': map_val('status', self.meeting_state)
528 'Need Analysis' : 'New',
529 'Closed Lost': 'Lost',
531 'Value Proposition': 'Proposition',
532 'Negotiation/Review': 'Negotiation'
535 def get_opportunity_status(self, sugar_val):
536 fields = ['name', 'type']
537 name = 'Opportunity_' + sugar_val['sales_stage']
538 data = [sugar_val['sales_stage'], 'Opportunity']
539 return self.import_object(fields, data, 'crm.case.stage', self.TABLE_STAGE, name, [('type', '=', 'opportunity'), ('name', 'ilike', sugar_val['sales_stage'])])
541 def import_opportunity_contact(self, val):
542 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))
544 partner_contact_id = False
545 partner_contact_email = False
546 partner_address_obj = self.obj.pool.get('res.partner.address')
547 partner_xml_id = self.name_exist(self.TABLE_ACCOUNT, val['account_name'], 'res.partner')
549 for contact in sugar_opportunities_contact:
550 address_id = self.get_mapped_id(self.TABLE_CONTACT, contact)
552 address = partner_address_obj.browse(self.cr, self.uid, address_id)
553 partner_name = address.partner_id and address.partner_id.name or False
554 if not partner_name: #link with partner id
555 fields = ['partner_id/id']
556 data = [partner_xml_id]
557 self.import_object(fields, data, 'res.partner.address', self.TABLE_CONTACT, contact, self.DO_NOT_FIND_DOMAIN)
558 if not partner_name or partner_name == val.get('account_name'):
559 partner_contact_id = self.xml_id_exist(self.TABLE_CONTACT, contact)
560 partner_contact_email = address.email
561 return partner_contact_id, partner_contact_email
563 def import_opp(self, val):
564 partner_contact_id, partner_contact_email = self.import_opportunity_contact(val)
565 val['partner_address_id/id'] = partner_contact_id
566 val['email_from'] = partner_contact_email
569 def get_opp_mapping(self):
571 'model' : 'crm.lead',
572 'dependencies' : [self.TABLE_USER, self.TABLE_ACCOUNT, self.TABLE_CONTACT,self.TABLE_COMPAIGN],
573 'hook' : self.import_opp,
576 'probability': 'probability',
577 'partner_id/id': refbyname(self.TABLE_ACCOUNT, 'account_name', 'res.partner'),
578 'title_action': 'next_step',
579 'partner_address_id/id': 'partner_address_id/id',
580 'planned_revenue': 'amount',
581 'date_deadline': 'date_closed',
582 'user_id/id' : ref(self.TABLE_USER, 'assigned_user_id'),
583 'stage_id/id' : self.get_opportunity_status,
584 'type' : const('opportunity'),
585 'categ_id/id': call(self.get_category, 'crm.lead', value('opportunity_type')),
586 'email_from': 'email_from',
587 'state': map_val('status', self.opp_state) , #TODO
595 def get_compaign_mapping(self):
597 'model' : 'crm.case.resource.type',
606 def get_lead_status(self, sugar_val):
607 fields = ['name', 'type']
608 name = 'lead_' + sugar_val.get('status', '')
609 data = [sugar_val.get('status', ''), 'lead']
610 return self.import_object(fields, data, 'crm.case.stage', self.TABLE_STAGE, name, [('type', '=', 'lead'), ('name', 'ilike', sugar_val.get('status', ''))])
615 'In Progress': 'open',
616 'Recycled': 'cancel',
621 def import_lead(self, val):
622 if val.get('opportunity_id'): #if lead is converted into opp, don't import as lead
624 if val.get('primary_address_country'):
625 country_id = self.get_all_countries(val.get('primary_address_country'))
626 val['country_id/id'] = country_id
627 val['state_id/id'] = self.get_all_states(val.get('primary_address_state'), country_id)
630 def get_lead_mapping(self):
632 'model' : 'crm.lead',
633 'dependencies' : [self.TABLE_COMPAIGN, self.TABLE_USER],
634 'hook' : self.import_lead,
636 'name': concat('first_name', 'last_name'),
637 'contact_name': concat('first_name', 'last_name'),
638 'description': ppconcat('description', 'refered_by', 'lead_source', 'lead_source_description', 'website', 'email2', 'status_description', 'lead_source_description', 'do_not_call'),
639 'partner_name': 'account_name',
640 'email_from': 'email1',
641 'phone': 'phone_work',
642 'mobile': 'phone_mobile',
643 'title/id': call(self.get_job_title, value('salutation')),
645 'street': 'primary_address_street',
646 'street2': 'alt_address_street',
647 'zip': 'primary_address_postalcode',
648 'city':'primary_address_city',
649 'user_id/id' : ref(self.TABLE_USER, 'assigned_user_id'),
650 'stage_id/id' : self.get_lead_status,
651 'type' : const('lead'),
652 'state': map_val('status', self.lead_state) ,
654 'referred': 'refered_by',
655 'optout': 'do_not_call',
656 'channel_id/id': call(self.get_channel_id, value('lead_source')),
657 'type_id/id': ref(self.TABLE_COMPAIGN, 'campaign_id'),
658 'country_id/id': 'country_id/id',
659 'state_id/id': 'state_id/id'
667 def get_email(self, val):
668 email_address = sugar.get_contact_by_email(self.context.get('port'), self.context.get('username'), self.context.get('password'), val.get('email1'))
670 return ','.join(email_address)
672 def import_contact(self, val):
673 if val.get('primary_address_country'):
674 country_id = self.get_all_countries(val.get('primary_address_country'))
675 state = self.get_all_states(val.get('primary_address_state'), country_id)
676 val['country_id/id'] = country_id
677 val['state_id/id'] = state
680 def get_contact_mapping(self):
682 'model' : 'res.partner.address',
683 'dependencies' : [self.TABLE_ACCOUNT],
684 'hook' : self.import_contact,
686 'name': concat('first_name', 'last_name'),
687 'partner_id/id': ref(self.TABLE_ACCOUNT,'account_id'),
688 'phone': 'phone_work',
689 'mobile': 'phone_mobile',
692 'street': 'primary_address_street',
693 'zip': 'primary_address_postalcode',
694 'city': 'primary_address_city',
695 'country_id/id': 'country_id/id',
696 'state_id/id': 'state_id/id',
697 'email': self.get_email,
698 'type': const('contact')
706 def get_address_type(self, val, type):
707 if type == 'invoice':
708 type_address = 'billing'
710 type_address = 'shipping'
712 if type == 'default':
713 map_partner_address = {
715 'type': const('default'),
719 map_partner_address = {
721 'phone': 'phone_office',
722 'mobile': 'phone_mobile',
725 'street': type_address + '_address_street',
726 'zip': type_address +'_address_postalcode',
727 'city': type_address +'_address_city',
728 'country_id/id': 'country_id/id',
732 if val.get(type_address +'_address_country'):
733 country_id = self.get_all_countries(val.get(type_address +'_address_country'))
734 state = self.get_all_states(val.get(type_address +'_address_state'), country_id)
735 val['country_id/id'] = country_id
736 val['state_id/id'] = state
739 val['id_new'] = val['id'] + '_address_' + type
740 return self.import_object_mapping(map_partner_address, val, 'res.partner.address', self.TABLE_CONTACT, val['id_new'], self.DO_NOT_FIND_DOMAIN)
742 def get_partner_address(self, val):
744 type_dict = {'billing_address_street' : 'invoice', 'shipping_address_street' : 'delivery', 'type': 'default'}
745 for key, type_value in type_dict.items():
747 id = self.get_address_type(val, type_value)
748 address_id.append(id)
750 return ','.join(address_id)
752 def get_partner_mapping(self):
754 'model' : 'res.partner',
755 'dependencies' : [self.TABLE_USER],
758 'website': 'website',
759 'user_id/id': ref(self.TABLE_USER,'assigned_user_id'),
761 'comment': ppconcat('description', 'employees', 'ownership', 'annual_revenue', 'rating', 'industry', 'ticker_symbol'),
762 'customer': const('1'),
763 'supplier': const('0'),
764 'address/id':'address/id',
765 'parent_id/id_parent' : 'parent_id',
766 'address/id' : self.get_partner_address,
773 def get_ressource(self, val):
775 'name': concat('first_name', 'last_name'),
777 return self.import_object_mapping(map_resource, val, 'resource.resource', self.TABLE_RESSOURCE, val['id'], self.DO_NOT_FIND_DOMAIN)
779 def get_job_id(self, val):
781 data = [val.get('title')]
782 return self.import_object(fields, data, 'hr.job', 'hr_job', val.get('title'))
784 def get_user_address(self, val):
786 'name': concat('first_name', 'last_name'),
787 'city': 'address_city',
788 'country_id/id': 'country_id/id',
789 'state_id/id': 'state_id/id',
790 'street': 'address_street',
791 'zip': 'address_postalcode',
793 'phone': 'phone_work',
794 'mobile':'phone_mobile',
798 if val.get('address_country'):
799 country_id = self.get_all_countries(val.get('address_country'))
800 state_id = self.get_all_states(val.get('address_state'), country_id)
801 val['country_id/id'] = country_id
802 val['state_id/id'] = state_id
804 return self.import_object_mapping(map_user_address, val, 'res.partner.address', self.TABLE_CONTACT, val['id'], self.DO_NOT_FIND_DOMAIN)
806 def get_employee_mapping(self):
808 'model' : 'hr.employee',
809 'dependencies' : [self.TABLE_USER],
811 'resource_id/id': self.get_ressource,
812 'name': concat('first_name', 'last_name'),
813 'work_phone': 'phone_work',
814 'mobile_phone': 'phone_mobile',
815 'user_id/id': ref(self.TABLE_USER, 'id'),
816 'address_home_id/id': self.get_user_address,
817 'notes': ppconcat('messenger_type', 'messenger_id', 'description'),
818 'job_id/id': self.get_job_id,
819 'work_email' : 'email1',
820 'coach_id/id_parent' : 'reports_to_id',
827 def import_user(self, val):
828 user_obj = self.obj.pool.get('res.users')
829 user_ids = user_obj.search(self.cr, self.uid, [('login', '=', val.get('user_name'))])
831 val['.id'] = str(user_ids[0])
833 val['password'] = 'sugarcrm' #default password for all user #TODO needed in documentation
835 val['context_lang'] = self.context.get('lang','en_US')
838 def get_users_department(self, val):
839 dep = val.get('department')
844 return self.import_object(fields, data, 'hr.department', 'hr_department_user', dep)
846 def get_user_mapping(self):
848 'model' : 'res.users',
849 'hook' : self.import_user,
851 'name': concat('first_name', 'last_name'),
852 'login': 'user_name',
853 'context_lang' : 'context_lang',
854 'password' : 'password',
856 'context_department_id/id': self.get_users_department,
857 'user_email' : 'email1',
862 def get_mapping(self):
864 self.TABLE_USER : self.get_user_mapping(),
865 self.TABLE_EMPLOYEE : self.get_employee_mapping(),
866 self.TABLE_ACCOUNT : self.get_partner_mapping(),
867 self.TABLE_CONTACT : self.get_contact_mapping(),
868 self.TABLE_LEAD : self.get_lead_mapping(),
869 self.TABLE_OPPORTUNITY : self.get_opp_mapping(),
870 self.TABLE_MEETING : self.get_meeting_mapping(),
871 self.TABLE_CALL : self.get_calls_mapping(),
872 self.TABLE_TASK : self.get_task_mapping(),
873 self.TABLE_PROJECT : self.get_project_mapping(),
874 self.TABLE_PROJECT_TASK: self.get_project_task_mapping(),
875 self.TABLE_BUG: self.get_project_issue_mapping(),
876 self.TABLE_CASE: self.get_crm_claim_mapping(),
877 self.TABLE_NOTE: self.get_history_mapping(),
878 self.TABLE_EMAIL: self.get_email_mapping(),
879 self.TABLE_DOCUMENT: self.get_document_mapping(),
880 self.TABLE_COMPAIGN: self.get_compaign_mapping()
886 def get_email_subject(self, result):
887 return "your sugarcrm data were successfully imported at %s" % self.date_ended
889 def get_body_header(self, result):
890 return "Sugarcrm import : report of last import"
893 class import_sugarcrm(osv.osv):
894 """Import SugarCRM DATA"""
896 _name = "import.sugarcrm"
897 _description = __doc__
899 'username': fields.char('User Name', size=64, required=True),
900 'password': fields.char('Password', size=24,required=True),
901 'url' : fields.char('SugarSoap Api url:', size=264, required=True, help="Webservice's url where to get the data.\
902 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"),
904 'opportunity': fields.boolean('Leads and Opportunities', help="If Opportunities are checked, SugarCRM opportunities data imported in OpenERP crm-Opportunity form"),
905 'contact': fields.boolean('Contacts', help="If Contacts are checked, SugarCRM Contacts data imported in OpenERP partner address form"),
906 'account': fields.boolean('Accounts', help="If Accounts are checked, SugarCRM Accounts data imported in OpenERP partners form"),
907 'employee': fields.boolean('Employee', help="If Employees is checked, SugarCRM Employees data imported in OpenERP employees form"),
908 'meeting': fields.boolean('Meetings', help="If Meetings is checked, SugarCRM Meetings and Meeting Tasks data imported in OpenERP meetings form"),
909 'call': fields.boolean('Calls', help="If Calls is checked, SugarCRM Calls data imported in OpenERP phonecalls form"),
910 'claim': fields.boolean('Cases', help="If Cases is checked, SugarCRM Cases data imported in OpenERP Claims form"),
911 '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"),
912 'project': fields.boolean('Projects', help="If Projects is checked, SugarCRM Projects data imported in OpenERP Projects form"),
913 'project_task': fields.boolean('Project Tasks', help="If Project Tasks is checked, SugarCRM Project Tasks data imported in OpenERP Project Tasks form"),
914 'bug': fields.boolean('Bugs', help="If Bugs is checked, SugarCRM Bugs data imported in OpenERP Project Issues form"),
915 'document': fields.boolean('Documents', help="If Documents is checked, SugarCRM Documents data imported in OpenERP Document Form"),
916 'email_from': fields.char('Notify End Of Import To:', size=128),
917 '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."),
920 _defaults = {#to be set to true, but easier for debugging
921 'opportunity': False,
928 'email_history' : False,
930 'project_task': False,
933 'instance_name': 'sugarcrm',
934 'email_from': 'tfr@openerp.com',
937 'url': "http://localhost/sugarcrm/soap.php"
940 def check_url(self, url, context):
943 user = context.get('username')
944 password = context.get('password')
947 sugar.login(user, password, url)
953 def parse_valid_url(self, context=None):
956 url = context.get('url')
959 url_split = str(url).split('/')
960 while len(url_split) >= 3:
961 #3 case, soap.php is already at the end of url should be valid
963 if url_split[-1] == 'soap.php':
964 url = "/".join(url_split)
965 elif url_split[-1] == '':
966 url = "/".join(url_split) + "soap.php"
968 url = "/".join(url_split) + "/soap.php"
970 if self.check_url(url, context):
973 url_split = url_split[:-1] #take parent folder
976 def get_key(self, cr, uid, ids, context=None):
977 """Select Key as For which Module data we want import data."""
981 for current in self.browse(cr, uid, ids, context):
982 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})
983 if current.opportunity:
984 key_list.append('Leads')
985 key_list.append('Opportunities')
987 key_list.append('Contacts')
989 key_list.append('Accounts')
991 key_list.append('Employees')
993 key_list.append('Meetings')
995 key_list.append('Calls')
997 key_list.append('Cases')
998 if current.email_history:
999 key_list.append('Emails')
1000 key_list.append('Notes')
1002 key_list.append('Project')
1003 if current.project_task:
1004 key_list.append('ProjectTask')
1006 key_list.append('Bugs')
1007 if current.document:
1008 key_list.append('DocumentRevisions')
1012 def do_import_all(self, cr, uid, *args):
1016 context = {'username': args[4], 'password': args[5], 'url': args[3], 'instance_name': args[3]}
1017 imp = sugar_import(self, cr, uid, args[2], "import_sugarcrm", [args[1]], context)
1018 imp.set_table_list(args[0])
1022 def import_from_scheduler_all(self, cr, uid, ids, context=None):
1023 keys = self.get_key(cr, uid, ids, context)
1025 raise osv.except_osv(_('Warning !'), _('Select Module to Import.'))
1026 cron_obj = self.pool.get('ir.cron')
1027 url = self.parse_valid_url(context)
1028 args = (keys,context.get('email_user'), context.get('instance_name'), url, context.get('username'), context.get('password') )
1029 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})
1031 'name': 'SugarCRM Scheduler',
1032 'view_type': 'form',
1033 'view_mode': 'form,tree',
1034 'res_model': 'ir.cron',
1035 'res_id': new_create_id,
1036 'type': 'ir.actions.act_window',
1040 def import_all(self, cr, uid, ids, context=None):
1042 # """Import all sugarcrm data into openerp module"""
1043 keys = self.get_key(cr, uid, ids, context)
1044 url = self.parse_valid_url(context)
1045 context.update({'url': url})
1046 imp = sugar_import(self, cr, uid, context.get('instance_name'), "import_sugarcrm", [context.get('email_user')], context)
1047 imp.set_table_list(keys)
1050 obj_model = self.pool.get('ir.model.data')
1051 model_data_ids = obj_model.search(cr,uid,[('model','=','ir.ui.view'),('name','=','import.message.form')])
1052 resource_id = obj_model.read(cr, uid, model_data_ids, fields=['res_id'])
1054 'view_type': 'form',
1055 'view_mode': 'form',
1056 'res_model': 'import.message',
1057 'views': [(resource_id,'form')],
1058 'type': 'ir.actions.act_window',