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
7 changes: 7 additions & 0 deletions src/subdomains/core/aml/enums/aml-error.enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ export const ManualPassWhitelistErrors: AmlError[] = [
AmlError.REFERRAL_NO_TRADE_HISTORY,
];

export const ManualPassBlacklistErrors: AmlError[] = [
AmlError.BANK_DATA_NOT_ACTIVE,
AmlError.BANK_DATA_MANUAL_REVIEW,
AmlError.BANK_DATA_MISSING,
AmlError.BANK_DATA_USER_MISMATCH,
];

export function canManualPass(comment: string | null | undefined): boolean {
const errors = (comment ?? '')
.split(';')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import { TransactionService } from 'src/subdomains/supporting/payment/services/t
import { PriceValidity } from 'src/subdomains/supporting/pricing/services/pricing.service';
import { Between, FindOptionsRelations, In, IsNull, MoreThan, Not } from 'typeorm';
import { ManualAmlCheckDto } from '../../../aml/dto/manual-aml-check.dto';
import { canManualPass } from '../../../aml/enums/aml-error.enum';
import { canManualPass, ManualPassBlacklistErrors } from '../../../aml/enums/aml-error.enum';
import { AmlReason, PhoneAmlReasons } from '../../../aml/enums/aml-reason.enum';
import { CheckStatus } from '../../../aml/enums/check-status.enum';
import { Buy } from '../../routes/buy/buy.entity';
Expand Down Expand Up @@ -299,6 +299,9 @@ export class BuyCryptoService implements OnModuleInit {
manualApproved: dto.bankDataManualApproved,
});

if (dto.amlCheck === CheckStatus.PASS && ManualPassBlacklistErrors.some((b) => entity.comment?.includes(b)))
throw new BadRequestException('Blacklisted aml error cannot set Pass');

if (dto.chargebackAllowedDate) {
if (entity.bankTx && !entity.chargebackOutput) {
if (!dto.chargebackCreditorName && !entity.creditorData)
Expand Down Expand Up @@ -729,8 +732,12 @@ export class BuyCryptoService implements OnModuleInit {
throw new BadRequestException('BuyCrypto is already complete or chargeback initiated');
if ([CheckStatus.PASS, CheckStatus.FAIL].includes(entity.amlCheck))
throw new BadRequestException('BuyCrypto amlCheck is already finalized');
if (dto.amlCheck === CheckStatus.PASS && !canManualPass(entity.comment))
throw new BadRequestException('Manual pass only allowed when all errors are phone-related');
if (dto.amlCheck === CheckStatus.PASS) {
if (ManualPassBlacklistErrors.some((b) => entity.comment?.includes(b)))
throw new BadRequestException('Blacklisted aml error cannot set Pass');
if (!canManualPass(entity.comment))
throw new BadRequestException('Manual pass only allowed when all errors are phone-related');
}

return this.update(id, {
amlCheck: dto.amlCheck,
Expand Down
17 changes: 11 additions & 6 deletions src/subdomains/core/buy-crypto/routes/buy/buy.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Controller,
Get,
Param,
ParseIntPipe,
Post,
Put,
UseGuards,
Expand Down Expand Up @@ -249,24 +250,28 @@ export class BuyController {
@ApiBearerAuth()
@UseGuards(AuthGuard(), RoleGuard(UserRole.USER), BuyActiveGuard())
@ApiExcludeEndpoint()
async updateBuyRoute(@GetJwt() jwt: JwtPayload, @Param('id') id: string, @Body() dto: UpdateBuyDto): Promise<BuyDto> {
return this.buyService.updateBuy(jwt.user, +id, dto).then((b) => this.toDto(jwt.user, b));
async updateBuyRoute(
@GetJwt() jwt: JwtPayload,
@Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateBuyDto,
): Promise<BuyDto> {
return this.buyService.updateBuy(jwt.user, id, dto).then((b) => this.toDto(jwt.user, b));
}

@Get(':id')
@ApiBearerAuth()
@UseGuards(AuthGuard(), RoleGuard(UserRole.USER), BuyActiveGuard())
@ApiOkResponse({ type: BuyDto })
async getBuy(@GetJwt() jwt: JwtPayload, @Param('id') id: string): Promise<BuyDto> {
return this.buyService.get(jwt.account, +id).then((l) => this.toDto(jwt.user, l));
async getBuy(@GetJwt() jwt: JwtPayload, @Param('id', ParseIntPipe) id: number): Promise<BuyDto> {
return this.buyService.get(jwt.account, id).then((l) => this.toDto(jwt.user, l));
}

@Get(':id/history')
@ApiBearerAuth()
@UseGuards(AuthGuard(), RoleGuard(UserRole.USER))
@ApiExcludeEndpoint()
async getBuyRouteHistory(@GetJwt() jwt: JwtPayload, @Param('id') id: string): Promise<BuyHistoryDto[]> {
return this.buyCryptoService.getBuyHistory(jwt.user, +id);
async getBuyRouteHistory(@GetJwt() jwt: JwtPayload, @Param('id', ParseIntPipe) id: number): Promise<BuyHistoryDto[]> {
return this.buyCryptoService.getBuyHistory(jwt.user, id);
}

// --- DTO --- //
Expand Down
16 changes: 10 additions & 6 deletions src/subdomains/core/buy-crypto/routes/swap/swap.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Get,
NotFoundException,
Param,
ParseIntPipe,
Post,
Put,
Query,
Expand Down Expand Up @@ -75,8 +76,8 @@ export class SwapController {
@ApiBearerAuth()
@UseGuards(AuthGuard(), RoleGuard(UserRole.USER), SwapActiveGuard())
@ApiOkResponse({ type: SwapDto })
async getSwap(@GetJwt() jwt: JwtPayload, @Param('id') id: string): Promise<SwapDto> {
return this.swapService.get(jwt.user, +id).then((l) => this.toDto(jwt.user, l));
async getSwap(@GetJwt() jwt: JwtPayload, @Param('id', ParseIntPipe) id: number): Promise<SwapDto> {
return this.swapService.get(jwt.user, id).then((l) => this.toDto(jwt.user, l));
}

@Post()
Expand Down Expand Up @@ -206,18 +207,21 @@ export class SwapController {
@ApiExcludeEndpoint()
async updateSwapRoute(
@GetJwt() jwt: JwtPayload,
@Param('id') id: string,
@Param('id', ParseIntPipe) id: number,
@Body() updateCryptoDto: UpdateSwapDto,
): Promise<SwapDto> {
return this.swapService.updateSwap(jwt.user, +id, updateCryptoDto).then((b) => this.toDto(jwt.user, b));
return this.swapService.updateSwap(jwt.user, id, updateCryptoDto).then((b) => this.toDto(jwt.user, b));
}

@Get(':id/history')
@ApiBearerAuth()
@UseGuards(AuthGuard(), RoleGuard(UserRole.USER))
@ApiExcludeEndpoint()
async getSwapRouteHistory(@GetJwt() jwt: JwtPayload, @Param('id') id: string): Promise<HistoryDtoDeprecated[]> {
return this.buyCryptoService.getCryptoHistory(jwt.user, +id);
async getSwapRouteHistory(
@GetJwt() jwt: JwtPayload,
@Param('id', ParseIntPipe) id: number,
): Promise<HistoryDtoDeprecated[]> {
return this.buyCryptoService.getCryptoHistory(jwt.user, id);
}

// --- DTO --- //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,12 @@ export class PaymentLinkPaymentService {
.innerJoin(
(qb) =>
qb
.select('plp2.linkId', 'linkId')
.select('plp2."linkId"', 'linkId')
.addSelect('MAX(plp2.id)', 'maxId')
.from(PaymentLinkPayment, 'plp2')
.groupBy('plp2.linkId'),
.groupBy('plp2."linkId"'),
'latest',
'latest.linkId = plp.linkId AND latest.maxId = plp.id',
'latest."linkId" = plp."linkId" AND latest."maxId" = plp.id',
)
.innerJoinAndSelect('plp.currency', 'currency')
.innerJoinAndSelect('plp.link', 'link')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ export class RefRewardService {
.innerJoin('r.user', 'u')
.select('u.userDataId', 'userDataId')
.addSelect('COUNT(*)', 'count')
.addSelect('ROUND(SUM(r.amountInChf), 0)', 'totalChf')
.addSelect('ROUND(SUM(r.amountInChf)::numeric, 0)', 'totalChf')
.where('r.status != :excluded', { excluded: RewardStatus.USER_SWITCH })
.groupBy('u.userDataId')
.orderBy('totalChf', 'DESC');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { SupportLogService } from 'src/subdomains/supporting/support-issue/servi
import { Between, FindOptionsRelations, In, IsNull, MoreThan } from 'typeorm';
import { FiatOutputService } from '../../../../supporting/fiat-output/fiat-output.service';
import { ManualAmlCheckDto } from '../../../aml/dto/manual-aml-check.dto';
import { canManualPass } from '../../../aml/enums/aml-error.enum';
import { canManualPass, ManualPassBlacklistErrors } from '../../../aml/enums/aml-error.enum';
import { AmlReason, PhoneAmlReasons } from '../../../aml/enums/aml-reason.enum';
import { CheckStatus } from '../../../aml/enums/check-status.enum';
import { BuyCryptoService } from '../../../buy-crypto/process/services/buy-crypto.service';
Expand Down Expand Up @@ -203,6 +203,9 @@ export class BuyFiatService implements OnModuleInit {
approved: dto.bankDataActive,
});

if (dto.amlCheck === CheckStatus.PASS && ManualPassBlacklistErrors.some((b) => entity.comment?.includes(b)))
throw new BadRequestException('Blacklisted aml error cannot set Pass');

const forceUpdate: Partial<BuyFiat> = {
...((BuyFiatEditableAmlCheck.includes(entity.amlCheck) ||
(entity.amlCheck === CheckStatus.FAIL && dto.amlCheck === CheckStatus.GSHEET)) &&
Expand Down Expand Up @@ -460,8 +463,12 @@ export class BuyFiatService implements OnModuleInit {
throw new BadRequestException('BuyFiat is already complete or chargeback initiated');
if ([CheckStatus.PASS, CheckStatus.FAIL].includes(entity.amlCheck))
throw new BadRequestException('BuyFiat amlCheck is already finalized');
if (dto.amlCheck === CheckStatus.PASS && !canManualPass(entity.comment))
throw new BadRequestException('Manual pass only allowed when all errors are phone-related');
if (dto.amlCheck === CheckStatus.PASS) {
if (ManualPassBlacklistErrors.some((b) => entity.comment?.includes(b)))
throw new BadRequestException('Blacklisted aml error cannot set Pass');
if (!canManualPass(entity.comment))
throw new BadRequestException('Manual pass only allowed when all errors are phone-related');
}

return this.update(id, {
amlCheck: dto.amlCheck,
Expand Down
20 changes: 14 additions & 6 deletions src/subdomains/core/sell-crypto/route/sell.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Get,
NotFoundException,
Param,
ParseIntPipe,
Post,
Put,
Query,
Expand Down Expand Up @@ -75,8 +76,8 @@ export class SellController {
@ApiBearerAuth()
@UseGuards(AuthGuard(), RoleGuard(UserRole.USER), SellActiveGuard())
@ApiOkResponse({ type: SellDto })
async getSell(@GetJwt() jwt: JwtPayload, @Param('id') id: string): Promise<SellDto> {
return this.sellService.get(jwt.user, +id).then((l) => this.toDto(l));
async getSell(@GetJwt() jwt: JwtPayload, @Param('id', ParseIntPipe) id: number): Promise<SellDto> {
return this.sellService.get(jwt.user, id).then((l) => this.toDto(l));
}

@Post()
Expand Down Expand Up @@ -215,16 +216,23 @@ export class SellController {
@ApiBearerAuth()
@UseGuards(AuthGuard(), RoleGuard(UserRole.USER), SellActiveGuard())
@ApiExcludeEndpoint()
async updateSell(@GetJwt() jwt: JwtPayload, @Param('id') id: string, @Body() dto: UpdateSellDto): Promise<SellDto> {
return this.sellService.updateSell(jwt.user, +id, dto).then((s) => this.toDto(s));
async updateSell(
@GetJwt() jwt: JwtPayload,
@Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateSellDto,
): Promise<SellDto> {
return this.sellService.updateSell(jwt.user, id, dto).then((s) => this.toDto(s));
}

@Get(':id/history')
@ApiBearerAuth()
@UseGuards(AuthGuard(), RoleGuard(UserRole.USER))
@ApiExcludeEndpoint()
async getSellRouteHistory(@GetJwt() jwt: JwtPayload, @Param('id') id: string): Promise<SellHistoryDto[]> {
return this.buyFiatService.getSellHistory(jwt.user, +id);
async getSellRouteHistory(
@GetJwt() jwt: JwtPayload,
@Param('id', ParseIntPipe) id: number,
): Promise<SellHistoryDto[]> {
return this.buyFiatService.getSellHistory(jwt.user, id);
}

// --- DTO --- //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ export class BankDataService {
.createQueryBuilder()
.update('bank_data')
.set({ active: false, default: false })
.where('bank_data.userDataId = :userDataId', { userDataId })
.where('bank_data."userDataId" = :userDataId', { userDataId })
.andWhere('bank_data.id != :id', { id: entity.id })
.andWhere('bank_data.iban = :iban', { iban: entity.iban })
.execute();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export class DepositRouteService {
.innerJoinAndSelect('depositRoute.user', 'user')
.innerJoinAndSelect('user.userData', 'userData')
.where(
`EXISTS (SELECT 1 FROM jsonb_array_elements_text((userData."paymentLinksConfig")::jsonb -> 'accessKeys') AS k WHERE k = :key)`,
`EXISTS (SELECT 1 FROM jsonb_array_elements_text(("userData"."paymentLinksConfig")::jsonb -> 'accessKeys') AS k WHERE k = :key)`,
{ key },
)
.andWhere('depositRoute.active = :active', { active: true })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ export class SupportIssueService {
for (let i = 0; i < termCount; i++) {
const param = `term${i}`;
qb.andWhere(
`(issue.name LIKE :${param} OR issue.uid LIKE :${param} OR issue.clerk LIKE :${param} OR userData.firstname LIKE :${param} OR userData.surname LIKE :${param} OR userData.organizationName LIKE :${param} OR EXISTS (SELECT 1 FROM support_message m WHERE m.issueId = issue.id AND m.message LIKE :${param}))`,
`(issue.name LIKE :${param} OR issue.uid LIKE :${param} OR issue.clerk LIKE :${param} OR "userData".firstname LIKE :${param} OR "userData".surname LIKE :${param} OR "userData"."organizationName" LIKE :${param} OR EXISTS (SELECT 1 FROM support_message m WHERE m."issueId" = issue.id AND m.message LIKE :${param}))`,
{ [param]: `%${terms[i]}%` },
);
}
Expand Down Expand Up @@ -327,14 +327,14 @@ export class SupportIssueService {
(chunk): Promise<{ issueId: string; count: string; lastDate: Date | null; lastAuthor: string | null }[]> =>
this.messageRepo
.createQueryBuilder('m')
.select('m.issueId', 'issueId')
.select('m."issueId"', 'issueId')
.addSelect('COUNT(*)', 'count')
.addSelect(
(sub) =>
sub
.select('m2.created')
.from(SupportMessage, 'm2')
.where('m2.issueId = m.issueId')
.where('m2."issueId" = m."issueId"')
.orderBy('m2.id', 'DESC')
.limit(1),
'lastDate',
Expand All @@ -344,13 +344,13 @@ export class SupportIssueService {
sub
.select('m2.author')
.from(SupportMessage, 'm2')
.where('m2.issueId = m.issueId')
.where('m2."issueId" = m."issueId"')
.orderBy('m2.id', 'DESC')
.limit(1),
'lastAuthor',
)
.where('m.issueId IN (:...ids)', { ids: chunk })
.groupBy('m.issueId')
.where('m."issueId" IN (:...ids)', { ids: chunk })
.groupBy('m."issueId"')
.getRawMany(),
1000,
);
Expand Down