fix(CSV): fix csv structure to match Desjardins

This commit is contained in:
Matthieu Haineault 2026-02-16 09:36:13 -05:00
parent a9637a93a6
commit ef969d3c42
3 changed files with 73 additions and 74 deletions

View File

@ -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. // You made a helper to pull bank codes from the db, but omitted to use it here... curious.
const REGULAR = 1; const REGULAR = 1;
const OVERTIME = 43; const OVERTIME = 43;
const VACATION = 109;
export const consolidateRowHoursAndAmountByType = (rows: CsvRow[]): CsvRow[] => { export const consolidateRowHoursAndAmountByType = (rows: InternalCsvRow[]): InternalCsvRow[] => {
const map = new Map<string, CsvRow>(); const map = new Map<string, InternalCsvRow>();
for (const row of rows) { for (const row of rows) {
const key = `${row.code}|${row.semaine_no}`; if (row.code = VACATION) {
if (!map.has(key)) { map.set(`${row.code}|${row.shift_date}`, row);
map.set(key, { ...row });
} else { } else {
const existing = map.get(key)!; const key = `${row.code}|${row.semaine_no}`;
existing.quantite_hre = (existing.quantite_hre ?? 0) + (row.quantite_hre ?? 0); if (!map.has(key)) {
existing.montant = (existing.montant ?? 0) + (row.montant ?? 0); 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()); return Array.from(map.values());
} }
export const applyHolidayRequalifications = async ( export const applyHolidayRequalifications = async (
rows: CsvRow[], rows: InternalCsvRow[],
holiday_service: HolidayService, holiday_service: HolidayService,
holiday_bank_code: string, holiday_bank_code: string,
): Promise<CsvRow[]> => { ): Promise<InternalCsvRow[]> => {
const result: CsvRow[] = []; const result: InternalCsvRow[] = [];
const HOLIDAY_BANK_CODE = Number(holiday_bank_code.slice(1,)); const HOLIDAY_BANK_CODE = Number(holiday_bank_code.slice(1,));
for (const row of rows) { for (const row of rows) {
@ -53,15 +58,15 @@ export const applyHolidayRequalifications = async (
} }
export const applyOvertimeRequalifications = async ( export const applyOvertimeRequalifications = async (
consolidated_rows: CsvRow[], consolidated_rows: InternalCsvRow[],
overtime_service: OvertimeService, overtime_service: OvertimeService,
): Promise<CsvRow[]> => { ): Promise<CsvRow[]> => {
const result: CsvRow[] = []; const result: InternalCsvRow[] = [];
//grouped by timesheet and week number //grouped by timesheet and week number
const grouped_rows = new Map<string, CsvRow[]>(); const grouped_rows = new Map<string, InternalCsvRow[]>();
for (const row of consolidated_rows) { 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)) { if (!grouped_rows.has(key)) {
grouped_rows.set(key, []); grouped_rows.set(key, []);
} }
@ -104,8 +109,6 @@ export const applyOvertimeRequalifications = async (
quantite_hre: deducted, quantite_hre: deducted,
}); });
} }
// return consolidateRowHoursAndAmountByType(result);
return result; return result;
} }

View File

@ -65,7 +65,7 @@ export interface CsvRow {
dernier_jour_absence: string | undefined; 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 = { export type Filters = {

View File

@ -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 { 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 { 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 { 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() @Injectable()
export class CsvExportService { export class CsvExportService {
@ -36,20 +33,16 @@ export class CsvExportService {
* @returns The desired filtered data in semi-colon-separated format, grouped and sorted by * @returns The desired filtered data in semi-colon-separated format, grouped and sorted by
* employee and by bank codes. * employee and by bank codes.
*/ */
async collectTransaction( year: number, period_no: number, filters: Filters ): Promise<CsvRow[]> { async collectTransaction(year: number, period_no: number, filters: Filters): Promise<CsvRow[]> {
const BILLABLE_SHIFT_TYPES: BillableShiftType[] = [ const BILLABLE_SHIFT_TYPES: BillableShiftType[] = [];
'REGULAR',
'EVENING', if (filters.types.shifts) BILLABLE_SHIFT_TYPES.push('REGULAR', 'OVERTIME', 'EMERGENCY', 'EVENING', 'SICK');
'EMERGENCY', if (filters.types.holiday) BILLABLE_SHIFT_TYPES.push('HOLIDAY');
'OVERTIME', if (filters.types.vacation) BILLABLE_SHIFT_TYPES.push('VACATION');
'SICK',
'HOLIDAY',
'VACATION',
];
const BILLABLE_SHIFT_CODES = await this.resolveShiftTypeCode(BILLABLE_SHIFT_TYPES); const BILLABLE_SHIFT_CODES = await this.resolveShiftTypeCode(BILLABLE_SHIFT_TYPES);
const PTO_SHIFT_CODES = await this.resolveShiftTypeCode(['VACATION', 'SICK', 'HOLIDAY']); 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({ const period = await this.prisma.payPeriods.findFirst({
where: { pay_year: year, pay_period_no: period_no }, where: { pay_year: year, pay_period_no: period_no },
@ -76,16 +69,7 @@ export class CsvExportService {
select: select_csv_shift_lines, select: select_csv_shift_lines,
}); });
const exportedExpenses = await this.prisma.expenses.findMany({ const rows: InternalCsvRow[] = exportedShifts.map(shift => {
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 employee = shift!.timesheet.employee; const employee = shift!.timesheet.employee;
const week = computeWeekNumber(start, shift!.date); const week = computeWeekNumber(start, shift!.date);
const type_transaction = shift!.bank_code.bank_code.charAt(0); 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) const isPTO = PTO_SHIFT_CODES.includes(shift.bank_code.bank_code)
return { return {
timesheet_id: shift.timesheet.id,
shift_date: shift.date,
compagnie_no: employee.company_code, compagnie_no: employee.company_code,
employee_matricule: employee.external_payroll_id, employee_matricule: employee.external_payroll_id,
releve: 0, releve: 0,
@ -112,31 +98,45 @@ export class CsvExportService {
} }
}); });
exportedExpenses.map(expense => { if (filters.types.expenses) {
const employee = expense.timesheet.employee; const exportedExpenses = await this.prisma.expenses.findMany({
const type_transaction = expense!.bank_code.bank_code.charAt(0); where: {
const code = Number(expense!.bank_code.bank_code.slice(1,)) date: { gte: start, lte: end },
const week = computeWeekNumber(start, expense.date); is_approved: true,
timesheet: { employee: { company_code: { in: company_codes } } },
rows.push({ },
compagnie_no: employee.company_code, select: select_csv_expense_lines,
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,
}); });
});
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 // Sort shifts and expenses according to their bank codes
rows.sort((a, b) => { rows.sort((a, b) => {
@ -146,11 +146,8 @@ export class CsvExportService {
return 0; return 0;
}); });
const holiday_rows = await applyHolidayRequalifications(rows, this.holiday_service, HOLIDAY_SHIFT_CODE[0]);
const holiday_rows = await applyHolidayRequalifications(rows, this.holiday_service, HOLIDAY_SHIFT_CODE);
const consolidated_rows = await consolidateRowHoursAndAmountByType(holiday_rows); const consolidated_rows = await consolidateRowHoursAndAmountByType(holiday_rows);
//requalifies regular hours into overtime when needed //requalifies regular hours into overtime when needed
const requalified_rows = await applyOvertimeRequalifications(consolidated_rows, this.overtime_service); const requalified_rows = await applyOvertimeRequalifications(consolidated_rows, this.overtime_service);
@ -173,7 +170,6 @@ export class CsvExportService {
const shiftCodes: string[] = []; const shiftCodes: string[] = [];
billableBankCodes.map(billableBankCode => shiftCodes.push(billableBankCode.bank_code)); billableBankCodes.map(billableBankCode => shiftCodes.push(billableBankCode.bank_code));
return shiftCodes; return shiftCodes;
} }