import { Injectable } from '@nestjs/common'; import { Cron, CronExpression } from '@nestjs/schedule'; import { InjectRepository } from '@nestjs/typeorm'; import { LoggingService } from 'src/database/logging/logging.service'; import { Player } from 'src/players/entities/player.entity'; import { Transaction } from 'src/transactions/entitites/transaction.entity'; import { DataSource, LessThanOrEqual, Repository } from 'typeorm'; import { RecurringTransactionIntervalEnum } from './recurring-transaction-interval.enum'; import { RecurringTransaction } from './entities/recurring-transaction.entity'; const INTERVAL_MONTHS: Record = { [RecurringTransactionIntervalEnum.monthly]: 1, [RecurringTransactionIntervalEnum.quarterly]: 3, [RecurringTransactionIntervalEnum.yearly]: 12, }; @Injectable() export class RecurringTransactionsScheduler { constructor( @InjectRepository(RecurringTransaction) private readonly repository: Repository, private readonly dataSource: DataSource, private readonly logger: LoggingService, ) {} @Cron(CronExpression.EVERY_DAY_AT_8AM) async runDueRecurringTransactions(): Promise { const start = Date.now(); await this.logger.info( { event: 'scheduled_recurring_transaction_check_start', details: `Starting sheduled recurring Transaction check`, userId: -1, }, ); const today = new Date().toISOString(); const due = await this.repository.find({ where: { active: true, nextRunDate: LessThanOrEqual(today) }, relations: ['team', 'type'], }); for (const definition of due) { await this.runOne(definition); } await this.logger.info( { event: 'scheduled_recurring_transaction_check_finished', details: `Finished sheduled recurring Transaction check, durationMS=${Date.now() - start}`, userId: -1, }, ); } private async runOne(definition: RecurringTransaction): Promise { await this.dataSource.transaction(async (manager) => { const players = await manager .getRepository(Player) .find({ where: { team: { id: definition.team.id }, active: true } }); const transactionRepository = manager.getRepository(Transaction); for (const player of players) { const transaction = transactionRepository.create({ amount: definition.amount, date: new Date().toISOString(), note: definition.description, player, type: definition.type, }); await transactionRepository.save(transaction); } const definitionRepository = manager.getRepository(RecurringTransaction); definition.nextRunDate = this.advance( definition.nextRunDate, definition.interval, ); await definitionRepository.save(definition); await this.logger.info( { event: 'recurring_transaction_run', details: `recurringTransactionId=${definition.id} teamId=${definition.team.id} gebuchteSpieler=${players.length}`, userId: -1, }, manager, ); }); } private advance( nextRunDate: string, interval: RecurringTransactionIntervalEnum, ): string { const date = new Date(nextRunDate); date.setUTCMonth(date.getUTCMonth() + INTERVAL_MONTHS[interval]); return date.toISOString(); } }