feat(approval): clean up Approval services. creation of a "shared" folder
This commit is contained in:
parent
ef5af80471
commit
c23da925e7
26
src/common/shared/base-approval.service.ts
Normal file
26
src/common/shared/base-approval.service.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { NotFoundException } from "@nestjs/common";
|
||||||
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
||||||
|
//abstract class for approving or rejecting a shift, expense, timesheet or pay-period
|
||||||
|
export abstract class BaseApprovalService<T> {
|
||||||
|
protected constructor(protected readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
|
//returns the corresponding Prisma delegate
|
||||||
|
protected abstract get delegate(): {
|
||||||
|
update(args: {where: {id: number };
|
||||||
|
data: { is_approved: boolean }
|
||||||
|
}): Promise<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
//standard update Aproval
|
||||||
|
async updateApproval(id: number, isApproved: boolean): Promise<T> {
|
||||||
|
const entity = await this.delegate.update({
|
||||||
|
where: { id },
|
||||||
|
data: { is_approved: isApproved },
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!entity) throw new NotFoundException(`Entity #${id} not found`);
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from "@nestjs/common";
|
import { Body, Controller, Delete, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Post, UseGuards } from "@nestjs/common";
|
||||||
import { ExpensesService } from "../services/expenses.service";
|
import { ExpensesService } from "../services/expenses.service";
|
||||||
import { CreateExpenseDto } from "../dtos/create-expense";
|
import { CreateExpenseDto } from "../dtos/create-expense";
|
||||||
import { Expenses } from "@prisma/client";
|
import { Expenses } from "@prisma/client";
|
||||||
|
|
@ -8,13 +8,17 @@ import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagg
|
||||||
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
||||||
import { JwtAuthGuard } from "src/modules/authentication/guards/jwt-auth.guard";
|
import { JwtAuthGuard } from "src/modules/authentication/guards/jwt-auth.guard";
|
||||||
import { ExpenseEntity } from "../dtos/swagger-entities/expenses.entity";
|
import { ExpenseEntity } from "../dtos/swagger-entities/expenses.entity";
|
||||||
|
import { ExpensesApprovalService } from "../services/expenses-approval.service";
|
||||||
|
|
||||||
@ApiTags('Expenses')
|
@ApiTags('Expenses')
|
||||||
@ApiBearerAuth('access-token')
|
@ApiBearerAuth('access-token')
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Controller('Expenses')
|
@Controller('Expenses')
|
||||||
export class ExpensesController {
|
export class ExpensesController {
|
||||||
constructor(private readonly expensesService: ExpensesService) {}
|
constructor(
|
||||||
|
private readonly expensesService: ExpensesService,
|
||||||
|
private readonly expensesApprovalService: ExpensesApprovalService,
|
||||||
|
) {}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||||
|
|
@ -61,4 +65,10 @@ export class ExpensesController {
|
||||||
return this.expensesService.remove(id);
|
return this.expensesService.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Patch(':id/approval')
|
||||||
|
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||||
|
async approve(@Param('id', ParseIntPipe) id: number, @Body('is_approved', ParseBoolPipe) isApproved: boolean) {
|
||||||
|
return this.expensesApprovalService.updateApproval(id, isApproved);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { Expenses } from "@prisma/client";
|
||||||
|
import { BaseApprovalService } from "src/common/shared/base-approval.service";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ExpensesApprovalService {
|
export class ExpensesApprovalService extends BaseApprovalService<Expenses> {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(prisma: PrismaService) { super(prisma); }
|
||||||
|
|
||||||
async updateApproval(expenseId: number, isApproved: boolean) {
|
|
||||||
|
|
||||||
|
protected get delegate() {
|
||||||
|
return this.prisma.expenses;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,40 @@
|
||||||
|
import { NotFoundException } from "@nestjs/common";
|
||||||
import { TimesheetsApprovalService } from "src/modules/timesheets/services/timesheets-approval.service";
|
import { TimesheetsApprovalService } from "src/modules/timesheets/services/timesheets-approval.service";
|
||||||
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
||||||
export class PayPeriodsApprovalService {
|
export class PayPeriodsApprovalService {
|
||||||
constructor(private readonly timesheetsApproval: TimesheetsApprovalService) {}
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly timesheetsApproval: TimesheetsApprovalService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async approvaPayperdiod(periodNumber: number): Promise<void> {
|
||||||
|
const period = await this.prisma.payPeriods.findUnique({
|
||||||
|
where: { period_number: periodNumber },
|
||||||
|
});
|
||||||
|
if (!period) throw new NotFoundException(`PayPeriod #${periodNumber} not found`);
|
||||||
|
|
||||||
|
//fetches timesheet of selected period if the timesheet as atleast 1 shift or 1 expense
|
||||||
|
const timesheetList = await this.prisma.timesheets.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ shift: {some: { date: { gte: period.start_date,
|
||||||
|
lte: period.end_date,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{ expense: { some: { date: { gte: period.start_date,
|
||||||
|
lte: period.end_date,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
//approval of both timesheet (cascading to the approval of related shifts and expenses)
|
||||||
|
for(const timesheet of timesheetList) {
|
||||||
|
await this.timesheetsApproval.updateApproval(timesheet.id, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { Injectable, NotFoundException, Param, ParseIntPipe, Patch } from "@nestjs/common";
|
||||||
import { PayPeriods } from "@prisma/client";
|
import { PayPeriods } from "@prisma/client";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
import { PayPeriodsApprovalService } from "./pay-periods-approval.service";
|
||||||
|
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
||||||
|
import { Roles as RoleEnum } from '.prisma/client';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PayPeriodsService {
|
export class PayPeriodsService {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(private readonly prisma: PrismaService,
|
||||||
|
private readonly payperiodsApprovalService: PayPeriodsApprovalService
|
||||||
|
) {}
|
||||||
|
|
||||||
async findAll(): Promise<PayPeriods[]> {
|
async findAll(): Promise<PayPeriods[]> {
|
||||||
return this.prisma.payPeriods.findMany({
|
return this.prisma.payPeriods.findMany({
|
||||||
|
|
@ -32,4 +37,13 @@ export class PayPeriodsService {
|
||||||
}
|
}
|
||||||
return period;
|
return period;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Patch(':periodNumber/approval')
|
||||||
|
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||||
|
async approve(@Param('periodNumber', ParseIntPipe) periodNumber: number): Promise<{message:string}> {
|
||||||
|
await this.payperiodsApprovalService.approvaPayperdiod(periodNumber);
|
||||||
|
return {message: `Pay-period #${periodNumber} approved`};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,21 +1,13 @@
|
||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import { Shifts } from "@prisma/client";
|
import { Shifts } from "@prisma/client";
|
||||||
|
import { BaseApprovalService } from "src/common/shared/base-approval.service";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ShiftsApprovalService {
|
export class ShiftsApprovalService extends BaseApprovalService<Shifts> {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(prisma: PrismaService) { super(prisma); }
|
||||||
|
|
||||||
async updateApproval(shiftId: number, isApproved: boolean): Promise<Shifts> {
|
protected get delegate() {
|
||||||
const shift = await this.prisma.shifts.update({
|
return this.prisma.shifts;
|
||||||
where: { id: shiftId },
|
|
||||||
data: { is_approved: isApproved },
|
|
||||||
});
|
|
||||||
|
|
||||||
if(!shift) {
|
|
||||||
throw new NotFoundException(`Shift # ${shiftId} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return shift;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -17,7 +17,7 @@ export interface OverviewRow {
|
||||||
export class ShiftsOverviewService {
|
export class ShiftsOverviewService {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
async getSummary(periodId: number): Promise<ValidationRow[]> {
|
async getSummary(periodId: number): Promise<OverviewRow[]> {
|
||||||
//fetch pay-period to display
|
//fetch pay-period to display
|
||||||
const period = await this.prisma.payPeriods.findUnique({
|
const period = await this.prisma.payPeriods.findUnique({
|
||||||
where: { period_number: periodId },
|
where: { period_number: periodId },
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Post, UseGuards } from '@nestjs/common';
|
||||||
import { TimesheetsService } from '../services/timesheets.service';
|
import { TimesheetsService } from '../services/timesheets.service';
|
||||||
import { CreateTimesheetDto } from '../dtos/create-timesheet.dto';
|
import { CreateTimesheetDto } from '../dtos/create-timesheet.dto';
|
||||||
import { Timesheets } from '@prisma/client';
|
import { Timesheets } from '@prisma/client';
|
||||||
|
|
@ -8,13 +8,17 @@ import { Roles as RoleEnum } from '.prisma/client';
|
||||||
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { JwtAuthGuard } from 'src/modules/authentication/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from 'src/modules/authentication/guards/jwt-auth.guard';
|
||||||
import { TimesheetEntity } from '../dtos/swagger-entities/timesheet.entity';
|
import { TimesheetEntity } from '../dtos/swagger-entities/timesheet.entity';
|
||||||
|
import { TimesheetsApprovalService } from '../services/timesheets-approval.service';
|
||||||
|
|
||||||
@ApiTags('Timesheets')
|
@ApiTags('Timesheets')
|
||||||
@ApiBearerAuth('access-token')
|
@ApiBearerAuth('access-token')
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Controller('timesheets')
|
@Controller('timesheets')
|
||||||
export class TimesheetsController {
|
export class TimesheetsController {
|
||||||
constructor(private readonly timesheetsService: TimesheetsService) {}
|
constructor(
|
||||||
|
private readonly timesheetsService: TimesheetsService,
|
||||||
|
private readonly timesheetsApprovalService: TimesheetsApprovalService,
|
||||||
|
) {}
|
||||||
|
|
||||||
@Post()
|
@Post()
|
||||||
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.EMPLOYEE, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||||
|
|
@ -63,4 +67,10 @@ export class TimesheetsController {
|
||||||
remove(@Param('id', ParseIntPipe) id: number): Promise<Timesheets> {
|
remove(@Param('id', ParseIntPipe) id: number): Promise<Timesheets> {
|
||||||
return this.timesheetsService.remove(id);
|
return this.timesheetsService.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Patch(':id/approval')
|
||||||
|
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
|
||||||
|
async approve(@Param('id', ParseIntPipe) id: number, @Body('is_approved', ParseBoolPipe) isApproved: boolean) {
|
||||||
|
return this.timesheetsApprovalService.updateApproval(id, isApproved);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,37 @@
|
||||||
|
|
||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import { Timesheets } from "@prisma/client";
|
import { Timesheets } from "@prisma/client";
|
||||||
|
import { BaseApprovalService } from "src/common/shared/base-approval.service";
|
||||||
|
import { ExpensesApprovalService } from "src/modules/expenses/services/expenses-approval.service";
|
||||||
|
import { ShiftsApprovalService } from "src/modules/shifts/services/shifts-approval.service";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TimesheetsApprovalService {
|
export class TimesheetsApprovalService extends BaseApprovalService<Timesheets>{
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(
|
||||||
|
prisma: PrismaService,
|
||||||
|
private readonly shiftsApproval: ShiftsApprovalService,
|
||||||
|
private readonly expensesApproval: ExpensesApprovalService,
|
||||||
|
) {super(prisma);}
|
||||||
|
|
||||||
|
protected get delegate() {
|
||||||
|
return this.prisma.timesheets;
|
||||||
|
}
|
||||||
|
|
||||||
async updateApproval(timesheetId: number, isApproved: boolean): Promise<Timesheets> {
|
async updateApproval(timesheetId: number, isApproved: boolean): Promise<Timesheets> {
|
||||||
const timesheet = await this.prisma.timesheets.update({
|
const timesheet = await super.updateApproval(timesheetId, isApproved);
|
||||||
where: { id: timesheetId },
|
|
||||||
data: { is_approved: isApproved},
|
await this.prisma.shifts.updateMany({
|
||||||
|
where: { timesheet_id: timesheetId },
|
||||||
|
data: { is_approved: isApproved },
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.prisma.expenses.updateMany({
|
||||||
|
where: { timesheet_id: timesheetId },
|
||||||
|
data: { is_approved: isApproved },
|
||||||
});
|
});
|
||||||
if (!timesheet) throw new NotFoundException(`Timesheet # ${timesheetId} not found`);
|
|
||||||
|
|
||||||
return timesheet;
|
return timesheet;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user