BREAKING(csv-report): Trim shift and expense retrieval, up to calculateOvertime portion, logic requires rework
This commit is contained in:
parent
df390b41b6
commit
a9637a93a6
|
|
@ -1,7 +1,6 @@
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { computeHours, getWeekStart } from "src/common/utils/date-utils";
|
import { computeHours, getWeekStart } from "src/common/utils/date-utils";
|
||||||
import { PrismaPostgresService } from "prisma/postgres/prisma-postgres.service";
|
import { PrismaPostgresService } from "prisma/postgres/prisma-postgres.service";
|
||||||
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
|
||||||
import { MS_PER_WEEK } from "src/common/utils/constants.utils";
|
import { MS_PER_WEEK } from "src/common/utils/constants.utils";
|
||||||
import { Result } from "src/common/errors/result-error.factory";
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
/*
|
/*
|
||||||
|
|
@ -15,25 +14,39 @@ import { Result } from "src/common/errors/result-error.factory";
|
||||||
export class HolidayService {
|
export class HolidayService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly prisma: PrismaPostgresService,
|
private readonly prisma: PrismaPostgresService,
|
||||||
private readonly emailResolver: EmailToIdResolver,
|
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
private async computeHoursPrevious4WeeksByEmail(email: string, holiday_date: Date): Promise<Result<number, string>> {
|
/**
|
||||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
* Calculates the amount of hours to be paid for a given employee on the provided holiday date.
|
||||||
if (!employee_id.success) return { success: false, error: employee_id.error };
|
*
|
||||||
return this.computeHoursPrevious4Weeks(employee_id.data, holiday_date);
|
* @param employee_id id of the employee whom the holiday shift belongs to
|
||||||
}
|
* @param holiday_date date that the holiday was taken on
|
||||||
|
*
|
||||||
private async computeHoursPrevious4Weeks(employee_id: number, holiday_date: Date): Promise<Result<number, string>> {
|
* @returns Average daily hours worked in the past four weeks as a `number`, to a maximum of `8`
|
||||||
|
*/
|
||||||
|
private async computeHoursPrevious4Weeks(external_payroll_id: number, company_code: number, holiday_date: Date): Promise<Result<number, string>> {
|
||||||
try {
|
try {
|
||||||
|
const valid_codes = ['G1', 'G43', 'G56', 'G104', 'G105', 'G305', 'G700', 'G720'];
|
||||||
const holiday_week_start = getWeekStart(holiday_date);
|
const holiday_week_start = getWeekStart(holiday_date);
|
||||||
const window_start = new Date(holiday_week_start.getTime() - 4 * MS_PER_WEEK);
|
const window_start = new Date(holiday_week_start.getTime() - 4 * MS_PER_WEEK);
|
||||||
const window_end = new Date(holiday_week_start.getTime() - 1);
|
const window_end = new Date(holiday_week_start.getTime() - 1);
|
||||||
|
|
||||||
const valid_codes = ['G1', 'G43', 'G56', 'G104', 'G105', 'G305', 'G700', 'G720'];
|
const employee = await this.prisma.employees.findFirst({
|
||||||
|
where: {
|
||||||
|
external_payroll_id,
|
||||||
|
company_code,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!employee)
|
||||||
|
return {success: false, error: 'EMPLOYEE_NOT_FOUND'};
|
||||||
|
|
||||||
const shifts = await this.prisma.shifts.findMany({
|
const shifts = await this.prisma.shifts.findMany({
|
||||||
where: {
|
where: {
|
||||||
timesheet: { employee_id: employee_id },
|
timesheet: { employee_id: employee.id },
|
||||||
date: { gte: window_start, lte: window_end },
|
date: { gte: window_start, lte: window_end },
|
||||||
bank_code: { bank_code: { in: valid_codes } },
|
bank_code: { bank_code: { in: valid_codes } },
|
||||||
},
|
},
|
||||||
|
|
@ -43,7 +56,10 @@ export class HolidayService {
|
||||||
const hours_by_week = new Map<number, number>();
|
const hours_by_week = new Map<number, number>();
|
||||||
for (const shift of shifts) {
|
for (const shift of shifts) {
|
||||||
const hours = computeHours(shift.start_time, shift.end_time);
|
const hours = computeHours(shift.start_time, shift.end_time);
|
||||||
if (hours <= 0) continue;
|
|
||||||
|
if (hours <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
const shift_week_start = getWeekStart(shift.date);
|
const shift_week_start = getWeekStart(shift.date);
|
||||||
const key = shift_week_start.getTime();
|
const key = shift_week_start.getTime();
|
||||||
hours_by_week.set(key, (hours_by_week.get(key) ?? 0) + hours);
|
hours_by_week.set(key, (hours_by_week.get(key) ?? 0) + hours);
|
||||||
|
|
@ -64,11 +80,12 @@ export class HolidayService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async calculateHolidayPay(email: string, holiday_date: Date, modifier: number): Promise<Result<number, string>> {
|
async calculateHolidayPay(employeePayrollID: number, companyCode: number, holiday_date: Date): Promise<Result<number, string>> {
|
||||||
const average_daily_hours = await this.computeHoursPrevious4WeeksByEmail(email, holiday_date);
|
const average_daily_hours = await this.computeHoursPrevious4Weeks(employeePayrollID, companyCode, holiday_date);
|
||||||
|
|
||||||
if (!average_daily_hours.success) return { success: false, error: average_daily_hours.error };
|
if (!average_daily_hours.success) return { success: false, error: average_daily_hours.error };
|
||||||
|
|
||||||
const daily_rate = (Math.min(average_daily_hours.data, 8)) * modifier;
|
const daily_rate = (Math.min(average_daily_hours.data, 8));
|
||||||
return { success: true, data: daily_rate };
|
return { success: true, data: daily_rate };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,14 +2,15 @@ import { HolidayService } from "src/time-and-attendance/domains/services/holiday
|
||||||
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";
|
||||||
|
|
||||||
const REGULAR = 'G1';
|
// You made a helper to pull bank codes from the db, but omitted to use it here... curious.
|
||||||
const OVERTIME = 'G43';
|
const REGULAR = 1;
|
||||||
|
const OVERTIME = 43;
|
||||||
|
|
||||||
export const consolidateRowHoursAndAmountByType = (rows: InternalCsvRow[]): InternalCsvRow[] => {
|
export const consolidateRowHoursAndAmountByType = (rows: CsvRow[]): CsvRow[] => {
|
||||||
const map = new Map<string, InternalCsvRow>();
|
const map = new Map<string, CsvRow>();
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const key = `${row.timesheet_id}|${row.bank_code}|${row.semaine_no}`;
|
const key = `${row.code}|${row.semaine_no}`;
|
||||||
if (!map.has(key)) {
|
if (!map.has(key)) {
|
||||||
map.set(key, { ...row });
|
map.set(key, { ...row });
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -22,22 +23,25 @@ export const consolidateRowHoursAndAmountByType = (rows: InternalCsvRow[]): Inte
|
||||||
}
|
}
|
||||||
|
|
||||||
export const applyHolidayRequalifications = async (
|
export const applyHolidayRequalifications = async (
|
||||||
rows: InternalCsvRow[],
|
rows: CsvRow[],
|
||||||
holiday_service: HolidayService,
|
holiday_service: HolidayService,
|
||||||
): Promise<InternalCsvRow[]> => {
|
holiday_bank_code: string,
|
||||||
const result: InternalCsvRow[] = [];
|
): Promise<CsvRow[]> => {
|
||||||
|
const result: CsvRow[] = [];
|
||||||
|
const HOLIDAY_BANK_CODE = Number(holiday_bank_code.slice(1,));
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
if (row.bank_code !== 'G104') {
|
if (row.code !== HOLIDAY_BANK_CODE) {
|
||||||
result.push(row);
|
result.push(row);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!row.holiday_date || !row.email) {
|
if (!row.premier_jour_absence || !row.dernier_jour_absence || !row.employee_matricule || !row.compagnie_no) {
|
||||||
result.push(row);
|
result.push(row);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const calculated = await holiday_service.calculateHolidayPay(row.email, new Date(row.holiday_date), 1);
|
const calculated = await holiday_service.calculateHolidayPay(row.employee_matricule, row.compagnie_no, new Date(row.premier_jour_absence));
|
||||||
|
|
||||||
if (!calculated.success) {
|
if (!calculated.success) {
|
||||||
result.push({ ...row, quantite_hre: 0 });
|
result.push({ ...row, quantite_hre: 0 });
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -49,15 +53,15 @@ export const applyHolidayRequalifications = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
export const applyOvertimeRequalifications = async (
|
export const applyOvertimeRequalifications = async (
|
||||||
consolidated_rows: InternalCsvRow[],
|
consolidated_rows: CsvRow[],
|
||||||
overtime_service: OvertimeService,
|
overtime_service: OvertimeService,
|
||||||
): Promise<CsvRow[]> => {
|
): Promise<CsvRow[]> => {
|
||||||
const result: InternalCsvRow[] = [];
|
const result: CsvRow[] = [];
|
||||||
//grouped by timesheet and week number
|
//grouped by timesheet and week number
|
||||||
const grouped_rows = new Map<string, InternalCsvRow[]>();
|
const grouped_rows = new Map<string, CsvRow[]>();
|
||||||
|
|
||||||
for (const row of consolidated_rows) {
|
for (const row of consolidated_rows) {
|
||||||
const key = `${row.timesheet_id}|${row.semaine_no}`;
|
const key = `${row.compagnie_no}|${row.employee_matricule}|${row.semaine_no}`;
|
||||||
if (!grouped_rows.has(key)) {
|
if (!grouped_rows.has(key)) {
|
||||||
grouped_rows.set(key, []);
|
grouped_rows.set(key, []);
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +79,7 @@ export const applyOvertimeRequalifications = async (
|
||||||
|
|
||||||
const overtime_hours = summary.data.total_overtime;
|
const overtime_hours = summary.data.total_overtime;
|
||||||
|
|
||||||
const regular_hours = rows.find(r => r.bank_code === REGULAR);
|
const regular_hours = rows.find(r => r.code === REGULAR);
|
||||||
if (!regular_hours || !regular_hours.quantite_hre) {
|
if (!regular_hours || !regular_hours.quantite_hre) {
|
||||||
result.push(...rows);
|
result.push(...rows);
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -96,7 +100,7 @@ export const applyOvertimeRequalifications = async (
|
||||||
|
|
||||||
result.push({
|
result.push({
|
||||||
...regular_hours,
|
...regular_hours,
|
||||||
bank_code: OVERTIME,
|
code: OVERTIME,
|
||||||
quantite_hre: deducted,
|
quantite_hre: deducted,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,20 +49,20 @@ export class ExportCsvOptionsDto {
|
||||||
export interface CsvRow {
|
export interface CsvRow {
|
||||||
compagnie_no: number;
|
compagnie_no: number;
|
||||||
employee_matricule: number;
|
employee_matricule: number;
|
||||||
releve:number;
|
releve: number;
|
||||||
type_transaction:string;
|
type_transaction: string;
|
||||||
code: number;
|
code: number;
|
||||||
quantite_hre?: number;
|
quantite_hre: number | undefined;
|
||||||
taux_horaire?:string;
|
taux_horaire: string | undefined;
|
||||||
montant?: number;
|
montant: number | undefined;
|
||||||
semaine_no: number;
|
semaine_no: number;
|
||||||
division_no?: number;
|
division_no: number | undefined;
|
||||||
service_no?: number;
|
service_no: number | undefined;
|
||||||
departem_no?:number;
|
departem_no: number | undefined;
|
||||||
sous_departem_no?:number;
|
sous_departem_no: number | undefined;
|
||||||
date_transaction: string;
|
date_transaction: string;
|
||||||
premier_jour_absence?: string;
|
premier_jour_absence: string | undefined;
|
||||||
dernier_jour_absence?:string;
|
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; email: string; }
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@ import { applyHolidayRequalifications, applyOvertimeRequalifications, computeWee
|
||||||
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 { 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 { Prisma } from "prisma/postgres/generated/prisma/client/postgres/client";
|
||||||
|
import { DefaultArgs } from "@prisma/client/runtime/client";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -16,13 +19,38 @@ export class CsvExportService {
|
||||||
private readonly holiday_service: HolidayService,
|
private readonly holiday_service: HolidayService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
async collectTransaction(
|
/**
|
||||||
year: number,
|
* Gets all approved shifts and expenses within the year and period number provided, according
|
||||||
period_no: number,
|
* to company and types specified in the filters. Any holiday shifts will be adjusted based on
|
||||||
filters: Filters,
|
* prior 4 weeks of work, where applicable. Vacation and Sick shifts will be split into separate
|
||||||
// approved: boolean = true
|
* lines if they are more than one day apart.
|
||||||
): Promise<CsvRow[]> {
|
*
|
||||||
//fetch period
|
* ex: A pay period goes from January 10 to 24. An employee has vacation shifts from
|
||||||
|
* January 11 to
|
||||||
|
* 13 and one shift on Jan 23, then two separate lines will be generated; one from Jan 11 to 13
|
||||||
|
* and one for Jan 23.
|
||||||
|
*
|
||||||
|
* @param year the year of the requested pay data
|
||||||
|
* @param period_no the period number of the requested pay data
|
||||||
|
* @param filters user-provided input that determines which company and types are exported
|
||||||
|
* @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<CsvRow[]> {
|
||||||
|
const BILLABLE_SHIFT_TYPES: BillableShiftType[] = [
|
||||||
|
'REGULAR',
|
||||||
|
'EVENING',
|
||||||
|
'EMERGENCY',
|
||||||
|
'OVERTIME',
|
||||||
|
'SICK',
|
||||||
|
'HOLIDAY',
|
||||||
|
'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 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 },
|
||||||
select: { period_start: true, period_end: true },
|
select: { period_start: true, period_end: true },
|
||||||
|
|
@ -32,79 +60,38 @@ export class CsvExportService {
|
||||||
const start = period.period_start;
|
const start = period.period_start;
|
||||||
const end = period.period_end;
|
const end = period.period_end;
|
||||||
|
|
||||||
//fetch company codes
|
|
||||||
const company_codes = resolveCompanyCodes(filters.companies);
|
const company_codes = resolveCompanyCodes(filters.companies);
|
||||||
if (company_codes.length === 0) throw new BadRequestException('No company selected');
|
if (company_codes.length === 0) throw new BadRequestException('NO_COMPANY_SELECTED');
|
||||||
|
|
||||||
//Flag types
|
if (Object.values(filters.types).every(type => type === false))
|
||||||
const { shifts: want_shifts, expenses: want_expense, holiday: want_holiday, vacation: want_vacation } = filters.types;
|
throw new BadRequestException('NO_TYPE_SELECTED');
|
||||||
if (!want_shifts && !want_expense && !want_holiday && !want_vacation) {
|
|
||||||
throw new BadRequestException(' No export type selected ');
|
|
||||||
}
|
|
||||||
|
|
||||||
const holiday_code = await this.resolveShiftTypeCode('HOLIDAY');
|
const exportedShifts = await this.prisma.shifts.findMany({
|
||||||
const vacation_code = await this.resolveShiftTypeCode('VACATION');
|
where: {
|
||||||
const regular_code = await this.resolveShiftTypeCode('REGULAR');
|
date: { gte: start, lte: end },
|
||||||
const evening_code = await this.resolveShiftTypeCode('EVENING');
|
is_approved: true,
|
||||||
const emergency_code = await this.resolveShiftTypeCode('EMERGENCY');
|
timesheet: { employee: { company_code: { in: company_codes } } },
|
||||||
const sick_code = await this.resolveShiftTypeCode('SICK');
|
bank_code: { bank_code: { in: BILLABLE_SHIFT_CODES } },
|
||||||
const shift_code_filter = [regular_code, evening_code, emergency_code, sick_code];
|
},
|
||||||
|
select: select_csv_shift_lines,
|
||||||
|
});
|
||||||
|
|
||||||
//Prisma queries
|
const exportedExpenses = await this.prisma.expenses.findMany({
|
||||||
const promises: Array<Promise<any[]>> = [];
|
where: {
|
||||||
|
date: { gte: start, lte: end },
|
||||||
|
is_approved: true,
|
||||||
|
timesheet: { employee: { company_code: { in: company_codes } } },
|
||||||
|
},
|
||||||
|
select: select_csv_expense_lines,
|
||||||
|
});
|
||||||
|
|
||||||
if (want_shifts) {
|
const rows: CsvRow[] = exportedShifts.map(shift => {
|
||||||
promises.push(this.prisma.shifts.findMany({
|
|
||||||
where: {
|
|
||||||
date: { gte: start, lte: end },
|
|
||||||
is_approved: true,
|
|
||||||
timesheet: { employee: { company_code: { in: company_codes } } },
|
|
||||||
bank_code: { bank_code: { in: shift_code_filter } },
|
|
||||||
},
|
|
||||||
select: select_csv_shift_lines,
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
promises.push(Promise.resolve([]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (want_holiday) {
|
|
||||||
promises.push(this.prisma.shifts.findMany({
|
|
||||||
where: {
|
|
||||||
date: { gte: start, lte: end },
|
|
||||||
is_approved: true,
|
|
||||||
timesheet: { employee: { company_code: { in: company_codes } } },
|
|
||||||
bank_code: { bank_code: holiday_code },
|
|
||||||
},
|
|
||||||
select: select_csv_shift_lines,
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
promises.push(Promise.resolve([]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (want_vacation) {
|
|
||||||
promises.push(this.prisma.shifts.findMany({
|
|
||||||
where: {
|
|
||||||
date: { gte: start, lte: end },
|
|
||||||
is_approved: true,
|
|
||||||
bank_code: { bank_code: vacation_code },
|
|
||||||
timesheet: { employee: { company_code: { in: company_codes } } },
|
|
||||||
},
|
|
||||||
select: select_csv_shift_lines,
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
promises.push(Promise.resolve([]));
|
|
||||||
}
|
|
||||||
|
|
||||||
//array of arrays
|
|
||||||
const [base_shifts, holiday_shifts, vacation_shifts, expenses] = await Promise.all(promises);
|
|
||||||
|
|
||||||
const rows: CsvRow[] = [];
|
|
||||||
|
|
||||||
const map_shifts = (shift: any, is_holiday: boolean): CsvRow => {
|
|
||||||
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);
|
||||||
const code = Number(shift!.bank_code.bank_code.slice(1,))
|
const code = Number(shift!.bank_code.bank_code.slice(1,));
|
||||||
|
const isPTO = PTO_SHIFT_CODES.includes(shift.bank_code.bank_code)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
compagnie_no: employee.company_code,
|
compagnie_no: employee.company_code,
|
||||||
employee_matricule: employee.external_payroll_id,
|
employee_matricule: employee.external_payroll_id,
|
||||||
|
|
@ -118,36 +105,19 @@ export class CsvExportService {
|
||||||
division_no: undefined,
|
division_no: undefined,
|
||||||
service_no: undefined,
|
service_no: undefined,
|
||||||
departem_no: undefined,
|
departem_no: undefined,
|
||||||
|
sous_departem_no: undefined,
|
||||||
date_transaction: formatDate(end),
|
date_transaction: formatDate(end),
|
||||||
premier_jour_absence: is_holiday ? formatDate(shift!.date) : '',
|
premier_jour_absence: isPTO ? formatDate(shift!.date) : '',
|
||||||
dernier_jour_absence: is_holiday ? formatDate(shift!.date) : '',
|
dernier_jour_absence: isPTO ? formatDate(shift!.date) : '',
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
//final mapping of all shifts based filters
|
exportedExpenses.map(expense => {
|
||||||
for (const shift of base_shifts) rows.push(map_shifts(shift, false));
|
|
||||||
for (const shift of holiday_shifts) rows.push(map_shifts(shift, true));
|
|
||||||
for (const shift of vacation_shifts) rows.push(map_shifts(shift, false));
|
|
||||||
|
|
||||||
|
|
||||||
if (want_expense) {
|
|
||||||
promises.push(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,
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
promises.push(Promise.resolve([]));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const expense of expenses) {
|
|
||||||
const employee = expense.timesheet.employee;
|
const employee = expense.timesheet.employee;
|
||||||
const type_transaction = expense!.bank_code.bank_code.charAt(0);
|
const type_transaction = expense!.bank_code.bank_code.charAt(0);
|
||||||
const code = Number(expense!.bank_code.bank_code.slice(1,))
|
const code = Number(expense!.bank_code.bank_code.slice(1,))
|
||||||
const week = computeWeekNumber(start, expense.date);
|
const week = computeWeekNumber(start, expense.date);
|
||||||
|
|
||||||
rows.push({
|
rows.push({
|
||||||
compagnie_no: employee.company_code,
|
compagnie_no: employee.company_code,
|
||||||
employee_matricule: employee.external_payroll_id,
|
employee_matricule: employee.external_payroll_id,
|
||||||
|
|
@ -161,28 +131,24 @@ export class CsvExportService {
|
||||||
division_no: undefined,
|
division_no: undefined,
|
||||||
service_no: undefined,
|
service_no: undefined,
|
||||||
departem_no: undefined,
|
departem_no: undefined,
|
||||||
|
sous_departem_no: undefined,
|
||||||
date_transaction: formatDate(end),
|
date_transaction: formatDate(end),
|
||||||
premier_jour_absence: undefined,
|
premier_jour_absence: undefined,
|
||||||
dernier_jour_absence: undefined,
|
dernier_jour_absence: undefined,
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
|
||||||
//Final mapping and sorts
|
// Sort shifts and expenses according to their bank codes
|
||||||
rows.sort((a, b) => {
|
rows.sort((a, b) => {
|
||||||
if (a.compagnie_no !== b.compagnie_no) {
|
if (a.code !== b.code)
|
||||||
return a.compagnie_no - b.compagnie_no;
|
return a.code - b.code;
|
||||||
}
|
|
||||||
const bank_code = String(a.bank_code).localeCompare(String(b.bank_code));
|
|
||||||
if (bank_code !== 0) return bank_code;
|
|
||||||
if (a.bank_code !== b.bank_code) return a.bank_code.localeCompare(b.bank_code);
|
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
//requalifies the real amount paid for holidays
|
const holiday_rows = await applyHolidayRequalifications(rows, this.holiday_service, HOLIDAY_SHIFT_CODE);
|
||||||
const holiday_rows = await applyHolidayRequalifications(rows, this.holiday_service);
|
|
||||||
|
|
||||||
//groups hours by bank_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
|
||||||
|
|
@ -191,9 +157,9 @@ export class CsvExportService {
|
||||||
return requalified_rows;
|
return requalified_rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveShiftTypeCode = async (shift_type: string): Promise<string> => {
|
resolveShiftTypeCode = async (shift_type: BillableShiftType[]): Promise<string[]> => {
|
||||||
const shift_code = await this.prisma.bankCodes.findFirst({
|
const billableBankCodes = await this.prisma.bankCodes.findMany({
|
||||||
where: { type: shift_type },
|
where: { type: { in: shift_type } },
|
||||||
select: {
|
select: {
|
||||||
bank_code: true,
|
bank_code: true,
|
||||||
shifts: {
|
shifts: {
|
||||||
|
|
@ -203,9 +169,12 @@ export class CsvExportService {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (!shift_code) throw new BadRequestException('Missing Shift bank code');
|
if (!billableBankCodes) throw new BadRequestException('Missing Shift bank code');
|
||||||
|
|
||||||
return shift_code.bank_code;
|
const shiftCodes: string[] = [];
|
||||||
|
billableBankCodes.map(billableBankCode => shiftCodes.push(billableBankCode.bank_code));
|
||||||
|
|
||||||
|
return shiftCodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
src/time-and-attendance/shifts/shift.types.ts
Normal file
1
src/time-and-attendance/shifts/shift.types.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export type BillableShiftType = 'REGULAR' | 'EVENING' | 'EMERGENCY' | 'VACATION' | 'HOLIDAY' | 'SICK' | 'OVERTIME';
|
||||||
|
|
@ -94,6 +94,7 @@ export const select_csv_shift_lines = {
|
||||||
id: true,
|
id: true,
|
||||||
employee: {
|
employee: {
|
||||||
select: {
|
select: {
|
||||||
|
id: true,
|
||||||
company_code: true,
|
company_code: true,
|
||||||
external_payroll_id: true,
|
external_payroll_id: true,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user