2022-11-02 21:36:48 +03:00

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,
}
);
}