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

192 lines
5.5 KiB

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';
import { BodyValidationPipe } from '../pipes/body.validation.pipe';
import {
Currency,
TransactionInput,
DiscountRuleForClientById,
DefaultCommissionPercentage,
DefaultCommissionAmount,
HighTurnoverDiscount,
} from './transaction.dto';
import { Transaction } from './transaction.entity';
type TransactionRuleData = Pick<Transaction, '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:
transactionInput.currency !== Currency.EUR
? parseFloat(
await this.getAmountWithExchange(transactionInput),
).toFixed(2)
: parseFloat(
await this.getAmountWithoutExchange(transactionInput),
).toFixed(2),
currency: Currency.EUR,
});
}
getClientDeposit = async (transactionInput: TransactionRuleData) => {
try {
const deposit =
await this.transactionService.findByClientIdWithinActualMonth(
transactionInput.client_id,
);
if (deposit) {
const initialDeposit = 0;
const totalDeposit = (await deposit).reduce(
(prevAmmount, transactionAmmount) =>
prevAmmount + transactionAmmount.base_amount,
initialDeposit,
);
return totalDeposit;
}
} 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.amount * DefaultCommissionPercentage.percentage) / 100;
return commissionAmount < DefaultCommissionAmount.amount
? DefaultCommissionAmount.amount
: commissionAmount;
}
async applyRules(
rules: ((
transactionInput: TransactionRuleData,
) => number | false | undefined | Promise<number | false | undefined>)[],
transactionInput: TransactionRuleData,
) {
let commissionAmount;
for (let i = 0; i < rules.length; i++) {
const ruleResult = await rules[i](transactionInput);
if (ruleResult) {
commissionAmount = ruleResult;
break;
} else {
continue;
}
}
return commissionAmount
? commissionAmount
: 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 transactionData = {
date: transactionInput.date,
amount: parseInt(transactionInput.amount),
currency: transactionInput.currency,
client_id: transactionInput.client_id,
};
const commissionAmount = this.applyRules(
[this.turnoverRule, this.discountRule],
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],
}),
),
)
.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,
};
const commissionAmount = await this.applyRules(
[this.turnoverRule, this.discountRule],
transactionData,
);
try {
this.transactionService
.insertOne({
date: transactionInput.date,
amount: parseInt(transactionInput.amount),
currency: transactionInput.currency,
client_id: transactionInput.client_id,
commission: commissionAmount,
base_currency: Currency.EUR,
base_amount: parseInt(transactionInput.amount),
})
.catch((error) => console.log(error));
} catch (error) {
console.log(error);
throw error;
}
return commissionAmount;
}
}