From bfd6f1d1f9a46340af324320b02fcaa5f8b17964 Mon Sep 17 00:00:00 2001 From: Chika Date: Tue, 16 Mar 2021 18:25:12 +0300 Subject: [PATCH] merge release/calc-56_limitations-on-degression --- .../Calculation/Sections/sectionsList.ts | 8 +- .../Calculation/lib/elements/components.ts | 2 +- .../Calculation/lib/elements/elementsProps.ts | 3 +- .../Calculation/lib/elements/tables.ts | 1 + .../Calculation/lib/elements/titles.ts | 2 +- .../Calculation/lib/elements/values.ts | 2 +- .../Effects/actions/calculate/validate.ts | 109 ++++++++++- .../Effects/reactions/otherReactions.ts | 72 ++++++- .../reactions/recalcWoRevisionReactions.ts | 2 +- .../Effects/reactions/tablesReactions.ts | 98 +++++++--- .../CalculationStore/config/initialOptions.ts | 23 ++- .../config/initialTables/tablePayments.ts | 180 +++++------------- src/core/types/Calculation/Store/elements.ts | 2 +- src/core/types/Calculation/Store/options.ts | 1 + 14 files changed, 332 insertions(+), 173 deletions(-) diff --git a/src/client/Containers/Calculation/Sections/sectionsList.ts b/src/client/Containers/Calculation/Sections/sectionsList.ts index 2dfb741..ab39676 100644 --- a/src/client/Containers/Calculation/Sections/sectionsList.ts +++ b/src/client/Containers/Calculation/Sections/sectionsList.ts @@ -58,9 +58,9 @@ const sections: ISection[] = [ { elements: ['selectProduct'], }, - { - elements: ['selectClientRisk'], - }, + // { + // elements: ['selectClientRisk'], + // }, // { // elements: ['selectClientType' ], // }, @@ -94,8 +94,8 @@ const sections: ISection[] = [ title: 'Параметры графика платежей', elements: [ 'radioGraphType', + 'selectSeasonType', 'tbxParmentsDecreasePercent', - // 'radioSeasonType', // 'selectHighSeasonStart', ], layout: { diff --git a/src/client/Containers/Calculation/lib/elements/components.ts b/src/client/Containers/Calculation/lib/elements/components.ts index a6f781b..ba70b9f 100644 --- a/src/client/Containers/Calculation/lib/elements/components.ts +++ b/src/client/Containers/Calculation/lib/elements/components.ts @@ -49,7 +49,7 @@ const elementsComponents: TElements = { tbxLeasingPeriod: InputNumber, radioGraphType: Radio, tbxParmentsDecreasePercent: InputNumber, - radioSeasonType: Radio, + selectSeasonType: Select, selectHighSeasonStart: Select, tbxComissionPerc: InputNumber, tbxComissionRub: InputNumber, diff --git a/src/client/Containers/Calculation/lib/elements/elementsProps.ts b/src/client/Containers/Calculation/lib/elements/elementsProps.ts index 81f0b0a..52c3f26 100644 --- a/src/client/Containers/Calculation/lib/elements/elementsProps.ts +++ b/src/client/Containers/Calculation/lib/elements/elementsProps.ts @@ -135,8 +135,7 @@ const elementsProps: TElements = { min: '50', max: '99', }, - radioSeasonType: { - style: 'button', + selectSeasonType: { }, tbxComissionPerc: { min: '0', diff --git a/src/client/Containers/Calculation/lib/elements/tables.ts b/src/client/Containers/Calculation/lib/elements/tables.ts index 8ae9a21..de25e76 100644 --- a/src/client/Containers/Calculation/lib/elements/tables.ts +++ b/src/client/Containers/Calculation/lib/elements/tables.ts @@ -23,6 +23,7 @@ const elementsTables: StoreTables = { min: '0.01', max: '100.00', step: '1.00', + precision: 2, // TODO: toFixed //numeral.js }, diff --git a/src/client/Containers/Calculation/lib/elements/titles.ts b/src/client/Containers/Calculation/lib/elements/titles.ts index 25d6643..822bcbe 100644 --- a/src/client/Containers/Calculation/lib/elements/titles.ts +++ b/src/client/Containers/Calculation/lib/elements/titles.ts @@ -38,7 +38,7 @@ const elementsTitles: TElements = { radioBalanceHolder: 'Балансодержатель', radioGraphType: 'Вид графика', tbxParmentsDecreasePercent: 'Процент убывания платежей', - radioSeasonType: 'Тип сезонности', + selectSeasonType: 'Тип дегрессии/сезонности', selectHighSeasonStart: 'С какого платежа начинается высокий сезон', tbxComissionPerc: 'Комиссия, %', tbxComissionRub: 'Комиссия, руб.', diff --git a/src/client/Containers/Calculation/lib/elements/values.ts b/src/client/Containers/Calculation/lib/elements/values.ts index 2c8fb25..13010ac 100644 --- a/src/client/Containers/Calculation/lib/elements/values.ts +++ b/src/client/Containers/Calculation/lib/elements/values.ts @@ -42,7 +42,7 @@ export const elementsValues: TElements = { radioBalanceHolder: 'balanceHolder', radioGraphType: 'graphType', tbxParmentsDecreasePercent: 'parmentsDecreasePercent', - radioSeasonType: 'seasonType', + selectSeasonType: 'seasonType', selectHighSeasonStart: 'highSeasonStart', tbxComissionPerc: 'comissionPerc', tbxComissionRub: 'comissionRub', diff --git a/src/client/stores/CalculationStore/Effects/actions/calculate/validate.ts b/src/client/stores/CalculationStore/Effects/actions/calculate/validate.ts index a37c897..e6c3525 100644 --- a/src/client/stores/CalculationStore/Effects/actions/calculate/validate.ts +++ b/src/client/stores/CalculationStore/Effects/actions/calculate/validate.ts @@ -56,7 +56,7 @@ const validateElements = () => { }); }; -const validateTables = () => { +const validateInsuranceTable = () => { const tableInsurance = CalculationStore.tables.tableInsurance; const kaskoRowIndex = tableInsurance.rows.findIndex( @@ -89,7 +89,112 @@ const validateTables = () => { ]); }; +const validatePaymentsTable = () => { + const { graphType } = CalculationStore.values; + const payments = CalculationStore.tables.tablePayments.rows; + switch (graphType) { + case 100000001: { + { + const areMiddleRowsEqual = + new Set( + payments + .slice(1, payments.length - 1) + .map(x => x.paymentRelation?.value), + ).size === 1; + + CalculationStore.setTableRows( + 'tablePayments', + 1, + )( + Array.from({ length: payments.length - 2 }, () => ({ + paymentRelation: { + validation: areMiddleRowsEqual ? false : true, + }, + })), + ); + } + + if ( + !CalculationStore.tables.tablePayments.rows.some( + x => x.paymentRelation?.validation === false, + ) + ) { + const target_payments = payments + .slice(1, 4) + .map(x => x.paymentRelation?.value); + const min = Math.min.apply(Math, target_payments); + const max = Math.max.apply(Math, target_payments); + const areInvalidRows = max - min > 10; + + CalculationStore.setTableRows( + 'tablePayments', + 1, + )( + Array.from({ length: 3 }, () => ({ + paymentRelation: { + validation: areInvalidRows ? false : true, + }, + })), + ); + } + + if ( + !CalculationStore.tables.tablePayments.rows.some( + x => x.paymentRelation?.validation === false, + ) + ) { + const target_payments = payments + .slice(1, payments.length - 1) + .map(x => x.paymentRelation?.value); + let pairs_number = 0; + new Set(target_payments).forEach(set_v => { + let setValueCount = 0; + target_payments.forEach(target_v => { + if (target_v === set_v) { + setValueCount++; + } + }); + if (setValueCount > 1) { + pairs_number++; + } + }); + + CalculationStore.setTableRows( + 'tablePayments', + 2, + )( + Array.from({ length: payments.length - 3 }, () => ({ + paymentRelation: { + validation: pairs_number >= 2 ? true : false, + }, + })), + ); + } + + break; + } + case 100000004: { + const areMiddleRowsEqual = + new Set(payments.slice(1, 4).map(x => x.paymentRelation?.value)) + .size === 1; + + CalculationStore.setTableRows( + 'tablePayments', + 1, + )( + Array.from({ length: 3 }, () => ({ + paymentRelation: { + validation: areMiddleRowsEqual ? false : true, + }, + })), + ); + break; + } + } +}; + export default () => { validateElements(); - validateTables(); + validateInsuranceTable(); + validatePaymentsTable(); }; diff --git a/src/client/stores/CalculationStore/Effects/reactions/otherReactions.ts b/src/client/stores/CalculationStore/Effects/reactions/otherReactions.ts index d06f43b..33a1f48 100644 --- a/src/client/stores/CalculationStore/Effects/reactions/otherReactions.ts +++ b/src/client/stores/CalculationStore/Effects/reactions/otherReactions.ts @@ -734,10 +734,39 @@ const reactionEffects: IReactionEffect[] = [ }, effect: graphType => { if (graphType) { + calculationStore.setValue('seasonType', null); switch (graphType) { + case 100000001: { + calculationStore.setStatus( + 'selectSeasonType', + ElementStatus.Default, + ); + + calculationStore.setFilter('selectSeasonType', options => + options.filter( + x => + x.value && + typeof x.value === 'number' && + [100000003, 100000004, 100000005, 100000006].includes( + x.value, + ), + ), + ); + + calculationStore.setStatus( + 'tbxParmentsDecreasePercent', + ElementStatus.Disabled, + ); + calculationStore.setStatus( + 'selectHighSeasonStart', + ElementStatus.Disabled, + ); + break; + } + case 100000002: { calculationStore.setStatus( - 'radioSeasonType', + 'selectSeasonType', ElementStatus.Disabled, ); calculationStore.setStatus( @@ -753,9 +782,18 @@ const reactionEffects: IReactionEffect[] = [ case 100000003: { calculationStore.setStatus( - 'radioSeasonType', + 'selectSeasonType', ElementStatus.Default, ); + calculationStore.setFilter('selectSeasonType', options => + options.filter( + x => + x.value && + typeof x.value === 'number' && + [100000000, 100000001, 100000002].includes(x.value), + ), + ); + calculationStore.setStatus( 'tbxParmentsDecreasePercent', ElementStatus.Disabled, @@ -769,7 +807,7 @@ const reactionEffects: IReactionEffect[] = [ default: { calculationStore.setStatus( - 'radioSeasonType', + 'selectSeasonType', ElementStatus.Disabled, ); calculationStore.setStatus( @@ -826,19 +864,36 @@ const reactionEffects: IReactionEffect[] = [ effect: leasingPeriod => { if (leasingPeriod) { if (parseInt(leasingPeriod) < 12) { + calculationStore.setValue('insNSIB', null); + calculationStore.setStatus('selectInsNSIB', ElementStatus.Disabled); + } else { + calculationStore.setStatus('selectInsNSIB', ElementStatus.Default); + } + } + }, + options: { + fireImmediately: true, + }, + }), + + calculationStore => ({ + expression: () => { + const { leasingPeriod } = calculationStore.values; + return leasingPeriod; + }, + effect: leasingPeriod => { + if (leasingPeriod) { + if (parseInt(leasingPeriod) < 13) { calculationStore.setStatus( 'radioBalanceHolder', ElementStatus.Disabled, ); calculationStore.setValue('balanceHolder', 100000000); - calculationStore.setValue('insNSIB', null); - calculationStore.setStatus('selectInsNSIB', ElementStatus.Disabled); } else { calculationStore.setStatus( 'radioBalanceHolder', ElementStatus.Default, ); - calculationStore.setStatus('selectInsNSIB', ElementStatus.Default); } } }, @@ -1678,7 +1733,10 @@ const reactionEffects: IReactionEffect[] = [ default: case 100000000: { calculationStore.setStatus('tbxIRR_Perc', ElementStatus.Default); - calculationStore.setStatus('tbxTotalPayments', ElementStatus.Disabled); + calculationStore.setStatus( + 'tbxTotalPayments', + ElementStatus.Disabled, + ); break; } case 100000002: { diff --git a/src/client/stores/CalculationStore/Effects/reactions/recalcWoRevisionReactions.ts b/src/client/stores/CalculationStore/Effects/reactions/recalcWoRevisionReactions.ts index f858c49..41f02c4 100644 --- a/src/client/stores/CalculationStore/Effects/reactions/recalcWoRevisionReactions.ts +++ b/src/client/stores/CalculationStore/Effects/reactions/recalcWoRevisionReactions.ts @@ -392,7 +392,7 @@ const elementsToDisable: (ElementsNames | TableNames)[] = [ 'radioLastPaymentRule', 'radioGraphType', 'tbxParmentsDecreasePercent', - 'radioSeasonType', + 'selectSeasonType', 'selectHighSeasonStart', 'selectLeaseObjectType', 'cbxLeaseObjectUsed', diff --git a/src/client/stores/CalculationStore/Effects/reactions/tablesReactions.ts b/src/client/stores/CalculationStore/Effects/reactions/tablesReactions.ts index 23864cc..e51c895 100644 --- a/src/client/stores/CalculationStore/Effects/reactions/tablesReactions.ts +++ b/src/client/stores/CalculationStore/Effects/reactions/tablesReactions.ts @@ -455,7 +455,6 @@ export default [ parmentsDecreasePercent, seasonType, highSeasonStart: highSeasonStartId, - lastPaymentPerc, } = calculationStore.values; const highSeasonStart = calculationStore.options.selectHighSeasonStart?.find( @@ -468,7 +467,6 @@ export default [ parmentsDecreasePercent, seasonType, highSeasonStart: parseInt(highSeasonStart?.name || '2'), - lastPaymentPerc, }; }, effect: async (nextParams, prevParams) => { @@ -476,14 +474,13 @@ export default [ return; } - const { firstPaymentPerc } = calculationStore.values; + const { firstPaymentPerc, lastPaymentPerc } = calculationStore.values; const { leasingPeriod, graphType, parmentsDecreasePercent, seasonType, highSeasonStart, - lastPaymentPerc, } = nextParams; const prevValues = toJS(calculationStore.tables.tablePayments.rows).map( @@ -514,22 +511,57 @@ export default [ } case 100000001: { - const middleRows = Array.from({ length: leasingPeriod - 3 }, () => ({ - paymentRelation: { - value: 100, - status: ElementStatus.Default, - }, - })); - payments = [ - ...payments, - { - paymentRelation: { - value: 100, - status: ElementStatus.Disabled, + if (seasonType) { + const paymentsInStep = Math.ceil((leasingPeriod - 2) / 3); + const targetSeasonType = calculationStore.options?.selectSeasonType?.find( + x => x?.value === seasonType, + ); + const steps: number[] = targetSeasonType && targetSeasonType.steps; + + const middleRows = Array.from( + { length: leasingPeriod - 2 }, + (_v, i) => { + let value = steps[2]; + + if (i <= paymentsInStep * 2 - 1) { + value = steps[1]; + } + + if (i <= paymentsInStep - 1) { + value = steps[0]; + } + + return { + paymentRelation: { + value, + status: ElementStatus.Disabled, + }, + }; }, - }, - ...middleRows, - ]; + ); + payments = [...payments, ...middleRows]; + + } else { + const middleRows = Array.from( + { length: leasingPeriod - 3 }, + () => ({ + paymentRelation: { + value: 100, + status: ElementStatus.Default, + }, + }), + ); + payments = [ + ...payments, + { + paymentRelation: { + value: 100, + status: ElementStatus.Disabled, + }, + }, + ...middleRows, + ]; + } break; } @@ -732,7 +764,10 @@ export default [ } } - payments = [ + calculationStore.setTableRows( + 'tablePayments', + 0, + )([ ...payments, { paymentRelation: { @@ -740,9 +775,28 @@ export default [ status: ElementStatus.Disabled, }, }, - ]; + ]); + }, + options: { + fireImmediately: true, + }, + }), - calculationStore.setTableRows('tablePayments', 0)(payments); + calculationStore => ({ + expression: () => { + const { lastPaymentPerc, leasingPeriod } = calculationStore.values; + return { lastPaymentPerc, leasingPeriod }; + }, + effect: ({ lastPaymentPerc }) => { + calculationStore.setTableRow( + 'tablePayments', + calculationStore.tables.tablePayments.rows.length - 1, + )({ + paymentRelation: { + value: lastPaymentPerc, + status: ElementStatus.Disabled, + }, + }); }, options: { fireImmediately: true, diff --git a/src/client/stores/CalculationStore/config/initialOptions.ts b/src/client/stores/CalculationStore/config/initialOptions.ts index 322a6a6..ce90491 100644 --- a/src/client/stores/CalculationStore/config/initialOptions.ts +++ b/src/client/stores/CalculationStore/config/initialOptions.ts @@ -81,12 +81,11 @@ const initialOptions: TElements = { }, ], - radioSeasonType: [ + selectSeasonType: [ { name: '6/6', value: 100000000, }, - { name: '8/4', value: 100000001, @@ -95,6 +94,26 @@ const initialOptions: TElements = { name: '4/4/4', value: 100000002, }, + { + name: '100.50.25', + value: 100000003, + steps: [100, 50, 25], + }, + { + name: '100.30.10', + value: 100000004, + steps: [100, 30, 10], + }, + { + name: '100.70.40', + value: 100000005, + steps: [100, 70, 40], + }, + { + name: '100.7.3', + value: 100000006, + steps: [100, 7, 3], + }, ], selectHighSeasonStart: [ diff --git a/src/client/stores/CalculationStore/config/initialTables/tablePayments.ts b/src/client/stores/CalculationStore/config/initialTables/tablePayments.ts index 00c1de5..e6ba8f6 100644 --- a/src/client/stores/CalculationStore/config/initialTables/tablePayments.ts +++ b/src/client/stores/CalculationStore/config/initialTables/tablePayments.ts @@ -1,5 +1,5 @@ import { ITable } from 'core/types/Calculation/Store/tables'; -import { toJS } from 'mobx'; +import { isEqual } from 'lodash'; const tablePayments: ITable = { rows: [ @@ -7,87 +7,46 @@ const tablePayments: ITable = { // paymentRelation: { value: 5 }, // }, ], - callbacks: { - paymentRelation: ({ - calculationStore, - tableName, - rowIndex, - value, - }) => { - const { graphType } = calculationStore.values; - if (graphType === 100000001) { - if ( - rowIndex > 1 && - rowIndex < calculationStore.tables[tableName].rows.length - 1 - ) { - calculationStore.setTableRows( - tableName, - rowIndex, - )( - Array.from( - { - length: - calculationStore.tables[tableName].rows.length - rowIndex - 1, - }, - () => ({ - paymentRelation: { - value, - }, - }), - ), - ); - for (let i in calculationStore.tables[tableName].rows) { - const currRow = - calculationStore.tables[tableName].rows[parseInt(i)]; - const prevRow = - calculationStore.tables[tableName].rows[parseInt(i) - 1]; - if ( - parseInt(i) > 1 && - currRow && - prevRow && - currRow.paymentRelation?.value > prevRow.paymentRelation?.value - ) { - calculationStore.setTableRow( - tableName, - parseInt(i), - )({ - paymentRelation: { - validation: false, - }, - }); - } else { - calculationStore.setTableRow( - tableName, - parseInt(i), - )({ - paymentRelation: { - validation: true, - }, - }); - } - } - const middleRows = toJS(calculationStore.tables[tableName].rows); - middleRows.splice(0, 1); - middleRows.splice(-1, 1); + callbacks: { + paymentRelation: ({ calculationStore, tableName, rowIndex, value }) => { + const rowLength = calculationStore.tables[tableName].rows.length; + const { graphType, seasonType } = calculationStore.values; + + if (graphType === 100000001 && !seasonType) { + if (rowIndex > 1 && rowIndex < rowLength - 1) { if ( - middleRows.every( - x => - x.paymentRelation?.value === - middleRows[0].paymentRelation?.value, - ) - ) { + calculationStore.tables[tableName].rows[rowIndex].paymentRelation + ?.value !== + calculationStore.tables[tableName].rows[rowIndex + 1] + .paymentRelation?.value + ) calculationStore.setTableRows( tableName, - 2, + rowIndex, )( - Array.from({ length: middleRows.length - 1 }, (v, i) => ({ - paymentRelation: { - validation: false, + Array.from( + { + length: rowLength - rowIndex - 1, }, - })), + (_v, i) => ({ + paymentRelation: { + value, + }, + }), + ), ); - } + + const prevRow = calculationStore.tables[tableName].rows[rowIndex - 1]; + const isCurrentValueGreater = value > prevRow.paymentRelation?.value; + calculationStore.setTableRow( + tableName, + rowIndex, + )({ + paymentRelation: { + validation: isCurrentValueGreater || value < 3 ? false : true, + }, + }); } } @@ -110,60 +69,23 @@ const tablePayments: ITable = { if (graphType === 100000004) { if (rowIndex > 0 && rowIndex < 4) { - for (let i in calculationStore.tables[tableName].rows) { - const currRow = - calculationStore.tables[tableName].rows[parseInt(i)]; - const nextRow = - calculationStore.tables[tableName].rows[parseInt(i) + 1]; - if ( - parseInt(i) > 0 && - parseInt(i) < 4 && - currRow && - nextRow && - currRow.paymentRelation?.value > nextRow.paymentRelation?.value - ) { - calculationStore.setTableRow( - tableName, - parseInt(i), - )({ - paymentRelation: { - validation: false, - }, - }); - } else { - calculationStore.setTableRow( - tableName, - parseInt(i), - )({ - paymentRelation: { - validation: true, - }, - }); - } - } - - const middleRows = toJS(calculationStore.tables[tableName].rows); - middleRows.splice(0, 1); - middleRows.length = 3; - - if ( - middleRows.every( - x => - x.paymentRelation?.value === - middleRows[0].paymentRelation?.value, - ) - ) { - calculationStore.setTableRows( - tableName, - 1, - )( - Array.from({ length: middleRows.length }, (v, i) => ({ - paymentRelation: { - validation: false, - }, - })), - ); - } + const isCorrect3MiddleRows = isEqual( + calculationStore.tables[tableName].rows + .slice(1, 4) + .map(x => x.paymentRelation?.value) + .sort((a, b) => a - b), + calculationStore.tables[tableName].rows + .slice(1, 4) + .map(x => x.paymentRelation?.value), + ); + calculationStore.setTableRow( + tableName, + rowIndex, + )({ + paymentRelation: { + validation: isCorrect3MiddleRows ? true : false, + }, + }); } } }, diff --git a/src/core/types/Calculation/Store/elements.ts b/src/core/types/Calculation/Store/elements.ts index bbd04e4..9600f99 100644 --- a/src/core/types/Calculation/Store/elements.ts +++ b/src/core/types/Calculation/Store/elements.ts @@ -37,7 +37,7 @@ export type ElementsNames = | 'radioBalanceHolder' | 'radioGraphType' | 'tbxParmentsDecreasePercent' - | 'radioSeasonType' + | 'selectSeasonType' | 'selectHighSeasonStart' | 'tbxComissionPerc' | 'tbxComissionRub' diff --git a/src/core/types/Calculation/Store/options.ts b/src/core/types/Calculation/Store/options.ts index 72b3c02..299b78b 100644 --- a/src/core/types/Calculation/Store/options.ts +++ b/src/core/types/Calculation/Store/options.ts @@ -4,4 +4,5 @@ export type IBaseOption = { */ name?: string; value?: string | number | boolean; + [prop: string]: any | any[]; };