hrms emp dashboard changes
This commit is contained in:
parent
a707df36cf
commit
e93480648a
|
|
@ -124,23 +124,33 @@ class HrmsEmployeeDashboard(http.Controller):
|
|||
|
||||
def _leave_balances(self, employee):
|
||||
LeaveType = request.env["hr.leave.type"].sudo()
|
||||
Allocation = request.env["hr.leave.allocation"].sudo()
|
||||
Leave = request.env["hr.leave"].sudo()
|
||||
employee = employee.sudo()
|
||||
leave_types = LeaveType.search([("active", "=", True)], order="sequence, name")
|
||||
allocation_data = leave_types.get_allocation_data(employee)[employee]
|
||||
allocation_data_by_type = {
|
||||
leave_type_id: data
|
||||
for _name, data, _requires_allocation, leave_type_id in allocation_data
|
||||
}
|
||||
balances = []
|
||||
for leave_type in LeaveType.search([("active", "=", True)], order="sequence, name"):
|
||||
allocations = Allocation.search([
|
||||
("employee_id", "=", employee.id),
|
||||
("holiday_status_id", "=", leave_type.id),
|
||||
("state", "=", "validate"),
|
||||
])
|
||||
leaves = Leave.search([
|
||||
("employee_id", "=", employee.id),
|
||||
("holiday_status_id", "=", leave_type.id),
|
||||
("state", "in", ["confirm", "validate1", "validate"]),
|
||||
])
|
||||
allocated = sum(allocations.mapped("number_of_days"))
|
||||
taken = sum(leaves.filtered(lambda leave: leave.state == "validate").mapped("number_of_days"))
|
||||
planned = sum(leaves.filtered(lambda leave: leave.state in ("confirm", "validate1")).mapped("number_of_days"))
|
||||
for leave_type in leave_types:
|
||||
if leave_type.requires_allocation == "yes":
|
||||
data = allocation_data_by_type.get(leave_type.id, {})
|
||||
allocated = data.get("max_leaves", 0)
|
||||
taken = data.get("leaves_taken", 0)
|
||||
planned = data.get("leaves_requested", 0)
|
||||
remaining = data.get("virtual_remaining_leaves", 0)
|
||||
else:
|
||||
leaves = Leave.search([
|
||||
("employee_id", "=", employee.id),
|
||||
("holiday_status_id", "=", leave_type.id),
|
||||
("state", "in", ["confirm", "validate1", "validate"]),
|
||||
])
|
||||
allocated = 0
|
||||
taken = sum(leaves.filtered(lambda leave: leave.state == "validate").mapped("number_of_days"))
|
||||
planned = sum(leaves.filtered(lambda leave: leave.state in ("confirm", "validate1")).mapped("number_of_days"))
|
||||
remaining = 0
|
||||
chart_total = max(allocated, taken + planned + max(remaining, 0), 1)
|
||||
if allocated or taken or planned or leave_type.requires_allocation == "no":
|
||||
balances.append({
|
||||
"id": leave_type.id,
|
||||
|
|
@ -148,7 +158,10 @@ class HrmsEmployeeDashboard(http.Controller):
|
|||
"allocated": round(allocated, 2),
|
||||
"taken": round(taken, 2),
|
||||
"planned": round(planned, 2),
|
||||
"remaining": round(allocated - taken - planned, 2) if leave_type.requires_allocation != "no" else 0,
|
||||
"remaining": round(remaining, 2),
|
||||
"taken_percent": round((taken / chart_total) * 100, 2),
|
||||
"planned_percent": round((planned / chart_total) * 100, 2),
|
||||
"remaining_percent": round((max(remaining, 0) / chart_total) * 100, 2),
|
||||
"requires_allocation": leave_type.requires_allocation,
|
||||
})
|
||||
return balances
|
||||
|
|
@ -173,16 +186,19 @@ class HrmsEmployeeDashboard(http.Controller):
|
|||
def _get_public_holidays(self, employee, month_start, month_end):
|
||||
start_dt = datetime.combine(month_start, time.min)
|
||||
end_dt = datetime.combine(month_end, time.max)
|
||||
|
||||
calendar = employee.resource_calendar_id or employee.company_id.resource_calendar_id
|
||||
holidays = request.env["resource.calendar.leaves"].sudo().search([
|
||||
("date_from", "<=", end_dt),
|
||||
("date_to", ">=", start_dt),
|
||||
("company_id", "in", [False, employee.company_id.id]),
|
||||
("resource_id", "=", False), # Only company/public holidays
|
||||
], order="date_from asc")
|
||||
return holidays.filtered(lambda holiday: (
|
||||
(not holiday.resource_id or holiday.resource_id == employee.resource_id)
|
||||
and (not holiday.calendar_id or holiday.calendar_id == calendar)
|
||||
))
|
||||
if calendar:
|
||||
holidays = holidays.filtered(
|
||||
lambda h: not h.calendar_id or h.calendar_id == calendar
|
||||
)
|
||||
return holidays
|
||||
|
||||
def _attendance_calendar(self, month_start, month_end, attendances, leaves, public_holidays, calendar_view="monthly"):
|
||||
if calendar_view == "yearly":
|
||||
|
|
@ -229,14 +245,14 @@ class HrmsEmployeeDashboard(http.Controller):
|
|||
if outside_period:
|
||||
status = "empty"
|
||||
show_metrics = False
|
||||
elif is_leave:
|
||||
status = "leave"
|
||||
label = _("Time Off")
|
||||
show_metrics = is_half_day_leave
|
||||
elif is_holiday:
|
||||
status = "holiday"
|
||||
label = holiday_by_day[cursor]
|
||||
show_metrics = has_worked_hours
|
||||
elif is_leave:
|
||||
status = "leave"
|
||||
label = _("Time Off")
|
||||
show_metrics = is_half_day_leave
|
||||
elif has_worked_hours:
|
||||
status = "present"
|
||||
elif is_weekend:
|
||||
|
|
@ -388,26 +404,48 @@ class HrmsEmployeeDashboard(http.Controller):
|
|||
return result
|
||||
|
||||
def _attendance_summary(self, month_start, month_end, attendances, leaves, public_holidays):
|
||||
days = self._attendance_calendar(month_start, month_end, attendances, leaves, public_holidays)
|
||||
days = self._attendance_calendar(
|
||||
month_start,
|
||||
month_end,
|
||||
attendances,
|
||||
leaves,
|
||||
public_holidays,
|
||||
)
|
||||
|
||||
counts = defaultdict(int)
|
||||
|
||||
expected_hours = 0.0
|
||||
worked_hours = 0.0
|
||||
break_hours = 0.0
|
||||
leave_hours = 0.0
|
||||
|
||||
for day in days:
|
||||
if day["outside_period"]:
|
||||
continue
|
||||
|
||||
counts[day["status"]] += 1
|
||||
expected_hours = sum(day["expected_hours"] for day in days if not day["outside_period"])
|
||||
worked_hours = sum(attendances.mapped("worked_hours"))
|
||||
break_hours = sum(day["break_hours"] for day in days if not day["outside_period"])
|
||||
leave_hours = sum(day["leave_hours"] for day in days if not day["outside_period"])
|
||||
|
||||
expected_hours += day["expected_hours"]
|
||||
worked_hours += day["worked_hours"]
|
||||
break_hours += day["break_hours"]
|
||||
leave_hours += day["leave_hours"]
|
||||
|
||||
remaining_hours = expected_hours - worked_hours - leave_hours
|
||||
|
||||
return {
|
||||
"present": counts["present"],
|
||||
"absent": counts["absent"],
|
||||
"leave": counts["leave"],
|
||||
"holiday": counts["holiday"],
|
||||
|
||||
"hours": round(worked_hours, 2),
|
||||
|
||||
"worked_hours": round(worked_hours, 2),
|
||||
"break_hours": round(break_hours, 2),
|
||||
"expected_hours": round(expected_hours, 2),
|
||||
"leave_hours": round(leave_hours, 2),
|
||||
"remaining_hours": round(remaining_hours, 2),
|
||||
|
||||
"worked_display": self._format_hours(worked_hours),
|
||||
"break_display": self._format_hours(break_hours),
|
||||
"expected_display": self._format_hours(expected_hours),
|
||||
|
|
|
|||
|
|
@ -473,6 +473,9 @@
|
|||
}
|
||||
|
||||
.hrms-leave-tile span {
|
||||
display: inline-block;
|
||||
min-width: 64px;
|
||||
margin-right: 10px;
|
||||
color: #64748b;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
|
@ -482,6 +485,91 @@
|
|||
color: #0f172a;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.hrms-leave-graph {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.hrms-leave-graph-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(140px, 190px) minmax(180px, 1fr);
|
||||
gap: 10px 14px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hrms-leave-graph-label strong {
|
||||
display: block;
|
||||
color: #0f172a;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.hrms-leave-graph-label span {
|
||||
color: #64748b;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.hrms-leave-bar {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 18px;
|
||||
overflow: hidden;
|
||||
border-radius: 8px;
|
||||
background: #e5e7eb;
|
||||
}
|
||||
|
||||
.hrms-leave-bar span {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.hrms-leave-bar-remaining {
|
||||
background: #16a34a;
|
||||
}
|
||||
|
||||
.hrms-leave-bar-taken {
|
||||
background: #2563eb;
|
||||
}
|
||||
|
||||
.hrms-leave-bar-planned {
|
||||
background: #f59e0b;
|
||||
}
|
||||
|
||||
.hrms-leave-legend {
|
||||
grid-column: 2;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px 14px;
|
||||
color: #64748b;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.hrms-leave-legend span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.hrms-leave-legend i {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.hrms-leave-legend i.remaining {
|
||||
background: #16a34a;
|
||||
}
|
||||
|
||||
.hrms-leave-legend i.taken {
|
||||
background: #2563eb;
|
||||
}
|
||||
|
||||
.hrms-leave-legend i.planned {
|
||||
background: #f59e0b;
|
||||
}
|
||||
|
||||
.hrms-leave-legend b {
|
||||
color: #0f172a;
|
||||
}
|
||||
.hrms-two-charts {
|
||||
display: grid;
|
||||
grid-template-columns: 1.3fr 0.7fr;
|
||||
|
|
@ -553,10 +641,15 @@
|
|||
|
||||
.hrms-employee-grid,
|
||||
.hrms-grid,
|
||||
.hrms-leave-graph-row,
|
||||
.hrms-two-charts {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.hrms-leave-legend {
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
.hrms-panel.wide {
|
||||
grid-column: span 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@ class HrmsEmployeeDashboard extends Component {
|
|||
return;
|
||||
}
|
||||
this.state.data = response;
|
||||
// setTimeout(() => this.initCharts(), 100);
|
||||
const expenses = response.expenses || {};
|
||||
const total =
|
||||
(expenses.series || []).reduce(
|
||||
|
|
@ -76,11 +75,7 @@ class HrmsEmployeeDashboard extends Component {
|
|||
0
|
||||
);
|
||||
this.state.expenseCount = total;
|
||||
if (this.state.expenseCount > 0) {
|
||||
setTimeout(() => this.initCharts(), 100);
|
||||
} else {
|
||||
this.destroyCharts();
|
||||
}
|
||||
setTimeout(() => this.initCharts(), 100);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.state.error = "Unable to load employee dashboard.";
|
||||
|
|
@ -134,17 +129,18 @@ class HrmsEmployeeDashboard extends Component {
|
|||
}
|
||||
|
||||
renderLeaveChart(leaves) {
|
||||
const allocatedLeaves = leaves.filter((leave) => leave.requires_allocation === "yes");
|
||||
this.chartBase("#hrmsLeaveChart", {
|
||||
chart: { type: "bar" },
|
||||
series: [
|
||||
{ name: "Remaining", data: leaves.map((leave) => leave.remaining) },
|
||||
{ name: "Taken", data: leaves.map((leave) => leave.taken) },
|
||||
{ name: "Planned", data: leaves.map((leave) => leave.planned) },
|
||||
{ name: "Remaining", data: allocatedLeaves.map((leave) => Number(leave.remaining || 0)) },
|
||||
{ name: "Taken", data: allocatedLeaves.map((leave) => Number(leave.taken || 0)) },
|
||||
{ name: "Planned", data: allocatedLeaves.map((leave) => Number(leave.planned || 0)) },
|
||||
],
|
||||
colors: ["#16a34a", "#2563eb", "#f59e0b"],
|
||||
plotOptions: { bar: { borderRadius: 4, columnWidth: "50%" } },
|
||||
dataLabels: { enabled: false },
|
||||
xaxis: { categories: leaves.map((leave) => leave.name), labels: { rotate: -25, trim: true } },
|
||||
xaxis: { categories: allocatedLeaves.map((leave) => leave.name), labels: { rotate: -25, trim: true } },
|
||||
yaxis: { min: 0, forceNiceScale: true },
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -172,11 +172,32 @@
|
|||
<strong t-esc="leave.name"/>
|
||||
<div>
|
||||
<span><b t-esc="leave.remaining"/> Balance</span>
|
||||
<span><b t-esc="leave.taken"/> Taken</span>
|
||||
<span><b t-esc="leave.planned"/> Confirmed</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hrms-leave-graph">
|
||||
<div t-foreach="state.data.leave_balances" t-as="leave" t-key="leave.id + '-graph'">
|
||||
<div t-if="leave.requires_allocation === 'yes'" class="hrms-leave-graph-row">
|
||||
<div class="hrms-leave-graph-label">
|
||||
<strong t-esc="leave.name"/>
|
||||
<span><t t-esc="leave.allocated"/> allocated</span>
|
||||
</div>
|
||||
<div class="hrms-leave-bar" role="img" t-att-aria-label="leave.name + ': ' + leave.remaining + ' balance, ' + leave.taken + ' taken, ' + leave.planned + ' confirmed'">
|
||||
<span class="hrms-leave-bar-remaining" t-att-style="'width: ' + leave.remaining_percent + '%'"/>
|
||||
<span class="hrms-leave-bar-taken" t-att-style="'width: ' + leave.taken_percent + '%'"/>
|
||||
<span class="hrms-leave-bar-planned" t-att-style="'width: ' + leave.planned_percent + '%'"/>
|
||||
</div>
|
||||
<div class="hrms-leave-legend">
|
||||
<span><i class="remaining"/>Balance <b t-esc="leave.remaining"/></span>
|
||||
<span><i class="taken"/>Taken <b t-esc="leave.taken"/></span>
|
||||
<span><i class="planned"/>Confirmed <b t-esc="leave.planned"/></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="hrmsLeaveChart"/>
|
||||
</section>
|
||||
|
||||
<section class="hrms-panel">
|
||||
|
|
|
|||
Loading…
Reference in New Issue