Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ export class Configuration {
{
id: 12,
name: 'Vollmacht',
ignore: (userData: UserData) => userData.accountOpenerAuthorization !== 'Vollmacht',
ignore: (userData: UserData) => userData.organization?.accountOpenerAuthorization !== 'Vollmacht',
files: [
{
prefixes: (userData: UserData) => [`user/${userData.id}/Authority`],
Expand Down
1 change: 0 additions & 1 deletion src/shared/services/process.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ export enum Process {
BLOCKCHAIN_FEE_UPDATE = 'BlockchainFeeUpdate',
TX_REQUEST_STATUS_SYNC = 'TxRequestStatusSync',
TX_REQUEST_WAITING_EXPIRY = 'TxRequestWaitingExpiry',
ORGANIZATION_SYNC = 'OrganizationSync',
BANK_TX_RETURN = 'BankTxReturn',
CUSTODY = 'Custody',
EXCHANGE_WITHDRAWAL = 'ExchangeWithdrawal',
Expand Down
2 changes: 1 addition & 1 deletion src/subdomains/generic/kyc/entities/kyc-step.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ export class KycStep extends IEntity {

get identDocumentId(): string {
const data = this.resultData;
return data ? `${this.userData.organizationName?.split(' ')?.join('') ?? ''}${data.documentNumber}` : undefined;
return data ? `${this.userData.organization.name?.split(' ')?.join('') ?? ''}${data.documentNumber}` : undefined;
}

get isValidCreatingBankData(): boolean {
Expand Down
6 changes: 4 additions & 2 deletions src/subdomains/generic/kyc/enums/kyc.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ export function requiredKycSteps(userData: UserData): KycStepName[] {
KycStepName.NATIONALITY_DATA,
userData.accountType === AccountType.ORGANIZATION ? KycStepName.LEGAL_ENTITY : null,
userData.accountType === AccountType.ORGANIZATION &&
!(userData.legalEntity === LegalEntity.GMBH && userData.organizationCountry?.symbol === 'CH')
!(userData.organization?.legalEntity === LegalEntity.GMBH && userData.organization?.country?.symbol === 'CH')
? KycStepName.OWNER_DIRECTORY
: null,
[AccountType.ORGANIZATION, AccountType.SOLE_PROPRIETORSHIP].includes(userData.accountType)
? KycStepName.COMMERCIAL_REGISTER
: null,
userData.accountType === AccountType.ORGANIZATION ? KycStepName.SIGNATORY_POWER : null,
[SignatoryPower.DOUBLE, SignatoryPower.NONE].includes(userData.signatoryPower) ? KycStepName.AUTHORITY : null,
[SignatoryPower.DOUBLE, SignatoryPower.NONE].includes(userData.organization?.signatoryPower)
? KycStepName.AUTHORITY
: null,
userData.accountType === AccountType.ORGANIZATION
? [KycStepName.BENEFICIAL_OWNER, KycStepName.OPERATIONAL_ACTIVITY]
: null,
Expand Down
20 changes: 12 additions & 8 deletions src/subdomains/generic/kyc/services/kyc.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { MergeReason } from '../../user/models/account-merge/account-merge.entit
import { AccountMergeService } from '../../user/models/account-merge/account-merge.service';
import { BankDataType } from '../../user/models/bank-data/bank-data.entity';
import { BankDataService } from '../../user/models/bank-data/bank-data.service';
import { OrganizationService } from '../../user/models/organization/organization.service';
import { UserDataRelationState } from '../../user/models/user-data-relation/dto/user-data-relation.enum';
import { UserDataRelationService } from '../../user/models/user-data-relation/user-data-relation.service';
import { AccountType } from '../../user/models/user-data/account-type.enum';
Expand All @@ -48,10 +49,12 @@ import {
KycBeneficialData,
KycContactData,
KycFileData,
KycLegalEntityData,
KycManualIdentData,
KycNationalityData,
KycOperationalData,
KycPersonalData,
KycSignatoryPowerData,
PaymentDataDto,
} from '../dto/input/kyc-data.dto';
import { KycFinancialInData, KycFinancialResponse } from '../dto/input/kyc-financial-in.dto';
Expand Down Expand Up @@ -115,6 +118,7 @@ export class KycService {
private readonly mailFactory: MailFactory,
@Inject(forwardRef(() => UserDataRelationService))
private readonly userDataRelationService: UserDataRelationService,
private readonly organizationService: OrganizationService,
) {
this.webhookQueue = new QueueHandler();
}
Expand Down Expand Up @@ -422,17 +426,17 @@ export class KycService {
async updateKycStep(
kycHash: string,
stepId: number,
data: Partial<UserData>,
data: KycLegalEntityData | KycNationalityData | KycSignatoryPowerData,
reviewStatus: KycStepStatus,
): Promise<KycStepBase> {
let user = await this.getUser(kycHash);
const user = await this.getUser(kycHash);
const kycStep = user.getPendingStepOrThrow(stepId);

if (data.nationality) {
if (data instanceof KycNationalityData) {
const nationality = await this.countryService.getCountry(data.nationality.id);
if (!nationality) throw new BadRequestException('Nationality not found');
} else {
user = await this.userDataService.updateUserDataInternal(user, data);
user.organization = await this.organizationService.updateOrganizationInternal(user.organization, data);
}

return this.updateKycStepAndLog(kycStep, user, data, reviewStatus);
Expand Down Expand Up @@ -463,7 +467,7 @@ export class KycService {
allBeneficialOwnersDomicile.push(beneficialOwner.country.name);
}

await this.userDataService.updateUserDataInternal(user, {
await this.organizationService.updateOrganizationInternal(user.organization, {
allBeneficialOwnersName: allBeneficialOwnersName.join('\n'),
allBeneficialOwnersDomicile: allBeneficialOwnersDomicile.join('\n'),
});
Expand Down Expand Up @@ -1005,11 +1009,11 @@ export class KycService {

async completeCommercialRegister(userData: UserData): Promise<UserData> {
if (
(!userData.verifiedName && userData.organizationName) ||
(!userData.verifiedName && userData.organization.name) ||
(userData.kycLevel > KycLevel.LEVEL_30 && userData.highRisk == null && userData.hasValidNameCheckDate)
)
return this.userDataService.updateUserDataInternal(userData, {
verifiedName: !userData.verifiedName && userData.organizationName ? userData.organizationName : undefined,
verifiedName: !userData.verifiedName && userData.organization.name ? userData.organization.name : undefined,
pep:
userData.kycLevel > KycLevel.LEVEL_30 && userData.highRisk == null && userData.hasValidNameCheckDate
? false
Expand Down Expand Up @@ -1135,7 +1139,7 @@ export class KycService {
if (!data.success) errors.push(KycError.INVALID_RESULT);

const userCountry =
identStep.userData.organizationCountry ?? identStep.userData.verifiedCountry ?? identStep.userData.country;
identStep.userData.organization.country ?? identStep.userData.verifiedCountry ?? identStep.userData.country;
if (identStep.userData.accountType === AccountType.PERSONAL) {
if (userCountry && !userCountry.dfxEnable) errors.push(KycError.COUNTRY_NOT_ALLOWED);

Expand Down
2 changes: 1 addition & 1 deletion src/subdomains/generic/kyc/services/name-check.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export class NameCheckService implements OnModuleInit {
const { data: businessSanctionData, file: businessSanctionFile } = await this.getRiskDataAndUploadPdf(
bankData.userData,
true,
bankData.userData.organizationName,
bankData.userData.organization.name,
);

const riskStatus = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export class AccountMergeService {
const [receiver, mentioned] = sendToSlave ? [request.slave, request.master] : [request.master, request.slave];
if (!receiver.mail) return false;

const name = mentioned.organizationName ?? mentioned.firstname ?? receiver.organizationName ?? receiver.firstname;
const name = mentioned.organization.name ?? mentioned.firstname ?? receiver.organization.name ?? receiver.firstname;
const url = this.buildConfirmationUrl(request.code);

await this.notificationService.sendMail({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,19 @@ export class Organization extends IEntity {

@OneToMany(() => UserData, (userData) => userData.organization)
userDatas: UserData[];

// --- ENTITY METHODS --- //

setAccountOpenerAuthorization(signatoryPower: SignatoryPower): Partial<Organization> {
const update: Partial<Organization> = {
accountOpenerAuthorization:
signatoryPower === SignatoryPower.SINGLE
? AccountOpenerAuthorization.SINGLE_SIGNATURE
: AccountOpenerAuthorization.AUTHORIZATION,
};

Object.assign(this, update);

return update;
}
}
Original file line number Diff line number Diff line change
@@ -1,60 +1,16 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { CronExpression } from '@nestjs/schedule';
import { CountryService } from 'src/shared/models/country/country.service';
import { DfxLogger } from 'src/shared/services/dfx-logger';
import { Process } from 'src/shared/services/process.service';
import { DfxCron } from 'src/shared/utils/cron';
import { In, IsNull } from 'typeorm';
import { AccountType } from '../user-data/account-type.enum';
import { UserDataRepository } from '../user-data/user-data.repository';
import { OrganizationDto } from './dto/organization.dto';
import { Organization } from './organization.entity';
import { OrganizationRepository } from './organization.repository';

@Injectable()
export class OrganizationService {
private readonly logger = new DfxLogger(OrganizationService);

constructor(
private readonly organizationRepo: OrganizationRepository,
private readonly countryService: CountryService,
private readonly userDataRepo: UserDataRepository,
) {}

@DfxCron(CronExpression.EVERY_MINUTE, { process: Process.ORGANIZATION_SYNC, timeout: 1800 })
async syncOrganization() {
const entities = await this.userDataRepo.findBy({
organization: { id: IsNull() },
accountType: In([AccountType.ORGANIZATION, AccountType.SOLE_PROPRIETORSHIP]),
});

for (const entity of entities) {
try {
const organization =
(await this.getOrganizationByName(entity.organizationName, entity.organizationZip)) ??
(await this.createOrganization({
name: entity.organizationName,
street: entity.organizationStreet,
houseNumber: entity.organizationHouseNumber,
location: entity.organizationLocation,
zip: entity.organizationZip,
country: entity.organizationCountry,
allBeneficialOwnersName: entity.allBeneficialOwnersName,
allBeneficialOwnersDomicile: entity.allBeneficialOwnersDomicile,
accountOpenerAuthorization: entity.accountOpenerAuthorization,
complexOrgStructure: entity.complexOrgStructure,
accountOpener: entity.accountOpener,
legalEntity: entity.legalEntity,
signatoryPower: entity.signatoryPower,
}));

await this.userDataRepo.update(entity.id, { organization });
} catch (e) {
this.logger.error(`Error in organization sync ${entity.id}: `, e);
}
}
}

async createOrganization(dto: OrganizationDto): Promise<Organization> {
const organization = this.organizationRepo.create(dto);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ import { FileType } from 'src/subdomains/generic/kyc/dto/kyc-file.dto';
import { KycStepName } from 'src/subdomains/generic/kyc/enums/kyc-step-name.enum';
import { KycStepStatus } from 'src/subdomains/generic/kyc/enums/kyc.enum';
import { IsNull, Like, MoreThan, Not } from 'typeorm';
import { OrganizationService } from '../organization/organization.service';
import { AccountType } from './account-type.enum';
import { KycLevel, KycType, SignatoryPower, UserDataStatus } from './user-data.entity';
import { UserDataRepository } from './user-data.repository';

@Injectable()
export class UserDataJobService {
constructor(private readonly userDataRepo: UserDataRepository) {}
constructor(
private readonly userDataRepo: UserDataRepository,
private readonly organizationService: OrganizationService,
) {}

@DfxCron(CronExpression.EVERY_MINUTE, { process: Process.USER_DATA, timeout: 1800 })
async fillUserData() {
Expand Down Expand Up @@ -41,7 +45,7 @@ export class UserDataJobService {
where: {
kycLevel: MoreThan(KycLevel.LEVEL_30),
accountType: AccountType.ORGANIZATION,
accountOpenerAuthorization: IsNull(),
organization: { accountOpenerAuthorization: IsNull() },
kycSteps: { name: KycStepName.SIGNATORY_POWER, status: KycStepStatus.COMPLETED },
},
relations: { kycSteps: true },
Expand All @@ -50,9 +54,12 @@ export class UserDataJobService {
for (const entity of entities) {
const signatoryResult = entity.kycSteps
.find((k) => k.name === KycStepName.SIGNATORY_POWER && k.status === KycStepStatus.COMPLETED)
.getResult<{signatoryPower: SignatoryPower}>();
.getResult<{ signatoryPower: SignatoryPower }>();

await this.userDataRepo.update(...entity.setAccountOpenerAuthorization(signatoryResult.signatoryPower));
await this.organizationService.updateOrganizationInternal(
entity.organization,
entity.organization.setAccountOpenerAuthorization(signatoryResult.signatoryPower),
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class UserDataNotificationService {
{ key: MailKey.SPACE, params: { value: '3' } },
{
key: `${MailTranslationKey.GENERAL}.welcome`,
params: { name: master.organizationName ?? master.firstname },
params: { name: master.organization.name ?? master.firstname },
},
{ key: MailKey.SPACE, params: { value: '2' } },
{
Expand Down Expand Up @@ -80,7 +80,7 @@ export class UserDataNotificationService {
{ key: MailKey.SPACE, params: { value: '3' } },
{
key: `${MailTranslationKey.GENERAL}.welcome`,
params: { name: master.organizationName ?? master.firstname },
params: { name: master.organization.name ?? master.firstname },
},
{ key: MailKey.SPACE, params: { value: '2' } },
{
Expand All @@ -105,7 +105,7 @@ export class UserDataNotificationService {
{ key: MailKey.SPACE, params: { value: '3' } },
{
key: `${MailTranslationKey.GENERAL}.welcome`,
params: { name: slave.organizationName ?? slave.firstname },
params: { name: slave.organization.name ?? slave.firstname },
},
{ key: MailKey.SPACE, params: { value: '2' } },
{
Expand Down
Loading