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
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
|
|
|