diff --git a/src/time-and-attendance/domains/services/holiday.service.ts b/src/time-and-attendance/domains/services/holiday.service.ts index 4bcb213..40ae1e7 100644 --- a/src/time-and-attendance/domains/services/holiday.service.ts +++ b/src/time-and-attendance/domains/services/holiday.service.ts @@ -1,7 +1,6 @@ import { Injectable } from "@nestjs/common"; import { computeHours, getWeekStart } from "src/common/utils/date-utils"; 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 { Result } from "src/common/errors/result-error.factory"; /* @@ -15,25 +14,39 @@ import { Result } from "src/common/errors/result-error.factory"; export class HolidayService { constructor( private readonly prisma: PrismaPostgresService, - private readonly emailResolver: EmailToIdResolver, ) { } - private async computeHoursPrevious4WeeksByEmail(email: string, holiday_date: Date): Promise> { - const employee_id = await this.emailResolver.findIdByEmail(email); - if (!employee_id.success) return { success: false, error: employee_id.error }; - return this.computeHoursPrevious4Weeks(employee_id.data, holiday_date); - } - - private async computeHoursPrevious4Weeks(employee_id: number, holiday_date: Date): Promise> { + /** + * Calculates the amount of hours to be paid for a given employee on the provided 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 + * + * @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> { try { + const valid_codes = ['G1', 'G43', 'G56', 'G104', 'G105', 'G305', 'G700', 'G720']; const holiday_week_start = getWeekStart(holiday_date); const window_start = new Date(holiday_week_start.getTime() - 4 * MS_PER_WEEK); 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({ where: { - timesheet: { employee_id: employee_id }, + timesheet: { employee_id: employee.id }, date: { gte: window_start, lte: window_end }, bank_code: { bank_code: { in: valid_codes } }, }, @@ -43,7 +56,10 @@ export class HolidayService { const hours_by_week = new Map(); for (const shift of shifts) { 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 key = shift_week_start.getTime(); 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> { - const average_daily_hours = await this.computeHoursPrevious4WeeksByEmail(email, holiday_date); + async calculateHolidayPay(employeePayrollID: number, companyCode: number, holiday_date: Date): Promise> { + const average_daily_hours = await this.computeHoursPrevious4Weeks(employeePayrollID, companyCode, holiday_date); + 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 }; } } \ No newline at end of file diff --git a/src/time-and-attendance/exports/csv-exports.utils.ts b/src/time-and-attendance/exports/csv-exports.utils.ts index 32a9399..0d4678e 100644 --- a/src/time-and-attendance/exports/csv-exports.utils.ts +++ b/src/time-and-attendance/exports/csv-exports.utils.ts @@ -2,20 +2,26 @@ import { HolidayService } from "src/time-and-attendance/domains/services/holiday import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.service"; import { CsvRow, InternalCsvRow } from "src/time-and-attendance/exports/export-csv-options.dto"; -const REGULAR = 'G1'; -const OVERTIME = 'G43'; +// 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: InternalCsvRow[]): InternalCsvRow[] => { const map = new Map(); for (const row of rows) { - const key = `${row.timesheet_id}|${row.bank_code}|${row.week_number}`; - 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.quantity_hours = (existing.quantity_hours ?? 0) + (row.quantity_hours ?? 0); - existing.amount = (existing.amount ?? 0) + (row.amount ?? 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()); @@ -24,26 +30,29 @@ export const consolidateRowHoursAndAmountByType = (rows: InternalCsvRow[]): Inte export const applyHolidayRequalifications = async ( rows: InternalCsvRow[], holiday_service: HolidayService, + holiday_bank_code: string, ): Promise => { const result: InternalCsvRow[] = []; + const HOLIDAY_BANK_CODE = Number(holiday_bank_code.slice(1,)); for (const row of rows) { - if (row.bank_code !== 'G104') { + if (row.code !== HOLIDAY_BANK_CODE) { result.push(row); 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); continue; } - - const calculated = await holiday_service.calculateHolidayPay(row.email, new Date(row.holiday_date), 1); - if (!calculated.success) { - result.push({ ...row, quantity_hours: 0 }); - continue; - } - result.push({ ...row, quantity_hours: calculated.data }); + 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; + } + + result.push({ ...row, quantite_hre: calculated.data }); } return result; } @@ -57,7 +66,7 @@ export const applyOvertimeRequalifications = async ( const grouped_rows = new Map(); for (const row of consolidated_rows) { - const key = `${row.timesheet_id}|${row.week_number}`; + const key = `${row.timesheet_id}|${row.semaine_no}`; if (!grouped_rows.has(key)) { grouped_rows.set(key, []); } @@ -75,19 +84,19 @@ export const applyOvertimeRequalifications = async ( const overtime_hours = summary.data.total_overtime; - const regular_hours = rows.find(r => r.bank_code === REGULAR); - if (!regular_hours || !regular_hours.quantity_hours) { + const regular_hours = rows.find(r => r.code === REGULAR); + if (!regular_hours || !regular_hours.quantite_hre) { result.push(...rows); continue; } - const deducted = Math.min(overtime_hours, regular_hours.quantity_hours); + const deducted = Math.min(overtime_hours, regular_hours.quantite_hre); for (const row of rows) { if (row === regular_hours) { - const remaining = regular_hours.quantity_hours - deducted; + const remaining = regular_hours.quantite_hre - deducted; if (remaining > 0) { - result.push({ ...regular_hours, quantity_hours: remaining }); + result.push({ ...regular_hours, quantite_hre: remaining }); } } else { result.push(row); @@ -96,12 +105,10 @@ export const applyOvertimeRequalifications = async ( result.push({ ...regular_hours, - bank_code: OVERTIME, - quantity_hours: deducted, + code: OVERTIME, + 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 939e228..218ca2b 100644 --- a/src/time-and-attendance/exports/export-csv-options.dto.ts +++ b/src/time-and-attendance/exports/export-csv-options.dto.ts @@ -47,18 +47,25 @@ export class ExportCsvOptionsDto { } export interface CsvRow { - company_code: number; - external_payroll_id: number; - full_name: string; - bank_code: string; - quantity_hours?: number; - amount?: number; - week_number: number; - pay_date: string; - holiday_date?: string; + compagnie_no: number; + employee_matricule: number; + releve: number; + type_transaction: string; + code: number; + quantite_hre: number | undefined; + taux_horaire: string | undefined; + montant: number | undefined; + semaine_no: number; + division_no: number | undefined; + service_no: number | undefined; + departem_no: number | undefined; + sous_departem_no: number | undefined; + date_transaction: string; + premier_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 = { diff --git a/src/time-and-attendance/exports/services/csv-builder.service.ts b/src/time-and-attendance/exports/services/csv-builder.service.ts index c6b944e..ccc9e17 100644 --- a/src/time-and-attendance/exports/services/csv-builder.service.ts +++ b/src/time-and-attendance/exports/services/csv-builder.service.ts @@ -5,34 +5,28 @@ import { CsvRow } from "src/time-and-attendance/exports/export-csv-options.dto"; export class CsvGeneratorService { //csv builder and "mise en page" generateCsv(rows: CsvRow[]): Buffer { - const header = [ - 'company_code', - 'external_payroll_id', - 'full_name', - 'bank_code', - 'quantity_hours', - 'amount', - 'week_number', - 'pay_date', - 'holiday_date', - ].join(';') + '\n'; - const body = rows.map(row => { - const full_name = `${String(row.full_name).replace(/"/g, '""')}`; - const quantity_hours = (typeof row.quantity_hours === 'number') ? row.quantity_hours.toFixed(2) : ''; - const amount = (typeof row.amount === 'number') ? row.amount.toFixed(2) : ''; + const quantity_hours = (typeof row.quantite_hre === 'number') ? row.quantite_hre.toFixed(2) : ''; + const amount = (typeof row.montant === 'number') ? row.montant.toFixed(2) : ''; return [ - row.company_code, - row.external_payroll_id, - full_name, - row.bank_code, + row.compagnie_no, + row.employee_matricule, + row.releve ?? '', + row.type_transaction, + row.code, quantity_hours, + row.taux_horaire ?? '', amount, - row.week_number, - row.pay_date, - row.holiday_date ?? '', + row.semaine_no, + row.division_no ?? '', + row.service_no ?? '', + row.departem_no ?? '', + row.sous_departem_no ?? '', + row.date_transaction, + row.premier_jour_absence ?? '', + row.dernier_jour_absence ?? '', ].join(';'); }).join('\n'); - return Buffer.from('\uFEFF' + header + body, 'utf8'); + return Buffer.from('\uFEFF' + body, 'utf8'); } } \ No newline at end of file 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 449dc84..7ca6019 100644 --- a/src/time-and-attendance/exports/services/csv-exports.service.ts +++ b/src/time-and-attendance/exports/services/csv-exports.service.ts @@ -5,6 +5,8 @@ import { computeHours } from "src/common/utils/date-utils"; import { applyHolidayRequalifications, applyOvertimeRequalifications, computeWeekNumber, consolidateRowHoursAndAmountByType, formatDate, resolveCompanyCodes } from "src/time-and-attendance/exports/csv-exports.utils"; import { OvertimeService } from "src/time-and-attendance/domains/services/overtime.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 { BillableShiftType } from "src/time-and-attendance/shifts/shift.types"; @Injectable() export class CsvExportService { @@ -14,13 +16,34 @@ export class CsvExportService { private readonly holiday_service: HolidayService, ) { } - async collectTransaction( - year: number, - period_no: number, - filters: Filters, - // approved: boolean = true - ): Promise { - //fetch period + /** + * Gets all approved shifts and expenses within the year and period number provided, according + * to company and types specified in the filters. Any holiday shifts will be adjusted based on + * prior 4 weeks of work, where applicable. Vacation and Sick shifts will be split into separate + * lines if they are more than one day apart. + * + * 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 { + 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']); + const period = await this.prisma.payPeriods.findFirst({ where: { pay_year: year, pay_period_no: period_no }, select: { period_start: true, period_end: true }, @@ -30,222 +53,110 @@ export class CsvExportService { const start = period.period_start; const end = period.period_end; - //fetch company codes 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 - const { shifts: want_shifts, expenses: want_expense, holiday: want_holiday, vacation: want_vacation } = filters.types; - if (!want_shifts && !want_expense && !want_holiday && !want_vacation) { - throw new BadRequestException(' No export type selected '); - } + if (Object.values(filters.types).every(type => type === false)) + throw new BadRequestException('NO_TYPE_SELECTED'); - const approved_filter = filters.approved ? { is_approved: true } : {}; + const exportedShifts = await 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: BILLABLE_SHIFT_CODES } }, + }, + select: select_csv_shift_lines, + }); - const holiday_code = await this.resolveHolidayTypeCode('HOLIDAY'); - const vacation_code = await this.resolveVacationTypeCode('VACATION'); - const leave_code_filter = [holiday_code, vacation_code]; + 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); + const code = Number(shift!.bank_code.bank_code.slice(1,)); + const isPTO = PTO_SHIFT_CODES.includes(shift.bank_code.bank_code) - //Prisma queries - const promises: Array> = []; - - if (want_shifts) { - promises.push(this.prisma.shifts.findMany({ - where: { - date: { gte: start, lte: end }, - ...approved_filter, - bank_code: { bank_code: { notIn: leave_code_filter } }, - timesheet: { employee: { company_code: { in: company_codes } } }, - }, - select: { - date: true, - start_time: true, - end_time: true, - bank_code: { select: { bank_code: true } }, - timesheet: { - select: { - id: true, - employee: { - select: { - company_code: true, - external_payroll_id: true, - user: { select: { first_name: true, last_name: true, email: true } }, - } - }, - } - }, - }, - })); - } else { - promises.push(Promise.resolve([])); - } - - if (want_holiday) { - promises.push(this.prisma.shifts.findMany({ - where: { - date: { gte: start, lte: end }, - ...approved_filter, - bank_code: { bank_code: holiday_code }, - timesheet: { employee: { company_code: { in: company_codes } } }, - }, - select: { - date: true, - start_time: true, - end_time: true, - bank_code: { select: { bank_code: true } }, - timesheet: { - select: { - id: true, - employee: { - select: { - company_code: true, - external_payroll_id: true, - user: { select: { first_name: true, last_name: true, email: true } }, - } - }, - } - }, - }, - })); - } else { - promises.push(Promise.resolve([])); - } - - if (want_vacation) { - promises.push(this.prisma.shifts.findMany({ - where: { - date: { gte: start, lte: end }, - ...approved_filter, - bank_code: { bank_code: vacation_code }, - timesheet: { employee: { company_code: { in: company_codes } } }, - }, - select: { - date: true, - start_time: true, - end_time: true, - bank_code: { select: { bank_code: true } }, - timesheet: { - select: { - id: true, - employee: { - select: { - company_code: true, - external_payroll_id: true, - user: { select: { first_name: true, last_name: true, email: true } }, - } - }, - } - }, - }, - })); - } else { - promises.push(Promise.resolve([])); - } - - if (want_expense) { - promises.push(this.prisma.expenses.findMany({ - where: { - date: { gte: start, lte: end }, - ...approved_filter, - timesheet: { employee: { company_code: { in: company_codes } } }, - }, - select: { - date: true, - amount: true, - bank_code: { select: { bank_code: true } }, - timesheet: { - select: { - id: true, - employee: { - select: { - company_code: true, - external_payroll_id: true, - user: { select: { first_name: true, last_name: true, email: true } }, - } - }, - } - }, - }, - })); - } else { - promises.push(Promise.resolve([])); - } - - //array of arrays - const [base_shifts, holiday_shifts, vacation_shifts, expenses] = await Promise.all(promises); - //mapping - const rows: InternalCsvRow[] = []; - - const map_shifts = (shift: any, is_holiday: boolean): InternalCsvRow => { - const employee = shift.timesheet.employee; - const week = computeWeekNumber(start, shift.date); return { - company_code: employee.company_code, - external_payroll_id: employee.external_payroll_id, timesheet_id: shift.timesheet.id, - email: shift.timesheet.employee.user.email, shift_date: shift.date, - full_name: `${employee.user.first_name} ${employee.user.last_name}`, - bank_code: shift.bank_code?.bank_code ?? '', - quantity_hours: computeHours(shift.start_time, shift.end_time), - amount: undefined, - week_number: week, - pay_date: formatDate(end), - holiday_date: is_holiday ? formatDate(shift.date) : '', + compagnie_no: employee.company_code, + employee_matricule: employee.external_payroll_id, + releve: 0, + type_transaction: type_transaction, + code: code, + quantite_hre: computeHours(shift!.start_time, shift!.end_time), + taux_horaire: '', + montant: undefined, + semaine_no: week, + division_no: undefined, + service_no: undefined, + departem_no: undefined, + sous_departem_no: undefined, + date_transaction: formatDate(end), + premier_jour_absence: isPTO ? formatDate(shift!.date) : '', + dernier_jour_absence: isPTO ? formatDate(shift!.date) : '', } - }; - //final mapping of all shifts based filters - 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 (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, + }); + }); - for (const expense of expenses) { - const employee = expense.timesheet.employee; - const week = computeWeekNumber(start, expense.date); - rows.push({ - company_code: employee.company_code, - external_payroll_id: employee.external_payroll_id, - timesheet_id: expense.timesheet.id, - email: '', - full_name: `${employee.user.first_name} ${employee.user.last_name}`, - bank_code: expense.bank_code?.bank_code ?? '', - quantity_hours: undefined, - amount: Number(expense.amount), - week_number: week, - pay_date: formatDate(end), - holiday_date: '', - shift_date: expense.date, - }) } - //Final mapping and sorts + // Sort shifts and expenses according to their bank codes rows.sort((a, b) => { - if (a.external_payroll_id !== b.external_payroll_id) { - return a.external_payroll_id - b.external_payroll_id; - } - 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); + if (a.code !== b.code) + return a.code - b.code; + return 0; }); - - //requalifies the real amount paid for holidays - const holiday_rows = await applyHolidayRequalifications(rows, this.holiday_service); - - //groups hours by bank_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); return requalified_rows; } - resolveHolidayTypeCode = async (holiday: string): Promise => { - const holiday_code = await this.prisma.bankCodes.findFirst({ - where: { type: holiday }, + resolveShiftTypeCode = async (shift_type: BillableShiftType[]): Promise => { + const billableBankCodes = await this.prisma.bankCodes.findMany({ + where: { type: { in: shift_type } }, select: { bank_code: true, shifts: { @@ -255,25 +166,11 @@ export class CsvExportService { }, }, }); - if (!holiday_code) throw new BadRequestException('Missing Holiday bank code'); + if (!billableBankCodes) throw new BadRequestException('Missing Shift bank code'); - return holiday_code.bank_code; - } - - resolveVacationTypeCode = async (vacation: string): Promise => { - const vacation_code = await this.prisma.bankCodes.findFirst({ - where: { type: vacation }, - select: { - bank_code: true, - shifts: { - select: { - date: true, - }, - }, - }, - }); - if (!vacation_code) throw new BadRequestException('Missing Vacation bank code'); - return vacation_code.bank_code; + const shiftCodes: string[] = []; + billableBankCodes.map(billableBankCode => shiftCodes.push(billableBankCode.bank_code)); + return shiftCodes; } } diff --git a/src/time-and-attendance/shifts/services/shifts-create.service.ts b/src/time-and-attendance/shifts/services/shifts-create.service.ts index 519712d..958f989 100644 --- a/src/time-and-attendance/shifts/services/shifts-create.service.ts +++ b/src/time-and-attendance/shifts/services/shifts-create.service.ts @@ -145,8 +145,14 @@ export class ShiftsCreateService { if (!result.success) return { success: false, error: result.error }; const valid_hours = result.data; + const valid_hour_amount = Math.floor(result.data); + const valid_minute_amount = Math.round(60 * (valid_hours - valid_hour_amount)); + adjusted_end_time = new Date(normed_shift.data.start_time); - adjusted_end_time.setHours(adjusted_end_time.getHours() + valid_hours); + adjusted_end_time.setHours( + adjusted_end_time.getHours() + valid_hour_amount, + adjusted_end_time.getMinutes() + valid_minute_amount, + ); } //sends data for creation of a shift in db diff --git a/src/time-and-attendance/shifts/shift.types.ts b/src/time-and-attendance/shifts/shift.types.ts new file mode 100644 index 0000000..744d6f9 --- /dev/null +++ b/src/time-and-attendance/shifts/shift.types.ts @@ -0,0 +1 @@ +export type BillableShiftType = 'REGULAR' | 'EVENING' | 'EMERGENCY' | 'VACATION' | 'HOLIDAY' | 'SICK' | 'OVERTIME'; \ No newline at end of file diff --git a/src/time-and-attendance/timesheets/services/timesheet-employee-overview.service.ts b/src/time-and-attendance/timesheets/services/timesheet-employee-overview.service.ts index 009db0d..d1341f8 100644 --- a/src/time-and-attendance/timesheets/services/timesheet-employee-overview.service.ts +++ b/src/time-and-attendance/timesheets/services/timesheet-employee-overview.service.ts @@ -70,6 +70,7 @@ export class GetTimesheetsOverviewService { return { success: true, data: { has_preset_schedule, employee_fullname, timesheets } }; } catch (error) { + console.error(error); return { success: false, error: 'TIMESHEET_NOT_FOUND' } } } diff --git a/src/time-and-attendance/utils/selects.utils.ts b/src/time-and-attendance/utils/selects.utils.ts index a760137..1c4e5f3 100644 --- a/src/time-and-attendance/utils/selects.utils.ts +++ b/src/time-and-attendance/utils/selects.utils.ts @@ -82,4 +82,40 @@ export const select_employee_timesheet = { }, }, }, -}; \ No newline at end of file +}; + +export const select_csv_shift_lines = { + date: true, + start_time: true, + end_time: true, + bank_code: { select: { bank_code: true } }, + timesheet: { + select: { + id: true, + employee: { + select: { + id: true, + company_code: true, + external_payroll_id: true, + }, + }, + }, + }, +} + +export const select_csv_expense_lines = { + date: true, + amount: true, + bank_code: { select: { bank_code: true } }, + timesheet: { + select: { + id: true, + employee: { + select: { + company_code: true, + external_payroll_id: true, + } + }, + } + }, +}