diff options
| -rw-r--r-- | addons/hr_expense/__manifest__.py | 1 | ||||
| -rw-r--r-- | addons/hr_expense/static/src/js/expense_qr_code_action.js | 24 | ||||
| -rw-r--r-- | addons/hr_expense/static/src/js/expense_views.js | 41 | ||||
| -rw-r--r-- | addons/hr_expense/static/src/xml/expense_qr_modal_template.xml | 11 | ||||
| -rw-r--r-- | addons/hr_expense/views/assets.xml | 1 | ||||
| -rw-r--r-- | addons/hr_expense/views/hr_expense_views.xml | 97 | ||||
| -rw-r--r-- | addons/hr_expense/views/res_config_settings_views.xml | 2 | ||||
| -rw-r--r-- | addons/sale_expense/models/hr_expense.py | 2 | ||||
| -rw-r--r-- | addons/sale_expense/models/sale_order.py | 2 | ||||
| -rw-r--r-- | addons/sale_expense/views/hr_expense_views.xml | 4 |
10 files changed, 152 insertions, 33 deletions
diff --git a/addons/hr_expense/__manifest__.py b/addons/hr_expense/__manifest__.py index a0b22f423d0..e6690449c94 100644 --- a/addons/hr_expense/__manifest__.py +++ b/addons/hr_expense/__manifest__.py @@ -48,6 +48,7 @@ This module also uses analytic accounting and is compatible with the invoice on 'qweb': [ "static/src/xml/documents_upload_views.xml", "static/src/xml/expense_dashboard.xml", + "static/src/xml/expense_qr_modal_template.xml", ], 'installable': True, 'application': True, diff --git a/addons/hr_expense/static/src/js/expense_qr_code_action.js b/addons/hr_expense/static/src/js/expense_qr_code_action.js new file mode 100644 index 00000000000..19226545ede --- /dev/null +++ b/addons/hr_expense/static/src/js/expense_qr_code_action.js @@ -0,0 +1,24 @@ +odoo.define('hr_expense.qr_code_action', function (require) { +"use strict"; + +const AbstractAction = require('web.AbstractAction'); +const core = require('web.core'); +const config = require('web.config'); + +const QRModalAction = AbstractAction.extend({ + template: 'hr_expense_qr_code', + xmlDependencies: ['/hr_expense/static/src/xml/expense_qr_modal_template.xml'], + + init: function(parent, action){ + this._super.apply(this, arguments); + const qr_vals = [ + '002', // Version + '1', // Character Set + action.params.url, // Download app url + ]; + this.url = _.str.sprintf("/report/barcode/?type=QR&value=%s&width=256&height=256&humanreadable=1", encodeURI(qr_vals.join('\n'))); + }, +}); + +core.action_registry.add('expense_qr_code_modal', QRModalAction); +}); diff --git a/addons/hr_expense/static/src/js/expense_views.js b/addons/hr_expense/static/src/js/expense_views.js index 5f2b7ad488d..adc9bfdd301 100644 --- a/addons/hr_expense/static/src/js/expense_views.js +++ b/addons/hr_expense/static/src/js/expense_views.js @@ -3,13 +3,16 @@ odoo.define('hr_expense.expenses.tree', function (require) { var DocumentUploadMixin = require('hr_expense.documents.upload.mixin'); var KanbanController = require('web.KanbanController'); var KanbanView = require('web.KanbanView'); + var PivotView = require('web.PivotView'); var ListController = require('web.ListController'); var ListView = require('web.ListView'); var viewRegistry = require('web.view_registry'); var core = require('web.core'); var ListRenderer = require('web.ListRenderer'); var KanbanRenderer = require('web.KanbanRenderer'); + var PivotRenderer = require('web.PivotRenderer'); var session = require('web.session'); + const config = require('web.config'); var QWeb = core.qweb; @@ -21,6 +24,38 @@ odoo.define('hr_expense.expenses.tree', function (require) { }), }); + const ExpenseQRCodeMixin = { + async _renderView() { + const self = this; + await this._super(...arguments); + const google_url = "https://play.google.com/store/apps/details?id=com.odoo.mobile"; + const apple_url = "https://apps.apple.com/be/app/odoo/id1272543640"; + const action_desktop = { + name: 'Download our App', + type: 'ir.actions.client', + tag: 'expense_qr_code_modal', + params: {'url': "https://apps.apple.com/be/app/odoo/id1272543640"}, + target: 'new', + }; + this.$el.find('img.o_expense_apple_store').on('click', function(event) { + event.preventDefault(); + if (!config.device.isMobile) { + self.do_action(_.extend(action_desktop, {params: {'url': apple_url}})); + } else { + self.do_action({type: 'ir.actions.act_url', url: apple_url}); + } + }); + this.$el.find('img.o_expense_google_store').on('click', function(event) { + event.preventDefault(); + if (!config.device.isMobile) { + self.do_action(_.extend(action_desktop, {params: {'url': google_url}})); + } else { + self.do_action({type: 'ir.actions.act_url', url: google_url}); + } + }); + }, + }; + const ExpenseDashboardMixin = { _render: async function () { var self = this; @@ -50,9 +85,9 @@ odoo.define('hr_expense.expenses.tree', function (require) { } return value; } - } + }; - var ExpenseListRenderer = ListRenderer.extend(ExpenseDashboardMixin); + var ExpenseListRenderer = ListRenderer.extend(ExpenseDashboardMixin, ExpenseQRCodeMixin); var ExpensesListViewDashboardUpload = ListView.extend({ config: _.extend({}, ListView.prototype.config, { @@ -75,7 +110,7 @@ odoo.define('hr_expense.expenses.tree', function (require) { }), }); - var ExpenseKanbanRenderer = KanbanRenderer.extend(ExpenseDashboardMixin); + var ExpenseKanbanRenderer = KanbanRenderer.extend(ExpenseDashboardMixin, ExpenseQRCodeMixin); var ExpensesKanbanView = KanbanView.extend({ config: _.extend({}, KanbanView.prototype.config, { diff --git a/addons/hr_expense/static/src/xml/expense_qr_modal_template.xml b/addons/hr_expense/static/src/xml/expense_qr_modal_template.xml new file mode 100644 index 00000000000..00a59f418fe --- /dev/null +++ b/addons/hr_expense/static/src/xml/expense_qr_modal_template.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates id="template" xml:space="preserve"> +<t t-name="hr_expense_qr_code"> + <div style="text-align:center;" class="o_expense_modal"> + <t t-if="widget.url"> + <h3>Scan this QR code to get the Odoo app:</h3><br/><br/> + <img class="border border-dark rounded" t-att-src="widget.url"/> + </t> + </div> +</t> +</templates> diff --git a/addons/hr_expense/views/assets.xml b/addons/hr_expense/views/assets.xml index 90c4c0011a9..3cf5e0f6831 100644 --- a/addons/hr_expense/views/assets.xml +++ b/addons/hr_expense/views/assets.xml @@ -4,6 +4,7 @@ <template id="assets_backend" name="HR Expense Assets assets" inherit_id="web.assets_backend"> <xpath expr="." position="inside"> <script type="text/javascript" src="/hr_expense/static/src/js/expense_views.js"/> + <script type="text/javascript" src="/hr_expense/static/src/js/expense_qr_code_action.js"/> <script type="text/javascript" src="/hr_expense/static/src/js/upload_mixin.js"/> </xpath> <xpath expr="link[last()]" position="after"> diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml index 2cae24fbcf9..f681dbe0af2 100644 --- a/addons/hr_expense/views/hr_expense_views.xml +++ b/addons/hr_expense/views/hr_expense_views.xml @@ -105,7 +105,7 @@ class="oe_stat_button" icon="fa-file-text-o" type="object"> - <field name="attachment_number" widget="statinfo" string="Documents" options="{'reload_on_button': true}"/> + <field name="attachment_number" widget="statinfo" string="Receipts" options="{'reload_on_button': true}"/> </button> </div> <div class="oe_title"> @@ -319,7 +319,7 @@ </record> <record id="hr_expense_actions_my_unsubmitted" model="ir.actions.act_window"> - <field name="name">My Expenses</field> + <field name="name">My Expenses to Report</field> <field name="res_model">hr.expense</field> <field name="view_mode">tree,kanban,form,graph,pivot,activity</field> <field name="search_view_id" ref="hr_expense_view_search"/> @@ -330,12 +330,35 @@ Did you try the mobile app? </p> <p>Snap pictures of your receipts and let Odoo<br/> automatically create expenses for you.</p> - <p class="d-none d-md-block"> + <p> <a href="https://apps.apple.com/be/app/odoo/id1272543640" target="_blank"> - <img alt="Apple App Store" class="img img-fluid h-100" src="/hr_expense/static/img/app_store.png"/> + <img alt="Apple App Store" class="img img-fluid h-100 o_expense_apple_store" src="/hr_expense/static/img/app_store.png"/> </a> - <a href="https://play.google.com/store/apps/details?id=com.odoo.mobile" target="_blank"> - <img alt="Google Play Store" class="img img-fluid h-100" src="/hr_expense/static/img/play_store.png"/> + <a href="https://play.google.com/store/apps/details?id=com.odoo.mobile" target="_blank" class="o_expense_google_store"> + <img alt="Google Play Store" class="img img-fluid h-100 o_expense_google_store" src="/hr_expense/static/img/play_store.png"/> + </a> + </p> + </field> + </record> + + <record id="hr_expense_actions_my_all" model="ir.actions.act_window"> + <field name="name">All My Expenses</field> + <field name="res_model">hr.expense</field> + <field name="view_mode">tree,kanban,form,graph,pivot,activity</field> + <field name="search_view_id" ref="hr_expense_view_search"/> + <field name="view_id" ref="view_my_expenses_tree"/> + <field name="context">{'search_default_my_expenses': 1}</field> + <field name="help" type="html"> + <p class="o_view_nocontent_expense_receipt"> + Did you try the mobile app? + </p> + <p>Snap pictures of your receipts and let Odoo<br/> automatically create expenses for you.</p> + <p> + <a href="https://apps.apple.com/be/app/odoo/id1272543640" target="_blank"> + <img alt="Apple App Store" class="img img-fluid h-100 o_expense_apple_store" src="/hr_expense/static/img/app_store.png"/> + </a> + <a href="https://play.google.com/store/apps/details?id=com.odoo.mobile" target="_blank" class="o_expense_google_store"> + <img alt="Google Play Store" class="img img-fluid h-100 o_expense_google_store" src="/hr_expense/static/img/play_store.png"/> </a> </p> </field> @@ -378,7 +401,7 @@ <group name="product_details"> <group string="General Information"> <field name="active" invisible="1"/> - <field name="type"/> + <field name="type" invisible="1"/> <field name="list_price"/> <field name="standard_price"/> <field name="uom_id" groups="uom.group_uom" options="{'no_create': True}"/> @@ -426,7 +449,9 @@ <field name="domain">[('can_be_expensed', '=', True)]</field> <field name="help" type="html"> <p class="o_view_nocontent_smiling_face"> - Create a new expense product + No expense products found. + </p><p> + Let's create one! Expense products can be reinvoiced to your customers. </p> </field> </record> @@ -455,17 +480,17 @@ <field name="model">hr.expense.sheet</field> <field name="arch" type="xml"> <tree string="Expense Reports" sample="1"> + <field name="employee_id" widget="many2one_avatar_employee"/> <field name="accounting_date" optional="show" groups="account.group_account_manager"/> <field name="create_date" optional="hide"/> <field name="name" string="Expense Report"/> - <field name="employee_id" widget="many2one_avatar_employee"/> - <field name="user_id" optional="show" widget="many2one_avatar_user"/> + <field name="user_id" optional="hide" widget="many2one_avatar_user"/> <field name="company_id" optional="show" groups="base.group_multi_company"/> + <field name="activity_ids" widget="list_activity" optional="show"/> <field name="total_amount" optional="show" sum="Total Amount" decoration-bf="True"/> <field name="currency_id" optional="hide"/> <field name="journal_id" optional="hide"/> <field name="state" optional="show" decoration-info="state == 'draft'" decoration-success="state in ['submit', 'approve', 'post', 'done']" decoration-danger="state == 'cancel'" widget="badge"/> - <field name="activity_ids" widget="list_activity" optional="show"/> <field name="message_unread" invisible="1"/> </tree> </field> @@ -513,6 +538,7 @@ <field name="state" widget="statusbar" statusbar_visible="draft,submit,approve,post,done"/> </header> <sheet> + <widget name="web_ribbon" title="Paid" bg_color="bg-success" attrs="{'invisible': [('state', '!=', 'done')]}"/> <div class="oe_button_box" name="button_box"> <button name="action_get_attachment_view" class="oe_stat_button" @@ -530,8 +556,8 @@ <group> <group name="employee_details"> <field name="employee_id" context="{'default_company_id': company_id}" widget="many2one_avatar_employee"/> - <field name="payment_mode"/> <field name="user_id" domain="[('share', '=', False)]" widget="many2one_avatar_user"/> + <field name="payment_mode"/> <field name="address_id" invisible="1" context="{'default_company_id': company_id}"/> <field name="department_id" invisible="1" context="{'default_company_id': company_id}"/> <field name="company_id" groups="base.group_multi_company"/> @@ -722,7 +748,7 @@ </record> <record id="action_hr_expense_sheet_my_all" model="ir.actions.act_window"> - <field name="name">My Expense Reports</field> + <field name="name">My Reports</field> <field name="res_model">hr.expense.sheet</field> <field name="view_mode">tree,kanban,form,pivot,graph,activity</field> <field name="search_view_id" ref="hr_expense_sheet_view_search"/> @@ -731,9 +757,9 @@ <field name="view_id" ref="view_hr_expense_sheet_dashboard_tree"/> <field name="help" type="html"> <p class="o_view_nocontent_smiling_face"> - Create a new expense report + No expense report found. </p><p> - Once you have created your expense, submit it to your manager who will validate it. + Let's create one! Once you have created your expense, submit it to your manager who will validate it. </p> </field> </record> @@ -748,7 +774,9 @@ <field name="view_id" ref="view_hr_expense_sheet_tree"/> <field name="help" type="html"> <p class="o_view_nocontent_empty_folder"> - No expense reports to approve + No expense reports found. + </p><p> + Approve the new expense reports submitted by the employees you manage. </p> </field> </record> @@ -762,9 +790,9 @@ <field name="context">{'search_default_to_post': 1}</field> <field name="help" type="html"> <p class="o_view_nocontent_smiling_face"> - Create a new expense report + No expense reports found. </p><p> - Once you have created your expense, submit it to your manager who will validate it. + Post the journal entries of the new expense reports approved by the employees' manager. </p> </field> </record> @@ -778,9 +806,23 @@ <field name="context">{'search_default_approved': 1}</field> <field name="help" type="html"> <p class="o_view_nocontent_smiling_face"> - Create a new expense report + No expense reports found. </p><p> - Once you have created your expense, submit it to your manager who will validate it. + Reimburse the employees who incurred these costs or simply register the corresponding payments. + </p> + </field> + </record> + + <record id="action_hr_expense_sheet_all" model="ir.actions.act_window"> + <field name="name">All Expense Reports</field> + <field name="res_model">hr.expense.sheet</field> + <field name="view_mode">tree,kanban,form,pivot,graph</field> + <field name="search_view_id" ref="hr_expense_sheet_view_search"/> + <field name="domain">[]</field> + <field name="context">{}</field> + <field name="help" type="html"> + <p class="o_view_nocontent_smiling_face"> + No expense reports found. </p> </field> </record> @@ -905,22 +947,27 @@ if records: <menuitem id="menu_hr_expense_root" name="Expenses" sequence="100" web_icon="hr_expense,static/description/icon.png"/> <menuitem id="menu_hr_expense_my_expenses" name="My Expenses" sequence="1" parent="menu_hr_expense_root" groups="base.group_user"/> - <menuitem id="menu_hr_expense_my_expenses_to_submit" sequence="1" parent="menu_hr_expense_my_expenses" action="hr_expense_actions_my_unsubmitted"/> - <menuitem id="menu_hr_expense_sheet_my_reports" sequence="3" parent="menu_hr_expense_my_expenses" action="action_hr_expense_sheet_my_all"/> + <menuitem id="menu_hr_expense_my_expenses_to_submit" sequence="1" parent="menu_hr_expense_my_expenses" action="hr_expense_actions_my_unsubmitted" name="My Expenses to Report"/> + <menuitem id="menu_hr_expense_my_expenses_all" sequence="2" parent="menu_hr_expense_my_expenses" action="hr_expense_actions_my_all" name="All My Expenses"/> + <menuitem id="menu_hr_expense_sheet_my_reports" sequence="3" parent="menu_hr_expense_my_expenses" action="action_hr_expense_sheet_my_all" name="My Reports"/> <menuitem id="menu_hr_expense_report" name="Expense Reports" sequence="2" parent="menu_hr_expense_root"/> <menuitem id="menu_hr_expense_sheet_all_to_approve" - name="To Approve" sequence="1" parent="menu_hr_expense_report" + name="Reports to Approve" sequence="1" parent="menu_hr_expense_report" action="action_hr_expense_sheet_all_to_approve" groups="hr_expense.group_hr_expense_team_approver"/> <menuitem id="menu_hr_expense_sheet_all_to_post" - name="To Post" sequence="2" parent="menu_hr_expense_report" + name="Reports to Post" sequence="2" parent="menu_hr_expense_report" action="action_hr_expense_sheet_all_to_post" groups="account.group_account_invoice,hr_expense.group_hr_expense_manager"/> <menuitem id="menu_hr_expense_sheet_all_to_pay" - name="To Pay" sequence="3" parent="menu_hr_expense_report" + name="Reports to Pay" sequence="3" parent="menu_hr_expense_report" action="action_hr_expense_sheet_all_to_pay" groups="account.group_account_invoice,hr_expense.group_hr_expense_manager"/> + <menuitem id="menu_hr_expense_sheet_all" + name="All Reports" sequence="4" parent="menu_hr_expense_report" + action="action_hr_expense_sheet_all" + groups="account.group_account_invoice,hr_expense.group_hr_expense_manager"/> <menuitem id="menu_hr_expense_reports" name="Reporting" sequence="4" parent="menu_hr_expense_root" groups="hr_expense.group_hr_expense_manager"/> <menuitem id="menu_hr_expense_all_expenses" name="Expenses Analysis" sequence="0" parent="menu_hr_expense_reports" action="hr_expense_actions_all"/> diff --git a/addons/hr_expense/views/res_config_settings_views.xml b/addons/hr_expense/views/res_config_settings_views.xml index ee47164eaad..39d4e47bbf2 100644 --- a/addons/hr_expense/views/res_config_settings_views.xml +++ b/addons/hr_expense/views/res_config_settings_views.xml @@ -44,7 +44,7 @@ <div class="o_setting_right_pane"> <label for="module_hr_payroll_expense" string="Reimburse in Payslip"/> <div class="text-muted"> - Expenses can be reimbursed in payslips. + Reimburse expenses in payslips </div> </div> </div> diff --git a/addons/sale_expense/models/hr_expense.py b/addons/sale_expense/models/hr_expense.py index 8a6d2c0c351..fc88dcbbff1 100644 --- a/addons/sale_expense/models/hr_expense.py +++ b/addons/sale_expense/models/hr_expense.py @@ -7,7 +7,7 @@ from odoo import api, fields, models class Expense(models.Model): _inherit = "hr.expense" - sale_order_id = fields.Many2one('sale.order', compute='_compute_sale_order_id', store=True, string='Reinvoice Customer', readonly=False, tracking=True, + sale_order_id = fields.Many2one('sale.order', compute='_compute_sale_order_id', store=True, string='Customer to Reinvoice', readonly=False, tracking=True, states={'approved': [('readonly', True)], 'done': [('readonly', True)], 'refused': [('readonly', True)]}, # NOTE: only confirmed SO can be selected, but this domain in activated throught the name search with the `sale_expense_all_order` # context key. So, this domain is not the one applied. diff --git a/addons/sale_expense/models/sale_order.py b/addons/sale_expense/models/sale_order.py index ccd3c1d1627..fef55214523 100644 --- a/addons/sale_expense/models/sale_order.py +++ b/addons/sale_expense/models/sale_order.py @@ -22,7 +22,7 @@ class SaleOrder(models.Model): @api.depends('expense_ids') def _compute_expense_count(self): - expense_data = self.env['hr.expense'].read_group([('sale_order_id', 'in', self.ids), ('state', '=', 'done')], ['sale_order_id'], ['sale_order_id']) + expense_data = self.env['hr.expense'].read_group([('sale_order_id', 'in', self.ids)], ['sale_order_id'], ['sale_order_id']) mapped_data = dict([(item['sale_order_id'][0], item['sale_order_id_count']) for item in expense_data]) for sale_order in self: sale_order.expense_count = mapped_data.get(sale_order.id, 0) diff --git a/addons/sale_expense/views/hr_expense_views.xml b/addons/sale_expense/views/hr_expense_views.xml index 68f442b7455..022c0cf9ac2 100644 --- a/addons/sale_expense/views/hr_expense_views.xml +++ b/addons/sale_expense/views/hr_expense_views.xml @@ -18,7 +18,7 @@ <field name="inherit_id" ref="hr_expense.view_expenses_tree"/> <field name="arch" type="xml"> <xpath expr="//field[@name='reference']" position="after"> - <field name="sale_order_id" optional="show" attrs="{'invisible': [('can_be_reinvoiced', '=', False)]}" options="{'no_create_edit': True, 'no_create': True, 'no_open': True}" context="{'sale_show_partner_name': True, 'sale_expense_all_order': True}" readonly="1"/> + <field name="sale_order_id" optional="hide" attrs="{'invisible': [('can_be_reinvoiced', '=', False)]}" options="{'no_create_edit': True, 'no_create': True, 'no_open': True}" context="{'sale_show_partner_name': True, 'sale_expense_all_order': True}" readonly="1"/> <field name="can_be_reinvoiced" invisible="1" readonly="1"/> </xpath> </field> @@ -64,7 +64,7 @@ <field name="name">Expenses</field> <field name="res_model">hr.expense</field> <field name="view_mode">tree,form</field> - <field name="domain">[('sale_order_id', '=', active_id), ('state', '=', 'done')]</field> + <field name="domain">[('sale_order_id', '=', active_id)]</field> <field name="context">{'default_sale_order_id': active_id}</field> </record> |
