594 lines
18 KiB
TypeScript
594 lines
18 KiB
TypeScript
/* eslint-disable function-paren-newline */
|
|
/* eslint-disable implicit-arrow-linebreak */
|
|
import type { ApolloClient } from '@apollo/client';
|
|
import type { QueryClient } from '@tanstack/react-query';
|
|
import { selectHighSeasonStart, selectSeasonType } from 'config/default-options';
|
|
import { comparer, reaction, toJS } from 'mobx';
|
|
import { shift } from 'radash';
|
|
import type { CalculationOptions } from 'stores/calculation/options/types';
|
|
import type RootStore from 'stores/root';
|
|
import type { Row } from 'stores/tables/payments/types';
|
|
import ValidationHelper from 'stores/validation/helper';
|
|
import { difference } from 'tools/array';
|
|
import { makeDisposable } from 'tools/mobx';
|
|
import * as seasonsConstants from './lib/seasons-constants';
|
|
import * as seasonsTools from './lib/seasons-tools';
|
|
import validatePaymentsTable from './validation';
|
|
|
|
export default function paymentsReactions(
|
|
store: RootStore,
|
|
apolloClient: ApolloClient<object>,
|
|
queryClient: QueryClient
|
|
) {
|
|
const { $calculation, $tables, $process } = store;
|
|
|
|
reaction(
|
|
() => $calculation.element('tbxFirstPaymentPerc').getValue(),
|
|
(firstPaymentPerc) => {
|
|
$tables.payments.setValue(0, firstPaymentPerc);
|
|
}
|
|
);
|
|
|
|
reaction(
|
|
() => $calculation.element('tbxLastPaymentPerc').getValue(),
|
|
(lastPaymentPerc) => {
|
|
const paymentsLength = $tables.payments.values.length;
|
|
$tables.payments.setValue(paymentsLength - 1, lastPaymentPerc);
|
|
}
|
|
);
|
|
|
|
/**
|
|
* Аннуитет
|
|
*/
|
|
makeDisposable(
|
|
() =>
|
|
reaction(
|
|
() => {
|
|
const graphType = $calculation.element('radioGraphType').getValue();
|
|
const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue();
|
|
|
|
return {
|
|
graphType,
|
|
leasingPeriod,
|
|
};
|
|
},
|
|
({ graphType, leasingPeriod }) => {
|
|
if (graphType === 100_000_000) {
|
|
const middlePayments: Row[] = Array.from(
|
|
{
|
|
length: leasingPeriod - 2,
|
|
},
|
|
() => ({
|
|
value: 100,
|
|
status: 'Disabled',
|
|
})
|
|
);
|
|
|
|
const firstPaymentPerc = $calculation.element('tbxFirstPaymentPerc').getValue();
|
|
const lastPaymentPerc = $calculation.element('tbxLastPaymentPerc').getValue();
|
|
|
|
$tables.payments.setRows([
|
|
{
|
|
value: firstPaymentPerc,
|
|
status: 'Disabled',
|
|
},
|
|
...middlePayments,
|
|
{
|
|
value: lastPaymentPerc,
|
|
status: 'Disabled',
|
|
},
|
|
]);
|
|
}
|
|
},
|
|
{
|
|
fireImmediately: true,
|
|
}
|
|
),
|
|
() => $process.has('LoadKP')
|
|
);
|
|
|
|
/**
|
|
* Равноубывающий
|
|
*/
|
|
makeDisposable(
|
|
() =>
|
|
reaction(
|
|
() => {
|
|
const graphType = $calculation.element('radioGraphType').getValue();
|
|
const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue();
|
|
const parmentsDecreasePercent = $calculation
|
|
.element('tbxParmentsDecreasePercent')
|
|
.getValue();
|
|
|
|
return {
|
|
graphType,
|
|
leasingPeriod,
|
|
parmentsDecreasePercent,
|
|
};
|
|
},
|
|
({ graphType, leasingPeriod, parmentsDecreasePercent }) => {
|
|
if (graphType === 100_000_002) {
|
|
const middlePayments: Row[] = Array.from(
|
|
{
|
|
length: leasingPeriod - 2,
|
|
},
|
|
(_, k) => {
|
|
const payment = 100 * (parmentsDecreasePercent / 100) ** k;
|
|
|
|
return {
|
|
value: Number(payment.toFixed(2)),
|
|
status: 'Disabled',
|
|
};
|
|
}
|
|
);
|
|
|
|
const firstPaymentPerc = $calculation.element('tbxFirstPaymentPerc').getValue();
|
|
const lastPaymentPerc = $calculation.element('tbxLastPaymentPerc').getValue();
|
|
|
|
$tables.payments.setRows([
|
|
{
|
|
value: firstPaymentPerc,
|
|
status: 'Disabled',
|
|
},
|
|
...middlePayments,
|
|
{
|
|
value: lastPaymentPerc,
|
|
status: 'Disabled',
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
),
|
|
() => $process.has('LoadKP')
|
|
);
|
|
|
|
/**
|
|
* Легкий старт
|
|
*/
|
|
makeDisposable(
|
|
() =>
|
|
reaction(
|
|
() => {
|
|
const graphType = $calculation.element('radioGraphType').getValue();
|
|
const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue();
|
|
|
|
return {
|
|
graphType,
|
|
leasingPeriod,
|
|
};
|
|
},
|
|
({ graphType, leasingPeriod }) => {
|
|
if (graphType === 100_000_004) {
|
|
const editablePayments: Row[] = [
|
|
{
|
|
value: 25,
|
|
status: 'Default',
|
|
},
|
|
{
|
|
value: 50,
|
|
status: 'Default',
|
|
},
|
|
{
|
|
value: 75,
|
|
status: 'Default',
|
|
},
|
|
];
|
|
|
|
const payments: Row[] = Array.from(
|
|
{
|
|
length: leasingPeriod - 5,
|
|
},
|
|
() => ({
|
|
value: 100,
|
|
status: 'Disabled',
|
|
})
|
|
);
|
|
|
|
const firstPaymentPerc = $calculation.element('tbxFirstPaymentPerc').getValue();
|
|
const lastPaymentPerc = $calculation.element('tbxLastPaymentPerc').getValue();
|
|
|
|
$tables.payments.setRows([
|
|
{
|
|
value: firstPaymentPerc,
|
|
status: 'Disabled',
|
|
},
|
|
...editablePayments,
|
|
...payments,
|
|
{
|
|
value: lastPaymentPerc,
|
|
status: 'Disabled',
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
),
|
|
() => $process.has('LoadKP')
|
|
);
|
|
|
|
/**
|
|
* Дегрессия
|
|
*/
|
|
// 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,
|
|
]);
|
|
const seasonSeasonTypes = new Set([100_000_000, 100_000_001, 100_000_002]);
|
|
|
|
reaction(
|
|
() => {
|
|
const graphType = $calculation.element('radioGraphType').getValue();
|
|
|
|
return graphType;
|
|
},
|
|
(graphType) => {
|
|
let selectSeasonTypeOptions: CalculationOptions['selectSeasonType'] = [];
|
|
|
|
if (graphType === 100_000_001) {
|
|
selectSeasonTypeOptions = selectSeasonType.filter((option) =>
|
|
degressionSeasonTypes.has(option.value)
|
|
);
|
|
} else if (graphType === 100_000_003) {
|
|
selectSeasonTypeOptions = selectSeasonType.filter((option) =>
|
|
seasonSeasonTypes.has(option.value)
|
|
);
|
|
}
|
|
$calculation.element('selectSeasonType').setOptions(selectSeasonTypeOptions);
|
|
}
|
|
);
|
|
|
|
const degressionSteps: { [key: number]: Array<number> } = {
|
|
100_000_003: [100, 50, 25],
|
|
100_000_004: [100, 30, 10],
|
|
100_000_005: [100, 70, 40],
|
|
100_000_006: [100, 7, 3],
|
|
};
|
|
|
|
makeDisposable(
|
|
() =>
|
|
reaction(
|
|
() => {
|
|
const degressionType = $calculation.element('selectSeasonType').getValue();
|
|
const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue();
|
|
const graphType = $calculation.element('radioGraphType').getValue();
|
|
|
|
return {
|
|
degressionType,
|
|
leasingPeriod,
|
|
graphType,
|
|
};
|
|
},
|
|
({ degressionType, leasingPeriod, graphType }) => {
|
|
if (graphType === 100_000_001) {
|
|
let payments: Row[] = [];
|
|
|
|
switch (degressionType) {
|
|
case 100_000_007: {
|
|
const editablePayments: Row[] = Array.from(
|
|
{
|
|
length: leasingPeriod - 3,
|
|
},
|
|
() => ({
|
|
value: 100,
|
|
status: 'Default',
|
|
})
|
|
);
|
|
|
|
payments = [
|
|
{
|
|
value: 100,
|
|
status: 'Disabled',
|
|
},
|
|
...editablePayments,
|
|
];
|
|
|
|
break;
|
|
}
|
|
case 100_000_003:
|
|
case 100_000_004:
|
|
case 100_000_005:
|
|
case 100_000_006: {
|
|
const [step1, step2, step3] = degressionSteps[degressionType];
|
|
const paymentsInStep = Math.ceil((leasingPeriod - 2) / 3);
|
|
|
|
payments = Array.from(
|
|
{
|
|
length: leasingPeriod - 2,
|
|
},
|
|
(_v, i) => {
|
|
let value = step3;
|
|
|
|
if (i <= paymentsInStep * 2 - 1) {
|
|
value = step2;
|
|
}
|
|
|
|
if (i <= paymentsInStep - 1) {
|
|
value = step1;
|
|
}
|
|
|
|
return {
|
|
value,
|
|
status: 'Disabled',
|
|
};
|
|
}
|
|
);
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
|
|
const firstPaymentPerc = $calculation.element('tbxFirstPaymentPerc').getValue();
|
|
const lastPaymentPerc = $calculation.element('tbxLastPaymentPerc').getValue();
|
|
|
|
$tables.payments.setRows([
|
|
{
|
|
value: firstPaymentPerc,
|
|
status: 'Disabled',
|
|
},
|
|
...payments,
|
|
{
|
|
value: lastPaymentPerc,
|
|
status: 'Disabled',
|
|
},
|
|
]);
|
|
}
|
|
}
|
|
),
|
|
() => $process.has('LoadKP')
|
|
);
|
|
|
|
makeDisposable(
|
|
() =>
|
|
reaction(
|
|
() => {
|
|
const graphType = $calculation.element('radioGraphType').getValue();
|
|
const payments = toJS($tables.payments.values);
|
|
const degressionType = $calculation.element('selectSeasonType').getValue();
|
|
|
|
return {
|
|
graphType,
|
|
payments,
|
|
degressionType,
|
|
};
|
|
},
|
|
(nextParams, prevParams) => {
|
|
if (nextParams.graphType === 100_000_001 && nextParams.degressionType === 100_000_007) {
|
|
const changes = difference(nextParams.payments, prevParams.payments);
|
|
|
|
if (changes === null || changes.length > 1) return;
|
|
|
|
const [changeIndex] = changes;
|
|
const value = nextParams.payments[changeIndex];
|
|
const payments = nextParams.payments.slice(1, -1).map((payment, i) => {
|
|
if (i <= changeIndex - 2) return payment;
|
|
|
|
return value;
|
|
});
|
|
|
|
const firstPaymentPerc = $calculation.element('tbxFirstPaymentPerc').getValue();
|
|
const lastPaymentPerc = $calculation.element('tbxLastPaymentPerc').getValue();
|
|
|
|
$tables.payments.setValues([firstPaymentPerc, ...payments, lastPaymentPerc]);
|
|
}
|
|
},
|
|
{
|
|
delay: 50,
|
|
equals: comparer.structural,
|
|
}
|
|
),
|
|
() => $process.has('LoadKP')
|
|
);
|
|
|
|
/**
|
|
* Сезонный
|
|
*/
|
|
|
|
reaction(
|
|
() => {
|
|
const graphType = $calculation.element('radioGraphType').getValue();
|
|
|
|
return {
|
|
graphType,
|
|
};
|
|
},
|
|
({ graphType }) => {
|
|
if (graphType !== 100_000_003) return;
|
|
|
|
const seasonType = $calculation.element('selectSeasonType').getValue();
|
|
const highSeasonStart = $calculation.element('selectHighSeasonStart').getValue();
|
|
|
|
if (!seasonType || !highSeasonStart) {
|
|
$tables.payments.setValues([]);
|
|
}
|
|
}
|
|
);
|
|
|
|
reaction(
|
|
() => {
|
|
const seasonType = $calculation.element('selectSeasonType').getValue();
|
|
|
|
return seasonType;
|
|
},
|
|
(seasonType) => {
|
|
const graphType = $calculation.element('radioGraphType').getValue();
|
|
if (graphType !== 100_000_003) return;
|
|
|
|
if (!seasonType) {
|
|
$calculation.element('selectHighSeasonStart').reset();
|
|
|
|
return;
|
|
}
|
|
|
|
const highSeasonStartOptions = selectHighSeasonStart.filter(
|
|
(option) => !seasonsConstants.FORBIDDEN_HIGH_SEASON_START[seasonType].includes(option.label)
|
|
);
|
|
|
|
$calculation.element('selectHighSeasonStart').setOptions(highSeasonStartOptions);
|
|
}
|
|
);
|
|
|
|
function generateSeasonPaymentsRows(seasonType: number, shiftNumber: number, payments: number[]) {
|
|
const shiftedPeriods = new Set(
|
|
seasonsConstants.SEASONS_PERIODS[seasonType].map(
|
|
(position) => (position + shiftNumber) % seasonsConstants.SEASONS_PERIOD_NUMBER
|
|
)
|
|
);
|
|
const rows: Row[] = payments.map((value, i) => ({
|
|
value,
|
|
status: shiftedPeriods.has(i) ? 'Default' : 'Disabled',
|
|
}));
|
|
|
|
return rows;
|
|
}
|
|
|
|
makeDisposable(
|
|
() =>
|
|
reaction(
|
|
() => {
|
|
const seasonType = $calculation.element('selectSeasonType').getValue();
|
|
const highSeasonStartOption = $calculation.element('selectHighSeasonStart').getOption();
|
|
const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue();
|
|
|
|
return {
|
|
seasonType,
|
|
highSeasonStartOption,
|
|
leasingPeriod,
|
|
};
|
|
},
|
|
({ seasonType, highSeasonStartOption, leasingPeriod }) => {
|
|
const graphType = $calculation.element('radioGraphType').getValue();
|
|
if (graphType !== 100_000_003) return;
|
|
|
|
if (!seasonType || !highSeasonStartOption) {
|
|
$tables.payments.setValues([]);
|
|
|
|
return;
|
|
}
|
|
|
|
const seasons = seasonsTools.generateSeasons(
|
|
seasonType,
|
|
seasonsConstants.DEFAULT_SEASONS_VALUES
|
|
);
|
|
const shiftNumber = Number.parseInt(highSeasonStartOption.label, 10) - 2;
|
|
const payments = seasonsTools.generateSeasonsPayments(
|
|
leasingPeriod,
|
|
shift(seasons, shiftNumber)
|
|
);
|
|
const rows: Row[] = generateSeasonPaymentsRows(seasonType, shiftNumber, payments);
|
|
const firstPaymentPerc = $calculation.element('tbxFirstPaymentPerc').getValue();
|
|
const lastPaymentPerc = $calculation.element('tbxLastPaymentPerc').getValue();
|
|
|
|
$tables.payments.setRows([
|
|
{
|
|
value: firstPaymentPerc,
|
|
status: 'Disabled',
|
|
},
|
|
...rows,
|
|
{
|
|
value: lastPaymentPerc,
|
|
status: 'Disabled',
|
|
},
|
|
]);
|
|
}
|
|
),
|
|
() => $process.has('LoadKP')
|
|
);
|
|
|
|
makeDisposable(
|
|
() =>
|
|
reaction(
|
|
() => {
|
|
const payments = toJS($tables.payments.values);
|
|
const seasons = payments.slice(1, seasonsConstants.SEASONS_PERIOD_NUMBER + 1);
|
|
|
|
return seasons;
|
|
},
|
|
(nextSeasons, prevSeasons) => {
|
|
const graphType = $calculation.element('radioGraphType').getValue();
|
|
if (graphType !== 100_000_003) return;
|
|
|
|
const seasonType = $calculation.element('selectSeasonType').getValue();
|
|
const highSeasonStartOption = $calculation.element('selectHighSeasonStart').getOption();
|
|
if (!seasonType || !highSeasonStartOption) return;
|
|
|
|
const shiftNumber = Number.parseInt(highSeasonStartOption.label, 10) - 2;
|
|
const unshiftedNextSeasons = shift(nextSeasons, -shiftNumber);
|
|
const unshiftedPrevSeasons = shift(prevSeasons, -shiftNumber);
|
|
|
|
const changes = difference(unshiftedNextSeasons, unshiftedPrevSeasons);
|
|
if (changes === null || changes.length > 1) return;
|
|
|
|
const [changeIndex] = changes;
|
|
const positionIndex = seasonsTools.getPositionIndex(seasonType, changeIndex);
|
|
|
|
const values = seasonsTools.getSeasonsValues(seasonType, unshiftedNextSeasons);
|
|
values[positionIndex] = unshiftedNextSeasons[changeIndex];
|
|
|
|
const seasons = seasonsTools.generateSeasons(seasonType, values);
|
|
const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue();
|
|
const payments = seasonsTools.generateSeasonsPayments(
|
|
leasingPeriod,
|
|
shift(seasons, shiftNumber)
|
|
);
|
|
const rows: Row[] = generateSeasonPaymentsRows(seasonType, shiftNumber, payments);
|
|
const firstPaymentPerc = $calculation.element('tbxFirstPaymentPerc').getValue();
|
|
const lastPaymentPerc = $calculation.element('tbxLastPaymentPerc').getValue();
|
|
|
|
$tables.payments.setRows([
|
|
{
|
|
value: firstPaymentPerc,
|
|
status: 'Disabled',
|
|
},
|
|
...rows,
|
|
{
|
|
value: lastPaymentPerc,
|
|
status: 'Disabled',
|
|
},
|
|
]);
|
|
},
|
|
{
|
|
delay: 50,
|
|
equals: comparer.structural,
|
|
}
|
|
),
|
|
() => $process.has('LoadKP')
|
|
);
|
|
|
|
/**
|
|
* Валидация
|
|
*/
|
|
const validationHelper = new ValidationHelper();
|
|
|
|
reaction(
|
|
() => {
|
|
const payments = toJS($tables.payments.values);
|
|
const graphType = $calculation.element('radioGraphType').getValue();
|
|
const seasonType = $calculation.element('selectSeasonType').getValue();
|
|
const highSeasonStart = $calculation.element('selectHighSeasonStart').getValue();
|
|
const leasingPeriod = $calculation.element('tbxLeasingPeriod').getValue();
|
|
|
|
return {
|
|
payments,
|
|
graphType,
|
|
seasonType,
|
|
highSeasonStart,
|
|
leasingPeriod,
|
|
};
|
|
},
|
|
() => {
|
|
validationHelper.removeErrors();
|
|
const errorText = validatePaymentsTable(store);
|
|
|
|
if (errorText) {
|
|
const removeError = $tables.payments.validation.addError(errorText);
|
|
validationHelper.add(removeError);
|
|
}
|
|
},
|
|
{
|
|
delay: 50,
|
|
equals: comparer.structural,
|
|
}
|
|
);
|
|
}
|