[MERGE] hr_expense: Usability review + Mail gateway integration with alias_contacs + Fixes
Merge branch 'saas-12-expense-fixes-yti' into saas-12
This commit is contained in:
@@ -29,5 +29,16 @@ Leave Management (keep track of employee leaves), Expense Management (manage emp
|
||||
<field name="image" eval="obj(ref('base.partner_root')).image" model="res.partner"/>
|
||||
</record>
|
||||
|
||||
<record id="mail_template_data_unknown_employee_email_address" model="mail.template">
|
||||
<field name="name">HR: unknown employee bounce</field>
|
||||
<field name="subject">Your document has not been created</field>
|
||||
<field name="email_from">${user.email}</field>
|
||||
<field name="email_to">${ctx['email_to']|safe}</field>
|
||||
<field name="lang">${user.lang}</field>
|
||||
<field name="model_id" ref="base.model_ir_module_module"/>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
<field name="body_html" ><![CDATA[Your document has not been created because your email address is not recognized. Please send emails with the email address recorded on your employee information, or contact your HR manager.]]></field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -2,5 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import hr
|
||||
import mail_alias
|
||||
import mail_thread
|
||||
import res_partner
|
||||
import res_users
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class Alias(models.Model):
|
||||
_inherit = 'mail.alias'
|
||||
|
||||
alias_contact = fields.Selection(selection_add=[('employees', 'Authenticated Employees')])
|
||||
@@ -0,0 +1,55 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
|
||||
from odoo import api, models
|
||||
from odoo.addons.mail.models.mail_thread import decode_header
|
||||
from odoo.tools import email_split
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class MailThread(models.AbstractModel):
|
||||
_inherit = "mail.thread"
|
||||
|
||||
@api.model
|
||||
def message_route_verify(self, message, message_dict, route, update_author=True, assert_model=True, create_fallback=True, allow_private=False):
|
||||
res = super(MailThread, self).message_route_verify(message, message_dict, route, update_author, assert_model, create_fallback, allow_private)
|
||||
|
||||
if res:
|
||||
alias = route[4]
|
||||
email_from = decode_header(message, 'From')
|
||||
message_id = message.get('Message-Id')
|
||||
|
||||
# Identically equal to the definition in mail module because sub methods are local
|
||||
# variables and cannot be called with super
|
||||
def _create_bounce_email(body_html):
|
||||
bounce_to = decode_header(message, 'Return-Path') or email_from
|
||||
bounce_mail_values = {
|
||||
'body_html': body_html,
|
||||
'subject': 'Re: %s' % message.get('subject'),
|
||||
'email_to': bounce_to,
|
||||
'auto_delete': True,
|
||||
}
|
||||
bounce_from = self.env['ir.mail_server']._get_default_bounce_address()
|
||||
if bounce_from:
|
||||
bounce_mail_values['email_from'] = 'MAILER-DAEMON <%s>' % bounce_from
|
||||
self.env['mail.mail'].create(bounce_mail_values).send()
|
||||
|
||||
def _warn(message):
|
||||
_logger.info('Routing mail with Message-Id %s: route %s: %s',
|
||||
message_id, route, message)
|
||||
|
||||
# Alias: check alias_contact settings for employees
|
||||
|
||||
if alias and alias.alias_contact == 'employees':
|
||||
email_address = email_split(email_from)[0]
|
||||
employee = self.env['hr.employee'].search([('work_email', 'ilike', email_address)], limit=1)
|
||||
if not employee:
|
||||
employee = self.env['hr.employee'].search([('user_id.email', 'ilike', email_address)], limit=1)
|
||||
if not employee:
|
||||
mail_template = self.env.ref('hr.mail_template_data_unknown_employee_email_address')
|
||||
_warn('alias %s does not accept unknown employees, skipping' % alias.alias_name)
|
||||
_create_bounce_email(mail_template.body_html)
|
||||
return False
|
||||
return res
|
||||
@@ -17,6 +17,7 @@
|
||||
<field name="alias_name">expense</field>
|
||||
<field name="alias_model_id" ref="model_hr_expense"/>
|
||||
<field name="alias_user_id" ref="base.user_root"/>
|
||||
<field name="alias_contact">employees</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -41,18 +42,24 @@
|
||||
<field name="name">Approved</field>
|
||||
<field name="res_model">hr.expense.sheet</field>
|
||||
<field name="default" eval="True"/>
|
||||
<field name="description">Expense approved</field>
|
||||
<field name="description">Expense report approved</field>
|
||||
</record>
|
||||
<record id="mt_expense_refused" model="mail.message.subtype">
|
||||
<field name="name">Refused</field>
|
||||
<field name="res_model">hr.expense.sheet</field>
|
||||
<field name="default" eval="True"/>
|
||||
<field name="description">Expense refused</field>
|
||||
<field name="description">Expense report refused</field>
|
||||
</record>
|
||||
<record id="mt_expense_confirmed" model="mail.message.subtype">
|
||||
<field name="name">To Approve</field>
|
||||
<field name="name">Submitted</field>
|
||||
<field name="res_model">hr.expense.sheet</field>
|
||||
<field name="description">Expense confirmed, waiting confirmation</field>
|
||||
<field name="description">Expense report submitted, waiting approval</field>
|
||||
<field name="default" eval="False"/>
|
||||
</record>
|
||||
<record id="mt_expense_paid" model="mail.message.subtype">
|
||||
<field name="name">Paid</field>
|
||||
<field name="res_model">hr.expense.sheet</field>
|
||||
<field name="description">Expense report paid</field>
|
||||
<field name="default" eval="False"/>
|
||||
</record>
|
||||
<!-- Department (Parent) related subtypes for messaging / Chatter -->
|
||||
@@ -65,15 +72,5 @@
|
||||
<field name="sequence" eval="10"/>
|
||||
</record>
|
||||
|
||||
<record id="mail_template_data_expense_unknown_email_address" model="mail.template">
|
||||
<field name="name">HrExpense: unknown employee bounce</field>
|
||||
<field name="subject">Your expense has not been created</field>
|
||||
<field name="email_from">help@odoo.com</field>
|
||||
<field name="email_to">${ctx['email_to']|safe}</field>
|
||||
<field name="lang">${user.lang}</field>
|
||||
<field name="model_id" ref="base.model_ir_module_module"/>
|
||||
<field name="auto_delete" eval="True"/>
|
||||
<field name="body_html" ><![CDATA[Your expense has not been created because your email address is not set on an employee or on a employee's user. Configure your employee's information correctly and try again.]]></field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -33,13 +33,14 @@ class HrExpense(models.Model):
|
||||
description = fields.Text()
|
||||
payment_mode = fields.Selection([("own_account", "Employee (to reimburse)"), ("company_account", "Company")], default='own_account', states={'done': [('readonly', True)], 'post': [('readonly', True)]}, string="Payment By")
|
||||
attachment_number = fields.Integer(compute='_compute_attachment_number', string='Number of Attachments')
|
||||
state = fields.Selection([('draft', 'To Report'),
|
||||
('reported', 'Reported'),
|
||||
('done', 'Posted'),
|
||||
('refused', 'Refused')
|
||||
], compute='_compute_state', default="draft", string='Status', index=True, readonly=True, copy=False, required=True, store=True,
|
||||
state = fields.Selection([
|
||||
('draft', 'To Report'),
|
||||
('reported', 'Reported'),
|
||||
('done', 'Posted'),
|
||||
('refused', 'Refused')
|
||||
], compute='_compute_state', string='Status', copy=False, default="draft", index=True, readonly=True, required=True, store=True,
|
||||
help="Status of the expense.")
|
||||
sheet_id = fields.Many2one('hr.expense.sheet', string="Expense Report", readonly=True)
|
||||
sheet_id = fields.Many2one('hr.expense.sheet', string="Expense Report", readonly=True, copy=False)
|
||||
reference = fields.Char(string="Bill Reference")
|
||||
|
||||
@api.depends('sheet_id', 'sheet_id.account_move_id', 'sheet_id.state')
|
||||
@@ -100,6 +101,8 @@ class HrExpense(models.Model):
|
||||
def submit_expenses(self):
|
||||
if any(expense.state != 'draft' 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!"))
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_mode': 'form',
|
||||
@@ -290,26 +293,13 @@ class HrExpense(models.Model):
|
||||
if custom_values is None:
|
||||
custom_values = {}
|
||||
|
||||
# Retrieve the email address from the email field. The string is constructed like
|
||||
# 'foo <bar>'. We will extract 'bar' from this
|
||||
email_address = email_split(msg_dict.get('email_from', False))[0]
|
||||
|
||||
# Look after an employee who has this email address or an employee for whom the related
|
||||
# user has this email address. In the case not employee is found, we send back an email
|
||||
# to explain that the expense will not be created.
|
||||
employee = self.env['hr.employee'].search([('work_email', 'ilike', email_address)], limit=1)
|
||||
if not employee:
|
||||
employee = self.env['hr.employee'].search([('user_id.email', 'ilike', email_address)], limit=1)
|
||||
if not employee:
|
||||
access = self.env['ir.config_parameter'].get_param("hr_expense.email.gateway") or 'open'
|
||||
if access == 'open':
|
||||
# Open access is mostly used during the on boarding flow, should not be used in production
|
||||
employee = self.env['hr.employee'].search([('user_id','<>',False)], limit=1)
|
||||
if not employee:
|
||||
# Send back an email to explain why the expense has not been created
|
||||
mail_template = self.env.ref('hr_expense.mail_template_data_expense_unknown_email_address')
|
||||
mail_template.with_context(email_to=email_address).send_mail(self.env.ref('base.module_hr_expense').id)
|
||||
return False
|
||||
employee = self.env['hr.employee'].search([
|
||||
'|',
|
||||
('work_email', 'ilike', email_address),
|
||||
('user_id.email', 'ilike', email_address)
|
||||
], limit=1)
|
||||
|
||||
expense_description = msg_dict.get('subject', '')
|
||||
|
||||
@@ -381,7 +371,7 @@ class HrExpenseSheet(models.Model):
|
||||
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=lambda self: self.env['account.journal'].search([('type', 'in', ['case', 'bank'])], limit=1), help="The payment method used when the expense is paid by the company.")
|
||||
accounting_date = fields.Date(string="Accounting Date")
|
||||
account_move_id = fields.Many2one('account.move', string='Journal Entry', copy=False, track_visibility="onchange")
|
||||
account_move_id = fields.Many2one('account.move', string='Journal Entry', copy=False)
|
||||
department_id = fields.Many2one('hr.department', string='Department', states={'post': [('readonly', True)], 'done': [('readonly', True)]})
|
||||
|
||||
@api.multi
|
||||
@@ -422,6 +412,8 @@ class HrExpenseSheet(models.Model):
|
||||
return 'hr_expense.mt_expense_confirmed'
|
||||
elif 'state' in init_values and self.state == 'cancel':
|
||||
return 'hr_expense.mt_expense_refused'
|
||||
elif 'state' in init_values and self.state == 'done':
|
||||
return 'hr_expense.mt_expense_paid'
|
||||
return super(HrExpenseSheet, self)._track_subtype(init_values)
|
||||
|
||||
def _add_followers(self):
|
||||
@@ -474,16 +466,8 @@ class HrExpenseSheet(models.Model):
|
||||
|
||||
@api.multi
|
||||
def approve_expense_sheets(self):
|
||||
self.message_post(body=_("The expense has been validated by %s") % (self.env.user.name))
|
||||
self.write({'state': 'approve', 'responsible_id': self.env.user.id})
|
||||
|
||||
@api.multi
|
||||
def refuse_expense_sheets(self, reason):
|
||||
self.write({'state': 'cancel'})
|
||||
for sheet in self:
|
||||
body = (_("Your Expense %s has been refused.<br/><ul class=o_timeline_tracking_value_list><li>Reason<span> : </span><span class=o_timeline_tracking_value>%s</span></li></ul>") % (sheet.name, reason))
|
||||
sheet.message_post(body=body)
|
||||
|
||||
@api.multi
|
||||
def paid_expense_sheets(self):
|
||||
self.write({'state': 'done'})
|
||||
|
||||
@@ -1,29 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import api, fields, models
|
||||
from openerp import SUPERUSER_ID
|
||||
|
||||
class HrExpenseConfigSettings(models.TransientModel):
|
||||
_name = 'hr.expense.config.settings'
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
alias_expense_open = fields.Selection([
|
||||
('open', "Accept expenses from any email address"),
|
||||
('restricted', "Accept expenses from employee email addresses only")
|
||||
], "Mail Gateway")
|
||||
|
||||
alias_prefix = fields.Char('Default Alias Name for Expenses')
|
||||
alias_domain = fields.Char('Alias Domain', default=lambda self: self.env["ir.config_parameter"].get_param("mail.catchall.domain"))
|
||||
|
||||
@api.model
|
||||
def get_default_alias_expense_open(self, fields):
|
||||
return {'alias_expense_open': self.env['ir.config_parameter'].get_param("hr_expense.email.gateway") or 'open'}
|
||||
|
||||
@api.multi
|
||||
def set_default_alias_expense_open(self):
|
||||
self.env['ir.config_parameter'].set_param("hr_expense.email.gateway", self.alias_expense_open)
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def get_default_alias_prefix(self, fields):
|
||||
alias_name = self.env.ref('hr_expense.mail_alias_expense').alias_name
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_expenses_tree" model="ir.ui.view">
|
||||
<field name="name">hr.expense.tree</field>
|
||||
<field name="model">hr.expense</field>
|
||||
@@ -187,7 +188,7 @@
|
||||
<record id="hr_expense_actions_all" model="ir.actions.act_window">
|
||||
<field name="name">All Expenses</field>
|
||||
<field name="res_model">hr.expense</field>
|
||||
<field name="view_mode">tree,kanban,form,graph,pivot</field>
|
||||
<field name="view_mode">pivot,graph,tree,kanban,form</field>
|
||||
<field name="search_view_id" ref="view_hr_expense_filter"/>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
@@ -204,26 +205,6 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_expense_actions_my_all" model="ir.actions.act_window">
|
||||
<field name="name">My Expenses</field>
|
||||
<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="domain">[('employee_id.user_id', '=', uid)]</field>
|
||||
<field name="context">{'show_report': True}</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
The steps to process expenses are:
|
||||
<ol>
|
||||
<li>You record expenses,</li>
|
||||
<li>Periodically, you submit expenses report to your manager,</li>
|
||||
<li>Once approved, your expense report, goes to the accounting,</li>
|
||||
<li>The accountant validates entries and reimburse you.</li>
|
||||
</ol>
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_expense_actions_my_unsubmitted" model="ir.actions.act_window">
|
||||
<field name="name">My Expenses to Report</field>
|
||||
<field name="res_model">hr.expense</field>
|
||||
@@ -244,25 +225,24 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_expense_tree_action" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="0"/>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="view_expenses_tree"/>
|
||||
<field name="act_window_id" ref="hr_expense_actions_all"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_expense_kanban_action" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="1"/>
|
||||
<field name="view_mode">kanban</field>
|
||||
<field name="view_id" ref="hr_expense_kanban_view"/>
|
||||
<field name="act_window_id" ref="hr_expense_actions_all"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_expense_form_action" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="2"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="hr_expense_form_view"/>
|
||||
<field name="act_window_id" ref="hr_expense_actions_all"/>
|
||||
<record id="action_hr_expense_refused_expenses" model="ir.actions.act_window">
|
||||
<field name="name">Refused Expenses</field>
|
||||
<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="domain">[('employee_id.user_id', '=', uid)]</field>
|
||||
<field name="context">{'search_default_refused': 1}</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
The steps to process expenses are:
|
||||
<ol>
|
||||
<li>You record expenses,</li>
|
||||
<li>Periodically, you submit expenses report to your manager,</li>
|
||||
<li>Once approved, your expense report, goes to the accounting,</li>
|
||||
<li>The accountant validates entries and reimburse you.</li>
|
||||
</ol>
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_product_hr_expense_form" model="ir.ui.view">
|
||||
@@ -412,6 +392,7 @@
|
||||
<field name="name" string="Expense Sheet"/>
|
||||
<field name="accounting_date"/>
|
||||
<field name="employee_id"/>
|
||||
<field name="currency_id" invisible="1"/>
|
||||
<field name="total_amount" sum="Total Amount" widget="monetary" options="{'currency_field': 'currency_id'}"/>
|
||||
<field name="state"/>
|
||||
<field name="message_unread" invisible="1"/>
|
||||
@@ -578,6 +559,7 @@
|
||||
<filter string="Department" domain="[]" context="{'group_by': 'department_id'}" groups="hr.group_multi_departments"/>
|
||||
<filter string="Company" domain="[]" context="{'group_by': 'company_id'}" groups="base.group_multi_company"/>
|
||||
<filter string="Expenses Month" domain="[]" context="{'group_by': 'accounting_date'}" help="Expenses by Month"/>
|
||||
<filter string="State" domain="[]" context="{'group_by': 'state'}" name="state"/>
|
||||
</group>
|
||||
<separator />
|
||||
<filter domain="[('employee_id.active', '=', False)]" string="Former Employees" name="inactive" groups="base.group_hr_user,base.group_hr_manager"/>
|
||||
@@ -585,22 +567,6 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_hr_expense_sheet_my_to_approve" model="ir.actions.act_window">
|
||||
<field name="name">My Reports</field>
|
||||
<field name="res_model">hr.expense.sheet</field>
|
||||
<field name="view_mode">tree,kanban,form,pivot,graph</field>
|
||||
<field name="search_view_id" ref="view_hr_expense_sheet_filter"/>
|
||||
<field name="domain">[('employee_id.user_id', '=', uid)]</field>
|
||||
<field name="context">{'search_default_submitted': 1}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">
|
||||
Click here to create a new expense report.
|
||||
</p><p>
|
||||
Once you have created your expense, submit it to your manager who will validate it.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_hr_expense_sheet_my_all" model="ir.actions.act_window">
|
||||
<field name="name">My Reports</field>
|
||||
<field name="res_model">hr.expense.sheet</field>
|
||||
@@ -668,7 +634,7 @@
|
||||
<record id="action_hr_expense_sheet_all_all" model="ir.actions.act_window">
|
||||
<field name="name">My Reports</field>
|
||||
<field name="res_model">hr.expense.sheet</field>
|
||||
<field name="view_mode">tree,kanban,form,pivot,graph</field>
|
||||
<field name="view_mode">pivot,graph,tree,kanban,form</field>
|
||||
<field name="search_view_id" ref="view_hr_expense_sheet_filter"/>
|
||||
<field name="domain">[]</field>
|
||||
<field name="context">{}</field>
|
||||
@@ -778,23 +744,24 @@
|
||||
|
||||
<menuitem id="menu_hr_expense_root" name="Expenses" sequence="100" web_icon="hr_expense,static/description/icon.png"/>
|
||||
|
||||
<menuitem id="menu_hr_expense_my_expenses" name="My Expenses" sequence="1" parent="menu_hr_expense_root"/>
|
||||
<menuitem id="menu_hr_expense_my_unsubmitted" name="My Expenses to Report" sequence="10" parent="menu_hr_expense_my_expenses" action="hr_expense_actions_my_unsubmitted"/>
|
||||
<menuitem id="menu_hr_expense_my_all" name="All Expenses" sequence="2" parent="menu_hr_expense_my_expenses"
|
||||
action="hr_expense_actions_my_all"/>
|
||||
<menuitem id="menu_hr_expense_my_expenses" name="My Expenses" sequence="1" parent="menu_hr_expense_root" groups="base.group_user"/>
|
||||
<menuitem id="menu_hr_expense_my_expenses_to_submit" name="Expenses to Submit" sequence="1" parent="menu_hr_expense_my_expenses" action="hr_expense_actions_my_unsubmitted"/>
|
||||
<menuitem id="menu_hr_expense_my_expenses_refused" name="Refused Expenses" sequence="2" parent="menu_hr_expense_my_expenses" action="action_hr_expense_refused_expenses"/>
|
||||
<menuitem id="menu_hr_expense_sheet_my_reports" name="Expense Reports" sequence="3" parent="menu_hr_expense_my_expenses" action="action_hr_expense_sheet_my_all" load_xmlid="True"/>
|
||||
|
||||
<menuitem id="menu_hr_expense_sheet_my_reports" name="My Reports" sequence="15" parent="menu_hr_expense_root"/>
|
||||
<menuitem id="menu_hr_expense_sheet_my_to_approve" name="To Approve" sequence="1" parent="menu_hr_expense_sheet_my_reports" action="action_hr_expense_sheet_my_to_approve"/>
|
||||
<menuitem id="menu_hr_expense_to_approve" name="To Approve" sequence="2" parent="menu_hr_expense_root" groups="base.group_hr_user"/>
|
||||
<menuitem id="menu_hr_expense_sheet_all_to_approve" name="Expense Reports To Approve" sequence="1" parent="menu_hr_expense_to_approve"
|
||||
action="action_hr_expense_sheet_all_to_approve"/>
|
||||
|
||||
<menuitem id="menu_hr_expense_accountant" name="Accountant" sequence="3" parent="menu_hr_expense_root" groups="base.group_hr_manager"/>
|
||||
<menuitem id="menu_hr_expense_sheet_all_to_post" name="Expense Reports To Post" sequence="2" parent="menu_hr_expense_accountant"
|
||||
action="action_hr_expense_sheet_all_to_post"/>
|
||||
<menuitem id="menu_hr_expense_sheet_all_to_pay" name="Expense Reports To Pay" sequence="3" parent="menu_hr_expense_accountant"
|
||||
action="action_hr_expense_sheet_all_to_pay"/>
|
||||
|
||||
<menuitem id="menu_hr_expense_sheet_my_all" name="All Reports" sequence="2" parent="menu_hr_expense_sheet_my_reports" action="action_hr_expense_sheet_my_all" load_xmlid="True"/>
|
||||
<menuitem id="menu_hr_expense_sheet_all" name="All Reports" sequence="20" parent="menu_hr_expense_root"/>
|
||||
<menuitem id="menu_hr_expense_sheet_all_to_approve" name="Reports To Approve" sequence="1" parent="menu_hr_expense_sheet_all"
|
||||
action="action_hr_expense_sheet_all_to_approve" groups="base.group_hr_user"/>
|
||||
<menuitem id="menu_hr_expense_sheet_all_to_post" name="Reports To Post" sequence="2" parent="menu_hr_expense_sheet_all"
|
||||
action="action_hr_expense_sheet_all_to_post" groups="base.group_hr_manager"/>
|
||||
<menuitem id="menu_hr_expense_sheet_all_to_pay" name="Reports To Pay" sequence="3" parent="menu_hr_expense_sheet_all"
|
||||
action="action_hr_expense_sheet_all_to_pay" groups="base.group_hr_manager"/>
|
||||
<menuitem id="menu_hr_expense_sheet_all_all" name="All Reports" sequence="4" parent="menu_hr_expense_sheet_all"
|
||||
<menuitem id="menu_hr_expense_reports" name="Reports" sequence="4" parent="menu_hr_expense_root" groups="base.group_hr_manager"/>
|
||||
<menuitem id="menu_hr_expense_all_expenses" name="All Expenses" sequence="0" parent="menu_hr_expense_reports" action="hr_expense_actions_all"/>
|
||||
<menuitem id="menu_hr_expense_sheet_all_all" name="All Expense Reports" sequence="4" parent="menu_hr_expense_reports"
|
||||
action="action_hr_expense_sheet_all_all" groups="base.group_hr_manager"/>
|
||||
|
||||
<menuitem id="menu_hr_expense_configuration" name="Configuration" parent="menu_hr_expense_root"
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
<button string="Cancel" type="object" name="cancel" class="oe_link"/>
|
||||
</header>
|
||||
<group string="Expenses" name="config_alias">
|
||||
<field name="alias_expense_open" widget="radio"/>
|
||||
<label for="id" string="Expense Email Alias"/>
|
||||
<div>
|
||||
<div attrs="{'invisible': [('alias_domain', 'in', ['localhost', '', False])]}">
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from openerp.exceptions import ValidationError
|
||||
|
||||
from werkzeug import url_encode
|
||||
|
||||
class HrExpenseRegisterPaymentWizard(models.TransientModel):
|
||||
|
||||
@@ -76,7 +76,7 @@ class HrExpenseRegisterPaymentWizard(models.TransientModel):
|
||||
payment.post()
|
||||
|
||||
# Log the payment in the chatter
|
||||
body = (_("A payment of %s %s with the reference %s related to your expense %s has been made.") % (payment.amount, payment.currency_id.symbol, payment.name, expense_sheet.name))
|
||||
body = (_("A payment of %s %s with the reference <a href='/mail/view?%s'>%s</a> related to your expense %s has been made.") % (payment.amount, payment.currency_id.symbol, url_encode({'model': 'account.payment', 'res_id': payment.id}), payment.name, expense_sheet.name))
|
||||
expense_sheet.message_post(body=body)
|
||||
|
||||
# Reconcile the payment and the expense, i.e. lookup on the payable account move lines
|
||||
|
||||
Reference in New Issue
Block a user