Files
sistema_base/apps/api/src/features/training/training.service.ts
2025-12-09 17:56:48 -04:00

224 lines
7.7 KiB
TypeScript

import { DRIZZLE_PROVIDER } from 'src/database/drizzle-provider';
import { Inject, Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
import * as schema from 'src/database/index';
import { trainingSurveys } from 'src/database/index';
import { eq, like, or, and, gte, lte, SQL, sql } from 'drizzle-orm';
import { CreateTrainingDto } from './dto/create-training.dto';
import { UpdateTrainingDto } from './dto/update-training.dto';
import { TrainingStatisticsFilterDto } from './dto/training-statistics-filter.dto';
import { states } from 'src/database/index';
import { PaginationDto } from '../../common/dto/pagination.dto';
@Injectable()
export class TrainingService {
constructor(
@Inject(DRIZZLE_PROVIDER) private drizzle: NodePgDatabase<typeof schema>,
) { }
async findAll(paginationDto?: PaginationDto) {
const { page = 1, limit = 10, search = '', sortBy = 'id', sortOrder = 'asc' } = paginationDto || {};
const offset = (page - 1) * limit;
let searchCondition: SQL<unknown> | undefined;
if (search) {
searchCondition = or(
like(trainingSurveys.firstname, `%${search}%`),
like(trainingSurveys.lastname, `%${search}%`),
like(trainingSurveys.ospName, `%${search}%`),
like(trainingSurveys.ospRif, `%${search}%`)
);
}
const orderBy = sortOrder === 'asc'
? sql`${trainingSurveys[sortBy as keyof typeof trainingSurveys]} asc`
: sql`${trainingSurveys[sortBy as keyof typeof trainingSurveys]} desc`;
const totalCountResult = await this.drizzle
.select({ count: sql<number>`count(*)` })
.from(trainingSurveys)
.where(searchCondition);
const totalCount = Number(totalCountResult[0].count);
const totalPages = Math.ceil(totalCount / limit);
const data = await this.drizzle
.select()
.from(trainingSurveys)
.where(searchCondition)
.orderBy(orderBy)
.limit(limit)
.offset(offset);
const meta = {
page,
limit,
totalCount,
totalPages,
hasNextPage: page < totalPages,
hasPreviousPage: page > 1,
nextPage: page < totalPages ? page + 1 : null,
previousPage: page > 1 ? page - 1 : null,
};
return { data, meta };
}
async getStatistics(filterDto: TrainingStatisticsFilterDto) {
const { startDate, endDate, stateId, municipalityId, parishId, ospType } = filterDto;
const filters: SQL[] = [];
if (startDate) {
filters.push(gte(trainingSurveys.visitDate, new Date(startDate)));
}
if (endDate) {
filters.push(lte(trainingSurveys.visitDate, new Date(endDate)));
}
if (stateId) {
filters.push(eq(trainingSurveys.state, stateId));
}
if (municipalityId) {
filters.push(eq(trainingSurveys.municipality, municipalityId));
}
if (parishId) {
filters.push(eq(trainingSurveys.parish, parishId));
}
if (ospType) {
filters.push(eq(trainingSurveys.ospType, ospType));
}
const whereCondition = filters.length > 0 ? and(...filters) : undefined;
const totalOspsResult = await this.drizzle
.select({ count: sql<number>`count(*)` })
.from(trainingSurveys)
.where(whereCondition);
const totalOsps = Number(totalOspsResult[0].count);
const totalProducersResult = await this.drizzle
.select({ sum: sql<number>`sum(${trainingSurveys.producerCount})` })
.from(trainingSurveys)
.where(whereCondition);
const totalProducers = Number(totalProducersResult[0].sum || 0);
const statusDistribution = await this.drizzle
.select({
name: trainingSurveys.currentStatus,
value: sql<number>`count(*)`
})
.from(trainingSurveys)
.where(whereCondition)
.groupBy(trainingSurveys.currentStatus);
const activityDistribution = await this.drizzle
.select({
name: trainingSurveys.productiveActivity,
value: sql<number>`count(*)`
})
.from(trainingSurveys)
.where(whereCondition)
.groupBy(trainingSurveys.productiveActivity);
const typeDistribution = await this.drizzle
.select({
name: trainingSurveys.ospType,
value: sql<number>`count(*)`
})
.from(trainingSurveys)
.where(whereCondition)
.groupBy(trainingSurveys.ospType);
// New Aggregations
const stateDistribution = await this.drizzle
.select({
name: states.name,
value: sql<number>`count(${trainingSurveys.id})`
})
.from(trainingSurveys)
.leftJoin(states, eq(trainingSurveys.state, states.id))
.where(whereCondition)
.groupBy(states.name);
const yearDistribution = await this.drizzle
.select({
name: sql<string>`cast(${trainingSurveys.companyConstitutionYear} as text)`,
value: sql<number>`count(*)`
})
.from(trainingSurveys)
.where(whereCondition)
.groupBy(trainingSurveys.companyConstitutionYear)
.orderBy(trainingSurveys.companyConstitutionYear);
return {
totalOsps,
totalProducers,
statusDistribution: statusDistribution.map(item => ({ ...item, value: Number(item.value) })),
activityDistribution: activityDistribution.map(item => ({ ...item, value: Number(item.value) })),
typeDistribution: typeDistribution.map(item => ({ ...item, value: Number(item.value) })),
stateDistribution: stateDistribution.map(item => ({ ...item, value: Number(item.value) })),
yearDistribution: yearDistribution.map(item => ({ ...item, value: Number(item.value) })),
};
}
async findOne(id: number) {
const find = await this.drizzle
.select()
.from(trainingSurveys)
.where(eq(trainingSurveys.id, id));
if (find.length === 0) {
throw new HttpException('Training record not found', HttpStatus.NOT_FOUND);
}
return find[0];
}
async create(createTrainingDto: CreateTrainingDto) {
const [newRecord] = await this.drizzle
.insert(trainingSurveys)
.values({
...createTrainingDto,
visitDate: new Date(createTrainingDto.visitDate),
})
.returning();
return newRecord;
}
async update(id: number, updateTrainingDto: UpdateTrainingDto) {
await this.findOne(id);
const updateData: any = { ...updateTrainingDto };
if (updateTrainingDto.visitDate) {
updateData.visitDate = new Date(updateTrainingDto.visitDate);
}
const [updatedRecord] = await this.drizzle
.update(trainingSurveys)
.set(updateData)
.where(eq(trainingSurveys.id, id))
.returning();
return updatedRecord;
}
async remove(id: number) {
await this.findOne(id);
const [deletedRecord] = await this.drizzle
.delete(trainingSurveys)
.where(eq(trainingSurveys.id, id))
.returning();
return { message: 'Training record deleted successfully', data: deletedRecord };
}
}