From d9411058327daefc4f622bbae627b63f1fd4f18a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Fran=C3=A7a?= Date: Thu, 13 Nov 2025 10:13:46 -0300 Subject: [PATCH 1/5] refactor(patient): add condition enum and referrals relationship --- src/domain/schemas/patient.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/domain/schemas/patient.ts b/src/domain/schemas/patient.ts index 67df24f..65d673a 100644 --- a/src/domain/schemas/patient.ts +++ b/src/domain/schemas/patient.ts @@ -34,7 +34,11 @@ export type PatientStatisticsResult = { }; export const PATIENT_CONDITION = ['in_crisis', 'stable'] as const; +<<<<<<< HEAD export type PatientCondition = (typeof PATIENT_CONDITION)[number]; +======= +export type PatientConditionType = (typeof PATIENT_CONDITION)[number]; +>>>>>>> a9e2708 (refactor(patient): add condition enum and referrals relationship) export const patientSchema = z .object({ From 13dd419d434d9270994245bd152c5cb8a81c48f5 Mon Sep 17 00:00:00 2001 From: jorgeCasalini Date: Wed, 19 Nov 2025 15:32:20 -0300 Subject: [PATCH 2/5] feat(referrals): create endpoint register new referrals --- .../http/referrals/referrals.controller.ts | 21 +++++++++-- src/app/http/referrals/referrals.dtos.ts | 5 +++ src/app/http/referrals/referrals.module.ts | 8 ++++- .../http/referrals/referrals.repository.ts | 25 +++++++++++++ src/app/http/referrals/referrals.service.ts | 35 ++++++++++++++++++- src/domain/entities/referral.ts | 4 +-- src/domain/schemas/patient.ts | 4 --- src/domain/schemas/referral.ts | 10 ++++++ 8 files changed, 102 insertions(+), 10 deletions(-) create mode 100644 src/app/http/referrals/referrals.repository.ts diff --git a/src/app/http/referrals/referrals.controller.ts b/src/app/http/referrals/referrals.controller.ts index ee7985c..59f7155 100644 --- a/src/app/http/referrals/referrals.controller.ts +++ b/src/app/http/referrals/referrals.controller.ts @@ -1,10 +1,27 @@ -import { Controller } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { CurrentUser } from '@/common/decorators/current-user.decorator'; +import { Roles } from '@/common/decorators/roles.decorator'; +import { BaseResponseSchema } from '@/domain/schemas/base'; +import { UserSchema } from '@/domain/schemas/user'; + +import { CreateReferralsDto } from './referrals.dtos'; import { ReferralsService } from './referrals.service'; @ApiTags('Encaminhamentos') @Controller('referrals') export class ReferralsController { constructor(private readonly referralsService: ReferralsService) {} + + @Post() + @Roles(['manager', 'nurse']) + @ApiOperation({ summary: 'Cadastra novo encaminhamento.' }) + async create( + @Body() createReferralsDto: CreateReferralsDto, + @CurrentUser() currentUser: UserSchema, + ): Promise { + await this.referralsService.create(createReferralsDto, currentUser.id); + return { success: true, message: 'Encaminhamento cadastrado com sucesso.' }; + } } diff --git a/src/app/http/referrals/referrals.dtos.ts b/src/app/http/referrals/referrals.dtos.ts index e69de29..d5c0287 100644 --- a/src/app/http/referrals/referrals.dtos.ts +++ b/src/app/http/referrals/referrals.dtos.ts @@ -0,0 +1,5 @@ +import { createZodDto } from 'nestjs-zod'; + +import { createReferralSchema } from '@/domain/schemas/referral'; + +export class CreateReferralsDto extends createZodDto(createReferralSchema) {} diff --git a/src/app/http/referrals/referrals.module.ts b/src/app/http/referrals/referrals.module.ts index c4a0119..fc6326e 100644 --- a/src/app/http/referrals/referrals.module.ts +++ b/src/app/http/referrals/referrals.module.ts @@ -1,10 +1,16 @@ import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { Referral } from '@/domain/entities/referral'; + +import { PatientsModule } from '../patients/patients.module'; import { ReferralsController } from './referrals.controller'; +import { ReferralsRepository } from './referrals.repository'; import { ReferralsService } from './referrals.service'; @Module({ + imports: [PatientsModule, TypeOrmModule.forFeature([Referral])], controllers: [ReferralsController], - providers: [ReferralsService], + providers: [ReferralsService, ReferralsRepository], }) export class ReferralsModule {} diff --git a/src/app/http/referrals/referrals.repository.ts b/src/app/http/referrals/referrals.repository.ts new file mode 100644 index 0000000..a66c4cd --- /dev/null +++ b/src/app/http/referrals/referrals.repository.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; + +import { Referral } from '@/domain/entities/referral'; +import { ReferralStatus } from '@/domain/schemas/referral'; + +import { CreateReferralsDto } from './referrals.dtos'; + +@Injectable() +export class ReferralsRepository { + constructor( + @InjectRepository(Referral) + private readonly referralsRepository: Repository, + ) {} + public async create( + createReferralsDto: CreateReferralsDto & { + status: ReferralStatus; + referred_by: string; + }, + ): Promise { + const referrals = this.referralsRepository.create(createReferralsDto); + return await this.referralsRepository.save(referrals); + } +} diff --git a/src/app/http/referrals/referrals.service.ts b/src/app/http/referrals/referrals.service.ts index b72e83b..9c51a98 100644 --- a/src/app/http/referrals/referrals.service.ts +++ b/src/app/http/referrals/referrals.service.ts @@ -1,6 +1,39 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; + +import { PatientsRepository } from '../patients/patients.repository'; +import { CreateReferralsDto } from './referrals.dtos'; +import { ReferralsRepository } from './referrals.repository'; @Injectable() export class ReferralsService { private readonly logger = new Logger(ReferralsService.name); + constructor( + private readonly referralsRepository: ReferralsRepository, + private readonly patientRepository: PatientsRepository, + ) {} + public async create( + createReferralsDto: CreateReferralsDto, + userId: string, + ): Promise { + const { patient_id, date, category, condition, annotation, referred_to } = + createReferralsDto; + const patient = await this.patientRepository.findById(patient_id); + if (!patient) { + throw new NotFoundException('Paciente não encontrado.'); + } + await this.referralsRepository.create({ + patient_id, + date, + category, + condition, + annotation, + referred_to, + status: 'scheduled', + referred_by: userId, + }); + this.logger.log( + { patientId: patient_id, referredBy: userId }, + 'Referral created successfully', + ); + } } diff --git a/src/domain/entities/referral.ts b/src/domain/entities/referral.ts index 22316cb..ca24574 100644 --- a/src/domain/entities/referral.ts +++ b/src/domain/entities/referral.ts @@ -8,7 +8,7 @@ import { UpdateDateColumn, } from 'typeorm'; -import { PATIENT_CONDITION, PatientCondition } from '../schemas/patient'; +import { PATIENT_CONDITION, PatientConditionType } from '../schemas/patient'; import { REFERRAL_CATEGORY, REFERRAL_STATUS, @@ -33,7 +33,7 @@ export class Referral implements ReferralSchema { category: ReferralCategory; @Column({ type: 'enum', enum: PATIENT_CONDITION }) - condition: PatientCondition; + condition: PatientConditionType; @Column({ type: 'enum', enum: REFERRAL_STATUS, default: 'scheduled' }) status: ReferralStatus; diff --git a/src/domain/schemas/patient.ts b/src/domain/schemas/patient.ts index 65d673a..a9ab50b 100644 --- a/src/domain/schemas/patient.ts +++ b/src/domain/schemas/patient.ts @@ -34,11 +34,7 @@ export type PatientStatisticsResult = { }; export const PATIENT_CONDITION = ['in_crisis', 'stable'] as const; -<<<<<<< HEAD -export type PatientCondition = (typeof PATIENT_CONDITION)[number]; -======= export type PatientConditionType = (typeof PATIENT_CONDITION)[number]; ->>>>>>> a9e2708 (refactor(patient): add condition enum and referrals relationship) export const patientSchema = z .object({ diff --git a/src/domain/schemas/referral.ts b/src/domain/schemas/referral.ts index 2f82f55..6942563 100644 --- a/src/domain/schemas/referral.ts +++ b/src/domain/schemas/referral.ts @@ -40,3 +40,13 @@ export const referralSchema = z }) .strict(); export type ReferralSchema = z.infer; + +export const createReferralSchema = referralSchema.pick({ + patient_id: true, + date: true, + category: true, + condition: true, + annotation: true, + referred_to: true, +}); +export type CreateReferralsSchema = z.infer; From ab347c0a35a138edf1312c2e9f1ec1973e70fcb2 Mon Sep 17 00:00:00 2001 From: jorgeCasalini Date: Tue, 2 Dec 2025 15:25:56 -0300 Subject: [PATCH 3/5] feat(referral): create schema referral by category --- src/domain/schemas/referral.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/domain/schemas/referral.ts b/src/domain/schemas/referral.ts index d327962..5c74bac 100644 --- a/src/domain/schemas/referral.ts +++ b/src/domain/schemas/referral.ts @@ -1,6 +1,8 @@ import { z } from 'zod'; +import { baseResponseSchema } from './base'; import { PATIENT_CONDITIONS } from './patient'; +import { baseQuerySchema } from './query'; export const REFERRAL_STATUSES = [ 'scheduled', @@ -50,3 +52,29 @@ export const createReferralSchema = referralSchema.pick({ referred_to: true, }); export type CreateReferralSchema = z.infer; + +export const getReferralByPeriodSchema = baseQuerySchema + .pick({ + period: true, + limit: true, + order: true, + withPercentage: true, + }) + .extend({ order: baseQuerySchema.shape.order.default('DESC') }); + +export const referralByCategorySchema = z.object({ + category: z.enum(REFERRAL_CATEGORIES), + total: z.number(), +}); +export type ReferralByCategoryType = z.infer; + +export const getReferralByCategoryResponseSchema = baseResponseSchema.extend({ + data: z.object({ + categories: z.array(referralByCategorySchema), + total: z.number(), + }), +}); +export type GetReferralByCategoryResponse = z.infer< + typeof getReferralByCategoryResponseSchema +>; +export type ReferralFieldType = 'category'; From 8a79770976d566a413d79c89961cc2e1f1327dab Mon Sep 17 00:00:00 2001 From: jorgeCasalini Date: Tue, 2 Dec 2025 15:43:04 -0300 Subject: [PATCH 4/5] feat(referral): create endpoint to get referrals by category --- .../http/referrals/referrals.controller.ts | 35 +++++++++++++- src/app/http/referrals/referrals.dtos.ts | 9 +++- src/app/http/referrals/referrals.module.ts | 3 +- .../http/referrals/referrals.repository.ts | 48 ++++++++++++++++++- src/app/http/referrals/referrals.service.ts | 21 +++++++- 5 files changed, 109 insertions(+), 7 deletions(-) diff --git a/src/app/http/referrals/referrals.controller.ts b/src/app/http/referrals/referrals.controller.ts index 87db5aa..21a1d29 100644 --- a/src/app/http/referrals/referrals.controller.ts +++ b/src/app/http/referrals/referrals.controller.ts @@ -1,12 +1,24 @@ -import { Body, Controller, Param, Patch, Post } from '@nestjs/common'; +import { + Body, + Controller, + Get, + Param, + Patch, + Post, + Query, +} from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { CurrentUser } from '@/common/decorators/current-user.decorator'; import { Roles } from '@/common/decorators/roles.decorator'; import { BaseResponseSchema } from '@/domain/schemas/base'; +import { + GetReferralByCategoryResponse, + ReferralByCategoryType, +} from '@/domain/schemas/referral'; import { UserSchema } from '@/domain/schemas/user'; -import { CreateReferralDto } from './referrals.dtos'; +import { CreateReferralDto, GetReferralByPeriodDto } from './referrals.dtos'; import { ReferralsService } from './referrals.service'; @ApiTags('Encaminhamentos') @@ -26,6 +38,25 @@ export class ReferralsController { return { success: true, message: 'Encaminhamento cadastrado com sucesso.' }; } + @Get('referrals-by-category') + @Roles(['manager', 'nurse']) + @ApiOperation({ summary: 'Encaminhamentos por categoria.' }) + async getReferralByCategory( + @Query() query: GetReferralByPeriodDto, + ): Promise { + const { items: categories, total } = + await this.referralsService.getReferralByPeriod( + 'category', + query, + ); + + return { + success: true, + message: 'Encaminhamentos por categorias retornado com sucesso.', + data: { categories, total }, + }; + } + @Patch(':id/cancel') @Roles(['nurse', 'manager', 'specialist']) @ApiOperation({ summary: 'Cancela um encaminhamento.' }) diff --git a/src/app/http/referrals/referrals.dtos.ts b/src/app/http/referrals/referrals.dtos.ts index 4440849..429bea8 100644 --- a/src/app/http/referrals/referrals.dtos.ts +++ b/src/app/http/referrals/referrals.dtos.ts @@ -1,5 +1,12 @@ import { createZodDto } from 'nestjs-zod'; -import { createReferralSchema } from '@/domain/schemas/referral'; +import { + createReferralSchema, + getReferralByPeriodSchema, +} from '@/domain/schemas/referral'; export class CreateReferralDto extends createZodDto(createReferralSchema) {} + +export class GetReferralByPeriodDto extends createZodDto( + getReferralByPeriodSchema, +) {} diff --git a/src/app/http/referrals/referrals.module.ts b/src/app/http/referrals/referrals.module.ts index 01388bf..6ed32e9 100644 --- a/src/app/http/referrals/referrals.module.ts +++ b/src/app/http/referrals/referrals.module.ts @@ -2,6 +2,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Referral } from '@/domain/entities/referral'; +import { UtilsModule } from '@/utils/utils.module'; import { PatientsModule } from '../patients/patients.module'; import { ReferralsController } from './referrals.controller'; @@ -9,7 +10,7 @@ import { ReferralsRepository } from './referrals.repository'; import { ReferralsService } from './referrals.service'; @Module({ - imports: [PatientsModule, TypeOrmModule.forFeature([Referral])], + imports: [PatientsModule, TypeOrmModule.forFeature([Referral]), UtilsModule], controllers: [ReferralsController], providers: [ReferralsService, ReferralsRepository], exports: [ReferralsRepository], diff --git a/src/app/http/referrals/referrals.repository.ts b/src/app/http/referrals/referrals.repository.ts index e7fd060..637790b 100644 --- a/src/app/http/referrals/referrals.repository.ts +++ b/src/app/http/referrals/referrals.repository.ts @@ -3,9 +3,12 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Referral } from '@/domain/entities/referral'; -import { ReferralStatusType } from '@/domain/schemas/referral'; +import { + ReferralFieldType, + ReferralStatusType, +} from '@/domain/schemas/referral'; -import { CreateReferralDto } from './referrals.dtos'; +import { CreateReferralDto, GetReferralByPeriodDto } from './referrals.dtos'; @Injectable() export class ReferralsRepository { @@ -28,6 +31,47 @@ export class ReferralsRepository { return await this.referralsRepository.save(referrals); } + async getReferralsByPeriod( + field: ReferralFieldType, + startDate: Date, + endDate: Date, + query: GetReferralByPeriodDto, + ): Promise<{ items: T[]; total: number }> { + const totalQuery = this.referralsRepository + .createQueryBuilder('referral') + .select(`COUNT(DISTINCT referral.${field})`, 'total') + .where('referral.date BETWEEN :start AND :end', { + start: startDate, + end: endDate, + }); + + const totalResult = await totalQuery.getRawOne<{ total: string }>(); + const total = Number(totalResult?.total ?? 0); + + const queryBuilder = this.referralsRepository + .createQueryBuilder('referral') + .select(`referral.${field}`, field) + .addSelect('COUNT(*)', 'total') + .where('referral.date BETWEEN :start AND :end', { + start: startDate, + end: endDate, + }) + .groupBy(`referral.${field}`) + .orderBy('total', query.order) + .limit(query.limit); + + if (query.withPercentage) { + queryBuilder.addSelect( + 'ROUND((COUNT(*) * 100.0 / SUM(COUNT(*)) OVER()), 1)', + 'percentage', + ); + } + + const items = await queryBuilder.getRawMany(); + + return { items, total }; + } + public async cancel(id: string): Promise { return await this.referralsRepository.save({ id, status: 'canceled' }); } diff --git a/src/app/http/referrals/referrals.service.ts b/src/app/http/referrals/referrals.service.ts index 65f97d7..0eb40cf 100644 --- a/src/app/http/referrals/referrals.service.ts +++ b/src/app/http/referrals/referrals.service.ts @@ -5,10 +5,12 @@ import { NotFoundException, } from '@nestjs/common'; +import { ReferralFieldType } from '@/domain/schemas/referral'; import { UserSchema } from '@/domain/schemas/user'; +import { UtilsService } from '@/utils/utils.service'; import { PatientsRepository } from '../patients/patients.repository'; -import { CreateReferralDto } from './referrals.dtos'; +import { CreateReferralDto, GetReferralByPeriodDto } from './referrals.dtos'; import { ReferralsRepository } from './referrals.repository'; @Injectable() @@ -18,6 +20,7 @@ export class ReferralsService { constructor( private readonly referralsRepository: ReferralsRepository, private readonly patientsRepository: PatientsRepository, + private readonly utilsService: UtilsService, ) {} public async create( @@ -44,6 +47,22 @@ export class ReferralsService { ); } + async getReferralByPeriod( + filter: ReferralFieldType, + query: GetReferralByPeriodDto, + ): Promise<{ items: T[]; total: number }> { + const { startDate, endDate } = this.utilsService.getDateRangeForPeriod( + query.period, + ); + + return await this.referralsRepository.getReferralsByPeriod( + filter, + startDate, + endDate, + query, + ); + } + async cancel(id: string, user: UserSchema): Promise { const referral = await this.referralsRepository.findById(id); From fc34320253f1065478cd13edd71fa472025c1335 Mon Sep 17 00:00:00 2001 From: Juliano Sill Date: Sat, 6 Dec 2025 23:52:48 -0300 Subject: [PATCH 5/5] refactor(statistics): refactor total referrals by category --- .../http/referrals/referrals.controller.ts | 35 +------ src/app/http/referrals/referrals.dtos.ts | 9 +- src/app/http/referrals/referrals.module.ts | 3 +- .../http/referrals/referrals.repository.ts | 93 ++++++++++--------- src/app/http/referrals/referrals.service.ts | 21 +---- .../http/statistics/statistics.controller.ts | 21 +++++ src/app/http/statistics/statistics.dtos.ts | 5 + src/app/http/statistics/statistics.service.ts | 15 +++ src/domain/schemas/referral.ts | 28 ------ src/domain/schemas/statistics.ts | 24 +++++ 10 files changed, 120 insertions(+), 134 deletions(-) diff --git a/src/app/http/referrals/referrals.controller.ts b/src/app/http/referrals/referrals.controller.ts index 21a1d29..87db5aa 100644 --- a/src/app/http/referrals/referrals.controller.ts +++ b/src/app/http/referrals/referrals.controller.ts @@ -1,24 +1,12 @@ -import { - Body, - Controller, - Get, - Param, - Patch, - Post, - Query, -} from '@nestjs/common'; +import { Body, Controller, Param, Patch, Post } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { CurrentUser } from '@/common/decorators/current-user.decorator'; import { Roles } from '@/common/decorators/roles.decorator'; import { BaseResponseSchema } from '@/domain/schemas/base'; -import { - GetReferralByCategoryResponse, - ReferralByCategoryType, -} from '@/domain/schemas/referral'; import { UserSchema } from '@/domain/schemas/user'; -import { CreateReferralDto, GetReferralByPeriodDto } from './referrals.dtos'; +import { CreateReferralDto } from './referrals.dtos'; import { ReferralsService } from './referrals.service'; @ApiTags('Encaminhamentos') @@ -38,25 +26,6 @@ export class ReferralsController { return { success: true, message: 'Encaminhamento cadastrado com sucesso.' }; } - @Get('referrals-by-category') - @Roles(['manager', 'nurse']) - @ApiOperation({ summary: 'Encaminhamentos por categoria.' }) - async getReferralByCategory( - @Query() query: GetReferralByPeriodDto, - ): Promise { - const { items: categories, total } = - await this.referralsService.getReferralByPeriod( - 'category', - query, - ); - - return { - success: true, - message: 'Encaminhamentos por categorias retornado com sucesso.', - data: { categories, total }, - }; - } - @Patch(':id/cancel') @Roles(['nurse', 'manager', 'specialist']) @ApiOperation({ summary: 'Cancela um encaminhamento.' }) diff --git a/src/app/http/referrals/referrals.dtos.ts b/src/app/http/referrals/referrals.dtos.ts index 429bea8..4440849 100644 --- a/src/app/http/referrals/referrals.dtos.ts +++ b/src/app/http/referrals/referrals.dtos.ts @@ -1,12 +1,5 @@ import { createZodDto } from 'nestjs-zod'; -import { - createReferralSchema, - getReferralByPeriodSchema, -} from '@/domain/schemas/referral'; +import { createReferralSchema } from '@/domain/schemas/referral'; export class CreateReferralDto extends createZodDto(createReferralSchema) {} - -export class GetReferralByPeriodDto extends createZodDto( - getReferralByPeriodSchema, -) {} diff --git a/src/app/http/referrals/referrals.module.ts b/src/app/http/referrals/referrals.module.ts index 77e18f4..77c9b8d 100644 --- a/src/app/http/referrals/referrals.module.ts +++ b/src/app/http/referrals/referrals.module.ts @@ -2,7 +2,6 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Referral } from '@/domain/entities/referral'; -import { UtilsModule } from '@/utils/utils.module'; import { PatientsModule } from '../patients/patients.module'; import { ReferralsController } from './referrals.controller'; @@ -10,7 +9,7 @@ import { ReferralsRepository } from './referrals.repository'; import { ReferralsService } from './referrals.service'; @Module({ - imports: [PatientsModule, TypeOrmModule.forFeature([Referral]), UtilsModule], + imports: [PatientsModule, TypeOrmModule.forFeature([Referral])], controllers: [ReferralsController], providers: [ReferralsService, ReferralsRepository], exports: [ReferralsService, ReferralsRepository], diff --git a/src/app/http/referrals/referrals.repository.ts b/src/app/http/referrals/referrals.repository.ts index 2adb972..61ee69d 100644 --- a/src/app/http/referrals/referrals.repository.ts +++ b/src/app/http/referrals/referrals.repository.ts @@ -6,12 +6,14 @@ import { LessThanOrEqual, MoreThanOrEqual, Repository, + type SelectQueryBuilder, } from 'typeorm'; import { Referral } from '@/domain/entities/referral'; -import { ReferralFieldType, ReferralStatus } from '@/domain/schemas/referral'; +import { ReferralStatus } from '@/domain/schemas/referral'; +import type { CategoryTotalReferrals } from '@/domain/schemas/statistics'; -import { CreateReferralDto, GetReferralByPeriodDto } from './referrals.dtos'; +import { CreateReferralDto } from './referrals.dtos'; @Injectable() export class ReferralsRepository { @@ -34,47 +36,6 @@ export class ReferralsRepository { return await this.referralsRepository.save(referrals); } - async getReferralsByPeriod( - field: ReferralFieldType, - startDate: Date, - endDate: Date, - query: GetReferralByPeriodDto, - ): Promise<{ items: T[]; total: number }> { - const totalQuery = this.referralsRepository - .createQueryBuilder('referral') - .select(`COUNT(DISTINCT referral.${field})`, 'total') - .where('referral.date BETWEEN :start AND :end', { - start: startDate, - end: endDate, - }); - - const totalResult = await totalQuery.getRawOne<{ total: string }>(); - const total = Number(totalResult?.total ?? 0); - - const queryBuilder = this.referralsRepository - .createQueryBuilder('referral') - .select(`referral.${field}`, field) - .addSelect('COUNT(*)', 'total') - .where('referral.date BETWEEN :start AND :end', { - start: startDate, - end: endDate, - }) - .groupBy(`referral.${field}`) - .orderBy('total', query.order) - .limit(query.limit); - - if (query.withPercentage) { - queryBuilder.addSelect( - 'ROUND((COUNT(*) * 100.0 / SUM(COUNT(*)) OVER()), 1)', - 'percentage', - ); - } - - const items = await queryBuilder.getRawMany(); - - return { items, total }; - } - public async cancel(id: string): Promise { return await this.referralsRepository.save({ id, status: 'canceled' }); } @@ -108,4 +69,50 @@ export class ReferralsRepository { return await this.referralsRepository.count({ where }); } + + public async getTotalReferralsByCategory( + input: { startDate?: Date; endDate?: Date; limit?: number } = {}, + ): Promise<{ categories: CategoryTotalReferrals[]; total: number }> { + const { startDate, endDate, limit = 10 } = input; + + function getQueryBuilderWithFilters( + queryBuilder: SelectQueryBuilder, + ) { + if (startDate && endDate) { + queryBuilder.andWhere('referral.date BETWEEN :start AND :end', { + start: startDate, + end: endDate, + }); + } + + return queryBuilder; + } + + const createQueryBuilder = (): SelectQueryBuilder => { + return this.referralsRepository.createQueryBuilder('referral'); + }; + + const categoryListQuery = getQueryBuilderWithFilters( + createQueryBuilder() + .select('referral.category', 'category') + .addSelect('COUNT(referral.id)', 'total') + .groupBy('referral.category') + .orderBy('COUNT(referral.id)', 'DESC') + .limit(limit), + ); + + const totalCategoriesQuery = getQueryBuilderWithFilters( + createQueryBuilder().select('COUNT(DISTINCT referral.category)', 'total'), + ); + + const [categories, totalResult] = await Promise.all([ + categoryListQuery.getRawMany(), + totalCategoriesQuery.getRawOne<{ total: string }>(), + ]); + + return { + categories, + total: Number(totalResult?.total || 0), + }; + } } diff --git a/src/app/http/referrals/referrals.service.ts b/src/app/http/referrals/referrals.service.ts index 0eb40cf..65f97d7 100644 --- a/src/app/http/referrals/referrals.service.ts +++ b/src/app/http/referrals/referrals.service.ts @@ -5,12 +5,10 @@ import { NotFoundException, } from '@nestjs/common'; -import { ReferralFieldType } from '@/domain/schemas/referral'; import { UserSchema } from '@/domain/schemas/user'; -import { UtilsService } from '@/utils/utils.service'; import { PatientsRepository } from '../patients/patients.repository'; -import { CreateReferralDto, GetReferralByPeriodDto } from './referrals.dtos'; +import { CreateReferralDto } from './referrals.dtos'; import { ReferralsRepository } from './referrals.repository'; @Injectable() @@ -20,7 +18,6 @@ export class ReferralsService { constructor( private readonly referralsRepository: ReferralsRepository, private readonly patientsRepository: PatientsRepository, - private readonly utilsService: UtilsService, ) {} public async create( @@ -47,22 +44,6 @@ export class ReferralsService { ); } - async getReferralByPeriod( - filter: ReferralFieldType, - query: GetReferralByPeriodDto, - ): Promise<{ items: T[]; total: number }> { - const { startDate, endDate } = this.utilsService.getDateRangeForPeriod( - query.period, - ); - - return await this.referralsRepository.getReferralsByPeriod( - filter, - startDate, - endDate, - query, - ); - } - async cancel(id: string, user: UserSchema): Promise { const referral = await this.referralsRepository.findById(id); diff --git a/src/app/http/statistics/statistics.controller.ts b/src/app/http/statistics/statistics.controller.ts index b0aae11..271f910 100644 --- a/src/app/http/statistics/statistics.controller.ts +++ b/src/app/http/statistics/statistics.controller.ts @@ -7,6 +7,7 @@ import type { GetPatientsByGenderResponse, GetReferredPatientsByStateResponse, GetTotalReferralsAndReferredPatientsPercentageResponse, + GetTotalReferralsByCategoryResponse, PatientsByCity, PatientsByGender, } from '@/domain/schemas/statistics'; @@ -15,6 +16,7 @@ import { GetPatientsByPeriodQuery, GetReferredPatientsByStateQuery, GetTotalReferralsAndReferredPatientsPercentageQuery, + GetTotalReferralsByCategoryQuery, } from './statistics.dtos'; import { StatisticsService } from './statistics.service'; @@ -93,6 +95,25 @@ export class StatisticsController { }; } + @Get('referrals-by-category') + @Roles(['manager', 'nurse']) + @ApiOperation({ + summary: 'Lista com o total de encaminhamentos por categoria', + }) + async getTotalReferralsByCategory( + @Query() query: GetTotalReferralsByCategoryQuery, + ): Promise { + const { categories, total } = + await this.statisticsService.getTotalReferralsByCategory(query); + + return { + success: true, + message: + 'Lista com o total de encaminhamentos por categoria retornada com sucesso.', + data: { categories, total }, + }; + } + @Get('referrals-by-state') @Roles(['manager', 'nurse']) @ApiOperation({ diff --git a/src/app/http/statistics/statistics.dtos.ts b/src/app/http/statistics/statistics.dtos.ts index bd9861e..9004d2d 100644 --- a/src/app/http/statistics/statistics.dtos.ts +++ b/src/app/http/statistics/statistics.dtos.ts @@ -4,6 +4,7 @@ import { getPatientsByPeriodQuerySchema, getReferredPatientsByStateQuerySchema, getTotalReferralsAndReferredPatientsPercentageQuerySchema, + getTotalReferralsByCategoryQuerySchema, } from '@/domain/schemas/statistics'; export class GetPatientsByPeriodQuery extends createZodDto( @@ -14,6 +15,10 @@ export class GetTotalReferralsAndReferredPatientsPercentageQuery extends createZ getTotalReferralsAndReferredPatientsPercentageQuerySchema, ) {} +export class GetTotalReferralsByCategoryQuery extends createZodDto( + getTotalReferralsByCategoryQuerySchema, +) {} + export class GetReferredPatientsByStateQuery extends createZodDto( getReferredPatientsByStateQuerySchema, ) {} diff --git a/src/app/http/statistics/statistics.service.ts b/src/app/http/statistics/statistics.service.ts index bf63d73..7fa5ffd 100644 --- a/src/app/http/statistics/statistics.service.ts +++ b/src/app/http/statistics/statistics.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@nestjs/common'; import type { + CategoryTotalReferrals, GetTotalPatientsByStatusResponse, PatientsStatisticField, StateReferredPatients, @@ -13,6 +14,7 @@ import type { GetPatientsByPeriodQuery, GetReferredPatientsByStateQuery, GetTotalReferralsAndReferredPatientsPercentageQuery, + GetTotalReferralsByCategoryQuery, } from './statistics.dtos'; @Injectable() @@ -69,6 +71,19 @@ export class StatisticsService { }; } + async getTotalReferralsByCategory( + query: GetTotalReferralsByCategoryQuery, + ): Promise<{ categories: CategoryTotalReferrals[]; total: number }> { + const { startDate, endDate } = this.utilsService.getDateRangeForPeriod( + query.period, + ); + + return await this.referralsRepository.getTotalReferralsByCategory({ + startDate, + endDate, + }); + } + async getReferredPatientsByState( query: GetReferredPatientsByStateQuery, ): Promise<{ states: StateReferredPatients[]; total: number }> { diff --git a/src/domain/schemas/referral.ts b/src/domain/schemas/referral.ts index 00d1eab..ac9aeca 100644 --- a/src/domain/schemas/referral.ts +++ b/src/domain/schemas/referral.ts @@ -1,8 +1,6 @@ import { z } from 'zod'; -import { baseResponseSchema } from './base'; import { PATIENT_CONDITIONS } from './patient'; -import { baseQuerySchema } from './query'; export const REFERRAL_STATUSES = [ 'scheduled', @@ -52,29 +50,3 @@ export const createReferralSchema = referralSchema.pick({ referred_to: true, }); export type CreateReferralSchema = z.infer; - -export const getReferralByPeriodSchema = baseQuerySchema - .pick({ - period: true, - limit: true, - order: true, - withPercentage: true, - }) - .extend({ order: baseQuerySchema.shape.order.default('DESC') }); - -export const referralByCategorySchema = z.object({ - category: z.enum(REFERRAL_CATEGORIES), - total: z.number(), -}); -export type ReferralByCategoryType = z.infer; - -export const getReferralByCategoryResponseSchema = baseResponseSchema.extend({ - data: z.object({ - categories: z.array(referralByCategorySchema), - total: z.number(), - }), -}); -export type GetReferralByCategoryResponse = z.infer< - typeof getReferralByCategoryResponseSchema ->; -export type ReferralFieldType = 'category'; diff --git a/src/domain/schemas/statistics.ts b/src/domain/schemas/statistics.ts index f8e4348..1a35938 100644 --- a/src/domain/schemas/statistics.ts +++ b/src/domain/schemas/statistics.ts @@ -5,6 +5,7 @@ import { BRAZILIAN_STATES } from '@/constants/brazilian-states'; import { baseResponseSchema } from './base'; import { GENDERS } from './patient'; import { baseQuerySchema } from './query'; +import { REFERRAL_CATEGORIES } from './referral'; // Patients @@ -104,3 +105,26 @@ export const getReferredPatientsByStateResponseSchema = export type GetReferredPatientsByStateResponse = z.infer< typeof getReferredPatientsByStateResponseSchema >; + +export const getTotalReferralsByCategoryQuerySchema = baseQuerySchema.pick({ + period: true, +}); + +export const categoryTotalReferralsSchema = z.object({ + category: z.enum(REFERRAL_CATEGORIES), + total: z.number(), +}); +export type CategoryTotalReferrals = z.infer< + typeof categoryTotalReferralsSchema +>; + +export const getTotalReferralsByCategoryResponseSchema = + baseResponseSchema.extend({ + data: z.object({ + categories: z.array(categoryTotalReferralsSchema), + total: z.number(), + }), + }); +export type GetTotalReferralsByCategoryResponse = z.infer< + typeof getTotalReferralsByCategoryResponseSchema +>;