feat(module): search and filter querries for shifts, expenses, timesheets, leave-requests
This commit is contained in:
parent
c23da925e7
commit
44da99e7c1
21
src/common/shared/build-prisma-where.ts
Normal file
21
src/common/shared/build-prisma-where.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
//Prisma 'where' clause for DTO filters
|
||||||
|
export function buildPrismaWhere<T extends Record<string, any>>(dto: T): Record <string, any> {
|
||||||
|
const where: Record<string,any> = {};
|
||||||
|
|
||||||
|
for (const [key,value] of Object.entries(dto)) {
|
||||||
|
if (value === undefined || value === null) continue;
|
||||||
|
|
||||||
|
if (key.endsWith('_contains')) {
|
||||||
|
const field = key.slice(0, - '_contains'.length);
|
||||||
|
where[field] = { constains: value };
|
||||||
|
} else if (key === 'start_date' || key === 'end_date') {
|
||||||
|
where.date = where.date || {};
|
||||||
|
const op = key === 'start_date' ? 'gte' : 'lte';
|
||||||
|
where.date[op] = new Date(value);
|
||||||
|
} else {
|
||||||
|
where[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return where;
|
||||||
|
}
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
import { Body, Controller, Delete, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Post, UseGuards } from "@nestjs/common";
|
import { Body, Controller, Delete, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Post, Query, UseGuards, UsePipes, ValidationPipe } 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.dto";
|
||||||
import { Expenses } from "@prisma/client";
|
import { Expenses } from "@prisma/client";
|
||||||
import { Roles as RoleEnum } from '.prisma/client';
|
import { Roles as RoleEnum } from '.prisma/client';
|
||||||
import { UpdateExpenseDto } from "../dtos/update-expense";
|
import { UpdateExpenseDto } from "../dtos/update-expense.dto";
|
||||||
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
|
||||||
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";
|
import { ExpensesApprovalService } from "../services/expenses-approval.service";
|
||||||
|
import { SearchExpensesDto } from "../dtos/search-expense.dto";
|
||||||
|
|
||||||
@ApiTags('Expenses')
|
@ApiTags('Expenses')
|
||||||
@ApiBearerAuth('access-token')
|
@ApiBearerAuth('access-token')
|
||||||
|
|
@ -34,8 +35,9 @@ export class ExpensesController {
|
||||||
@ApiOperation({ summary: 'Find all expenses' })
|
@ApiOperation({ summary: 'Find all expenses' })
|
||||||
@ApiResponse({ status: 201, description: 'List of expenses found',type: ExpenseEntity, isArray: true })
|
@ApiResponse({ status: 201, description: 'List of expenses found',type: ExpenseEntity, isArray: true })
|
||||||
@ApiResponse({ status: 400, description: 'List of expenses not found' })
|
@ApiResponse({ status: 400, description: 'List of expenses not found' })
|
||||||
findAll(): Promise<Expenses[]> {
|
@UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
|
||||||
return this.expensesService.findAll();
|
findAll(@Query() filters: SearchExpensesDto): Promise<Expenses[]> {
|
||||||
|
return this.expensesService.findAll(filters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
|
|
||||||
26
src/modules/expenses/dtos/search-expense.dto.ts
Normal file
26
src/modules/expenses/dtos/search-expense.dto.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { Type } from "class-transformer";
|
||||||
|
import { IsDateString, IsInt, IsOptional, IsString } from "class-validator";
|
||||||
|
|
||||||
|
export class SearchExpensesDto {
|
||||||
|
@IsOptional()
|
||||||
|
@Type(()=> Number)
|
||||||
|
@IsInt()
|
||||||
|
timesheet_id?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@Type(()=> Number)
|
||||||
|
@IsInt()
|
||||||
|
bank_code_id?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
description_contains?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
start_date: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
end_date: string;
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { PartialType } from "@nestjs/swagger";
|
import { PartialType } from "@nestjs/swagger";
|
||||||
import { CreateExpenseDto } from "./create-expense";
|
import { CreateExpenseDto } from "./create-expense.dto";
|
||||||
|
|
||||||
export class UpdateExpenseDto extends PartialType(CreateExpenseDto) {}
|
export class UpdateExpenseDto extends PartialType(CreateExpenseDto) {}
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
|
||||||
import { ExpensesController } from "./controllers/expenses.controller";
|
import { ExpensesController } from "./controllers/expenses.controller";
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import { ExpensesService } from "./services/expenses.service";
|
import { ExpensesService } from "./services/expenses.service";
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { CreateExpenseDto } from "../dtos/create-expense";
|
import { CreateExpenseDto } from "../dtos/create-expense.dto";
|
||||||
import { Expenses, ExpensesArchive } from "@prisma/client";
|
import { Expenses, ExpensesArchive } from "@prisma/client";
|
||||||
import { UpdateExpenseDto } from "../dtos/update-expense";
|
import { UpdateExpenseDto } from "../dtos/update-expense.dto";
|
||||||
import { MileageService } from "src/modules/business-logics/services/mileage.service";
|
import { MileageService } from "src/modules/business-logics/services/mileage.service";
|
||||||
|
import { SearchExpensesDto } from "../dtos/search-expense.dto";
|
||||||
|
import { buildPrismaWhere } from "src/common/shared/build-prisma-where";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ExpensesService {
|
export class ExpensesService {
|
||||||
|
|
@ -42,10 +44,10 @@ export class ExpensesService {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(): Promise<Expenses[]> {
|
async findAll(filters: SearchExpensesDto): Promise<Expenses[]> {
|
||||||
return this.prisma.expenses.findMany({
|
const where = buildPrismaWhere(filters);
|
||||||
include: { timesheet: { include: { employee: { include: { user: true } } } } },
|
const expenses = await this.prisma.expenses.findMany({ where })
|
||||||
});
|
return expenses;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOne(id: number): Promise<Expenses> {
|
async findOne(id: number): Promise<Expenses> {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from "@nestjs/common";
|
import { BadRequestException, Body, Controller, Delete, Get, Param, ParseBoolPipe, ParseEnumPipe, ParseIntPipe, Patch, Post, Query, UseGuards, UsePipes, ValidationPipe } from "@nestjs/common";
|
||||||
import { LeaveRequestsService } from "../services/leave-requests.service";
|
import { LeaveRequestsService } from "../services/leave-requests.service";
|
||||||
import { CreateLeaveRequestsDto } from "../dtos/create-leave-requests.dto";
|
import { CreateLeaveRequestsDto } from "../dtos/create-leave-requests.dto";
|
||||||
import { LeaveRequests } from "@prisma/client";
|
import { LeaveRequests } from "@prisma/client";
|
||||||
import { UpdateLeaveRequestsDto } from "../dtos/update-leave-requests.dto";
|
import { UpdateLeaveRequestsDto } from "../dtos/update-leave-requests.dto";
|
||||||
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
||||||
import { Roles as RoleEnum } from '.prisma/client';
|
import { LeaveApprovalStatus, 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 { LeaveRequestEntity } from "../dtos/swagger-entities/leave-requests.entity";
|
import { LeaveRequestEntity } from "../dtos/swagger-entities/leave-requests.entity";
|
||||||
|
import { SearchLeaveRequestsDto } from "../dtos/search-leave-requests.dto";
|
||||||
|
|
||||||
@ApiTags('Leave Requests')
|
@ApiTags('Leave Requests')
|
||||||
@ApiBearerAuth('access-token')
|
@ApiBearerAuth('access-token')
|
||||||
|
|
@ -30,8 +31,9 @@ export class LeaveRequestController {
|
||||||
@ApiOperation({summary: 'Find all leave request' })
|
@ApiOperation({summary: 'Find all leave request' })
|
||||||
@ApiResponse({ status: 201, description: 'List of Leave requests found',type: LeaveRequestEntity, isArray: true })
|
@ApiResponse({ status: 201, description: 'List of Leave requests found',type: LeaveRequestEntity, isArray: true })
|
||||||
@ApiResponse({ status: 400, description: 'List of leave request not found' })
|
@ApiResponse({ status: 400, description: 'List of leave request not found' })
|
||||||
findAll(): Promise<LeaveRequests[]> {
|
@UsePipes(new ValidationPipe({transform: true, whitelist: true}))
|
||||||
return this.leaveRequetsService.findAll();
|
findAll(@Query() filters: SearchLeaveRequestsDto): Promise<(LeaveRequests & {daysRequested:number; cost: number})[]> {
|
||||||
|
return this.leaveRequetsService.findAll(filters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
|
@ -60,4 +62,14 @@ export class LeaveRequestController {
|
||||||
remove(@Param('id', ParseIntPipe) id: number): Promise<LeaveRequests> {
|
remove(@Param('id', ParseIntPipe) id: number): Promise<LeaveRequests> {
|
||||||
return this.leaveRequetsService.remove(id);
|
return this.leaveRequetsService.remove(id);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Patch(':id/approval')
|
||||||
|
updateApproval( @Param('id', ParseIntPipe) id: number,
|
||||||
|
@Body('is_approved', ParseBoolPipe) isApproved: boolean): Promise<LeaveRequests> {
|
||||||
|
const approvalStatus = isApproved ?
|
||||||
|
LeaveApprovalStatus.APPROVED : LeaveApprovalStatus.DENIED;
|
||||||
|
return this.leaveRequetsService.update(id, { approval_status: approvalStatus });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
27
src/modules/leave-requests/dtos/search-leave-requests.dto.ts
Normal file
27
src/modules/leave-requests/dtos/search-leave-requests.dto.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { LeaveApprovalStatus } from "@prisma/client";
|
||||||
|
import { Type } from "class-transformer";
|
||||||
|
import { IsOptional, IsInt, IsEnum, IsDateString } from "class-validator";
|
||||||
|
|
||||||
|
export class SearchLeaveRequestsDto {
|
||||||
|
@IsOptional()
|
||||||
|
@Type(()=> Number)
|
||||||
|
@IsInt()
|
||||||
|
employee_id?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@Type(()=> Number)
|
||||||
|
@IsInt()
|
||||||
|
bank_code_id?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsEnum(LeaveApprovalStatus)
|
||||||
|
approval_status?: LeaveApprovalStatus
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
start_date?: Date;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
end_date?: Date;
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,8 @@ import { UpdateLeaveRequestsDto } from "../dtos/update-leave-requests.dto";
|
||||||
import { HolidayService } from "src/modules/business-logics/services/holiday.service";
|
import { HolidayService } from "src/modules/business-logics/services/holiday.service";
|
||||||
import { SickLeaveService } from "src/modules/business-logics/services/sick-leave.service";
|
import { SickLeaveService } from "src/modules/business-logics/services/sick-leave.service";
|
||||||
import { VacationService } from "src/modules/business-logics/services/vacation.service";
|
import { VacationService } from "src/modules/business-logics/services/vacation.service";
|
||||||
|
import { SearchLeaveRequestsDto } from "../dtos/search-leave-requests.dto";
|
||||||
|
import { buildPrismaWhere } from "src/common/shared/build-prisma-where";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LeaveRequestsService {
|
export class LeaveRequestsService {
|
||||||
|
|
@ -30,8 +32,19 @@ export class LeaveRequestsService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAll(): Promise<any[]> {
|
async findAll(filters: SearchLeaveRequestsDto): Promise<any[]> {
|
||||||
|
const {start_date, end_date, ...otherFilters } = filters;
|
||||||
|
const where: Record<string, any> = buildPrismaWhere(otherFilters);
|
||||||
|
|
||||||
|
if (start_date) {
|
||||||
|
where.start_date_time = { ...(where.start_date_time ?? {}), gte: new Date(start_date) };
|
||||||
|
}
|
||||||
|
if(end_date) {
|
||||||
|
where.end_date_time = { ...(where.end_date_time ?? {}), lte: new Date(end_date) };
|
||||||
|
}
|
||||||
|
|
||||||
const list = await this.prisma.leaveRequests.findMany({
|
const list = await this.prisma.leaveRequests.findMany({
|
||||||
|
where,
|
||||||
include: { employee: { include: { user: true } },
|
include: { employee: { include: { user: true } },
|
||||||
bank_code: true,
|
bank_code: true,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,14 @@ export class ShiftsOverviewController {
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
async getSummary( @Query() query: GetShiftsOverviewDto): Promise<OverviewRow[]> {
|
async getSummary( @Query() query: GetShiftsOverviewDto): Promise<OverviewRow[]> {
|
||||||
return this.shiftsValidationService.getSummary(query.periodId);
|
return this.shiftsValidationService.getSummary(query.period_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('export.csv')
|
@Get('export.csv')
|
||||||
@Header('Content-Type', 'text/csv; charset=utf-8')
|
@Header('Content-Type', 'text/csv; charset=utf-8')
|
||||||
@Header('Content-Disposition', 'attachment; filename="shifts-validation.csv"')
|
@Header('Content-Disposition', 'attachment; filename="shifts-validation.csv"')
|
||||||
async exportCsv(@Query() query: GetShiftsOverviewDto): Promise<Buffer>{
|
async exportCsv(@Query() query: GetShiftsOverviewDto): Promise<Buffer>{
|
||||||
const rows = await this.shiftsValidationService.getSummary(query.periodId);
|
const rows = await this.shiftsValidationService.getSummary(query.period_id);
|
||||||
|
|
||||||
//CSV Headers
|
//CSV Headers
|
||||||
const header = [
|
const header = [
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
import { Body, Controller, Delete, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Post, UseGuards } from "@nestjs/common";
|
import { Body, Controller, Delete, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Post, Query, UseGuards, UsePipes, ValidationPipe } from "@nestjs/common";
|
||||||
import { ShiftsService } from "../services/shifts.service";
|
import { ShiftsService } from "../services/shifts.service";
|
||||||
import { Shifts } from "@prisma/client";
|
import { Shifts } from "@prisma/client";
|
||||||
import { CreateShiftDto } from "../dtos/create-shifts.dto";
|
import { CreateShiftDto } from "../dtos/create-shift.dto";
|
||||||
import { UpdateShiftsDto } from "../dtos/update-shifts.dto";
|
import { UpdateShiftsDto } from "../dtos/update-shift.dto";
|
||||||
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
import { RolesAllowed } from "src/common/decorators/roles.decorators";
|
||||||
import { Roles as RoleEnum } from '.prisma/client';
|
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 { ShiftEntity } from "../dtos/swagger-entities/shift.entity";
|
import { ShiftEntity } from "../dtos/swagger-entities/shift.entity";
|
||||||
import { ShiftsApprovalService } from "../services/shifts-approval.service";
|
import { ShiftsApprovalService } from "../services/shifts-approval.service";
|
||||||
|
import { SearchShiftsDto } from "../dtos/search-shifts.dto";
|
||||||
|
|
||||||
@ApiTags('Shifts')
|
@ApiTags('Shifts')
|
||||||
@ApiBearerAuth('access-token')
|
@ApiBearerAuth('access-token')
|
||||||
|
|
@ -34,8 +35,9 @@ export class ShiftsController {
|
||||||
@ApiOperation({ summary: 'Find all shifts' })
|
@ApiOperation({ summary: 'Find all shifts' })
|
||||||
@ApiResponse({ status: 201, description: 'List of shifts found',type: ShiftEntity, isArray: true })
|
@ApiResponse({ status: 201, description: 'List of shifts found',type: ShiftEntity, isArray: true })
|
||||||
@ApiResponse({ status: 400, description: 'List of shifts not found' })
|
@ApiResponse({ status: 400, description: 'List of shifts not found' })
|
||||||
findAll(): Promise<Shifts[]> {
|
@UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
|
||||||
return this.shiftsService.findAll();
|
findAll(@Query() filters: SearchShiftsDto) {
|
||||||
|
return this.shiftsService.findAll(filters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,5 @@ export class GetShiftsOverviewDto {
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
@Max(26)
|
@Max(26)
|
||||||
periodId: number;
|
period_id: number;
|
||||||
}
|
}
|
||||||
29
src/modules/shifts/dtos/search-shifts.dto.ts
Normal file
29
src/modules/shifts/dtos/search-shifts.dto.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { Type } from "class-transformer";
|
||||||
|
import { IsDateString, IsInt, IsOptional, IsString } from "class-validator";
|
||||||
|
|
||||||
|
export class SearchShiftsDto {
|
||||||
|
@IsOptional()
|
||||||
|
@Type(()=> Number)
|
||||||
|
@IsInt()
|
||||||
|
employee_id?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@Type(()=> Number)
|
||||||
|
@IsInt()
|
||||||
|
bank_code_id?: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
description_contains?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsDateString()
|
||||||
|
end_date?: string;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@Type(()=> Number)
|
||||||
|
@IsInt()
|
||||||
|
timesheet_id?: number;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { PartialType } from "@nestjs/swagger";
|
import { PartialType } from "@nestjs/swagger";
|
||||||
import { CreateShiftDto } from "./create-shifts.dto";
|
import { CreateShiftDto } from "./create-shift.dto";
|
||||||
|
|
||||||
export class UpdateShiftsDto extends PartialType(CreateShiftDto){}
|
export class UpdateShiftsDto extends PartialType(CreateShiftDto){}
|
||||||
|
|
@ -17,13 +17,13 @@ export interface OverviewRow {
|
||||||
export class ShiftsOverviewService {
|
export class ShiftsOverviewService {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
async getSummary(periodId: number): Promise<OverviewRow[]> {
|
async getSummary(period_id: 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: period_id },
|
||||||
});
|
});
|
||||||
if(!period) {
|
if(!period) {
|
||||||
throw new NotFoundException(`pay-period ${periodId} not found`);
|
throw new NotFoundException(`pay-period ${period_id} not found`);
|
||||||
}
|
}
|
||||||
const { start_date, end_date } = period;
|
const { start_date, end_date } = period;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||||
import { PrismaService } from "src/prisma/prisma.service";
|
import { PrismaService } from "src/prisma/prisma.service";
|
||||||
import { CreateShiftDto } from "../dtos/create-shifts.dto";
|
import { CreateShiftDto } from "../dtos/create-shift.dto";
|
||||||
import { Shifts, ShiftsArchive } from "@prisma/client";
|
import { Shifts, ShiftsArchive } from "@prisma/client";
|
||||||
import { UpdateShiftsDto } from "../dtos/update-shifts.dto";
|
import { UpdateShiftsDto } from "../dtos/update-shift.dto";
|
||||||
|
import { buildPrismaWhere } from "src/common/shared/build-prisma-where";
|
||||||
|
import { SearchShiftsDto } from "../dtos/search-shifts.dto";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ShiftsService {
|
export class ShiftsService {
|
||||||
|
|
@ -18,10 +20,10 @@ export class ShiftsService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
findAll(): Promise<Shifts[]> {
|
async findAll(filters: SearchShiftsDto): Promise <Shifts[]> {
|
||||||
return this.prisma.shifts.findMany({
|
const where = buildPrismaWhere(filters);
|
||||||
include: { timesheet: { include: { employee: { include: { user:true } } } } },
|
const shifts = await this.prisma.shifts.findMany({ where })
|
||||||
});
|
return shifts;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOne(id: number): Promise<Shifts> {
|
async findOne(id: number): Promise<Shifts> {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Body, Controller, Delete, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Post, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Post, Query, UseGuards, UsePipes, ValidationPipe } 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';
|
||||||
|
|
@ -9,6 +9,7 @@ import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagg
|
||||||
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';
|
import { TimesheetsApprovalService } from '../services/timesheets-approval.service';
|
||||||
|
import { SearchTimesheetDto } from '../dtos/search-timesheets.dto';
|
||||||
|
|
||||||
@ApiTags('Timesheets')
|
@ApiTags('Timesheets')
|
||||||
@ApiBearerAuth('access-token')
|
@ApiBearerAuth('access-token')
|
||||||
|
|
@ -34,8 +35,9 @@ export class TimesheetsController {
|
||||||
@ApiOperation({ summary: 'Find all timesheets' })
|
@ApiOperation({ summary: 'Find all timesheets' })
|
||||||
@ApiResponse({ status: 201, description: 'List of timesheet found', type: TimesheetEntity, isArray: true })
|
@ApiResponse({ status: 201, description: 'List of timesheet found', type: TimesheetEntity, isArray: true })
|
||||||
@ApiResponse({ status: 400, description: 'List of timesheets not found' })
|
@ApiResponse({ status: 400, description: 'List of timesheets not found' })
|
||||||
findAll(): Promise<Timesheets[]> {
|
@UsePipes(new ValidationPipe({transform: true, whitelist: true }))
|
||||||
return this.timesheetsService.findAll();
|
findAll(@Query() filters: SearchTimesheetDto): Promise<any[]> {
|
||||||
|
return this.timesheetsService.findAll(filters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
|
|
|
||||||
20
src/modules/timesheets/dtos/search-timesheets.dto.ts
Normal file
20
src/modules/timesheets/dtos/search-timesheets.dto.ts
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { Type } from "class-transformer";
|
||||||
|
import { IsBoolean, IsInt, IsOptional } from "class-validator";
|
||||||
|
|
||||||
|
|
||||||
|
export class SearchTimesheetDto {
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsInt()
|
||||||
|
timesheet_id: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@Type(()=> Number)
|
||||||
|
@IsInt()
|
||||||
|
employee_id: number;
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@Type(()=> Boolean)
|
||||||
|
@IsBoolean()
|
||||||
|
is_approved: boolean;
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,8 @@ import { Timesheets, TimesheetsArchive } from '@prisma/client';
|
||||||
import { UpdateTimesheetDto } from '../dtos/update-timesheet.dto';
|
import { UpdateTimesheetDto } from '../dtos/update-timesheet.dto';
|
||||||
import { OvertimeService } from 'src/modules/business-logics/services/overtime.service';
|
import { OvertimeService } from 'src/modules/business-logics/services/overtime.service';
|
||||||
import { computeHours } from 'src/common/utils/date-utils';
|
import { computeHours } from 'src/common/utils/date-utils';
|
||||||
|
import { buildPrismaWhere } from 'src/common/shared/build-prisma-where';
|
||||||
|
import { SearchTimesheetDto } from '../dtos/search-timesheets.dto';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TimesheetsService {
|
export class TimesheetsService {
|
||||||
|
|
@ -24,32 +26,38 @@ export class TimesheetsService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async findAll(): Promise<any[]> {
|
async findAll(filters: SearchTimesheetDto): Promise<any[]> {
|
||||||
const list = await this.prisma.timesheets.findMany({
|
const where = buildPrismaWhere(filters);
|
||||||
include: {
|
|
||||||
shift: { include: { bank_code: true } },
|
//fetchs lists of shifts and expenses for a selected timesheet
|
||||||
expense: { include: { bank_code: true } },
|
const rawlist = await this.prisma.timesheets.findMany({
|
||||||
employee: { include: { user : true } },
|
where, include: {
|
||||||
|
shift: { include: {bank_code: true } },
|
||||||
|
expense: { include: { bank_code: true } },
|
||||||
|
employee: { include: { user : true } },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return Promise.all(
|
const detailedlist = await Promise.all(
|
||||||
list.map(async timesheet => {
|
rawlist.map(async timesheet => {
|
||||||
const detailedShifts = timesheet.shift.map(s => {
|
//detailed shifts
|
||||||
const hours = computeHours(s.start_time, s.end_time,5);
|
const detailedShifts = timesheet.shift.map(shift => {
|
||||||
const regularHours = Math.min(8, hours);
|
const totalhours = computeHours(shift.start_time, shift.end_time,5);
|
||||||
const dailyOvertime = this.overtime.getDailyOvertimeHours(s.start_time, s.end_time);
|
const regularHours = Math.min(8, totalhours);
|
||||||
const payRegular = regularHours * s.bank_code.modifier;
|
const dailyOvertime = this.overtime.getDailyOvertimeHours(shift.start_time, shift.end_time);
|
||||||
const payOvertime = this.overtime.calculateOvertimePay(dailyOvertime, s.bank_code.modifier);
|
const payRegular = regularHours * shift.bank_code.modifier;
|
||||||
return { ...s, hours, payRegular, payOvertime };
|
const payOvertime = this.overtime.calculateOvertimePay(dailyOvertime, shift.bank_code.modifier);
|
||||||
|
return { ...shift, totalhours, payRegular, payOvertime };
|
||||||
});
|
});
|
||||||
|
//calculate overtime weekly
|
||||||
const weeklyOvertimeHours = detailedShifts.length
|
const weeklyOvertimeHours = detailedShifts.length
|
||||||
? await this.overtime.getWeeklyOvertimeHours(
|
? await this.overtime.getWeeklyOvertimeHours(
|
||||||
timesheet.employee_id,
|
timesheet.employee_id,
|
||||||
timesheet.shift[0].date): 0;
|
timesheet.shift[0].date): 0;
|
||||||
return { ...timesheet, shift: detailedShifts, weeklyOvertimeHours };
|
return { ...timesheet, shift: detailedShifts, weeklyOvertimeHours };
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
return detailedlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findOne(id: number): Promise<any> {
|
async findOne(id: number): Promise<any> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user