import { PrismaService } from "src/prisma/prisma.service"; import { ExportCompany } from "../dtos/export-csv-options.dto"; import { Injectable, NotFoundException } from "@nestjs/common"; export interface CsvRow { companyCode: number; externalPayrollId: number; fullName: string; bankCode: string; quantityHours?: number; amount?: number; weekNumber: number; payDate: string; holidayDate?: string; } @Injectable() export class CsvExportService { constructor(private readonly prisma: PrismaService) {} async collectTransaction(periodId: number, companies: ExportCompany[]): Promise { const companyCodes = companies.map(c => c === ExportCompany.TARGO ? 1 : 2); const period = await this.prisma.payPeriods.findUnique({ where: { period_number: periodId }, }); if(!period) { throw new NotFoundException(`Pay period ${periodId} not found`); } const startDate = period.start_date; const endDate = period.end_date; //fetching shifts const shifts = await this.prisma.shifts.findMany({ where: { date: { gte: startDate, lte: endDate }, timesheet: { employee: { company_code: { in: companyCodes} } }, }, include: { bank_code: true, timesheet: { include: {employee: { include: { user:true, supervisor: { include: { user:true } }, }}, }}, }, }); //fetching expenses const expenses = await this.prisma.expenses.findMany({ where: { date: { gte: startDate, lte: endDate }, timesheet: { employee: { company_code: { in: companyCodes} } }, }, include: { bank_code: true, timesheet: { include: { employee: { include: { user: true, supervisor: { include: { user:true } }, } }, } }, }, }); //fetching leave requests const leaves = await this.prisma.leaveRequests.findMany({ where : { start_date_time: { gte: startDate, lte: endDate }, employee: { company_code: { in: companyCodes } }, }, include: { bank_code: true, employee: { include: { user: true, supervisor: { include: { user: true } }, }}, }, }); const rows: CsvRow[] = []; //Shifts Mapping for (const s of shifts) { const emp = s.timesheet.employee; const weekNumber = this.computeWeekNumber(startDate, s.date); const hours = this.computeHours(s.start_time, s.end_time); rows.push({ companyCode: emp.company_code, externalPayrollId: emp.external_payroll_id, fullName: `${emp.user.first_name} ${emp.user.last_name}`, bankCode: s.bank_code.bank_code, quantityHours: hours, amount: undefined, weekNumber, payDate: this.formatDate(endDate), holidayDate: undefined, }); } //Expenses Mapping for (const e of expenses) { const emp = e.timesheet.employee; const weekNumber = this.computeWeekNumber(startDate, e.date); rows.push({ companyCode: emp.company_code, externalPayrollId: emp.external_payroll_id, fullName: `${emp.user.first_name} ${emp.user.last_name}`, bankCode: e.bank_code.bank_code, quantityHours: undefined, amount: Number(e.amount), weekNumber, payDate: this.formatDate(endDate), holidayDate: undefined, }); } //Leaves Mapping for(const l of leaves) { if(!l.bank_code) continue; const emp = l.employee; const start = l.start_date_time; const end = l.end_date_time ?? start; const weekNumber = this.computeWeekNumber(startDate, start); const hours = this.computeHours(start, end); rows.push({ companyCode: emp.company_code, externalPayrollId: emp.external_payroll_id, fullName: `${emp.user.first_name} ${emp.user.last_name}`, bankCode: l.bank_code.bank_code, quantityHours: hours, amount: undefined, weekNumber, payDate: this.formatDate(endDate), holidayDate: undefined, }); } //Final Mapping and sorts return rows.sort((a,b) => { if(a.externalPayrollId !== b.externalPayrollId) { return a.externalPayrollId - b.externalPayrollId; } if(a.bankCode !== b.bankCode) { return a.bankCode.localeCompare(b.bankCode); } return a.weekNumber - b.weekNumber; }); } generateCsv(rows: CsvRow[]): Buffer { const header = [ 'companyCode', 'externalPayrolId', 'fullName', 'bankCode', 'quantityHours', 'amount', 'weekNumber', 'payDate', 'holidayDate', ].join(',') + '\n'; const body = rows.map(r => [ r.companyCode, r.externalPayrollId, `${r.fullName.replace(/"/g, '""')}"`, r.bankCode, r.quantityHours?.toFixed(2) ?? '', r.weekNumber, r.payDate, r.holidayDate ?? '', ].join(',')).join('\n'); return Buffer.from('\uFEFF' + header + body, 'utf8'); } private computeHours(start: Date, end: Date): number { const diffMs = end.getTime() - start.getTime(); return +(diffMs / 1000 / 3600).toFixed(2); } private computeWeekNumber(start: Date, date: Date): number { const days = Math.floor((date.getTime() - start.getTime()) / (1000*60*60*24)); return Math.floor(days / 7 ) + 1; } private formatDate(d:Date): string { return d.toISOString().split('T')[0]; } }