import { Controller, ForbiddenException, Get, Param, ParseBoolPipe, ParseIntPipe, Patch, Query } from "@nestjs/common"; import { ApiNotFoundResponse, ApiOperation, ApiParam, ApiQuery, ApiResponse, ApiTags } from "@nestjs/swagger"; import { PayPeriodDto } from "../dtos/pay-period.dto"; import { PayPeriodOverviewDto } from "../dtos/overview-pay-period.dto"; import { PayPeriodsQueryService } from "../services/pay-periods-query.service"; import { RolesAllowed } from "src/common/decorators/roles.decorators"; import { Roles as RoleEnum } from '.prisma/client'; import { Req } from '@nestjs/common'; import { Request } from 'express'; import { PayPeriodsCommandService } from "../services/pay-periods-command.service"; import { PayPeriodBundleDto } from "../dtos/bundle-pay-period.dto"; @ApiTags('pay-periods') @Controller('pay-periods') export class PayPeriodsController { constructor( private readonly queryService: PayPeriodsQueryService, private readonly commandService: PayPeriodsCommandService, ) {} @Get() @ApiOperation({ summary: 'Find all pay period' }) @ApiResponse({status: 200,description: 'List of pay period found', type: PayPeriodDto, isArray: true }) async findAll(): Promise { return this.queryService.findAll(); } @Get('bundle/current-and-all') @ApiOperation({summary: 'Return current pay period and the full list'}) @ApiQuery({name: 'date', required:false, example: '2025-08-11', description:'Override for resolving the current period'}) @ApiResponse({status: 200, description:'Find current and all pay periods', type: PayPeriodBundleDto}) async getCurrentAndAll(@Query('date') date?: string): Promise { const [current, periods] = await Promise.all([ this.queryService.findCurrent(date), this.queryService.findAll(), ]); return { current, periods }; } @Get("date/:date") @ApiOperation({ summary: "Resolve a period by a date within it" }) @ApiResponse({ status: 200, description: "Pay period found for the selected date", type: PayPeriodDto }) @ApiNotFoundResponse({ description: "Pay period not found for the selected date" }) async findByDate(@Param("date") date: string) { return this.queryService.findByDate(date); } @Get(":year/:periodNumber") @ApiOperation({ summary: "Find pay period by year and period number" }) @ApiParam({ name: "year", type: Number, example: 2024 }) @ApiParam({ name: "periodNumber", type: Number, example: 1, description: "1..26" }) @ApiResponse({ status: 200, description: "Pay period found", type: PayPeriodDto }) @ApiNotFoundResponse({ description: "Pay period not found" }) async findOneByYear( @Param("year", ParseIntPipe) year: number, @Param("periodNumber", ParseIntPipe) periodNumber: number, ) { return this.queryService.findOneByYearPeriod(year, periodNumber); } @Patch(":year/:periodNumber/approval") @RolesAllowed(RoleEnum.ACCOUNTING, RoleEnum.ADMIN, RoleEnum.HR, RoleEnum.SUPERVISOR) @ApiOperation({ summary: "Approve all timesheets with activity in the period" }) @ApiParam({ name: "year", type: Number, example: 2024 }) @ApiParam({ name: "periodNumber", type: Number, example: 1, description: "1..26" }) @ApiResponse({ status: 200, description: "Pay period approved" }) async approve( @Param("year", ParseIntPipe) year: number, @Param("periodNumber", ParseIntPipe) periodNumber: number, ) { await this.commandService.approvalPayPeriod(year, periodNumber); return { message: `Pay-period ${year}-${periodNumber} approved` }; } @Get(':year/:periodNumber/crew-overview') @RolesAllowed(RoleEnum.SUPERVISOR) @ApiOperation({ summary: 'Supervisor crew overview for a given pay period' }) @ApiParam({ name: 'year', type: Number, example: 2024 }) @ApiParam({ name: 'periodNumber', type: Number, example: 1, description: '1..26' }) @ApiQuery({ name: 'includeSubtree', required: false, type: Boolean, example: false, description: 'Include indirect reports' }) @ApiResponse({ status: 200, description: 'Crew overview', type: PayPeriodOverviewDto }) @ApiNotFoundResponse({ description: 'Pay period not found' }) async getCrewOverview( @Param('year', ParseIntPipe) year: number, @Param('periodNumber', ParseIntPipe) periodNumber: number, @Query('includeSubtree', new ParseBoolPipe({ optional: true })) includeSubtree = false, @Req() req: Request, ): Promise { const rawUser = (req as any).user ?? {}; const userId: string | undefined = rawUser.id ?? rawUser.sub ?? rawUser.userId; //needs ajusting according to passport logic if (!userId) { throw new ForbiddenException('Authenticated user not found on request'); } return this.queryService.getCrewOverview(year, periodNumber, userId, includeSubtree); } @Get(':year/:periodNumber/overview') @ApiOperation({ summary: 'Detailed view of a pay period by year + number' }) @ApiParam({ name: 'year', type: Number, example: 2024 }) @ApiParam({ name: 'periodNumber', type: Number, example: 1, description: '1..26' }) @ApiResponse({ status: 200, description: 'Pay period overview found', type: PayPeriodOverviewDto }) @ApiNotFoundResponse({ description: 'Pay period not found' }) async getOverviewByYear( @Param('year', ParseIntPipe) year: number, @Param('periodNumber', ParseIntPipe) periodNumber: number, ): Promise { return this.queryService.getOverviewByYearPeriod(year, periodNumber); } }