[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:
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user