diff --git a/.docker_files/main/__manifest__.py b/.docker_files/main/__manifest__.py
index 3ba7f6b..a5faf36 100644
--- a/.docker_files/main/__manifest__.py
+++ b/.docker_files/main/__manifest__.py
@@ -13,7 +13,8 @@
'depends': [
'base',
'github_event',
- 'github_pull_request'
+ 'github_pull_request',
+ 'github_pull_request_project',
],
'installable': True,
}
diff --git a/Dockerfile b/Dockerfile
index 373713c..1001dab 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -15,6 +15,7 @@ USER odoo
COPY github_event /mnt/extra-addons/github_event
COPY github_pull_request /mnt/extra-addons/github_pull_request
+COPY github_pull_request_project /mnt/extra-addons/github_pull_request_project
COPY .docker_files/main /mnt/extra-addons/main
COPY .docker_files/odoo.conf /etc/odoo
diff --git a/github_pull_request_project/README.rst b/github_pull_request_project/README.rst
new file mode 100644
index 0000000..5eba95e
--- /dev/null
+++ b/github_pull_request_project/README.rst
@@ -0,0 +1,19 @@
+Github Pull Request Project
+===========================
+
+This modules adds a relation between tasks and github pull request.
+
+
+Tasks in Pull Request Form view
+-------------------------------
+
+.. image:: static/description/pull_request_form_tasks.png
+
+Pull Requests in Tasks Form View
+--------------------------------
+
+.. image:: static/description/project_task_pull_requests.png
+
+Contributors
+------------
+* Numigi (tm) and all its contributors (https://bit.ly/numigiens)
diff --git a/github_pull_request_project/__init__.py b/github_pull_request_project/__init__.py
new file mode 100644
index 0000000..ac4a686
--- /dev/null
+++ b/github_pull_request_project/__init__.py
@@ -0,0 +1,4 @@
+# Copyright 2023 - today Numigi (tm) and all its contributors (https://bit.ly/numigiens)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
+
+from . import models
diff --git a/github_pull_request_project/__manifest__.py b/github_pull_request_project/__manifest__.py
new file mode 100644
index 0000000..c7ee2a8
--- /dev/null
+++ b/github_pull_request_project/__manifest__.py
@@ -0,0 +1,23 @@
+# Copyright 2023 - Today Numigi (tm) and all its contributors (https://bit.ly/numigiens)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
+
+{
+ 'name': 'Github Pull Request Project',
+ 'version': "16.0.1.0.0",
+ 'author': 'Numigi',
+ 'maintainer': 'Numigi',
+ 'website': 'https://bit.ly/numigi-com',
+ 'license': 'LGPL-3',
+ 'category': 'Project',
+ 'summary': 'Create a relation between tasks and github.pull_request models.',
+ 'depends': ['project', 'github_pull_request'],
+ 'data': [
+ 'data/project_tags.xml',
+ 'views/ir_actions_act_window.xml',
+ 'views/ir_ui_menu.xml',
+ 'views/github_pull_request.xml',
+ 'views/project_task.xml',
+ ],
+ 'auto_install': True,
+ 'installable': True,
+}
diff --git a/github_pull_request_project/data/project_tags.xml b/github_pull_request_project/data/project_tags.xml
new file mode 100644
index 0000000..e8a6d9b
--- /dev/null
+++ b/github_pull_request_project/data/project_tags.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ PR: Open
+
+
+
+
+ PR: Merged
+
+
+
+
+ PR: Closed
+
+
+
+
diff --git a/github_pull_request_project/i18n/fr.po b/github_pull_request_project/i18n/fr.po
new file mode 100644
index 0000000..b7604dd
--- /dev/null
+++ b/github_pull_request_project/i18n/fr.po
@@ -0,0 +1,50 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * github_pull_request_project
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 16.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2019-11-14 19:48+0000\n"
+"PO-Revision-Date: 2019-11-14 19:48+0000\n"
+"Last-Translator: <>\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: github_pull_request_project
+#: model:ir.model,name:github_pull_request_project.model_github_pull_request
+msgid "Github Pull Request"
+msgstr ""
+
+#. module: github_pull_request_project
+#: model:ir.model.fields,field_description:github_pull_request_project.field_project_task__no_pull_request_open
+msgid "No Pull Request Open"
+msgstr "Aucune pull request d'ouverte"
+
+#. module: github_pull_request_project
+#: model:ir.model.fields,field_description:github_pull_request_project.field_project_task__pull_request_qty
+msgid "Pull Request Qty"
+msgstr "Quantité de pull request"
+
+#. module: github_pull_request_project
+#: model:ir.actions.act_window,name:github_pull_request_project.pull_requests_action_window_context
+#: model:ir.model.fields,field_description:github_pull_request_project.field_project_task__pull_request_ids
+#: model:ir.ui.menu,name:github_pull_request_project.project_pull_request_menu
+#: model_terms:ir.ui.view,arch_db:github_pull_request_project.project_task_form_pull_requests
+msgid "Pull Requests"
+msgstr ""
+
+#. module: github_pull_request_project
+#: model:ir.model,name:github_pull_request_project.model_project_task
+msgid "Task"
+msgstr "Tâche"
+
+#. module: github_pull_request_project
+#: model:ir.model.fields,field_description:github_pull_request_project.field_github_pull_request__task_ids
+msgid "Tasks"
+msgstr "Tâches"
+
diff --git a/github_pull_request_project/models/__init__.py b/github_pull_request_project/models/__init__.py
new file mode 100644
index 0000000..753acce
--- /dev/null
+++ b/github_pull_request_project/models/__init__.py
@@ -0,0 +1,4 @@
+# Copyright 2023 - today Numigi (tm) and all its contributors (https://bit.ly/numigiens)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
+
+from . import github_pull_request
diff --git a/github_pull_request_project/models/github_pull_request.py b/github_pull_request_project/models/github_pull_request.py
new file mode 100644
index 0000000..2a9d382
--- /dev/null
+++ b/github_pull_request_project/models/github_pull_request.py
@@ -0,0 +1,106 @@
+# Copyright 2023 - today Numigi (tm) and all its contributors (https://bit.ly/numigiens)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
+
+from odoo import fields, models, api
+
+
+class GithubPullRequestTask(models.Model):
+
+ _inherit = "github.pull_request"
+
+ task_ids = fields.Many2many(
+ 'project.task',
+ 'pull_request_task_ref',
+ 'pull_request_id',
+ 'task_id',
+ string='Tasks',
+ )
+
+ @api.model
+ def create(self, vals):
+ pr = super().create(vals)
+ pr.task_ids._update_pull_request_tags()
+ return pr
+
+ def write(self, vals):
+ must_update_tags_on_tasks = 'task_ids' in vals or 'state' in vals
+
+ if must_update_tags_on_tasks:
+ tasks_to_update = self.mapped('task_ids')
+
+ super().write(vals)
+
+ if must_update_tags_on_tasks:
+ tasks_to_update |= self.mapped('task_ids')
+ tasks_to_update._update_pull_request_tags()
+
+ return True
+
+
+def has_pull_request_at_state(task: 'project.task', state: str) -> bool:
+ """Return True if the task has at least one PR at the given state."""
+ return task.pull_request_ids.filtered(lambda pr: pr.state == state)
+
+
+class ProjectTaskPullRequest(models.Model):
+
+ _inherit = "project.task"
+
+ pull_request_ids = fields.Many2many(
+ 'github.pull_request',
+ 'pull_request_task_ref',
+ 'task_id',
+ 'pull_request_id',
+ string='Pull Requests',
+ copy=False,
+ )
+
+ def _compute_pull_request_qty(self):
+ for record in self:
+ record.pull_request_qty = len(record.pull_request_ids)
+
+ pull_request_qty = fields.Integer(compute="_compute_pull_request_qty")
+
+ @api.model
+ def create(self, vals):
+ task = super().create(vals)
+ task._update_pull_request_tags()
+ return task
+
+ def write(self, vals):
+ super().write(vals)
+
+ if 'pull_request_ids' in vals:
+ self._update_pull_request_tags()
+
+ return True
+
+ def _update_pull_request_tags(self):
+ tag_open = self.env.ref('github_pull_request_project.tag_pull_request_open')
+ tag_merged = self.env.ref('github_pull_request_project.tag_pull_request_merged')
+ tag_closed = self.env.ref('github_pull_request_project.tag_pull_request_closed')
+
+ for task in self:
+ show_open_tag = has_pull_request_at_state(task, 'open')
+ show_merged_tag = (
+ has_pull_request_at_state(task, 'merged') and not show_open_tag
+ )
+ show_closed_tag = (
+ has_pull_request_at_state(task, 'closed')
+ and not show_open_tag
+ and not show_merged_tag
+ )
+
+ task.update(
+ {
+ 'tag_ids': [
+ (4 if show_open_tag else 3, tag_open.id),
+ (4 if show_merged_tag else 3, tag_merged.id),
+ (4 if show_closed_tag else 3, tag_closed.id),
+ ]
+ }
+ )
+
+ @api.onchange('pull_request_ids')
+ def _onchange_pull_requests_update_tags(self):
+ self._update_pull_request_tags()
diff --git a/github_pull_request_project/static/description/icon.png b/github_pull_request_project/static/description/icon.png
new file mode 100644
index 0000000..92a86b1
Binary files /dev/null and b/github_pull_request_project/static/description/icon.png differ
diff --git a/github_pull_request_project/static/description/project_task_pull_requests.png b/github_pull_request_project/static/description/project_task_pull_requests.png
new file mode 100644
index 0000000..be72ddb
Binary files /dev/null and b/github_pull_request_project/static/description/project_task_pull_requests.png differ
diff --git a/github_pull_request_project/static/description/pull_request_form_tasks.png b/github_pull_request_project/static/description/pull_request_form_tasks.png
new file mode 100644
index 0000000..c67b609
Binary files /dev/null and b/github_pull_request_project/static/description/pull_request_form_tasks.png differ
diff --git a/github_pull_request_project/tests/__init__.py b/github_pull_request_project/tests/__init__.py
new file mode 100644
index 0000000..cef8020
--- /dev/null
+++ b/github_pull_request_project/tests/__init__.py
@@ -0,0 +1,4 @@
+# Copyright 2023 - today Numigi (tm) and all its contributors (https://bit.ly/numigiens)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
+
+from . import test_project_task
diff --git a/github_pull_request_project/tests/test_project_task.py b/github_pull_request_project/tests/test_project_task.py
new file mode 100644
index 0000000..5b0da95
--- /dev/null
+++ b/github_pull_request_project/tests/test_project_task.py
@@ -0,0 +1,173 @@
+# Copyright 2023 - today Numigi (tm) and all its contributors (https://bit.ly/numigiens)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
+
+
+from odoo.tests import common
+
+
+class TestProjectTask(common.SavepointCase):
+
+ @classmethod
+ def setUpClass(cls):
+ super().setUpClass()
+ cls.pull_request_open = cls.env["github.pull_request"].create(
+ {
+ "source": "https://github.com/Numigi/odoo-git-addons/pull/1",
+ "state": "open",
+ }
+ )
+ cls.pull_request_closed = cls.env["github.pull_request"].create(
+ {
+ "source": "https://github.com/Numigi/odoo-git-addons/pull/2",
+ "state": "closed",
+ }
+ )
+ cls.pull_request_merged = cls.env["github.pull_request"].create(
+ {
+ "source": "https://github.com/Numigi/odoo-git-addons/pull/3",
+ "state": "merged",
+ }
+ )
+
+ cls.tag_open = cls.env.ref('github_pull_request_project.tag_pull_request_open')
+ cls.tag_merged = cls.env.ref(
+ 'github_pull_request_project.tag_pull_request_merged'
+ )
+ cls.tag_closed = cls.env.ref(
+ 'github_pull_request_project.tag_pull_request_closed'
+ )
+
+ # Simple cases
+ def test_whenNoPR_thenNoTag(self):
+ task = self.env["project.task"].create(
+ {"name": "ttask", "pull_request_ids": [(5, False, False)]}
+ )
+ assert not task.tag_ids
+
+ def test_whenPrAreMerged_thenTaskMerged(self):
+ task = self.env["project.task"].create(
+ {
+ "name": "ttask",
+ "pull_request_ids": [(6, False, (self.pull_request_merged.id,))],
+ }
+ )
+ assert task.tag_ids == self.tag_merged
+
+ def test_whenPrAreClosed_thenTaskClosed(self):
+ task = self.env["project.task"].create(
+ {
+ "name": "ttask",
+ "pull_request_ids": [(6, False, (self.pull_request_closed.id,))],
+ }
+ )
+ assert task.tag_ids == self.tag_closed
+
+ def test_whenPrAreOpen_thenTaskOpen(self):
+ task = self.env["project.task"].create(
+ {
+ "name": "ttask",
+ "pull_request_ids": [(6, False, (self.pull_request_open.id,))],
+ }
+ )
+ assert task.tag_ids == self.tag_open
+
+ # Mixing cases
+ def test_whenAllStates_thenTaskOpen(self):
+ task = self.env["project.task"].create(
+ {
+ "name": "ttask",
+ "pull_request_ids": [
+ (
+ 6,
+ False,
+ (
+ self.pull_request_open.id,
+ self.pull_request_closed.id,
+ self.pull_request_merged.id,
+ ),
+ )
+ ],
+ }
+ )
+ assert task.tag_ids == self.tag_open
+
+ def test_whenClosedAndMerged_thenTaskMerged(self):
+ task = self.env["project.task"].create(
+ {
+ "name": "ttask",
+ "pull_request_ids": [
+ (
+ 6,
+ False,
+ (self.pull_request_closed.id, self.pull_request_merged.id),
+ )
+ ],
+ }
+ )
+ assert task.tag_ids == self.tag_merged
+
+ def test_whenMergedAndOpen_thenTaskOpen(self):
+ task = self.env["project.task"].create(
+ {
+ "name": "ttask",
+ "pull_request_ids": [
+ (6, False, (self.pull_request_merged.id, self.pull_request_open.id))
+ ],
+ }
+ )
+ assert task.tag_ids == self.tag_open
+
+ def test_whenClosedAndOpen_thenTaskOpen(self):
+ task = self.env["project.task"].create(
+ {
+ "name": "ttask",
+ "pull_request_ids": [
+ (6, False, (self.pull_request_closed.id, self.pull_request_open.id))
+ ],
+ }
+ )
+ assert task.tag_ids == self.tag_open
+
+ def test_onTaskWrite_tagsUpdated(self):
+ task = self.env["project.task"].create(
+ {
+ "name": "ttask",
+ }
+ )
+ task.pull_request_ids = self.pull_request_open
+ assert task.tag_ids == self.tag_open
+
+ def test_onPullRequestStateChange_tagsUpdated(self):
+ task = self.env["project.task"].create(
+ {
+ "name": "ttask",
+ "pull_request_ids": [(4, self.pull_request_open.id)],
+ }
+ )
+ assert task.tag_ids == self.tag_open
+ self.pull_request_open.state = 'merged'
+ assert task.tag_ids == self.tag_merged
+
+ def test_onPullRequestTaskIdsChange_tagsUpdated(self):
+ task = self.env["project.task"].create(
+ {
+ "name": "ttask",
+ }
+ )
+ self.pull_request_open.task_ids = task
+ assert task.tag_ids == self.tag_open
+
+ def test_onPullRequestCreate_tagsUpdated(self):
+ task = self.env["project.task"].create(
+ {
+ "name": "ttask",
+ }
+ )
+ self.env["github.pull_request"].create(
+ {
+ "source": "https://github.com/Numigi/odoo-git-addons/pull/999",
+ "state": "open",
+ "task_ids": [(4, task.id)],
+ }
+ )
+ assert task.tag_ids == self.tag_open
diff --git a/github_pull_request_project/views/github_pull_request.xml b/github_pull_request_project/views/github_pull_request.xml
new file mode 100644
index 0000000..7d03d4e
--- /dev/null
+++ b/github_pull_request_project/views/github_pull_request.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ github.pull_request
+ Github Pull Request: task ids
+ form
+
+
+
+
+
+
+
+
+
+
+ github.pull_request
+ Github Pull Request: task ids
+ 16
+ search
+
+
diff --git a/github_pull_request_project/views/ir_actions_act_window.xml b/github_pull_request_project/views/ir_actions_act_window.xml
new file mode 100644
index 0000000..00a7120
--- /dev/null
+++ b/github_pull_request_project/views/ir_actions_act_window.xml
@@ -0,0 +1,13 @@
+
+
+
+ Pull Requests
+ github.pull_request
+ current
+ ir.actions.act_window
+ tree,form
+ {
+ 'search_default_task_ids': [active_id],
+ }
+
+
diff --git a/github_pull_request_project/views/ir_ui_menu.xml b/github_pull_request_project/views/ir_ui_menu.xml
new file mode 100644
index 0000000..cce2fff
--- /dev/null
+++ b/github_pull_request_project/views/ir_ui_menu.xml
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/github_pull_request_project/views/project_task.xml b/github_pull_request_project/views/project_task.xml
new file mode 100644
index 0000000..1d16560
--- /dev/null
+++ b/github_pull_request_project/views/project_task.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ project.task
+ Project Task: pull request fields
+ 99
+
+
+