Browse Source

Aug 12 [ADD] Initial Commit 'odoo_jira_connector'

pull/331/head
AjmalCybro 12 months ago
parent
commit
6cfe341108
  1. 47
      odoo_jira_connector/README.rst
  2. 33
      odoo_jira_connector/__init__.py
  3. 55
      odoo_jira_connector/__manifest__.py
  4. 22
      odoo_jira_connector/controllers/__init__.py
  5. 44
      odoo_jira_connector/controllers/odoo_jira_connector.py
  6. 6
      odoo_jira_connector/doc/RELEASE_NOTES.md
  7. 29
      odoo_jira_connector/models/__init__.py
  8. 102
      odoo_jira_connector/models/ir_attachment.py
  9. 80
      odoo_jira_connector/models/jira_sprint.py
  10. 90
      odoo_jira_connector/models/mail_message.py
  11. 160
      odoo_jira_connector/models/project.py
  12. 537
      odoo_jira_connector/models/project_task.py
  13. 112
      odoo_jira_connector/models/project_task_type.py
  14. 561
      odoo_jira_connector/models/res_config_settings.py
  15. 81
      odoo_jira_connector/models/res_users.py
  16. 2
      odoo_jira_connector/security/ir.model.access.csv
  17. BIN
      odoo_jira_connector/static/description/assets/gif/Projects_in_jira.png
  18. BIN
      odoo_jira_connector/static/description/assets/gif/api_token2.png
  19. BIN
      odoo_jira_connector/static/description/assets/gif/creat_api_token.png
  20. BIN
      odoo_jira_connector/static/description/assets/gif/e_exported_stage.png
  21. BIN
      odoo_jira_connector/static/description/assets/gif/e_scrum_with_sprint.png
  22. BIN
      odoo_jira_connector/static/description/assets/gif/hero.gif
  23. BIN
      odoo_jira_connector/static/description/assets/gif/jira-qqqqq.png
  24. BIN
      odoo_jira_connector/static/description/assets/gif/jira-wwww.png
  25. BIN
      odoo_jira_connector/static/description/assets/gif/jira_1.png
  26. BIN
      odoo_jira_connector/static/description/assets/gif/pro_views.png
  27. BIN
      odoo_jira_connector/static/description/assets/gif/queue_job.png
  28. BIN
      odoo_jira_connector/static/description/assets/gif/sprint.png
  29. BIN
      odoo_jira_connector/static/description/assets/gif/user_jira.png
  30. BIN
      odoo_jira_connector/static/description/assets/gif/webhook.png
  31. BIN
      odoo_jira_connector/static/description/assets/gif/webhook_setup_1.png
  32. BIN
      odoo_jira_connector/static/description/assets/gif/webhook_setup_2.png
  33. BIN
      odoo_jira_connector/static/description/assets/gif/webhook_setup_3.png
  34. BIN
      odoo_jira_connector/static/description/assets/icons/check.png
  35. BIN
      odoo_jira_connector/static/description/assets/icons/chevron.png
  36. BIN
      odoo_jira_connector/static/description/assets/icons/cogs.png
  37. BIN
      odoo_jira_connector/static/description/assets/icons/consultation.png
  38. BIN
      odoo_jira_connector/static/description/assets/icons/ecom-black.png
  39. BIN
      odoo_jira_connector/static/description/assets/icons/education-black.png
  40. BIN
      odoo_jira_connector/static/description/assets/icons/hotel-black.png
  41. BIN
      odoo_jira_connector/static/description/assets/icons/license.png
  42. BIN
      odoo_jira_connector/static/description/assets/icons/lifebuoy.png
  43. BIN
      odoo_jira_connector/static/description/assets/icons/manufacturing-black.png
  44. BIN
      odoo_jira_connector/static/description/assets/icons/pos-black.png
  45. BIN
      odoo_jira_connector/static/description/assets/icons/puzzle.png
  46. BIN
      odoo_jira_connector/static/description/assets/icons/restaurant-black.png
  47. BIN
      odoo_jira_connector/static/description/assets/icons/service-black.png
  48. BIN
      odoo_jira_connector/static/description/assets/icons/trading-black.png
  49. BIN
      odoo_jira_connector/static/description/assets/icons/training.png
  50. BIN
      odoo_jira_connector/static/description/assets/icons/update.png
  51. BIN
      odoo_jira_connector/static/description/assets/icons/user.png
  52. BIN
      odoo_jira_connector/static/description/assets/icons/wrench.png
  53. BIN
      odoo_jira_connector/static/description/assets/misc/Cybrosys R.png
  54. BIN
      odoo_jira_connector/static/description/assets/misc/categories.png
  55. BIN
      odoo_jira_connector/static/description/assets/misc/check-box.png
  56. BIN
      odoo_jira_connector/static/description/assets/misc/compass.png
  57. BIN
      odoo_jira_connector/static/description/assets/misc/corporate.png
  58. BIN
      odoo_jira_connector/static/description/assets/misc/customer-support.png
  59. BIN
      odoo_jira_connector/static/description/assets/misc/cybrosys-logo.png
  60. BIN
      odoo_jira_connector/static/description/assets/misc/features.png
  61. BIN
      odoo_jira_connector/static/description/assets/misc/logo.png
  62. BIN
      odoo_jira_connector/static/description/assets/misc/pictures.png
  63. BIN
      odoo_jira_connector/static/description/assets/misc/pie-chart.png
  64. BIN
      odoo_jira_connector/static/description/assets/misc/right-arrow.png
  65. BIN
      odoo_jira_connector/static/description/assets/misc/star.png
  66. BIN
      odoo_jira_connector/static/description/assets/misc/support.png
  67. BIN
      odoo_jira_connector/static/description/assets/misc/whatsapp.png
  68. BIN
      odoo_jira_connector/static/description/assets/modules/1.png
  69. BIN
      odoo_jira_connector/static/description/assets/modules/2.png
  70. BIN
      odoo_jira_connector/static/description/assets/modules/3.jpg
  71. BIN
      odoo_jira_connector/static/description/assets/modules/5.gif
  72. BIN
      odoo_jira_connector/static/description/assets/modules/5.jpg
  73. BIN
      odoo_jira_connector/static/description/assets/modules/6.png
  74. BIN
      odoo_jira_connector/static/description/assets/modules/7.jpg
  75. BIN
      odoo_jira_connector/static/description/assets/screenshots/Projects_in_jira.png
  76. BIN
      odoo_jira_connector/static/description/assets/screenshots/api_token2.png
  77. BIN
      odoo_jira_connector/static/description/assets/screenshots/creat_api_token.png
  78. BIN
      odoo_jira_connector/static/description/assets/screenshots/e_exported_stage.png
  79. BIN
      odoo_jira_connector/static/description/assets/screenshots/e_scrum_with_sprint.png
  80. BIN
      odoo_jira_connector/static/description/assets/screenshots/hero.gif
  81. BIN
      odoo_jira_connector/static/description/assets/screenshots/jira-qqqqq.png
  82. BIN
      odoo_jira_connector/static/description/assets/screenshots/jira-wwww.png
  83. BIN
      odoo_jira_connector/static/description/assets/screenshots/jira_1.png
  84. BIN
      odoo_jira_connector/static/description/assets/screenshots/pro_views.png
  85. BIN
      odoo_jira_connector/static/description/assets/screenshots/queue_job.png
  86. BIN
      odoo_jira_connector/static/description/assets/screenshots/sprint.png
  87. BIN
      odoo_jira_connector/static/description/assets/screenshots/user_jira.png
  88. BIN
      odoo_jira_connector/static/description/assets/screenshots/webhook.png
  89. BIN
      odoo_jira_connector/static/description/assets/screenshots/webhook_setup_1.png
  90. BIN
      odoo_jira_connector/static/description/assets/screenshots/webhook_setup_2.png
  91. BIN
      odoo_jira_connector/static/description/assets/screenshots/webhook_setup_3.png
  92. BIN
      odoo_jira_connector/static/description/banner.jpg
  93. BIN
      odoo_jira_connector/static/description/icon.png
  94. 779
      odoo_jira_connector/static/description/index.html
  95. 56
      odoo_jira_connector/views/jira_sprint_views.xml
  96. 16
      odoo_jira_connector/views/project_task_type_views.xml
  97. 34
      odoo_jira_connector/views/project_views.xml
  98. 90
      odoo_jira_connector/views/res_config_settings_views.xml
  99. 16
      odoo_jira_connector/views/res_users_views.xml

47
odoo_jira_connector/README.rst

@ -0,0 +1,47 @@
.. image:: https://img.shields.io/badge/licence-LGPL--3-green.svg
:target: https://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
Odoo Jira Connector
===================
* Connect Odoo to Jira
Configuration:
--------------
After installing this module, you need to set the URL for Jira, user name and API token.
Test the connection. Once the connection is established, you can export/ import the projects, tasks, and users.
Company
-------
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__
License
-------
General Public License, Version 3 (LGPL v3).
(https://www.gnu.org/licenses/lgpl-3.0-standalone.html)
Credits
-------
Developer: (V17) Dhanya B, Contact: odoo@cybrosys.com
Contacts
--------
* Mail Contact : odoo@cybrosys.com
* Website : https://cybrosys.com
Bug Tracker
-----------
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported.
Maintainer
==========
.. image:: https://cybrosys.com/images/logo.png
:target: https://cybrosys.com
This module is maintained by Cybrosys Technologies.
For support and more information, please visit `Our Website <https://cybrosys.com/>`__
Further information
===================
HTML Description: `<static/description/index.html>`__

33
odoo_jira_connector/__init__.py

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Dhanya B (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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import controllers, models
from odoo.exceptions import UserError
def pre_init_hook(env):
queue_job = env['ir.model.data'].sudo().search(
[('module', '=', 'queue_job')])
queue_job_cron_jobrunner = env['ir.model.data'].search(
[('module', '=', 'queue_job_cron_jobrunner')])
if not queue_job_cron_jobrunner or not queue_job:
raise UserError("Please make sure you have added and installed Queue "
"Job and Queue Job Cron Jobrunner in your system")

55
odoo_jira_connector/__manifest__.py

@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Dhanya B (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 <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Odoo Jira Connector',
'version': '17.0.1.0.0',
'category': 'Project',
'summary': 'Odoo Jira Connector is a valuable integration tool for '
'businesses that use both Odoo and Jira. By connecting these '
'two systems, businesses can streamline their project '
'management processes and improve their overall efficiency.',
'description': 'The Odoo Jira Connector offers a range of features, '
'including bi-directional synchronization of data, '
'automatic creation of Jira issues from Odoo records, and '
'real-time updates of Jira issues in Odoo. To meet the '
'specific needs of any business users can leverage, they '
'can use Odoo to handle their business.',
'author': 'Cybrosys Techno Solutions',
'company': 'Cybrosys Techno Solutions',
'maintainer': 'Cybrosys Techno Solutions',
'website': 'https://www.cybrosys.com',
'depends': ['project'],
'data': [
'security/ir.model.access.csv',
'views/res_config_settings_views.xml',
'views/res_users_views.xml',
'views/project_views.xml',
'views/project_task_type_views.xml',
'views/jira_sprint_views.xml',
],
'images': ['static/description/banner.jpg'],
'license': 'LGPL-3',
'installable': True,
'application': False,
'auto_install': False,
'pre_init_hook': 'pre_init_hook'
}

22
odoo_jira_connector/controllers/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Dhanya B (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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import odoo_jira_connector

44
odoo_jira_connector/controllers/odoo_jira_connector.py

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Dhanya B (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 <http://www.gnu.org/licenses/>.
#
##############################################################################
import json
from odoo import http
from odoo.http import request
class JiraWebhook(http.Controller):
"""Class to fetch Jira data using webhook"""
@http.route('/jira_webhook', type="json", auth='public',
methods=['POST'], csrf=False)
def import_jira_data(self, *args, **kwargs):
"""function to import data from Jira based on webhook events"""
automated_import_export = request.env['ir.config_parameter'] \
.sudo().get_param('odoo_jira_connector.automatic')
if automated_import_export:
data = json.loads(request.httprequest.data)
jira = json.dumps(data, sort_keys=True,
indent=4, separators=(',', ': '))
jira_data = json.loads(jira)
webhook_event = jira_data['webhookEvent']
delay = request.env['project.task'].sudo(). \
with_delay(priority=1, eta=60)
delay.webhook_data_handle(jira_data, webhook_event)

6
odoo_jira_connector/doc/RELEASE_NOTES.md

@ -0,0 +1,6 @@
## Module <odoo_jira_connector>
#### 30.07.2024
#### Version 17.0.1.0.0
#### ADD
- Initial commit for Odoo Jira Connector

29
odoo_jira_connector/models/__init__.py

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Dhanya B (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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import ir_attachment
from . import jira_sprint
from . import mail_message
from . import project
from . import project_task
from . import project_task_type
from . import res_config_settings
from . import res_users

102
odoo_jira_connector/models/ir_attachment.py

@ -0,0 +1,102 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Dhanya B (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 <http://www.gnu.org/licenses/>.
#
##############################################################################
import base64
import os
import requests
from odoo import models, fields, api
# The Header parameters
HEADERS = {'Accept': 'application/json', 'Content-Type': 'application/json'}
class IrAttachment(models.Model):
_inherit = 'ir.attachment'
attachment_id_jira = fields.Integer(string="Jira ID",
help="Jira id of attachment.")
@api.model_create_multi
def create(self, values_list):
""" For creating attachment in Jira and attachment in the chatter """
attachment = super(IrAttachment, self).create(values_list)
if values_list and not values_list[0].get('attachment_id_jira') :
ir_config_parameter = self.env['ir.config_parameter'].sudo()
if ir_config_parameter.get_param('odoo_jira_connector.connection'):
url = ir_config_parameter.get_param('odoo_jira_connector.url')
user = ir_config_parameter.get_param(
'odoo_jira_connector.user_id_jira')
password = ir_config_parameter.get_param(
'odoo_jira_connector.api_token')
if attachment.res_model == 'project.task':
task = self.env['project.task'].browse(attachment.res_id)
attachment_url = url + 'rest/api/3/issue/%s/' \
'attachments' % task.task_id_jira
attachment_type = (self.env['res.config.settings'].
find_attachment_type(attachment))
if attachment.datas and attachment_type in (
'pdf', 'xlsx', 'jpg'):
temp_file_path = f'/tmp/temp.{attachment_type}'
binary_data = base64.b64decode(attachment.datas)
# Save the binary data to a file
with open(temp_file_path, 'wb') as file:
file.write(binary_data)
if attachment_type == 'jpg' and os.path.splitext(
temp_file_path)[1].lower() != '.jpg':
# Rename the saved file to its corresponding JPG
# file format
file_path = os.path.splitext(temp_file_path)[
0] + '.jpg'
os.rename(temp_file_path, file_path)
temp_file_path = file_path
attachment_file = {
'file': (
attachment.name, open(temp_file_path, 'rb'))
}
response = requests.post(attachment_url,
headers={
'X-Atlassian-Token':
'no-check'},
files=attachment_file,
auth=(user, password))
data = response.json()
attachment.write(
{'attachment_id_jira': data[0].get('id')})
return attachment
def unlink(self):
""" Overrides the unlink method of attachment to delete an attachment
in Jira when we delete the attachment in Odoo"""
for attachment in self:
jira_connection = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.connection')
if jira_connection:
jira_url = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.url', '')
user = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.user_id_jira')
password = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.api_token')
if attachment.attachment_id_jira:
requests.delete(
jira_url + '/rest/api/3/attachment/' +
str(attachment.attachment_id_jira),
headers=HEADERS, auth=(user, password))
return super(IrAttachment, self).unlink()

80
odoo_jira_connector/models/jira_sprint.py

@ -0,0 +1,80 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Dhanya B (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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from odoo import fields, models
class JiraSprint(models.Model):
"""class for Sprint"""
_name = "jira.sprint"
_description = "jira sprint"
sprint_id_jira = fields.Integer(string="Sprint id", readonly=True,
help="sprint id in jira.")
name = fields.Char(string="Sprint Name", help="Name of the sprint.")
sprint_goal = fields.Text(string="Goal", help="Goal of the sprint.")
start_date = fields.Datetime(string="Start Date", help="Sprint start date.")
end_date = fields.Datetime(string="End Date", help="Sprint end date.")
project_id = fields.Many2one('project.project', readonly=True,
help="Respective Project ID.")
state = fields.Selection(string="State",
selection=[('to_start', 'To start'),
('ongoing', 'Ongoing'),
('completed', 'Completed')],
default='to_start', help="State of the sprint.")
def action_get_tasks(self):
"""Sprint added tasks"""
return {
'type': 'ir.actions.act_window',
'name': 'Tasks',
'view_mode': 'kanban',
'res_model': 'project.task',
'views': [[False, 'kanban'], [False, 'tree'], [False, 'form']],
'domain': [('project_id', '=', self.project_id.id),
('sprint_id.state', '=', 'ongoing')],
'context': "{'create': False}"
}
def action_get_backlogs(self):
"""Tasks in backlogs"""
return {
'type': 'ir.actions.act_window',
'name': 'Backlogs',
'view_mode': 'kanban',
'res_model': 'project.task',
'views': [[False, 'kanban'], [False, 'tree'], [False, 'form']],
'domain': [('project_id', '=', self.project_id.id),
('sprint_id.state', '=', 'to_start')],
'context': "{'create': False}"
}
def action_get_all_tasks(self):
"""All tasks in the project"""
return {
'type': 'ir.actions.act_window',
'name': 'All Tasks',
'view_mode': 'kanban',
'res_model': 'project.task',
'views': [[False, 'kanban'], [False, 'tree'], [False, 'form']],
'domain': [('project_id', '=', self.project_id.id)],
'context': "{'create': False}"
}

90
odoo_jira_connector/models/mail_message.py

@ -0,0 +1,90 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Dhanya B (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 <http://www.gnu.org/licenses/>.
#
##############################################################################
import json
import requests
from odoo import api, fields, models
from odoo.tools import html2plaintext
class MailMessage(models.Model):
"""
This class is inherited for adding an extra field and
override the create function
Methods:
create(values_list):
extends create() to create comment in Jira
"""
_inherit = 'mail.message'
message_id_jira = fields.Integer(string='Message ID',
help='ID for the comments in Jira.')
@api.model_create_multi
def create(self, values_list):
""" For creating comment in Jira and comments in the chatter """
message = super(MailMessage, self).create(values_list)
if message.message_id_jira == 0:
ir_config_parameter = self.env['ir.config_parameter'].sudo()
if ir_config_parameter.get_param('odoo_jira_connector.connection'):
url = ir_config_parameter.get_param('odoo_jira_connector.url')
user = ir_config_parameter.get_param(
'odoo_jira_connector.user_id_jira')
password = ir_config_parameter.get_param(
'odoo_jira_connector.api_token')
if message.model == 'project.task':
task = self.env['project.task'].browse(message.res_id)
current_message = str(html2plaintext(message.body))
response = requests.get(
f'{url}rest/api/3/issue/{task.task_id_jira}/comment',
headers={
'Accept': 'application/json',
'Content-Type': 'application/json'},
auth=(user, password))
data = response.json()
if response.status_code == 200:
list_of_comments_jira = [
str(comments['body']['content'][0]['content'][0][
'text']) for comments in data['comments']]
if current_message not in list(
filter(None, list_of_comments_jira)):
data = json.dumps({
'body': {
'type': 'doc',
'version': 1,
'content': [{
'type': 'paragraph',
'content': [{
'text': current_message,
'type': 'text'
}]
}]
}
})
response = requests.post(
url + 'rest/api/3/issue/%s/comment' % (
task.task_id_jira), headers={
'Accept': 'application/json',
'Content-Type': 'application/json'},
data=data, auth=(user, password))
data = response.json()
message.write({'message_id_jira': data.get('id')})
return message

160
odoo_jira_connector/models/project.py

@ -0,0 +1,160 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Dhanya B (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 <http://www.gnu.org/licenses/>.
#
##############################################################################
import json
import requests
from requests.auth import HTTPBasicAuth
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
# The Header parameters
HEADERS = {'Accept': 'application/json', 'Content-Type': 'application/json'}
class ProjectProject(models.Model):
"""
This class is inherited for adding some extra field and override the
create and write function also to add function to show sprint
Methods:
create(vals_list):
extends create() to export project to Jira
write(vals):
extends write() to update corresponding project in Jira
"""
_inherit = 'project.project'
project_id_jira = fields.Integer(string='Jira Project ID',
help='Corresponding project id of Jira.',
readonly=True)
jira_project_key = fields.Char(string='Jira Project Key',
help='Corresponding project key of Jira.',
readonly=True)
sprint_active = fields.Boolean(string='Sprint active',
help='To show sprint smart button.')
board_id_jira = fields.Integer(string='Jira Board ID',
help='Corresponding Board id of Jira.',
readonly=True)
def action_get_sprint(self):
"""Getting sprint inside the project"""
return {
'type': 'ir.actions.act_window',
'name': 'Sprints',
'view_mode': 'tree,form',
'res_model': 'jira.sprint',
'context': {'default_project_id': self.id},
'domain': [('project_id', '=', self.id)],
}
@api.model_create_multi
def create(self, vals_list):
""" Overrides create method of project to export project to Jira """
self = self.with_context(mail_create_nosubscribe=True)
projects = super().create(vals_list)
jira_connection = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.connection')
if jira_connection:
jira_url = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.url', False)
user = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.user_id_jira', False)
password = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.api_token', False)
auth = HTTPBasicAuth(user, password)
project_headers = {'Accept': 'application/json'}
response = requests.request(
'GET', jira_url + '/rest/api/3/project/',
headers=project_headers, auth=auth)
projects_json = json.dumps(
json.loads(response.text), sort_keys=True, indent=4,
separators=(',', ': '))
project_json = json.loads(projects_json)
name_list = [project['name'] for project in project_json]
key = projects.name.upper()
project_key = key[:3] + '1' + key[-3:]
project_keys = project_key.replace(' ', '')
auth = HTTPBasicAuth(user, password)
project_payload = {
'name': projects.name, 'key': project_keys,
'templateKey': 'com.pyxis.greenhopper.jira:gh-simplified'
'-kanban-classic'
}
if projects.name not in name_list:
response = requests.request(
'POST', jira_url + 'rest/simplified/latest/project',
data=json.dumps(project_payload),
headers=HEADERS, auth=auth)
data = response.json()
if 'projectId' in data:
projects.write({'project_id_jira': data['projectId'],
'jira_project_key': data['projectKey']})
self.env['ir.config_parameter'].sudo().set_param(
'import_project_count', int(
self.env['ir.config_parameter'].sudo().get_param(
'import_project_count')) + 1)
elif 'errors' in data and 'projectName' in data['errors']:
raise ValidationError(
"A project with this name already exists. Please "
"rename the project.")
elif 'errors' in data and 'projectKey' in data['errors']:
raise ValidationError(data['errors']['projectKey'])
return projects
def write(self, vals):
""" Overrides the write method of project.project to update project
name in Jira when we update the project in Odoo"""
jira_connection = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.connection')
if jira_connection:
for project in self:
jira_url = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.url')
user = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.user_id_jira')
password = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.api_token')
auth = (user, password)
headers = {
"Accept": "application/json",
"Content-Type": "application/json"
}
url = (f"{jira_url}/rest/api/3/project/"
f"{project.jira_project_key}")
payload = json.dumps({
"name": vals.get('name'),
})
payload_json = json.loads(payload)
response = requests.get(
url,
headers=headers,
auth=auth)
data = response.json()
if 'name' in data:
if data['name'] != payload_json['name']:
requests.request(
"PUT",
url, data=payload, headers=headers, auth=auth)
else:
requests.request(
"PUT",
url, data=payload, headers=headers, auth=auth)
return super(ProjectProject, self).write(vals)

537
odoo_jira_connector/models/project_task.py

@ -0,0 +1,537 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Dhanya B (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 <http://www.gnu.org/licenses/>.
#
##############################################################################
import base64
import json
from datetime import datetime
import requests
from requests.auth import HTTPBasicAuth
from odoo import api, fields, models, _
from odoo.tools import html2plaintext
# The Header parameters
HEADERS = {'Accept': 'application/json', 'Content-Type': 'application/json'}
class ProjectTask(models.Model):
"""
This class is inherited for adding some extra field and override the
create function
Methods:
create(vals_list):
extends create() to export tasks to Jira
unlink():
extends unlink() to delete a task in Jira when we delete the
task in Odoo
write(vals):
extends write() to update a task in Jira when we update the
task in Odoo
"""
_inherit = 'project.task'
task_id_jira = fields.Char(string='Jira Task ID', help='Task id of Jira.',
readonly=True)
sprint_id = fields.Many2one('jira.sprint',
help="Sprint of this task.", readonly=True)
task_sprint_active = fields.Boolean(string="Active Sprint",
compute="_compute_task_sprint_active",
store=True,
help="Boolean field to check whether "
"the sprint is active or not.")
@api.depends('project_id.sprint_active')
def _compute_task_sprint_active(self):
"""compute function to make sprint_id invisible by changing
'task_sprint_active' field to true"""
for rec in self:
if rec.project_id.sprint_active:
rec.task_sprint_active = True
@api.model
def create(self, vals_list):
""" Override the create method of tasks to export tasks to Jira """
res = super(ProjectTask, self).create(vals_list)
jira_connection = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.connection')
if jira_connection:
jira_url = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.url')
user = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.user_id_jira')
password = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.api_token')
query = {'jql': 'project = %s' % res.project_id.jira_project_key}
requests.get(jira_url + 'rest/api/3/search', headers=HEADERS,
params=query, auth=(user, password))
if not res.task_id_jira:
payload = json.dumps({
'fields': {
'project': {'key': res.project_id.jira_project_key},
'summary': res.name,
'description': res.description,
'issuetype': {'name': 'Task'}
}
})
response = requests.post(
jira_url + '/rest/api/2/issue', headers=HEADERS,
data=payload, auth=(user, password))
data = response.json()
res.task_id_jira = str(data.get('key'))
self.env['ir.config_parameter'].sudo().set_param(
'export_task_count', int(
self.env['ir.config_parameter'].sudo().get_param(
'export_task_count')) + 1)
return res
def unlink(self):
""" Overrides the unlink method of task to delete a task in Jira when
we delete the task in Odoo """
for task in self:
if task.stage_id and task.stage_id.fold:
raise Warning(_('You cannot delete a task in a folded stage.'))
jira_connection = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.connection')
if jira_connection:
jira_url = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.url', '')
user = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.user_id_jira')
password = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.api_token')
if task.task_id_jira:
requests.delete(
jira_url + '/rest/api/3/issue/' + task.task_id_jira,
headers=HEADERS, auth=(user, password))
return super(ProjectTask, self).unlink()
def write(self, vals):
""" Overrides the write method of task to update a task's name in
Jira when we update the task in Odoo"""
jira_connection = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.connection')
if jira_connection:
jira_url = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.url', '')
user = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.user_id_jira')
password = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.api_token')
for task in self:
if task.task_id_jira and 'name' in vals:
new_task_name = vals['name']
payload = {
"fields": {
"summary": new_task_name
}
}
requests.put(
jira_url + '/rest/api/3/issue/' + task.task_id_jira,
json=payload, headers=HEADERS, auth=(user, password))
return super(ProjectTask, self).write(vals)
def webhook_data_handle(self, jira_data, webhook_event):
"""Function to Handle Jira Data Received from Webhook"""
if webhook_event == 'project_created':
self.create_project(jira_data)
elif webhook_event == 'project_updated':
self.update_project(jira_data)
elif webhook_event == 'project_soft_deleted':
self.delete_project(jira_data)
elif webhook_event == 'jira:issue_created':
self.create_task(jira_data)
elif webhook_event == 'jira:issue_deleted':
self.delete_task(jira_data)
elif webhook_event == 'comment_created':
self.create_comment(jira_data)
elif webhook_event == 'comment_deleted':
self.delete_comment(jira_data)
elif webhook_event == 'user_created':
self.create_user(jira_data)
elif webhook_event == 'user_deleted':
self.delete_user(jira_data)
elif webhook_event == 'board_configuration_changed':
self.board_configuration_change(jira_data)
elif webhook_event == 'jira:issue_updated':
self.update_task(jira_data)
elif webhook_event == 'attachment_deleted':
self.delete_attachment(jira_data)
elif webhook_event == 'sprint_started':
self.sprint_started(jira_data)
elif webhook_event == 'sprint_closed':
self.sprint_closed(jira_data)
def create_project(self, jira_data):
"""function to create project based on webhook response"""
jira_project = jira_data['project']
existing_project = self.env['project.project'].sudo().search(
[('project_id_jira', '=', jira_project['id'])])
values = {
'name': jira_project['name'],
'project_id_jira': jira_project['id'],
'jira_project_key': jira_project['key']
}
if not existing_project:
imported_project = self.env['project.project'].sudo().create(
values)
url = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.url')
user = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.user_id_jira')
password = self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.api_token')
auth = HTTPBasicAuth(user, password)
headers = {
"Accept": "application/json"
}
response = requests.request(
"GET",
url + "/rest/api/3/project/" + jira_project['key'],
headers=headers,
auth=auth
)
data = response.json()
style_value = data.get('style')
if style_value == 'classic':
imported_project.write({'sprint_active': False})
else:
imported_project.write({'sprint_active': True})
def update_project(self, jira_data):
"""function to update project based on webhook response"""
project_id = jira_data['project']['id']
existing_project = self.env['project.project'].sudo().search(
[('project_id_jira', '=', project_id)])
if existing_project.name != jira_data['project']['name']:
existing_project.write({'name': jira_data['project']['name']})
def delete_project(self, jira_data):
"""function to delete project based on webhook response"""
project_id = (jira_data['project']['id'])
self.env['project.project'].sudo().search(
[('project_id_jira', '=', project_id)]).unlink()
def create_task(self, jira_data):
"""function to create task based on webhook response"""
task_name = jira_data['issue']['fields']['summary']
task_key = jira_data['issue']['key']
jira_project_id = jira_data['issue']['fields']['project']['id']
project = self.env['project.project'].sudo().search(
[('project_id_jira', '=', int(jira_project_id))])
existing_task = self.env['project.task'].sudo().search(
[('task_id_jira', '=', jira_data['issue']['key'])])
if not existing_task:
self.env['project.task'].sudo().create({
'project_id': project.id,
'name': task_name,
'task_id_jira': task_key
})
def delete_task(self, jira_data):
"""function to delete task based on webhook response"""
task_key = jira_data['issue']['key']
self.env['project.task'].sudo().search(
[('task_id_jira', '=', task_key)]).unlink()
def create_comment(self, jira_data):
"""function to create comment based on webhook response"""
text = jira_data['comment']['body']
task_key = jira_data['issue']['key']
task = self.env['project.task'].sudo().search(
[('task_id_jira', '=', task_key)])
existing_message = self.env['mail.message'].sudo().search(
['&', ('res_id', '=', task.id),
('model', '=', 'project.task'),
('message_id_jira', '=', jira_data['comment']['id'])])
if not existing_message:
input_string = str(text)
parts = input_string.split(".")
if len(parts) > 1:
body = parts[1]
else:
body = parts[0]
self.env['mail.message'].sudo().create(
{"body": html2plaintext(body),
'model': 'project.task',
'res_id': task.id,
'message_id_jira': jira_data['comment']['id']
})
def delete_comment(self, jira_data):
"""function to delete comment based on webhook response"""
self.env['mail.message'].sudo().search(
[('message_id_jira', '=',
jira_data['comment']['id'])]).unlink()
def create_user(self, jira_data):
"""function to create user based on webhook response"""
existing_user = self.env['res.users']. \
search([('jira_user_key', '=', jira_data['user']['accountId'])])
if not existing_user:
self.env['res.users'].sudo().create({
'login': jira_data['user']['displayName'],
'name': jira_data['user']['displayName'],
'jira_user_key': jira_data['user']['accountId']
})
def delete_user(self, jira_data):
"""function to delete user based on webhook response"""
self.env['res.users'].sudo().search(
[('jira_user_key', '=', jira_data['accountId'])]).unlink()
def board_configuration_change(self, jira_data):
"""function to create stages or write project into stages based on
webhook response"""
columns = jira_data['configuration']['columnConfig']['columns']
if jira_data['configuration'].get('location'):
project_key = jira_data['configuration']['location']['key']
project = self.env['project.project'].sudo().search(
[('jira_project_key', '=', project_key)])
sequence_value = 1
for column in columns:
if column['name'] != 'Backlog':
stages_jira_id = column['statuses'][0]['id']
existing_stage = self.env[
'project.task.type'].sudo().search(
[('stages_jira_id', '=', stages_jira_id)])
existing_stage.write({'project_ids': project,
'sequence': sequence_value})
if not existing_stage:
values = {
'name': column['name'],
'stages_jira_id': stages_jira_id,
'jira_project_key': project_key,
'project_ids': project,
'sequence': sequence_value,
}
self.env['project.task.type'].sudo().create(
values)
sequence_value += 1
project.write({'board_id_jira': jira_data['configuration']['id']})
else:
board_id_jira = jira_data['configuration']['id']
project = self.env['project.project'].search(
[('board_id_jira', '=', board_id_jira)])
existing_stages = self.env[
'project.task.type'].sudo().search(
[('project_ids', 'in', project.id),
('stages_jira_id', '!=', '0')])
jira_status_ids = []
for column in columns:
for status in column['statuses']:
jira_status_ids.append(status['id'])
if len(jira_status_ids) < len(existing_stages.ids):
removed_stage = self.env[
'project.task.type'].sudo().search(
[('project_ids', 'in', project.id),
('stages_jira_id', 'not in', jira_status_ids)])
removed_stage.unlink()
elif len(jira_status_ids) > len(existing_stages.ids):
columns = jira_data['configuration']['columnConfig'][
'columns']
num_stages = len(columns)
stage_id = columns[num_stages - 1]['statuses'][0]['id']
values = {
'name': columns[num_stages - 1]['name'],
'stages_jira_id': stage_id,
'project_ids': project,
'sequence': num_stages,
}
self.env['project.task.type'].sudo().create(values)
def update_task(self, jira_data):
"""function to update a task, which includes changing the task stage,
adding attachments, adding a description to the task,
changing the task's name,
and adding a sprint based on webhook response"""
task_key = jira_data['issue']['key']
imported_task = self.env['project.task'].sudo().search(
[('task_id_jira', '=', task_key)])
to_value = jira_data['changelog']['items'][0]['to']
if jira_data['changelog']['items'][0]['field'] == 'resolution':
second_to_value = jira_data['changelog']['items'][1]['to']
task_stage = self.env['project.task.type'].sudo().search(
[('stages_jira_id', '=', second_to_value)])
imported_task.write({'stage_id': task_stage.id})
elif jira_data['changelog']['items'][0]['field'] == 'status':
task_stage = self.env['project.task.type'].sudo().search(
[('stages_jira_id', '=', to_value)])
imported_task.write({'stage_id': task_stage.id})
elif jira_data['changelog']['items'][0]['field'] == 'Attachment':
if jira_data['changelog']['items'][0]['to'] != 'None':
attachments = jira_data["issue"]['fields']['attachment']
jira_attachment_id = [attachment['id'] for attachment in
attachments]
num_attachments = len(jira_attachment_id)
user_name = self.env[
'ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.user_id_jira')
api_token = self.env[
'ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.api_token')
auth = HTTPBasicAuth(user_name, api_token)
if num_attachments > 0:
name = attachments[num_attachments - 1].get('filename')
mime_type = attachments[num_attachments - 1].get(
'mimeType')
src = attachments[num_attachments - 1].get('content')
jira_id = attachments[num_attachments - 1].get('id')
image = base64.b64encode(
requests.get(src, auth=auth).content)
existing_attachments = self.env[
'ir.attachment'].sudo().search(
[('res_id', '=', imported_task.id),
('res_model', '=', 'project.task'),
('attachment_id_jira', '=', jira_id)]
)
values = {
'name': name,
'type': 'binary',
'datas': image,
'res_model': 'project.task',
'res_id': imported_task.id,
'mimetype': mime_type,
'attachment_id_jira': jira_id
}
if not existing_attachments:
self.env['ir.attachment'].sudo().create(values)
else:
pass
elif jira_data['changelog']['items'][0]['field'] == 'description':
imported_task.update({'description': jira_data['changelog']
['items'][0]['toString']})
elif jira_data['changelog']['items'][0]['field'] == 'summary':
if imported_task.name != jira_data['changelog']['items'][0] \
['toString']:
imported_task.write(
{'name': jira_data['changelog']['items'][0]
['toString']})
elif jira_data['changelog']['items'][0]['field'] == 'Sprint':
project_key = jira_data['issue']['fields']['project']['key']
project = self.env['project.project'].sudo().search(
[('jira_project_key', '=', project_key)])
custom_field = jira_data['issue']['fields']['customfield_10020']
if len(custom_field) > 1:
jira_sprint = self.env['jira.sprint'].sudo().search(
[('sprint_id_jira', '=',
custom_field[len(custom_field) - 1]['id'])])
if not jira_sprint:
vals = {
'name': custom_field[len(custom_field) - 1]['name'],
'sprint_id_jira':
custom_field[len(custom_field) - 1]['id'],
'project_id': project.id
}
sprint = self.env['jira.sprint'].sudo().create(vals)
if project.task_ids:
for rec in project.task_ids:
rec.write({'sprint_id': sprint.id})
else:
jira_sprint = self.env['jira.sprint'].sudo().search([(
'sprint_id_jira', '=', custom_field[0]['id'])])
if not jira_sprint:
vals = {
'name': custom_field[0]['name'],
'sprint_id_jira': custom_field[0]['id'],
'project_id': project.id
}
sprint = self.env['jira.sprint'].sudo().create(vals)
if project.task_ids:
for rec in project.task_ids:
rec.write({'sprint_id': sprint.id})
if rec.task_id_jira != task_key:
self.create({
'project_id': project.id,
'name': jira_data['issue']['fields'][
'summary'],
'task_id_jira': task_key,
'sprint_id': jira_sprint.id
})
break
else:
task_name = jira_data['issue']['fields']['summary']
self.create({
'project_id': project.id,
'name': task_name,
'task_id_jira': task_key,
'sprint_id': sprint.id
})
else:
if project.task_ids:
for rec in project.task_ids:
rec.write({'sprint_id': jira_sprint.id})
if rec.task_id_jira != task_key:
self.create({
'project_id': project.id,
'name': jira_data['issue']['fields'][
'summary'],
'task_id_jira': task_key,
'sprint_id': jira_sprint.id
})
break
else:
task_name = jira_data['issue']['fields']['summary']
self.create({
'project_id': project.id,
'name': task_name,
'task_id_jira': task_key,
'sprint_id': jira_sprint.id
})
def delete_attachment(self, jira_data):
"""function to delete attachment based on the response received from
webhook"""
jira_id = jira_data['attachment']['id']
self.env['ir.attachment'].sudo().search(
[('attachment_id_jira', '=', jira_id)]).unlink()
def sprint_started(self, jira_data):
"""function to start sprint which is created using webhook response"""
sprint_in_odoo = self.env['jira.sprint'].sudo().search(
[('sprint_id_jira', '=', jira_data['sprint']['id'])])
if sprint_in_odoo:
start_date = jira_data['sprint']['startDate']
input_start_date = datetime. \
strptime(start_date, '%Y-%m-%dT%H:%M:%S.%fZ')
jira_start_date = input_start_date.strftime(
'%Y-%m-%d %H:%M:%S')
end_date = jira_data['sprint']['endDate']
input_end_date = datetime. \
strptime(end_date, '%Y-%m-%dT%H:%M:%S.%fZ')
jira_end_date = input_end_date.strftime(
'%Y-%m-%d %H:%M:%S')
sprint_in_odoo.write({
'start_date': jira_start_date,
'end_date': jira_end_date,
'sprint_goal': jira_data['sprint']['goal'],
'state': 'ongoing'
})
def sprint_closed(self, jira_data):
"""function to close sprint which is created using webhook response"""
sprint_in_odoo = self.env['jira.sprint'].sudo().search(
[('sprint_id_jira', '=', jira_data['sprint']['id'])])
if sprint_in_odoo:
sprint_in_odoo.write({'state': 'completed'})
self.env['project.task'].sudo().search(
[('stage_id.name', '=', 'Done'),
('sprint_id', '=', sprint_in_odoo.id)]).unlink()

112
odoo_jira_connector/models/project_task_type.py

@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Dhanya B (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 <http://www.gnu.org/licenses/>.
#
##############################################################################
import json
import requests
from requests.auth import HTTPBasicAuth
from odoo import models, fields, api
class ProjectTaskType(models.Model):
"""This class is inherited for adding some extra field and override the
create function
Methods:
create(vals):
extends create() to export tasks stages to Jira"""
_inherit = 'project.task.type'
stages_jira_id = fields.Integer(string="Jira ID",
help="Jira id for task stages.",
readonly=True)
jira_project_key = fields.Char(string='Jira Project Key',
help='Corresponding project key of Jira.',
readonly=True)
jira_stages_category = fields.Selection([
('TO_DO', 'TO_DO'),
('IN_PROGRESS', 'IN_PROGRESS'),
('DONE', 'DONE')],
default='IN_PROGRESS',
string="Jira Status Category", help="Here we can choose the category "
"and the Stage will create in "
"jira under the chosen category.")
@api.model_create_multi
def create(self, vals):
""" Override the create method of tasks stages to export
tasks stages to Jira """
stages = super(ProjectTaskType, self).create(vals)
for stage in stages:
if stage.stages_jira_id == 0 and len(stage.project_ids) == 1:
ir_config_parameter = self.env['ir.config_parameter'].sudo()
if ir_config_parameter.get_param('odoo_jira_connector'
'.connection'):
url = ir_config_parameter.get_param('odoo_jira_connector'
'.url',
False)
user = ir_config_parameter.get_param(
'odoo_jira_connector.user_id_jira', False)
password = ir_config_parameter.get_param(
'odoo_jira_connector.api_token', False)
auth = HTTPBasicAuth(user, password)
if stage.project_ids[0].sprint_active:
payload = json.dumps({
"scope": {
"project": {
"id": str(stage.project_ids[0].
project_id_jira)
},
"type": "PROJECT"
},
"statuses": [
{
"description": "The issue is resolved",
"name": stages.name,
"statusCategory": str(
stage.jira_stages_category),
}
]
})
else:
payload = json.dumps({
"scope": {
"type": "GLOBAL"
},
"statuses": [
{
"description": "The issue is resolved",
"name": stage.name,
"statusCategory": str(
stage.jira_stages_category),
}
]
})
headers = {
"Accept": "application/json",
"Content-Type": "application/json"
}
requests.request(
"POST",
url + "rest/api/3/statuses",
data=payload,
headers=headers,
auth=auth
)
return stages

561
odoo_jira_connector/models/res_config_settings.py

@ -0,0 +1,561 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Dhanya B (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 <http://www.gnu.org/licenses/>.
#
##############################################################################
import base64
import json
import os
import re
import requests
from requests.auth import HTTPBasicAuth
from odoo import fields, models, _
from odoo.exceptions import ValidationError
from odoo.tools import html2plaintext
# The Header parameters
HEADERS = {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
JIRA_HEADERS = {
'Accept': 'application/json'
}
ATTACHMENT_HEADERS = {
'X-Atlassian-Token': 'no-check'
}
class ResConfigSettings(models.TransientModel):
""" This class is inheriting the model res.config.settings It contains
fields and functions for the model.
Methods:
get_values():
extends get_values() to include new config parameters
set_values():
extends set_values() to include new config parameters
action_test_connection():
action to perform when clicking on the 'Test Connection'
button.
action_export_to_jira():
action to perform when clicking on the 'Export/Sync Project'
button.
action_import_from_jira():
action to perform when clicking on the 'Export Users' button.
action_export_users():
action to perform when clicking on the 'Reset to Draft' button.
action_import_users():
action to perform when clicking on the 'Import Users' button.
_export_attachments(attachments, attachment_url):
it is used to export the given attachments to Jira.
find_attachment_type(attachment):
it is used to find the attachment type for the given attachment.
"""
_inherit = 'res.config.settings'
url = fields.Char(
string='URL', config_parameter='odoo_jira_connector.url',
help='Your Jira URL: E.g. https://yourname.atlassian.net/')
user_id_jira = fields.Char(
string='User Name', help='E.g. yourmail@gmail.com ',
config_parameter='odoo_jira_connector.user_id_jira')
api_token = fields.Char(string='API Token', help='API token in your Jira.',
config_parameter='odoo_jira_connector.api_token')
connection = fields.Boolean(
string='Connection', default=False, help='To identify the connection.',
config_parameter='odoo_jira_connector.connection')
export_project_count = fields.Integer(
string='Export Project Count', default=0, readonly=True,
help='Number of export projects.',
)
export_task_count = fields.Integer(
string='Export Task Count', default=0, readonly=True,
help='Number of export tasks.',
)
import_project_count = fields.Integer(
string='Import Project Count',
help='Number of import project.', readonly=True,
)
import_task_count = fields.Integer(
string='Import Task Count',
help='Number of import tasks.', readonly=True,
)
automatic = fields.Boolean(string='Automatic',
help='to make export/import data automated '
'while creating it on configured Jira '
'account.',
config_parameter='odoo_jira_connector.automatic')
def action_test_connection(self):
""" Test the connection to Jira
Raises: ValidationError: If the credentials are invalid.
Returns:
dict: client action for displaying notification
"""
try:
# Create an authentication object, using registered email-ID, and
# token received.
auth = HTTPBasicAuth(self.user_id_jira, self.api_token)
response = requests.request('GET',
self.url + 'rest/api/2/project',
headers=JIRA_HEADERS, auth=auth)
if response.status_code == 200 and 'expand' in response.text:
self.env['ir.config_parameter'].sudo().set_param(
'odoo_jira_connector.connection', True)
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'success',
'message': _(
'Test connection to Jira successful.'),
'next': {
'type': 'ir.actions.act_window_close'
}
}
}
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'danger',
'message': _('Please Enter Valid Credentials.'),
'next': {
'type': 'ir.actions.act_window_close'
}
}
}
except Exception:
raise ValidationError(_('Please Enter Valid Credentials.'))
def action_export_to_jira(self):
""" Exporting All The Projects And Corresponding Tasks to Jira,
and updating the project or task on Jira if it is updated in Odoo.
"""
auth = HTTPBasicAuth(self.user_id_jira, self.api_token)
response = requests.request('GET', self.url + 'rest/api/2/project',
headers=JIRA_HEADERS, auth=auth)
projects = json.dumps(json.loads(response.text), sort_keys=True,
indent=4, separators=(',', ': '))
project_json = json.loads(projects)
name_list = [project['name'] for project in project_json]
id_list = [project['id'] for project in project_json]
odoo_projects = self.env['project.project'].search(
[('project_id_jira', 'in', id_list)])
for project in odoo_projects:
if project.jira_project_key:
project_keys = project.jira_project_key
else:
key = project.name.upper()
project_key = key[:3] + '1' + key[-3:]
project_keys = project_key.replace(' ', '')
response = requests.get(
self.url + 'rest/api/3/search', headers=HEADERS,
params={'jql': 'project = %s' % project_keys},
auth=(self.user_id_jira, self.api_token))
data = response.json()
issue_keys = [issue.get('key') for issue in data.get('issues', {})]
tasks = self.env['project.task'].search(
[('project_id', '=', project.id)])
for task in tasks:
attachment_url = self.url + 'rest/api/3/issue/%s/' \
'attachments' % task.task_id_jira
comment_url = self.url + 'rest/api/3/issue/%s/comment' % (
task.task_id_jira)
if str(task.task_id_jira) in issue_keys:
messages = self.env['mail.message'].search(
['&', ('res_id', '=', task.id),
('model', '=', 'project.task')])
attachments = self.env['ir.attachment'].search(
[('res_id', '=', task.id)])
self._export_attachments(attachments, attachment_url)
response = requests.get(
comment_url, headers=HEADERS, auth=(
self.user_id_jira, self.api_token))
data = response.json()
jira_comment_list = []
for comments in data['comments']:
content = comments.get('body', {}).get('content', [])
if content and isinstance(content, list) and content[
0].get('type') == 'paragraph':
text = content[0]['content'][0].get('text')
if text:
jira_comment_list.append(str(text))
odoo_comment_list = [str(
html2plaintext(chat.body)) for chat in messages if
str(html2plaintext(
chat.body)) not in jira_comment_list]
comment_list = list(filter(None, odoo_comment_list))
if len(comment_list) > 0:
for comment in comment_list:
data = json.dumps({
'body': {
'type': 'doc',
'version': 1,
'content': [{
'type': 'paragraph',
'content': [{
'text': comment,
'type': 'text'
}]}
]}
})
requests.post(
comment_url, headers=HEADERS, data=data,
auth=(self.user_id_jira, self.api_token))
else:
payload = json.dumps({
'fields': {
'project':
{
'key': project_keys
},
'summary': task.name,
'description': task.description,
'issuetype': {
'name': 'Task'
}
}
})
response = requests.post(
self.url + '/rest/api/2/issue', headers=HEADERS,
data=payload, auth=(self.user_id_jira, self.api_token))
data = response.json()
task.task_id_jira = data['key']
self.env['ir.config_parameter'].sudo().set_param(
'odoo_jira_connector.export_task_count', int(
self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.export_task_count')) + 1)
messages = self.env['mail.message'].search(
['&', ('res_id', '=', task.id),
('model', '=', 'project.task')])
attachments = self.env['ir.attachment'].search(
[('res_id', '=', task.id)])
self._export_attachments(attachments, attachment_url)
for chat in messages:
data = json.dumps({
'body': {
'type': 'doc',
'version': 1,
'content': [{
'type': 'paragraph',
'content': [{
'text': str(html2plaintext(chat.body)),
'type': 'text'}]
}]
}
})
requests.post(
comment_url, headers=HEADERS, data=data,
auth=(self.user_id_jira, self.api_token))
odoo_projects = self.env['project.project'].search(
[('project_id_jira', 'not in', id_list),
('name', 'not in', name_list)])
for project in odoo_projects:
key = project.name.upper()
project_key = key[:3] + '1' + key[-3:]
project_keys = project_key.replace(' ', "")
auth = HTTPBasicAuth(self.user_id_jira, self.api_token)
project_payload = {
'name': project.name,
'key': project_keys,
'templateKey': 'com.pyxis.greenhopper.jira:gh-simplified'
'-kanban-classic'
}
response = requests.request(
'POST', self.url + 'rest/simplified/latest/project',
data=json.dumps(project_payload), headers=HEADERS, auth=auth)
data = response.json()
if 'projectId' in data:
project.write({
'project_id_jira': data['projectId'],
'jira_project_key': data['projectKey']
})
self.env['ir.config_parameter'].sudo().set_param(
'odoo_jira_connector.export_project_count',
int(self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.export_project_count')) + 1)
# for creating a new task inside the project
tasks = self.env['project.task'].search(
[('project_id', '=', project.id)])
for task in tasks:
payload = json.dumps({
'fields': {
'project': {
'key': project_keys
},
'summary': task.name,
'description': task.description,
'issuetype': {
'name': 'Task'
}
}
})
response2 = requests.post(
self.url + '/rest/api/2/issue', headers=HEADERS,
data=payload, auth=(self.user_id_jira, self.api_token))
data = response2.json()
task.task_id_jira = data['key']
attachment_url = self.url + 'rest/api/3/issue/%s/' \
'attachments' % task.task_id_jira
comment_url = self.url + 'rest/api/3/issue/%s/comment' % (
task.task_id_jira)
self.env['ir.config_parameter'].sudo().set_param(
'odoo_jira_connector.export_task_count', int(
self.env['ir.config_parameter'].sudo().get_param(
'odoo_jira_connector.export_task_count')) + 1)
messages = self.env['mail.message'].search(
['&', ('res_id', '=', task.id),
('model', '=', 'project.task')])
attachments = self.env['ir.attachment'].search(
[('res_id', '=', task.id)])
self._export_attachments(attachments, attachment_url)
for message in messages:
data = json.dumps({
'body': {
'type': 'doc',
'version': 1,
'content': [{
'type': 'paragraph',
'content': [{
'text': str(
html2plaintext(message.body)),
'type': 'text'}]
}]
}
})
requests.post(comment_url, headers=HEADERS, data=data,
auth=(self.user_id_jira, self.api_token))
elif 'errors' in data and 'projectName' in data['errors']:
raise ValidationError(
"A project with the names already exists in Jira. Please "
"rename the project to export as a new project.")
elif 'errors' in data and 'projectKey' in data['errors']:
raise ValidationError(data['errors']['projectKey'])
def action_export_users(self):
""" Exporting all the users from Odoo to Jira, and updating the user's
information on Jira if it has been updated in Odoo
Raises: ValidationError: If the credentials are not valid.
"""
response = requests.get(
self.url + 'rest/api/2/users/search', headers=HEADERS,
auth=(self.user_id_jira, self.api_token))
data = response.json()
issue_keys = [issue['accountId'] for issue in data]
users = self.env['res.users'].search(
[('jira_user_key', 'in', issue_keys)])
non_jira_users = self.env['res.users'].search(
[('jira_user_key', 'not in', issue_keys)])
if users:
for user_data in data:
for user in users:
if user_data['accountId'] == user.jira_user_key:
user_data.update({
'displayName': user.name
})
if non_jira_users:
regex = '^\S+@\S+\.\S+$'
for user in non_jira_users:
objs = re.search(regex, user.login)
if objs:
if objs.string == str(user.login):
payload = json.dumps({
'emailAddress': user.login,
'displayName': user.name,
'name': user.name
})
response = requests.post(
self.url + 'rest/api/3/user', headers=HEADERS,
data=payload,
auth=(self.user_id_jira, self.api_token))
data = response.json()
user.write({
'jira_user_key': data['accountId']
})
else:
raise ValidationError('Invalid E-mail address.')
def action_import_users(self):
""" Importing all the users from Jira to Odoo, and updating the user's
information on Odoo if it has been updated in Jira.
"""
response = requests.get(
self.url + 'rest/api/2/users/search', headers=HEADERS,
auth=(self.user_id_jira, self.api_token))
data = response.json()
for user_data in data:
users = self.env['res.users'].sudo().search(
[('login', '=', user_data['displayName'])])
if users:
users.write({
'jira_user_key': user_data['accountId']
})
else:
self.env['res.users'].create({
'login': user_data['displayName'],
'name': user_data['displayName'],
'jira_user_key': user_data['accountId'],
})
def _export_attachments(self, attachments, attachment_url):
""" To find the corresponding attachment type in the attachment model
Args:
attachments (model.Model): values for creating new records.
attachment_url (str): URL for the attachment.
"""
for attachment in attachments:
attachment_type = self.find_attachment_type(attachment)
if attachment.datas and attachment_type in ('pdf', 'xlsx', 'jpg'):
temp_file_path = f'/tmp/temp.{attachment_type}'
binary_data = base64.b64decode(attachment.datas)
# Save the binary data to a file
with open(temp_file_path, 'wb') as file:
file.write(binary_data)
if attachment_type == 'jpg' and os.path.splitext(
temp_file_path)[1].lower() != '.jpg':
# Rename the saved file to its corresponding JPG file format
file_path = os.path.splitext(temp_file_path)[0] + '.jpg'
os.rename(temp_file_path, file_path)
temp_file_path = file_path
attachment_file = {
'file': (attachment.name, open(temp_file_path, 'rb'))
}
requests.post(attachment_url, headers=ATTACHMENT_HEADERS,
files=attachment_file,
auth=(self.user_id_jira, self.api_token))
def find_attachment_type(self, attachment):
""" To find the corresponding attachment type in the attachment model
Args:
attachment (model.Model): attachment to fetch the type.
Returns:
str: the attachment type
"""
if attachment.mimetype == 'application/pdf':
return 'pdf'
if attachment.mimetype == 'image/png':
return 'jpg'
if attachment.mimetype == 'application/vnd.openxmlformats-' \
'officedocument.spreadsheetml.sheet':
return 'xlsx'
return ''
def action_import_from_jira(self):
""" Import all the projects and corresponding tasks
from Odoo to Jira. If a project or task is modified in Odoo,
it will also be updated in Jira.
"""
auth = HTTPBasicAuth(self.user_id_jira, self.api_token)
response = requests.get(self.url + 'rest/api/2/project',
headers=JIRA_HEADERS, auth=auth)
projects = json.loads(response.text)
odoo_projects = self.env['project.project'].search([])
jira_project_ids = [int(a_dict['id']) for a_dict in projects]
name_list = [a_dict['name'] for a_dict in projects]
key_list = [a_dict['key'] for a_dict in projects]
for (name, key, jira_id) in zip(name_list, key_list, jira_project_ids):
if jira_id in [project.project_id_jira for project in
odoo_projects]:
response = requests.get(self.url + 'rest/api/3/search',
headers=JIRA_HEADERS,
params={'jql': 'project = %s' % key},
auth=auth)
data = response.json()
project = self.env['project.project'].search(
[('project_id_jira', '=', jira_id)])
tasks = self.env['project.task'].search(
[('project_id', '=', project.id)])
task_jira_ids = [task.task_id_jira for task in tasks]
for issue in data['issues']:
comment_url = self.url + 'rest/api/3/issue/%s/comment' % \
issue['key']
if issue['key'] in task_jira_ids:
task = self.env['project.task'].search(
[('task_id_jira', '=', issue['key'])])
else:
task = self.env['project.task'].create({
'project_id': project.id,
'name': issue['fields']['summary'],
'task_id_jira': issue['key']
})
self.import_task_count += 10
response = requests.get(comment_url, headers=JIRA_HEADERS,
auth=auth)
data = response.json()
messages = self.env['mail.message'].search(
[('res_id', '=', task.id),
('model', '=', 'project.task')])
odoo_comment_list = [str(html2plaintext(chat.body)) for chat
in messages]
jira_comment_list = [
str(comment['body']['content'][0]['content'][0]['text'])
for comment in data['comments'] if str(
comment['body']['content'][0]['content'][0][
'text']) not in odoo_comment_list]
comment_list = list(filter(None, jira_comment_list))
for comment in comment_list:
task.message_post(body=comment)
else:
project = self.env['project.project'].create({
'name': name,
'project_id_jira': jira_id,
'jira_project_key': key
})
self.import_project_count = 10
response = requests.get(self.url + 'rest/api/3/search',
headers=JIRA_HEADERS,
params={'jql': 'project = %s' % key},
auth=auth)
data = response.json()
for issue in data['issues']:
comment_url = self.url + 'rest/api/3/issue/%s/comment' % \
issue['key']
task = self.env['project.task'].create({
'project_id': project.id,
'name': issue['fields']['summary'],
'task_id_jira': issue['key']
})
self.import_task_count += 1
response = requests.get(comment_url, headers=JIRA_HEADERS,
auth=auth)
data = response.json()
messages = self.env['mail.message'].search(
[('res_id', '=', task.id),
('model', '=', 'project.task')])
odoo_comment_list = [str(html2plaintext(chat.body)) for chat
in messages]
jira_comment_list = [
str(comment['body']['content'][0]['content'][0]['text'])
for comment in data['comments'] if str(
comment['body']['content'][0]['content'][0][
'text']) not in odoo_comment_list]
comment_list = list(filter(None, jira_comment_list))
for comment in comment_list:
task.message_post(body=comment)

81
odoo_jira_connector/models/res_users.py

@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Dhanya B (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 <http://www.gnu.org/licenses/>.
#
##############################################################################
import json
import re
import requests
from odoo import api, fields, models
class ResUsers(models.Model):
"""
This class is inherited for adding an extra field and
override the create function.
Methods:
create():
extends create(vals_list) for exporting the new users to Jira
"""
_inherit = 'res.users'
jira_user_key = fields.Char(string='Jira User Key',
help='The user key of Jira.', readonly=True)
@api.model_create_multi
def create(self, vals_list):
""" Overrides the create method of users for exporting the new users
to Jira """
ir_config_parameter = self.env['ir.config_parameter'].sudo()
jira_connection = ir_config_parameter.get_param(
'odoo_jira_connector.connection')
if jira_connection:
user_auth = ir_config_parameter.get_param(
'odoo_jira_connector.user_id_jira')
password = ir_config_parameter.get_param(
'odoo_jira_connector.api_token')
users = super(ResUsers, self).create(vals_list)
odoo_user_url = ir_config_parameter.get_param(
'odoo_jira_connector.url') + 'rest/api/3/user'
odoo_user_headers = {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
payload = json.dumps({
'emailAddress': users.login,
'displayName': users.name,
'name': users.name,
'products':[]
})
match = re.search('^\S+@\S+\.\S+$', users.login)
if match and match.string == str(users.login):
response = requests.post(
odoo_user_url, headers=odoo_user_headers, data=payload,
auth=(user_auth, password))
data = response.json()
users.write({'jira_user_key': data['accountId']})
return users
else:
users = super(ResUsers, self).create(vals_list)
for user in users:
# if partner is global we keep it that way
if user.partner_id.company_id:
user.partner_id.company_id = user.company_id
user.partner_id.active = user.active
return users

2
odoo_jira_connector/security/ir.model.access.csv

@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_jira_sprint,jira.sprint.access,model_jira_sprint,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_jira_sprint jira.sprint.access model_jira_sprint base.group_user 1 1 1 1

BIN
odoo_jira_connector/static/description/assets/gif/Projects_in_jira.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
odoo_jira_connector/static/description/assets/gif/api_token2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
odoo_jira_connector/static/description/assets/gif/creat_api_token.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
odoo_jira_connector/static/description/assets/gif/e_exported_stage.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
odoo_jira_connector/static/description/assets/gif/e_scrum_with_sprint.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
odoo_jira_connector/static/description/assets/gif/hero.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

BIN
odoo_jira_connector/static/description/assets/gif/jira-qqqqq.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
odoo_jira_connector/static/description/assets/gif/jira-wwww.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
odoo_jira_connector/static/description/assets/gif/jira_1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
odoo_jira_connector/static/description/assets/gif/pro_views.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
odoo_jira_connector/static/description/assets/gif/queue_job.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
odoo_jira_connector/static/description/assets/gif/sprint.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
odoo_jira_connector/static/description/assets/gif/user_jira.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

BIN
odoo_jira_connector/static/description/assets/gif/webhook.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
odoo_jira_connector/static/description/assets/gif/webhook_setup_1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

BIN
odoo_jira_connector/static/description/assets/gif/webhook_setup_2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
odoo_jira_connector/static/description/assets/gif/webhook_setup_3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

BIN
odoo_jira_connector/static/description/assets/icons/check.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
odoo_jira_connector/static/description/assets/icons/chevron.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

BIN
odoo_jira_connector/static/description/assets/icons/cogs.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
odoo_jira_connector/static/description/assets/icons/consultation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
odoo_jira_connector/static/description/assets/icons/ecom-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

BIN
odoo_jira_connector/static/description/assets/icons/education-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

BIN
odoo_jira_connector/static/description/assets/icons/hotel-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

BIN
odoo_jira_connector/static/description/assets/icons/license.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
odoo_jira_connector/static/description/assets/icons/lifebuoy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
odoo_jira_connector/static/description/assets/icons/manufacturing-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

BIN
odoo_jira_connector/static/description/assets/icons/pos-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

BIN
odoo_jira_connector/static/description/assets/icons/puzzle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

BIN
odoo_jira_connector/static/description/assets/icons/restaurant-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

BIN
odoo_jira_connector/static/description/assets/icons/service-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

BIN
odoo_jira_connector/static/description/assets/icons/trading-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

BIN
odoo_jira_connector/static/description/assets/icons/training.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

BIN
odoo_jira_connector/static/description/assets/icons/update.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
odoo_jira_connector/static/description/assets/icons/user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

BIN
odoo_jira_connector/static/description/assets/icons/wrench.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
odoo_jira_connector/static/description/assets/misc/Cybrosys R.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
odoo_jira_connector/static/description/assets/misc/categories.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
odoo_jira_connector/static/description/assets/misc/check-box.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
odoo_jira_connector/static/description/assets/misc/compass.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
odoo_jira_connector/static/description/assets/misc/corporate.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
odoo_jira_connector/static/description/assets/misc/customer-support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
odoo_jira_connector/static/description/assets/misc/cybrosys-logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
odoo_jira_connector/static/description/assets/misc/features.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

BIN
odoo_jira_connector/static/description/assets/misc/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
odoo_jira_connector/static/description/assets/misc/pictures.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
odoo_jira_connector/static/description/assets/misc/pie-chart.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
odoo_jira_connector/static/description/assets/misc/right-arrow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

BIN
odoo_jira_connector/static/description/assets/misc/star.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
odoo_jira_connector/static/description/assets/misc/support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
odoo_jira_connector/static/description/assets/misc/whatsapp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
odoo_jira_connector/static/description/assets/modules/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
odoo_jira_connector/static/description/assets/modules/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
odoo_jira_connector/static/description/assets/modules/3.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
odoo_jira_connector/static/description/assets/modules/5.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

BIN
odoo_jira_connector/static/description/assets/modules/5.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
odoo_jira_connector/static/description/assets/modules/6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
odoo_jira_connector/static/description/assets/modules/7.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/Projects_in_jira.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/api_token2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/creat_api_token.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/e_exported_stage.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/e_scrum_with_sprint.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/hero.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 424 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/jira-qqqqq.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/jira-wwww.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/jira_1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/pro_views.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/queue_job.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/sprint.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/user_jira.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/webhook.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/webhook_setup_1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/webhook_setup_2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
odoo_jira_connector/static/description/assets/screenshots/webhook_setup_3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

BIN
odoo_jira_connector/static/description/banner.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

BIN
odoo_jira_connector/static/description/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

779
odoo_jira_connector/static/description/index.html

@ -0,0 +1,779 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Odoo App 3 Index</title>
<!-- Bootstrap CSS -->
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap"
rel="stylesheet">
</head>
<body>
<section>
<div class="container"
style="font-family: 'Inter', sans-serif !important;background-color: #fff !important;">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12 d-flex justify-content-between flex-wrap align-items-sm-center"
style="border-bottom:1px solid rgba(0, 0, 0, 0.22)">
<div class="my-3">
<img src="assets/misc/Cybrosys R.png"
style="width:auto !important; height:40px !important">
</div>
<div class="my-3 d-flex align-items-center">
<div class="text-center"
style="background-color:#017E84 !important;font-size: 0.8rem !important; color:#fff !important; font-weight:500 !important; padding:4px !important; margin:0 3px !important; border-radius:50px !important; min-width: 120px !important;">
Community
</div>
<div class="text-center"
style="background-color:#875A7B !important; color:#fff !important;font-size: 0.8rem !important; font-weight:500 !important; padding:4px !important; margin:0 3px !important; border-radius:50px !important;min-width: 120px !important;">
Enterprise
</div>
<div class="text-center"
style="background-color:#7C7BAD !important; color:#fff !important;font-size: 0.8rem !important; font-weight:500 !important; padding:4px !important; margin:0 3px !important; border-radius:50px !important; min-width: 120px !important;">
Odoo.sh
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12 text-center d-flex align-items-center flex-column"
style="margin: 80px 0px !important;">
<h1 style="font-size: 2.8rem;font-weight: 700; color:
#1A202C;">
Odoo Jira Connector</h1>
<p class="my-3 mb-4"
style="max-width: 80%; font-weight: 400 !important; line-height: 32px; color: #718096;">
Odoo Jira Connector is a valuable integration tool for businesses that use both Odoo and Jira. </p>
<div style="width: 80%; margin-top: 3rem;">
<img src="assets/screenshots/hero.gif"
class="img-responsive" width="100%" height="auto">
</div>
</div>
</div>
<div class="container mt-5 mb-5">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-4">
<p class="m-0"
style="font-weight: 600; font-size: 24px; color:#714b67 !important">
Key Highlights
</p>
</div>
<div class="row py-4">
<div class="col-md-6 col-sm-12 p-3">
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px;
background: #FFF;
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); ">
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67;
display: flex; justify-content: center; align-items: center;
margin-right: 10px; flex-shrink: 0;">
<i class="fa-solid fa-star "
style="color: #fff;font-size:14px;"></i>
</div>
<div>
<p style="color: #1A202C;font-weight: 600;
font-size: 1.2rem; margin-bottom: 2px;">
Export/import all the information of Project and Task.</p>
</div>
</div>
</div>
<div class="col-md-6 col-sm-12 p-3">
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px;
background: #FFF;
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); ">
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67;
display: flex; justify-content: center; align-items: center;
margin-right: 10px; flex-shrink: 0;">
<i class="fa-solid fa-star "
style="color: #fff;font-size:14px;"></i>
</div>
<div>
<p style="color: #1A202C;font-weight: 600;
font-size: 1.2rem; margin-bottom: 2px;">
Projects, Tasks, Attachment, Status, Users are created automatically in Jira when they
are created in Odoo,
and these are also created automatically in Odoo when they are created in Jira.</p>
</div>
</div>
</div>
<div class="col-md-6 col-sm-12 p-3">
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px;
background: #FFF;
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); ">
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67;
display: flex; justify-content: center; align-items: center;
margin-right: 10px; flex-shrink: 0;">
<i class="fa-solid fa-star "
style="color: #fff;font-size:14px;"></i>
</div>
<div>
<p style="color: #1A202C;font-weight: 600;
font-size: 1.2rem; margin-bottom: 2px;">
If we delete Task and Attachments in Odoo, it will be automatically removed from Jira,
Also if we delete Task and Attachments in Jira, it will be automatically removed from
Odoo.</p>
</div>
</div>
</div>
<div class="col-md-6 col-sm-12 p-3">
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px;
background: #FFF;
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); ">
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67;
display: flex; justify-content: center; align-items: center;
margin-right: 10px; flex-shrink: 0;">
<i class="fa-solid fa-star "
style="color: #fff;font-size:14px;"></i>
</div>
<div>
<p style="color: #1A202C;font-weight: 600;
font-size: 1.2rem; margin-bottom: 2px;">
Available in Odoo 17.0 Community and Enterprise.</p>
</div>
</div>
</div>
<div class="col-md-6 col-sm-12 p-3">
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px;
background: #FFF;
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); ">
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67;
display: flex; justify-content: center; align-items: center;
margin-right: 10px; flex-shrink: 0;">
<i class="fa-solid fa-star "
style="color: #fff;font-size:14px;"></i>
</div>
<div>
<p style="color: #1A202C;font-weight: 600;
font-size: 1.2rem; margin-bottom: 2px;">
All the Messages and log notes in the chatter of the Task are automatically added to the
comments of corresponding Task in Jira.</p>
</div>
</div>
</div>
<div class="col-md-6 col-sm-12 p-3">
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px;
background: #FFF;
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); ">
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67;
display: flex; justify-content: center; align-items: center;
margin-right: 10px; flex-shrink: 0;">
<i class="fa-solid fa-star "
style="color: #fff;font-size:14px;"></i>
</div>
<div>
<p style="color: #1A202C;font-weight: 600;
font-size: 1.2rem; margin-bottom: 2px;">
Available Sprint feature.</p>
</div>
</div>
</div>
<div class="col-md-6 col-sm-12 p-3">
<div class="d-flex h-100" style="padding: 30px;border-radius: 12px;
background: #FFF;
box-shadow: 1px 2px 3px 0px rgba(0, 0, 0, 0.25); ">
<div style="width: 36px; height: 36px; border-radius: 50%; background: #714B67;
display: flex; justify-content: center; align-items: center;
margin-right: 10px; flex-shrink: 0;">
<i class="fa-solid fa-star "
style="color: #fff;font-size:14px;"></i>
</div>
<div>
<p style="color: #1A202C;font-weight: 600;
font-size: 1.2rem; margin-bottom: 2px;">
Exporting Attachment from Odoo to Jira.</p>
</div>
</div>
</div>
</div>
</div>
<div class="container rounded">
<ul class="nav nav-tabs d-flex"
style="width: fit-content;margin: 0 auto;gap: 1rem;">
<li class="col text-center py-2 text-nowrap "
style="color: #fff; background-color: #714B67;border-radius: 6px 6px 0px 0px;">
<a
class="active show" data-toggle="tab" href="#tab1"
style="color: #fff;font-weight: 500; background-color: #714B67; text-decoration: none;">
<i class="fa-regular fa-image pr-2"
style="color: #fff;"></i>
Screenshots</a></li>
<li class="col text-center py-2 text-nowrap "
style="color: #fff; background-color: #714B67;border-radius: 6px 6px 0px 0px;">
<a
data-toggle="tab" href="#tab2"
style="color: #fff;font-weight: 500; text-decoration: none;"><i
class="fa-solid fa-star pr-2"
style="color: #fff;"></i>Features</a></li>
<li class="col text-center py-2 text-nowrap "
style="color: #fff; background-color: #714B67;border-radius: 6px 6px 0px 0px;">
<a
data-toggle="tab" href="#tab3"
style="color: #fff;font-weight: 500; text-decoration: none; background-color: #714B67;"><i
class="fa-solid fa-book-open pr-2"
style="color: #fff;"></i>Released Notes</a></li>
</ul>
<div class="tab-content"
style="background-color: rgba(121, 113, 119, 0.04);">
<div id="tab1" class="tab-pane fade in active show">
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div
style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="row justify-content-center p-3 w-100 m-0">
</div>
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
Jira Api Key Generation
</h4>
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">Go
to Jira --> Security --> Create API token.</p>
<img src="assets/screenshots/creat_api_token.png" class="img-thumbnail">
<p style="font-weight: 400; font-family: 'Montserrat', sans-serif; font-size: 14px;">
Provide a label and click on the Create button.</p>
<img src="assets/screenshots/api_token2.png" class="img-thumbnail">
</div>
</div>
</div>
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div
style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="row justify-content-center p-3 w-100 m-0">
</div>
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
Settings View
</h4>
<h2 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">
Project Admin can only access Jira connector in settings.</h2>
<img src="assets/screenshots/jira-qqqqq.png" class="img-thumbnail">
<p>First set the URL of Jira, Username and API Token. Then, you can test the
connection.</p>
<img src="assets/screenshots/jira_1.png" class="img-thumbnail">
<p>After that you can export/import or sync all the project, task and users. It also
syncs the comments and attachments.</p>
<p>You can import projects while creating them in your Jira account using the
"Automatic" feature .</p>
<img src="assets/screenshots/jira-wwww.png" class="img-thumbnail">
<p>For that you need to create a webhook in your Jira account.</p>
<h3>WebHook setup</h3>
<p>Go to Jira --> Settings --> System settings --> WebHooks</p>
<img src="assets/screenshots/webhook.png" class="img-thumbnail">
<p>click on create.</p>
<img src="assets/screenshots/webhook_setup_1.png" class="img-thumbnail">
<p>After adding details like 'name' and 'status',Add url "/jira_webhook" with your app's
url.</p>
<h4>Only allowed protocol is HTTPS.</h4>
<img src="assets/screenshots/webhook_setup_2.png" class="img-thumbnail">
<p>Then enable events to trigger that webhook.</p>
<img src="assets/screenshots/webhook_setup_3.png" class="img-thumbnail">
<p>After that save the webhook.</p></div>
</div>
</div>
</div>
<div id="tab4 class=" tab-pane fade in active show
">
<div class="col-lg-12 py-2"
style="padding: 1rem 4rem !important;">
<div
style="border: 1px solid #d8d6d6; border-radius: 4px; background: #fff; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="row justify-content-center p-3 w-100 m-0">
</div>
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
Jira View</h4>
<p>
Jira view of Projects.
.</p>
<img src="assets/screenshots/Projects_in_jira.png" class="img-thumbnail">
<p>
you can add a sprint in that project from Jira.
</p>
<img src="assets/screenshots/sprint.png" class="img-thumbnail">
</div>
<div class="px-3">
<h4 class="mt-2"
style=" font-weight:600 !important; color:#282F33 !important; font-size:1.3rem !important">
Jira View</h4>
<p>
Jira view of Users.
.</p>
<img src="assets/screenshots/user_jira.png" class="img-thumbnail">
</div>
</div>
</div>
</div>
<div id="tab3" class="tab-pane fade">
<div class="col-mg-12 active" style="padding: 1rem 4rem;">
<div class="py-3"
style="font-weight: 500;background-color: #fff; border-radius: 4px; padding: 1rem; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);">
<div class="d-flex mb-3"
style="font-size: 0.8rem; font-weight: 500;"><span>Version
17.0.1.0.0</span><span
class="px-2">|</span><span
style="color: #714B67;font-weight: 600;">Released on:15th January 2024</span>
</div>
<p class="m-0"
style=" color:#718096!important; font-size:1rem !important;line-height: 28px;">
Initial Commit for Odoo Jira Connector.</p>
</div>
</div>
</div>
</div>
</div>
<div class="container mt-5">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-5">
<p class="m-0"
style="font-weight: 600; font-size: 24px; color:#000 !important">
Related Products</p>
</div>
</div>
<div id="myCarousel" class="carousel slide py-3" data-ride="carousel">
<div class="carousel-inner">
<div class="carousel-item active">
<div class="row p-4">
<div class="col">
<div class="p-3">
<a href="https://apps.odoo.com/apps/modules/17.0/project_task_attachments/"
style="color: #000; text-decoration: none;">
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;">
<div style="width: 300px; ">
<img src="assets/modules/1.png"
alt="" width="100%"
height="auto">
</div>
<p class="text-center pt-2 text-black font-weight-bold">
Project And Task Attachments</p>
</div>
</a>
</div>
</div>
<div class="col">
<div class="p-3">
<a href="https://apps.odoo.com/apps/modules/17.0/project_task_timer/"
style="color: #000; text-decoration: none;">
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;">
<div style="width: 300px; ">
<img src="assets/modules/3.jpg"
alt="" width="100%"
height="auto">
</div>
<p class="text-center pt-2 text-black font-weight-bold">
Project Task Timer</p>
</div>
</a>
</div>
</div>
<div class="col">
<div class="p-3">
<a href="https://apps.odoo.com/apps/modules/17.0/projects_task_checklists/"
style="color: #000; text-decoration: none;">
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;">
<div style="width: 300px; ">
<img src="assets/modules/2.png"
alt="" width="100%"
height="auto">
</div>
<p class="text-center pt-2 text-black font-weight-bold">
Project Task Checklist</p>
</div>
</a>
</div>
</div>
</div>
</div>
<div class="carousel-item">
<div class="row p-4">
<div class="col">
<div class="p-3">
<a href="https://apps.odoo.com/apps/modules/17.0/project_tasks_from_templates/"
style="color: #000; text-decoration: none;">
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;">
<div style="width: 300px; ">
<img src="assets/modules/5.jpg"
alt="" width="100%"
height="auto">
</div>
<p class="text-center pt-2 text-black font-weight-bold">
Project Templates</p>
</div>
</a>
</div>
</div>
<div class="col">
<div class="p-3">
<a href="https://apps.odoo.com/apps/modules/17.0/advanced_project_management_system/"
style="color: #000; text-decoration: none;">
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;">
<div style="width: 300px;">
<img src="assets/modules/7.jpg"
alt="" width="100%"
height="auto">
</div>
<p class="text-center pt-2 text-black font-weight-bold">
Advanced Project Management System</p>
</div>
</a>
</div>
</div>
<div class="col">
<div class="p-3">
<a href="https://apps.odoo.com/apps/modules/17.0/project_report_pdf/"
style="color: #000; text-decoration: none;">
<div style="border:1px solid #CBCBCB !important;border-radius: 4px;">
<div style="width: 300px;">
<img src="assets/modules/6.png"
alt="" width="100%"
height="auto">
</div>
<p class="text-center pt-2 text-black font-weight-bold">
Project Report XLS & PDF</p>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
<a class="carousel-control-prev" href="#myCarousel"
data-slide="prev" style="width: 35px; color: #000;">
<span class="carousel-control-prev-icon">
<i class="fa fa-chevron-left"
style="font-size: 24px;"></i>
</span>
</a>
<a class="carousel-control-next" href="#myCarousel"
data-slide="next" style="width: 35px; color: #000;">
<span class="carousel-control-next-icon">
<i class="fa fa-chevron-right"
style="font-size: 24px;"></i>
</span>
</a>
</div>
<div class="container mt-5">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-4">
<p class="m-0"
style="font-weight: 600; font-size: 24px; color:#000 !important">
Our Services</p>
</div>
</div>
<div class="container my-5">
<div class="row py-3">
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#13EA36 ; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/cogs.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Customization</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#DBC711; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/wrench.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Implementation</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative; border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#FF6B6B ; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/lifebuoy.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Support</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative; border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#FFA801 ; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/user.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Hire
Odoo Developer</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative; border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#54A0FF; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/puzzle.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Integration</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#6D7680 ; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/update.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Migration</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#786FA6 ; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/consultation.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Consultancy</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px;position: relative;border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#F8A5C2 ; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/training.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Implementation</p>
</div>
</div>
<div class="col-md-4 col-sm-6 px-4 py-4">
<div
style="background-color: #fff; padding: 25px; text-align: center; box-shadow: rgba(0, 0, 0, 0.1) 0px 1px 3px 0px, rgba(0, 0, 0, 0.06) 0px 1px 2px 0px; position: relative;border-radius: 4px;">
<div style="position: absolute; top: 0%; left: 50%; transform: translate(-50%, -50%);">
<div style="background-color:#E6BE26; border-radius: 50%; padding: 15px; width: 68px;
height: 68px; display: inline-block; box-shadow:0px 4px 4px rgba(0, 0, 0, 0.25);">
<img src="assets/icons/license.png"
alt="service-icon" width="38px"
height="auto">
</div>
</div>
<p style="margin-top: 20px; font-weight: bold;">Odoo
Licensing Consultancy</p>
</div>
</div>
</div>
</div>
<div class="container mt-5">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-4">
<p class="m-0"
style="font-weight: 600; font-size: 24px; color:#000 !important">
Our Industries</p>
</div>
</div>
<div class="container">
<div class="row my-5 py-4">
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100 "
style="border-right: 1px solid rgb(209, 209, 209); border-bottom: 1px solid rgb(209, 209, 209); padding: 30px; box-shadow: 6px 0 10px rgba(228, 227, 227, 0.373);">
<img src="assets/icons/trading-black.png" width="42px"
height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">Trading</p>
<p>Easily procure and sell your products</p>
</div>
</div>
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100"
style="border-right: 1px solid rgb(209, 209, 209);border-bottom: 1px solid rgb(209, 209, 209); padding: 30px;">
<img src="assets/icons/pos-black.png" width="42px"
height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">POS</p>
<p>Easy configuration and convivial experience</p>
</div>
</div>
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100"
style="border-right: 1px solid rgb(209, 209, 209);border-bottom: 1px solid rgba(0, 0, 0, 0.2); padding: 30px; box-shadow: 0 5px 10px rgba(228, 227, 227, 0.373)">
<img src="assets/icons/education-black.png" width="42px"
height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">
Education</p>
<p>A platform for educational management</p>
</div>
</div>
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100"
style="border-bottom: 1px solid rgb(209, 209, 209); padding: 30px; ">
<img src="assets/icons/manufacturing-black.png"
width="42px" height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">
Manufacturing</p>
<p>Plan, track and schedule your operations</p>
</div>
</div>
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100"
style="border-right: 1px solid rgb(209, 209, 209); padding: 30px;">
<img src="assets/icons/ecom-black.png" width="42px"
height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">E-commerce &
Website</p>
<p>Mobile friendly, awe-inspiring product pages</p>
</div>
</div>
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100"
style="border-right: 1px solid rgb(209, 209, 209); padding: 30px;box-shadow: 0 -5px 10px rgba(228, 227, 227, 0.373);">
<img src="assets/icons/service-black.png" width="42px"
height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">Service
Management</p>
<p>Keep track of services and invoice</p>
</div>
</div>
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100"
style="border-right: 1px solid rgb(209, 209, 209); padding: 30px; ">
<img src="assets/icons/restaurant-black.png"
width="42px" height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">
Restaurant</p>
<p>Run your bar or restaurant methodically</p>
</div>
</div>
<div class="col-md-3 col-sm-6 p-0">
<div class="d-flex flex-column h-100"
style=" padding: 30px;box-shadow: -5px 0 10px rgba(228, 227, 227, 0.373);">
<img src="assets/icons/hotel-black.png" width="42px"
height="auto" alt="">
<p style="color: #714B67;font-weight: 600; margin-top: 10px;
font-size: 1.2rem; margin-bottom: 2px;">Hotel
Management</p>
<p>An all-inclusive hotel management application</p>
</div>
</div>
</div>
</div>
<div class="container mt-5">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center mt-5">
<p class="m-0"
style="font-weight: 600; font-size: 24px; color:#000 !important">
Support</p>
</div>
</div>
<div class="container my-5">
<div class="row" style="background-color: #FFFAFE;">
<div class="col-md-6 pb-4 d-flex align-items-center justify-content-center"
style="border-right: 1px solid #D9D9D9;">
<div style="padding: 30px;">
<div class="d-flex align-items-center">
<img src="assets/misc/support (1) 1.svg" alt=""
width="60px" style="margin-right: 12px;">
<div style="padding: 0px 8px;">
<span
style="color: #714B67;font-size: 24px;font-weight: 600;padding-bottom: 1rem;">Need
Help?</span>
<p class="m-0" style="color:#718096;">Got
questions or need help? Get in touch.</p>
<div style="font-weight: 400;"><span><img
src="assets/misc/support-email.svg"
alt=""
width="18px"
style="filter: invert(1);margin-right: 0.8rem;"></span>odoo@cybrosys.com
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6 pb-4 d-flex align-items-center justify-content-center">
<div style="padding: 30px;">
<div class="d-flex align-items-center">
<img src="assets/misc/whatsapp 1.svg" alt=""
width="60px" style="margin-right: 12px;">
<div>
<span style="color: #714B67;font-size: 24px;font-weight: 600;">WhatsApp</span>
<p class="m-0" style="color:#718096;">Say hi to
us on WhatsApp!</p>
<div style="font-weight: 400; font-size: 16px;"><span><img
src="assets/misc/phone.svg"
alt="" width="14px"
style="filter: invert(1); margin-right: 0.8rem;"></span>+91
99456767686
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

56
odoo_jira_connector/views/jira_sprint_views.xml

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- SPRINT TREE VIEW-->
<record id="jira_sprint_view_tree" model="ir.ui.view">
<field name="name">jira.sprint.view.tree</field>
<field name="model">jira.sprint</field>
<field name="arch" type="xml">
<tree>
<field name="name" />
<field name="sprint_goal"/>
<field name="start_date"/>
<field name="end_date"/>
</tree>
</field>
</record>
<!-- SPRINT FORM VIEW-->
<record id="jira_sprint_view_form" model="ir.ui.view">
<field name="name">jira.sprint.view.form</field>
<field name="model">jira.sprint</field>
<field name="arch" type="xml">
<form>
<header>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button string="Tasks" class="oe_stat_button" type="object" name="action_get_tasks"/>
<button string="Backlogs" class="oe_stat_button" type="object" name="action_get_backlogs"/>
<button string="All tasks" class="oe_stat_button" type="object" name="action_get_all_tasks"/>
</div>
<group>
<h3>
<field name="name" placeholder="Sprint name...." help="Enter name of the sprint."/>
</h3>
<group>
<field name="start_date" help="Start date of sprint."/>
<field name="end_date" help="End date of sprint."/>
<field name="project_id" help="Related project."/>
<field name="sprint_id_jira" help="Sprint id of Jira."/>
</group>
<notebook>
<page string="Goals">
<field name="sprint_goal" widget="html" help="Goals of the sprint."/>
</page>
</notebook>
</group>
</sheet>
</form>
</field>
</record>
<!-- Action for jira sprint model.-->
<record id="jira_sprint_view_action" model="ir.actions.act_window">
<field name="name">Sprint</field>
<field name="res_model">jira.sprint</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>

16
odoo_jira_connector/views/project_task_type_views.xml

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Inheriting project.task.type form view and adding new field to project_id_jira -->
<record id="task_type_edit" model="ir.ui.view">
<field name="name">project.task.type.view.form.inherit.odoo.jira.connector</field>
<field name="model">project.task.type</field>
<field name="type">form</field>
<field name="inherit_id" ref="project.task_type_edit"/>
<field name="arch" type="xml">
<field name="project_ids" position="after">
<field name="stages_jira_id" help="stage id of Jira."/>
<field name="jira_stages_category" help="Category of stages in jira."/>
</field>
</field>
</record>
</odoo>

34
odoo_jira_connector/views/project_views.xml

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Inheriting project.project form view and adding new field to project_id_jira -->
<record id="edit_project" model="ir.ui.view">
<field name="name">project.project.view.form.inherit.odoo.jira.connector</field>
<field name="model">project.project</field>
<field name="type">form</field>
<field name="inherit_id" ref="project.edit_project"/>
<field name="arch" type="xml">
<field name="user_id" position="after">
<field name="project_id_jira"/>
</field>
<xpath expr="//div[hasclass('oe_button_box')]" position="inside">
<field name="sprint_active" invisible="1"/>
<button string="Sprint" class="oe_stat_button" type="object" name="action_get_sprint" icon="fa-clock-o" width="5"
height="5" invisible="sprint_active != True"/>
</xpath>
</field>
</record>
<!-- Inheriting the project.task form view and adding new field task_id_jira -->
<record id="view_task_form2" model="ir.ui.view">
<field name="name">project.task.view.form.inherit.odoo.jira.connector</field>
<field name="model">project.task</field>
<field name="type">form</field>
<field name="inherit_id" ref="project.view_task_form2"/>
<field name="arch" type="xml">
<field name="tag_ids" position="after">
<field name="task_id_jira" help="id of task in jira."/>
<field name="sprint_id" invisible="task_sprint_active != True" help="related sprint."/>
<field name="task_sprint_active" invisible="1"/>
</field>
</field>
</record>
</odoo>

90
odoo_jira_connector/views/res_config_settings_views.xml

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Inheriting the config setting and add the fields and buttons for connecting to Jira -->
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.odoo.jira.connector</field>
<field name="model">res.config.settings</field>
<field name="priority" eval="20"/>
<field name="inherit_id" ref="base_setup.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//form" position="inside">
<div class="app_settings_block" data-string="Jira Connector" string="Jira Connector"
data-key="odoo_jira_connector" groups="project.group_project_manager">
<h2>Jira Connector</h2>
<div class="row mt16 o_settings_container">
<div class="col-xs-12 col-md-6 o_setting_box" id="print_node_settings">
<div class="o_setting_right_pane">
<div class="content-group">
<div class="row mt8">
<label class="col-lg-3" string="URL" for="url"/>
<field name="url" help="Url of Jira."/>
</div>
<div class="row mt8">
<label class="col-lg-3" string="User Name" for="user_id_jira"/>
<field name="user_id_jira" help="Enter User name in Jira."/>
</div>
<div class="row mt8">
<label class="col-lg-3" string="API Token" for="api_token"/>
<field name="api_token" help="Enter api token."/>
</div>
<div class="row mt8" invisible="connection == False">
<label class="col-lg-3" string="Automatic" for="automatic"/>
<field name="automatic"
help="Using this field we can automate the project management in Jira ,Eg:we can create project in odoo while creating it on Jira."/>
</div>
<div class="row mt8">
<label class="col-lg-6" string="Export Project Count"
for="export_project_count"/>
<field name="export_project_count" help="count of exported project."/>
</div>
<div class="row mt8">
<label class="col-lg-6" string="Export Task Count" for="export_task_count"/>
<field name="export_task_count" help="count of exported tasks."/>
</div>
<div class="row mt8">
<label class="col-lg-6" string="Import Project Count"
for="import_project_count"/>
<field name="import_project_count" help="count of imported project."/>
</div>
<div class="row mt8">
<label class="col-lg-6" string="Import Task Count" for="import_task_count"/>
<field name="import_task_count" help="count of imported task."/>
</div>
<div class="row mt8">
<field name="connection" invisible="1"/>
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_right_pane" style="display: flex; flex-wrap: wrap;">
<button name="action_test_connection" string="Test Connection"
invisible="connection == True" type="object" class="btn-primary"/>
<div style="margin:10px !important">
<button name="action_export_to_jira" string="Export/Sync Project and Task"
invisible="connection != True" type="object"
class="btn-primary"/>
</div>
<div style="margin:10px !important">
<button name="action_import_from_jira" string="Import/Sync Project &amp; Tasks"
invisible="connection == False" type="object"
class="btn-primary"/>
</div>
<div style="margin:10px !important">
<button name="action_export_users" string="Export Users"
invisible="connection == False" type="object"
class="btn-primary"/>
</div>
<div style="margin:10px !important">
<button name="action_import_users" string="Import Users"
invisible="connection == False" type="object"
class="btn-primary"/>
</div>
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>
</odoo>

16
odoo_jira_connector/views/res_users_views.xml

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- inheriting res.user form view and adding new field -->
<record id="view_users_form" model="ir.ui.view">
<field name="name">res.users.view.form.inherit.odoo.jira.connector</field>
<field name="model">res.users</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<field name="login" position="after">
<label for="jira_user_key">Jira Key</label>
<field name="jira_user_key" string="Jira Key" help="Jira user Key." readonly="1"/>
</field>
</field>
</record>
</odoo>
Loading…
Cancel
Save