[IMP] hr_expense: add draft state to expense lines
We need to be able to change an expense report before submitting it, for this we add a new step in the expense process (a draft state for the expense sheets). Task #41700 Closes #24806
This commit is contained in:
@@ -82,7 +82,7 @@ class HrExpense(models.Model):
|
||||
@api.depends('sheet_id', 'sheet_id.account_move_id', 'sheet_id.state')
|
||||
def _compute_state(self):
|
||||
for expense in self:
|
||||
if not expense.sheet_id:
|
||||
if not expense.sheet_id or expense.sheet_id.state == 'draft':
|
||||
expense.state = "draft"
|
||||
elif expense.sheet_id.state == "cancel":
|
||||
expense.state = "refused"
|
||||
@@ -177,7 +177,7 @@ class HrExpense(models.Model):
|
||||
|
||||
@api.multi
|
||||
def action_submit_expenses(self):
|
||||
if any(expense.state != 'draft' for expense in self):
|
||||
if any(expense.state != 'draft' or expense.sheet_id for expense in self):
|
||||
raise UserError(_("You cannot report twice the same line!"))
|
||||
if len(self.mapped('employee_id')) != 1:
|
||||
raise UserError(_("You cannot report expenses for different employees in the same report."))
|
||||
@@ -508,19 +508,20 @@ class HrExpenseSheet(models.Model):
|
||||
name = fields.Char('Expense Report Summary', required=True)
|
||||
expense_line_ids = fields.One2many('hr.expense', 'sheet_id', string='Expense Lines', states={'approve': [('readonly', True)], 'done': [('readonly', True)], 'post': [('readonly', True)]}, copy=False)
|
||||
state = fields.Selection([
|
||||
('draft', 'Draft'),
|
||||
('submit', 'Submitted'),
|
||||
('approve', 'Approved'),
|
||||
('post', 'Posted'),
|
||||
('done', 'Paid'),
|
||||
('cancel', 'Refused')
|
||||
], string='Status', index=True, readonly=True, track_visibility='onchange', copy=False, default='submit', required=True, help='Expense Report State')
|
||||
employee_id = fields.Many2one('hr.employee', string="Employee", required=True, readonly=True, states={'submit': [('readonly', False)]}, default=lambda self: self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1))
|
||||
], string='Status', index=True, readonly=True, track_visibility='onchange', copy=False, default='draft', required=True, help='Expense Report State')
|
||||
employee_id = fields.Many2one('hr.employee', string="Employee", required=True, readonly=True, states={'draft': [('readonly', False)]}, default=lambda self: self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1))
|
||||
address_id = fields.Many2one('res.partner', string="Employee Home Address")
|
||||
payment_mode = fields.Selection([("own_account", "Employee (to reimburse)"), ("company_account", "Company")], related='expense_line_ids.payment_mode', default='own_account', readonly=True, string="Paid By")
|
||||
user_id = fields.Many2one('res.users', 'Manager', readonly=True, copy=False, states={'submit': [('readonly', False)]}, track_visibility='onchange', oldname='responsible_id')
|
||||
user_id = fields.Many2one('res.users', 'Manager', readonly=True, copy=False, states={'draft': [('readonly', False)]}, track_visibility='onchange', oldname='responsible_id')
|
||||
total_amount = fields.Monetary('Total Amount', currency_field='currency_id', compute='_compute_amount', store=True, digits=dp.get_precision('Account'))
|
||||
company_id = fields.Many2one('res.company', string='Company', readonly=True, states={'submit': [('readonly', False)]}, default=lambda self: self.env.user.company_id)
|
||||
currency_id = fields.Many2one('res.currency', string='Currency', readonly=True, states={'submit': [('readonly', False)]}, default=lambda self: self.env.user.company_id.currency_id)
|
||||
company_id = fields.Many2one('res.company', string='Company', readonly=True, states={'draft': [('readonly', False)]}, default=lambda self: self.env.user.company_id)
|
||||
currency_id = fields.Many2one('res.currency', string='Currency', readonly=True, states={'draft': [('readonly', False)]}, default=lambda self: self.env.user.company_id.currency_id)
|
||||
attachment_number = fields.Integer(compute='_compute_attachment_number', string='Number of Attachments')
|
||||
journal_id = fields.Many2one('account.journal', string='Expense Journal', states={'done': [('readonly', True)], 'post': [('readonly', True)]}, default=_default_journal_id, help="The journal used when the expense is done.")
|
||||
bank_journal_id = fields.Many2one('account.journal', string='Bank Journal', states={'done': [('readonly', True)], 'post': [('readonly', True)]}, default=_default_bank_journal_id, help="The payment method used when the expense is paid by the company.")
|
||||
@@ -528,6 +529,7 @@ class HrExpenseSheet(models.Model):
|
||||
account_move_id = fields.Many2one('account.move', string='Journal Entry', ondelete='restrict', copy=False)
|
||||
department_id = fields.Many2one('hr.department', string='Department', states={'post': [('readonly', True)], 'done': [('readonly', True)]})
|
||||
is_multiple_currency = fields.Boolean("Handle lines with different currencies", compute='_compute_is_multiple_currency')
|
||||
can_reset = fields.Boolean('Can Reset', compute='_compute_can_reset')
|
||||
|
||||
@api.depends('expense_line_ids.total_amount_company')
|
||||
def _compute_amount(self):
|
||||
@@ -544,6 +546,12 @@ class HrExpenseSheet(models.Model):
|
||||
for sheet in self:
|
||||
sheet.is_multiple_currency = len(sheet.expense_line_ids.mapped('currency_id')) > 1
|
||||
|
||||
@api.multi
|
||||
def _compute_can_reset(self):
|
||||
is_expense_user = self.user_has_groups('hr_expense.group_hr_expense_user')
|
||||
for sheet in self:
|
||||
sheet.can_reset = is_expense_user if is_expense_user else sheet.employee_id.user_id == self.env.user
|
||||
|
||||
@api.onchange('employee_id')
|
||||
def _onchange_employee_id(self):
|
||||
self.address_id = self.employee_id.sudo().address_home_id
|
||||
@@ -647,6 +655,10 @@ class HrExpenseSheet(models.Model):
|
||||
def set_to_paid(self):
|
||||
self.write({'state': 'done'})
|
||||
|
||||
@api.multi
|
||||
def action_submit_sheet(self):
|
||||
self.write({'state': 'submit'})
|
||||
|
||||
@api.multi
|
||||
def approve_expense_sheets(self):
|
||||
if not self.user_has_groups('hr_expense.group_hr_expense_user'):
|
||||
@@ -688,8 +700,10 @@ class HrExpenseSheet(models.Model):
|
||||
|
||||
@api.multi
|
||||
def reset_expense_sheets(self):
|
||||
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': 'submit'})
|
||||
self.write({'state': 'draft'})
|
||||
self.activity_update()
|
||||
return True
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_hr_expense_user,hr.expense.user,model_hr_expense,hr_expense.group_hr_expense_user,1,1,1,1
|
||||
access_hr_expense_employee,hr.expense.employee,model_hr_expense,base.group_user,1,1,1,1
|
||||
access_hr_expense_sheet_manager,hr.expense.sheet.manager,model_hr_expense_sheet,hr_expense.group_hr_expense_manager,1,1,1,1
|
||||
access_hr_expense_sheet_user,hr.expense.sheet.user,model_hr_expense_sheet,hr_expense.group_hr_expense_user,1,1,1,1
|
||||
access_hr_expense_sheet_employee,hr.expense.sheet.employee,model_hr_expense_sheet,base.group_user,1,1,1,1
|
||||
access_hr_expense_user,hr.expense.user,model_hr_expense,hr_expense.group_hr_expense_user,1,1,1,1
|
||||
access_hr_expense_sheet_user,hr.expense.sheet.user,model_hr_expense_sheet,hr_expense.group_hr_expense_user,1,1,1,1
|
||||
access_hr_expense_manager,hr.expense.manager,model_hr_expense,hr_expense.group_hr_expense_manager,1,1,1,1
|
||||
access_hr_expense_sheet_manager,hr.expense.sheet.manager,model_hr_expense_sheet,hr_expense.group_hr_expense_manager,1,1,1,1
|
||||
access_product_product_hr_expense_user,product.product.hr.expense.user,product.model_product_product,hr_expense.group_hr_expense_user,1,1,1,1
|
||||
access_product_template_hr_expense_user,product.template.hr.expense.user,product.model_product_template,hr_expense.group_hr_expense_user,1,1,1,1
|
||||
access_uom_uom_hr_expense_user,uom.uom.hr.expense.user,uom.model_uom_uom,hr_expense.group_hr_expense_user,1,1,1,1
|
||||
|
||||
|
@@ -15,20 +15,19 @@ tour.register('hr_expense_tour' ,
|
||||
trigger: '.o_app[data-menu-xmlid="hr_expense.menu_hr_expense_root"], .oe_menu_toggler[data-menu-xmlid="hr_expense.menu_hr_expense_root"]',
|
||||
content: _t("Want to manage your expenses? It starts here."),
|
||||
position: 'bottom',
|
||||
}, {
|
||||
trigger: '.o_list_button_add',
|
||||
extra_trigger: ".o_expense_tree",
|
||||
content: _t('<p>Let’s start by creating your first expense.</p>'),
|
||||
position: 'bottom'
|
||||
}, {
|
||||
trigger: '.o_form_button_save',
|
||||
extra_trigger: ".o_expense_sheet",
|
||||
content: _t("<p>Once your <b>Expense report</b> is ready, you can save it and wait for the approval from your manager.</p>"),
|
||||
content: _t("<p>Once your <b> Expense </b> is ready, you can save it.</p>"),
|
||||
position: 'bottom',
|
||||
}, {
|
||||
trigger: '.o_expense_form .o_chatter_button_new_message',
|
||||
trigger: '.o_attach_document',
|
||||
content: _t("Attach your receipt here."),
|
||||
position: 'top',
|
||||
position: 'bottom',
|
||||
}, {
|
||||
trigger: '.o_expense_submit',
|
||||
extra_triggger: ".o_expense_form",
|
||||
content: _t('<p>Click on <b> Create Report </b> to create the report.</p>'),
|
||||
position: 'right',
|
||||
}, {
|
||||
trigger: '.o_expense_tree .o_checkbox > input[type=checkbox]',
|
||||
content: _t('<p>Select expenses to submit them to your manager</p>'),
|
||||
@@ -36,11 +35,15 @@ tour.register('hr_expense_tour' ,
|
||||
}, {
|
||||
trigger: '.o_dropdown_toggler_btn',
|
||||
extra_trigger: ".o_expense_tree",
|
||||
content: _t('<p>Click on <b> Action Submit To Manager </b> to submit selected expenses to your manager</p>'),
|
||||
content: _t('<p>Click on <b> Action Create Report </b> to submit selected expenses to your manager</p>'),
|
||||
position: 'right',
|
||||
}, {
|
||||
trigger: '.o_expense_sheet_submit',
|
||||
content: _t('Once your <b>Expense report</b> is ready, you can submit it to your manager and wait for the approval from your manager.'),
|
||||
position: 'bottom',
|
||||
}, {
|
||||
trigger: '.o_expense_sheet_approve',
|
||||
content: _t("<p>Approve the sheet here.</p><p>Tip: if you refuse, don’t forget to give the reason thanks to the hereunder message tool</p>"),
|
||||
content: _t("<p>Approve the report here.</p><p>Tip: if you refuse, don’t forget to give the reason thanks to the hereunder message tool</p>"),
|
||||
position: 'bottom',
|
||||
}, {
|
||||
trigger: '.o_expense_sheet_post',
|
||||
|
||||
@@ -43,7 +43,10 @@ class TestAccountEntry(TestExpenseCommon):
|
||||
'analytic_account_id': self.analytic_account.id,
|
||||
})
|
||||
expense_line._onchange_product_id()
|
||||
# State should default to draft
|
||||
self.assertEquals(expense.state, 'draft', 'Expense should be created in Draft state')
|
||||
# Submitted to Manager
|
||||
expense.action_submit_sheet()
|
||||
self.assertEquals(expense.state, 'submit', 'Expense is not in Reported state')
|
||||
# Approve
|
||||
expense.approve_expense_sheets()
|
||||
|
||||
@@ -43,10 +43,10 @@
|
||||
<header>
|
||||
<widget name="attach_document" string="Attach Document" action="message_post" attrs="{'invisible': [('attachment_number', '<=', 0)]}"/>
|
||||
<widget name="attach_document" string="Attach Document" action="message_post" class="oe_highlight" attrs="{'invisible': [('attachment_number', '>=', 1)]}"/>
|
||||
<button name="action_submit_expenses" string="Submit to Manager" type="object" class="o_expense_submit" attrs="{'invisible': ['|', ('attachment_number', '>=', 1), ('sheet_id', '!=', False)]}"/>
|
||||
<button name="action_submit_expenses" string="Submit to Manager" type="object" class="oe_highlight o_expense_submit" attrs="{'invisible': ['|', ('attachment_number', '<=', 0), ('sheet_id', '!=', False)]}"/>
|
||||
<button name="action_submit_expenses" string="Create Report" type="object" class="o_expense_submit" attrs="{'invisible': ['|', ('attachment_number', '>=', 1), ('sheet_id', '!=', False)]}"/>
|
||||
<button name="action_submit_expenses" string="Create Report" type="object" class="oe_highlight o_expense_submit" attrs="{'invisible': ['|', ('attachment_number', '<=', 0), ('sheet_id', '!=', False)]}"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,reported,approved,done,refused"/>
|
||||
<button name="action_view_sheet" type="object" string="View Report" class="oe_highlight" attrs="{'invisible': [('state', '=','draft')]}"/>
|
||||
<button name="action_view_sheet" type="object" string="View Report" class="oe_highlight" attrs="{'invisible': [('sheet_id', '=', False)]}"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_button_box">
|
||||
@@ -200,6 +200,7 @@
|
||||
<filter domain="[('state', '=', 'refused')]" string="Refused" name="refused" help="Refused Expenses"/>
|
||||
<filter domain="['|', ('state', '=', 'approved'), ('state', '=', 'done')]" string="Approved" name="approved" help="Actual expense sheets, not the refused ones"/>
|
||||
<separator />
|
||||
<filter domain="[('sheet_id', '=', False)]" string="Not in a report" name="no_report"/>
|
||||
<filter string="My Team Expenses" name="my_team_expenses" domain="[('employee_id.parent_id.user_id', '=', uid)]" groups="hr_expense.group_hr_expense_manager" help="Expenses of Your Team Member"/>
|
||||
<filter string="My Expenses" name="my_expenses" domain="[('employee_id.user_id', '=', uid)]"/>
|
||||
<separator />
|
||||
@@ -245,7 +246,7 @@
|
||||
<field name="res_model">hr.expense</field>
|
||||
<field name="view_mode">tree,kanban,form,graph,pivot</field>
|
||||
<field name="search_view_id" ref="view_hr_expense_filter"/>
|
||||
<field name="context">{'search_default_to_report': 1, 'search_default_my_expenses': 1}</field>
|
||||
<field name="context">{'search_default_to_report': 1, 'search_default_my_expenses': 1, 'search_default_no_report': 1}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new expense
|
||||
@@ -401,7 +402,7 @@
|
||||
<field name="model">hr.expense.sheet</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Expense Reports" decoration-warning="state=='draft'" decoration-bf="message_unread == True" decoration-danger="state=='cancel'">
|
||||
<field name="name" string="Expense Sheet"/>
|
||||
<field name="name" string="Expense Report"/>
|
||||
<field name="accounting_date"/>
|
||||
<field name="employee_id"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
@@ -418,11 +419,13 @@
|
||||
<field eval="25" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Expense Reports" class="o_expense_sheet">
|
||||
<field name="can_reset" invisible="1"/>
|
||||
<header>
|
||||
<button name="action_submit_sheet" states="draft" string="Submit to Manager" type="object" class="oe_highlight o_expense_sheet_submit"/>
|
||||
<button name="approve_expense_sheets" states="submit" string="Approve" type="object" groups="hr_expense.group_hr_expense_user" class="oe_highlight o_expense_sheet_approve"/>
|
||||
<button name="action_sheet_move_create" states="approve" string="Post Journal Entries" type="object" groups="account.group_account_manager" class="oe_highlight o_expense_sheet_post"/>
|
||||
<button name="%(hr_expense.hr_expense_sheet_register_payment_wizard_action)d" type="action" string="Register Payment" class="oe_highlight o_expense_sheet_pay" attrs="{'invisible': [('state', '!=', 'post')]}" context="{'default_amount': total_amount, 'partner_id': address_id}" groups="account.group_account_manager"/>
|
||||
<button name="reset_expense_sheets" states="cancel" string="Resubmit" type="object"/>
|
||||
<button name="reset_expense_sheets" string="Reset to Draft" type="object" attrs="{'invisible': ['|', ('can_reset', '=', False), ('state', 'not in', ['submit', 'cancel'])]}"/>
|
||||
<button name="%(hr_expense.hr_expense_refuse_wizard_action)d" states="submit,approve" context="{'hr_expense_refuse_model':'hr.expense.sheet'}" string="Refuse" type="action" groups="hr_expense.group_hr_expense_user" />
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,submit,approve,post,done"/>
|
||||
</header>
|
||||
@@ -578,6 +581,7 @@
|
||||
<field name="department_id" string="Department"/>
|
||||
<field name="journal_id"/>
|
||||
<separator />
|
||||
<filter domain="[('state', '=', 'draft')]" string="Draft" name="draft" help="Expenses in Draft"/>
|
||||
<filter domain="[('state', '=', 'submit')]" string="To Approve" name="submitted" help="Confirmed Expenses"/>
|
||||
<filter domain="[('state', '=', 'approve')]" string="To Post" name="to_post" help="Approved Expenses"/>
|
||||
<filter domain="['|', ('state', '=', 'approve'), ('state', '=', 'post')]" string="To Pay" name="approved" help="Expenses to Invoice"/>
|
||||
@@ -777,7 +781,7 @@
|
||||
</record>
|
||||
|
||||
<record id="hr_expense_submit_action_server" model="ir.actions.server">
|
||||
<field name="name">Submit To Manager</field>
|
||||
<field name="name">Create Report</field>
|
||||
<field name="type">ir.actions.server</field>
|
||||
<field name="model_id" ref="model_hr_expense"/>
|
||||
<field name="binding_model_id" ref="model_hr_expense"/>
|
||||
|
||||
Reference in New Issue
Block a user