feat(timesheet-approval): add overtime calculation for overview and timesheet details
This commit is contained in:
parent
8f93c2b0f7
commit
fa64b7d919
|
|
@ -3,7 +3,6 @@ import { UserMessageDto } from 'src/chatbot/dtos/user-message.dto';
|
||||||
import { HttpService } from '@nestjs/axios';
|
import { HttpService } from '@nestjs/axios';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
import { PageContextDto } from 'src/chatbot/dtos/page-context.dto';
|
import { PageContextDto } from 'src/chatbot/dtos/page-context.dto';
|
||||||
import { UserDto } from 'src/identity-and-account/users-management/user.dto';
|
|
||||||
import { Message } from 'src/chatbot/dtos/dialog-message.dto';
|
import { Message } from 'src/chatbot/dtos/dialog-message.dto';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -19,8 +18,6 @@ export class ChatbotService {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('chatbot response: ', response);
|
|
||||||
|
|
||||||
const cleanText =
|
const cleanText =
|
||||||
Array.isArray(response.data) && response.data[0]?.output
|
Array.isArray(response.data) && response.data[0]?.output
|
||||||
? response.data[0].output
|
? response.data[0].output
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
import { Result } from "src/common/errors/result-error.factory";
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
import { computeHours, computePeriod, toDateFromString } from "src/common/utils/date-utils";
|
import { computeHours, computePeriod, sevenDaysFrom, toDateFromString, toStringFromDate } from "src/common/utils/date-utils";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { EmployeePeriodOverviewDto, Overview, PayPeriodOverviewDto } from "src/time-and-attendance/pay-period/dtos/overview-pay-period.dto";
|
import { EmployeePeriodOverviewDto, Overview, PayPeriodOverviewDto } from "src/time-and-attendance/pay-period/dtos/overview-pay-period.dto";
|
||||||
|
|
||||||
|
|
@ -62,12 +63,15 @@ export class GetOverviewService {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
is_approved: true,
|
is_approved: true,
|
||||||
|
start_date: true,
|
||||||
shift: {
|
shift: {
|
||||||
select: {
|
select: {
|
||||||
start_time: true,
|
start_time: true,
|
||||||
end_time: true,
|
end_time: true,
|
||||||
|
date: true,
|
||||||
bank_code: { select: { type: true } },
|
bank_code: { select: { type: true } },
|
||||||
},
|
},
|
||||||
|
orderBy: { date: 'asc' },
|
||||||
},
|
},
|
||||||
expense: {
|
expense: {
|
||||||
select: {
|
select: {
|
||||||
|
|
@ -91,8 +95,8 @@ export class GetOverviewService {
|
||||||
} else {
|
} else {
|
||||||
for (const employee of employee_overviews) {
|
for (const employee of employee_overviews) {
|
||||||
const record = this.createEmployeeSeeds(
|
const record = this.createEmployeeSeeds(
|
||||||
employee.user.email,
|
employee.user.email,
|
||||||
employee.user.first_name,
|
employee.user.first_name,
|
||||||
employee.user.last_name,
|
employee.user.last_name,
|
||||||
employee.supervisor?.user ?? null,
|
employee.supervisor?.user ?? null,
|
||||||
);
|
);
|
||||||
|
|
@ -109,45 +113,68 @@ export class GetOverviewService {
|
||||||
|
|
||||||
for (const employee of employee_overviews) {
|
for (const employee of employee_overviews) {
|
||||||
const record = ensure(
|
const record = ensure(
|
||||||
employee.id,
|
employee.id,
|
||||||
employee.user.first_name,
|
employee.user.first_name,
|
||||||
employee.user.last_name,
|
employee.user.last_name,
|
||||||
employee.user.email
|
employee.user.email
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const timesheet of employee.timesheet) {
|
for (const timesheet of employee.timesheet) {
|
||||||
let total_weekly_hours: number = 0;
|
let total_weekly_hours: number = 0;
|
||||||
|
let daily_hours: number = 0;
|
||||||
|
let previous_shift_date: string = '';
|
||||||
|
|
||||||
//totals by types for shifts
|
//totals by types for shifts
|
||||||
for (const shift of timesheet.shift) {
|
for (const shift of timesheet.shift) {
|
||||||
const hours = computeHours(shift.start_time, shift.end_time);
|
const hours = computeHours(shift.start_time, shift.end_time);
|
||||||
const type = (shift.bank_code?.type ?? '').toUpperCase();
|
const type = (shift.bank_code?.type ?? '').toUpperCase();
|
||||||
|
|
||||||
|
if (previous_shift_date !== toStringFromDate(shift.date)) {
|
||||||
|
previous_shift_date = toStringFromDate(shift.date);
|
||||||
|
daily_hours = 0;
|
||||||
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "EVENING": record.other_hours.evening_hours += hours;
|
case "EVENING":
|
||||||
|
if (total_weekly_hours + hours <= 40) {
|
||||||
|
record.other_hours.evening_hours += Math.min(hours, 8 - daily_hours);
|
||||||
|
record.other_hours.overtime_hours += Math.max(daily_hours + hours - 8, 0);
|
||||||
|
} else {
|
||||||
|
record.other_hours.evening_hours += Math.max(40 - total_weekly_hours, 0);
|
||||||
|
record.other_hours.overtime_hours += Math.min(total_weekly_hours + hours - 40, hours);
|
||||||
|
}
|
||||||
|
total_weekly_hours += hours;
|
||||||
|
record.total_hours += hours;
|
||||||
|
break;
|
||||||
|
case "EMERGENCY":
|
||||||
|
record.other_hours.emergency_hours += hours;
|
||||||
record.total_hours += hours;
|
record.total_hours += hours;
|
||||||
total_weekly_hours += hours;
|
total_weekly_hours += hours;
|
||||||
break;
|
break;
|
||||||
case "EMERGENCY": record.other_hours.emergency_hours += hours;
|
case "SICK":
|
||||||
record.total_hours += hours;
|
record.other_hours.sick_hours += hours;
|
||||||
total_weekly_hours += hours;
|
|
||||||
break;
|
break;
|
||||||
case "OVERTIME": record.other_hours.overtime_hours += hours;
|
case "HOLIDAY":
|
||||||
record.total_hours += hours;
|
record.other_hours.holiday_hours += hours;
|
||||||
total_weekly_hours += hours;
|
|
||||||
break;
|
|
||||||
case "SICK": record.other_hours.sick_hours += hours;
|
|
||||||
break;
|
|
||||||
case "HOLIDAY": record.other_hours.holiday_hours += hours;
|
|
||||||
record.total_hours += hours;
|
record.total_hours += hours;
|
||||||
total_weekly_hours += hours;
|
total_weekly_hours += hours;
|
||||||
break;
|
break;
|
||||||
case "VACATION": record.other_hours.vacation_hours += hours;
|
case "VACATION": record.other_hours.vacation_hours += hours;
|
||||||
break;
|
break;
|
||||||
case "REGULAR": record.regular_hours += hours;
|
case "REGULAR":
|
||||||
record.total_hours += hours;
|
if (total_weekly_hours + hours <= 40) {
|
||||||
|
record.regular_hours += Math.min(hours, 8 - daily_hours);
|
||||||
|
record.other_hours.overtime_hours += Math.max(daily_hours + hours - 8, 0);
|
||||||
|
} else {
|
||||||
|
record.regular_hours += Math.max(40 - total_weekly_hours, 0);
|
||||||
|
record.other_hours.overtime_hours += Math.min(total_weekly_hours + hours - 40, hours);
|
||||||
|
}
|
||||||
total_weekly_hours += hours;
|
total_weekly_hours += hours;
|
||||||
|
record.total_hours += hours;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
daily_hours += hours;
|
||||||
}
|
}
|
||||||
//totals by type for expenses
|
//totals by type for expenses
|
||||||
for (const expense of timesheet.expense) {
|
for (const expense of timesheet.expense) {
|
||||||
|
|
@ -170,7 +197,7 @@ export class GetOverviewService {
|
||||||
if (!record) continue;
|
if (!record) continue;
|
||||||
const timesheets = employee.timesheet;
|
const timesheets = employee.timesheet;
|
||||||
const has_data = timesheets.some(timesheet => timesheet.shift.length > 0 || timesheet.expense.length > 0);
|
const has_data = timesheets.some(timesheet => timesheet.shift.length > 0 || timesheet.expense.length > 0);
|
||||||
|
|
||||||
const cutoff_date = new Date();
|
const cutoff_date = new Date();
|
||||||
cutoff_date.setDate(cutoff_date.getDate() + 14);
|
cutoff_date.setDate(cutoff_date.getDate() + 14);
|
||||||
const is_active = employee.last_work_day ? employee.last_work_day.getTime() >= cutoff_date.getTime() : true;
|
const is_active = employee.last_work_day ? employee.last_work_day.getTime() >= cutoff_date.getTime() : true;
|
||||||
|
|
@ -198,12 +225,12 @@ export class GetOverviewService {
|
||||||
}
|
}
|
||||||
|
|
||||||
createEmployeeSeeds = (
|
createEmployeeSeeds = (
|
||||||
email: string,
|
email: string,
|
||||||
employee_first_name: string,
|
employee_first_name: string,
|
||||||
employee_last_name: string,
|
employee_last_name: string,
|
||||||
supervisor: {
|
supervisor: {
|
||||||
first_name: string;
|
first_name: string;
|
||||||
last_name:string;
|
last_name: string;
|
||||||
email: string;
|
email: string;
|
||||||
} | null = null,
|
} | null = null,
|
||||||
): EmployeePeriodOverviewDto => ({
|
): EmployeePeriodOverviewDto => ({
|
||||||
|
|
|
||||||
|
|
@ -74,10 +74,28 @@ export const mapOneTimesheet = async (timesheet: Prisma.TimesheetsGetPayload<{
|
||||||
for (const shift of shifts_source) {
|
for (const shift of shifts_source) {
|
||||||
const hours = diffOfHours(shift.start_time, shift.end_time);
|
const hours = diffOfHours(shift.start_time, shift.end_time);
|
||||||
const subgroup = hoursSubGroupFromBankCode(shift.bank_code);
|
const subgroup = hoursSubGroupFromBankCode(shift.bank_code);
|
||||||
daily_hours[subgroup] += hours;
|
const worked_weekly_hours = weekly_hours.regular + weekly_hours.emergency + weekly_hours.banking + weekly_hours.evening + weekly_hours.overtime + weekly_hours.holiday;
|
||||||
weekly_hours[subgroup] += hours;
|
|
||||||
|
if ((worked_weekly_hours + hours <= 40) && (subgroup === 'regular' || subgroup === 'evening')) {
|
||||||
|
daily_hours['overtime'] += Math.max(daily_hours[subgroup] + hours - 8, 0);
|
||||||
|
weekly_hours['overtime'] += Math.max(daily_hours[subgroup] + hours - 8, 0);
|
||||||
|
|
||||||
|
weekly_hours[subgroup] += Math.min(hours, 8 - daily_hours[subgroup]);
|
||||||
|
daily_hours[subgroup] += Math.min(hours, 8 - daily_hours[subgroup]);
|
||||||
|
} else if (subgroup === 'regular' || subgroup === 'evening') {
|
||||||
|
daily_hours[subgroup] += Math.max((40 - worked_weekly_hours), 0);
|
||||||
|
daily_hours['overtime'] += Math.min((worked_weekly_hours + hours) - 40, hours);
|
||||||
|
|
||||||
|
weekly_hours[subgroup] += Math.max((40 - worked_weekly_hours), 0);
|
||||||
|
weekly_hours['overtime'] += Math.min((worked_weekly_hours + hours) - 40, hours);
|
||||||
|
} else {
|
||||||
|
daily_hours[subgroup] += hours;
|
||||||
|
weekly_hours[subgroup] += hours;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//totals by expense types
|
//totals by expense types
|
||||||
for (const expense of expenses_source) {
|
for (const expense of expenses_source) {
|
||||||
const subgroup = expenseSubgroupFromBankCode(expense.bank_code);
|
const subgroup = expenseSubgroupFromBankCode(expense.bank_code);
|
||||||
|
|
@ -108,6 +126,9 @@ export const mapOneTimesheet = async (timesheet: Prisma.TimesheetsGetPayload<{
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
weekly_hours['overtime'] = Math.max(weekly_hours['regular'] - 40, 0);
|
||||||
|
weekly_hours['regular'] = Math.min(weekly_hours['regular'], 40);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
timesheet_id: timesheet.id,
|
timesheet_id: timesheet.id,
|
||||||
is_approved: timesheet.is_approved ?? false,
|
is_approved: timesheet.is_approved ?? false,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user