Files
teamwallet/myteamwallet_backend/src/recurring-transactions/recurring-transactions.scheduler.ts
Bastian Wagner cd9d7b165f pdf export
2026-08-04 13:11:14 +02:00

102 lines
3.4 KiB
TypeScript

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, number> = {
[RecurringTransactionIntervalEnum.monthly]: 1,
[RecurringTransactionIntervalEnum.quarterly]: 3,
[RecurringTransactionIntervalEnum.yearly]: 12,
};
@Injectable()
export class RecurringTransactionsScheduler {
constructor(
@InjectRepository(RecurringTransaction)
private readonly repository: Repository<RecurringTransaction>,
private readonly dataSource: DataSource,
private readonly logger: LoggingService,
) {}
@Cron(CronExpression.EVERY_DAY_AT_8AM)
async runDueRecurringTransactions(): Promise<void> {
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<void> {
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();
}
}