[FIX] hr_expense: to give report manager rights and make expense_line_ids editable

1) Now report manager (user_id) (with Team Approver rights) can't see the report.
Here we grant him the same rights as the manager (on employee app)
of the employee will have. Moreover, he can approve the report as well.

2) Purpose: accountant, manager, should be able to edit expense_line_ids
for expense report not only in draft, but also for submitted and approved states.

On the other hand, employee can only edit his expense report only in draft state.

task - 2768700

closes odoo/odoo#84978

Related: odoo/upgrade#3274
Related: odoo/enterprise#24579
Signed-off-by: Kevin Baptiste <kba@odoo.com>
This commit is contained in:
sofiagvaladze
2022-02-24 11:26:43 +00:00
parent e3a7aa5bd0
commit ae687dadc0
3 changed files with 49 additions and 20 deletions
+23 -6
View File
@@ -577,7 +577,6 @@ Or send your receipts at <a href="mailto:%(email)s?subject=Lunch%%20with%%20cust
unit_amount = expense.unit_amount if expense.product_has_cost else expense.total_amount
quantity = expense.quantity if expense.unit_amount else 1
# sgv todo - chage so that we do not recompute again. We have all the info from expense
taxes = expense.tax_ids.with_context(round=True).compute_all(unit_amount, expense.currency_id, quantity, expense.product_id)
total_amount = 0.0
total_amount_currency = 0.0
@@ -912,6 +911,7 @@ class HrExpenseSheet(models.Model):
name = fields.Char('Expense Report Summary', required=True, tracking=True)
expense_line_ids = fields.One2many('hr.expense', 'sheet_id', string='Expense Lines', copy=False)
is_editable = fields.Boolean("Expense Lines Are Editable By Current User", compute='_compute_is_editable')
expense_number = fields.Integer(compute='_compute_expense_number', string='Number of Expenses')
state = fields.Selection([
('draft', 'Draft'),
@@ -1003,6 +1003,20 @@ class HrExpenseSheet(models.Model):
sheet.department_id = sheet.employee_id.department_id
sheet.user_id = sheet.employee_id.expense_manager_id or sheet.employee_id.parent_id.user_id
@api.depends_context('uid')
@api.depends('employee_id', 'user_id', 'state')
def _compute_is_editable(self):
is_manager = self.user_has_groups('hr_expense.group_hr_expense_manager')
is_approver = self.user_has_groups('hr_expense.group_hr_expense_user')
for report in self:
# Employee can edit his own expense in draft only
is_editable = (report.employee_id.user_id == self.env.user and report.state == 'draft') or (is_manager and report.state in ['draft', 'submit', 'approve'])
if not is_editable and report.state in ['draft', 'submit', 'approve']:
# expense manager can edit, unless it's own expense
current_managers = report.employee_id.expense_manager_id | report.employee_id.parent_id.user_id | report.employee_id.department_id.manager_id.user_id | report.user_id
is_editable = (is_approver or self.env.user in current_managers) and report.employee_id.user_id != self.env.user
report.is_editable = is_editable
@api.constrains('expense_line_ids')
def _check_payment_mode(self):
for sheet in self:
@@ -1095,7 +1109,10 @@ class HrExpenseSheet(models.Model):
for sheet in self:
move = sheet.account_move_id
sheet.account_move_id = False
move._reverse_moves(cancel=True)
if move.state == 'draft':
move.unlink()
else:
move._reverse_moves(cancel=True)
sheet.write({'state': 'draft'})
def action_get_attachment_view(self):
@@ -1139,13 +1156,13 @@ class HrExpenseSheet(models.Model):
def action_submit_sheet(self):
self.write({'state': 'submit'})
self.activity_update()
self.sudo().activity_update()
def _check_can_approve(self):
if not self.user_has_groups('hr_expense.group_hr_expense_team_approver'):
raise UserError(_("Only Managers and HR Officers can approve expenses"))
elif not self.user_has_groups('hr_expense.group_hr_expense_manager'):
current_managers = self.employee_id.expense_manager_id | self.employee_id.parent_id.user_id | self.employee_id.department_id.manager_id.user_id
current_managers = self.employee_id.expense_manager_id | self.employee_id.parent_id.user_id | self.employee_id.department_id.manager_id.user_id | self.user_id
if self.employee_id.user_id == self.env.user:
raise UserError(_("You cannot approve your own expenses"))
@@ -1197,7 +1214,7 @@ class HrExpenseSheet(models.Model):
if not self.user_has_groups('hr_expense.group_hr_expense_team_approver'):
raise UserError(_("Only Managers and HR Officers can approve expenses"))
elif not self.user_has_groups('hr_expense.group_hr_expense_manager'):
current_managers = self.employee_id.expense_manager_id | self.employee_id.parent_id.user_id | self.employee_id.department_id.manager_id.user_id
current_managers = self.employee_id.expense_manager_id | self.employee_id.parent_id.user_id | self.employee_id.department_id.manager_id.user_id | self.user_id
if self.employee_id.user_id == self.env.user:
raise UserError(_("You cannot refuse your own expenses"))
@@ -1214,7 +1231,7 @@ class HrExpenseSheet(models.Model):
if not self.can_reset:
raise UserError(_("Only HR Officers or the concerned employee can reset to draft."))
self.mapped('expense_line_ids').write({'is_refused': False})
self.write({'state': 'draft', 'approval_date': False})
self.sudo().write({'state': 'draft', 'approval_date': False})
self.activity_update()
return True
+16 -5
View File
@@ -12,11 +12,12 @@
<record id="ir_rule_hr_expense_approver" model="ir.rule">
<field name="name">Team Approver Expense</field>
<field name="model_id" ref="model_hr_expense"/>
<field name="domain_force">['|', '|', '|',
<field name="domain_force">['|', '|', '|', '|',
('employee_id.user_id', '=', user.id),
('employee_id.department_id.manager_id.user_id', '=', user.id),
('employee_id.parent_id.user_id', '=', user.id),
('employee_id.expense_manager_id', '=', user.id)]</field>
('employee_id.expense_manager_id', '=', user.id),
('sheet_id.user_id', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('hr_expense.group_hr_expense_team_approver'))]"/>
</record>
<record id="ir_rule_hr_expense_employee" model="ir.rule">
@@ -37,19 +38,29 @@
<record id="ir_rule_hr_expense_sheet_approver" model="ir.rule">
<field name="name">Approver Expense Sheet</field>
<field name="model_id" ref="model_hr_expense_sheet"/>
<field name="domain_force">['|', '|', '|',
<field name="domain_force">['|', '|', '|', '|',
('employee_id.user_id', '=', user.id),
('employee_id.department_id.manager_id.user_id', '=', user.id),
('employee_id.parent_id.user_id', '=', user.id),
('employee_id.expense_manager_id', '=', user.id)]</field>
('employee_id.expense_manager_id', '=', user.id),
('user_id', '=', user.id)]</field>
<field name="groups" eval="[(4, ref('hr_expense.group_hr_expense_team_approver'))]"/>
</record>
<record id="ir_rule_hr_expense_sheet_employee" model="ir.rule">
<field name="name">Employee Expense Sheet</field>
<field name="model_id" ref="model_hr_expense_sheet"/>
<field name="domain_force">[('employee_id.user_id', '=', user.id)]</field>
<field name="domain_force">[('employee_id.user_id', '=', user.id), ('state', '=', 'draft')]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
</record>
<record id="ir_rule_hr_expense_sheet_employee_not_draft" model="ir.rule">
<field name="name">Employee can't modify expense sheet that is not in draft state</field>
<field name="model_id" ref="model_hr_expense_sheet"/>
<field name="domain_force">[('employee_id.user_id', '=', user.id), ('state', '!=', 'draft')]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_create" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
<record id="hr_expense_comp_rule" model="ir.rule">
<field name="name">Expense multi company rule</field>
+10 -9
View File
@@ -67,7 +67,7 @@
<field name="quantity" optional="hide"/>
<field name="tax_ids" optional="hide" widget="many2many_tags" groups="account.group_account_invoice,account.group_account_readonly"/>
<field name="amount_tax_company" optional="hide" groups="account.group_account_invoice,account.group_account_readonly"/>
<field name="attachment_number" nolabel="1" class='fa fa-paperclip float-right pr-0' attrs="{'invisible': [('attachment_number', '=', 0)]}"/>
<field name="attachment_number" nolabel="1" class='fa fa-paperclip pr-0' attrs="{'invisible': [('attachment_number', '=', 0)]}"/>
<field name="total_amount_company" optional="show" sum="Total Company Currency" widget='monetary' options="{'currency_field': 'company_currency_id'}" decoration-bf="True"/>
<field name="total_amount" optional="hide" decoration-bf="True" groups="base.group_multi_currency"/>
<field name="currency_id" optional="hide" groups="base.group_multi_currency"/>
@@ -731,7 +731,7 @@
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1>
<field name="name" placeholder="e.g. Trip to NY" attrs="{'readonly': [('state', 'in', ['post', 'done'])]}"/>
<field name="name" placeholder="e.g. Trip to NY" attrs="{'readonly': [('is_editable', '=', False)]}"/>
</h1>
</div>
<group>
@@ -745,17 +745,18 @@
</group>
<group>
<field name="company_id" groups="base.group_multi_company"/>
<field name="user_id" domain="[('share', '=', False)]" widget="many2one_avatar_user"/>
<field name="user_id" widget="many2one_avatar_user"/>
<field name="accounting_date" groups="account.group_account_invoice,account.group_account_readonly" attrs="{'invisible': [('state', 'not in', ['approve', 'post', 'done'])], 'readonly': [('state', 'in', ['post', 'done'])]}"/>
</group>
</group>
<notebook>
<page name="expenses" string="Expense">
<field name="expense_line_ids" nolabel="1" widget="many2many" domain="[('state', '=', 'draft'), ('employee_id', '=', employee_id), ('company_id', '=', company_id)]" options="{'reload_on_button': True}" context="{'form_view_ref' : 'hr_expense.hr_expense_view_form_without_header', 'default_company_id': company_id, 'default_employee_id': employee_id}" attrs="{'readonly': [('state', '!=', 'draft')]}">
<field name="is_editable" invisible="1"/>
<field name="expense_line_ids" nolabel="1" widget="many2many" domain="[('state', '=', 'draft'), ('employee_id', '=', employee_id), ('company_id', '=', company_id)]" options="{'reload_on_button': True}" context="{'form_view_ref' : 'hr_expense.hr_expense_view_form_without_header', 'default_company_id': company_id, 'default_employee_id': employee_id}" attrs="{'readonly': [('is_editable', '=', False)]}">
<tree decoration-danger="is_refused" editable="bottom" class="o_expense_line_list">
<field name="date" optional="show" readonly="True"/>
<field name="product_id" readonly="True"/>
<field name="name" readonly="True"/>
<field name="date" optional="show"/>
<field name="product_id"/>
<field name="name"/>
<field name="state" invisible="1"/>
<field name="reference" optional="hide" readonly="True"/>
<field name="analytic_account_id" optional="show" domain="['|', ('company_id', '=', parent.company_id), ('company_id', '=', False)]" groups="analytic.group_analytic_accounting"/>
@@ -768,9 +769,9 @@
<field name="currency_id" optional="hide" readonly="True"/>
<field name="quantity" optional="hide" readonly="True"/>
<field name="company_id" invisible="1"/>
<field name="tax_ids" string="Taxes" optional="show" widget="many2many_tags" context="{'default_company_id': company_id}" readonly="True"/>
<field name="tax_ids" string="Taxes" optional="show" widget="many2many_tags" context="{'default_company_id': company_id}"/>
<field name="amount_tax" optional="hide" context="{'default_company_id': company_id}" readonly="True"/>
<field name="total_amount" optional="show" readonly="True"/>
<field name="total_amount" optional="show"/>
<field name="amount_tax_company" optional="hide" readonly="True"/>
<field name="company_currency_id" invisible="1"/>
<field name="total_amount_company" optional="show" groups="base.group_multi_currency" readonly="True"/>