189 lines
6.5 KiB
TypeScript
189 lines
6.5 KiB
TypeScript
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<CsvRow[]> {
|
|
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];
|
|
}
|
|
|
|
}
|