mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2025-11-26 00:24:54 +00:00
187 lines
6.4 KiB
TypeScript
187 lines
6.4 KiB
TypeScript
import { BadRequestException, forwardRef, Inject, Injectable, Logger } from '@nestjs/common';
|
|
import Decimal from 'decimal.js';
|
|
import { Transactional } from 'typeorm-transactional';
|
|
import { AccountCardStatusChangedWebhookRequest } from '~/common/modules/neoleap/dtos/requests';
|
|
import { NeoLeapService } from '~/common/modules/neoleap/services';
|
|
import { Customer } from '~/customer/entities';
|
|
import { KycStatus } from '~/customer/enums';
|
|
import { CustomerService } from '~/customer/services';
|
|
import { OciService } from '~/document/services';
|
|
import { Card } from '../entities';
|
|
import { CardColors } from '../enums';
|
|
import { CardStatusMapper } from '../mappers/card-status.mapper';
|
|
import { CardRepository } from '../repositories';
|
|
import { AccountService } from './account.service';
|
|
import { TransactionService } from './transaction.service';
|
|
|
|
@Injectable()
|
|
export class CardService {
|
|
private readonly logger = new Logger(CardService.name);
|
|
constructor(
|
|
private readonly cardRepository: CardRepository,
|
|
private readonly accountService: AccountService,
|
|
private readonly ociService: OciService,
|
|
@Inject(forwardRef(() => TransactionService)) private readonly transactionService: TransactionService,
|
|
@Inject(forwardRef(() => NeoLeapService)) private readonly neoleapService: NeoLeapService,
|
|
@Inject(forwardRef(() => CustomerService)) private readonly customerService: CustomerService,
|
|
) {}
|
|
|
|
@Transactional()
|
|
async createCard(customerId: string): Promise<Card> {
|
|
const customer = await this.customerService.findCustomerById(customerId);
|
|
|
|
if (customer.kycStatus !== KycStatus.APPROVED) {
|
|
throw new BadRequestException('CUSTOMER.KYC_NOT_APPROVED');
|
|
}
|
|
|
|
if (customer.cards.length > 0) {
|
|
throw new BadRequestException('CUSTOMER.ALREADY_HAS_CARD');
|
|
}
|
|
|
|
const data = await this.neoleapService.createApplication(customer);
|
|
const account = await this.accountService.createAccount(data);
|
|
const createdCard = await this.cardRepository.createCard(customerId, account.id, data);
|
|
|
|
return this.getCardById(createdCard.id);
|
|
}
|
|
|
|
async getChildCards(guardianId: string): Promise<Card[]> {
|
|
const cards = await this.cardRepository.findChildCardsForGuardian(guardianId);
|
|
await this.prepareJuniorImages(cards);
|
|
return cards;
|
|
}
|
|
|
|
async createCardForChild(parentCustomer: Customer, childCustomer: Customer, cardColor: CardColors, cardPin: string) {
|
|
const data = await this.neoleapService.createChildCard(parentCustomer, childCustomer, cardPin);
|
|
const createdCard = await this.cardRepository.createCard(
|
|
childCustomer.id,
|
|
parentCustomer.cards[0].account.id,
|
|
data,
|
|
cardColor,
|
|
parentCustomer.id,
|
|
);
|
|
|
|
return this.getCardById(createdCard.id);
|
|
}
|
|
|
|
async getCardByChildId(guardianId: string, childId: string): Promise<Card> {
|
|
const card = await this.cardRepository.findCardByChildId(guardianId, childId);
|
|
if (!card) {
|
|
throw new BadRequestException('CARD.NOT_FOUND');
|
|
}
|
|
await this.prepareJuniorImages([card]);
|
|
return card;
|
|
}
|
|
async getCardById(id: string): Promise<Card> {
|
|
const card = await this.cardRepository.getCardById(id);
|
|
|
|
if (!card) {
|
|
throw new BadRequestException('CARD.NOT_FOUND');
|
|
}
|
|
|
|
return card;
|
|
}
|
|
|
|
async getCardByReferenceNumber(referenceNumber: string): Promise<Card> {
|
|
const card = await this.cardRepository.getCardByReferenceNumber(referenceNumber);
|
|
|
|
if (!card) {
|
|
throw new BadRequestException('CARD.NOT_FOUND');
|
|
}
|
|
|
|
return card;
|
|
}
|
|
|
|
async getCardByVpan(vpan: string): Promise<Card> {
|
|
const card = await this.cardRepository.getCardByVpan(vpan);
|
|
|
|
if (!card) {
|
|
throw new BadRequestException('CARD.NOT_FOUND');
|
|
}
|
|
return card;
|
|
}
|
|
|
|
async getCardByCustomerId(customerId: string): Promise<Card> {
|
|
const card = await this.cardRepository.getCardByCustomerId(customerId);
|
|
if (!card) {
|
|
throw new BadRequestException('CARD.NOT_FOUND');
|
|
}
|
|
|
|
return card;
|
|
}
|
|
|
|
async updateCardStatus(body: AccountCardStatusChangedWebhookRequest) {
|
|
const card = await this.getCardByVpan(body.cardId);
|
|
const { description, status } = CardStatusMapper[body.newStatus] || CardStatusMapper['99'];
|
|
|
|
return this.cardRepository.updateCardStatus(card.id, status, description);
|
|
}
|
|
|
|
async getEmbossingInformation(customerId: string) {
|
|
const card = await this.getCardByCustomerId(customerId);
|
|
|
|
return this.neoleapService.getEmbossingInformation(card);
|
|
}
|
|
|
|
async getChildCardEmbossingInformation(cardId: string, guardianId: string) {
|
|
const card = await this.getCardById(cardId);
|
|
if (card.parentId !== guardianId) {
|
|
throw new BadRequestException('CARD.DOES_NOT_BELONG_TO_GUARDIAN');
|
|
}
|
|
return this.neoleapService.getEmbossingInformation(card);
|
|
}
|
|
|
|
async updateCardLimit(cardId: string, newLimit: number) {
|
|
const { affected } = await this.cardRepository.updateCardLimit(cardId, newLimit);
|
|
|
|
if (affected === 0) {
|
|
throw new BadRequestException('CARD.NOT_FOUND');
|
|
}
|
|
}
|
|
|
|
async getIbanInformation(customerId: string) {
|
|
const account = await this.accountService.getAccountByCustomerId(customerId);
|
|
return account.iban;
|
|
}
|
|
|
|
@Transactional()
|
|
async transferToChild(juniorId: string, amount: number) {
|
|
const card = await this.getCardByCustomerId(juniorId);
|
|
|
|
if (amount > card.account.balance - card.account.reservedBalance) {
|
|
throw new BadRequestException('CARD.INSUFFICIENT_BALANCE');
|
|
}
|
|
|
|
const finalAmount = Decimal(amount).plus(card.limit);
|
|
await Promise.all([
|
|
this.neoleapService.updateCardControl(card.cardReference, finalAmount.toNumber()),
|
|
this.updateCardLimit(card.id, finalAmount.toNumber()),
|
|
this.accountService.increaseReservedBalance(card.account, amount),
|
|
this.transactionService.createInternalChildTransaction(card.id, amount),
|
|
]);
|
|
|
|
return finalAmount.toNumber();
|
|
}
|
|
|
|
getWeeklySummary(juniorId: string, startDate?: Date, endDate?: Date) {
|
|
return this.transactionService.getWeeklySummary(juniorId, startDate, endDate);
|
|
}
|
|
|
|
fundIban(iban: string, amount: number) {
|
|
return this.accountService.fundIban(iban, amount);
|
|
}
|
|
|
|
private async prepareJuniorImages(cards: Card[]) {
|
|
this.logger.log(`Preparing junior images`);
|
|
await Promise.all(
|
|
cards.map(async (card) => {
|
|
const profilePicture = card.customer?.user?.profilePicture;
|
|
|
|
if (profilePicture) {
|
|
profilePicture.url = await this.ociService.generatePreSignedUrl(profilePicture);
|
|
}
|
|
}),
|
|
);
|
|
}
|
|
}
|