19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:31:00 +01:00
parent a1137a1456
commit e1d89e11e3
2789 changed files with 1093187 additions and 605897 deletions

View file

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="account_journal_dashboard_kanban_view_inherit_hr_expense" model="ir.ui.view">
<field name="name">account.journal.dashboard.kanban</field>
<field name="model">account.journal</field>
<field name="inherit_id" ref="account.account_journal_dashboard_kanban_view" />
<field name="arch" type="xml">
<xpath expr="//t[@id='account.JournalBodySalePurchase']//div[hasclass('o_kanban_primary_right')]" position="inside">
<div class="row" t-if="dashboard.number_expenses_to_pay">
<div class="col overflow-hidden text-start">
<a type="object" t-if="journal_type == 'purchase'" name="open_expenses_action">
<t t-esc="dashboard.number_expenses_to_pay"/> Expenses to Process
</a>
</div>
<div class="col-auto text-end">
<span t-if="journal_type == 'purchase'"><t t-esc="dashboard.sum_expenses_to_pay"/></span>
</div>
</div>
</xpath>
</field>
</record>
</odoo>

View file

@ -7,15 +7,53 @@
<field name="inherit_id" ref="account.view_move_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<field name="expense_sheet_id" invisible="1"/>
<button name="action_open_expense_report"
<button name="action_open_expense"
class="oe_stat_button"
string="Expense Report"
icon="fa-file-text-o"
type="object"
attrs="{'invisible': [('expense_sheet_id', '=', [])]}"/>
invisible="nb_expenses == 0">
<field string="Expenses" name="nb_expenses" widget="statinfo"/>
</button>
</xpath>
</field>
</record>
<record id="view_move_list_expense" model="ir.ui.view">
<field name="name">account.move.hr.expense.list</field>
<field name="model">account.move</field>
<field name="arch" type="xml">
<list js_class="account_tree" decoration-info="state == 'draft'" decoration-muted="state == 'cancel'" sample="1">
<field name="made_sequence_gap" column_invisible="True"/>
<field name="name" decoration-bf="1" decoration-danger="made_sequence_gap and state == 'posted'" widget="char_with_placeholder_field" placeholder="/"/>
<field name="partner_id" string="Partner" optional="show" readonly="True"/>
<field name="date" optional="hide" string="Accounting Date" readonly="state in ['cancel', 'posted']"/>
<field name="invoice_date_due" widget="remaining_days" optional="show" invisible="payment_state in ('paid', 'in_payment', 'reversed') or state == 'cancel'"/>
<field name="invoice_origin" optional="hide" string="Source Document"/>
<field name="payment_reference" optional="hide"/>
<field name="ref" optional="hide"/>
<field name="activity_ids" widget="list_activity" optional="hide"/>
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}" optional="hide"/>
<field name="company_id" groups="!base.group_multi_company" column_invisible="True"/>
<field name="amount_untaxed_in_currency_signed" string="Tax Excluded" sum="Total" optional="show"/>
<field name="amount_tax_signed" string="Tax" sum="Total" optional="hide"/>
<field name="amount_total_in_currency_signed" string="Total" sum="Total" optional="show" decoration-bf="1" decoration-warning="abnormal_amount_warning" groups="base.group_multi_currency"/>
<field name="amount_total_in_currency_signed" string="Total" sum="Total" optional="hide" decoration-bf="1" decoration-warning="abnormal_amount_warning" groups="!base.group_multi_currency"/>
<field name="amount_residual_signed" string="Amount Due" sum="Amount Due" optional="hide"/>
<field name="currency_id" optional="hide" readonly="state in ['cancel', 'posted']"/>
<field name="company_currency_id" column_invisible="True"/>
<field name="checked" optional="hide" widget="boolean_toggle"/>
<field name="status_in_payment"
string="Status"
widget="badge"
decoration-info="status_in_payment == 'draft'"
decoration-danger="status_in_payment == 'cancel'"
decoration-muted="status_in_payment in ('posted', 'sent', 'partial')"
decoration-success="status_in_payment in ('in_payment', 'paid', 'reversed')"
invisible="payment_state == 'invoicing_legacy' or move_type == 'entry'"
optional="show"
/>
</list>
</field>
</record>
</data>
</odoo>

View file

@ -7,13 +7,16 @@
<field name="inherit_id" ref="account.view_account_payment_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<field name="expense_sheet_id" invisible="1"/>
<button name="action_open_expense_report"
<field name="expense_ids" invisible="1"/>
<button name="action_open_expense"
class="oe_stat_button"
string="Expense Report"
icon="fa-file-text-o"
type="object"
attrs="{'invisible': [('expense_sheet_id', '=', [])]}"/>
invisible="not expense_ids">
<div class="o_stat_info">
<span class="o_stat_text">Expense</span>
</div>
</button>
</xpath>
</field>
</record>

View file

@ -6,24 +6,21 @@
<field name="inherit_id" ref="hr.hr_department_view_kanban"/>
<field name="arch" type="xml">
<data>
<xpath expr="//templates" position="before">
<field name="expense_sheets_to_approve_count" groups="hr_expense.group_hr_expense_team_approver"/>
</xpath>
<xpath expr="//div[hasclass('o_kanban_primary_right')]" position="inside">
<div t-if="record.expense_sheets_to_approve_count.raw_value > 0" class="row ml16" groups="hr_expense.group_hr_expense_team_approver">
<div class="col">
<a name="%(action_hr_expense_sheet_department_to_approve)d" type="action">
<t t-esc="record.expense_sheets_to_approve_count.raw_value"/> Expense Reports
</a>
</div>
<xpath expr="//div[@name='kanban_primary_right']" position="inside">
<div t-if="record.expenses_to_approve_count.raw_value > 0" class="row ml32 g-0" groups="hr_expense.group_hr_expense_team_approver">
<a name="%(action_hr_expense_department_to_approve)d" class="col" type="action">
<field name="expenses_to_approve_count" groups="hr_expense.group_hr_expense_team_approver"/> Expenses
</a>
</div>
</xpath>
<xpath expr="//div[hasclass('o_kanban_manage_reports')]" position="inside">
<a role="menuitem" class="dropdown-item" name="%(action_hr_expense_sheet_department_filtered)d" type="action" groups="hr_expense.group_hr_expense_team_approver">
Expenses
</a>
<div role="menuitem">
<a class="dropdown-item" name="%(action_hr_expense_department_filtered)d"
type="action" groups="hr_expense.group_hr_expense_team_approver">
Expenses
</a>
</div>
</xpath>
</data>
</field>

View file

@ -0,0 +1,36 @@
<odoo>
<data>
<record id="hr_employee_view_form_inherit_expense" model="ir.ui.view">
<field name="name">hr.employee.view.form.expense</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='managers']" position="inside">
<field name="expense_manager_id" placeholder="Auto-validation" string="Expense" context="{'default_company_id': company_id}" widget="many2one_avatar_user"/>
</xpath>
</field>
</record>
<record id="view_employee_tree_inherit_expense" model="ir.ui.view">
<field name="name">hr.employee.list.expense</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='work_location_id']" position="after">
<field name="expense_manager_id" placeholder="Auto-validation" optional="hide" widget="many2one_avatar_user"/>
</xpath>
</field>
</record>
<record id="hr_employee_search_view" model="ir.ui.view">
<field name="name">hr.employee.search.view</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_filter"/>
<field name="arch" type="xml">
<xpath expr="//separator[@name='managers_groupby_separator']" position="before">
<filter name="group_expense_manager" string="Expense Approver" context="{'group_by': 'expense_manager_id'}"/>
</xpath>
</field>
</record>
</data>
</odoo>

View file

@ -4,9 +4,9 @@
<record id="mail_activity_type_action_config_hr_expense" model="ir.actions.act_window">
<field name="name">Activity Types</field>
<field name="res_model">mail.activity.type</field>
<field name="view_mode">tree,form</field>
<field name="domain">['|', ('res_model', '=', False), ('res_model', '=', 'hr.expense.sheet')]</field>
<field name="context">{'default_res_model': 'hr.expense.sheet'}</field>
<field name="view_mode">list,kanban,form</field>
<field name="domain">['|', ('res_model', '=', False), ('res_model', '=', 'hr.expense')]</field>
<field name="context">{'default_res_model': 'hr.expense'}</field>
</record>
<menuitem id="hr_expense_menu_config_activity_type"
action="mail_activity_type_action_config_hr_expense"

View file

@ -0,0 +1,167 @@
<odoo>
<data>
<record id="view_product_hr_expense_form" model="ir.ui.view">
<field name="name">product.template.expense.form</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<span name="sale_option" position="after">
<span class="d-inline-flex">
<field name="can_be_expensed"/>
<label for="can_be_expensed"/>
</span>
</span>
</field>
</record>
<record id="product_template_search_view_inherit_hr_expense" model="ir.ui.view">
<field name="name">product.template.search.view.inherit.hr_expense</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_search_view"/>
<field name="arch" type="xml">
<filter name="filter_to_sell" position="after">
<filter string="Expenses" name="filter_to_expense" domain="[('can_be_expensed', '=', True)]"/>
</filter>
</field>
</record>
<record id="product_product_expense_form_view" model="ir.ui.view">
<field name="name">product.product.expense.form</field>
<field name="model">product.product</field>
<field name="arch" type="xml">
<form string="Expense Categories">
<div class="alert alert-warning" role="alert" invisible="not standard_price_update_warning">
<field name="standard_price_update_warning"/>
</div>
<sheet>
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
<field name='product_variant_count' invisible='1'/>
<field name="id" invisible="1"/>
<field name="image_1920" widget="image" class="oe_avatar" options="{'image_preview': 'image_128'}"/>
<field name="type" invisible="1"/>
<div class="oe_title px-3">
<label for="name" string="Product Name"/>
<h1><field name="name" placeholder="e.g. Lunch"/></h1>
</div>
<group name="product_details">
<group>
<field name="active" invisible="1"/>
<field name="type" invisible="1"/>
<field name="currency_id" invisible="1"/>
<label for="standard_price"/>
<div class="o_row" groups="base.group_user">
<field name="standard_price"
class="w-50"
widget="monetary"
options="{'currency_field': 'currency_id'}"
help="When the cost of an expense product is different than 0, then the user
using this product won't be able to change the amount of the expense,
only the quantity. Use a cost different than 0 for expense categories funded by
the company at fixed cost like allowances for mileage, per diem, accommodation
or meal."/>
<span class="d-flex w-50 ms-2" invisible="standard_price == 0">
per <field name="uom_id" widget="many2one_uom" class="ms-2" options="{'no_create': True}"/>
</span>
</div>
<field name="default_code" string="Reference" class="w-50"/>
<field name="categ_id" string="Category" class="w-50"/>
<field name="company_id" class="w-50" groups="base.group_multi_company"/>
<field name="description" string="Guideline" placeholder="e.g. Restaurants: only week days, for lunch"/>
</group>
<group>
<field name="property_account_expense_id" class="w-50" groups="account.group_account_readonly"/>
<field name="supplier_taxes_id" class="w-50" widget="many2many_tax_tags"
context="{'default_type_tax_use':'purchase', 'default_price_include': 1}"
options="{'no_quick_create': True}" placeholder="no taxes"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="product_product_expense_kanban_view" model="ir.ui.view">
<field name="name">product.product.kanban.expense</field>
<field name="inherit_id" ref="product.product_kanban_view"/>
<field name="mode">primary</field>
<field name="model">product.product</field>
<field name="arch" type="xml">
<xpath expr="//main" position="inside">
<span>
Cost: <field name="standard_price"/>
</span>
</xpath>
</field>
</record>
<record id="product_product_expense_tree_view" model="ir.ui.view">
<field name="name">product.product.expense.list</field>
<field name="model">product.product</field>
<field eval="50" name="priority"/>
<field name="arch" type="xml">
<list string="Product Variants" default_order="is_favorite desc, default_code, name, id">
<field name="default_code"/>
<field name="name"/>
<field name="product_template_attribute_value_ids" widget="many2many_tags"
groups="product.group_product_variant"/>
<field name="standard_price"/>
<field name="uom_id" widget="many2one_uom" options="{'no_open': True, 'no_create': True}" groups="uom.group_uom"/>
<field name="barcode"/>
</list>
</field>
</record>
<record model="ir.ui.view" id="product_product_expense_categories_tree_view">
<field name="name">product.product.expense.categories.list.view</field>
<field name="model">product.product</field>
<field name="arch" type="xml">
<list class="o_expense_categories" default_order="is_favorite desc, default_code, name, id">
<field name="name" readonly="1"/>
<field name="currency_id" column_invisible="True"/>
<field name="default_code" optional="show" string="Reference" readonly="1"/>
<field name="description" string="Note" optional="show" readonly="1"/>
<field name="lst_price" widget='monetary' options="{'currency_field': 'currency_id', 'field_digits': True}" optional="show" string="Sales Price"/>
<field name="standard_price" widget='monetary' options="{'currency_field': 'currency_id', 'field_digits': True}" optional="show"/>
<field name="supplier_taxes_id" widget="many2many_tax_tags" optional="show"/>
</list>
</field>
</record>
<record id="hr_expense_product" model="ir.actions.act_window">
<field name="name">Expense Categories</field>
<field name="res_model">product.product</field>
<field name="view_mode">list,kanban,form</field>
<field name="search_view_id" ref="product.product_search_form_view"/>
<field name="context">{"default_can_be_expensed": 1, 'default_type': 'service'}</field>
<field name="domain">[('can_be_expensed', '=', True)]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No expense categories found. Let's create one!
</p><p>
Expense categories can be reinvoiced to your customers.
</p>
</field>
</record>
<record id="hr_expense_product_tree" model="ir.actions.act_window.view">
<field name="sequence" eval="1"/>
<field name="view_mode">list</field>
<field name="view_id" ref="product_product_expense_categories_tree_view"/>
<field name="act_window_id" ref="hr_expense_product"/>
</record>
<record id="hr_expense_product_kanban" model="ir.actions.act_window.view">
<field name="sequence" eval="2"/>
<field name="view_mode">kanban</field>
<field name="view_id" ref="product_product_expense_kanban_view"/>
<field name="act_window_id" ref="hr_expense_product"/>
</record>
<record id="hr_expense_product_form" model="ir.actions.act_window.view">
<field name="sequence" eval="3"/>
<field name="view_mode">form</field>
<field name="view_id" ref="product_product_expense_form_view"/>
<field name="act_window_id" ref="hr_expense_product"/>
</record>
</data>
</odoo>

View file

@ -7,100 +7,66 @@
<field name="priority" eval="85"/>
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[hasclass('settings')]" position="inside">
<div class="app_settings_block" data-string="Expenses" string="Expenses" data-key="hr_expense" groups="hr_expense.group_hr_expense_manager">
<h2>Expenses</h2>
<div class="row mt16 o_settings_container" name="expenses_setting_container">
<div class="col-xs-12 col-md-6 o_setting_box"
id="create_expense_setting"
title="Send an email to this email alias with the receipt in attachment to create an expense in one click. If the first word of the mail subject contains the category's internal reference or the category name, the corresponding category will automatically be set. Type the expense amount in the mail subject to set it on the expense too.">
<div class="o_setting_left_pane">
<field name="use_mailgateway"/>
</div>
<div class="o_setting_right_pane">
<label string="Incoming Emails" for="use_mailgateway"/>
<div class="text-muted">
Create expenses from incoming emails
</div>
<div class="content-group" attrs="{'invisible': ['|', ('use_mailgateway', '=', False), ('alias_domain', 'in', ['localhost', '', False])]}">
<div class="mt16" dir="ltr">
<label for="expense_alias_prefix" string="Alias" class="o_light_label"/>
<field name="expense_alias_prefix" class="oe_inline ps-2"/>
<span>@</span>
<field name="alias_domain" class="oe_inline" readonly="1" force_save="1"/>
</div>
</div>
<div class="content-group" attrs="{'invisible': ['|', ('use_mailgateway', '=', False), ('alias_domain', 'not in', ['localhost', '', False])]}">
<div class="mt16">
<button type="action" name="base_setup.action_general_configuration" icon="fa-arrow-right" string="Setup your domain alias" class="btn-link"/>
</div>
<xpath expr="//form" position="inside">
<app data-string="Expenses" string="Expenses" name="hr_expense" groups="hr_expense.group_hr_expense_manager">
<block title="Expenses" name="expenses_setting_container">
<setting id="create_expense_setting" string="Incoming Emails"
help="Create expenses from incoming emails"
title="Send an email to this email alias with the receipt in attachment to create an expense in one click. If the first word of the mail subject contains the category's internal reference or the category name, the corresponding category will automatically be set. Type the expense amount in the mail subject to set it on the expense too.">
<field name="hr_expense_use_mailgateway"/>
<div class="content-group" invisible="not hr_expense_use_mailgateway or not alias_domain_id">
<div class="mt16 d-flex align-items-start" dir="ltr">
<label for="hr_expense_alias_prefix" string="Alias" class="o_light_label"/>
<field name="hr_expense_alias_prefix" placeholder="alias" class="oe_inline ps-2"/>
<span>@</span>
<field name="hr_expense_alias_domain_id" class="oe_inline" placeholder="e.g. mycompany.com"
options="{'no_create': True, 'no_open': True}"/>
</div>
</div>
</div>
<div class="col-lg-6 col-12 o_setting_box" id="hr_payroll_accountant">
<div class="o_setting_left_pane">
<field name="module_hr_payroll_expense" widget="upgrade_boolean"/>
</div>
<div class="o_setting_right_pane">
<label for="module_hr_payroll_expense" string="Reimburse in Payslip"/>
<div class="text-muted">
Reimburse expenses in payslips
<div class="content-group" invisible="not hr_expense_use_mailgateway or alias_domain_id">
<div class="mt16">
<a href="/odoo/settings#email-alias-setting" class="btn-link" role="button"><i class="oi oi-arrow-right"></i> Setup your alias domain</a>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" title="use OCR to fill data from a picture of the bill">
<div class="o_setting_left_pane">
<field name="module_hr_expense_extract" widget="upgrade_boolean"/>
</div>
<div class="o_setting_right_pane" id="expense_extract_settings">
<label string="Expense Digitalization (OCR)" for="module_hr_expense_extract"/>
<span class="fa fa-lg fa-building-o" title="Values set here are company-specific."/>
<div class="text-muted">
Digitalize your receipts with OCR and Artificial Intelligence
</div>
</div>
</div>
</div>
<h2>Default Journals</h2>
<div class="row mt16 o_settings_container">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane"></div>
<div class="o_setting_right_pane">
<span class="o_form_label">Employee Expense Journal</span>
<span class="fa fa-lg fa-building-o" title="Values set here are company-specific." role="img" aria-label="Values set here are company-specific." groups="base.group_multi_company"/>
<div class="text-muted">
Default accounting journal for expenses paid by employees.
</div>
<div class="row mt8">
<field name="expense_journal_id"/>
</setting>
<setting string="Reimburse in Payslip" help="Refund employees via their payslips." id="hr_payroll_accountant">
<field name="module_hr_payroll_expense" widget="upgrade_boolean"/>
</setting>
<setting id="expense_extract_settings" string="Expense Digitalization (OCR)" company_dependent="1"
help="Digitalize your receipts with OCR and Artificial Intelligence"
title="use OCR to fill data from a picture of the bill">
<field name="module_hr_expense_extract" widget="upgrade_boolean"/>
</setting>
<setting string="Expense Card" company_dependent="True" id="expense_cards_stripe_issuing"
help="Create prepaid virtual and physical cards for both one-time and recurring expenses, integrated into Odoo through Stripe Issuing.">
<field name="module_hr_expense_stripe" widget="upgrade_boolean"/>
<div class="content-group" invisible="not module_hr_expense_stripe" name="expenses_stripe_issuing_container">
<div class="text-warning mt16 mb4">
Save this page and come back here to set up the feature.
</div>
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane"></div>
<div class="o_setting_right_pane">
<span class="o_form_label">Company Expense Journal</span>
<span class="fa fa-lg fa-building-o" title="Values set here are company-specific." role="img" aria-label="Values set here are company-specific." groups="base.group_multi_company"/>
<div class="text-muted">
Default accounting journal for expenses paid by the company.
</div>
<div class="row mt8">
<field name="company_expense_journal_id"/>
</div>
</div>
</div>
</div>
</div>
</setting>
</block>
<block title="Accounting">
<setting company_dependent="1" help="Default accounting journal for expenses paid by employees."
string="Employee Expense Journal">
<field name="expense_journal_id"/>
</setting>
<setting company_dependent="1" string="Payment methods"
help="Payment method allowed for expenses paid by company.">
<field name="company_expense_allowed_payment_method_line_ids" widget="many2many_tags"
placeholder="All payment methods allowed" options="{'no_create': True}"/>
</setting>
</block>
</app>
</xpath>
</field>
</record>
<record id="action_hr_expense_configuration" model="ir.actions.act_window">
<field name="name">Settings</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.config.settings</field>
<field name="view_mode">form</field>
<field name="target">inline</field>
<field name="context">{'module' : 'hr_expense', 'bin_size': False}</field>
</record>