You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

312 lines
14 KiB

# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Arjun S(odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
import base64
import json
import requests
from requests import RequestException
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
class ResConfigSettings(models.TransientModel):
"""
Inherits the model Res Config Settings to extend and add the fields and
methods
"""
_inherit = 'res.config.settings'
domain = fields.Char(string='Domain',
help='Enter the domain of the freshdesk of the user',
config_parameter='odoo_freshdesk_connector.domain')
api_key = fields.Char(string='API Key',
help='Enter the API key to connect to the freshdesk',
config_parameter='odoo_freshdesk_connector.api_key')
import_contacts = fields.Boolean(string='Import Contacts',
help='Make true if you want to import '
'contacts',
config_parameter='odoo_freshdesk_connector.import_contacts')
import_tickets = fields.Boolean(string='Import Tickets',
help='Make true if you want to import '
'tickets',
config_parameter='odoo_freshdesk_connector.import_tickets')
export_tickets = fields.Boolean(string='Export Tickets',
help='Make true if you want to export '
'tickets',
config_parameter='odoo_freshdesk_connector.export_tickets')
@api.onchange('import_contacts')
def _onchange_import_contacts(self):
"""
Summary:
This is the method _onchange_import_contacts which is here used to
make the import_tickets booleans if the import_contacts boolean is
True
"""
if self.import_contacts:
self.import_tickets = True
@api.onchange('import_tickets')
def _onchange_import_tickets(self):
"""
Summary:
This is the method _onchange_import_tickets which is triggered on
the change of the field import_tickets to change the import_contacts
boolean to false if the import_tickets boolean is False
"""
if self.import_contacts and not self.import_tickets:
self.import_contacts = False
def action_test_freshdesk_connection(self):
"""
Summary:
This is the method action_test_freshdesk_connection which is
triggered when clicked on the test, which checks whether we can
connect to the freshdesk with the given credentials as API key and
the domain.
Returns:
notification: The notification which returns the notification if
whether the connection to the freshdesk in successful or not.
"""
if self.api_key and self.domain:
api_key_base64 = base64.b64encode(self.api_key.encode()).decode()
url = self.domain + '/api/v2/tickets'
payload = {}
headers = {
'Content-Type': 'application/json',
'Authorization': f'Basic {api_key_base64}',
}
try:
response = requests.request("GET", url, headers=headers,
data=payload, timeout=10)
return self.action_notify(
True) if response.status_code == 200 else self.action_notify(
False)
except RequestException as exception:
raise ValidationError(_('Invalid domain')) from exception
else:
raise ValidationError(_('Please enter the credentials'))
def action_notify(self, success):
"""
Summary:
This is the method action_notify which is triggered from the method
action_test_freshdesk_connection to create the notification whether
the connection is successful or not.
Args:
success: True if the connection to the freshdesk is successful
else False
Returns:
notification: which returns the client action to show the
notification as successful or not to the method
action_test_freshdesk_connection
"""
notification = {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('Connection successful!') if success is True else _(
'Connection not successful!'),
'message': 'Connection to Freshdesk is successful.' if success is True else 'Connection to Freshdesk is not successful.',
'sticky': True,
'type': 'success' if success is True else 'danger'
}
}
return notification
def action_execute_freshdesk_operation(self):
"""
Summary:
This is the method action_execute_freshdesk_operation which is
triggered when the button to execute the operations of importing
and exporting the data from the Freshdesk is clicked from section
of the freshdesk from the general settings.
"""
imported_tickets = 0
exported_tickets = 0
if self.import_tickets:
imported_tickets = self.action_import_tickets()
if self.export_tickets:
exported_tickets = self.action_export_tickets()
notification = {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _(
'Executed the operation successfully!'),
'message': f'Successfully imported {imported_tickets} Tickets '
f'and exported {exported_tickets} tickets',
'sticky': True,
'type': 'success'
}
}
return notification
def action_import_tickets(self):
"""
Summary:
This is the method create_tickets which is triggered from the method
action_execute_freshdesk_operation if the import_tickets boolean is
true from the record when executing the operation.
"""
if self.api_key and self.domain:
api_key_base64 = base64.b64encode(self.api_key.encode()).decode()
ticket_url = self.domain + '/api/v2/tickets?include=description'
payload = {}
headers = {
'Content-Type': 'application/json',
'Authorization': f'Basic {api_key_base64}',
}
try:
tickets_response = requests.request("GET", ticket_url,
headers=headers,
data=payload, timeout=10)
tickets_data = json.loads(tickets_response.text)
tickets_count = 0
for ticket in tickets_data:
existing_ticket = self.env['help.ticket'].search(
[('freshdesk_id', '=', ticket.get('id'))], limit=1)
if not existing_ticket:
ticket_vals = {
'subject': ticket.get('subject'),
'description': ticket.get('description'),
'freshdesk_id': ticket.get('id'),
}
if self.import_contacts:
contact = self.action_import_contact(ticket=ticket,
headers=headers,
payload=payload)
ticket_vals['customer_id'] = contact.id
self.env['help.ticket'].create(ticket_vals)
tickets_count += 1
return tickets_count
except RequestException as exception:
raise ValidationError(
_('Invalid domain')) from exception
else:
raise ValidationError(_('Please enter the credentials'))
def action_import_contact(self, ticket, headers, payload):
"""
Summary:
This is the method create_contact which is called from the method
create_tickets, which is here used to create the contact for the
corresponding ticket and add to that ticket if the boolean import
contact is true in the configuration of the freshdesk
Args:
ticket: The ticket which gets the contact id
headers: Headers needed for the request to get data of the contact
payload: The payload for the contact request
Returns:
contact: Checks if there is already any contact present with this
id which means that is already imported if not then a new
contact will be created and returned to the parent method.
existing_contact: Returns the existing contact if there is already
a contact with this freshdesk id.
"""
contact_url = self.domain + '/api/v2/contacts/' + str(
ticket.get('requester_id'))
contact_response = requests.request("GET", contact_url,
headers=headers,
data=payload, timeout=10)
contacts_data = json.loads(contact_response.text)
existing_contact = self.env['res.partner'].search(
[('freshdesk_id', '=', contacts_data.get('id'))], limit=1)
if not existing_contact:
contact = self.env['res.partner'].create({
'name': contacts_data.get('name'),
'email': contacts_data.get('email'),
'freshdesk_id': contacts_data.get('id'),
'phone': contacts_data.get('phone'),
})
return contact
return existing_contact
def action_export_tickets(self):
"""
Summary:
This is the method action_export_tickets which is called from the
method action_execute_freshdesk_operation, this method acts the
method to export the tickets to the freshdesk that is already not
exported
"""
tickets_link = self.domain + '/api/v2/tickets'
api_key_base64 = base64.b64encode(self.api_key.encode()).decode()
ticket_count = 0
for ticket in self.env['help.ticket'].search([]).filtered(
lambda x: not x.freshdesk_id):
headers = {
'Content-Type': 'application/json',
'Authorization': f'Basic {api_key_base64}'
}
contact = self.action_export_contact(ticket=ticket, header=headers)
payload = json.dumps({
"subject": ticket.subject,
"description": ticket.description,
"email": ticket.customer_id.email if ticket.customer_id else None,
"priority": int(ticket.priority),
'requester_id': int(contact),
'status': ticket.stage_id.id,
})
try:
ticket_response = requests.post(tickets_link, headers=headers,
data=payload, timeout=10)
ticket_data = json.loads(ticket_response.text)
ticket.freshdesk_id = ticket_data.get('id')
ticket_count += 1
except RequestException as exception:
raise ValidationError(_('Invalid domain')) from exception
return ticket_count
def action_export_contact(self, ticket, header):
"""
Summary:
This is the method export_contact which is called from the method
action_export_tickets, which is here used to create the contact
which is present in the customer that is not imported or exported
already.
Args:
ticket: Record of the ticket that is present in the model that
should be exported.
Returns:
contact: Create contact id or if the user already has the id ie,
already imported then that already existing freshdesk id will be
returned
existing_contact: Returns the existing contact is the contact with
this freshdesk id is already imported or existing
"""
if not ticket.customer_id.freshdesk_id:
contact_link = self.domain + '/api/v2/contacts'
payload = json.dumps({
"name": ticket.customer_id.name,
"email": ticket.customer_id.email,
"active": True,
"address": ticket.customer_id.contact_address,
"phone": ticket.customer_id.phone,
})
create_response = requests.post(contact_link, headers=header,
data=payload, timeout=10)
contacts_data = json.loads(create_response.text)
contact = contacts_data.get('id')
ticket.customer_id.freshdesk_id = contact
return contact
existing_contact = ticket.customer_id.freshdesk_id
return existing_contact