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