/* 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, 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 } = { 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, } ); }