fix(payperiods): refactor general overview method to return all employee overviews.
This commit is contained in:
parent
2958403f08
commit
7912695b8f
|
|
@ -104,6 +104,14 @@
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "employee_email",
|
||||||
|
"required": true,
|
||||||
|
"in": "query",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ generator client {
|
||||||
|
|
||||||
datasource db {
|
datasource db {
|
||||||
provider = "postgresql"
|
provider = "postgresql"
|
||||||
url = env("DATABASE_URL_DEV")
|
url = env("DATABASE_URL_STAGING")
|
||||||
}
|
}
|
||||||
|
|
||||||
model Users {
|
model Users {
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ import { ANCHOR_ISO, MS_PER_DAY, PERIODS_PER_YEAR, PERIOD_DAYS } from "src/commo
|
||||||
//ensures the week starts from sunday
|
//ensures the week starts from sunday
|
||||||
export function weekStartSunday(date_local: Date): Date {
|
export function weekStartSunday(date_local: Date): Date {
|
||||||
const start_date = new Date();
|
const start_date = new Date();
|
||||||
|
start_date.setUTCFullYear(date_local.getUTCFullYear(), date_local.getUTCMonth());
|
||||||
start_date.setDate(date_local.getUTCDate() - date_local.getUTCDay());
|
start_date.setDate(date_local.getUTCDate() - date_local.getUTCDay());
|
||||||
return start_date;
|
return start_date;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ export class ExpenseUpsertService {
|
||||||
|
|
||||||
//push updates and get updated datas
|
//push updates and get updated datas
|
||||||
const expense = await this.prisma.expenses.update({
|
const expense = await this.prisma.expenses.update({
|
||||||
where: { id: dto.id, timesheet_id: dto.timesheet_id },
|
where: { id: dto.id, timesheet_id: timesheet.id },
|
||||||
data,
|
data,
|
||||||
select: expense_select,
|
select: expense_select,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ export class PayPeriodsController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('crew/:year/:periodNumber')
|
@Get('crew/:year/:periodNumber')
|
||||||
@RolesAllowed(RoleEnum.SUPERVISOR)
|
@RolesAllowed(RoleEnum.SUPERVISOR, RoleEnum.ADMIN)
|
||||||
async getCrewOverview(@Req() req,
|
async getCrewOverview(@Req() req,
|
||||||
@Param('year', ParseIntPipe) year: number,
|
@Param('year', ParseIntPipe) year: number,
|
||||||
@Param('periodNumber', ParseIntPipe) period_no: number,
|
@Param('periodNumber', ParseIntPipe) period_no: number,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { PayPeriodOverviewDto } from "../dtos/overview-pay-period.dto";
|
||||||
import { EmployeePeriodOverviewDto } from "../dtos/overview-employee-period.dto";
|
import { EmployeePeriodOverviewDto } from "../dtos/overview-employee-period.dto";
|
||||||
import { PayPeriodDto } from "../dtos/pay-period.dto";
|
import { PayPeriodDto } from "../dtos/pay-period.dto";
|
||||||
import { mapPayPeriodToDto } from "../mappers/pay-periods.mapper";
|
import { mapPayPeriodToDto } from "../mappers/pay-periods.mapper";
|
||||||
|
import { Prisma } from "@prisma/client";
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PayPeriodsQueryService {
|
export class PayPeriodsQueryService {
|
||||||
constructor(private readonly prisma: PrismaService) { }
|
constructor(private readonly prisma: PrismaService) { }
|
||||||
|
|
@ -142,14 +143,17 @@ export class PayPeriodsQueryService {
|
||||||
: new Date(`${period.payday}T00:00:00.000Z`);
|
: new Date(`${period.payday}T00:00:00.000Z`);
|
||||||
|
|
||||||
//restrictEmployeeIds = filter for shifts and expenses by employees
|
//restrictEmployeeIds = filter for shifts and expenses by employees
|
||||||
const where_employee = options?.filtered_employee_ids?.length ? { employee_id: { in: options.filtered_employee_ids } } : {};
|
const where_employee = options?.filtered_employee_ids?.length ?
|
||||||
|
{
|
||||||
|
date: { gte: start, lte: end },
|
||||||
|
timesheet: { employee_id: { in: options.filtered_employee_ids } },
|
||||||
|
}
|
||||||
|
:
|
||||||
|
{ date: { gte: start, lte: end } };
|
||||||
|
|
||||||
// SHIFTS (filtered by crew)
|
// SHIFTS (filtered by crew)
|
||||||
const shifts = await this.prisma.shifts.findMany({
|
const shifts = await this.prisma.shifts.findMany({
|
||||||
where: {
|
where: where_employee,
|
||||||
date: { gte: start, lte: end },
|
|
||||||
timesheet: where_employee,
|
|
||||||
},
|
|
||||||
select: {
|
select: {
|
||||||
start_time: true,
|
start_time: true,
|
||||||
end_time: true,
|
end_time: true,
|
||||||
|
|
@ -177,10 +181,7 @@ export class PayPeriodsQueryService {
|
||||||
|
|
||||||
// EXPENSES (filtered by crew)
|
// EXPENSES (filtered by crew)
|
||||||
const expenses = await this.prisma.expenses.findMany({
|
const expenses = await this.prisma.expenses.findMany({
|
||||||
where: {
|
where: where_employee,
|
||||||
date: { gte: start, lte: end },
|
|
||||||
timesheet: where_employee,
|
|
||||||
},
|
|
||||||
select: {
|
select: {
|
||||||
amount: true,
|
amount: true,
|
||||||
timesheet: {
|
timesheet: {
|
||||||
|
|
@ -228,6 +229,39 @@ export class PayPeriodsQueryService {
|
||||||
is_remote: true,
|
is_remote: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
const all_employees = await this.prisma.employees.findMany({
|
||||||
|
include: {
|
||||||
|
user: {
|
||||||
|
select: {
|
||||||
|
first_name: true,
|
||||||
|
last_name: true,
|
||||||
|
email: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const employee of all_employees) {
|
||||||
|
by_employee.set(employee.id, {
|
||||||
|
email: employee.user.email,
|
||||||
|
employee_name: employee.user.first_name + ' ' + employee.user.last_name,
|
||||||
|
regular_hours: 0,
|
||||||
|
other_hours: {
|
||||||
|
evening_hours: 0,
|
||||||
|
emergency_hours: 0,
|
||||||
|
overtime_hours: 0,
|
||||||
|
sick_hours: 0,
|
||||||
|
holiday_hours: 0,
|
||||||
|
vacation_hours: 0,
|
||||||
|
},
|
||||||
|
total_hours: 0,
|
||||||
|
expenses: 0,
|
||||||
|
mileage: 0,
|
||||||
|
is_approved: true,
|
||||||
|
is_remote: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ensure = (id: number, name: string, email: string) => {
|
const ensure = (id: number, name: string, email: string) => {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Req, UnauthorizedException } from "@nestjs/common";
|
import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query, Req, UnauthorizedException } from "@nestjs/common";
|
||||||
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
||||||
import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service";
|
import { GetTimesheetsOverviewService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-get-overview.service";
|
||||||
import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service";
|
import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service";
|
||||||
|
|
@ -17,11 +17,12 @@ export class TimesheetController {
|
||||||
getTimesheetByPayPeriod(
|
getTimesheetByPayPeriod(
|
||||||
@Req() req,
|
@Req() req,
|
||||||
@Param('year', ParseIntPipe) year: number,
|
@Param('year', ParseIntPipe) year: number,
|
||||||
@Param('period_number', ParseIntPipe) period_number: number
|
@Param('period_number', ParseIntPipe) period_number: number,
|
||||||
|
@Query('employee_email') employee_email?: string,
|
||||||
) {
|
) {
|
||||||
const email = req.user?.email;
|
const email = req.user?.email;
|
||||||
if (!email) throw new UnauthorizedException('Unauthorized User');
|
if (!email) throw new UnauthorizedException('Unauthorized User');
|
||||||
return this.timesheetOverview.getTimesheetsForEmployeeByPeriod(email, year, period_number);
|
return this.timesheetOverview.getTimesheetsForEmployeeByPeriod(email, year, period_number, employee_email);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch('timesheet-approval')
|
@Patch('timesheet-approval')
|
||||||
|
|
|
||||||
|
|
@ -14,16 +14,16 @@ export class Timesheet {
|
||||||
timesheet_id: number;
|
timesheet_id: number;
|
||||||
is_approved: boolean;
|
is_approved: boolean;
|
||||||
days: TimesheetDay[];
|
days: TimesheetDay[];
|
||||||
weekly_hours: TotalHours[];
|
weekly_hours: TotalHours;
|
||||||
weekly_expenses: TotalExpenses[];
|
weekly_expenses: TotalExpenses;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TimesheetDay {
|
export class TimesheetDay {
|
||||||
date: string;
|
date: string;
|
||||||
shifts: Shift[];
|
shifts: Shift[];
|
||||||
expenses: Expense[];
|
expenses: Expense[];
|
||||||
daily_hours: TotalHours[];
|
daily_hours: TotalHours;
|
||||||
daily_expenses: TotalExpenses[];
|
daily_expenses: TotalExpenses;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TotalHours {
|
export class TotalHours {
|
||||||
|
|
|
||||||
|
|
@ -35,15 +35,17 @@ export class GetTimesheetsOverviewService {
|
||||||
//-----------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------
|
||||||
// GET TIMESHEETS FOR A SELECTED EMPLOYEE
|
// GET TIMESHEETS FOR A SELECTED EMPLOYEE
|
||||||
//-----------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------
|
||||||
async getTimesheetsForEmployeeByPeriod(email: string, pay_year: number, pay_period_no: number): Promise<Result<Timesheets, string>> {
|
async getTimesheetsForEmployeeByPeriod(email: string, pay_year: number, pay_period_no: number, employee_email?: string): Promise<Result<Timesheets, string>> {
|
||||||
try {
|
try {
|
||||||
|
const account_email = employee_email ?? email;
|
||||||
|
|
||||||
//find period using year and period_no
|
//find period using year and period_no
|
||||||
const period = await this.prisma.payPeriods.findFirst({ where: { pay_year, pay_period_no } });
|
const period = await this.prisma.payPeriods.findFirst({ where: { pay_year, pay_period_no } });
|
||||||
if (!period) return { success: false, error: `Pay period ${pay_year}-${pay_period_no} not found` };
|
if (!period) return { success: false, error: `Pay period ${pay_year}-${pay_period_no} not found` };
|
||||||
|
|
||||||
//fetch the employee_id using the email
|
//fetch the employee_id using the email
|
||||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
const employee_id = await this.emailResolver.findIdByEmail(account_email);
|
||||||
if (!employee_id.success) return { success: false, error: `employee with email: ${email} not found` + employee_id.error }
|
if (!employee_id.success) return { success: false, error: `employee with email: ${account_email} not found` + employee_id.error }
|
||||||
|
|
||||||
//loads the timesheets related to the fetched pay-period
|
//loads the timesheets related to the fetched pay-period
|
||||||
let rows = await this.loadTimesheets(employee_id.data, period.period_start, period.period_end);
|
let rows = await this.loadTimesheets(employee_id.data, period.period_start, period.period_end);
|
||||||
|
|
@ -132,8 +134,8 @@ export class GetTimesheetsOverviewService {
|
||||||
expenses_by_date.set(date_string, arr);
|
expenses_by_date.set(date_string, arr);
|
||||||
}
|
}
|
||||||
//weekly totals
|
//weekly totals
|
||||||
const weekly_hours: TotalHours[] = [emptyHours()];
|
const weekly_hours: TotalHours = emptyHours();
|
||||||
const weekly_expenses: TotalExpenses[] = [emptyExpenses()];
|
const weekly_expenses: TotalExpenses = emptyExpenses();
|
||||||
|
|
||||||
//map of days
|
//map of days
|
||||||
const days = day_dates.map((date) => {
|
const days = day_dates.map((date) => {
|
||||||
|
|
@ -169,15 +171,15 @@ export class GetTimesheetsOverviewService {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//daily totals
|
//daily totals
|
||||||
const daily_hours = [emptyHours()];
|
const daily_hours = emptyHours();
|
||||||
const daily_expenses = [emptyExpenses()];
|
const daily_expenses = emptyExpenses();
|
||||||
|
|
||||||
//totals by shift types
|
//totals by shift types
|
||||||
for (const shift of shifts_source) {
|
for (const shift of shifts_source) {
|
||||||
const hours = diffOfHours(shift.start_time, shift.end_time);
|
const hours = diffOfHours(shift.start_time, shift.end_time);
|
||||||
const subgroup = hoursSubGroupFromBankCode(shift.bank_code);
|
const subgroup = hoursSubGroupFromBankCode(shift.bank_code);
|
||||||
daily_hours[0][subgroup] += hours;
|
daily_hours[subgroup] += hours;
|
||||||
weekly_hours[0][subgroup] += hours;
|
weekly_hours[subgroup] += hours;
|
||||||
}
|
}
|
||||||
|
|
||||||
//totals by expense types
|
//totals by expense types
|
||||||
|
|
@ -185,20 +187,20 @@ export class GetTimesheetsOverviewService {
|
||||||
const subgroup = expenseSubgroupFromBankCode(expense.bank_code);
|
const subgroup = expenseSubgroupFromBankCode(expense.bank_code);
|
||||||
if (subgroup === 'mileage') {
|
if (subgroup === 'mileage') {
|
||||||
const mileage = num(expense.mileage);
|
const mileage = num(expense.mileage);
|
||||||
daily_expenses[0].mileage += mileage;
|
daily_expenses.mileage += mileage;
|
||||||
weekly_expenses[0].mileage += mileage;
|
weekly_expenses.mileage += mileage;
|
||||||
} else if (subgroup === 'per_diem') {
|
} else if (subgroup === 'per_diem') {
|
||||||
const amount = num(expense.amount);
|
const amount = num(expense.amount);
|
||||||
daily_expenses[0].per_diem += amount;
|
daily_expenses.per_diem += amount;
|
||||||
weekly_expenses[0].per_diem += amount;
|
weekly_expenses.per_diem += amount;
|
||||||
} else if (subgroup === 'on_call') {
|
} else if (subgroup === 'on_call') {
|
||||||
const amount = num(expense.amount);
|
const amount = num(expense.amount);
|
||||||
daily_expenses[0].on_call += amount;
|
daily_expenses.on_call += amount;
|
||||||
weekly_expenses[0].on_call += amount;
|
weekly_expenses.on_call += amount;
|
||||||
} else {
|
} else {
|
||||||
const amount = num(expense.amount);
|
const amount = num(expense.amount);
|
||||||
daily_expenses[0].expenses += amount;
|
daily_expenses.expenses += amount;
|
||||||
weekly_expenses[0].expenses += amount;
|
weekly_expenses.expenses += amount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user