# -*- coding: utf-8 -*- ############################################################################# # # Cybrosys Technologies Pvt. Ltd. # # Copyright (C) 2023-TODAY Cybrosys Technologies() # Author: Vishnuraj P (odoo@cybrosys.com) # # You can modify it under the terms of the GNU LESSER # GENERAL PUBLIC LICENSE (LGPL v3), Version 3. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. # # You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE # (LGPL v3) along with this program. # If not, see . # ############################################################################# import base64 from datetime import datetime, timezone from hubspot import HubSpot from hubspot.crm.deals import BatchInputSimplePublicObjectBatchInput from hubspot.crm.deals import SimplePublicObjectInput import requests from odoo import fields, models, _ from odoo.exceptions import AccessError, UserError class HubspotConnector(models.Model): """ Model for hubspot connector to set up the credentials and contains methods for various sync operations. Methods: action_connect(self): CONNECT smart button action to check the hubspot account with provided credentials, if account exist it connects, and shows the sync options. action_contact_sync(self): Button action for sync contacts based on user preference. action_company_sync(self): Button action for sync company based on user preference. action_deal_sync(self): Button action for sync deals based on user preference. #--- Contacts Various Sync Methods ---# action_export_partner(self): Method for exporting contacts when the contact is not available in hubspot. action_import_partner(self): Method for importing contacts when the contact is not available in odoo. action_update_hub_partner(self): Method for update contacts in hubspot, based on odoo contacts, action_update_odoo_partner(self): Method for update contacts in odoo, based on hubspot contacts, #--- Various Sync Methods for Company ---# action_export_company(self): Method for exporting company when the company is not available in hubspot. action_import_company(self): Method for importing company when the company is not available in odoo. action_update_hub_company(self): Method for update company in hubspot, based on odoo company. action_update_odoo_company(self): Method for update company in odoo, based on hubspot company. #--- Various Sync Methods for Deals ---# action_export_deals(self): Method for exporting deals when the deals are not available in hubspot. action_import_deals(self): Method for importing deals when the deals are not available in odoo. action_update_hub_deals(self): Method for update deals in hubspot, based on odoo deals. action_update_odoo_deals(self): Method for update deals in odoo, based on hubspot deals. """ _name = 'hubspot.connector' _description = 'HubSpot Connector' state = fields.Selection(string="State", selection=[("disconnected", "Disconnected"), ("connected", "Connected")], default="disconnected", help="Shows the connection is true or false") name = fields.Char(string="Connector Name", help="name of the instance") access_key = fields.Char(string="Access Token", help="Private app key of hubspot account") owner_id = fields.Char(string="Owner ID", required=True, help="Owner ID of Hubspot account") connection = fields.Boolean(string="Connection", help="Shows connected to hubspot or nor") # Toggles for Contacts sync import_contacts = fields.Boolean(string="Import Contacts", help="This will enable import of contact " "from hubspot to odoo") export_contacts = fields.Boolean(string="Export Contacts", help="This will enable export of contact " "from odoo to hubspot") update_odoo_contacts = fields.Boolean(string="Update Odoo Contacts", help="This will update contact in " "odoo from hubspot data") update_hub_contacts = fields.Boolean(string="Update Hubspot Contacts", help="This will update contact in " "hubspot from odoo data") # Toggles for Company sync import_company = fields.Boolean(string="Import Company", help="This will enable import of companies" "from hubspot to odoo") export_company = fields.Boolean(string="Export Company", help="This will enable export of companies " "from Odoo to HubSpot") update_odoo_company = fields.Boolean(string="Update Odoo Company", help="This will update company in odoo" "from hubspot data") update_hub_company = fields.Boolean(string="Update Hubspot Company", help="This will update company in " "hubspot from odoo data") # Toggles for Deals sync export_deals = fields.Boolean( string="Export Deals", help="This will enable export of deals from odoo to hubspot") import_deals = fields.Boolean( string="Import Deals", help="This will enable import of deals from hubspot to odoo") update_odoo_deals = fields.Boolean( string="Update Odoo Deals", help="This will update deals in odoo from hubspot data") update_hub_deals = fields.Boolean( string="Update Hubspot Deals", help="This will update deals in hubspot from odoo data") # Contacts last sync Times contacts_last_imported = fields.Datetime( string="Contact Last Imported", readonly=True, help="This is the last imported time") contacts_last_exported = fields.Datetime( string="Contact Last Exported", readonly=True, help="This is the last exported time") hub_contact_last_updated = fields.Datetime( string="Hubspot Contacts Updated", readonly=True, help="Last Hubspot Contacts Updated Time") odoo_contact_last_updated = fields.Datetime( string="Odoo Contacts Updated", readonly=True, help="Last Odoo Contacts Updated Time") # Company last sync Times company_last_imported = fields.Datetime( string="Company Last Imported", readonly=True, help="This is the last imported time") company_last_exported = fields.Datetime( string="Company Last Exported", readonly=True, help="This is the last exported time") hub_company_last_updated = fields.Datetime( string="Hubspot Company Updated", readonly=True, help="Last Hubspot Company Updated Time") odoo_company_last_updated = fields.Datetime( string="Odoo Company Updated", readonly=True, help="Last Odoo Company Updated Time") # Deals last sync Times deals_last_imported = fields.Datetime( string="Deals Last Imported", readonly=True, help="This is the last imported time") deals_last_exported = fields.Datetime( string="Deals Last Exported", readonly=True, help="This is the last exported time") hub_deal_last_updated = fields.Datetime( string="Hubspot Deal Updated", readonly=True, help="Last Hubspot Deal Updated Time") odoo_deal_last_updated = fields.Datetime( string="Odoo Deal Updated", readonly=True, help="Last Odoo Deal Updated Time") def action_connect(self): """ Method for testing connection; if credentials are correct connects and shows sync options, if connected disconnects. """ if not self.access_key or not self.owner_id: raise UserError(_("Access key and owner ID are required")) owners_endpoint = 'https://api.hubapi.com/crm/v3/owners' headers = {'Authorization': f'Bearer {self.access_key}'} try: response = requests.get(owners_endpoint, headers=headers) if response.status_code == 200: data = response.json() owners = data.get('results', []) if not owners: raise UserError(_("No owners found in HubSpot account")) for owner in owners: if str(owner['id']) == self.owner_id: self.connection = True self.state = "connected" return raise UserError(_("Owner ID does not match any HubSpot owner")) else: raise UserError(_("Failed to connect to HubSpot: %s") % response.text) except requests.exceptions.RequestException as e: raise UserError(_("Network error connecting to HubSpot: %s") % str(e)) def action_contact_sync(self): """ Method used to sync contacts it calls methods for import, export and update contacts methods when user need specific condition on sync """ rainbow_msg = "Congrats, " if self.export_contacts: exported_count = self.action_export_partner() if exported_count > 0: rainbow_msg += f"# {exported_count} Contacts Exported" if self.import_contacts: imported_count = self.action_import_partner() if imported_count > 0: rainbow_msg += f", # {imported_count} Contacts Imported" if self.update_hub_contacts: hub_update_count = self.action_update_hub_partner() if hub_update_count > 0: rainbow_msg += f", #{hub_update_count} Hubspot Contacts Updated" if self.update_odoo_contacts: odoo_update_count = self.action_update_odoo_partner() if odoo_update_count > 0: rainbow_msg += f", # {odoo_update_count} Odoo Contacts Updated" # If there is no sync option modifies data if rainbow_msg == "Congrats, ": rainbow_msg += "Contacts are already synced" # Rainbow man displays status of sync return { 'effect': { 'fadeout': 'slow', 'message': rainbow_msg, 'type': 'rainbow_man' } } def action_company_sync(self): """ Method used to sync company it calls methods for import, export and update company methods when user need specific condition on sync """ rainbow_msg = "Congrats, " if self.export_company: exported_count = self.action_export_company() if exported_count > 0: rainbow_msg += f"# {exported_count} Companies Exported" if self.import_company: imported_count = self.action_import_company() if imported_count > 0: rainbow_msg += f", # {imported_count} Companies Imported" if self.update_hub_company: hub_update_count = self.action_update_hub_company() if hub_update_count > 0: rainbow_msg += (f", # {hub_update_count}" f" Hubspot Companies Updated") if self.update_odoo_company: odoo_update_count = self.action_update_odoo_company() if odoo_update_count > 0: rainbow_msg += f", # {odoo_update_count} Odoo Companies Updated" if rainbow_msg == "Congrats, ": rainbow_msg += "Companies are already synced" return { 'effect': { 'fadeout': 'slow', 'message': rainbow_msg, 'type': 'rainbow_man' } } def action_deal_sync(self): """ Method used to sync deals, it calls methods for import, export and update deals methods when user need specific condition on sync """ rainbow_msg = "Congrats, " if self.export_deals: exported_count = self.action_export_deals() if exported_count > 0: rainbow_msg += f"# {exported_count} Deals Exported" if self.import_deals: imported_count = self.action_import_deals() if imported_count > 0: rainbow_msg += f", # {imported_count} Deals Imported" if self.update_hub_deals: hub_update_count = self.action_update_hub_deals() if hub_update_count > 0: rainbow_msg += f", # {hub_update_count} Hubspot Deals Updated" if self.update_odoo_deals: odoo_update_count = self.action_update_odoo_deals() if odoo_update_count > 0: rainbow_msg += f", # {odoo_update_count} Odoo Deals Updated" if rainbow_msg == "Congrats, ": rainbow_msg += "Deals are already synced" return { 'effect': { 'fadeout': 'slow', 'message': rainbow_msg, 'type': 'rainbow_man' } } def action_export_partner(self): """ Method used to Export Contacts from Odoo to Hubspot, also creates non-existing odoo_mail and odoo_image_string fields which are not present in hubspot and export according data to it. """ # Set up HubSpot API connection. api_key = self.access_key base_url = 'https://api.hubapi.com' # Lists fields and their properties need to create in hubspot. partner_fields = [ { 'name': 'odoo_mail', 'label': 'Mail', 'type': 'string' }, { 'name': 'odoo_image_string', 'label': 'Image String', 'type': 'string' }, ] for field in partner_fields: # Check each field in partner_fields exists in HubSpot or not endpoint = f"/properties/v1/contacts/properties/named/" \ f"{field['name']}" headers = { 'Content-Type': 'application/json', 'Authorization': f'Bearer {api_key}' } response = requests.get(base_url + endpoint, headers=headers) # Response returns a status code "200" when field exist in hubspot. if response.status_code != 200: # Create custom field in HubSpot endpoint = '/properties/v1/contacts/properties' headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer {}'.format(api_key) } # Properties of Field ie, going to create payload = { 'name': field['name'], 'label': field['label'], 'description': 'Custom field created form odoo', 'groupName': 'contactinformation', 'type': field['type'] } # API call for Field creation response = requests.post(base_url + endpoint, json=payload, headers=headers) # Returns status code "200" when successfully created the field if response.status_code == 200: pass # Notification : Successfully connected. else: raise AccessError(f"Failed to create {field['name']} field," f"Status code: {response.status_code}") # Setting api client connection via api api_client = HubSpot(access_token=self.access_key) odoo_partners = self.env['res.partner'].search([]) success_count = 0 # Fetch HubSpot contact's ID as list hubspot_partners = [rec.properties['hs_object_id'] for rec in api_client.crm.contacts.get_all()] for rec in odoo_partners: # If the Odoo contact not present in Hubspot ID list export it if rec.hs_object_id not in hubspot_partners: properties = { "firstname": rec.name, "lastname": "", "odoo_mail": rec.email, "phone": rec.phone if rec.phone else None, "company": rec.commercial_company_name or rec.company_name if rec.commercial_company_name or rec.company_name else None, "jobtitle": rec.function if rec.function else None, "website": rec.website if rec.website else None, "address": rec.street + "," + rec.street2 if ( rec.street and rec.street2) else ( rec.street or rec.street2 or ""), "city": rec.city if rec.city else None, "state": rec.state_id.name if rec.state_id else None, "zip": rec.zip if rec.zip else None, "country": rec.country_id.name if rec.country_id else None, "odoo_image_string": base64.b64encode(rec.image_1920). decode('utf-8') if (rec.image_1920 and len(base64.b64encode( rec.image_1920).decode('utf-8')) < 65500) else None, } api_response = api_client.crm.contacts.basic_api.create( simple_public_object_input_for_create= SimplePublicObjectInput(properties)) # If Exported then update Hubspot ID in Odoo if api_response: rec.write({ 'hs_object_id': api_response.properties['hs_object_id'], 'sync_mode': 'export'}) else: # change it to Failure msg. pass success_count += 1 # If Any record exported Create Sync History if success_count > 0: self.env['hubspot.sync.history'].sudo().create({ 'date': datetime.now(), 'res_model_id': self.env.ref('base.model_res_partner').id, 'sync_mode': 'export', 'state': 'success', 'count': success_count, }) self.contacts_last_exported = datetime.now() # Returns Exported Count return success_count def action_import_partner(self): """ Method used fetch data from Hubspot and creates contacts based on it. """ # Fields needs to fetch from hubspot. needed_fields = [ "firstname", "lastname", "email", "phone", "company", "jobtitle", "website", "address", "city", "state", "zip", "country", "odoo_mail", "odoo_image_string", "hs_object_id" ] api_client = HubSpot(access_token=self.access_key) odoo_partners = self.env['res.partner'].search([]).mapped( 'hs_object_id') # Getting needed fields from hubspot hubspot_partners = api_client.crm.contacts.get_all( properties=needed_fields) partners_to_create = [] success_count = 0 # Creates Dictionaries from country and state model to fetch # state_id and country_id state_dict = {state['name']: state['id'] for state in self.env[ 'res.country.state'].search_read([], ['name'])} country_dict = {country['name']: country['id'] for country in self.env[ 'res.country'].search_read([], ['name'])} for rec in hubspot_partners: # If hubspot record not present in odoo records creates it. if rec.properties['hs_object_id'] not in odoo_partners: partners_to_create.append({ 'name': rec.properties['firstname'] + '' + rec.properties[ 'lastname'] if rec.properties[ 'lastname'] else rec.properties['firstname'], 'email': rec.properties['email'] if rec.properties[ 'email'] else rec.properties[ 'odoo_mail'] if rec.properties['odoo_mail'] else None, 'phone': rec.properties['phone'], 'function': rec.properties['jobtitle'], 'website': rec.properties['website'], 'street': rec.properties['address'], 'city': rec.properties['city'], 'zip': rec.properties['zip'], 'state_id': state_dict.get(str(rec.properties['state']), None), 'country_id': country_dict.get( str(rec.properties['country']), None), 'image_1920': base64.b64decode( rec.properties['odoo_image_string']) if rec.properties[ 'odoo_image_string'] else None, 'hs_object_id': rec.properties['hs_object_id'], 'sync_mode': 'import' }) # counting total records created success_count += 1 if partners_to_create: self.env['res.partner'].sudo().create(partners_to_create) # If new record created, then creates sync history. if success_count > 0: self.env['hubspot.sync.history'].sudo().create({ 'date': datetime.now(), 'res_model_id': self.env.ref('base.model_res_partner').id, 'sync_mode': 'import', 'state': 'success', 'count': success_count, }) self.contacts_last_imported = datetime.now() return success_count def action_update_hub_partner(self): """ Method used to update hubspot contacts based on odoo contacts. """ api_client = HubSpot(access_token=self.access_key) odoo_partners = self.env['res.partner'].search([]) odoo_partners_list = odoo_partners.mapped('hs_object_id') hubspot_partners = api_client.crm.contacts.get_all() update_success = 0 data_to_update = [] for rec in hubspot_partners: # Checking the hubspot contact present in odoo # then take the corresponding odoo record if rec.properties['hs_object_id'] in odoo_partners_list: odoo_record = self.env['res.partner'].search( [('hs_object_id', '=', rec.properties['hs_object_id'])]) # If the corresponding odoo record is last updated then # update hubspot record based on that if odoo_record.write_date > ( self.hub_contact_last_updated or rec.updated_at. astimezone(timezone.utc).replace(tzinfo=None)): data_to_update.append({ 'id': rec.properties['hs_object_id'], 'properties': { "firstname": odoo_record.name, "lastname": "", "odoo_mail": odoo_record.email, "phone": odoo_record.phone, "company": odoo_record.commercial_company_name or odoo_record.company_name, "jobtitle": odoo_record.function, "website": odoo_record.website, "address": odoo_record.street + "," + odoo_record.street2 if ( odoo_record.street and odoo_record.street2) else (odoo_record.street or odoo_record.street2 or ""), "city": odoo_record.city if odoo_record.city else None, "state": odoo_record.state_id.name if odoo_record.state_id else None, "zip": odoo_record.zip if odoo_record.zip else None, "country": odoo_record.country_id.name if odoo_record.country_id else None, "odoo_image_string": base64.b64encode( odoo_record.image_1920).decode('utf-8') if ( odoo_record.image_1920 and len( base64.b64encode( odoo_record.image_1920).decode( 'utf-8')) < 65500) else "", } }) update_success += 1 # Batch Update of record to hubspot. api_client.crm.contacts.batch_api.update( batch_input_simple_public_object_batch_input= BatchInputSimplePublicObjectBatchInput(data_to_update)) self.hub_contact_last_updated = datetime.now() if update_success > 0: self.env['hubspot.sync.history'].sudo().create({ 'date': self.hub_contact_last_updated, 'res_model_id': self.env.ref('base.model_res_partner').id, 'sync_mode': 'hub_updated', 'state': 'success', 'count': update_success, }) # Returns successfully updated count. return update_success def action_update_odoo_partner(self): """Method used to update odoo partner based on the hubspot contacts.""" api_client = HubSpot(access_token=self.access_key) odoo_partners = self.env['res.partner'].search([]) # Fields need to fetch from hubspot. needed_fields = [ "firstname", "lastname", "email", "phone", "company", "jobtitle", "website", "address", "city", "state", "zip", "country", "odoo_mail", "odoo_image_string", "hs_object_id" ] hubspot_partners = api_client.crm.contacts.get_all( properties=needed_fields) hubspot_partners_list = [rec.properties['hs_object_id'] for rec in hubspot_partners] update_success = 0 # Creates Dictionaries from country and state model to fetch # state_id and country_id state_dict = {state['name']: state['id'] for state in self.env[ 'res.country.state'].search_read([], ['name'])} country_dict = {country['name']: country['id'] for country in self.env[ 'res.country'].search_read([], ['name'])} for rec in odoo_partners: # If odoo record present in hubspot record then teke # corresponding hubspot record if rec.hs_object_id in hubspot_partners_list: hubspot_partner = {h.id: h for h in hubspot_partners} hub_record = hubspot_partner.get(rec.hs_object_id, None) # If hubspot record is recently updated, then update # odoo record based on that if hub_record.updated_at.astimezone(timezone.utc).replace( tzinfo=None) > ( self.odoo_contact_last_updated or rec.write_date): data_to_update = { 'name': hub_record.properties[ 'firstname'] + '' + hub_record.properties[ 'lastname'] if hub_record.properties[ 'lastname'] else hub_record.properties['firstname'], 'email': hub_record.properties[ 'email'] if hub_record.properties[ 'email'] else hub_record.properties[ 'odoo_mail'] if hub_record.properties[ 'odoo_mail'] else None, 'phone': hub_record.properties['phone'], 'function': hub_record.properties['jobtitle'], 'website': hub_record.properties['website'], 'street': hub_record.properties['address'], 'city': hub_record.properties['city'], 'zip': hub_record.properties['zip'], 'state_id': state_dict.get(str(hub_record.properties[ 'state']), None), 'country_id': country_dict.get( str(hub_record.properties['country']), None), 'image_1920': base64.b64decode( hub_record.properties[ 'odoo_image_string']) if hub_record.properties[ 'odoo_image_string'] else None, } update_success += 1 rec.write(data_to_update) self.odoo_contact_last_updated = datetime.now() # If there is any contact updated then creates sync history. if update_success > 0: self.env['hubspot.sync.history'].sudo().create({ 'date': self.odoo_contact_last_updated, 'res_model_id': self.env.ref('base.model_res_partner').id, 'sync_mode': 'odoo_updated', 'state': 'success', 'count': update_success, }) # Returns updated record's count. return update_success def action_export_company(self): """ Method to export companies to hubspot. """ api_client = HubSpot(access_token=self.access_key) odoo_companies = self.env['res.company'].search([]) hubspot_companies = [] success_count = 0 for rec in api_client.crm.companies.get_all(): hubspot_companies.append(rec.properties['hs_object_id']) for rec in odoo_companies: # If odoo record not present in hubspot record create it # via api call if rec.hs_object_id not in hubspot_companies: properties = { "name": rec.name, "domain": rec.website, "description": rec.company_details if rec.company_details else None, "phone": rec.phone, "address": rec.street + "," + rec.street2 if ( rec.street and rec.street2) else ( rec.street or rec.street2 or ""), "city": rec.city, "state": rec.state_id.name if rec.state_id else None, "zip": rec.zip if rec.zip else None, "country": rec.country_id.name if rec.country_id else None, "industry": "", } # API call for create company record on hubspot. api_response = api_client.crm.companies.basic_api.create( simple_public_object_input_for_create= SimplePublicObjectInput(properties)) # After exporting save the unique hubspot id on that record if api_response: rec.write({ 'hs_object_id': api_response.properties['hs_object_id'], 'sync_mode': 'export' }) success_count += 1 self.company_last_exported = datetime.now() # If there is any record exported then creates history record. if success_count > 0: self.env['hubspot.sync.history'].sudo().create({ 'date': self.company_last_exported, 'res_model_id': self.env.ref('base.model_res_company').id, 'sync_mode': 'export', 'state': 'success', 'count': success_count, }) # Returns the exported count. return success_count def action_import_company(self): """ Method to Import record from hubspot """ api_client = HubSpot(access_token=self.access_key) odoo_companies = self.env['res.company'].search([]).mapped( 'hs_object_id') # The fields need to fetch from hubspot. needed_fields = [ "hs_object_id", "name", "domain", "website", "description", "phone", "city", "state", "country", "zip" ] hubspot_companies = api_client.crm.companies.get_all( properties=needed_fields) companies_to_create = [] success_count = 0 # Creates Dictionaries from country and state model to fetch # state_id and country_id state_dict = {state['name']: state['id'] for state in self.env[ 'res.country.state'].search_read([], ['name'])} country_dict = {country['name']: country['id'] for country in self.env[ 'res.country'].search_read([], ['name'])} for rec in hubspot_companies: # If hubspot record not present in odoo record, create new record. if rec.properties['hs_object_id'] not in odoo_companies: companies_to_create.append({ 'name': rec.properties['name'], 'website': rec.properties['domain'], 'company_details': rec.properties['description'], 'phone': rec.properties['phone'], 'city': rec.properties['city'], 'state_id': state_dict.get(str(rec.properties['state']), None), 'country_id': country_dict.get( str(rec.properties['country']), None), 'zip': rec.properties['zip'], 'hs_object_id': rec.properties['hs_object_id'], 'sync_mode': 'import', }) success_count += 1 if companies_to_create: self.env['res.company'].sudo().create(companies_to_create) self.company_last_imported = datetime.now() # If any new record created then create history record. if success_count > 0: self.env['hubspot.sync.history'].sudo().create({ 'date': self.company_last_imported, 'res_model_id': self.env.ref('base.model_res_company').id, 'sync_mode': 'import', 'state': 'success', 'count': success_count, }) # Returns the created record count. return success_count def action_update_hub_company(self): """ Method to update hubspot record based on odoo records """ api_client = HubSpot(access_token=self.access_key) odoo_company = self.env['res.company'].search([]) odoo_company_list = odoo_company.mapped('hs_object_id') hubspot_company = api_client.crm.companies.get_all() update_success = 0 data_to_update = [] for rec in hubspot_company: # If hubspot record present in odoo record then take # corresponding odoo record if rec.properties['hs_object_id'] in odoo_company_list: odoo_record = self.env['res.company'].search( [('hs_object_id', '=', rec.properties['hs_object_id'])]) # If odoo record is recently updated then update hubspot record. if odoo_record.write_date > ( self.hub_company_last_updated or rec.updated_at. astimezone(timezone.utc).replace(tzinfo=None)): data_to_update.append({ 'id': rec.properties['hs_object_id'], 'properties': { "name": odoo_record.name, "domain": odoo_record.website, "description": odoo_record.company_details if odoo_record.company_details else None, "phone": odoo_record.phone, "address": odoo_record.street + "," + odoo_record.street2 if (odoo_record.street and odoo_record.street2) else (odoo_record.street or odoo_record.street2 or ""), "city": odoo_record.city, "state": odoo_record.state_id.name if odoo_record.state_id else None, "zip": odoo_record.zip if odoo_record.zip else None, "country": odoo_record.country_id.name if odoo_record.country_id else None, "industry": "", } }) update_success += 1 api_client.crm.companies.batch_api.update( batch_input_simple_public_object_batch_input= BatchInputSimplePublicObjectBatchInput(data_to_update)) self.hub_company_last_updated = datetime.now() if update_success > 0: self.env['hubspot.sync.history'].sudo().create({ 'date': self.hub_company_last_updated, 'res_model_id': self.env.ref('base.model_res_company').id, 'sync_mode': 'hub_updated', 'state': 'success', 'count': update_success, }) # Returns the updated count. return update_success def action_update_odoo_company(self): """ Method to update odoo company records based on hubspot record. """ api_client = HubSpot(access_token=self.access_key) odoo_company = self.env['res.company'].search([]) # Needed fields to fetch from hubspot. needed_fields = [ "hs_object_id", "name", "domain", "website", "description", "phone", "city", "state", "country", "zip" ] hubspot_company = api_client.crm.companies.get_all( properties=needed_fields) hubspot_company_list = [rec.properties['hs_object_id'] for rec in hubspot_company] update_success = 0 # Creates Dictionaries from country and state model to fetch # state_id and country_id. state_dict = {state['name']: state['id'] for state in self.env[ 'res.country.state'].search_read([], ['name'])} country_dict = {country['name']: country['id'] for country in self.env[ 'res.country'].search_read([], ['name'])} for rec in odoo_company: # If the odoo company record is present in hubspot company, # take the corresponding hubspot record. if rec.hs_object_id in hubspot_company_list: hubspot_company_dict = {h.id: h for h in hubspot_company} hub_record = hubspot_company_dict.get(rec.hs_object_id, None) # If the hubspot record is recently modified then update # odoo record based on that. if hub_record.updated_at.astimezone(timezone.utc).replace( tzinfo=None) > rec.write_date: data_to_update = { 'name': hub_record.properties['name'], 'website': hub_record.properties['domain'], 'company_details': hub_record.properties['description'], 'phone': hub_record.properties['phone'], 'city': hub_record.properties['city'], 'state_id': state_dict.get(str(hub_record.properties[ 'state']), None), 'country_id': country_dict.get( str(hub_record.properties['country']), None), 'zip': hub_record.properties['zip'], } update_success += 1 rec.write(data_to_update) self.odoo_company_last_updated = datetime.now() if update_success > 0: self.env['hubspot.sync.history'].sudo().create({ 'date': self.odoo_company_last_updated, 'res_model_id': self.env.ref('base.model_res_company').id, 'sync_mode': 'odoo_updated', 'state': 'success', 'count': update_success, }) # Returns updated count. return update_success def action_export_deals(self): """ Method for export deals from odoo to hubspot """ api_client = HubSpot(access_token=self.access_key) odoo_deals = self.env['crm.lead'].search([]) hubspot_deals = [] success_count = 0 for rec in api_client.crm.deals.get_all(): hubspot_deals.append(rec.properties['hs_object_id']) for rec in odoo_deals: # If the deal present in odoo not available in hubspot # creates it via api call. if rec.hs_object_id not in hubspot_deals: properties = { "dealname": rec.name, "amount": rec.expected_revenue if rec.expected_revenue else None, "closedate": rec.date_deadline if rec.date_deadline else '', "createdate": rec.create_date.strftime( '%Y-%m-%dT%H:%M:%SZ') if rec.create_date else None, } # API call to create deals in hubspot. api_response = api_client.crm.deals.basic_api.create( simple_public_object_input_for_create= SimplePublicObjectInput(properties)) # If the record is created in hubspot then store the unique # hubspot id in deal record in odoo too. if api_response: rec.write({ 'hs_object_id': api_response.properties['hs_object_id'], 'sync_mode': 'export' }) success_count += 1 self.deals_last_exported = datetime.now() # If there is any record exported then creates history record if success_count > 0: self.env['hubspot.sync.history'].sudo().create({ 'date': self.deals_last_exported, 'res_model_id': self.env.ref('crm.model_crm_lead').id, 'sync_mode': 'export', 'state': 'success', 'count': success_count, }) # Returns the exported count. return success_count def action_import_deals(self): """ Method to import deals record from hubspot to odoo. """ # Needed fields to fetch from hubspot. needed_fields = [ "dealname", "amount", "closedate" ] api_client = HubSpot(access_token=self.access_key) odoo_deals = self.env['crm.lead'].search([]).mapped('hs_object_id') hubspot_deals = api_client.crm.deals.get_all(properties=needed_fields) deals_to_create = [] success_count = 0 for rec in hubspot_deals: # If hubspot record not present in odoo creates the record. if rec.properties['hs_object_id'] not in odoo_deals: deals_to_create.append({ 'name': rec.properties['dealname'] or 'Lead', 'expected_revenue': rec.properties['amount'], 'date_deadline': rec.properties['closedate'], 'hs_object_id': rec.properties['hs_object_id'], 'sync_mode': 'import', }) success_count += 1 if deals_to_create: self.env['crm.lead'].sudo().create(deals_to_create) self.deals_last_imported = datetime.now() # If create any records then creates the history for it. if success_count > 0: self.env['hubspot.sync.history'].sudo().create({ 'date': self.deals_last_imported, 'res_model_id': self.env.ref('crm.model_crm_lead').id, 'sync_mode': 'import', 'state': 'success', 'count': success_count, }) # Return the created records count. return success_count def action_update_hub_deals(self): """ Method for update hubspot record based on odoo record. """ api_client = HubSpot(access_token=self.access_key) odoo_deal = self.env['crm.lead'].search([]) odoo_deal_list = odoo_deal.mapped('hs_object_id') hubspot_deal = api_client.crm.deals.get_all() update_success = 0 data_to_update = [] for rec in hubspot_deal: # If hubspot record present in odoo records then take the # corresponding odoo record. if rec.properties['hs_object_id'] in odoo_deal_list: odoo_record = self.env['crm.lead'].search( [('hs_object_id', '=', rec.properties['hs_object_id'])]) # If the odoo record is recently modified then update hubspot # record based on that. if odoo_record.write_date > rec.updated_at.astimezone( timezone.utc).replace(tzinfo=None): data_to_update.append({ 'id': rec.properties['hs_object_id'], 'properties': { "dealname": odoo_record.name, "amount": odoo_record.expected_revenue if odoo_record.expected_revenue else None, "closedate": odoo_record.date_deadline if odoo_record.date_deadline else '', "createdate": odoo_record.create_date.strftime( '%Y-%m-%dT%H:%M:%SZ') if odoo_record.create_date else None, } }) update_success += 1 api_client.crm.deals.batch_api.update( batch_input_simple_public_object_batch_input= BatchInputSimplePublicObjectBatchInput(data_to_update)) self.hub_deal_last_updated = datetime.now() # Creates history record when any deal updated. if update_success > 0: self.env['hubspot.sync.history'].sudo().create({ 'date': self.hub_deal_last_updated, 'res_model_id': self.env.ref('crm.model_crm_lead').id, 'sync_mode': 'hub_updated', 'state': 'success', 'count': update_success, }) # Returns the updated count. return update_success def action_update_odoo_deals(self): """ Method to update odoo leads based on hubspot records. """ api_client = HubSpot(access_token=self.access_key) odoo_deal = self.env['crm.lead'].search([]) # Needed field to fetch from hubspot. needed_fields = ["dealname", "hs_object_id", "amount", "closedate"] hubspot_deal = api_client.crm.deals.get_all( properties=needed_fields) hubspot_deal_list = [rec.properties['hs_object_id'] for rec in hubspot_deal] update_success = 0 for rec in odoo_deal: # If odoo record present in hubspot record, take the hubspot record. if rec.hs_object_id in hubspot_deal_list: hubspot_deal_dict = {h.id: h for h in hubspot_deal} hub_record = hubspot_deal_dict.get(rec.hs_object_id, None) # If the hubspot record is recently modified then # update odoo record based on that. if hub_record.updated_at.astimezone(timezone.utc).replace( tzinfo=None) > rec.write_date: data_to_update = { 'name': hub_record.properties['dealname'] or 'Lead', 'expected_revenue': hub_record.properties['amount'], 'date_deadline': hub_record.properties['closedate'], } update_success += 1 rec.write(data_to_update) self.odoo_deal_last_updated = datetime.now() # Creates history record based on updated records count. if update_success > 0: self.env['hubspot.sync.history'].sudo().create({ 'date': self.odoo_deal_last_updated, 'res_model_id': self.env.ref('crm.model_crm_lead').id, 'sync_mode': 'odoo_updated', 'state': 'success', 'count': update_success, }) # returns the updated count. return update_success