Compare commits

..

1 Commits

Author SHA1 Message Date
cff87c4ecd rollup migrations into one 2025-07-30 13:08:31 +03:00
93 changed files with 264 additions and 4384 deletions

3
.gitignore vendored
View File

@ -53,5 +53,4 @@ pids
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
zod-certs
zod-certs

View File

@ -11,7 +11,6 @@
"exclude": "**/*.md"
},
{ "include": "common/modules/**/templates/**/*", "watchAssets": true },
{ "include": "common/modules/neoleap/zod-certs" },
"i18n",
"files"
]

7
package-lock.json generated
View File

@ -33,7 +33,6 @@
"cacheable": "^1.8.5",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"decimal.js": "^10.6.0",
"firebase-admin": "^13.0.2",
"google-libphonenumber": "^3.2.39",
"handlebars": "^4.7.8",
@ -5168,12 +5167,6 @@
"node": ">=0.10.0"
}
},
"node_modules/decimal.js": {
"version": "10.6.0",
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
"integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
"license": "MIT"
},
"node_modules/dedent": {
"version": "1.5.3",
"dev": true,

View File

@ -51,7 +51,6 @@
"cacheable": "^1.8.5",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"decimal.js": "^10.6.0",
"firebase-admin": "^13.0.2",
"google-libphonenumber": "^3.2.39",
"handlebars": "^4.7.8",

View File

@ -12,7 +12,6 @@ import { AllowanceModule } from './allowance/allowance.module';
import { AuthModule } from './auth/auth.module';
import { CacheModule } from './common/modules/cache/cache.module';
import { LookupModule } from './common/modules/lookup/lookup.module';
import { NeoLeapModule } from './common/modules/neoleap/neoleap.module';
import { NotificationModule } from './common/modules/notification/notification.module';
import { OtpModule } from './common/modules/otp/otp.module';
import { AllExceptionsFilter, buildI18nValidationExceptionFilter } from './core/filters';
@ -31,7 +30,6 @@ import { MoneyRequestModule } from './money-request/money-request.module';
import { SavingGoalsModule } from './saving-goals/saving-goals.module';
import { TaskModule } from './task/task.module';
import { UserModule } from './user/user.module';
import { CardModule } from './card/card.module';
@Module({
controllers: [],
@ -82,8 +80,6 @@ import { CardModule } from './card/card.module';
UserModule,
CronModule,
NeoLeapModule,
CardModule,
],
providers: [
// Global Pipes

View File

@ -1,25 +0,0 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Card } from './entities';
import { Account } from './entities/account.entity';
import { Transaction } from './entities/transaction.entity';
import { CardRepository } from './repositories';
import { AccountRepository } from './repositories/account.repository';
import { TransactionRepository } from './repositories/transaction.repository';
import { CardService } from './services';
import { AccountService } from './services/account.service';
import { TransactionService } from './services/transaction.service';
@Module({
imports: [TypeOrmModule.forFeature([Card, Account, Transaction])],
providers: [
CardService,
CardRepository,
TransactionService,
TransactionRepository,
AccountService,
AccountRepository,
],
exports: [CardService, TransactionService],
})
export class CardModule {}

View File

@ -1,31 +0,0 @@
import { Column, CreateDateColumn, Entity, Index, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
import { Card } from './card.entity';
import { Transaction } from './transaction.entity';
@Entity('accounts')
export class Account {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column('varchar', { length: 255, nullable: false, unique: true, name: 'account_reference' })
@Index({ unique: true })
accountReference!: string;
@Column('varchar', { length: 255, nullable: false, name: 'currency' })
currency!: string;
@Column('decimal', { precision: 10, scale: 2, default: 0.0, name: 'balance' })
balance!: number;
@OneToMany(() => Card, (card) => card.account, { cascade: true })
cards!: Card[];
@OneToMany(() => Transaction, (transaction) => transaction.account, { cascade: true })
transactions!: Transaction[];
@CreateDateColumn({ name: 'created_at', type: 'timestamp with time zone' })
createdAt!: Date;
@UpdateDateColumn({ name: 'updated_at', type: 'timestamp with time zone' })
updatedAt!: Date;
}

View File

@ -1,85 +0,0 @@
import {
Column,
CreateDateColumn,
Entity,
Index,
JoinColumn,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
import { Customer } from '~/customer/entities';
import { CardColors, CardIssuers, CardScheme, CardStatus, CardStatusDescription, CustomerType } from '../enums';
import { Account } from './account.entity';
import { Transaction } from './transaction.entity';
@Entity('cards')
export class Card {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Index({ unique: true })
@Column({ name: 'card_reference', nullable: false, type: 'varchar' })
cardReference!: string;
@Column({ length: 6, name: 'first_six_digits', nullable: false, type: 'varchar' })
firstSixDigits!: string;
@Column({ length: 4, name: 'last_four_digits', nullable: false, type: 'varchar' })
lastFourDigits!: string;
@Column({ type: 'varchar', nullable: false })
expiry!: string;
@Column({ type: 'varchar', nullable: false, name: 'customer_type' })
customerType!: CustomerType;
@Column({ type: 'varchar', nullable: false, default: CardColors.BLUE })
color!: CardColors;
@Column({ type: 'varchar', nullable: false, default: CardStatus.PENDING })
status!: CardStatus;
@Column({ type: 'varchar', nullable: false, default: CardStatusDescription.PENDING_ACTIVATION })
statusDescription!: CardStatusDescription;
@Column({ type: 'decimal', precision: 10, scale: 2, default: 0.0, name: 'limit' })
limit!: number;
@Column({ type: 'varchar', nullable: false, default: CardScheme.VISA })
scheme!: CardScheme;
@Column({ type: 'varchar', nullable: false })
issuer!: CardIssuers;
@Column({ type: 'uuid', name: 'customer_id', nullable: false })
customerId!: string;
@Column({ type: 'uuid', name: 'parent_id', nullable: true })
parentId?: string;
@Column({ type: 'uuid', name: 'account_id', nullable: false })
accountId!: string;
@ManyToOne(() => Customer, (customer) => customer.childCards)
@JoinColumn({ name: 'parent_id' })
parentCustomer?: Customer;
@ManyToOne(() => Customer, (customer) => customer.cards, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'customer_id' })
customer!: Customer;
@ManyToOne(() => Account, (account) => account.cards, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'account_id' })
account!: Account;
@OneToMany(() => Transaction, (transaction) => transaction.card, { cascade: true })
transactions!: Transaction[];
@CreateDateColumn({ name: 'created_at', type: 'timestamp with time zone' })
createdAt!: Date;
@UpdateDateColumn({ type: 'timestamp with time zone', name: 'updated_at' })
updatedAt!: Date;
}

View File

@ -1 +0,0 @@
export * from './card.entity';

View File

@ -1,69 +0,0 @@
import { Column, CreateDateColumn, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
import { TransactionScope, TransactionType } from '../enums';
import { Account } from './account.entity';
import { Card } from './card.entity';
@Entity('transactions')
export class Transaction {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column({ name: 'transaction_scope', type: 'varchar', nullable: false })
transactionScope!: TransactionScope;
@Column({ name: 'transaction_type', type: 'varchar', default: TransactionType.EXTERNAL })
transactionType!: TransactionType;
@Column({ name: 'card_reference', nullable: true, type: 'varchar' })
cardReference!: string;
@Column({ name: 'account_reference', nullable: true, type: 'varchar' })
accountReference!: string;
@Column({ name: 'transaction_id', unique: true, nullable: true, type: 'varchar' })
transactionId!: string;
@Column({ name: 'card_masked_number', nullable: true, type: 'varchar' })
cardMaskedNumber!: string;
@Column({ type: 'timestamp with time zone', name: 'transaction_date', nullable: true })
transactionDate!: Date;
@Column({ name: 'rrn', nullable: true, type: 'varchar' })
rrn!: string;
@Column({ type: 'decimal', precision: 12, scale: 2, name: 'transaction_amount' })
transactionAmount!: number;
@Column({ type: 'varchar', name: 'transaction_currency' })
transactionCurrency!: string;
@Column({ type: 'decimal', name: 'billing_amount', precision: 12, scale: 2 })
billingAmount!: number;
@Column({ type: 'decimal', name: 'settlement_amount', precision: 12, scale: 2 })
settlementAmount!: number;
@Column({ type: 'decimal', name: 'fees', precision: 12, scale: 2 })
fees!: number;
@Column({ type: 'decimal', name: 'vat_on_fees', precision: 12, scale: 2, default: 0.0 })
vatOnFees!: number;
@Column({ name: 'card_id', type: 'uuid', nullable: true })
cardId!: string;
@Column({ name: 'account_id', type: 'uuid', nullable: true })
accountId!: string;
@ManyToOne(() => Card, (card) => card.transactions, { onDelete: 'CASCADE', nullable: true })
@JoinColumn({ name: 'card_id' })
card!: Card;
@ManyToOne(() => Account, (account) => account.transactions, { onDelete: 'CASCADE', nullable: true })
@JoinColumn({ name: 'account_id' })
account!: Account;
@CreateDateColumn({ name: 'created_at', type: 'timestamp with time zone' })
createdAt!: Date;
}

View File

@ -1,4 +0,0 @@
export enum CardColors {
RED = 'RED',
BLUE = 'BLUE',
}

View File

@ -1,3 +0,0 @@
export enum CardIssuers {
NEOLEAP = 'NEOLEAP',
}

View File

@ -1,4 +0,0 @@
export enum CardScheme {
VISA = 'VISA',
MASTERCARD = 'MASTERCARD',
}

View File

@ -1,68 +0,0 @@
/**
* import { CardStatus, CardStatusDescription } from '../enums';
export const CardStatusMapper: Record<string, { description: CardStatusDescription; status: CardStatus }> = {
//ACTIVE
'00': { description: 'NORMAL', status: CardStatus.ACTIVE },
//PENDING
'02': { description: 'NOT_YET_ISSUED', status: CardStatus.PENDING },
'20': { description: 'PENDING_ISSUANCE', status: CardStatus.PENDING },
'21': { description: 'CARD_EXTRACTED', status: CardStatus.PENDING },
'22': { description: 'EXTRACTION_FAILED', status: CardStatus.PENDING },
'23': { description: 'FAILED_PRINTING_BULK', status: CardStatus.PENDING },
'24': { description: 'FAILED_PRINTING_INST', status: CardStatus.PENDING },
'30': { description: 'PENDING_ACTIVATION', status: CardStatus.PENDING },
'27': { description: 'PENDING_PIN', status: CardStatus.PENDING },
'16': { description: 'PREPARE_TO_CLOSE', status: CardStatus.PENDING },
//BLOCKED
'01': { description: 'PIN_TRIES_EXCEEDED', status: CardStatus.BLOCKED },
'03': { description: 'CARD_EXPIRED', status: CardStatus.BLOCKED },
'04': { description: 'LOST', status: CardStatus.BLOCKED },
'05': { description: 'STOLEN', status: CardStatus.BLOCKED },
'06': { description: 'CUSTOMER_CLOSE', status: CardStatus.BLOCKED },
'07': { description: 'BANK_CANCELLED', status: CardStatus.BLOCKED },
'08': { description: 'FRAUD', status: CardStatus.BLOCKED },
'09': { description: 'DAMAGED', status: CardStatus.BLOCKED },
'50': { description: 'SAFE_BLOCK', status: CardStatus.BLOCKED },
'51': { description: 'TEMPORARY_BLOCK', status: CardStatus.BLOCKED },
'52': { description: 'RISK_BLOCK', status: CardStatus.BLOCKED },
'53': { description: 'OVERDRAFT', status: CardStatus.BLOCKED },
'54': { description: 'BLOCKED_FOR_FEES', status: CardStatus.BLOCKED },
'67': { description: 'CLOSED_CUSTOMER_DEAD', status: CardStatus.BLOCKED },
'75': { description: 'RETURN_CARD', status: CardStatus.BLOCKED },
//Fallback
'99': { description: 'UNKNOWN', status: CardStatus.PENDING },
};
*/
export enum CardStatusDescription {
NORMAL = 'NORMAL',
NOT_YET_ISSUED = 'NOT_YET_ISSUED',
PENDING_ISSUANCE = 'PENDING_ISSUANCE',
CARD_EXTRACTED = 'CARD_EXTRACTED',
EXTRACTION_FAILED = 'EXTRACTION_FAILED',
FAILED_PRINTING_BULK = 'FAILED_PRINTING_BULK',
FAILED_PRINTING_INST = 'FAILED_PRINTING_INST',
PENDING_ACTIVATION = 'PENDING_ACTIVATION',
PENDING_PIN = 'PENDING_PIN',
PREPARE_TO_CLOSE = 'PREPARE_TO_CLOSE',
PIN_TRIES_EXCEEDED = 'PIN_TRIES_EXCEEDED',
CARD_EXPIRED = 'CARD_EXPIRED',
LOST = 'LOST',
STOLEN = 'STOLEN',
CUSTOMER_CLOSE = 'CUSTOMER_CLOSE',
BANK_CANCELLED = 'BANK_CANCELLED',
FRAUD = 'FRAUD',
DAMAGED = 'DAMAGED',
SAFE_BLOCK = 'SAFE_BLOCK',
TEMPORARY_BLOCK = 'TEMPORARY_BLOCK',
RISK_BLOCK = 'RISK_BLOCK',
OVERDRAFT = 'OVERDRAFT',
BLOCKED_FOR_FEES = 'BLOCKED_FOR_FEES',
CLOSED_CUSTOMER_DEAD = 'CLOSED_CUSTOMER_DEAD',
RETURN_CARD = 'RETURN_CARD',
UNKNOWN = 'UNKNOWN',
}

View File

@ -1,6 +0,0 @@
export enum CardStatus {
ACTIVE = 'ACTIVE',
CANCELED = 'CANCELED',
BLOCKED = 'BLOCKED',
PENDING = 'PENDING',
}

View File

@ -1,4 +0,0 @@
export enum CustomerType {
PARENT = 'PARENT',
CHILD = 'CHILD',
}

View File

@ -1,8 +0,0 @@
export * from './card-colors.enum';
export * from './card-issuers.enum';
export * from './card-scheme.enum';
export * from './card-status-description.enum';
export * from './card-status.enum';
export * from './customer-type.enum';
export * from './transaction-scope.enum';
export * from './transaction-type.enum';

View File

@ -1,4 +0,0 @@
export enum TransactionScope {
CARD = 'CARD',
ACCOUNT = 'ACCOUNT',
}

View File

@ -1,4 +0,0 @@
export enum TransactionType {
INTERNAL = 'INTERNAL',
EXTERNAL = 'EXTERNAL',
}

View File

@ -1,109 +0,0 @@
import { UserLocale } from '~/core/enums';
import { CardStatusDescription } from '../enums';
export const CardStatusMapper: Record<CardStatusDescription, { [key in UserLocale]: { description: string } }> = {
[CardStatusDescription.NORMAL]: {
[UserLocale.ENGLISH]: { description: 'The card is active' },
[UserLocale.ARABIC]: { description: 'البطاقة نشطة' },
},
[CardStatusDescription.NOT_YET_ISSUED]: {
[UserLocale.ENGLISH]: { description: 'The card is not yet issued' },
[UserLocale.ARABIC]: { description: 'البطاقة لم تصدر بعد' },
},
[CardStatusDescription.PENDING_ISSUANCE]: {
[UserLocale.ENGLISH]: { description: 'The card is pending issuance' },
[UserLocale.ARABIC]: { description: 'البطاقة قيد الإصدار' },
},
[CardStatusDescription.CARD_EXTRACTED]: {
[UserLocale.ENGLISH]: { description: 'The card has been extracted' },
[UserLocale.ARABIC]: { description: 'تم استخراج البطاقة' },
},
[CardStatusDescription.EXTRACTION_FAILED]: {
[UserLocale.ENGLISH]: { description: 'The card extraction has failed' },
[UserLocale.ARABIC]: { description: 'فشل استخراج البطاقة' },
},
[CardStatusDescription.FAILED_PRINTING_BULK]: {
[UserLocale.ENGLISH]: { description: 'The card printing in bulk has failed' },
[UserLocale.ARABIC]: { description: 'فشل الطباعة بالجملة للبطاقة' },
},
[CardStatusDescription.FAILED_PRINTING_INST]: {
[UserLocale.ENGLISH]: { description: 'The card printing in institution has failed' },
[UserLocale.ARABIC]: { description: 'فشل الطباعة في المؤسسة للبطاقة' },
},
[CardStatusDescription.PENDING_ACTIVATION]: {
[UserLocale.ENGLISH]: { description: 'The card is pending activation' },
[UserLocale.ARABIC]: { description: 'البطاقة قيد التفعيل' },
},
[CardStatusDescription.PENDING_PIN]: {
[UserLocale.ENGLISH]: { description: 'The card is pending PIN' },
[UserLocale.ARABIC]: { description: 'البطاقة قيد الانتظار لرقم التعريف الشخصي' },
},
[CardStatusDescription.PREPARE_TO_CLOSE]: {
[UserLocale.ENGLISH]: { description: 'The card is being prepared for closure' },
[UserLocale.ARABIC]: { description: 'البطاقة قيد التحضير للإغلاق' },
},
[CardStatusDescription.PIN_TRIES_EXCEEDED]: {
[UserLocale.ENGLISH]: { description: 'The card PIN tries have been exceeded' },
[UserLocale.ARABIC]: { description: 'تم تجاوز محاولات رقم التعريف الشخصي للبطاقة' },
},
[CardStatusDescription.CARD_EXPIRED]: {
[UserLocale.ENGLISH]: { description: 'The card has expired' },
[UserLocale.ARABIC]: { description: 'انتهت صلاحية البطاقة' },
},
[CardStatusDescription.LOST]: {
[UserLocale.ENGLISH]: { description: 'The card is lost' },
[UserLocale.ARABIC]: { description: 'البطاقة ضائعة' },
},
[CardStatusDescription.STOLEN]: {
[UserLocale.ENGLISH]: { description: 'The card is stolen' },
[UserLocale.ARABIC]: { description: 'البطاقة مسروقة' },
},
[CardStatusDescription.CUSTOMER_CLOSE]: {
[UserLocale.ENGLISH]: { description: 'The card is being closed by the customer' },
[UserLocale.ARABIC]: { description: 'البطاقة قيد الإغلاق من قبل العميل' },
},
[CardStatusDescription.BANK_CANCELLED]: {
[UserLocale.ENGLISH]: { description: 'The card has been cancelled by the bank' },
[UserLocale.ARABIC]: { description: 'البطاقة ألغيت من قبل البنك' },
},
[CardStatusDescription.FRAUD]: {
[UserLocale.ENGLISH]: { description: 'Fraud' },
[UserLocale.ARABIC]: { description: 'احتيال' },
},
[CardStatusDescription.DAMAGED]: {
[UserLocale.ENGLISH]: { description: 'The card is damaged' },
[UserLocale.ARABIC]: { description: 'البطاقة تالفة' },
},
[CardStatusDescription.SAFE_BLOCK]: {
[UserLocale.ENGLISH]: { description: 'The card is in a safe block' },
[UserLocale.ARABIC]: { description: 'البطاقة في حظر آمن' },
},
[CardStatusDescription.TEMPORARY_BLOCK]: {
[UserLocale.ENGLISH]: { description: 'The card is in a temporary block' },
[UserLocale.ARABIC]: { description: 'البطاقة في حظر مؤقت' },
},
[CardStatusDescription.RISK_BLOCK]: {
[UserLocale.ENGLISH]: { description: 'The card is in a risk block' },
[UserLocale.ARABIC]: { description: 'البطاقة في حظر المخاطر' },
},
[CardStatusDescription.OVERDRAFT]: {
[UserLocale.ENGLISH]: { description: 'The card is in overdraft' },
[UserLocale.ARABIC]: { description: 'البطاقة في السحب على المكشوف' },
},
[CardStatusDescription.BLOCKED_FOR_FEES]: {
[UserLocale.ENGLISH]: { description: 'The card is blocked for fees' },
[UserLocale.ARABIC]: { description: 'البطاقة محظورة للرسوم' },
},
[CardStatusDescription.CLOSED_CUSTOMER_DEAD]: {
[UserLocale.ENGLISH]: { description: 'The card is closed because the customer is dead' },
[UserLocale.ARABIC]: { description: 'البطاقة مغلقة لأن العميل متوفى' },
},
[CardStatusDescription.RETURN_CARD]: {
[UserLocale.ENGLISH]: { description: 'The card is being returned' },
[UserLocale.ARABIC]: { description: 'البطاقة قيد الإرجاع' },
},
[CardStatusDescription.UNKNOWN]: {
[UserLocale.ENGLISH]: { description: 'The card status is unknown' },
[UserLocale.ARABIC]: { description: 'حالة البطاقة غير معروفة' },
},
};

View File

@ -1,37 +0,0 @@
import { CardStatus, CardStatusDescription } from '../enums';
export const CardStatusMapper: Record<string, { description: CardStatusDescription; status: CardStatus }> = {
//ACTIVE
'00': { description: CardStatusDescription.NORMAL, status: CardStatus.ACTIVE },
//PENDING
'02': { description: CardStatusDescription.NOT_YET_ISSUED, status: CardStatus.PENDING },
'20': { description: CardStatusDescription.PENDING_ISSUANCE, status: CardStatus.PENDING },
'21': { description: CardStatusDescription.CARD_EXTRACTED, status: CardStatus.PENDING },
'22': { description: CardStatusDescription.EXTRACTION_FAILED, status: CardStatus.PENDING },
'23': { description: CardStatusDescription.FAILED_PRINTING_BULK, status: CardStatus.PENDING },
'24': { description: CardStatusDescription.FAILED_PRINTING_INST, status: CardStatus.PENDING },
'30': { description: CardStatusDescription.PENDING_ACTIVATION, status: CardStatus.PENDING },
'27': { description: CardStatusDescription.PENDING_PIN, status: CardStatus.PENDING },
'16': { description: CardStatusDescription.PREPARE_TO_CLOSE, status: CardStatus.PENDING },
//BLOCKED
'01': { description: CardStatusDescription.PIN_TRIES_EXCEEDED, status: CardStatus.BLOCKED },
'03': { description: CardStatusDescription.CARD_EXPIRED, status: CardStatus.BLOCKED },
'04': { description: CardStatusDescription.LOST, status: CardStatus.BLOCKED },
'05': { description: CardStatusDescription.STOLEN, status: CardStatus.BLOCKED },
'06': { description: CardStatusDescription.CUSTOMER_CLOSE, status: CardStatus.BLOCKED },
'07': { description: CardStatusDescription.BANK_CANCELLED, status: CardStatus.BLOCKED },
'08': { description: CardStatusDescription.FRAUD, status: CardStatus.BLOCKED },
'09': { description: CardStatusDescription.DAMAGED, status: CardStatus.BLOCKED },
'50': { description: CardStatusDescription.SAFE_BLOCK, status: CardStatus.BLOCKED },
'51': { description: CardStatusDescription.TEMPORARY_BLOCK, status: CardStatus.BLOCKED },
'52': { description: CardStatusDescription.RISK_BLOCK, status: CardStatus.BLOCKED },
'53': { description: CardStatusDescription.OVERDRAFT, status: CardStatus.BLOCKED },
'54': { description: CardStatusDescription.BLOCKED_FOR_FEES, status: CardStatus.BLOCKED },
'67': { description: CardStatusDescription.CLOSED_CUSTOMER_DEAD, status: CardStatus.BLOCKED },
'75': { description: CardStatusDescription.RETURN_CARD, status: CardStatus.BLOCKED },
//Fallback
'99': { description: CardStatusDescription.UNKNOWN, status: CardStatus.PENDING },
};

View File

@ -1,34 +0,0 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Account } from '../entities/account.entity';
@Injectable()
export class AccountRepository {
constructor(@InjectRepository(Account) private readonly accountRepository: Repository<Account>) {}
createAccount(accountId: string): Promise<Account> {
return this.accountRepository.save(
this.accountRepository.create({
accountReference: accountId,
balance: 0,
currency: '682',
}),
);
}
getAccountByReferenceNumber(accountReference: string): Promise<Account | null> {
return this.accountRepository.findOne({
where: { accountReference },
relations: ['cards'],
});
}
topUpAccountBalance(accountReference: string, amount: number) {
return this.accountRepository.increment({ accountReference }, 'balance', amount);
}
decreaseAccountBalance(accountReference: string, amount: number) {
return this.accountRepository.decrement({ accountReference }, 'balance', amount);
}
}

View File

@ -1,49 +0,0 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateApplicationResponse } from '~/common/modules/neoleap/dtos/response';
import { Card } from '../entities';
import { CardColors, CardIssuers, CardScheme, CardStatus, CardStatusDescription, CustomerType } from '../enums';
@Injectable()
export class CardRepository {
constructor(@InjectRepository(Card) private readonly cardRepository: Repository<Card>) {}
createCard(customerId: string, accountId: string, card: CreateApplicationResponse): Promise<Card> {
return this.cardRepository.save(
this.cardRepository.create({
customerId: customerId,
expiry: card.expiryDate,
cardReference: card.cardId,
customerType: CustomerType.PARENT,
firstSixDigits: card.firstSixDigits,
lastFourDigits: card.lastFourDigits,
color: CardColors.BLUE,
scheme: CardScheme.VISA,
issuer: CardIssuers.NEOLEAP,
accountId: accountId,
}),
);
}
getCardById(id: string): Promise<Card | null> {
return this.cardRepository.findOne({ where: { id } });
}
getCardByReferenceNumber(referenceNumber: string): Promise<Card | null> {
return this.cardRepository.findOne({ where: { cardReference: referenceNumber }, relations: ['account'] });
}
getActiveCardForCustomer(customerId: string): Promise<Card | null> {
return this.cardRepository.findOne({
where: { customerId, status: CardStatus.ACTIVE },
});
}
updateCardStatus(id: string, status: CardStatus, statusDescription: CardStatusDescription) {
return this.cardRepository.update(id, {
status: status,
statusDescription: statusDescription,
});
}
}

View File

@ -1 +0,0 @@
export * from './card.repository';

View File

@ -1,65 +0,0 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import moment from 'moment';
import { Repository } from 'typeorm';
import {
AccountTransactionWebhookRequest,
CardTransactionWebhookRequest,
} from '~/common/modules/neoleap/dtos/requests';
import { Card } from '../entities';
import { Account } from '../entities/account.entity';
import { Transaction } from '../entities/transaction.entity';
import { TransactionScope, TransactionType } from '../enums';
@Injectable()
export class TransactionRepository {
constructor(@InjectRepository(Transaction) private transactionRepository: Repository<Transaction>) {}
createCardTransaction(card: Card, transactionData: CardTransactionWebhookRequest): Promise<Transaction> {
return this.transactionRepository.save(
this.transactionRepository.create({
transactionId: transactionData.transactionId,
cardReference: transactionData.cardId,
transactionAmount: transactionData.transactionAmount,
transactionCurrency: transactionData.transactionCurrency,
billingAmount: transactionData.billingAmount,
settlementAmount: transactionData.settlementAmount,
transactionDate: moment(transactionData.date + transactionData.time, 'YYYYMMDDHHmmss').toDate(),
rrn: transactionData.rrn,
cardMaskedNumber: transactionData.cardMaskedNumber,
fees: transactionData.fees,
cardId: card.id,
accountId: card.account!.id,
transactionType: TransactionType.EXTERNAL,
accountReference: card.account!.accountReference,
transactionScope: TransactionScope.CARD,
vatOnFees: transactionData.vatOnFees,
}),
);
}
createAccountTransaction(account: Account, transactionData: AccountTransactionWebhookRequest): Promise<Transaction> {
return this.transactionRepository.save(
this.transactionRepository.create({
transactionId: transactionData.transactionId,
transactionAmount: transactionData.amount,
transactionCurrency: transactionData.currency,
billingAmount: 0,
settlementAmount: 0,
transactionDate: moment(transactionData.date + transactionData.time, 'YYYYMMDDHHmmss').toDate(),
fees: 0,
accountReference: account.accountReference,
accountId: account.id,
transactionType: TransactionType.EXTERNAL,
transactionScope: TransactionScope.ACCOUNT,
vatOnFees: 0,
}),
);
}
findTransactionByReference(transactionId: string, accountReference: string): Promise<Transaction | null> {
return this.transactionRepository.findOne({
where: { transactionId, accountReference },
});
}
}

View File

@ -1,38 +0,0 @@
import { Injectable, UnprocessableEntityException } from '@nestjs/common';
import { Account } from '../entities/account.entity';
import { AccountRepository } from '../repositories/account.repository';
@Injectable()
export class AccountService {
constructor(private readonly accountRepository: AccountRepository) {}
createAccount(accountId: string): Promise<Account> {
return this.accountRepository.createAccount(accountId);
}
async getAccountByReferenceNumber(accountReference: string): Promise<Account> {
const account = await this.accountRepository.getAccountByReferenceNumber(accountReference);
if (!account) {
throw new UnprocessableEntityException('ACCOUNT.NOT_FOUND');
}
return account;
}
async creditAccountBalance(accountReference: string, amount: number) {
return this.accountRepository.topUpAccountBalance(accountReference, amount);
}
async decreaseAccountBalance(accountReference: string, amount: number) {
const account = await this.getAccountByReferenceNumber(accountReference);
/**
* While there is no need to check for insufficient balance because this is a webhook handler,
* I just added this check to ensure we don't have corruption in our data especially if this service is used elsewhere.
*/
if (account.balance < amount) {
throw new UnprocessableEntityException('ACCOUNT.INSUFFICIENT_BALANCE');
}
return this.accountRepository.decreaseAccountBalance(accountReference, amount);
}
}

View File

@ -1,54 +0,0 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { Transactional } from 'typeorm-transactional';
import { AccountCardStatusChangedWebhookRequest } from '~/common/modules/neoleap/dtos/requests';
import { CreateApplicationResponse } from '~/common/modules/neoleap/dtos/response';
import { Card } from '../entities';
import { CardStatusMapper } from '../mappers/card-status.mapper';
import { CardRepository } from '../repositories';
import { AccountService } from './account.service';
@Injectable()
export class CardService {
constructor(private readonly cardRepository: CardRepository, private readonly accountService: AccountService) {}
@Transactional()
async createCard(customerId: string, cardData: CreateApplicationResponse): Promise<Card> {
const account = await this.accountService.createAccount(cardData.accountId);
return this.cardRepository.createCard(customerId, account.id, cardData);
}
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 getActiveCardForCustomer(customerId: string): Promise<Card> {
const card = await this.cardRepository.getActiveCardForCustomer(customerId);
if (!card) {
throw new BadRequestException('CARD.NOT_FOUND');
}
return card;
}
async updateCardStatus(body: AccountCardStatusChangedWebhookRequest) {
const card = await this.getCardByReferenceNumber(body.cardId);
const { description, status } = CardStatusMapper[body.newStatus] || CardStatusMapper['99'];
return this.cardRepository.updateCardStatus(card.id, status, description);
}
}

View File

@ -1 +0,0 @@
export * from './card.service';

View File

@ -1,62 +0,0 @@
import { Injectable, UnprocessableEntityException } from '@nestjs/common';
import Decimal from 'decimal.js';
import { Transactional } from 'typeorm-transactional';
import {
AccountTransactionWebhookRequest,
CardTransactionWebhookRequest,
} from '~/common/modules/neoleap/dtos/requests';
import { Transaction } from '../entities/transaction.entity';
import { TransactionRepository } from '../repositories/transaction.repository';
import { AccountService } from './account.service';
import { CardService } from './card.service';
@Injectable()
export class TransactionService {
constructor(
private readonly transactionRepository: TransactionRepository,
private readonly cardService: CardService,
private readonly accountService: AccountService,
) {}
@Transactional()
async createCardTransaction(body: CardTransactionWebhookRequest) {
const card = await this.cardService.getCardByReferenceNumber(body.cardId);
const existingTransaction = await this.findExistingTransaction(body.transactionId, card.account.accountReference);
if (existingTransaction) {
throw new UnprocessableEntityException('TRANSACTION.ALREADY_EXISTS');
}
const transaction = await this.transactionRepository.createCardTransaction(card, body);
const total = new Decimal(body.transactionAmount).plus(body.billingAmount).plus(body.fees).plus(body.vatOnFees);
await this.accountService.decreaseAccountBalance(card.account.accountReference, total.toNumber());
return transaction;
}
@Transactional()
async createAccountTransaction(body: AccountTransactionWebhookRequest) {
const account = await this.accountService.getAccountByReferenceNumber(body.accountId);
const existingTransaction = await this.findExistingTransaction(body.transactionId, account.accountReference);
if (existingTransaction) {
throw new UnprocessableEntityException('TRANSACTION.ALREADY_EXISTS');
}
const transaction = await this.transactionRepository.createAccountTransaction(account, body);
await this.accountService.creditAccountBalance(account.accountReference, body.amount);
return transaction;
}
private async findExistingTransaction(transactionId: string, accountReference: string): Promise<Transaction | null> {
const existingTransaction = await this.transactionRepository.findTransactionByReference(
transactionId,
accountReference,
);
return existingTransaction;
}
}

View File

@ -1,750 +0,0 @@
export const CREATE_APPLICATION_MOCK = {
ResponseHeader: {
Version: '1.0.0',
MsgUid: 'adaa1893-9f95-48a8-b7a1-0422bcf629b5',
Source: 'ZOD',
ServiceId: 'CreateNewApplication',
ReqDateTime: '2025-06-03T07:32:16.304Z',
RspDateTime: '2025-06-03T08:21:15.662',
ResponseCode: '000',
ResponseType: 'Success',
ProcessingTime: 1665,
EncryptionKey: null,
ResponseDescription: 'Operation Successful',
LocalizedResponseDescription: null,
CustomerSpecificResponseDescriptionList: null,
HeaderUserDataList: null,
},
CreateNewApplicationResponseDetails: {
InstitutionCode: '1100',
ApplicationTypeDetails: {
TypeCode: '01',
Description: 'Normal Primary',
Additional: false,
Corporate: false,
UserData: null,
},
ApplicationDetails: {
cif: null,
ApplicationNumber: '3300000000073',
ExternalApplicationNumber: '3',
ApplicationStatus: '04',
Organization: 0,
Product: '1101',
ApplicatonDate: '2025-05-29',
ApplicationSource: 'O',
SalesSource: null,
DeliveryMethod: 'V',
ProgramCode: null,
Campaign: null,
Plastic: null,
Design: null,
ProcessStage: '99',
ProcessStageStatus: 'S',
Score: null,
ExternalScore: null,
RequestedLimit: 0,
SuggestedLimit: null,
AssignedLimit: 0,
AllowedLimitList: null,
EligibilityCheckResult: '00',
EligibilityCheckDescription: null,
Title: 'Mr.',
FirstName: 'Abdalhamid',
SecondName: null,
ThirdName: null,
LastName: ' Ahmad',
FullName: 'Abdalhamid Ahmad',
EmbossName: 'ABDALHAMID AHMAD',
PlaceOfBirth: null,
DateOfBirth: '1999-01-07',
LocalizedDateOfBirth: '1999-01-07',
Age: 26,
Gender: 'M',
Married: 'U',
Nationality: '682',
IdType: '01',
IdNumber: '1089055972',
IdExpiryDate: '2031-09-17',
EducationLevel: null,
ProfessionCode: 0,
NumberOfDependents: 0,
EmployerName: 'N/A',
EmploymentYears: 0,
EmploymentMonths: 0,
EmployerPhoneArea: null,
EmployerPhoneNumber: null,
EmployerPhoneExtension: null,
EmployerMobile: null,
EmployerFaxArea: null,
EmployerFax: null,
EmployerCity: null,
EmployerAddress: null,
EmploymentActivity: null,
EmploymentStatus: null,
CIF: null,
BankAccountNumber: ' ',
Currency: {
CurrCode: '682',
AlphaCode: 'SAR',
},
RequestedCurrencyList: null,
CreditAccountNumber: '6000000000000000',
AccountType: '30',
OpenDate: null,
Income: 0,
AdditionalIncome: 0,
TotalIncome: 0,
CurrentBalance: 0,
AverageBalance: 0,
AssetsBalance: 0,
InsuranceBalance: 0,
DepositAmount: 0,
GuarenteeAccountNumber: null,
GuarenteeAmount: 0,
InstalmentAmount: 0,
AutoDebit: 'N',
PaymentMethod: '2',
BillingCycle: 'C1',
OldIssueDate: null,
OtherPaymentsDate: null,
MaximumDelinquency: null,
CreditBureauDecision: null,
CreditBureauUserData: null,
ECommerce: 'N',
NumberOfCards: 0,
OtherBank: null,
OtherBankDescription: null,
InsuranceProduct: null,
SocialCode: '000',
JobGrade: 0,
Flags: [
{
Position: 1,
Value: '0',
},
{
Position: 2,
Value: '0',
},
{
Position: 3,
Value: '0',
},
{
Position: 4,
Value: '0',
},
{
Position: 5,
Value: '0',
},
{
Position: 6,
Value: '0',
},
{
Position: 7,
Value: '0',
},
{
Position: 8,
Value: '0',
},
{
Position: 9,
Value: '0',
},
{
Position: 10,
Value: '0',
},
{
Position: 11,
Value: '0',
},
{
Position: 12,
Value: '0',
},
{
Position: 13,
Value: '0',
},
{
Position: 14,
Value: '0',
},
{
Position: 15,
Value: '0',
},
{
Position: 16,
Value: '0',
},
{
Position: 17,
Value: '0',
},
{
Position: 18,
Value: '0',
},
{
Position: 19,
Value: '0',
},
{
Position: 20,
Value: '0',
},
{
Position: 21,
Value: '0',
},
{
Position: 22,
Value: '0',
},
{
Position: 23,
Value: '0',
},
{
Position: 24,
Value: '0',
},
{
Position: 25,
Value: '0',
},
{
Position: 26,
Value: '0',
},
{
Position: 27,
Value: '0',
},
{
Position: 28,
Value: '0',
},
{
Position: 29,
Value: '0',
},
{
Position: 30,
Value: '0',
},
{
Position: 31,
Value: '0',
},
{
Position: 32,
Value: '0',
},
{
Position: 33,
Value: '0',
},
{
Position: 34,
Value: '0',
},
{
Position: 35,
Value: '0',
},
{
Position: 36,
Value: '0',
},
{
Position: 37,
Value: '0',
},
{
Position: 38,
Value: '0',
},
{
Position: 39,
Value: '0',
},
{
Position: 40,
Value: '0',
},
{
Position: 41,
Value: '0',
},
{
Position: 42,
Value: '0',
},
{
Position: 43,
Value: '0',
},
{
Position: 44,
Value: '0',
},
{
Position: 45,
Value: '0',
},
{
Position: 46,
Value: '0',
},
{
Position: 47,
Value: '0',
},
{
Position: 48,
Value: '0',
},
{
Position: 49,
Value: '0',
},
{
Position: 50,
Value: '0',
},
{
Position: 51,
Value: '0',
},
{
Position: 52,
Value: '0',
},
{
Position: 53,
Value: '0',
},
{
Position: 54,
Value: '0',
},
{
Position: 55,
Value: '0',
},
{
Position: 56,
Value: '0',
},
{
Position: 57,
Value: '0',
},
{
Position: 58,
Value: '0',
},
{
Position: 59,
Value: '0',
},
{
Position: 60,
Value: '0',
},
{
Position: 61,
Value: '0',
},
{
Position: 62,
Value: '0',
},
{
Position: 63,
Value: '0',
},
{
Position: 64,
Value: '0',
},
],
CheckFlags: [
{
Position: 1,
Value: '0',
},
{
Position: 2,
Value: '0',
},
{
Position: 3,
Value: '0',
},
{
Position: 4,
Value: '0',
},
{
Position: 5,
Value: '0',
},
{
Position: 6,
Value: '0',
},
{
Position: 7,
Value: '0',
},
{
Position: 8,
Value: '0',
},
{
Position: 9,
Value: '0',
},
{
Position: 10,
Value: '0',
},
{
Position: 11,
Value: '0',
},
{
Position: 12,
Value: '0',
},
{
Position: 13,
Value: '0',
},
{
Position: 14,
Value: '0',
},
{
Position: 15,
Value: '0',
},
{
Position: 16,
Value: '0',
},
{
Position: 17,
Value: '0',
},
{
Position: 18,
Value: '0',
},
{
Position: 19,
Value: '0',
},
{
Position: 20,
Value: '0',
},
{
Position: 21,
Value: '0',
},
{
Position: 22,
Value: '0',
},
{
Position: 23,
Value: '0',
},
{
Position: 24,
Value: '0',
},
{
Position: 25,
Value: '0',
},
{
Position: 26,
Value: '0',
},
{
Position: 27,
Value: '0',
},
{
Position: 28,
Value: '0',
},
{
Position: 29,
Value: '0',
},
{
Position: 30,
Value: '0',
},
{
Position: 31,
Value: '0',
},
{
Position: 32,
Value: '0',
},
{
Position: 33,
Value: '0',
},
{
Position: 34,
Value: '0',
},
{
Position: 35,
Value: '0',
},
{
Position: 36,
Value: '0',
},
{
Position: 37,
Value: '0',
},
{
Position: 38,
Value: '0',
},
{
Position: 39,
Value: '0',
},
{
Position: 40,
Value: '0',
},
{
Position: 41,
Value: '0',
},
{
Position: 42,
Value: '0',
},
{
Position: 43,
Value: '0',
},
{
Position: 44,
Value: '0',
},
{
Position: 45,
Value: '0',
},
{
Position: 46,
Value: '0',
},
{
Position: 47,
Value: '0',
},
{
Position: 48,
Value: '0',
},
{
Position: 49,
Value: '0',
},
{
Position: 50,
Value: '0',
},
{
Position: 51,
Value: '0',
},
{
Position: 52,
Value: '0',
},
{
Position: 53,
Value: '0',
},
{
Position: 54,
Value: '0',
},
{
Position: 55,
Value: '0',
},
{
Position: 56,
Value: '0',
},
{
Position: 57,
Value: '0',
},
{
Position: 58,
Value: '0',
},
{
Position: 59,
Value: '0',
},
{
Position: 60,
Value: '0',
},
{
Position: 61,
Value: '0',
},
{
Position: 62,
Value: '0',
},
{
Position: 63,
Value: '0',
},
{
Position: 64,
Value: '0',
},
],
Maker: null,
Checker: null,
ReferredTo: null,
ReferralReason: null,
UserData1: null,
UserData2: null,
UserData3: null,
UserData4: null,
UserData5: null,
AdditionalFields: [],
},
ApplicationStatusDetails: {
StatusCode: '04',
Description: 'Approved',
Canceled: false,
},
CorporateDetails: null,
CustomerDetails: {
Id: 115158,
CustomerCode: '100000024619',
IdNumber: ' ',
TypeId: 0,
PreferredLanguage: 'EN',
ExternalCustomerCode: null,
Title: ' ',
FirstName: ' ',
LastName: ' ',
DateOfBirth: null,
UserData1: '2031-09-17',
UserData2: '01',
UserData3: null,
UserData4: '682',
CustomerSegment: null,
Gender: 'U',
Married: 'U',
},
AccountDetailsList: [
{
Id: 21017,
InstitutionCode: '1100',
AccountNumber: '6899999999999999',
Currency: {
CurrCode: '682',
AlphaCode: 'SAR',
},
AccountTypeCode: '30',
ClassId: '2',
AccountStatus: '00',
VipFlag: '0',
BlockedAmount: 0,
EquivalentBlockedAmount: null,
UnclearCredit: 0,
EquivalentUnclearCredit: null,
AvailableBalance: 0,
EquivalentAvailableBalance: null,
AvailableBalanceToSpend: 0,
CreditLimit: 0,
RemainingCashLimit: null,
UserData1: 'D36407C9AE4C28D2185',
UserData2: null,
UserData3: 'D36407C9AE4C28D2185',
UserData4: null,
UserData5: 'SA2380900000752991120011',
},
],
CardDetailsList: [
{
pvv: null,
ResponseCardIdentifier: {
Id: 28595,
Pan: 'DDDDDDDDDDDDDDDDDDD',
MaskedPan: '999999_9999',
VPan: '1100000000000000',
Seqno: 0,
},
ExpiryDate: '2031-09-30',
EffectiveDate: '2025-06-02',
CardStatus: '30',
OldPlasticExpiryDate: null,
OldPlasticCardStatus: null,
EmbossingName: 'ABDALHAMID AHMAD',
Title: 'Mr.',
FirstName: 'Abdalhamid',
LastName: ' Ahmad',
Additional: false,
BatchNumber: 8849,
ServiceCode: '226',
Kinship: null,
DateOfBirth: '1999-01-07',
LastActivity: null,
LastStatusChangeDate: '2025-06-03',
ActivationDate: null,
DateLastIssued: null,
PVV: null,
UserData: '4',
UserData1: '3',
UserData2: null,
UserData3: null,
UserData4: null,
UserData5: null,
Memo: null,
CardAuthorizationParameters: null,
L10NTitle: null,
L10NFirstName: null,
L10NLastName: null,
PinStatus: '40',
OldPinStatus: '0',
CustomerIdNumber: '1089055972',
Language: 0,
},
],
},
};

View File

@ -1,2 +0,0 @@
export * from './create-application.mock';
export * from './inquire-application.mock';

View File

@ -1,728 +0,0 @@
export const INQUIRE_APPLICATION_MOCK = {
ResponseHeader: {
Version: '1.0.0',
MsgUid: 'adaa1893-9f95-48a8-b7a1-0422bcf629b4',
Source: 'ZOD',
ServiceId: 'InquireApplication',
ReqDateTime: '2023-07-18T10:34:12.553Z',
RspDateTime: '2025-06-03T11:14:54.748',
ResponseCode: '000',
ResponseType: 'Success',
ProcessingTime: 476,
EncryptionKey: null,
ResponseDescription: 'Operation Successful',
LocalizedResponseDescription: null,
CustomerSpecificResponseDescriptionList: null,
HeaderUserDataList: null,
},
InquireApplicationResponseDetails: {
InstitutionCode: '1100',
ApplicationTypeDetails: {
TypeCode: '01',
Description: 'Normal Primary',
Additional: false,
Corporate: false,
UserData: null,
},
ApplicationDetails: {
cif: null,
ApplicationNumber: '3300000000070',
ExternalApplicationNumber: '10000002',
ApplicationStatus: '04',
Organization: 0,
Product: '1101',
ApplicatonDate: '2025-05-29',
ApplicationSource: 'O',
SalesSource: null,
DeliveryMethod: 'V',
ProgramCode: null,
Campaign: null,
Plastic: null,
Design: null,
ProcessStage: '99',
ProcessStageStatus: 'S',
Score: null,
ExternalScore: null,
RequestedLimit: 0,
SuggestedLimit: null,
AssignedLimit: 0,
AllowedLimitList: null,
EligibilityCheckResult: '00',
EligibilityCheckDescription: null,
Title: 'Mr.',
FirstName: 'Abdalhamid',
SecondName: null,
ThirdName: null,
LastName: ' Ahmad',
FullName: 'Abdalhamid Ahmad',
EmbossName: 'ABDALHAMID AHMAD',
PlaceOfBirth: null,
DateOfBirth: '1999-01-07',
LocalizedDateOfBirth: '1999-01-07',
Age: 26,
Gender: 'M',
Married: 'U',
Nationality: '682',
IdType: '01',
IdNumber: '1089055972',
IdExpiryDate: '2031-09-17',
EducationLevel: null,
ProfessionCode: 0,
NumberOfDependents: 0,
EmployerName: 'N/A',
EmploymentYears: 0,
EmploymentMonths: 0,
EmployerPhoneArea: null,
EmployerPhoneNumber: null,
EmployerPhoneExtension: null,
EmployerMobile: null,
EmployerFaxArea: null,
EmployerFax: null,
EmployerCity: null,
EmployerAddress: null,
EmploymentActivity: null,
EmploymentStatus: null,
CIF: null,
BankAccountNumber: ' ',
Currency: {
CurrCode: '682',
AlphaCode: 'SAR',
},
RequestedCurrencyList: null,
CreditAccountNumber: '6823000000000019',
AccountType: '30',
OpenDate: null,
Income: 0,
AdditionalIncome: 0,
TotalIncome: 0,
CurrentBalance: 0,
AverageBalance: 0,
AssetsBalance: 0,
InsuranceBalance: 0,
DepositAmount: 0,
GuarenteeAccountNumber: null,
GuarenteeAmount: 0,
InstalmentAmount: 0,
AutoDebit: 'N',
PaymentMethod: '2',
BillingCycle: 'C1',
OldIssueDate: null,
OtherPaymentsDate: null,
MaximumDelinquency: null,
CreditBureauDecision: null,
CreditBureauUserData: null,
ECommerce: 'N',
NumberOfCards: 0,
OtherBank: null,
OtherBankDescription: null,
InsuranceProduct: null,
SocialCode: '000',
JobGrade: 0,
Flags: [
{
Position: 1,
Value: '0',
},
{
Position: 2,
Value: '0',
},
{
Position: 3,
Value: '0',
},
{
Position: 4,
Value: '0',
},
{
Position: 5,
Value: '0',
},
{
Position: 6,
Value: '0',
},
{
Position: 7,
Value: '0',
},
{
Position: 8,
Value: '0',
},
{
Position: 9,
Value: '0',
},
{
Position: 10,
Value: '0',
},
{
Position: 11,
Value: '0',
},
{
Position: 12,
Value: '0',
},
{
Position: 13,
Value: '0',
},
{
Position: 14,
Value: '0',
},
{
Position: 15,
Value: '0',
},
{
Position: 16,
Value: '0',
},
{
Position: 17,
Value: '0',
},
{
Position: 18,
Value: '0',
},
{
Position: 19,
Value: '0',
},
{
Position: 20,
Value: '0',
},
{
Position: 21,
Value: '0',
},
{
Position: 22,
Value: '0',
},
{
Position: 23,
Value: '0',
},
{
Position: 24,
Value: '0',
},
{
Position: 25,
Value: '0',
},
{
Position: 26,
Value: '0',
},
{
Position: 27,
Value: '0',
},
{
Position: 28,
Value: '0',
},
{
Position: 29,
Value: '0',
},
{
Position: 30,
Value: '0',
},
{
Position: 31,
Value: '0',
},
{
Position: 32,
Value: '0',
},
{
Position: 33,
Value: '0',
},
{
Position: 34,
Value: '0',
},
{
Position: 35,
Value: '0',
},
{
Position: 36,
Value: '0',
},
{
Position: 37,
Value: '0',
},
{
Position: 38,
Value: '0',
},
{
Position: 39,
Value: '0',
},
{
Position: 40,
Value: '0',
},
{
Position: 41,
Value: '0',
},
{
Position: 42,
Value: '0',
},
{
Position: 43,
Value: '0',
},
{
Position: 44,
Value: '0',
},
{
Position: 45,
Value: '0',
},
{
Position: 46,
Value: '0',
},
{
Position: 47,
Value: '0',
},
{
Position: 48,
Value: '0',
},
{
Position: 49,
Value: '0',
},
{
Position: 50,
Value: '0',
},
{
Position: 51,
Value: '0',
},
{
Position: 52,
Value: '0',
},
{
Position: 53,
Value: '0',
},
{
Position: 54,
Value: '0',
},
{
Position: 55,
Value: '0',
},
{
Position: 56,
Value: '0',
},
{
Position: 57,
Value: '0',
},
{
Position: 58,
Value: '0',
},
{
Position: 59,
Value: '0',
},
{
Position: 60,
Value: '0',
},
{
Position: 61,
Value: '0',
},
{
Position: 62,
Value: '0',
},
{
Position: 63,
Value: '0',
},
{
Position: 64,
Value: '0',
},
],
CheckFlags: [
{
Position: 1,
Value: '0',
},
{
Position: 2,
Value: '0',
},
{
Position: 3,
Value: '0',
},
{
Position: 4,
Value: '0',
},
{
Position: 5,
Value: '0',
},
{
Position: 6,
Value: '0',
},
{
Position: 7,
Value: '0',
},
{
Position: 8,
Value: '0',
},
{
Position: 9,
Value: '0',
},
{
Position: 10,
Value: '0',
},
{
Position: 11,
Value: '0',
},
{
Position: 12,
Value: '0',
},
{
Position: 13,
Value: '0',
},
{
Position: 14,
Value: '0',
},
{
Position: 15,
Value: '0',
},
{
Position: 16,
Value: '0',
},
{
Position: 17,
Value: '0',
},
{
Position: 18,
Value: '0',
},
{
Position: 19,
Value: '0',
},
{
Position: 20,
Value: '0',
},
{
Position: 21,
Value: '0',
},
{
Position: 22,
Value: '0',
},
{
Position: 23,
Value: '0',
},
{
Position: 24,
Value: '0',
},
{
Position: 25,
Value: '0',
},
{
Position: 26,
Value: '0',
},
{
Position: 27,
Value: '0',
},
{
Position: 28,
Value: '0',
},
{
Position: 29,
Value: '0',
},
{
Position: 30,
Value: '0',
},
{
Position: 31,
Value: '0',
},
{
Position: 32,
Value: '0',
},
{
Position: 33,
Value: '0',
},
{
Position: 34,
Value: '0',
},
{
Position: 35,
Value: '0',
},
{
Position: 36,
Value: '0',
},
{
Position: 37,
Value: '0',
},
{
Position: 38,
Value: '0',
},
{
Position: 39,
Value: '0',
},
{
Position: 40,
Value: '0',
},
{
Position: 41,
Value: '0',
},
{
Position: 42,
Value: '0',
},
{
Position: 43,
Value: '0',
},
{
Position: 44,
Value: '0',
},
{
Position: 45,
Value: '0',
},
{
Position: 46,
Value: '0',
},
{
Position: 47,
Value: '0',
},
{
Position: 48,
Value: '0',
},
{
Position: 49,
Value: '0',
},
{
Position: 50,
Value: '0',
},
{
Position: 51,
Value: '0',
},
{
Position: 52,
Value: '0',
},
{
Position: 53,
Value: '0',
},
{
Position: 54,
Value: '0',
},
{
Position: 55,
Value: '0',
},
{
Position: 56,
Value: '0',
},
{
Position: 57,
Value: '0',
},
{
Position: 58,
Value: '0',
},
{
Position: 59,
Value: '0',
},
{
Position: 60,
Value: '0',
},
{
Position: 61,
Value: '0',
},
{
Position: 62,
Value: '0',
},
{
Position: 63,
Value: '0',
},
{
Position: 64,
Value: '0',
},
],
Maker: null,
Checker: null,
ReferredTo: null,
ReferralReason: null,
UserData1: null,
UserData2: null,
UserData3: null,
UserData4: null,
UserData5: null,
AdditionalFields: [],
},
ApplicationStatusDetails: {
StatusCode: '04',
Description: 'Approved',
Canceled: false,
},
ApplicationHistoryList: null,
ApplicationAddressList: [
{
Id: 43859,
AddressLine1: '5536 abdullah Ibn al zubair ',
AddressLine2: ' Umm Alarad Dist.',
AddressLine3: null,
AddressLine4: null,
AddressLine5: null,
Directions: null,
City: 'AT TAIF',
PostalCode: null,
Province: null,
Territory: null,
State: null,
Region: null,
County: null,
Country: '682',
CountryDetails: {
IsoCode: '682',
Alpha3: 'SAU',
Alpha2: 'SA',
DefaultCurrency: {
CurrCode: '682',
AlphaCode: 'SAR',
},
Description: [
{
Language: 'EN',
Description: 'SAUDI ARABIA',
},
{
Language: 'GB',
Description: 'SAUDI ARABIA',
},
],
},
Phone1: '+966541884784',
Phone2: null,
Extension: null,
Email: 'a.ahmad@zod-alkhair.com',
Fax: null,
District: null,
PoBox: null,
OwnershipType: 'O',
UserData1: null,
UserData2: null,
AddressRole: 0,
AddressCustomValues: null,
},
],
CorporateDetails: null,
CustomerDetails: {
Id: 115129,
CustomerCode: '100000024552',
IdNumber: ' ',
TypeId: 0,
PreferredLanguage: 'EN',
ExternalCustomerCode: null,
Title: ' ',
FirstName: ' ',
LastName: ' ',
DateOfBirth: null,
UserData1: '2031-09-17',
UserData2: '01',
UserData3: null,
UserData4: '682',
CustomerSegment: null,
Gender: 'U',
Married: 'U',
},
BranchDetails: null,
CardAccountLinkageList: null,
},
};

View File

@ -1,29 +0,0 @@
import { Body, Controller, Post } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import {
AccountCardStatusChangedWebhookRequest,
AccountTransactionWebhookRequest,
CardTransactionWebhookRequest,
} from '../dtos/requests';
import { NeoLeapWebhookService } from '../services';
@Controller('neoleap-webhooks')
@ApiTags('Neoleap Webhooks')
export class NeoLeapWebhooksController {
constructor(private readonly neoleapWebhookService: NeoLeapWebhookService) {}
@Post('card-transaction')
async handleCardTransactionWebhook(@Body() body: CardTransactionWebhookRequest) {
return this.neoleapWebhookService.handleCardTransactionWebhook(body);
}
@Post('account-transaction')
async handleAccountTransactionWebhook(@Body() body: AccountTransactionWebhookRequest) {
return this.neoleapWebhookService.handleAccountTransactionWebhook(body);
}
@Post('account-card-status-changed')
async handleAccountCardStatusChangedWebhook(@Body() body: AccountCardStatusChangedWebhookRequest) {
return this.neoleapWebhookService.handleAccountCardStatusChangedWebhook(body);
}
}

View File

@ -1,62 +0,0 @@
import { Body, Controller, Post, UseGuards } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { IJwtPayload } from '~/auth/interfaces';
import { CardService } from '~/card/services';
import { AuthenticatedUser } from '~/common/decorators';
import { AccessTokenGuard } from '~/common/guards';
import { ApiDataResponse } from '~/core/decorators';
import { ResponseFactory } from '~/core/utils';
import { CustomerResponseDto } from '~/customer/dtos/response';
import { CustomerService } from '~/customer/services';
import { UpdateCardControlsRequestDto } from '../dtos/requests';
import { CreateApplicationResponse, InquireApplicationResponse } from '../dtos/response';
import { NeoLeapService } from '../services/neoleap.service';
@Controller('neotest')
@ApiTags('Neoleap Test API , for testing purposes only, will be removed in production')
@UseGuards(AccessTokenGuard)
@ApiBearerAuth()
export class NeoTestController {
constructor(
private readonly neoleapService: NeoLeapService,
private readonly customerService: CustomerService,
private readonly cardService: CardService,
private readonly configService: ConfigService,
) {}
@Post('update-kys')
@ApiDataResponse(CustomerResponseDto)
async updateKys(@AuthenticatedUser() user: IJwtPayload) {
const customer = await this.customerService.updateKyc(user.sub);
return ResponseFactory.data(new CustomerResponseDto(customer));
}
@Post('inquire-application')
@ApiDataResponse(InquireApplicationResponse)
async inquireApplication(@AuthenticatedUser() user: IJwtPayload) {
const customer = await this.customerService.findCustomerById(user.sub);
const data = await this.neoleapService.inquireApplication(customer.applicationNumber.toString());
return ResponseFactory.data(data);
}
@Post('create-application')
@ApiDataResponse(CreateApplicationResponse)
async createApplication(@AuthenticatedUser() user: IJwtPayload) {
const customer = await this.customerService.findCustomerById(user.sub);
const data = await this.neoleapService.createApplication(customer);
await this.cardService.createCard(customer.id, data);
return ResponseFactory.data(data);
}
@Post('update-card-controls')
async updateCardControls(
@AuthenticatedUser() user: IJwtPayload,
@Body() { amount, count }: UpdateCardControlsRequestDto,
) {
const card = await this.cardService.getActiveCardForCustomer(user.sub);
await this.neoleapService.updateCardControl(card.cardReference, amount, count);
return ResponseFactory.data({ message: 'Card controls updated successfully' });
}
}

View File

@ -1,20 +0,0 @@
import { ApiProperty } from '@nestjs/swagger';
import { Expose } from 'class-transformer';
import { IsString } from 'class-validator';
export class AccountCardStatusChangedWebhookRequest {
@ApiProperty()
@Expose({ name: 'InstId' })
@IsString()
instId!: string;
@ApiProperty()
@Expose({ name: 'cardId' })
@IsString()
cardId!: string;
@ApiProperty()
@Expose({ name: 'newStatus' })
@IsString()
newStatus!: string;
}

View File

@ -1,68 +0,0 @@
import { ApiProperty } from '@nestjs/swagger';
import { Expose, Type } from 'class-transformer';
import { IsNumber, IsPositive, IsString } from 'class-validator';
export class AccountTransactionWebhookRequest {
@Expose({ name: 'InstId' })
@IsString()
@ApiProperty({ name: 'InstId', example: '9000' })
instId!: string;
@Expose()
@IsString()
@ApiProperty({ example: '143761' })
transactionId!: string;
@Expose()
@IsString()
@ApiProperty({ example: '0037' })
transactionType!: string;
@Expose()
@IsString()
@ApiProperty({ example: '26' })
transactionCode!: string;
@Expose()
@IsString()
@ApiProperty({ example: '6823000000000018' })
accountNumber!: string;
@Expose()
@IsString()
@ApiProperty({ example: '6823000000000018' })
accountId!: string;
@Expose()
@Type(() => Number)
@IsNumber()
@ApiProperty({ example: '7080.15' })
otb!: number;
@Expose()
@Type(() => Number)
@IsNumber()
@IsPositive()
@ApiProperty({ example: '3050.95' })
amount!: number;
@Expose()
@IsString()
@ApiProperty({ example: 'C' })
sign!: string;
@Expose()
@IsString()
@ApiProperty({ example: '682' })
currency!: string;
@Expose()
@IsString()
@ApiProperty({ name: 'Date', example: '20241112' })
date!: string;
@Expose()
@IsString()
@ApiProperty({ name: 'Time', example: '125340' })
time!: string;
}

View File

@ -1,158 +0,0 @@
import { ApiProperty } from '@nestjs/swagger';
import { Expose, Type } from 'class-transformer';
import { IsNumber, IsString, Min, ValidateNested } from 'class-validator';
export class CardAcceptorLocationDto {
@Expose()
@IsString()
@ApiProperty()
merchantId!: string;
@Expose()
@IsString()
@ApiProperty()
merchantName!: string;
@Expose()
@IsString()
@ApiProperty()
merchantCountry!: string;
@Expose()
@IsString()
@ApiProperty()
merchantCity!: string;
@Expose()
@IsString()
@ApiProperty()
mcc!: string;
}
export class CardTransactionWebhookRequest {
@Expose({ name: 'InstId' })
@IsString()
@ApiProperty({ name: 'InstId', example: '1100' })
instId!: string;
@Expose()
@IsString()
@ApiProperty({ example: '30829' })
cardId!: string;
@Expose()
@IsString()
@ApiProperty({ example: '1234567890123456' })
transactionId!: string;
@Expose()
@IsString()
@ApiProperty({ example: '277012*****3456' })
cardMaskedNumber!: string;
@Expose()
@IsString()
@ApiProperty({ example: '1234567890123456' })
accountNumber!: string;
@Expose({ name: 'Date' })
@IsString()
@ApiProperty({ name: 'Date', example: '20241112' })
date!: string;
@Expose({ name: 'Time' })
@IsString()
@ApiProperty({ name: 'Time', example: '125250' })
time!: string;
@Expose()
@Type(() => Number)
@IsNumber()
@ApiProperty({ example: '132' })
otb!: number;
@Expose()
@IsString()
@ApiProperty({ example: '0' })
transactionCode!: string;
@Expose()
@IsString()
@ApiProperty({ example: '1' })
messageClass!: string;
@Expose({ name: 'RRN' })
@IsString()
@ApiProperty({ name: 'RRN', example: '431712003306' })
rrn!: string;
@Expose()
@IsString()
@ApiProperty({ example: '3306' })
stan!: string;
@Expose()
@ValidateNested()
@Type(() => CardAcceptorLocationDto)
@ApiProperty({ type: CardAcceptorLocationDto })
cardAcceptorLocation!: CardAcceptorLocationDto;
@Type(() => Number)
@IsNumber()
@Min(0, { message: 'amount must be zero or a positive number' })
@ApiProperty({ example: '100.5' })
transactionAmount!: number;
@IsString()
@ApiProperty({ example: '682' })
transactionCurrency!: string;
@Type(() => Number)
@IsNumber()
@Min(0, { message: 'amount must be zero or a positive number' })
@ApiProperty({ example: '100.5' })
billingAmount!: number;
@IsString()
@ApiProperty({ example: '682' })
billingCurrency!: string;
@Type(() => Number)
@IsNumber()
@Min(0, { message: 'amount must be zero or a positive number' })
@ApiProperty({ example: '100.5' })
settlementAmount!: number;
@IsString()
@ApiProperty({ example: '682' })
settlementCurrency!: string;
@Expose()
@Type(() => Number)
@IsNumber()
@Min(0, { message: 'amount must be zero or a positive number' })
@ApiProperty({ example: '20' })
fees!: number;
@Expose()
@Type(() => Number)
@IsNumber()
@Min(0, { message: 'amount must be zero or a positive number' })
@ApiProperty({ example: '4.5' })
vatOnFees!: number;
@Expose()
@IsString()
@ApiProperty({ example: '9' })
posEntryMode!: string;
@Expose()
@IsString()
@ApiProperty({ example: '036657' })
authIdResponse!: string;
@Expose({ name: 'POSCDIM' })
@IsString()
@ApiProperty({ name: 'POSCDIM', example: '9' })
posCdim!: string;
}

View File

@ -1,4 +0,0 @@
export * from './account-card-status-changed-webhook.request.dto';
export * from './account-transaction-webhook.request.dto';
export * from './card-transaction-webhook.request.dto';
export * from './update-card-controls.request.dto';

View File

@ -1,15 +0,0 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsNumber, IsOptional, IsPositive } from 'class-validator';
export class UpdateCardControlsRequestDto {
@ApiProperty()
@IsNumber()
@IsPositive()
amount!: number;
@IsNumber()
@IsPositive()
@IsOptional()
@ApiPropertyOptional()
count?: number;
}

View File

@ -1,40 +0,0 @@
import { ApiProperty } from '@nestjs/swagger';
import { Expose, Transform } from 'class-transformer';
import { InquireApplicationResponse } from './inquire-application.response';
export class CreateApplicationResponse extends InquireApplicationResponse {
@Transform(({ obj }) => obj.CardDetailsList?.[0]?.ResponseCardIdentifier.Id.toString())
@Expose()
@ApiProperty()
cardId!: string;
@Transform(({ obj }) => obj.CardDetailsList?.[0]?.ResponseCardIdentifier.VPan)
@Expose()
@ApiProperty()
vpan!: string;
@Transform(({ obj }) => obj.CardDetailsList?.[0]?.ExpiryDate)
@Expose()
@ApiProperty()
expiryDate!: string;
@Transform(({ obj }) => obj.CardDetailsList?.[0]?.CardStatus)
@Expose()
@ApiProperty()
cardStatus!: string;
@Transform(({ obj }) => obj.CardDetailsList?.[0]?.ResponseCardIdentifier?.MaskedPan?.split('_')[0])
@Expose()
@ApiProperty()
firstSixDigits!: string;
@Transform(({ obj }) => obj.CardDetailsList?.[0]?.ResponseCardIdentifier?.MaskedPan?.split('_')[1])
@Expose()
@ApiProperty()
lastFourDigits!: string;
@Transform(({ obj }) => obj.AccountDetailsList?.[0]?.Id.toString())
@Expose()
@ApiProperty()
accountId!: string;
}

View File

@ -1,3 +0,0 @@
export * from './create-application.response.dto';
export * from './inquire-application.response';
export * from './update-card-controls.response.dto';

View File

@ -1,179 +0,0 @@
import { ApiProperty } from '@nestjs/swagger';
import { Expose, Transform } from 'class-transformer';
export class InquireApplicationResponse {
@Transform(({ obj }) => obj.ApplicationDetails?.ApplicationNumber)
@Expose()
@ApiProperty()
applicationNumber!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.ExternalApplicationNumber)
@Expose()
@ApiProperty()
externalApplicationNumber!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.ApplicationStatus)
@Expose()
@ApiProperty()
applicationStatus!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.Organization)
@Expose()
@ApiProperty()
organization!: number;
@Transform(({ obj }) => obj.ApplicationDetails?.Product)
@Expose()
@ApiProperty()
product!: string;
// this typo is from neoleap, so we keep it as is
@Transform(({ obj }) => obj.ApplicationDetails?.ApplicatonDate)
@Expose()
@ApiProperty()
applicationDate!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.ApplicationSource)
@Expose()
@ApiProperty()
applicationSource!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.SalesSource)
@Expose()
@ApiProperty()
salesSource!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.DeliveryMethod)
@Expose()
@ApiProperty()
deliveryMethod!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.ProgramCode)
@Expose()
@ApiProperty()
ProgramCode!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.Plastic)
@Expose()
@ApiProperty()
plastic!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.Design)
@Expose()
@ApiProperty()
design!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.ProcessStage)
@Expose()
@ApiProperty()
processStage!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.ProcessStageStatus)
@Expose()
@ApiProperty()
processStageStatus!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.EligibilityCheckResult)
@Expose()
@ApiProperty()
eligibilityCheckResult!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.EligibilityCheckDescription)
@Expose()
@ApiProperty()
eligibilityCheckDescription!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.Title)
@Expose()
@ApiProperty()
title!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.FirstName)
@Expose()
@ApiProperty()
firstName!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.SecondName)
@Expose()
@ApiProperty()
secondName!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.ThirdName)
@Expose()
@ApiProperty()
thirdName!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.LastName)
@Expose()
@ApiProperty()
lastName!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.FullName)
@Expose()
@ApiProperty()
fullName!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.EmbossName)
@Expose()
@ApiProperty()
embossName!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.PlaceOfBirth)
@Expose()
@ApiProperty()
placeOfBirth!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.DateOfBirth)
@Expose()
@ApiProperty()
dateOfBirth!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.LocalizedDateOfBirth)
@Expose()
@ApiProperty()
localizedDateOfBirth!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.Age)
@Expose()
@ApiProperty()
age!: number;
@Transform(({ obj }) => obj.ApplicationDetails?.Gender)
@Expose()
@ApiProperty()
gender!: string;
@Transform(({ obj }) => obj.ApplicationDetails?.Married)
@Expose()
@ApiProperty()
married!: string;
@Transform(({ obj }) => obj.ApplicationDetails.Nationality)
@Expose()
@ApiProperty()
nationality!: string;
@Transform(({ obj }) => obj.ApplicationDetails.IdType)
@Expose()
@ApiProperty()
idType!: string;
@Transform(({ obj }) => obj.ApplicationDetails.IdNumber)
@Expose()
@ApiProperty()
idNumber!: string;
@Transform(({ obj }) => obj.ApplicationDetails.IdExpiryDate)
@Expose()
@ApiProperty()
idExpiryDate!: string;
@Transform(({ obj }) => obj.ApplicationStatusDetails?.Description)
@Expose()
@ApiProperty()
applicationStatusDescription!: string;
@Transform(({ obj }) => obj.ApplicationStatusDetails?.Canceled)
@Expose()
@ApiProperty()
canceled!: boolean;
}

View File

@ -1 +0,0 @@
export class UpdateCardControlsResponseDto {}

View File

@ -1,61 +0,0 @@
import { INeoleapHeaderRequest } from './neoleap-header.request.interface';
export interface ICreateApplicationRequest extends INeoleapHeaderRequest {
CreateNewApplicationRequestDetails: {
ApplicationRequestDetails: {
InstitutionCode: string;
ExternalApplicationNumber: string;
ApplicationType: string;
Product: string;
ApplicationDate: string;
BranchCode: '000';
ApplicationSource: 'O';
DeliveryMethod: 'V';
};
ApplicationProcessingDetails: {
ProcessControl: string;
RequestedLimit: number;
SuggestedLimit: number;
AssignedLimit: number;
};
ApplicationFinancialInformation: {
Currency: {
AlphaCode: 'SAR';
};
BillingCycle: 'C1';
};
ApplicationOtherInfo: object;
ApplicationCustomerDetails: {
Title: string;
FirstName: string;
LastName: string;
FullName: string;
EmbossName: string;
DateOfBirth: string;
LocalizedDateOfBirth: string;
Gender: string;
Nationality: string;
IdType: string;
IdNumber: string;
IdExpiryDate: string;
};
ApplicationAddress: {
AddressLine1: string;
AddressLine2: string;
City: string;
Region: string;
Country: string;
CountryDetails: {
DefaultCurrency: {};
Description: [];
};
Phone1: string;
Email: string;
AddressRole: number;
};
};
}

View File

@ -1,4 +0,0 @@
export * from './create-application.request.interface';
export * from './inquire-application.request.interface';
export * from './neoleap-header.request.interface';
export * from './update-card-control.request.interface';

View File

@ -1,22 +0,0 @@
import { INeoleapHeaderRequest } from './neoleap-header.request.interface';
export interface IInquireApplicationRequest extends INeoleapHeaderRequest {
InquireApplicationRequestDetails: {
ApplicationIdentifier: {
InstitutionCode: string;
ExternalApplicationNumber: string;
};
AdditionalData?: {
ReturnApplicationType?: boolean;
ReturnApplicationStatus?: boolean;
ReturnAddresses?: boolean;
ReturnBranch?: boolean;
ReturnHistory?: boolean;
ReturnCard?: boolean;
ReturnCustomer?: boolean;
ReturnAccount?: boolean;
ReturnDirectDebitDetails?: boolean;
};
HistoryTypeFilterList?: number[];
};
}

View File

@ -1,9 +0,0 @@
export interface INeoleapHeaderRequest {
RequestHeader: {
Version: string;
MsgUid: string;
Source: 'ZOD';
ServiceId: string;
ReqDateTime: Date;
};
}

View File

@ -1,77 +0,0 @@
import { INeoleapHeaderRequest } from './neoleap-header.request.interface';
export interface IUpdateCardControlRequest extends INeoleapHeaderRequest {
UpdateCardControlsRequestDetails: {
InstitutionCode: string;
CardIdentifier: {
Id: string;
InstitutionCode: string;
};
UsageTransactionLimit?: {
AmountLimit: number;
CountLimit: number;
};
DomesticCashDailyLimit?: {
AmountLimit: number;
CountLimit: number;
};
InternationalCashDailyLimit?: {
AmountLimit: number;
CountLimit: number;
};
DomesticPosDailyLimit?: {
AmountLimit: number;
CountLimit: number;
lenient?: boolean;
};
InternationalPosDailyLimit?: {
AmountLimit: number;
CountLimit: number;
};
EcommerceDailyLimit?: {
AmountLimit: number;
CountLimit: number;
};
DomesticCashMonthlyLimit?: {
AmountLimit: number;
CountLimit: number;
};
InternationalCashMonthlyLimit?: {
AmountLimit: number;
CountLimit: number;
};
DomesticPosMonthlyLimit?: {
AmountLimit: number;
CountLimit: number;
};
InternationalPosMonthlyLimit?: {
AmountLimit: number;
CountLimit: number;
};
EcommerceMonthlyLimit?: {
AmountLimit: number;
CountLimit: number;
};
DomesticCashTransactionLimit?: {
AmountLimit: number;
CountLimit: number;
};
InternationalCashTransactionLimit?: {
AmountLimit: number;
CountLimit: number;
};
DomesticPosTransactionLimit?: {
AmountLimit: number;
CountLimit: number;
};
InternationalPosTransactionLimit?: {
AmountLimit: number;
CountLimit: number;
};
EcommerceTransactionLimit?: {
AmountLimit: number;
CountLimit: number;
};
};
}

View File

@ -1,15 +0,0 @@
import { HttpModule } from '@nestjs/axios';
import { Module } from '@nestjs/common';
import { CardModule } from '~/card/card.module';
import { CustomerModule } from '~/customer/customer.module';
import { NeoLeapWebhooksController } from './controllers/neoleap-webhooks.controller';
import { NeoTestController } from './controllers/neotest.controller';
import { NeoLeapWebhookService } from './services';
import { NeoLeapService } from './services/neoleap.service';
@Module({
imports: [HttpModule, CustomerModule, CardModule],
controllers: [NeoTestController, NeoLeapWebhooksController],
providers: [NeoLeapService, NeoLeapWebhookService],
})
export class NeoLeapModule {}

View File

@ -1,2 +0,0 @@
export * from './neoleap-webook.service';
export * from './neoleap.service';

View File

@ -1,25 +0,0 @@
import { Injectable } from '@nestjs/common';
import { CardService } from '~/card/services';
import { TransactionService } from '~/card/services/transaction.service';
import {
AccountCardStatusChangedWebhookRequest,
AccountTransactionWebhookRequest,
CardTransactionWebhookRequest,
} from '../dtos/requests';
@Injectable()
export class NeoLeapWebhookService {
constructor(private readonly transactionService: TransactionService, private readonly cardService: CardService) {}
handleCardTransactionWebhook(body: CardTransactionWebhookRequest) {
return this.transactionService.createCardTransaction(body);
}
handleAccountTransactionWebhook(body: AccountTransactionWebhookRequest) {
return this.transactionService.createAccountTransaction(body);
}
handleAccountCardStatusChangedWebhook(body: AccountCardStatusChangedWebhookRequest) {
return this.cardService.updateCardStatus(body);
}
}

View File

@ -1,217 +0,0 @@
import { HttpService } from '@nestjs/axios';
import { BadRequestException, Injectable, InternalServerErrorException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ClassConstructor, plainToInstance } from 'class-transformer';
import moment from 'moment';
import { v4 as uuid } from 'uuid';
import { CountriesNumericISO } from '~/common/constants';
import { Customer } from '~/customer/entities';
import { Gender, KycStatus } from '~/customer/enums';
import { CREATE_APPLICATION_MOCK, INQUIRE_APPLICATION_MOCK } from '../__mocks__/';
import { CreateApplicationResponse, InquireApplicationResponse, UpdateCardControlsResponseDto } from '../dtos/response';
import {
ICreateApplicationRequest,
IInquireApplicationRequest,
INeoleapHeaderRequest,
IUpdateCardControlRequest,
} from '../interfaces';
@Injectable()
export class NeoLeapService {
private readonly baseUrl: string;
private readonly apiKey: string;
private readonly useGateway: boolean;
private readonly institutionCode = '1100';
useLocalCert: boolean;
constructor(private readonly httpService: HttpService, private readonly configService: ConfigService) {
this.baseUrl = this.configService.getOrThrow<string>('GATEWAY_URL');
this.apiKey = this.configService.getOrThrow<string>('GATEWAY_API_KEY');
this.useGateway = [true, 'true'].includes(this.configService.get<boolean>('USE_GATEWAY', false));
this.useLocalCert = this.configService.get<boolean>('USE_LOCAL_CERT', false);
}
async createApplication(customer: Customer) {
const responseKey = 'CreateNewApplicationResponseDetails';
if (customer.kycStatus !== KycStatus.APPROVED) {
throw new BadRequestException('CUSTOMER.KYC_NOT_APPROVED');
}
if (customer.cards.length > 0) {
throw new BadRequestException('CUSTOMER.ALREADY_HAS_CARD');
}
if (!this.useGateway) {
return plainToInstance(CreateApplicationResponse, CREATE_APPLICATION_MOCK[responseKey], {
excludeExtraneousValues: true,
});
}
const payload: ICreateApplicationRequest = {
CreateNewApplicationRequestDetails: {
ApplicationRequestDetails: {
InstitutionCode: this.institutionCode,
ExternalApplicationNumber: customer.applicationNumber.toString(),
ApplicationType: '01',
Product: '1101',
ApplicationDate: moment().format('YYYY-MM-DD'),
BranchCode: '000',
ApplicationSource: 'O',
DeliveryMethod: 'V',
},
ApplicationProcessingDetails: {
SuggestedLimit: 0,
RequestedLimit: 0,
AssignedLimit: 0,
ProcessControl: 'STND',
},
ApplicationFinancialInformation: {
Currency: {
AlphaCode: 'SAR',
},
BillingCycle: 'C1',
},
ApplicationOtherInfo: {},
ApplicationCustomerDetails: {
FirstName: customer.firstName,
LastName: customer.lastName,
FullName: customer.fullName,
DateOfBirth: moment(customer.dateOfBirth).format('YYYY-MM-DD'),
EmbossName: customer.fullName.toUpperCase(), // TODO Enter Emboss Name
IdType: '01',
IdNumber: customer.nationalId,
IdExpiryDate: moment(customer.nationalIdExpiry).format('YYYY-MM-DD'),
Title: customer.gender === Gender.MALE ? 'Mr' : 'Ms',
Gender: customer.gender === Gender.MALE ? 'M' : 'F',
LocalizedDateOfBirth: moment(customer.dateOfBirth).format('YYYY-MM-DD'),
Nationality: CountriesNumericISO[customer.countryOfResidence],
},
ApplicationAddress: {
City: customer.city,
Country: CountriesNumericISO[customer.country],
Region: customer.region,
AddressLine1: `${customer.street} ${customer.building}`,
AddressLine2: customer.neighborhood,
AddressRole: 0,
Email: customer.user.email,
Phone1: customer.user.phoneNumber,
CountryDetails: {
DefaultCurrency: {},
Description: [],
},
},
},
RequestHeader: this.prepareHeaders('CreateNewApplication'),
};
return this.sendRequestToNeoLeap<ICreateApplicationRequest, CreateApplicationResponse>(
'application/CreateNewApplication',
payload,
responseKey,
CreateApplicationResponse,
);
}
async inquireApplication(externalApplicationNumber: string) {
const responseKey = 'InquireApplicationResponseDetails';
if (!this.useGateway) {
return plainToInstance(InquireApplicationResponse, INQUIRE_APPLICATION_MOCK[responseKey], {
excludeExtraneousValues: true,
});
}
const payload = {
InquireApplicationRequestDetails: {
ApplicationIdentifier: {
InstitutionCode: this.institutionCode,
ExternalApplicationNumber: externalApplicationNumber,
},
AdditionalData: {
ReturnApplicationType: true,
ReturnApplicationStatus: true,
ReturnCustomer: true,
ReturnAddresses: true,
},
},
RequestHeader: this.prepareHeaders('InquireApplication'),
};
return this.sendRequestToNeoLeap<IInquireApplicationRequest, InquireApplicationResponse>(
'application/InquireApplication',
payload,
responseKey,
InquireApplicationResponse,
);
}
async updateCardControl(cardId: string, amount: number, count?: number) {
const responseKey = 'UpdateCardControlResponseDetails';
if (!this.useGateway) {
return;
}
const payload: IUpdateCardControlRequest = {
UpdateCardControlsRequestDetails: {
InstitutionCode: this.institutionCode,
CardIdentifier: {
InstitutionCode: this.institutionCode,
Id: cardId,
},
UsageTransactionLimit: {
AmountLimit: amount,
CountLimit: count || 10,
},
},
RequestHeader: this.prepareHeaders('UpdateCardControl'),
};
return this.sendRequestToNeoLeap<IUpdateCardControlRequest, UpdateCardControlsResponseDto>(
'cardcontrol/UpdateCardControl',
payload,
responseKey,
UpdateCardControlsResponseDto,
);
}
private prepareHeaders(serviceName: string): INeoleapHeaderRequest['RequestHeader'] {
return {
Version: '1.0.0',
MsgUid: uuid(),
Source: 'ZOD',
ServiceId: serviceName,
ReqDateTime: new Date(),
};
}
private async sendRequestToNeoLeap<T, R>(
endpoint: string,
payload: T,
responseKey: string,
responseClass: ClassConstructor<R>,
): Promise<R> {
try {
const { data } = await this.httpService.axiosRef.post(`${this.baseUrl}/${endpoint}`, payload, {
headers: {
'Content-Type': 'application/json',
Authorization: `${this.apiKey}`,
},
});
if (data.data?.ResponseHeader?.ResponseCode !== '000') {
throw new BadRequestException(
data.data.ResponseHeader.ResponseDescription || 'Unexpected Error from Service Provider',
);
}
return plainToInstance(responseClass, data.data[responseKey], {
excludeExtraneousValues: true,
});
} catch (error: any) {
if (error.status === 400) {
console.error('Error sending request to NeoLeap:', error);
throw new BadRequestException(error.response?.data?.ResponseHeader?.ResponseDescription || error.message);
}
console.error('Error sending request to NeoLeap:', error);
throw new InternalServerErrorException('Error communicating with NeoLeap service');
}
}
}

View File

@ -1,8 +1,4 @@
import { ObjectValues } from '../types';
export const UserLocale = {
ARABIC: 'ar',
ENGLISH: 'en',
} as const;
export type UserLocale = ObjectValues<typeof UserLocale>;
export enum UserLocale {
ARABIC = 'ar',
ENGLISH = 'en',
}

View File

@ -9,7 +9,7 @@ export function buildValidationPipe(config: ConfigService): ValidationPipe {
transform: true,
validateCustomDecorators: true,
stopAtFirstError: true,
forbidNonWhitelisted: false,
forbidNonWhitelisted: true,
dismissDefaultMessages: true,
enableDebugMessages: config.getOrThrow('NODE_ENV') === Environment.DEV,
exceptionFactory: i18nValidationErrorFactory,

View File

@ -58,24 +58,6 @@ export class CustomerResponseDto {
@ApiProperty()
waitingNumber!: number;
@ApiProperty()
country!: string | null;
@ApiProperty()
region!: string | null;
@ApiProperty()
city!: string | null;
@ApiProperty()
neighborhood!: string | null;
@ApiProperty()
street!: string | null;
@ApiProperty()
building!: string | null;
@ApiPropertyOptional({ type: DocumentMetaResponseDto })
profilePicture!: DocumentMetaResponseDto | null;
@ -98,12 +80,7 @@ export class CustomerResponseDto {
this.isJunior = customer.isJunior;
this.isGuardian = customer.isGuardian;
this.waitingNumber = customer.applicationNumber;
this.country = customer.country;
this.region = customer.region;
this.city = customer.city;
this.neighborhood = customer.neighborhood;
this.street = customer.street;
this.building = customer.building;
this.profilePicture = customer.profilePicture ? new DocumentMetaResponseDto(customer.profilePicture) : null;
}
}

View File

@ -5,13 +5,10 @@ import {
Entity,
Generated,
JoinColumn,
OneToMany,
OneToOne,
PrimaryColumn,
UpdateDateColumn,
} from 'typeorm';
import { Card } from '~/card/entities';
import { CountryIso } from '~/common/enums';
import { Document } from '~/document/entities';
import { Guardian } from '~/guardian/entities/guradian.entity';
import { Junior } from '~/junior/entities';
@ -48,7 +45,7 @@ export class Customer extends BaseEntity {
nationalIdExpiry!: Date;
@Column('varchar', { length: 255, nullable: true, name: 'country_of_residence' })
countryOfResidence!: CountryIso;
countryOfResidence!: string;
@Column('varchar', { length: 255, nullable: true, name: 'source_of_income' })
sourceOfIncome!: string;
@ -78,24 +75,6 @@ export class Customer extends BaseEntity {
@Column('varchar', { name: 'user_id' })
userId!: string;
@Column('varchar', { name: 'country', length: 255, nullable: true })
country!: CountryIso;
@Column('varchar', { name: 'region', length: 255, nullable: true })
region!: string;
@Column('varchar', { name: 'city', length: 255, nullable: true })
city!: string;
@Column('varchar', { name: 'neighborhood', length: 255, nullable: true })
neighborhood!: string;
@Column('varchar', { name: 'street', length: 255, nullable: true })
street!: string;
@Column('varchar', { name: 'building', length: 255, nullable: true })
building!: string;
@Column('varchar', { name: 'profile_picture_id', nullable: true })
profilePictureId!: string;
@ -127,21 +106,9 @@ export class Customer extends BaseEntity {
@JoinColumn({ name: 'civil_id_back_id' })
civilIdBack!: Document;
// relation ship between customer and card
@OneToMany(() => Card, (card) => card.customer)
cards!: Card[];
// relationship between cards and their parent customer
@OneToMany(() => Card, (card) => card.parentCustomer)
childCards!: Card[];
@CreateDateColumn({ type: 'timestamp with time zone', default: () => 'CURRENT_TIMESTAMP', name: 'created_at' })
createdAt!: Date;
@UpdateDateColumn({ type: 'timestamp with time zone', default: () => 'CURRENT_TIMESTAMP', name: 'updated_at' })
updatedAt!: Date;
get fullName(): string {
return `${this.firstName} ${this.lastName}`;
}
}

View File

@ -15,7 +15,7 @@ export class CustomerRepository {
findOne(where: FindOptionsWhere<Customer>) {
return this.customerRepository.findOne({
where,
relations: ['profilePicture', 'user', 'civilIdFront', 'civilIdBack', 'cards'],
relations: ['profilePicture', 'user', 'civilIdFront', 'civilIdBack'],
});
}

View File

@ -1,11 +1,8 @@
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
import moment from 'moment';
import { Transactional } from 'typeorm-transactional';
import { CountryIso } from '~/common/enums';
import { DocumentService, OciService } from '~/document/services';
import { GuardianService } from '~/guardian/services';
import { CreateJuniorRequestDto } from '~/junior/dtos/request';
import { User } from '~/user/entities';
import {
CreateCustomerRequestDto,
CustomerFiltersRequestDto,
@ -13,7 +10,7 @@ import {
UpdateCustomerRequestDto,
} from '../dtos/request';
import { Customer } from '../entities';
import { Gender, KycStatus } from '../enums';
import { KycStatus } from '../enums';
import { CustomerRepository } from '../repositories/customer.repository';
@Injectable()
@ -128,33 +125,6 @@ export class CustomerService {
this.logger.log(`KYC rejected for customer ${customerId}`);
}
// this function is for testing only and will be removed
@Transactional()
async updateKyc(userId: string) {
this.logger.log(`Updating KYC for customer ${userId}`);
await this.customerRepository.updateCustomer(userId, {
kycStatus: KycStatus.APPROVED,
gender: Gender.MALE,
nationalId: '1089055972',
nationalIdExpiry: moment('2031-09-17').toDate(),
countryOfResidence: CountryIso.SAUDI_ARABIA,
country: CountryIso.SAUDI_ARABIA,
region: 'Mecca',
city: 'AT Taif',
neighborhood: 'Al Faisaliah',
street: 'Al Faisaliah Street',
building: '4',
});
await User.update(userId, {
phoneNumber: this.generateSaudiPhoneNumber(),
countryCode: '+966',
});
this.logger.log(`KYC updated for customer ${userId}`);
return this.findCustomerById(userId);
}
private async validateProfilePictureForCustomer(userId: string, profilePictureId?: string) {
if (!profilePictureId) return;
@ -228,19 +198,4 @@ export class CustomerService {
customer.civilIdBack.url = civilIdBackUrl;
return customer;
}
async findAnyCustomer() {
return this.customerRepository.findOne({ isGuardian: true });
}
// TO BE REMOVED: This function is for testing only and will be removed
private generateSaudiPhoneNumber(): string {
// Saudi mobile numbers are 9 digits, always starting with '5'
const firstDigit = '5';
let rest = '';
for (let i = 0; i < 8; i++) {
rest += Math.floor(Math.random() * 10);
}
return `${firstDigit}${rest}`;
}
}

View File

@ -1,22 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateDocumentEntity1732434281561 implements MigrationInterface {
name = 'CreateDocumentEntity1732434281561';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "documents" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"name" character varying(255) NOT NULL,
"extension" character varying(255) NOT NULL,
"document_type" character varying(255) NOT NULL,
"updated_at" TIMESTAMP NOT NULL DEFAULT now(),
"created_at" TIMESTAMP NOT NULL DEFAULT now(),
CONSTRAINT "PK_ac51aa5181ee2036f5ca482857c" PRIMARY KEY ("id"))`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "documents"`);
}
}

View File

@ -1,29 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateUserEntity1733206728721 implements MigrationInterface {
name = 'CreateUserEntity1733206728721';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "users" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"email" character varying(255),
"phone_number" character varying(255) NOT NULL,
"country_code" character varying(10) NOT NULL,
"password" character varying(255),
"salt" character varying(255),
"google_id" character varying(255),
"apple_id" character varying(255),
"is_profile_completed" boolean NOT NULL DEFAULT false,
"roles" text array,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY ("id"))`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "users" DROP CONSTRAINT "FK_02ec15de199e79a0c46869895f4"`);
await queryRunner.query(`DROP TABLE "users"`);
}
}

View File

@ -1,30 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateOtpEntity1733209041336 implements MigrationInterface {
name = 'CreateOtpEntity1733209041336';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE TABLE "otp"
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"value" character varying(255) NOT NULL,
"scope" character varying(255) NOT NULL,
"otp_type" character varying(255) NOT NULL,
"expires_at" TIMESTAMP WITH TIME ZONE NOT NULL,
"user_id" uuid NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT "PK_32556d9d7b22031d7d0e1fd6723" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_6427c192ef35355ebac18fb683" ON "otp" ("scope") `);
await queryRunner.query(`CREATE INDEX "IDX_258d028d322ea3b856bf9f12f2" ON "otp" ("user_id") `);
await queryRunner.query(
`ALTER TABLE "otp" ADD CONSTRAINT "FK_258d028d322ea3b856bf9f12f25" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "otp" DROP CONSTRAINT "FK_258d028d322ea3b856bf9f12f25"`);
await queryRunner.query(`DROP INDEX "public"."IDX_258d028d322ea3b856bf9f12f2"`);
await queryRunner.query(`DROP INDEX "public"."IDX_6427c192ef35355ebac18fb683"`);
await queryRunner.query(`DROP TABLE "otp"`);
}
}

View File

@ -1,46 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateCustomerEntity1733298524771 implements MigrationInterface {
name = 'CreateCustomerEntity1733298524771';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "customers"
("id" uuid NOT NULL,
"customer_status" character varying(255) NOT NULL DEFAULT 'PENDING',
"rejection_reason" text,
"first_name" character varying(255),
"last_name" character varying(255),
"date_of_birth" date,
"national_id" character varying(255),
"national_id_expiry" date,
"country_of_residence" character varying(255),
"source_of_income" character varying(255),
"profession" character varying(255),
"profession_type" character varying(255),
"is_pep" boolean NOT NULL DEFAULT false,
"gender" character varying(255),
"is_junior" boolean NOT NULL DEFAULT false,
"is_guardian" boolean NOT NULL DEFAULT false,
"profile_picture_id" uuid,
"user_id" uuid NOT NULL,
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT "REL_5d1f609371a285123294fddcf3" UNIQUE ("user_id"),
CONSTRAINT "REL_02ec15de199e79a0c46869895f" UNIQUE ("profile_picture_id"),
CONSTRAINT "PK_a7a13f4cacb744524e44dfdad32" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "customers" ADD CONSTRAINT "FK_e7574892da11dd01de5cfc46499" FOREIGN KEY ("profile_picture_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "customers" ADD CONSTRAINT "FK_11d81cd7be87b6f8865b0cf7661" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "FK_11d81cd7be87b6f8865b0cf7661"`);
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "FK_e7574892da11dd01de5cfc46499"`);
await queryRunner.query(`DROP TABLE "customers"`);
}
}

View File

@ -1,27 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateDeviceEntity1733314952318 implements MigrationInterface {
name = 'CreateDeviceEntity1733314952318';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "devices"
("deviceId" character varying(255) NOT NULL,
"user_id" uuid NOT NULL,
"device_name" character varying,
"public_key" character varying,
"last_access_on" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT "PK_666c9b59efda8ca85b29157152c" PRIMARY KEY ("deviceId"))`,
);
await queryRunner.query(
`ALTER TABLE "devices" ADD CONSTRAINT "FK_5e9bee993b4ce35c3606cda194c" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "devices" DROP CONSTRAINT "FK_5e9bee993b4ce35c3606cda194c"`);
await queryRunner.query(`DROP TABLE "devices"`);
}
}

View File

@ -1,37 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateJuniorEntity1733731507261 implements MigrationInterface {
name = 'CreateJuniorEntity1733731507261';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "juniors"
("id" uuid NOT NULL,
"relationship" character varying(255) NOT NULL,
"civil_id_front_id" uuid NOT NULL,
"civil_id_back_id" uuid NOT NULL,
"customer_id" uuid NOT NULL,
"created_at" TIMESTAMP NOT NULL DEFAULT now(),
"updated_at" TIMESTAMP NOT NULL DEFAULT now(),
CONSTRAINT "REL_6a72e1a5758643737cc563b96c" UNIQUE ("civil_id_front_id"),
CONSTRAINT "REL_4662c4433223c01fe69fc1382f" UNIQUE ("civil_id_back_id"),
CONSTRAINT "REL_dfbf64ede1ff823a489902448a" UNIQUE ("customer_id"), CONSTRAINT "PK_2d273092322c1f8bf26296fa608" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "juniors" ADD CONSTRAINT "FK_6a72e1a5758643737cc563b96c7" FOREIGN KEY ("civil_id_front_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "juniors" ADD CONSTRAINT "FK_4662c4433223c01fe69fc1382f5" FOREIGN KEY ("civil_id_back_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "juniors" ADD CONSTRAINT "FK_dfbf64ede1ff823a489902448a2" FOREIGN KEY ("customer_id") REFERENCES "customers"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "juniors" DROP CONSTRAINT "FK_dfbf64ede1ff823a489902448a2"`);
await queryRunner.query(`ALTER TABLE "juniors" DROP CONSTRAINT "FK_4662c4433223c01fe69fc1382f5"`);
await queryRunner.query(`ALTER TABLE "juniors" DROP CONSTRAINT "FK_6a72e1a5758643737cc563b96c7"`);
await queryRunner.query(`DROP TABLE "juniors"`);
}
}

View File

@ -1,30 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateGuardianEntity1733732021622 implements MigrationInterface {
name = 'CreateGuardianEntity1733732021622';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "guardians"
("id" uuid NOT NULL,
"customer_id" uuid NOT NULL,
"created_at" TIMESTAMP NOT NULL DEFAULT now(),
"updated_at" TIMESTAMP NOT NULL DEFAULT now(),
CONSTRAINT "REL_6c46a1b6af00e6457cb1b70f7e" UNIQUE ("customer_id"), CONSTRAINT "PK_3dcf02f3dc96a2c017106f280be" PRIMARY KEY ("id"))`,
);
await queryRunner.query(`ALTER TABLE "juniors" ADD "guardian_id" uuid NOT NULL`);
await queryRunner.query(
`ALTER TABLE "juniors" ADD CONSTRAINT "FK_0b11aa56264184690e2220da4a0" FOREIGN KEY ("guardian_id") REFERENCES "guardians"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "guardians" ADD CONSTRAINT "FK_6c46a1b6af00e6457cb1b70f7e7" FOREIGN KEY ("customer_id") REFERENCES "customers"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "guardians" DROP CONSTRAINT "FK_6c46a1b6af00e6457cb1b70f7e7"`);
await queryRunner.query(`ALTER TABLE "juniors" DROP CONSTRAINT "FK_0b11aa56264184690e2220da4a0"`);
await queryRunner.query(`ALTER TABLE "juniors" DROP COLUMN "guardian_id"`);
await queryRunner.query(`DROP TABLE "guardians"`);
}
}

View File

@ -1,28 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateThemeEntity1733748083604 implements MigrationInterface {
name = 'CreateThemeEntity1733748083604';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "themes"
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"color" character varying(255) NOT NULL,
"avatar_id" uuid, "junior_id" uuid NOT NULL,
CONSTRAINT "REL_73fcb76399a308cdd2d431a8f2" UNIQUE ("junior_id"),
CONSTRAINT "PK_ddbeaab913c18682e5c88155592" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "themes" ADD CONSTRAINT "FK_169b672cc28cc757e1f4464864d" FOREIGN KEY ("avatar_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "themes" ADD CONSTRAINT "FK_73fcb76399a308cdd2d431a8f2e" FOREIGN KEY ("junior_id") REFERENCES "juniors"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "themes" DROP CONSTRAINT "FK_73fcb76399a308cdd2d431a8f2e"`);
await queryRunner.query(`ALTER TABLE "themes" DROP CONSTRAINT "FK_169b672cc28cc757e1f4464864d"`);
await queryRunner.query(`DROP TABLE "themes"`);
}
}

View File

@ -0,0 +1,249 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class InitialMigration1733750228289 implements MigrationInterface {
name = 'InitialMigration1733750228289';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "gift_replies" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "status" character varying NOT NULL DEFAULT 'PENDING', "color" character varying NOT NULL, "gift_id" uuid NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "REL_8292da97f1ceb9a806b8bc812f" UNIQUE ("gift_id"), CONSTRAINT "PK_ec6567bb5ab318bb292fa6599a2" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "gift" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying(255) NOT NULL, "description" text NOT NULL, "color" character varying NOT NULL, "amount" numeric(10,3) NOT NULL, "status" character varying NOT NULL DEFAULT 'AVAILABLE', "redeemed_at" TIMESTAMP WITH TIME ZONE, "giver_id" uuid NOT NULL, "image_id" uuid NOT NULL, "recipient_id" uuid NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_f91217caddc01a085837ebe0606" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "gift_redemptions" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "gift_id" uuid NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_6ad7ac76169c3a224ce4a3afff4" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "saving_goals" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying(255) NOT NULL, "description" character varying(255), "due_date" TIMESTAMP WITH TIME ZONE NOT NULL, "target_amount" numeric(12,3) NOT NULL, "current_amount" numeric(12,3) NOT NULL DEFAULT '0', "image_id" uuid, "junior_id" uuid NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_5193f14c1c3a38e6657a159795e" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "categories" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying(255) NOT NULL, "type" character varying(255) NOT NULL, "junior_id" uuid, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_24dbc6126a28ff948da33e97d3b" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "task_submissions" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "status" character varying NOT NULL, "submitted_at" TIMESTAMP WITH TIME ZONE NOT NULL, "task_id" uuid NOT NULL, "proof_of_completion_id" uuid, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "REL_d6cfaee118a0300d652e28ee16" UNIQUE ("task_id"), CONSTRAINT "PK_8d19d6b5dd776e373113de50018" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "tasks" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "title" character varying(255) NOT NULL, "description" character varying(255) NOT NULL, "reward_amount" numeric(12,3) NOT NULL, "image_id" uuid NOT NULL, "task_frequency" character varying NOT NULL, "start_date" TIMESTAMP WITH TIME ZONE NOT NULL, "due_date" TIMESTAMP WITH TIME ZONE NOT NULL, "is_proof_required" boolean NOT NULL, "assigned_to_id" uuid NOT NULL, "assigned_by_id" uuid NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_8d12ff38fcc62aaba2cab748772" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "notifications" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "title" character varying NOT NULL, "message" character varying NOT NULL, "recipient" character varying, "scope" character varying NOT NULL, "status" character varying, "channel" character varying NOT NULL, "user_id" uuid, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_6a72c3c0f683f6462415e653c3a" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "otp" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "value" character varying(255) NOT NULL, "scope" character varying(255) NOT NULL, "otp_type" character varying(255) NOT NULL, "expires_at" TIMESTAMP WITH TIME ZONE NOT NULL, "user_id" uuid NOT NULL, "is_used" boolean NOT NULL DEFAULT false, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_32556d9d7b22031d7d0e1fd6723" PRIMARY KEY ("id"))`,
);
await queryRunner.query(`CREATE INDEX "IDX_6427c192ef35355ebac18fb683" ON "otp" ("scope") `);
await queryRunner.query(`CREATE INDEX "IDX_258d028d322ea3b856bf9f12f2" ON "otp" ("user_id") `);
await queryRunner.query(
`CREATE TABLE "user_registration_tokens" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "token" character varying(255) NOT NULL, "user_type" character varying(255) NOT NULL, "is_used" boolean NOT NULL DEFAULT false, "expiry_date" TIMESTAMP NOT NULL, "user_id" uuid, "junior_id" uuid, "updated_at" TIMESTAMP NOT NULL DEFAULT now(), "created_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UQ_5881556d05b46fc7bd9e3bba935" UNIQUE ("token"), CONSTRAINT "PK_135a2d86443071ff0ba1c14135c" PRIMARY KEY ("id"))`,
);
await queryRunner.query(`CREATE INDEX "IDX_5881556d05b46fc7bd9e3bba93" ON "user_registration_tokens" ("token") `);
await queryRunner.query(
`CREATE TABLE "users" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "email" character varying(255), "phone_number" character varying(255), "country_code" character varying(10), "password" character varying(255), "salt" character varying(255), "google_id" character varying(255), "apple_id" character varying(255), "is_phone_verified" boolean NOT NULL DEFAULT false, "is_email_verified" boolean NOT NULL DEFAULT false, "is_profile_completed" boolean NOT NULL DEFAULT false, "is_email_enabled" boolean NOT NULL DEFAULT false, "is_push_enabled" boolean NOT NULL DEFAULT false, "is_sms_enabled" boolean NOT NULL DEFAULT false, "roles" text array, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "devices" ("deviceId" character varying(255) NOT NULL, "user_id" uuid NOT NULL, "device_name" character varying, "public_key" character varying, "fcm_token" character varying, "last_access_on" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_666c9b59efda8ca85b29157152c" PRIMARY KEY ("deviceId"))`,
);
await queryRunner.query(
`CREATE TABLE "documents" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying(255) NOT NULL, "extension" character varying(255) NOT NULL, "document_type" character varying(255) NOT NULL, "created_by_id" uuid, "updated_at" TIMESTAMP NOT NULL DEFAULT now(), "created_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_ac51aa5181ee2036f5ca482857c" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "customers" ("id" uuid NOT NULL, "customer_status" character varying(255) NOT NULL DEFAULT 'PENDING', "kyc_status" character varying(255) NOT NULL DEFAULT 'PENDING', "rejection_reason" text, "first_name" character varying(255), "last_name" character varying(255), "date_of_birth" date, "national_id" character varying(255), "national_id_expiry" date, "country_of_residence" character varying(255), "source_of_income" character varying(255), "profession" character varying(255), "profession_type" character varying(255), "is_pep" boolean NOT NULL DEFAULT false, "gender" character varying(255), "is_junior" boolean NOT NULL DEFAULT false, "is_guardian" boolean NOT NULL DEFAULT false, "application_number" SERIAL NOT NULL, "user_id" uuid NOT NULL, "profile_picture_id" uuid, "civil_id_front_id" uuid, "civil_id_back_id" uuid, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "REL_e7574892da11dd01de5cfc4649" UNIQUE ("profile_picture_id"), CONSTRAINT "REL_11d81cd7be87b6f8865b0cf766" UNIQUE ("user_id"), CONSTRAINT "REL_d5f99c497892ce31598ba19a72" UNIQUE ("civil_id_front_id"), CONSTRAINT "REL_2191662d124c56dd968ba01bf1" UNIQUE ("civil_id_back_id"), CONSTRAINT "PK_133ec679a801fab5e070f73d3ea" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "money_requests" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "requested_amount" numeric(10,3) NOT NULL, "message" character varying NOT NULL, "frequency" character varying NOT NULL DEFAULT 'ONE_TIME', "status" character varying NOT NULL DEFAULT 'PENDING', "reviewed_at" TIMESTAMP WITH TIME ZONE, "start_date" TIMESTAMP WITH TIME ZONE, "end_date" TIMESTAMP WITH TIME ZONE, "requester_id" uuid NOT NULL, "reviewer_id" uuid NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_28cff23e9fb06cd5dbf73cd53e7" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "themes" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "color" character varying(255) NOT NULL, "avatar_id" uuid, "junior_id" uuid NOT NULL, CONSTRAINT "REL_73fcb76399a308cdd2d431a8f2" UNIQUE ("junior_id"), CONSTRAINT "PK_ddbeaab913c18682e5c88155592" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "juniors" ("id" uuid NOT NULL, "relationship" character varying(255) NOT NULL, "customer_id" uuid NOT NULL, "guardian_id" uuid NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "REL_dfbf64ede1ff823a489902448a" UNIQUE ("customer_id"), CONSTRAINT "PK_2d273092322c1f8bf26296fa608" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "allowances" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying(255) NOT NULL, "amount" numeric(10,2) NOT NULL, "frequency" character varying(255) NOT NULL, "type" character varying(255) NOT NULL, "start_date" TIMESTAMP WITH TIME ZONE NOT NULL, "end_date" TIMESTAMP WITH TIME ZONE, "number_of_transactions" integer, "guardian_id" uuid NOT NULL, "junior_id" uuid NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deleted_at" TIMESTAMP WITH TIME ZONE, CONSTRAINT "PK_3731e781e7c4e932ba4d4213ac1" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "allowance_change_requests" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "reason" text NOT NULL, "amount" numeric(10,2) NOT NULL, "status" character varying(255) NOT NULL DEFAULT 'PENDING', "allowance_id" uuid NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "PK_664715670e1e72c64ce65a078de" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "guardians" ("id" uuid NOT NULL, "customer_id" uuid NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "REL_6c46a1b6af00e6457cb1b70f7e" UNIQUE ("customer_id"), CONSTRAINT "PK_3dcf02f3dc96a2c017106f280be" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "saving_goals_categories" ("saving_goal_id" uuid NOT NULL, "category_id" uuid NOT NULL, CONSTRAINT "PK_a49d4f57d06d0a36a8385b6c28f" PRIMARY KEY ("saving_goal_id", "category_id"))`,
);
await queryRunner.query(
`CREATE INDEX "IDX_d421de423f21c01672ea7c2e98" ON "saving_goals_categories" ("saving_goal_id") `,
);
await queryRunner.query(
`CREATE INDEX "IDX_b0a721a8f7f5b6fe93f3603ebc" ON "saving_goals_categories" ("category_id") `,
);
await queryRunner.query(
`ALTER TABLE "gift_replies" ADD CONSTRAINT "FK_8292da97f1ceb9a806b8bc812f2" FOREIGN KEY ("gift_id") REFERENCES "gift"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "gift" ADD CONSTRAINT "FK_0d317b68508819308455db9b9be" FOREIGN KEY ("giver_id") REFERENCES "guardians"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "gift" ADD CONSTRAINT "FK_4a46b5734fb573dc956904c18d0" FOREIGN KEY ("recipient_id") REFERENCES "juniors"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "gift" ADD CONSTRAINT "FK_83bb54c127d0e6ee487b90e2996" FOREIGN KEY ("image_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "gift_redemptions" ADD CONSTRAINT "FK_243c4349f0c45ce5385ac316aaa" FOREIGN KEY ("gift_id") REFERENCES "gift"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "saving_goals" ADD CONSTRAINT "FK_dad35932272342c1a247a2cee1c" FOREIGN KEY ("image_id") REFERENCES "documents"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "saving_goals" ADD CONSTRAINT "FK_f494ba0a361b2f9cbe5f6e56e38" FOREIGN KEY ("junior_id") REFERENCES "juniors"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "categories" ADD CONSTRAINT "FK_4f98e0b010d5e90cae0a2007748" FOREIGN KEY ("junior_id") REFERENCES "juniors"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "task_submissions" ADD CONSTRAINT "FK_d6cfaee118a0300d652e28ee166" FOREIGN KEY ("task_id") REFERENCES "tasks"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "task_submissions" ADD CONSTRAINT "FK_87876dfe440de7aafce216e9f58" FOREIGN KEY ("proof_of_completion_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "tasks" ADD CONSTRAINT "FK_f1f00d41b1e95d0bbda2710a62b" FOREIGN KEY ("image_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "tasks" ADD CONSTRAINT "FK_9430f12c5a1604833f64595a57f" FOREIGN KEY ("assigned_to_id") REFERENCES "juniors"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "tasks" ADD CONSTRAINT "FK_3e08a7ca125a175cf899b09f71a" FOREIGN KEY ("assigned_by_id") REFERENCES "guardians"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "notifications" ADD CONSTRAINT "FK_9a8a82462cab47c73d25f49261f" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "otp" ADD CONSTRAINT "FK_258d028d322ea3b856bf9f12f25" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "user_registration_tokens" ADD CONSTRAINT "FK_57cbbe079a7945d6ed1df114825" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "user_registration_tokens" ADD CONSTRAINT "FK_e41bec3ed6e549cbf90f57cc344" FOREIGN KEY ("junior_id") REFERENCES "juniors"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "devices" ADD CONSTRAINT "FK_5e9bee993b4ce35c3606cda194c" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "documents" ADD CONSTRAINT "FK_7f46f4f77acde1dcedba64cb220" FOREIGN KEY ("created_by_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "customers" ADD CONSTRAINT "FK_e7574892da11dd01de5cfc46499" FOREIGN KEY ("profile_picture_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "customers" ADD CONSTRAINT "FK_11d81cd7be87b6f8865b0cf7661" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "customers" ADD CONSTRAINT "FK_d5f99c497892ce31598ba19a72c" FOREIGN KEY ("civil_id_front_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "customers" ADD CONSTRAINT "FK_2191662d124c56dd968ba01bf18" FOREIGN KEY ("civil_id_back_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "money_requests" ADD CONSTRAINT "FK_5cce02836c6033b6e2412995e34" FOREIGN KEY ("requester_id") REFERENCES "juniors"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "money_requests" ADD CONSTRAINT "FK_75ba0766db9a7bf03126facf31c" FOREIGN KEY ("reviewer_id") REFERENCES "guardians"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "themes" ADD CONSTRAINT "FK_169b672cc28cc757e1f4464864d" FOREIGN KEY ("avatar_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "themes" ADD CONSTRAINT "FK_73fcb76399a308cdd2d431a8f2e" FOREIGN KEY ("junior_id") REFERENCES "juniors"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "juniors" ADD CONSTRAINT "FK_dfbf64ede1ff823a489902448a2" FOREIGN KEY ("customer_id") REFERENCES "customers"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "juniors" ADD CONSTRAINT "FK_0b11aa56264184690e2220da4a0" FOREIGN KEY ("guardian_id") REFERENCES "guardians"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "allowances" ADD CONSTRAINT "FK_80b144a74e630ed63311e97427b" FOREIGN KEY ("guardian_id") REFERENCES "guardians"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "allowances" ADD CONSTRAINT "FK_61e6e612f6d4644f8910d453cc9" FOREIGN KEY ("junior_id") REFERENCES "juniors"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "allowance_change_requests" ADD CONSTRAINT "FK_4ea6382927f50cb93873fae16d2" FOREIGN KEY ("allowance_id") REFERENCES "allowances"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "guardians" ADD CONSTRAINT "FK_6c46a1b6af00e6457cb1b70f7e7" FOREIGN KEY ("customer_id") REFERENCES "customers"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "saving_goals_categories" ADD CONSTRAINT "FK_d421de423f21c01672ea7c2e98f" FOREIGN KEY ("saving_goal_id") REFERENCES "saving_goals"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
await queryRunner.query(
`ALTER TABLE "saving_goals_categories" ADD CONSTRAINT "FK_b0a721a8f7f5b6fe93f3603ebc8" FOREIGN KEY ("category_id") REFERENCES "categories"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "saving_goals_categories" DROP CONSTRAINT "FK_b0a721a8f7f5b6fe93f3603ebc8"`);
await queryRunner.query(`ALTER TABLE "saving_goals_categories" DROP CONSTRAINT "FK_d421de423f21c01672ea7c2e98f"`);
await queryRunner.query(`ALTER TABLE "guardians" DROP CONSTRAINT "FK_6c46a1b6af00e6457cb1b70f7e7"`);
await queryRunner.query(`ALTER TABLE "allowance_change_requests" DROP CONSTRAINT "FK_4ea6382927f50cb93873fae16d2"`);
await queryRunner.query(`ALTER TABLE "allowances" DROP CONSTRAINT "FK_61e6e612f6d4644f8910d453cc9"`);
await queryRunner.query(`ALTER TABLE "allowances" DROP CONSTRAINT "FK_80b144a74e630ed63311e97427b"`);
await queryRunner.query(`ALTER TABLE "juniors" DROP CONSTRAINT "FK_0b11aa56264184690e2220da4a0"`);
await queryRunner.query(`ALTER TABLE "juniors" DROP CONSTRAINT "FK_dfbf64ede1ff823a489902448a2"`);
await queryRunner.query(`ALTER TABLE "themes" DROP CONSTRAINT "FK_73fcb76399a308cdd2d431a8f2e"`);
await queryRunner.query(`ALTER TABLE "themes" DROP CONSTRAINT "FK_169b672cc28cc757e1f4464864d"`);
await queryRunner.query(`ALTER TABLE "money_requests" DROP CONSTRAINT "FK_75ba0766db9a7bf03126facf31c"`);
await queryRunner.query(`ALTER TABLE "money_requests" DROP CONSTRAINT "FK_5cce02836c6033b6e2412995e34"`);
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "FK_2191662d124c56dd968ba01bf18"`);
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "FK_d5f99c497892ce31598ba19a72c"`);
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "FK_11d81cd7be87b6f8865b0cf7661"`);
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "FK_e7574892da11dd01de5cfc46499"`);
await queryRunner.query(`ALTER TABLE "documents" DROP CONSTRAINT "FK_7f46f4f77acde1dcedba64cb220"`);
await queryRunner.query(`ALTER TABLE "devices" DROP CONSTRAINT "FK_5e9bee993b4ce35c3606cda194c"`);
await queryRunner.query(`ALTER TABLE "user_registration_tokens" DROP CONSTRAINT "FK_e41bec3ed6e549cbf90f57cc344"`);
await queryRunner.query(`ALTER TABLE "user_registration_tokens" DROP CONSTRAINT "FK_57cbbe079a7945d6ed1df114825"`);
await queryRunner.query(`ALTER TABLE "otp" DROP CONSTRAINT "FK_258d028d322ea3b856bf9f12f25"`);
await queryRunner.query(`ALTER TABLE "notifications" DROP CONSTRAINT "FK_9a8a82462cab47c73d25f49261f"`);
await queryRunner.query(`ALTER TABLE "tasks" DROP CONSTRAINT "FK_3e08a7ca125a175cf899b09f71a"`);
await queryRunner.query(`ALTER TABLE "tasks" DROP CONSTRAINT "FK_9430f12c5a1604833f64595a57f"`);
await queryRunner.query(`ALTER TABLE "tasks" DROP CONSTRAINT "FK_f1f00d41b1e95d0bbda2710a62b"`);
await queryRunner.query(`ALTER TABLE "task_submissions" DROP CONSTRAINT "FK_87876dfe440de7aafce216e9f58"`);
await queryRunner.query(`ALTER TABLE "task_submissions" DROP CONSTRAINT "FK_d6cfaee118a0300d652e28ee166"`);
await queryRunner.query(`ALTER TABLE "categories" DROP CONSTRAINT "FK_4f98e0b010d5e90cae0a2007748"`);
await queryRunner.query(`ALTER TABLE "saving_goals" DROP CONSTRAINT "FK_f494ba0a361b2f9cbe5f6e56e38"`);
await queryRunner.query(`ALTER TABLE "saving_goals" DROP CONSTRAINT "FK_dad35932272342c1a247a2cee1c"`);
await queryRunner.query(`ALTER TABLE "gift_redemptions" DROP CONSTRAINT "FK_243c4349f0c45ce5385ac316aaa"`);
await queryRunner.query(`ALTER TABLE "gift" DROP CONSTRAINT "FK_83bb54c127d0e6ee487b90e2996"`);
await queryRunner.query(`ALTER TABLE "gift" DROP CONSTRAINT "FK_4a46b5734fb573dc956904c18d0"`);
await queryRunner.query(`ALTER TABLE "gift" DROP CONSTRAINT "FK_0d317b68508819308455db9b9be"`);
await queryRunner.query(`ALTER TABLE "gift_replies" DROP CONSTRAINT "FK_8292da97f1ceb9a806b8bc812f2"`);
await queryRunner.query(`DROP INDEX "public"."IDX_b0a721a8f7f5b6fe93f3603ebc"`);
await queryRunner.query(`DROP INDEX "public"."IDX_d421de423f21c01672ea7c2e98"`);
await queryRunner.query(`DROP TABLE "saving_goals_categories"`);
await queryRunner.query(`DROP TABLE "guardians"`);
await queryRunner.query(`DROP TABLE "allowance_change_requests"`);
await queryRunner.query(`DROP TABLE "allowances"`);
await queryRunner.query(`DROP TABLE "juniors"`);
await queryRunner.query(`DROP TABLE "themes"`);
await queryRunner.query(`DROP TABLE "money_requests"`);
await queryRunner.query(`DROP TABLE "customers"`);
await queryRunner.query(`DROP TABLE "documents"`);
await queryRunner.query(`DROP TABLE "devices"`);
await queryRunner.query(`DROP TABLE "users"`);
await queryRunner.query(`DROP INDEX "public"."IDX_5881556d05b46fc7bd9e3bba93"`);
await queryRunner.query(`DROP TABLE "user_registration_tokens"`);
await queryRunner.query(`DROP INDEX "public"."IDX_258d028d322ea3b856bf9f12f2"`);
await queryRunner.query(`DROP INDEX "public"."IDX_6427c192ef35355ebac18fb683"`);
await queryRunner.query(`DROP TABLE "otp"`);
await queryRunner.query(`DROP TABLE "notifications"`);
await queryRunner.query(`DROP TABLE "tasks"`);
await queryRunner.query(`DROP TABLE "task_submissions"`);
await queryRunner.query(`DROP TABLE "categories"`);
await queryRunner.query(`DROP TABLE "saving_goals"`);
await queryRunner.query(`DROP TABLE "gift_redemptions"`);
await queryRunner.query(`DROP TABLE "gift"`);
await queryRunner.query(`DROP TABLE "gift_replies"`);
}
}

View File

@ -1,39 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateTaskEntities1733904556416 implements MigrationInterface {
name = 'CreateTaskEntities1733904556416';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "task_submissions" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "status" character varying NOT NULL, "submitted_at" TIMESTAMP WITH TIME ZONE NOT NULL, "task_id" uuid NOT NULL, "proof_of_completion_id" uuid, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "REL_d6cfaee118a0300d652e28ee16" UNIQUE ("task_id"), CONSTRAINT "PK_8d19d6b5dd776e373113de50018" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "tasks" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "title" character varying(255) NOT NULL, "description" character varying(255) NOT NULL, "reward_amount" numeric(12,3) NOT NULL, "image_id" uuid NOT NULL, "task_frequency" character varying NOT NULL, "start_date" date NOT NULL, "due_date" date NOT NULL, "is_proof_required" boolean NOT NULL, "assigned_to_id" uuid NOT NULL, "assigned_by_id" uuid NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "PK_8d12ff38fcc62aaba2cab748772" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "task_submissions" ADD CONSTRAINT "FK_d6cfaee118a0300d652e28ee166" FOREIGN KEY ("task_id") REFERENCES "tasks"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "task_submissions" ADD CONSTRAINT "FK_87876dfe440de7aafce216e9f58" FOREIGN KEY ("proof_of_completion_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "tasks" ADD CONSTRAINT "FK_f1f00d41b1e95d0bbda2710a62b" FOREIGN KEY ("image_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "tasks" ADD CONSTRAINT "FK_9430f12c5a1604833f64595a57f" FOREIGN KEY ("assigned_to_id") REFERENCES "juniors"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "tasks" ADD CONSTRAINT "FK_3e08a7ca125a175cf899b09f71a" FOREIGN KEY ("assigned_by_id") REFERENCES "guardians"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "tasks" DROP CONSTRAINT "FK_3e08a7ca125a175cf899b09f71a"`);
await queryRunner.query(`ALTER TABLE "tasks" DROP CONSTRAINT "FK_9430f12c5a1604833f64595a57f"`);
await queryRunner.query(`ALTER TABLE "tasks" DROP CONSTRAINT "FK_f1f00d41b1e95d0bbda2710a62b"`);
await queryRunner.query(`ALTER TABLE "task_submissions" DROP CONSTRAINT "FK_87876dfe440de7aafce216e9f58"`);
await queryRunner.query(`ALTER TABLE "task_submissions" DROP CONSTRAINT "FK_d6cfaee118a0300d652e28ee166"`);
await queryRunner.query(`DROP TABLE "tasks"`);
await queryRunner.query(`DROP TABLE "task_submissions"`);
}
}

View File

@ -1,29 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateCustomerNotificationsSettingsTable1733993920226 implements MigrationInterface {
name = 'CreateCustomerNotificationsSettingsTable1733993920226';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "cutsomer_notification_settings"
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"is_email_enabled" boolean NOT NULL DEFAULT false,
"is_push_enabled" boolean NOT NULL DEFAULT false,
"is_sms_enabled" boolean NOT NULL DEFAULT false,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"customer_id" uuid, CONSTRAINT "REL_32f2b707407298a9eecd6cc7ea" UNIQUE ("customer_id"),
CONSTRAINT "PK_ea94fb22410c89ae6b37d63b0e3" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "cutsomer_notification_settings" ADD CONSTRAINT "FK_32f2b707407298a9eecd6cc7ea6" FOREIGN KEY ("customer_id") REFERENCES "customers"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "cutsomer_notification_settings" DROP CONSTRAINT "FK_32f2b707407298a9eecd6cc7ea6"`,
);
await queryRunner.query(`DROP TABLE "cutsomer_notification_settings"`);
}
}

View File

@ -1,70 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateSavingGoalsEntities1734246386471 implements MigrationInterface {
name = 'CreateSavingGoalsEntities1734246386471';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "saving_goals"
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"name" character varying(255) NOT NULL,
"description" character varying(255),
"due_date" date NOT NULL,
"target_amount" numeric(12,3) NOT NULL,
"current_amount" numeric(12,3) NOT NULL DEFAULT '0',
"image_id" uuid, "junior_id" uuid NOT NULL,
"created_at" TIMESTAMP NOT NULL DEFAULT now(),
"updated_at" TIMESTAMP NOT NULL DEFAULT now(),
CONSTRAINT "PK_5193f14c1c3a38e6657a159795e" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "categories"
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"name" character varying(255) NOT NULL,
"type" character varying(255) NOT NULL,
"created_at" TIMESTAMP NOT NULL DEFAULT now(),
"updated_at" TIMESTAMP NOT NULL DEFAULT now(),
"junior_id" uuid, CONSTRAINT "PK_24dbc6126a28ff948da33e97d3b" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "saving_goals_categories"
("saving_goal_id" uuid NOT NULL,
"category_id" uuid NOT NULL,
CONSTRAINT "PK_a49d4f57d06d0a36a8385b6c28f" PRIMARY KEY ("saving_goal_id", "category_id"))`,
);
await queryRunner.query(
`CREATE INDEX "IDX_d421de423f21c01672ea7c2e98" ON "saving_goals_categories" ("saving_goal_id") `,
);
await queryRunner.query(
`CREATE INDEX "IDX_b0a721a8f7f5b6fe93f3603ebc" ON "saving_goals_categories" ("category_id") `,
);
await queryRunner.query(
`ALTER TABLE "saving_goals" ADD CONSTRAINT "FK_dad35932272342c1a247a2cee1c" FOREIGN KEY ("image_id") REFERENCES "documents"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "saving_goals" ADD CONSTRAINT "FK_f494ba0a361b2f9cbe5f6e56e38" FOREIGN KEY ("junior_id") REFERENCES "juniors"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "categories" ADD CONSTRAINT "FK_4f98e0b010d5e90cae0a2007748" FOREIGN KEY ("junior_id") REFERENCES "juniors"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "saving_goals_categories" ADD CONSTRAINT "FK_d421de423f21c01672ea7c2e98f" FOREIGN KEY ("saving_goal_id") REFERENCES "saving_goals"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
await queryRunner.query(
`ALTER TABLE "saving_goals_categories" ADD CONSTRAINT "FK_b0a721a8f7f5b6fe93f3603ebc8" FOREIGN KEY ("category_id") REFERENCES "categories"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "saving_goals_categories" DROP CONSTRAINT "FK_b0a721a8f7f5b6fe93f3603ebc8"`);
await queryRunner.query(`ALTER TABLE "saving_goals_categories" DROP CONSTRAINT "FK_d421de423f21c01672ea7c2e98f"`);
await queryRunner.query(`ALTER TABLE "categories" DROP CONSTRAINT "FK_4f98e0b010d5e90cae0a2007748"`);
await queryRunner.query(`ALTER TABLE "saving_goals" DROP CONSTRAINT "FK_f494ba0a361b2f9cbe5f6e56e38"`);
await queryRunner.query(`ALTER TABLE "saving_goals" DROP CONSTRAINT "FK_dad35932272342c1a247a2cee1c"`);
await queryRunner.query(`DROP INDEX "public"."IDX_b0a721a8f7f5b6fe93f3603ebc"`);
await queryRunner.query(`DROP INDEX "public"."IDX_d421de423f21c01672ea7c2e98"`);
await queryRunner.query(`DROP TABLE "saving_goals_categories"`);
await queryRunner.query(`DROP TABLE "categories"`);
await queryRunner.query(`DROP TABLE "saving_goals"`);
}
}

View File

@ -1,32 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateJuniorRegistrationTokenTable1734262619426 implements MigrationInterface {
name = 'CreateJuniorRegistrationTokenTable1734262619426';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "junior_registration_tokens"
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"token" character varying(255) NOT NULL,
"is_used" boolean NOT NULL DEFAULT false,
"expiry_date" TIMESTAMP NOT NULL,
"junior_id" uuid NOT NULL,
"updated_at" TIMESTAMP NOT NULL DEFAULT now(),
"created_at" TIMESTAMP NOT NULL DEFAULT now(),
CONSTRAINT "UQ_e6a3e23d5a63be76812dc5d7728" UNIQUE ("token"),
CONSTRAINT "PK_610992ebec8f664113ae48b946f" PRIMARY KEY ("id"))`,
);
await queryRunner.query(`CREATE INDEX "IDX_e6a3e23d5a63be76812dc5d772" ON "junior_registration_tokens" ("token") `);
await queryRunner.query(
`ALTER TABLE "junior_registration_tokens" ADD CONSTRAINT "FK_19c52317b5b7aeecc0a11789b53" FOREIGN KEY ("junior_id") REFERENCES "juniors"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "junior_registration_tokens" DROP CONSTRAINT "FK_19c52317b5b7aeecc0a11789b53"`,
);
await queryRunner.query(`DROP INDEX "public"."IDX_e6a3e23d5a63be76812dc5d772"`);
await queryRunner.query(`DROP TABLE "junior_registration_tokens"`);
}
}

View File

@ -1,36 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateMoneyRequestEntity1734503895302 implements MigrationInterface {
name = 'CreateMoneyRequestEntity1734503895302';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "money_requests"
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"requested_amount" numeric(10,3) NOT NULL,
"message" character varying NOT NULL,
"frequency" character varying NOT NULL DEFAULT 'ONE_TIME',
"status" character varying NOT NULL DEFAULT 'PENDING',
"reviewed_at" TIMESTAMP WITH TIME ZONE,
"start_date" TIMESTAMP WITH TIME ZONE,
"end_date" TIMESTAMP WITH TIME ZONE,
"requester_id" uuid NOT NULL,
"reviewer_id" uuid NOT NULL,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT "PK_28cff23e9fb06cd5dbf73cd53e7" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "money_requests" ADD CONSTRAINT "FK_5cce02836c6033b6e2412995e34" FOREIGN KEY ("requester_id") REFERENCES "juniors"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "money_requests" ADD CONSTRAINT "FK_75ba0766db9a7bf03126facf31c" FOREIGN KEY ("reviewer_id") REFERENCES "guardians"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "money_requests" DROP CONSTRAINT "FK_75ba0766db9a7bf03126facf31c"`);
await queryRunner.query(`ALTER TABLE "money_requests" DROP CONSTRAINT "FK_5cce02836c6033b6e2412995e34"`);
await queryRunner.query(`DROP TABLE "money_requests"`);
}
}

View File

@ -1,53 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateAllowanceEntities1734601976591 implements MigrationInterface {
name = 'CreateAllowanceEntities1734601976591';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "allowances"
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"name" character varying(255) NOT NULL,
"amount" numeric(10,2) NOT NULL,
"frequency" character varying(255) NOT NULL,
"type" character varying(255) NOT NULL,
"start_date" TIMESTAMP WITH TIME ZONE NOT NULL,
"end_date" TIMESTAMP WITH TIME ZONE,
"number_of_transactions" integer,
"guardian_id" uuid NOT NULL,
"junior_id" uuid NOT NULL,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"deleted_at" TIMESTAMP WITH TIME ZONE,
CONSTRAINT "PK_3731e781e7c4e932ba4d4213ac1" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "allowance_change_requests"
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"reason" text NOT NULL,
"amount" numeric(10,2) NOT NULL,
"status" character varying(255) NOT NULL DEFAULT 'PENDING',
"allowance_id" uuid NOT NULL,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT "PK_664715670e1e72c64ce65a078de" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "allowances" ADD CONSTRAINT "FK_80b144a74e630ed63311e97427b" FOREIGN KEY ("guardian_id") REFERENCES "guardians"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "allowances" ADD CONSTRAINT "FK_61e6e612f6d4644f8910d453cc9" FOREIGN KEY ("junior_id") REFERENCES "juniors"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "allowance_change_requests" ADD CONSTRAINT "FK_4ea6382927f50cb93873fae16d2" FOREIGN KEY ("allowance_id") REFERENCES "allowances"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "allowance_change_requests" DROP CONSTRAINT "FK_4ea6382927f50cb93873fae16d2"`);
await queryRunner.query(`ALTER TABLE "allowances" DROP CONSTRAINT "FK_61e6e612f6d4644f8910d453cc9"`);
await queryRunner.query(`ALTER TABLE "allowances" DROP CONSTRAINT "FK_80b144a74e630ed63311e97427b"`);
await queryRunner.query(`DROP TABLE "allowance_change_requests"`);
await queryRunner.query(`DROP TABLE "allowances"`);
}
}

View File

@ -1,66 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateGiftEntities1734861516657 implements MigrationInterface {
name = 'CreateGiftEntities1734861516657';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "gift_replies"
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"status" character varying NOT NULL DEFAULT 'PENDING',
"color" character varying NOT NULL,
"gift_id" uuid NOT NULL,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT "REL_8292da97f1ceb9a806b8bc812f" UNIQUE ("gift_id"),
CONSTRAINT "PK_ec6567bb5ab318bb292fa6599a2" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "gift"
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"name" character varying(255) NOT NULL,
"description" text NOT NULL,
"color" character varying NOT NULL,
"amount" numeric(10,3) NOT NULL,
"status" character varying NOT NULL DEFAULT 'AVAILABLE',
"redeemed_at" TIMESTAMP WITH TIME ZONE,
"giver_id" uuid NOT NULL, "image_id" uuid NOT NULL,
"recipient_id" uuid NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT "PK_f91217caddc01a085837ebe0606" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "gift_redemptions"
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"gift_id" uuid NOT NULL,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT "PK_6ad7ac76169c3a224ce4a3afff4" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "gift_replies" ADD CONSTRAINT "FK_8292da97f1ceb9a806b8bc812f2" FOREIGN KEY ("gift_id") REFERENCES "gift"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "gift" ADD CONSTRAINT "FK_0d317b68508819308455db9b9be" FOREIGN KEY ("giver_id") REFERENCES "guardians"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "gift" ADD CONSTRAINT "FK_4a46b5734fb573dc956904c18d0" FOREIGN KEY ("recipient_id") REFERENCES "juniors"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "gift" ADD CONSTRAINT "FK_83bb54c127d0e6ee487b90e2996" FOREIGN KEY ("image_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "gift_redemptions" ADD CONSTRAINT "FK_243c4349f0c45ce5385ac316aaa" FOREIGN KEY ("gift_id") REFERENCES "gift"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "gift_redemptions" DROP CONSTRAINT "FK_243c4349f0c45ce5385ac316aaa"`);
await queryRunner.query(`ALTER TABLE "gift" DROP CONSTRAINT "FK_83bb54c127d0e6ee487b90e2996"`);
await queryRunner.query(`ALTER TABLE "gift" DROP CONSTRAINT "FK_4a46b5734fb573dc956904c18d0"`);
await queryRunner.query(`ALTER TABLE "gift" DROP CONSTRAINT "FK_0d317b68508819308455db9b9be"`);
await queryRunner.query(`ALTER TABLE "gift_replies" DROP CONSTRAINT "FK_8292da97f1ceb9a806b8bc812f2"`);
await queryRunner.query(`DROP TABLE "gift_redemptions"`);
await queryRunner.query(`DROP TABLE "gift"`);
await queryRunner.query(`DROP TABLE "gift_replies"`);
}
}

View File

@ -1,32 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateNotificationEntityAndEditDevice1734944692999 implements MigrationInterface {
name = 'CreateNotificationEntityAndEditDevice1734944692999';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "notifications"
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"title" character varying NOT NULL,
"message" character varying NOT NULL,
"recipient" character varying,
"scope" character varying NOT NULL,
"status" character varying,
"channel" character varying NOT NULL,
"user_id" uuid,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT "PK_6a72c3c0f683f6462415e653c3a" PRIMARY KEY ("id"))`,
);
await queryRunner.query(`ALTER TABLE "devices" ADD "fcm_token" character varying`);
await queryRunner.query(
`ALTER TABLE "notifications" ADD CONSTRAINT "FK_9a8a82462cab47c73d25f49261f" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "notifications" DROP CONSTRAINT "FK_9a8a82462cab47c73d25f49261f"`);
await queryRunner.query(`ALTER TABLE "devices" DROP COLUMN "fcm_token"`);
await queryRunner.query(`DROP TABLE "notifications"`);
}
}

View File

@ -1,19 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddFlagsToUserEntity1736414850257 implements MigrationInterface {
name = 'AddFlagsToUserEntity1736414850257';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "users" ADD "is_phone_verified" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "users" ADD "is_email_verified" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "phone_number" DROP NOT NULL`);
await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "country_code" DROP NOT NULL`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "country_code" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "users" ALTER COLUMN "phone_number" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "is_email_verified"`);
await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "is_phone_verified"`);
}
}

View File

@ -1,17 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddCreatedByToDocumentTable1736753223884 implements MigrationInterface {
name = 'AddCreatedByToDocumentTable1736753223884';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "documents" ADD "created_by_id" uuid `);
await queryRunner.query(
`ALTER TABLE "documents" ADD CONSTRAINT "FK_7f46f4f77acde1dcedba64cb220" FOREIGN KEY ("created_by_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "documents" DROP CONSTRAINT "FK_7f46f4f77acde1dcedba64cb220"`);
await queryRunner.query(`ALTER TABLE "documents" DROP COLUMN "created_by_id"`);
}
}

View File

@ -1,15 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddKycStatusToCustomer1739868002943 implements MigrationInterface {
name = 'AddKycStatusToCustomer1739868002943';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "customers" ADD "kyc_status" character varying(255) NOT NULL DEFAULT 'PENDING'`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN "kyc_status"`);
}
}

View File

@ -1,42 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class AddCivilidToCustomersAndUpdateNotificationsSettings1739954239949 implements MigrationInterface {
name = 'AddCivilidToCustomersAndUpdateNotificationsSettings1739954239949'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "juniors" DROP CONSTRAINT "FK_4662c4433223c01fe69fc1382f5"`);
await queryRunner.query(`ALTER TABLE "juniors" DROP CONSTRAINT "FK_6a72e1a5758643737cc563b96c7"`);
await queryRunner.query(`ALTER TABLE "juniors" DROP CONSTRAINT "REL_6a72e1a5758643737cc563b96c"`);
await queryRunner.query(`ALTER TABLE "juniors" DROP COLUMN "civil_id_front_id"`);
await queryRunner.query(`ALTER TABLE "juniors" DROP CONSTRAINT "REL_4662c4433223c01fe69fc1382f"`);
await queryRunner.query(`ALTER TABLE "juniors" DROP COLUMN "civil_id_back_id"`);
await queryRunner.query(`ALTER TABLE "customers" ADD "civil_id_front_id" uuid NOT NULL`);
await queryRunner.query(`ALTER TABLE "customers" ADD CONSTRAINT "UQ_d5f99c497892ce31598ba19a72c" UNIQUE ("civil_id_front_id")`);
await queryRunner.query(`ALTER TABLE "customers" ADD "civil_id_back_id" uuid NOT NULL`);
await queryRunner.query(`ALTER TABLE "customers" ADD CONSTRAINT "UQ_2191662d124c56dd968ba01bf18" UNIQUE ("civil_id_back_id")`);
await queryRunner.query(`ALTER TABLE "users" ADD "is_email_enabled" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "users" ADD "is_push_enabled" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "users" ADD "is_sms_enabled" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "customers" ADD CONSTRAINT "FK_d5f99c497892ce31598ba19a72c" FOREIGN KEY ("civil_id_front_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "customers" ADD CONSTRAINT "FK_2191662d124c56dd968ba01bf18" FOREIGN KEY ("civil_id_back_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "FK_2191662d124c56dd968ba01bf18"`);
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "FK_d5f99c497892ce31598ba19a72c"`);
await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "is_sms_enabled"`);
await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "is_push_enabled"`);
await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "is_email_enabled"`);
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "UQ_2191662d124c56dd968ba01bf18"`);
await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN "civil_id_back_id"`);
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "UQ_d5f99c497892ce31598ba19a72c"`);
await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN "civil_id_front_id"`);
await queryRunner.query(`ALTER TABLE "juniors" ADD "civil_id_back_id" uuid NOT NULL`);
await queryRunner.query(`ALTER TABLE "juniors" ADD CONSTRAINT "REL_4662c4433223c01fe69fc1382f" UNIQUE ("civil_id_back_id")`);
await queryRunner.query(`ALTER TABLE "juniors" ADD "civil_id_front_id" uuid NOT NULL`);
await queryRunner.query(`ALTER TABLE "juniors" ADD CONSTRAINT "REL_6a72e1a5758643737cc563b96c" UNIQUE ("civil_id_front_id")`);
await queryRunner.query(`ALTER TABLE "juniors" ADD CONSTRAINT "FK_6a72e1a5758643737cc563b96c7" FOREIGN KEY ("civil_id_front_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "juniors" ADD CONSTRAINT "FK_4662c4433223c01fe69fc1382f5" FOREIGN KEY ("civil_id_back_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`);
}
}

View File

@ -1,20 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class CreateUserRegistrationTable1740045960580 implements MigrationInterface {
name = 'CreateUserRegistrationTable1740045960580'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "user_registration_tokens" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "token" character varying(255) NOT NULL, "user_type" character varying(255) NOT NULL, "is_used" boolean NOT NULL DEFAULT false, "expiry_date" TIMESTAMP NOT NULL, "user_id" uuid, "junior_id" uuid, "updated_at" TIMESTAMP NOT NULL DEFAULT now(), "created_at" TIMESTAMP NOT NULL DEFAULT now(), CONSTRAINT "UQ_5881556d05b46fc7bd9e3bba935" UNIQUE ("token"), CONSTRAINT "PK_135a2d86443071ff0ba1c14135c" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE INDEX "IDX_5881556d05b46fc7bd9e3bba93" ON "user_registration_tokens" ("token") `);
await queryRunner.query(`ALTER TABLE "user_registration_tokens" ADD CONSTRAINT "FK_57cbbe079a7945d6ed1df114825" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "user_registration_tokens" ADD CONSTRAINT "FK_e41bec3ed6e549cbf90f57cc344" FOREIGN KEY ("junior_id") REFERENCES "juniors"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "user_registration_tokens" DROP CONSTRAINT "FK_e41bec3ed6e549cbf90f57cc344"`);
await queryRunner.query(`ALTER TABLE "user_registration_tokens" DROP CONSTRAINT "FK_57cbbe079a7945d6ed1df114825"`);
await queryRunner.query(`DROP INDEX "public"."IDX_5881556d05b46fc7bd9e3bba93"`);
await queryRunner.query(`DROP TABLE "user_registration_tokens"`);
}
}

View File

@ -1,33 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddUsedFlagToOtpAndRemoveConstraintsFromCustomers1741087742821 implements MigrationInterface {
name = 'AddUsedFlagToOtpAndRemoveConstraintsFromCustomers1741087742821';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "otp" ADD "is_used" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "FK_d5f99c497892ce31598ba19a72c"`);
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "FK_2191662d124c56dd968ba01bf18"`);
await queryRunner.query(`ALTER TABLE "customers" ALTER COLUMN "civil_id_front_id" DROP NOT NULL`);
await queryRunner.query(`ALTER TABLE "customers" ALTER COLUMN "civil_id_back_id" DROP NOT NULL`);
await queryRunner.query(
`ALTER TABLE "customers" ADD CONSTRAINT "FK_d5f99c497892ce31598ba19a72c" FOREIGN KEY ("civil_id_front_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "customers" ADD CONSTRAINT "FK_2191662d124c56dd968ba01bf18" FOREIGN KEY ("civil_id_back_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "FK_2191662d124c56dd968ba01bf18"`);
await queryRunner.query(`ALTER TABLE "customers" DROP CONSTRAINT "FK_d5f99c497892ce31598ba19a72c"`);
await queryRunner.query(`ALTER TABLE "customers" ALTER COLUMN "civil_id_back_id" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "customers" ALTER COLUMN "civil_id_front_id" SET NOT NULL`);
await queryRunner.query(
`ALTER TABLE "customers" ADD CONSTRAINT "FK_2191662d124c56dd968ba01bf18" FOREIGN KEY ("civil_id_back_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "customers" ADD CONSTRAINT "FK_d5f99c497892ce31598ba19a72c" FOREIGN KEY ("civil_id_front_id") REFERENCES "documents"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(`ALTER TABLE "otp" DROP COLUMN "is_used"`);
}
}

View File

@ -1,36 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class UpdateCustomerTable1742112997024 implements MigrationInterface {
name = 'UpdateCustomerTable1742112997024';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE "customers"
RENAME COLUMN "createdAt"
TO "created_at"
`);
await queryRunner.query(`
ALTER TABLE "customers"
RENAME COLUMN "updatedAt"
TO "updated_at"
`);
await queryRunner.query(`ALTER TABLE "customers" ADD "waiting_number" SERIAL NOT NULL`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN "waiting_number"`);
await queryRunner.query(`
ALTER TABLE "customers"
RENAME COLUMN "created_at"
TO "createdAt"
`);
await queryRunner.query(`
ALTER TABLE "customers"
RENAME COLUMN "updated_at"
TO "updatedAt"
`);
}
}

View File

@ -1,23 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddAddressFieldsToCustomers1747569536067 implements MigrationInterface {
name = 'AddAddressFieldsToCustomers1747569536067';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "customers" ADD "country" character varying(255)`);
await queryRunner.query(`ALTER TABLE "customers" ADD "region" character varying(255)`);
await queryRunner.query(`ALTER TABLE "customers" ADD "city" character varying(255)`);
await queryRunner.query(`ALTER TABLE "customers" ADD "neighborhood" character varying(255)`);
await queryRunner.query(`ALTER TABLE "customers" ADD "street" character varying(255)`);
await queryRunner.query(`ALTER TABLE "customers" ADD "building" character varying(255)`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN "building"`);
await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN "street"`);
await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN "neighborhood"`);
await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN "city"`);
await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN "region"`);
await queryRunner.query(`ALTER TABLE "customers" DROP COLUMN "country"`);
}
}

View File

@ -1,40 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateCardEntity1749633935436 implements MigrationInterface {
name = 'CreateCardEntity1749633935436';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "cards"
("id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"card_reference" character varying NOT NULL,
"first_six_digits" character varying(6) NOT NULL,
"last_four_digits" character varying(4) NOT NULL,
"expiry" character varying NOT NULL,
"customer_type" character varying NOT NULL,
"color" character varying NOT NULL DEFAULT 'BLUE',
"status" character varying NOT NULL DEFAULT 'PENDING',
"scheme" character varying NOT NULL DEFAULT 'VISA',
"issuer" character varying NOT NULL,
"customer_id" uuid NOT NULL,
"parent_id" uuid,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT "PK_9451069b6f1199730791a7f4ae4" PRIMARY KEY ("id"))`,
);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_6fb82820c0b1b7ec32a8dbf911" ON "cards" ("card_reference") `);
await queryRunner.query(
`ALTER TABLE "cards" ADD CONSTRAINT "FK_d46837f6ab27271d8125517d0b6" FOREIGN KEY ("parent_id") REFERENCES "customers"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "cards" ADD CONSTRAINT "FK_f5aa0baf4ff1b397b3f946a443e" FOREIGN KEY ("customer_id") REFERENCES "customers"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "cards" DROP CONSTRAINT "FK_f5aa0baf4ff1b397b3f946a443e"`);
await queryRunner.query(`ALTER TABLE "cards" DROP CONSTRAINT "FK_d46837f6ab27271d8125517d0b6"`);
await queryRunner.query(`DROP INDEX "public"."IDX_6fb82820c0b1b7ec32a8dbf911"`);
await queryRunner.query(`DROP TABLE "cards"`);
}
}

View File

@ -1,45 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateAccountEntity1751456987627 implements MigrationInterface {
name = 'CreateAccountEntity1751456987627';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "cards" DROP CONSTRAINT "FK_f5aa0baf4ff1b397b3f946a443e"`);
await queryRunner.query(`ALTER TABLE "cards" DROP CONSTRAINT "FK_d46837f6ab27271d8125517d0b6"`);
await queryRunner.query(`DROP INDEX "public"."IDX_6fb82820c0b1b7ec32a8dbf911"`);
await queryRunner.query(
`CREATE TABLE "accounts" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "account_reference" character varying(255) NOT NULL, "currency" character varying(255) NOT NULL, "balance" numeric(10,2) NOT NULL DEFAULT '0', "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updated_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "UQ_829183fe026a0ce5fc8026b2417" UNIQUE ("account_reference"), CONSTRAINT "PK_5a7a02c20412299d198e097a8fe" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE UNIQUE INDEX "IDX_829183fe026a0ce5fc8026b241" ON "accounts" ("account_reference") `,
);
await queryRunner.query(`ALTER TABLE "cards" ADD "account_id" uuid NOT NULL`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_4f7df5c5dc950295dc417a11d8" ON "cards" ("card_reference") `);
await queryRunner.query(
`ALTER TABLE "cards" ADD CONSTRAINT "FK_8ba18e7060c38ddae12e5bdf907" FOREIGN KEY ("parent_id") REFERENCES "customers"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "cards" ADD CONSTRAINT "FK_2fd0ee722ec57594d2e448c73d7" FOREIGN KEY ("customer_id") REFERENCES "customers"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "cards" ADD CONSTRAINT "FK_b2874ef49ff7da2dee49e4bc6d3" FOREIGN KEY ("account_id") REFERENCES "accounts"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "cards" DROP CONSTRAINT "FK_b2874ef49ff7da2dee49e4bc6d3"`);
await queryRunner.query(`ALTER TABLE "cards" DROP CONSTRAINT "FK_2fd0ee722ec57594d2e448c73d7"`);
await queryRunner.query(`ALTER TABLE "cards" DROP CONSTRAINT "FK_8ba18e7060c38ddae12e5bdf907"`);
await queryRunner.query(`DROP INDEX "public"."IDX_4f7df5c5dc950295dc417a11d8"`);
await queryRunner.query(`ALTER TABLE "cards" DROP COLUMN "account_id"`);
await queryRunner.query(`DROP INDEX "public"."IDX_829183fe026a0ce5fc8026b241"`);
await queryRunner.query(`DROP TABLE "accounts"`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_6fb82820c0b1b7ec32a8dbf911" ON "cards" ("card_reference") `);
await queryRunner.query(
`ALTER TABLE "cards" ADD CONSTRAINT "FK_d46837f6ab27271d8125517d0b6" FOREIGN KEY ("parent_id") REFERENCES "customers"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "cards" ADD CONSTRAINT "FK_f5aa0baf4ff1b397b3f946a443e" FOREIGN KEY ("customer_id") REFERENCES "customers"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
);
}
}

View File

@ -1,18 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class CreateTransactionTable1751466314709 implements MigrationInterface {
name = 'CreateTransactionTable1751466314709'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "transactions" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "card_reference" character varying, "transaction_type" character varying NOT NULL DEFAULT 'EXTERNAL', "account_reference" character varying, "transaction_id" character varying, "card_masked_number" character varying, "transaction_date" TIMESTAMP WITH TIME ZONE, "rrn" character varying, "transaction_amount" numeric(12,2) NOT NULL, "transaction_currency" character varying NOT NULL, "billing_amount" numeric(12,2) NOT NULL, "settlement_amount" numeric(12,2) NOT NULL, "fees" numeric(12,2) NOT NULL, "card_id" uuid, "account_id" uuid, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), CONSTRAINT "UQ_9162bf9ab4e31961a8f7932974c" UNIQUE ("transaction_id"), CONSTRAINT "PK_a219afd8dd77ed80f5a862f1db9" PRIMARY KEY ("id"))`);
await queryRunner.query(`ALTER TABLE "transactions" ADD CONSTRAINT "FK_80ad48141be648db2d84ff32f79" FOREIGN KEY ("card_id") REFERENCES "cards"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "transactions" ADD CONSTRAINT "FK_49c0d6e8ba4bfb5582000d851f0" FOREIGN KEY ("account_id") REFERENCES "accounts"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "transactions" DROP CONSTRAINT "FK_49c0d6e8ba4bfb5582000d851f0"`);
await queryRunner.query(`ALTER TABLE "transactions" DROP CONSTRAINT "FK_80ad48141be648db2d84ff32f79"`);
await queryRunner.query(`DROP TABLE "transactions"`);
}
}

View File

@ -1,16 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class EditTransactionTable1752056898465 implements MigrationInterface {
name = 'EditTransactionTable1752056898465'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "transactions" ADD "transaction_scope" character varying NOT NULL`);
await queryRunner.query(`ALTER TABLE "transactions" ADD "vat_on_fees" numeric(12,2) NOT NULL DEFAULT '0'`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "transactions" DROP COLUMN "vat_on_fees"`);
await queryRunner.query(`ALTER TABLE "transactions" DROP COLUMN "transaction_scope"`);
}
}

View File

@ -1,16 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class UpdateCardTable1753098116701 implements MigrationInterface {
name = 'UpdateCardTable1753098116701'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "cards" ADD "statusDescription" character varying NOT NULL DEFAULT 'PENDING_ACTIVATION'`);
await queryRunner.query(`ALTER TABLE "cards" ADD "limit" numeric(10,2) NOT NULL DEFAULT '0'`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "cards" DROP COLUMN "limit"`);
await queryRunner.query(`ALTER TABLE "cards" DROP COLUMN "statusDescription"`);
}
}

View File

@ -1,16 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm";
export class EditCustomerTable1753098326876 implements MigrationInterface {
name = 'EditCustomerTable1753098326876'
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "customers" RENAME COLUMN "waiting_number" TO "application_number"`);
await queryRunner.query(`ALTER SEQUENCE "customers_waiting_number_seq" RENAME TO "customers_application_number_seq"`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER SEQUENCE "customers_application_number_seq" RENAME TO "customers_waiting_number_seq"`);
await queryRunner.query(`ALTER TABLE "customers" RENAME COLUMN "application_number" TO "waiting_number"`);
}
}

View File

@ -58,7 +58,7 @@ const DEFAULT_AVATARS = [
documentType: DocumentType.DEFAULT_AVATAR,
},
];
export class SeedDefaultAvatar1733750228289 implements MigrationInterface {
export class SeedDefaultAvatar1753869637732 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.manager.getRepository(Document).save(DEFAULT_AVATARS);
}

View File

@ -1,33 +1,4 @@
export * from './1732434281561-create-document-entity';
export * from './1733206728721-create-user-entity';
export * from './1733209041336-create-otp-entity';
export * from './1733298524771-create-customer-entity';
export * from './1733314952318-create-device-entity';
export * from './1733731507261-create-junior-entity';
export * from './1733732021622-create-guardian-entity';
export * from './1733748083604-create-theme-entity';
export * from './1733750228289-seed-default-avatar';
export * from './1733904556416-create-task-entities';
export * from './1753869637732-seed-default-avatar';
export * from './1733990253208-seeds-default-tasks-logo';
export * from './1733993920226-create-customer-notifications-settings-table';
export * from './1734246386471-create-saving-goals-entities';
export * from './1734247702310-seeds-goals-categories';
export * from './1734262619426-create-junior-registration-token-table';
export * from './1734503895302-create-money-request-entity';
export * from './1734601976591-create-allowance-entities';
export * from './1734861516657-create-gift-entities';
export * from './1734944692999-create-notification-entity-and-edit-device';
export * from './1736414850257-add-flags-to-user-entity';
export * from './1736753223884-add_created_by_to_document_table';
export * from './1739868002943-add-kyc-status-to-customer';
export * from './1739954239949-add-civilid-to-customers-and-update-notifications-settings';
export * from './1740045960580-create-user-registration-table';
export * from './1741087742821-add-used-flag-to-otp-and-remove-constraints-from-customers';
export * from './1742112997024-update-customer-table';
export * from './1747569536067-add-address-fields-to-customers';
export * from './1749633935436-create-card-entity';
export * from './1751456987627-create-account-entity';
export * from './1751466314709-create-transaction-table';
export * from './1752056898465-edit-transaction-table';
export * from './1753098116701-update-card-table';
export * from './1753098326876-edit-customer-table';
export * from './1733750228289-initial-migration';

View File

@ -43,8 +43,7 @@
},
"CUSTOMER": {
"NOT_FOUND": "لم يتم العثور على العميل.",
"ALREADY_EXISTS": "العميل موجود بالفعل.",
"KYC_NOT_APPROVED": "لم يتم الموافقة على هوية العميل بعد."
"ALREADY_EXISTS": "العميل موجود بالفعل."
},
"GIFT": {

View File

@ -42,8 +42,7 @@
},
"CUSTOMER": {
"NOT_FOUND": "The customer was not found.",
"ALREADY_EXISTS": "The customer already exists.",
"KYC_NOT_APPROVED": "The customer's KYC has not been approved yet."
"ALREADY_EXISTS": "The customer already exists."
},
"GIFT": {