feat(banking_hours): added a function to add or take banked_hours
This commit is contained in:
parent
41efccac17
commit
70575e1772
|
|
@ -1,4 +1,4 @@
|
||||||
import { BankedHoursService } from "./services/banking-hours.service.service";
|
import { BankedHoursService } from "./services/banking-hours.service";
|
||||||
import { SickLeaveService } from "./services/sick-leave.service";
|
import { SickLeaveService } from "./services/sick-leave.service";
|
||||||
import { OvertimeService } from "./services/overtime.service";
|
import { OvertimeService } from "./services/overtime.service";
|
||||||
import { VacationService } from "./services/vacation.service";
|
import { VacationService } from "./services/vacation.service";
|
||||||
|
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
// import { Injectable } from "@nestjs/common";
|
|
||||||
// import { Result } from "src/common/errors/result-error.factory";
|
|
||||||
// import { PrismaService } from "src/prisma/prisma.service";
|
|
||||||
|
|
||||||
// @Injectable()
|
|
||||||
// export class BankedHoursService {
|
|
||||||
|
|
||||||
// constructor(private readonly prisma: PrismaService) { }
|
|
||||||
|
|
||||||
// //manage shifts with bank_code.type BANKING
|
|
||||||
// bankingHours = async (employee_id: number, hours: number): Promise<Result<number, string>> => {
|
|
||||||
// if (hours <= 0) return { success: false, error: 'INVALID_BANKING_HOURS' };
|
|
||||||
|
|
||||||
// try {
|
|
||||||
// const result = await this.prisma.$transaction(async (tx) => {
|
|
||||||
// const employee = await this.prisma.employees.findUnique({
|
|
||||||
// where: { id: employee_id },
|
|
||||||
// select: {
|
|
||||||
// id: true,
|
|
||||||
// paid_time_off: {
|
|
||||||
// select: {
|
|
||||||
// banked_hours: true
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// if (!employee) {
|
|
||||||
// return { success: false, error: 'EMPLOYEE_NOT_FOUND' } as Result<number, string>
|
|
||||||
// }
|
|
||||||
// if (!employee.paid_time_off) {
|
|
||||||
// return { success: false, error: 'VACATION_HOURS_BANK_NOT_FOUND' } as Result<number, string>
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const new_balance = await tx.paidTimeOff.update({
|
|
||||||
// where: { employee_id: employee.id },
|
|
||||||
// data: {
|
|
||||||
// banked_hours: { increment: hours },
|
|
||||||
// },
|
|
||||||
// select: {
|
|
||||||
// banked_hours: true,
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// return { success: true, data: new_balance };
|
|
||||||
// });
|
|
||||||
|
|
||||||
// return result;
|
|
||||||
// } catch (error) {
|
|
||||||
// return { success: false, error: 'INVALID_BANKING_SHIFT' };
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// //manage shifts with bank_code.type WITHDRAW_BANKED
|
|
||||||
// withdrawBankedHours = async (employee_id: number, asked_hours: number): Promise<Result<number, string>> => {
|
|
||||||
// if (asked_hours <= 0) return { success: false, error: 'INVALID_WITHDRAW_BANKED' };
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BankedHoursService {
|
||||||
|
|
||||||
|
constructor(private readonly prisma: PrismaService) { }
|
||||||
|
|
||||||
|
//manage shifts with bank_code.type BANKING
|
||||||
|
manageBankingHours = async (employee_id: number, asked_hours: number, type: string): Promise<Result<number, string>> => {
|
||||||
|
if (asked_hours <= 0) return { success: false, error: 'INVALID_BANKING_HOURS' };
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await this.prisma.$transaction(async (tx) => {
|
||||||
|
const employee = await this.prisma.employees.findUnique({
|
||||||
|
where: { id: employee_id },
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
paid_time_off: {
|
||||||
|
select: {
|
||||||
|
banked_hours: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!employee) {
|
||||||
|
return { success: false, error: 'EMPLOYEE_NOT_FOUND' } as Result<number, string>;
|
||||||
|
}
|
||||||
|
if (!employee.paid_time_off) {
|
||||||
|
return { success: false, error: 'BANKING_HOURS_BANK_NOT_FOUND' } as Result<number, string>;
|
||||||
|
}
|
||||||
|
const banked_hours = (employee.paid_time_off.banked_hours).toNumber();
|
||||||
|
if (banked_hours <= 0) {
|
||||||
|
return { success: false, error: 'EMPTY_BANKED_HOURS' } as Result<number, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'BANKING') {
|
||||||
|
const new_balance = await tx.paidTimeOff.update({
|
||||||
|
where: { employee_id: employee.id },
|
||||||
|
data: { banked_hours: { increment: asked_hours } },
|
||||||
|
select: { banked_hours: true },
|
||||||
|
});
|
||||||
|
return { success: true, data: (new_balance.banked_hours).toNumber() } as Result<number, string>;
|
||||||
|
|
||||||
|
} else if (type === 'WITHDRAW_BANKED') {
|
||||||
|
if (asked_hours > banked_hours) {
|
||||||
|
return { success: true, data: banked_hours } as Result<number, string>;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
await tx.paidTimeOff.update({
|
||||||
|
where: { employee_id: employee.id },
|
||||||
|
data: { banked_hours: { decrement: asked_hours } },
|
||||||
|
select: { banked_hours: true },
|
||||||
|
});
|
||||||
|
return { success: true, data: asked_hours } as Result<number, string>;
|
||||||
|
} else {
|
||||||
|
return { success: false, error: 'INVALID_SHIFT_TYPE' } as Result<number, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return { success: false, error: 'INVALID_BANKING_SHIFT' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -53,7 +53,7 @@ export class SickLeaveService {
|
||||||
|
|
||||||
if (!updated_pto.success) return { success: updated_pto.success, error: updated_pto.error }
|
if (!updated_pto.success) return { success: updated_pto.success, error: updated_pto.error }
|
||||||
}
|
}
|
||||||
|
|
||||||
const year_difference = today.getFullYear() - (pto_details!.last_updated.getFullYear() ?? today.getFullYear());
|
const year_difference = today.getFullYear() - (pto_details!.last_updated.getFullYear() ?? today.getFullYear());
|
||||||
const months_since_last_update = (today.getMonth() + year_difference * 12) - pto_details!.last_updated.getMonth();
|
const months_since_last_update = (today.getMonth() + year_difference * 12) - pto_details!.last_updated.getMonth();
|
||||||
|
|
||||||
|
|
@ -61,7 +61,7 @@ export class SickLeaveService {
|
||||||
const updated_pto = await this.addHoursToPTO(months_since_last_update * 8, employee.id, today);
|
const updated_pto = await this.addHoursToPTO(months_since_last_update * 8, employee.id, today);
|
||||||
|
|
||||||
if (updated_pto.success) {
|
if (updated_pto.success) {
|
||||||
return {success: true, data: true}
|
return { success: true, data: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,68 +111,114 @@ export class SickLeaveService {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// switch employeeId for email
|
takeSickLeaveHours = async (employee_id: number, asked_hours: number): Promise<Result<number, string>> => {
|
||||||
async calculateSickLeavePay(
|
if (asked_hours <= 0) return { success: false, error: 'INVALID_BANKING_HOURS' };
|
||||||
employee_id: number,
|
console.log('also got here')
|
||||||
reference_date: Date,
|
try {
|
||||||
days_requested: number,
|
const result = await this.prisma.$transaction(async (tx) => {
|
||||||
hours_per_day: number,
|
const employee = await this.prisma.employees.findUnique({
|
||||||
modifier: number,
|
where: { id: employee_id },
|
||||||
): Promise<Result<number, string>> {
|
select: {
|
||||||
if (days_requested <= 0 || hours_per_day <= 0 || modifier <= 0) {
|
id: true,
|
||||||
return { success: true, data: 0 };
|
paid_time_off: {
|
||||||
|
select: {
|
||||||
|
banked_hours: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!employee) {
|
||||||
|
return { success: false, error: 'EMPLOYEE_NOT_FOUND' } as Result<number, string>;
|
||||||
|
}
|
||||||
|
if (!employee.paid_time_off) {
|
||||||
|
return { success: false, error: 'SICK_HOURS_BANK_NOT_FOUND' } as Result<number, string>;
|
||||||
|
}
|
||||||
|
const sick_bank = (employee.paid_time_off.banked_hours).toNumber();
|
||||||
|
if (sick_bank <= 0) return { success: false, error: 'EMPTY_SICK_HOURS_BANK' } as Result<number, string>;
|
||||||
|
|
||||||
|
if (asked_hours > sick_bank) {
|
||||||
|
return { success: true, data: sick_bank } as Result<number, string>;
|
||||||
|
} else {
|
||||||
|
await tx.paidTimeOff.update({
|
||||||
|
where: { employee_id: employee.id },
|
||||||
|
data: { banked_hours: { decrement: asked_hours } },
|
||||||
|
select: { banked_hours: true },
|
||||||
|
});
|
||||||
|
return { success: true, data: asked_hours } as Result<number, string>;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return { success: false, error: 'INVALID_BANKING_SHIFT' };
|
||||||
}
|
}
|
||||||
|
|
||||||
//sets the year to jan 1st to dec 31st
|
|
||||||
const period_start = getYearStart(reference_date);
|
|
||||||
const period_end = reference_date;
|
|
||||||
|
|
||||||
//fetches all shifts of a selected employee
|
|
||||||
const shifts = await this.prisma.shifts.findMany({
|
|
||||||
where: {
|
|
||||||
timesheet: { employee_id: employee_id },
|
|
||||||
date: { gte: period_start, lte: period_end },
|
|
||||||
},
|
|
||||||
select: { date: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
//count the amount of worked days
|
|
||||||
const worked_dates = new Set(
|
|
||||||
shifts.map((shift) => shift.date.toISOString().slice(0, 10)),
|
|
||||||
);
|
|
||||||
const days_worked = worked_dates.size;
|
|
||||||
|
|
||||||
//less than 30 worked days returns 0
|
|
||||||
if (days_worked < 30) {
|
|
||||||
return { success: true, data: 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
//default 3 days allowed after 30 worked days
|
|
||||||
let acquired_days = 3;
|
|
||||||
|
|
||||||
//identify the date of the 30th worked day
|
|
||||||
const ordered_dates = Array.from(worked_dates).sort();
|
|
||||||
const threshold_date = new Date(ordered_dates[29]); // index 29 is the 30th day
|
|
||||||
|
|
||||||
//calculate each completed month, starting the 1st of the next month
|
|
||||||
const first_bonus_date = new Date(
|
|
||||||
threshold_date.getFullYear(),
|
|
||||||
threshold_date.getMonth() + 1,
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
let months =
|
|
||||||
(period_end.getFullYear() - first_bonus_date.getFullYear()) * 12 +
|
|
||||||
(period_end.getMonth() - first_bonus_date.getMonth()) +
|
|
||||||
1;
|
|
||||||
if (months < 0) months = 0;
|
|
||||||
acquired_days += months;
|
|
||||||
|
|
||||||
//cap of 10 days
|
|
||||||
if (acquired_days > 10) acquired_days = 10;
|
|
||||||
|
|
||||||
const payable_days = Math.min(acquired_days, days_requested);
|
|
||||||
const raw_hours = payable_days * hours_per_day * modifier;
|
|
||||||
const rounded = roundToQuarterHour(raw_hours);
|
|
||||||
return { success: true, data: rounded };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//LEAVE REQUEST FUNCTION - DEPRECATED
|
||||||
|
// async calculateSickLeavePay(
|
||||||
|
// employee_id: number,
|
||||||
|
// reference_date: Date,
|
||||||
|
// days_requested: number,
|
||||||
|
// hours_per_day: number,
|
||||||
|
// modifier: number,
|
||||||
|
// ): Promise<Result<number, string>> {
|
||||||
|
// if (days_requested <= 0 || hours_per_day <= 0 || modifier <= 0) {
|
||||||
|
// return { success: true, data: 0 };
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //sets the year to jan 1st to dec 31st
|
||||||
|
// const period_start = getYearStart(reference_date);
|
||||||
|
// const period_end = reference_date;
|
||||||
|
|
||||||
|
// //fetches all shifts of a selected employee
|
||||||
|
// const shifts = await this.prisma.shifts.findMany({
|
||||||
|
// where: {
|
||||||
|
// timesheet: { employee_id: employee_id },
|
||||||
|
// date: { gte: period_start, lte: period_end },
|
||||||
|
// },
|
||||||
|
// select: { date: true },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// //count the amount of worked days
|
||||||
|
// const worked_dates = new Set(
|
||||||
|
// shifts.map((shift) => shift.date.toISOString().slice(0, 10)),
|
||||||
|
// );
|
||||||
|
// const days_worked = worked_dates.size;
|
||||||
|
|
||||||
|
// //less than 30 worked days returns 0
|
||||||
|
// if (days_worked < 30) {
|
||||||
|
// return { success: true, data: 0 };
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //default 3 days allowed after 30 worked days
|
||||||
|
// let acquired_days = 3;
|
||||||
|
|
||||||
|
// //identify the date of the 30th worked day
|
||||||
|
// const ordered_dates = Array.from(worked_dates).sort();
|
||||||
|
// const threshold_date = new Date(ordered_dates[29]); // index 29 is the 30th day
|
||||||
|
|
||||||
|
// //calculate each completed month, starting the 1st of the next month
|
||||||
|
// const first_bonus_date = new Date(
|
||||||
|
// threshold_date.getFullYear(),
|
||||||
|
// threshold_date.getMonth() + 1,
|
||||||
|
// 1,
|
||||||
|
// );
|
||||||
|
// let months =
|
||||||
|
// (period_end.getFullYear() - first_bonus_date.getFullYear()) * 12 +
|
||||||
|
// (period_end.getMonth() - first_bonus_date.getMonth()) +
|
||||||
|
// 1;
|
||||||
|
// if (months < 0) months = 0;
|
||||||
|
// acquired_days += months;
|
||||||
|
|
||||||
|
// //cap of 10 days
|
||||||
|
// if (acquired_days > 10) acquired_days = 10;
|
||||||
|
|
||||||
|
// const payable_days = Math.min(acquired_days, days_requested);
|
||||||
|
// const raw_hours = payable_days * hours_per_day * modifier;
|
||||||
|
// const rounded = roundToQuarterHour(raw_hours);
|
||||||
|
// return { success: true, data: rounded };
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,16 +84,17 @@ export class VacationService {
|
||||||
select: { paid_time_off: { select: { vacation_hours: true } } },
|
select: { paid_time_off: { select: { vacation_hours: true } } },
|
||||||
});
|
});
|
||||||
if (!employee) {
|
if (!employee) {
|
||||||
return { success: false, error: 'EMPLOYEE_NOT_FOUND' } as Result<number, string>
|
return { success: false, error: 'EMPLOYEE_NOT_FOUND' } as Result<number, string>;
|
||||||
}
|
}
|
||||||
if (!employee.paid_time_off) {
|
if (!employee.paid_time_off) {
|
||||||
return { success: false, error: 'VACATION_HOURS_BANK_NOT_FOUND' } as Result<number, string>
|
return { success: false, error: 'VACATION_HOURS_BANK_NOT_FOUND' } as Result<number, string>;
|
||||||
}
|
}
|
||||||
const vacation_bank = employee.paid_time_off.vacation_hours.toNumber()
|
const vacation_bank = employee.paid_time_off.vacation_hours.toNumber()
|
||||||
|
if (vacation_bank <= 0) return { success: false, error: 'EMPTY_VACATION_BANK' } as Result<number, string>;
|
||||||
|
|
||||||
//check if remaining hours are less than asked and return maximum available hours
|
//check if remaining hours are less than asked and return maximum available hours
|
||||||
if (asked_hours > vacation_bank) {
|
if (asked_hours > vacation_bank) {
|
||||||
return { success: true, data: vacation_bank } as Result<number, string>
|
return { success: true, data: vacation_bank } as Result<number, string>;
|
||||||
} else {
|
} else {
|
||||||
//update vacation_bank
|
//update vacation_bank
|
||||||
await tx.paidTimeOff.update({
|
await tx.paidTimeOff.update({
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ import { SchedulePresetsApplyService } from "src/time-and-attendance/schedule-pr
|
||||||
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
||||||
import { ShiftsCreateService } from "src/time-and-attendance/shifts/services/shifts-create.service";
|
import { ShiftsCreateService } from "src/time-and-attendance/shifts/services/shifts-create.service";
|
||||||
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
|
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
|
||||||
|
import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service";
|
||||||
|
import { BankedHoursService } from "src/time-and-attendance/domains/services/banking-hours.service";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -26,6 +28,8 @@ import { VacationService } from "src/time-and-attendance/domains/services/vacati
|
||||||
ShiftsCreateService,
|
ShiftsCreateService,
|
||||||
BankCodesResolver,
|
BankCodesResolver,
|
||||||
VacationService,
|
VacationService,
|
||||||
|
SickLeaveService,
|
||||||
|
BankedHoursService,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
SchedulePresetsGetService,
|
SchedulePresetsGetService,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ import { Result } from "src/common/errors/result-error.factory";
|
||||||
import { toStringFromHHmm, toStringFromDate, toDateFromString, overlaps, toDateFromHHmm, computeHours } from "src/common/utils/date-utils";
|
import { toStringFromHHmm, toStringFromDate, toDateFromString, overlaps, toDateFromHHmm, computeHours } from "src/common/utils/date-utils";
|
||||||
import { ShiftDto } from "src/time-and-attendance/shifts/shift.dto";
|
import { ShiftDto } from "src/time-and-attendance/shifts/shift.dto";
|
||||||
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
|
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
|
||||||
|
import { BankedHoursService } from "src/time-and-attendance/domains/services/banking-hours.service";
|
||||||
|
import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ShiftsCreateService {
|
export class ShiftsCreateService {
|
||||||
|
|
@ -16,6 +18,8 @@ export class ShiftsCreateService {
|
||||||
private readonly emailResolver: EmailToIdResolver,
|
private readonly emailResolver: EmailToIdResolver,
|
||||||
private readonly typeResolver: BankCodesResolver,
|
private readonly typeResolver: BankCodesResolver,
|
||||||
private readonly vacationService: VacationService,
|
private readonly vacationService: VacationService,
|
||||||
|
private readonly bankingService: BankedHoursService,
|
||||||
|
private readonly sickService: SickLeaveService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
|
|
@ -95,15 +99,33 @@ export class ShiftsCreateService {
|
||||||
return { success: false, error: `SHIFT_OVERLAP` };
|
return { success: false, error: `SHIFT_OVERLAP` };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//api call to validate available hours in vacation_bank and ajust end_time accordingly
|
//api call to validate available hours in vacation_bank and ajust end_time accordingly
|
||||||
if (dto.type === 'VACATION') {
|
if (dto.type === 'VACATION') {
|
||||||
const asked_hours = computeHours(toDateFromString(dto.start_time), toDateFromString(dto.end_time));
|
const asked_hours = computeHours(toDateFromHHmm(dto.start_time), toDateFromHHmm(dto.end_time));
|
||||||
const vacation_shift = await this.vacationService.manageVacationHoursBank(employee_id, asked_hours)
|
const vacation_shift = await this.vacationService.manageVacationHoursBank(employee_id, asked_hours)
|
||||||
if (!vacation_shift.success) return { success: false, error: vacation_shift.error };
|
if (!vacation_shift.success) return { success: false, error: vacation_shift.error };
|
||||||
dto.end_time = this.addHourstoDateString(dto.start_time, vacation_shift.data);
|
dto.end_time = this.addHourstoDateString(dto.start_time, vacation_shift.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
//ADD HERE THE LOGICS TO CHECK FOR AVAILABLE BANK TYPE "PAID_BANKED_HOUR" AND BANKING_HOUR
|
//api call to validate available hours in banked_hours and ajust end_time accordingly
|
||||||
|
const banking_types: string[] = ['BANKING', 'WITHDRAW_BANKED'];
|
||||||
|
if (banking_types.includes(dto.type)) {
|
||||||
|
const hours = computeHours(toDateFromHHmm(dto.start_time), toDateFromHHmm(dto.end_time));
|
||||||
|
const banking_shift = await this.bankingService.manageBankingHours(employee_id, hours, dto.type);
|
||||||
|
if (!banking_shift.success) return { success: false, error: banking_shift.error };
|
||||||
|
dto.end_time = this.addHourstoDateString(dto.start_time, banking_shift.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
//api call to validate available hours in sick_hours and ajust end_time accordingly
|
||||||
|
if (dto.type === 'SICK') {
|
||||||
|
console.log('got here')
|
||||||
|
const hours = computeHours(toDateFromHHmm(dto.start_time), toDateFromHHmm(dto.end_time));
|
||||||
|
const sick_hours = await this.sickService.takeSickLeaveHours(employee_id, hours);
|
||||||
|
if (!sick_hours.success) return { success: false, error: sick_hours.error };
|
||||||
|
console.log(sick_hours.data)
|
||||||
|
dto.end_time = this.addHourstoDateString(dto.start_time, sick_hours.data);
|
||||||
|
}
|
||||||
|
|
||||||
//sends data for creation of a shift in db
|
//sends data for creation of a shift in db
|
||||||
const created_shift = await this.prisma.shifts.create({
|
const created_shift = await this.prisma.shifts.create({
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { Result } from "src/common/errors/result-error.factory";
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
|
import { computeHours } from "src/common/utils/date-utils";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -9,15 +10,64 @@ export class ShiftsDeleteService {
|
||||||
// DELETE
|
// DELETE
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
//finds shifts using shit_ids
|
//finds shifts using shit_ids
|
||||||
|
//ajust paid-time-off banks
|
||||||
//blocs deletion if approved
|
//blocs deletion if approved
|
||||||
async deleteShift(shift_id: number): Promise<Result<number, string>> {
|
async deleteShift(shift_id: number): Promise<Result<number, string>> {
|
||||||
try {
|
try {
|
||||||
return await this.prisma.$transaction(async (tx) => {
|
return await this.prisma.$transaction(async (tx) => {
|
||||||
|
const paid_time_off_types: string[] = ['SICK', 'VACATION', 'BANKING', 'WITHDRAW_BANKED'];
|
||||||
const shift = await tx.shifts.findUnique({
|
const shift = await tx.shifts.findUnique({
|
||||||
where: { id: shift_id },
|
where: { id: shift_id },
|
||||||
select: { id: true, date: true, timesheet_id: true },
|
select: {
|
||||||
|
id: true,
|
||||||
|
date: true,
|
||||||
|
start_time: true,
|
||||||
|
end_time: true,
|
||||||
|
timesheet: true,
|
||||||
|
bank_code: { select: { type: true } }
|
||||||
|
},
|
||||||
});
|
});
|
||||||
if (!shift) return { success: false, error: `SHIFT_NOT_FOUND` };
|
if (!shift) return { success: false, error: `SHIFT_NOT_FOUND` };
|
||||||
|
const ajusted_hours = computeHours(shift.start_time, shift.end_time);
|
||||||
|
|
||||||
|
//manage banked types, ensures update of amount of hours in bank is ajusted when a paid_time_off shift is deleted
|
||||||
|
if (paid_time_off_types.includes(shift.bank_code.type)) {
|
||||||
|
switch (shift.bank_code.type) {
|
||||||
|
case 'SICK':
|
||||||
|
await this.prisma.paidTimeOff.update({
|
||||||
|
where: { employee_id: shift.timesheet.employee_id },
|
||||||
|
data: {
|
||||||
|
sick_hours: { increment: ajusted_hours },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'VACATION':
|
||||||
|
await this.prisma.paidTimeOff.update({
|
||||||
|
where: { employee_id: shift.timesheet.employee_id },
|
||||||
|
data: {
|
||||||
|
vacation_hours: { increment: ajusted_hours },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'WITHDRAW_BANKED':
|
||||||
|
await this.prisma.paidTimeOff.update({
|
||||||
|
where: { employee_id: shift.timesheet.employee_id },
|
||||||
|
data: {
|
||||||
|
banked_hours: { decrement: ajusted_hours },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
case 'BANKING':
|
||||||
|
await this.prisma.paidTimeOff.update({
|
||||||
|
where: { employee_id: shift.timesheet.employee_id },
|
||||||
|
data: {
|
||||||
|
banked_hours: { increment: ajusted_hours },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await tx.shifts.delete({ where: { id: shift_id } });
|
await tx.shifts.delete({ where: { id: shift_id } });
|
||||||
return { success: true, data: shift.id };
|
return { success: true, data: shift.id };
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { toDateFromString, toStringFromHHmm, toStringFromDate, toDateFromHHmm, overlaps } from "src/common/utils/date-utils";
|
import { toDateFromString, toStringFromHHmm, toStringFromDate, toDateFromHHmm, overlaps, computeHours } from "src/common/utils/date-utils";
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { Result } from "src/common/errors/result-error.factory";
|
||||||
import { shift_select } from "src/time-and-attendance/utils/selects.utils";
|
import { shift_select } from "src/time-and-attendance/utils/selects.utils";
|
||||||
import { Normalized } from "src/time-and-attendance/utils/type.utils";
|
import { Normalized } from "src/time-and-attendance/utils/type.utils";
|
||||||
import { ShiftDto } from "src/time-and-attendance/shifts/shift.dto";
|
import { ShiftDto } from "src/time-and-attendance/shifts/shift.dto";
|
||||||
|
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ShiftsUpdateService {
|
export class ShiftsUpdateService {
|
||||||
|
|
@ -16,6 +17,7 @@ export class ShiftsUpdateService {
|
||||||
private readonly prisma: PrismaService,
|
private readonly prisma: PrismaService,
|
||||||
private readonly typeResolver: BankCodesResolver,
|
private readonly typeResolver: BankCodesResolver,
|
||||||
private readonly timesheetResolver: EmployeeTimesheetResolver,
|
private readonly timesheetResolver: EmployeeTimesheetResolver,
|
||||||
|
private readonly emailResolver: EmailToIdResolver,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
async updateOneOrManyShifts(shifts: ShiftDto[], email: string): Promise<Result<boolean, string>> {
|
async updateOneOrManyShifts(shifts: ShiftDto[], email: string): Promise<Result<boolean, string>> {
|
||||||
|
|
@ -60,8 +62,11 @@ export class ShiftsUpdateService {
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
async updateShift(dto: ShiftDto, email: string): Promise<Result<ShiftDto, string>> {
|
async updateShift(dto: ShiftDto, email: string): Promise<Result<ShiftDto, string>> {
|
||||||
try {
|
try {
|
||||||
|
// const paid_time_off_types: string[] = ['SICK', 'VACATION', 'BANKING', 'WITHDRAW_BANKED'];
|
||||||
const timesheet = await this.timesheetResolver.findTimesheetIdByEmail(email, toDateFromString(dto.date));
|
const timesheet = await this.timesheetResolver.findTimesheetIdByEmail(email, toDateFromString(dto.date));
|
||||||
if (!timesheet.success) return { success: false, error: timesheet.error }
|
if (!timesheet.success) return { success: false, error: timesheet.error };
|
||||||
|
const employee = await this.emailResolver.findIdByEmail(email);
|
||||||
|
if (!employee.success) return { success: false, error: employee.error };
|
||||||
|
|
||||||
//finds original shift
|
//finds original shift
|
||||||
const original = await this.prisma.shifts.findFirst({
|
const original = await this.prisma.shifts.findFirst({
|
||||||
|
|
@ -78,6 +83,51 @@ export class ShiftsUpdateService {
|
||||||
//finds bank_code_id using the type
|
//finds bank_code_id using the type
|
||||||
const bank_code = await this.typeResolver.findBankCodeIDByType(dto.type);
|
const bank_code = await this.typeResolver.findBankCodeIDByType(dto.type);
|
||||||
if (!bank_code.success) return { success: false, error: bank_code.error };
|
if (!bank_code.success) return { success: false, error: bank_code.error };
|
||||||
|
// const original_hours = computeHours(original.start_time, original.end_time);
|
||||||
|
// const ajusted_hours = computeHours(toDateFromHHmm(dto.start_time), toDateFromHHmm(dto.end_time));
|
||||||
|
|
||||||
|
// if (paid_time_off_types.includes(dto.type)) {
|
||||||
|
// switch (dto.type) {
|
||||||
|
// case 'SICK':
|
||||||
|
// if (ajusted_hours < original_hours){
|
||||||
|
// const diff_hours = original_hours - ajusted_hours;
|
||||||
|
// await this.prisma.paidTimeOff.update({
|
||||||
|
// where: { employee_id: employee.data },
|
||||||
|
// data: {
|
||||||
|
// sick_hours: { decrement: diff_hours },
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// } else {
|
||||||
|
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// case 'VACATION':
|
||||||
|
// await this.prisma.paidTimeOff.update({
|
||||||
|
// where: { employee_id: shift.timesheet.employee_id },
|
||||||
|
// data: {
|
||||||
|
// vacation_hours: { increment: ajusted_hours },
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// break;
|
||||||
|
// case 'WITHDRAW_BANKED':
|
||||||
|
// await this.prisma.paidTimeOff.update({
|
||||||
|
// where: { employee_id: shift.timesheet.employee_id },
|
||||||
|
// data: {
|
||||||
|
// banked_hours: { decrement: ajusted_hours },
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// case 'BANKING':
|
||||||
|
// await this.prisma.paidTimeOff.update({
|
||||||
|
// where: { employee_id: shift.timesheet.employee_id },
|
||||||
|
// data: {
|
||||||
|
// banked_hours: { increment: ajusted_hours },
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
// break;
|
||||||
|
// default:
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
//updates sent to DB
|
//updates sent to DB
|
||||||
const updated = await this.prisma.shifts.update({
|
const updated = await this.prisma.shifts.update({
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,11 @@ import { ShiftsCreateService } from 'src/time-and-attendance/shifts/services/shi
|
||||||
import { ShiftsDeleteService } from 'src/time-and-attendance/shifts/services/shifts-delete.service';
|
import { ShiftsDeleteService } from 'src/time-and-attendance/shifts/services/shifts-delete.service';
|
||||||
import { ShiftsUpdateService } from 'src/time-and-attendance/shifts/services/shifts-update-delete.service';
|
import { ShiftsUpdateService } from 'src/time-and-attendance/shifts/services/shifts-update-delete.service';
|
||||||
import { VacationService } from 'src/time-and-attendance/domains/services/vacation.service';
|
import { VacationService } from 'src/time-and-attendance/domains/services/vacation.service';
|
||||||
|
import { BankedHoursService } from 'src/time-and-attendance/domains/services/banking-hours.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
controllers: [ShiftController],
|
controllers: [ShiftController],
|
||||||
providers: [ShiftsCreateService, ShiftsUpdateService, ShiftsDeleteService, VacationService],
|
providers: [ShiftsCreateService, ShiftsUpdateService, ShiftsDeleteService, VacationService, BankedHoursService],
|
||||||
exports: [ShiftsCreateService, ShiftsUpdateService, ShiftsDeleteService],
|
exports: [ShiftsCreateService, ShiftsUpdateService, ShiftsDeleteService],
|
||||||
})
|
})
|
||||||
export class ShiftsModule { }
|
export class ShiftsModule { }
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ import { SchedulePresetsCreateService } from "src/time-and-attendance/schedule-p
|
||||||
import { SchedulePresetsApplyService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-apply.service";
|
import { SchedulePresetsApplyService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-apply.service";
|
||||||
import { CsvGeneratorService } from "src/time-and-attendance/exports/services/csv-builder.service";
|
import { CsvGeneratorService } from "src/time-and-attendance/exports/services/csv-builder.service";
|
||||||
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
|
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
|
||||||
|
import { BankedHoursService } from "src/time-and-attendance/domains/services/banking-hours.service";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -82,6 +83,7 @@ import { VacationService } from "src/time-and-attendance/domains/services/vacati
|
||||||
CsvExportService,
|
CsvExportService,
|
||||||
CsvGeneratorService,
|
CsvGeneratorService,
|
||||||
VacationService,
|
VacationService,
|
||||||
|
BankedHoursService,
|
||||||
],
|
],
|
||||||
exports: [TimesheetApprovalService],
|
exports: [TimesheetApprovalService],
|
||||||
}) export class TimeAndAttendanceModule { };
|
}) export class TimeAndAttendanceModule { };
|
||||||
Loading…
Reference in New Issue
Block a user