-
Notifications
You must be signed in to change notification settings - Fork 169
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ADD] new module project_task_report
- Loading branch information
Clément Mombereau
committed
Dec 9, 2024
1 parent
5af6c89
commit c88e7b0
Showing
10 changed files
with
348 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
=================== | ||
Project Task Report | ||
=================== | ||
|
||
Task wizard to sum up the progress of some projects tasks between two dates | ||
|
||
Usage | ||
===== | ||
|
||
To use this module, you need to: | ||
|
||
#. Go to Project > Reporting > Tasks Report | ||
#. Select the related Projects and the Start and End dates of the report | ||
#. The resulting report is a list of the Tasks with new timesheets and/or with stage change between the two selected dates | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import wizards |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Copyright 2024 Akretion | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). | ||
|
||
{ | ||
"name": "Project Task Report", | ||
"summary": """Task wizard to sum up the progress between two dates""", | ||
"version": "16.0.1.0.0", | ||
"license": "AGPL-3", | ||
"author": "Akretion", | ||
"website": "http://akretion.com", | ||
"depends": ["project", "hr_timesheet"], | ||
"data": [ | ||
"security/ir.model.access.csv", | ||
"wizards/project_task_report.xml", | ||
], | ||
"demo": ["data/project_task_report_demo.xml"], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<odoo> | ||
<data> | ||
<!-- Tasks --> | ||
<record id="task_1" model="project.task"> | ||
<field name="project_id" ref="project.project_project_1"/> | ||
<field name="name">Task 1</field> | ||
</record> | ||
<record id="task_2" model="project.task"> | ||
<field name="project_id" ref="project.project_project_1"/> | ||
<field name="name">Task 2</field> | ||
<field name="create_date" eval="datetime(2023, 12, 1)"/> | ||
</record> | ||
|
||
<!-- Timesheets --> | ||
<record id="timesheet_11" model="account.analytic.line"> | ||
<field name="name">Timesheet 1.1</field> | ||
<field name="employee_id" ref="hr.employee_qdp"/> | ||
<field name="date" eval="datetime(2024, 3, 2)"/> | ||
<field name="task_id" ref="task_1"/> | ||
<field name="unit_amount">1.00</field> | ||
</record> | ||
<record id="timesheet_21" model="account.analytic.line"> | ||
<field name="name">Timesheet 2.1</field> | ||
<field name="employee_id" ref="hr.employee_qdp"/> | ||
<field name="date" eval="datetime(2024, 2, 1)"/> | ||
<field name="task_id" ref="task_2"/> | ||
</record> | ||
<record id="timesheet_22" model="account.analytic.line"> | ||
<field name="name">Timesheet 2.2</field> | ||
<field name="employee_id" ref="hr.employee_qdp"/> | ||
<field name="date" eval="datetime(2024, 3, 1)"/> | ||
<field name="task_id" ref="task_2"/> | ||
<field name="unit_amount">10.00</field> | ||
</record> | ||
<record id="timesheet_23" model="account.analytic.line"> | ||
<field name="name">Timesheet 2.3</field> | ||
<field name="employee_id" ref="hr.employee_qdp"/> | ||
<field name="date" eval="datetime(2024, 3, 2)"/> | ||
<field name="task_id" ref="task_2"/> | ||
<field name="unit_amount">2.00</field> | ||
</record> | ||
|
||
<!-- Historic stage changes on Task 2 --> | ||
<record id="task_2_mail_message_1" model="mail.message"> | ||
<field name="model">project.task</field> | ||
<field name="res_id" ref="task_2"/> | ||
<field name="message_type">notification</field> | ||
<field name="date" eval="datetime(2024, 1, 1)"/> | ||
<field name="subtype_id" ref="project.mt_task_stage"/> | ||
<field name="author_id" ref="base.partner_admin"/> | ||
</record> | ||
<record id="task_2_mail_message_1_track" model="mail.tracking.value"> | ||
<field name="field" model="ir.model.fields" eval="obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])"/> | ||
<field name="field_desc">Stage</field> | ||
<field name="old_value_char">New</field> | ||
<field name="new_value_char">In Progress</field> | ||
<field name="field_type">many2one</field> | ||
<field name="old_value_integer">1</field> | ||
<field name="new_value_integer">2</field> | ||
<field name="mail_message_id" ref="task_2_mail_message_1"/> | ||
</record> | ||
|
||
<record id="task_2_mail_message_2" model="mail.message"> | ||
<field name="model">project.task</field> | ||
<field name="res_id" ref="task_2"/> | ||
<field name="message_type">notification</field> | ||
<field name="date" eval="datetime(2024, 4, 1)"/> | ||
<field name="subtype_id" ref="project.mt_task_stage"/> | ||
<field name="author_id" ref="base.partner_admin"/> | ||
</record> | ||
<record id="task_2_mail_message_2_track" model="mail.tracking.value"> | ||
<field name="field" model="ir.model.fields" eval="obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])"/> | ||
<field name="field_desc">Stage</field> | ||
<field name="old_value_char">In Progress</field> | ||
<field name="new_value_char">Done</field> | ||
<field name="field_type">many2one</field> | ||
<field name="old_value_integer">2</field> | ||
<field name="new_value_integer">3</field> | ||
<field name="mail_message_id" ref="task_2_mail_message_2"/> | ||
</record> | ||
|
||
<record id="task_2_mail_message_3" model="mail.message"> | ||
<field name="model">project.task</field> | ||
<field name="res_id" ref="task_2"/> | ||
<field name="message_type">notification</field> | ||
<field name="date" eval="datetime(2024, 5, 1)"/> | ||
<field name="subtype_id" ref="project.mt_task_stage"/> | ||
<field name="author_id" ref="base.partner_admin"/> | ||
</record> | ||
<record id="task_2_mail_message_3_track" model="mail.tracking.value"> | ||
<field name="field" model="ir.model.fields" eval="obj().search([('model', '=', 'project.task'), ('name', '=', 'stage_id')])"/> | ||
<field name="field_desc">Stage</field> | ||
<field name="old_value_char">Done</field> | ||
<field name="new_value_char">Canceled</field> | ||
<field name="field_type">many2one</field> | ||
<field name="old_value_integer">3</field> | ||
<field name="new_value_integer">4</field> | ||
<field name="mail_message_id" ref="task_2_mail_message_3"/> | ||
</record> | ||
|
||
|
||
</data> | ||
</odoo> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink | ||
project_task_report.access_project_task_report,access_project_task_report,project_task_report.model_project_task_report,base.group_user,1,1,1,1 | ||
project_task_report.access_project_task_report_line,access_project_task_report_line,project_task_report.model_project_task_report_line,base.group_user,1,1,1,1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import test_project_task_report |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# Copyright 2024 Akretion | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). | ||
|
||
from datetime import datetime | ||
from odoo.tests.common import TransactionCase | ||
|
||
|
||
class TestProjectTaskReport(TransactionCase): | ||
|
||
def setUp(self): | ||
super().setUp() | ||
self.task_report_id = self.env["project.task.report"].create( | ||
{ | ||
"start_date": datetime(2024, 2, 2), | ||
"end_date": datetime(2024, 6, 1), | ||
"project_ids": self.env.ref("project.project_project_1").ids, | ||
} | ||
) | ||
line_ids = self.task_report_id._create_line_ids() | ||
self.line_ids = line_ids.sorted(lambda l: l.task_id.name) | ||
|
||
self.stage_new = self.env.ref("project.project_stage_0") | ||
self.stage_in_progress = self.env.ref("project.project_stage_1") | ||
self.stage_canceled = self.env.ref("project.project_stage_3") | ||
|
||
def test_task_report_no_stage_change(self): | ||
task_line_id = self.line_ids[0] | ||
self.assertEqual(task_line_id.task_id.name, "Task 1") | ||
self.assertEqual(task_line_id.hours_spent, 1) | ||
self.assertEqual(task_line_id.start_stage_id, self.stage_new) | ||
self.assertEqual(task_line_id.end_stage_id, self.stage_new) | ||
|
||
def test_task_report_with_stage_change(self): | ||
task_line_id = self.line_ids[1] | ||
self.assertEqual(task_line_id.task_id.name, "Task 2") | ||
self.assertEqual(task_line_id.hours_spent, 12) | ||
self.assertEqual(task_line_id.start_stage_id, self.stage_in_progress) | ||
self.assertEqual(task_line_id.end_stage_id, self.stage_canceled) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import project_task_report |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# Copyright 2024 Akretion | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). | ||
|
||
from odoo import _, api, fields, models | ||
|
||
|
||
class ProjectTaskReport(models.TransientModel): | ||
_name = "project.task.report" | ||
_description = "Task Report" | ||
|
||
start_date = fields.Date() | ||
end_date = fields.Date() | ||
project_ids = fields.Many2many("project.project", string="Projects") | ||
|
||
def _create_line_ids(self): | ||
line_vals = [] | ||
task_ids = self.env["project.task"].search( | ||
[("project_id", "in", self.project_ids.ids)] | ||
) | ||
field_stage_id = self.env["ir.model.fields"].search( | ||
[("model", "=", "project.task"), ("name", "=", "stage_id")] | ||
) | ||
|
||
for task_id in task_ids: | ||
line_val = {"task_id": task_id.id, "timesheet_ids": []} | ||
|
||
# Catch the timesheets between the start and end dates | ||
for timesheet_id in task_id.timesheet_ids: | ||
if self.start_date < timesheet_id.date < self.end_date: | ||
line_val["timesheet_ids"].append(timesheet_id.id) | ||
|
||
# Catch the historic stage changes between the start and end dates | ||
track_ids = task_id.message_ids.tracking_value_ids.filtered( | ||
lambda t: t.field == field_stage_id | ||
and self.start_date < t.mail_message_id.date.date() < self.end_date | ||
).sorted(lambda t: t.mail_message_id.date) | ||
|
||
if track_ids: | ||
line_val.update( | ||
{ | ||
"start_stage_id": track_ids[0].old_value_integer, | ||
"end_stage_id": track_ids[-1].new_value_integer, | ||
} | ||
) | ||
|
||
if line_val["timesheet_ids"] or line_val.get("start_stage_id"): | ||
# Fill stage fields in case there have been timesheets without stage change | ||
if not line_val.get("start_stage_id"): | ||
line_val.update( | ||
{ | ||
"start_stage_id": task_id.stage_id.id, | ||
"end_stage_id": task_id.stage_id.id, | ||
} | ||
) | ||
line_vals.append(line_val) | ||
|
||
return self.env["project.task.report.line"].create(line_vals) | ||
|
||
def action_view_task_report(self): | ||
self.ensure_one() | ||
|
||
line_ids = self._create_line_ids() | ||
|
||
action_xml_id = "project_task_report.project_task_report_line_act_window" | ||
action = self.env["ir.actions.act_window"]._for_xml_id(action_xml_id) | ||
action.update({"domain": [("id", "in", line_ids.ids)]}) | ||
return action | ||
|
||
|
||
class ProjectTaskReportLine(models.TransientModel): | ||
_name = "project.task.report.line" | ||
_description = "Task Report Line" | ||
|
||
task_id = fields.Many2one("project.task", readonly=True) | ||
start_stage_id = fields.Many2one("project.task.type", readonly=True) | ||
end_stage_id = fields.Many2one("project.task.type", readonly=True) | ||
|
||
timesheet_ids = fields.Many2many("account.analytic.line", readonly=True) | ||
hours_spent = fields.Float(compute="_compute_hours_spent", store=True) | ||
|
||
@api.depends("timesheet_ids.unit_amount") | ||
def _compute_hours_spent(self): | ||
for rec in self: | ||
rec.hours_spent = sum(rec.timesheet_ids.mapped("unit_amount")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<!-- Copyright 2024 Akretion | ||
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> | ||
|
||
<odoo> | ||
|
||
<record id="project_task_report_form_view" model="ir.ui.view"> | ||
<field name="model">project.task.report</field> | ||
<field name="arch" type="xml"> | ||
<form string="Project Task Report"> | ||
<group> | ||
<field name="start_date"/> | ||
<field name="end_date"/> | ||
<field name="project_ids" widget="many2many_tags"/> | ||
</group> | ||
<footer> | ||
<button name="action_view_task_report" | ||
string="Create Task Report" | ||
class="btn-primary" | ||
type="object"/> | ||
<button string="Cancel" | ||
class="btn-default" | ||
special="cancel"/> | ||
</footer> | ||
</form> | ||
</field> | ||
</record> | ||
|
||
<record model="ir.actions.act_window" id="project_task_report_act_window"> | ||
<field name="name">Tasks Report</field> | ||
<field name="res_model">project.task.report</field> | ||
<field name="view_mode">form</field> | ||
<field name="target">new</field> | ||
</record> | ||
|
||
<record id="project_task_report_line_tree_view" model="ir.ui.view"> | ||
<field name="name">project.task.report.line.tree</field> | ||
<field name="model">project.task.report.line</field> | ||
<field name="arch" type="xml"> | ||
<tree string="Task Report" create="false" delete="false" edit="false"> | ||
<field name="task_id"/> | ||
<field name="start_stage_id"/> | ||
<field name="end_stage_id"/> | ||
<field name="hours_spent" widget="timesheet_uom"/> | ||
</tree> | ||
</field> | ||
</record> | ||
|
||
<record id="project_task_report_line_form_view" model="ir.ui.view"> | ||
<field name="model">project.task.report.line</field> | ||
<field name="arch" type="xml"> | ||
<form string="Project Task Report Line"> | ||
<sheet> | ||
<div class="oe_title"> | ||
<label for="task_id"/> | ||
<h1><field name="task_id"/></h1> | ||
</div> | ||
<group class="col-12 col-lg-6"> | ||
<field name="start_stage_id"/> | ||
<field name="end_stage_id"/> | ||
</group> | ||
<group class="col-12 col-lg-6"> | ||
<field name="timesheet_ids"/> | ||
</group> | ||
</sheet> | ||
</form> | ||
</field> | ||
</record> | ||
|
||
<record model="ir.actions.act_window" id="project_task_report_line_act_window"> | ||
<field name="name">Tasks Report</field> | ||
<field name="res_model">project.task.report.line</field> | ||
<field name="view_mode">tree,form</field> | ||
</record> | ||
|
||
<record model="ir.ui.menu" id="project_task_report_menu"> | ||
<field name="name">Tasks Report</field> | ||
<field name="parent_id" ref="project.menu_project_report"/> | ||
<field name="action" ref="project_task_report_act_window"/> | ||
<field name="sequence" eval="10"/> | ||
</record> | ||
</odoo> |