diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/hr_expense/static/src | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/hr_expense/static/src')
| -rw-r--r-- | addons/hr_expense/static/src/img/expense.png | bin | 0 -> 26896 bytes | |||
| -rw-r--r-- | addons/hr_expense/static/src/js/expense_qr_code_action.js | 19 | ||||
| -rw-r--r-- | addons/hr_expense/static/src/js/expense_views.js | 167 | ||||
| -rw-r--r-- | addons/hr_expense/static/src/js/tours/hr_expense.js | 65 | ||||
| -rw-r--r-- | addons/hr_expense/static/src/js/upload_mixin.js | 77 | ||||
| -rw-r--r-- | addons/hr_expense/static/src/scss/hr_expense.scss | 47 | ||||
| -rw-r--r-- | addons/hr_expense/static/src/xml/documents_upload_views.xml | 31 | ||||
| -rw-r--r-- | addons/hr_expense/static/src/xml/expense_dashboard.xml | 23 | ||||
| -rw-r--r-- | addons/hr_expense/static/src/xml/expense_qr_modal_template.xml | 11 |
9 files changed, 440 insertions, 0 deletions
diff --git a/addons/hr_expense/static/src/img/expense.png b/addons/hr_expense/static/src/img/expense.png Binary files differnew file mode 100644 index 00000000..04e43790 --- /dev/null +++ b/addons/hr_expense/static/src/img/expense.png 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 00000000..5de326d0 --- /dev/null +++ b/addons/hr_expense/static/src/js/expense_qr_code_action.js @@ -0,0 +1,19 @@ +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); + this.url = _.str.sprintf("/report/barcode/?type=QR&value=%s&width=256&height=256&humanreadable=1", action.params.url); + }, +}); + +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 new file mode 100644 index 00000000..a031f647 --- /dev/null +++ b/addons/hr_expense/static/src/js/expense_views.js @@ -0,0 +1,167 @@ +odoo.define('hr_expense.expenses.tree', function (require) { +"use strict"; + 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; + + var ExpensesListController = ListController.extend(DocumentUploadMixin, { + buttons_template: 'ExpensesListView.buttons', + events: _.extend({}, ListController.prototype.events, { + 'click .o_button_upload_expense': '_onUpload', + 'change .o_expense_documents_upload .o_form_binary_form': '_onAddAttachment', + }), + }); + + 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; + await this._super(...arguments); + const result = await this._rpc({ + model: 'hr.expense', + method: 'get_expense_dashboard', + context: this.context, + }); + + self.$el.parent().find('.o_expense_container').remove(); + const elem = QWeb.render('hr_expense.dashboard_list_header', { + expenses: result, + render_monetary_field: self.render_monetary_field, + }); + self.$el.before(elem); + }, + render_monetary_field: function (value, currency_id) { + value = value.toFixed(2); + var currency = session.get_currency(currency_id); + if (currency) { + if (currency.position === "after") { + value += currency.symbol; + } else { + value = currency.symbol + value; + } + } + return value; + } + }; + + // Expense List Renderer + var ExpenseListRenderer = ListRenderer.extend(ExpenseQRCodeMixin); + + // Expense List Renderer with the Header + // Used in "My Expenses to Report", "All My Expenses" & "My Reports" + var ExpenseListRendererHeader = ExpenseListRenderer.extend(ExpenseDashboardMixin); + + var ExpensesListViewDashboardUpload = ListView.extend({ + config: _.extend({}, ListView.prototype.config, { + Renderer: ExpenseListRenderer, + Controller: ExpensesListController, + }), + }); + + // Used in "My Expenses to Report" & "All My Expenses" + var ExpensesListViewDashboardUploadHeader = ExpensesListViewDashboardUpload.extend({ + config: _.extend({}, ExpensesListViewDashboardUpload.prototype.config, { + Renderer: ExpenseListRendererHeader, + }), + }); + + // The dashboard view of the expense module + var ExpensesListViewDashboard = ListView.extend({ + config: _.extend({}, ListView.prototype.config, { + Renderer: ExpenseListRenderer, + Controller: ExpensesListController, + }), + }); + + // The dashboard view of the expense module with an header + // Used in "My Expenses" + var ExpensesListViewDashboardHeader = ExpensesListViewDashboard.extend({ + config: _.extend({}, ExpensesListViewDashboard.prototype.config, { + Renderer: ExpenseListRendererHeader, + }) + }); + + var ExpensesKanbanController = KanbanController.extend(DocumentUploadMixin, { + buttons_template: 'ExpensesKanbanView.buttons', + events: _.extend({}, KanbanController.prototype.events, { + 'click .o_button_upload_expense': '_onUpload', + 'change .o_expense_documents_upload .o_form_binary_form': '_onAddAttachment', + }), + }); + + var ExpenseKanbanRenderer = KanbanRenderer.extend(ExpenseQRCodeMixin); + + var ExpenseKanbanRendererHeader = ExpenseKanbanRenderer.extend(ExpenseDashboardMixin); + + // The kanban view + var ExpensesKanbanView = KanbanView.extend({ + config: _.extend({}, KanbanView.prototype.config, { + Controller: ExpensesKanbanController, + Renderer: ExpenseKanbanRenderer, + }), + }); + + // The kanban view with the Header + // Used in "My Expenses to Report", "All My Expenses" & "My Repo + var ExpensesKanbanViewHeader = ExpensesKanbanView.extend({ + config: _.extend({}, ExpensesKanbanView.prototype.config, { + Renderer: ExpenseKanbanRendererHeader, + }) + }); + + viewRegistry.add('hr_expense_tree_dashboard_upload', ExpensesListViewDashboardUpload); + // Tree view with the header. + // Used in "My Expenses to Report" & "All My Expenses" + viewRegistry.add('hr_expense_tree_dashboard_upload_header', ExpensesListViewDashboardUploadHeader); + viewRegistry.add('hr_expense_tree_dashboard', ExpensesListViewDashboard); + // Tree view with the header. + // Used in "My Reports" + viewRegistry.add('hr_expense_tree_dashboard_header', ExpensesListViewDashboardHeader); + viewRegistry.add('hr_expense_kanban', ExpensesKanbanView); + // Kanban view with the header. + // Used in "My Expenses to Report", "All My Expenses" & "My Reports" + viewRegistry.add('hr_expense_kanban_header', ExpensesKanbanViewHeader); +}); diff --git a/addons/hr_expense/static/src/js/tours/hr_expense.js b/addons/hr_expense/static/src/js/tours/hr_expense.js new file mode 100644 index 00000000..0422d687 --- /dev/null +++ b/addons/hr_expense/static/src/js/tours/hr_expense.js @@ -0,0 +1,65 @@ +odoo.define('hr_expense.tour', function(require) { +"use strict"; + +var core = require('web.core'); +var tour = require('web_tour.tour'); + +var _t = core._t; + +tour.register('hr_expense_tour' , { + url: "/web" +}, [tour.stepUtils.showAppsMenuItem(), { + trigger: '.o_app[data-menu-xmlid="hr_expense.menu_hr_expense_root"]', + content: _t("Want to manage your expenses? It starts here."), + position: 'right', + edition: 'community' +}, { + trigger: '.o_app[data-menu-xmlid="hr_expense.menu_hr_expense_root"]', + content: _t("Want to manage your expenses? It starts here."), + position: 'bottom', + edition: 'enterprise' +}, { + trigger: '.o_form_button_save', + content: _t("<p>Once your <b> Expense </b> is ready, you can save it.</p>"), + position: 'bottom', +}, { + trigger: '.o_attach_document', + content: _t("Attach your receipt here."), + position: 'bottom', +}, { + trigger: '.o_expense_submit', + extra_triggger: ".o_expense_form", + content: _t('<p>Click on <b> Create Report </b> to create the report.</p>'), + position: 'right', +}, { + trigger: '.o_expense_tree input[type=checkbox]', + content: _t('<p>Select expenses to submit them to your manager</p>'), + position: 'bottom' +}, { + trigger: '.o_dropdown_toggler_btn', + extra_trigger: ".o_expense_tree", + content: _t('<p>Click on <b> Action Create Report </b> to submit selected expenses to your manager</p>'), + position: 'right', +}, { + trigger: '.o_expense_sheet_submit', + content: _t('Once your <b>Expense report</b> is ready, you can submit it to your manager and wait for the approval from your manager.'), + position: 'bottom', +}, { + trigger: '.o_expense_sheet_approve', + content: _t("<p>Approve the report here.</p><p>Tip: if you refuse, don’t forget to give the reason thanks to the hereunder message tool</p>"), + position: 'bottom', +}, { + trigger: '.o_expense_sheet_post', + content: _t("<p>The accountant receive approved expense reports.</p><p>He can post journal entries in one click if taxes and accounts are right.</p>"), + position: 'bottom', +}, { + trigger: '.o_expense_sheet_pay', + content: _t("The accountant can register a payment to reimburse the employee directly."), + position: 'bottom', +}, { + trigger: 'li a[data-menu-xmlid="hr_expense.menu_hr_expense_sheet_my_all"], div[data-menu-xmlid="hr_expense.menu_hr_expense_sheet_my_all"]', + content: _t("Managers can get all reports to approve from this menu."), + position: 'bottom', +}]); + +}); diff --git a/addons/hr_expense/static/src/js/upload_mixin.js b/addons/hr_expense/static/src/js/upload_mixin.js new file mode 100644 index 00000000..f5fbd74c --- /dev/null +++ b/addons/hr_expense/static/src/js/upload_mixin.js @@ -0,0 +1,77 @@ +odoo.define('hr_expense.documents.upload.mixin', function (require) { +"use strict"; + +var core = require('web.core'); +var config = require('web.config'); +var _t = core._t; +var qweb = core.qweb; + +/** +* Mixin for uploading single or multiple documents. +*/ +var DocumentUploadMixin = { + start: function () { + // define a unique uploadId and a callback method + this.fileUploadID = _.uniqueId('hr_expense_document_upload'); + $(window).on(this.fileUploadID, this._onFileUploaded.bind(this)); + return this._super.apply(this, arguments); + }, + /** + * @private + */ + _onAddAttachment: function (ev) { + // Auto submit form once we've selected an attachment + var $input = $(ev.currentTarget).find('input.o_input_file'); + if ($input.val() !== '') { + var $binaryForm = this.$('.o_expense_documents_upload form.o_form_binary_form'); + $binaryForm.submit(); + } + }, + /** + * @private + */ + _onFileUploaded: function () { + // Callback once attachment have been created, create an expense with attachment ids + var self = this; + var attachments = Array.prototype.slice.call(arguments, 1); + // Get id from result + var attachent_ids = attachments.reduce(function(filtered, record) { + if (record.id) { + filtered.push(record.id); + } + return filtered; + }, []); + if (!attachent_ids.length) { + return self.do_notify(false, _t("An error occurred during the upload")); + } + var myContext = this.initialState.context + myContext['isMobile'] = config.device.isMobile + return this._rpc({ + model: 'hr.expense', + method: 'create_expense_from_attachments', + args: ["", attachent_ids, this.viewType], + context: myContext, + }).then(function(result) { + self.do_action(result); + }); + }, + /** + * @private + * @param {Event} event + */ + _onUpload: function (event) { + var self = this; + // If hidden upload form don't exists, create it + var $formContainer = this.$('.o_content').find('.o_expense_documents_upload'); + if (!$formContainer.length) { + $formContainer = $(qweb.render('hr.expense.DocumentsHiddenUploadForm', {widget: this})); + $formContainer.appendTo(this.$('.o_content')); + } + // Trigger the input to select a file + this.$('.o_expense_documents_upload .o_input_file').click(); + }, +}; + +return DocumentUploadMixin; + +}); diff --git a/addons/hr_expense/static/src/scss/hr_expense.scss b/addons/hr_expense/static/src/scss/hr_expense.scss new file mode 100644 index 00000000..5c0d1e84 --- /dev/null +++ b/addons/hr_expense/static/src/scss/hr_expense.scss @@ -0,0 +1,47 @@ +.o_content { + .o_expense_card { + border-bottom: 1px solid darken($o-control-panel-background-color, 20%); + border-right: 1px solid darken($o-control-panel-background-color, 20%); + text-align: center; + padding: 10px; + .content_center { + margin-top: auto; + margin-bottom: auto; + } + div { + line-height: 1; + } + } + + .o_expense_container { + @include o-position-sticky($left: 0px); + } + + .o_expense_card_last { + border-right: 0px; + } + + .o_expense_purple { + color: $o-enterprise-color; + } +} +.hr_expense { + &.o_list_view, &.o_kanban_view { + height: auto; + min-height: auto; + } +} +.hr_expense .o_view_nocontent { + top: 10%; +} +.o_view_nocontent { + .o_nocontent_help { + .o_view_nocontent_expense_receipt:before { + @extend %o-nocontent-init-image; + @include size(300px, 230px); + background: transparent url(/hr_expense/static/img/nocontent.png) no-repeat center; + background-size: 300px 230px; + margin-bottom: 1rem; + } + } +} diff --git a/addons/hr_expense/static/src/xml/documents_upload_views.xml b/addons/hr_expense/static/src/xml/documents_upload_views.xml new file mode 100644 index 00000000..a491d1a7 --- /dev/null +++ b/addons/hr_expense/static/src/xml/documents_upload_views.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<templates> + <t t-name="hr.expense.DocumentsHiddenUploadForm"> + <div class="d-none o_expense_documents_upload"> + <t t-call="HiddenInputFile"> + <t t-set="multi_upload" t-value="true"/> + <t t-set="fileupload_id" t-value="widget.fileUploadID"/> + <t t-set="fileupload_action" t-translation="off">/web/binary/upload_attachment</t> + <input type="hidden" name="model" t-att-value="'hr.expense'"/> + <input type="hidden" name="id" t-att-value="0"/> + </t> + </div> + </t> + + <t t-extend="ListView.buttons" t-name="ExpensesListView.buttons"> + <t t-jquery="button.o_list_button_add" t-operation="after"> + <button type="button" class="btn btn-primary o_button_upload_expense"> + Upload + </button> + </t> + </t> + + <t t-extend="KanbanView.buttons" t-name="ExpensesKanbanView.buttons"> + <t t-jquery="button" t-operation="after"> + <button type="button" class="btn btn-primary o_button_upload_expense"> + Upload + </button> + </t> + </t> +</templates> diff --git a/addons/hr_expense/static/src/xml/expense_dashboard.xml b/addons/hr_expense/static/src/xml/expense_dashboard.xml new file mode 100644 index 00000000..ba1a10c8 --- /dev/null +++ b/addons/hr_expense/static/src/xml/expense_dashboard.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<templates id="template"> + <t t-name="hr_expense.dashboard_list_header"> + <div class="o_expense_container d-flex o_form_statusbar"> + <t t-foreach="expenses" t-as="expense"> + <div t-attf-class="o_expense_card o_arrow_button flex-grow-1 d-flex flex-column border-right-0"> + <div class="content_center"> + <div> + <span t-esc="render_monetary_field(expenses[expense]['amount'], expenses[expense]['currency'])" class="h2 o_expense_purple"/> + </div> + <b class="m-2"><span t-esc="expenses[expense]['description']"/></b> + </div> + </div> + <div t-if="expense !== 'approved'" t-attf-class="o_expense_card o_arrow_button flex-grow-1 d-flex flex-column border-right-0"> + <div class="content_center"> + <span class="fa fa-angle-right fa-3x"/> + </div> + </div> + </t> + </div> + </t> +</templates> 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 00000000..00a59f41 --- /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> |
