diff --git a/src/shared/rules.ts b/src/shared/rules.ts new file mode 100644 index 0000000..481e066 --- /dev/null +++ b/src/shared/rules.ts @@ -0,0 +1,11 @@ +import _ from 'lodash'; + +type RuleResult = number | false | undefined; + +export const getMinimumRuleOutput = async ( + rules: ((input: T) => RuleResult | Promise)[], + input: T, +) => { + const results = await Promise.all(rules.map((rule) => rule(input))); + return Math.min(..._.compact(results)); +}; diff --git a/src/transaction/transaction.controller.spec.ts b/src/transaction/transaction.controller.spec.ts index 91a424a..6849cb2 100644 --- a/src/transaction/transaction.controller.spec.ts +++ b/src/transaction/transaction.controller.spec.ts @@ -71,91 +71,55 @@ describe('TransactionController Unit Tests', () => { it('calling applyRules method minimum result of applied rules should be got', async () => { { - const result = await transactionController.applyRules( - [ - transactionController.discountRule, - transactionController.turnoverRule, - ], - { - base_amount: 1000, - client_id: 1, - }, - ); + const result = await transactionController.applyRules({ + base_amount: 1000, + client_id: 1, + }); expect(result).to.eql(5); } { - const result = await transactionController.applyRules( - [ - transactionController.discountRule, - transactionController.turnoverRule, - ], - { - base_amount: 1, - client_id: 1, - }, - ); + const result = await transactionController.applyRules({ + base_amount: 1, + client_id: 1, + }); expect(result).to.eql(0.05); } { - const result = await transactionController.applyRules( - [ - transactionController.discountRule, - transactionController.turnoverRule, - ], - { - base_amount: 1000, - client_id: 42, - }, - ); + const result = await transactionController.applyRules({ + base_amount: 1000, + client_id: 42, + }); expect(result).to.eql(0.03); } { - const result = await transactionController.applyRules( - [ - transactionController.discountRule, - transactionController.turnoverRule, - ], - { - base_amount: 1, - client_id: 42, - }, - ); + const result = await transactionController.applyRules({ + base_amount: 1, + client_id: 42, + }); expect(result).to.eql(0.03); } { - const result = await transactionController.applyRules( - [ - transactionController.discountRule, - transactionController.turnoverRule, - ], - { - base_amount: 1000, - client_id: 99, - }, - ); + const result = await transactionController.applyRules({ + base_amount: 1000, + client_id: 99, + }); expect(result).to.eql(0.03); } { - const result = await transactionController.applyRules( - [ - transactionController.discountRule, - transactionController.turnoverRule, - ], - { - base_amount: 1, - client_id: 99, - }, - ); + const result = await transactionController.applyRules({ + base_amount: 1, + client_id: 99, + }); expect(result).to.eql(0.03); } diff --git a/src/transaction/transaction.controller.ts b/src/transaction/transaction.controller.ts index 0a8c5ca..9bbd985 100644 --- a/src/transaction/transaction.controller.ts +++ b/src/transaction/transaction.controller.ts @@ -1,5 +1,4 @@ import { Controller, Post, UsePipes, Body } from '@nestjs/common'; -import _ from 'lodash'; import { ExchangeRateInput, ExchangeRateResponse, @@ -17,6 +16,7 @@ import { HighTurnoverDiscount, } from './transaction.dto'; import { Transaction } from './transaction.entity'; +import { getMinimumRuleOutput } from 'src/shared/rules'; type TransactionRuleData = Pick; @@ -33,10 +33,7 @@ export class TransactionController { @Body() transactionInput: TransactionInput, ): Promise { return JSON.stringify({ - amount: - transactionInput.currency !== Currency.EUR - ? (await this.getAmountWithExchange(transactionInput)).toFixed(2) - : (await this.getAmountWithoutExchange(transactionInput)).toFixed(2), + amount: (await this.getCommissionAmount(transactionInput)).toFixed(2), currency: Currency.EUR, }); } @@ -90,19 +87,14 @@ export class TransactionController { : commissionAmount; } - async applyRules( - rules: (( - transactionInput: TransactionRuleData, - ) => number | false | undefined | Promise)[], - transactionInput: TransactionRuleData, - ) { - const commissions = await Promise.all( - [...rules, this.defaultRule].map((rule) => rule(transactionInput)), + async applyRules(transactionInput: TransactionRuleData) { + return getMinimumRuleOutput( + [this.turnoverRule, this.discountRule, this.defaultRule], + transactionInput, ); - return Math.min(..._.compact(commissions)); } - getExchangeRate(exchangeRateInput: ExchangeRateInput) { + getExchangeRateResponse(exchangeRateInput: ExchangeRateInput) { return new Promise((resolve, reject) => { try { this.exchangeRateService.convertCurrency(exchangeRateInput).subscribe({ @@ -115,11 +107,21 @@ export class TransactionController { }); } - async getAmountWithExchange(transactionInput: TransactionInput) { - const exchangeRateResponse = await this.getExchangeRate({ + async getExchangeRateIfNeeded(transactionInput: TransactionInput) { + if (transactionInput.currency === Currency.EUR) { + return 1; + } + + const exchangeRateResponse = await this.getExchangeRateResponse({ date: transactionInput.date, }); + return exchangeRateResponse[transactionInput.currency]; + } + + async getCommissionAmount(transactionInput: TransactionInput) { + const exchangeRate = await this.getExchangeRateIfNeeded(transactionInput); + const transactionAmount = parseInt(transactionInput.amount); const transactionData = { date: transactionInput.date, @@ -127,53 +129,18 @@ export class TransactionController { currency: transactionInput.currency, client_id: transactionInput.client_id, base_currency: Currency.EUR, - base_amount: - transactionAmount / exchangeRateResponse[transactionInput.currency], + base_amount: transactionAmount / exchangeRate, }; - const commissionAmount = this.applyRules( - [this.turnoverRule, this.discountRule], - transactionData, - ); + const commission = await this.applyRules(transactionData); - commissionAmount - .then((commission) => - this.transactionService.insertOne({ - ...transactionData, - commission, - }), - ) + this.transactionService + .insertOne({ + ...transactionData, + commission, + }) .catch((error) => console.log(error)); - return commissionAmount; - } - - async getAmountWithoutExchange(transactionInput: TransactionInput) { - const transactionData = { - date: transactionInput.date, - 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( - [this.turnoverRule, this.discountRule], - transactionData, - ); - try { - this.transactionService - .insertOne({ - ...transactionData, - commission: commissionAmount, - }) - .catch((error) => console.log(error)); - } catch (error) { - console.log(error); - throw error; - } - - return commissionAmount; + return commission; } }