summaryrefslogtreecommitdiff
path: root/addons/hr_expense/static/src
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/hr_expense/static/src
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/hr_expense/static/src')
-rw-r--r--addons/hr_expense/static/src/img/expense.pngbin0 -> 26896 bytes
-rw-r--r--addons/hr_expense/static/src/js/expense_qr_code_action.js19
-rw-r--r--addons/hr_expense/static/src/js/expense_views.js167
-rw-r--r--addons/hr_expense/static/src/js/tours/hr_expense.js65
-rw-r--r--addons/hr_expense/static/src/js/upload_mixin.js77
-rw-r--r--addons/hr_expense/static/src/scss/hr_expense.scss47
-rw-r--r--addons/hr_expense/static/src/xml/documents_upload_views.xml31
-rw-r--r--addons/hr_expense/static/src/xml/expense_dashboard.xml23
-rw-r--r--addons/hr_expense/static/src/xml/expense_qr_modal_template.xml11
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
new file mode 100644
index 00000000..04e43790
--- /dev/null
+++ b/addons/hr_expense/static/src/img/expense.png
Binary files differ
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>