diff --git a/src/subdomains/core/buy-crypto/process/entities/buy-crypto.entity.ts b/src/subdomains/core/buy-crypto/process/entities/buy-crypto.entity.ts index 0990d473ed..e9381ec384 100644 --- a/src/subdomains/core/buy-crypto/process/entities/buy-crypto.entity.ts +++ b/src/subdomains/core/buy-crypto/process/entities/buy-crypto.entity.ts @@ -715,7 +715,7 @@ export class BuyCrypto extends IEntity { refFactor: null, usedFees: null, networkStartFeeAmount: null, - status: null, + status: BuyCryptoStatus.CREATED, }; Object.assign(this, update); diff --git a/src/subdomains/core/referral/referral.module.ts b/src/subdomains/core/referral/referral.module.ts index 7cd4d6e019..265bc34db0 100644 --- a/src/subdomains/core/referral/referral.module.ts +++ b/src/subdomains/core/referral/referral.module.ts @@ -46,6 +46,6 @@ import { RefRewardService } from './reward/services/ref-reward.service'; RefRewardOutService, RefRewardJobService, ], - exports: [RefService, RefRewardService, RefRewardRepository], + exports: [RefService, RefRewardService], }) export class ReferralModule {} diff --git a/src/subdomains/core/referral/reward/services/ref-reward.service.ts b/src/subdomains/core/referral/reward/services/ref-reward.service.ts index af6a35d23d..ab4b88423f 100644 --- a/src/subdomains/core/referral/reward/services/ref-reward.service.ts +++ b/src/subdomains/core/referral/reward/services/ref-reward.service.ts @@ -239,4 +239,22 @@ export class RefRewardService { cryptoCurrency: v.outputAsset.dexName, })); } + + async getRewardRecipients(from?: Date): Promise<{ userDataId: number; count: number; totalChf: number }[]> { + const query = this.rewardRepo + .createQueryBuilder('r') + .innerJoin('r.user', 'u') + .select('u.userDataId', 'userDataId') + .addSelect('COUNT(*)', 'count') + .addSelect('ROUND(SUM(r.amountInChf), 0)', 'totalChf') + .where('r.status != :excluded', { excluded: RewardStatus.USER_SWITCH }) + .groupBy('u.userDataId') + .orderBy('totalChf', 'DESC'); + + if (from) { + query.andWhere('r.created >= :from', { from }); + } + + return query.getRawMany(); + } } diff --git a/src/subdomains/generic/kyc/dto/kyc-error.enum.ts b/src/subdomains/generic/kyc/dto/kyc-error.enum.ts index 161edd6550..3517e36308 100644 --- a/src/subdomains/generic/kyc/dto/kyc-error.enum.ts +++ b/src/subdomains/generic/kyc/dto/kyc-error.enum.ts @@ -36,7 +36,7 @@ export enum KycError { DENIED_RECOMMENDATION = 'DeniedRecommendation', RECOMMENDER_BLOCKED = 'RecommenderBlocked', - // FinancialData errors + // FinancialData errors/reasons MISSING_INFO = 'MissingInfo', RISKY_BUSINESS = 'RiskyBusiness', INCORRECT_INFO = 'IncorrectInfo', @@ -50,6 +50,8 @@ export enum KycError { // DfxApproval errors BANK_RECALL_FEE_NOT_PAID = 'BankRecallFeeNotPaid', OPEN_SANCTIONED_NAME_CHECK = 'OpenSanctionedNameCheck', + RISK_ACCEPTED = 'RiskAccepted', + NO_GWG_RISK = 'NoGwgRisk', // Deactivated userData errors USER_DATA_DEACTIVATED = 'UserDataDeactivated', @@ -95,6 +97,8 @@ export const KycErrorMap: Record = { [KycError.RESIDENCE_PERMIT_CHECK_REQUIRED]: undefined, [KycError.EXPIRED_STEP]: 'Your documents are expired', [KycError.MANUAL_REVIEW_REQUIRED]: undefined, + [KycError.RISK_ACCEPTED]: undefined, + [KycError.NO_GWG_RISK]: undefined, }; export const KycReasonMap: { [e in KycError]?: KycStepReason } = { diff --git a/src/subdomains/generic/kyc/services/kyc-log.service.ts b/src/subdomains/generic/kyc/services/kyc-log.service.ts index 0e5877285c..5471604c1f 100644 --- a/src/subdomains/generic/kyc/services/kyc-log.service.ts +++ b/src/subdomains/generic/kyc/services/kyc-log.service.ts @@ -91,11 +91,10 @@ export class KycLogService { } async getLogsByUserDataId(userDataId: number): Promise { - return this.kycLogRepo - .createQueryBuilder('log') - .where('log.userDataId = :userDataId', { userDataId }) - .orderBy('log.created', 'DESC') - .getMany(); + return this.kycLogRepo.find({ + where: { userData: { id: userDataId } }, + order: { created: 'DESC' }, + }); } async createKycFileLog(log: string, user?: UserData) { diff --git a/src/subdomains/generic/kyc/services/kyc.service.ts b/src/subdomains/generic/kyc/services/kyc.service.ts index b6338915fc..e0f9abbf06 100644 --- a/src/subdomains/generic/kyc/services/kyc.service.ts +++ b/src/subdomains/generic/kyc/services/kyc.service.ts @@ -468,11 +468,39 @@ export class KycService { async initializeProcess(userData: UserData): Promise { const user = await this.getUser(userData.kycHash); - if (user.getStepsWith(KycStepName.CONTACT_DATA).length > 0) return user; + + const contactSteps = user.getStepsWith(KycStepName.CONTACT_DATA); + if (contactSteps.length > 0) { + // Complete pending ContactData step if user now has an email + const pendingStep = contactSteps.find((s) => s.isInProgress); + if (pendingStep && user.mail) { + const result = await this.trySetMail(user, pendingStep, user.mail); + await this.kycStepRepo.update(...result); + await this.createStepLog(user, pendingStep); + await this.updateProgress(user, false); + } + return user; + } return this.updateProgress(user, true, false); } + async failContactStepForMail(userData: UserData, mail: string, error: string): Promise { + try { + const user = await this.getUser(userData.kycHash); + const pendingStep = user.getStepsWith(KycStepName.CONTACT_DATA).find((s) => s.isInProgress); + if (!pendingStep) return; + + const kycError = error.includes('account merge request sent') + ? KycError.USER_DATA_MERGE_REQUESTED + : KycError.USER_DATA_EXISTING; + await this.kycStepRepo.update(...pendingStep.fail({ mail }, kycError)); + await this.createStepLog(user, pendingStep); + } catch (e) { + this.logger.error(`Failed to update ContactData step for account ${userData.id}:`, e); + } + } + public getMailFailedReason(comment: string, language: string): string { return `
    ${comment ?.split(';') diff --git a/src/subdomains/generic/user/models/user-data/user-data.service.ts b/src/subdomains/generic/user/models/user-data/user-data.service.ts index b33b4ae329..c48789205d 100644 --- a/src/subdomains/generic/user/models/user-data/user-data.service.ts +++ b/src/subdomains/generic/user/models/user-data/user-data.service.ts @@ -731,6 +731,8 @@ export class UserDataService { if (mergeRequested) errorMessage += ' - account merge request sent'; } + await this.kycService.failContactStepForMail(userData, mail, errorMessage); + throw new ConflictException(errorMessage); } diff --git a/src/subdomains/supporting/dashboard/dashboard-financial.service.ts b/src/subdomains/supporting/dashboard/dashboard-financial.service.ts index ef6f4f17ba..3b5b6f622a 100644 --- a/src/subdomains/supporting/dashboard/dashboard-financial.service.ts +++ b/src/subdomains/supporting/dashboard/dashboard-financial.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { AssetService } from 'src/shared/models/asset/asset.service'; -import { RefRewardRepository } from '../../core/referral/reward/ref-reward.repository'; +import { RefRewardService } from '../../core/referral/reward/services/ref-reward.service'; import { Log } from '../log/log.entity'; import { LogService } from '../log/log.service'; import { FinanceLog } from '../log/dto/log.dto'; @@ -19,7 +19,7 @@ export class DashboardFinancialService { constructor( private readonly logService: LogService, private readonly assetService: AssetService, - private readonly refRewardRepo: RefRewardRepository, + private readonly refRewardService: RefRewardService, ) {} async getFinancialLog(from?: Date, dailySample?: boolean): Promise { @@ -37,21 +37,7 @@ export class DashboardFinancialService { } async getRefRewardRecipients(from?: Date): Promise { - const query = this.refRewardRepo - .createQueryBuilder('r') - .innerJoin('r.user', 'u') - .select('u.userDataId', 'userDataId') - .addSelect('COUNT(*)', 'count') - .addSelect('ROUND(SUM(r.amountInChf), 0)', 'totalChf') - .where('r.status != :excluded', { excluded: 'UserSwitch' }) - .groupBy('u.userDataId') - .orderBy('totalChf', 'DESC'); - - if (from) { - query.andWhere('r.created >= :from', { from }); - } - - return query.getRawMany(); + return this.refRewardService.getRewardRecipients(from); } async getLatestFinancialChanges(): Promise {