402 lines
13 KiB
TypeScript
402 lines
13 KiB
TypeScript
import { Injectable, NotFoundException } from '@nestjs/common';
|
||
import { PrismaService } from 'src/prisma/prisma.service';
|
||
import { CreateEmployeeDto } from '../dtos/create-employee.dto';
|
||
import { UpdateEmployeeDto } from '../dtos/update-employee.dto';
|
||
import { Employees, EmployeesArchive, Users } from '@prisma/client';
|
||
import { EmployeeListItemDto } from '../dtos/list-employee.dto';
|
||
import { EmployeeProfileItemDto } from '../dtos/profil-employee.dto';
|
||
|
||
function toDateOrNull(v?: string | null): Date | null {
|
||
if (!v) return null;
|
||
const day = new Date(v);
|
||
return isNaN(day.getTime()) ? null : day;
|
||
}
|
||
function toDateOrUndefined(v?: string | null): Date | undefined {
|
||
const day = toDateOrNull(v ?? undefined);
|
||
return day === null ? undefined : day;
|
||
}
|
||
|
||
@Injectable()
|
||
export class EmployeesService {
|
||
constructor(private readonly prisma: PrismaService) {}
|
||
|
||
async create(dto: CreateEmployeeDto): Promise<Employees> {
|
||
const {
|
||
first_name,
|
||
last_name,
|
||
email,
|
||
phone_number,
|
||
residence,
|
||
external_payroll_id,
|
||
company_code,
|
||
job_title,
|
||
first_work_day,
|
||
last_work_day,
|
||
is_supervisor,
|
||
} = dto;
|
||
|
||
return this.prisma.$transaction(async (transaction) => {
|
||
const user: Users = await transaction.users.create({
|
||
data: {
|
||
first_name,
|
||
last_name,
|
||
email,
|
||
phone_number,
|
||
residence,
|
||
},
|
||
});
|
||
return transaction.employees.create({
|
||
data: {
|
||
user_id: user.id,
|
||
external_payroll_id,
|
||
company_code,
|
||
job_title,
|
||
first_work_day,
|
||
last_work_day,
|
||
is_supervisor,
|
||
},
|
||
});
|
||
});
|
||
}
|
||
|
||
findAll(): Promise<Employees[]> {
|
||
return this.prisma.employees.findMany({
|
||
include: { user: true },
|
||
});
|
||
}
|
||
|
||
findListEmployees(): Promise<EmployeeListItemDto[]> {
|
||
return this.prisma.employees.findMany({
|
||
select: {
|
||
user: {
|
||
select: {
|
||
first_name: true,
|
||
last_name: true,
|
||
email: true,
|
||
},
|
||
},
|
||
supervisor: {
|
||
select: {
|
||
user: {
|
||
select: {
|
||
first_name: true,
|
||
last_name: true,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
job_title: true,
|
||
company_code: true,
|
||
}
|
||
}).then(rows => rows.map(r => ({
|
||
first_name: r.user.first_name,
|
||
last_name: r.user.last_name,
|
||
employee_full_name: `${r.user.first_name} ${r.user.last_name}`,
|
||
email: r.user.email,
|
||
supervisor_full_name: r.supervisor ? `${r.supervisor.user.first_name} ${r.supervisor.user.last_name}` : null,
|
||
company_name: r.company_code,
|
||
job_title: r.job_title,
|
||
})),
|
||
);
|
||
}
|
||
|
||
async findOne(email: string): Promise<Employees> {
|
||
const emp = await this.prisma.employees.findFirst({
|
||
where: { user: { email } },
|
||
include: { user: true },
|
||
});
|
||
|
||
//add search for archived employees
|
||
if (!emp) {
|
||
throw new NotFoundException(`Employee with email: ${email} not found`);
|
||
}
|
||
return emp;
|
||
}
|
||
|
||
async findOneProfile(email:string): Promise<EmployeeProfileItemDto> {
|
||
const emp = await this.prisma.employees.findFirst({
|
||
where: { user: { email } },
|
||
select: {
|
||
user: {
|
||
select: {
|
||
first_name: true,
|
||
last_name: true,
|
||
email: true,
|
||
phone_number: true,
|
||
residence: true,
|
||
},
|
||
},
|
||
supervisor: {
|
||
select: {
|
||
user: {
|
||
select: {
|
||
first_name: true,
|
||
last_name: true,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
job_title: true,
|
||
company_code: true,
|
||
first_work_day: true,
|
||
last_work_day: true,
|
||
}
|
||
});
|
||
if (!emp) throw new NotFoundException(`Employee with email ${email} not found`);
|
||
|
||
return {
|
||
first_name: emp.user.first_name,
|
||
last_name: emp.user.last_name,
|
||
employee_full_name: `${emp.user.first_name} ${emp.user.last_name}`,
|
||
email: emp.user.email,
|
||
residence: emp.user.residence,
|
||
phone_number: emp.user.phone_number,
|
||
supervisor_full_name: emp.supervisor ? `${emp.supervisor.user.first_name}, ${emp.supervisor.user.last_name}` : null,
|
||
company_name: emp.company_code,
|
||
job_title: emp.job_title,
|
||
first_work_day: emp.first_work_day.toISOString().slice(0,10),
|
||
last_work_day: emp.last_work_day ? emp.last_work_day.toISOString().slice(0,10) : null,
|
||
};
|
||
}
|
||
|
||
async update(
|
||
email: string,
|
||
dto: UpdateEmployeeDto,
|
||
): Promise<Employees> {
|
||
const emp = await this.findOne(email);
|
||
|
||
const {
|
||
first_name,
|
||
last_name,
|
||
phone_number,
|
||
residence,
|
||
external_payroll_id,
|
||
company_code,
|
||
job_title,
|
||
first_work_day,
|
||
last_work_day,
|
||
is_supervisor,
|
||
email: new_email,
|
||
} = dto;
|
||
|
||
return this.prisma.$transaction(async (transaction) => {
|
||
if(
|
||
first_name !== undefined ||
|
||
last_name !== undefined ||
|
||
new_email !== undefined ||
|
||
phone_number !== undefined ||
|
||
residence !== undefined
|
||
){
|
||
await transaction.users.update({
|
||
where: { id: emp.user_id },
|
||
data: {
|
||
...(first_name !== undefined && { first_name }),
|
||
...(last_name !== undefined && { last_name }),
|
||
...(email !== undefined && { email }),
|
||
...(phone_number !== undefined && { phone_number }),
|
||
...(residence !== undefined && { residence }),
|
||
},
|
||
});
|
||
}
|
||
|
||
const updated = await transaction.employees.update({
|
||
where: { id: emp.id },
|
||
data: {
|
||
...(external_payroll_id !== undefined && { external_payroll_id }),
|
||
...(company_code !== undefined && { company_code }),
|
||
...(first_work_day !== undefined && { first_work_day }),
|
||
...(last_work_day !== undefined && { last_work_day }),
|
||
...(job_title !== undefined && { job_title }),
|
||
...(is_supervisor !== undefined && { is_supervisor }),
|
||
},
|
||
});
|
||
return updated;
|
||
});
|
||
}
|
||
|
||
|
||
async remove(email: string): Promise<Employees> {
|
||
|
||
const emp = await this.findOne(email);
|
||
|
||
return this.prisma.$transaction(async (transaction) => {
|
||
await transaction.employees.updateMany({
|
||
where: { supervisor_id: emp.id },
|
||
data: { supervisor_id: null },
|
||
});
|
||
const deleted_employee = await transaction.employees.delete({
|
||
where: {id: emp.id },
|
||
});
|
||
await transaction.users.delete({
|
||
where: { id: emp.user_id },
|
||
});
|
||
return deleted_employee;
|
||
});
|
||
}
|
||
|
||
|
||
//archivation functions ******************************************************
|
||
|
||
async patchEmployee(email: string, dto: UpdateEmployeeDto): Promise<Employees | EmployeesArchive | null> {
|
||
// 1) Tenter sur employés actifs
|
||
const active = await this.prisma.employees.findFirst({
|
||
where: { user: { email } },
|
||
include: { user: true },
|
||
});
|
||
|
||
if (active) {
|
||
// Archivage : si on reçoit un last_work_day défini et que l'employé n’est pas déjà terminé
|
||
if (dto.last_work_day !== undefined && active.last_work_day == null && dto.last_work_day !== null) {
|
||
return this.archiveOnTermination(active, dto);
|
||
}
|
||
|
||
// Sinon, update standard (split Users/Employees)
|
||
const {
|
||
first_name,
|
||
last_name,
|
||
email: new_email,
|
||
phone_number,
|
||
residence,
|
||
external_payroll_id,
|
||
company_code,
|
||
job_title,
|
||
first_work_day,
|
||
last_work_day,
|
||
supervisor_id,
|
||
is_supervisor,
|
||
} = dto as any;
|
||
|
||
const first_work_d = toDateOrUndefined(first_work_day);
|
||
const last_work_d = Object.prototype.hasOwnProperty('last_work_day')
|
||
? toDateOrNull(last_work_day ?? null)
|
||
: undefined;
|
||
|
||
await this.prisma.$transaction(async (transaction) => {
|
||
if(
|
||
first_name !== undefined ||
|
||
last_name !== undefined ||
|
||
new_email !== undefined ||
|
||
phone_number !== undefined ||
|
||
residence !== undefined
|
||
) {
|
||
await transaction.users.update({
|
||
where: { id: active.user_id },
|
||
data: {
|
||
...(first_name !== undefined ? { first_name } : {}),
|
||
...(last_name !== undefined ? { last_name } : {}),
|
||
...(email !== undefined ? { email: new_email }: {}),
|
||
...(phone_number !== undefined ? { phone_number } : {}),
|
||
...(residence !== undefined ? { residence } : {}),
|
||
},
|
||
});
|
||
|
||
}
|
||
|
||
const updated = await transaction.employees.update({
|
||
where: { id: active.id },
|
||
data: {
|
||
...(external_payroll_id !== undefined ? { external_payroll_id } : {}),
|
||
...(company_code !== undefined ? { company_code } : {}),
|
||
...(job_title !== undefined ? { job_title } : {}),
|
||
...(first_work_d !== undefined ? { first_work_day: first_work_d } : {}),
|
||
...(last_work_d !== undefined ? { last_work_day: last_work_d } : {}),
|
||
...(is_supervisor !== undefined ? { is_supervisor } : {}),
|
||
...(supervisor_id !== undefined ? { supervisor_id } : {}),
|
||
},
|
||
include: { user: true },
|
||
});
|
||
|
||
return updated;
|
||
});
|
||
|
||
return this.prisma.employees.findFirst({ where: { user: {email} } });
|
||
}
|
||
|
||
const user = await this.prisma.users.findUnique({where: {email}});
|
||
if(!user) return null;
|
||
// 2) Pas trouvé en actifs → regarder en archive (pour restauration)
|
||
const archived = await this.prisma.employeesArchive.findFirst({
|
||
where: { user_id: user.id },
|
||
include: { user: true },
|
||
});
|
||
|
||
if (archived) {
|
||
// Condition de restauration : last_work_day === null ou first_work_day fourni
|
||
const restore = dto.last_work_day === null || dto.first_work_day != null;
|
||
if (restore) {
|
||
return this.restoreEmployee(archived, dto);
|
||
}
|
||
}
|
||
// 3) Ni actif, ni archivé → 404 dans le controller
|
||
return null;
|
||
}
|
||
|
||
//transfers the employee to archive and then delete from employees table
|
||
private async archiveOnTermination(active: Employees & {user: Users }, dto: UpdateEmployeeDto): Promise<EmployeesArchive> {
|
||
const last_work_d = toDateOrNull(dto.last_work_day!);
|
||
if(!last_work_d) throw new Error('invalide last_work_day for archive');
|
||
return this.prisma.$transaction(async transaction => {
|
||
//detach crew from supervisor if employee is a supervisor
|
||
await transaction.employees.updateMany({
|
||
where: { supervisor_id: active.id },
|
||
data: { supervisor_id: null },
|
||
})
|
||
const archived = await transaction.employeesArchive.create({
|
||
data: {
|
||
employee_id: active.id,
|
||
user_id: active.user_id,
|
||
first_name: active.user.first_name,
|
||
last_name: active.user.last_name,
|
||
external_payroll_id: active.external_payroll_id,
|
||
company_code: active.company_code,
|
||
job_title: active.job_title,
|
||
first_work_day: active.first_work_day,
|
||
last_work_day: last_work_d,
|
||
supervisor_id: active.supervisor_id ?? null,
|
||
is_supervisor: active.is_supervisor,
|
||
},
|
||
include: { user: true}
|
||
});
|
||
//delete from employees table
|
||
await transaction.employees.delete({ where: { id: active.id } });
|
||
//return archived employee
|
||
return archived
|
||
});
|
||
}
|
||
|
||
//transfers the employee from archive to the employees table
|
||
private async restoreEmployee(archived: EmployeesArchive & { user:Users }, dto: UpdateEmployeeDto): Promise<Employees> {
|
||
// const first_work_d = toDateOrUndefined(dto.first_work_day);
|
||
return this.prisma.$transaction(async transaction => {
|
||
//restores the archived employee into the employees table
|
||
const restored = await transaction.employees.create({
|
||
data: {
|
||
user_id: archived.user_id,
|
||
external_payroll_id: archived.external_payroll_id,
|
||
company_code: archived.company_code,
|
||
job_title: archived.job_title,
|
||
first_work_day: archived.first_work_day,
|
||
last_work_day: null,
|
||
is_supervisor: archived.is_supervisor ?? false,
|
||
},
|
||
});
|
||
//deleting archived entry by id
|
||
await transaction.employeesArchive.delete({ where: { id: archived.id } });
|
||
|
||
//return restored employee
|
||
return restored;
|
||
});
|
||
}
|
||
|
||
//fetches all archived employees
|
||
async findAllArchived(): Promise<EmployeesArchive[]> {
|
||
return this.prisma.employeesArchive.findMany();
|
||
}
|
||
|
||
//fetches an archived employee
|
||
async findOneArchived(id: number): Promise<EmployeesArchive> {
|
||
return this.prisma.employeesArchive.findUniqueOrThrow({ where: { id } });
|
||
}
|
||
|
||
}
|