From 279e5230e5b9a1334f26b4b07a20b5aa11849ed6 Mon Sep 17 00:00:00 2001 From: Inga Date: Mon, 15 Jan 2024 20:24:50 +0000 Subject: [PATCH 1/6] refactor: implemented async getExchangeRate method --- src/transaction/transaction.controller.ts | 56 +++++++++++++---------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/src/transaction/transaction.controller.ts b/src/transaction/transaction.controller.ts index 0c50631..b478188 100644 --- a/src/transaction/transaction.controller.ts +++ b/src/transaction/transaction.controller.ts @@ -1,4 +1,5 @@ import { Controller, Post, UsePipes, Body } from '@nestjs/common'; +import { ExchangeRateInput } from 'src/exchange-rate/exchange-rate.dto'; import { ExchangeRateService } from 'src/exchange-rate/exchange-rate.service'; import { TransactionService } from './transaction.service'; import { transactionBodySchema } from './transaction.validation'; @@ -105,6 +106,19 @@ export class TransactionController { : this.defaultRule(transactionInput); } + getExchangeRate(exchangeRateInput: ExchangeRateInput) { + return new Promise((resolve, reject) => { + try { + this.exchangeRateService.convertCurrency(exchangeRateInput).subscribe({ + next: resolve, + error: reject, + }); + } catch (error) { + reject(error); + } + }); + } + getAmountWithExchange(transactionInput: TransactionInput) { const commissionAmount = this.applyRules( [this.turnoverRule, this.discountRule], @@ -115,31 +129,23 @@ export class TransactionController { date: transactionInput.date, }; - try { - this.exchangeRateService.convertCurrency(exhangeRateInput).subscribe({ - next: (exchangeRateResponse) => - commissionAmount - .then((commission) => - this.transactionService.insertOne({ - date: transactionInput.date, - amount: parseInt(transactionInput.amount), - currency: transactionInput.currency, - client_id: transactionInput.client_id, - commission, - base_currency: Currency.EUR, - base_amount: - parseInt(transactionInput.amount) * - exchangeRateResponse[transactionInput.currency], - }), - ) - .catch((error) => console.log(error)), - error: (error) => { - console.log(error); - }, - }); - } catch (error) { - console.log(error); - } + this.getExchangeRate(exhangeRateInput) + .then((exchangeRateResponse) => + commissionAmount.then((commission) => + this.transactionService.insertOne({ + date: transactionInput.date, + amount: parseInt(transactionInput.amount), + currency: transactionInput.currency, + client_id: transactionInput.client_id, + commission, + base_currency: Currency.EUR, + base_amount: + parseInt(transactionInput.amount) * + exchangeRateResponse[transactionInput.currency], + }), + ), + ) + .catch((error) => console.log(error)); return commissionAmount; } From ac7aabb7c4252822e11be5f55728272ec8c40ab1 Mon Sep 17 00:00:00 2001 From: Inga Date: Mon, 15 Jan 2024 20:41:52 +0000 Subject: [PATCH 2/6] refactor: rules now accept partial transaction object instead of TransactionInput --- .../transaction.controller.spec.ts | 4 +- src/transaction/transaction.controller.ts | 43 +++++++++++++------ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/transaction/transaction.controller.spec.ts b/src/transaction/transaction.controller.spec.ts index 4c0c389..521fa29 100644 --- a/src/transaction/transaction.controller.spec.ts +++ b/src/transaction/transaction.controller.spec.ts @@ -70,9 +70,9 @@ describe('TransactionController Unit Tests', () => { }); it('calling applyRules method minimum result of applied rules should be got', async () => { - const mockTransactionInput: TransactionInput = { + const mockTransactionInput = { date: '2021-01-05', - amount: '1000.00', + amount: 1000.0, currency: 'EUR', client_id: 1, }; diff --git a/src/transaction/transaction.controller.ts b/src/transaction/transaction.controller.ts index b478188..065ae35 100644 --- a/src/transaction/transaction.controller.ts +++ b/src/transaction/transaction.controller.ts @@ -12,6 +12,7 @@ import { DefaultCommissionAmount, HighTurnoverDiscount, } from './transaction.dto'; +import { Transaction } from './transaction.entity'; @Controller('transaction') export class TransactionController { @@ -38,7 +39,9 @@ export class TransactionController { }); } - getClientDeposit = async (transactionInput: TransactionInput) => { + getClientDeposit = async ( + transactionInput: Pick, + ) => { try { const deposit = await this.transactionService.findByClientIdWithinActualMonth( @@ -61,7 +64,7 @@ export class TransactionController { return 0; }; - turnoverRule = async (transactionInput: TransactionInput) => { + turnoverRule = async (transactionInput: Pick) => { try { const clientDeposit = await this.getClientDeposit(transactionInput); if (clientDeposit) { @@ -72,24 +75,25 @@ export class TransactionController { } }; - discountRule(transactionInput: TransactionInput) { + discountRule(transactionInput: Pick) { return transactionInput.client_id === 42 ? DiscountRuleForClientById.client_42 : false; } - defaultRule(transactionInput: TransactionInput) { + defaultRule(transactionInput: Pick) { const commissionAmount = - (parseInt(transactionInput.amount) / 100) * - DefaultCommissionPercentage.percentage; + (transactionInput.amount * DefaultCommissionPercentage.percentage) / 100; return commissionAmount < DefaultCommissionAmount.amount ? DefaultCommissionAmount.amount : commissionAmount; } async applyRules( - rules: ((transactionInput: TransactionInput) => any)[], - transactionInput: TransactionInput, + rules: (( + transactionInput: Pick, + ) => any)[], + transactionInput: Pick, ) { let commissionAmount; for (let i = 0; i < rules.length; i++) { @@ -120,9 +124,16 @@ export class TransactionController { } getAmountWithExchange(transactionInput: TransactionInput) { + const transactionData = { + date: transactionInput.date, + amount: parseInt(transactionInput.amount), + currency: transactionInput.currency, + client_id: transactionInput.client_id, + }; + const commissionAmount = this.applyRules( [this.turnoverRule, this.discountRule], - transactionInput, + transactionData, ); const exhangeRateInput = { @@ -133,10 +144,7 @@ export class TransactionController { .then((exchangeRateResponse) => commissionAmount.then((commission) => this.transactionService.insertOne({ - date: transactionInput.date, - amount: parseInt(transactionInput.amount), - currency: transactionInput.currency, - client_id: transactionInput.client_id, + ...transactionData, commission, base_currency: Currency.EUR, base_amount: @@ -151,9 +159,16 @@ export class TransactionController { } async getAmountWithoutExchange(transactionInput: TransactionInput) { + const transactionData = { + date: transactionInput.date, + amount: parseInt(transactionInput.amount), + currency: transactionInput.currency, + client_id: transactionInput.client_id, + }; + const commissionAmount = await this.applyRules( [this.turnoverRule, this.discountRule], - transactionInput, + transactionData, ); try { this.transactionService From dcf34b6791cd2d82ed3abd1a1444d7beb24c07f7 Mon Sep 17 00:00:00 2001 From: Inga Date: Mon, 15 Jan 2024 20:45:26 +0000 Subject: [PATCH 3/6] refactor: better types --- src/transaction/transaction.controller.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/transaction/transaction.controller.ts b/src/transaction/transaction.controller.ts index 065ae35..3fd1bc1 100644 --- a/src/transaction/transaction.controller.ts +++ b/src/transaction/transaction.controller.ts @@ -14,6 +14,8 @@ import { } from './transaction.dto'; import { Transaction } from './transaction.entity'; +type TransactionRuleData = Pick; + @Controller('transaction') export class TransactionController { constructor( @@ -39,9 +41,7 @@ export class TransactionController { }); } - getClientDeposit = async ( - transactionInput: Pick, - ) => { + getClientDeposit = async (transactionInput: TransactionRuleData) => { try { const deposit = await this.transactionService.findByClientIdWithinActualMonth( @@ -64,7 +64,7 @@ export class TransactionController { return 0; }; - turnoverRule = async (transactionInput: Pick) => { + turnoverRule = async (transactionInput: TransactionRuleData) => { try { const clientDeposit = await this.getClientDeposit(transactionInput); if (clientDeposit) { @@ -75,13 +75,13 @@ export class TransactionController { } }; - discountRule(transactionInput: Pick) { + discountRule(transactionInput: TransactionRuleData) { return transactionInput.client_id === 42 ? DiscountRuleForClientById.client_42 : false; } - defaultRule(transactionInput: Pick) { + defaultRule(transactionInput: TransactionRuleData) { const commissionAmount = (transactionInput.amount * DefaultCommissionPercentage.percentage) / 100; return commissionAmount < DefaultCommissionAmount.amount @@ -91,9 +91,9 @@ export class TransactionController { async applyRules( rules: (( - transactionInput: Pick, - ) => any)[], - transactionInput: Pick, + transactionInput: TransactionRuleData, + ) => number | false | undefined | Promise)[], + transactionInput: TransactionRuleData, ) { let commissionAmount; for (let i = 0; i < rules.length; i++) { From 296fdb49457f55ebb0596fd5bf221d3e15e950d5 Mon Sep 17 00:00:00 2001 From: Inga Date: Mon, 15 Jan 2024 20:51:37 +0000 Subject: [PATCH 4/6] fix: use base_amount to calculate commission --- .../transaction.controller.spec.ts | 2 +- src/transaction/transaction.controller.ts | 48 +++++++++---------- 2 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/transaction/transaction.controller.spec.ts b/src/transaction/transaction.controller.spec.ts index 521fa29..a8bf324 100644 --- a/src/transaction/transaction.controller.spec.ts +++ b/src/transaction/transaction.controller.spec.ts @@ -72,7 +72,7 @@ describe('TransactionController Unit Tests', () => { it('calling applyRules method minimum result of applied rules should be got', async () => { const mockTransactionInput = { date: '2021-01-05', - amount: 1000.0, + base_amount: 1000.0, currency: 'EUR', client_id: 1, }; diff --git a/src/transaction/transaction.controller.ts b/src/transaction/transaction.controller.ts index 3fd1bc1..5cdd52a 100644 --- a/src/transaction/transaction.controller.ts +++ b/src/transaction/transaction.controller.ts @@ -14,7 +14,7 @@ import { } from './transaction.dto'; import { Transaction } from './transaction.entity'; -type TransactionRuleData = Pick; +type TransactionRuleData = Pick; @Controller('transaction') export class TransactionController { @@ -83,7 +83,8 @@ export class TransactionController { defaultRule(transactionInput: TransactionRuleData) { const commissionAmount = - (transactionInput.amount * DefaultCommissionPercentage.percentage) / 100; + (transactionInput.base_amount * DefaultCommissionPercentage.percentage) / + 100; return commissionAmount < DefaultCommissionAmount.amount ? DefaultCommissionAmount.amount : commissionAmount; @@ -123,12 +124,20 @@ export class TransactionController { }); } - getAmountWithExchange(transactionInput: TransactionInput) { + async getAmountWithExchange(transactionInput: TransactionInput) { + const exchangeRateResponse = await this.getExchangeRate({ + date: transactionInput.date, + }); + + const transactionAmount = parseInt(transactionInput.amount); const transactionData = { date: transactionInput.date, - amount: parseInt(transactionInput.amount), + amount: transactionAmount, currency: transactionInput.currency, client_id: transactionInput.client_id, + base_currency: Currency.EUR, + base_amount: + transactionAmount * exchangeRateResponse[transactionInput.currency], }; const commissionAmount = this.applyRules( @@ -136,22 +145,12 @@ export class TransactionController { transactionData, ); - const exhangeRateInput = { - date: transactionInput.date, - }; - - this.getExchangeRate(exhangeRateInput) - .then((exchangeRateResponse) => - commissionAmount.then((commission) => - this.transactionService.insertOne({ - ...transactionData, - commission, - base_currency: Currency.EUR, - base_amount: - parseInt(transactionInput.amount) * - exchangeRateResponse[transactionInput.currency], - }), - ), + commissionAmount + .then((commission) => + this.transactionService.insertOne({ + ...transactionData, + commission, + }), ) .catch((error) => console.log(error)); @@ -164,6 +163,8 @@ export class TransactionController { amount: parseInt(transactionInput.amount), currency: transactionInput.currency, client_id: transactionInput.client_id, + base_currency: Currency.EUR, + base_amount: parseInt(transactionInput.amount), }; const commissionAmount = await this.applyRules( @@ -173,13 +174,8 @@ export class TransactionController { try { this.transactionService .insertOne({ - date: transactionInput.date, - amount: parseInt(transactionInput.amount), - currency: transactionInput.currency, - client_id: transactionInput.client_id, + ...transactionData, commission: commissionAmount, - base_currency: Currency.EUR, - base_amount: parseInt(transactionInput.amount), }) .catch((error) => console.log(error)); } catch (error) { From dd5c719345433c04f8bccef3c1cd072b5a53c1ba Mon Sep 17 00:00:00 2001 From: Inga Date: Mon, 15 Jan 2024 20:58:03 +0000 Subject: [PATCH 5/6] fix: declared return type on getExchangeRate --- src/transaction/transaction.controller.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transaction/transaction.controller.ts b/src/transaction/transaction.controller.ts index 5cdd52a..f0728e8 100644 --- a/src/transaction/transaction.controller.ts +++ b/src/transaction/transaction.controller.ts @@ -1,5 +1,5 @@ import { Controller, Post, UsePipes, Body } from '@nestjs/common'; -import { ExchangeRateInput } from 'src/exchange-rate/exchange-rate.dto'; +import { ExchangeRateInput, ExchangeRateResponse } from 'src/exchange-rate/exchange-rate.dto'; import { ExchangeRateService } from 'src/exchange-rate/exchange-rate.service'; import { TransactionService } from './transaction.service'; import { transactionBodySchema } from './transaction.validation'; @@ -112,7 +112,7 @@ export class TransactionController { } getExchangeRate(exchangeRateInput: ExchangeRateInput) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { try { this.exchangeRateService.convertCurrency(exchangeRateInput).subscribe({ next: resolve, From 61d8b18c0aa5ccc008ec064a016f2ebb31f78b61 Mon Sep 17 00:00:00 2001 From: Inga Date: Mon, 15 Jan 2024 20:59:03 +0000 Subject: [PATCH 6/6] fix: divide by 'rate' instead of multiplying by it, so that tests pass --- src/transaction/transaction.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transaction/transaction.controller.ts b/src/transaction/transaction.controller.ts index f0728e8..740794d 100644 --- a/src/transaction/transaction.controller.ts +++ b/src/transaction/transaction.controller.ts @@ -137,7 +137,7 @@ export class TransactionController { client_id: transactionInput.client_id, base_currency: Currency.EUR, base_amount: - transactionAmount * exchangeRateResponse[transactionInput.currency], + transactionAmount / exchangeRateResponse[transactionInput.currency], }; const commissionAmount = this.applyRules(