Compare commits

..

1 Commits

  1. 2
      src/shared/reactive.ts
  2. 13
      src/shared/rules.ts
  3. 36
      src/transaction/transaction.controller.spec.ts
  4. 29
      src/transaction/transaction.controller.ts
  5. 8
      src/transaction/transaction.dto.ts

@ -4,7 +4,7 @@ export const promisifyObservable =
<TArgs extends any[], TOutput>(f: (...args: TArgs) => Observable<TOutput>) => <TArgs extends any[], TOutput>(f: (...args: TArgs) => Observable<TOutput>) =>
(...args: TArgs) => (...args: TArgs) =>
new Promise<TOutput>((_resolve, _reject) => { new Promise<TOutput>((_resolve, _reject) => {
let isCalled = false; const isCalled = false;
const resolve = (result: TOutput) => { const resolve = (result: TOutput) => {
if (isCalled) { if (isCalled) {
console.log('observable emits more than once'); console.log('observable emits more than once');

@ -6,6 +6,15 @@ export const getMinimumRuleOutput = async <T>(
rules: ((input: T) => RuleResult | Promise<RuleResult>)[], rules: ((input: T) => RuleResult | Promise<RuleResult>)[],
input: T, input: T,
) => { ) => {
const results = await Promise.all(rules.map((rule) => rule(input))); const results = await Promise.all(
return Math.min(..._.compact(results)); rules.map(async (rule) => {
const result = await rule(input);
if (result === undefined || result === false) {
return null;
}
return { commission: result };
}),
);
return Math.min(..._.compact(results).map(({ commission }) => commission));
}; };

@ -72,6 +72,7 @@ describe('TransactionController Unit Tests', () => {
it('calling applyRules method minimum result of applied rules should be got', async () => { it('calling applyRules method minimum result of applied rules should be got', async () => {
{ {
const result = await transactionController.applyRules({ const result = await transactionController.applyRules({
date: '2021-01-05',
base_amount: 1000, base_amount: 1000,
client_id: 1, client_id: 1,
}); });
@ -81,6 +82,7 @@ describe('TransactionController Unit Tests', () => {
{ {
const result = await transactionController.applyRules({ const result = await transactionController.applyRules({
date: '2021-01-05',
base_amount: 1, base_amount: 1,
client_id: 1, client_id: 1,
}); });
@ -90,6 +92,37 @@ describe('TransactionController Unit Tests', () => {
{ {
const result = await transactionController.applyRules({ const result = await transactionController.applyRules({
date: '2021-01-01',
base_amount: 1,
client_id: 1,
});
expect(result).to.eql(0);
}
{
const result = await transactionController.applyRules({
date: '2021-01-05',
base_amount: 1000,
client_id: 20,
});
expect(result).to.eql(0.01);
}
{
const result = await transactionController.applyRules({
date: '2021-01-05',
base_amount: 1,
client_id: 20,
});
expect(result).to.eql(0.01);
}
{
const result = await transactionController.applyRules({
date: '2021-01-05',
base_amount: 1000, base_amount: 1000,
client_id: 42, client_id: 42,
}); });
@ -99,6 +132,7 @@ describe('TransactionController Unit Tests', () => {
{ {
const result = await transactionController.applyRules({ const result = await transactionController.applyRules({
date: '2021-01-05',
base_amount: 1, base_amount: 1,
client_id: 42, client_id: 42,
}); });
@ -108,6 +142,7 @@ describe('TransactionController Unit Tests', () => {
{ {
const result = await transactionController.applyRules({ const result = await transactionController.applyRules({
date: '2021-01-05',
base_amount: 1000, base_amount: 1000,
client_id: 99, client_id: 99,
}); });
@ -117,6 +152,7 @@ describe('TransactionController Unit Tests', () => {
{ {
const result = await transactionController.applyRules({ const result = await transactionController.applyRules({
date: '2021-01-05',
base_amount: 1, base_amount: 1,
client_id: 99, client_id: 99,
}); });

@ -1,5 +1,5 @@
import { Controller, Post, UsePipes, Body } from '@nestjs/common'; import { Controller, Post, UsePipes, Body } from '@nestjs/common';
import _ from 'lodash'; import _, { endsWith } from 'lodash';
import { BodyValidationPipe } from '../pipes/body.validation.pipe'; import { BodyValidationPipe } from '../pipes/body.validation.pipe';
import { ExchangeRateService } from '../exchange-rate/exchange-rate.service'; import { ExchangeRateService } from '../exchange-rate/exchange-rate.service';
import { promisifyObservable } from '../shared/reactive'; import { promisifyObservable } from '../shared/reactive';
@ -9,14 +9,14 @@ import { transactionBodySchema } from './transaction.validation';
import { import {
Currency, Currency,
TransactionInput, TransactionInput,
DiscountRuleForClientById,
DefaultCommissionPercentage, DefaultCommissionPercentage,
DefaultCommissionAmount, DefaultCommissionAmount,
HighTurnoverDiscount, HighTurnoverDiscount,
DISCOUNTED_COMMISSIONS,
} from './transaction.dto'; } from './transaction.dto';
import { Transaction } from './transaction.entity'; import { Transaction } from './transaction.entity';
type TransactionRuleData = Pick<Transaction, 'base_amount' | 'client_id'>; type TransactionRuleData = Pick<Transaction, 'base_amount' | 'client_id' | 'date'>;
@Controller('transaction') @Controller('transaction')
export class TransactionController { export class TransactionController {
@ -38,11 +38,12 @@ export class TransactionController {
getClientDeposit = async (transactionInput: TransactionRuleData) => { getClientDeposit = async (transactionInput: TransactionRuleData) => {
try { try {
const existingTransactions = await this.transactionService.findByClientIdWithinActualMonth( const existingTransactions =
await this.transactionService.findByClientIdWithinActualMonth(
transactionInput.client_id, transactionInput.client_id,
); );
return _.sum(existingTransactions.map(t => t.base_amount)); return _.sum(existingTransactions.map((t) => t.base_amount));
} catch (error) { } catch (error) {
console.log(error); console.log(error);
return 0; return 0;
@ -61,8 +62,8 @@ export class TransactionController {
}; };
discountRule(transactionInput: TransactionRuleData) { discountRule(transactionInput: TransactionRuleData) {
return transactionInput.client_id === 42 return DISCOUNTED_COMMISSIONS.has(transactionInput.client_id)
? DiscountRuleForClientById.client_42 ? DISCOUNTED_COMMISSIONS.get(transactionInput.client_id)
: false; : false;
} }
@ -75,14 +76,24 @@ export class TransactionController {
: commissionAmount; : commissionAmount;
} }
firstDayOfMonthFreeRule(transactionInput: TransactionRuleData) {
if (transactionInput.date.endsWith('-01')) {
return 0;
}
return undefined;
}
async applyRules(transactionInput: TransactionRuleData) { async applyRules(transactionInput: TransactionRuleData) {
return getMinimumRuleOutput( return getMinimumRuleOutput(
[this.turnoverRule, this.discountRule, this.defaultRule], [this.turnoverRule, this.discountRule, this.firstDayOfMonthFreeRule, this.defaultRule],
transactionInput, transactionInput,
); );
} }
getExchangeRateResponse = promisifyObservable(this.exchangeRateService.convertCurrency.bind(this.exchangeRateService)); getExchangeRateResponse = promisifyObservable(
this.exchangeRateService.convertCurrency.bind(this.exchangeRateService),
);
async getExchangeRateIfNeeded(transactionInput: TransactionInput) { async getExchangeRateIfNeeded(transactionInput: TransactionInput) {
if (transactionInput.currency === Currency.EUR) { if (transactionInput.currency === Currency.EUR) {

@ -9,9 +9,11 @@ export enum Currency {
EUR = 'EUR', EUR = 'EUR',
} }
export enum DiscountRuleForClientById { export const DISCOUNTED_COMMISSIONS = new Map<number, number>([
client_42 = 0.05, [42, 0.05],
} [10, 0.02],
[20, 0.01]
]);
export enum DefaultCommissionPercentage { export enum DefaultCommissionPercentage {
percentage = 0.5, percentage = 0.5,

Loading…
Cancel
Save