diff --git a/addons/hr/__manifest__.py b/addons/hr/__manifest__.py index 62beb9d34f7..bbc8f6ace05 100644 --- a/addons/hr/__manifest__.py +++ b/addons/hr/__manifest__.py @@ -52,6 +52,10 @@ 'hr/static/src/models/*.js', ], 'web.assets_backend': [ + 'hr/static/src/mixins/*.js', + 'hr/static/src/views/**/*.js', + 'hr/static/src/components/**/*.js', + 'hr/static/src/components/**/*.scss', 'hr/static/src/scss/hr.scss', 'hr/static/src/js/chat_mixin.js', 'hr/static/src/js/hr_employee.js', @@ -62,6 +66,7 @@ 'hr/static/src/js/work_permit_upload.js', ], 'web.assets_qweb': [ + 'hr/static/src/components/**/*.xml', 'hr/static/src/xml/*.xml', ], 'web.qunit_suite_tests': [ diff --git a/addons/hr/static/src/components/background_image/background_image.js b/addons/hr/static/src/components/background_image/background_image.js new file mode 100644 index 00000000000..e691f9817fe --- /dev/null +++ b/addons/hr/static/src/components/background_image/background_image.js @@ -0,0 +1,10 @@ +/** @odoo-module */ + +import { registry } from '@web/core/registry'; + +import { ImageField } from '@web/views/fields/image/image_field'; + +export class BackgroundImageField extends ImageField {} +BackgroundImageField.template = 'hr.BackgroundImage'; + +registry.category("fields").add("background_image", BackgroundImageField); diff --git a/addons/hr/static/src/components/background_image/background_image.scss b/addons/hr/static/src/components/background_image/background_image.scss new file mode 100644 index 00000000000..e659f30a39c --- /dev/null +++ b/addons/hr/static/src/components/background_image/background_image.scss @@ -0,0 +1,9 @@ +.o_field_background_image { + > div { + background-position: center; + background-repeat: no-repeat; + background-size: cover; + width: 100%; + height: 100%; + } +} diff --git a/addons/hr/static/src/components/background_image/background_image.xml b/addons/hr/static/src/components/background_image/background_image.xml new file mode 100644 index 00000000000..1e627e9eb23 --- /dev/null +++ b/addons/hr/static/src/components/background_image/background_image.xml @@ -0,0 +1,10 @@ + + + +
+ + diff --git a/addons/hr/static/src/js/hr_employee.js b/addons/hr/static/src/js/hr_employee.js index 0edc4929574..dd91c35754e 100644 --- a/addons/hr/static/src/js/hr_employee.js +++ b/addons/hr/static/src/js/hr_employee.js @@ -9,11 +9,6 @@ odoo.define('hr.employee_chat', function (require) { const ListController = require('web.ListController'); const ListView = require('web.ListView'); - var KanbanController = require('web.KanbanController'); - var KanbanView = require('web.KanbanView'); - var KanbanRenderer = require('web.KanbanRenderer'); - var KanbanRecord = require('web.KanbanRecord'); - const ChatMixin = require('hr.chat_mixin'); @@ -138,22 +133,4 @@ odoo.define('hr.employee_chat', function (require) { }) viewRegistry.add('hr_employee_list', EmployeeListView); - - // USAGE OF CHAT MIXIN IN KANBAN VIEWS - var EmployeeKanbanRecord = KanbanRecord.extend(ChatMixin); - - var EmployeeKanbanRenderer = KanbanRenderer.extend({ - config: Object.assign({}, KanbanRenderer.prototype.config, { - KanbanRecord: EmployeeKanbanRecord, - }), - }); - - var EmployeeKanbanView = KanbanView.extend({ - config: _.extend({}, KanbanView.prototype.config, { - Controller: KanbanController, - Renderer: EmployeeKanbanRenderer - }), - }); - - viewRegistry.add('hr_employee_kanban', EmployeeKanbanView); }); diff --git a/addons/hr/static/src/mixins/chat_mixin.js b/addons/hr/static/src/mixins/chat_mixin.js new file mode 100644 index 00000000000..3b75270e762 --- /dev/null +++ b/addons/hr/static/src/mixins/chat_mixin.js @@ -0,0 +1,10 @@ +/** @odoo-module */ + +const { Component } = owl; + +export const EmployeeChatMixin = { + async openChat(employeeId) { + const messaging = await Component.env.services.messaging.get(); + messaging.openChat({ employeeId }); + }, +} diff --git a/addons/hr/static/src/views/kanban_view.js b/addons/hr/static/src/views/kanban_view.js new file mode 100644 index 00000000000..46681947252 --- /dev/null +++ b/addons/hr/static/src/views/kanban_view.js @@ -0,0 +1,20 @@ +/** @odoo-module */ + +import { registry } from '@web/core/registry'; +import { patch } from '@web/core/utils/patch'; + +import { kanbanView } from '@web/views/kanban/kanban_view'; +import { KanbanModel } from '@web/views/kanban/kanban_model'; + +import { EmployeeChatMixin } from '../mixins/chat_mixin'; + +export class EmployeeKanbanRecord extends KanbanModel.Record {} +patch(EmployeeKanbanRecord.prototype, 'employee_kanban_record_mixin', EmployeeChatMixin); + +export class EmployeeKanbanModel extends KanbanModel {} +EmployeeKanbanModel.Record = EmployeeKanbanRecord; + +registry.category('views').add('hr_employee_kanban', { + ...kanbanView, + Model: EmployeeKanbanModel, +}); diff --git a/addons/hr/views/hr_employee_public_views.xml b/addons/hr/views/hr_employee_public_views.xml index d63efaf5a01..176d12cd245 100644 --- a/addons/hr/views/hr_employee_public_views.xml +++ b/addons/hr/views/hr_employee_public_views.xml @@ -128,7 +128,8 @@
- +
@@ -178,7 +179,7 @@
- +
diff --git a/addons/hr/views/hr_employee_views.xml b/addons/hr/views/hr_employee_views.xml index 66c75d1db9a..8ae203b1c9f 100644 --- a/addons/hr/views/hr_employee_views.xml +++ b/addons/hr/views/hr_employee_views.xml @@ -247,6 +247,7 @@ + @@ -266,7 +267,8 @@
- +
@@ -324,10 +326,10 @@
- -
- -
+ +
+ +
diff --git a/addons/hr_expense/__manifest__.py b/addons/hr_expense/__manifest__.py index d8c57f06faf..60e5c5c2672 100644 --- a/addons/hr_expense/__manifest__.py +++ b/addons/hr_expense/__manifest__.py @@ -52,6 +52,8 @@ This module also uses analytic accounting and is compatible with the invoice on 'application': True, 'assets': { 'web.assets_backend': [ + 'hr_expense/static/src/mixins/*.js', + 'hr_expense/static/src/views/*.js', 'hr_expense/static/src/js/expense_views.js', 'hr_expense/static/src/js/expense_form_view.js', 'hr_expense/static/src/js/expense_qr_code_action.js', @@ -63,6 +65,7 @@ This module also uses analytic accounting and is compatible with the invoice on 'hr_expense/static/tests/tours/expense_upload_tours.js', ], 'web.assets_qweb': [ + 'hr_expense/static/src/views/*.xml', 'hr_expense/static/src/xml/**/*', ], }, diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index 995ca762cc3..c7ebbc6170a 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -335,7 +335,7 @@ class HrExpense(models.Model): expense=expense.name, product=expense.product_id.name, )) - def create_expense_from_attachments(self, attachment_ids=None, view_type='tree'): + def create_expense_from_attachments(self, attachment_ids=None, view_type='list'): ''' Create the expenses from files. :return: An action redirecting to hr.expense tree view. ''' diff --git a/addons/hr_expense/static/src/js/expense_views.js b/addons/hr_expense/static/src/js/expense_views.js index a5ba179fe16..043edae5921 100644 --- a/addons/hr_expense/static/src/js/expense_views.js +++ b/addons/hr_expense/static/src/js/expense_views.js @@ -1,14 +1,11 @@ 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 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 session = require('web.session'); const config = require('web.config'); @@ -129,34 +126,6 @@ odoo.define('hr_expense.expenses.tree', function (require) { }) }); - 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" @@ -165,8 +134,4 @@ odoo.define('hr_expense.expenses.tree', function (require) { // 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/mixins/document_upload.js b/addons/hr_expense/static/src/mixins/document_upload.js new file mode 100644 index 00000000000..9bc62313c1c --- /dev/null +++ b/addons/hr_expense/static/src/mixins/document_upload.js @@ -0,0 +1,50 @@ +/** @odoo-module */ + +import { useService } from '@web/core/utils/hooks'; + +const { useRef } = owl; + +export const ExpenseDocumentUpload = { + setup() { + this._super(); + this.actionService = useService('action'); + this.notification = useService('notification'); + this.orm = useService('orm'); + this.http = useService('http'); + + this.fileInput = useRef('fileInput'); + }, + + uploadDocument() { + this.fileInput.el.click(); + }, + + async onChangeFileInput() { + const params = { + csrf_token: odoo.csrf_token, + ufile: [...this.fileInput.el.files], + model: 'hr.expense', + id: 0, + }; + + const fileData = await this.http.post('/web/binary/upload_attachment', params, "text"); + const attachments = JSON.parse(fileData); + if (attachments.error) { + throw new Error(attachments.error); + } + this.onUpload(attachments); + }, + + async onUpload(attachments) { + const attachmentIds = attachments.map((a) => a.id); + if (!attachmentIds.length) { + this.notification.add( + this.env._t('An error occurred during the upload') + ); + return; + } + + const action = await this.orm.call('hr.expense', 'create_expense_from_attachments', ["", attachmentIds]); + this.actionService.doAction(action); + }, +}; diff --git a/addons/hr_expense/static/src/mixins/expense_dashboard.js b/addons/hr_expense/static/src/mixins/expense_dashboard.js new file mode 100644 index 00000000000..d631f8b53c3 --- /dev/null +++ b/addons/hr_expense/static/src/mixins/expense_dashboard.js @@ -0,0 +1,36 @@ +/** @odoo-module */ + +import { useService } from '@web/core/utils/hooks'; +import session from 'web.session'; + +const { onWillStart, useState } = owl; + + +export const ExpenseDashboardMixin = { + setup() { + this._super(); + this.orm = useService('orm'); + + this.state = useState({ + expenses: {} + }); + + onWillStart(async () => { + const expense_states = await this.orm.call("hr.expense", 'get_expense_dashboard', []); + this.state.expenses = expense_states; + }); + }, + + renderMonetaryField(value, currency_id) { + value = value.toFixed(2); + const currency = session.get_currency(currency_id); + if (currency) { + if (currency.position === "after") { + value += currency.symbol; + } else { + value = currency.symbol + value; + } + } + return value; + } +} diff --git a/addons/hr_expense/static/src/mixins/qrcode.js b/addons/hr_expense/static/src/mixins/qrcode.js new file mode 100644 index 00000000000..8d7bd395f0f --- /dev/null +++ b/addons/hr_expense/static/src/mixins/qrcode.js @@ -0,0 +1,46 @@ +/** @odoo-module */ + +import { useService } from "@web/core/utils/hooks"; + +const { onMounted, onPatched, useRef } = owl; + +export const ExpenseMobileQRCode = { + setup() { + this._super(); + this.root = useRef('root'); + this.actionService = useService('action'); + + onMounted(this.bindAppsIcons); + onPatched(this.bindAppsIcons); + }, + + bindAppsIcons() { + const apps = this.root.el.querySelectorAll('.o_expense_mobile_app'); + if (!apps) { + return; + } + + const handler = this.handleClick.bind(this); + for (const app of apps) { + app.addEventListener('click', handler); + } + }, + + handleClick(ev) { + ev.preventDefault(); + ev.stopPropagation(); + + const url = ev.currentTarget && ev.currentTarget.href; + if (!this.env.isSmall) { + this.actionService.doAction({ + name: this.env._t("Download our App"), + type: "ir.actions.client", + tag: 'expense_qr_code_modal', + target: "new", + params: { url }, + }); + } else { + this.actionService.doAction({ type: "ir.actions.act_url", url }); + } + } +}; diff --git a/addons/hr_expense/static/src/scss/hr_expense.scss b/addons/hr_expense/static/src/scss/hr_expense.scss index 8ea31cc9c80..da4481e753f 100644 --- a/addons/hr_expense/static/src/scss/hr_expense.scss +++ b/addons/hr_expense/static/src/scss/hr_expense.scss @@ -1,6 +1,6 @@ .hr_expense { @include media-breakpoint-up(md) { - &.o_list_view, &.o_legacy_kanban_view { + &.o_list_view, &.o_kanban_renderer { min-height: auto; } } diff --git a/addons/hr_expense/static/src/views/kanban.js b/addons/hr_expense/static/src/views/kanban.js new file mode 100644 index 00000000000..747ed0efc34 --- /dev/null +++ b/addons/hr_expense/static/src/views/kanban.js @@ -0,0 +1,36 @@ +/** @odoo-module */ + +import { registry } from '@web/core/registry'; +import { patch } from '@web/core/utils/patch'; + +import { ExpenseDashboardMixin } from '../mixins/expense_dashboard'; +import { ExpenseMobileQRCode } from '../mixins/qrcode'; +import { ExpenseDocumentUpload } from '../mixins/document_upload'; + +import { kanbanView } from '@web/views/kanban/kanban_view'; +import { KanbanController } from '@web/views/kanban/kanban_controller'; +import { KanbanRenderer } from '@web/views/kanban/kanban_renderer'; + +export class ExpenseKanbanController extends KanbanController {} +patch(ExpenseKanbanController.prototype, 'expense_kanban_controller_upload', ExpenseDocumentUpload); + +export class ExpenseKanbanRenderer extends KanbanRenderer {} +patch(ExpenseKanbanRenderer.prototype, 'expense_kanban_renderer_qrcode', ExpenseMobileQRCode); +ExpenseKanbanRenderer.template = 'hr_expense.KanbanRenderer'; + +export class ExpenseDashboardKanbanRenderer extends ExpenseKanbanRenderer {} +patch(ExpenseDashboardKanbanRenderer.prototype, 'expense_kanban_renderer_dashboard', ExpenseDashboardMixin); + +registry.category('views').add('hr_expense_kanban', { + ...kanbanView, + buttonTemplate: 'hr_expense.KanbanButtons', + Controller: ExpenseKanbanController, + Renderer: ExpenseKanbanRenderer, +}); + +registry.category('views').add('hr_expense_dashboard_kanban', { + ...kanbanView, + buttonTemplate: 'hr_expense.KanbanButtons', + Controller: ExpenseKanbanController, + Renderer: ExpenseDashboardKanbanRenderer, +}); diff --git a/addons/hr_expense/static/src/views/kanban.xml b/addons/hr_expense/static/src/views/kanban.xml new file mode 100644 index 00000000000..28adb7e02df --- /dev/null +++ b/addons/hr_expense/static/src/views/kanban.xml @@ -0,0 +1,38 @@ + + + + +
+ + + +
+ + +
+
+ +
+
+
+
+
+ + + + + + + + + + + + + + +
diff --git a/addons/hr_expense/static/src/xml/documents_upload_views.xml b/addons/hr_expense/static/src/xml/documents_upload_views.xml index ab16011ad68..af7771eeccc 100644 --- a/addons/hr_expense/static/src/xml/documents_upload_views.xml +++ b/addons/hr_expense/static/src/xml/documents_upload_views.xml @@ -32,18 +32,4 @@ - - - - - - - - - - diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml index 1663a74026d..dc9cd645fd6 100644 --- a/addons/hr_expense/views/hr_expense_views.xml +++ b/addons/hr_expense/views/hr_expense_views.xml @@ -290,7 +290,7 @@ primary - hr_expense_kanban_header + hr_expense_dashboard_kanban @@ -429,10 +429,10 @@

Snap pictures of your receipts and let Odoo
automatically create expenses for you.

- + Apple App Store - + Google Play Store

@@ -466,10 +466,10 @@

Snap pictures of your receipts and let Odoo
automatically create expenses for you.

- + Apple App Store - + Google Play Store

@@ -868,7 +868,7 @@ primary - hr_expense_kanban_header + hr_expense_dashboard_kanban diff --git a/addons/hr_holidays/static/src/scss/accrual_plan_level.scss b/addons/hr_holidays/static/src/scss/accrual_plan_level.scss index 3682ca9be8b..32425fa17b2 100644 --- a/addons/hr_holidays/static/src/scss/accrual_plan_level.scss +++ b/addons/hr_holidays/static/src/scss/accrual_plan_level.scss @@ -30,6 +30,7 @@ $o-hr-holidays-border-color: map-get($grays, '300'); border-radius: 25px; } + .o_legacy_kanban_view.o_kanban_ungrouped .o_kanban_record, .o_kanban_renderer.o_kanban_ungrouped .o_kanban_record { counter-increment: o-hr-holidays-accrual-plan-level-counter; display: flex; diff --git a/addons/hr_holidays/views/hr_leave_accrual_views.xml b/addons/hr_holidays/views/hr_leave_accrual_views.xml index c58f2245bc6..ce71bf98166 100644 --- a/addons/hr_holidays/views/hr_leave_accrual_views.xml +++ b/addons/hr_holidays/views/hr_leave_accrual_views.xml @@ -105,7 +105,6 @@

diff --git a/addons/hr_holidays/views/hr_leave_views.xml b/addons/hr_holidays/views/hr_leave_views.xml index 05b00887376..ab0db3e6ee2 100644 --- a/addons/hr_holidays/views/hr_leave_views.xml +++ b/addons/hr_holidays/views/hr_leave_views.xml @@ -96,7 +96,7 @@ hr.leave.view.kanban hr.leave - + diff --git a/addons/hr_holidays/views/hr_views.xml b/addons/hr_holidays/views/hr_views.xml index 59c0fd44d82..ffd9feeec97 100644 --- a/addons/hr_holidays/views/hr_views.xml +++ b/addons/hr_holidays/views/hr_views.xml @@ -136,7 +136,7 @@ + t-att-title="luxon.DateTime.fromJSDate(record.leave_date_from.raw_value).toFormat('ccc d MMM') + ' - ' + luxon.DateTime.fromJSDate(record.leave_date_to.raw_value).toFormat('ccc d MMM')">