gradido/backend/src/interactions/updateUnconfirmedContribution/AbstractUnconfirmedContribution.role.ts
2023-11-28 21:08:27 +01:00

98 lines
3.3 KiB
TypeScript

import { Contribution } from '@entity/Contribution'
import { User } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Role } from '@/auth/Role'
import { ContributionLogic } from '@/data/Contribution.logic'
import { ContributionMessageBuilder } from '@/data/ContributionMessage.builder'
import { ContributionStatus } from '@/graphql/enum/ContributionStatus'
import { Context, getClientTimezoneOffset } from '@/server/context'
import { LogError } from '@/server/LogError'
export abstract class AbstractUnconfirmedContributionRole {
private availableCreationSums?: Decimal[]
protected changed = true
private currentStep = 0
public constructor(
protected self: Contribution,
protected updatedAmount: Decimal,
protected updatedCreationDate: Date,
) {
if (self.confirmedAt || self.deniedAt) {
throw new LogError("this contribution isn't unconfirmed!")
}
}
public isChanged(): boolean {
return this.changed
}
// steps which return void throw on each error
// first, check if it can be updated
protected abstract checkAuthorization(user: User, role: Role): void
// second, check if contribution is still valid after update
protected async validate(clientTimezoneOffset: number): Promise<void> {
// TODO: refactor frontend and remove this restriction
if (this.self.contributionDate.getMonth() !== this.updatedCreationDate.getMonth()) {
throw new LogError('Month of contribution can not be changed')
}
if (this.self.contributionStatus === ContributionStatus.CONFIRMED) {
throw new LogError('the contribution is already confirmed, cannot be changed anymore')
}
const contributionLogic = new ContributionLogic(this.self)
this.availableCreationSums = await contributionLogic.getAvailableCreationSums(
clientTimezoneOffset,
true,
)
contributionLogic.checkAvailableCreationSumsNotExceeded(
this.updatedAmount,
this.updatedCreationDate,
clientTimezoneOffset,
)
}
// third, actually update entity
protected abstract update(): void
protected wasUpdateAlreadyCalled(): boolean {
return this.currentStep > 3
}
// call all steps in order
public async checkAndUpdate(context: Context): Promise<void> {
if (!context.user || !context.role) {
throw new LogError('missing user or role on context')
}
this.currentStep = 1
this.checkAuthorization(context.user, context.role)
this.currentStep = 2
await this.validate(getClientTimezoneOffset(context))
this.currentStep = 3
this.update()
this.currentStep = 4
}
public createContributionMessage(): ContributionMessageBuilder | undefined {
// must be called before call at update
if (this.wasUpdateAlreadyCalled()) {
throw new LogError('please call before call of checkAndUpdate')
}
const contributionMessageBuilder = new ContributionMessageBuilder()
return contributionMessageBuilder.setParentContribution(this.self).setHistoryType(this.self)
}
public getAvailableCreationSums(): Decimal[] {
if (!this.availableCreationSums) {
throw new LogError('availableCreationSums is empty, please call validate before!')
}
return this.availableCreationSums
}
public isCreatedFromUser(): boolean {
return !this.self.moderatorId
}
}