[IMP] project,sale,hr(_timesheet): generic improvements for project
Purpose of this PR to improve generic usage of project app.
So, in this PR done following changes:
- In project.project form view :
- Added name label above name field
- Changed placeholder name of field to 'e.g. Office Party'
- in project_project_view_form_simplified:
- displayed name as Title
- renamed 'create' into 'create project'
- in project.task form view > extra info notebook :
- added the 'personal stage' field (Only visible in debug mode)
- in project.project form view:
- based timesheet encoding unit set the 'cost' field name to Daily cost
if uom in Days or In Hourly cost if uom in Hours
- configuration > projects: if possible, clicking on a project kanban card
will be open its form view
task-2821488
closes odoo/odoo#90588
Related: odoo/enterprise#26977
Related: odoo/upgrade#3492
Signed-off-by: Laurent Stukkens (ltu) <ltu@odoo.com>
This commit is contained in:
committed by
Barad Mahendra
parent
c624ed5603
commit
854260f436
@@ -59,9 +59,19 @@
|
||||
<field name="inherit_id" ref="project.project_project_view_form_simplified"/>
|
||||
<field name="priority">24</field>
|
||||
<field name="arch" type="xml">
|
||||
<field name="user_id" position="before">
|
||||
<field name="allow_timesheets"/>
|
||||
</field>
|
||||
<xpath expr="//div[hasclass('o_settings_container')]" position="inside">
|
||||
<div class="col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="allow_timesheets"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="allow_timesheets" string="Timesheets"/>
|
||||
<div class="text-muted">
|
||||
Log time on tasks
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -1060,8 +1060,6 @@ class Task(models.Model):
|
||||
|
||||
@api.model
|
||||
def _default_personal_stage_type_id(self):
|
||||
if self._context.get('default_project_id'):
|
||||
return False
|
||||
return self.env['project.task.type'].search([('user_id', '=', self.env.user.id)], limit=1).id
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -323,14 +323,25 @@
|
||||
<tree string="Task Stage" delete="0" sample="1" multi_edit="1">
|
||||
<field name="sequence" widget="handle" optional="show"/>
|
||||
<field name="name"/>
|
||||
<field name="mail_template_id" optional="hide"/>
|
||||
<field name="rating_template_id" optional="hide" groups="project.group_project_rating"/>
|
||||
<field name="project_ids" optional="show" widget="many2many_tags" options="{'color_field': 'color'}" />
|
||||
<field name="fold" optional="show"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="task_type_tree_inherited" model="ir.ui.view">
|
||||
<field name="name">project.task.type.tree.inherited</field>
|
||||
<field name="model">project.task.type</field>
|
||||
<field name="mode">primary</field>
|
||||
<field name="inherit_id" ref="task_type_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='name']" position="after">
|
||||
<field name="mail_template_id" optional="hide"/>
|
||||
<field name="rating_template_id" optional="hide" groups="project.group_project_rating"/>
|
||||
<field name="project_ids" optional="show" widget="many2many_tags" options="{'color_field': 'color'}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_project_task_type_kanban" model="ir.ui.view">
|
||||
<field name="name">project.task.type.kanban</field>
|
||||
<field name="model">project.task.type</field>
|
||||
@@ -364,7 +375,7 @@
|
||||
<field name="name">Task Stages</field>
|
||||
<field name="res_model">project.task.type</field>
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
<field name="view_id" ref="task_type_tree"/>
|
||||
<field name="view_id" ref="task_type_tree_inherited"/>
|
||||
<field name="domain">[('user_id', '=', False)]</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
@@ -380,7 +391,7 @@
|
||||
<field name="res_model">project.task.type</field>
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
<field name="domain">[('project_ids','=', project_id)]</field>
|
||||
<field name="view_id" ref="task_type_tree"/>
|
||||
<field name="view_id" ref="task_type_tree_inherited"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new stage in the task pipeline
|
||||
@@ -515,8 +526,8 @@
|
||||
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<div class="oe_title">
|
||||
<h1 class="d-flex flex-row">
|
||||
<field name="is_favorite" nolabel="1" widget="boolean_favorite" class="me-3"/>
|
||||
<field name="name" class="text-break" placeholder="Project Name"/>
|
||||
<field name="is_favorite" nolabel="1" widget="boolean_favorite" class="me-2"/>
|
||||
<field name="name" class="o_text_overflow" placeholder="e.g. Office Party"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
@@ -756,13 +767,16 @@
|
||||
<field name="model">project.project</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Project">
|
||||
<group>
|
||||
<field name="name" class="o_project_name oe_inline"
|
||||
string="Project Name" placeholder="e.g. Office Party"/>
|
||||
<field name="user_id" invisible="1"/>
|
||||
</group>
|
||||
<div class="oe_title">
|
||||
<label for="name" string="Name"/>
|
||||
<h1>
|
||||
<field name="name" class="o_project_name oe_inline" placeholder="e.g. Office Party"/>
|
||||
</h1>
|
||||
</div>
|
||||
<field name="user_id" invisible="1"/>
|
||||
<div class="row mt16 o_settings_container"/>
|
||||
<div name="alias_def" colspan="2" attrs="{'invisible': [('alias_domain', '=', False)]}">
|
||||
<label for="alias_name" class="oe_inline" string="Create tasks by sending an email to"/>
|
||||
<label for="alias_name" class="oe_inline mt-4" string="Create tasks by sending an email to"/>
|
||||
<field name="alias_enabled" invisible="1"/>
|
||||
<span>
|
||||
<field name="alias_name" class="oe_inline" placeholder="e.g. office-party"/>@<field name="alias_domain" class="oe_inline" readonly="1" />
|
||||
@@ -778,9 +792,9 @@
|
||||
<field name="inherit_id" ref="project.project_project_view_form_simplified"/>
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//group" position="after">
|
||||
<xpath expr="//div[@name='alias_def']" position="after">
|
||||
<footer>
|
||||
<button string="Create Project" name="action_view_tasks" type="object" class="btn-primary o_open_tasks" data-hotkey="q"/>
|
||||
<button string="Create project" name="action_view_tasks" type="object" class="btn-primary o_open_tasks" data-hotkey="q"/>
|
||||
<button string="Discard" class="btn-secondary" special="cancel" data-hotkey="z"/>
|
||||
</footer>
|
||||
</xpath>
|
||||
@@ -943,6 +957,18 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_project_config_kanban" model="ir.ui.view">
|
||||
<field name="name">project.kanban.inherit.config.project</field>
|
||||
<field name="model">project.project</field>
|
||||
<field name="mode">primary</field>
|
||||
<field name="inherit_id" ref="view_project_kanban"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//kanban" position="attributes">
|
||||
<attribute name="action"></attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_project_calendar" model="ir.ui.view">
|
||||
<field name="name">project.project.calendar</field>
|
||||
<field name="model">project.project</field>
|
||||
@@ -1014,7 +1040,7 @@
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
<field name="view_ids" eval="[(5, 0, 0),
|
||||
(0, 0, {'view_mode': 'tree', 'view_id': ref('view_project')}),
|
||||
(0, 0, {'view_mode': 'kanban', 'view_id': ref('view_project_kanban')})]"/>
|
||||
(0, 0, {'view_mode': 'kanban', 'view_id': ref('view_project_config_kanban')})]"/>
|
||||
<field name="search_view_id" ref="view_project_project_filter"/>
|
||||
<field name="context">{}</field>
|
||||
<field name="help" type="html">
|
||||
@@ -1055,7 +1081,7 @@
|
||||
<field name="sequence" eval="20"/>
|
||||
<field name="view_mode">kanban</field>
|
||||
<field name="act_window_id" ref="project.open_view_project_all_config_group_stage"/>
|
||||
<field name="view_id" ref="view_project_kanban"/>
|
||||
<field name="view_id" ref="view_project_config_kanban"/>
|
||||
</record>
|
||||
|
||||
<!-- Task -->
|
||||
@@ -1314,6 +1340,7 @@
|
||||
<group>
|
||||
<field name="is_analytic_account_id_changed" invisible="1"/>
|
||||
<field name="parent_id" attrs="{'invisible': [('allow_subtasks', '=', False)]}" groups="base.group_no_one"/>
|
||||
<field name="personal_stage_type_id" string="Personal Stage" groups="base.group_no_one" domain="[('user_id', '!=', False)]" context="{'default_user_id' : uid}" readonly="0" required="1"/>
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" context="{'default_partner_id': partner_id}"/>
|
||||
<field name="analytic_tag_ids" groups="analytic.group_analytic_tags" widget="many2many_tags"/>
|
||||
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<record id="task_type_edit_view_tree_inherit_project_sms" model="ir.ui.view">
|
||||
<field name="name">project.task.type.view.tree.inherit.project.sms</field>
|
||||
<field name="model">project.task.type</field>
|
||||
<field name="inherit_id" ref="project.task_type_tree"/>
|
||||
<field name="inherit_id" ref="project.task_type_tree_inherited"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="mail_template_id" position="after">
|
||||
<field name="sms_template_id" optional="hide"/>
|
||||
|
||||
@@ -56,6 +56,14 @@ class Project(models.Model):
|
||||
compute='_compute_partner_id', store=True, readonly=False)
|
||||
allocated_hours = fields.Float(compute='_compute_allocated_hours', store=True, readonly=False, copy=False)
|
||||
|
||||
@api.model
|
||||
def _get_view(self, view_id=None, view_type='form', **options):
|
||||
arch, view = super()._get_view(view_id, view_type, **options)
|
||||
if view_type == 'form' and self.env.company.timesheet_encode_uom_id == self.env.ref('uom.product_uom_day'):
|
||||
for node in arch.xpath("//field[@name='display_cost'][not(@string)]"):
|
||||
node.set('string', 'Daily Cost')
|
||||
return arch, view
|
||||
|
||||
@api.depends('sale_line_id', 'sale_line_employee_ids', 'allow_billable')
|
||||
def _compute_pricing_type(self):
|
||||
billable_projects = self.filtered('allow_billable')
|
||||
|
||||
@@ -25,6 +25,7 @@ class ProjectProductEmployeeMap(models.Model):
|
||||
currency_id = fields.Many2one('res.currency', string="Currency", compute='_compute_currency_id', store=True, readonly=False)
|
||||
cost = fields.Monetary(currency_field='cost_currency_id', compute='_compute_cost', store=True, readonly=False,
|
||||
help="This cost overrides the employee's default timesheet cost in employee's HR Settings")
|
||||
display_cost = fields.Monetary(currency_field='cost_currency_id', compute="_compute_display_cost", inverse="_inverse_display_cost", string="Hourly Cost")
|
||||
cost_currency_id = fields.Many2one('res.currency', string="Cost Currency", related='employee_id.currency_id', readonly=True)
|
||||
is_cost_changed = fields.Boolean('Is Cost Manually Changed', compute='_compute_is_cost_changed', store=True)
|
||||
|
||||
@@ -61,6 +62,45 @@ class ProjectProductEmployeeMap(models.Model):
|
||||
if not map_entry.is_cost_changed:
|
||||
map_entry.cost = map_entry.employee_id.timesheet_cost or 0.0
|
||||
|
||||
def _get_working_hours_per_calendar(self, is_uom_day=False):
|
||||
resource_calendar_per_hours = {}
|
||||
|
||||
if not is_uom_day:
|
||||
return resource_calendar_per_hours
|
||||
|
||||
read_group_data = self.env['resource.calendar']._read_group(
|
||||
[('id', 'in', self.employee_id.resource_calendar_id.ids)],
|
||||
['ids:array_agg(id)', 'hours_per_day'],
|
||||
['hours_per_day'],
|
||||
)
|
||||
for res in read_group_data:
|
||||
for calendar_id in res.get('ids', []):
|
||||
resource_calendar_per_hours[calendar_id] = res.get('hours_per_day')
|
||||
|
||||
return resource_calendar_per_hours
|
||||
|
||||
@api.depends_context('company')
|
||||
@api.depends('cost', 'employee_id.resource_calendar_id')
|
||||
def _compute_display_cost(self):
|
||||
is_uom_day = self.env.ref('uom.product_uom_day') == self.env.company.timesheet_encode_uom_id
|
||||
resource_calendar_per_hours = self._get_working_hours_per_calendar(is_uom_day)
|
||||
|
||||
for map_line in self:
|
||||
if is_uom_day:
|
||||
map_line.display_cost = map_line.cost * resource_calendar_per_hours.get(map_line.employee_id.resource_calendar_id.id, 1)
|
||||
else:
|
||||
map_line.display_cost = map_line.cost
|
||||
|
||||
def _inverse_display_cost(self):
|
||||
is_uom_day = self.env.ref('uom.product_uom_day') == self.env.company.timesheet_encode_uom_id
|
||||
resource_calendar_per_hours = self._get_working_hours_per_calendar(is_uom_day)
|
||||
|
||||
for map_line in self:
|
||||
if is_uom_day:
|
||||
map_line.cost = map_line.display_cost / resource_calendar_per_hours.get(map_line.employee_id.resource_calendar_id.id, 1)
|
||||
else:
|
||||
map_line.cost = map_line.display_cost
|
||||
|
||||
@api.depends('cost')
|
||||
def _compute_is_cost_changed(self):
|
||||
for map_entry in self:
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<field name="employee_id" options="{'no_create': True}"/>
|
||||
<field name="sale_line_id" attrs="{'required': True}" options="{'no_create': True}"/>
|
||||
<field name="price_unit" widget="monetary" force_save="1" options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="cost" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="display_cost" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="is_cost_changed" invisible="1"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="cost_currency_id" invisible="1"/>
|
||||
@@ -58,10 +58,20 @@
|
||||
<field name="model">project.project</field>
|
||||
<field name="inherit_id" ref="hr_timesheet.project_project_view_form_simplified_inherit_timesheet"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="allow_timesheets" position="before">
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="allow_billable"/>
|
||||
</field>
|
||||
<xpath expr="//div[hasclass('o_setting_box')]" position="before">
|
||||
<div class="col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="allow_billable"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="allow_billable"/>
|
||||
<div class="text-muted" id="allow_billable_setting">
|
||||
Invoice your time and material to customers
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user