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)