None
diff --git a/addons/hr_holidays/static/src/scss/time_off.scss b/addons/hr_holidays/static/src/scss/time_off.scss
index ccad3e23d9d..7b656207d5e 100644
--- a/addons/hr_holidays/static/src/scss/time_off.scss
+++ b/addons/hr_holidays/static/src/scss/time_off.scss
@@ -1,110 +1,3 @@
-.o_timeoff_calendar .o_content {
- .fc-dayGridYear-view {
- .fc-bgevent {
- border-radius: 25px;
- }
- > .fc-month-container {
- > .fc-month {
- .fc-dayGridMonth-view {
- .fc-has-event {
- background-color: unset;
- }
- }
- .fc-content-skeleton {
- padding: 0;
-
- tbody {
- display: none;
- }
- }
- }
- }
- }
-
- .o_calendar_filter {
- > h5 {
- font-weight: bold;
- }
- }
- .fc-week-number {
- color: #adb5bd;
- }
-
- .o_legacy_calendar_view .o_calendar_widget .fc-dayGridMonth-view {
- .fc-week-number {
- padding-top: 0.2rem !important;
- padding-bottom: 0.3rem !important;
- padding-left: 0.3rem !important;
- line-height: unset;
- span {
- font-size: 0.85rem;
- }
- }
- .fc-week-number.fc-widget-header {
- font-size: 0.85rem;
- }
- }
-
- @for $size from 1 through length($o-colors) {
- .hr_stress_day_#{$size - 1} {
- background-color: lighten(nth($o-colors, $size), 25%) !important;
- }
- }
-}
-
-.o_timeoff_calendar .o_cp_bottom {
- @include media-breakpoint-down(md) {
- position: relative;
- flex-direction: column;
- margin-left: -16px;
- .o_cp_bottom_right {
- position: absolute;
- column-gap: 0;
- top: 0;
- right: 0;
- }
- .o_cp_bottom_left {
- flex: 1;
- width: auto;
- .o_cp_buttons {
- max-width: 100%;
- }
- .o_calendar_scale_buttons {
- display: block;
- max-width: 100vw;
- }
- }
- }
- @include media-breakpoint-down(sm) {
- .o_cp_bottom_left {
- button {
- padding: 7px 6px;
- }
- }
- }
- .o_cp_bottom_left {
- flex: 1;
- width: auto;
- }
- .o_cp_bottom_right {
- flex: none;
- width: auto;
- }
-}
-
-.o_timeoff_dashboard_popover {
- hr {
- margin-top: 0px;
- margin-bottom: 0px;
- }
-
- ul {
- list-style: none;
- padding-left: 0;
- margin: 0px;
- }
-}
-
.o_hr_leave_form {
.o_form_sheet {
padding: 0!important;
diff --git a/addons/hr_holidays/static/src/tours/hr_holidays_tour.js b/addons/hr_holidays/static/src/tours/hr_holidays_tour.js
index 540845250fd..d36180b8f87 100644
--- a/addons/hr_holidays/static/src/tours/hr_holidays_tour.js
+++ b/addons/hr_holidays/static/src/tours/hr_holidays_tour.js
@@ -1,7 +1,6 @@
/** @odoo-module **/
import tour from 'web_tour.tour';
import { _t } from 'web.core';
-import session from 'web.session';
const leaveType = "NotLimitedHR";
const leaveDateFrom = "01/17/2022";
@@ -25,39 +24,30 @@ tour.register('hr_holidays_tour', {
position: 'bottom',
},
{
- trigger: '.o_field_widget[name="holiday_status_id"]',
+ trigger: 'div[name="holiday_status_id"] input',
content: _t("Let's try to create a Sick Time Off, select it in the list"),
- position: 'right',
- run: function(actions) {
- actions.text(leaveType, this.$anchor.find('input'));
- }
+ run: `text ${leaveType}`,
},
{
- trigger: ".ui-menu-item > a",
+ trigger: `.ui-autocomplete .ui-menu-item a:contains("${leaveType}")`,
+ run: "click",
auto: true,
- in_modal: false
+ in_modal: false,
},
{
- trigger: 'input[name="request_date_from"]',
+ trigger: '.o_field_widget[name="request_date_from"] input',
content: _t("You can select the period you need to take off, from start date to end date"),
position: 'right',
- run: function(actions) {
- this.$anchor.val(leaveDateFrom);
- this.$anchor.trigger("change");
- }
+ run: `text ${leaveDateFrom}`,
},
{
- trigger: 'input[name="request_date_to"]',
+ trigger: '.o_field_widget[name="request_date_to"] input',
content: _t("You can select the period you need to take off, from start date to end date"),
position: 'right',
- run: function(actions) {
- this.$anchor.val(leaveDateTo);
- this.$anchor.trigger("change");
- },
- auto: true
+ run: `text ${leaveDateTo}`,
},
{
- trigger: 'textarea[name="name"]',
+ trigger: 'div[name="name"] textarea',
content: _t("Add some description for the people that will validate it"),
run: `text ${description}`,
position: 'right'
@@ -83,15 +73,7 @@ tour.register('hr_holidays_tour', {
position: 'bottom',
run: function(actions) {
const rows = this.$anchor.find('tr.o_data_row');
- const createdRow = rows.filter((i, row) => {
- const el = $(row);
- return el.find(`div[name="all_employee_ids"]:contains("${session.name}")`).length &&
- el.find(`td[name="name"]:contains("${description}")`).length &&
- el.find(`td[name="holiday_status_id"]:contains("${leaveType}")`).length &&
- el.find(`td[name="date_from"]:contains("${leaveDateFrom}")`).length &&
- el.find(`td[name="date_to"]:contains("${leaveDateTo}")`).length;
- });
- actions.click(createdRow);
+ actions.click(rows[0]);
}
},
{
diff --git a/addons/hr_holidays/static/src/views/calendar/calendar_controller.js b/addons/hr_holidays/static/src/views/calendar/calendar_controller.js
new file mode 100644
index 00000000000..7d765c488d3
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/calendar/calendar_controller.js
@@ -0,0 +1,105 @@
+/** @odoo-module */
+
+import { CalendarController } from '@web/views/calendar/calendar_controller';
+import { FormViewDialog } from '@web/views/view_dialogs/form_view_dialog';
+
+import { serializeDate } from "@web/core/l10n/dates";
+
+import { TimeOffCalendarFilterPanel } from './filter_panel/calendar_filter_panel';
+import { TimeOffFormViewDialog } from '../view_dialog/form_view_dialog';
+import { useLeaveCancelWizard } from '../hooks';
+
+const { EventBus, useSubEnv } = owl;
+
+export class TimeOffCalendarController extends CalendarController {
+ setup() {
+ super.setup();
+ useSubEnv({
+ timeOffBus: new EventBus(),
+ });
+ this.leaveCancelWizard = useLeaveCancelWizard();
+ }
+
+ get employeeId() {
+ return this.model.employeeId;
+ }
+
+ newTimeOffRequest() {
+ const context = {};
+ if (this.employeeId) {
+ context['default_employee_id'] = this.employeeId;
+ }
+ if (this.model.meta.scale == 'day') {
+ context['default_date_from'] = serializeDate(
+ this.model.data.range.start.set({ hours: 7 }), "datetime"
+ );
+ context['default_date_to'] = serializeDate(
+ this.model.data.range.end.set({ hours: 19 }), "datetime"
+ );
+ }
+
+ this.displayDialog(FormViewDialog, {
+ resModel: 'hr.leave',
+ title: this.env._t('New Time Off'),
+ viewId: this.model.formViewId,
+ onRecordSaved: () => {
+ this.model.load();
+ this.env.timeOffBus.trigger('update_dashboard');
+ },
+ context: context,
+ });
+ }
+
+ newAllocationRequest() {
+ const context = {
+ 'default_state': 'confirm',
+ 'form_view_ref': 'hr_holidays.hr_leave_allocation_view_form_dashboard',
+ };
+ if (this.employeeId) {
+ context['default_employee_id'] = this.employeeId;
+ context['form_view_ref'] = 'hr_holidays.hr_leave_allocation_view_form_manager_dashboard';
+ }
+
+ this.displayDialog(FormViewDialog, {
+ resModel: 'hr.leave.allocation',
+ title: this.env._t('New Allocation'),
+ context: context,
+ });
+ }
+
+ deleteRecord(record) {
+ if (!record.rawRecord.can_cancel) {
+ return super.deleteRecord(record);
+ }
+
+ this.leaveCancelWizard(record.id, () => {
+ this.model.load();
+ this.env.timeOffBus.trigger('update_dashboard');
+ });
+ }
+
+ async editRecord(record, context = {}, shouldFetchFormViewId = true) {
+ return new Promise((resolve) => {
+ this.displayDialog(
+ TimeOffFormViewDialog, {
+ resModel: this.model.resModel,
+ resId: record.id || false,
+ context,
+ title: record.title,
+ viewId: this.model.formViewId,
+ onRecordSaved: () => this.model.load(),
+ onLeaveCancelled: () => {
+ this.model.load();
+ this.env.timeOffBus.trigger('update_dashboard');
+ }
+ },
+ { onClose: () => resolve() }
+ );
+ });
+ }
+}
+TimeOffCalendarController.template = "hr_holidays.CalendarController";
+TimeOffCalendarController.components = {
+ ...TimeOffCalendarController.components,
+ FilterPanel: TimeOffCalendarFilterPanel,
+}
diff --git a/addons/hr_holidays/static/src/views/calendar/calendar_controller.xml b/addons/hr_holidays/static/src/views/calendar/calendar_controller.xml
new file mode 100644
index 00000000000..18af09af5bf
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/calendar/calendar_controller.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/hr_holidays/static/src/views/calendar/calendar_model.js b/addons/hr_holidays/static/src/views/calendar/calendar_model.js
new file mode 100644
index 00000000000..6ab6150e79a
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/calendar/calendar_model.js
@@ -0,0 +1,53 @@
+/** @odoo-module */
+
+import { CalendarModel } from '@web/views/calendar/calendar_model';
+import { serializeDate } from "@web/core/l10n/dates";
+
+export class TimeOffCalendarModel extends CalendarModel {
+ setup(params, services) {
+ super.setup(params, services);
+
+ this.data.stressDays = {};
+ if (this.env.isSmall) {
+ this.meta.scale = 'month';
+ }
+ }
+
+ makeContextDefaults(record) {
+ const context = super.makeContextDefaults(record);
+ if (this.employeeId) {
+ context['default_employee_id'] = this.employeeId;
+ }
+ return context;
+ }
+
+ async updateData(data) {
+ await super.updateData(data);
+ data.stressDays = await this.fetchStressDays(data);
+ }
+
+ async fetchStressDays(data) {
+ return this.orm.call("hr.employee", "get_stress_days", [
+ this.employeeId,
+ serializeDate(data.range.start, "datetime"),
+ serializeDate(data.range.end, "datetime"),
+ ]);
+ }
+
+ get stressDays() {
+ return this.data.stressDays;
+ }
+
+ get employeeId() {
+ return this.meta.context.employee_id && this.meta.context.employee_id[0] || null;
+ }
+
+ fetchRecords(data) {
+ const { fieldNames, resModel } = this.meta;
+ const context = {};
+ if (!this.employeeId) {
+ context['short_name'] = 1;
+ }
+ return this.orm.searchRead(resModel, this.computeDomain(data), fieldNames, { context });
+ }
+}
diff --git a/addons/hr_holidays/static/src/views/calendar/calendar_renderer.js b/addons/hr_holidays/static/src/views/calendar/calendar_renderer.js
new file mode 100644
index 00000000000..7dd41dad394
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/calendar/calendar_renderer.js
@@ -0,0 +1,33 @@
+/** @odoo-module */
+
+import { CalendarRenderer } from '@web/views/calendar/calendar_renderer';
+
+import { TimeOffCalendarCommonRenderer } from './common/calendar_common_renderer';
+import { TimeOffCalendarYearRenderer } from './year/calendar_year_renderer';
+
+import { TimeOffDashboard } from '../../dashboard/time_off_dashboard';
+
+export class TimeOffCalendarRenderer extends CalendarRenderer {
+ get employeeId() {
+ return this.props.model.employeeId;
+ }
+
+ get showDashboard() {
+ return false;
+ }
+}
+TimeOffCalendarRenderer.template = 'hr_holidays.CalendarRenderer';
+TimeOffCalendarRenderer.components = {
+ ...TimeOffCalendarRenderer.components,
+ day: TimeOffCalendarCommonRenderer,
+ week: TimeOffCalendarCommonRenderer,
+ month: TimeOffCalendarCommonRenderer,
+ year: TimeOffCalendarYearRenderer,
+ TimeOffDashboard,
+};
+
+export class TimeOffDashboardCalendarRenderer extends TimeOffCalendarRenderer {
+ get showDashboard() {
+ return !this.env.isSmall;
+ }
+}
diff --git a/addons/hr_holidays/static/src/views/calendar/calendar_renderer.scss b/addons/hr_holidays/static/src/views/calendar/calendar_renderer.scss
new file mode 100644
index 00000000000..b539bff56ec
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/calendar/calendar_renderer.scss
@@ -0,0 +1,43 @@
+.o_timeoff_calendar {
+ .o_calendar_renderer {
+ height: unset;
+
+ .o_calendar_widget {
+ @for $size from 1 through length($o-colors) {
+ .hr_stress_day_#{$size - 1}:not(.fc-disabled-day) {
+ background-color: lighten(nth($o-colors, $size), 25%) !important;
+ }
+ }
+ }
+
+ .fc-bgevent {
+ border-radius: 25px;
+ }
+
+ @for $i from 1 through length($o-filter_colors) {
+ $color: nth($o-filter_colors, $i);
+
+ .o_calendar_color_#{$i - 1} {
+ &.o_event_hatched {
+ background: repeating-linear-gradient(
+ 45deg,
+ $color,
+ $color 10px,
+ rgba($color, 0.4) 10px,
+ rgba($color, 0.4) 20px
+ ) !important;
+
+ .fc-bg {
+ background: repeating-linear-gradient(
+ 45deg,
+ $color,
+ $color 10px,
+ rgba($color, 0.5) 10px,
+ rgba($color, 0.5) 20px
+ );
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/addons/hr_holidays/static/src/views/calendar/calendar_renderer.xml b/addons/hr_holidays/static/src/views/calendar/calendar_renderer.xml
new file mode 100644
index 00000000000..45fc4acdb5c
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/calendar/calendar_renderer.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/hr_holidays/static/src/views/calendar/calendar_view.js b/addons/hr_holidays/static/src/views/calendar/calendar_view.js
new file mode 100644
index 00000000000..6439c10280a
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/calendar/calendar_view.js
@@ -0,0 +1,25 @@
+/** @odoo-module */
+
+import { calendarView } from '@web/views/calendar/calendar_view';
+
+import { TimeOffCalendarController } from './calendar_controller';
+import { TimeOffCalendarModel } from './calendar_model';
+import { TimeOffCalendarRenderer, TimeOffDashboardCalendarRenderer } from './calendar_renderer';
+
+import { registry } from '@web/core/registry';
+
+const TimeOffCalendarView = {
+ ...calendarView,
+
+ Controller: TimeOffCalendarController,
+ Renderer: TimeOffCalendarRenderer,
+ Model: TimeOffCalendarModel,
+
+ buttonTemplate: "hr_holidays.CalendarController.controlButtons",
+}
+
+registry.category('views').add('time_off_calendar', TimeOffCalendarView);
+registry.category('views').add('time_off_calendar_dashboard', {
+ ...TimeOffCalendarView,
+ Renderer: TimeOffDashboardCalendarRenderer,
+});
diff --git a/addons/hr_holidays/static/src/views/calendar/common/calendar_common_popover.js b/addons/hr_holidays/static/src/views/calendar/common/calendar_common_popover.js
new file mode 100644
index 00000000000..4df3c1954d2
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/calendar/common/calendar_common_popover.js
@@ -0,0 +1,25 @@
+/** @odoo-module */
+
+import { CalendarCommonPopover } from "@web/views/calendar/calendar_common/calendar_common_popover";
+
+import { useService } from "@web/core/utils/hooks";
+
+export class TimeOffCalendarCommonPopover extends CalendarCommonPopover {
+ setup() {
+ super.setup();
+
+ this.dialog = useService('dialog');
+ this.action = useService('action');
+ }
+
+ get isEventDeletable() {
+ const record = this.props.record.rawRecord;
+ const state = record.state;
+ return record.can_cancel || state && !['validate', 'refuse'].includes(state);
+ }
+
+ get isEventEditable() {
+ const state = this.props.record.rawRecord.state;
+ return state !== undefined;
+ }
+}
diff --git a/addons/hr_holidays/static/src/views/calendar/common/calendar_common_renderer.js b/addons/hr_holidays/static/src/views/calendar/common/calendar_common_renderer.js
new file mode 100644
index 00000000000..6a51f4fbc72
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/calendar/common/calendar_common_renderer.js
@@ -0,0 +1,23 @@
+/** @odoo-module */
+
+import { CalendarCommonRenderer } from '@web/views/calendar/calendar_common/calendar_common_renderer';
+
+import { useStressDays } from '../../hooks';
+import { TimeOffCalendarCommonPopover } from './calendar_common_popover';
+
+
+export class TimeOffCalendarCommonRenderer extends CalendarCommonRenderer {
+ setup() {
+ super.setup();
+ this.stressDays = useStressDays(this.props);
+ }
+
+ onDayRender(info) {
+ super.onDayRender(info);
+ this.stressDays(info);
+ }
+}
+TimeOffCalendarCommonRenderer.components = {
+ ...TimeOffCalendarCommonRenderer,
+ Popover: TimeOffCalendarCommonPopover,
+}
diff --git a/addons/hr_holidays/static/src/views/calendar/filter_panel/calendar_filter_panel.js b/addons/hr_holidays/static/src/views/calendar/filter_panel/calendar_filter_panel.js
new file mode 100644
index 00000000000..60a9f242b36
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/calendar/filter_panel/calendar_filter_panel.js
@@ -0,0 +1,47 @@
+/** @odoo-module */
+
+import { CalendarFilterPanel } from "@web/views/calendar/filter_panel/calendar_filter_panel";
+import { TimeOffCardMobile } from "../../../dashboard/time_off_card";
+
+import { useService } from "@web/core/utils/hooks";
+
+const { useState, onWillStart } = owl;
+
+export class TimeOffCalendarFilterPanel extends CalendarFilterPanel {
+ setup() {
+ super.setup();
+
+ this.orm = useService('orm');
+ this.leaveState = useState({
+ holidays: [],
+ });
+
+ onWillStart(async () => {
+ await this.loadFilterData();
+ });
+ }
+
+ async loadFilterData() {
+ if(!this.env.isSmall) {
+ return;
+ }
+
+ const filterData = {};
+ const data = await this.orm.call(
+ 'hr.leave.type', 'get_days_all_request', [],
+ );
+
+ data.forEach((leave) => {
+ filterData[leave[3]] = leave;
+ });
+ this.leaveState.holidays = filterData;
+ }
+}
+TimeOffCalendarFilterPanel.template = 'hr_holidays.CalendarFilterPanel';
+TimeOffCalendarFilterPanel.components = {
+ ...TimeOffCalendarFilterPanel.components,
+ TimeOffCardMobile,
+}
+TimeOffCalendarFilterPanel.subTemplates = {
+ filter: "hr_holidays.CalendarFilterPanel.filter",
+}
diff --git a/addons/hr_holidays/static/src/views/calendar/filter_panel/calendar_filter_panel.xml b/addons/hr_holidays/static/src/views/calendar/filter_panel/calendar_filter_panel.xml
new file mode 100644
index 00000000000..007373e0bb8
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/calendar/filter_panel/calendar_filter_panel.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
Legend
+
+
+
Validated
+
To Approve
+
Refused
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/hr_holidays/static/src/views/calendar/year/calendar_year_renderer.js b/addons/hr_holidays/static/src/views/calendar/year/calendar_year_renderer.js
new file mode 100644
index 00000000000..65403f2d7a8
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/calendar/year/calendar_year_renderer.js
@@ -0,0 +1,40 @@
+/** @odoo-module */
+
+import { CalendarYearRenderer } from '@web/views/calendar/calendar_year/calendar_year_renderer';
+
+import { useStressDays } from '../../hooks';
+
+const { useEffect } = owl;
+
+export class TimeOffCalendarYearRenderer extends CalendarYearRenderer {
+ setup() {
+ super.setup();
+ this.stressDays = useStressDays(this.props);
+
+ useEffect((el) => {
+ for (const week of el) {
+ const row = week.parentElement;
+
+ // Remove the week number if the week is empty.
+ // FullCalendar always displays 6 weeks even when empty.
+ if (!row.children[1].classList.length &&
+ !row.children[row.children.length - 1].classList.length) {
+ row.remove();
+ }
+ }
+ }, () => [this.rootRef.el && this.rootRef.el.querySelectorAll('.fc-content-skeleton td.fc-week-number')]);
+ }
+
+ get options() {
+ return Object.assign(super.options, {
+ weekNumbers: true,
+ weekNumbersWithinDays: false,
+ weekLabel: this.env._t('Week'),
+ });
+ }
+
+ onDayRender(info) {
+ super.onDayRender(info);
+ this.stressDays(info);
+ }
+}
diff --git a/addons/hr_holidays/static/src/views/calendar/year/calendar_year_renderer.scss b/addons/hr_holidays/static/src/views/calendar/year/calendar_year_renderer.scss
new file mode 100644
index 00000000000..a258bf9c5cf
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/calendar/year/calendar_year_renderer.scss
@@ -0,0 +1,14 @@
+.o_timeoff_calendar {
+ .o_calendar_widget {
+ .fc-dayGridYear-view {
+ .fc-week-number {
+ color: #adb5bd;
+ font-size: 0.85rem;
+ cursor: default;
+ vertical-align: middle;
+
+ line-height: unset;
+ }
+ }
+ }
+}
diff --git a/addons/hr_holidays/static/src/views/hooks.js b/addons/hr_holidays/static/src/views/hooks.js
new file mode 100644
index 00000000000..563f2312bdb
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/hooks.js
@@ -0,0 +1,35 @@
+/** @odoo-module */
+
+import { useService } from "@web/core/utils/hooks";
+
+const { useEnv } = owl;
+
+export function useStressDays(props) {
+ return (info) => {
+ const date = luxon.DateTime.fromJSDate(info.date).toISODate();
+ const stressDay = props.model.stressDays[date];
+ if (stressDay) {
+ info.el.classList.add('hr_stress_day_' + stressDay);
+ }
+ }
+}
+
+export function useLeaveCancelWizard() {
+ const action = useService('action');
+ const env = useEnv();
+
+ return (leaveId, callback) => {
+ action.doAction({
+ name: env._t('Delete Confirmation'),
+ type: "ir.actions.act_window",
+ res_model: "hr.holidays.cancel.leave",
+ target: "new",
+ views: [[false, "form"]],
+ context: {
+ default_leave_id: leaveId,
+ }
+ }, {
+ onClose: callback,
+ });
+ }
+}
diff --git a/addons/hr_holidays/static/src/views/view_dialog/form_view_dialog.js b/addons/hr_holidays/static/src/views/view_dialog/form_view_dialog.js
new file mode 100644
index 00000000000..f752380fc8a
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/view_dialog/form_view_dialog.js
@@ -0,0 +1,63 @@
+/** @odoo-module */
+
+import { FormViewDialog } from "@web/views/view_dialogs/form_view_dialog";
+
+import { registry } from '@web/core/registry';
+
+import { formView } from '@web/views/form/form_view';
+import { FormController } from '@web/views/form/form_controller';
+
+import { useLeaveCancelWizard } from '../hooks';
+
+export class TimeOffDialogFormController extends FormController {
+ setup() {
+ super.setup();
+ this.leaveCancelWizard = useLeaveCancelWizard();
+ }
+
+ deleteRecord() {
+ const leaveId = this.model.root.data.id;
+
+ this.props.onCancelLeave();
+ if (this.model.root.data.can_cancel) {
+ this.leaveCancelWizard(leaveId, () => {
+ this.props.onLeaveCancelled();
+ });
+ }
+ }
+
+ get canDelete() {
+ return this.model.root.data.can_cancel || false;
+ }
+}
+
+TimeOffDialogFormController.props = {
+ ...FormController.props,
+ onCancelLeave: Function,
+ onLeaveCancelled: Function,
+}
+
+registry.category('views').add('timeoff_dialog_form', {
+ ...formView,
+ Controller: TimeOffDialogFormController,
+});
+
+
+export class TimeOffFormViewDialog extends FormViewDialog {
+ setup() {
+ super.setup();
+
+ this.viewProps = Object.assign(this.viewProps, {
+ type: "timeoff_dialog_form",
+ buttonTemplate: 'hr_holidays.FormViewDialog.buttons',
+ onCancelLeave: () => {
+ this.props.close();
+ },
+ onLeaveCancelled: this.props.onLeaveCancelled.bind(this),
+ })
+ }
+}
+TimeOffFormViewDialog.props = {
+ ...TimeOffFormViewDialog.props,
+ onLeaveCancelled: Function,
+}
diff --git a/addons/hr_holidays/static/src/views/view_dialog/form_view_dialog.xml b/addons/hr_holidays/static/src/views/view_dialog/form_view_dialog.xml
new file mode 100644
index 00000000000..73f559533fd
--- /dev/null
+++ b/addons/hr_holidays/static/src/views/view_dialog/form_view_dialog.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/hr_holidays/static/src/xml/time_off_calendar.xml b/addons/hr_holidays/static/src/xml/time_off_calendar.xml
deleted file mode 100644
index 4e7ffebeb9f..00000000000
--- a/addons/hr_holidays/static/src/xml/time_off_calendar.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
![]()
-
-
-
-
-
-
-
Legend
-
Validated
-
To Approve
-
Refused
-
-
-
-
-
diff --git a/addons/hr_holidays/static/tests/qunit_suite_tests/test_leave_stats_widget.js b/addons/hr_holidays/static/tests/qunit_suite_tests/test_leave_stats_widget.js
index 4636abb7b42..ddc42cb028a 100644
--- a/addons/hr_holidays/static/tests/qunit_suite_tests/test_leave_stats_widget.js
+++ b/addons/hr_holidays/static/tests/qunit_suite_tests/test_leave_stats_widget.js
@@ -1,14 +1,19 @@
-odoo.define('hr_holidays.leave_stats_widget_tests', function (require) {
- "use strict";
+/** @odoo-module */
- var FormView = require("web.FormView");
- var testUtils = require('web.test_utils');
+import { selectDropdownItem, editInput, getFixture } from '@web/../tests/helpers/utils';
+import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
- var createView = testUtils.createView;
- QUnit.module('leave_stats_widget', {
- beforeEach: function () {
- this.data = {
+let serverData;
+let target;
+
+QUnit.module('leave_stats_widget', (hooks) => {
+ hooks.beforeEach(() => {
+ setupViewRegistries();
+
+ target = getFixture();
+ serverData = {
+ models: {
department: {
fields: {
name: { string: "Name", type: "char" },
@@ -82,74 +87,73 @@ odoo.define('hr_holidays.leave_stats_widget_tests', function (require) {
holiday_type: 'employee',
}]
}
- };
- }
- }, function () {
- QUnit.test('leave stats renders correctly', async function (assert) {
- assert.expect(5);
- var self = this;
- var form = await createView({
- View: FormView,
- model: 'hr.leave',
- data: this.data,
- arch: '
',
- res_id: 12,
- mockRPC: function (route, args) {
- if (args.model === 'hr.leave' && args.method === 'search') {
- return Promise.resolve(self.data['hr.leave'].records.map(function (record) { return record.id; }));
- }
- return this._super.apply(this, arguments);
- },
- });
- const $leaveTypeBody = form.$('.o_leave_stats #o_leave_stats_employee');
- const $leavesDepartmentBody = form.$('.o_leave_stats #o_leave_stats_department');
+ }
+ };
+ });
- assert.strictEqual($leaveTypeBody.find('span:contains(Legal Leave)').length, 1, "it should have leave type");
- assert.strictEqual($leaveTypeBody.find('span:contains(6)').length, 1, "it should have 6 days");
-
- assert.strictEqual($leavesDepartmentBody.find('span:contains(Richard)').length, 2, "it should have 2 leaves for Richard");
- assert.strictEqual($leavesDepartmentBody.find('span:contains(Jesus)').length, 1, "it should have 1 leaves for Jesus");
- assert.strictEqual($leavesDepartmentBody.find('div.o_horizontal_separator:contains(R&D)').length, 1, "it should have R&D title");
- form.destroy();
+ QUnit.test('leave stats renders correctly', async (assert) => {
+ assert.expect(5);
+ await makeView({
+ serverData,
+ type: "form",
+ resModel: 'hr.leave',
+ arch: '
',
+ resId: 12,
+ mockRPC(route, args) {
+ if (args.model === 'hr.leave' && args.method === 'search') {
+ return Promise.resolve(this.data['hr.leave'].records.map(function (record) { return record.id; }));
+ }
+ },
});
- QUnit.test('leave stats reload when employee/department changes', async function (assert) {
- assert.expect(2);
- var form = await createView({
- View: FormView,
- model: 'hr.leave',
- mode: 'edit',
- data: this.data,
- arch: '
',
- mockRPC: function (route, args) {
- if (args.model === 'hr.leave' && args.method === 'search_read') {
- assert.ok(_.some(args.args[0], _.matcher(['department_id', '=', 11])), "It should load department's leaves data");
- }
- if (args.model === 'hr.leave' && args.method === 'read_group') {
- assert.ok(_.some(args.kwargs.domain, _.matcher(['employee_id', '=', 200])), "It should load employee's leaves data");
- }
- return this._super.apply(this, arguments);
- },
- });
- // Set date => shouldn't load data yet (no employee nor department defined)
- await testUtils.fields.editSelect($('input[name="date_from"]'), '2016-10-12 09:00:00');
- // Set employee => should load employee's date
- await testUtils.fields.many2one.clickOpenDropdown("employee_id");
- await testUtils.fields.many2one.clickItem("employee_id", "Jesus");
- // Set department => should load department's data
- await testUtils.fields.many2one.clickOpenDropdown("department_id");
- await testUtils.fields.many2one.clickItem("department_id", "R&D");
- form.destroy();
+ const $leaveTypeBody = target.querySelector('.o_leave_stats #o_leave_stats_employee');
+ const $leavesDepartmentBody = target.querySelector('.o_leave_stats #o_leave_stats_department');
+
+ assert.containsOnce($leaveTypeBody, 'span:contains(Legal Leave)', "it should have leave type");
+ assert.containsOnce($leaveTypeBody, 'span:contains(6)', "it should have 6 days");
+
+ assert.containsN($leavesDepartmentBody, 'span:contains(Richard)', 2, "it should have 2 leaves for Richard");
+ assert.containsOnce($leavesDepartmentBody, 'span:contains(Jesus)', "it should have 1 leaves for Jesus");
+ assert.containsOnce($leavesDepartmentBody, 'div.o_horizontal_separator:contains(R&D)', "it should have R&D title");
+ });
+ QUnit.test('leave stats reload when employee/department changes', async (assert) => {
+ assert.expect(3);
+ await makeView({
+ serverData,
+ type: "form",
+ resModel: 'hr.leave',
+ mode: 'edit',
+ arch: '
',
+ mockRPC(route, args) {
+ if (args.model === 'hr.leave' && args.method === 'search_read') {
+ assert.ok(_.some(args.kwargs.domain, _.matcher(['department_id', '=', 11])), "It should load department's leaves data");
+ }
+ if (args.model === 'hr.leave' && args.method === 'read_group') {
+ assert.ok(_.some(args.kwargs.domain, _.matcher(['employee_id', '=', 200])), "It should load employee's leaves data");
+ }
+ },
});
+
+ // Set date => shouldn't load data yet (no employee nor department defined)
+ await editInput(
+ target,
+ "div[name='date_from'] input",
+ "2016-10-12 09:00:00"
+ );
+
+ // Set employee => should load employee's date
+ await selectDropdownItem(target, "employee_id", "Jesus");
+ // Set department => should load department's data
+ await selectDropdownItem(target, "department_id", "R&D");
});
});
diff --git a/addons/hr_holidays/views/hr_leave_allocation_views.xml b/addons/hr_holidays/views/hr_leave_allocation_views.xml
index ad37a56c2bb..1f96a80498d 100644
--- a/addons/hr_holidays/views/hr_leave_allocation_views.xml
+++ b/addons/hr_holidays/views/hr_leave_allocation_views.xml
@@ -63,7 +63,7 @@
hr.leave.allocation
32
-