[IMP] hr_expense: Allow modifications depending on the stage
Purpose ======= When an Expense report hits the "Approved" stage, the Accountant can no longer edit the account / tax / analytic account fields. However, this information can still be modified from the Expense itself, impacting directly the related Expense report anyway. In any case, it makes sense for an accountant to be able to edit this type of information, as it is likely that an employee or a manager will input the wrong accounts. On the other hand, once the Journal entries are posted, the Expense report should not be editable anymore. Specifications ============== For the Expense report (hr.expense.sheet model): If the report is in the "Approved" stage AND the user has at least the Accounting / Accountant access right level The following fields should be editable - Taxes (tax_ids) - Bill reference (reference) - Account (account_id) - Analytic account (analytic_account_id) If the user has a lower access right level => the fields listed above remain in read only. If it is in the "Posted" stage (regardless of the access right level of the user): The following fields should be in read only: - Taxes (tax_ids) - Account (account_id) - Analytic account (analytic_account_id) The "Bill reference" (reference) field should still be editable (current behavior). Based on the Expense report's stage and the access right level of the user, the fields of the related Expenses should also be editable (or not). closes odoo/odoo#32918 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
committed by
Yannick Tivisse
parent
1e8db02368
commit
ccf4fc244a
@@ -52,16 +52,16 @@ class HrExpense(models.Model):
|
||||
product_uom_id = fields.Many2one('uom.uom', string='Unit of Measure', readonly=True, states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}, default=_default_product_uom_id)
|
||||
unit_amount = fields.Float("Unit Price", readonly=True, required=True, states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}, digits=dp.get_precision('Product Price'))
|
||||
quantity = fields.Float(required=True, readonly=True, states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}, digits=dp.get_precision('Product Unit of Measure'), default=1)
|
||||
tax_ids = fields.Many2many('account.tax', 'expense_tax', 'expense_id', 'tax_id', string='Taxes', states={'done': [('readonly', True)], 'post': [('readonly', True)]})
|
||||
tax_ids = fields.Many2many('account.tax', 'expense_tax', 'expense_id', 'tax_id', string='Taxes')
|
||||
untaxed_amount = fields.Float("Subtotal", store=True, compute='_compute_amount', digits=dp.get_precision('Account'))
|
||||
total_amount = fields.Monetary("Total", compute='_compute_amount', store=True, currency_field='currency_id', digits=dp.get_precision('Account'))
|
||||
total_amount_company = fields.Monetary("Total (Company Currency)", compute='_compute_total_amount_company', store=True, currency_field='company_currency_id', digits=dp.get_precision('Account'))
|
||||
company_id = fields.Many2one('res.company', string='Company', readonly=True, states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}, default=lambda self: self.env.company)
|
||||
currency_id = fields.Many2one('res.currency', string='Currency', readonly=True, states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}, default=lambda self: self.env.company.currency_id)
|
||||
company_currency_id = fields.Many2one('res.currency', string="Report Company Currency", related='sheet_id.currency_id', store=True, readonly=False)
|
||||
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account', states={'post': [('readonly', True)], 'done': [('readonly', True)]}, oldname='analytic_account')
|
||||
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account', oldname='analytic_account')
|
||||
analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags', states={'post': [('readonly', True)], 'done': [('readonly', True)]})
|
||||
account_id = fields.Many2one('account.account', string='Account', states={'post': [('readonly', True)], 'done': [('readonly', True)]}, default=_default_account_id, help="An expense account is expected")
|
||||
account_id = fields.Many2one('account.account', string='Account', default=_default_account_id, help="An expense account is expected")
|
||||
description = fields.Text('Notes...', readonly=True, states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]})
|
||||
payment_mode = fields.Selection([
|
||||
("own_account", "Employee (to reimburse)"),
|
||||
@@ -79,6 +79,9 @@ class HrExpense(models.Model):
|
||||
reference = fields.Char("Bill Reference")
|
||||
is_refused = fields.Boolean("Explicitely Refused by manager or acccountant", readonly=True, copy=False)
|
||||
|
||||
is_editable = fields.Boolean("Is Editable By Current User", compute='_compute_is_editable')
|
||||
is_ref_editable = fields.Boolean("Reference Is Editable By Current User", compute='_compute_is_ref_editable')
|
||||
|
||||
@api.depends('sheet_id', 'sheet_id.account_move_id', 'sheet_id.state')
|
||||
def _compute_state(self):
|
||||
for expense in self:
|
||||
@@ -118,6 +121,26 @@ class HrExpense(models.Model):
|
||||
for expense in self:
|
||||
expense.attachment_number = attachment.get(expense.id, 0)
|
||||
|
||||
@api.depends('employee_id')
|
||||
def _compute_is_editable(self):
|
||||
is_account_manager = self.env.user.has_group('account.group_account_user') or self.env.user.has_group('account.group_account_manager')
|
||||
for expense in self:
|
||||
if expense.state == 'draft' or expense.sheet_id.state in ['draft', 'submit']:
|
||||
expense.is_editable = True
|
||||
elif expense.sheet_id.state == 'approve':
|
||||
expense.is_editable = is_account_manager
|
||||
else:
|
||||
expense.is_editable = False
|
||||
|
||||
@api.depends('employee_id')
|
||||
def _compute_is_ref_editable(self):
|
||||
is_account_manager = self.env.user.has_group('account.group_account_user') or self.env.user.has_group('account.group_account_manager')
|
||||
for expense in self:
|
||||
if expense.state == 'draft' or expense.sheet_id.state in ['draft', 'submit']:
|
||||
expense.is_ref_editable = True
|
||||
else:
|
||||
expense.is_ref_editable = is_account_manager
|
||||
|
||||
@api.onchange('product_id')
|
||||
def _onchange_product_id(self):
|
||||
if self.product_id:
|
||||
@@ -149,6 +172,16 @@ class HrExpense(models.Model):
|
||||
raise UserError(_('You cannot delete a posted or approved expense.'))
|
||||
return super(HrExpense, self).unlink()
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
if 'tax_ids' in vals or 'analytic_account_id' in vals or 'account_id' in vals:
|
||||
if any(not expense.is_editable for expense in self):
|
||||
raise UserError(_('You are not autorized to edit this expense report.'))
|
||||
if 'reference' in vals:
|
||||
if any(not expense.is_ref_editable for expense in self):
|
||||
raise UserError(_('You are not autorized to edit the reference of this expense report.'))
|
||||
return super(HrExpense, self).write(vals)
|
||||
|
||||
@api.model
|
||||
def get_empty_list_help(self, help_message):
|
||||
if help_message and "o_view_nocontent_smiling_face" not in help_message:
|
||||
|
||||
@@ -76,6 +76,8 @@
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="is_editable" invisible="1"/>
|
||||
<field name="is_ref_editable" invisible="1"/>
|
||||
<field name="product_id" required="1" context="{'default_can_be_expensed': 1, 'tree_view_ref': 'hr_expense.product_product_expense_tree_view'}"
|
||||
widget="many2one_barcode"
|
||||
/>
|
||||
@@ -85,16 +87,16 @@
|
||||
<field name="quantity" class="oe_inline"/>
|
||||
<field name="product_uom_id" required="1" widget="selection" class="oe_inline" groups="uom.group_uom"/>
|
||||
</div>
|
||||
<field name="tax_ids" domain="[('type_tax_use', '=', 'purchase')]" widget="many2many_tags" groups="account.group_account_user"/>
|
||||
<field name="tax_ids" domain="[('type_tax_use', '=', 'purchase')]" widget="many2many_tags" groups="account.group_account_user" attrs="{'readonly': [('is_editable', '=', False)]}"/>
|
||||
</group><group>
|
||||
<field name="reference"/>
|
||||
<field name="reference" attrs="{'readonly': [('is_ref_editable', '=', False)]}"/>
|
||||
<field name="date"/>
|
||||
<field name="account_id" options="{'no_create': True}" domain="[('internal_type', '=', 'other')]" groups="account.group_account_user"/>
|
||||
<field name="account_id" options="{'no_create': True}" domain="[('internal_type', '=', 'other')]" groups="account.group_account_user" attrs="{'readonly': [('is_editable', '=', False)]}"/>
|
||||
<field name="employee_id" groups="hr_expense.group_hr_expense_team_approver"/>
|
||||
<field name="sheet_id" invisible="1"/>
|
||||
<field name="currency_id" groups="base.group_multi_currency"/>
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
|
||||
<field name="analytic_account_id" groups="analytic.group_analytic_accounting" attrs="{'readonly': [('is_editable', '=', False)]}"/>
|
||||
<field name="analytic_tag_ids" widget="many2many_tags" groups="analytic.group_analytic_tags" attrs="{'readonly': [('is_editable', '=', False)]}"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</group><group>
|
||||
<field name="total_amount" widget='monetary' options="{'currency_field': 'currency_id'}"/>
|
||||
|
||||
Reference in New Issue
Block a user