mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2025-07-10 07:07:23 +00:00
feat: finish working on account transaction webhook
This commit is contained in:
7
package-lock.json
generated
7
package-lock.json
generated
@ -33,6 +33,7 @@
|
|||||||
"cacheable": "^1.8.5",
|
"cacheable": "^1.8.5",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "^0.14.1",
|
||||||
|
"decimal.js": "^10.6.0",
|
||||||
"firebase-admin": "^13.0.2",
|
"firebase-admin": "^13.0.2",
|
||||||
"google-libphonenumber": "^3.2.39",
|
"google-libphonenumber": "^3.2.39",
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
@ -5167,6 +5168,12 @@
|
|||||||
"node": ">=0.10.0"
|
"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": {
|
"node_modules/dedent": {
|
||||||
"version": "1.5.3",
|
"version": "1.5.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
"cacheable": "^1.8.5",
|
"cacheable": "^1.8.5",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "^0.14.1",
|
||||||
|
"decimal.js": "^10.6.0",
|
||||||
"firebase-admin": "^13.0.2",
|
"firebase-admin": "^13.0.2",
|
||||||
"google-libphonenumber": "^3.2.39",
|
"google-libphonenumber": "^3.2.39",
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Column, CreateDateColumn, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
import { Column, CreateDateColumn, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import { TransactionType } from '../enums';
|
import { TransactionScope, TransactionType } from '../enums';
|
||||||
import { Account } from './account.entity';
|
import { Account } from './account.entity';
|
||||||
import { Card } from './card.entity';
|
import { Card } from './card.entity';
|
||||||
|
|
||||||
@ -8,12 +8,15 @@ export class Transaction {
|
|||||||
@PrimaryGeneratedColumn('uuid')
|
@PrimaryGeneratedColumn('uuid')
|
||||||
id!: string;
|
id!: string;
|
||||||
|
|
||||||
@Column({ name: 'card_reference', nullable: true, type: 'varchar' })
|
@Column({ name: 'transaction_scope', type: 'varchar', nullable: false })
|
||||||
cardReference!: string;
|
transactionScope!: TransactionScope;
|
||||||
|
|
||||||
@Column({ name: 'transaction_type', type: 'varchar', default: TransactionType.EXTERNAL })
|
@Column({ name: 'transaction_type', type: 'varchar', default: TransactionType.EXTERNAL })
|
||||||
transactionType!: TransactionType;
|
transactionType!: TransactionType;
|
||||||
|
|
||||||
|
@Column({ name: 'card_reference', nullable: true, type: 'varchar' })
|
||||||
|
cardReference!: string;
|
||||||
|
|
||||||
@Column({ name: 'account_reference', nullable: true, type: 'varchar' })
|
@Column({ name: 'account_reference', nullable: true, type: 'varchar' })
|
||||||
accountReference!: string;
|
accountReference!: string;
|
||||||
|
|
||||||
@ -44,6 +47,9 @@ export class Transaction {
|
|||||||
@Column({ type: 'decimal', name: 'fees', precision: 12, scale: 2 })
|
@Column({ type: 'decimal', name: 'fees', precision: 12, scale: 2 })
|
||||||
fees!: number;
|
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 })
|
@Column({ name: 'card_id', type: 'uuid', nullable: true })
|
||||||
cardId!: string;
|
cardId!: string;
|
||||||
|
|
||||||
@ -57,6 +63,7 @@ export class Transaction {
|
|||||||
@ManyToOne(() => Account, (account) => account.transactions, { onDelete: 'CASCADE', nullable: true })
|
@ManyToOne(() => Account, (account) => account.transactions, { onDelete: 'CASCADE', nullable: true })
|
||||||
@JoinColumn({ name: 'account_id' })
|
@JoinColumn({ name: 'account_id' })
|
||||||
account!: Account;
|
account!: Account;
|
||||||
|
|
||||||
@CreateDateColumn({ name: 'created_at', type: 'timestamp with time zone' })
|
@CreateDateColumn({ name: 'created_at', type: 'timestamp with time zone' })
|
||||||
createdAt!: Date;
|
createdAt!: Date;
|
||||||
}
|
}
|
||||||
|
@ -3,4 +3,5 @@ export * from './card-issuers.enum';
|
|||||||
export * from './card-scheme.enum';
|
export * from './card-scheme.enum';
|
||||||
export * from './card-status.enum';
|
export * from './card-status.enum';
|
||||||
export * from './customer-type.enum';
|
export * from './customer-type.enum';
|
||||||
|
export * from './transaction-scope.enum';
|
||||||
export * from './transaction-type.enum';
|
export * from './transaction-type.enum';
|
||||||
|
4
src/card/enums/transaction-scope.enum.ts
Normal file
4
src/card/enums/transaction-scope.enum.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export enum TransactionScope {
|
||||||
|
CARD = 'CARD',
|
||||||
|
ACCOUNT = 'ACCOUNT',
|
||||||
|
}
|
@ -16,4 +16,19 @@ export class AccountRepository {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,14 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { CardTransactionWebhookRequest } from '~/common/modules/neoleap/dtos/requests';
|
import {
|
||||||
|
AccountTransactionWebhookRequest,
|
||||||
|
CardTransactionWebhookRequest,
|
||||||
|
} from '~/common/modules/neoleap/dtos/requests';
|
||||||
import { Card } from '../entities';
|
import { Card } from '../entities';
|
||||||
|
import { Account } from '../entities/account.entity';
|
||||||
import { Transaction } from '../entities/transaction.entity';
|
import { Transaction } from '../entities/transaction.entity';
|
||||||
import { TransactionType } from '../enums';
|
import { TransactionScope, TransactionType } from '../enums';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TransactionRepository {
|
export class TransactionRepository {
|
||||||
@ -28,7 +32,34 @@ export class TransactionRepository {
|
|||||||
accountId: card.account!.id,
|
accountId: card.account!.id,
|
||||||
transactionType: TransactionType.EXTERNAL,
|
transactionType: TransactionType.EXTERNAL,
|
||||||
accountReference: card.account!.accountReference,
|
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 },
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable, UnprocessableEntityException } from '@nestjs/common';
|
||||||
import { Account } from '../entities/account.entity';
|
import { Account } from '../entities/account.entity';
|
||||||
import { AccountRepository } from '../repositories/account.repository';
|
import { AccountRepository } from '../repositories/account.repository';
|
||||||
|
|
||||||
@ -9,4 +9,30 @@ export class AccountService {
|
|||||||
createAccount(accountId: string): Promise<Account> {
|
createAccount(accountId: string): Promise<Account> {
|
||||||
return this.accountRepository.createAccount(accountId);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable, UnprocessableEntityException } from '@nestjs/common';
|
||||||
import { CardTransactionWebhookRequest } from '~/common/modules/neoleap/dtos/requests';
|
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 { TransactionRepository } from '../repositories/transaction.repository';
|
||||||
|
import { AccountService } from './account.service';
|
||||||
import { CardService } from './card.service';
|
import { CardService } from './card.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -8,9 +15,52 @@ export class TransactionService {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly transactionRepository: TransactionRepository,
|
private readonly transactionRepository: TransactionRepository,
|
||||||
private readonly cardService: CardService,
|
private readonly cardService: CardService,
|
||||||
|
private readonly accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
@Transactional()
|
||||||
async createCardTransaction(body: CardTransactionWebhookRequest) {
|
async createCardTransaction(body: CardTransactionWebhookRequest) {
|
||||||
const card = await this.cardService.getCardByReferenceNumber(body.cardId);
|
const card = await this.cardService.getCardByReferenceNumber(body.cardId);
|
||||||
return this.transactionRepository.createCardTransaction(card, body);
|
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.settlementAmount)
|
||||||
|
.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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Body, Controller, Post } from '@nestjs/common';
|
import { Body, Controller, Post } from '@nestjs/common';
|
||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
import { TransactionService } from '~/card/services/transaction.service';
|
import { TransactionService } from '~/card/services/transaction.service';
|
||||||
import { CardTransactionWebhookRequest } from '../dtos/requests';
|
import { AccountTransactionWebhookRequest, CardTransactionWebhookRequest } from '../dtos/requests';
|
||||||
|
|
||||||
@Controller('neoleap-webhooks')
|
@Controller('neoleap-webhooks')
|
||||||
@ApiTags('Neoleap Webhooks')
|
@ApiTags('Neoleap Webhooks')
|
||||||
@ -12,4 +12,9 @@ export class NeoLeapWebhooksController {
|
|||||||
async handleCardTransactionWebhook(@Body() body: CardTransactionWebhookRequest) {
|
async handleCardTransactionWebhook(@Body() body: CardTransactionWebhookRequest) {
|
||||||
return this.transactionService.createCardTransaction(body);
|
return this.transactionService.createCardTransaction(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('account-transaction')
|
||||||
|
async handleAccountTransactionWebhook(@Body() body: AccountTransactionWebhookRequest) {
|
||||||
|
return this.transactionService.createAccountTransaction(body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
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;
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Expose, Type } from 'class-transformer';
|
import { Expose, Type } from 'class-transformer';
|
||||||
import { IsNumber, IsString, ValidateNested } from 'class-validator';
|
import { IsNumber, IsString, Min, ValidateNested } from 'class-validator';
|
||||||
|
|
||||||
export class CardAcceptorLocationDto {
|
export class CardAcceptorLocationDto {
|
||||||
@Expose()
|
@Expose()
|
||||||
@ -99,6 +99,7 @@ export class CardTransactionWebhookRequest {
|
|||||||
|
|
||||||
@Type(() => Number)
|
@Type(() => Number)
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
|
@Min(0, { message: 'amount must be zero or a positive number' })
|
||||||
@ApiProperty({ example: '100.5' })
|
@ApiProperty({ example: '100.5' })
|
||||||
transactionAmount!: number;
|
transactionAmount!: number;
|
||||||
|
|
||||||
@ -108,6 +109,7 @@ export class CardTransactionWebhookRequest {
|
|||||||
|
|
||||||
@Type(() => Number)
|
@Type(() => Number)
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
|
@Min(0, { message: 'amount must be zero or a positive number' })
|
||||||
@ApiProperty({ example: '100.5' })
|
@ApiProperty({ example: '100.5' })
|
||||||
billingAmount!: number;
|
billingAmount!: number;
|
||||||
|
|
||||||
@ -117,6 +119,7 @@ export class CardTransactionWebhookRequest {
|
|||||||
|
|
||||||
@Type(() => Number)
|
@Type(() => Number)
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
|
@Min(0, { message: 'amount must be zero or a positive number' })
|
||||||
@ApiProperty({ example: '100.5' })
|
@ApiProperty({ example: '100.5' })
|
||||||
settlementAmount!: number;
|
settlementAmount!: number;
|
||||||
|
|
||||||
@ -127,12 +130,14 @@ export class CardTransactionWebhookRequest {
|
|||||||
@Expose()
|
@Expose()
|
||||||
@Type(() => Number)
|
@Type(() => Number)
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
|
@Min(0, { message: 'amount must be zero or a positive number' })
|
||||||
@ApiProperty({ example: '20' })
|
@ApiProperty({ example: '20' })
|
||||||
fees!: number;
|
fees!: number;
|
||||||
|
|
||||||
@Expose()
|
@Expose()
|
||||||
@Type(() => Number)
|
@Type(() => Number)
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
|
@Min(0, { message: 'amount must be zero or a positive number' })
|
||||||
@ApiProperty({ example: '4.5' })
|
@ApiProperty({ example: '4.5' })
|
||||||
vatOnFees!: number;
|
vatOnFees!: number;
|
||||||
|
|
||||||
|
@ -1 +1,2 @@
|
|||||||
|
export * from './account-transaction-webhook.request.dto';
|
||||||
export * from './card-transaction-webhook.request.dto';
|
export * from './card-transaction-webhook.request.dto';
|
||||||
|
16
src/db/migrations/1752056898465-edit-transaction-table.ts
Normal file
16
src/db/migrations/1752056898465-edit-transaction-table.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
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"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -28,3 +28,4 @@ export * from './1747569536067-add-address-fields-to-customers';
|
|||||||
export * from './1749633935436-create-card-entity';
|
export * from './1749633935436-create-card-entity';
|
||||||
export * from './1751456987627-create-account-entity';
|
export * from './1751456987627-create-account-entity';
|
||||||
export * from './1751466314709-create-transaction-table';
|
export * from './1751466314709-create-transaction-table';
|
||||||
|
export * from './1752056898465-edit-transaction-table';
|
||||||
|
Reference in New Issue
Block a user