From 28ae6f55726a83e30ccbd510e7427fb4cd223296 Mon Sep 17 00:00:00 2001 From: Quentin Mondot Date: Wed, 2 Apr 2025 14:49:28 +0200 Subject: [PATCH 1/3] [NEW] helpdesk_transfer_timesheet_to_task: assign the timesheets ticket to the linked task --- .../.gitignore | 2 + helpdesk_transfer_timesheet_to_task/README.md | 39 ++++ .../__init__.py | 1 + .../__manifest__.py | 23 +++ .../models/__init__.py | 1 + .../models/helpdesk_ticket.py | 12 ++ .../tests/__init__.py | 1 + .../tests/test_helpdesk_ticket.py | 181 ++++++++++++++++++ 8 files changed, 260 insertions(+) create mode 100644 helpdesk_transfer_timesheet_to_task/.gitignore create mode 100644 helpdesk_transfer_timesheet_to_task/README.md create mode 100644 helpdesk_transfer_timesheet_to_task/__init__.py create mode 100644 helpdesk_transfer_timesheet_to_task/__manifest__.py create mode 100644 helpdesk_transfer_timesheet_to_task/models/__init__.py create mode 100644 helpdesk_transfer_timesheet_to_task/models/helpdesk_ticket.py create mode 100644 helpdesk_transfer_timesheet_to_task/tests/__init__.py create mode 100644 helpdesk_transfer_timesheet_to_task/tests/test_helpdesk_ticket.py diff --git a/helpdesk_transfer_timesheet_to_task/.gitignore b/helpdesk_transfer_timesheet_to_task/.gitignore new file mode 100644 index 0000000..6da5887 --- /dev/null +++ b/helpdesk_transfer_timesheet_to_task/.gitignore @@ -0,0 +1,2 @@ +*.*~ +*pyc diff --git a/helpdesk_transfer_timesheet_to_task/README.md b/helpdesk_transfer_timesheet_to_task/README.md new file mode 100644 index 0000000..98927d6 --- /dev/null +++ b/helpdesk_transfer_timesheet_to_task/README.md @@ -0,0 +1,39 @@ +=================================== +helpdesk_transfer_timesheet_to_task +=================================== + +This module assigns the field task of the timesheets' ticket to the task linked to the ticket. +Exception : the timesheet is not transfered to the task linked to the ticket if +it has already been invoiced (timesheet_invoice_id != False). + +# Installation + +Use Odoo normal module installation procedure to install +`helpdesk_transfer_timesheets_to_task`. + +# Known issues / Roadmap + +None. + +# Bug Tracker + +Bugs are tracked on `our issues website `\_. In case of +trouble, please check there if your issue has already been +reported. If you spotted it first, help us smashing it by providing a +detailed and welcomed feedback. + +# Credits + +## Contributors + +- Quentin Mondot + +## Funders + +The development of this module has been financially supported by: + +- Elabore (https://elabore.coop) + +## Maintainer + +This module is maintained by Elabore. diff --git a/helpdesk_transfer_timesheet_to_task/__init__.py b/helpdesk_transfer_timesheet_to_task/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/helpdesk_transfer_timesheet_to_task/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/helpdesk_transfer_timesheet_to_task/__manifest__.py b/helpdesk_transfer_timesheet_to_task/__manifest__.py new file mode 100644 index 0000000..ad500d2 --- /dev/null +++ b/helpdesk_transfer_timesheet_to_task/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2024 Quentin Mondot +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "helpdesk_transfer_timesheet_to_task", + "version": "16.0.1.0.0", + "author": "Elabore", + "website": "https://elabore.coop", + "maintainer": "Quentin Mondot", + "license": "AGPL-3", + "category": "Tools", + "summary": "This module assigns timesheets to the task linked to a ticket.", + "depends": [ + "base", + "helpdesk_mgmt", + "helpdesk_mgmt_project", + "helpdesk_mgmt_timesheet", + "sale_timesheet" + ], + "data": [], + "installable": True, + "auto_install": False, + "application": False, +} diff --git a/helpdesk_transfer_timesheet_to_task/models/__init__.py b/helpdesk_transfer_timesheet_to_task/models/__init__.py new file mode 100644 index 0000000..8e082e7 --- /dev/null +++ b/helpdesk_transfer_timesheet_to_task/models/__init__.py @@ -0,0 +1 @@ +from . import helpdesk_ticket diff --git a/helpdesk_transfer_timesheet_to_task/models/helpdesk_ticket.py b/helpdesk_transfer_timesheet_to_task/models/helpdesk_ticket.py new file mode 100644 index 0000000..42c49ea --- /dev/null +++ b/helpdesk_transfer_timesheet_to_task/models/helpdesk_ticket.py @@ -0,0 +1,12 @@ +from odoo import models, api, Command + + +class HelpdeskTicket(models.Model): + _inherit = "helpdesk.ticket" + + @api.onchange("task_id") + def _onchange_task_id(self): + for record in self: + if record.timesheet_ids and record.task_id: + not_yet_invoiced_timesheet_ids = [t.id for t in record.timesheet_ids if not t.timesheet_invoice_id] + record.task_id.timesheet_ids = [Command.link(t_id) for t_id in not_yet_invoiced_timesheet_ids] diff --git a/helpdesk_transfer_timesheet_to_task/tests/__init__.py b/helpdesk_transfer_timesheet_to_task/tests/__init__.py new file mode 100644 index 0000000..5b42001 --- /dev/null +++ b/helpdesk_transfer_timesheet_to_task/tests/__init__.py @@ -0,0 +1 @@ +from . import test_helpdesk_ticket diff --git a/helpdesk_transfer_timesheet_to_task/tests/test_helpdesk_ticket.py b/helpdesk_transfer_timesheet_to_task/tests/test_helpdesk_ticket.py new file mode 100644 index 0000000..102c0ea --- /dev/null +++ b/helpdesk_transfer_timesheet_to_task/tests/test_helpdesk_ticket.py @@ -0,0 +1,181 @@ +from odoo.tests.common import TransactionCase + + +class TestHelpdeskTicket(TransactionCase): + + def setUp(self): + super(TestHelpdeskTicket, self).setUp() + self.HelpdeskTicket = self.env["helpdesk.ticket"] + self.ProjectTask = self.env["project.task"] + self.AccountAnalyticLine = self.env["account.analytic.line"] + self.AccountMove = self.env["account.move"] + self.Project = self.env["project.project"] + + self.project = self.Project.create( + {"name": "Test project", "allow_timesheets": True} + ) + self.analytic_account = self.project.analytic_account_id + # Create a sample task + self.task = self.ProjectTask.create( + {"name": "Sample Task", "project_id": self.project.id} + ) + + def test_timesheet_added_to_linked_task(self): + # Create a ticket + ticket = self.HelpdeskTicket.create( + {"name": "Test Ticket", "description": "My ticket"} + ) + + # Associate a task with the ticket + ticket.task_id = self.task.id + + # Log time on the ticket + timesheet = self.AccountAnalyticLine.create( + { + "name": "Time Entry", + "ticket_id": ticket.id, + "unit_amount": 1.0, + "account_id": self.analytic_account.id, + } + ) + + timesheet.onchange_ticket_id() + + # Check that timesheet is linked to the task + self.assertIn(timesheet, self.task.timesheet_ids) + + def test_timesheet_added_to_new_task(self): + # Create a ticket + ticket = self.HelpdeskTicket.create( + {"name": "Test Ticket", "description": "My ticket"} + ) + + # Log time on the ticket + timesheet = self.AccountAnalyticLine.create( + { + "name": "Time Entry", + "ticket_id": ticket.id, + "unit_amount": 1.0, + "account_id": self.analytic_account.id, + } + ) + + # Associate a task with the ticket + ticket.task_id = self.task.id + ticket._onchange_task_id() + + # Check that timesheet is linked to the task + self.assertIn(timesheet, self.task.timesheet_ids) + + def test_timesheet_for_task_linked_to_several_tickets(self): + # Create a first ticket + ticket_1 = self.HelpdeskTicket.create( + {"name": "Test Ticket 1", "description": "My 1st ticket"} + ) + + # Log time on the first ticket + first_timesheet = self.AccountAnalyticLine.create( + { + "name": "First Time Entry", + "ticket_id": ticket_1.id, + "unit_amount": 1.0, + "account_id": self.analytic_account.id, + } + ) + # Associate a task to the first ticket + ticket_1.task_id = self.task.id + ticket_1._onchange_task_id() + # Create a second ticket + ticket_2 = self.HelpdeskTicket.create( + {"name": "Test Ticket 2", "description": "My 2nd ticket"} + ) + # Log time on the second ticket + second_timesheet = self.AccountAnalyticLine.create( + { + "name": "Second Time Entry", + "ticket_id": ticket_2.id, + "unit_amount": 2.0, + "account_id": self.analytic_account.id, + } + ) + # Associate the same task to the second ticket + ticket_2.task_id = self.task.id + ticket_2._onchange_task_id() + + # Check that both timesheets are linked to the task + self.assertIn(first_timesheet, self.task.timesheet_ids) + self.assertIn(second_timesheet, self.task.timesheet_ids) + + def test_timesheet_moved_between_tasks(self): + # Create a second task + task_2 = self.ProjectTask.create( + {"name": "Second Task", "project_id": self.project.id} + ) + + # Create a ticket + ticket = self.HelpdeskTicket.create( + {"name": "Test Ticket", "description": "My ticket"} + ) + + # Log time on the ticket + timesheet = self.AccountAnalyticLine.create( + { + "name": "Time Entry for Moving", + "ticket_id": ticket.id, + "unit_amount": 1.0, + "account_id": self.analytic_account.id, + } + ) + + # Associate the first task with the ticket + ticket.task_id = self.task.id + ticket._onchange_task_id() + + # Associate the second task with the ticket + ticket.task_id = task_2.id + ticket._onchange_task_id() + + # Check if timesheet is moved to the second task + self.assertNotIn(timesheet, self.task.timesheet_ids) + self.assertIn(timesheet, task_2.timesheet_ids) + + def test_timesheet_already_invoiced_are_not_moved(self): + journal = self.env["account.journal"].create( + {"name": "My journal", "code": "test", "type": "bank"} + ) + account_move = self.AccountMove.create( + {"move_type": "entry", "journal_id": journal.id} + ) + # Create a ticket + ticket = self.HelpdeskTicket.create( + {"name": "Test Ticket", "description": "My ticket"} + ) + + # Log already invoiced timesheet on the ticket + already_invoiced_timesheet = self.AccountAnalyticLine.create( + { + "name": "Already Invoiced Time Entry", + "ticket_id": ticket.id, + "unit_amount": 1.0, + "account_id": self.analytic_account.id, + "timesheet_invoice_id": account_move.id, + } + ) + + # Log not invoiced timesheet on the ticket + not_invoiced_timesheet = self.AccountAnalyticLine.create( + { + "name": "Not Invoiced Time Entry", + "ticket_id": ticket.id, + "unit_amount": 2.0, + "account_id": self.analytic_account.id, + } + ) + + # Associate a task with the ticket + ticket.task_id = self.task.id + ticket._onchange_task_id() + + # Check already invoiced timesheet has not been moved + self.assertNotIn(already_invoiced_timesheet, self.task.timesheet_ids) + self.assertIn(not_invoiced_timesheet, self.task.timesheet_ids) -- 2.49.1 From fcff55635245e8be8799fc01c94c1bfcdf440dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phan=20Sainl=C3=A9ger?= Date: Wed, 21 May 2025 16:53:54 +0200 Subject: [PATCH 2/3] [ADD] helpdesk_user_default_ticket_team: create add-on --- helpdesk_user_default_ticket_team/.gitignore | 2 + helpdesk_user_default_ticket_team/README.rst | 46 +++++++++++++++++++ helpdesk_user_default_ticket_team/__init__.py | 3 ++ .../__manifest__.py | 35 ++++++++++++++ helpdesk_user_default_ticket_team/i18n/README | 1 + helpdesk_user_default_ticket_team/i18n/fr.po | 31 +++++++++++++ .../helpdesk_user_default_ticket_team.pot | 31 +++++++++++++ .../models/__init__.py | 2 + .../models/helpdesk_ticket.py | 19 ++++++++ .../models/res_users.py | 6 +++ .../views/res_users_views.xml | 15 ++++++ 11 files changed, 191 insertions(+) create mode 100644 helpdesk_user_default_ticket_team/.gitignore create mode 100644 helpdesk_user_default_ticket_team/README.rst create mode 100644 helpdesk_user_default_ticket_team/__init__.py create mode 100644 helpdesk_user_default_ticket_team/__manifest__.py create mode 100644 helpdesk_user_default_ticket_team/i18n/README create mode 100644 helpdesk_user_default_ticket_team/i18n/fr.po create mode 100644 helpdesk_user_default_ticket_team/i18n/helpdesk_user_default_ticket_team.pot create mode 100644 helpdesk_user_default_ticket_team/models/__init__.py create mode 100644 helpdesk_user_default_ticket_team/models/helpdesk_ticket.py create mode 100644 helpdesk_user_default_ticket_team/models/res_users.py create mode 100644 helpdesk_user_default_ticket_team/views/res_users_views.xml diff --git a/helpdesk_user_default_ticket_team/.gitignore b/helpdesk_user_default_ticket_team/.gitignore new file mode 100644 index 0000000..6da5887 --- /dev/null +++ b/helpdesk_user_default_ticket_team/.gitignore @@ -0,0 +1,2 @@ +*.*~ +*pyc diff --git a/helpdesk_user_default_ticket_team/README.rst b/helpdesk_user_default_ticket_team/README.rst new file mode 100644 index 0000000..dbfe8df --- /dev/null +++ b/helpdesk_user_default_ticket_team/README.rst @@ -0,0 +1,46 @@ +================================= +helpdesk_user_default_ticket_team +================================= + +Automate ticket team attribution when ticket created by portal user. +- configure default team on portal user form view +- assign automatically the new tickets to the team configured in the user's profile. + +Installation +============ + +Use Odoo normal module installation procedure to install +``helpdesk_user_default_ticket_team``. + +Known issues / Roadmap +====================== + +None yet. + +Bug Tracker +=========== + +Bugs are tracked on `our issues website `_. In case of +trouble, please check there if your issue has already been +reported. If you spotted it first, help us smashing it by providing a +detailed and welcomed feedback. + +Credits +======= + +Contributors +------------ + +* Stéphan Sainléger + +Funders +------- + +The development of this module has been financially supported by: +* Elabore (https://elabore.coop) + + +Maintainer +---------- + +This module is maintained by Elabore. diff --git a/helpdesk_user_default_ticket_team/__init__.py b/helpdesk_user_default_ticket_team/__init__.py new file mode 100644 index 0000000..cde864b --- /dev/null +++ b/helpdesk_user_default_ticket_team/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import models diff --git a/helpdesk_user_default_ticket_team/__manifest__.py b/helpdesk_user_default_ticket_team/__manifest__.py new file mode 100644 index 0000000..02895c0 --- /dev/null +++ b/helpdesk_user_default_ticket_team/__manifest__.py @@ -0,0 +1,35 @@ +# Copyright 2022 Stéphan Sainléger (Elabore) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "helpdesk_user_default_ticket_team", + "version": "16.0.1.0.0", + "author": "Elabore", + "website": "https://elabore.coop", + "maintainer": "Stéphan Sainléger", + "license": "AGPL-3", + "category": "Tools", + "summary": "Automate ticket team attribution when ticket created by portal user.", + # any module necessary for this one to work correctly + "depends": [ + "base", + "helpdesk_mgmt", + ], + "qweb": [], + "external_dependencies": { + "python": [], + }, + # always loaded + "data": [ + "views/res_users_views.xml", + ], + # only loaded in demonstration mode + "demo": [], + "js": [], + "css": [], + "installable": True, + # Install this module automatically if all dependency have been previously + # and independently installed. Used for synergetic or glue modules. + "auto_install": False, + "application": False, +} diff --git a/helpdesk_user_default_ticket_team/i18n/README b/helpdesk_user_default_ticket_team/i18n/README new file mode 100644 index 0000000..62197a1 --- /dev/null +++ b/helpdesk_user_default_ticket_team/i18n/README @@ -0,0 +1 @@ +This directory should contain the *.po for Odoo translation. diff --git a/helpdesk_user_default_ticket_team/i18n/fr.po b/helpdesk_user_default_ticket_team/i18n/fr.po new file mode 100644 index 0000000..32f1213 --- /dev/null +++ b/helpdesk_user_default_ticket_team/i18n/fr.po @@ -0,0 +1,31 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * helpdesk_user_default_ticket_team +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-05-21 14:37+0000\n" +"PO-Revision-Date: 2025-05-21 16:39+0200\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: helpdesk_user_default_ticket_team +#: model:ir.model.fields,field_description:helpdesk_user_default_ticket_team.field_res_users__default_helpdesk_ticket_team_id +msgid "Default Helpdesk Team" +msgstr "Équipe d'assistance par défaut" + +#. module: helpdesk_user_default_ticket_team +#: model:ir.model,name:helpdesk_user_default_ticket_team.model_helpdesk_ticket +msgid "Helpdesk Ticket" +msgstr "Ticket d'assistance" + +#. module: helpdesk_user_default_ticket_team +#: model:ir.model,name:helpdesk_user_default_ticket_team.model_res_users +msgid "User" +msgstr "Utilisateur" diff --git a/helpdesk_user_default_ticket_team/i18n/helpdesk_user_default_ticket_team.pot b/helpdesk_user_default_ticket_team/i18n/helpdesk_user_default_ticket_team.pot new file mode 100644 index 0000000..64425cb --- /dev/null +++ b/helpdesk_user_default_ticket_team/i18n/helpdesk_user_default_ticket_team.pot @@ -0,0 +1,31 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * helpdesk_user_default_ticket_team +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-05-21 14:36+0000\n" +"PO-Revision-Date: 2025-05-21 14:36+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: helpdesk_user_default_ticket_team +#: model:ir.model.fields,field_description:helpdesk_user_default_ticket_team.field_res_users__default_helpdesk_ticket_team_id +msgid "Default Helpdesk Team" +msgstr "" + +#. module: helpdesk_user_default_ticket_team +#: model:ir.model,name:helpdesk_user_default_ticket_team.model_helpdesk_ticket +msgid "Helpdesk Ticket" +msgstr "" + +#. module: helpdesk_user_default_ticket_team +#: model:ir.model,name:helpdesk_user_default_ticket_team.model_res_users +msgid "User" +msgstr "" diff --git a/helpdesk_user_default_ticket_team/models/__init__.py b/helpdesk_user_default_ticket_team/models/__init__.py new file mode 100644 index 0000000..cc61958 --- /dev/null +++ b/helpdesk_user_default_ticket_team/models/__init__.py @@ -0,0 +1,2 @@ +from . import res_users +from . import helpdesk_ticket diff --git a/helpdesk_user_default_ticket_team/models/helpdesk_ticket.py b/helpdesk_user_default_ticket_team/models/helpdesk_ticket.py new file mode 100644 index 0000000..b3fd22e --- /dev/null +++ b/helpdesk_user_default_ticket_team/models/helpdesk_ticket.py @@ -0,0 +1,19 @@ +from odoo import api, fields, models, tools + +class HelpdeskTicket(models.Model): + _inherit = "helpdesk.ticket" + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + if not vals.get("team_id") and vals.get("partner_id"): + partner = self.env["res.partner"].browse(vals.get("partner_id")) + if not partner: + continue + user = self.env["res.users"].browse(partner.user_ids[0].id) + if not user: + continue + if user.default_helpdesk_ticket_team_id: + vals["team_id"] = user.default_helpdesk_ticket_team_id.id + + return super().create(vals_list) diff --git a/helpdesk_user_default_ticket_team/models/res_users.py b/helpdesk_user_default_ticket_team/models/res_users.py new file mode 100644 index 0000000..6429e28 --- /dev/null +++ b/helpdesk_user_default_ticket_team/models/res_users.py @@ -0,0 +1,6 @@ +from odoo import _, api, fields, models + +class Users(models.Model): + _inherit = "res.users" + + default_helpdesk_ticket_team_id = fields.Many2one('helpdesk.ticket.team', string='Default Helpdesk Team') diff --git a/helpdesk_user_default_ticket_team/views/res_users_views.xml b/helpdesk_user_default_ticket_team/views/res_users_views.xml new file mode 100644 index 0000000..85af16d --- /dev/null +++ b/helpdesk_user_default_ticket_team/views/res_users_views.xml @@ -0,0 +1,15 @@ + + + + view.users.form.inherit.default.helpdesk.team + res.users + + + + + + + + + + -- 2.49.1 From 1273441f27a681d0f6126d3d9286a9c31f24fa21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phan=20Sainl=C3=A9ger?= Date: Mon, 2 Jun 2025 17:50:05 +0200 Subject: [PATCH 3/3] [IMP] helpdesk_user_default_ticket_team: fill project field if defined in the helpdesk team --- helpdesk_user_default_ticket_team/__manifest__.py | 3 ++- .../models/helpdesk_ticket.py | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/helpdesk_user_default_ticket_team/__manifest__.py b/helpdesk_user_default_ticket_team/__manifest__.py index 02895c0..d6d92db 100644 --- a/helpdesk_user_default_ticket_team/__manifest__.py +++ b/helpdesk_user_default_ticket_team/__manifest__.py @@ -3,7 +3,7 @@ { "name": "helpdesk_user_default_ticket_team", - "version": "16.0.1.0.0", + "version": "16.0.1.1.0", "author": "Elabore", "website": "https://elabore.coop", "maintainer": "Stéphan Sainléger", @@ -14,6 +14,7 @@ "depends": [ "base", "helpdesk_mgmt", + "helpdesk_mgmt_project", ], "qweb": [], "external_dependencies": { diff --git a/helpdesk_user_default_ticket_team/models/helpdesk_ticket.py b/helpdesk_user_default_ticket_team/models/helpdesk_ticket.py index b3fd22e..81a380d 100644 --- a/helpdesk_user_default_ticket_team/models/helpdesk_ticket.py +++ b/helpdesk_user_default_ticket_team/models/helpdesk_ticket.py @@ -7,13 +7,23 @@ class HelpdeskTicket(models.Model): def create(self, vals_list): for vals in vals_list: if not vals.get("team_id") and vals.get("partner_id"): + # Find the user who creates the ticket partner = self.env["res.partner"].browse(vals.get("partner_id")) if not partner: continue user = self.env["res.users"].browse(partner.user_ids[0].id) if not user: continue - if user.default_helpdesk_ticket_team_id: - vals["team_id"] = user.default_helpdesk_ticket_team_id.id + + # Get its default team_id + team = user.default_helpdesk_ticket_team_id + if not team: + continue + + vals["team_id"] = team.id + + # Set the linked project + if team.default_project_id: + vals["project_id"] = team.default_project_id.id return super().create(vals_list) -- 2.49.1