tables/payments: Сезонные
This commit is contained in:
parent
2d2d5d6cc3
commit
3cb0a6a73c
@ -36,6 +36,16 @@ export const selectSeasonType = [
|
||||
},
|
||||
];
|
||||
|
||||
const selectHighSeasonStart = Array.from(
|
||||
{
|
||||
length: 12,
|
||||
},
|
||||
(_, i) => ({
|
||||
label: `${i + 2}`,
|
||||
value: 100_000_000 + i,
|
||||
})
|
||||
);
|
||||
|
||||
const defaultOptions: CalculationOptions = {
|
||||
radioLastPaymentRule: [
|
||||
{
|
||||
@ -88,15 +98,7 @@ const defaultOptions: CalculationOptions = {
|
||||
|
||||
selectSeasonType,
|
||||
|
||||
selectHighSeasonStart: Array.from(
|
||||
{
|
||||
length: 12,
|
||||
},
|
||||
(_, i) => ({
|
||||
label: `${i + 2}`,
|
||||
value: 100_000_000 + i,
|
||||
})
|
||||
),
|
||||
selectHighSeasonStart,
|
||||
|
||||
radioDeliveryTime: [
|
||||
{
|
||||
|
||||
@ -24,7 +24,7 @@ const defaultValues: CalculationValues = {
|
||||
graphType: 100_000_000,
|
||||
parmentsDecreasePercent: 94,
|
||||
seasonType: null,
|
||||
highSeasonStart: 100_000_000,
|
||||
highSeasonStart: null,
|
||||
comissionPerc: 0,
|
||||
comissionRub: 0,
|
||||
saleBonus: 1.3,
|
||||
|
||||
20
process/payments/lib/__tests__/seasons.test.js
Normal file
20
process/payments/lib/__tests__/seasons.test.js
Normal file
@ -0,0 +1,20 @@
|
||||
import { getSeasonsIndex } from '../seasons';
|
||||
|
||||
describe('process/payments/lib/seasons', () => {
|
||||
describe('[function] getSeasonsIndex', () => {
|
||||
test('should return 0', () => {
|
||||
const result = getSeasonsIndex(0);
|
||||
expect(result).toEqual(0);
|
||||
});
|
||||
|
||||
test('should return 7', () => {
|
||||
const result = getSeasonsIndex(7);
|
||||
expect(result).toEqual(7);
|
||||
});
|
||||
|
||||
test('should return 7', () => {
|
||||
const result = getSeasonsIndex(55);
|
||||
expect(result).toEqual(7);
|
||||
});
|
||||
});
|
||||
});
|
||||
65
process/payments/lib/seasons.ts
Normal file
65
process/payments/lib/seasons.ts
Normal file
@ -0,0 +1,65 @@
|
||||
/* eslint-disable implicit-arrow-linebreak */
|
||||
import type { CalculationValues } from 'stores/calculation/values/types';
|
||||
|
||||
type SeasonType = NonNullable<CalculationValues['seasonType']>;
|
||||
type LeasingPeriod = CalculationValues['leasingPeriod'];
|
||||
|
||||
const SEASONS_PERIODS: Record<number, Array<number>> = {
|
||||
100_000_000: [0, 6],
|
||||
100_000_001: [0, 8],
|
||||
100_000_002: [0, 4, 8],
|
||||
};
|
||||
export const SEASONS_PERIOD_NUMBER = 12;
|
||||
|
||||
export function getSeasonsIndex(paymentsIndex: number) {
|
||||
return paymentsIndex - Math.floor(paymentsIndex / SEASONS_PERIOD_NUMBER) * SEASONS_PERIOD_NUMBER;
|
||||
}
|
||||
|
||||
export function getPositionIndex(seasonType: SeasonType, seasonsIndex: number) {
|
||||
let positionIndex = 0;
|
||||
|
||||
SEASONS_PERIODS[seasonType].forEach((position, i) => {
|
||||
if (seasonsIndex >= position) {
|
||||
positionIndex = i;
|
||||
}
|
||||
});
|
||||
|
||||
return positionIndex;
|
||||
}
|
||||
|
||||
export function getSeasonsValues(seasonType: SeasonType, seasons: Array<number>) {
|
||||
return SEASONS_PERIODS[seasonType].map((index) => seasons[index]);
|
||||
}
|
||||
|
||||
export function generateSeasons(seasonType: SeasonType, values: Array<number>) {
|
||||
const positions = SEASONS_PERIODS[seasonType];
|
||||
|
||||
const seasons: Array<number> = [];
|
||||
|
||||
[...positions, SEASONS_PERIOD_NUMBER].forEach((position, i, arr) => {
|
||||
if (position < SEASONS_PERIOD_NUMBER) {
|
||||
const start = arr[i];
|
||||
const end = arr[i + 1];
|
||||
const value = values[i];
|
||||
|
||||
// eslint-disable-next-line unicorn/new-for-builtins
|
||||
seasons.push(...Array(end - start).fill(value));
|
||||
}
|
||||
});
|
||||
|
||||
return seasons;
|
||||
}
|
||||
|
||||
export function generateSeasonsPayments(leasingPeriod: LeasingPeriod, seasons: Array<number>) {
|
||||
return (
|
||||
Array.from(
|
||||
{
|
||||
length: Math.floor((leasingPeriod - 2) / SEASONS_PERIOD_NUMBER),
|
||||
},
|
||||
() => seasons
|
||||
)
|
||||
.flat()
|
||||
// eslint-disable-next-line unicorn/prefer-spread
|
||||
.concat(seasons.slice(0, (leasingPeriod - 2) % SEASONS_PERIOD_NUMBER))
|
||||
);
|
||||
}
|
||||
@ -7,7 +7,8 @@ import { comparer, reaction, toJS } from 'mobx';
|
||||
import type { CalculationOptions } from 'stores/calculation/options/types';
|
||||
import type RootStore from 'stores/root';
|
||||
import type { Row } from 'stores/tables/payments/types';
|
||||
import { difference } from 'tools/array';
|
||||
import { difference, shift } from 'tools/array';
|
||||
import * as seasonsTools from './lib/seasons';
|
||||
import validatePaymentsTable from './validation';
|
||||
|
||||
export default function paymentsReactions(
|
||||
@ -189,6 +190,7 @@ export default function paymentsReactions(
|
||||
/**
|
||||
* Дегрессия
|
||||
*/
|
||||
// TODO: const exeption = tarif?.evo_seasons_type_exception
|
||||
const degressionSeasonTypes = new Set([
|
||||
100_000_003, 100_000_004, 100_000_005, 100_000_006, 100_000_007,
|
||||
]);
|
||||
@ -329,39 +331,181 @@ export default function paymentsReactions(
|
||||
},
|
||||
(nextParams, prevParams) => {
|
||||
if (nextParams.graphType === 100_000_001 && nextParams.degressionType === 100_000_007) {
|
||||
const index = difference(nextParams.payments, prevParams.payments);
|
||||
const changes = difference(nextParams.payments, prevParams.payments);
|
||||
|
||||
if (index) {
|
||||
const value = nextParams.payments[index];
|
||||
const payments = nextParams.payments.slice(1, -1).map((payment, i) => {
|
||||
if (i <= index - 2) return payment;
|
||||
if (changes === null || changes.length > 1) return;
|
||||
|
||||
return value;
|
||||
});
|
||||
const [changeIndex] = changes;
|
||||
const value = nextParams.payments[changeIndex];
|
||||
const payments = nextParams.payments.slice(1, -1).map((payment, i) => {
|
||||
if (i <= changeIndex - 2) return payment;
|
||||
|
||||
const firstPaymentPerc = $calculation.getElementValue('tbxFirstPaymentPerc');
|
||||
const lastPaymentPerc = $calculation.getElementValue('tbxLastPaymentPerc');
|
||||
return value;
|
||||
});
|
||||
|
||||
$tables.payments.setValues([firstPaymentPerc, ...payments, lastPaymentPerc]);
|
||||
}
|
||||
const firstPaymentPerc = $calculation.getElementValue('tbxFirstPaymentPerc');
|
||||
const lastPaymentPerc = $calculation.getElementValue('tbxLastPaymentPerc');
|
||||
|
||||
$tables.payments.setValues([firstPaymentPerc, ...payments, lastPaymentPerc]);
|
||||
}
|
||||
},
|
||||
{
|
||||
delay: 10,
|
||||
delay: 50,
|
||||
equals: comparer.structural,
|
||||
}
|
||||
);
|
||||
|
||||
let removeError: () => void;
|
||||
/**
|
||||
* Сезонный
|
||||
*/
|
||||
|
||||
reaction(
|
||||
() => {
|
||||
const graphType = $calculation.getElementValue('radioGraphType');
|
||||
const payments = toJS($tables.payments.values);
|
||||
|
||||
return {
|
||||
graphType,
|
||||
};
|
||||
},
|
||||
({ graphType }) => {
|
||||
if (graphType !== 100_000_003) return;
|
||||
|
||||
const seasonType = $calculation.getElementValue('selectSeasonType');
|
||||
const highSeasonStart = $calculation.getElementValue('selectHighSeasonStart');
|
||||
|
||||
if (!seasonType || !highSeasonStart) {
|
||||
$tables.payments.setValues([]);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const defaultSeasonsValues = [100, 75, 50];
|
||||
|
||||
reaction(
|
||||
() => {
|
||||
const seasonType = $calculation.getElementValue('selectSeasonType');
|
||||
const highSeasonStartOption = $calculation.getElementOption('selectHighSeasonStart');
|
||||
const leasingPeriod = $calculation.getElementValue('tbxLeasingPeriod');
|
||||
|
||||
return {
|
||||
seasonType,
|
||||
highSeasonStartOption,
|
||||
leasingPeriod,
|
||||
};
|
||||
},
|
||||
({ seasonType, highSeasonStartOption, leasingPeriod }) => {
|
||||
const graphType = $calculation.getElementValue('radioGraphType');
|
||||
if (graphType !== 100_000_003) return;
|
||||
|
||||
if (!seasonType || !highSeasonStartOption) {
|
||||
$tables.payments.setValues([]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const seasons = seasonsTools.generateSeasons(seasonType, defaultSeasonsValues);
|
||||
const payments = seasonsTools.generateSeasonsPayments(leasingPeriod, seasons);
|
||||
const rows: Row[] = payments.map((value) => ({
|
||||
value,
|
||||
status: 'Default',
|
||||
}));
|
||||
|
||||
const firstPaymentPerc = $calculation.getElementValue('tbxFirstPaymentPerc');
|
||||
const lastPaymentPerc = $calculation.getElementValue('tbxLastPaymentPerc');
|
||||
|
||||
const shiftNumber = Number.parseInt(highSeasonStartOption.label, 10) - 2;
|
||||
|
||||
$tables.payments.setRows([
|
||||
{
|
||||
value: firstPaymentPerc,
|
||||
status: 'Disabled',
|
||||
},
|
||||
...shift(rows, shiftNumber),
|
||||
{
|
||||
value: lastPaymentPerc,
|
||||
status: 'Disabled',
|
||||
},
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
reaction(
|
||||
() => {
|
||||
const payments = toJS($tables.payments.values);
|
||||
|
||||
return payments;
|
||||
},
|
||||
(nextPayments, prevPayments) => {
|
||||
const graphType = $calculation.getElementValue('radioGraphType');
|
||||
if (graphType !== 100_000_003) return;
|
||||
|
||||
const seasonType = $calculation.getElementValue('selectSeasonType');
|
||||
const highSeasonStartOption = $calculation.getElementOption('selectHighSeasonStart');
|
||||
if (!seasonType || !highSeasonStartOption) return;
|
||||
|
||||
const shiftNumber = Number.parseInt(highSeasonStartOption.label, 10) - 2;
|
||||
const unshiftedNextPayments = shift(nextPayments.slice(1, -1), -shiftNumber);
|
||||
const unshiftedPrevPayments = shift(prevPayments.slice(1, -1), -shiftNumber);
|
||||
|
||||
const changes = difference(unshiftedNextPayments, unshiftedPrevPayments);
|
||||
if (changes === null || changes.length > 1) return;
|
||||
const [changeIndex] = changes;
|
||||
|
||||
const nextSeasons = unshiftedNextPayments.slice(0, seasonsTools.SEASONS_PERIOD_NUMBER + 1);
|
||||
const values = seasonsTools.getSeasonsValues(seasonType, nextSeasons);
|
||||
|
||||
const positionIndex = seasonsTools.getPositionIndex(
|
||||
seasonType,
|
||||
seasonsTools.getSeasonsIndex(changeIndex)
|
||||
);
|
||||
values[positionIndex] = unshiftedNextPayments[changeIndex];
|
||||
|
||||
const seasons = seasonsTools.generateSeasons(seasonType, values);
|
||||
const leasingPeriod = $calculation.getElementValue('tbxLeasingPeriod');
|
||||
const payments = seasonsTools.generateSeasonsPayments(leasingPeriod, seasons);
|
||||
const rows: Row[] = payments.map((value) => ({
|
||||
value,
|
||||
status: 'Default',
|
||||
}));
|
||||
|
||||
const firstPaymentPerc = $calculation.getElementValue('tbxFirstPaymentPerc');
|
||||
const lastPaymentPerc = $calculation.getElementValue('tbxLastPaymentPerc');
|
||||
|
||||
$tables.payments.setRows([
|
||||
{
|
||||
value: firstPaymentPerc,
|
||||
status: 'Disabled',
|
||||
},
|
||||
...shift(rows, shiftNumber),
|
||||
{
|
||||
value: lastPaymentPerc,
|
||||
status: 'Disabled',
|
||||
},
|
||||
]);
|
||||
},
|
||||
{
|
||||
delay: 50,
|
||||
equals: comparer.structural,
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* Валидация
|
||||
*/
|
||||
let removeError: () => void;
|
||||
|
||||
reaction(
|
||||
() => {
|
||||
const payments = toJS($tables.payments.values);
|
||||
const graphType = $calculation.getElementValue('radioGraphType');
|
||||
const seasonType = $calculation.getElementValue('selectSeasonType');
|
||||
const highSeasonStart = $calculation.getElementValue('selectHighSeasonStart');
|
||||
|
||||
return {
|
||||
payments,
|
||||
graphType,
|
||||
seasonType,
|
||||
highSeasonStart,
|
||||
};
|
||||
},
|
||||
({ payments }) => {
|
||||
@ -373,7 +517,7 @@ export default function paymentsReactions(
|
||||
}
|
||||
},
|
||||
{
|
||||
delay: 10,
|
||||
delay: 50,
|
||||
equals: comparer.structural,
|
||||
}
|
||||
);
|
||||
|
||||
@ -79,6 +79,10 @@ export default function validatePaymentsTable(
|
||||
return 'Не выбран тип сезонности';
|
||||
}
|
||||
|
||||
if (!values.highSeasonStart) {
|
||||
return 'Не выбрано смещение сезонности';
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -95,6 +99,7 @@ export default function validatePaymentsTable(
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
39
tools/__tests__/array.test.js
Normal file
39
tools/__tests__/array.test.js
Normal file
@ -0,0 +1,39 @@
|
||||
import { shift } from 'tools/array';
|
||||
|
||||
describe('tools/array', () => {
|
||||
describe('[function] shift', () => {
|
||||
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
test('should shift array right 3 positions', () => {
|
||||
const result = shift(arr, 3);
|
||||
expect(result).toEqual([7, 8, 9, 1, 2, 3, 4, 5, 6]);
|
||||
});
|
||||
test('should shift array left 3 positions', () => {
|
||||
const result = shift(arr, -3);
|
||||
expect(result).toEqual([4, 5, 6, 7, 8, 9, 1, 2, 3]);
|
||||
});
|
||||
test('should shift array right 6 positions', () => {
|
||||
const result = shift(arr, 15);
|
||||
expect(result).toEqual([4, 5, 6, 7, 8, 9, 1, 2, 3]);
|
||||
});
|
||||
test('should shift array left 6 positions', () => {
|
||||
const result = shift(arr, -15);
|
||||
expect(result).toEqual([7, 8, 9, 1, 2, 3, 4, 5, 6]);
|
||||
});
|
||||
test('should keep array as is', () => {
|
||||
const result = shift(arr, 0);
|
||||
expect(result).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
});
|
||||
test('should keep array as is', () => {
|
||||
const result = shift(arr, 9);
|
||||
expect(result).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
});
|
||||
test('should return empty array', () => {
|
||||
const result = shift([], 0);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
test('should return empty array', () => {
|
||||
const result = shift([], 0);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -4,12 +4,24 @@ export function areEqual<T>(arr1: ReadonlyArray<T>, arr2: ReadonlyArray<T>) {
|
||||
|
||||
export function difference<T>(arr1: ReadonlyArray<T>, arr2: ReadonlyArray<T>) {
|
||||
if (arr1.length !== arr2.length) return null;
|
||||
|
||||
const changes = [];
|
||||
// eslint-disable-next-line unicorn/no-for-loop
|
||||
for (let i = 0; i < arr1.length; i += 1) {
|
||||
if (arr1[i] !== arr2[i]) {
|
||||
return i;
|
||||
changes.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return changes;
|
||||
}
|
||||
|
||||
export function shift<T>(arr: Array<T>, n: number) {
|
||||
if (arr.length === 0) return arr;
|
||||
|
||||
const shiftNumber = n % arr.length;
|
||||
|
||||
if (shiftNumber === 0) return arr;
|
||||
|
||||
return [...arr.slice(-shiftNumber, arr.length), ...arr.slice(0, -shiftNumber)];
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user