Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

project_task_scheduling: Module for automatic task planning #419

Closed
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5578e2d
project_task_scheduling: New module for automated task scheduling
ernestotejeda Aug 3, 2018
4e5a21e
project_task_scheduling: Fix flake8 errors and fix unit test error
ernestotejeda Sep 19, 2018
07022bb
project_task_scheduling: Fix E231 flake8 error
ernestotejeda Sep 19, 2018
e83e1ce
project_task_scheduling: Use date_start and date_end for scheduling
ernestotejeda Sep 19, 2018
ba1d9f5
project_task_assignment: Update readme description
ernestotejeda Sep 20, 2018
fb3b5c7
project_task_assignment: Change module name to project_task_employee
ernestotejeda Sep 20, 2018
37ac1a5
project_task_schedule: Update dependence on project_task_employee
ernestotejeda Sep 21, 2018
02144ea
project_task_employee: Add domain to employee_id field
ernestotejeda Sep 20, 2018
560de9d
project_task_employee: Refactoring code
ernestotejeda Sep 20, 2018
e62beea
project_task_scheduling: Refactoring code
ernestotejeda Sep 22, 2018
27851be
project_task_scheduling: Improve scheduling for tasks with skill
ernestotejeda Sep 23, 2018
7841cb2
project_task_employee: Add help to fields
ernestotejeda Sep 23, 2018
129ed4e
project_task_scheduling: Add help to fields
ernestotejeda Sep 23, 2018
79045c2
project_task_scheduling: Add references to the readme
ernestotejeda Sep 23, 2018
7e637c3
project_task_scheduling: Improve USAGE section of the readme
ernestotejeda Sep 23, 2018
ab28bee
project_task_employee: Fix unite test error
ernestotejeda Sep 24, 2018
36bc972
project_task_employee: Refactoring code
ernestotejeda Sep 24, 2018
0ceb2d4
project_task_schedule: Fix the way to remove an interval from gaps
ernestotejeda Sep 25, 2018
7920b43
project_task_employee: Add unit test
ernestotejeda Sep 25, 2018
f7211b2
project_task_employee: Set task closed field store=True
ernestotejeda Sep 25, 2018
4101b4c
project_task_scheduling: Add states to proposals.
ernestotejeda Sep 25, 2018
445a350
project_task_scheduling: Change the name of a function
ernestotejeda Sep 25, 2018
05acf10
project_task_scheduling: Refactoring code
ernestotejeda Sep 25, 2018
2f74b5a
project_task_scheduling: Add copyright to files
ernestotejeda Sep 25, 2018
02b6f64
project_task_scheduling: Remove copyright line from __init__.py files
ernestotejeda Sep 26, 2018
de17f06
project_task_employee: Add copyright to files
ernestotejeda Sep 26, 2018
4ea656a
project_task_employee: Refactoring code
ernestotejeda Sep 27, 2018
cd71b48
project_task_scheduling: Refactoring code
ernestotejeda Sep 27, 2018
8b3619c
project_task_scheduling: Improve DESCRIPTION section of the readme
ernestotejeda Sep 27, 2018
608fdbe
project_task_scheduling: Remove the 'closed' field
ernestotejeda Sep 27, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 0 additions & 61 deletions project_task_assignment/models/project.py

This file was deleted.

6 changes: 0 additions & 6 deletions project_task_assignment/readme/DESCRIPTION.rst

This file was deleted.

File renamed without changes.
File renamed without changes.
@@ -1,6 +1,6 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Project task assigment",
"name": "Project task employee",
"version": "11.0.1.0.0",
"category": "Project Management",
"author": "Tecnativa, "
Expand All @@ -13,6 +13,7 @@
],
"data": [
"views/project_view.xml",
"views/project_task_view.xml",
],
"demo": [
"demo/project_task.xml",
Expand Down
@@ -1,2 +1,3 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import project
from . import project_task
11 changes: 11 additions & 0 deletions project_task_employee/models/project.py
@@ -0,0 +1,11 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models


class ProjectProject(models.Model):
_inherit = 'project.project'

employee_ids = fields.Many2many(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comodel_name='hr.employee',
string="Employees",
)
73 changes: 73 additions & 0 deletions project_task_employee/models/project_task.py
@@ -0,0 +1,73 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, fields, models


class Task(models.Model):
_inherit = 'project.task'

employee_domain_ids = fields.Many2many(
compute='_compute_employee_domain_ids',
comodel_name='hr.employee',
string="Employees",
)
employee_scheduling_ids = fields.Many2many(
compute='_compute_employee_scheduling_ids',
comodel_name='hr.employee',
string="Employees",
)
employee_id = fields.Many2one(
comodel_name='hr.employee',
string="Assigned to employee",
domain="[('id', 'in', employee_domain_ids)]",
help="If the task is not scheduled (without ending date), the "
"automated scheduling will only assign the task to the employee "
"selected here.",
)
employee_category_id = fields.Many2one(
comodel_name='hr.employee.category',
string="Employee category",
help="Only employee selected on the project belonging to the task "
"that have the categories selected here can do the task.",
)
scheduled = fields.Boolean(
compute='_compute_scheduled',
readonly=True,
store=True,
)

@api.multi
@api.depends('project_id.employee_ids.category_ids',
'employee_category_id')
def _compute_employee_domain_ids(self):
for record in self:
emp = record.mapped('project_id.employee_ids')
task_skill = record.employee_category_id
if task_skill:
emp = emp.filtered(lambda r: task_skill in r.category_ids)
record.employee_domain_ids = emp.ids

@api.multi
@api.depends('project_id.employee_ids.category_ids',
'employee_category_id')
def _compute_employee_scheduling_ids(self):
for record in self:
employees = record.employee_id
if record.date_end or not record.employee_id:
employees = record.employee_domain_ids
record.employee_scheduling_ids = employees

@api.multi
@api.depends('employee_id', 'date_start', 'date_end')
def _compute_scheduled(self):
for record in self:
record.scheduled = record.date_end

@api.onchange('employee_domain_ids')
def _onchange_employee_domain_ids(self):
if self.employee_id not in self.employee_domain_ids:
self.employee_id = False

@api.onchange('employee_id')
def _onchange_employee_id(self):
if self.user_id != self.employee_id.user_id:
self.user_id = self.employee_id.user_id
3 changes: 3 additions & 0 deletions project_task_employee/readme/DESCRIPTION.rst
@@ -0,0 +1,3 @@
This module extends the functionality of project to allow you to add employees
to a project, to specify required skills (Employee category) to a task and to
add an employee to a task.
Expand Up @@ -7,22 +7,22 @@ def setUp(self):
super(TestProjectTask, self).setUp()

self.task_3 = self.env['project.task'].browse(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ernestotejeda IMO Add one variable and use this obj self.env['project.task'].

Ex: self.project_task_obj = self.env['project.task']

self.ref("project_task_assignment.restricted_task_3"))
self.ref("project_task_employee.restricted_task_3"))
self.task_2 = self.env['project.task'].browse(
self.ref("project_task_assignment.restricted_task_2"))
self.ref("project_task_employee.restricted_task_2"))
self.task_7 = self.env['project.task'].browse(
self.ref("project_task_assignment.restricted_task_7"))
self.ref("project_task_employee.restricted_task_7"))
self.task_1 = self.env['project.task'].browse(
self.ref("project_task_assignment.restricted_task_1"))
self.ref("project_task_employee.restricted_task_1"))

def test_get_employees(self):
def test_employee_domain_ids(self):
jth = self.env['hr.employee'].browse(self.ref("hr.employee_jth"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ernestotejeda same as here

root = self.env['hr.employee'].browse(self.ref("hr.employee_root"))

self.assertEqual(self.task_3.employee_ids, jth + root)
self.assertEqual(self.task_2.employee_ids, jth + root)
self.assertEqual(self.task_7.employee_ids, root)
self.assertEqual(self.task_1.employee_ids, jth)
self.assertEqual(self.task_3.employee_domain_ids, jth + root)
self.assertEqual(self.task_2.employee_domain_ids, jth + root)
self.assertEqual(self.task_7.employee_domain_ids, root)
self.assertEqual(self.task_1.employee_domain_ids, jth)

def test_onchange_project_id_employee_id(self):
onchange_result = self.task_3._onchange_project_id_employee_id()
Expand Down
Expand Up @@ -2,21 +2,6 @@
<!--License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).-->
<odoo>

<record id="edit_project" model="ir.ui.view">
<field name="name">project.project.form.inherit.scheduling</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.edit_project"/>
<field name="arch" type="xml">
<notebook>
<page string="Employees">
<group>
<field name="employee_ids" nolabel="1"/>
</group>
</page>
</notebook>
</field>
</record>

<record id="view_task_form2_inherit_schedule" model="ir.ui.view">
<field name="name">project.task.form.inherit.schedule</field>
<field name="model">project.task</field>
Expand All @@ -26,6 +11,7 @@
<attribute name="string">Assigned to user</attribute>
</field>
<field name="user_id" position="after">
<field name="employee_domain_ids" invisible="1"/>
<field name="employee_id"/>
</field>
<field name="tag_ids" position="after">
Expand Down
20 changes: 20 additions & 0 deletions project_task_employee/views/project_view.xml
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).-->
<odoo>

<record id="edit_project" model="ir.ui.view">
<field name="name">project.project.form.inherit.scheduling</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.edit_project"/>
<field name="arch" type="xml">
<notebook>
<page string="Employees">
<group>
<field name="employee_ids" nolabel="1"/>
</group>
</page>
</notebook>
</field>
</record>

</odoo>
2 changes: 1 addition & 1 deletion project_task_scheduling/__manifest__.py
Expand Up @@ -11,7 +11,7 @@
"hr",
"hr_timesheet",
"project",
"project_task_assignment",
"project_task_employee",
"project_task_dependency",
"project_timeline",
"project_stage_closed",
Expand Down
2 changes: 1 addition & 1 deletion project_task_scheduling/models/project_task_scheduling.py
Expand Up @@ -48,7 +48,7 @@ def action_set_assignation(self):
for record in self:
record.task_id.write({
'employee_id': record.employee_id.id,
'user_id': record.employee_id.user_id.id or self.env.user.id,
'user_id': record.employee_id.user_id.id,
'date_start': record.datetime_start,
'date_end': record.datetime_stop,
})
4 changes: 4 additions & 0 deletions project_task_scheduling/readme/DESCRIPTION.rst
@@ -1 +1,5 @@
This module allow you to execute an automated project task scheduling.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you improve the description and add a use case?


References:

* Demeulemeester, E., Herroelen W./ PROJECT SCHEDULING A Research Handbook
9 changes: 6 additions & 3 deletions project_task_scheduling/readme/USAGE.rst
@@ -1,6 +1,9 @@
To use this module, you need to:

#. Go to Project / Automation / Project task scheduler
#. Set the parameters to compute the proposals of scheduling. List of task to be scheduled, The date to start the scheduling and The cooling ratio
#. Create employees with skills (Categories) and set working hours (calendar).
#. Create Project and choose employees belongs to the project.
#. Add Tasks to the project. For each task, set deadline, employee category required by task, Initially planned hours, and dependencies on other tasks. If you set starting date but not ending date, starting date is considered as the earliest date-time required for the assignation during automatic scheduling of the task. If you set employee but not ending date, it will be the only employee required for the assignation during automatic scheduling of the task.
#. Go to Project > Automation > Project task scheduler
#. Choose a task option (Not finished, Not Scheduled, Customized), a Date Start and Calculation Speed (Slow, Middle, Fast)
#. Click on Accept button
#. You will see a list of proposals of scheduling. For each proposal you can see a time line view of that or click on one item to see all data of the scheduling in a form view or set the scheduling as definitive
#. You will see a list of proposals of scheduling. For each proposal you can see a time line view for all task grouped by employee. You can also click on one item to see all data of the scheduling in a form view and set it as a definitive scheduling.
12 changes: 5 additions & 7 deletions project_task_scheduling/tests/common.py
Expand Up @@ -8,7 +8,7 @@ def setUp(self):
super(TestSchedulingCommon, self).setUp()

self.restricted_project = self.env['project.project'].browse(
self.ref("project_task_assignment.restricted_project"))
self.ref("project_task_employee.restricted_project"))

employee_obj = self.env['hr.employee']
# these are all employees of the self.restricted_project
Expand All @@ -19,13 +19,13 @@ def setUp(self):

# these are all tasks of the self.restricted_project
self.task_2 = self.env['project.task'].browse(
self.ref("project_task_assignment.restricted_task_2"))
self.ref("project_task_employee.restricted_task_2"))
self.task_3 = self.env['project.task'].browse(
self.ref("project_task_assignment.restricted_task_3"))
self.ref("project_task_employee.restricted_task_3"))
self.task_7 = self.env['project.task'].browse(
self.ref("project_task_assignment.restricted_task_7"))
self.ref("project_task_employee.restricted_task_7"))
self.task_1 = self.env['project.task'].browse(
self.ref("project_task_assignment.restricted_task_1"))
self.ref("project_task_employee.restricted_task_1"))
# set task dependency
self.task_1.dependency_task_ids = [(6, 0, [self.task_7.id])]

Expand All @@ -50,5 +50,3 @@ def setUp(self):
# set tz 'UTC'
self.root_emp.resource_id.user_id.tz = 'UTC'
self.env.user.tz = 'UTC'

self.wizard.init_accum_inter()