You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
test-assignment-payments/src/transaction/transaction.controller.ts

123 lines
3.8 KiB

import { Controller, Post, UsePipes, Body } from '@nestjs/common';
import _ from 'lodash';
import { BodyValidationPipe } from '../pipes/body.validation.pipe';
import { ExchangeRateService } from '../exchange-rate/exchange-rate.service';
import { promisifyObservable } from '../shared/reactive';
import { getMinimumRuleOutput } from '../shared/rules';
import { TransactionService } from './transaction.service';
import { transactionBodySchema } from './transaction.validation';
import {
Currency,
TransactionInput,
DiscountRuleForClientById,
DefaultCommissionPercentage,
DefaultCommissionAmount,
HighTurnoverDiscount,
} from './transaction.dto';
import { Transaction } from './transaction.entity';
type TransactionRuleData = Pick<Transaction, 'base_amount' | 'client_id'>;
@Controller('transaction')
export class TransactionController {
constructor(
private readonly transactionService: TransactionService,
private readonly exchangeRateService: ExchangeRateService,
) {}
@Post()
@UsePipes(new BodyValidationPipe(transactionBodySchema))
async commission(
@Body() transactionInput: TransactionInput,
): Promise<string> {
return JSON.stringify({
amount: (await this.getCommissionAmount(transactionInput)).toFixed(2),
currency: Currency.EUR,
});
}
getClientDeposit = async (transactionInput: TransactionRuleData) => {
try {
const existingTransactions = await this.transactionService.findByClientIdWithinActualMonth(
transactionInput.client_id,
);
return _.sum(existingTransactions.map(t => t.base_amount));
} catch (error) {
console.log(error);
return 0;
}
};
turnoverRule = async (transactionInput: TransactionRuleData) => {
try {
const clientDeposit = await this.getClientDeposit(transactionInput);
if (clientDeposit) {
return clientDeposit > 1000 ? HighTurnoverDiscount.amount : false;
}
} catch (error) {
console.log(error);
}
};
discountRule(transactionInput: TransactionRuleData) {
return transactionInput.client_id === 42
? DiscountRuleForClientById.client_42
: false;
}
defaultRule(transactionInput: TransactionRuleData) {
const commissionAmount =
(transactionInput.base_amount * DefaultCommissionPercentage.percentage) /
100;
return commissionAmount < DefaultCommissionAmount.amount
? DefaultCommissionAmount.amount
: commissionAmount;
}
async applyRules(transactionInput: TransactionRuleData) {
return getMinimumRuleOutput(
[this.turnoverRule, this.discountRule, this.defaultRule],
transactionInput,
);
}
getExchangeRateResponse = promisifyObservable(this.exchangeRateService.convertCurrency.bind(this.exchangeRateService));
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,
amount: transactionAmount,
currency: transactionInput.currency,
client_id: transactionInput.client_id,
base_currency: Currency.EUR,
base_amount: transactionAmount / exchangeRate,
};
const commission = await this.applyRules(transactionData);
this.transactionService
.insertOne({
...transactionData,
commission,
})
.catch((error) => console.log(error));
return commission;
}
}