feat(pay-period): added approval of timesheets, shifts and expenses by bulk. added route to controller

This commit is contained in:
Matthieu Haineault 2025-11-03 14:14:09 -05:00
parent 5268737bd1
commit bdbec4f68c
11 changed files with 177 additions and 155 deletions

View File

@ -162,7 +162,31 @@
] ]
} }
}, },
"/pay-periods/{year}/{periodNumber}/{email}": { "/pay-periods/crew/pay-period-approval": {
"patch": {
"operationId": "PayPeriodsController_bulkApproval",
"parameters": [],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/BulkCrewApprovalDto"
}
}
}
},
"responses": {
"200": {
"description": ""
}
},
"tags": [
"PayPeriods"
]
}
},
"/pay-periods/crew/{year}/{periodNumber}": {
"get": { "get": {
"operationId": "PayPeriodsController_getCrewOverview", "operationId": "PayPeriodsController_getCrewOverview",
"parameters": [ "parameters": [
@ -224,6 +248,37 @@
] ]
} }
}, },
"/timesheets": {
"get": {
"operationId": "TimesheetController_getTimesheetByIds",
"parameters": [
{
"name": "year",
"required": true,
"in": "query",
"schema": {
"type": "number"
}
},
{
"name": "period_number",
"required": true,
"in": "query",
"schema": {
"type": "number"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"Timesheet"
]
}
},
"/preferences": { "/preferences": {
"patch": { "patch": {
"operationId": "PreferencesController_updatePreferences", "operationId": "PreferencesController_updatePreferences",
@ -257,37 +312,6 @@
] ]
} }
}, },
"/timesheets": {
"get": {
"operationId": "TimesheetController_getTimesheetByIds",
"parameters": [
{
"name": "year",
"required": true,
"in": "query",
"schema": {
"type": "number"
}
},
{
"name": "period_number",
"required": true,
"in": "query",
"schema": {
"type": "number"
}
}
],
"responses": {
"200": {
"description": ""
}
},
"tags": [
"Timesheet"
]
}
},
"/shift/create": { "/shift/create": {
"post": { "post": {
"operationId": "ShiftController_createBatch", "operationId": "ShiftController_createBatch",
@ -609,6 +633,10 @@
} }
}, },
"schemas": { "schemas": {
"BulkCrewApprovalDto": {
"type": "object",
"properties": {}
},
"PreferencesDto": { "PreferencesDto": {
"type": "object", "type": "object",
"properties": {} "properties": {}

View File

@ -1,7 +1,6 @@
import { CreateExpenseResult, UpdateExpensePayload, UpdateExpenseResult, DeleteExpenseResult, NormalizedExpense } from "src/time-and-attendance/utils/type.utils"; import { CreateExpenseResult, UpdateExpensePayload, UpdateExpenseResult, DeleteExpenseResult, NormalizedExpense } from "src/time-and-attendance/utils/type.utils";
import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils"; import { toDateFromString, toStringFromDate, weekStartSunday } from "src/time-and-attendance/utils/date-time.utils";
import { Injectable, NotFoundException, Req } from "@nestjs/common"; import { Injectable, NotFoundException } from "@nestjs/common";
import { expense_select } from "src/time-and-attendance/utils/selects.utils"; import { expense_select } from "src/time-and-attendance/utils/selects.utils";
import { PrismaService } from "src/prisma/prisma.service"; import { PrismaService } from "src/prisma/prisma.service";
import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto"; import { ExpenseDto } from "src/time-and-attendance/expenses/dtos/expense-create.dto";

View File

@ -1,10 +1,9 @@
import { Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Query, Req } from "@nestjs/common"; import { Body, Controller, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query, Req, UnauthorizedException } from "@nestjs/common";
import { PayPeriodDto } from "../dtos/pay-period.dto";
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 { 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 { PayPeriodsCommandService } from "../services/pay-periods-command.service"; import { PayPeriodsCommandService } from "../services/pay-periods-command.service";
import { PayPeriodBundleDto } from "../dtos/bundle-pay-period.dto"; import { PayPeriodBundleDto } from "../dtos/bundle-pay-period.dto";
import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto"; import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto";
@ -14,8 +13,8 @@ export class PayPeriodsController {
constructor( constructor(
private readonly queryService: PayPeriodsQueryService, private readonly queryService: PayPeriodsQueryService,
// private readonly commandService: PayPeriodsCommandService, private readonly commandService: PayPeriodsCommandService,
) {} ) { }
@Get('current-and-all') @Get('current-and-all')
async getCurrentAndAll(@Query('date') date?: string): Promise<PayPeriodBundleDto> { async getCurrentAndAll(@Query('date') date?: string): Promise<PayPeriodBundleDto> {
@ -39,42 +38,32 @@ export class PayPeriodsController {
return this.queryService.findOneByYearPeriod(year, period_no); return this.queryService.findOneByYearPeriod(year, period_no);
} }
// @Patch("crew/bulk-approval") @Patch("crew/pay-period-approval")
// //@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
// @ApiOperation({ summary: "Approve all selected timesheets in the period" }) async bulkApproval(@Req() req, @Body() dto: BulkCrewApprovalDto) {
// @ApiResponse({ status: 200, description: "Pay period approved" }) const email = req.user?.email;
// async bulkApproval(@Body() dto: BulkCrewApprovalDto) { if(!email) throw new UnauthorizedException(`Session infos not found`);
// return this.commandService.bulkApproveCrew(dto); return this.commandService.bulkApproveCrew(email, dto);
// } }
@Get(':year/:periodNumber/:email') @Get('crew/:year/:periodNumber')
//@RolesAllowed(RoleEnum.SUPERVISOR) @RolesAllowed(RoleEnum.SUPERVISOR)
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,
@Query('includeSubtree', new ParseBoolPipe({ optional: true })) include_subtree = false, @Query('includeSubtree', new ParseBoolPipe({ optional: true })) include_subtree = false,
): Promise<PayPeriodOverviewDto> { ): Promise<PayPeriodOverviewDto> {
const email = req.user?.email; const email = req.user?.email;
if(!email) throw new UnauthorizedException(`Session infos not found`);
return this.queryService.getCrewOverview(year, period_no, email, include_subtree); return this.queryService.getCrewOverview(year, period_no, email, include_subtree);
} }
@Get('overview/:year/:periodNumber') @Get('overview/:year/:periodNumber')
async getOverviewByYear( @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR)
@Param('year', ParseIntPipe) year: number, async getOverviewByYear(
@Param('year', ParseIntPipe) year: number,
@Param('periodNumber', ParseIntPipe) period_no: number, @Param('periodNumber', ParseIntPipe) period_no: number,
): Promise<PayPeriodOverviewDto> { ): Promise<PayPeriodOverviewDto> {
return this.queryService.getOverviewByYearPeriod(year, period_no); return this.queryService.getOverviewByYearPeriod(year, period_no);
} }
//_____________________________________________________________________________________________
// Deprecated or unused methods
//_____________________________________________________________________________________________
// @Get()
// @ApiOperation({ summary: 'Find all pay period' })
// @ApiResponse({status: 200,description: 'List of pay period found', type: PayPeriodDto, isArray: true })
// async findAll(): Promise<PayPeriodDto[]> {
// return this.queryService.findAll();
// }
} }

View File

@ -16,9 +16,6 @@ export class BulkCrewApprovalItemDto {
} }
export class BulkCrewApprovalDto { export class BulkCrewApprovalDto {
@IsEmail()
supervisor_email: string;
@IsBoolean() @IsBoolean()
include_subtree: boolean = false; include_subtree: boolean = false;

View File

@ -1,10 +1,20 @@
import { PayPeriodsQueryService } from "./services/pay-periods-query.service"; import { PayPeriodsQueryService } from "./services/pay-periods-query.service";
import { PayPeriodsController } from "./controllers/pay-periods.controller"; import { PayPeriodsController } from "./controllers/pay-periods.controller";
import { Module } from "@nestjs/common"; import { Module } from "@nestjs/common";
import { PayPeriodsCommandService } from "src/time-and-attendance/pay-period/services/pay-periods-command.service";
import { TimesheetsModule } from "src/time-and-attendance/time-tracker/timesheets/timesheets.module";
import { EmailToIdResolver } from "src/time-and-attendance/utils/resolve-email-id.utils";
import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service";
@Module({ @Module({
imports:[TimesheetsModule],
controllers: [PayPeriodsController], controllers: [PayPeriodsController],
providers: [PayPeriodsQueryService], providers: [
PayPeriodsQueryService,
PayPeriodsCommandService,
EmailToIdResolver,
TimesheetApprovalService,
],
}) })
export class PayperiodsModule {} export class PayperiodsModule {}

View File

@ -1,71 +1,72 @@
// import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from "@nestjs/common"; import { BadRequestException, ForbiddenException, Injectable, NotFoundException } from "@nestjs/common";
// import { PrismaService } from "src/prisma/prisma.service"; import { PrismaService } from "src/prisma/prisma.service";
// import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto"; import { BulkCrewApprovalDto } from "../dtos/bulk-crew-approval.dto";
// import { PayPeriodsQueryService } from "./pay-periods-query.service"; import { PayPeriodsQueryService } from "./pay-periods-query.service";
// import { TimesheetApprovalService } from "src/modules/timesheets/services/timesheet-approval.service"; import { TimesheetApprovalService } from "src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service";
// @Injectable() @Injectable()
// export class PayPeriodsCommandService { export class PayPeriodsCommandService {
// constructor( constructor(
// private readonly prisma: PrismaService, private readonly prisma: PrismaService,
// private readonly timesheets_approval: TimesheetApprovalService, private readonly timesheets_approval: TimesheetApprovalService,
// private readonly query: PayPeriodsQueryService, private readonly query: PayPeriodsQueryService,
// ) {} ) {}
// //function to approve pay-periods according to selected crew members //function to approve pay-periods according to selected crew members
// async bulkApproveCrew(dto:BulkCrewApprovalDto): Promise<{updated: number}> { async bulkApproveCrew(email: string, dto:BulkCrewApprovalDto): Promise<{updated: number}> {
// const { supervisor_email, include_subtree, items } = dto; const { include_subtree, items } = dto;
// if(!items?.length) throw new BadRequestException('no items to process'); if(!items?.length) throw new BadRequestException('no items to process');
// //fetch and validate supervisor status //fetch and validate supervisor status
// const supervisor = await this.query.getSupervisor(supervisor_email); const supervisor = await this.query.getSupervisor(email);
// if(!supervisor) throw new NotFoundException('No employee record linked to current user'); if(!supervisor) throw new NotFoundException('No employee record linked to current user');
// if(!supervisor.is_supervisor) throw new ForbiddenException('Employee is not a supervisor'); if(!supervisor.is_supervisor) throw new ForbiddenException('Employee is not a supervisor');
// //fetches emails of crew members linked to supervisor //fetches emails of crew members linked to supervisor
// const crew_emails = await this.query.resolveCrewEmails(supervisor.id, include_subtree); const crew_emails = await this.query.resolveCrewEmails(supervisor.id, include_subtree);
// for(const item of items) { for(const item of items) {
// if(!crew_emails.has(item.employee_email)) { if(!crew_emails.has(item.employee_email)) {
// throw new ForbiddenException(`Employee ${item.employee_email} not in supervisor crew`); throw new ForbiddenException(`Employee ${item.employee_email} not in supervisor crew`);
// } }
// } }
// const period_cache = new Map<string, {period_start: Date, period_end: Date}>(); const period_cache = new Map<string, {period_start: Date, period_end: Date}>();
// const getPeriod = async (y:number, no: number) => { const getPeriod = async (year:number, period_no: number) => {
// const key = `${y}-${no}`; const key = `${year}-${period_no}`;
// if(!period_cache.has(key)) return period_cache.get(key)!; if(period_cache.has(key)) return period_cache.get(key)!;
// const period = await this.query.getPeriodWindow(y,no);
// if(!period) throw new NotFoundException(`Pay period ${y}-${no} not found`);
// period_cache.set(key, period);
// return period;
// };
// let updated = 0; const period = await this.query.getPeriodWindow(year,period_no);
if(!period) throw new NotFoundException(`Pay period ${year}-${period_no} not found`);
period_cache.set(key, period);
return period;
};
// await this.prisma.$transaction(async (transaction) => { let updated = 0;
// for(const item of items) {
// const { period_start, period_end } = await getPeriod(item.pay_year, item.period_no);
// const t_sheets = await transaction.timesheets.findMany({ await this.prisma.$transaction(async (transaction) => {
// where: { for(const item of items) {
// employee: { user: { email: item.employee_email } }, const { period_start, period_end } = await getPeriod(item.pay_year, item.period_no);
// OR: [
// {shift : { some: { date: { gte: period_start, lte: period_end } } } },
// {expense: { some: { date: { gte: period_start, lte: period_end } } } },
// ],
// },
// select: { id: true },
// });
// for(const { id } of t_sheets) { const t_sheets = await transaction.timesheets.findMany({
// await this.timesheets_approval.updateApprovalWithTransaction(transaction, id, item.approve); where: {
// updated++; employee: { user: { email: item.employee_email } },
// } OR: [
{shift : { some: { date: { gte: period_start, lte: period_end } } } },
{expense: { some: { date: { gte: period_start, lte: period_end } } } },
],
},
select: { id: true },
});
// } for(const { id } of t_sheets) {
// }); await this.timesheets_approval.cascadeApprovalWithtx(transaction, id, item.approve);
// return {updated}; updated++;
// } }
// }
}
});
return {updated};
}
}

View File

@ -1,10 +1,11 @@
import { Controller, Param, Query, Body, Get, Post, BadRequestException, ParseIntPipe, Delete, Patch, Req } from "@nestjs/common"; import { Controller, Param, Query, Body, Get, Post, BadRequestException, ParseIntPipe, Delete, Patch, Req } from "@nestjs/common";
import { RolesAllowed } from "src/common/decorators/roles.decorators";
import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto"; import { SchedulePresetsDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/create-schedule-presets.dto";
import { SchedulePresetsUpdateDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto"; import { SchedulePresetsUpdateDto } from "src/time-and-attendance/time-tracker/schedule-presets/dtos/update-schedule-presets.dto";
import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service"; import { SchedulePresetsApplyService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-apply.service";
import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service"; import { SchedulePresetsGetService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-get.service";
import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service"; import { SchedulePresetsUpsertService } from "src/time-and-attendance/time-tracker/schedule-presets/services/schedule-presets-upsert.service";
import { Roles as RoleEnum } from '.prisma/client';
@Controller('schedule-presets') @Controller('schedule-presets')
export class SchedulePresetsController { export class SchedulePresetsController {
@ -16,45 +17,39 @@ export class SchedulePresetsController {
//used to create a schedule preset //used to create a schedule preset
@Post('create') @Post('create')
async createPreset( @Req() req, @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN)
@Body() dto: SchedulePresetsDto, async createPreset( @Req() req, @Body() dto: SchedulePresetsDto ) {
) {
const email = req.user?.email; const email = req.user?.email;
return await this.upsertService.createPreset(email, dto); return await this.upsertService.createPreset(email, dto);
} }
//used to update an already existing schedule preset //used to update an already existing schedule preset
@Patch('update/:preset_id') @Patch('update/:preset_id')
async updatePreset( @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN)
@Param('preset_id', ParseIntPipe) preset_id: number, async updatePreset( @Param('preset_id', ParseIntPipe) preset_id: number,@Body() dto: SchedulePresetsUpdateDto ) {
@Body() dto: SchedulePresetsUpdateDto,
) {
return await this.upsertService.updatePreset(preset_id, dto); return await this.upsertService.updatePreset(preset_id, dto);
} }
//used to delete a schedule preset //used to delete a schedule preset
@Delete('delete/:preset_id') @Delete('delete/:preset_id')
async deletePreset( @RolesAllowed(RoleEnum.ADMIN)
@Param('preset_id') preset_id: number, async deletePreset( @Param('preset_id') preset_id: number ) {
) {
return await this.upsertService.deletePreset(preset_id); return await this.upsertService.deletePreset(preset_id);
} }
//used to show the list of available schedule presets //used to show the list of available schedule presets
@Get('find-list') @Get('find-list')
@RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN)
async findListById( @Req() req) { async findListById( @Req() req) {
const email = req.user?.email; const email = req.user?.email;
return this.getService.getSchedulePresets(email); return this.getService.getSchedulePresets(email);
} }
//used to apply a preset to a timesheet //used to apply a preset to a timesheet
@Post('apply-presets') @Post('apply-presets')
async applyPresets( @Req() req, @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN)
@Query('preset') preset_name: string, async applyPresets( @Req() req, @Query('preset') preset_name: string, @Query('start') start_date: string ) {
@Query('start') start_date: string,
) {
const email = req.user?.email; const email = req.user?.email;
if(!preset_name?.trim()) throw new BadRequestException('Query "preset" is required'); if(!preset_name?.trim()) throw new BadRequestException('Query "preset" is required');
if(!start_date?.trim()) throw new BadRequestException('Query "start" is required YYYY-MM-DD'); if(!start_date?.trim()) throw new BadRequestException('Query "start" is required YYYY-MM-DD');

View File

@ -3,16 +3,16 @@ import { ShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift
import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto"; import { UpdateShiftDto } from "src/time-and-attendance/time-tracker/shifts/dtos/shift-update.dto";
import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service"; import { ShiftsUpsertService } from "src/time-and-attendance/time-tracker/shifts/services/shifts-upsert.service";
import { CreateShiftResult, UpdateShiftResult } from "src/time-and-attendance/utils/type.utils"; import { CreateShiftResult, UpdateShiftResult } from "src/time-and-attendance/utils/type.utils";
import { Roles as RoleEnum } from '.prisma/client';
import { RolesAllowed } from "src/common/decorators/roles.decorators";
@Controller('shift') @Controller('shift')
export class ShiftController { export class ShiftController {
constructor( private readonly upsert_service: ShiftsUpsertService ){} constructor( private readonly upsert_service: ShiftsUpsertService ){}
@Post('create') @Post('create')
createBatch( @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN)
@Req() req, createBatch( @Req() req, @Body()dtos: ShiftDto[]): Promise<CreateShiftResult[]> {
@Body()dtos: ShiftDto[]): Promise<CreateShiftResult[]> {
const email = req.user?.email; const email = req.user?.email;
const list = Array.isArray(dtos) ? dtos : []; const list = Array.isArray(dtos) ? dtos : [];
if(list.length === 0) throw new BadRequestException('Body is missing or invalid (create shifts)'); if(list.length === 0) throw new BadRequestException('Body is missing or invalid (create shifts)');
@ -22,14 +22,15 @@ export class ShiftController {
//change Body to receive dtos //change Body to receive dtos
@Patch('update') @Patch('update')
updateBatch( @RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN)
@Body() dtos: UpdateShiftDto[]): Promise<UpdateShiftResult[]>{ updateBatch( @Body() dtos: UpdateShiftDto[]): Promise<UpdateShiftResult[]>{
const list = Array.isArray(dtos) ? dtos: []; const list = Array.isArray(dtos) ? dtos: [];
if(list.length === 0) throw new BadRequestException('Body is missing or invalid (update shifts)'); if(list.length === 0) throw new BadRequestException('Body is missing or invalid (update shifts)');
return this.upsert_service.updateShifts(dtos); return this.upsert_service.updateShifts(dtos);
} }
@Delete(':shift_id') @Delete(':shift_id')
@RolesAllowed(RoleEnum.EMPLOYEE, RoleEnum.ACCOUNTING, RoleEnum.HR, RoleEnum.SUPERVISOR, RoleEnum.ADMIN)
remove(@Param('shift_id') shift_id: number ) { remove(@Param('shift_id') shift_id: number ) {
return this.upsert_service.deleteShift(shift_id); return this.upsert_service.deleteShift(shift_id);
} }

View File

@ -11,12 +11,9 @@ export class TimesheetController {
@Get() @Get()
@RolesAllowed(RoleEnum.SUPERVISOR, RoleEnum.HR, RoleEnum.ACCOUNTING, RoleEnum.ADMIN) @RolesAllowed(RoleEnum.SUPERVISOR, RoleEnum.HR, RoleEnum.ACCOUNTING, RoleEnum.ADMIN)
async getTimesheetByIds( async getTimesheetByIds(
@Req() req, @Req() req, @Query('year', ParseIntPipe) year:number, @Query('period_number', ParseIntPipe) period_number: number) {
@Query('year', ParseIntPipe) year: number,
@Query('period_number', ParseIntPipe) period_number: number,
) {
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);
} }
} }

View File

@ -27,11 +27,11 @@ import { Injectable } from "@nestjs/common";
const timesheet = await this.updateApprovalWithTransaction(transaction, timesheetId, isApproved); const timesheet = await this.updateApprovalWithTransaction(transaction, timesheetId, isApproved);
await transaction.shifts.updateMany({ await transaction.shifts.updateMany({
where: { timesheet_id: timesheetId }, where: { timesheet_id: timesheetId },
data: { is_approved: isApproved }, data: { is_approved: isApproved },
}); });
await transaction.expenses.updateManyAndReturn({ await transaction.expenses.updateManyAndReturn({
where: { timesheet_id: timesheetId }, where: { timesheet_id: timesheetId },
data: { is_approved: isApproved }, data: { is_approved: isApproved },
}); });
return timesheet; return timesheet;
} }

View File

@ -1,14 +1,19 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TimesheetController } from 'src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller'; import { TimesheetController } from 'src/time-and-attendance/time-tracker/timesheets/controllers/timesheet.controller';
import { TimesheetApprovalService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-approval.service';
import { TimesheetArchiveService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service'; import { TimesheetArchiveService } from 'src/time-and-attendance/time-tracker/timesheets/services/timesheet-archive.service';
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 { EmailToIdResolver } from 'src/time-and-attendance/utils/resolve-email-id.utils';
@Module({ @Module({
controllers: [TimesheetController], controllers: [TimesheetController],
providers: [ providers: [
TimesheetArchiveService, TimesheetArchiveService,
GetTimesheetsOverviewService, GetTimesheetsOverviewService,
TimesheetApprovalService,
EmailToIdResolver,
], ],
exports: [], exports: [],
}) })