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.
179 lines
5.2 KiB
179 lines
5.2 KiB
import { Controller, Post, UsePipes, Body } from '@nestjs/common';
|
|
import _ from 'lodash';
|
|
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';
|
|
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, '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:
|
|
transactionInput.currency !== Currency.EUR
|
|
? (await this.getAmountWithExchange(transactionInput)).toFixed(2)
|
|
: (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.base_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,
|
|
) {
|
|
const commissions = await Promise.all(
|
|
[...rules, this.defaultRule].map((rule) => rule(transactionInput)),
|
|
);
|
|
return Math.min(..._.compact(commissions));
|
|
}
|
|
|
|
getExchangeRate(exchangeRateInput: ExchangeRateInput) {
|
|
return new Promise<ExchangeRateResponse>((resolve, reject) => {
|
|
try {
|
|
this.exchangeRateService.convertCurrency(exchangeRateInput).subscribe({
|
|
next: resolve,
|
|
error: reject,
|
|
});
|
|
} catch (error) {
|
|
reject(error);
|
|
}
|
|
});
|
|
}
|
|
|
|
async getAmountWithExchange(transactionInput: TransactionInput) {
|
|
const exchangeRateResponse = await this.getExchangeRate({
|
|
date: transactionInput.date,
|
|
});
|
|
|
|
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 / exchangeRateResponse[transactionInput.currency],
|
|
};
|
|
|
|
const commissionAmount = this.applyRules(
|
|
[this.turnoverRule, this.discountRule],
|
|
transactionData,
|
|
);
|
|
|
|
commissionAmount
|
|
.then((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;
|
|
}
|
|
}
|
|
|