[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:
@@ -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)"/>
|
||||
|
||||
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user