From ef969d3c42076e883a483d1a107b3ef19f63d0ad Mon Sep 17 00:00:00 2001 From: Matthieu Haineault Date: Mon, 16 Feb 2026 09:36:13 -0500 Subject: [PATCH] fix(CSV): fix csv structure to match Desjardins --- .../exports/csv-exports.utils.ts | 41 +++---- .../exports/export-csv-options.dto.ts | 2 +- .../exports/services/csv-exports.service.ts | 104 +++++++++--------- 3 files changed, 73 insertions(+), 74 deletions(-) diff --git a/src/time-and-attendance/exports/csv-exports.utils.ts b/src/time-and-attendance/exports/csv-exports.utils.ts index 3db50f9..0d4678e 100644 --- a/src/time-and-attendance/exports/csv-exports.utils.ts +++ b/src/time-and-attendance/exports/csv-exports.utils.ts @@ -5,29 +5,34 @@ import { CsvRow, InternalCsvRow } from "src/time-and-attendance/exports/export-c // You made a helper to pull bank codes from the db, but omitted to use it here... curious. const REGULAR = 1; const OVERTIME = 43; +const VACATION = 109; -export const consolidateRowHoursAndAmountByType = (rows: CsvRow[]): CsvRow[] => { - const map = new Map(); +export const consolidateRowHoursAndAmountByType = (rows: InternalCsvRow[]): InternalCsvRow[] => { + const map = new Map(); for (const row of rows) { - const key = `${row.code}|${row.semaine_no}`; - if (!map.has(key)) { - map.set(key, { ...row }); + if (row.code = VACATION) { + map.set(`${row.code}|${row.shift_date}`, row); } else { - const existing = map.get(key)!; - existing.quantite_hre = (existing.quantite_hre ?? 0) + (row.quantite_hre ?? 0); - existing.montant = (existing.montant ?? 0) + (row.montant ?? 0); + const key = `${row.code}|${row.semaine_no}`; + if (!map.has(key)) { + map.set(key, row); + } else { + const existing = map.get(key)!; + existing.quantite_hre = (existing.quantite_hre ?? 0) + (row.quantite_hre ?? 0); + existing.montant = (existing.montant ?? 0) + (row.montant ?? 0); + } } } return Array.from(map.values()); } export const applyHolidayRequalifications = async ( - rows: CsvRow[], + rows: InternalCsvRow[], holiday_service: HolidayService, holiday_bank_code: string, -): Promise => { - const result: CsvRow[] = []; +): Promise => { + const result: InternalCsvRow[] = []; const HOLIDAY_BANK_CODE = Number(holiday_bank_code.slice(1,)); for (const row of rows) { @@ -39,9 +44,9 @@ export const applyHolidayRequalifications = async ( result.push(row); continue; } - + const calculated = await holiday_service.calculateHolidayPay(row.employee_matricule, row.compagnie_no, new Date(row.premier_jour_absence)); - + if (!calculated.success) { result.push({ ...row, quantite_hre: 0 }); continue; @@ -53,15 +58,15 @@ export const applyHolidayRequalifications = async ( } export const applyOvertimeRequalifications = async ( - consolidated_rows: CsvRow[], + consolidated_rows: InternalCsvRow[], overtime_service: OvertimeService, ): Promise => { - const result: CsvRow[] = []; + const result: InternalCsvRow[] = []; //grouped by timesheet and week number - const grouped_rows = new Map(); + const grouped_rows = new Map(); for (const row of consolidated_rows) { - const key = `${row.compagnie_no}|${row.employee_matricule}|${row.semaine_no}`; + const key = `${row.timesheet_id}|${row.semaine_no}`; if (!grouped_rows.has(key)) { grouped_rows.set(key, []); } @@ -104,8 +109,6 @@ export const applyOvertimeRequalifications = async ( quantite_hre: deducted, }); } - - // return consolidateRowHoursAndAmountByType(result); return result; } diff --git a/src/time-and-attendance/exports/export-csv-options.dto.ts b/src/time-and-attendance/exports/export-csv-options.dto.ts index a485cb4..218ca2b 100644 --- a/src/time-and-attendance/exports/export-csv-options.dto.ts +++ b/src/time-and-attendance/exports/export-csv-options.dto.ts @@ -65,7 +65,7 @@ export interface CsvRow { dernier_jour_absence: string | undefined; } -export type InternalCsvRow = CsvRow & { timesheet_id: number; shift_date: Date; email: string; } +export type InternalCsvRow = CsvRow & { timesheet_id: number; shift_date: Date; } export type Filters = { diff --git a/src/time-and-attendance/exports/services/csv-exports.service.ts b/src/time-and-attendance/exports/services/csv-exports.service.ts index 3c4696d..7ca6019 100644 --- a/src/time-and-attendance/exports/services/csv-exports.service.ts +++ b/src/time-and-attendance/exports/services/csv-exports.service.ts @@ -7,9 +7,6 @@ import { OvertimeService } from "src/time-and-attendance/domains/services/overti import { HolidayService } from "src/time-and-attendance/domains/services/holiday.service"; import { select_csv_expense_lines, select_csv_shift_lines } from "src/time-and-attendance/utils/selects.utils"; import { BillableShiftType } from "src/time-and-attendance/shifts/shift.types"; -import { Prisma } from "prisma/postgres/generated/prisma/client/postgres/client"; -import { DefaultArgs } from "@prisma/client/runtime/client"; - @Injectable() export class CsvExportService { @@ -36,20 +33,16 @@ export class CsvExportService { * @returns The desired filtered data in semi-colon-separated format, grouped and sorted by * employee and by bank codes. */ - async collectTransaction( year: number, period_no: number, filters: Filters ): Promise { - const BILLABLE_SHIFT_TYPES: BillableShiftType[] = [ - 'REGULAR', - 'EVENING', - 'EMERGENCY', - 'OVERTIME', - 'SICK', - 'HOLIDAY', - 'VACATION', - ]; + async collectTransaction(year: number, period_no: number, filters: Filters): Promise { + const BILLABLE_SHIFT_TYPES: BillableShiftType[] = []; + + if (filters.types.shifts) BILLABLE_SHIFT_TYPES.push('REGULAR', 'OVERTIME', 'EMERGENCY', 'EVENING', 'SICK'); + if (filters.types.holiday) BILLABLE_SHIFT_TYPES.push('HOLIDAY'); + if (filters.types.vacation) BILLABLE_SHIFT_TYPES.push('VACATION'); const BILLABLE_SHIFT_CODES = await this.resolveShiftTypeCode(BILLABLE_SHIFT_TYPES); const PTO_SHIFT_CODES = await this.resolveShiftTypeCode(['VACATION', 'SICK', 'HOLIDAY']); - const HOLIDAY_SHIFT_CODE = await this.resolveShiftTypeCode(['HOLIDAY'])[0]; + const HOLIDAY_SHIFT_CODE = await this.resolveShiftTypeCode(['HOLIDAY']); const period = await this.prisma.payPeriods.findFirst({ where: { pay_year: year, pay_period_no: period_no }, @@ -76,16 +69,7 @@ export class CsvExportService { select: select_csv_shift_lines, }); - const exportedExpenses = await this.prisma.expenses.findMany({ - where: { - date: { gte: start, lte: end }, - is_approved: true, - timesheet: { employee: { company_code: { in: company_codes } } }, - }, - select: select_csv_expense_lines, - }); - - const rows: CsvRow[] = exportedShifts.map(shift => { + const rows: InternalCsvRow[] = exportedShifts.map(shift => { const employee = shift!.timesheet.employee; const week = computeWeekNumber(start, shift!.date); const type_transaction = shift!.bank_code.bank_code.charAt(0); @@ -93,6 +77,8 @@ export class CsvExportService { const isPTO = PTO_SHIFT_CODES.includes(shift.bank_code.bank_code) return { + timesheet_id: shift.timesheet.id, + shift_date: shift.date, compagnie_no: employee.company_code, employee_matricule: employee.external_payroll_id, releve: 0, @@ -112,45 +98,56 @@ export class CsvExportService { } }); - exportedExpenses.map(expense => { - const employee = expense.timesheet.employee; - const type_transaction = expense!.bank_code.bank_code.charAt(0); - const code = Number(expense!.bank_code.bank_code.slice(1,)) - const week = computeWeekNumber(start, expense.date); - - rows.push({ - compagnie_no: employee.company_code, - employee_matricule: employee.external_payroll_id, - releve: 0, - type_transaction: type_transaction, - code: code, - quantite_hre: undefined, - taux_horaire: undefined, - montant: Number(expense.amount), - semaine_no: week, - division_no: undefined, - service_no: undefined, - departem_no: undefined, - sous_departem_no: undefined, - date_transaction: formatDate(end), - premier_jour_absence: undefined, - dernier_jour_absence: undefined, + if (filters.types.expenses) { + const exportedExpenses = await this.prisma.expenses.findMany({ + where: { + date: { gte: start, lte: end }, + is_approved: true, + timesheet: { employee: { company_code: { in: company_codes } } }, + }, + select: select_csv_expense_lines, }); - }); + + exportedExpenses.map(expense => { + const employee = expense.timesheet.employee; + const type_transaction = expense!.bank_code.bank_code.charAt(0); + const code = Number(expense!.bank_code.bank_code.slice(1,)) + const week = computeWeekNumber(start, expense.date); + + rows.push({ + timesheet_id: expense.timesheet.id, + shift_date: expense.date, + compagnie_no: employee.company_code, + employee_matricule: employee.external_payroll_id, + releve: 0, + type_transaction: type_transaction, + code: code, + quantite_hre: undefined, + taux_horaire: undefined, + montant: Number(expense.amount), + semaine_no: week, + division_no: undefined, + service_no: undefined, + departem_no: undefined, + sous_departem_no: undefined, + date_transaction: formatDate(end), + premier_jour_absence: undefined, + dernier_jour_absence: undefined, + }); + }); + + } // Sort shifts and expenses according to their bank codes rows.sort((a, b) => { if (a.code !== b.code) return a.code - b.code; - + return 0; }); - - const holiday_rows = await applyHolidayRequalifications(rows, this.holiday_service, HOLIDAY_SHIFT_CODE); - + const holiday_rows = await applyHolidayRequalifications(rows, this.holiday_service, HOLIDAY_SHIFT_CODE[0]); const consolidated_rows = await consolidateRowHoursAndAmountByType(holiday_rows); - //requalifies regular hours into overtime when needed const requalified_rows = await applyOvertimeRequalifications(consolidated_rows, this.overtime_service); @@ -173,7 +170,6 @@ export class CsvExportService { const shiftCodes: string[] = []; billableBankCodes.map(billableBankCode => shiftCodes.push(billableBankCode.bank_code)); - return shiftCodes; }