[IMP] hr,hr_expense: adapt kanban views to OWL
task-2900536 closes odoo/odoo#95317 Related: odoo/enterprise#29116 Signed-off-by: Kevin Baptiste <kba@odoo.com>
This commit is contained in:
@@ -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': [
|
||||
|
||||
@@ -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);
|
||||
@@ -0,0 +1,9 @@
|
||||
.o_field_background_image {
|
||||
> div {
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="hr.BackgroundImage" owl="1">
|
||||
<div
|
||||
t-att-data-tooltip-template="hasTooltip and tooltipAttributes.template"
|
||||
t-att-data-tooltip-info="hasTooltip and tooltipAttributes.info"
|
||||
t-att-data-tooltip-delay="hasTooltip and props.zoomDelay"
|
||||
t-attf-style="background-image: url('#{getUrl(props.previewImage or props.name)}');"/>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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 });
|
||||
},
|
||||
}
|
||||
@@ -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,
|
||||
});
|
||||
@@ -128,7 +128,8 @@
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div class="oe_kanban_global_click o_kanban_record_has_image_fill o_hr_kanban_record">
|
||||
<field name="avatar_128" widget="image" class="o_kanban_image_fill_left" options="{'zoom': true, 'zoom_delay': 1000, 'background': true, 'preventClicks': false}"/>
|
||||
<field name="image_1024" class="o_kanban_image_fill_left" preview_image="image_128"
|
||||
widget="background_image" options="{'zoom': true, 'zoom_delay': 1000}"/>
|
||||
|
||||
<div class="oe_kanban_details">
|
||||
<div class="o_kanban_record_top">
|
||||
@@ -178,7 +179,7 @@
|
||||
<div class="o_kanban_record_bottom">
|
||||
<div class="oe_kanban_bottom_left"/>
|
||||
<div class="oe_kanban_bottom_right">
|
||||
<a title="Chat" icon="fa-comments" href="#" class="ml8 o_employee_chat_btn" attrs="{'invisible': [('user_id','=', False)]}" role="button"><i class="fa fa-comments"/></a>
|
||||
<a title="Chat" icon="fa-comments" href="#" t-on-click.prevent="() => props.record.openChat(props.record.resId)" class="ml8" attrs="{'invisible': [('user_id','=', False)]}" role="button"><i class="fa fa-comments"/></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
<field name="coach_id" invisible="1"/>
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="category_ids" widget="many2many_tags" options="{'color_field': 'color'}" optional="hide"/>
|
||||
<field name="country_id" optional="hide"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -266,7 +267,8 @@
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div class="oe_kanban_global_click o_kanban_record_has_image_fill o_hr_kanban_record">
|
||||
<field name="avatar_128" widget="image" class="o_kanban_image_fill_left" options="{'zoom': true, 'zoom_delay': 1000, 'background': true, 'preventClicks': false}"/>
|
||||
<field name="image_1024" class="o_kanban_image_fill_left" preview_image="image_128"
|
||||
widget="background_image" options="{'zoom': true, 'zoom_delay': 1000}"/>
|
||||
|
||||
<div class="oe_kanban_details">
|
||||
<div class="o_kanban_record_top">
|
||||
@@ -324,10 +326,10 @@
|
||||
<div class="o_kanban_record_bottom">
|
||||
<div class="oe_kanban_bottom_left"/>
|
||||
<div class="oe_kanban_bottom_right float-end">
|
||||
<a title="Chat" icon="fa-comments" href="#" class="ml8 o_employee_chat_btn" attrs="{'invisible': [('user_id','=', False)]}" role="button"><i class="fa fa-comments"/></a>
|
||||
<div class="hr_activity_container">
|
||||
<field name="activity_ids" widget="kanban_activity"/>
|
||||
</div>
|
||||
<a title="Chat" icon="fa-comments" href="#" t-on-click.prevent="() => props.record.openChat(props.record.resId)" class="ml8" attrs="{'invisible': [('user_id','=', False)]}" role="button"><i class="fa fa-comments"/></a>
|
||||
<div class="hr_activity_container">
|
||||
<field name="activity_ids" widget="kanban_activity"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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/**/*',
|
||||
],
|
||||
},
|
||||
|
||||
@@ -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.
|
||||
'''
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="hr_expense.KanbanRenderer" t-inherit="web.KanbanRenderer" t-inherit-mode="primary" owl="1">
|
||||
<xpath expr="//div[hasclass('o_kanban_renderer')]" position="before">
|
||||
<div class="o_expense_container position-sticky start-0 d-flex o_form_statusbar">
|
||||
<t t-foreach="Object.entries(state.expenses)" t-as="expense" t-key="expense[0]">
|
||||
<t t-set="name" t-value="expense[0]"/>
|
||||
<t t-set="data" t-value="expense[1]"/>
|
||||
<div t-attf-class="o_expense_card o_arrow_button flex-grow-1 d-flex flex-column p-3 border-bottom text-center">
|
||||
<span t-esc="renderMonetaryField(data['amount'], data['currency'])" class="h2 m-0 text-odoo"/>
|
||||
<b class="mx-2" t-esc="data['description']"/>
|
||||
</div>
|
||||
<div t-if="name !== 'approved'" t-attf-class="o_expense_card o_arrow_button flex-grow-1 d-flex flex-column p-3 border-bottom text-center">
|
||||
<i class="fa fa-angle-right fa-3x"/>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</xpath>
|
||||
</t>
|
||||
|
||||
<t t-name="hr_expense.KanbanButtons" t-inherit="web.KanbanView.Buttons" t-inherit-mode="primary" owl="1">
|
||||
<xpath expr="//t[@t-if='canCreate']" position="after">
|
||||
<button type="button" class="d-none d-md-inline o_button_upload_expense btn btn-primary mx-1" t-on-click.prevent="uploadDocument">
|
||||
Upload
|
||||
</button>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//t[@t-if='canCreate']" position="before">
|
||||
<button type="button" class="d-inline d-md-none o_button_upload_expense btn btn-primary mx-1" t-on-click.prevent="uploadDocument">
|
||||
Scan
|
||||
</button>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//div" position="inside">
|
||||
<input type="file" name="ufile" class="d-none" t-ref="fileInput" multiple="1" accept="*" t-on-change="onChangeFileInput" />
|
||||
</xpath>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -32,18 +32,4 @@
|
||||
</button>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-extend="KanbanView.buttons" t-name="ExpensesKanbanView.buttons">
|
||||
<t t-jquery="button:last" t-operation="after">
|
||||
<button type="button" class="d-none d-md-inline o_button_upload_expense btn btn-primary mx-1">
|
||||
Upload
|
||||
</button>
|
||||
</t>
|
||||
|
||||
<t t-jquery="button:first" t-operation="before">
|
||||
<button type="button" class="d-inline d-md-none o_button_upload_expense btn btn-primary mx-1">
|
||||
Scan
|
||||
</button>
|
||||
</t>
|
||||
</t>
|
||||
</templates>
|
||||
|
||||
@@ -290,7 +290,7 @@
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//kanban" position="attributes">
|
||||
<attribute name="js_class">hr_expense_kanban_header</attribute>
|
||||
<attribute name="js_class">hr_expense_dashboard_kanban</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
@@ -429,10 +429,10 @@
|
||||
</p>
|
||||
<p>Snap pictures of your receipts and let Odoo<br/> automatically create expenses for you.</p>
|
||||
<p class="d-none d-md-block">
|
||||
<a href="https://apps.apple.com/be/app/odoo/id1272543640" target="_blank">
|
||||
<a href="https://apps.apple.com/be/app/odoo/id1272543640" target="_blank" class="o_expense_mobile_app">
|
||||
<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">
|
||||
<a href="https://play.google.com/store/apps/details?id=com.odoo.mobile" target="_blank" class="o_expense_mobile_app">
|
||||
<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>
|
||||
@@ -466,10 +466,10 @@
|
||||
</p>
|
||||
<p>Snap pictures of your receipts and let Odoo<br/> automatically create expenses for you.</p>
|
||||
<p class="d-none d-md-block">
|
||||
<a href="https://apps.apple.com/be/app/odoo/id1272543640" target="_blank">
|
||||
<a href="https://apps.apple.com/be/app/odoo/id1272543640" target="_blank" class="o_expense_mobile_app">
|
||||
<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">
|
||||
<a href="https://play.google.com/store/apps/details?id=com.odoo.mobile" target="_blank" class="o_expense_mobile_app">
|
||||
<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>
|
||||
@@ -868,7 +868,7 @@
|
||||
<field name="mode">primary</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//kanban" position="attributes">
|
||||
<attribute name="js_class">hr_expense_kanban_header</attribute>
|
||||
<attribute name="js_class">hr_expense_dashboard_kanban</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -105,7 +105,6 @@
|
||||
</p>
|
||||
</div>
|
||||
<field name="level_ids" mode="kanban" nolabel="1"
|
||||
|
||||
class="o_hr_holidays_plan_level_container o_hr_holidays_plan_level_hierarchy"
|
||||
add-label="Add a new level"
|
||||
>
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
<field name="name">hr.leave.view.kanban</field>
|
||||
<field name="model">hr.leave</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_kanban_mobile" js_class="time_off_kanban" create="0" sample="1">
|
||||
<kanban class="o_kanban_mobile" create="0" sample="1">
|
||||
<field name="employee_id"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
<xpath expr="//li[@id='last_login']" position="inside">
|
||||
<span t-if="record.current_leave_id.raw_value" style="font-size: 100%"
|
||||
t-att-class="record.current_leave_state.raw_value=='validate'?'oe_kanban_button oe_kanban_color_3':'oe_kanban_button oe_kanban_color_2'"
|
||||
t-att-title="moment(record.leave_date_from.raw_value).format('ddd Do MMM') + ' - ' + moment(record.leave_date_to.raw_value).format('ddd Do MMM')">
|
||||
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')">
|
||||
<field name="current_leave_id"/>
|
||||
</span>
|
||||
</xpath>
|
||||
|
||||
Reference in New Issue
Block a user