Merge branch 'main' of git.targo.ca:Targo/targo_backend into dev/matthieu/prisma/MariaDB
This commit is contained in:
commit
25e9200c41
|
|
@ -1,4 +1,4 @@
|
||||||
import { Controller, Get, Req, Res, UnauthorizedException, UseGuards } from '@nestjs/common';
|
import { Controller, Get, Post, Req, Res, UnauthorizedException, UseGuards } from '@nestjs/common';
|
||||||
import { OIDCLoginGuard } from '../guards/authentik-auth.guard';
|
import { OIDCLoginGuard } from '../guards/authentik-auth.guard';
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import { UsersService } from 'src/identity-and-account/users-management/services/users.service';
|
import { UsersService } from 'src/identity-and-account/users-management/services/users.service';
|
||||||
|
|
@ -8,19 +8,19 @@ import { Access } from 'src/common/decorators/module-access.decorators';
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly usersService: UsersService,
|
private readonly usersService: UsersService,
|
||||||
){}
|
) { }
|
||||||
|
|
||||||
@UseGuards(OIDCLoginGuard)
|
@UseGuards(OIDCLoginGuard)
|
||||||
@Get('/v1/login')
|
@Get('v1/login')
|
||||||
login() { }
|
login() { }
|
||||||
|
|
||||||
@Get('/callback')
|
@Get('callback')
|
||||||
@UseGuards(OIDCLoginGuard)
|
@UseGuards(OIDCLoginGuard)
|
||||||
loginCallback(@Req() req: Request, @Res() res: Response) {
|
loginCallback(@Req() req: Request, @Res() res: Response) {
|
||||||
res.redirect(process.env.REDIRECT_URL_DEV!);
|
res.redirect(process.env.REDIRECT_URL_DEV!);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('/me')
|
@Get('me')
|
||||||
async getProfile(
|
async getProfile(
|
||||||
@Access('email') email: string,
|
@Access('email') email: string,
|
||||||
@Req() req: Request) {
|
@Req() req: Request) {
|
||||||
|
|
@ -30,4 +30,19 @@ export class AuthController {
|
||||||
return this.usersService.findOneByEmail(email);
|
return this.usersService.findOneByEmail(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('logout')
|
||||||
|
logout(
|
||||||
|
@Req() request: Request,
|
||||||
|
@Res() response: Response,
|
||||||
|
) {
|
||||||
|
request.session.destroy(error => {
|
||||||
|
if (error) {
|
||||||
|
console.error('error during logout: ', error, 'user: ', request.user);
|
||||||
|
}
|
||||||
|
|
||||||
|
response.clearCookie('connect.sid', {
|
||||||
|
path: '/',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ async function bootstrap() {
|
||||||
|
|
||||||
// Enable CORS
|
// Enable CORS
|
||||||
app.enableCors({
|
app.enableCors({
|
||||||
origin: ['http://10.100.251.2:9011', 'http://10.100.251.2:9012', 'http://10.100.251.2:9013', 'http://localhost:9000', 'https://app.targo.ca', 'https://app2.targo.ca','https://staging.app.targo.ca'],
|
origin: ['http://10.100.251.2:9011', 'http://10.100.251.2:9012', 'http://10.100.251.2:9013', 'http://localhost:9000', 'https://app.targo.ca', 'https://portail.targo.ca','https://staging.app.targo.ca'],
|
||||||
credentials: true,
|
credentials: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,11 @@ export class ExpenseController {
|
||||||
|
|
||||||
@Delete('delete/:expense_id')
|
@Delete('delete/:expense_id')
|
||||||
@ModuleAccessAllowed(ModulesEnum.timesheets)
|
@ModuleAccessAllowed(ModulesEnum.timesheets)
|
||||||
remove(@Param('expense_id') expense_id: number): Promise<Result<number, string>> {
|
remove(
|
||||||
return this.deleteService.deleteExpense(expense_id);
|
@Param('expense_id') expense_id: number,
|
||||||
|
@Access('email') email: string,
|
||||||
|
): Promise<Result<number, string>> {
|
||||||
|
return this.deleteService.deleteExpense(expense_id, email);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { BankCodesResolver } from "src/common/mappers/bank-type-id.mapper";
|
||||||
import { EmployeeTimesheetResolver } from "src/common/mappers/timesheet.mapper";
|
import { EmployeeTimesheetResolver } from "src/common/mappers/timesheet.mapper";
|
||||||
import { ExpenseDeleteService } from "src/time-and-attendance/expenses/services/expense-delete.service";
|
import { ExpenseDeleteService } from "src/time-and-attendance/expenses/services/expense-delete.service";
|
||||||
import { ExpenseCreateService } from "src/time-and-attendance/expenses/services/expense-create.service";
|
import { ExpenseCreateService } from "src/time-and-attendance/expenses/services/expense-create.service";
|
||||||
|
import { PayPeriodEventService } from "src/time-and-attendance/pay-period/services/pay-period-event.service";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
controllers: [ExpenseController],
|
controllers: [ExpenseController],
|
||||||
|
|
@ -16,6 +17,7 @@ import { ExpenseCreateService } from "src/time-and-attendance/expenses/services/
|
||||||
EmailToIdResolver,
|
EmailToIdResolver,
|
||||||
BankCodesResolver,
|
BankCodesResolver,
|
||||||
EmployeeTimesheetResolver,
|
EmployeeTimesheetResolver,
|
||||||
|
PayPeriodEventService,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { toStringFromDate, weekStartSunday } from "src/common/utils/date-utils";
|
||||||
import { PrismaService } from "prisma/postgres/prisma-postgres.service";
|
import { PrismaService } from "prisma/postgres/prisma-postgres.service";
|
||||||
import { ExpenseDto } from "src/time-and-attendance/expenses/expense-create.dto";
|
import { ExpenseDto } from "src/time-and-attendance/expenses/expense-create.dto";
|
||||||
import { normalizeAndParseExpenseDto } from "src/time-and-attendance/expenses/expense.utils";
|
import { normalizeAndParseExpenseDto } from "src/time-and-attendance/expenses/expense.utils";
|
||||||
|
import { PayPeriodEventService } from "src/time-and-attendance/pay-period/services/pay-period-event.service";
|
||||||
import { expense_select } from "src/time-and-attendance/utils/selects.utils";
|
import { expense_select } from "src/time-and-attendance/utils/selects.utils";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -14,6 +15,7 @@ export class ExpenseCreateService {
|
||||||
private readonly prisma: PrismaService,
|
private readonly prisma: PrismaService,
|
||||||
private readonly emailResolver: EmailToIdResolver,
|
private readonly emailResolver: EmailToIdResolver,
|
||||||
private readonly typeResolver: BankCodesResolver,
|
private readonly typeResolver: BankCodesResolver,
|
||||||
|
private readonly payPeriodEventService: PayPeriodEventService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
|
|
@ -64,8 +66,15 @@ export class ExpenseCreateService {
|
||||||
attachment: expense.attachment ?? undefined,
|
attachment: expense.attachment ?? undefined,
|
||||||
supervisor_comment: expense.supervisor_comment ?? undefined,
|
supervisor_comment: expense.supervisor_comment ?? undefined,
|
||||||
};
|
};
|
||||||
return { success: true, data: created };
|
|
||||||
|
|
||||||
|
// notify timesheet approval observers of changes
|
||||||
|
this.payPeriodEventService.emit({
|
||||||
|
employee_email: email,
|
||||||
|
event_type: 'expense',
|
||||||
|
action: 'create',
|
||||||
|
});
|
||||||
|
|
||||||
|
return { success: true, data: created };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { success: false, error: 'INVALID_EXPENSE' };
|
return { success: false, error: 'INVALID_EXPENSE' };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,40 @@
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { Result } from "src/common/errors/result-error.factory";
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
import { PrismaService } from "prisma/postgres/prisma-postgres.service";
|
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
||||||
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
import { PayPeriodEventService } from "src/time-and-attendance/pay-period/services/pay-period-event.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ExpenseDeleteService {
|
export class ExpenseDeleteService {
|
||||||
constructor(private readonly prisma: PrismaService) { }
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly payPeriodEventService: PayPeriodEventService,
|
||||||
|
private readonly emailResolver: EmailToIdResolver,
|
||||||
|
){}
|
||||||
|
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
// DELETE
|
// DELETE
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
async deleteExpense(expense_id: number): Promise<Result<number, string>> {
|
async deleteExpense(expense_id: number, email: string): Promise<Result<number, string>> {
|
||||||
|
// get employee id of employee who made delete request
|
||||||
|
const employee = await this.emailResolver.findIdByEmail(email);
|
||||||
|
|
||||||
|
if (!employee.success) return employee;
|
||||||
|
|
||||||
|
// confirm ownership of expense to employee who made request
|
||||||
|
const expense = await this.prisma.expenses.findUnique({
|
||||||
|
where: { id: expense_id},
|
||||||
|
select: {
|
||||||
|
timesheet: {
|
||||||
|
select: {
|
||||||
|
employee_id: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!expense || expense.timesheet.employee_id !== employee.data) return { success: false, error: 'EXPENSE_NOT_FOUND'};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.prisma.$transaction(async (tx) => {
|
await this.prisma.$transaction(async (tx) => {
|
||||||
const expense = await tx.expenses.findUnique({
|
const expense = await tx.expenses.findUnique({
|
||||||
|
|
@ -21,6 +46,14 @@ export class ExpenseDeleteService {
|
||||||
await tx.expenses.delete({ where: { id: expense.id } });
|
await tx.expenses.delete({ where: { id: expense.id } });
|
||||||
return { success: true, data: expense.id };
|
return { success: true, data: expense.id };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// notify timesheet-approval observers of changes
|
||||||
|
this.payPeriodEventService.emit({
|
||||||
|
employee_email: email,
|
||||||
|
event_type: 'expense',
|
||||||
|
action: 'delete',
|
||||||
|
});
|
||||||
|
|
||||||
return { success: true, data: expense_id };
|
return { success: true, data: expense_id };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { success: false, error: `EXPENSE_NOT_FOUND` };
|
return { success: false, error: `EXPENSE_NOT_FOUND` };
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { BankCodesResolver } from "src/common/mappers/bank-type-id.mapper";
|
||||||
import { ExpenseDto } from "src/time-and-attendance/expenses/expense-create.dto";
|
import { ExpenseDto } from "src/time-and-attendance/expenses/expense-create.dto";
|
||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import { normalizeAndParseExpenseDto } from "src/time-and-attendance/expenses/expense.utils";
|
import { normalizeAndParseExpenseDto } from "src/time-and-attendance/expenses/expense.utils";
|
||||||
|
import { PayPeriodEventService } from "src/time-and-attendance/pay-period/services/pay-period-event.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ExpenseUpdateService {
|
export class ExpenseUpdateService {
|
||||||
|
|
@ -15,6 +16,7 @@ export class ExpenseUpdateService {
|
||||||
private readonly prisma: PrismaService,
|
private readonly prisma: PrismaService,
|
||||||
private readonly emailResolver: EmailToIdResolver,
|
private readonly emailResolver: EmailToIdResolver,
|
||||||
private readonly typeResolver: BankCodesResolver,
|
private readonly typeResolver: BankCodesResolver,
|
||||||
|
private readonly payPeriodEventService: PayPeriodEventService,
|
||||||
) { }
|
) { }
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
// UPDATE
|
// UPDATE
|
||||||
|
|
@ -66,6 +68,17 @@ export class ExpenseUpdateService {
|
||||||
attachment: expense.attachment ?? undefined,
|
attachment: expense.attachment ?? undefined,
|
||||||
supervisor_comment: expense.supervisor_comment ?? undefined,
|
supervisor_comment: expense.supervisor_comment ?? undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// notify timesheet-approval observers of changes, but only if it came
|
||||||
|
// from timesheet and not timesheet-approval (no employee_email)
|
||||||
|
if (!employee_email) {
|
||||||
|
this.payPeriodEventService.emit({
|
||||||
|
employee_email: email,
|
||||||
|
event_type: 'expense',
|
||||||
|
action: 'update',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return { success: true, data: updated };
|
return { success: true, data: updated };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { success: false, error: 'EXPENSE_NOT_FOUND' };
|
return { success: false, error: 'EXPENSE_NOT_FOUND' };
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
export class PayPeriodEvent {
|
||||||
|
employee_email: string;
|
||||||
|
event_type: 'expense' | 'shift' | 'preset';
|
||||||
|
action: 'create' | 'update' | 'delete';
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Body, Controller, Get, Param, ParseIntPipe, Patch } from "@nestjs/common";
|
import { Body, Controller, Get, Param, MessageEvent, ParseIntPipe, Patch, Sse } from "@nestjs/common";
|
||||||
import { PayPeriodOverviewDto } from "./dtos/overview-pay-period.dto";
|
import { PayPeriodOverviewDto } from "./dtos/overview-pay-period.dto";
|
||||||
import { PayPeriodsQueryService } from "./services/pay-periods-query.service";
|
import { PayPeriodsQueryService } from "./services/pay-periods-query.service";
|
||||||
import { PayPeriodsCommandService } from "./services/pay-periods-command.service";
|
import { PayPeriodsCommandService } from "./services/pay-periods-command.service";
|
||||||
|
|
@ -6,6 +6,8 @@ import { Result } from "src/common/errors/result-error.factory";
|
||||||
import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators";
|
import { ModuleAccessAllowed } from "src/common/decorators/modules-guard.decorators";
|
||||||
import { Modules as ModulesEnum } from ".prisma/client";
|
import { Modules as ModulesEnum } from ".prisma/client";
|
||||||
import { GetOverviewService } from "src/time-and-attendance/pay-period/services/pay-periods-build-overview.service";
|
import { GetOverviewService } from "src/time-and-attendance/pay-period/services/pay-periods-build-overview.service";
|
||||||
|
import { map, Observable } from "rxjs";
|
||||||
|
import { PayPeriodEventService } from "src/time-and-attendance/pay-period/services/pay-period-event.service";
|
||||||
|
|
||||||
@Controller('pay-periods')
|
@Controller('pay-periods')
|
||||||
export class PayPeriodsController {
|
export class PayPeriodsController {
|
||||||
|
|
@ -14,8 +16,14 @@ export class PayPeriodsController {
|
||||||
private readonly queryService: PayPeriodsQueryService,
|
private readonly queryService: PayPeriodsQueryService,
|
||||||
private readonly getOverviewService: GetOverviewService,
|
private readonly getOverviewService: GetOverviewService,
|
||||||
private readonly commandService: PayPeriodsCommandService,
|
private readonly commandService: PayPeriodsCommandService,
|
||||||
|
private readonly payPeriodEventService: PayPeriodEventService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
|
@Sse("subscribe")
|
||||||
|
sse(): Observable<MessageEvent> {
|
||||||
|
return this.payPeriodEventService.stream().pipe(map(event => ({ data: event, })));
|
||||||
|
}
|
||||||
|
|
||||||
@Get("date/:date")
|
@Get("date/:date")
|
||||||
@ModuleAccessAllowed(ModulesEnum.timesheets)
|
@ModuleAccessAllowed(ModulesEnum.timesheets)
|
||||||
async findByDate(@Param("date") date: string) {
|
async findByDate(@Param("date") date: string) {
|
||||||
|
|
@ -38,9 +46,9 @@ export class PayPeriodsController {
|
||||||
@Body('timesheet_ids') timesheet_ids: number[],
|
@Body('timesheet_ids') timesheet_ids: number[],
|
||||||
@Body('is_approved') is_approved: boolean,
|
@Body('is_approved') is_approved: boolean,
|
||||||
): Promise<Result<{ shifts: number, expenses: number }, string>> {
|
): Promise<Result<{ shifts: number, expenses: number }, string>> {
|
||||||
if (!email) return {success: false, error: 'EMAIL_REQUIRED'};
|
if (!email) return { success: false, error: 'EMAIL_REQUIRED' };
|
||||||
if (!timesheet_ids || timesheet_ids.length < 1) return {success: false, error: 'TIMESHEET_ID_REQUIRED'};
|
if (!timesheet_ids || timesheet_ids.length < 1) return { success: false, error: 'TIMESHEET_ID_REQUIRED' };
|
||||||
if (is_approved === null) return {success: false, error: 'APPROVAL_STATUS_REQUIRED'}
|
if (is_approved === null) return { success: false, error: 'APPROVAL_STATUS_REQUIRED' }
|
||||||
return this.commandService.bulkApproveEmployee(email, timesheet_ids, is_approved);
|
return this.commandService.bulkApproveEmployee(email, timesheet_ids, is_approved);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { PayPeriodsCommandService } from "src/time-and-attendance/pay-period/ser
|
||||||
import { TimesheetsModule } from "src/time-and-attendance/timesheets/timesheets.module";
|
import { TimesheetsModule } from "src/time-and-attendance/timesheets/timesheets.module";
|
||||||
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
||||||
import { GetOverviewService } from "src/time-and-attendance/pay-period/services/pay-periods-build-overview.service";
|
import { GetOverviewService } from "src/time-and-attendance/pay-period/services/pay-periods-build-overview.service";
|
||||||
|
import { PayPeriodEventService } from "src/time-and-attendance/pay-period/services/pay-period-event.service";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports:[TimesheetsModule],
|
imports:[TimesheetsModule],
|
||||||
|
|
@ -13,6 +14,7 @@ import { GetOverviewService } from "src/time-and-attendance/pay-period/services/
|
||||||
PayPeriodsQueryService,
|
PayPeriodsQueryService,
|
||||||
PayPeriodsCommandService,
|
PayPeriodsCommandService,
|
||||||
GetOverviewService,
|
GetOverviewService,
|
||||||
|
PayPeriodEventService,
|
||||||
EmailToIdResolver,
|
EmailToIdResolver,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { Observable, Subject } from "rxjs";
|
||||||
|
import { PayPeriodEvent } from "src/time-and-attendance/pay-period/dtos/pay-period-event.dto";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PayPeriodEventService {
|
||||||
|
private readonly pay_period_events$ = new Subject<PayPeriodEvent>();
|
||||||
|
|
||||||
|
emit(event: PayPeriodEvent) {
|
||||||
|
this.pay_period_events$.next(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
stream(): Observable<PayPeriodEvent> {
|
||||||
|
return this.pay_period_events$.asObservable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -50,11 +50,22 @@ export class SchedulePresetsController {
|
||||||
@Post('apply-preset')
|
@Post('apply-preset')
|
||||||
@ModuleAccessAllowed(ModulesEnum.timesheets)
|
@ModuleAccessAllowed(ModulesEnum.timesheets)
|
||||||
async applyPresetToTimesheet(
|
async applyPresetToTimesheet(
|
||||||
@Access('email') email: string, @Body('timesheet_id') timesheet_id: number,
|
@Access('email') email: string,
|
||||||
|
@Body('timesheet_id') timesheet_id: number,
|
||||||
) {
|
) {
|
||||||
return await this.applyService.applyPresetToTimesheet(email, timesheet_id);
|
return await this.applyService.applyPresetToTimesheet(email, timesheet_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('apply-preset/:email')
|
||||||
|
@ModuleAccessAllowed(ModulesEnum.timesheets_approval)
|
||||||
|
async applyPresetToTimesheetFromApproval(
|
||||||
|
@Access('email') email: string,
|
||||||
|
@Body('timesheet_id') timesheet_id: number,
|
||||||
|
@Param('email') employee_email: string,
|
||||||
|
) {
|
||||||
|
return await this.applyService.applyPresetToTimesheet(email, timesheet_id, employee_email);
|
||||||
|
}
|
||||||
|
|
||||||
@Post('apply-day-preset')
|
@Post('apply-day-preset')
|
||||||
@ModuleAccessAllowed(ModulesEnum.timesheets)
|
@ModuleAccessAllowed(ModulesEnum.timesheets)
|
||||||
async applyPresetToDay(
|
async applyPresetToDay(
|
||||||
|
|
@ -65,4 +76,16 @@ export class SchedulePresetsController {
|
||||||
) {
|
) {
|
||||||
return await this.applyService.applyPresetToDay(email, timesheet_id, week_day_index, date);
|
return await this.applyService.applyPresetToDay(email, timesheet_id, week_day_index, date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('apply-day-preset/:email')
|
||||||
|
@ModuleAccessAllowed(ModulesEnum.timesheets_approval)
|
||||||
|
async applyPresetToDayFromApproval(
|
||||||
|
@Access('email') email: string,
|
||||||
|
@Body('timesheet_id') timesheet_id: number,
|
||||||
|
@Body('week_day_index') week_day_index: number,
|
||||||
|
@Body('date') date: string,
|
||||||
|
@Param('email') employee_email: string,
|
||||||
|
) {
|
||||||
|
return await this.applyService.applyPresetToDay(email, timesheet_id, week_day_index, date, employee_email);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ import { ShiftsCreateService } from "src/time-and-attendance/shifts/services/shi
|
||||||
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
|
import { VacationService } from "src/time-and-attendance/domains/services/vacation.service";
|
||||||
import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service";
|
import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service";
|
||||||
import { BankedHoursService } from "src/time-and-attendance/domains/services/banking-hours.service";
|
import { BankedHoursService } from "src/time-and-attendance/domains/services/banking-hours.service";
|
||||||
|
import { PayPeriodEventService } from "../pay-period/services/pay-period-event.service";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -30,6 +31,7 @@ import { BankedHoursService } from "src/time-and-attendance/domains/services/ban
|
||||||
VacationService,
|
VacationService,
|
||||||
SickLeaveService,
|
SickLeaveService,
|
||||||
BankedHoursService,
|
BankedHoursService,
|
||||||
|
PayPeriodEventService,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
SchedulePresetsGetService,
|
SchedulePresetsGetService,
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { timesheet_select } from "src/time-and-attendance/utils/selects.utils";
|
||||||
import { ShiftDto } from "src/time-and-attendance/shifts/shift.dto";
|
import { ShiftDto } from "src/time-and-attendance/shifts/shift.dto";
|
||||||
import { WEEKDAY_MAP } from "src/time-and-attendance/schedule-presets/schedule-presets.dto";
|
import { WEEKDAY_MAP } from "src/time-and-attendance/schedule-presets/schedule-presets.dto";
|
||||||
import { $Enums, Prisma, SchedulePresetShifts } from "@prisma/client";
|
import { $Enums, Prisma, SchedulePresetShifts } from "@prisma/client";
|
||||||
|
import { PayPeriodEventService } from "src/time-and-attendance/pay-period/services/pay-period-event.service";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|
@ -20,10 +21,12 @@ export class SchedulePresetsApplyService {
|
||||||
private readonly emailResolver: EmailToIdResolver,
|
private readonly emailResolver: EmailToIdResolver,
|
||||||
private readonly shiftService: ShiftsCreateService,
|
private readonly shiftService: ShiftsCreateService,
|
||||||
private readonly typeResolver: BankCodesResolver,
|
private readonly typeResolver: BankCodesResolver,
|
||||||
|
private readonly payPeriodEventService: PayPeriodEventService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
async applyPresetToTimesheet(email: string, timesheet_id: number): Promise<Result<boolean, string>> {
|
async applyPresetToTimesheet(email: string, timesheet_id: number, employee_email?: string): Promise<Result<boolean, string>> {
|
||||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
const user_email = employee_email ?? email;
|
||||||
|
const employee_id = await this.emailResolver.findIdByEmail(user_email);
|
||||||
if (!employee_id.success) return { success: false, error: 'EMPLOYEE_NOT_FOUND' };
|
if (!employee_id.success) return { success: false, error: 'EMPLOYEE_NOT_FOUND' };
|
||||||
|
|
||||||
const employee_default_schedule_preset = await this.prisma.employees.findFirst({
|
const employee_default_schedule_preset = await this.prisma.employees.findFirst({
|
||||||
|
|
@ -74,16 +77,27 @@ export class SchedulePresetsApplyService {
|
||||||
created_shifts.push(shift.data);
|
created_shifts.push(shift.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await this.shiftService.createOneOrManyShifts(email, created_shifts);
|
const response = await this.shiftService.createOneOrManyShifts(user_email, created_shifts, false);
|
||||||
if (response.success)
|
if (response.success) {
|
||||||
|
// notify timesheet-approval observers of changes
|
||||||
|
if (!employee_email) {
|
||||||
|
this.payPeriodEventService.emit({
|
||||||
|
employee_email: user_email,
|
||||||
|
event_type: 'preset',
|
||||||
|
action: 'create',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return { success: true, data: true };
|
return { success: true, data: true };
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return { success: false, error: 'There was an error applying presets for this week' };
|
return { success: false, error: 'There was an error applying presets for this week' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async applyPresetToDay(email: string, timesheet_id: number, week_day_index: number, date: string): Promise<Result<boolean, string>> {
|
async applyPresetToDay(email: string, timesheet_id: number, week_day_index: number, date: string, employee_email?: string): Promise<Result<boolean, string>> {
|
||||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
const user_email = employee_email ?? email;
|
||||||
|
const employee_id = await this.emailResolver.findIdByEmail(user_email);
|
||||||
if (!employee_id.success) return { success: false, error: 'EMPLOYEE_NOT_FOUND' };
|
if (!employee_id.success) return { success: false, error: 'EMPLOYEE_NOT_FOUND' };
|
||||||
const week_day = Object.keys(WEEKDAY_MAP)[week_day_index];
|
const week_day = Object.keys(WEEKDAY_MAP)[week_day_index];
|
||||||
|
|
||||||
|
|
@ -116,9 +130,19 @@ export class SchedulePresetsApplyService {
|
||||||
|
|
||||||
if (!created_shift.success)
|
if (!created_shift.success)
|
||||||
return { success: false, error: 'SHIFT_CREATE_FAILED' }
|
return { success: false, error: 'SHIFT_CREATE_FAILED' }
|
||||||
|
|
||||||
await this.shiftService.createShift(employee_id.data, created_shift.data);
|
await this.shiftService.createShift(employee_id.data, created_shift.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notify timesheet-approval observers of changes
|
||||||
|
if (!employee_email) {
|
||||||
|
this.payPeriodEventService.emit({
|
||||||
|
employee_email: user_email,
|
||||||
|
event_type: 'preset',
|
||||||
|
action: 'create',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return { success: true, data: true };
|
return { success: true, data: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,7 +159,7 @@ export class SchedulePresetsApplyService {
|
||||||
is_approved: false,
|
is_approved: false,
|
||||||
is_remote: preset.is_remote!,
|
is_remote: preset.is_remote!,
|
||||||
};
|
};
|
||||||
|
|
||||||
return { success: true, data: shift };
|
return { success: true, data: shift };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ import { VacationService } from "src/time-and-attendance/domains/services/vacati
|
||||||
import { BankedHoursService } from "src/time-and-attendance/domains/services/banking-hours.service";
|
import { BankedHoursService } from "src/time-and-attendance/domains/services/banking-hours.service";
|
||||||
import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service";
|
import { SickLeaveService } from "src/time-and-attendance/domains/services/sick-leave.service";
|
||||||
import { paid_time_off_types } from "src/time-and-attendance/paid-time-off/paid-time-off.dto";
|
import { paid_time_off_types } from "src/time-and-attendance/paid-time-off/paid-time-off.dto";
|
||||||
|
import { PayPeriodEventService } from "src/time-and-attendance/pay-period/services/pay-period-event.service";
|
||||||
|
import { Modules } from "@prisma/client";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ShiftsCreateService {
|
export class ShiftsCreateService {
|
||||||
|
|
@ -21,15 +23,16 @@ export class ShiftsCreateService {
|
||||||
private readonly vacationService: VacationService,
|
private readonly vacationService: VacationService,
|
||||||
private readonly bankingService: BankedHoursService,
|
private readonly bankingService: BankedHoursService,
|
||||||
private readonly sickService: SickLeaveService,
|
private readonly sickService: SickLeaveService,
|
||||||
|
private readonly payPeriodEventService: PayPeriodEventService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
// CREATE WRAPPER FUNCTION FOR ONE OR MANY INPUT
|
// CREATE WRAPPER FUNCTION FOR ONE OR MANY INPUT
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
async createOneOrManyShifts(email: string, shifts: ShiftDto[]): Promise<Result<boolean, string>> {
|
async createOneOrManyShifts(email: string, shifts: ShiftDto[], is_from_timesheet: boolean = true): Promise<Result<boolean, string>> {
|
||||||
try {
|
try {
|
||||||
//verify if array is empty or not
|
//verify if array is empty or not
|
||||||
if (!Array.isArray(shifts) || shifts.length === 0) return { success: false, error: 'No data received' };
|
if (!Array.isArray(shifts) || shifts.length === 0) return { success: false, error: 'NO_DATA_RECEIVED' };
|
||||||
|
|
||||||
//verify if email is valid or not
|
//verify if email is valid or not
|
||||||
const employee_id = await this.emailResolver.findIdByEmail(email);
|
const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||||
|
|
@ -57,6 +60,15 @@ export class ShiftsCreateService {
|
||||||
//verify if shifts were created and returns an array of errors if needed
|
//verify if shifts were created and returns an array of errors if needed
|
||||||
if (created_shifts.length === 0) return { success: false, error: errors.join(' | ') || 'No shift created' };
|
if (created_shifts.length === 0) return { success: false, error: errors.join(' | ') || 'No shift created' };
|
||||||
|
|
||||||
|
// push to event service to notify timesheet-approval subscribers of change
|
||||||
|
if (is_from_timesheet) {
|
||||||
|
this.payPeriodEventService.emit({
|
||||||
|
employee_email: email,
|
||||||
|
event_type: 'shift',
|
||||||
|
action: 'create'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// returns array of created shifts
|
// returns array of created shifts
|
||||||
return { success: true, data: true }
|
return { success: true, data: true }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -150,6 +162,7 @@ export class ShiftsCreateService {
|
||||||
comment: dto.comment ?? '',
|
comment: dto.comment ?? '',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
//builds an object to return for display in the frontend
|
//builds an object to return for display in the frontend
|
||||||
const shift: ShiftDto = {
|
const shift: ShiftDto = {
|
||||||
id: created_shift.id,
|
id: created_shift.id,
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
import { Result } from "src/common/errors/result-error.factory";
|
import { Result } from "src/common/errors/result-error.factory";
|
||||||
|
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
||||||
import { computeHours } from "src/common/utils/date-utils";
|
import { computeHours } from "src/common/utils/date-utils";
|
||||||
import { PrismaService } from "prisma/postgres/prisma-postgres.service";
|
import { PrismaService } from "prisma/postgres/prisma-postgres.service";
|
||||||
import { PaidTimeOFfBankHoursService } from "src/time-and-attendance/paid-time-off/paid-time-off.service";
|
import { PaidTimeOFfBankHoursService } from "src/time-and-attendance/paid-time-off/paid-time-off.service";
|
||||||
|
import { PayPeriodEventService } from "src/time-and-attendance/pay-period/services/pay-period-event.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ShiftsDeleteService {
|
export class ShiftsDeleteService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly prisma: PrismaService,
|
private readonly prisma: PrismaService,
|
||||||
private readonly paidTimeOffService: PaidTimeOFfBankHoursService,
|
private readonly paidTimeOffService: PaidTimeOFfBankHoursService,
|
||||||
|
private readonly emailResolver: EmailToIdResolver,
|
||||||
|
private readonly payPeriodEventService: PayPeriodEventService,
|
||||||
) { }
|
) { }
|
||||||
//_________________________________________________________________
|
//_________________________________________________________________
|
||||||
// DELETE
|
// DELETE
|
||||||
|
|
@ -16,8 +20,29 @@ export class ShiftsDeleteService {
|
||||||
//finds shifts using shit_ids
|
//finds shifts using shit_ids
|
||||||
//ajust paid-time-off banks
|
//ajust paid-time-off banks
|
||||||
//blocs deletion if approved
|
//blocs deletion if approved
|
||||||
async deleteShift(shift_id: number): Promise<Result<number, string>> {
|
async deleteShift(shift_id: number, email: string, is_from_timesheet: boolean = true): Promise<Result<number, string>> {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
//verify if email is valid or not
|
||||||
|
const employee_id = await this.emailResolver.findIdByEmail(email);
|
||||||
|
if (!employee_id.success) return { success: false, error: employee_id.error };
|
||||||
|
|
||||||
|
// check if shift actually belongs to employee
|
||||||
|
const shift = await this.prisma.shifts.findUnique({
|
||||||
|
where: { id: shift_id },
|
||||||
|
select: {
|
||||||
|
timesheet: {
|
||||||
|
select: {
|
||||||
|
employee_id: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!shift || shift.timesheet.employee_id !== employee_id.data)
|
||||||
|
return { success: false, error: 'SHIFT_NOT_FOUND'}
|
||||||
|
|
||||||
|
// return deletion result
|
||||||
return await this.prisma.$transaction(async (tx) => {
|
return await this.prisma.$transaction(async (tx) => {
|
||||||
const shift = await tx.shifts.findUnique({
|
const shift = await tx.shifts.findUnique({
|
||||||
where: { id: shift_id },
|
where: { id: shift_id },
|
||||||
|
|
@ -31,9 +56,9 @@ export class ShiftsDeleteService {
|
||||||
bank_code: { select: { type: true } },
|
bank_code: { select: { type: true } },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!shift) return { success: false, error: `SHIFT_NOT_FOUND` };
|
if (!shift) return { success: false, error: `SHIFT_NOT_FOUND` };
|
||||||
if (shift.is_approved) return { success: false, error: 'APPROUVED_SHIFT' };
|
if (shift.is_approved) return { success: false, error: 'APPROVED_SHIFT' };
|
||||||
|
|
||||||
//call to ajust paid_time_off hour banks
|
//call to ajust paid_time_off hour banks
|
||||||
await this.paidTimeOffService.updatePaidTimeoffBankHoursWhenShiftDelete(
|
await this.paidTimeOffService.updatePaidTimeoffBankHoursWhenShiftDelete(
|
||||||
|
|
@ -43,6 +68,16 @@ export class ShiftsDeleteService {
|
||||||
shift.timesheet.employee_id
|
shift.timesheet.employee_id
|
||||||
);
|
);
|
||||||
await tx.shifts.delete({ where: { id: shift_id } });
|
await tx.shifts.delete({ where: { id: shift_id } });
|
||||||
|
|
||||||
|
// push to event service to notify timesheet-approval subscribers of change
|
||||||
|
if (is_from_timesheet) {
|
||||||
|
this.payPeriodEventService.emit({
|
||||||
|
employee_email: email,
|
||||||
|
event_type: 'shift',
|
||||||
|
action: 'delete'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return { success: true, data: shift.id };
|
return { success: true, data: shift.id };
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { ShiftDto } from "src/time-and-attendance/shifts/shift.dto";
|
||||||
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
import { EmailToIdResolver } from "src/common/mappers/email-id.mapper";
|
||||||
import { PaidTimeOFfBankHoursService } from "src/time-and-attendance/paid-time-off/paid-time-off.service";
|
import { PaidTimeOFfBankHoursService } from "src/time-and-attendance/paid-time-off/paid-time-off.service";
|
||||||
import { paid_time_off_types } from "src/time-and-attendance/paid-time-off/paid-time-off.dto";
|
import { paid_time_off_types } from "src/time-and-attendance/paid-time-off/paid-time-off.dto";
|
||||||
|
import { PayPeriodEventService } from "src/time-and-attendance/pay-period/services/pay-period-event.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ShiftsUpdateService {
|
export class ShiftsUpdateService {
|
||||||
|
|
@ -21,9 +22,10 @@ export class ShiftsUpdateService {
|
||||||
private readonly timesheetResolver: EmployeeTimesheetResolver,
|
private readonly timesheetResolver: EmployeeTimesheetResolver,
|
||||||
private readonly emailResolver: EmailToIdResolver,
|
private readonly emailResolver: EmailToIdResolver,
|
||||||
private readonly paidTimeOffService: PaidTimeOFfBankHoursService,
|
private readonly paidTimeOffService: PaidTimeOFfBankHoursService,
|
||||||
|
private readonly payPeriodEventService: PayPeriodEventService,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
async updateOneOrManyShifts(shifts: ShiftDto[], email: string): Promise<Result<boolean, string>> {
|
async updateOneOrManyShifts(shifts: ShiftDto[], email: string, is_from_timesheet: boolean = true): Promise<Result<boolean, string>> {
|
||||||
try {
|
try {
|
||||||
//verify if array is empty or not
|
//verify if array is empty or not
|
||||||
if (!Array.isArray(shifts) || shifts.length === 0) return { success: false, error: 'No data received' };
|
if (!Array.isArray(shifts) || shifts.length === 0) return { success: false, error: 'No data received' };
|
||||||
|
|
@ -54,6 +56,15 @@ export class ShiftsUpdateService {
|
||||||
//verify if shifts were updated and returns an array of errors if needed
|
//verify if shifts were updated and returns an array of errors if needed
|
||||||
if (updated_shifts.length === 0) return { success: false, error: errors.join(' | ') || 'No shift updated' };
|
if (updated_shifts.length === 0) return { success: false, error: errors.join(' | ') || 'No shift updated' };
|
||||||
|
|
||||||
|
// push to event service to notify timesheet-approval subscribers of change
|
||||||
|
if (is_from_timesheet) {
|
||||||
|
this.payPeriodEventService.emit({
|
||||||
|
employee_email: email,
|
||||||
|
event_type: 'shift',
|
||||||
|
action: 'update'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// returns array of updated shifts
|
// returns array of updated shifts
|
||||||
return { success: true, data: true }
|
return { success: true, data: true }
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ export class ShiftController {
|
||||||
|
|
||||||
@Post('create/:email')
|
@Post('create/:email')
|
||||||
@ModuleAccessAllowed(ModulesEnum.timesheets_approval)
|
@ModuleAccessAllowed(ModulesEnum.timesheets_approval)
|
||||||
createBatchByTimesheetsApproval(@Param('email') email:string, @Body() dtos: ShiftDto[]): Promise<Result<boolean, string>> {
|
createBatchByTimesheetsApproval(@Param('email') email: string, @Body() dtos: ShiftDto[]): Promise<Result<boolean, string>> {
|
||||||
return this.create_service.createOneOrManyShifts(email,dtos);
|
return this.create_service.createOneOrManyShifts(email, dtos, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Patch('update')
|
@Patch('update')
|
||||||
|
|
@ -36,10 +36,21 @@ export class ShiftController {
|
||||||
return this.update_service.updateOneOrManyShifts(dtos, email);
|
return this.update_service.updateOneOrManyShifts(dtos, email);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Delete(':shift_id')
|
@Patch('update/:email')
|
||||||
@ModuleAccessAllowed(ModulesEnum.timesheets)
|
@ModuleAccessAllowed(ModulesEnum.timesheets_approval)
|
||||||
remove(@Param('shift_id') shift_id: number): Promise<Result<number, string>> {
|
updateBatchByTimesheetApproval(@Param('email') email: string, @Body() dtos: ShiftDto[]): Promise<Result<boolean, string>> {
|
||||||
return this.delete_service.deleteShift(shift_id);
|
return this.update_service.updateOneOrManyShifts(dtos, email, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Delete(':shift_id')
|
||||||
|
@ModuleAccessAllowed(ModulesEnum.timesheets)
|
||||||
|
remove(@Access('email') email: string, @Param('shift_id') shift_id: number): Promise<Result<number, string>> {
|
||||||
|
return this.delete_service.deleteShift(shift_id, email);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':shift_id/:email')
|
||||||
|
@ModuleAccessAllowed(ModulesEnum.timesheets)
|
||||||
|
removeByTimesheetApproval(@Param('shift_id') shift_id: number, @Param('email') email: string): Promise<Result<number, string>> {
|
||||||
|
return this.delete_service.deleteShift(shift_id, email, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { VacationService } from 'src/time-and-attendance/domains/services/vacati
|
||||||
import { BankedHoursService } from 'src/time-and-attendance/domains/services/banking-hours.service';
|
import { BankedHoursService } from 'src/time-and-attendance/domains/services/banking-hours.service';
|
||||||
import { PaidTimeOffModule } from 'src/time-and-attendance/paid-time-off/paid-time-off.module';
|
import { PaidTimeOffModule } from 'src/time-and-attendance/paid-time-off/paid-time-off.module';
|
||||||
import { PaidTimeOFfBankHoursService } from 'src/time-and-attendance/paid-time-off/paid-time-off.service';
|
import { PaidTimeOFfBankHoursService } from 'src/time-and-attendance/paid-time-off/paid-time-off.service';
|
||||||
|
import { PayPeriodEventService } from 'src/time-and-attendance/pay-period/services/pay-period-event.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [PaidTimeOffModule],
|
imports: [PaidTimeOffModule],
|
||||||
|
|
@ -20,6 +21,7 @@ import { PaidTimeOFfBankHoursService } from 'src/time-and-attendance/paid-time-o
|
||||||
VacationService,
|
VacationService,
|
||||||
BankedHoursService,
|
BankedHoursService,
|
||||||
PaidTimeOFfBankHoursService,
|
PaidTimeOFfBankHoursService,
|
||||||
|
PayPeriodEventService,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
ShiftsCreateService,
|
ShiftsCreateService,
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ import { SchedulePresetDeleteService } from "src/time-and-attendance/schedule-pr
|
||||||
import { SchedulePresetUpdateService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-update.service";
|
import { SchedulePresetUpdateService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-update.service";
|
||||||
import { SchedulePresetsCreateService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-create.service";
|
import { SchedulePresetsCreateService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-create.service";
|
||||||
import { SchedulePresetsApplyService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-apply.service";
|
import { SchedulePresetsApplyService } from "src/time-and-attendance/schedule-presets/services/schedule-presets-apply.service";
|
||||||
|
import { PayPeriodEventService } from "./pay-period/services/pay-period-event.service";
|
||||||
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
|
@ -90,6 +91,7 @@ import { SchedulePresetsApplyService } from "src/time-and-attendance/schedule-pr
|
||||||
VacationService,
|
VacationService,
|
||||||
BankedHoursService,
|
BankedHoursService,
|
||||||
PaidTimeOFfBankHoursService,
|
PaidTimeOFfBankHoursService,
|
||||||
|
PayPeriodEventService,
|
||||||
],
|
],
|
||||||
exports: [TimesheetApprovalService],
|
exports: [TimesheetApprovalService],
|
||||||
}) export class TimeAndAttendanceModule { };
|
}) export class TimeAndAttendanceModule { };
|
||||||
Loading…
Reference in New Issue
Block a user