fix(timesheets): ajusted type of function mapOneTimesheet

This commit is contained in:
Matthieu Haineault 2025-11-12 14:13:04 -05:00
parent c59844560a
commit 14c00522db
3 changed files with 110 additions and 116 deletions

View File

@ -1,6 +1,5 @@
import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils"; import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils";
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
// import { NormalizedExpense } from "src/time-and-attendance/utils/type.utils";
import { expense_select } from "src/time-and-attendance/utils/selects.utils"; import { expense_select } from "src/time-and-attendance/utils/selects.utils";
import { PrismaService } from "src/prisma/prisma.service"; import { PrismaService } from "src/prisma/prisma.service";
import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto"; import { GetExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-get.dto";

View File

@ -1,5 +1,3 @@
import { BankCodeEntity } from "src/modules/bank-codes/dtos/bank-code-entity";
export class ShiftEntity { export class ShiftEntity {
id: number; id: number;
timesheet_id: number; timesheet_id: number;
@ -10,5 +8,4 @@ export class ShiftEntity {
is_remote: boolean; is_remote: boolean;
is_approved: boolean; is_approved: boolean;
comment?: string | null ; comment?: string | null ;
bank_code?: BankCodeEntity;
} }

View File

@ -5,7 +5,7 @@ import { PrismaService } from "src/prisma/prisma.service";
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils"; import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
import { Timesheet, TimesheetEntity, Timesheets } from "src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto"; import { Timesheet, TimesheetEntity, Timesheets } from "src/time-and-attendance/time-tracker/timesheets/dtos/timesheet.dto";
import { Result } from "src/common/errors/result-error.factory"; import { Result } from "src/common/errors/result-error.factory";
import { Users } from "@prisma/client"; import { Prisma, Users } from "@prisma/client";
import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-entity.dto"; import { ShiftEntity } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-entity.dto";
import { ExpenseEntity } from "src/time-and-attendance/expenses/dtos/expense-entity.dto"; import { ExpenseEntity } from "src/time-and-attendance/expenses/dtos/expense-entity.dto";
@ -77,11 +77,11 @@ export class GetTimesheetsOverviewService {
//builds employee full name //builds employee full name
const user = employee.user; const user = employee.user;
const employee_fullname = `${user.first_name} ${user.last_name}`.trim(); const employee_fullname = `${user.first_name} ${user.last_name}`.trim();
//maps all timesheet's infos //maps all timesheet's infos
const timesheets = await Promise.all(rows.map((timesheet) => this.mapOneTimesheet(timesheet))); const timesheets = await Promise.all(rows.map((timesheet) => this.mapOneTimesheet(timesheet)));
if(!timesheets) return { success: false, error: 'an error occured during the mapping of a timesheet'} if (!timesheets) return { success: false, error: 'an error occured during the mapping of a timesheet' }
return { success: true, data: { employee_fullname, timesheets } }; return { success: true, data: { employee_fullname, timesheets } };
} catch (error) { } catch (error) {
@ -105,112 +105,118 @@ export class GetTimesheetsOverviewService {
}); });
} }
private async mapOneTimesheet(timesheet: TimesheetResult): Promise<Timesheet> { private async mapOneTimesheet(timesheet: Prisma.TimesheetsGetPayload<{
//converts string to UTC date format include: {
const start = toDateFromString(timesheet.start_date); employee: { include: { user } },
const day_dates = sevenDaysFrom(start); shift: { include: { bank_code } },
expense: { include: { bank_code } },
}
}>): Promise<Timesheet> {
//converts string to UTC date format
const start = toDateFromString(timesheet.start_date);
const day_dates = sevenDaysFrom(start);
//map of shifts by days //map of shifts by days
const shifts_by_date = new Map<string, any[]>(); const shifts_by_date = new Map<string, any[]>();
for (const shift of timesheet.shift) { for (const shift of timesheet.shift) {
const date_string = toStringFromDate(shift.date); const date_string = toStringFromDate(shift.date);
const arr = shifts_by_date.get(date_string) ?? []; const arr = shifts_by_date.get(date_string) ?? [];
arr.push(shift); arr.push(shift);
shifts_by_date.set(date_string, arr); shifts_by_date.set(date_string, arr);
}
//map of expenses by days
const expenses_by_date = new Map<string, any[]>();
for (const expense of timesheet.expense) {
const date_string = toStringFromDate(expense.date);
const arr = expenses_by_date.get(date_string) ?? [];
arr.push(expense);
expenses_by_date.set(date_string, arr);
}
//weekly totals
const weekly_hours: TotalHours[] = [emptyHours()];
const weekly_expenses: TotalExpenses[] = [emptyExpenses()];
//map of days
const days = day_dates.map((date) => {
const date_iso = toStringFromDate(date);
const shifts_source = shifts_by_date.get(date_iso) ?? [];
const expenses_source = expenses_by_date.get(date_iso) ?? [];
//inner map of shifts
const shifts = shifts_source.map((shift) => ({
timesheet_id: shift.timesheet_id,
date: toStringFromDate(shift.date),
start_time: toHHmmFromDate(shift.start_time),
end_time: toHHmmFromDate(shift.end_time),
type: shift.bank_code?.type ?? '',
is_remote: shift.is_remote ?? false,
is_approved: shift.is_approved ?? false,
id: shift.id ?? null,
comment: shift.comment ?? null,
}));
//inner map of expenses
const expenses = expenses_source.map((expense) => ({
date: toStringFromDate(expense.date),
amount: expense.amount != null ? Number(expense.amount) : undefined,
mileage: expense.mileage != null ? Number(expense.mileage) : undefined,
expense_id: expense.id ?? null,
attachment: expense.attachment_record ? String(expense.attachment_record.id) : undefined,
is_approved: expense.is_approved ?? false,
comment: expense.comment ?? '',
supervisor_comment: expense.supervisor_comment,
type: expense.type,
}));
//daily totals
const daily_hours = [emptyHours()];
const daily_expenses = [emptyExpenses()];
//totals by shift types
for (const shift of shifts_source) {
const hours = diffOfHours(shift.start_time, shift.end_time);
const subgroup = hoursSubGroupFromBankCode(shift.bank_code);
daily_hours[0][subgroup] += hours;
weekly_hours[0][subgroup] += hours;
} }
//map of expenses by days
const expenses_by_date = new Map<string, any[]>(); //totals by expense types
for (const expense of timesheet.expense) { for (const expense of expenses_source) {
const date_string = toStringFromDate(expense.date); const subgroup = expenseSubgroupFromBankCode(expense.bank_code);
const arr = expenses_by_date.get(date_string) ?? []; if (subgroup === 'mileage') {
arr.push(expense); const mileage = num(expense.mileage);
expenses_by_date.set(date_string, arr); daily_expenses[0].mileage += mileage;
weekly_expenses[0].mileage += mileage;
} else if (subgroup === 'per_diem') {
const amount = num(expense.amount);
daily_expenses[0].per_diem += amount;
weekly_expenses[0].per_diem += amount;
} else if (subgroup === 'on_call') {
const amount = num(expense.amount);
daily_expenses[0].on_call += amount;
weekly_expenses[0].on_call += amount;
} else {
const amount = num(expense.amount);
daily_expenses[0].expenses += amount;
weekly_expenses[0].expenses += amount;
}
} }
//weekly totals
const weekly_hours: TotalHours[] = [emptyHours()];
const weekly_expenses: TotalExpenses[] = [emptyExpenses()];
//map of days
const days = day_dates.map((date) => {
const date_iso = toStringFromDate(date);
const shifts_source = shifts_by_date.get(date_iso) ?? [];
const expenses_source = expenses_by_date.get(date_iso) ?? [];
//inner map of shifts
const shifts = shifts_source.map((shift) => ({
timesheet_id: shift.timesheet_id,
date: toStringFromDate(shift.date),
start_time: toHHmmFromDate(shift.start_time),
end_time: toHHmmFromDate(shift.end_time),
type: shift.bank_code?.type ?? '',
is_remote: shift.is_remote ?? false,
is_approved: shift.is_approved ?? false,
id: shift.id ?? null,
comment: shift.comment ?? null,
}));
//inner map of expenses
const expenses = expenses_source.map((expense) => ({
date: toStringFromDate(expense.date),
amount: expense.amount != null ? Number(expense.amount) : undefined,
mileage: expense.mileage != null ? Number(expense.mileage) : undefined,
expense_id: expense.id ?? null,
attachment: expense.attachment_record ? String(expense.attachment_record.id) : undefined,
is_approved: expense.is_approved ?? false,
comment: expense.comment ?? '',
supervisor_comment: expense.supervisor_comment,
type: expense.type,
}));
//daily totals
const daily_hours = [emptyHours()];
const daily_expenses = [emptyExpenses()];
//totals by shift types
for (const shift of shifts_source) {
const hours = diffOfHours(shift.start_time, shift.end_time);
const subgroup = hoursSubGroupFromBankCode(shift.bank_code);
daily_hours[0][subgroup] += hours;
weekly_hours[0][subgroup] += hours;
}
//totals by expense types
for (const expense of expenses_source) {
const subgroup = expenseSubgroupFromBankCode(expense.bank_code);
if (subgroup === 'mileage') {
const mileage = num(expense.mileage);
daily_expenses[0].mileage += mileage;
weekly_expenses[0].mileage += mileage;
} else if (subgroup === 'per_diem') {
const amount = num(expense.amount);
daily_expenses[0].per_diem += amount;
weekly_expenses[0].per_diem += amount;
} else if (subgroup === 'on_call') {
const amount = num(expense.amount);
daily_expenses[0].on_call += amount;
weekly_expenses[0].on_call += amount;
} else {
const amount = num(expense.amount);
daily_expenses[0].expenses += amount;
weekly_expenses[0].expenses += amount;
}
}
return {
date: date_iso,
shifts,
expenses,
daily_hours,
daily_expenses,
};
});
return { return {
timesheet_id: timesheet.id, date: date_iso,
is_approved: timesheet.is_approved ?? false, shifts,
days, expenses,
weekly_hours, daily_hours,
weekly_expenses, daily_expenses,
}; };
});
return {
timesheet_id: timesheet.id,
is_approved: timesheet.is_approved ?? false,
days,
weekly_hours,
weekly_expenses,
};
} }
private ensureTimesheet = async (employee_id: number, start_date: Date | string) => { private ensureTimesheet = async (employee_id: number, start_date: Date | string) => {
@ -246,14 +252,6 @@ export class GetTimesheetsOverviewService {
} }
} }
interface TimesheetResult extends TimesheetEntity {
employee: {
user: Users
},
shift: ShiftEntity[],
expense: ExpenseEntity[],
}
//filled array with default values //filled array with default values
const emptyHours = (): TotalHours => { return { regular: 0, evening: 0, emergency: 0, overtime: 0, vacation: 0, holiday: 0, sick: 0 } }; const emptyHours = (): TotalHours => { return { regular: 0, evening: 0, emergency: 0, overtime: 0, vacation: 0, holiday: 0, sick: 0 } };
const emptyExpenses = (): TotalExpenses => { return { expenses: 0, per_diem: 0, on_call: 0, mileage: 0 } }; const emptyExpenses = (): TotalExpenses => { return { expenses: 0, per_diem: 0, on_call: 0, mileage: 0 } };