Merge branch 'main' of git.targo.ca:Targo/targo_backend

This commit is contained in:
Matthieu Haineault 2026-01-09 12:01:11 -05:00
commit 25c9e0a673
3 changed files with 214 additions and 184 deletions

View File

@ -1,5 +1,6 @@
import { IsArray, IsBoolean, IsDateString, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPositive, IsString } from 'class-validator'; import { IsArray, IsBoolean, IsDateString, IsEmail, IsInt, IsNotEmpty, IsOptional, IsPositive, IsString } from 'class-validator';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
import { PaidTimeOffDto } from 'src/time-and-attendance/domains/paid-time-off.dto';
export class EmployeeDetailedDto { export class EmployeeDetailedDto {
@IsString() @IsNotEmpty() first_name: string; @IsString() @IsNotEmpty() first_name: string;
@ -15,6 +16,7 @@ export class EmployeeDetailedDto {
@IsInt() daily_expected_hours: number; @IsInt() daily_expected_hours: number;
@IsDateString() @IsOptional() last_work_day?: string | null; @IsDateString() @IsOptional() last_work_day?: string | null;
@IsString() @IsOptional() residence?: string; @IsString() @IsOptional() residence?: string;
@IsOptional() @Type(() => PaidTimeOffDto) paid_time_off?: Partial<PaidTimeOffDto> | null;
@IsInt() @IsPositive() @Type(() => Number) external_payroll_id: number; @IsInt() @IsPositive() @Type(() => Number) external_payroll_id: number;
@IsArray() @IsString({ each: true }) user_module_access: string[]; @IsArray() @IsString({ each: true }) user_module_access: string[];
@IsInt() @IsOptional() preset_id?: number; @IsInt() @IsOptional() preset_id?: number;

View File

@ -12,201 +12,218 @@ import { toKeysFromBoolean } from "src/common/utils/boolean-utils";
@Injectable() @Injectable()
export class EmployeesGetService { export class EmployeesGetService {
constructor( constructor(
private readonly prisma: PrismaService, private readonly prisma: PrismaService,
private readonly emailResolver: EmailToIdResolver, private readonly emailResolver: EmailToIdResolver,
) { } ) { }
async findListEmployees(): Promise<Result<Partial<EmployeeDetailedDto>[], string>> { async findListEmployees(): Promise<Result<Partial<EmployeeDetailedDto>[], string>> {
const employee_list = await this.prisma.employees.findMany({ const employee_list = await this.prisma.employees.findMany({
select: { select: {
user: { user: {
select: { select: {
first_name: true, first_name: true,
last_name: true, last_name: true,
email: true, email: true,
}, phone_number: true,
}, },
supervisor: { },
select: { supervisor: {
user: { select: {
select: { user: {
first_name: true, select: {
last_name: true, first_name: true,
}, last_name: true,
}, },
}, },
}, },
is_supervisor: true, },
daily_expected_hours: true, is_supervisor: true,
job_title: true, daily_expected_hours: true,
company_code: true, job_title: true,
external_payroll_id: true, company_code: true,
first_work_day: true, external_payroll_id: true,
last_work_day: true, first_work_day: true,
schedule_preset_id: true, last_work_day: true,
schedule_preset_id: true,
} }
}).then(rows => rows.map(r => ({ }).then(rows => rows.map(r => ({
first_name: r.user.first_name, first_name: r.user.first_name,
last_name: r.user.last_name, last_name: r.user.last_name,
email: r.user.email, email: r.user.email,
company_name: toStringFromCompanyCode(r.company_code), phone_number: r.user.phone_number,
job_title: r.job_title ?? '', company_name: toStringFromCompanyCode(r.company_code),
daily_expected_hours: r.daily_expected_hours, job_title: r.job_title ?? '',
external_payroll_id: r.external_payroll_id, daily_expected_hours: r.daily_expected_hours,
employee_full_name: `${r.user.first_name} ${r.user.last_name}`, external_payroll_id: r.external_payroll_id,
is_supervisor: r.is_supervisor, employee_full_name: `${r.user.first_name} ${r.user.last_name}`,
supervisor_full_name: `${r.supervisor?.user.first_name} ${r.supervisor?.user.last_name}`, is_supervisor: r.is_supervisor,
first_work_day: toStringFromDate(r.first_work_day), supervisor_full_name: `${r.supervisor?.user.first_name} ${r.supervisor?.user.last_name}`,
last_work_day: r.last_work_day ? toStringFromDate(r.last_work_day) : null, first_work_day: toStringFromDate(r.first_work_day),
preset_id: r.schedule_preset_id ?? undefined, last_work_day: r.last_work_day ? toStringFromDate(r.last_work_day) : null,
}))); preset_id: r.schedule_preset_id ?? undefined,
return { success: true, data: employee_list }; })));
}; return { success: true, data: employee_list };
};
async findOwnProfile(email: string): Promise<Result<Partial<EmployeeDetailedDto>, string>> { async findOwnProfile(email: string): Promise<Result<Partial<EmployeeDetailedDto>, string>> {
const user_id = await this.emailResolver.resolveUserIdWithEmail(email); const user_id = await this.emailResolver.resolveUserIdWithEmail(email);
if (!user_id.success) return { success: false, error: 'INVALID_USER' }; if (!user_id.success) return { success: false, error: 'INVALID_USER' };
const existing_profile = await this.prisma.employees.findUnique({ const existing_profile = await this.prisma.employees.findUnique({
where: { user_id: user_id.data }, where: { user_id: user_id.data },
select: { select: {
user: { user: {
select: { select: {
first_name: true, first_name: true,
last_name: true, last_name: true,
email: true, email: true,
phone_number: true, phone_number: true,
residence: true, residence: true,
}, },
}, },
first_work_day: true, first_work_day: true,
company_code: true, company_code: true,
job_title: true, job_title: true,
external_payroll_id: true, external_payroll_id: true,
is_supervisor: true, paid_time_off: true,
schedule_preset_id: true, is_supervisor: true,
daily_expected_hours: true, schedule_preset_id: true,
supervisor: { daily_expected_hours: true,
select: { supervisor: {
id: true, user: { select: {
select: { id: true, user: {
first_name: true, select: {
last_name: true, first_name: true,
}, last_name: true,
}, },
}, },
}, },
}, },
}); },
if (!existing_profile) return { success: false, error: 'EMPLOYEE_NOT_FOUND' }; });
if (!existing_profile) return { success: false, error: 'EMPLOYEE_NOT_FOUND' };
const company_name = toStringFromCompanyCode(existing_profile.company_code); const company_name = toStringFromCompanyCode(existing_profile.company_code);
return { return {
success: true, data: { success: true, data: {
first_name: existing_profile.user.first_name, first_name: existing_profile.user.first_name,
last_name: existing_profile.user.last_name, last_name: existing_profile.user.last_name,
email: existing_profile.user.email, email: existing_profile.user.email,
supervisor_full_name: `${existing_profile.supervisor?.user.first_name} ${existing_profile.supervisor?.user.last_name}`, supervisor_full_name: `${existing_profile.supervisor?.user.first_name} ${existing_profile.supervisor?.user.last_name}`,
company_name: company_name, company_name: company_name,
daily_expected_hours: existing_profile.daily_expected_hours, daily_expected_hours: existing_profile.daily_expected_hours,
job_title: existing_profile.job_title ?? '', job_title: existing_profile.job_title ?? '',
external_payroll_id: existing_profile.external_payroll_id, external_payroll_id: existing_profile.external_payroll_id,
is_supervisor: existing_profile.is_supervisor, paid_time_off: {
phone_number: existing_profile.user.phone_number, vacation_hours: existing_profile.paid_time_off?.vacation_hours.toNumber() ?? 0,
residence: existing_profile.user.phone_number, banked_hours: existing_profile.paid_time_off?.banked_hours.toNumber() ?? 0,
first_work_day: toStringFromDate(existing_profile.first_work_day), last_updated: existing_profile.paid_time_off?.last_updated.toISOString().slice(0, 10) ?? '',
preset_id: existing_profile.schedule_preset_id ?? undefined, },
}, is_supervisor: existing_profile.is_supervisor,
}; phone_number: existing_profile.user.phone_number,
}; residence: existing_profile.user.phone_number,
first_work_day: toStringFromDate(existing_profile.first_work_day),
preset_id: existing_profile.schedule_preset_id ?? undefined,
},
};
};
async findOneDetailedProfile(email: string, employee_email?: string): Promise<Result<EmployeeDetailedDto, string>> { async findOneDetailedProfile(email: string, employee_email?: string): Promise<Result<EmployeeDetailedDto, string>> {
const account_email = employee_email ?? email; const account_email = employee_email ?? email;
const user_id = await this.emailResolver.resolveUserIdWithEmail(account_email); const user_id = await this.emailResolver.resolveUserIdWithEmail(account_email);
if (!user_id.success) return { success: false, error: 'INVALID_USER' }; if (!user_id.success) return { success: false, error: 'INVALID_USER' };
const employee = await this.prisma.employees.findUnique({ const employee = await this.prisma.employees.findUnique({
where: { user_id: user_id.data }, where: { user_id: user_id.data },
select: { select: {
user: { user: {
select: { select: {
first_name: true, first_name: true,
last_name: true, last_name: true,
email: true, email: true,
phone_number: true, phone_number: true,
residence: true, residence: true,
user_module_access: { user_module_access: {
select: { select: {
dashboard: true, dashboard: true,
employee_list: true, employee_list: true,
employee_management: true, employee_management: true,
personal_profile: true, personal_profile: true,
timesheets: true, timesheets: true,
timesheets_approval: true, timesheets_approval: true,
}, },
}, },
}, },
}, },
supervisor: { supervisor: {
select: { select: {
user: { user: {
select: { select: {
first_name: true, first_name: true,
last_name: true, last_name: true,
}, },
}, },
}, },
}, },
job_title: true, job_title: true,
company_code: true, company_code: true,
first_work_day: true, first_work_day: true,
last_work_day: true, last_work_day: true,
external_payroll_id: true, external_payroll_id: true,
is_supervisor: true, paid_time_off: true,
daily_expected_hours: true, is_supervisor: true,
schedule_preset_id: true, daily_expected_hours: true,
schedule_preset: { schedule_preset_id: true,
select: { schedule_preset: {
id: true, select: {
} id: true,
} }
} }
}); }
if (!employee) return { success: false, error: `EMPLOYEE_NOT_FOUND` }; });
if (!employee.user) return { success: false, error: 'USER_NOT_FOUND' }; if (!employee) return { success: false, error: `EMPLOYEE_NOT_FOUND` };
if (!employee.user) return { success: false, error: 'USER_NOT_FOUND' };
let module_access_array: Modules[] = []; let module_access_array: Modules[] = [];
if (employee.user.user_module_access) { if (employee.user.user_module_access) {
module_access_array = toStringFromBoolean(employee.user.user_module_access); module_access_array = toStringFromBoolean(employee.user.user_module_access);
} }
const company_name = toStringFromCompanyCode(employee.company_code); const company_name = toStringFromCompanyCode(employee.company_code);
return { return {
success: true, success: true,
data: { data: {
first_name: employee.user.first_name, first_name: employee.user.first_name,
last_name: employee.user.last_name, last_name: employee.user.last_name,
email: employee.user.email, email: employee.user.email,
residence: employee.user.residence ?? '', residence: employee.user.residence ?? '',
phone_number: employee.user.phone_number, phone_number: employee.user.phone_number,
company_name: company_name, company_name: company_name,
is_supervisor: employee.is_supervisor ?? false, is_supervisor: employee.is_supervisor ?? false,
job_title: employee.job_title ?? '', job_title: employee.job_title ?? '',
external_payroll_id: employee.external_payroll_id, external_payroll_id: employee.external_payroll_id,
employee_full_name: `${employee.user.first_name} ${employee.user.last_name}`, paid_time_off: {
first_work_day: toStringFromDate(employee.first_work_day), id: employee.paid_time_off?.id ?? -1,
last_work_day: employee.last_work_day ? toStringFromDate(employee.last_work_day) : undefined, employee_id: employee.paid_time_off?.employee_id ?? -1,
supervisor_full_name: employee.supervisor ? `${employee.supervisor?.user.first_name} ${employee.supervisor?.user.last_name}` : '', sick_hours: employee.paid_time_off?.sick_hours.toNumber() ?? 0,
user_module_access: module_access_array, vacation_hours: employee.paid_time_off?.vacation_hours.toNumber() ?? 0,
daily_expected_hours: employee.daily_expected_hours, banked_hours: employee.paid_time_off?.banked_hours.toNumber() ?? 0,
preset_id: employee.schedule_preset_id ? employee.schedule_preset_id : undefined, last_updated: employee.paid_time_off?.last_updated.toISOString().slice(0, 10) ?? '',
}, },
}; employee_full_name: `${employee.user.first_name} ${employee.user.last_name}`,
}; first_work_day: toStringFromDate(employee.first_work_day),
last_work_day: employee.last_work_day ? toStringFromDate(employee.last_work_day) : undefined,
supervisor_full_name: employee.supervisor ? `${employee.supervisor?.user.first_name} ${employee.supervisor?.user.last_name}` : '',
user_module_access: module_access_array,
daily_expected_hours: employee.daily_expected_hours,
preset_id: employee.schedule_preset_id ? employee.schedule_preset_id : undefined,
},
};
};
} }
const createDefaultModuleAccess = (): Record<Modules, boolean> => const createDefaultModuleAccess = (): Record<Modules, boolean> =>

View File

@ -0,0 +1,11 @@
import { Type } from "class-transformer";
import { IsDateString, IsDecimal, IsInt, IsNotEmpty, IsNumber, IsOptional } from "class-validator";
export class PaidTimeOffDto {
@IsInt() @IsNotEmpty() id: number;
@IsInt() @IsNotEmpty() employee_id: number;
@IsOptional() @Type(() => Number) vacation_hours?: number;
@IsOptional() @Type(() => Number) sick_hours?: number;
@IsOptional() @Type(() => Number) banked_hours?: number;
@IsDateString() @IsOptional() last_updated: string;
}