feat(csv and expenses): added employee_email to update expense and added call to holiday service to display the right amount of hour paid for holiday shifts inside csv(needs more work. see next commit)

This commit is contained in:
Matthieu Haineault 2026-01-07 08:42:52 -05:00
parent 70aefdab73
commit e10a7da1e4
6 changed files with 66 additions and 15 deletions

View File

@ -1,4 +1,4 @@
import { Controller, Post, Param, Body, Patch, Delete, Req, UnauthorizedException } from "@nestjs/common"; import { Controller, Post, Param, Body, Patch, Delete, Req, UnauthorizedException, Query } from "@nestjs/common";
import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service"; import { ExpenseUpsertService } from "src/time-and-attendance/expenses/services/expense-upsert.service";
import { ExpenseDto } from "src/time-and-attendance/expenses/expense-create.dto"; import { ExpenseDto } from "src/time-and-attendance/expenses/expense-create.dto";
import { Result } from "src/common/errors/result-error.factory"; import { Result } from "src/common/errors/result-error.factory";
@ -19,8 +19,12 @@ export class ExpenseController {
@Patch('update') @Patch('update')
@ModuleAccessAllowed(ModulesEnum.timesheets) @ModuleAccessAllowed(ModulesEnum.timesheets)
update(@Body() dto: ExpenseDto, @Access('email') email: string): Promise<Result<ExpenseDto, string>> { update(
return this.upsert_service.updateExpense(dto, email); @Body() dto: ExpenseDto,
@Access('email') email: string,
@Query('employee_email') employee_email?: string,
): Promise<Result<ExpenseDto, string>> {
return this.upsert_service.updateExpense(dto, email, employee_email);
} }
@Delete('delete/:expense_id') @Delete('delete/:expense_id')

View File

@ -72,10 +72,11 @@ export class ExpenseUpsertService {
//_________________________________________________________________ //_________________________________________________________________
// UPDATE // UPDATE
//_________________________________________________________________ //_________________________________________________________________
async updateExpense(dto: ExpenseDto, email: string): Promise<Result<ExpenseDto, string>> { async updateExpense(dto: ExpenseDto, email: string, employee_email?: string): Promise<Result<ExpenseDto, string>> {
try { try {
const account_email = employee_email ?? email;
//fetch employee_id using req.user.email //fetch employee_id using req.user.email
const employee_id = await this.emailResolver.findIdByEmail(email); const employee_id = await this.emailResolver.findIdByEmail(account_email);
if (!employee_id.success) return { success: false, error: employee_id.error }; if (!employee_id.success) return { success: false, error: employee_id.error };
//normalize string , date format and parse numbers //normalize string , date format and parse numbers
const normed_expense = await this.normalizeAndParseExpenseDto(dto); const normed_expense = await this.normalizeAndParseExpenseDto(dto);

View File

@ -3,9 +3,17 @@ import { CsvExportController } from "./csv-exports.controller";
import { CsvExportService } from "./services/csv-exports.service"; import { CsvExportService } from "./services/csv-exports.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 { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service";
import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service";
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
@Module({ @Module({
providers: [CsvExportService, CsvGeneratorService, OvertimeService], providers: [
CsvExportService,
CsvGeneratorService,
OvertimeService,
HolidayService,
EmailToIdResolver,
],
controllers: [CsvExportController], controllers: [CsvExportController],
}) })
export class CsvExportModule { } export class CsvExportModule { }

View File

@ -1,3 +1,4 @@
import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service";
import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service";
import { CsvRow, InternalCsvRow } from "src/time-and-attendance/exports/export-csv-options.dto"; import { CsvRow, InternalCsvRow } from "src/time-and-attendance/exports/export-csv-options.dto";
@ -20,6 +21,33 @@ export const consolidateRowHoursAndAmountByType = (rows: InternalCsvRow[]): Inte
return Array.from(map.values()); return Array.from(map.values());
} }
export const applyHolidayRequalifications = async (
rows: InternalCsvRow[],
holiday_service: HolidayService,
): Promise<InternalCsvRow[]> => {
const result: InternalCsvRow[] = [];
for (const row of rows) {
if (row.bank_code !== 'G104') {
result.push(row);
continue;
}
if (!row.holiday_date || !row.email) {
result.push(row);
continue;
}
const calculated = await holiday_service.calculateHolidayPay(row.email, new Date(row.holiday_date), 1);
if (!calculated.success) {
result.push({ ...row, quantity_hours: 0 });
continue;
}
result.push({ ...row, quantity_hours: calculated.data });
}
return result;
}
export const applyOvertimeRequalifications = async ( export const applyOvertimeRequalifications = async (
consolidated_rows: InternalCsvRow[], consolidated_rows: InternalCsvRow[],
overtime_service: OvertimeService, overtime_service: OvertimeService,

View File

@ -58,7 +58,7 @@ export interface CsvRow {
holiday_date?: string; holiday_date?: string;
} }
export type InternalCsvRow = CsvRow & { timesheet_id: number; shift_date: Date; } export type InternalCsvRow = CsvRow & { timesheet_id: number; shift_date: Date; email: string; }
export type Filters = { export type Filters = {

View File

@ -2,14 +2,16 @@ import { PrismaService } from "src/prisma/prisma.service";
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common"; import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
import { Filters, CsvRow, InternalCsvRow } from "src/time-and-attendance/exports/export-csv-options.dto"; import { Filters, CsvRow, InternalCsvRow } from "src/time-and-attendance/exports/export-csv-options.dto";
import { computeHours } from "src/common/utils/date-utils"; import { computeHours } from "src/common/utils/date-utils";
import { applyOvertimeRequalifications, computeWeekNumber, consolidateRowHoursAndAmountByType, formatDate, resolveCompanyCodes } from "src/time-and-attendance/exports/csv-exports.utils"; import { applyHolidayRequalifications, applyOvertimeRequalifications, computeWeekNumber, consolidateRowHoursAndAmountByType, formatDate, resolveCompanyCodes } from "src/time-and-attendance/exports/csv-exports.utils";
import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service";
import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service";
@Injectable() @Injectable()
export class CsvExportService { export class CsvExportService {
constructor( constructor(
private readonly prisma: PrismaService, private readonly prisma: PrismaService,
private readonly overtime_service: OvertimeService, private readonly overtime_service: OvertimeService,
private readonly holiday_service: HolidayService,
) { } ) { }
async collectTransaction( async collectTransaction(
@ -67,7 +69,7 @@ export class CsvExportService {
select: { select: {
company_code: true, company_code: true,
external_payroll_id: true, external_payroll_id: true,
user: { select: { first_name: true, last_name: true } }, user: { select: { first_name: true, last_name: true, email: true } },
} }
}, },
} }
@ -98,7 +100,7 @@ export class CsvExportService {
select: { select: {
company_code: true, company_code: true,
external_payroll_id: true, external_payroll_id: true,
user: { select: { first_name: true, last_name: true } }, user: { select: { first_name: true, last_name: true, email: true } },
} }
}, },
} }
@ -129,7 +131,7 @@ export class CsvExportService {
select: { select: {
company_code: true, company_code: true,
external_payroll_id: true, external_payroll_id: true,
user: { select: { first_name: true, last_name: true } }, user: { select: { first_name: true, last_name: true, email: true } },
} }
}, },
} }
@ -158,7 +160,7 @@ export class CsvExportService {
select: { select: {
company_code: true, company_code: true,
external_payroll_id: true, external_payroll_id: true,
user: { select: { first_name: true, last_name: true } }, user: { select: { first_name: true, last_name: true, email: true } },
} }
}, },
} }
@ -181,6 +183,7 @@ export class CsvExportService {
company_code: employee.company_code, company_code: employee.company_code,
external_payroll_id: employee.external_payroll_id, external_payroll_id: employee.external_payroll_id,
timesheet_id: shift.timesheet.id, timesheet_id: shift.timesheet.id,
email: '',
shift_date: shift.date, shift_date: shift.date,
full_name: `${employee.user.first_name} ${employee.user.last_name}`, full_name: `${employee.user.first_name} ${employee.user.last_name}`,
bank_code: shift.bank_code?.bank_code ?? '', bank_code: shift.bank_code?.bank_code ?? '',
@ -203,6 +206,7 @@ export class CsvExportService {
company_code: employee.company_code, company_code: employee.company_code,
external_payroll_id: employee.external_payroll_id, external_payroll_id: employee.external_payroll_id,
timesheet_id: expense.timesheet.id, timesheet_id: expense.timesheet.id,
email: '',
full_name: `${employee.user.first_name} ${employee.user.last_name}`, full_name: `${employee.user.first_name} ${employee.user.last_name}`,
bank_code: expense.bank_code?.bank_code ?? '', bank_code: expense.bank_code?.bank_code ?? '',
quantity_hours: undefined, quantity_hours: undefined,
@ -210,7 +214,7 @@ export class CsvExportService {
week_number: week, week_number: week,
pay_date: formatDate(end), pay_date: formatDate(end),
holiday_date: '', holiday_date: '',
shift_date : expense.date, shift_date: expense.date,
}) })
} }
@ -225,9 +229,15 @@ export class CsvExportService {
return 0; return 0;
}); });
const consolidated_rows = consolidateRowHoursAndAmountByType(rows);
const requalified_rows = await applyOvertimeRequalifications(consolidated_rows, this.overtime_service); //groups hours by bank_code
const consolidated_rows = await consolidateRowHoursAndAmountByType(rows);
//requalifies the real amount paid for holidays
const holiday_rows = await applyHolidayRequalifications(consolidated_rows, this.holiday_service);
//requalifies regular hours into overtime when needed
const requalified_rows = await applyOvertimeRequalifications(holiday_rows, this.overtime_service);
return requalified_rows; return requalified_rows;
} }