[IMP] project,hr(_timesheet)_holidays: usability improvement for project

Purpose of the commit is to improve the usability and onboarding
of the project app.

So in this commit, done the below changes:
 - Rename column "Kanban state" in list view screens
 - Hide column "analytic account" when not needed
 - Do not show "Internal" project

PS: Moved leave_timesheet_project_id into hr_timesheet and rename
it as internal_project_id and we used it in the domain in the
kanban/action to the kanban in order not to have it in the result.

closes odoo/odoo#68857

Taskid: 2451722
Related: odoo/upgrade#2479
Signed-off-by: LTU-Odoo <IT-Ideas@users.noreply.github.com>
This commit is contained in:
Barad Mahendra
2021-06-24 05:18:14 +00:00
parent f6ab481ad9
commit 27538bca03
11 changed files with 82 additions and 51 deletions
+24
View File
@@ -27,6 +27,7 @@ class Project(models.Model):
compute='_compute_total_timesheet_time',
help="Total number of time (in the proper UoM) recorded in the project, rounded to the unit.")
encode_uom_in_days = fields.Boolean(compute='_compute_encode_uom_in_days')
is_internal_project = fields.Boolean(compute='_compute_is_internal_project', search='_search_is_internal_project')
def _compute_encode_uom_in_days(self):
self.encode_uom_in_days = self.env.company.timesheet_encode_uom_id == self.env.ref('uom.product_uom_day')
@@ -36,6 +37,29 @@ class Project(models.Model):
without_account = self.filtered(lambda t: not t.analytic_account_id and t._origin)
without_account.update({'allow_timesheets': False})
@api.depends('company_id')
def _compute_is_internal_project(self):
for project in self:
project.is_internal_project = bool(project.company_id.internal_project_id)
@api.model
def _search_is_internal_project(self, operator, value):
if not isinstance(value, bool):
raise ValueError('Invalid value: %s' % (value))
if operator not in ['=', '!=']:
raise ValueError('Invalid operator: %s' % (operator))
query = """
SELECT C.internal_project_id
FROM res_company C
WHERE C.internal_project_id IS NOT NULL
"""
if (operator == '=' and value is True) or (operator == '!=' and value is False):
operator_new = 'inselect'
else:
operator_new = 'not inselect'
return [('id', operator_new, (query, ()))]
@api.constrains('allow_timesheets', 'analytic_account_id')
def _check_allow_timesheet(self):
for project in self:
+12
View File
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
class ResCompany(models.Model):
@@ -36,6 +37,14 @@ class ResCompany(models.Model):
default=_default_timesheet_encode_uom_id, domain=lambda self: [('category_id', '=', self.env.ref('uom.uom_categ_wtime').id)],
help="""This will set the unit of measure used to encode timesheet. This will simply provide tools
and widgets to help the encoding. All reporting will still be expressed in hours (default value).""")
internal_project_id = fields.Many2one(
'project.project', string="Internal Project",
help="Default project value for timesheet generated from time off type.")
@api.constrains('internal_project_id')
def _check_internal_project_id_company(self):
if self.filtered(lambda company: company.internal_project_id and company.internal_project_id.sudo().company_id != company):
raise ValidationError(_('The Internal Project of a company should be in that company.'))
@api.model_create_multi
def create(self, values):
@@ -62,6 +71,9 @@ class ResCompany(models.Model):
}) for name in [_('Training'), _('Meeting')]]
}]
project_ids = self.env['project.project'].create(results)
projects_by_company = {project.company_id.id: project for project in project_ids}
for company in self:
company.internal_project_id = projects_by_company.get(company.id, False)
return project_ids
def _is_timesheet_hour_uom(self):
@@ -322,5 +322,9 @@
</field>
</record>
<record id="project.open_view_project_all" model="ir.actions.act_window">
<field name="domain">[('is_internal_project', '=', False)]</field>
</record>
</data>
</odoo>
+1 -1
View File
@@ -692,7 +692,7 @@ class Task(models.Model):
kanban_state = fields.Selection([
('normal', 'In Progress'),
('done', 'Ready'),
('blocked', 'Blocked')], string='Kanban State',
('blocked', 'Blocked')], string='Status',
copy=False, default='normal', required=True)
kanban_state_label = fields.Char(compute='_compute_kanban_state_label', string='Kanban State Label', tracking=True)
create_date = fields.Datetime("Created On", readonly=True, index=True)
+1 -1
View File
@@ -439,7 +439,7 @@
<field name="name" string="Name" class="font-weight-bold"/>
<field name="user_id" optional="show" string="Project Manager" widget="many2one_avatar_user"/>
<field name="partner_id" optional="show" string="Customer"/>
<field name="analytic_account_id" optional="hide"/>
<field name="analytic_account_id" optional="hide" groups="analytic.group_analytic_accounting"/>
<field name="privacy_visibility" optional="hide"/>
<field name="label_tasks" optional="hide"/>
<field name="company_id" optional="show" groups="base.group_multi_company"/>
@@ -15,6 +15,6 @@ def post_init(cr, registry):
for hr_leave_type in env['hr.leave.type'].search([('timesheet_generate', '=', True), ('timesheet_project_id', '=', False)]):
company = hr_leave_type.company_id or env.company
hr_leave_type.write({
'timesheet_project_id': company.leave_timesheet_project_id.id,
'timesheet_project_id': company.internal_project_id.id,
'timesheet_task_id': company.leave_timesheet_task_id.id,
})
@@ -10,7 +10,7 @@ class HolidaysType(models.Model):
def _default_project_id(self):
company = self.company_id if self.company_id else self.env.company
return company.leave_timesheet_project_id.id
return company.internal_project_id.id
def _default_task_id(self):
company = self.company_id if self.company_id else self.env.company
@@ -1,50 +1,44 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
from odoo import fields, models, _
class Company(models.Model):
_inherit = 'res.company'
leave_timesheet_project_id = fields.Many2one(
'project.project', string="Internal Project",
help="Default project value for timesheet generated from time off type.")
leave_timesheet_task_id = fields.Many2one(
'project.task', string="Time Off Task",
domain="[('project_id', '=', leave_timesheet_project_id)]")
@api.constrains('leave_timesheet_project_id')
def _check_leave_timesheet_project_id_company(self):
for company in self:
if company.leave_timesheet_project_id:
if company.leave_timesheet_project_id.sudo().company_id != company:
raise ValidationError(_('The Internal Project of a company should be in that company.'))
domain="[('project_id', '=', internal_project_id)]")
def init(self):
type_ids = [(4, self.env.ref('hr_timesheet.internal_project_default_stage').id)]
for company in self.search([('leave_timesheet_project_id', '=', False)]):
companies = self.search(['|', ('internal_project_id', '=', False), ('leave_timesheet_task_id', '=', False)])
internal_projects_by_company_dict = None
Project = self.env['project.project']
for company in companies:
company = company.with_company(company)
project = company.env['project.project'].search([
('name', '=', _('Internal')),
('allow_timesheets', '=', True),
('company_id', '=', company.id),
], limit=1)
if not project:
project = company.env['project.project'].create({
'name': _('Internal'),
'allow_timesheets': True,
'company_id': company.id,
'type_ids': type_ids,
})
company.write({
'leave_timesheet_project_id': project.id,
})
if not company.internal_project_id:
if not internal_projects_by_company_dict:
internal_projects_by_company_read = Project.search_read([
('name', '=', _('Internal')),
('allow_timesheets', '=', True),
('company_id', 'in', companies.ids),
], ['company_id', 'id'])
internal_projects_by_company_dict = {res['company_id'][0]: res['id'] for res in internal_projects_by_company_read}
project_id = internal_projects_by_company_dict.get(company.id, False)
if not project_id:
project_id = Project.create({
'name': _('Internal'),
'allow_timesheets': True,
'company_id': company.id,
'type_ids': type_ids,
}).id
company.write({'internal_project_id': project_id})
if not company.leave_timesheet_task_id:
task = company.env['project.task'].create({
'name': _('Time Off'),
'project_id': company.leave_timesheet_project_id.id,
'project_id': company.internal_project_id.id,
'active': True,
'company_id': company.id,
})
@@ -57,17 +51,14 @@ class Company(models.Model):
for project in projects:
company = project.company_id
company = company.with_company(company)
if not company.leave_timesheet_project_id:
company.write({
'leave_timesheet_project_id': project.id,
})
if not company.leave_timesheet_task_id:
task = company.env['project.task'].sudo().create({
'name': _('Time Off'),
'project_id': company.leave_timesheet_project_id.id,
'project_id': company.internal_project_id.id,
'active': True,
'company_id': company.id,
})
company.write({
'leave_timesheet_task_id': task.id,
})
return projects
@@ -7,19 +7,19 @@ from odoo import api, fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
leave_timesheet_project_id = fields.Many2one(
related='company_id.leave_timesheet_project_id', required=True, string="Internal Project",
internal_project_id = fields.Many2one(
related='company_id.internal_project_id', required=True, string="Internal Project",
domain="[('company_id', '=', company_id)]", readonly=False)
leave_timesheet_task_id = fields.Many2one(
related='company_id.leave_timesheet_task_id', string="Time Off Task", readonly=False,
domain="[('company_id', '=', company_id), ('project_id', '=?', leave_timesheet_project_id)]")
domain="[('company_id', '=', company_id), ('project_id', '=?', internal_project_id)]")
@api.onchange('leave_timesheet_project_id')
@api.onchange('internal_project_id')
def _onchange_timesheet_project_id(self):
if self.leave_timesheet_project_id != self.leave_timesheet_task_id.project_id:
if self.internal_project_id != self.leave_timesheet_task_id.project_id:
self.leave_timesheet_task_id = False
@api.onchange('leave_timesheet_task_id')
def _onchange_timesheet_task_id(self):
if self.leave_timesheet_task_id:
self.leave_timesheet_project_id = self.leave_timesheet_task_id.project_id
self.internal_project_id = self.leave_timesheet_task_id.project_id
@@ -20,7 +20,7 @@ class TestTimesheetHolidaysCreate(common.TransactionCase):
})
company = self.env.company
self.assertEqual(status.timesheet_project_id, company.leave_timesheet_project_id, 'The default project linked to the status should be the same as the company')
self.assertEqual(status.timesheet_project_id, company.internal_project_id, 'The default project linked to the status should be the same as the company')
self.assertEqual(status.timesheet_task_id, company.leave_timesheet_task_id, 'The default task linked to the status should be the same as the company')
def test_company_create(self):
@@ -33,7 +33,7 @@ class TestTimesheetHolidaysCreate(common.TransactionCase):
Company = Company.with_user(user)
Company = Company.with_company(main_company)
company = Company.create({'name': "Wall Company"})
self.assertEqual(company.leave_timesheet_project_id.sudo().company_id, company, "It should have created a project for the company")
self.assertEqual(company.internal_project_id.sudo().company_id, company, "It should have created a project for the company")
class TestTimesheetHolidays(TestCommonTimesheet):
@@ -48,7 +48,7 @@ class TestTimesheetHolidays(TestCommonTimesheet):
self.leave_end_datetime = self.leave_start_datetime + relativedelta(days=3)
# all company have those internal project/task (created by default)
self.internal_project = self.env.company.leave_timesheet_project_id
self.internal_project = self.env.company.internal_project_id
self.internal_task_leaves = self.env.company.leave_timesheet_task_id
self.hr_leave_type_with_ts = self.env['hr.leave.type'].create({
@@ -10,12 +10,12 @@
<div attrs="{'invisible': [('module_project_timesheet_holidays','=',False)]}">
<div class="row mt16">
<div class="w-100">
<label string="Project" for="leave_timesheet_project_id" class="col-3 col-lg-3 o_light_label"/>
<field name="leave_timesheet_project_id" context="{'active_test': False}" class="oe_inline"/>
<label string="Project" for="internal_project_id" class="col-3 col-lg-3 o_light_label"/>
<field name="internal_project_id" context="{'active_test': False}" class="oe_inline"/>
</div>
<div class="w-100">
<label string="Task" for="leave_timesheet_task_id" class="col-3 col-lg-3 o_light_label"/>
<field name="leave_timesheet_task_id" context="{'active_test': False, 'default_project_id': leave_timesheet_project_id}" class="oe_inline"/>
<field name="leave_timesheet_task_id" context="{'active_test': False, 'default_project_id': internal_project_id}" class="oe_inline"/>
</div>
</div>
</div>