Expenses To Approve
diff --git a/addons/hr_expense/models/__init__.py b/addons/hr_expense/models/__init__.py
index c600bfe5848..af1e04d2dd2 100644
--- a/addons/hr_expense/models/__init__.py
+++ b/addons/hr_expense/models/__init__.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
+from . import hr_employee
from . import account_move_line
from . import hr_department
from . import hr_expense
diff --git a/addons/hr_expense/models/hr_employee.py b/addons/hr_expense/models/hr_employee.py
new file mode 100644
index 00000000000..c4388789e15
--- /dev/null
+++ b/addons/hr_expense/models/hr_employee.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import fields, models
+
+class Employee(models.Model):
+ _inherit = 'hr.employee'
+
+ expense_manager_id = fields.Many2one(
+ 'res.users', string='Expense Responsible',
+ domain=lambda self: [('groups_id', 'in', self.env.ref('hr_expense.group_hr_expense_manager').id)],
+ help="User responsible of expense approval. Should be Expense Manager.")
diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py
index e07c10f46ac..0d50cf95af8 100644
--- a/addons/hr_expense/models/hr_expense.py
+++ b/addons/hr_expense/models/hr_expense.py
@@ -29,13 +29,13 @@ class HrExpense(models.Model):
def _default_account_id(self):
return self.env['ir.property'].get('property_account_expense_categ_id', 'product.category')
- name = fields.Char(string='Description', readonly=True, required=True, states={'draft': [('readonly', False)], 'refused': [('readonly', False)]})
- date = fields.Date(readonly=True, states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}, default=fields.Date.context_today, string="Date")
- employee_id = fields.Many2one('hr.employee', string="Employee", required=True, readonly=True, states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}, default=_default_employee_id)
- product_id = fields.Many2one('product.product', string='Product', readonly=True, states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}, domain=[('can_be_expensed', '=', True)], required=True)
+ name = fields.Char('Description', readonly=True, required=True, states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]})
+ date = fields.Date(readonly=True, states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}, default=fields.Date.context_today, string="Date")
+ employee_id = fields.Many2one('hr.employee', string="Employee", required=True, readonly=True, states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}, default=_default_employee_id)
+ product_id = fields.Many2one('product.product', string='Product', readonly=True, states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}, domain=[('can_be_expensed', '=', True)], required=True)
product_uom_id = fields.Many2one('product.uom', string='Unit of Measure', required=True, 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)], 'refused': [('readonly', False)]}, digits=dp.get_precision('Product Price'))
- quantity = fields.Float(required=True, readonly=True, states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}, digits=dp.get_precision('Product Unit of Measure'), default=1)
+ 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)]})
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'))
@@ -45,7 +45,7 @@ class HrExpense(models.Model):
company_currency_id = fields.Many2one('res.currency', string="Report Company Currency", related='sheet_id.currency_id', store=True)
analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account', states={'post': [('readonly', True)], 'done': [('readonly', True)]}, oldname='analytic_account')
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")
- description = fields.Text()
+ 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)"),
("company_account", "Company")
@@ -53,8 +53,9 @@ class HrExpense(models.Model):
attachment_number = fields.Integer('Number of Attachments', compute='_compute_attachment_number')
state = fields.Selection([
('draft', 'To Submit'),
- ('reported', 'Reported'),
- ('done', 'Posted'),
+ ('reported', 'Submitted'),
+ ('approved', 'Approved'),
+ ('done', 'Paid'),
('refused', 'Refused')
], compute='_compute_state', string='Status', copy=False, index=True, readonly=True, store=True, help="Status of the expense.")
sheet_id = fields.Many2one('hr.expense.sheet', string="Expense Report", readonly=True, copy=False)
@@ -68,6 +69,8 @@ class HrExpense(models.Model):
expense.state = "draft"
elif expense.sheet_id.state == "cancel":
expense.state = "refused"
+ elif expense.sheet_id.state == "approve" or expense.sheet_id.state == "post":
+ expense.state = "approved"
elif not expense.sheet_id.account_move_id:
expense.state = "reported"
else:
@@ -120,8 +123,8 @@ class HrExpense(models.Model):
@api.multi
def unlink(self):
for expense in self:
- if expense.state in ['done']:
- raise UserError(_('You cannot delete a posted expense.'))
+ if expense.state in ['done', 'approved']:
+ raise UserError(_('You cannot delete a posted or approved expense.'))
return super(HrExpense, self).unlink()
@api.model
@@ -472,7 +475,7 @@ class HrExpenseSheet(models.Model):
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))
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")
- responsible_id = fields.Many2one('res.users', 'Validation By', readonly=True, copy=False, states={'submit': [('readonly', False)]})
+ responsible_id = fields.Many2one('res.users', 'Manager', readonly=True, copy=False, states={'submit': [('readonly', False)]}, track_visibility='onchange')
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)
@@ -503,6 +506,7 @@ class HrExpenseSheet(models.Model):
def _onchange_employee_id(self):
self.address_id = self.employee_id.address_home_id
self.department_id = self.employee_id.department_id
+ self.responsible_id = self.employee_id.expense_manager_id
@api.multi
@api.constrains('expense_line_ids')
@@ -530,8 +534,6 @@ class HrExpenseSheet(models.Model):
res = super(HrExpenseSheet, self).write(vals)
if vals.get('employee_id'):
self._add_followers()
- if vals.get('responsible_id'):
- self.message_subscribe([self.responsible_id.partner_id.id])
return res
@api.multi
@@ -556,6 +558,8 @@ class HrExpenseSheet(models.Model):
return 'hr_expense.mt_expense_refused'
elif 'state' in init_values and self.state == 'done':
return 'hr_expense.mt_expense_paid'
+ elif 'responsible_id' in init_values:
+ return 'hr_expense.mt_expense_responsible'
return super(HrExpenseSheet, self)._track_subtype(init_values)
def _get_users_to_subscribe(self, employee=False):
@@ -587,8 +591,8 @@ class HrExpenseSheet(models.Model):
for partner in users.mapped('partner_id'):
values['message_follower_ids'] += MailFollowers._add_follower_command(self._name, [], {partner.id: None}, {})[0]
- if vals.get('responsible_id') and vals.get('responsible_id') != vals.get('employee_id'):
- resp_partner = self.env['res.users'].browse(vals['responsible_id'])
+ if values.get('responsible_id') and values.get('responsible_id') != values.get('employee_id'):
+ resp_partner = self.env['res.users'].browse(values['responsible_id'])
values['message_follower_ids'] += MailFollowers._add_follower_command(self._name, [], {resp_partner.partner_id.id: None}, {})[0]
# --------------------------------------------
diff --git a/addons/hr_expense/static/src/js/tour.js b/addons/hr_expense/static/src/js/tour.js
index 2c757c5e62e..7f53ae364cc 100644
--- a/addons/hr_expense/static/src/js/tour.js
+++ b/addons/hr_expense/static/src/js/tour.js
@@ -22,7 +22,7 @@ tour.register('hr_expense_tour' ,
position: 'bottom'
}, {
trigger: '.o_form_button_save',
- extra_trigger: ".o_expense_form",
+ extra_trigger: ".o_expense_sheet",
content: _t("Once your Expense report is ready, you can save it and wait for the approval from your manager.
"),
position: 'bottom',
}, {
diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml
index 6c15c9bdda9..e18e85ddacc 100644
--- a/addons/hr_expense/views/hr_expense_views.xml
+++ b/addons/hr_expense/views/hr_expense_views.xml
@@ -1,6 +1,19 @@
+
+ hr.employee.view.form.expense
+ hr.employee
+
+
+
+
+
+
+
+
+
+
hr.expense.tree
hr.expense
@@ -82,9 +95,9 @@
@@ -126,8 +139,8 @@
-
+
@@ -174,9 +187,9 @@
-
+
-
+
@@ -361,7 +374,7 @@
form
graph,pivot
{
- 'search_default_uncancelled': 1,
+ 'search_default_approved': 1,
}
diff --git a/addons/sale_expense/views/hr_expense_views.xml b/addons/sale_expense/views/hr_expense_views.xml
index 007e017595c..8af3d92c768 100644
--- a/addons/sale_expense/views/hr_expense_views.xml
+++ b/addons/sale_expense/views/hr_expense_views.xml
@@ -3,7 +3,7 @@
hr.expense.form.inherit.sale.expense
hr.expense
-
+