| REPOSITORY | |
|---|---|
| REPOSITORY | odoo/odoo |
| GIT | |
| GIT | https://github.com/odoo/odoo.git |
| GIT FOLDER | |
| GIT FOLDER | https://github.com/odoo/odoo/tree/19.0/hr_expense |
| VERSION | |
| VERSION | 2.1 |
| CATEGORY | |
| CATEGORY | Human Resources/Expenses |
| LICENSE | |
| LICENSE | LGPL-3 |
| APPLICATION | |
| APPLICATION | Yes |
| AUTO-INSTALLABLE | |
| AUTO-INSTALLABLE | No |
| AUTHORS | |
| AUTHORS | Odoo S.A. |
| MAINTAINERS | |
| MAINTAINERS | Odoo S.A. |
| COMMITTERS | |
| COMMITTERS | Raphael Collet, Odoo Translation Bot, Thibault Delavallée, Xavier Morel, Aaron Bohy, Yannick Tivisse, Pierre Masereel, Laurent Smet, Julien Castiaux, Adrien Dieudonné, Anh Thao Pham (pta), Brice bib Bartoletti, Prakash Prajapati, Tiffany Chang (tic), Gorash, Mathieu Duckerts-Antoine, Pierre Paridans, william-andre, JF Aubert, Antoine Guenet, Louis Baudoux, Renaud Thiry, jbw-odoo, Gaetan Vanden Bergh (gavb), Louis Wicket (wil), Maximilien (malb), Victor Piryns (pivi), Mahamadasif Ansari, Thomas Becquevort (thbe), Gauthier Wala (gawa), Antoine Dupuis (andu), Dylan Kiss (dyki), hupo-odoo, Julien Alardot (jual), Chong Wang (cwg), Xavier Bol (xbo), Arnaud Sibille, omra-odoo, Nicolas Viseur (vin), Paul Stroobant, maad-odoo, Seif (segn), kcv-odoo, mega, Louis Gobert, aamo-odoo, Patricia Daloze (pda), Abdelrahman Mahmoud (amah), gteboul, sabr, Saif (segn), Victor Miguel Armenta Carrillo, Mohammad Abdulmoneim (abdu), Pierre Lamotte (pila), Michaël Mattiello, Pierre Pulinckx, Bastien PIERRE, pish-odoo, Christihan Laurel [Vauxoo], Krzysztof Magusiak (krma), krip-odoo, Meet, hatr-odoo, Hadi El Yakhni, sagu-odoo, bhra-odoo, bhna-odoo, mojem, agbr-odoo, igbe, Dhwani Patel, Jeanne Delneste, malb, h4818, leclerc-leo, Amr Elkhatieb, jond-odoo, rapa, Mohamed Barakat, saurabh, smjo-odoo, Mauricio Rubio Haro, chpa-odoo, Oussema Heni, 3amo-magdy, Mathias Mathy, Léo Leclerc |
| WEBSITE | |
| WEBSITE | https://www.odoo.com/app/expenses |
| LAST TRACKING UPDATE | |
| LAST TRACKING UPDATE | 2026-07-06 01:51:43 |
| ODOO DEPENDENCIES | |
| ODOO DEPENDENCIES |
odoo/odoo: - account - base_setup - base - web - onboarding - product - bus - web_tour - html_editor - uom - analytic - portal - http_routing - auth_signup - digest - resource - hr - phone_validation - resource_mail |
| PYTHON DEPENDENCIES | |
| PYTHON DEPENDENCIES | Not have |
| SYSTEM DEPENDENCIES | |
| SYSTEM DEPENDENCIES | Not have |
| DESCRIPTION | |
| DESCRIPTION |
Manage expenses by Employees
============================
This application allows you to manage your employees' daily expenses. It gives you access to your employees’ fee notes and give you the right to complete and validate or refuse the notes. After validation it creates an invoice for the employee.
Employee can encode their own expenses and the validation flow puts it automatically in the accounting after validation by managers.
The whole flow is implemented as:
---------------------------------
* Draft expense
* Submitted by the employee to his manager
* Approved by his manager
* Validation by the accountant and accounting entries creation
This module also uses analytic accounting and is compatible with the invoice on timesheet module so that you are able to automatically re-invoice your customers' expenses if your work by project.
|
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
hr_department_view_kanban |
hr.department.kanban.inherit | hr.department | data | Inherits hr.hr_department_view_kanban |
hr_employee_search_view |
hr.employee.search.view | hr.employee | xpath | Inherits hr.view_employee_filter |
hr_employee_view_form_inherit_expense |
hr.employee.view.form.expense | hr.employee | xpath | Inherits hr.view_employee_form |
hr_expense.hr_expense_post_wizard_view |
Post Expenses | hr.expense.post.wizard | form | New |
hr_expense_approve_duplicate_view_form |
hr.expense.approve.duplicate | form | New | |
hr_expense_kanban_view |
hr.expense.kanban | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_kanban |
hr_expense_kanban_view_header |
hr.expense.kanban | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_kanban |
hr_expense_kanban_view_minimal |
hr.expense.kanban | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_kanban |
hr_expense_refuse_wizard_view_form |
hr.expense.refuse.wizard.form | hr.expense.refuse.wizard | form | New |
hr_expense_split |
Expense split | hr.expense.split.wizard | form | New |
hr_expense_template_refuse_reason |
hr_expense_template_refuse_reason | ir.ui.view | qweb | New |
hr_expense_template_register |
hr_expense_template_register | ir.ui.view | qweb | New |
hr_expense_template_register_no_user |
hr_expense_template_register_no_user | ir.ui.view | qweb | New |
hr_expense_template_submitted_expenses |
hr_expense_template_submitted_expenses | ir.ui.view | qweb | New |
hr_expense_view_activity |
hr.expense.activity | hr.expense | activity | New |
hr_expense_view_expenses_analysis_kanban |
hr.expense.kanban | hr.expense | kanban | New |
hr_expense_view_expenses_analysis_tree |
hr.expense.list | hr.expense | list | New |
hr_expense_view_form |
hr.expense.view.form | hr.expense | form | New |
hr_expense_view_form_without_header |
hr.expense.view.form | hr.expense | xpath | Inherits hr_expense.hr_expense_view_form |
hr_expense_view_graph |
hr.expense.graph | hr.expense | graph | New |
hr_expense_view_pivot |
hr.expense.pivot | hr.expense | pivot | New |
hr_expense_view_search |
hr.expense.view.search | hr.expense | search | New |
hr_expense_view_search_with_panel |
hr.expense.view.search.with.panel | hr.expense | xpath | Inherits hr_expense_view_search |
product_product_expense_categories_tree_view |
product.product.expense.categories.list.view | product.product | list | New |
product_product_expense_form_view |
product.product.expense.form | product.product | form | New |
product_product_expense_kanban_view |
product.product.kanban.expense | product.product | xpath | Inherits product.product_kanban_view |
product_product_expense_tree_view |
product.product.expense.list | product.product | list | New |
product_template_search_view_inherit_hr_expense |
product.template.search.view.inherit.hr_expense | product.template | filter | Inherits product.product_template_search_view |
report_expense |
report_expense | ir.ui.view | qweb | New |
report_expense_img |
report_expense_img | ir.ui.view | qweb | New |
res_config_settings_view_form |
res.config.settings.view.form.inherit.hr.expense | res.config.settings | xpath | Inherits base.res_config_settings_view_form |
view_employee_tree_inherit_expense |
hr.employee.list.expense | hr.employee | xpath | Inherits hr.view_employee_tree |
view_expenses_tree |
hr.expense.list | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_tree |
view_move_form_inherit_expense |
account.move.form.inherit | account.move | xpath | Inherits account.view_move_form |
view_move_list_expense |
account.move.hr.expense.list | account.move | list | New |
view_my_expenses_tree |
hr.expense.list | hr.expense | xpath | Inherits hr_expense.view_expenses_tree |
view_payment_form_inherit_expense |
account.payment.form.inherit | account.payment | xpath | Inherits account.view_account_payment_form |
view_product_hr_expense_form |
product.template.expense.form | product.template | span | Inherits product.product_template_form_view |
No new fields.
Public methods (0)No public methods.
business_domain
Selection
ondelete={'expense': 'cascade'}
selection_add=[('expense', 'Expense')]
No public methods.
expense_ids
One2many → hr.expense
comodel_name='hr.expense'
inverse_name='account_move_id'
nb_expenses
Integer
compute='_compute_nb_expenses'
compute_sudo=True
string='Number of Expenses'
action_open_expense(self)
button_cancel(self)
expense_id
Many2one → hr.expense
copy=True
index='btree_not_null'
string='Expense'
args: 'hr.expense'
No public methods.
expense_ids
One2many
related='move_id.expense_ids'
action_open_expense(self)
write(self, vals)
No new fields.
Public methods (0)No public methods.
No new fields.
Public methods (0)No public methods.
expenses_to_approve_count
Integer
compute='_compute_expenses_to_approve_count'
string='Expenses to Approve'
No public methods.
expense_manager_id
Many2one → res.users
comodel_name='res.users'
compute='_compute_expense_manager'
domain=_group_hr_expense_user_domain
help='Select the user responsible for approving "Expenses" of this employee.\nIf empty, the approval is done by an Administrator or Approver (determined in settings/users).'
readonly=False
store=True
string='Expense Approver'
filter_for_expense
Boolean
groups='hr.group_hr_user'
search='_search_filter_for_expense'
store=False
No public methods.
expense_manager_id
Many2one → res.users
readonly=True
args: 'res.users'
No public methods.
filter_for_expense
Boolean
groups='hr.group_hr_user'
search='_search_filter_for_expense'
store=False
No public methods.
account_id
Many2one → account.account
check_company=True
comodel_name='account.account'
compute='_compute_account_id'
domain="[('account_type', 'not in', ('asset_receivable', 'liability_payable', 'asset_cash', 'liability_credit_card'))]"
help='An expense account is expected'
precompute=True
readonly=False
store=True
string='Account'
account_move_id
Many2one → account.move
comodel_name='account.move'
copy=False
index='btree_not_null'
readonly=True
string='Journal Entry'
amount_residual
Monetary
currency_field='company_currency_id'
readonly=True
related='account_move_id.amount_residual'
string='Amount Due'
approval_date
Datetime
readonly=True
string='Approval Date'
approval_state
Selection
copy=False
readonly=True
selection=EXPENSE_APPROVAL_STATE
attachment_ids
One2many → ir.attachment
comodel_name='ir.attachment'
domain=[('res_model', '=', 'hr.expense')]
inverse_name='res_id'
string='Attachments'
can_approve
Boolean
compute='_compute_can_approve'
readonly=True
string='Can Approve'
can_reset
Boolean
compute='_compute_can_reset'
readonly=True
string='Can Reset'
company_currency_id
Many2one → res.currency
comodel_name='res.currency'
readonly=True
related='company_id.currency_id'
string='Report Company Currency'
company_id
Many2one → res.company
comodel_name='res.company'
default=<expr>
readonly=True
required=True
string='Company'
currency_id
Many2one → res.currency
comodel_name='res.currency'
compute='_compute_currency_id'
default=<expr>
precompute=True
readonly=False
required=True
store=True
string='Currency'
currency_rate
Float
compute='_compute_currency_rate'
digits=(16, 9)
readonly=True
tracking=True
date
Date
default=fields.Date.context_today
string='Expense Date'
department_id
Many2one → hr.department
comodel_name='hr.department'
compute='_compute_from_employee_id'
copy=False
store=True
string='Department'
description
Text
string='Internal Notes'
duplicate_expense_ids
Many2many → hr.expense
comodel_name='hr.expense'
compute='_compute_duplicate_expense_ids'
employee_id
Many2one → hr.employee
check_company=True
comodel_name='hr.employee'
compute='_compute_employee_id'
default=_default_employee_id
domain=[('filter_for_expense', '=', True)]
index=True
precompute=True
readonly=False
required=True
store=True
string='Employee'
tracking=True
former_sheet_id
Integer
string='Former Report'
is_editable
Boolean
compute='_compute_is_editable'
readonly=True
string='Is Editable By Current User'
is_multiple_currency
Boolean
compute='_compute_is_multiple_currency'
string='Is currency_id different from the company_currency_id'
journal_id
Many2one → account.journal
comodel_name='account.journal'
readonly=True
related='payment_method_line_id.journal_id'
label_currency_rate
Char
compute='_compute_currency_rate'
readonly=True
manager_id
Many2one → res.users
comodel_name='res.users'
compute='_compute_from_employee_id'
copy=False
domain=<expr>
store=True
string='Manager'
tracking=True
message_main_attachment_checksum
Char
related='message_main_attachment_id.checksum'
name
Char
compute='_compute_name'
copy=True
precompute=True
readonly=False
required=True
store=True
string='Description'
nb_attachment
Integer
compute='_compute_nb_attachment'
string='Number of Attachments'
payment_method_line_id
Many2one → account.payment.method.line
comodel_name='account.payment.method.line'
compute='_compute_payment_method_line_id'
domain="[('id', 'in', selectable_payment_method_line_ids)]"
help='The payment method used when the expense is paid by the company.'
readonly=False
store=True
string='Payment Method'
payment_mode
Selection
default='own_account'
required=True
selection=[('own_account', 'Employee (to reimburse)'), ('company_account', 'Company')]
string='Paid By'
tracking=True
price_unit
Float
compute='_compute_price_unit'
copy=True
min_display_digits='Product Price'
precompute=True
readonly=True
required=True
store=True
string='Unit Price'
product_description
Html
compute='_compute_product_description'
product_has_cost
Boolean
compute='_compute_from_product'
product_has_tax
Boolean
compute='_compute_from_product'
string='Whether tax is defined on a selected product'
product_id
Many2one → product.product
check_company=True
comodel_name='product.product'
domain=[('can_be_expensed', '=', True)]
ondelete='restrict'
string='Category'
tracking=True
product_uom_id
Many2one → uom.uom
comodel_name='uom.uom'
compute='_compute_uom_id'
copy=True
precompute=True
store=True
string='Unit'
quantity
Float
default=1
digits='Product Unit'
required=True
same_receipt_expense_ids
Many2many → hr.expense
comodel_name='hr.expense'
compute='_compute_same_receipt_expense_ids'
selectable_payment_method_line_ids
Many2many → account.payment.method.line
comodel_name='account.payment.method.line'
compute='_compute_selectable_payment_method_line_ids'
compute_sudo=True
split_expense_origin_id
Many2one → hr.expense
comodel_name='hr.expense'
help='Original expense from a split.'
string='Origin Split Expense'
state
Selection
compute='_compute_state'
copy=False
default='draft'
index=True
readonly=True
selection=[('draft', 'Draft'), ('submitted', 'Submitted'), ('approved', 'Approved'), ('posted', 'Posted'), ('in_payment', 'In Payment'), ('paid', 'Paid'), ('refused', 'Refused')]
store=True
string='Status'
tracking=True
tax_amount
Monetary
compute='_compute_tax_amount'
currency_field='company_currency_id'
help='Tax amount in company currency'
precompute=True
store=True
string='Tax amount'
tax_amount_currency
Monetary
compute='_compute_tax_amount_currency'
currency_field='currency_id'
help='Tax amount in currency'
precompute=True
store=True
string='Tax amount in Currency'
tax_ids
Many2many → account.tax
check_company=True
column1='expense_id'
column2='tax_id'
comodel_name='account.tax'
compute='_compute_tax_ids'
domain="[('type_tax_use', '=', 'purchase')]"
help='Both price-included and price-excluded taxes will behave as price-included taxes for expenses.'
precompute=True
readonly=False
relation='expense_tax'
store=True
string='Included taxes'
total_amount
Monetary
compute='_compute_total_amount'
currency_field='company_currency_id'
inverse='_inverse_total_amount'
precompute=True
readonly=False
store=True
string='Total'
tracking=True
total_amount_currency
Monetary
compute='_compute_total_amount_currency'
currency_field='currency_id'
precompute=True
readonly=False
store=True
string='Total In Currency'
tracking=True
untaxed_amount
Monetary
compute='_compute_tax_amount'
currency_field='currency_id'
precompute=True
store=True
string='Total Untaxed Amount'
untaxed_amount_currency
Monetary
compute='_compute_tax_amount_currency'
currency_field='currency_id'
precompute=True
store=True
string='Total Untaxed Amount In Currency'
vendor_id
Many2one → res.partner
comodel_name='res.partner'
string='Vendor'
action_approve(self)
action_approve_duplicates(self)
action_open_account_move(self)
action_open_split_expense(self)
action_pay(self)
action_post(self)
action_refuse(self)
action_reset(self)
action_show_same_receipt_expense_ids(self)
action_split_wizard(self)
action_submit(self)
attach_document(self, **kwargs)
create(self, vals_list)
create_expense_from_attachments(self, attachment_ids=None, view_type='list')
get_empty_list_help(self, help_message)
get_expense_dashboard(self)
message_new(self, msg_dict, custom_values=None)
update_activities_and_mails(self)
write(self, vals)
expense_ids
Many2many → hr.expense
readonly=True
args: 'hr.expense'
action_approve(self)
action_refuse(self)
default_get(self, fields)
accounting_date
Date
default=fields.Date.context_today
help='Specify the bill date of the related vendor bill.'
string='Accounting Date'
company_id
Many2one → res.company
comodel_name='res.company'
default=<expr>
readonly=True
string='Company'
employee_journal_id
Many2one → account.journal
check_company=True
comodel_name='account.journal'
default=_default_journal_id
domain=[('type', '=', 'purchase')]
help='The journal used when the expense is paid by employee.'
string='Journal'
action_post_entry(self)
expense_ids
Many2many → hr.expense
comodel_name='hr.expense'
reason
Char
required=True
string='Reason'
action_refuse(self)
default_get(self, fields)
approval_date
Datetime
readonly=True
string='Approval Date'
approval_state
Selection
copy=False
readonly=True
selection=EXPENSE_APPROVAL_STATE
company_id
Many2one → res.company
comodel_name='res.company'
currency_id
Many2one → res.currency
comodel_name='res.currency'
employee_id
Many2one → hr.employee
comodel_name='hr.employee'
required=True
string='Employee'
expense_id
Many2one → hr.expense
comodel_name='hr.expense'
string='Expense'
manager_id
Many2one → res.users
comodel_name='res.users'
domain=<expr>
readonly=True
string='Manager'
name
Char
required=True
string='Description'
product_has_cost
Boolean
compute='_compute_from_product_id'
store=True
string='Is product with non zero cost selected'
product_has_tax
Boolean
compute='_compute_product_has_tax'
string='Whether tax is defined on a selected product'
product_id
Many2one → product.product
check_company=True
comodel_name='product.product'
domain=[('can_be_expensed', '=', True)]
required=True
string='Product'
tax_amount_currency
Monetary
compute='_compute_tax_amount_currency'
string='Tax amount in Currency'
tax_ids
Many2many → account.tax
check_company=True
comodel_name='account.tax'
domain="[('type_tax_use', '=', 'purchase')]"
total_amount_currency
Monetary
compute='_compute_from_product_id'
readonly=False
required=True
store=True
string='Total In Currency'
wizard_id
Many2one → hr.expense.split.wizard
comodel_name='hr.expense.split.wizard'
default_get(self, fields)
currency_id
Many2one → res.currency
comodel_name='res.currency'
related='expense_id.currency_id'
expense_id
Many2one → hr.expense
comodel_name='hr.expense'
required=True
string='Expense'
expense_split_line_ids
One2many → hr.expense.split
comodel_name='hr.expense.split'
inverse_name='wizard_id'
split_possible
Boolean
compute='_compute_split_possible'
help='The sum of after split shut remain the same'
tax_amount_currency
Monetary
compute='_compute_tax_amount_currency'
currency_field='currency_id'
string='Taxes'
total_amount_currency
Monetary
compute='_compute_total_amount_currency'
currency_field='currency_id'
string='Total Amount'
total_amount_currency_original
Monetary
currency_field='currency_id'
help='Total amount of the original Expense that we are splitting'
related='expense_id.total_amount_currency'
string='Total amount original'
action_split_expense(self)
No new fields.
Public methods (0)No public methods.
No new fields.
Public methods (1)create(self, vals_list)
standard_price_update_warning
Char
compute='_compute_standard_price_update_warning'
write(self, vals)
can_be_expensed
Boolean
compute='_compute_can_be_expensed'
help='Specify whether the product can be selected in an expense.'
readonly=False
store=True
string='Expenses'
default_get(self, fields)
company_expense_allowed_payment_method_line_ids
Many2many → account.payment.method.line
check_company=True
domain="[('payment_type', '=', 'outbound'), ('journal_id', '!=', False), ('journal_id.active', '=', True)]"
string='Payment methods available for expenses paid by company'
args: 'account.payment.method.line'
expense_journal_id
Many2one → account.journal
check_company=True
domain="[('type', '=', 'purchase')]"
help="The company's default journal used when an employee expense is created."
string='Default Expense Journal'
args: 'account.journal'
No public methods.
company_expense_allowed_payment_method_line_ids
Many2many → account.payment.method.line
check_company=True
comodel_name='account.payment.method.line'
readonly=False
related='company_id.company_expense_allowed_payment_method_line_ids'
expense_journal_id
Many2one → account.journal
check_company=True
domain="[('type', '=', 'purchase')]"
readonly=False
related='company_id.expense_journal_id'
args: 'account.journal'
hr_expense_alias_domain_id
Many2one → mail.alias.domain
comodel_name='mail.alias.domain'
compute='_compute_hr_expense_alias_domain_id'
inverse='_inverse_hr_expense_alias_domain_id'
readonly=False
hr_expense_alias_prefix
Char
compute='_compute_hr_expense_alias_prefix'
readonly=False
store=True
args: 'Default Alias Name for Expenses'
hr_expense_use_mailgateway
Boolean
config_parameter='hr_expense.use_mailgateway'
string='Let your employees record expenses by email'
module_hr_expense_extract
Boolean
string='Send bills to OCR to generate expenses'
module_hr_expense_stripe
Boolean
string='Link your stripe issuing account to manage company credit cards for your employees through Odoo'
module_hr_payroll_expense
Boolean
string='Reimburse Expenses in Payslip'
get_values(self)
set_values(self)
| REPOSITORY | |
|---|---|
| REPOSITORY | odoo/odoo |
| GIT | |
| GIT | https://github.com/odoo/odoo.git |
| GIT FOLDER | |
| GIT FOLDER | https://github.com/odoo/odoo/tree/18.0/hr_expense |
| VERSION | |
| VERSION | 2.0 |
| CATEGORY | |
| CATEGORY | Human Resources/Expenses |
| LICENSE | |
| LICENSE | LGPL-3 |
| APPLICATION | |
| APPLICATION | Yes |
| AUTO-INSTALLABLE | |
| AUTO-INSTALLABLE | No |
| AUTHORS | |
| AUTHORS | |
| MAINTAINERS | |
| MAINTAINERS | |
| COMMITTERS | |
| COMMITTERS | Raphael Collet, Odoo Translation Bot, Martin Trigaux, Thibault Delavallée, Christophe Simonis, Xavier Morel, qdp-odoo, Aaron Bohy, Damien Bouvy, Yannick Tivisse, Odoo Online, Laurent Smet, Jorge Pinna Puissant, Victor Feyens, Andrea Grazioso (agr-odoo), Sébastien Theys, Anh Thao Pham (pta), Achraf (abz), Brice bib Bartoletti, Prakash Prajapati, Tiffany Chang (tic), Gorash, Romain Estievenart, sofiagvaladze, william-andre, Víctor Martínez, Didier (did), Florian Vranckx, assellalou, Hubert Van De Walle, jbw-odoo, alsh-odoo, Louis Wicket (wil), tsm-odoo, Maximilien (malb), Victor Piryns (pivi), Yash Vaishnav, Thomas Becquevort (thbe), Gauthier Wala (gawa), Ali Alfie (alal), Pierre-Yves Dufays, Claire Bretton (clbr), Antoine Dupuis (andu), Antoine Boonen, Dylan Kiss (dyki), Julien Alardot (jual), Chong Wang (cwg), Leloup Loïc (lole), Cameron, Arnaud Sibille, Noe Antoine, omra-odoo, sesn-odoo, guva-odoo, kdeb@odoo.com, Paul Stroobant, Hesham, ravb-odoo, Mélanie, Maryam Kia, Maxime de Neuville, Sven Fuehr, Seif (segn), yosa-odoo, Ruben Gomes (rugo), Nathaniel (naja), jobl, Julien Carion (juca), kcv-odoo, moerradi, yaih-odoo, mega, Louis Gobert, aath-odoo, Louis (wil), asno-odoo, mano-odoo, Mathieu Coutant, gteboul, sabr, Pierre Pulinckx (PIPU), Victor Miguel Armenta Carrillo, Trinh Ngoc Hung, Maira Salazar (srma), elhayyany, khsr-odoo, Pierre Lamotte (pila), Pierre Pulinckx (pipu), laro-odoo, Pierre Pulinckx, Bastien PIERRE, pish-odoo, Christihan Laurel [Vauxoo], devp-odoo, Martin Quinteros, Christopher Rogos, twalter-c2c, Meet, Hadi El Yakhni, bhra-odoo, sawer, igbe, Dhwani Patel, late-odoo, leclerc-leo, Logan Staelens (lost), jond-odoo, saurabh |
| WEBSITE | |
| WEBSITE | https://www.odoo.com/app/expenses |
| LAST TRACKING UPDATE | |
| LAST TRACKING UPDATE | 2026-07-06 01:25:56 |
| ODOO DEPENDENCIES | |
| ODOO DEPENDENCIES |
odoo/odoo: - account - base_setup - base - web - onboarding - product - bus - web_tour - html_editor - uom - analytic - portal - web_editor - http_routing - auth_signup - digest - resource - hr - phone_validation - resource_mail |
| PYTHON DEPENDENCIES | |
| PYTHON DEPENDENCIES | Not have |
| SYSTEM DEPENDENCIES | |
| SYSTEM DEPENDENCIES | Not have |
| DESCRIPTION | |
| DESCRIPTION |
Manage expenses by Employees
============================
This application allows you to manage your employees' daily expenses. It gives you access to your employees’ fee notes and give you the right to complete and validate or refuse the notes. After validation it creates an invoice for the employee.
Employee can encode their own expenses and the validation flow puts it automatically in the accounting after validation by managers.
The whole flow is implemented as:
---------------------------------
* Draft expense
* Submitted by the employee to his manager
* Approved by his manager
* Validation by the accountant and accounting entries creation
This module also uses analytic accounting and is compatible with the invoice on timesheet module so that you are able to automatically re-invoice your customers' expenses if your work by project.
|
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
hr_department_view_kanban |
hr.department.kanban.inherit | hr.department | data | Inherits hr.hr_department_view_kanban |
hr_employee_view_form_inherit_expense |
hr.employee.view.form.expense | hr.employee | xpath | Inherits hr.view_employee_form |
hr_expense_approve_duplicate_view_form |
hr.expense.approve.duplicate | form | New | |
hr_expense_kanban_view |
hr.expense.kanban | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_kanban |
hr_expense_kanban_view_header |
hr.expense.kanban | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_kanban |
hr_expense_kanban_view_minimal |
hr.expense.kanban | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_kanban |
hr_expense_refuse_wizard_view_form |
hr.expense.refuse.wizard.form | hr.expense.refuse.wizard | form | New |
hr_expense_sheet_view_activity |
hr.expense.sheet.activity | hr.expense.sheet | activity | New |
hr_expense_sheet_view_search |
hr.expense.sheet.view.search | hr.expense.sheet | search | New |
hr_expense_sheet_view_search_with_panel |
hr.expense.sheet.view.search.with.panel | hr.expense.sheet | xpath | Inherits hr_expense_sheet_view_search |
hr_expense_split |
Expense split | hr.expense.split.wizard | form | New |
hr_expense_template_refuse_reason |
hr_expense_template_refuse_reason | ir.ui.view | qweb | New |
hr_expense_template_register |
hr_expense_template_register | ir.ui.view | qweb | New |
hr_expense_template_register_no_user |
hr_expense_template_register_no_user | ir.ui.view | qweb | New |
hr_expense_view_activity |
hr.expense.activity | hr.expense | activity | New |
hr_expense_view_expenses_analysis_kanban |
hr.expense.kanban | hr.expense | kanban | New |
hr_expense_view_expenses_analysis_tree |
hr.expense.list | hr.expense | list | New |
hr_expense_view_form |
hr.expense.view.form | hr.expense | form | New |
hr_expense_view_form_without_header |
hr.expense.view.form | hr.expense | xpath | Inherits hr_expense.hr_expense_view_form |
hr_expense_view_graph |
hr.expense.graph | hr.expense | graph | New |
hr_expense_view_pivot |
hr.expense.pivot | hr.expense | pivot | New |
hr_expense_view_search |
hr.expense.view.search | hr.expense | search | New |
product_product_expense_categories_tree_view |
product.product.expense.categories.list.view | product.product | list | New |
product_product_expense_form_view |
product.product.expense.form | product.product | form | New |
product_product_expense_kanban_view |
product.product.kanban.expense | product.product | xpath | Inherits product.product_kanban_view |
product_product_expense_tree_view |
product.product.expense.list | product.product | list | New |
product_template_search_view_inherit_hr_expense |
product.template.search.view.inherit.hr_expense | product.template | filter | Inherits product.product_template_search_view |
report_expense_sheet |
report_expense_sheet | ir.ui.view | qweb | New |
report_expense_sheet_img |
report_expense_sheet_img | ir.ui.view | qweb | New |
res_config_settings_view_form |
res.config.settings.view.form.inherit.hr.expense | res.config.settings | xpath | Inherits base.res_config_settings_view_form |
res_users_view_form_preferences |
hr.user.preferences.form.inherit.hr.expense | res.users | xpath | Inherits hr.res_users_view_form_profile |
view_employee_tree_inherit_expense |
hr.employee.list.expense | hr.employee | xpath | Inherits hr.view_employee_tree |
view_expenses_tree |
hr.expense.list | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_tree |
view_hr_expense_sheet_dashboard_tree_header |
hr.expense.sheet.dashboard.list | hr.expense.sheet | xpath | Inherits hr_expense.view_hr_expense_sheet_tree |
view_hr_expense_sheet_form |
hr.expense.sheet.form | hr.expense.sheet | form | New |
view_hr_expense_sheet_graph |
hr.expense.sheet.graph | hr.expense.sheet | graph | New |
view_hr_expense_sheet_kanban |
hr.expense.sheet.kanban | hr.expense.sheet | kanban | New |
view_hr_expense_sheet_kanban_header |
hr.expense.sheet.kanban | hr.expense.sheet | xpath | Inherits view_hr_expense_sheet_kanban |
view_hr_expense_sheet_kanban_no_header |
hr.expense.sheet.kanban | hr.expense.sheet | xpath | Inherits view_hr_expense_sheet_kanban |
view_hr_expense_sheet_pivot |
hr.expense.sheet.pivot | hr.expense.sheet | pivot | New |
view_hr_expense_sheet_tree |
hr.expense.sheet.list | hr.expense.sheet | list | New |
view_move_form_inherit_expense |
account.move.form.inherit | account.move | xpath | Inherits account.view_move_form |
view_my_expenses_tree |
hr.expense.list | hr.expense | xpath | Inherits hr_expense.view_expenses_tree |
view_payment_form_inherit_expense |
account.payment.form.inherit | account.payment | xpath | Inherits account.view_account_payment_form |
view_product_hr_expense_form |
product.template.expense.form | product.template | div | Inherits product.product_template_form_view |
No new fields.
Public methods (0)No public methods.
business_domain
Selection
ondelete={'expense': 'cascade'}
selection_add=[('expense', 'Expense')]
No public methods.
expense_sheet_id
Many2one → hr.expense.sheet
comodel_name='hr.expense.sheet'
copy=False
index='btree_not_null'
ondelete='set null'
show_commercial_partner_warning
Boolean
compute='_compute_show_commercial_partner_warning'
action_open_expense_report(self)
button_cancel(self)
expense_id
Many2one → hr.expense
copy=True
index='btree_not_null'
string='Expense'
args: 'hr.expense'
No public methods.
expense_sheet_id
Many2one
related='move_id.expense_sheet_id'
action_open_expense_report(self)
write(self, vals)
No new fields.
Public methods (0)No public methods.
No new fields.
Public methods (0)No public methods.
expense_sheets_to_approve_count
Integer
compute='_compute_expense_sheets_to_approve'
string='Expenses Reports to Approve'
No public methods.
expense_manager_id
Many2one → res.users
comodel_name='res.users'
compute='_compute_expense_manager'
domain=_group_hr_expense_user_domain
help='Select the user responsible for approving "Expenses" of this employee.\nIf empty, the approval is done by an Administrator or Approver (determined in settings/users).'
readonly=False
store=True
string='Expense'
No public methods.
filter_for_expense
Boolean
groups='hr.group_hr_user'
search='_search_filter_for_expense'
store=False
No public methods.
expense_manager_id
Many2one → res.users
readonly=True
args: 'res.users'
No public methods.
account_id
Many2one → account.account
check_company=True
comodel_name='account.account'
compute='_compute_account_id'
domain="[('account_type', 'not in', ('asset_receivable', 'liability_payable', 'asset_cash', 'liability_credit_card'))]"
help='An expense account is expected'
precompute=True
readonly=False
store=True
string='Account'
accounting_date
Date
groups='account.group_account_invoice,account.group_account_readonly'
related='sheet_id.accounting_date'
store=True
string='Accounting Date'
approved_by
Many2one → res.users
comodel_name='res.users'
related='sheet_id.user_id'
string='Approved By'
tracking=False
approved_on
Datetime
related='sheet_id.approval_date'
string='Approved On'
attachment_ids
One2many → ir.attachment
comodel_name='ir.attachment'
domain=[('res_model', '=', 'hr.expense')]
inverse_name='res_id'
string='Attachments'
company_currency_id
Many2one → res.currency
comodel_name='res.currency'
readonly=True
related='company_id.currency_id'
string='Report Company Currency'
company_id
Many2one → res.company
comodel_name='res.company'
default=<expr>
readonly=True
required=True
string='Company'
currency_id
Many2one → res.currency
comodel_name='res.currency'
compute='_compute_currency_id'
default=<expr>
precompute=True
readonly=False
required=True
store=True
string='Currency'
currency_rate
Float
compute='_compute_currency_rate'
digits=(16, 9)
readonly=True
tracking=True
date
Date
default=fields.Date.context_today
string='Expense Date'
description
Text
string='Internal Notes'
duplicate_expense_ids
Many2many → hr.expense
comodel_name='hr.expense'
compute='_compute_duplicate_expense_ids'
employee_id
Many2one → hr.employee
check_company=True
comodel_name='hr.employee'
compute='_compute_employee_id'
default=_default_employee_id
domain=[('filter_for_expense', '=', True)]
precompute=True
readonly=False
required=True
store=True
string='Employee'
tracking=True
is_editable
Boolean
compute='_compute_is_editable'
string='Is Editable By Current User'
is_multiple_currency
Boolean
compute='_compute_is_multiple_currency'
string='Is currency_id different from the company_currency_id'
label_currency_rate
Char
compute='_compute_currency_rate'
readonly=True
message_main_attachment_checksum
Char
related='message_main_attachment_id.checksum'
name
Char
compute='_compute_name'
copy=True
precompute=True
readonly=False
required=True
store=True
string='Description'
nb_attachment
Integer
compute='_compute_nb_attachment'
string='Number of Attachments'
payment_mode
Selection
default='own_account'
required=True
selection=[('own_account', 'Employee (to reimburse)'), ('company_account', 'Company')]
string='Paid By'
tracking=True
price_unit
Float
compute='_compute_price_unit'
copy=True
min_display_digits='Product Price'
precompute=True
readonly=True
required=True
store=True
string='Unit Price'
product_description
Html
compute='_compute_product_description'
product_has_cost
Boolean
compute='_compute_from_product'
product_has_tax
Boolean
compute='_compute_from_product'
string='Whether tax is defined on a selected product'
product_id
Many2one → product.product
check_company=True
comodel_name='product.product'
domain=[('can_be_expensed', '=', True)]
ondelete='restrict'
string='Category'
tracking=True
product_uom_category_id
Many2one → uom.category
comodel_name='uom.category'
readonly=True
related='product_id.uom_id.category_id'
string='UoM Category'
product_uom_id
Many2one → uom.uom
comodel_name='uom.uom'
compute='_compute_uom_id'
copy=True
domain="[('category_id', '=', product_uom_category_id)]"
precompute=True
store=True
string='Unit of Measure'
quantity
Float
default=1
digits='Product Unit of Measure'
required=True
same_receipt_expense_ids
Many2many → hr.expense
comodel_name='hr.expense'
compute='_compute_same_receipt_expense_ids'
sheet_id
Many2one → hr.expense.sheet
comodel_name='hr.expense.sheet'
copy=False
domain="[('employee_id', '=', employee_id), ('company_id', '=', company_id)]"
index=True
readonly=True
string='Expense Report'
state
Selection
compute='_compute_state'
copy=False
default='draft'
index=True
readonly=True
selection=[('draft', 'To Report'), ('reported', 'To Submit'), ('submitted', 'Submitted'), ('approved', 'Approved'), ('done', 'Done'), ('refused', 'Refused')]
store=True
string='Status'
tax_amount
Monetary
compute='_compute_tax_amount'
currency_field='company_currency_id'
help='Tax amount in company currency'
precompute=True
store=True
string='Tax amount'
tax_amount_currency
Monetary
compute='_compute_tax_amount_currency'
currency_field='currency_id'
help='Tax amount in currency'
precompute=True
store=True
string='Tax amount in Currency'
tax_ids
Many2many → account.tax
check_company=True
column1='expense_id'
column2='tax_id'
comodel_name='account.tax'
compute='_compute_tax_ids'
domain="[('type_tax_use', '=', 'purchase')]"
help='Both price-included and price-excluded taxes will behave as price-included taxes for expenses.'
precompute=True
readonly=False
relation='expense_tax'
store=True
string='Included taxes'
total_amount
Monetary
compute='_compute_total_amount'
currency_field='company_currency_id'
inverse='_inverse_total_amount'
precompute=True
readonly=False
store=True
string='Total'
tracking=True
total_amount_currency
Monetary
compute='_compute_total_amount_currency'
currency_field='currency_id'
precompute=True
readonly=False
store=True
string='Total In Currency'
tracking=True
untaxed_amount_currency
Monetary
compute='_compute_tax_amount_currency'
currency_field='currency_id'
precompute=True
store=True
string='Total Untaxed Amount In Currency'
vendor_id
Many2one → res.partner
comodel_name='res.partner'
string='Vendor'
action_approve_duplicates(self)
action_get_attachment_view(self)
action_show_same_receipt_expense_ids(self)
action_split_wizard(self)
action_submit_expenses(self)
action_view_sheet(self)
attach_document(self, **kwargs)
check_amount_not_zero(self, vals)
create(self, vals_list)
create_expense_from_attachments(self, attachment_ids=None, view_type='list')
get_empty_list_help(self, help_message)
get_expense_attachments(self)
get_expense_dashboard(self)
get_expenses_to_submit(self)
message_new(self, msg_dict, custom_values=None)
unlink(self)
write(self, vals)
expense_ids
Many2many → hr.expense
readonly=True
args: 'hr.expense'
sheet_ids
Many2many → hr.expense.sheet
action_approve(self)
action_refuse(self)
default_get(self, fields)
reason
Char
required=True
string='Reason'
sheet_ids
Many2many → hr.expense.sheet
action_refuse(self)
default_get(self, fields)
account_move_ids
One2many → account.move
comodel_name='account.move'
inverse_name='expense_sheet_id'
readonly=True
string='Journal Entries'
accounting_date
Date
help='Specify the bill date of the related vendor bill.'
string='Expense Report Date'
amount_residual
Monetary
compute='_compute_from_account_move_ids'
currency_field='company_currency_id'
readonly=True
store=True
string='Amount Due'
approval_date
Datetime
readonly=True
string='Approval Date'
approval_state
Selection
copy=False
selection=[('submit', 'Submitted'), ('approve', 'Approved'), ('cancel', 'Refused')]
attachment_ids
One2many → ir.attachment
comodel_name='ir.attachment'
domain="[('res_model', '=', 'hr.expense.sheet')]"
inverse_name='res_id'
string='Attachments of expenses'
can_approve
Boolean
compute='_compute_can_approve'
string='Can Approve'
can_reset
Boolean
compute='_compute_can_reset'
string='Can Reset'
cannot_approve_reason
Char
compute='_compute_can_approve'
string='Cannot Approve Reason'
company_currency_id
Many2one → res.currency
comodel_name='res.currency'
related='company_id.currency_id'
string='Report Company Currency'
company_id
Many2one → res.company
comodel_name='res.company'
default=<expr>
readonly=True
required=True
string='Company'
currency_id
Many2one → res.currency
comodel_name='res.currency'
compute='_compute_currency_id'
readonly=True
store=True
string='Currency'
department_id
Many2one → hr.department
comodel_name='hr.department'
copy=False
related='employee_id.department_id'
store=True
string='Department'
employee_id
Many2one → hr.employee
check_company=True
comodel_name='hr.employee'
default=_default_employee_id
domain=[('filter_for_expense', '=', True)]
readonly=True
required=True
string='Employee'
tracking=True
employee_journal_id
Many2one → account.journal
check_company=True
comodel_name='account.journal'
default=_default_journal_id
domain=[('type', '=', 'purchase')]
help='The journal used when the expense is paid by employee.'
string='Journal'
expense_line_ids
One2many → hr.expense
comodel_name='hr.expense'
copy=False
inverse_name='sheet_id'
string='Expense Lines'
is_editable
Boolean
compute='_compute_is_editable'
string='Expense Lines Are Editable By Current User'
is_multiple_currency
Boolean
compute='_compute_is_multiple_currency'
string='Handle lines with different currencies'
journal_id
Many2one → account.journal
check_company=True
comodel_name='account.journal'
compute='_compute_journal_id'
store=True
string='Expense Journal'
message_main_attachment_id
Many2one
compute='_compute_main_attachment'
store=True
name
Char
required=True
string='Expense Report Summary'
tracking=True
nb_account_move
Integer
compute='_compute_nb_account_move'
string='Number of Journal Entries'
nb_expense
Integer
compute='_compute_nb_expense'
string='Number of Expenses'
payment_method_line_id
Many2one → account.payment.method.line
comodel_name='account.payment.method.line'
compute='_compute_payment_method_line_id'
domain="[('id', 'in', selectable_payment_method_line_ids)]"
help='The payment method used when the expense is paid by the company.'
readonly=False
store=True
string='Payment Method'
payment_mode
Selection
readonly=True
related='expense_line_ids.payment_mode'
string='Paid By'
tracking=True
payment_state
Selection
compute='_compute_from_account_move_ids'
copy=False
readonly=True
selection=<expr>
store=True
string='Payment Status'
tracking=True
product_ids
Many2many → product.product
check_company=True
comodel_name='product.product'
compute='_compute_product_ids'
search='_search_product_ids'
string='Categories'
selectable_payment_method_line_ids
Many2many → account.payment.method.line
comodel_name='account.payment.method.line'
compute='_compute_selectable_payment_method_line_ids'
state
Selection
compute='_compute_state'
copy=False
default='draft'
index=True
readonly=True
required=True
selection=[('draft', 'To Submit'), ('submit', 'Submitted'), ('approve', 'Approved'), ('post', 'Posted'), ('done', 'Done'), ('cancel', 'Refused')]
store=True
string='Status'
tracking=True
total_amount
Monetary
compute='_compute_amount'
currency_field='company_currency_id'
readonly=True
store=True
string='Total'
tracking=True
total_tax_amount
Monetary
compute='_compute_amount'
currency_field='company_currency_id'
readonly=True
store=True
string='Taxes'
untaxed_amount
Monetary
compute='_compute_amount'
currency_field='company_currency_id'
readonly=True
store=True
string='Untaxed Amount'
user_id
Many2one → res.users
comodel_name='res.users'
compute='_compute_from_employee_id'
copy=False
domain=<expr>
readonly=True
store=True
string='Manager'
tracking=True
action_approve_expense_sheets(self)
action_open_account_moves(self)
action_open_expense_view(self)
action_refuse_expense_sheets(self)
action_register_payment(self)
action_reset_expense_sheets(self)
action_sheet_move_post(self)
action_submit_sheet(self)
activity_update(self)
create(self, vals_list)
set_to_paid(self)
set_to_posted(self)
write(self, values)
company_id
Many2one → res.company
comodel_name='res.company'
currency_id
Many2one → res.currency
comodel_name='res.currency'
employee_id
Many2one → hr.employee
comodel_name='hr.employee'
required=True
string='Employee'
expense_id
Many2one → hr.expense
comodel_name='hr.expense'
string='Expense'
name
Char
required=True
string='Description'
product_has_cost
Boolean
compute='_compute_from_product_id'
store=True
string='Is product with non zero cost selected'
product_has_tax
Boolean
compute='_compute_product_has_tax'
string='Whether tax is defined on a selected product'
product_id
Many2one → product.product
check_company=True
comodel_name='product.product'
domain=[('can_be_expensed', '=', True)]
required=True
string='Product'
tax_amount_currency
Monetary
compute='_compute_tax_amount_currency'
string='Tax amount in Currency'
tax_ids
Many2many → account.tax
check_company=True
comodel_name='account.tax'
domain="[('type_tax_use', '=', 'purchase')]"
total_amount_currency
Monetary
compute='_compute_from_product_id'
readonly=False
required=True
store=True
string='Total In Currency'
wizard_id
Many2one → hr.expense.split.wizard
comodel_name='hr.expense.split.wizard'
default_get(self, fields)
currency_id
Many2one → res.currency
comodel_name='res.currency'
related='expense_id.currency_id'
expense_id
Many2one → hr.expense
comodel_name='hr.expense'
required=True
string='Expense'
expense_split_line_ids
One2many → hr.expense.split
comodel_name='hr.expense.split'
inverse_name='wizard_id'
split_possible
Boolean
compute='_compute_split_possible'
help='The sum of after split shut remain the same'
tax_amount_currency
Monetary
compute='_compute_tax_amount_currency'
currency_field='currency_id'
string='Taxes'
total_amount_currency
Monetary
compute='_compute_total_amount_currency'
currency_field='currency_id'
string='Total Amount'
total_amount_currency_original
Monetary
currency_field='currency_id'
help='Total amount of the original Expense that we are splitting'
related='expense_id.total_amount_currency'
string='Total amount original'
action_split_expense(self)
No new fields.
Public methods (0)No public methods.
No new fields.
Public methods (2)create(self, vals_list)
unlink(self)
standard_price_update_warning
Char
compute='_compute_standard_price_update_warning'
write(self, vals)
can_be_expensed
Boolean
compute='_compute_can_be_expensed'
help='Specify whether the product can be selected in an expense.'
readonly=False
store=True
string='Expenses'
default_get(self, fields)
company_expense_allowed_payment_method_line_ids
Many2many → account.payment.method.line
check_company=True
domain="[('payment_type', '=', 'outbound'), ('journal_id', '!=', False), ('journal_id.active', '=', True)]"
string='Payment methods available for expenses paid by company'
args: 'account.payment.method.line'
expense_journal_id
Many2one → account.journal
check_company=True
domain="[('type', '=', 'purchase')]"
help="The company's default journal used when an employee expense is created."
string='Default Expense Journal'
args: 'account.journal'
expense_outstanding_account_id
Many2one → account.account
check_company=True
domain="[('account_type', '=', 'asset_current'), ('reconcile', '=', True)]"
help='The account used to record the outstanding amount of the company expenses.'
string='Outstanding Account'
args: 'account.account'
No public methods.
company_expense_allowed_payment_method_line_ids
Many2many → account.payment.method.line
check_company=True
comodel_name='account.payment.method.line'
readonly=False
related='company_id.company_expense_allowed_payment_method_line_ids'
expense_journal_id
Many2one → account.journal
check_company=True
domain="[('type', '=', 'purchase')]"
readonly=False
related='company_id.expense_journal_id'
args: 'account.journal'
expense_outstanding_account_id
Many2one → account.account
check_company=True
comodel_name='account.account'
domain="[('account_type', '=', 'asset_current'), ('reconcile', '=', True)]"
readonly=False
related='company_id.expense_outstanding_account_id'
hr_expense_alias_domain_id
Many2one → mail.alias.domain
comodel_name='mail.alias.domain'
compute='_compute_hr_expense_alias_domain_id'
inverse='_inverse_hr_expense_alias_domain_id'
readonly=False
hr_expense_alias_prefix
Char
compute='_compute_hr_expense_alias_prefix'
readonly=False
store=True
args: 'Default Alias Name for Expenses'
hr_expense_use_mailgateway
Boolean
config_parameter='hr_expense.use_mailgateway'
string='Let your employees record expenses by email'
module_hr_expense_extract
Boolean
string='Send bills to OCR to generate expenses'
module_hr_payroll_expense
Boolean
string='Reimburse Expenses in Payslip'
get_values(self)
set_values(self)
expense_manager_id
Many2one
readonly=False
related='employee_id.expense_manager_id'
SELF_READABLE_FIELDS(self)
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
account_journal_dashboard_kanban_view_inherit_hr_expense |
account.journal.dashboard.kanban | account.journal | xpath | Inherits account.account_journal_dashboard_kanban_view |
hr_department_view_kanban |
hr.department.kanban.inherit | hr.department | data | Inherits hr.hr_department_view_kanban |
hr_employee_view_form_inherit_expense |
hr.employee.view.form.expense | hr.employee | xpath | Inherits hr.view_employee_form |
hr_expense_approve_duplicate_view_form |
hr.expense.approve.duplicate | form | New | |
hr_expense_kanban_view |
hr.expense.kanban | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_kanban |
hr_expense_kanban_view_header |
hr.expense.kanban | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_kanban |
hr_expense_refuse_wizard_view_form |
hr.expense.refuse.wizard.form | hr.expense.refuse.wizard | form | New |
hr_expense_sheet_view_activity |
hr.expense.sheet.activity | hr.expense.sheet | activity | New |
hr_expense_sheet_view_search |
hr.expense.sheet.view.search | hr.expense.sheet | search | New |
hr_expense_sheet_view_search_with_panel |
hr.expense.sheet.view.search.with.panel | hr.expense.sheet | xpath | Inherits hr_expense_sheet_view_search |
hr_expense_split |
Expense split | hr.expense.split.wizard | form | New |
hr_expense_template_refuse_reason |
hr_expense_template_refuse_reason | ir.ui.view | qweb | New |
hr_expense_template_register |
hr_expense_template_register | ir.ui.view | qweb | New |
hr_expense_template_register_no_user |
hr_expense_template_register_no_user | ir.ui.view | qweb | New |
hr_expense_view_activity |
hr.expense.activity | hr.expense | activity | New |
hr_expense_view_expenses_analysis_kanban |
hr.expense.kanban | hr.expense | kanban | New |
hr_expense_view_expenses_analysis_tree |
hr.expense.tree | hr.expense | tree | New |
hr_expense_view_form |
hr.expense.view.form | hr.expense | form | New |
hr_expense_view_form_without_header |
hr.expense.view.form | hr.expense | xpath | Inherits hr_expense.hr_expense_view_form |
hr_expense_view_graph |
hr.expense.graph | hr.expense | graph | New |
hr_expense_view_pivot |
hr.expense.pivot | hr.expense | pivot | New |
hr_expense_view_search |
hr.expense.view.search | hr.expense | search | New |
product_product_expense_categories_tree_view |
product.product.expense.categories.tree.view | product.product | tree | New |
product_product_expense_form_view |
product.product.expense.form | product.product | form | New |
product_product_expense_kanban_view |
product.product.kanban.expense | product.product | xpath | Inherits product.product_kanban_view |
product_product_expense_tree_view |
product.product.expense.tree | product.product | tree | New |
product_template_search_view_inherit_hr_expense |
product.template.search.view.inherit.hr_expense | product.template | filter | Inherits product.product_template_search_view |
report_expense_sheet |
report_expense_sheet | ir.ui.view | qweb | New |
report_expense_sheet_img |
report_expense_sheet_img | ir.ui.view | qweb | New |
res_config_settings_view_form |
res.config.settings.view.form.inherit.hr.expense | res.config.settings | xpath | Inherits base.res_config_settings_view_form |
res_users_view_form_preferences |
hr.user.preferences.form.inherit.hr.expense | res.users | xpath | Inherits hr.res_users_view_form_profile |
view_employee_tree_inherit_expense |
hr.employee.tree.expense | hr.employee | xpath | Inherits hr.view_employee_tree |
view_expenses_tree |
hr.expense.tree | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_tree |
view_hr_expense_sheet_dashboard_tree_header |
hr.expense.sheet.dashboard.tree | hr.expense.sheet | xpath | Inherits hr_expense.view_hr_expense_sheet_tree |
view_hr_expense_sheet_form |
hr.expense.sheet.form | hr.expense.sheet | form | New |
view_hr_expense_sheet_graph |
hr.expense.sheet.graph | hr.expense.sheet | graph | New |
view_hr_expense_sheet_kanban |
hr.expense.sheet.kanban | hr.expense.sheet | kanban | New |
view_hr_expense_sheet_kanban_header |
hr.expense.sheet.kanban | hr.expense.sheet | xpath | Inherits view_hr_expense_sheet_kanban |
view_hr_expense_sheet_kanban_no_header |
hr.expense.sheet.kanban | hr.expense.sheet | xpath | Inherits view_hr_expense_sheet_kanban |
view_hr_expense_sheet_pivot |
hr.expense.sheet.pivot | hr.expense.sheet | pivot | New |
view_hr_expense_sheet_tree |
hr.expense.sheet.tree | hr.expense.sheet | tree | New |
view_move_form_inherit_expense |
account.move.form.inherit | account.move | xpath | Inherits account.view_move_form |
view_my_expenses_tree |
hr.expense.tree | hr.expense | xpath | Inherits hr_expense.view_expenses_tree |
view_payment_form_inherit_expense |
account.payment.form.inherit | account.payment | xpath | Inherits account.view_account_payment_form |
view_product_hr_expense_form |
product.template.expense.form | product.template | div | Inherits product.product_template_form_view |
No new fields.
Public methods (0)No public methods.
business_domain
Selection
ondelete={'expense': 'cascade'}
selection_add=[('expense', 'Expense')]
No public methods.
No new fields.
Public methods (1)open_expenses_action(self)
expense_sheet_id
Many2one → hr.expense.sheet
comodel_name='hr.expense.sheet'
copy=False
index='btree_not_null'
ondelete='set null'
show_commercial_partner_warning
Boolean
compute='_compute_show_commercial_partner_warning'
action_open_expense_report(self)
expense_id
Many2one → hr.expense
copy=True
string='Expense'
args: 'hr.expense'
No public methods.
No new fields.
Public methods (1)action_open_expense_report(self)
No new fields.
Public methods (0)No public methods.
No new fields.
Public methods (0)No public methods.
expense_sheets_to_approve_count
Integer
compute='_compute_expense_sheets_to_approve'
string='Expenses Reports to Approve'
No public methods.
expense_manager_id
Many2one → res.users
comodel_name='res.users'
compute='_compute_expense_manager'
domain=_group_hr_expense_user_domain
help='Select the user responsible for approving "Expenses" of this employee.\nIf empty, the approval is done by an Administrator or Approver (determined in settings/users).'
readonly=False
store=True
string='Expense'
No public methods.
filter_for_expense
Boolean
search='_search_filter_for_expense'
store=False
No public methods.
expense_manager_id
Many2one → res.users
readonly=True
args: 'res.users'
No public methods.
account_id
Many2one → account.account
check_company=True
comodel_name='account.account'
compute='_compute_account_id'
domain="[('account_type', 'not in', ('asset_receivable', 'liability_payable', 'asset_cash', 'liability_credit_card'))]"
help='An expense account is expected'
precompute=True
readonly=False
store=True
string='Account'
accounting_date
Date
groups='account.group_account_invoice,account.group_account_readonly'
related='sheet_id.accounting_date'
store=True
string='Accounting Date'
approved_by
Many2one → res.users
comodel_name='res.users'
related='sheet_id.user_id'
string='Approved By'
tracking=False
approved_on
Datetime
related='sheet_id.approval_date'
string='Approved On'
attachment_ids
One2many → ir.attachment
comodel_name='ir.attachment'
domain="[('res_model', '=', 'hr.expense')]"
inverse_name='res_id'
string='Attachments'
company_currency_id
Many2one → res.currency
comodel_name='res.currency'
readonly=True
related='company_id.currency_id'
string='Report Company Currency'
company_id
Many2one → res.company
comodel_name='res.company'
default=<expr>
readonly=True
required=True
string='Company'
currency_id
Many2one → res.currency
comodel_name='res.currency'
compute='_compute_currency_id'
default=<expr>
precompute=True
readonly=False
required=True
store=True
string='Currency'
currency_rate
Float
compute='_compute_currency_rate'
digits=(16, 9)
readonly=True
tracking=True
date
Date
default=fields.Date.context_today
string='Expense Date'
description
Text
string='Internal Notes'
duplicate_expense_ids
Many2many → hr.expense
comodel_name='hr.expense'
compute='_compute_duplicate_expense_ids'
employee_id
Many2one → hr.employee
check_company=True
comodel_name='hr.employee'
compute='_compute_employee_id'
default=_default_employee_id
domain=[('filter_for_expense', '=', True)]
precompute=True
readonly=False
required=True
store=True
string='Employee'
tracking=True
is_editable
Boolean
compute='_compute_is_editable'
string='Is Editable By Current User'
is_multiple_currency
Boolean
compute='_compute_is_multiple_currency'
string='Is currency_id different from the company_currency_id'
label_currency_rate
Char
compute='_compute_currency_rate'
readonly=True
message_main_attachment_checksum
Char
related='message_main_attachment_id.checksum'
name
Char
compute='_compute_name'
copy=True
precompute=True
readonly=False
required=True
store=True
string='Description'
nb_attachment
Integer
compute='_compute_nb_attachment'
string='Number of Attachments'
payment_mode
Selection
default='own_account'
selection=[('own_account', 'Employee (to reimburse)'), ('company_account', 'Company')]
string='Paid By'
tracking=True
price_unit
Float
compute='_compute_price_unit'
copy=True
digits='Product Price'
precompute=True
readonly=True
required=True
store=True
string='Unit Price'
product_description
Html
compute='_compute_product_description'
product_has_cost
Boolean
compute='_compute_from_product'
product_has_tax
Boolean
compute='_compute_from_product'
string='Whether tax is defined on a selected product'
product_id
Many2one → product.product
check_company=True
comodel_name='product.product'
domain=[('can_be_expensed', '=', True)]
ondelete='restrict'
string='Category'
tracking=True
product_uom_category_id
Many2one → uom.category
comodel_name='uom.category'
readonly=True
related='product_id.uom_id.category_id'
string='UoM Category'
product_uom_id
Many2one → uom.uom
comodel_name='uom.uom'
compute='_compute_uom_id'
copy=True
domain="[('category_id', '=', product_uom_category_id)]"
precompute=True
store=True
string='Unit of Measure'
quantity
Float
default=1
digits='Product Unit of Measure'
required=True
sheet_id
Many2one → hr.expense.sheet
comodel_name='hr.expense.sheet'
copy=False
domain="[('employee_id', '=', employee_id), ('company_id', '=', company_id)]"
readonly=True
string='Expense Report'
state
Selection
compute='_compute_state'
copy=False
default='draft'
index=True
readonly=True
selection=[('draft', 'To Report'), ('reported', 'To Submit'), ('submitted', 'Submitted'), ('approved', 'Approved'), ('done', 'Done'), ('refused', 'Refused')]
store=True
string='Status'
tax_amount
Monetary
compute='_compute_tax_amount'
currency_field='company_currency_id'
help='Tax amount in company currency'
precompute=True
store=True
string='Tax amount'
tax_amount_currency
Monetary
compute='_compute_tax_amount_currency'
currency_field='currency_id'
help='Tax amount in currency'
precompute=True
store=True
string='Tax amount in Currency'
tax_ids
Many2many → account.tax
check_company=True
column1='expense_id'
column2='tax_id'
comodel_name='account.tax'
compute='_compute_tax_ids'
domain="[('type_tax_use', '=', 'purchase')]"
help='Both price-included and price-excluded taxes will behave as price-included taxes for expenses.'
precompute=True
readonly=False
relation='expense_tax'
store=True
string='Included taxes'
total_amount
Monetary
compute='_compute_total_amount'
currency_field='company_currency_id'
inverse='_inverse_total_amount'
precompute=True
readonly=False
store=True
string='Total'
tracking=True
total_amount_currency
Monetary
compute='_compute_total_amount_currency'
currency_field='currency_id'
precompute=True
readonly=False
store=True
string='Total In Currency'
tracking=True
untaxed_amount_currency
Monetary
compute='_compute_tax_amount_currency'
currency_field='currency_id'
precompute=True
store=True
string='Total Untaxed Amount In Currency'
action_approve_duplicates(self)
action_get_attachment_view(self)
action_split_wizard(self)
action_submit_expenses(self)
action_view_sheet(self)
attach_document(self, **kwargs)
create_expense_from_attachments(self, attachment_ids=None, view_type='list')
get_empty_list_help(self, help_message)
get_expense_dashboard(self)
get_expenses_to_submit(self)
message_new(self, msg_dict, custom_values=None)
unlink(self)
write(self, vals)
expense_ids
Many2many → hr.expense
readonly=True
args: 'hr.expense'
sheet_ids
Many2many → hr.expense.sheet
action_approve(self)
action_refuse(self)
default_get(self, fields)
reason
Char
required=True
string='Reason'
sheet_ids
Many2many → hr.expense.sheet
action_refuse(self)
default_get(self, fields)
account_move_ids
One2many → account.move
comodel_name='account.move'
inverse_name='expense_sheet_id'
readonly=True
string='Journal Entries'
accounting_date
Date
compute='_compute_accounting_date'
store=True
string='Accounting Date'
amount_residual
Monetary
compute='_compute_from_account_move_ids'
currency_field='company_currency_id'
readonly=True
store=True
string='Amount Due'
approval_date
Datetime
readonly=True
string='Approval Date'
approval_state
Selection
copy=False
selection=[('submit', 'Submitted'), ('approve', 'Approved'), ('cancel', 'Refused')]
attachment_ids
One2many → ir.attachment
comodel_name='ir.attachment'
domain="[('res_model', '=', 'hr.expense.sheet')]"
inverse_name='res_id'
string='Attachments of expenses'
can_approve
Boolean
compute='_compute_can_approve'
string='Can Approve'
can_reset
Boolean
compute='_compute_can_reset'
string='Can Reset'
cannot_approve_reason
Char
compute='_compute_can_approve'
string='Cannot Approve Reason'
company_currency_id
Many2one → res.currency
comodel_name='res.currency'
related='company_id.currency_id'
string='Report Company Currency'
company_id
Many2one → res.company
comodel_name='res.company'
default=<expr>
readonly=True
required=True
string='Company'
currency_id
Many2one → res.currency
comodel_name='res.currency'
compute='_compute_currency_id'
readonly=True
store=True
string='Currency'
department_id
Many2one → hr.department
comodel_name='hr.department'
copy=False
related='employee_id.department_id'
store=True
string='Department'
employee_id
Many2one → hr.employee
check_company=True
comodel_name='hr.employee'
default=_default_employee_id
domain=[('filter_for_expense', '=', True)]
readonly=True
required=True
string='Employee'
tracking=True
employee_journal_id
Many2one → account.journal
check_company=True
comodel_name='account.journal'
default=_default_journal_id
domain=[('type', '=', 'purchase')]
help='The journal used when the expense is paid by employee.'
string='Journal'
expense_line_ids
One2many → hr.expense
comodel_name='hr.expense'
copy=False
inverse_name='sheet_id'
string='Expense Lines'
is_editable
Boolean
compute='_compute_is_editable'
string='Expense Lines Are Editable By Current User'
is_multiple_currency
Boolean
compute='_compute_is_multiple_currency'
string='Handle lines with different currencies'
journal_id
Many2one → account.journal
check_company=True
comodel_name='account.journal'
compute='_compute_journal_id'
store=True
string='Expense Journal'
message_main_attachment_id
Many2one
compute='_compute_main_attachment'
store=True
name
Char
required=True
string='Expense Report Summary'
tracking=True
nb_account_move
Integer
compute='_compute_nb_account_move'
string='Number of Journal Entries'
nb_expense
Integer
compute='_compute_nb_expense'
string='Number of Expenses'
payment_method_line_id
Many2one → account.payment.method.line
comodel_name='account.payment.method.line'
compute='_compute_payment_method_line_id'
domain="[('id', 'in', selectable_payment_method_line_ids)]"
help='The payment method used when the expense is paid by the company.'
readonly=False
store=True
string='Payment Method'
payment_mode
Selection
readonly=True
related='expense_line_ids.payment_mode'
string='Paid By'
tracking=True
payment_state
Selection
compute='_compute_from_account_move_ids'
copy=False
readonly=True
selection=<expr>
store=True
string='Payment Status'
tracking=True
product_ids
Many2many → product.product
check_company=True
comodel_name='product.product'
compute='_compute_product_ids'
search='_search_product_ids'
string='Categories'
selectable_payment_method_line_ids
Many2many → account.payment.method.line
comodel_name='account.payment.method.line'
compute='_compute_selectable_payment_method_line_ids'
state
Selection
compute='_compute_state'
copy=False
default='draft'
index=True
readonly=True
required=True
selection=[('draft', 'To Submit'), ('submit', 'Submitted'), ('approve', 'Approved'), ('post', 'Posted'), ('done', 'Done'), ('cancel', 'Refused')]
store=True
string='Status'
tracking=True
total_amount
Monetary
compute='_compute_amount'
currency_field='company_currency_id'
readonly=True
store=True
string='Total'
tracking=True
total_tax_amount
Monetary
compute='_compute_amount'
currency_field='company_currency_id'
readonly=True
store=True
string='Taxes'
untaxed_amount
Monetary
compute='_compute_amount'
currency_field='company_currency_id'
readonly=True
store=True
string='Untaxed Amount'
user_id
Many2one → res.users
comodel_name='res.users'
compute='_compute_from_employee_id'
copy=False
domain=<expr>
readonly=True
store=True
string='Manager'
tracking=True
action_approve_expense_sheets(self)
action_open_account_moves(self)
action_open_expense_view(self)
action_refuse_expense_sheets(self)
action_register_payment(self)
action_reset_approval_expense_sheets(self)
action_reset_expense_sheets(self)
action_sheet_move_create(self)
action_submit_sheet(self)
activity_update(self)
create(self, vals_list)
set_to_paid(self)
set_to_posted(self)
write(self, vals)
company_id
Many2one → res.company
comodel_name='res.company'
currency_id
Many2one → res.currency
comodel_name='res.currency'
employee_id
Many2one → hr.employee
comodel_name='hr.employee'
required=True
string='Employee'
expense_id
Many2one → hr.expense
comodel_name='hr.expense'
string='Expense'
name
Char
required=True
string='Description'
product_has_cost
Boolean
compute='_compute_from_product_id'
store=True
string='Is product with non zero cost selected'
product_has_tax
Boolean
compute='_compute_product_has_tax'
string='Whether tax is defined on a selected product'
product_id
Many2one → product.product
check_company=True
comodel_name='product.product'
domain=[('can_be_expensed', '=', True)]
required=True
string='Product'
tax_amount_currency
Monetary
compute='_compute_tax_amount_currency'
string='Tax amount in Currency'
tax_ids
Many2many → account.tax
check_company=True
comodel_name='account.tax'
domain="[('type_tax_use', '=', 'purchase')]"
total_amount_currency
Monetary
compute='_compute_from_product_id'
readonly=False
required=True
store=True
string='Total In Currency'
wizard_id
Many2one → hr.expense.split.wizard
comodel_name='hr.expense.split.wizard'
default_get(self, fields)
currency_id
Many2one → res.currency
comodel_name='res.currency'
related='expense_id.currency_id'
expense_id
Many2one → hr.expense
comodel_name='hr.expense'
required=True
string='Expense'
expense_split_line_ids
One2many → hr.expense.split
comodel_name='hr.expense.split'
inverse_name='wizard_id'
split_possible
Boolean
compute='_compute_split_possible'
help='The sum of after split shut remain the same'
tax_amount_currency
Monetary
compute='_compute_tax_amount_currency'
currency_field='currency_id'
string='Taxes'
total_amount_currency
Monetary
compute='_compute_total_amount_currency'
currency_field='currency_id'
string='Total Amount'
total_amount_currency_original
Monetary
currency_field='currency_id'
help='Total amount of the original Expense that we are splitting'
related='expense_id.total_amount_currency'
string='Total amount original'
action_split_expense(self)
No new fields.
Public methods (0)No public methods.
No new fields.
Public methods (2)create(self, vals_list)
unlink(self)
standard_price_update_warning
Char
compute='_compute_standard_price_update_warning'
No public methods.
can_be_expensed
Boolean
compute='_compute_can_be_expensed'
help='Specify whether the product can be selected in an expense.'
readonly=False
store=True
string='Can be Expensed'
default_get(self, fields)
company_expense_allowed_payment_method_line_ids
Many2many → account.payment.method.line
check_company=True
domain="[('payment_type', '=', 'outbound'), ('journal_id', '!=', False), ('journal_id.active', '=', True)]"
string='Payment methods available for expenses paid by company'
args: 'account.payment.method.line'
expense_journal_id
Many2one → account.journal
check_company=True
domain="[('type', '=', 'purchase')]"
help="The company's default journal used when an employee expense is created."
string='Default Expense Journal'
args: 'account.journal'
expense_product_id
Many2one → product.product
check_company=True
domain="[('can_be_expensed', '=', True)]"
string='Default Expense Category'
args: 'product.product'
No public methods.
company_expense_allowed_payment_method_line_ids
Many2many → account.payment.method.line
check_company=True
comodel_name='account.payment.method.line'
readonly=False
related='company_id.company_expense_allowed_payment_method_line_ids'
expense_journal_id
Many2one → account.journal
check_company=True
readonly=False
related='company_id.expense_journal_id'
args: 'account.journal'
expense_product_id
Many2one → product.product
check_company=True
readonly=False
related='company_id.expense_product_id'
args: 'product.product'
hr_expense_alias_domain_id
Many2one → mail.alias.domain
comodel_name='mail.alias.domain'
compute='_compute_hr_expense_alias_domain_id'
inverse='_inverse_hr_expense_alias_domain_id'
readonly=False
hr_expense_alias_prefix
Char
compute='_compute_hr_expense_alias_prefix'
readonly=False
store=True
args: 'Default Alias Name for Expenses'
hr_expense_use_mailgateway
Boolean
config_parameter='hr_expense.use_mailgateway'
string='Let your employees record expenses by email'
module_hr_expense_extract
Boolean
string='Send bills to OCR to generate expenses'
module_hr_payroll_expense
Boolean
string='Reimburse Expenses in Payslip'
get_values(self)
set_values(self)
expense_manager_id
Many2one
readonly=False
related='employee_id.expense_manager_id'
SELF_READABLE_FIELDS(self)
| REPOSITORY | |
|---|---|
| REPOSITORY | odoo/odoo |
| GIT | |
| GIT | https://github.com/odoo/odoo.git |
| GIT FOLDER | |
| GIT FOLDER | https://github.com/odoo/odoo/tree/16.0/hr_expense |
| VERSION | |
| VERSION | 2.0 |
| CATEGORY | |
| CATEGORY | Human Resources/Expenses |
| LICENSE | |
| LICENSE | LGPL-3 |
| APPLICATION | |
| APPLICATION | Yes |
| AUTO-INSTALLABLE | |
| AUTO-INSTALLABLE | No |
| AUTHORS | |
| AUTHORS | |
| MAINTAINERS | |
| MAINTAINERS | |
| COMMITTERS | |
| COMMITTERS | Raphael Collet, Odoo Translation Bot, Martin Trigaux, Fabien Pinckaers, Thibault Delavallée, Denis Ledoux, Christophe Simonis, Goffin Simon, qdp-odoo, Aaron Bohy, Jeremy Kersten, GitHub, Damien Bouvy, Barad Mahendra, Yannick Tivisse, Laurent Smet, Jorge Pinna Puissant, wan, Romeo Fragomeli, Victor Feyens, Andrea Grazioso (agr-odoo), oco-odoo, Anh Thao Pham (pta), Achraf (abz), Brice bib Bartoletti, Nasreddin Boulif (bon), Tiffany Chang (tic), Gorash, Pierre Paridans, William Braeckman, Kevin Baptiste, Guillaume (guva), Philippe Wauthy, roen-odoo, Hubert Van de Walle (huvw), sofiagvaladze, Nicolas (vin), william-andre, Paolo (pgi), Víctor Martínez, Florian Damhaut, Merlin (megu), MerlinGuillaume, thcl-odoo, Didier (did), aliya, william, Florian Vranckx, Louis Baudoux, Yolann Sabaux, Abdelouahab (abla), John Laterre (jol), Thibault Libioulle, gawa-odoo, Hubert Van De Walle, Dossogne Bertrand, Roy Le, niyasraphy, Habib (ayh), Thomas Lefebvre (thle), alsh-odoo, Louis Wicket (wil), Maximilien (malb), Victor Piryns (pivi), Hamza (hisl), Thomas Becquevort (thbe), Gauthier Wala (gawa), Ali Alfie (alal), hieu, duongnguyen, Antoine Dupuis (andu), Antoine Boonen, Dylan Kiss (dyki), hupo-odoo, Bruno Boi, flvr-odoo, Julien Alardot (jual), Nam Dao, FrancoisGe, Leloup Loïc (lole), Nguyễn Đại Dương, Leonardo Pavan Rocha, Cameron, sesn-odoo, Paul Stroobant, stefanorigano (SRI), BHanq, ravb-odoo, Dang Van Loc, Seif (segn), yosa-odoo, Aktiv Software, det, mega, SaddemAmine, Martin Trigaux (mat), duongnguyen-viindoo, aath-odoo, csnauwaert, Andrea Gidalti, Ryan Cen, Antoine (anso), Mohammed Basioni, Maira Salazar (srma), Pierre Lamotte (pila), tdetry (det) |
| WEBSITE | |
| WEBSITE | https://www.odoo.com/app/expenses |
| LAST TRACKING UPDATE | |
| LAST TRACKING UPDATE | 2026-07-06 00:40:49 |
| ODOO DEPENDENCIES | |
| ODOO DEPENDENCIES |
odoo/odoo: - hr_contract - hr - base_setup - base - web - bus - web_tour - resource - account - product - uom - analytic - portal - web_editor - http_routing - auth_signup - digest |
| PYTHON DEPENDENCIES | |
| PYTHON DEPENDENCIES | Not have |
| SYSTEM DEPENDENCIES | |
| SYSTEM DEPENDENCIES | Not have |
| DESCRIPTION | |
| DESCRIPTION |
Manage expenses by Employees
============================
This application allows you to manage your employees' daily expenses. It gives you access to your employees’ fee notes and give you the right to complete and validate or refuse the notes. After validation it creates an invoice for the employee.
Employee can encode their own expenses and the validation flow puts it automatically in the accounting after validation by managers.
The whole flow is implemented as:
---------------------------------
* Draft expense
* Submitted by the employee to his manager
* Approved by his manager
* Validation by the accountant and accounting entries creation
This module also uses analytic accounting and is compatible with the invoice on timesheet module so that you are able to automatically re-invoice your customers' expenses if your work by project.
|
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
account_journal_dashboard_kanban_view_inherit_hr_expense |
account.journal.dashboard.kanban | account.journal | xpath | Inherits account.account_journal_dashboard_kanban_view |
hr_department_view_kanban |
hr.department.kanban.inherit | hr.department | data | Inherits hr.hr_department_view_kanban |
hr_employee_view_form_inherit_expense |
hr.employee.view.form.expense | hr.employee | xpath | Inherits hr.view_employee_form |
hr_expense_approve_duplicate_view_form |
hr.expense.approve.duplicate | form | New | |
hr_expense_kanban_view |
hr.expense.kanban | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_kanban |
hr_expense_kanban_view_header |
hr.expense.kanban | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_kanban |
hr_expense_refuse_wizard_view_form |
hr.expense.refuse.wizard.form | hr.expense.refuse.wizard | form | New |
hr_expense_sheet_view_activity |
hr.expense.sheet.activity | hr.expense.sheet | activity | New |
hr_expense_sheet_view_search |
hr.expense.sheet.view.search | hr.expense.sheet | search | New |
hr_expense_sheet_view_search_with_panel |
hr.expense.sheet.view.search.with.panel | hr.expense.sheet | xpath | Inherits hr_expense_sheet_view_search |
hr_expense_split |
Expense split | hr.expense.split.wizard | form | New |
hr_expense_template_refuse_reason |
hr_expense_template_refuse_reason | ir.ui.view | qweb | New |
hr_expense_template_register |
hr_expense_template_register | ir.ui.view | qweb | New |
hr_expense_template_register_no_user |
hr_expense_template_register_no_user | ir.ui.view | qweb | New |
hr_expense_view_activity |
hr.expense.activity | hr.expense | activity | New |
hr_expense_view_expenses_analysis_kanban |
hr.expense.kanban | hr.expense | kanban | New |
hr_expense_view_expenses_analysis_tree |
hr.expense.tree | hr.expense | tree | New |
hr_expense_view_form |
hr.expense.view.form | hr.expense | form | New |
hr_expense_view_form_without_header |
hr.expense.view.form | hr.expense | xpath | Inherits hr_expense.hr_expense_view_form |
hr_expense_view_graph |
hr.expense.graph | hr.expense | graph | New |
hr_expense_view_pivot |
hr.expense.pivot | hr.expense | pivot | New |
hr_expense_view_search |
hr.expense.view.search | hr.expense | search | New |
product_product_expense_categories_tree_view |
product.product.expense.categories.tree.view | product.product | tree | New |
product_product_expense_form_view |
product.product.expense.form | product.product | form | New |
product_product_expense_tree_view |
product.product.expense.tree | product.product | tree | New |
product_template_search_view_inherit_hr_expense |
product.template.search.view.inherit.hr_expense | product.template | filter | Inherits product.product_template_search_view |
report_expense_sheet |
report_expense_sheet | ir.ui.view | qweb | New |
res_config_settings_view_form |
res.config.settings.view.form.inherit.hr.expense | res.config.settings | xpath | Inherits base.res_config_settings_view_form |
res_users_view_form_preferences |
hr.user.preferences.form.inherit.hr.expense | res.users | xpath | Inherits hr.res_users_view_form_profile |
view_employee_tree_inherit_expense |
hr.employee.tree.expense | hr.employee | xpath | Inherits hr.view_employee_tree |
view_expenses_tree |
hr.expense.tree | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_tree |
view_hr_expense_sheet_dashboard_tree_header |
hr.expense.sheet.dashboard.tree | hr.expense.sheet | xpath | Inherits hr_expense.view_hr_expense_sheet_tree |
view_hr_expense_sheet_form |
hr.expense.sheet.form | hr.expense.sheet | form | New |
view_hr_expense_sheet_graph |
hr.expense.sheet.graph | hr.expense.sheet | graph | New |
view_hr_expense_sheet_kanban |
hr.expense.sheet.kanban | hr.expense.sheet | kanban | New |
view_hr_expense_sheet_kanban_header |
hr.expense.sheet.kanban | hr.expense.sheet | xpath | Inherits view_hr_expense_sheet_kanban |
view_hr_expense_sheet_kanban_no_header |
hr.expense.sheet.kanban | hr.expense.sheet | xpath | Inherits view_hr_expense_sheet_kanban |
view_hr_expense_sheet_pivot |
hr.expense.sheet.pivot | hr.expense.sheet | pivot | New |
view_hr_expense_sheet_tree |
hr.expense.sheet.tree | hr.expense.sheet | tree | New |
view_move_form_inherit_expense |
account.move.form.inherit | account.move | xpath | Inherits account.view_move_form |
view_my_expenses_tree |
hr.expense.tree | hr.expense | xpath | Inherits hr_expense.view_expenses_tree |
view_payment_form_inherit_expense |
account.payment.form.inherit | account.payment | xpath | Inherits account.view_account_payment_form |
view_product_hr_expense_form |
product.template.expense.form | product.template | div | Inherits product.product_template_form_view |
No new fields.
Public methods (0)No public methods.
business_domain
Selection
ondelete={'expense': 'cascade'}
selection_add=[('expense', 'Expense')]
No public methods.
No new fields.
Public methods (2)get_journal_dashboard_datas(self)
open_expenses_action(self)
expense_sheet_id
One2many → hr.expense.sheet
action_open_expense_report(self)
button_draft(self)
unlink(self)
expense_id
Many2one → hr.expense
copy=True
string='Expense'
args: 'hr.expense'
reconcile(self)
No new fields.
Public methods (3)action_cancel(self)
action_draft(self)
action_open_expense_report(self)
No new fields.
Public methods (0)No public methods.
expense_sheets_to_approve_count
Integer
compute='_compute_expense_sheets_to_approve'
string='Expenses Reports to Approve'
No public methods.
expense_manager_id
Many2one → res.users
compute='_compute_expense_manager'
domain=_group_hr_expense_user_domain
help='Select the user responsible for approving "Expenses" of this employee.\nIf empty, the approval is done by an Administrator or Approver (determined in settings/users).'
readonly=False
store=True
string='Expense'
args: 'res.users'
No public methods.
expense_manager_id
Many2one → res.users
readonly=True
args: 'res.users'
No public methods.
account_id
Many2one → account.account
compute='_compute_account_id'
domain="[('account_type', 'not in', ('asset_receivable','liability_payable','asset_cash','liability_credit_card')), ('company_id', '=', company_id)]"
help='An expense account is expected'
precompute=True
readonly=False
store=True
string='Account'
args: 'account.account'
accounting_date
Date
groups='account.group_account_invoice,account.group_account_readonly'
related='sheet_id.accounting_date'
store=True
string='Accounting Date'
amount_residual
Monetary
compute='_compute_amount_residual'
string='Amount Due'
amount_tax
Monetary
compute='_compute_amount_tax'
currency_field='currency_id'
help='Tax amount in currency'
store=True
string='Tax amount in Currency'
amount_tax_company
Monetary
compute='_compute_total_amount_company'
currency_field='company_currency_id'
help='Tax amount in company currency'
store=True
args: 'Tax amount'
approved_by
Many2one → res.users
related='sheet_id.user_id'
string='Approved By'
tracking=False
args: 'res.users'
approved_on
Datetime
related='sheet_id.approval_date'
string='Approved On'
attachment_ids
One2many → ir.attachment
domain="[('res_model', '=', 'hr.expense')]"
string='Attachments'
args: 'ir.attachment', 'res_id'
attachment_number
Integer
compute='_compute_attachment_number'
args: 'Number of Attachments'
company_currency_id
Many2one → res.currency
readonly=True
related='company_id.currency_id'
string='Report Company Currency'
args: 'res.currency'
company_id
Many2one → res.company
default=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
compute='_compute_currency_id'
default=<expr>
readonly=False
required=True
states={'reported': [('readonly', True)], 'approved': [('readonly', True)], 'done': [('readonly', True)]}
store=True
string='Currency'
args: 'res.currency'
currency_rate
Float
compute='_compute_currency_rate'
date
Date
default=fields.Date.context_today
states={'done': [('readonly', True)]}
string='Expense Date'
description
Text
readonly=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
args: 'Internal Notes'
duplicate_expense_ids
Many2many → hr.expense
compute='_compute_duplicate_expense_ids'
args: 'hr.expense'
employee_id
Many2one → hr.employee
check_company=True
compute='_compute_employee_id'
default=_default_employee_id
domain=<expr>
readonly=False
required=True
states={'approved': [('readonly', True)], 'done': [('readonly', True)]}
store=True
string='Employee'
tracking=True
args: 'hr.employee'
is_editable
Boolean
compute='_compute_is_editable'
args: 'Is Editable By Current User'
is_ref_editable
Boolean
compute='_compute_is_ref_editable'
args: 'Reference Is Editable By Current User'
is_refused
Boolean
copy=False
readonly=True
args: 'Explicitly Refused by manager or accountant'
label_convert_rate
Char
compute='_compute_label_convert_rate'
name
Char
compute='_compute_name'
copy=True
precompute=True
readonly=False
required=True
states={'done': [('readonly', True)]}
store=True
args: 'Description'
payment_mode
Selection
default='own_account'
states={'done': [('readonly', True)], 'approved': [('readonly', True)], 'reported': [('readonly', True)]}
string='Paid By'
tracking=True
args: [('own_account', 'Employee (to reimburse)'), ('company_account', 'Company')]
product_description
Html
compute='_compute_product_description'
product_has_cost
Boolean
compute='_compute_product_has_cost'
args: 'Is product with non zero cost selected'
product_has_tax
Boolean
compute='_compute_product_has_cost'
args: 'Whether tax is defined on a selected product'
product_id
Many2one → product.product
domain="[('can_be_expensed', '=', True), '|', ('company_id', '=', False), ('company_id', '=', company_id)]"
ondelete='restrict'
states={'done': [('readonly', True)]}
string='Category'
tracking=True
args: 'product.product'
product_uom_category_id
Many2one
readonly=True
related='product_id.uom_id.category_id'
string='UoM Category'
product_uom_id
Many2one → uom.uom
compute='_compute_product_uom_id'
copy=True
domain="[('category_id', '=', product_uom_category_id)]"
precompute=True
readonly=True
store=True
string='Unit of Measure'
args: 'uom.uom'
quantity
Float
default=1
digits='Product Unit of Measure'
required=True
states={'done': [('readonly', True)]}
reference
Char
same_currency
Boolean
compute='_compute_same_currency'
args: 'Is currency_id different from the company_currency_id'
sample
Boolean
sheet_id
Many2one → hr.expense.sheet
copy=False
domain="[('employee_id', '=', employee_id), ('company_id', '=', company_id)]"
readonly=True
string='Expense Report'
args: 'hr.expense.sheet'
sheet_is_editable
Boolean
compute='_compute_sheet_is_editable'
state
Selection
compute='_compute_state'
copy=False
default='draft'
index=True
readonly=True
store=True
string='Status'
args: [('draft', 'To Submit'), ('reported', 'Submitted'), ('approved', 'Approved'), ('done', 'Done'), ('refused', 'Refused')]
tax_ids
Many2many → account.tax
compute='_compute_tax_ids'
domain="[('company_id', '=', company_id), ('type_tax_use', '=', 'purchase')]"
help='Both price-included and price-excluded taxes will behave as price-included taxes for expenses.'
precompute=True
readonly=False
store=True
string='Included taxes'
args: 'account.tax', 'expense_tax', 'expense_id', 'tax_id'
total_amount
Monetary
compute='_compute_amount'
currency_field='currency_id'
inverse='_inverse_total_amount'
readonly=False
store=True
tracking=True
args: 'Total In Currency'
total_amount_company
Monetary
compute='_compute_total_amount_company'
currency_field='company_currency_id'
store=True
args: 'Total'
unit_amount
Float
compute='_compute_unit_amount'
copy=True
digits='Product Price'
precompute=True
readonly=False
required=True
states={'done': [('readonly', True)]}
store=True
args: 'Unit Price'
unit_amount_display
Float
compute='_compute_unit_amount_display'
digits='Product Price'
args: 'Unit Price Display'
untaxed_amount
Monetary
compute='_compute_amount_tax'
currency_field='currency_id'
store=True
args: 'Total Untaxed Amount In Currency'
action_approve_duplicates(self)
action_get_attachment_view(self)
action_move_create(self)
action_split_wizard(self)
action_submit_expenses(self)
action_view_sheet(self)
attach_document(self, **kwargs)
create_expense_from_attachments(self, attachment_ids=None, view_type='list')
get_empty_list_help(self, help_message)
get_expense_dashboard(self)
get_expenses_to_submit(self)
message_new(self, msg_dict, custom_values=None)
refuse_expense(self, reason)
unlink(self)
write(self, vals)
expense_ids
Many2many → hr.expense
readonly=True
args: 'hr.expense'
sheet_ids
Many2many → hr.expense.sheet
action_approve(self)
action_refuse(self)
default_get(self, fields)
hr_expense_ids
Many2many → hr.expense
hr_expense_sheet_id
Many2one → hr.expense.sheet
reason
Char
required=True
string='Reason'
default_get(self, fields)
expense_refuse_reason(self)
account_move_id
Many2one → account.move
copy=False
ondelete='restrict'
readonly=True
string='Journal Entry'
args: 'account.move'
accounting_date
Date
compute='_compute_accounting_date'
store=True
string='Accounting Date'
address_id
Many2one → res.partner
check_company=True
compute='_compute_from_employee_id'
copy=True
readonly=False
store=True
string='Employee Home Address'
args: 'res.partner'
amount_residual
Monetary
currency_field='currency_id'
related='account_move_id.amount_residual'
store=True
string='Amount Due'
approval_date
Datetime
readonly=True
args: 'Approval Date'
attachment_ids
One2many → ir.attachment
domain="[('res_model', '=', 'hr.expense.sheet')]"
string='Attachments of expenses'
args: 'ir.attachment', 'res_id'
attachment_number
Integer
compute='_compute_attachment_number'
string='Number of Attachments'
bank_journal_id
Many2one → account.journal
check_company=True
default=_default_bank_journal_id
domain="[('type', 'in', ['cash', 'bank']), ('company_id', '=', company_id)]"
help='The payment method used when the expense is paid by the company.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Bank Journal'
args: 'account.journal'
can_approve
Boolean
compute='_compute_can_approve'
args: 'Can Approve'
can_reset
Boolean
compute='_compute_can_reset'
args: 'Can Reset'
company_id
Many2one → res.company
default=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)]}
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
compute='_compute_currency_id'
readonly=True
states={'draft': [('readonly', False)]}
store=True
string='Currency'
args: 'res.currency'
department_id
Many2one → hr.department
compute='_compute_from_employee_id'
copy=False
readonly=False
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
store=True
string='Department'
args: 'hr.department'
employee_id
Many2one → hr.employee
check_company=True
default=_default_employee_id
domain=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)]}
string='Employee'
tracking=True
args: 'hr.employee'
expense_line_ids
One2many → hr.expense
copy=False
string='Expense Lines'
args: 'hr.expense', 'sheet_id'
expense_number
Integer
compute='_compute_expense_number'
string='Number of Expenses'
is_editable
Boolean
compute='_compute_is_editable'
args: 'Expense Lines Are Editable By Current User'
is_multiple_currency
Boolean
compute='_compute_is_multiple_currency'
args: 'Handle lines with different currencies'
journal_displayed_id
Many2one → account.journal
compute='_compute_journal_displayed_id'
string='Journal'
args: 'account.journal'
journal_id
Many2one → account.journal
check_company=True
default=_default_journal_id
domain="[('type', '=', 'purchase'), ('company_id', '=', company_id)]"
help='The journal used when the expense is done.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Expense Journal'
args: 'account.journal'
message_main_attachment_id
Many2one
compute='_compute_main_attachment'
store=True
name
Char
required=True
tracking=True
args: 'Expense Report Summary'
payment_mode
Selection
readonly=True
related='expense_line_ids.payment_mode'
string='Paid By'
tracking=True
payment_state
Selection
compute='_compute_payment_state'
copy=False
readonly=True
store=True
string='Payment Status'
tracking=True
args: <expr>
product_ids
Many2many → product.product
compute='_compute_product_ids'
search='_search_product_ids'
string='Categories'
args: 'product.product'
state
Selection
copy=False
default='draft'
index=True
readonly=True
required=True
string='Status'
tracking=True
args: [('draft', 'Draft'), ('submit', 'Submitted'), ('approve', 'Approved'), ('post', 'Posted'), ('done', 'Done'), ('cancel', 'Refused')]
total_amount
Monetary
compute='_compute_amount'
currency_field='currency_id'
store=True
tracking=True
args: 'Total Amount'
total_amount_taxes
Monetary
compute='_compute_amount'
currency_field='currency_id'
store=True
args: 'Taxes'
untaxed_amount
Monetary
compute='_compute_amount'
currency_field='currency_id'
store=True
args: 'Untaxed Amount'
user_id
Many2one → res.users
compute='_compute_from_employee_id'
copy=False
domain=<expr>
readonly=True
states={'draft': [('readonly', False)]}
store=True
tracking=True
args: 'res.users', 'Manager'
action_get_attachment_view(self)
action_get_expense_view(self)
action_open_account_move(self)
action_register_payment(self)
action_sheet_move_create(self)
action_submit_sheet(self)
action_unpost(self)
activity_update(self)
approve_expense_sheets(self)
create(self, vals_list)
paid_expense_sheets(self)
refuse_sheet(self, reason)
reset_expense_sheets(self)
set_to_paid(self)
write(self, vals)
amount_tax
Monetary
compute='_compute_amount_tax'
string='Tax amount in Currency'
company_id
Many2one → res.company
currency_id
Many2one → res.currency
employee_id
Many2one → hr.employee
required=True
string='Employee'
args: 'hr.employee'
expense_id
Many2one → hr.expense
string='Expense'
args: 'hr.expense'
name
Char
required=True
args: 'Description'
product_has_cost
Boolean
compute='_compute_from_product_id'
store=True
args: 'Is product with non zero cost selected'
product_has_tax
Boolean
compute='_compute_product_has_tax'
args: 'Whether tax is defined on a selected product'
product_id
Many2one → product.product
domain="[('can_be_expensed', '=', True), '|', ('company_id', '=', False), ('company_id', '=', company_id)]"
required=True
string='Product'
args: 'product.product'
tax_ids
Many2many → account.tax
domain="[('company_id', '=', company_id), ('type_tax_use', '=', 'purchase')]"
args: 'account.tax'
total_amount
Monetary
compute='_compute_from_product_id'
readonly=False
required=True
store=True
args: 'Total In Currency'
wizard_id
Many2one → hr.expense.split.wizard
default_get(self, fields)
currency_id
Many2one → res.currency
related='expense_id.currency_id'
args: 'res.currency'
expense_id
Many2one → hr.expense
required=True
string='Expense'
args: 'hr.expense'
expense_split_line_ids
One2many → hr.expense.split
split_possible
Boolean
compute='_compute_split_possible'
help='The sum of after split shut remain the same'
total_amount
Monetary
compute='_compute_total_amount'
currency_field='currency_id'
args: 'Total Amount'
total_amount_original
Monetary
currency_field='currency_id'
help='Total amount of the original Expense that we are splitting'
related='expense_id.total_amount'
args: 'Total amount original'
total_amount_taxes
Monetary
compute='_compute_total_amount_taxes'
currency_field='currency_id'
args: 'Taxes'
action_split_expense(self)
No new fields.
Public methods (2)create(self, vals_list)
unlink(self)
standard_price_update_warning
Char
compute='_compute_standard_price_update_warning'
No public methods.
can_be_expensed
Boolean
compute='_compute_can_be_expensed'
help='Specify whether the product can be selected in an expense.'
readonly=False
store=True
string='Can be Expensed'
default_get(self, fields)
company_expense_journal_id
Many2one → account.journal
check_company=True
domain="[('type', 'in', ['cash', 'bank']), ('company_id', '=', company_id)]"
help="The company's default journal used when a company expense is created."
string='Default Company Expense Journal'
args: 'account.journal'
expense_journal_id
Many2one → account.journal
check_company=True
domain="[('type', '=', 'purchase'), ('company_id', '=', company_id)]"
help="The company's default journal used when an employee expense is created."
string='Default Expense Journal'
args: 'account.journal'
No public methods.
company_expense_journal_id
Many2one → account.journal
readonly=False
related='company_id.company_expense_journal_id'
args: 'account.journal'
expense_alias_prefix
Char
compute='_compute_expense_alias_prefix'
readonly=False
store=True
args: 'Default Alias Name for Expenses'
expense_journal_id
Many2one → account.journal
readonly=False
related='company_id.expense_journal_id'
args: 'account.journal'
module_hr_expense_extract
Boolean
string='Send bills to OCR to generate expenses'
module_hr_payroll_expense
Boolean
string='Reimburse Expenses in Payslip'
use_mailgateway
Boolean
config_parameter='hr_expense.use_mailgateway'
string='Let your employees record expenses by email'
get_values(self)
set_values(self)
expense_manager_id
Many2one
readonly=False
related='employee_id.expense_manager_id'
SELF_READABLE_FIELDS(self)
| REPOSITORY | |
|---|---|
| REPOSITORY | odoo/odoo |
| GIT | |
| GIT | https://github.com/odoo/odoo.git |
| GIT FOLDER | |
| GIT FOLDER | https://github.com/odoo/odoo/tree/15.0/hr_expense |
| VERSION | |
| VERSION | 2.0 |
| CATEGORY | |
| CATEGORY | Human Resources/Expenses |
| LICENSE | |
| LICENSE | LGPL-3 |
| APPLICATION | |
| APPLICATION | Yes |
| AUTO-INSTALLABLE | |
| AUTO-INSTALLABLE | No |
| AUTHORS | |
| AUTHORS | |
| MAINTAINERS | |
| MAINTAINERS | |
| COMMITTERS | |
| COMMITTERS | Raphael Collet, Odoo Translation Bot, Martin Trigaux, Thibault Delavallée, Christophe Simonis, Xavier Morel, Goffin Simon, Aaron Bohy, GitHub, Barad Mahendra, Yannick Tivisse, Dharmraj Jhala, Laurent Smet, wan, Julien Castiaux, Xavier-Do, Victor Feyens, Andrea Grazioso (agr-odoo), Sébastien Theys, alt-odoo, oco-odoo, Julien Mougenot, Achraf (abz), Brice bib Bartoletti, Adrien Widart, Nasreddin Boulif (bon), Touati Djamel (otd), Kamesh Patel, Swapnesh Shah, Tiffany Chang (tic), IEL, Mathieu Duckerts-Antoine, Pierre Paridans, jbw, jbm-odoo, Aurélien (avd), Kevin Baptiste, Guillaume (guva), roen-odoo, sofiagvaladze, Nicolas (vin), william-andre, Víctor Martínez, Florian Damhaut, Merlin (megu), thcl-odoo, Florian Vranckx, Nshimiyimana Séna, Yolann Sabaux, Florian Gilbert, Jinjiu Liu, Laurent Stukkens (LTU), Michael Mattiello (mcm), dht-odoo, Habib (ayh), Thomas Lefebvre (thle), mehjabinfarsana, alsh-odoo, Adrien Widart (awt), Maximilien (malb), Victor Piryns (pivi), Hamza (hisl), Giacomello Nathan (ngi), Gauthier Wala (gawa), clesgow, hieu, Antoine Boonen, Bruno Boi, ntlquynh2209, lathuat1997, flvr-odoo, Paolo Gatti (pgi), Kairis, Julien Alardot (jual), Nam Dao, Kartik Chavda, tuyenphung, Leonardo Pavan Rocha, Cameron, Paul Stroobant, BHanq, Seif (segn), Aktiv Software, det |
| WEBSITE | |
| WEBSITE | https://www.odoo.com/app/expenses |
| LAST TRACKING UPDATE | |
| LAST TRACKING UPDATE | 2026-07-06 00:25:28 |
| ODOO DEPENDENCIES | |
| ODOO DEPENDENCIES |
odoo/odoo: - hr_contract - hr - base_setup - base - web - bus - web_tour - resource - account - product - uom - analytic - portal - web_editor - http_routing - auth_signup - digest |
| PYTHON DEPENDENCIES | |
| PYTHON DEPENDENCIES | Not have |
| SYSTEM DEPENDENCIES | |
| SYSTEM DEPENDENCIES | Not have |
| DESCRIPTION | |
| DESCRIPTION |
Manage expenses by Employees
============================
This application allows you to manage your employees' daily expenses. It gives you access to your employees’ fee notes and give you the right to complete and validate or refuse the notes. After validation it creates an invoice for the employee.
Employee can encode their own expenses and the validation flow puts it automatically in the accounting after validation by managers.
The whole flow is implemented as:
---------------------------------
* Draft expense
* Submitted by the employee to his manager
* Approved by his manager
* Validation by the accountant and accounting entries creation
This module also uses analytic accounting and is compatible with the invoice on timesheet module so that you are able to automatically re-invoice your customers' expenses if your work by project.
|
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
account_journal_dashboard_kanban_view_inherit_hr_expense |
account.journal.dashboard.kanban | account.journal | xpath | Inherits account.account_journal_dashboard_kanban_view |
hr_department_view_kanban |
hr.department.kanban.inherit | hr.department | data | Inherits hr.hr_department_view_kanban |
hr_employee_view_form_inherit_expense |
hr.employee.view.form.expense | hr.employee | xpath | Inherits hr.view_employee_form |
hr_expense_approve_duplicate_view_form |
hr.expense.approve.duplicate | form | New | |
hr_expense_kanban_view |
hr.expense.kanban | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_kanban |
hr_expense_kanban_view_header |
hr.expense.kanban | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_kanban |
hr_expense_refuse_wizard_view_form |
hr.expense.refuse.wizard.form | hr.expense.refuse.wizard | form | New |
hr_expense_sheet_view_activity |
hr.expense.sheet.activity | hr.expense.sheet | activity | New |
hr_expense_sheet_view_search |
hr.expense.sheet.view.search | hr.expense.sheet | search | New |
hr_expense_template_refuse_reason |
hr_expense_template_refuse_reason | ir.ui.view | qweb | New |
hr_expense_template_register |
hr_expense_template_register | ir.ui.view | qweb | New |
hr_expense_template_register_no_user |
hr_expense_template_register_no_user | ir.ui.view | qweb | New |
hr_expense_view_activity |
hr.expense.activity | hr.expense | activity | New |
hr_expense_view_expenses_analysis_kanban |
hr.expense.kanban | hr.expense | kanban | New |
hr_expense_view_expenses_analysis_tree |
hr.expense.tree | hr.expense | tree | New |
hr_expense_view_form |
hr.expense.view.form | hr.expense | form | New |
hr_expense_view_form_without_header |
hr.expense.view.form | hr.expense | xpath | Inherits hr_expense.hr_expense_view_form |
hr_expense_view_graph |
hr.expense.graph | hr.expense | graph | New |
hr_expense_view_pivot |
hr.expense.pivot | hr.expense | pivot | New |
hr_expense_view_search |
hr.expense.view.search | hr.expense | search | New |
product_product_expense_form_view |
product.product.expense.form | product.product | form | New |
product_product_expense_tree_view |
product.product.expense.tree | product.product | tree | New |
product_template_search_view_inherit_hr_expense |
product.template.search.view.inherit.hr_expense | product.template | filter | Inherits product.product_template_search_view |
report_expense_sheet |
report_expense_sheet | ir.ui.view | qweb | New |
res_config_settings_view_form |
res.config.settings.view.form.inherit.hr.expense | res.config.settings | xpath | Inherits base.res_config_settings_view_form |
res_users_view_form_preferences |
hr.user.preferences.form.inherit.hr.expense | res.users | xpath | Inherits hr.res_users_view_form_profile |
view_employee_tree_inherit_expense |
hr.employee.tree.expense | hr.employee | xpath | Inherits hr.view_employee_tree |
view_expenses_tree |
hr.expense.tree | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_tree |
view_hr_expense_sheet_dashboard_tree |
hr.expense.sheet.dashboard.tree | hr.expense.sheet | xpath | Inherits hr_expense.view_hr_expense_sheet_tree |
view_hr_expense_sheet_dashboard_tree_header |
hr.expense.sheet.dashboard.tree | hr.expense.sheet | xpath | Inherits hr_expense.view_hr_expense_sheet_tree |
view_hr_expense_sheet_form |
hr.expense.sheet.form | hr.expense.sheet | form | New |
view_hr_expense_sheet_graph |
hr.expense.sheet.graph | hr.expense.sheet | graph | New |
view_hr_expense_sheet_kanban |
hr.expense.sheet.kanban | hr.expense.sheet | kanban | New |
view_hr_expense_sheet_kanban_header |
hr.expense.sheet.kanban | hr.expense.sheet | xpath | Inherits view_hr_expense_sheet_kanban |
view_hr_expense_sheet_kanban_no_header |
hr.expense.sheet.kanban | hr.expense.sheet | xpath | Inherits view_hr_expense_sheet_kanban |
view_hr_expense_sheet_pivot |
hr.expense.sheet.pivot | hr.expense.sheet | pivot | New |
view_hr_expense_sheet_tree |
hr.expense.sheet.tree | hr.expense.sheet | tree | New |
view_my_expenses_tree |
hr.expense.tree | hr.expense | xpath | Inherits hr_expense.view_expenses_tree |
No new fields.
Public methods (2)get_journal_dashboard_datas(self)
open_expenses_action(self)
No new fields.
Public methods (0)No public methods.
No new fields.
Public methods (2)button_cancel(self)
button_draft(self)
expense_id
Many2one → hr.expense
copy=False
help='Expense where the move line come from'
string='Expense'
args: 'hr.expense'
reconcile(self)
No new fields.
Public methods (0)No public methods.
expense_sheets_to_approve_count
Integer
compute='_compute_expense_sheets_to_approve'
string='Expenses Reports to Approve'
No public methods.
expense_manager_id
Many2one → res.users
compute='_compute_expense_manager'
domain=_group_hr_expense_user_domain
help='Select the user responsible for approving "Expenses" of this employee.\nIf empty, the approval is done by an Administrator or Approver (determined in settings/users).'
readonly=False
store=True
string='Expense'
args: 'res.users'
No public methods.
expense_manager_id
Many2one → res.users
readonly=True
args: 'res.users'
No public methods.
account_id
Many2one → account.account
compute='_compute_from_product_id_company_id'
default=_default_account_id
domain="[('internal_type', '=', 'other'), ('company_id', '=', company_id)]"
help='An expense account is expected'
readonly=False
store=True
string='Account'
args: 'account.account'
accounting_date
Date
groups='account.group_account_invoice,account.group_account_readonly'
related='sheet_id.accounting_date'
store=True
string='Accounting Date'
amount_residual
Monetary
compute='_compute_amount_residual'
string='Amount Due'
analytic_account_id
Many2one → account.analytic.account
check_company=True
string='Analytic Account'
args: 'account.analytic.account'
analytic_tag_ids
Many2many → account.analytic.tag
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]"
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
string='Analytic Tags'
args: 'account.analytic.tag'
approved_by
Many2one → res.users
related='sheet_id.user_id'
string='Approved By'
args: 'res.users'
approved_on
Datetime
related='sheet_id.approval_date'
string='Approved On'
attachment_number
Integer
compute='_compute_attachment_number'
args: 'Number of Attachments'
company_currency_id
Many2one → res.currency
readonly=True
related='company_id.currency_id'
string='Report Company Currency'
args: 'res.currency'
company_id
Many2one → res.company
default=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
compute='_compute_currency_id'
default=<expr>
readonly=False
required=True
states={'reported': [('readonly', True)], 'approved': [('readonly', True)], 'done': [('readonly', True)]}
store=True
string='Currency'
args: 'res.currency'
date
Date
default=fields.Date.context_today
readonly=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'approved': [('readonly', False)], 'refused': [('readonly', False)]}
string='Expense Date'
description
Text
readonly=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
args: 'Notes...'
duplicate_expense_ids
Many2many → hr.expense
compute='_compute_duplicate_expense_ids'
args: 'hr.expense'
employee_id
Many2one → hr.employee
check_company=True
compute='_compute_employee_id'
default=_default_employee_id
domain=<expr>
readonly=False
required=True
states={'approved': [('readonly', True)], 'done': [('readonly', True)]}
store=True
string='Employee'
tracking=True
args: 'hr.employee'
is_editable
Boolean
compute='_compute_is_editable'
args: 'Is Editable By Current User'
is_ref_editable
Boolean
compute='_compute_is_ref_editable'
args: 'Reference Is Editable By Current User'
is_refused
Boolean
copy=False
readonly=True
args: 'Explicitly Refused by manager or accountant'
label_convert_rate
Char
compute='_compute_label_convert_rate'
label_total_amount_company
Char
compute='_compute_label_total_amount_company'
name
Char
compute='_compute_from_product_id_company_id'
copy=True
required=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'approved': [('readonly', False)], 'refused': [('readonly', False)]}
store=True
args: 'Description'
payment_mode
Selection
default='own_account'
states={'done': [('readonly', True)], 'approved': [('readonly', True)], 'reported': [('readonly', True)]}
string='Paid By'
tracking=True
args: [('own_account', 'Employee (to reimburse)'), ('company_account', 'Company')]
product_has_cost
Boolean
compute='_compute_product_has_cost'
args: 'Is product with non zero cost selected'
product_id
Many2one → product.product
domain="[('can_be_expensed', '=', True), '|', ('company_id', '=', False), ('company_id', '=', company_id)]"
ondelete='restrict'
readonly=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'approved': [('readonly', False)], 'refused': [('readonly', False)]}
string='Product'
tracking=True
args: 'product.product'
product_uom_category_id
Many2one
readonly=True
related='product_id.uom_id.category_id'
string='UoM Category'
product_uom_id
Many2one → uom.uom
compute='_compute_from_product_id_company_id'
copy=True
default=_default_product_uom_id
domain="[('category_id', '=', product_uom_category_id)]"
readonly=True
store=True
string='Unit of Measure'
args: 'uom.uom'
quantity
Float
default=1
digits='Product Unit of Measure'
readonly=True
required=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'approved': [('readonly', False)], 'refused': [('readonly', False)]}
reference
Char
same_currency
Boolean
compute='_compute_same_currency'
args: 'Is currency_id different from the company_currency_id'
sample
Boolean
sheet_id
Many2one → hr.expense.sheet
copy=False
domain="[('employee_id', '=', employee_id), ('company_id', '=', company_id)]"
readonly=True
string='Expense Report'
args: 'hr.expense.sheet'
sheet_is_editable
Boolean
compute='_compute_sheet_is_editable'
state
Selection
compute='_compute_state'
copy=False
default='draft'
help='Status of the expense.'
index=True
readonly=True
store=True
string='Status'
args: [('draft', 'To Submit'), ('reported', 'Submitted'), ('approved', 'Approved'), ('done', 'Paid'), ('refused', 'Refused')]
tax_ids
Many2many → account.tax
compute='_compute_from_product_id_company_id'
domain="[('company_id', '=', company_id), ('type_tax_use', '=', 'purchase'), ('price_include', '=', True)]"
help='The taxes should be "Included In Price"'
readonly=False
store=True
string='Taxes'
args: 'account.tax', 'expense_tax', 'expense_id', 'tax_id'
total_amount
Monetary
compute='_compute_amount'
currency_field='currency_id'
readonly=False
store=True
tracking=True
args: 'Total In Currency'
total_amount_company
Monetary
compute='_compute_total_amount_company'
currency_field='company_currency_id'
store=True
args: 'Total'
unit_amount
Float
compute='_compute_from_product_id_company_id'
copy=True
digits='Product Price'
required=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'approved': [('readonly', False)], 'refused': [('readonly', False)]}
store=True
args: 'Unit Price'
untaxed_amount
Float
compute='_compute_amount_tax'
copy=True
digits='Account'
store=True
args: 'Subtotal'
action_approve_duplicates(self)
action_get_attachment_view(self)
action_move_create(self)
action_submit_expenses(self)
action_view_sheet(self)
attach_document(self, **kwargs)
create_expense_from_attachments(self, attachment_ids=None, view_type='tree')
get_empty_list_help(self, help_message)
get_expense_dashboard(self)
message_new(self, msg_dict, custom_values=None)
refuse_expense(self, reason)
write(self, vals)
expense_ids
Many2many → hr.expense
readonly=True
args: 'hr.expense'
sheet_ids
Many2many → hr.expense.sheet
action_approve(self)
action_refuse(self)
default_get(self, fields)
hr_expense_ids
Many2many → hr.expense
hr_expense_sheet_id
Many2one → hr.expense.sheet
reason
Char
required=True
string='Reason'
default_get(self, fields)
expense_refuse_reason(self)
account_move_id
Many2one → account.move
copy=False
ondelete='restrict'
readonly=True
string='Journal Entry'
args: 'account.move'
accounting_date
Date
address_id
Many2one → res.partner
check_company=True
compute='_compute_from_employee_id'
copy=True
readonly=False
store=True
string='Employee Home Address'
args: 'res.partner'
amount_residual
Monetary
compute='_compute_amount_residual'
currency_field='currency_id'
store=True
string='Amount Due'
approval_date
Datetime
readonly=True
args: 'Approval Date'
attachment_number
Integer
compute='_compute_attachment_number'
string='Number of Attachments'
bank_journal_id
Many2one → account.journal
check_company=True
default=_default_bank_journal_id
domain="[('type', 'in', ['cash', 'bank']), ('company_id', '=', company_id)]"
help='The payment method used when the expense is paid by the company.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Bank Journal'
args: 'account.journal'
can_approve
Boolean
compute='_compute_can_approve'
args: 'Can Approve'
can_reset
Boolean
compute='_compute_can_reset'
args: 'Can Reset'
company_id
Many2one → res.company
default=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)]}
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
readonly=True
states={'draft': [('readonly', False)]}
string='Currency'
args: 'res.currency'
department_id
Many2one → hr.department
compute='_compute_from_employee_id'
copy=False
readonly=False
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
store=True
string='Department'
args: 'hr.department'
employee_id
Many2one → hr.employee
check_company=True
default=_default_employee_id
domain=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)]}
string='Employee'
tracking=True
args: 'hr.employee'
expense_line_ids
One2many → hr.expense
copy=False
string='Expense Lines'
args: 'hr.expense', 'sheet_id'
is_editable
Boolean
compute='_compute_is_editable'
args: 'Expense Lines Are Editable By Current User'
is_multiple_currency
Boolean
compute='_compute_is_multiple_currency'
args: 'Handle lines with different currencies'
journal_id
Many2one → account.journal
check_company=True
default=_default_journal_id
domain="[('type', '=', 'purchase'), ('company_id', '=', company_id)]"
help='The journal used when the expense is done.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Expense Journal'
args: 'account.journal'
name
Char
required=True
tracking=True
args: 'Expense Report Summary'
payment_mode
Selection
readonly=True
related='expense_line_ids.payment_mode'
string='Paid By'
tracking=True
payment_state
Selection
compute='_compute_payment_state'
copy=False
readonly=True
selection=PAYMENT_STATE_SELECTION
store=True
string='Payment Status'
tracking=True
state
Selection
copy=False
default='draft'
help='Expense Report State'
index=True
readonly=True
required=True
string='Status'
tracking=True
args: [('draft', 'Draft'), ('submit', 'Submitted'), ('approve', 'Approved'), ('post', 'Posted'), ('done', 'Done'), ('cancel', 'Refused')]
total_amount
Monetary
compute='_compute_amount'
currency_field='currency_id'
store=True
tracking=True
args: 'Total Amount'
user_id
Many2one → res.users
compute='_compute_from_employee_id'
copy=False
domain=<expr>
readonly=True
states={'draft': [('readonly', False)]}
store=True
tracking=True
args: 'res.users', 'Manager'
action_get_attachment_view(self)
action_open_account_move(self)
action_register_payment(self)
action_sheet_move_create(self)
action_submit_sheet(self)
action_unpost(self)
activity_update(self)
approve_expense_sheets(self)
create(self, vals)
paid_expense_sheets(self)
refuse_sheet(self, reason)
reset_expense_sheets(self)
set_to_paid(self)
write(self, vals)
can_be_expensed
Boolean
compute='_compute_can_be_expensed'
help='Specify whether the product can be selected in an expense.'
readonly=False
store=True
string='Can be Expensed'
default_get(self, fields)
expense_alias_prefix
Char
compute='_compute_expense_alias_prefix'
readonly=False
store=True
args: 'Default Alias Name for Expenses'
module_hr_expense_extract
Boolean
string='Send bills to OCR to generate expenses'
module_hr_payroll_expense
Boolean
string='Reimburse Expenses in Payslip'
use_mailgateway
Boolean
config_parameter='hr_expense.use_mailgateway'
string='Let your employees record expenses by email'
get_values(self)
set_values(self)
expense_manager_id
Many2one
readonly=False
related='employee_id.expense_manager_id'
SELF_READABLE_FIELDS(self)
| REPOSITORY | |
|---|---|
| REPOSITORY | odoo/odoo |
| GIT | |
| GIT | https://github.com/odoo/odoo.git |
| GIT FOLDER | |
| GIT FOLDER | https://github.com/odoo/odoo/tree/14.0/hr_expense |
| VERSION | |
| VERSION | 2.0 |
| CATEGORY | |
| CATEGORY | Human Resources/Expenses |
| LICENSE | |
| LICENSE | LGPL-3 |
| APPLICATION | |
| APPLICATION | Yes |
| AUTO-INSTALLABLE | |
| AUTO-INSTALLABLE | No |
| AUTHORS | |
| AUTHORS | |
| MAINTAINERS | |
| MAINTAINERS | |
| COMMITTERS | |
| COMMITTERS | Raphael Collet, Odoo Translation Bot, Martin Trigaux, Olivier Dony, Thibault Delavallée, Christophe Simonis, Xavier Morel, Goffin Simon, Nicolas Martinelli, qdp-odoo, Aaron Bohy, GitHub, Lucas Perais (lpe), Barad Mahendra, Yannick Tivisse, Laurent Smet, Adrien Dieudonne, wan, mreficent, Julien Castiaux, Xavier-Do, Victor Feyens, Andrea Grazioso (agr-odoo), Sébastien Theys, Nasreddin (bon), alt-odoo, oco-odoo, Julien Mougenot, fw-bot, Adrien Widart, Nasreddin Boulif (bon), Touati Djamel (otd), Swapnesh Shah, IEL, Andrius Laukavičius, Mathieu Duckerts-Antoine, Pierre Paridans, jbw, jbm-odoo, Aurélien (avd), Arnaud Joset, Kevin Baptiste, Guillaume (guva), Xavier BOL (xbo), roen-odoo, jdoutreloux, sofiagvaladze, Jigar Vaghela, Nicolas (vin), william-andre, Florian Damhaut, Merlin (megu), tranngocson1996, william, Nshimiyimana Séna, Yolann Sabaux, Jinjiu Liu, bve-odoo, mehjabinfarsana, alsh-odoo, Giacomello Nathan (ngi), Simon Genin (ges), hieu, lathuat1997, flvr-odoo, Kairis, Julien Alardot (jual), Thomas Carlier, Nguyen Viet Phuong, Nam Dao |
| WEBSITE | |
| WEBSITE | https://www.odoo.com/page/expenses |
| LAST TRACKING UPDATE | |
| LAST TRACKING UPDATE | 2026-07-06 00:14:29 |
| ODOO DEPENDENCIES | |
| ODOO DEPENDENCIES |
odoo/odoo: - hr_contract - hr - base_setup - base - web - bus - web_tour - resource - account - product - uom - analytic - portal - web_editor - http_routing - auth_signup - digest |
| PYTHON DEPENDENCIES | |
| PYTHON DEPENDENCIES | Not have |
| SYSTEM DEPENDENCIES | |
| SYSTEM DEPENDENCIES | Not have |
| DESCRIPTION | |
| DESCRIPTION |
Manage expenses by Employees
============================
This application allows you to manage your employees' daily expenses. It gives you access to your employees’ fee notes and give you the right to complete and validate or refuse the notes. After validation it creates an invoice for the employee.
Employee can encode their own expenses and the validation flow puts it automatically in the accounting after validation by managers.
The whole flow is implemented as:
---------------------------------
* Draft expense
* Submitted by the employee to his manager
* Approved by his manager
* Validation by the accountant and accounting entries creation
This module also uses analytic accounting and is compatible with the invoice on timesheet module so that you are able to automatically re-invoice your customers' expenses if your work by project.
|
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
account_journal_dashboard_kanban_view_inherit_hr_expense |
account.journal.dashboard.kanban | account.journal | xpath | Inherits account.account_journal_dashboard_kanban_view |
assets_backend |
HR Expense Assets assets | ir.ui.view | qweb | Inherits web.assets_backend |
assets_tests |
HR Expense Assets Tests | ir.ui.view | qweb | Inherits web.assets_tests |
hr_department_view_kanban |
hr.department.kanban.inherit | hr.department | data | Inherits hr.hr_department_view_kanban |
hr_employee_view_form_inherit_expense |
hr.employee.view.form.expense | hr.employee | xpath | Inherits hr.view_employee_form |
hr_expense_kanban_view |
hr.expense.kanban | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_kanban |
hr_expense_kanban_view_header |
hr.expense.kanban | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_kanban |
hr_expense_refuse_wizard_view_form |
hr.expense.refuse.wizard.form | hr.expense.refuse.wizard | form | New |
hr_expense_sheet_view_activity |
hr.expense.sheet.activity | hr.expense.sheet | activity | New |
hr_expense_sheet_view_search |
hr.expense.sheet.view.search | hr.expense.sheet | search | New |
hr_expense_template_refuse_reason |
hr_expense_template_refuse_reason | ir.ui.view | qweb | New |
hr_expense_template_register |
hr_expense_template_register | ir.ui.view | qweb | New |
hr_expense_template_register_no_user |
hr_expense_template_register_no_user | ir.ui.view | qweb | New |
hr_expense_view_activity |
hr.expense.activity | hr.expense | activity | New |
hr_expense_view_expenses_analysis_kanban |
hr.expense.kanban | hr.expense | kanban | New |
hr_expense_view_expenses_analysis_tree |
hr.expense.tree | hr.expense | tree | New |
hr_expense_view_form |
hr.expense.view.form | hr.expense | form | New |
hr_expense_view_form_without_header |
hr.expense.view.form | hr.expense | xpath | Inherits hr_expense.hr_expense_view_form |
hr_expense_view_graph |
hr.expense.graph | hr.expense | graph | New |
hr_expense_view_pivot |
hr.expense.pivot | hr.expense | pivot | New |
hr_expense_view_search |
hr.expense.view.search | hr.expense | search | New |
product_product_expense_form_view |
product.product.expense.form | product.product | form | New |
product_product_expense_tree_view |
product.product.expense.tree | product.product | tree | New |
report_expense_sheet |
report_expense_sheet | ir.ui.view | qweb | New |
res_config_settings_view_form |
res.config.settings.view.form.inherit.hr.expense | res.config.settings | xpath | Inherits base.res_config_settings_view_form |
res_users_view_form_preferences |
hr.user.preferences.form.inherit.hr.expense | res.users | xpath | Inherits hr.res_users_view_form_profile |
view_employee_tree_inherit_expense |
hr.employee.tree.expense | hr.employee | xpath | Inherits hr.view_employee_tree |
view_expenses_tree |
hr.expense.tree | hr.expense | xpath | Inherits hr_expense_view_expenses_analysis_tree |
view_hr_expense_sheet_dashboard_tree |
hr.expense.sheet.dashboard.tree | hr.expense.sheet | xpath | Inherits hr_expense.view_hr_expense_sheet_tree |
view_hr_expense_sheet_dashboard_tree_header |
hr.expense.sheet.dashboard.tree | hr.expense.sheet | xpath | Inherits hr_expense.view_hr_expense_sheet_tree |
view_hr_expense_sheet_form |
hr.expense.sheet.form | hr.expense.sheet | form | New |
view_hr_expense_sheet_graph |
hr.expense.sheet.graph | hr.expense.sheet | graph | New |
view_hr_expense_sheet_kanban |
hr.expense.sheet.kanban | hr.expense.sheet | kanban | New |
view_hr_expense_sheet_kanban_header |
hr.expense.sheet.kanban | hr.expense.sheet | xpath | Inherits view_hr_expense_sheet_kanban |
view_hr_expense_sheet_kanban_no_header |
hr.expense.sheet.kanban | hr.expense.sheet | xpath | Inherits view_hr_expense_sheet_kanban |
view_hr_expense_sheet_pivot |
hr.expense.sheet.pivot | hr.expense.sheet | pivot | New |
view_hr_expense_sheet_tree |
hr.expense.sheet.tree | hr.expense.sheet | tree | New |
view_my_expenses_tree |
hr.expense.tree | hr.expense | xpath | Inherits hr_expense.view_expenses_tree |
view_product_hr_expense_form |
product.template.expense.form | product.template | div | Inherits product.product_template_form_view |
No new fields.
Public methods (2)get_journal_dashboard_datas(self)
open_expenses_action(self)
No new fields.
Public methods (2)button_cancel(self)
button_draft(self)
expense_id
Many2one → hr.expense
copy=False
help='Expense where the move line come from'
string='Expense'
args: 'hr.expense'
reconcile(self)
No new fields.
Public methods (0)No public methods.
expense_sheets_to_approve_count
Integer
compute='_compute_expense_sheets_to_approve'
string='Expenses Reports to Approve'
No public methods.
expense_manager_id
Many2one → res.users
compute='_compute_expense_manager'
domain=_group_hr_expense_user_domain
help='Select the user responsible for approving "Expenses" of this employee.\nIf empty, the approval is done by an Administrator or Approver (determined in settings/users).'
readonly=False
store=True
string='Expense'
args: 'res.users'
No public methods.
expense_manager_id
Many2one → res.users
readonly=True
args: 'res.users'
No public methods.
account_id
Many2one → account.account
compute='_compute_from_product_id_company_id'
default=_default_account_id
domain="[('internal_type', '=', 'other'), ('company_id', '=', company_id)]"
help='An expense account is expected'
readonly=False
store=True
string='Account'
args: 'account.account'
accounting_date
Date
groups='account.group_account_invoice,account.group_account_readonly'
related='sheet_id.accounting_date'
store=True
string='Accounting Date'
amount_residual
Monetary
compute='_compute_amount_residual'
compute_sudo=True
string='Amount Due'
analytic_account_id
Many2one → account.analytic.account
check_company=True
string='Analytic Account'
args: 'account.analytic.account'
analytic_tag_ids
Many2many → account.analytic.tag
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]"
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
string='Analytic Tags'
args: 'account.analytic.tag'
attachment_number
Integer
compute='_compute_attachment_number'
args: 'Number of Attachments'
company_currency_id
Many2one → res.currency
readonly=False
related='sheet_id.currency_id'
store=True
string='Report Company Currency'
args: 'res.currency'
company_id
Many2one → res.company
default=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
readonly=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Currency'
args: 'res.currency'
date
Date
default=fields.Date.context_today
readonly=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
string='Expense Date'
description
Text
readonly=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
args: 'Notes...'
employee_id
Many2one → hr.employee
check_company=True
compute='_compute_employee_id'
default=_default_employee_id
domain=<expr>
readonly=False
required=True
states={'approved': [('readonly', True)], 'done': [('readonly', True)]}
store=True
string='Employee'
tracking=True
args: 'hr.employee'
is_editable
Boolean
compute='_compute_is_editable'
args: 'Is Editable By Current User'
is_ref_editable
Boolean
compute='_compute_is_ref_editable'
args: 'Reference Is Editable By Current User'
is_refused
Boolean
copy=False
readonly=True
args: 'Explicitly Refused by manager or accountant'
name
Char
compute='_compute_from_product_id_company_id'
copy=True
required=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
store=True
args: 'Description'
payment_mode
Selection
default='own_account'
states={'done': [('readonly', True)], 'approved': [('readonly', True)], 'reported': [('readonly', True)]}
string='Paid By'
tracking=True
args: [('own_account', 'Employee (to reimburse)'), ('company_account', 'Company')]
product_id
Many2one → product.product
domain="[('can_be_expensed', '=', True), '|', ('company_id', '=', False), ('company_id', '=', company_id)]"
ondelete='restrict'
readonly=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
string='Product'
tracking=True
args: 'product.product'
product_uom_category_id
Many2one
readonly=True
related='product_id.uom_id.category_id'
product_uom_id
Many2one → uom.uom
compute='_compute_from_product_id_company_id'
copy=True
default=_default_product_uom_id
domain="[('category_id', '=', product_uom_category_id)]"
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
store=True
string='Unit of Measure'
args: 'uom.uom'
quantity
Float
default=1
digits='Product Unit of Measure'
readonly=True
required=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
reference
Char
sample
Boolean
sheet_id
Many2one → hr.expense.sheet
copy=False
domain="[('employee_id', '=', employee_id), ('company_id', '=', company_id)]"
readonly=True
string='Expense Report'
args: 'hr.expense.sheet'
state
Selection
compute='_compute_state'
copy=False
default='draft'
help='Status of the expense.'
index=True
readonly=True
store=True
string='Status'
args: [('draft', 'To Submit'), ('reported', 'Submitted'), ('approved', 'Approved'), ('done', 'Paid'), ('refused', 'Refused')]
tax_ids
Many2many → account.tax
compute='_compute_from_product_id_company_id'
domain="[('company_id', '=', company_id), ('type_tax_use', '=', 'purchase')]"
readonly=False
store=True
string='Taxes'
args: 'account.tax', 'expense_tax', 'expense_id', 'tax_id'
total_amount
Monetary
compute='_compute_amount'
currency_field='currency_id'
store=True
tracking=True
args: 'Total'
total_amount_company
Monetary
compute='_compute_total_amount_company'
currency_field='company_currency_id'
store=True
args: 'Total (Company Currency)'
unit_amount
Float
compute='_compute_from_product_id_company_id'
copy=True
digits='Product Price'
required=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
store=True
args: 'Unit Price'
untaxed_amount
Float
compute='_compute_amount'
digits='Account'
store=True
args: 'Subtotal'
action_get_attachment_view(self)
action_move_create(self)
action_submit_expenses(self)
action_view_sheet(self)
create_expense_from_attachments(self, attachment_ids=None, view_type='tree')
get_empty_list_help(self, help_message)
get_expense_dashboard(self)
message_new(self, msg_dict, custom_values=None)
refuse_expense(self, reason)
unlink(self)
write(self, vals)
hr_expense_ids
Many2many → hr.expense
hr_expense_sheet_id
Many2one → hr.expense.sheet
reason
Char
required=True
string='Reason'
default_get(self, fields)
expense_refuse_reason(self)
account_move_id
Many2one → account.move
copy=False
ondelete='restrict'
readonly=True
string='Journal Entry'
args: 'account.move'
accounting_date
Date
address_id
Many2one → res.partner
check_company=True
compute='_compute_from_employee_id'
copy=True
readonly=False
store=True
string='Employee Home Address'
args: 'res.partner'
amount_residual
Monetary
compute='_compute_amount_residual'
currency_field='currency_id'
store=True
string='Amount Due'
attachment_number
Integer
compute='_compute_attachment_number'
string='Number of Attachments'
bank_journal_id
Many2one → account.journal
check_company=True
default=_default_bank_journal_id
domain="[('type', 'in', ['cash', 'bank']), ('company_id', '=', company_id)]"
help='The payment method used when the expense is paid by the company.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Bank Journal'
args: 'account.journal'
can_reset
Boolean
compute='_compute_can_reset'
args: 'Can Reset'
company_id
Many2one → res.company
default=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)]}
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
readonly=True
states={'draft': [('readonly', False)]}
string='Currency'
args: 'res.currency'
department_id
Many2one → hr.department
compute='_compute_from_employee_id'
copy=False
readonly=False
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
store=True
string='Department'
args: 'hr.department'
employee_id
Many2one → hr.employee
check_company=True
default=_default_employee_id
domain=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)]}
string='Employee'
tracking=True
args: 'hr.employee'
expense_line_ids
One2many → hr.expense
comodel_name='hr.expense'
copy=False
inverse_name='sheet_id'
states={'post': [('readonly', True)], 'done': [('readonly', True)], 'cancel': [('readonly', True)]}
string='Expense Lines'
is_multiple_currency
Boolean
compute='_compute_is_multiple_currency'
args: 'Handle lines with different currencies'
journal_id
Many2one → account.journal
check_company=True
default=_default_journal_id
domain="[('type', '=', 'purchase'), ('company_id', '=', company_id)]"
help='The journal used when the expense is done.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Expense Journal'
args: 'account.journal'
name
Char
required=True
tracking=True
args: 'Expense Report Summary'
payment_mode
Selection
default='own_account'
readonly=True
related='expense_line_ids.payment_mode'
string='Paid By'
tracking=True
state
Selection
copy=False
default='draft'
help='Expense Report State'
index=True
readonly=True
required=True
string='Status'
tracking=True
args: [('draft', 'Draft'), ('submit', 'Submitted'), ('approve', 'Approved'), ('post', 'Posted'), ('done', 'Paid'), ('cancel', 'Refused')]
total_amount
Monetary
compute='_compute_amount'
currency_field='currency_id'
store=True
tracking=True
args: 'Total Amount'
user_id
Many2one → res.users
compute='_compute_from_employee_id'
copy=False
domain=<expr>
readonly=True
states={'draft': [('readonly', False)]}
store=True
tracking=True
args: 'res.users', 'Manager'
action_get_attachment_view(self)
action_register_payment(self)
action_sheet_move_create(self)
action_submit_sheet(self)
activity_update(self)
approve_expense_sheets(self)
create(self, vals)
paid_expense_sheets(self)
refuse_sheet(self, reason)
reset_expense_sheets(self)
set_to_paid(self)
unlink(self)
can_be_expensed
Boolean
compute='_compute_can_be_expensed'
help='Specify whether the product can be selected in an expense.'
readonly=False
store=True
string='Can be Expensed'
default_get(self, fields)
expense_alias_prefix
Char
compute='_compute_expense_alias_prefix'
readonly=False
store=True
args: 'Default Alias Name for Expenses'
module_hr_expense_extract
Boolean
string='Send bills to OCR to generate expenses'
module_hr_payroll_expense
Boolean
string='Reimburse Expenses in Payslip'
use_mailgateway
Boolean
config_parameter='hr_expense.use_mailgateway'
string='Let your employees record expenses by email'
get_values(self)
set_values(self)
expense_manager_id
Many2one
readonly=False
related='employee_id.expense_manager_id'
No public methods.
| REPOSITORY | |
|---|---|
| REPOSITORY | odoo/odoo |
| GIT | |
| GIT | https://github.com/odoo/odoo.git |
| GIT FOLDER | |
| GIT FOLDER | https://github.com/odoo/odoo/tree/13.0/hr_expense |
| VERSION | |
| VERSION | 2.0 |
| CATEGORY | |
| CATEGORY | Accounting/Expenses |
| LICENSE | |
| LICENSE | LGPL-3 |
| APPLICATION | |
| APPLICATION | Yes |
| AUTO-INSTALLABLE | |
| AUTO-INSTALLABLE | No |
| AUTHORS | |
| AUTHORS | |
| MAINTAINERS | |
| MAINTAINERS | |
| COMMITTERS | |
| COMMITTERS | Raphael Collet, Odoo Translation Bot, Martin Trigaux, Olivier Dony, Thibault Delavallée, Denis Ledoux, Christophe Simonis, Xavier Morel, Goffin Simon, Mohammed Shekha, Nicolas Martinelli, qdp-odoo, Jeremy Kersten, GitHub, Damien Bouvy, Olivier Colson, Yannick Tivisse, Martin Geubelle, Odoo Online, Laurent Smet, jem-odoo, Adrien Dieudonne, RomainLibert, Jorge Pinna Puissant, Alexandre Kühn, Adrian Torres, wan, Xavier-Do, Romeo Fragomeli, Victor Feyens, Andrea Grazioso (agr-odoo), Sébastien Theys, Nasreddin (bon), alt-odoo, oco-odoo, Florent de Labarre, fw-bot, Anh Thao Pham (pta), Lucas Lefèvre, Adrien Widart, IEL, Bhavesh Odedra, odoo, qmo-odoo, jdoutreloux, william-andre, Florian Damhaut, tranngocson1996, Ankita Raval, MaxLarue, Jinjiu Liu, Nimesh Jethva |
| WEBSITE | |
| WEBSITE | https://www.odoo.com/page/expenses |
| LAST TRACKING UPDATE | |
| LAST TRACKING UPDATE | 2026-07-06 00:06:42 |
| ODOO DEPENDENCIES | |
| ODOO DEPENDENCIES |
odoo/odoo: - hr_contract - hr - base_setup - base - web - bus - web_tour - resource - mail_bot - account - product - uom - analytic - portal - web_editor - http_routing - auth_signup - digest |
| PYTHON DEPENDENCIES | |
| PYTHON DEPENDENCIES | Not have |
| SYSTEM DEPENDENCIES | |
| SYSTEM DEPENDENCIES | Not have |
| DESCRIPTION | |
| DESCRIPTION |
Manage expenses by Employees
============================
This application allows you to manage your employees' daily expenses. It gives you access to your employees’ fee notes and give you the right to complete and validate or refuse the notes. After validation it creates an invoice for the employee.
Employee can encode their own expenses and the validation flow puts it automatically in the accounting after validation by managers.
The whole flow is implemented as:
---------------------------------
* Draft expense
* Submitted by the employee to his manager
* Approved by his manager
* Validation by the accountant and accounting entries creation
This module also uses analytic accounting and is compatible with the invoice on timesheet module so that you are able to automatically re-invoice your customers' expenses if your work by project.
|
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
account_journal_dashboard_kanban_view_inherit_hr_expense |
account.journal.dashboard.kanban | account.journal | xpath | Inherits account.account_journal_dashboard_kanban_view |
assets_backend |
HR Expense Assets assets | ir.ui.view | qweb | Inherits web.assets_backend |
assets_tests |
HR Expense Assets Tests | ir.ui.view | qweb | Inherits web.assets_tests |
hr_department_view_kanban |
hr.department.kanban.inherit | hr.department | data | Inherits hr.hr_department_view_kanban |
hr_employee_view_form_inherit_expense |
hr.employee.view.form.expense | hr.employee | xpath | Inherits hr.view_employee_form |
hr_expense_kanban_view |
hr.expense.kanban | hr.expense | kanban | New |
hr_expense_refuse_wizard_view_form |
hr.expense.refuse.wizard.form | hr.expense.refuse.wizard | form | New |
hr_expense_sheet_register_payment_view_form |
hr.expense.sheet.register.payment.wizard.form | hr.expense.sheet.register.payment.wizard | form | New |
hr_expense_sheet_view_activity |
hr.expense.sheet.activity | hr.expense.sheet | activity | New |
hr_expense_sheet_view_search |
hr.expense.sheet.view.search | hr.expense.sheet | search | New |
hr_expense_template_refuse_reason |
hr_expense_template_refuse_reason | ir.ui.view | qweb | New |
hr_expense_template_register |
hr_expense_template_register | ir.ui.view | qweb | New |
hr_expense_template_register_no_user |
hr_expense_template_register_no_user | ir.ui.view | qweb | New |
hr_expense_view_activity |
hr.expense.activity | hr.expense | activity | New |
hr_expense_view_form |
hr.expense.view.form | hr.expense | form | New |
hr_expense_view_form_without_header |
hr.expense.view.form | hr.expense | xpath | Inherits hr_expense.hr_expense_view_form |
hr_expense_view_graph |
hr.expense.graph | hr.expense | graph | New |
hr_expense_view_pivot |
hr.expense.pivot | hr.expense | pivot | New |
hr_expense_view_search |
hr.expense.view.search | hr.expense | search | New |
product_product_expense_form_view |
product.product.expense.form | product.product | form | New |
product_product_expense_tree_view |
product.product.expense.tree | product.product | tree | New |
report_expense_sheet |
report_expense_sheet | ir.ui.view | qweb | New |
res_config_settings_view_form |
res.config.settings.view.form.inherit.hr.expense | res.config.settings | xpath | Inherits base.res_config_settings_view_form |
res_users_view_form_preferences |
hr.user.preferences.form.inherit.hr.expense | res.users | xpath | Inherits hr.res_users_view_form_profile |
view_expenses_tree |
hr.expense.tree | hr.expense | tree | New |
view_hr_expense_sheet_form |
hr.expense.sheet.form | hr.expense.sheet | form | New |
view_hr_expense_sheet_graph |
hr.expense.sheet.graph | hr.expense.sheet | graph | New |
view_hr_expense_sheet_kanban |
hr.expense.sheet.kanban | hr.expense.sheet | kanban | New |
view_hr_expense_sheet_pivot |
hr.expense.sheet.pivot | hr.expense.sheet | pivot | New |
view_hr_expense_sheet_tree |
hr.expense.sheet.tree | hr.expense.sheet | tree | New |
view_product_hr_expense_form |
product.template.expense.form | product.template | div | Inherits product.product_template_form_view |
No new fields.
Public methods (2)get_journal_dashboard_datas(self)
open_expenses_action(self)
No new fields.
Public methods (1)button_cancel(self)
expense_id
Many2one → hr.expense
copy=False
help='Expense where the move line come from'
string='Expense'
args: 'hr.expense'
reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False)
expense_sheets_to_approve_count
Integer
compute='_compute_expense_sheets_to_approve'
string='Expenses Reports to Approve'
No public methods.
expense_manager_id
Many2one → res.users
domain=_group_hr_expense_user_domain
help='User responsible of expense approval. Should be Expense approver.'
string='Expense'
args: 'res.users'
No public methods.
expense_manager_id
Many2one → res.users
readonly=True
args: 'res.users'
No public methods.
account_id
Many2one → account.account
default=_default_account_id
domain="[('internal_type', '=', 'other'), ('company_id', '=', company_id)]"
help='An expense account is expected'
string='Account'
args: 'account.account'
analytic_account_id
Many2one → account.analytic.account
check_company=True
string='Analytic Account'
args: 'account.analytic.account'
analytic_tag_ids
Many2many → account.analytic.tag
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]"
states={'approved': [('readonly', True)], 'done': [('readonly', True)]}
string='Analytic Tags'
args: 'account.analytic.tag'
attachment_number
Integer
compute='_compute_attachment_number'
args: 'Number of Attachments'
company_currency_id
Many2one → res.currency
readonly=False
related='sheet_id.currency_id'
store=True
string='Report Company Currency'
args: 'res.currency'
company_id
Many2one → res.company
default=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
readonly=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Currency'
args: 'res.currency'
date
Date
default=fields.Date.context_today
readonly=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
string='Date'
description
Text
readonly=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
args: 'Notes...'
employee_id
Many2one → hr.employee
check_company=True
default=_default_employee_id
domain=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
string='Employee'
args: 'hr.employee'
is_editable
Boolean
compute='_compute_is_editable'
args: 'Is Editable By Current User'
is_ref_editable
Boolean
compute='_compute_is_ref_editable'
args: 'Reference Is Editable By Current User'
is_refused
Boolean
copy=False
readonly=True
args: 'Explicitely Refused by manager or acccountant'
name
Char
readonly=True
required=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
args: 'Description'
payment_mode
Selection
default='own_account'
states={'done': [('readonly', True)], 'approved': [('readonly', True)], 'reported': [('readonly', True)]}
string='Paid By'
args: [('own_account', 'Employee (to reimburse)'), ('company_account', 'Company')]
product_id
Many2one → product.product
domain="[('can_be_expensed', '=', True), '|', ('company_id', '=', False), ('company_id', '=', company_id)]"
ondelete='restrict'
readonly=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
string='Product'
args: 'product.product'
product_uom_category_id
Many2one
readonly=True
related='product_id.uom_id.category_id'
product_uom_id
Many2one → uom.uom
default=_default_product_uom_id
domain="[('category_id', '=', product_uom_category_id)]"
readonly=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Unit of Measure'
args: 'uom.uom'
quantity
Float
default=1
digits='Product Unit of Measure'
readonly=True
required=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
reference
Char
sheet_id
Many2one → hr.expense.sheet
copy=False
domain="[('employee_id', '=', employee_id), ('company_id', '=', company_id)]"
readonly=True
string='Expense Report'
args: 'hr.expense.sheet'
state
Selection
compute='_compute_state'
copy=False
help='Status of the expense.'
index=True
readonly=True
store=True
string='Status'
args: [('draft', 'To Submit'), ('reported', 'Submitted'), ('approved', 'Approved'), ('done', 'Paid'), ('refused', 'Refused')]
tax_ids
Many2many → account.tax
domain="[('company_id', '=', company_id), ('type_tax_use', '=', 'purchase')]"
string='Taxes'
args: 'account.tax', 'expense_tax', 'expense_id', 'tax_id'
total_amount
Monetary
compute='_compute_amount'
currency_field='currency_id'
store=True
args: 'Total'
total_amount_company
Monetary
compute='_compute_total_amount_company'
currency_field='company_currency_id'
store=True
args: 'Total (Company Currency)'
unit_amount
Float
digits='Product Price'
readonly=True
required=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
args: 'Unit Price'
untaxed_amount
Float
compute='_compute_amount'
digits='Account'
store=True
args: 'Subtotal'
action_get_attachment_view(self)
action_move_create(self)
action_submit_expenses(self)
action_view_sheet(self)
create_expense_from_attachments(self, attachment_ids=None)
get_empty_list_help(self, help_message)
message_new(self, msg_dict, custom_values=None)
refuse_expense(self, reason)
unlink(self)
write(self, vals)
hr_expense_ids
Many2many → hr.expense
hr_expense_sheet_id
Many2one → hr.expense.sheet
reason
Char
required=True
string='Reason'
default_get(self, fields)
expense_refuse_reason(self)
account_move_id
Many2one → account.move
copy=False
ondelete='restrict'
readonly=True
string='Journal Entry'
args: 'account.move'
accounting_date
Date
address_id
Many2one → res.partner
check_company=True
string='Employee Home Address'
args: 'res.partner'
attachment_number
Integer
compute='_compute_attachment_number'
string='Number of Attachments'
bank_journal_id
Many2one → account.journal
check_company=True
default=_default_bank_journal_id
domain="[('type', 'in', ['cash', 'bank']), ('company_id', '=', company_id)]"
help='The payment method used when the expense is paid by the company.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Bank Journal'
args: 'account.journal'
can_reset
Boolean
compute='_compute_can_reset'
args: 'Can Reset'
company_id
Many2one → res.company
default=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)]}
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
readonly=True
states={'draft': [('readonly', False)]}
string='Currency'
args: 'res.currency'
department_id
Many2one → hr.department
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
string='Department'
args: 'hr.department'
employee_id
Many2one → hr.employee
check_company=True
default=_default_employee_id
readonly=True
required=True
states={'draft': [('readonly', False)]}
string='Employee'
args: 'hr.employee'
expense_line_ids
One2many → hr.expense
copy=False
string='Expense Lines'
args: 'hr.expense', 'sheet_id'
is_multiple_currency
Boolean
compute='_compute_is_multiple_currency'
args: 'Handle lines with different currencies'
journal_id
Many2one → account.journal
check_company=True
default=_default_journal_id
domain="[('type', '=', 'purchase'), ('company_id', '=', company_id)]"
help='The journal used when the expense is done.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Expense Journal'
args: 'account.journal'
name
Char
required=True
args: 'Expense Report Summary'
payment_mode
Selection
default='own_account'
readonly=True
related='expense_line_ids.payment_mode'
string='Paid By'
state
Selection
copy=False
default='draft'
help='Expense Report State'
index=True
readonly=True
required=True
string='Status'
tracking=True
args: [('draft', 'Draft'), ('submit', 'Submitted'), ('approve', 'Approved'), ('post', 'Posted'), ('done', 'Paid'), ('cancel', 'Refused')]
total_amount
Monetary
compute='_compute_amount'
currency_field='currency_id'
store=True
args: 'Total Amount'
user_id
Many2one → res.users
copy=False
readonly=True
states={'draft': [('readonly', False)]}
tracking=True
args: 'res.users', 'Manager'
action_get_attachment_view(self)
action_sheet_move_create(self)
action_submit_sheet(self)
activity_update(self)
approve_expense_sheets(self)
create(self, vals)
paid_expense_sheets(self)
refuse_sheet(self, reason)
reset_expense_sheets(self)
set_to_paid(self)
unlink(self)
amount
Monetary
required=True
string='Payment Amount'
communication
Char
string='Memo'
company_id
Many2one → res.company
readonly=True
related='expense_sheet_id.company_id'
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
required=True
string='Currency'
args: 'res.currency'
expense_sheet_id
Many2one → hr.expense.sheet
required=True
string='Expense Report'
args: 'hr.expense.sheet'
hide_payment_method
Boolean
compute='_compute_hide_payment_method'
help="Technical field used to hide the payment method if the selected journal has only one available which is 'manual'"
journal_id
Many2one → account.journal
domain="[('type', 'in', ('bank', 'cash')), ('company_id', '=', company_id)]"
required=True
string='Payment Method'
args: 'account.journal'
partner_bank_account_id
Many2one → res.partner.bank
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]"
string='Recipient Bank Account'
args: 'res.partner.bank'
partner_id
Many2one → res.partner
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]"
required=True
string='Partner'
args: 'res.partner'
payment_date
Date
default=fields.Date.context_today
required=True
string='Payment Date'
payment_method_id
Many2one → account.payment.method
required=True
string='Payment Type'
args: 'account.payment.method'
require_partner_bank_account
Boolean
compute='_compute_show_partner_bank'
help='Technical field used to know whether the field `partner_bank_account_id` needs to be required or not in the payments form views'
show_partner_bank_account
Boolean
compute='_compute_show_partner_bank'
help='Technical field used to know whether the field `partner_bank_account_id` needs to be displayed or not in the payments form views'
default_get(self, fields)
expense_post_payment(self)
can_be_expensed
Boolean
help='Specify whether the product can be selected in an expense.'
string='Can be Expensed'
default_get(self, fields)
expense_alias_prefix
Char
module_hr_payroll_expense
Boolean
string='Reimburse Expenses in Payslip'
use_mailgateway
Boolean
config_parameter='hr_expense.use_mailgateway'
string='Let your employees record expenses by email'
get_values(self)
set_values(self)
expense_manager_id
Many2one
readonly=False
related='employee_id.expense_manager_id'
No public methods.
| REPOSITORY | |
|---|---|
| REPOSITORY | odoo/odoo |
| GIT | |
| GIT | https://github.com/odoo/odoo.git |
| GIT FOLDER | |
| GIT FOLDER | https://github.com/odoo/odoo/tree/12.0/hr_expense |
| VERSION | |
| VERSION | 2.0 |
| CATEGORY | |
| CATEGORY | Human Resources |
| LICENSE | |
| LICENSE | LGPL-3 |
| APPLICATION | |
| APPLICATION | Yes |
| AUTO-INSTALLABLE | |
| AUTO-INSTALLABLE | No |
| AUTHORS | |
| AUTHORS | |
| MAINTAINERS | |
| MAINTAINERS | |
| COMMITTERS | |
| COMMITTERS | Raphael Collet, Odoo Translation Bot, Christophe Matthieu, Martin Trigaux, Fabien Pinckaers, Thibault Delavallée, Denis Ledoux, Christophe Simonis, Xavier Morel, Goffin Simon, Simon Lejeune, Nicolas Martinelli, Jérome Maes, Richard Mathot, qdp-odoo, Géry Debongnie, GitHub, Lucas Perais (lpe), Olivier Colson, Yannick Tivisse, Alexandre Kühn, qsm-odoo, Martin Geubelle, Toufik Benjaa, jem-odoo, RomainLibert, Christophe Monniez, Nans Lefebvre, Alexandre Kühn, jev-odoo, fda-odoo, Adrian Torres, mreficent, XavierDo, Xavier-Do, Luis Torres, david, Alex Tuyls |
| WEBSITE | |
| WEBSITE | https://www.odoo.com/page/expenses |
| LAST TRACKING UPDATE | |
| LAST TRACKING UPDATE | 2026-07-05 23:57:01 |
| ODOO DEPENDENCIES | |
| ODOO DEPENDENCIES |
odoo/odoo: - hr_contract - hr - base_setup - base - web - bus - web_tour - resource - account - product - decimal_precision - uom - analytic - portal - http_routing - digest |
| PYTHON DEPENDENCIES | |
| PYTHON DEPENDENCIES | Not have |
| SYSTEM DEPENDENCIES | |
| SYSTEM DEPENDENCIES | Not have |
| DESCRIPTION | |
| DESCRIPTION |
Manage expenses by Employees
============================
This application allows you to manage your employees' daily expenses. It gives you access to your employees’ fee notes and give you the right to complete and validate or refuse the notes. After validation it creates an invoice for the employee.
Employee can encode their own expenses and the validation flow puts it automatically in the accounting after validation by managers.
The whole flow is implemented as:
---------------------------------
* Draft expense
* Submitted by the employee to his manager
* Approved by his manager
* Validation by the accountant and accounting entries creation
This module also uses analytic accounting and is compatible with the invoice on timesheet module so that you are able to automatically re-invoice your customers' expenses if your work by project.
|
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
account_journal_dashboard_kanban_view_inherit_hr_expense |
account.journal.dashboard.kanban | account.journal | xpath | Inherits account.account_journal_dashboard_kanban_view |
assets_backend_expense |
hr_expense assets | ir.ui.view | qweb | Inherits web.assets_backend |
hr_department_view_kanban |
hr.department.kanban.inherit | hr.department | data | Inherits hr.hr_department_view_kanban |
hr_employee_view_form_inherit_expense |
hr.employee.view.form.expense | hr.employee | xpath | Inherits hr.view_employee_form |
hr_expense_kanban_view |
hr.expense.kanban | hr.expense | kanban | New |
hr_expense_refuse_wizard_view_form |
hr.expense.refuse.wizard.form | hr.expense.refuse.wizard | form | New |
hr_expense_sheet_register_payment_view_form |
hr.expense.sheet.register.payment.wizard.form | hr.expense.sheet.register.payment.wizard | form | New |
hr_expense_template_refuse_reason |
hr_expense_template_refuse_reason | ir.ui.view | qweb | New |
hr_expense_view_form |
hr.expense.view.form | hr.expense | form | New |
hr_expense_view_form_without_header |
hr.expense.view.form | hr.expense | xpath | Inherits hr_expense.hr_expense_view_form |
hr_expense_view_graph |
hr.expense.graph | hr.expense | graph | New |
hr_expense_view_pivot |
hr.expense.pivot | hr.expense | pivot | New |
product_product_expense_form_view |
product.product.expense.form | product.product | form | New |
product_product_expense_tree_view |
product.product.expense.tree | product.product | tree | New |
report_expense_sheet |
report_expense_sheet | ir.ui.view | qweb | New |
res_config_settings_view_form |
res.config.settings.view.form.inherit.hr.expense | res.config.settings | xpath | Inherits base.res_config_settings_view_form |
view_expenses_tree |
hr.expense.tree | hr.expense | tree | New |
view_hr_expense_filter |
hr.expense.filter | hr.expense | search | New |
view_hr_expense_sheet_filter |
hr.expense.sheet.filter | hr.expense.sheet | search | New |
view_hr_expense_sheet_form |
hr.expense.sheet.form | hr.expense.sheet | form | New |
view_hr_expense_sheet_graph |
hr.expense.sheet.graph | hr.expense.sheet | graph | New |
view_hr_expense_sheet_kanban |
hr.expense.sheet.kanban | hr.expense.sheet | kanban | New |
view_hr_expense_sheet_pivot |
hr.expense.sheet.pivot | hr.expense.sheet | pivot | New |
view_hr_expense_sheet_tree |
hr.expense.sheet.tree | hr.expense.sheet | tree | New |
view_product_hr_expense_form |
product.template.expense.form | product.template | div | Inherits product.product_template_form_view |
No new fields.
Public methods (2)get_journal_dashboard_datas(self)
open_expenses_action(self)
expense_id
Many2one → hr.expense
copy=False
help='Expense where the move line come from'
string='Expense'
args: 'hr.expense'
reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False)
expense_sheets_to_approve_count
Integer
compute='_compute_expense_sheets_to_approve'
string='Expenses Reports to Approve'
No public methods.
expense_manager_id
Many2one → res.users
domain=<expr>
help='User responsible of expense approval. Should be Expense Officer or Manager.'
string='Expense Responsible'
args: 'res.users'
No public methods.
account_id
Many2one → account.account
default=_default_account_id
help='An expense account is expected'
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
string='Account'
args: 'account.account'
analytic_account_id
Many2one → account.analytic.account
oldname='analytic_account'
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
string='Analytic Account'
args: 'account.analytic.account'
analytic_tag_ids
Many2many → account.analytic.tag
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
string='Analytic Tags'
args: 'account.analytic.tag'
attachment_number
Integer
compute='_compute_attachment_number'
args: 'Number of Attachments'
company_currency_id
Many2one → res.currency
readonly=False
related='sheet_id.currency_id'
store=True
string='Report Company Currency'
args: 'res.currency'
company_id
Many2one → res.company
default=<expr>
readonly=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
readonly=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Currency'
args: 'res.currency'
date
Date
default=fields.Date.context_today
readonly=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
string='Date'
description
Text
readonly=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
args: 'Notes...'
employee_id
Many2one → hr.employee
default=_default_employee_id
domain=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
string='Employee'
args: 'hr.employee'
is_refused
Boolean
copy=False
readonly=True
args: 'Explicitely Refused by manager or acccountant'
name
Char
readonly=True
required=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
args: 'Description'
payment_mode
Selection
default='own_account'
states={'done': [('readonly', True)], 'approved': [('readonly', True)], 'reported': [('readonly', True)]}
string='Paid By'
args: [('own_account', 'Employee (to reimburse)'), ('company_account', 'Company')]
product_id
Many2one → product.product
domain=[('can_be_expensed', '=', True)]
readonly=True
required=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
string='Product'
args: 'product.product'
product_uom_id
Many2one → uom.uom
default=_default_product_uom_id
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Unit of Measure'
args: 'uom.uom'
quantity
Float
default=1
digits=dp.get_precision('Product Unit of Measure')
readonly=True
required=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
reference
Char
sheet_id
Many2one → hr.expense.sheet
copy=False
readonly=True
string='Expense Report'
args: 'hr.expense.sheet'
state
Selection
compute='_compute_state'
copy=False
help='Status of the expense.'
index=True
readonly=True
store=True
string='Status'
args: [('draft', 'To Submit'), ('reported', 'Submitted'), ('approved', 'Approved'), ('done', 'Paid'), ('refused', 'Refused')]
tax_ids
Many2many → account.tax
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Taxes'
args: 'account.tax', 'expense_tax', 'expense_id', 'tax_id'
total_amount
Monetary
compute='_compute_amount'
currency_field='currency_id'
digits=dp.get_precision('Account')
store=True
args: 'Total'
total_amount_company
Monetary
compute='_compute_total_amount_company'
currency_field='company_currency_id'
digits=dp.get_precision('Account')
store=True
args: 'Total (Company Currency)'
unit_amount
Float
digits=dp.get_precision('Product Price')
readonly=True
required=True
states={'draft': [('readonly', False)], 'reported': [('readonly', False)], 'refused': [('readonly', False)]}
args: 'Unit Price'
untaxed_amount
Float
compute='_compute_amount'
digits=dp.get_precision('Account')
store=True
args: 'Subtotal'
action_get_attachment_view(self)
action_move_create(self)
action_submit_expenses(self)
action_view_sheet(self)
get_empty_list_help(self, help_message)
message_new(self, msg_dict, custom_values=None)
refuse_expense(self, reason)
unlink(self)
hr_expense_ids
Many2many → hr.expense
hr_expense_sheet_id
Many2one → hr.expense.sheet
reason
Char
required=True
string='Reason'
default_get(self, fields)
expense_refuse_reason(self)
account_move_id
Many2one → account.move
copy=False
ondelete='restrict'
string='Journal Entry'
args: 'account.move'
accounting_date
Date
address_id
Many2one → res.partner
string='Employee Home Address'
args: 'res.partner'
attachment_number
Integer
compute='_compute_attachment_number'
string='Number of Attachments'
bank_journal_id
Many2one → account.journal
default=_default_bank_journal_id
help='The payment method used when the expense is paid by the company.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Bank Journal'
args: 'account.journal'
can_reset
Boolean
compute='_compute_can_reset'
args: 'Can Reset'
company_id
Many2one → res.company
default=<expr>
readonly=True
states={'draft': [('readonly', False)]}
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
readonly=True
states={'draft': [('readonly', False)]}
string='Currency'
args: 'res.currency'
department_id
Many2one → hr.department
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
string='Department'
args: 'hr.department'
employee_id
Many2one → hr.employee
default=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)]}
string='Employee'
args: 'hr.employee'
expense_line_ids
One2many → hr.expense
copy=False
states={'approve': [('readonly', True)], 'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Expense Lines'
args: 'hr.expense', 'sheet_id'
is_multiple_currency
Boolean
compute='_compute_is_multiple_currency'
args: 'Handle lines with different currencies'
journal_id
Many2one → account.journal
default=_default_journal_id
help='The journal used when the expense is done.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Expense Journal'
args: 'account.journal'
name
Char
required=True
args: 'Expense Report Summary'
payment_mode
Selection
default='own_account'
readonly=True
related='expense_line_ids.payment_mode'
string='Paid By'
args: [('own_account', 'Employee (to reimburse)'), ('company_account', 'Company')]
state
Selection
copy=False
default='draft'
help='Expense Report State'
index=True
readonly=True
required=True
string='Status'
track_visibility='onchange'
args: [('draft', 'Draft'), ('submit', 'Submitted'), ('approve', 'Approved'), ('post', 'Posted'), ('done', 'Paid'), ('cancel', 'Refused')]
total_amount
Monetary
compute='_compute_amount'
currency_field='currency_id'
digits=dp.get_precision('Account')
store=True
args: 'Total Amount'
user_id
Many2one → res.users
copy=False
oldname='responsible_id'
readonly=True
states={'draft': [('readonly', False)]}
track_visibility='onchange'
args: 'res.users', 'Manager'
action_get_attachment_view(self)
action_sheet_move_create(self)
action_submit_sheet(self)
activity_update(self)
approve_expense_sheets(self)
create(self, vals)
paid_expense_sheets(self)
refuse_sheet(self, reason)
reset_expense_sheets(self)
set_to_paid(self)
unlink(self)
amount
Monetary
required=True
string='Payment Amount'
communication
Char
string='Memo'
company_id
Many2one → res.company
readonly=True
related='journal_id.company_id'
required=True
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
required=True
string='Currency'
args: 'res.currency'
hide_payment_method
Boolean
compute='_compute_hide_payment_method'
help="Technical field used to hide the payment method if the selected journal has only one available which is 'manual'"
journal_id
Many2one → account.journal
domain=[('type', 'in', ('bank', 'cash'))]
required=True
string='Payment Method'
args: 'account.journal'
partner_bank_account_id
Many2one → res.partner.bank
string='Recipient Bank Account'
args: 'res.partner.bank'
partner_id
Many2one → res.partner
default=_default_partner_id
required=True
string='Partner'
args: 'res.partner'
payment_date
Date
default=fields.Date.context_today
required=True
string='Payment Date'
payment_method_id
Many2one → account.payment.method
required=True
string='Payment Type'
args: 'account.payment.method'
show_partner_bank_account
Boolean
compute='_compute_show_partner_bank'
help='Technical field used to know whether the field `partner_bank_account_id` needs to be displayed or not in the payments form views'
expense_post_payment(self)
can_be_expensed
Boolean
help='Specify whether the product can be selected in an expense.'
string='Can be Expensed'
create(self, vals)
expense_alias_prefix
Char
use_mailgateway
Boolean
config_parameter='hr_expense.use_mailgateway'
string='Let your employees record expenses by email'
get_values(self)
set_values(self)
| REPOSITORY | |
|---|---|
| REPOSITORY | odoo/odoo |
| GIT | |
| GIT | https://github.com/odoo/odoo.git |
| GIT FOLDER | |
| GIT FOLDER | https://github.com/odoo/odoo/tree/11.0/hr_expense |
| VERSION | |
| VERSION | 2.0 |
| CATEGORY | |
| CATEGORY | Human Resources |
| LICENSE | |
| LICENSE | LGPL-3 |
| APPLICATION | |
| APPLICATION | Yes |
| AUTO-INSTALLABLE | |
| AUTO-INSTALLABLE | No |
| AUTHORS | |
| AUTHORS | |
| MAINTAINERS | |
| MAINTAINERS | |
| COMMITTERS | |
| COMMITTERS | Raphael Collet, Odoo Translation Bot, Martin Trigaux, Fabien Pinckaers, Olivier Dony, Thibault Delavallée, Christophe Simonis, Xavier Morel, Goffin Simon, Nicolas Martinelli, Nicolas Lempereur, Jérome Maes, qdp-odoo, Aaron Bohy, GitHub, Lucas Perais (lpe), Olivier Colson, Yannick Tivisse, qsm-odoo, Martin Geubelle, Odoo Online, Laurent Smet, Adrien Dieudonne |
| WEBSITE | |
| WEBSITE | https://www.odoo.com/page/expenses |
| LAST TRACKING UPDATE | |
| LAST TRACKING UPDATE | 2026-07-05 23:53:43 |
| ODOO DEPENDENCIES | |
| ODOO DEPENDENCIES |
odoo/odoo: - hr_contract - hr - base_setup - base - web - bus - web_tour - resource - account - product - decimal_precision - analytic - web_planner - portal - http_routing |
| PYTHON DEPENDENCIES | |
| PYTHON DEPENDENCIES | Not have |
| SYSTEM DEPENDENCIES | |
| SYSTEM DEPENDENCIES | Not have |
| DESCRIPTION | |
| DESCRIPTION |
Manage expenses by Employees
============================
This application allows you to manage your employees' daily expenses. It gives you access to your employees’ fee notes and give you the right to complete and validate or refuse the notes. After validation it creates an invoice for the employee.
Employee can encode their own expenses and the validation flow puts it automatically in the accounting after validation by managers.
The whole flow is implemented as:
---------------------------------
* Draft expense
* Submitted by the employee to his manager
* Approved by his manager
* Validation by the accountant and accounting entries creation
This module also uses analytic accounting and is compatible with the invoice on timesheet module so that you are able to automatically re-invoice your customers' expenses if your work by project.
|
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
assets_backend_expense |
hr_expense assets | ir.ui.view | qweb | Inherits web.assets_backend |
hr_department_view_kanban |
hr.department.kanban.inherit | hr.department | data | Inherits hr.hr_department_view_kanban |
hr_expense_form_view |
hr.expense.form | hr.expense | form | New |
hr_expense_kanban_view |
hr.expense.kanban | hr.expense | kanban | New |
hr_expense_planner |
hr_expense_planner | ir.ui.view | qweb | New |
hr_expense_refuse_wizard_view_form |
hr.expense.refuse.wizard.form | hr.expense.refuse.wizard | form | New |
hr_expense_sheet_register_payment_view_form |
hr.expense.sheet.register.payment.wizard.form | hr.expense.sheet.register.payment.wizard | form | New |
hr_expense_template_refuse_reason |
hr_expense_template_refuse_reason | ir.ui.view | qweb | New |
hr_expense_view_graph |
hr.expense.graph | hr.expense | graph | New |
hr_expense_view_pivot |
hr.expense.pivot | hr.expense | pivot | New |
product_product_expense_form_view |
product.product.expense.form | product.product | form | New |
product_product_expense_tree_view |
product.product.expense.tree | product.product | tree | New |
report_expense |
report_expense | ir.ui.view | qweb | New |
report_expense_sheet |
report_expense_sheet | ir.ui.view | qweb | New |
res_config_settings_view_form |
res.config.settings.view.form.inherit.hr.expense | res.config.settings | xpath | Inherits base.res_config_settings_view_form |
view_expenses_tree |
hr.expense.tree | hr.expense | tree | New |
view_hr_expense_filter |
hr.expense.filter | hr.expense | search | New |
view_hr_expense_sheet_filter |
hr.expense.sheet.filter | hr.expense.sheet | search | New |
view_hr_expense_sheet_form |
hr.expense.sheet.form | hr.expense.sheet | form | New |
view_hr_expense_sheet_graph |
hr.expense.sheet.graph | hr.expense.sheet | graph | New |
view_hr_expense_sheet_kanban |
hr.expense.sheet.kanban | hr.expense.sheet | kanban | New |
view_hr_expense_sheet_pivot |
hr.expense.sheet.pivot | hr.expense.sheet | pivot | New |
view_hr_expense_sheet_tree |
hr.expense.sheet.tree | hr.expense.sheet | tree | New |
view_product_hr_expense_form |
product.template.expense.form | product.template | div | Inherits product.product_template_form_view |
expense_id
Many2one → hr.expense
copy=False
help='Expense where the move line come from'
string='Expense'
args: 'hr.expense'
reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False)
expense_sheets_to_approve_count
Integer
compute='_compute_expense_sheets_to_approve'
string='Expenses Reports to Approve'
No public methods.
account_id
Many2one → account.account
default=<expr>
help='An expense account is expected'
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
string='Account'
args: 'account.account'
analytic_account_id
Many2one → account.analytic.account
oldname='analytic_account'
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
string='Analytic Account'
args: 'account.analytic.account'
attachment_number
Integer
compute='_compute_attachment_number'
string='Number of Attachments'
company_id
Many2one → res.company
default=<expr>
readonly=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
readonly=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Currency'
args: 'res.currency'
date
Date
default=fields.Date.context_today
readonly=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Expense Date'
description
Text
employee_id
Many2one → hr.employee
default=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Employee'
args: 'hr.employee'
is_refused
Boolean
copy=False
readonly=True
string='Explicitely Refused by manager or acccountant'
name
Char
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Expense Description'
payment_mode
Selection
default='own_account'
states={'done': [('readonly', True)], 'post': [('readonly', True)], 'submitted': [('readonly', True)]}
string='Payment By'
args: [('own_account', 'Employee (to reimburse)'), ('company_account', 'Company')]
product_id
Many2one → product.product
domain=[('can_be_expensed', '=', True)]
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Product'
args: 'product.product'
product_uom_id
Many2one → product.uom
default=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Unit of Measure'
args: 'product.uom'
quantity
Float
default=1
digits=dp.get_precision('Product Unit of Measure')
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
reference
Char
string='Bill Reference'
sheet_id
Many2one → hr.expense.sheet
copy=False
readonly=True
string='Expense Report'
args: 'hr.expense.sheet'
state
Selection
compute='_compute_state'
copy=False
help='Status of the expense.'
index=True
readonly=True
store=True
string='Status'
args: [('draft', 'To Submit'), ('reported', 'Reported'), ('done', 'Posted'), ('refused', 'Refused')]
tax_ids
Many2many → account.tax
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Taxes'
args: 'account.tax', 'expense_tax', 'expense_id', 'tax_id'
total_amount
Float
compute='_compute_amount'
digits=dp.get_precision('Account')
store=True
string='Total'
unit_amount
Float
digits=dp.get_precision('Product Price')
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Unit Price'
untaxed_amount
Float
compute='_compute_amount'
digits=dp.get_precision('Account')
store=True
string='Subtotal'
action_get_attachment_view(self)
action_move_create(self)
get_empty_list_help(self, help_message)
message_new(self, msg_dict, custom_values=None)
refuse_expense(self, reason)
submit_expenses(self)
unlink(self)
view_sheet(self)
hr_expense_ids
Many2many → hr.expense
hr_expense_sheet_id
Many2one → hr.expense.sheet
reason
Char
required=True
string='Reason'
default_get(self, fields)
expense_refuse_reason(self)
account_move_id
Many2one → account.move
copy=False
ondelete='restrict'
string='Journal Entry'
args: 'account.move'
accounting_date
Date
string='Date'
address_id
Many2one → res.partner
string='Employee Home Address'
args: 'res.partner'
attachment_number
Integer
compute='_compute_attachment_number'
string='Number of Attachments'
bank_journal_id
Many2one → account.journal
default=<expr>
help='The payment method used when the expense is paid by the company.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Bank Journal'
args: 'account.journal'
company_id
Many2one → res.company
default=<expr>
readonly=True
states={'submit': [('readonly', False)]}
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
readonly=True
states={'submit': [('readonly', False)]}
string='Currency'
args: 'res.currency'
department_id
Many2one → hr.department
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
string='Department'
args: 'hr.department'
employee_id
Many2one → hr.employee
default=<expr>
readonly=True
required=True
states={'submit': [('readonly', False)]}
string='Employee'
args: 'hr.employee'
expense_line_ids
One2many → hr.expense
copy=False
states={'approve': [('readonly', True)], 'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Expense Lines'
args: 'hr.expense', 'sheet_id'
journal_id
Many2one → account.journal
default=<expr>
help='The journal used when the expense is done.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Expense Journal'
args: 'account.journal'
name
Char
required=True
string='Expense Report Summary'
payment_mode
Selection
default='own_account'
readonly=True
related='expense_line_ids.payment_mode'
string='Payment By'
args: [('own_account', 'Employee (to reimburse)'), ('company_account', 'Company')]
responsible_id
Many2one → res.users
copy=False
readonly=True
states={'submit': [('readonly', False)], 'submit': [('readonly', False)]}
args: 'res.users', 'Validation By'
state
Selection
copy=False
default='submit'
help='Expense Report State'
index=True
readonly=True
required=True
string='Status'
track_visibility='onchange'
args: [('submit', 'Submitted'), ('approve', 'Approved'), ('post', 'Posted'), ('done', 'Paid'), ('cancel', 'Refused')]
total_amount
Float
compute='_compute_amount'
digits=dp.get_precision('Account')
store=True
string='Total Amount'
action_get_attachment_view(self)
action_sheet_move_create(self)
approve_expense_sheets(self)
check_consistency(self)
create(self, vals)
paid_expense_sheets(self)
refuse_sheet(self, reason)
reset_expense_sheets(self)
set_to_paid(self)
unlink(self)
write(self, vals)
amount
Monetary
required=True
string='Payment Amount'
communication
Char
string='Memo'
company_id
Many2one → res.company
readonly=True
related='journal_id.company_id'
required=True
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
required=True
string='Currency'
args: 'res.currency'
hide_payment_method
Boolean
compute='_compute_hide_payment_method'
help="Technical field used to hide the payment method if the selected journal has only one available which is 'manual'"
journal_id
Many2one → account.journal
domain=[('type', 'in', ('bank', 'cash'))]
required=True
string='Payment Method'
args: 'account.journal'
partner_id
Many2one → res.partner
default=_default_partner_id
required=True
string='Partner'
args: 'res.partner'
payment_date
Date
default=fields.Date.context_today
required=True
string='Payment Date'
payment_method_id
Many2one → account.payment.method
required=True
string='Payment Type'
args: 'account.payment.method'
expense_post_payment(self)
can_be_expensed
Boolean
help='Specify whether the product can be selected in an HR expense.'
string='Can be Expensed'
create(self, vals)
expense_alias_prefix
Char
module_sale_management
Boolean
string='Customer Billing'
use_mailgateway
Boolean
string='Let your employees record expenses by email'
get_values(self)
set_values(self)
No new fields.
Public methods (0)No public methods.
| REPOSITORY | |
|---|---|
| REPOSITORY | odoo/odoo |
| GIT | |
| GIT | https://github.com/odoo/odoo.git |
| GIT FOLDER | |
| GIT FOLDER | https://github.com/odoo/odoo/tree/10.0/hr_expense |
| VERSION | |
| VERSION | 2.0 |
| CATEGORY | |
| CATEGORY | Human Resources |
| LICENSE | |
| LICENSE | LGPL-3 |
| APPLICATION | |
| APPLICATION | Yes |
| AUTO-INSTALLABLE | |
| AUTO-INSTALLABLE | No |
| AUTHORS | |
| AUTHORS | |
| MAINTAINERS | |
| MAINTAINERS | |
| COMMITTERS | |
| COMMITTERS | Odoo Translation Bot, Christophe Matthieu, Martin Trigaux, Fabien Pinckaers, Olivier Dony, Thibault Delavallée, Denis Ledoux, Christophe Simonis, Goffin Simon, Cedric Snauwaert, Nicolas Martinelli, Nicolas Lempereur, Julien Legros, Richard Mathot, qdp-odoo, Géry Debongnie, Aaron Bohy, Jeremy Kersten, Lucas Perais (lpe), Yannick Tivisse, qsm-odoo, Martin Geubelle, Pierre Masereel, Romain Derie, Denis Vermylen (dve), jem-odoo, kujiu |
| WEBSITE | |
| WEBSITE | https://www.odoo.com/page/expenses |
| LAST TRACKING UPDATE | |
| LAST TRACKING UPDATE | 2026-07-05 23:50:48 |
| ODOO DEPENDENCIES | |
| ODOO DEPENDENCIES |
odoo/odoo: - hr_contract - base_action_rule - base - resource - base_setup - web_kanban - web - bus - web_tour - hr - account_accountant - account - product - decimal_precision - report - analytic - web_planner |
| PYTHON DEPENDENCIES | |
| PYTHON DEPENDENCIES | Not have |
| SYSTEM DEPENDENCIES | |
| SYSTEM DEPENDENCIES | Not have |
| DESCRIPTION | |
| DESCRIPTION |
Manage expenses by Employees
============================
This application allows you to manage your employees' daily expenses. It gives you access to your employees’ fee notes and give you the right to complete and validate or refuse the notes. After validation it creates an invoice for the employee.
Employee can encode their own expenses and the validation flow puts it automatically in the accounting after validation by managers.
The whole flow is implemented as:
---------------------------------
* Draft expense
* Submitted by the employee to his manager
* Approved by his manager
* Validation by the accountant and accounting entries creation
This module also uses analytic accounting and is compatible with the invoice on timesheet module so that you are able to automatically re-invoice your customers' expenses if your work by project.
|
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
assets_backend_expense |
hr_expense assets | ir.ui.view | qweb | Inherits web.assets_backend |
hr_department_view_kanban |
hr.department.kanban.inherit | hr.department | data | Inherits hr.hr_department_view_kanban |
hr_expense_form_view |
hr.expense.form | hr.expense | form | New |
hr_expense_kanban_view |
hr.expense.kanban | hr.expense | kanban | New |
hr_expense_planner |
hr_expense_planner | ir.ui.view | qweb | New |
hr_expense_refuse_wizard_view_form |
hr.expense.refuse.wizard.form | hr.expense.refuse.wizard | form | New |
hr_expense_register_payment_view_form |
hr.expense.register.payment.wizard.form | hr.expense.register.payment.wizard | form | New |
hr_expense_view_graph |
hr.expense.graph | hr.expense | graph | New |
hr_expense_view_pivot |
hr.expense.pivot | hr.expense | pivot | New |
product_product_expense_form_view |
product.product.expense.form | product.product | form | New |
report_expense |
report_expense | ir.ui.view | qweb | New |
report_expense_sheet |
report_expense_sheet | ir.ui.view | qweb | New |
view_expenses_tree |
hr.expense.tree | hr.expense | tree | New |
view_hr_expense_configuration |
Configure Expense | hr.expense.config.settings | form | New |
view_hr_expense_filter |
hr.expense.filter | hr.expense | search | New |
view_hr_expense_sheet_filter |
hr.expense.sheet.filter | hr.expense.sheet | search | New |
view_hr_expense_sheet_form |
hr.expense.sheet.form | hr.expense.sheet | form | New |
view_hr_expense_sheet_graph |
hr.expense.sheet.graph | hr.expense.sheet | graph | New |
view_hr_expense_sheet_kanban |
hr.expense.sheet.kanban | hr.expense.sheet | kanban | New |
view_hr_expense_sheet_pivot |
hr.expense.sheet.pivot | hr.expense.sheet | pivot | New |
view_hr_expense_sheet_tree |
hr.expense.sheet.tree | hr.expense.sheet | tree | New |
view_product_hr_expense_form |
product.template.expense.form | product.template | div | Inherits product.product_template_form_view |
No new fields.
Public methods (1)reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False)
expense_sheets_to_approve_count
Integer
compute='_compute_expense_sheets_to_approve'
string='Expenses Reports to Approve'
No public methods.
account_id
Many2one → account.account
default=<expr>
help='An expense account is expected'
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
string='Account'
args: 'account.account'
analytic_account_id
Many2one → account.analytic.account
oldname='analytic_account'
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
string='Analytic Account'
args: 'account.analytic.account'
attachment_number
Integer
compute='_compute_attachment_number'
string='Number of Attachments'
company_id
Many2one → res.company
default=<expr>
readonly=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
readonly=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Currency'
args: 'res.currency'
date
Date
default=fields.Date.context_today
readonly=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Date'
description
Text
employee_id
Many2one → hr.employee
default=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Employee'
args: 'hr.employee'
name
Char
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Expense Description'
payment_mode
Selection
default='own_account'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Payment By'
args: [('own_account', 'Employee (to reimburse)'), ('company_account', 'Company')]
product_id
Many2one → product.product
domain=[('can_be_expensed', '=', True)]
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Product'
args: 'product.product'
product_uom_id
Many2one → product.uom
default=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Unit of Measure'
args: 'product.uom'
quantity
Float
default=1
digits=dp.get_precision('Product Unit of Measure')
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
reference
Char
string='Bill Reference'
sheet_id
Many2one → hr.expense.sheet
copy=False
readonly=True
string='Expense Report'
args: 'hr.expense.sheet'
state
Selection
compute='_compute_state'
copy=False
help='Status of the expense.'
index=True
readonly=True
store=True
string='Status'
args: [('draft', 'To Submit'), ('reported', 'Reported'), ('done', 'Posted'), ('refused', 'Refused')]
tax_ids
Many2many → account.tax
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Taxes'
args: 'account.tax', 'expense_tax', 'expense_id', 'tax_id'
total_amount
Float
compute='_compute_amount'
digits=dp.get_precision('Account')
store=True
string='Total'
unit_amount
Float
digits=dp.get_precision('Product Price')
readonly=True
required=True
states={'draft': [('readonly', False)], 'refused': [('readonly', False)]}
string='Unit Price'
untaxed_amount
Float
compute='_compute_amount'
digits=dp.get_precision('Account')
store=True
string='Subtotal'
action_get_attachment_view(self)
action_move_create(self)
get_empty_list_help(self, help_message)
message_new(self, msg_dict, custom_values=None)
submit_expenses(self)
unlink(self)
view_sheet(self)
alias_domain
Char
default=<expr>
args: 'Alias Domain'
alias_prefix
Char
get_default_alias_prefix(self, fields)
set_default_alias_prefix(self)
description
Char
required=True
string='Reason'
expense_refuse_reason(self)
amount
Monetary
required=True
string='Payment Amount'
communication
Char
string='Memo'
company_id
Many2one → res.company
readonly=True
related='journal_id.company_id'
required=True
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
required=True
string='Currency'
args: 'res.currency'
hide_payment_method
Boolean
compute='_compute_hide_payment_method'
help="Technical field used to hide the payment method if the selected journal has only one available which is 'manual'"
journal_id
Many2one → account.journal
domain=[('type', 'in', ('bank', 'cash'))]
required=True
string='Payment Method'
args: 'account.journal'
partner_id
Many2one → res.partner
default=_default_partner_id
required=True
string='Partner'
args: 'res.partner'
payment_date
Date
default=fields.Date.context_today
required=True
string='Payment Date'
payment_method_id
Many2one → account.payment.method
required=True
string='Payment Type'
args: 'account.payment.method'
expense_post_payment(self)
get_payment_vals(self)
account_move_id
Many2one → account.move
copy=False
string='Journal Entry'
args: 'account.move'
accounting_date
Date
string='Accounting Date'
address_id
Many2one → res.partner
string='Employee Home Address'
args: 'res.partner'
attachment_number
Integer
compute='_compute_attachment_number'
string='Number of Attachments'
bank_journal_id
Many2one → account.journal
default=<expr>
help='The payment method used when the expense is paid by the company.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Bank Journal'
args: 'account.journal'
company_id
Many2one → res.company
default=<expr>
readonly=True
states={'submit': [('readonly', False)]}
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
readonly=True
states={'submit': [('readonly', False)]}
string='Currency'
args: 'res.currency'
department_id
Many2one → hr.department
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
string='Department'
args: 'hr.department'
employee_id
Many2one → hr.employee
default=<expr>
readonly=True
required=True
states={'submit': [('readonly', False)]}
string='Employee'
args: 'hr.employee'
expense_line_ids
One2many → hr.expense
copy=False
states={'approve': [('readonly', True)], 'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Expense Lines'
args: 'hr.expense', 'sheet_id'
journal_id
Many2one → account.journal
default=<expr>
help='The journal used when the expense is done.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Expense Journal'
args: 'account.journal'
name
Char
required=True
string='Expense Report Summary'
payment_mode
Selection
default='own_account'
readonly=True
related='expense_line_ids.payment_mode'
string='Payment By'
args: [('own_account', 'Employee (to reimburse)'), ('company_account', 'Company')]
responsible_id
Many2one → res.users
copy=False
readonly=True
states={'submit': [('readonly', False)], 'submit': [('readonly', False)]}
args: 'res.users', 'Validation By'
state
Selection
copy=False
default='submit'
help='Expense Report State'
index=True
readonly=True
required=True
string='Status'
track_visibility='onchange'
args: [('submit', 'Submitted'), ('approve', 'Approved'), ('post', 'Posted'), ('done', 'Paid'), ('cancel', 'Refused')]
total_amount
Float
compute='_compute_amount'
digits=dp.get_precision('Account')
store=True
string='Total Amount'
action_get_attachment_view(self)
action_open_journal_entries(self)
action_sheet_move_create(self)
approve_expense_sheets(self)
check_consistency(self)
create(self, vals)
paid_expense_sheets(self)
refuse_expenses(self, reason)
reset_expense_sheets(self)
set_to_paid(self)
unlink(self)
write(self, vals)
can_be_expensed
Boolean
help='Specify whether the product can be selected in an HR expense.'
string='Can be Expensed'
create(self, vals)
No new fields.
Public methods (0)No public methods.
| REPOSITORY | |
|---|---|
| REPOSITORY | odoo/odoo |
| GIT | |
| GIT | https://github.com/odoo/odoo.git |
| GIT FOLDER | |
| GIT FOLDER | https://github.com/odoo/odoo/tree/9.0/hr_expense |
| VERSION | |
| VERSION | 2.0 |
| CATEGORY | |
| CATEGORY | Human Resources |
| LICENSE | |
| LICENSE | LGPL-3 |
| APPLICATION | |
| APPLICATION | Yes |
| AUTO-INSTALLABLE | |
| AUTO-INSTALLABLE | No |
| AUTHORS | |
| AUTHORS | |
| MAINTAINERS | |
| MAINTAINERS | |
| COMMITTERS | |
| COMMITTERS | Odoo Translation Bot, Martin Trigaux, Fabien Pinckaers, Antony Lesuisse, Olivier Dony, Thibault Delavallée, Josse Colpaert, Denis Ledoux, Fabien Meghazi, Christophe Simonis, Cedric Snauwaert, Nicolas Martinelli, Jérome Maes, Richard Mathot, qdp-odoo, Géry Debongnie, Damien Bouvy, Yannick Tivisse, Cédric Snauwaert, Martin Geubelle, Quentin De Paoli |
| WEBSITE | |
| WEBSITE | https://www.odoo.com/page/expenses |
| LAST TRACKING UPDATE | |
| LAST TRACKING UPDATE | 2026-07-05 23:48:30 |
| ODOO DEPENDENCIES | |
| ODOO DEPENDENCIES |
odoo/odoo: - hr_contract - base_action_rule - base - resource - base_setup - web_kanban - web - bus - hr - web_tip - account_accountant - account - product - decimal_precision - report - analytic - web_planner |
| PYTHON DEPENDENCIES | |
| PYTHON DEPENDENCIES | Not have |
| SYSTEM DEPENDENCIES | |
| SYSTEM DEPENDENCIES | Not have |
| DESCRIPTION | |
| DESCRIPTION |
Manage expenses by Employees
============================
This application allows you to manage your employees' daily expenses. It gives you access to your employees’ fee notes and give you the right to complete and validate or refuse the notes. After validation it creates an invoice for the employee.
Employee can encode their own expenses and the validation flow puts it automatically in the accounting after validation by managers.
The whole flow is implemented as:
---------------------------------
* Draft expense
* Submitted by the employee to his manager
* Approved by his manager
* Validation by the accountant and accounting entries creation
This module also uses analytic accounting and is compatible with the invoice on timesheet module so that you are able to automatically re-invoice your customers' expenses if your work by project.
|
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
hr_department_view_kanban |
hr.department.kanban.inherit | hr.department | data | Inherits hr.hr_department_view_kanban |
hr_expense_form_view |
hr.expense.form | hr.expense | form | New |
hr_expense_kanban_view |
hr.expense.kanban | hr.expense | kanban | New |
hr_expense_my_tree |
my.hr.expense.tree | hr.expense | tree | New |
hr_expense_refuse_wizard_view_form |
hr.expense.refuse.wizard.form | hr.expense.refuse.wizard | form | New |
hr_expense_view_graph |
hr.expense.graph | hr.expense | graph | New |
hr_expense_view_pivot |
hr.expense.pivot | hr.expense | pivot | New |
report_expense |
report_expense | ir.ui.view | qweb | New |
view_expenses_tree |
hr.expense.tree | hr.expense | tree | New |
view_hr_expense_filter |
hr.expense.filter | hr.expense | search | New |
view_product_hr_expense_form |
product.template.expense.form | product.template | div | Inherits product.product_template_form_view |
No new fields.
Public methods (1)reconcile(self, writeoff_acc_id=False, writeoff_journal_id=False)
expense_to_approve_count
Integer
compute='_compute_expense_to_approve'
string='Expenses to Approve'
No public methods.
account_move_id
Many2one → account.move
copy=False
string='Journal Entry'
track_visibility='onchange'
args: 'account.move'
analytic_account_id
Many2one → account.analytic.account
domain=[('account_type', '=', 'normal')]
oldname='analytic_account'
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
string='Analytic Account'
args: 'account.analytic.account'
attachment_number
Integer
compute='_compute_attachment_number'
string='Number of Attachments'
bank_journal_id
Many2one → account.journal
default=<expr>
help='The payment method used when the expense is paid by the company.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Bank Journal'
args: 'account.journal'
company_id
Many2one → res.company
default=<expr>
readonly=True
states={'draft': [('readonly', False)]}
string='Company'
args: 'res.company'
currency_id
Many2one → res.currency
default=<expr>
readonly=True
states={'draft': [('readonly', False)]}
string='Currency'
args: 'res.currency'
date
Date
default=fields.Date.context_today
readonly=True
states={'draft': [('readonly', False)]}
string='Date'
department_id
Many2one → hr.department
states={'post': [('readonly', True)], 'done': [('readonly', True)]}
string='Department'
args: 'hr.department'
description
Text
employee_id
Many2one → hr.employee
default=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)]}
string='Employee'
args: 'hr.employee'
journal_id
Many2one → account.journal
default=<expr>
help='The journal used when the expense is done.'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Expense Journal'
args: 'account.journal'
name
Char
readonly=True
required=True
states={'draft': [('readonly', False)]}
string='Expense Description'
payment_mode
Selection
default='own_account'
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Payment By'
args: [('own_account', 'Employee (to reimburse)'), ('company_account', 'Company')]
product_id
Many2one → product.product
domain=[('can_be_expensed', '=', True)]
readonly=True
required=True
states={'draft': [('readonly', False)]}
string='Product'
args: 'product.product'
product_uom_id
Many2one → product.uom
default=<expr>
readonly=True
required=True
states={'draft': [('readonly', False)]}
string='Unit of Measure'
args: 'product.uom'
quantity
Float
default=1
digits=dp.get_precision('Product Unit of Measure')
readonly=True
required=True
states={'draft': [('readonly', False)]}
state
Selection
copy=False
default='draft'
help="When the expense request is created the status is 'To Submit'.\n It is submitted by the employee and request is sent to manager, the status is 'Submitted'. \nIf the manager approve it, the status is 'Approved'.\n If the accountant genrate the accounting entries for the expense request, the status is 'Waiting Payment'."
index=True
readonly=True
required=True
string='Status'
track_visibility='onchange'
args: [('draft', 'To Submit'), ('submit', 'Submitted'), ('approve', 'Approved'), ('post', 'Waiting Payment'), ('done', 'Paid'), ('cancel', 'Refused')]
tax_ids
Many2many → account.tax
states={'done': [('readonly', True)], 'post': [('readonly', True)]}
string='Taxes'
args: 'account.tax', 'expense_tax', 'expense_id', 'tax_id'
total_amount
Float
compute='_compute_amount'
digits=dp.get_precision('Account')
store=True
string='Total'
unit_amount
Float
digits=dp.get_precision('Product Price')
readonly=True
required=True
states={'draft': [('readonly', False)]}
string='Unit Price'
untaxed_amount
Float
compute='_compute_amount'
digits=dp.get_precision('Account')
store=True
string='Subtotal'
action_get_attachment_view(self)
action_move_create(self)
approve_expenses(self)
create(self, vals)
paid_expenses(self)
refuse_expenses(self, reason)
reset_expenses(self)
submit_expenses(self)
unlink(self)
write(self, vals)
description
Char
required=True
string='Reason'
expense_refuse_reason(self)
can_be_expensed
Boolean
help='Specify whether the product can be selected in an HR expense.'
No public methods.
No new fields.
Public methods (0)No public methods.
| REPOSITORY | |
|---|---|
| REPOSITORY | odoo/odoo |
| GIT | |
| GIT | https://github.com/odoo/odoo.git |
| GIT FOLDER | |
| GIT FOLDER | https://github.com/odoo/odoo/tree/8.0/hr_expense |
| VERSION | |
| VERSION | 1.0 |
| CATEGORY | |
| CATEGORY | Human Resources |
| LICENSE | |
| LICENSE | LGPL-3 |
| APPLICATION | |
| APPLICATION | Yes |
| AUTO-INSTALLABLE | |
| AUTO-INSTALLABLE | No |
| AUTHORS | |
| AUTHORS | OpenERP SA |
| MAINTAINERS | |
| MAINTAINERS | OpenERP SA |
| COMMITTERS | |
| COMMITTERS | Raphael Collet, Stephane Wirtel, Odoo Translation Bot, Christophe Matthieu, Martin Trigaux, Fabien Pinckaers, Antony Lesuisse, Olivier Dony, Tejas Tank, Thibault Delavallée, Vo Minh Thu, Josse Colpaert, Launchpad Translations on behalf of openerp, Quentin (OpenERP), Denis Ledoux, Fabien Meghazi, ggh-openerp, Christophe Simonis, Xavier Morel, Goffin Simon, Simon Lejeune, Frédéric van der Essen, Vishmita, Richard Mathot, Géry Debongnie, Aaron Bohy, Vishmita Jadeja (openerp), Richard Mathot (OpenERP), ged-odoo, Gery Debongnie, Randhir Mayatra rma-openerp, Darshan Kalola (OpenERP), Darshan Kalola, Sunil Sharma (OpenERP), Rakesh Sindhav (OpenERP) |
| WEBSITE | |
| WEBSITE | https://www.odoo.com/page/expenses |
| LAST TRACKING UPDATE | |
| LAST TRACKING UPDATE | 2026-07-05 23:45:39 |
| ODOO DEPENDENCIES | |
| ODOO DEPENDENCIES |
odoo/odoo: - hr - base_setup - base - web_kanban - web - resource - board - account_accountant - account_voucher - account - product - decimal_precision - report - analytic - edi - email_template |
| PYTHON DEPENDENCIES | |
| PYTHON DEPENDENCIES | Not have |
| SYSTEM DEPENDENCIES | |
| SYSTEM DEPENDENCIES | Not have |
| DESCRIPTION | |
| DESCRIPTION |
Manage expenses by Employees
============================
This application allows you to manage your employees' daily expenses. It gives you access to your employees’ fee notes and give you the right to complete and validate or refuse the notes. After validation it creates an invoice for the employee.
Employee can encode their own expenses and the validation flow puts it automatically in the accounting after validation by managers.
The whole flow is implemented as:
---------------------------------
* Draft expense
* Confirmation of the sheet by the employee
* Validation by his manager
* Validation by the accountant and accounting entries creation
This module also uses analytic accounting and is compatible with the invoice on timesheet module so that you are able to automatically re-invoice your customers' expenses if your work by project.
|
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
product_expense_installer_tree_view |
product.product.tree | product.product | tree | New |
report_expense |
report_expense | ir.ui.view | qweb | New |
view_editable_expenses_tree |
hr.expense.expense.tree | hr.expense.expense | tree | New |
view_expenses_form |
hr.expense.form | hr.expense.expense | form | New |
view_expenses_line_tree |
hr.expense.line.tree | hr.expense.line | tree | New |
view_expenses_tree |
hr.expense.expense.tree | hr.expense.expense | tree | New |
view_hr_expense_filter |
hr.expense.expense.filter | hr.expense.expense | search | New |
view_hr_expense_report_graph |
hr.expense.report.graph | hr.expense.report | graph | New |
view_hr_expense_report_search |
hr.expense.report.search | hr.expense.report | search | New |
view_product_hr_expense_form |
product.template.expense.form | product.template | div | Inherits product.product_template_form_view |
No models found for this module.
| REPOSITORY | |
|---|---|
| REPOSITORY | odoo/odoo |
| GIT | |
| GIT | https://github.com/odoo/odoo.git |
| GIT FOLDER | |
| GIT FOLDER | https://github.com/odoo/odoo/tree/7.0/hr_expense |
| VERSION | |
| VERSION | 1.0 |
| CATEGORY | |
| CATEGORY | Human Resources |
| LICENSE | |
| LICENSE | LGPL-3 |
| APPLICATION | |
| APPLICATION | Yes |
| AUTO-INSTALLABLE | |
| AUTO-INSTALLABLE | No |
| AUTHORS | |
| AUTHORS | OpenERP SA |
| MAINTAINERS | |
| MAINTAINERS | OpenERP SA |
| COMMITTERS | |
| COMMITTERS | Raphael Collet, Jagdish Panchal (Open ERP), Twinkle Christian (OpenERP), Cecile Tonglet, Rucha (Open ERP), Atul Patel (OpenERP), Randhir Mayatra (OpenERP), Odoo Translation Bot, Turkesh Patel (Open ERP), Christophe Matthieu, Pinakin Nayi (OpenERP), Benoit Guillot, DBR (OpenERP), Harry (OpenERP), Antonin Bourguignon, pankita shah (Open ERP), Martin Trigaux, Ajay Chauhan (OpenERP), Foram Katharotiya (OpenERP), Bhumika (OpenERP), Purnendu Singh (OpenERP), Saurang Suthar (OpenERP), Fabien Pinckaers, Antony Lesuisse, Meera Trambadia (OpenERP), Saurang Suthar, Olivier Dony, Rajesh Prajapati (OpenERP), Divyesh Makwana (Open ERP), Thibault Delavallée, Vo Minh Thu, Hardik, Josse Colpaert, pso (OpenERP), Sbh (Openerp), Amit Patel (OpenERP), Launchpad Translations on behalf of openerp, vta vta@openerp.com, Quentin (OpenERP), Kuldeep Joshi (OpenERP), Nimesh (Open ERP), ara@tinyerp.com, Mayur Maheshwari (OpenERP), Jigar Amin - OpenERP, Sanjay Gohel (Open ERP), Alexis de Lattre, help, Fabien Meghazi, Jiten (OpenERP), Denis Ledoux dle@openerp.com, Khushboo Bhatt (Open ERP), dle@openerp.com, Bhumi Thakkar (Open ERP), Amit Patel, Frédéric van der Essen, Gilles Major, Vijaykumar Baladaniya, Yannick Vaucher, Ravish (Open ERP), Anand Patel (OpenERP), Amit Dodiya (OpenERP), Jean-Christophe VASSORT, Juan Rial |
| WEBSITE | |
| WEBSITE | http://www.openerp.com |
| LAST TRACKING UPDATE | |
| LAST TRACKING UPDATE | 2026-07-05 23:43:09 |
| ODOO DEPENDENCIES | |
| ODOO DEPENDENCIES |
odoo/odoo: - hr - base_setup - base - web_kanban - web - resource - process - board - account_voucher - account - product - decimal_precision - analytic - edi - email_template - account_accountant |
| PYTHON DEPENDENCIES | |
| PYTHON DEPENDENCIES | Not have |
| SYSTEM DEPENDENCIES | |
| SYSTEM DEPENDENCIES | Not have |
| DESCRIPTION | |
| DESCRIPTION |
Manage expenses by Employees
============================
This application allows you to manage your employees' daily expenses. It gives you access to your employees’ fee notes and give you the right to complete and validate or refuse the notes. After validation it creates an invoice for the employee.
Employee can encode their own expenses and the validation flow puts it automatically in the accounting after validation by managers.
The whole flow is implemented as:
---------------------------------
* Draft expense
* Confirmation of the sheet by the employee
* Validation by his manager
* Validation by the accountant and receipt creation
This module also uses analytic accounting and is compatible with the invoice on timesheet module so that you are able to automatically re-invoice your customers' expenses if your work by project.
|
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
board_hr_expense_form |
board.hr.expense.form | board.board | xpath | Inherits hr.board_hr_form |
product_expense_installer_tree_view |
product.product.tree | product.product | tree | New |
view_editable_expenses_tree |
hr.expense.expense.tree | hr.expense.expense | tree | New |
view_expenses_form |
hr.expense.form | hr.expense.expense | form | New |
view_expenses_line_tree |
hr.expense.line.tree | hr.expense.line | tree | New |
view_expenses_tree |
hr.expense.expense.tree | hr.expense.expense | tree | New |
view_hr_expense_filter |
hr.expense.expense.filter | hr.expense.expense | search | New |
view_hr_expense_report_graph |
hr.expense.report.graph | hr.expense.report | graph | New |
view_hr_expense_report_search |
hr.expense.report.search | hr.expense.report | search | New |
view_hr_expense_report_tree |
hr.expense.report.tree | hr.expense.report | tree | New |
view_product_hr_expense_form |
product.product.expense.form | product.product | div | Inherits product.product_normal_form_view |
No models found for this module.
| REPOSITORY | |
|---|---|
| REPOSITORY | odoo/odoo |
| GIT | |
| GIT | https://github.com/odoo/odoo.git |
| GIT FOLDER | |
| GIT FOLDER | https://github.com/odoo/odoo/tree/6.1/hr_expense |
| VERSION | |
| VERSION | 1.0 |
| CATEGORY | |
| CATEGORY | Human Resources |
| LICENSE | |
| LICENSE | LGPL-3 |
| APPLICATION | |
| APPLICATION | Yes |
| AUTO-INSTALLABLE | |
| AUTO-INSTALLABLE | No |
| AUTHORS | |
| AUTHORS | OpenERP SA |
| MAINTAINERS | |
| MAINTAINERS | OpenERP SA |
| COMMITTERS | |
| COMMITTERS | Raphael Collet, Jagdish Panchal (Open ERP), Rucha (Open ERP), Stephane Wirtel, Harry (OpenERP), Fabien Pinckaers, Antony Lesuisse, Meera Trambadia (OpenERP), Olivier Dony, Vo Minh Thu, Launchpad Translations on behalf of openerp, Quentin (OpenERP), Mayur Maheshwari (OpenERP), Jigar Amin - OpenERP, Alexis de Lattre, vro, olt@tinyerp.com, niv-openerp, Xavier ALT, Christophe Simonis, Ujjvala Collins (OpenERP), Xavier Morel, Minh Tran, Jean-Christophe VASSORT, Vaibhav (OpenERP), Carlos Vásquez, ksa (Open ERP), Els Van Vossel, Israel Fermin Montilla, Rifakat Haradwala (Open ERP), Hardik Ansodariy (OpenERP), ssu, rch-openerp, Bharat (OpenERP), ARA (OpenERP), aag (OpenERP), mtr, Mustufa Rangwala, skh, Atik Agewan (OpenERP) |
| WEBSITE | |
| WEBSITE | http://www.openerp.com |
| LAST TRACKING UPDATE | |
| LAST TRACKING UPDATE | 2026-07-05 23:41:35 |
| ODOO DEPENDENCIES | |
| ODOO DEPENDENCIES |
odoo/odoo: - hr - base_setup - base - base_tools - resource - process - board - account - product - decimal_precision - analytic - edi - email_template |
| PYTHON DEPENDENCIES | |
| PYTHON DEPENDENCIES | Not have |
| SYSTEM DEPENDENCIES | |
| SYSTEM DEPENDENCIES | Not have |
| DESCRIPTION | |
| DESCRIPTION |
This module aims to manage employee's expenses.
===============================================
The whole workflow is implemented:
* Draft expense
* Confirmation of the sheet by the employee
* Validation by his manager
* Validation by the accountant and invoice creation
* Payment of the invoice to the employee
This module also uses the analytic accounting and is compatible with
the invoice on timesheet module so that you will be able to automatically
re-invoice your customer's expenses if your work by project.
|
| XML ID | Name | Model | Type | Status |
|---|---|---|---|---|
board_hr_expense_form |
board.hr.expense.form | board.board | xpath | Inherits hr.board_hr_form |
board_hr_manager_expense_form |
board.hr.manager.expense.form | board.board | xpath | Inherits hr.board_hr_manager_form |
product_expense_installer_tree_view |
product.product.tree | product.product | tree | New |
view_editable_expenses_tree |
hr.expense.expense.tree | hr.expense.expense | tree | New |
view_expenses_form |
hr.expense.form | hr.expense.expense | form | New |
view_expenses_line_tree |
hr.expense.line.tree | hr.expense.line | tree | New |
view_expenses_tree |
hr.expense.expense.tree | hr.expense.expense | tree | New |
view_hr_expense_filter |
hr.expense.expense.filter | hr.expense.expense | search | New |
view_hr_expense_report_graph |
hr.expense.report.graph | hr.expense.report | graph | New |
view_hr_expense_report_search |
hr.expense.report.search | hr.expense.report | search | New |
view_hr_expense_report_tree |
hr.expense.report.tree | hr.expense.report | tree | New |
view_product_hr_expense_form |
product.product.expense.form | product.product | field | Inherits product.product_normal_form_view |
No models found for this module.