/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import LogError from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' import { getConnection } from '@dbTools/typeorm' import { Contribution } from '@entity/Contribution' import Decimal from 'decimal.js-light' import { FULL_CREATION_AVAILABLE, MAX_CREATION_AMOUNT } from '../const/const' import { OpenCreation } from '@model/OpenCreation' interface CreationMap { id: number creations: Decimal[] } export const validateContribution = ( creations: Decimal[], amount: Decimal, creationDate: Date, timezoneOffset: number, ): void => { logger.trace('isContributionValid: ', creations, amount, creationDate) const index = getCreationIndex(creationDate.getMonth(), timezoneOffset) if (index < 0) { throw new LogError('No information for available creations for the given date', creationDate) } if (amount.greaterThan(creations[index].toString())) { throw new LogError( 'The amount to be created exceeds the amount still available for this month', amount, creations[index], ) } } export const getUserCreations = async ( ids: number[], timezoneOffset: number, includePending = true, ): Promise => { logger.trace('getUserCreations:', ids, includePending) const months = getCreationMonths(timezoneOffset) logger.trace('getUserCreations months', months) const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() const dateFilter = 'last_day(curdate() - interval 3 month) + interval 1 day' logger.trace('getUserCreations dateFilter=', dateFilter) const sumAmountContributionPerUserAndLast3MonthQuery = queryRunner.manager .createQueryBuilder(Contribution, 'c') .select('month(contribution_date)', 'month') .addSelect('user_id', 'userId') .addSelect('sum(amount)', 'sum') .where(`user_id in (${ids.toString()})`) .andWhere(`contribution_date >= ${dateFilter}`) .andWhere('deleted_at IS NULL') .andWhere('denied_at IS NULL') .groupBy('month') .addGroupBy('userId') .orderBy('month', 'DESC') if (!includePending) { sumAmountContributionPerUserAndLast3MonthQuery.andWhere('confirmed_at IS NOT NULL') } const sumAmountContributionPerUserAndLast3Month = await sumAmountContributionPerUserAndLast3MonthQuery.getRawMany() logger.trace(sumAmountContributionPerUserAndLast3Month) await queryRunner.release() return ids.map((id) => { return { id, creations: months.map((month) => { const creation = sumAmountContributionPerUserAndLast3Month.find( (raw: { month: string; userId: string; creation: number[] }) => parseInt(raw.month) === month && parseInt(raw.userId) === id, ) return MAX_CREATION_AMOUNT.minus(creation ? creation.sum : 0) }), } }) } export const getUserCreation = async ( id: number, timezoneOffset: number, includePending = true, ): Promise => { logger.trace('getUserCreation', id, includePending, timezoneOffset) const creations = await getUserCreations([id], timezoneOffset, includePending) logger.trace('getUserCreation creations=', creations) return creations[0] ? creations[0].creations : FULL_CREATION_AVAILABLE } const getCreationMonths = (timezoneOffset: number): number[] => { return getCreationDates(timezoneOffset).map((date) => date.getMonth() + 1) } const getCreationDates = (timezoneOffset: number): Date[] => { const clientNow = new Date() clientNow.setTime(clientNow.getTime() - timezoneOffset * 60 * 1000) logger.info( `getCreationMonths -- offset: ${timezoneOffset} -- clientNow: ${clientNow.toISOString()}`, ) return [ new Date(clientNow.getFullYear(), clientNow.getMonth() - 2, 1), new Date(clientNow.getFullYear(), clientNow.getMonth() - 1, 1), clientNow, ] } const getCreationIndex = (month: number, timezoneOffset: number): number => { return getCreationMonths(timezoneOffset).findIndex((el) => el === month + 1) } export const isStartEndDateValid = ( startDate: string | null | undefined, endDate: string | null | undefined, ): void => { if (!startDate) { throw new LogError('A Start-Date must be set') } if (!endDate) { throw new LogError('An End-Date must be set') } // check if endDate is before startDate if (new Date(endDate).getTime() - new Date(startDate).getTime() < 0) { throw new LogError(`The value of validFrom must before or equals the validTo`) } } export const updateCreations = ( creations: Decimal[], contribution: Contribution, timezoneOffset: number, ): Decimal[] => { const index = getCreationIndex(contribution.contributionDate.getMonth(), timezoneOffset) if (index < 0) { throw new LogError('You cannot create GDD for a month older than the last three months') } creations[index] = creations[index].plus(contribution.amount.toString()) return creations } export const isValidDateString = (dateString: string): boolean => { return new Date(dateString).toString() !== 'Invalid Date' } export const getOpenCreations = async ( userId: number, timezoneOffset: number, ): Promise => { const creations = await getUserCreation(userId, timezoneOffset) const creationDates = getCreationDates(timezoneOffset) return creationDates.map((date, index) => { return { month: date.getMonth(), year: date.getFullYear(), amount: creations[index], } }) }