[IMP] hr_holidays: UX changes to increase usability

In this commit, next to UX changes, we make some changes in order to
increase time off dashboard usability.
In particular, - New Allocation Request - was moved to Time Off Card.
Now user can also ask for specific time off type allocation from
the time-off-card-popover.

Moreover, we fix the following issue:
Previously allocation name was not computed on create. We also fix the
computation of the name, in case time of type is not filled in yet.

With the later change, test_allocation_request test was failing as
first the name field was assigned, then holiday_status_id;
Which caused for the name field to be recomputed and assigned to False.
As the field is required, leave_form.save() was raising an error.

task - 3389144

closes odoo/odoo#132187

Related: odoo/enterprise#47651
Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
sofiagvaladze
2023-09-21 16:52:07 +00:00
parent a4fb297d32
commit 3b3eb8f4db
11 changed files with 75 additions and 38 deletions
@@ -151,7 +151,9 @@ class HolidaysAllocation(models.Model):
def _compute_is_officer(self):
self.is_officer = self.env.user.has_group("hr_holidays.group_hr_holidays_user")
# Useless depends, so that name is computed on new, before saving the record
@api.depends_context('uid')
@api.depends('holiday_status_id')
def _compute_description(self):
self.check_access_rights('read')
self.check_access_rule('read')
@@ -162,11 +164,14 @@ class HolidaysAllocation(models.Model):
if is_officer or allocation.employee_id.user_id == self.env.user or allocation.employee_id.leave_manager_id == self.env.user:
title = allocation.sudo().private_name
if allocation.env.context.get('is_employee_allocation'):
allocation_duration = allocation.number_of_days_display if allocation.type_request_unit != 'hour' else allocation.number_of_hours_display
title = _(" %s Allocation Request ( %s %s)" % (
allocation.holiday_status_id.name,
allocation_duration,
allocation.type_request_unit))
if allocation.holiday_status_id:
allocation_duration = allocation.number_of_days_display if allocation.type_request_unit != 'hour' else allocation.number_of_hours_display
title = _(" %s Allocation Request ( %s %s)" % (
allocation.holiday_status_id.name,
allocation_duration,
allocation.type_request_unit))
else:
title = _("Allocation Request")
allocation.name = title
else:
allocation.name = '*****'
@@ -589,6 +594,7 @@ class HolidaysAllocation(models.Model):
if 'lastcall' not in values:
values['lastcall'] = fields.Date.today()
holidays = super(HolidaysAllocation, self.with_context(mail_create_nosubscribe=True)).create(vals_list)
created_from_dashboard = self.env.context.get('created_from_dashboard')
for holiday in holidays:
partners_to_subscribe = set()
if holiday.employee_id.user_id:
@@ -601,6 +607,8 @@ class HolidaysAllocation(models.Model):
holiday.activity_update()
if holiday.validation_type == 'no' and holiday.state == 'draft':
holiday.action_confirm()
if created_from_dashboard:
holiday.action_confirm()
return holidays
def write(self, values):
@@ -457,6 +457,7 @@ class HolidaysType(models.Model):
'closest_allocation_expire': format_date(self.env, self.closest_allocation_to_expire.date_to) if self.closest_allocation_to_expire.date_to else False,
'request_unit': self.request_unit,
'icon': self.sudo().icon_id.url,
'id': self.id,
}, self.requires_allocation, self.id)
def _get_contextual_employee_id(self):
@@ -1,17 +1,19 @@
/* @odoo-module */
import { usePopover } from "@web/core/popover/popover_hook";
import { useNewAllocationRequest } from '@hr_holidays/views/hooks';
const { Component } = owl;
export class TimeOffCardPopover extends Component {}
TimeOffCardPopover.template = 'hr_holidays.TimeOffCardPopover';
TimeOffCardPopover.props = ['allocated', 'approved', 'planned', 'left', 'close?'];
TimeOffCardPopover.props = ['allocated', 'approved', 'planned', 'left', 'employeeId', 'holidayStatusId', 'close?', 'onClickNewAllocationRequest?'];
export class TimeOffCard extends Component {
setup() {
this.popover = usePopover(TimeOffCardPopover, { position: "right", popoverClass: "bg-view" });
this.newAllocationRequest = useNewAllocationRequest();
}
onClickInfo(ev) {
@@ -21,12 +23,20 @@ export class TimeOffCard extends Component {
approved: data.leaves_approved,
planned: data.leaves_requested,
left: data.virtual_remaining_leaves,
employeeId: this.props.employeeId,
holidayStatusId: this.props.holidayStatusId,
onClickNewAllocationRequest: this.newAllocationRequestFrom.bind(this),
});
}
async newAllocationRequestFrom() {
this.popover.close();
await this.newAllocationRequest(this.props.employeeId, this.props.holidayStatusId);
}
}
TimeOffCard.template = 'hr_holidays.TimeOffCard';
TimeOffCard.props = ['name', 'id', 'data', 'requires_allocation'];
TimeOffCard.props = ['name', 'data', 'requires_allocation', 'employeeId', 'holidayStatusId'];
export class TimeOffCardMobile extends TimeOffCard {}
@@ -54,7 +54,7 @@
<t t-name="hr_holidays.TimeOffCardPopover">
<ul class="list-unstyled p-3 mb-0">
<li class="d-flex justify-content-between">Allocated: <span class="ps-1" t-esc="props.allocated"/></li>
<li class="d-flex justify-content-between">Allocated (<span class="btn-link p-0 cursor-pointer" t-on-click="props.onClickNewAllocationRequest">new request</span>): <span class="ps-1" t-esc="props.allocated"/></li>
<li class="d-flex justify-content-between">Approved: <span class="ps-1" t-esc="props.approved"/></li>
<li class="d-flex justify-content-between border-bottom">Planned: <span class="ps-1" t-esc="props.planned"/></li>
<li class="d-flex justify-content-between">Available: <span class="ps-1" t-esc="props.left"/></li>
@@ -1,6 +1,7 @@
/* @odoo-module */
import { TimeOffCard } from './time_off_card';
import { useNewAllocationRequest } from '@hr_holidays/views/hooks';
import { useBus, useService } from "@web/core/utils/hooks";
const { Component, useState, onWillStart } = owl;
@@ -8,6 +9,7 @@ const { Component, useState, onWillStart } = owl;
export class TimeOffDashboard extends Component {
setup() {
this.orm = useService("orm");
this.newRequest = useNewAllocationRequest();
this.state = useState({
holidays: [],
});
@@ -19,7 +21,7 @@ export class TimeOffDashboard extends Component {
await this.loadDashboardData();
});
}
async loadDashboardData() {
const context = {};
if (this.props.employeeId !== null) {
@@ -35,6 +37,9 @@ export class TimeOffDashboard extends Component {
}
);
}
async newAllocationRequest() {
await this.newRequest(this.props.employeeId);
}
}
TimeOffDashboard.components = { TimeOffCard };
@@ -2,7 +2,13 @@
<templates xml:space="preserve">
<div t-name="hr_holidays.TimeOffDashboard" class="o_timeoff_dashboard">
<t t-foreach="state.holidays" t-as="holiday" t-key="holiday[3]">
<TimeOffCard name="holiday[0]" data="holiday[1]" requires_allocation="holiday[2] == 'yes'" id="holiday[3]"/>
<TimeOffCard name="holiday[0]" data="holiday[1]" requires_allocation="holiday[2] == 'yes'" holidayStatusId="holiday[3]" employeeId="props.employeeId"/>
</t>
<div class="o_timeoff_card py-3 text-odoo d-flex flex-grow-0 justify-content-center">
<button class="btn btn-link p-4" t-on-click="newAllocationRequest" type="button">
<t t-if="employeeId">Grant Time</t>
<t t-else="">New Allocation Request</t>
</button>
</div>
</div>
</templates>
@@ -60,23 +60,6 @@ export class TimeOffCalendarController extends CalendarController {
});
}
newAllocationRequest() {
const context = {
'form_view_ref': 'hr_holidays.hr_leave_allocation_view_form_dashboard',
};
if (this.employeeId) {
context['default_employee_id'] = this.employeeId;
context['default_employee_ids'] = [this.employeeId];
context['form_view_ref'] = 'hr_holidays.hr_leave_allocation_view_form_manager_dashboard';
}
this.displayDialog(FormViewDialog, {
resModel: 'hr.leave.allocation',
title: _t('New Allocation'),
context: context,
});
}
deleteRecord(record) {
if (!record.data.can_cancel) {
this.displayDialog(ConfirmationDialog, {
@@ -9,16 +9,11 @@
<button class="btn btn-primary btn-time-off " t-on-click="newTimeOffRequest" type="button">
New
</button>
<Dropdown togglerClass="'btn btn-primary'" showCaret="true" class="'btn-group'">
<button class="btn btn-link" t-on-click="newAllocationRequest" type="button">
<t t-if="employeeId">Grant Time</t>
<t t-else="">New Allocation Request</t>
</button>
</Dropdown>
</div>
</span>
</t>
</xpath>
<DatePicker position="replace"/>
</t>
</templates>
@@ -5,7 +5,6 @@
<xpath expr="//t[@t-foreach='props.model.filterSections']" position="after">
<div class="o_calendar_filter">
<h5>Legend</h5>
<div class="d-flex flex-column">
<span><img class="o_calendar_filter_plain" src="/hr/static/src/img/icons/plain.svg"/> Validated</span>
<span><img class="o_calendar_filter_hatched" src="/hr/static/src/img/icons/hatched.svg"/> To Approve</span>
@@ -14,7 +13,7 @@
<div class="d-flex flex-column mt-4" t-if="leaveState.mandatoryDays.length">
<h5>Mandatory Days</h5>
<ul class="ps-2">
<ul class="ps-0">
<li t-foreach="leaveState.mandatoryDays" t-as="mandatoryDay" t-key="mandatoryDay.id" class="mt-2 list-unstyled">
<strong
t-esc="getFormattedDateSpan(mandatoryDay.start, mandatoryDay.end)"
@@ -26,7 +25,7 @@
<div class="d-flex flex-column mt-4" t-if="leaveState.bankHolidays.length">
<h5>Public Holidays</h5>
<ul class="ps-2">
<ul class="ps-0">
<li t-foreach="leaveState.bankHolidays" t-as="bankHoliday" t-key="bankHoliday.id" class="mt-2 list-unstyled">
<strong
t-esc="getFormattedDateSpan(bankHoliday.start, bankHoliday.end)"/>
+31 -1
View File
@@ -1,7 +1,9 @@
/** @odoo-module **/
import { _t } from "@web/core/l10n/translation";
import { useService } from "@web/core/utils/hooks";
import { useService, useOwnedDialogs } from "@web/core/utils/hooks";
import { FormViewDialog } from '@web/views/view_dialogs/form_view_dialog';
import { useComponent } from '@odoo/owl';
export function useMandatoryDays(props) {
return (info) => {
@@ -39,3 +41,31 @@ export function useLeaveCancelWizard() {
});
}
}
export function useNewAllocationRequest() {
const addDialog = useOwnedDialogs();
const component = useComponent();
return async (employeeId, holidayStatusId) => {
const context = {
'form_view_ref': 'hr_holidays.hr_leave_allocation_view_form_dashboard',
'is_employee_allocation': true,
'created_from_dashboard': true,
};
if (employeeId) {
context['default_employee_id'] = employeeId;
context['default_employee_ids'] = [employeeId];
context['form_view_ref'] = 'hr_holidays.hr_leave_allocation_view_form_manager_dashboard';
}
if (holidayStatusId) {
context['default_holiday_status_id'] = holidayStatusId;
}
addDialog(FormViewDialog, {
resModel: 'hr.leave.allocation',
title: _t('New Allocation'),
context: context,
onRecordSaved: () => {
component.env.timeOffBus.trigger('update_dashboard');
}
});
}
}
@@ -197,10 +197,10 @@ class TestLeaveRequests(TestHrHolidaysCommon):
""" Create an allocation request """
# employee should be set to current user
allocation_form = Form(self.env['hr.leave.allocation'].with_user(self.user_employee))
allocation_form.name = 'New Allocation Request'
allocation_form.holiday_status_id = self.holidays_type_2
allocation_form.date_from = date(2019, 5, 6)
allocation_form.date_to = date(2019, 5, 6)
allocation_form.name = 'New Allocation Request'
allocation = allocation_form.save()
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')