-
Notifications
You must be signed in to change notification settings - Fork 3
feat: implementando CRUD de Eleições #404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
mateusmrosa
wants to merge
2
commits into
homol
Choose a base branch
from
feat/SA10.TV014-implementar-CRUD-eleicoes
base: homol
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import { ApiProperty } from '@nestjs/swagger'; | ||
| import { EleicaoTipo } from '@prisma/client'; | ||
| import { Transform, Type } from 'class-transformer'; | ||
| import { IsBoolean, IsEnum, IsInt, Max, Min } from 'class-validator'; | ||
|
|
||
| export class CreateEleicaoDto { | ||
| @ApiProperty({ enum: EleicaoTipo, enumName: 'EleicaoTipo' }) | ||
| @IsEnum(EleicaoTipo, { | ||
| message: '$property| Precisa ser um dos seguintes valores: ' + Object.values(EleicaoTipo).join(', '), | ||
| }) | ||
| tipo: EleicaoTipo; | ||
|
|
||
| @ApiProperty({ description: 'Ano da eleição', example: 2024 }) | ||
| @Type(() => Number) | ||
| @IsInt() | ||
| @Min(1900) | ||
| @Max(2100) | ||
| ano: number; | ||
|
|
||
| @ApiProperty({ description: 'Se esta eleição está sendo usada para mandatos atuais' }) | ||
| @IsBoolean() | ||
| @Transform(({ value }) => value === 'true' || value === true) | ||
| atual_para_mandatos: boolean; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| import { ApiPropertyOptional } from '@nestjs/swagger'; | ||
| import { EleicaoTipo } from '@prisma/client'; | ||
| import { Transform } from 'class-transformer'; | ||
| import { IsBoolean, IsEnum, IsInt, IsOptional, Max, Min } from 'class-validator'; | ||
|
|
||
| export class FilterEleicaoDto { | ||
| @ApiPropertyOptional({ enum: EleicaoTipo, enumName: 'EleicaoTipo' }) | ||
| @IsOptional() | ||
| @IsEnum(EleicaoTipo) | ||
| tipo?: EleicaoTipo; | ||
|
|
||
| @ApiPropertyOptional({ description: 'Ano da eleição' }) | ||
| @IsOptional() | ||
| @IsInt() | ||
| @Min(1900) | ||
| @Max(2100) | ||
| @Transform(({ value }) => (value === '' || value === null || value === undefined ? undefined : +value)) | ||
| ano?: number; | ||
|
|
||
| @ApiPropertyOptional({ description: 'Filtrar apenas eleições atuais para mandatos' }) | ||
| @IsOptional() | ||
| @IsBoolean() | ||
| @Transform(({ value }) => { | ||
| if (value === undefined || value === null || value === '') { | ||
| return undefined; | ||
| } | ||
| if (typeof value === 'string') { | ||
| return value.toLowerCase() === 'true'; | ||
| } | ||
| return value; | ||
| }) | ||
| atual_para_mandatos?: boolean; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import { PartialType } from '@nestjs/swagger'; | ||
| import { CreateEleicaoDto } from './create-eleicao.dto'; | ||
| import { Transform } from 'class-transformer'; | ||
| import { IsBoolean, IsOptional } from 'class-validator'; | ||
|
|
||
| export class UpdateEleicaoDto extends PartialType(CreateEleicaoDto) { | ||
| @IsOptional() | ||
| @IsBoolean() | ||
| @Transform(({ value }) => | ||
| value === '' || value === null || value === undefined | ||
| ? undefined | ||
| : typeof value === 'string' | ||
| ? value.toLowerCase() === 'true' | ||
| : value | ||
| ) | ||
| atual_para_mandatos?: boolean; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,65 @@ | ||
| import { Controller, Get } from '@nestjs/common'; | ||
| import { Body, Controller, Delete, Get, HttpCode, Param, Patch, Post, Query } from '@nestjs/common'; | ||
| import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; | ||
| import { CurrentUser } from '../auth/decorators/current-user.decorator'; | ||
| import { PessoaFromJwt } from '../auth/models/PessoaFromJwt'; | ||
| import { EleicaoService } from './eleicao.service'; | ||
| import { Roles } from 'src/auth/decorators/roles.decorator'; | ||
| import { CreateEleicaoDto } from './dto/create-eleicao.dto'; | ||
| import { RecordWithId } from 'src/common/dto/record-with-id.dto'; | ||
| import { FilterEleicaoDto } from './dto/filter-eleicao.dto'; | ||
| import { Eleicao } from '@prisma/client'; | ||
| import { FindOneParams } from 'src/common/decorators/find-params'; | ||
| import { UpdateEleicaoDto } from './dto/update-eleicao.dto'; | ||
| import { Logger } from '@nestjs/common'; | ||
| import { ListEleicaoDto } from './entity/eleicao.entity'; | ||
|
|
||
| @ApiTags('Eleição') | ||
| @Controller('eleicao') | ||
| export class EleicaoController { | ||
| constructor(private readonly eleicaoService: EleicaoService) {} | ||
|
|
||
| @Post() | ||
| @ApiBearerAuth('access-token') | ||
| @Roles(['SMAE.superadmin']) | ||
| async create( | ||
| @Body() createEleicaoDto: CreateEleicaoDto, | ||
| @CurrentUser() user: PessoaFromJwt | ||
| ): Promise<RecordWithId> { | ||
| const created = await this.eleicaoService.create(createEleicaoDto, user); | ||
| return { id: created.id }; | ||
| } | ||
|
|
||
| @Get() | ||
| @ApiBearerAuth('access-token') | ||
| async getList(@CurrentUser() user: PessoaFromJwt) { | ||
| return await this.eleicaoService.findAll(); | ||
| @Roles(['SMAE.superadmin']) | ||
| async findAll(@Query() filters: FilterEleicaoDto): Promise<ListEleicaoDto[]> { | ||
| return this.eleicaoService.findAll(filters); | ||
| } | ||
|
|
||
| @Get(':id') | ||
| @ApiBearerAuth('access-token') | ||
| @Roles(['SMAE.superadmin']) | ||
| async findOne(@Param() params: FindOneParams): Promise<Eleicao> { | ||
| return this.eleicaoService.findOne(params.id); | ||
| } | ||
|
|
||
| @Patch(':id') | ||
| @ApiBearerAuth('access-token') | ||
| @Roles(['SMAE.superadmin']) | ||
| async update( | ||
| @Param() params: FindOneParams, | ||
| @Body() updateEleicaoDto: UpdateEleicaoDto, | ||
| @CurrentUser() user: PessoaFromJwt | ||
| ): Promise<RecordWithId> { | ||
| const updated = await this.eleicaoService.update(params.id, updateEleicaoDto, user); | ||
| return { id: updated.id }; | ||
| } | ||
|
|
||
| @Delete(':id') | ||
| @ApiBearerAuth('access-token') | ||
| @Roles(['SMAE.superadmin']) | ||
| @HttpCode(204) | ||
| async remove(@Param() params: FindOneParams, @CurrentUser() user: PessoaFromJwt): Promise<void> { | ||
| await this.eleicaoService.remove(params.id, user); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,161 @@ | ||
| import { Injectable } from '@nestjs/common'; | ||
| import { Injectable, BadRequestException } from '@nestjs/common'; | ||
| import { PrismaService } from '../prisma/prisma.service'; | ||
| import { EleicaoDto } from './entity/eleicao.entity'; | ||
| import { Prisma } from '@prisma/client'; | ||
| import { ListEleicaoDto } from './entity/eleicao.entity'; | ||
| import { CreateEleicaoDto } from './dto/create-eleicao.dto'; | ||
| import { Eleicao } from '@prisma/client'; | ||
| import { FilterEleicaoDto } from './dto/filter-eleicao.dto'; | ||
| import { UpdateEleicaoDto } from './dto/update-eleicao.dto'; | ||
| import { LoggerWithLog } from 'src/common/LoggerWithLog'; | ||
| import { PessoaFromJwt } from 'src/auth/models/PessoaFromJwt'; | ||
|
|
||
| @Injectable() | ||
| export class EleicaoService { | ||
| constructor(private readonly prisma: PrismaService) {} | ||
|
|
||
| async findAll(): Promise<EleicaoDto[]> { | ||
| return await this.prisma.eleicao.findMany(); | ||
| async create(createEleicaoDto: CreateEleicaoDto, user: PessoaFromJwt): Promise<Eleicao> { | ||
| const logger = LoggerWithLog('Eleição: Criação'); | ||
| try { | ||
| const created = await this.prisma.$transaction(async (tx) => { | ||
| logger.log(`Criando eleição do tipo ${createEleicaoDto.tipo}, ano ${createEleicaoDto.ano}`); | ||
| const record = await tx.eleicao.create({ | ||
| data: createEleicaoDto, | ||
| select: { | ||
| id: true, | ||
| tipo: true, | ||
| ano: true, | ||
| atual_para_mandatos: true, | ||
| removido_em: true, | ||
| }, | ||
| }); | ||
| await logger.saveLogs(tx, user.getLogData()); | ||
| return record; | ||
| }); | ||
| return created; | ||
| } catch (error) { | ||
| if (error instanceof Prisma.PrismaClientKnownRequestError) { | ||
| if (error.code === 'P2002') { | ||
| throw new BadRequestException('Já existe uma eleição deste tipo para este ano'); | ||
| } | ||
| } | ||
| throw error; | ||
| } | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| async findAll(filters: FilterEleicaoDto): Promise<ListEleicaoDto[]> { | ||
| const eleicoes = await this.prisma.eleicao.findMany({ | ||
| where: { | ||
| removido_em: null, | ||
| tipo: filters?.tipo, | ||
| ano: filters?.ano, | ||
| atual_para_mandatos: filters?.atual_para_mandatos, | ||
| }, | ||
| select: { | ||
| id: true, | ||
| tipo: true, | ||
| ano: true, | ||
| atual_para_mandatos: true, | ||
| removido_em: true, | ||
| }, | ||
| orderBy: [{ ano: 'desc' }, { tipo: 'asc' }], | ||
| }); | ||
| return [{ linhas: eleicoes }]; | ||
| } | ||
mateusmrosa marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| async findOne(id: number): Promise<Eleicao> { | ||
| const eleicao = await this.prisma.eleicao.findFirst({ | ||
| where: { | ||
| id: id, | ||
| removido_em: null, | ||
| }, | ||
| select: { | ||
| id: true, | ||
| tipo: true, | ||
| ano: true, | ||
| atual_para_mandatos: true, | ||
| removido_em: true, | ||
| }, | ||
| }); | ||
|
|
||
| if (!eleicao) { | ||
| throw new BadRequestException('Eleição não encontrada'); | ||
| } | ||
|
|
||
| return eleicao; | ||
| } | ||
|
|
||
| async update(id: number, dto: UpdateEleicaoDto, user: PessoaFromJwt): Promise<Eleicao> { | ||
| const logger = LoggerWithLog('Eleição: Atualização'); | ||
|
|
||
| if (dto.tipo !== undefined && dto.ano !== undefined) { | ||
| const conflito = await this.prisma.eleicao.count({ | ||
| where: { | ||
| tipo: dto.tipo, | ||
| ano: dto.ano, | ||
| removido_em: null, | ||
| NOT: { id }, | ||
| }, | ||
| }); | ||
| if (conflito > 0) { | ||
| throw new BadRequestException('Já existe uma eleição deste tipo para este ano'); | ||
| } | ||
| } | ||
|
|
||
| try { | ||
| const updated = await this.prisma.$transaction(async (tx) => { | ||
| logger.log( | ||
| `Atualizando eleição ${id}: novo tipo=${dto.tipo ?? 'sem alteração'}, novo ano=${dto.ano ?? 'sem alteração'}, atual_para_mandatos=${dto.atual_para_mandatos ?? 'sem alteração'}` | ||
| ); | ||
| const record = await tx.eleicao.update({ | ||
| where: { id }, | ||
| data: dto, | ||
| select: { | ||
| id: true, | ||
| tipo: true, | ||
| ano: true, | ||
| atual_para_mandatos: true, | ||
| removido_em: true, | ||
| }, | ||
| }); | ||
| await logger.saveLogs(tx, user.getLogData()); | ||
| return record; | ||
| }); | ||
| return updated; | ||
| } catch (error) { | ||
| if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002') { | ||
| throw new BadRequestException('Já existe uma eleição deste tipo para este ano'); | ||
| } | ||
| throw error; | ||
| } | ||
| } | ||
|
|
||
| async remove(id: number, user: PessoaFromJwt): Promise<void> { | ||
| const logger = LoggerWithLog('Eleição: Remoção'); | ||
| logger.log(`Removendo eleição ${id}`); | ||
| await this.findOne(id); // Verifica se existe | ||
|
|
||
| // Verifica se tem relacionamentos antes de remover | ||
| const mandatos = await this.prisma.parlamentarMandato.count({ | ||
| where: { eleicao_id: id }, | ||
| }); | ||
|
|
||
| const comparecimentos = await this.prisma.eleicaoComparecimento.count({ | ||
| where: { eleicao_id: id }, | ||
| }); | ||
|
|
||
| if (mandatos > 0 || comparecimentos > 0) { | ||
| throw new BadRequestException( | ||
| 'Não é possível remover esta eleição pois ela possui mandatos ou comparecimentos associados' | ||
| ); | ||
| } | ||
|
|
||
| await this.prisma.$transaction(async (prismaTx: Prisma.TransactionClient) => { | ||
| logger.log(`Eleição: ${id}`); | ||
| await prismaTx.eleicao.update({ | ||
| where: { id: id }, | ||
| data: { removido_em: new Date() }, | ||
| }); | ||
| await logger.saveLogs(prismaTx, user.getLogData()); | ||
| }); | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.